VaultApplicationSpec.java
package com.cloudforgeci.api.application.secrets;
import com.cloudforge.core.annotation.ApplicationPlugin;
import com.cloudforge.core.interfaces.ApplicationSpec;
import com.cloudforge.core.interfaces.Ec2Context;
import com.cloudforge.core.interfaces.UserDataBuilder;
import java.util.List;
/**
* HashiCorp Vault ApplicationSpec implementation.
*
* <p>Vault is a tool for securely accessing secrets, managing encryption keys,
* and providing identity-based access.</p>
*
* <p><strong>Key Features:</strong></p>
* <ul>
* <li>Secrets management (API keys, passwords, certificates)</li>
* <li>Dynamic secrets generation</li>
* <li>Encryption as a service</li>
* <li>Identity-based access control</li>
* <li>Audit logging</li>
* </ul>
*
* <p><strong>Compliance Use Cases:</strong></p>
* <ul>
* <li>PCI-DSS: Secure storage of payment gateway credentials</li>
* <li>HIPAA: PHI encryption key management</li>
* <li>SOC2: Centralized secrets management and audit trails</li>
* <li>GDPR: Data encryption and key rotation</li>
* </ul>
*
* <p><strong>Fintech Applications:</strong></p>
* <ul>
* <li>Payment gateway API key storage (Stripe, PayPal, Square)</li>
* <li>Database credential rotation</li>
* <li>TLS certificate management</li>
* <li>Customer data encryption keys</li>
* </ul>
*
* <p><strong>Security Note:</strong></p>
* <ul>
* <li>Initialize and unseal Vault after deployment</li>
* <li>Store unseal keys in separate secure locations</li>
* <li>Enable audit logging to CloudWatch</li>
* <li>Use auto-unseal with AWS KMS for production</li>
* </ul>
*
* @see <a href="https://www.vaultproject.io/docs">Vault Documentation</a>
*/
@ApplicationPlugin(
value = "vault",
category = "secrets",
displayName = "Vault",
description = "Secrets and encryption management",
defaultCpu = 1024,
defaultMemory = 2048,
defaultInstanceType = "t3.small",
supportsFargate = true,
supportsEc2 = true,
supportsOidc = false
)
public class VaultApplicationSpec implements ApplicationSpec {
private static final String APPLICATION_ID = "vault";
private static final String DEFAULT_IMAGE = "hashicorp/vault:latest";
private static final int APPLICATION_PORT = 8200;
private static final String CONTAINER_DATA_PATH = "/vault/file";
private static final String EFS_DATA_PATH = "/vault";
private static final String VOLUME_NAME = "vaultData";
private static final String CONTAINER_USER = "100:1000"; // vault user
private static final String EFS_PERMISSIONS = "750";
private static final String EBS_DEVICE_NAME = "/dev/xvdh";
private static final String EC2_DATA_PATH = "/opt/vault/data";
private static final List<String> EC2_LOG_PATHS = List.of(
"/var/log/vault/vault.log",
"/var/log/vault/audit.log",
"/var/log/userdata.log"
);
@Override
public String applicationId() {
return APPLICATION_ID;
}
@Override
public String defaultContainerImage() {
return DEFAULT_IMAGE;
}
@Override
public int applicationPort() {
return APPLICATION_PORT;
}
@Override
public String containerDataPath() {
return CONTAINER_DATA_PATH;
}
@Override
public String efsDataPath() {
return EFS_DATA_PATH;
}
@Override
public String volumeName() {
return VOLUME_NAME;
}
@Override
public String containerUser() {
return CONTAINER_USER;
}
@Override
public String efsPermissions() {
return EFS_PERMISSIONS;
}
@Override
public String ebsDeviceName() {
return EBS_DEVICE_NAME;
}
@Override
public String ec2DataPath() {
return EC2_DATA_PATH;
}
@Override
public List<String> ec2LogPaths() {
return EC2_LOG_PATHS;
}
@Override
public void configureUserData(UserDataBuilder builder, Ec2Context context) {
builder.addSystemUpdate();
// Install Docker
builder.addCommands(
"# Install Docker",
"yum install -y docker",
"systemctl enable docker",
"systemctl start docker",
"echo 'Docker installed' >> /var/log/userdata.log"
);
// Install CloudWatch Agent
String logGroupName = String.format("/aws/%s/%s/%s",
context.stackName(),
context.runtimeType(),
context.securityProfile());
builder.installCloudWatchAgent(logGroupName, ec2LogPaths());
// Mount storage
String[] userParts = containerUser().split(":");
String uid = userParts[0];
String gid = userParts[1];
if (context.hasEfs()) {
builder.mountEfs(
context.efsId().orElseThrow(),
context.accessPointId().orElseThrow(),
ec2DataPath(),
uid,
gid
);
} else {
builder.mountEbs(
ebsDeviceName(),
ec2DataPath(),
uid,
gid
);
}
// Create Vault configuration
builder.addCommands(
"# Create Vault configuration directory",
"mkdir -p /etc/vault.d",
"mkdir -p /var/log/vault",
"",
"# Create Vault configuration file",
"cat > /etc/vault.d/vault.hcl <<'EOF'",
"# Vault Server Configuration",
"",
"storage \"file\" {",
" path = \"/vault/file\"",
"}",
"",
"listener \"tcp\" {",
" address = \"0.0.0.0:8200\"",
" tls_disable = false",
" tls_cert_file = \"/vault/config/tls/cert.pem\"",
" tls_key_file = \"/vault/config/tls/key.pem\"",
"}",
"",
"# Enable audit logging",
"# audit_device \"file\" {",
"# file_path = \"/vault/logs/audit.log\"",
"# }",
"",
"# UI Configuration",
"ui = true",
"",
"# Disable mlock for containers",
"disable_mlock = true",
"",
"# API address",
"api_addr = \"https://vault.example.com:8200\"",
"cluster_addr = \"https://vault.example.com:8201\"",
"EOF",
"",
"# Set ownership",
"chown -R " + uid + ":" + gid + " /etc/vault.d"
);
// Run Vault container
builder.addCommands(
"# Run Vault container",
"# Note: TLS certificate required for production",
"docker run -d \\",
" --name vault \\",
" -p 8200:8200 \\",
" -v " + ec2DataPath() + ":/vault/file \\",
" -v /etc/vault.d:/vault/config \\",
" -v /var/log/vault:/vault/logs \\",
" --cap-add=IPC_LOCK \\",
" -e 'VAULT_LOCAL_CONFIG={\"storage\": {\"file\": {\"path\": \"/vault/file\"}}, \"listener\": {\"tcp\": {\"address\": \"0.0.0.0:8200\", \"tls_disable\": \"1\"}}, \"ui\": true, \"disable_mlock\": true}' \\",
" " + DEFAULT_IMAGE + " server",
"echo 'Vault container started' >> /var/log/userdata.log",
"",
"# Wait for Vault to start",
"sleep 10",
"",
"# Display initialization instructions",
"cat >> /var/log/userdata.log <<'INSTRUCTIONS'",
"================================================================================",
"VAULT INITIALIZATION REQUIRED",
"================================================================================",
"",
"Vault is running but needs to be initialized and unsealed.",
"",
"1. Set Vault address:",
" export VAULT_ADDR='http://127.0.0.1:8200'",
"",
"2. Initialize Vault (do this ONCE):",
" vault operator init",
"",
" IMPORTANT: Save the unseal keys and root token in a secure location!",
"",
"3. Unseal Vault (required after every restart):",
" vault operator unseal <unseal-key-1>",
" vault operator unseal <unseal-key-2>",
" vault operator unseal <unseal-key-3>",
"",
"4. Login with root token:",
" vault login <root-token>",
"",
"5. Enable audit logging:",
" vault audit enable file file_path=/vault/logs/audit.log",
"",
"For production, configure AWS KMS auto-unseal:",
"https://www.vaultproject.io/docs/configuration/seal/awskms",
"================================================================================",
"INSTRUCTIONS",
"",
"echo 'Vault initialization instructions written to log' >> /var/log/userdata.log"
);
}
@Override
public String toString() {
return "VaultApplicationSpec{" +
"applicationId='" + APPLICATION_ID + '\'' +
", defaultImage='" + DEFAULT_IMAGE + '\'' +
", applicationPort=" + APPLICATION_PORT +
'}';
}
}