From f7de68748734998714ee9ecc6ca29bc1cd92e935 Mon Sep 17 00:00:00 2001 From: vorpax Date: Tue, 20 Jan 2026 14:48:34 +0000 Subject: [PATCH] feat: add Makefile and README.md for Step-CA Docker stack setup --- Makefile | 64 ++++++++++++ README.md | 288 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 352 insertions(+) create mode 100644 Makefile create mode 100644 README.md diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..86f1be5 --- /dev/null +++ b/Makefile @@ -0,0 +1,64 @@ +.PHONY: help configure clean + +# Colors for output +BLUE := \033[0;34m +GREEN := \033[0;32m +YELLOW := \033[0;33m +RED := \033[0;31m +NC := \033[0m # No Color + +help: ## Show this help message + @echo "$(BLUE)Step-CA Docker Stack - Available commands:$(NC)" + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " $(GREEN)%-15s$(NC) %s\n", $$1, $$2}' + +configure: ## Create secret files from .env if they don't exist +@echo "$(BLUE)Configuring secrets...$(NC)" +@if [ ! -f .env ]; then \ +echo "$(RED)Error: .env file not found. Please copy .env.example to .env and configure it.$(NC)"; \ +exit 1; \ +fi +@mkdir -p secrets +@if [ ! -f secrets/postgres_password.txt ]; then \ +echo "$(YELLOW)Creating secrets/postgres_password.txt$(NC)"; \ +grep '^POSTGRES_PASSWORD=' .env | cut -d '=' -f2- | tr -d '"' > secrets/postgres_password.txt; \ +chmod 600 secrets/postgres_password.txt; \ +echo "$(GREEN)✓ Created secrets/postgres_password.txt$(NC)"; \ +else \ +echo "$(GREEN)✓ secrets/postgres_password.txt already exists$(NC)"; \ +fi +@if [ ! -f secrets/step_pwd.txt ]; then \ +echo "$(YELLOW)Creating secrets/step_pwd.txt$(NC)"; \ +if grep -q '^STEP_CA_PASSWORD=' .env; then \ +grep '^STEP_CA_PASSWORD=' .env | cut -d '=' -f2- | tr -d '"' > secrets/step_pwd.txt; \ +else \ +openssl rand -base64 32 > secrets/step_pwd.txt; \ +echo "$(YELLOW)No STEP_CA_PASSWORD in .env, generated random password$(NC)"; \ +fi; \ +chmod 600 secrets/step_pwd.txt; \ +echo "$(GREEN)✓ Created secrets/step_pwd.txt$(NC)"; \ +else \ +echo "$(GREEN)✓ secrets/step_pwd.txt already exists$(NC)"; \ +fi +@echo "$(GREEN)✓ Configuration complete$(NC)" + +clean: +docker compose down +@echo "$(RED)WARNING: This will destroy all data (certificates, database)$(NC)" +@read -p "Are you sure? [y/N] " -n 1 -r; \ +echo; \ +if [[ $$REPLY =~ ^[Yy]$$ ]]; then \ +echo "$(BLUE)Removing volumes...$(NC)"; \ +docker compose down -v; \ +echo "$(GREEN)✓ Volumes removed$(NC)"; \ +else \ +echo "$(YELLOW)Cancelled$(NC)"; \ +fi + +exec-ca: ## Execute ash in step-ca container +docker compose exec step-ca sh + +exec-db: ## Execute psql in postgres container +docker compose exec postgres psql -U stepca -d stepca + +fingerprint: ## Get CA root certificate fingerprint +@docker compose exec step-ca step certificate fingerprint /home/step/certs/root_ca.crt 2>/dev/null || echo "$(RED)CA not initialized yet$(NC)" diff --git a/README.md b/README.md new file mode 100644 index 0000000..5b13fad --- /dev/null +++ b/README.md @@ -0,0 +1,288 @@ +# Step-CA Docker Stack + +A Docker Compose setup for deploying [Smallstep CA](https://smallstep.com/certificates/) (step-ca) with PostgreSQL backend support for certificate management and PKI infrastructure. + +## Features + +- **Automated Initialization**: Automatic step-ca setup with optional SSH and ACME support +- **PostgreSQL Backend**: Uses PostgreSQL as the primary database (replacing the default Badger) +- **Health Checks**: Built-in health checks for PostgreSQL service +- **Customizable Configuration**: Full environment variable support for customization +- **Docker Build**: Custom Dockerfile with jq for JSON manipulation +- **Remote Management**: Support for remote management capabilities +- **Admin Provisioner**: Built-in JWK admin provisioner for certificate issuance + +## Prerequisites + +- Docker +- Docker Compose +- Basic knowledge of step-ca and PKI concepts + +## Quick Start + +### 1. Clone and Setup + +```bash +cd /path/to/step-ca +cp .env.example .env +# Edit .env with your specific configuration +``` + +### 2. Configure Environment + +Edit `.env` file with your desired configuration: + +```env +# PostgreSQL Configuration +POSTGRES_PASSWORD=your_secure_password + +# Step-CA Configuration +STEP_CA_IP=192.168.1.100 +STEP_CA_PORT=9000 +STEP_CA_NAME=My Internal CA +STEP_CA_ISSUER=CN=My Internal CA, O=MyOrg, C=US +STEP_CA_DNS_NAMES=ca.example.com,ca.internal,pki.local +STEP_CA_PROVISIONER=admin-provisioner +STEP_CA_ADMIN_SUBJECT=admin@example.com +STEP_CA_SSH=true +STEP_CA_ACME=true +STEP_CA_REMOTE_MANAGEMENT=true + +# DNS Configuration +DNS_1=8.8.8.8 +DNS_2=8.8.4.4 +DNS_SEARCH_1=example.com +DNS_SEARCH_2=internal.local +``` + +### 3. Initialize Secrets + +Create the required secret files. These are mounted as Docker secrets at `/run/secrets/` inside containers: + +```bash +mkdir -p secrets + +echo "your_secure_password" > secrets/postgres_password.txt + +echo "your_ca_password" > secrets/step_pwd.txt + +chmod 600 secrets/*.txt +``` + +**Important**: These secrets are automatically mounted by Docker Compose: +- `secrets/postgres_password.txt` → `/run/secrets/postgres_password` +- `secrets/step_pwd.txt` → `/run/secrets/password` + +or just `make configure` + +### 4. Start the Stack + +```bash +docker-compose up -d --build +``` + +### 5. Verify Deployment + +```bash +docker-compose logs -f step-ca + +docker-compose ps +``` + +## Configuration + +### Environment Variables + +All configuration is managed through the `.env` file. Key variables: + +| Variable | Description | Default | Example | +|----------|-------------|---------|---------| +| `POSTGRES_PASSWORD` | PostgreSQL password | - | `secure_pass_123` | +| `STEP_CA_IP` | IP address for CA service | `127.0.0.1` | `192.168.1.100` | +| `STEP_CA_PORT` | Port for CA service | `9000` | `9000` | +| `STEP_CA_NAME` | CA name | `Default CA` | `Company Internal CA` | +| `STEP_CA_ISSUER` | CA issuer certificate DN | - | `CN=My CA, O=Org, C=US` | +| `STEP_CA_DNS_NAMES` | DNS names for CA cert | `localhost,ca.local` | `ca.example.com,ca.internal` | +| `STEP_CA_PROVISIONER` | Provisioner name | `admin` | `admin-provisioner` | +| `STEP_CA_ADMIN_SUBJECT` | Admin email address | `admin@example.com` | `ca-admin@company.com` | +| `STEP_CA_SSH` | Enable SSH support | `true` | `true` or `false` | +| `STEP_CA_ACME` | Enable ACME support | `true` | `true` or `false` | +| `STEP_CA_REMOTE_MANAGEMENT` | Enable remote management | `true` | `true` or `false` | +| `DNS_1`, `DNS_2` | DNS servers | `8.8.8.8`, `8.8.4.4` | `1.1.1.1`, `1.0.0.1` | +| `DNS_SEARCH_1-3` | DNS search domains | `localhost,local,internal` | `example.com,internal.local` | + +## Service Details + +### PostgreSQL Service +- **Image**: `postgres:16-alpine` +- **Database**: `stepca` +- **User**: `stepca` +- **Health Check**: PostgreSQL readiness probe every 10 seconds +- **Persistent Storage**: `postgres-data` volume + +### Step-CA Service +- **Build**: Custom image based on `smallstep/step-ca` +- **Port**: Configurable via `STEP_CA_PORT` (default: 9000) +- **Database Migration**: Automatic transition from Badger to PostgreSQL +- **Persistent Storage**: `stepca-data` volume for certificates and configuration + +## Initialization Flow + +1. **First Run**: If `ca.json` doesn't exist: + - Step-ca performs automated initialization with Badger database + - Uses Docker secret from `/run/secrets/password` for key encryption + - Generates root, intermediate, and SSH certificates + - Creates admin provisioner + +2. **PostgreSQL Migration**: + - Configuration is migrated from Badger to PostgreSQL + - Database connection details are stored in `ca.json` + - Marker file created to prevent re-initialization + +3. **Subsequent Runs**: + - Step-ca starts directly with PostgreSQL backend + - Uses the same Docker secret for decrypting keys + - No re-initialization occurs (marker file prevents this) + + +## File Structure + +``` +step-ca/ +├── compose.yaml # Docker Compose configuration +├── Dockerfile # Custom Docker image build +├── init.sh # Initialization script +├── .env.example # Environment variables template +├── .gitignore # Git ignore rules +├── config/ +│ └── ca.json # Step-ca configuration (generated) +├── secrets/ +│ ├── postgres_password.txt +│ └── step_pwd.txt +└── README.md # This file +``` + + +## Usage Examples + +### Access Step-CA Admin Interface + +Once the container is running, you can interact with step-ca: + +```bash +# Enter the container +docker-compose exec step-ca sh + +# List certificates +step certificate list + +# Get CA fingerprint +step certificate fingerprint /home/step/certs/root_ca.crt +``` + +### Request a Certificate via JWK Provisioner + +```bash +# From inside the container, using the mounted Docker secret +docker-compose exec step-ca sh -c \ + 'step ca certificate --provisioner=admin --provisioner-password-file=/run/secrets/password \ + "my-service.example.com" /tmp/cert.pem /tmp/key.pem' + +# Or from the host with your CA password +step ca certificate --provisioner=admin \ + "my-service.example.com" cert.pem key.pem +``` + +### ACME Client Setup + +If ACME is enabled, configure your ACME client: + +``` +CA URL: https://ca.example.com:9000 +Directory: https://ca.example.com:9000/acme/acme/directory +``` + +## Troubleshooting + +### PostgreSQL Connection Fails + +```bash +# Check PostgreSQL logs +docker-compose logs postgres + +# Verify database exists +docker-compose exec postgres psql -U stepca -d stepca -c "\dt" +``` + +### Step-CA Won't Start + +```bash +# Check step-ca logs +docker-compose logs step-ca + +# Verify configuration +docker-compose exec step-ca cat /home/step/config/ca.json +``` + +### Remove and Reset + +To completely reset the environment: + +```bash +# Stop and remove containers +docker-compose down + +# Remove data volumes +docker volume rm ca-stepca-data ca-postgres-data + +# Restart fresh +docker-compose up -d --build +``` + +## Security Considerations + +- **Secret Management**: Store passwords in `secrets/` directory and add to `.gitignore` +- **Network**: Use internal Docker network for PostgreSQL communication +- **Firewall**: Restrict access to port 9000 from trusted networks only +- **TLS**: Ensure step-ca certificates are properly validated by clients +- **Backups**: Regularly backup the `stepca-data` and `postgres-data` volumes + +## Ports and Networks + +- **Step-CA Port**: 9000 (configurable) +- **PostgreSQL Port**: 5432 (internal only) +- **Network**: `ca-internal` (internal Docker bridge) + +## Debugging + +Enable verbose logging by modifying the init script: + +```bash +# Add to init.sh +set -x # Enable debug output +``` + +View real-time logs: + +```bash +docker-compose logs -f step-ca +docker-compose logs -f postgres +``` + +## Related Documentation + +- [Step-CA Documentation](https://smallstep.com/docs/step-ca) +- [Step CLI Reference](https://smallstep.com/docs/step-cli) +- [Docker Compose Documentation](https://docs.docker.com/compose/) +- [PostgreSQL Documentation](https://www.postgresql.org/docs/) + +## Support + +For issues with step-ca, refer to: +- [Step-CA GitHub](https://github.com/smallstep/certificates) +- [Step Community Discord](https://u.step.sm/discord) +- [Step Community Discussions](https://github.com/smallstep/certificates/discussions) + +## License + +This Docker Compose stack follows the licensing of Smallstep and individual components.