From 9c0d389dd31d35e36a8d66f081e3eff545991991 Mon Sep 17 00:00:00 2001 From: Patrick de Ruiter Date: Mon, 10 Nov 2025 11:32:35 +0100 Subject: [PATCH] Migrate certificate-automation from consul-template to vault-agent - Migrated Ansible integration from consul_template to vault_agent - Copied vault_agent role from terraform-vsphere-infra module - Created vault_agent-playbook.yml for deployment - Archived consul_template role as consul_template-legacy - Updated Terraform configuration: - Changed Ansible inventory group from consul_template to vault_agent - Added vault_secret_path variable for vault-agent - Added ssl_certs_dir and ssl_private_dir variables - Formatted all Terraform files - Implemented CI/CD pipeline: - Created .gitea/workflows/pipeline.yaml - Added TFLint, Tfsec, and Checkov security scans - Added Terraform validate step - Added SonarQube integration - Created sonar-project.properties - Documentation updates: - Updated README.md with vault-agent information - Added migration section comparing consul-template vs vault-agent - Updated CLAUDE.md with vault-agent architecture - Added vault-agent configuration examples Why vault-agent over consul-template: - Full AppRole support with role_id/secret_id files - Advanced token auto-renewal with auto_auth - Better credential security (separate files vs config) - Actively developed by HashiCorp Note: The ansible/ directory changes (vault_agent role and playbook) are not committed as the directory is in .gitignore. These files exist locally and will be deployed during Ansible runs. --- .gitea/workflows/pipeline.yaml | 116 +++++++++++++++++++++++++++++++++ CLAUDE.md | 114 ++++++++++++++++++++++++++++++++ README.md | 36 +++++++--- sonar-project.properties | 13 ++++ terraform/backend.tf | 6 +- terraform/main.tf | 15 +++-- terraform/provider.tf | 14 ++-- terraform/variables.tf | 6 +- 8 files changed, 291 insertions(+), 29 deletions(-) create mode 100644 .gitea/workflows/pipeline.yaml create mode 100644 CLAUDE.md create mode 100644 sonar-project.properties diff --git a/.gitea/workflows/pipeline.yaml b/.gitea/workflows/pipeline.yaml new file mode 100644 index 0000000..94db290 --- /dev/null +++ b/.gitea/workflows/pipeline.yaml @@ -0,0 +1,116 @@ +on: + push: + branches: + - master + pull_request: + types: [opened, synchronize, reopened] + +name: Code Quality & Security Scan +jobs: + tflint: + name: TFLint + runs-on: ubuntu-latest + steps: + - name: Checking out + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup TFLint + uses: terraform-linters/setup-tflint@v4 + with: + tflint_version: latest + + - name: Initialize TFLint + working-directory: terraform + run: tflint --init + + - name: Run TFLint + working-directory: terraform + run: tflint --format compact + + tfsec: + name: Tfsec Security Scan + runs-on: ubuntu-latest + needs: tflint + steps: + - name: Checking out + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Run Tfsec + uses: aquasecurity/tfsec-action@v1.0.3 + with: + working_directory: terraform + format: default + soft_fail: false + + checkov: + name: Checkov Security Scan + runs-on: ubuntu-latest + needs: tfsec + steps: + - name: Checking out + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Run Checkov + uses: bridgecrewio/checkov-action@v12 + with: + directory: terraform + framework: terraform + output_format: cli + soft_fail: false + + terraform-validate: + name: Terraform Validate + runs-on: ubuntu-latest + needs: checkov + steps: + - name: Checking out + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + with: + terraform_version: latest + + - name: Terraform Format Check + working-directory: terraform + run: terraform fmt -check -recursive + + - name: Terraform Init (for validation) + working-directory: terraform + env: + TF_VAR_vault_address: "https://vault.example.com:8200" + TF_VAR_environment: "test" + TF_VAR_short_hostname: "test-host" + run: terraform init -backend=false + + - name: Terraform Validate + working-directory: terraform + env: + TF_VAR_vault_address: "https://vault.example.com:8200" + TF_VAR_environment: "test" + TF_VAR_short_hostname: "test-host" + run: terraform validate + + sonarqube: + name: SonarQube Trigger + runs-on: ubuntu-latest + needs: terraform-validate + steps: + - name: Checking out + uses: actions/checkout@v4 + with: + # Disabling shallow clone is recommended for improving relevancy of reporting + fetch-depth: 0 + - name: SonarQube Scan + uses: sonarsource/sonarqube-scan-action@v6 + env: + SONAR_HOST_URL: ${{ secrets.SONARQUBE_HOST }} + SONAR_TOKEN: ${{ secrets.SONARQUBE_TOKEN }} diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..176c70a --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,114 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Repository Overview + +This is a Terraform module for automated TLS certificate deployment using Vault, vault-agent, Terraform, and Ansible. The module creates Vault AppRole authentication and policies, then deploys vault-agent via Ansible to automatically fetch and deploy certificates from Vault. + +**Note**: This module has been migrated from consul-template to vault-agent for better AppRole support and improved security. + +## Architecture + +### Terraform Configuration (`terraform/`) +- **main.tf**: Creates Vault policies, AppRole authentication, and Ansible inventory +- **variables.tf**: Defines `environment` and `short_hostname` variables +- **outputs.tf**: Outputs sensitive AppRole credentials (role_id and secret_id) +- **backend.tf, provider.tf, data.tf**: Standard Terraform configuration files + +### Ansible Configuration (`ansible/`) +- **vault_agent-playbook.yml**: Main playbook that deploys vault-agent +- **roles/vault_agent/**: Ansible role for vault-agent installation and configuration + - Downloads and installs vault binary from HashiCorp releases + - Creates systemd service for vault-agent + - Configures AppRole authentication with role_id/secret_id files + - Deploys certificate templates for automatic renewal +- **roles/consul_template-legacy/**: Archived legacy consul-template role (for reference) +- **collections/**: Contains Ansible collections (cloud.terraform, community.crypto, etc.) + +### Key Components +- **Vault Integration**: Uses AppRole authentication for secure certificate access +- **Certificate Templates**: Automatically fetches certificates, private keys, and certificate chains +- **System Integration**: Configures systemd service and file permissions for certificate deployment + +## Common Commands + +### Terraform Operations +```bash +cd terraform/ +terraform init +terraform plan +terraform apply +terraform destroy +``` + +### Ansible Operations +```bash +cd ansible/ +ansible-playbook -i inventory.yml vault_agent-playbook.yml +``` + +### Certificate Template Files +- `certificate.tpl`: Certificate template (fullchain) +- `private_key.tpl`: Private key template +- `chain_pem.tpl`: Certificate chain template + +**Note**: Template files are dynamically generated by the vault_agent role during deployment. + +## Security Considerations + +- AppRole credentials are marked as sensitive in Terraform outputs +- Certificate files are deployed with restricted permissions (600) +- Vault policies follow principle of least privilege (read-only access to specific secret paths) +- Ansible Vault should be used for encrypting sensitive variables (`vault_credentials.yml`) + +## Module Usage Pattern + +1. Deploy Vault AppRoles and policies with Terraform +2. Generate Ansible Vault credentials using `ansible_vault_output.sh` +3. Run Ansible playbook to deploy vault-agent: `ansible-playbook vault_agent-playbook.yml` +4. vault-agent automatically fetches and renews certificates from Vault using AppRole authentication + +## Why Vault-Agent? + +This module migrated from consul-template to vault-agent for several reasons: + +| Feature | consul-template | vault-agent | +|---------|----------------|-------------| +| AppRole Authentication | ❌ Limited support | ✅ Full support with role_id/secret_id files | +| Token Auto-Renewal | ⚠️ Basic | ✅ Advanced with auto_auth | +| Credential Storage | ❌ In config file | ✅ Separate secure files | +| Active Development | ⚠️ Maintenance mode | ✅ Actively developed | + +## vault-agent Configuration + +vault-agent uses AppRole auto_auth: + +```hcl +auto_auth { + method "approle" { + mount_path = "auth/approle" + config = { + role_id_file_path = "/etc/vault-agent/role_id" + secret_id_file_path = "/etc/vault-agent/secret_id" + } + } + + sink "file" { + config = { + path = "/opt/vault-agent/vault-token" + } + } +} +``` + +Certificate templates automatically fetch secrets from Vault: + +```hcl +template { + source = "/etc/vault-agent/certificate.tpl" + destination = "/etc/ssl/certs/hostname.crt" + perms = 0644 + command = "systemctl reload nginx" # Service-specific reload +} +``` \ No newline at end of file diff --git a/README.md b/README.md index e7f6d40..1e8adcf 100755 --- a/README.md +++ b/README.md @@ -4,12 +4,12 @@ This Terraform module automates TLS certificate deployment by creating Vault App ## Purpose -This module sets up the infrastructure needed to automatically fetch and deploy TLS certificates from HashiCorp Vault to target servers. It creates: +This module sets up the infrastructure needed to automatically fetch and deploy TLS certificates from HashiCorp Vault to target servers using Vault Agent. It creates: - Vault policies with read-only access to certificate secrets - AppRole authentication backend configuration - AppRole credentials for secure authentication -- Ansible inventory entries for automated deployment +- Ansible inventory entries for automated vault-agent deployment ## What It Does @@ -84,7 +84,7 @@ This module creates the following Vault resources: - Token Max TTL: 4 hours - Secret ID TTL: 24 hours -- **Ansible Host**: Added to `consul_template` group with Vault credentials +- **Ansible Host**: Added to `vault_agent` group with Vault credentials ## Secret Path Convention @@ -106,22 +106,25 @@ Example: `secret/data/production/web01/certificate` This module automatically creates an Ansible inventory entry with: - Inventory hostname: `{short_hostname}` -- Group: `consul_template` +- Group: `vault_agent` - Variables: - `vault_approle_role_id` - `vault_approle_secret_id` - `vault_address` + - `vault_secret_path` - `environment` - `short_hostname` + - `ssl_certs_dir` + - `ssl_private_dir` -The generated inventory can be used with the included Ansible playbooks in the `ansible/` directory to deploy consul-template for automated certificate retrieval. +The generated inventory can be used with the included Ansible playbooks in the `ansible/` directory to deploy vault-agent for automated certificate retrieval. ## Deployment Steps 1. Deploy Vault AppRoles and policies with Terraform 2. Generate Ansible Vault credentials (`ansible_vault_output.sh`) -3. Run Ansible playbook to deploy consul-template -4. consul-template automatically fetches and renews certificates from Vault +3. Run Ansible playbook to deploy vault-agent: `ansible-playbook vault_agent-playbook.yml` +4. vault-agent automatically fetches and renews certificates from Vault using AppRole authentication ## Security Considerations @@ -135,9 +138,22 @@ The generated inventory can be used with the included Ansible playbooks in the ` This module works in conjunction with: -- **Ansible Playbooks** (in `ansible/` directory): Deploy consul-template to target servers -- **Consul-Template**: Automatically fetches and renews certificates from Vault -- **Vault PKI**: Stores certificates that this module provides access to +- **Ansible Playbooks** (in `ansible/` directory): Deploy vault-agent to target servers +- **Vault Agent**: Automatically fetches and renews certificates from Vault using AppRole authentication +- **Vault KV Secrets Engine**: Stores certificates that this module provides access to + +## Migration from consul-template + +This module has been migrated from consul-template to vault-agent for better AppRole support and improved security. Key differences: + +| Feature | consul-template (legacy) | vault-agent (current) | +|---------|-------------------------|----------------------| +| AppRole Auth | ❌ Limited support | ✅ Full support with role_id/secret_id files | +| Token Management | ⚠️ Basic | ✅ Advanced auto_auth | +| Security | ❌ Credentials in config | ✅ Separate credential files | +| Active Development | ⚠️ Maintenance mode | ✅ Actively developed | + +The legacy consul-template role is archived as `consul_template-legacy` for reference. ## Notes diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 0000000..5442354 --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,13 @@ +sonar.projectKey=terraform-certificate-automation +sonar.projectName=Terraform Certificate Automation Module +sonar.projectVersion=1.0 + +# Path to source directories +sonar.sources=terraform,ansible + +# Exclusions +sonar.exclusions=**/*.tfvars,**/.terraform/**,**/files/**,**/collections/**,**/consul_template-legacy/** + +# Terraform-specific settings +sonar.language=terraform +sonar.sourceEncoding=UTF-8 diff --git a/terraform/backend.tf b/terraform/backend.tf index b742446..f258cfa 100644 --- a/terraform/backend.tf +++ b/terraform/backend.tf @@ -1,11 +1,11 @@ terraform { backend "s3" { endpoints = { - s3 = "https://minio.bsdserver.nl:443" + s3 = "https://minio.bsdserver.nl:443" } - bucket = "home-terraform" - key = "home/security/encryption/certificate-automation.tfstate" + bucket = "home-terraform" + key = "home/security/encryption/certificate-automation.tfstate" # Configure credentials via environment variables: # export AWS_ACCESS_KEY_ID="your-access-key" diff --git a/terraform/main.tf b/terraform/main.tf index 3b722d7..8d25cd8 100755 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -1,11 +1,11 @@ locals { - secret_path = "secret/data/${var.environment}/${var.short_hostname}/certificate" - policy_name = "${var.environment}-${var.short_hostname}-cert-policy" + secret_path = "secret/data/${var.environment}/${var.short_hostname}/certificate" + policy_name = "${var.environment}-${var.short_hostname}-cert-policy" approle_name = "${var.environment}-${var.short_hostname}-approle" } resource "vault_policy" "cert_access" { - name = local.policy_name + name = local.policy_name policy = <