Some checks failed
Deploy to Phoenix / deploy (push) Has been cancelled
- ADD_CHAIN138_TO_LEDGER_LIVE: Ledger form done; public code review repo bis-innovations/LedgerLive; init/push commands - CONTRACT_DEPLOYMENT_RUNBOOK: Chain 138 gas price 1 gwei, 36-addr check, TransactionMirror workaround - CONTRACT_*: AddressMapper, MirrorManager deployed 2026-02-12; 36-address on-chain check - NEXT_STEPS_FOR_YOU: Ledger done; steps completable now (no LAN); run-completable-tasks-from-anywhere - MASTER_INDEX, OPERATOR_OPTIONAL, SMART_CONTRACTS_INVENTORY_SIMPLE: updates - LEDGER_BLOCKCHAIN_INTEGRATION_COMPLETE: bis-innovations/LedgerLive reference Co-authored-by: Cursor <cursoragent@cursor.com>
451 lines
10 KiB
Markdown
451 lines
10 KiB
Markdown
# Phoenix Vault Integration Guide
|
|
|
|
**Last Updated:** 2026-01-31
|
|
**Document Version:** 1.0
|
|
**Status:** Active Documentation
|
|
|
|
---
|
|
|
|
**Date:** 2026-01-19
|
|
**Status:** ✅ Ready for Integration
|
|
**Purpose:** Guide for integrating Phoenix services with Vault cluster
|
|
|
|
---
|
|
|
|
## Overview
|
|
|
|
This guide provides step-by-step instructions for integrating Phoenix services (API, Portal, and other components) with the deployed Vault cluster.
|
|
|
|
---
|
|
|
|
## Prerequisites
|
|
|
|
- Vault cluster deployed and operational
|
|
- AppRole authentication configured
|
|
- AppRole credentials available
|
|
- Phoenix services ready for integration
|
|
|
|
---
|
|
|
|
## AppRole Credentials
|
|
|
|
AppRole credentials are stored in:
|
|
```
|
|
.secure/vault-credentials/phoenix-approle-credentials-YYYYMMDD.txt
|
|
```
|
|
|
|
**Phoenix API:**
|
|
- Role ID: `27f213e2-f15e-b6de-3cf4-db2f02029dd5`
|
|
- Secret ID: (stored in credentials file)
|
|
|
|
**Phoenix Portal:**
|
|
- Role ID: `70278dee-a85e-9007-c769-46b71a8c1460`
|
|
- Secret ID: (stored in credentials file)
|
|
|
|
---
|
|
|
|
## Integration Methods
|
|
|
|
### Method 1: Environment Variables (Recommended)
|
|
|
|
Set environment variables in your Phoenix service configuration:
|
|
|
|
```bash
|
|
export VAULT_ADDR=http://192.168.11.200:8200
|
|
export VAULT_ROLE_ID=<your-role-id>
|
|
export VAULT_SECRET_ID=<your-secret-id>
|
|
```
|
|
|
|
### Method 2: Configuration Files
|
|
|
|
Create a Vault configuration file (e.g., `vault-config.json`):
|
|
|
|
```json
|
|
{
|
|
"vault_addr": "http://10.160.0.40:8200",
|
|
"role_id": "<your-role-id>",
|
|
"secret_id": "<your-secret-id>"
|
|
}
|
|
```
|
|
|
|
**⚠️ Security:** Never commit configuration files with credentials to Git. Use environment variables or secure secret management.
|
|
|
|
---
|
|
|
|
## Phoenix API Integration
|
|
|
|
### Node.js/TypeScript Example
|
|
|
|
```typescript
|
|
import Vault from 'node-vault';
|
|
|
|
// Initialize Vault client
|
|
const vault = Vault({
|
|
endpoint: process.env.VAULT_ADDR || 'http://192.168.11.200:8200',
|
|
token: await authenticateWithAppRole(
|
|
process.env.VAULT_ROLE_ID!,
|
|
process.env.VAULT_SECRET_ID!
|
|
)
|
|
});
|
|
|
|
// Authenticate with AppRole
|
|
async function authenticateWithAppRole(roleId: string, secretId: string): Promise<string> {
|
|
const response = await fetch(`${process.env.VAULT_ADDR}/v1/auth/approle/login`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ role_id: roleId, secret_id: secretId })
|
|
});
|
|
const data = await response.json();
|
|
return data.auth.client_token;
|
|
}
|
|
|
|
// Get database credentials
|
|
async function getDatabaseCredentials() {
|
|
const response = await vault.read('secret/data/phoenix/database/postgres');
|
|
return {
|
|
username: response.data.data.username,
|
|
password: response.data.data.password,
|
|
host: response.data.data.host,
|
|
port: response.data.data.port,
|
|
database: response.data.data.database
|
|
};
|
|
}
|
|
|
|
// Get JWT secrets
|
|
async function getJWTSecrets() {
|
|
const response = await vault.read('secret/data/phoenix/api/jwt-secrets');
|
|
return {
|
|
accessTokenSecret: response.data.data['access-token-secret'],
|
|
refreshTokenSecret: response.data.data['refresh-token-secret']
|
|
};
|
|
}
|
|
```
|
|
|
|
### Python Example
|
|
|
|
```python
|
|
import hvac
|
|
import os
|
|
|
|
# Initialize Vault client
|
|
client = hvac.Client(url=os.getenv('VAULT_ADDR', 'http://10.160.0.40:8200'))
|
|
|
|
# Authenticate with AppRole
|
|
role_id = os.getenv('VAULT_ROLE_ID')
|
|
secret_id = os.getenv('VAULT_SECRET_ID')
|
|
client.auth.approle.login(role_id=role_id, secret_id=secret_id)
|
|
|
|
# Get database credentials
|
|
db_secrets = client.secrets.kv.v2.read_secret_version(path='phoenix/database/postgres')
|
|
db_config = db_secrets['data']['data']
|
|
|
|
# Get JWT secrets
|
|
jwt_secrets = client.secrets.kv.v2.read_secret_version(path='phoenix/api/jwt-secrets')
|
|
jwt_config = jwt_secrets['data']['data']
|
|
```
|
|
|
|
---
|
|
|
|
## Phoenix Portal Integration
|
|
|
|
Similar to API integration, but use the `phoenix-portal` AppRole:
|
|
|
|
```typescript
|
|
// Portal-specific Vault configuration
|
|
const vault = Vault({
|
|
endpoint: process.env.VAULT_ADDR || 'http://192.168.11.200:8200',
|
|
token: await authenticateWithAppRole(
|
|
process.env.VAULT_PORTAL_ROLE_ID!,
|
|
process.env.VAULT_PORTAL_SECRET_ID!
|
|
)
|
|
});
|
|
|
|
// Get JWT secrets for portal
|
|
const jwtSecrets = await vault.read('secret/data/phoenix/api/jwt-secrets');
|
|
```
|
|
|
|
---
|
|
|
|
## Secret Paths Reference
|
|
|
|
### Available Secret Paths
|
|
|
|
| Path | Description | Access Policy |
|
|
|------|-------------|---------------|
|
|
| `secret/data/phoenix/api/jwt-secrets` | JWT signing secrets | phoenix-api, phoenix-portal |
|
|
| `secret/data/phoenix/api/api-keys` | API keys | phoenix-api |
|
|
| `secret/data/phoenix/database/postgres` | PostgreSQL credentials | phoenix-api |
|
|
| `secret/data/phoenix/database/redis` | Redis credentials | phoenix-api |
|
|
| `secret/data/phoenix/keycloak/admin-credentials` | Keycloak admin | phoenix-api |
|
|
| `secret/data/phoenix/keycloak/oidc-secrets` | OIDC client secrets | phoenix-api |
|
|
| `secret/data/phoenix/services/blockchain` | Blockchain RPC/keys | phoenix-api |
|
|
| `secret/data/phoenix/services/integrations` | Integration tokens | phoenix-api |
|
|
|
|
### Reading Secrets
|
|
|
|
```bash
|
|
# Using Vault CLI
|
|
export VAULT_ADDR=http://192.168.11.200:8200
|
|
export VAULT_TOKEN=$(vault write -field=token auth/approle/login \
|
|
role_id=$VAULT_ROLE_ID secret_id=$VAULT_SECRET_ID)
|
|
|
|
# Read secret
|
|
vault kv get secret/phoenix/database/postgres
|
|
```
|
|
|
|
---
|
|
|
|
## Token Management
|
|
|
|
### Token Lifecycle
|
|
|
|
- **Token TTL:** 1 hour (default)
|
|
- **Token Max TTL:** 4 hours
|
|
- **Secret ID TTL:** 24 hours
|
|
|
|
### Token Renewal
|
|
|
|
Tokens should be renewed before expiration:
|
|
|
|
```typescript
|
|
// Renew token
|
|
async function renewToken(token: string): Promise<void> {
|
|
await fetch(`${process.env.VAULT_ADDR}/v1/auth/token/renew-self`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-Vault-Token': token
|
|
}
|
|
});
|
|
}
|
|
```
|
|
|
|
### Automatic Token Refresh
|
|
|
|
Implement automatic token refresh in your services:
|
|
|
|
```typescript
|
|
class VaultClient {
|
|
private token: string | null = null;
|
|
private tokenExpiry: Date | null = null;
|
|
|
|
async getToken(): Promise<string> {
|
|
if (!this.token || this.isTokenExpired()) {
|
|
await this.authenticate();
|
|
}
|
|
return this.token!;
|
|
}
|
|
|
|
private isTokenExpired(): boolean {
|
|
if (!this.tokenExpiry) return true;
|
|
return new Date() >= this.tokenExpiry;
|
|
}
|
|
|
|
private async authenticate(): Promise<void> {
|
|
const response = await fetch(`${process.env.VAULT_ADDR}/v1/auth/approle/login`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
role_id: process.env.VAULT_ROLE_ID,
|
|
secret_id: process.env.VAULT_SECRET_ID
|
|
})
|
|
});
|
|
const data = await response.json();
|
|
this.token = data.auth.client_token;
|
|
this.tokenExpiry = new Date(Date.now() + data.auth.lease_duration * 1000);
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Error Handling
|
|
|
|
### Common Errors
|
|
|
|
**1. Authentication Failed**
|
|
```
|
|
Error: invalid role_id or secret_id
|
|
```
|
|
**Solution:** Verify AppRole credentials are correct.
|
|
|
|
**2. Permission Denied**
|
|
```
|
|
Error: permission denied
|
|
```
|
|
**Solution:** Check that the AppRole has the correct policy attached.
|
|
|
|
**3. Secret Not Found**
|
|
```
|
|
Error: no secret found at path
|
|
```
|
|
**Solution:** Verify the secret path exists and is accessible.
|
|
|
|
### Retry Logic
|
|
|
|
Implement retry logic for transient failures:
|
|
|
|
```typescript
|
|
async function getSecretWithRetry(path: string, maxRetries = 3): Promise<any> {
|
|
for (let i = 0; i < maxRetries; i++) {
|
|
try {
|
|
return await vault.read(path);
|
|
} catch (error) {
|
|
if (i === maxRetries - 1) throw error;
|
|
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Security Best Practices
|
|
|
|
1. **Never Hardcode Credentials**
|
|
- Use environment variables
|
|
- Use secure secret injection
|
|
- Rotate credentials regularly
|
|
|
|
2. **Use Least Privilege**
|
|
- Each service should only access secrets it needs
|
|
- Use separate AppRoles for different services
|
|
- Review policies regularly
|
|
|
|
3. **Monitor Access**
|
|
- Enable audit logging
|
|
- Monitor token usage
|
|
- Alert on suspicious activity
|
|
|
|
4. **Token Management**
|
|
- Implement automatic token renewal
|
|
- Handle token expiration gracefully
|
|
- Use short token TTLs
|
|
|
|
5. **Network Security**
|
|
- Use TLS in production
|
|
- Restrict network access to Vault
|
|
- Use firewall rules
|
|
|
|
---
|
|
|
|
## Testing Integration
|
|
|
|
### Test Script
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
# Test Vault integration
|
|
|
|
export VAULT_ADDR=http://192.168.11.200:8200
|
|
export VAULT_ROLE_ID=<your-role-id>
|
|
export VAULT_SECRET_ID=<your-secret-id>
|
|
|
|
# Authenticate
|
|
TOKEN=$(vault write -field=token auth/approle/login \
|
|
role_id=$VAULT_ROLE_ID secret_id=$VAULT_SECRET_ID)
|
|
|
|
export VAULT_TOKEN=$TOKEN
|
|
|
|
# Test secret access
|
|
echo "Testing secret access..."
|
|
vault kv get secret/phoenix/api/jwt-secrets
|
|
vault kv get secret/phoenix/database/postgres
|
|
|
|
echo "✅ Integration test passed"
|
|
```
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
### Cannot Connect to Vault
|
|
|
|
1. Check network connectivity:
|
|
```bash
|
|
curl http://10.160.0.40:8200/v1/sys/health
|
|
```
|
|
|
|
2. Verify Vault is unsealed:
|
|
```bash
|
|
vault status
|
|
```
|
|
|
|
3. Check firewall rules
|
|
|
|
### Authentication Fails
|
|
|
|
1. Verify credentials:
|
|
```bash
|
|
vault read auth/approle/role/phoenix-api/role-id
|
|
```
|
|
|
|
2. Check AppRole is enabled:
|
|
```bash
|
|
vault auth list
|
|
```
|
|
|
|
3. Verify policy is attached:
|
|
```bash
|
|
vault read auth/approle/role/phoenix-api
|
|
```
|
|
|
|
### Permission Denied
|
|
|
|
1. Check policy:
|
|
```bash
|
|
vault policy read phoenix-api-policy
|
|
```
|
|
|
|
2. Verify secret path:
|
|
```bash
|
|
vault kv list secret/phoenix/
|
|
```
|
|
|
|
3. Check token capabilities:
|
|
```bash
|
|
vault token capabilities secret/data/phoenix/api/jwt-secrets
|
|
```
|
|
|
|
---
|
|
|
|
## Migration from Existing Secrets
|
|
|
|
If migrating from existing `.env` files or other secret storage:
|
|
|
|
1. **Inventory Current Secrets**
|
|
- List all secrets currently in use
|
|
- Map to Vault paths
|
|
|
|
2. **Create Secrets in Vault**
|
|
```bash
|
|
vault kv put secret/phoenix/database/postgres \
|
|
username=current_username \
|
|
password=current_password \
|
|
host=current_host \
|
|
port=5432 \
|
|
database=phoenix
|
|
```
|
|
|
|
3. **Update Services**
|
|
- Replace `.env` file reading with Vault client
|
|
- Test thoroughly
|
|
- Deploy gradually
|
|
|
|
4. **Remove Old Secrets**
|
|
- Delete `.env` files after migration
|
|
- Update `.gitignore` if needed
|
|
|
|
---
|
|
|
|
## Related Documentation
|
|
|
|
- [Phoenix Vault Cluster Deployment](PHOENIX_VAULT_CLUSTER_DEPLOYMENT.md)
|
|
- [Vault TLS Configuration](VAULT_TLS_CONFIGURATION.md)
|
|
- [Master Secrets Inventory](MASTER_SECRETS_INVENTORY.md)
|
|
- [HashiCorp Vault Documentation](https://developer.hashicorp.com/vault/docs)
|
|
|
|
---
|
|
|
|
**Status:** ✅ Ready for Integration
|
|
**Last Updated:** 2026-01-19
|