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.
This commit is contained in:
Patrick de Ruiter 2025-11-10 11:32:35 +01:00
parent 47aaaa2143
commit 9c0d389dd3
Signed by: pderuiter
GPG Key ID: 5EBA7F21CF583321
8 changed files with 291 additions and 29 deletions

View File

@ -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 }}

114
CLAUDE.md Normal file
View File

@ -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
}
```

View File

@ -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

13
sonar-project.properties Normal file
View File

@ -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

View File

@ -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"

View File

@ -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 = <<EOT
path "${local.secret_path}" {
capabilities = ["read"]
@ -27,10 +27,10 @@ resource "vault_approle_auth_backend_role_secret_id" "cert_role_secret" {
role_name = vault_approle_auth_backend_role.cert_role.role_name
}
resource "ansible_host" "consul_template_node" {
resource "ansible_host" "vault_agent_node" {
inventory_hostname = var.short_hostname
groups = ["consul_template"]
groups = ["vault_agent"]
vars = {
ansible_user = "ansible"
ansible_ssh_private_key_file = "~/.ssh/id_ed25519"
@ -38,7 +38,10 @@ resource "ansible_host" "consul_template_node" {
vault_approle_role_id = vault_approle_auth_backend_role.cert_role.role_id
vault_approle_secret_id = vault_approle_auth_backend_role_secret_id.cert_role_secret.secret_id
vault_address = var.vault_address
vault_secret_path = local.secret_path
environment = var.environment
short_hostname = var.short_hostname
ssl_certs_dir = "/etc/ssl/certs"
ssl_private_dir = "/etc/ssl/private"
}
}

View File

@ -11,14 +11,14 @@ terraform {
# Configure the Vault provider
provider "vault" {
address = var.vault_address
auth_login {
path = "auth/approle/login"
parameters = {
role_id = var.role_id
secret_id = var.secret_id
}
address = var.vault_address
auth_login {
path = "auth/approle/login"
parameters = {
role_id = var.role_id
secret_id = var.secret_id
}
}
}
# Ansible Provider

View File

@ -1,7 +1,7 @@
variable "environment" {
type = string
description = "Environment name (e.g., dev, staging, prod)"
validation {
condition = can(regex("^[a-zA-Z0-9-_]+$", var.environment))
error_message = "Environment must contain only alphanumeric characters, hyphens, and underscores."
@ -11,7 +11,7 @@ variable "environment" {
variable "short_hostname" {
type = string
description = "Short hostname for the target server"
validation {
condition = can(regex("^[a-zA-Z0-9-]+$", var.short_hostname))
error_message = "Hostname must contain only alphanumeric characters and hyphens."
@ -21,7 +21,7 @@ variable "short_hostname" {
variable "vault_address" {
type = string
description = "Vault server address (e.g., https://vault.example.com:8200)"
validation {
condition = can(regex("^https?://", var.vault_address))
error_message = "Vault address must be a valid HTTP or HTTPS URL."