1。 Vault
を使用した Spring BootSpring Cloud Vault
Spring Cloud Vault は、Spring Boot の PropertySource として Vault を統合します。Vault のシークレットはアプリケーション プロパティに自動的にバインドされます。
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-vault-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.vault</groupId>
<artifactId>spring-vault-core</artifactId>
</dependency>
構成
# application.yml
spring:
cloud:
vault:
uri: https://vault.company.com:8200
authentication: KUBERNETES
kubernetes:
role: webapp
kubernetes-path: auth/kubernetes
service-account-token-file: /var/run/secrets/kubernetes.io/serviceaccount/token
kv:
enabled: true
backend: secret
default-context: production/webapp
profile-separator: /
database:
enabled: true
role: webapp-db
backend: database
config:
lifecycle:
enabled: true
min-renewal: 10s
expiry-threshold: 1m
AppRole Authentication
spring:
cloud:
vault:
uri: https://vault.company.com:8200
authentication: APPROLE
app-role:
role-id: ${VAULT_ROLE_ID}
secret-id: ${VAULT_SECRET_ID}
app-role-path: auth/approle
コードでのシークレットの使用
// Secrets từ Vault tự động binding vào @Value
@Service
public class DatabaseService {
@Value("${db.username}")
private String dbUsername;
@Value("${db.password}")
private String dbPassword;
@Value("${api.key}")
private String apiKey;
}
// Hoặc dùng @ConfigurationProperties
@Configuration
@ConfigurationProperties(prefix = "db")
public class DatabaseConfig {
private String host;
private int port;
private String username;
private String password;
// getters, setters
}
Dynamic Database Credentials
@Configuration
public class VaultDatabaseConfig {
@Bean
public DataSource dataSource(
SecretLeaseContainer leaseContainer,
@Value("${spring.datasource.url}") String url) {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl(url);
// Lắng nghe secret rotation
leaseContainer.addLeaseListener(event -> {
if (event.getSource().getPath().equals("database/creds/webapp-db")) {
if (event instanceof SecretLeaseExpiredEvent ||
event instanceof SecretLeaseCreatedEvent) {
Map<String, Object> secrets = event.getSource().getSecrets();
dataSource.setUsername((String) secrets.get("username"));
dataSource.setPassword((String) secrets.get("password"));
// Soft evict connections
dataSource.getHikariPoolMXBean()
.softEvictConnections();
}
}
});
return dataSource;
}
}
Transit Encryption trong Java
@Service
public class EncryptionService {
private final VaultTransitOperations transit;
public EncryptionService(VaultTemplate vaultTemplate) {
this.transit = vaultTemplate.opsForTransit();
}
public String encrypt(String plaintext) {
return transit.encrypt("my-app-key", plaintext);
}
public String decrypt(String ciphertext) {
return transit.decrypt("my-app-key", ciphertext);
}
// Batch encryption
public List<VaultEncryptionResult> encryptBatch(List<String> plaintexts) {
List<Plaintext> batch = plaintexts.stream()
.map(Plaintext::of)
.collect(Collectors.toList());
return transit.encrypt("my-app-key", batch);
}
}
2。 Vault
を使用した Node.jsnode-vault Client
// npm install node-vault
const vault = require('node-vault')({
apiVersion: 'v1',
endpoint: process.env.VAULT_ADDR || 'https://vault.company.com:8200',
});
// Login với AppRole
async function login() {
const result = await vault.approleLogin({
role_id: process.env.VAULT_ROLE_ID,
secret_id: process.env.VAULT_SECRET_ID,
});
vault.token = result.auth.client_token;
return result;
}
// Đọc KV secret
async function getSecret(path) {
const result = await vault.read(`secret/data/${path}`);
return result.data.data;
}
// Sinh database credentials
async function getDatabaseCreds(role) {
const result = await vault.read(`database/creds/${role}`);
return {
username: result.data.username,
password: result.data.password,
leaseId: result.lease_id,
leaseDuration: result.lease_duration,
};
}
// Encrypt với Transit
async function encrypt(plaintext) {
const result = await vault.write('transit/encrypt/my-key', {
plaintext: Buffer.from(plaintext).toString('base64'),
});
return result.data.ciphertext;
}
// Main
(async () => {
await login();
const dbConfig = await getSecret('production/db');
console.log(`Connecting to ${dbConfig.host}:${dbConfig.port}`);
})();
Node.js からの Kubernetes 認証
const fs = require('fs');
async function k8sLogin() {
const jwt = fs.readFileSync(
'/var/run/secrets/kubernetes.io/serviceaccount/token',
'utf8'
);
const result = await vault.kubernetesLogin({
role: 'webapp',
jwt: jwt,
});
vault.token = result.auth.client_token;
return result;
}
3。 hvac
を使用した Python# pip install hvac
import hvac
client = hvac.Client(url='https://vault.company.com:8200')
# AppRole login
client.auth.approle.login(
role_id=os.environ['VAULT_ROLE_ID'],
secret_id=os.environ['VAULT_SECRET_ID'],
)
# KV v2
secret = client.secrets.kv.v2.read_secret_version(
path='production/db',
mount_point='secret',
)
db_password = secret['data']['data']['password']
# Database dynamic credentials
creds = client.secrets.database.generate_credentials(
name='app-role',
mount_point='database',
)
print(f"Username: {creds['data']['username']}")
print(f"Password: {creds['data']['password']}")
# Transit encrypt
result = client.secrets.transit.encrypt_data(
name='my-key',
plaintext=base64.b64encode(b'sensitive data').decode(),
)
ciphertext = result['data']['ciphertext']
4. Application Patterns
| パターン | 仕組み | メリット | デメリット |
|---|---|---|---|
| 直接 API | アプリは Vault API を直接呼び出します | フル コントロール | アプリは Vault を認識し、更新を処理する必要があります |
| Agent Sidecar | Vault Agent レンダリング ファイル | App がファイルを読み取る、不明 Vault | Sidecar オーバーヘッド |
| Env Injection | Secrets を env vars に注入 | シンプル、ユニバーサル | 静的、自動回転なし |
| CSI ボリューム | ボリュームにシークレットをマウント | ネイティブ K8、サイドカーなし | 動的シークレットを制限 |
| VSO | Operator を K8s Secret に同期 | 推奨、自動更新 | K8s の Secret etcd |
5. Secret Zero Problem
「シークレット ゼロ」は矛盾しています。Vault からシークレットを取得するには、認証に最初のシークレットが必要です。解決策:
プラットフォーム ID: Kubernetes SA、AWS IAM ロール、Azure マネージド ID → シークレットは不要
Response Wrapping: Orchestrator はラップされた SecretID を生成し、アプリに割り当てます。アプリは一度アンラップ
CI/CD OIDC: GitHub Actions/GitLab CI OIDC トークン → JWT 認証、静的シークレットなし
6。概要
Spring Cloud Vault — 最も深い統合、自動 PropertySource バインディング、動的な認証情報のローテーション
Node.js (node-vault) — 柔軟なクライアント ライブラリ、非同期/待機 API
Python (hvac) — スクリプトおよびアプリケーション用の包括的なクライアント
プラットフォーム ID — シークレット ゼロの問題を最も効果的に解決します
次の記事では、Vault を Terraform、Ansible、CI/CD パイプラインと統合する方法を学びます — コードとしてのインフラストラクチャとシークレット管理の融合。