CertMate is a powerful SSL certificate management system designed for modern infrastructure. Built with multi-DNS provider support, Docker containerization, and comprehensive REST API, it's the perfect solution for managing certificates across multiple datacenters and cloud environments.
π Quick Start β’ π Documentation β’ π§ Installation β’ π DNS Providers β’ ποΈ CA Providers β’ ποΈ Storage Backends β’ πΎ Backup & Recovery β’ π API Reference
CertMate solves the complexity of SSL certificate management in modern distributed architectures. Whether you're running a single application or managing certificates across multiple datacenters, CertMate provides:
- π Zero-Downtime Automation - Certificates renew automatically 30 days before expiry
- π Multi-Cloud Support - Works with 19 DNS providers (Cloudflare, AWS, Azure, GCP, Hetzner, Porkbun, GoDaddy, and more)
- π Enterprise-Ready - Docker, Kubernetes, REST API, and monitoring built-in
- π¦ Simple Integration - One-URL certificate downloads for easy automation
- π Security-First - Bearer token authentication, secure file permissions, audit logging
- πΎ Unified Backup System - Atomic backups of settings and certificates ensuring data consistency
- Multiple CA Providers - Support for Let's Encrypt, DigiCert ACME, and Private CAs
- Let's Encrypt Integration - Free, automated SSL certificates with staging/production environments
- DigiCert ACME Support - Enterprise-grade certificates with External Account Binding (EAB)
- Private CA Support - Internal/corporate CAs with custom trust bundles and ACME compatibility
- Wildcard Support - Single certificate for
*.example.comandexample.com - Multi-Domain Certificates - SAN certificates for multiple domains
- Automatic Renewal - Smart renewal 30 days before expiry
- Certificate Validation - Real-time SSL certificate status checking
- Per-Certificate CA Selection - Choose different CAs for different certificates
- Multi-Account Support - Manage multiple accounts per provider for enterprise environments
- Cloudflare - Global CDN with edge locations worldwide (β¨ Multi-Account)
- AWS Route53 - Amazon's scalable DNS service (β¨ Multi-Account)
- Azure DNS - Microsoft's cloud DNS solution (β¨ Multi-Account)
- Google Cloud DNS - Google's high-performance DNS (β¨ Multi-Account)
- DigitalOcean - Cloud infrastructure DNS (β¨ Multi-Account)
- PowerDNS - Open-source DNS server with REST API (β¨ Multi-Account)
- Multi-Account Management - Support multiple accounts per DNS provider for enterprise workflows
- REST API - Complete programmatic control with Swagger/OpenAPI docs
- Web Dashboard - Modern, responsive UI built with Tailwind CSS
- Docker Ready - Full containerization with Docker Compose
- Kubernetes Compatible - Deploy in any Kubernetes cluster
- High Availability - Stateless design for horizontal scaling
- Monitoring Integration - Health checks and structured logging
- π Unified Backups - Atomic snapshots of both settings and certificates ensuring data consistency
- Automatic Backups - Settings and certificates backed up automatically on changes
- Manual Backup Creation - On-demand backup creation via web UI or API
- Comprehensive Coverage - Backs up DNS configurations, certificates, and application settings
- Retention Management - Configurable retention policies with automatic cleanup
- Easy Restore - Simple restore process from any backup point with atomic consistency
- Download Support - Export backups for external storage and disaster recovery
- Local Filesystem - Default secure local storage with proper file permissions (600/700)
- π· Azure Key Vault - Enterprise-grade secret management with Azure integration and HSM protection
- π AWS Secrets Manager - Scalable secret storage with AWS ecosystem integration and cross-region replication
- β« HashiCorp Vault - Industry-standard secret management with versioning, audit logging, and fine-grained policies
- π£ Infisical - Modern open-source secret management with team collaboration and end-to-end encryption
- π§ Pluggable Architecture - Easy to extend with additional storage backends
- π Migration Support - Seamless migration between storage backends without downtime
- β‘ Backward Compatibility - Existing installations continue working without changes
- Bearer Token Authentication - Secure API access control
- File Permissions - Proper certificate file security (600/700)
- Audit Logging - Complete certificate lifecycle tracking
- Environment Variables - Secure credential management
- Rate Limit Handling - Let's Encrypt rate limit awareness
- One-URL Downloads - Simple certificate retrieval for automation
- Multiple Output Formats - PEM, ZIP, individual files
- SDK Examples - Python, Bash, Ansible, Terraform examples
- Webhook Support - Certificate renewal notifications
- Backup API - Programmatic backup creation and restoration
- Extensive Documentation - API docs, guides, and examples
CertMate supports 19 DNS providers through Let's Encrypt DNS-01 challenge via individual certbot plugins that provide reliable, well-tested DNS challenge support. Multi-account support is available for major providers, enabling enterprise-grade deployments with separate accounts for production, staging, and disaster recovery.
| Provider | Credentials Required | Multi-Account | Use Case | Status |
|---|---|---|---|---|
| πΆ Cloudflare | API Token | β Yes | Global CDN, Free tier available | β Stable |
| π AWS Route53 | Access Key, Secret Key | β Yes | AWS infrastructure, Enterprise | β Stable |
| π΅ Azure DNS | Service Principal credentials | β Yes | Microsoft ecosystem | β Stable |
| π’ Google Cloud DNS | Service Account JSON | β Yes | Google Cloud Platform | β Stable |
| π· DigitalOcean | API Token | β Yes | Cloud infrastructure | β Stable |
| β« PowerDNS | API URL, API Key | β Yes | Self-hosted, On-premises | β Stable |
| π· RFC2136 | Nameserver, TSIG Key/Secret | β Yes | Standard DNS update protocol | β Stable |
| π£ Linode | API Key | β Single | Cloud hosting | β Stable |
| π‘ Gandi | API Token | β Single | Domain registrar | β Stable |
| π΄ OVH | API Credentials | β Single | European hosting | β Stable |
| π’ Namecheap | Username, API Key | β Single | Domain registrar | β Stable |
| π¦ Vultr | API Key | β Single | Global cloud infrastructure | β Stable |
| πΊ DNS Made Easy | API Key, Secret Key | β Single | Enterprise DNS management | β Stable |
| π£ NS1 | API Key | β Single | Intelligent DNS platform | β Stable |
| ** Hetzner** | API Token | β Single | European cloud hosting | β Stable |
| π‘ Porkbun | API Key, Secret Key | β Single | Domain registrar with DNS | β Stable |
| π’ GoDaddy | API Key, Secret | β Single | Popular domain registrar | β Stable |
| π΅ Hurricane Electric | Username, Password | β Single | Free DNS hosting | β Stable |
| πΆ Dynu | API Token | β Single | Dynamic DNS service | β Stable |
- Enterprise Multi-Account: Cloudflare, AWS Route53, Azure DNS, Google Cloud DNS, DigitalOcean, PowerDNS, RFC2136
- Cloud Providers: AWS Route53, Azure DNS, Google Cloud DNS, DigitalOcean, Linode, Vultr, Hetzner
- Enterprise DNS: Cloudflare, DNS Made Easy, NS1, PowerDNS
- Domain Registrars: Gandi, OVH, Namecheap, Porkbun, GoDaddy
- European Providers: OVH, Gandi, Hetzner
- Free Services: Hurricane Electric, Dynu
- Standard Protocols: RFC2136 (for BIND and compatible servers)
For supported providers, you can configure multiple accounts to enable:
- Environment Separation: Different accounts for production, staging, and development
- Multi-Region Management: Separate accounts for different geographical regions
- Team Isolation: Department-specific accounts with tailored permissions
- Disaster Recovery: Backup accounts for high-availability scenarios
- Permission Scoping: Accounts with minimal required permissions for security
π Detailed Setup Instructions: See DNS_PROVIDERS.md for provider-specific configuration.
π§ Step-by-Step Installation: See INSTALLATION.md for complete setup guide.
π’ Multi-Account Examples: See MULTI_ACCOUNT_EXAMPLES.md for enterprise configuration examples.
Get CertMate running in under 5 minutes with Docker Compose:
- Docker 20.10+
- Docker Compose 2.0+
- Domain with DNS managed by supported provider
# Clone the repository
git clone https://github.com/fabriziosalmi/certmate.git
cd certmate
# Copy environment template
cp .env.example .envEdit .env file with your credentials:
# π Required: API Security
API_BEARER_TOKEN=your_super_secure_api_token_here_change_this
# π DNS Provider Configuration (choose one or multiple)
# Option 1: Cloudflare (Recommended for beginners)
CLOUDFLARE_TOKEN=your_cloudflare_api_token_here
# Option 2: AWS Route53
# AWS_ACCESS_KEY_ID=your_aws_access_key
# AWS_SECRET_ACCESS_KEY=your_aws_secret_key
# AWS_DEFAULT_REGION=us-east-1
# Option 3: Azure DNS
# AZURE_SUBSCRIPTION_ID=your_azure_subscription_id
# AZURE_RESOURCE_GROUP=your_resource_group
# AZURE_TENANT_ID=your_tenant_id
# AZURE_CLIENT_ID=your_client_id
# AZURE_CLIENT_SECRET=your_client_secret
# Option 4: Google Cloud DNS
# GOOGLE_PROJECT_ID=your_gcp_project_id
# GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json
# Option 5: PowerDNS
# POWERDNS_API_URL=https://your-powerdns-server:8081
# POWERDNS_API_KEY=your_powerdns_api_key
# βοΈ Optional: Application Settings
SECRET_KEY=your_flask_secret_key_here
FLASK_ENV=production
HOST=0.0.0.0
PORT=8000π‘ Storage Backends: By default, certificates are stored locally. For enterprise deployments, you can configure Azure Key Vault, AWS Secrets Manager, HashiCorp Vault, or Infisical via the web interface after startup. See ποΈ Storage Backends for details.
π Backup Best Practices: CertMate v1.2.0 includes a unified backup system that creates atomic snapshots of both settings and certificates. After setup, create your first backup from Settings β Backup Management.
# Start all services
docker-compose up -d
# Check status
docker-compose ps
# View logs
docker-compose logs -f certmate| Service | URL | Description |
|---|---|---|
| Web Dashboard | http://localhost:8000 | Main certificate management interface |
| API Documentation | http://localhost:8000/docs/ | Interactive Swagger/OpenAPI docs |
| Alternative API Docs | http://localhost:8000/redoc/ | ReDoc documentation |
| Health Check | http://localhost:8000/health | Service health monitoring |
Using the Web Interface:
- Navigate to http://localhost:8000
- Go to Settings and configure your DNS provider
- Add your domain (e.g.
example.com) - Click "Create Certificate"
Using the API:
curl -X POST "http://localhost:8000/api/certificates/create" \
-H "Authorization: Bearer your_api_token_here" \
-H "Content-Type: application/json" \
-d '{"domain": "example.com"}'Choose the installation method that best fits your environment:
Perfect for production deployments with isolation and easy scaling. Supports multiple architectures: AMD64 (Intel/AMD), ARM64 (Apple Silicon, ARM servers), and ARM v7 (Raspberry Pi).
# Quick start with Docker Compose
git clone https://github.com/fabriziosalmi/certmate.git
cd certmate
cp .env.example .env
# Edit .env with your configuration
docker-compose up -dMulti-Platform Support:
# Build for multiple architectures (ARM64 + AMD64)
./build-multiplatform.sh
# Build and push to Docker Hub for all platforms
./build-multiplatform.sh -r YOUR_DOCKERHUB_USERNAME -p
# Use pre-built multi-platform image
docker run --platform linux/arm64 -d --name certmate --env-file .env -p 8000:8000 USERNAME/certmate:latestπ Multi-Platform Guide: See DOCKER_MULTIPLATFORM.md for comprehensive multi-architecture setup instructions.
Ideal for development and testing environments.
# Create and activate virtual environment
python3 -m venv certmate-env
source certmate-env/bin/activate # On Windows: certmate-env\Scripts\activate
# Install dependencies
git clone https://github.com/fabriziosalmi/certmate.git
cd certmate
pip install -r requirements.txt
# Set environment variables
export API_BEARER_TOKEN="your_token_here"
export CLOUDFLARE_TOKEN="your_cloudflare_token"
# Run the application
python app.pyFor container orchestration and high availability deployments.
# Example Kubernetes deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: certmate
spec:
replicas: 2
selector:
matchLabels:
app: certmate
template:
metadata:
labels:
app: certmate
spec:
containers:
- name: certmate
image: certmate:latest
ports:
- containerPort: 8000
env:
- name: API_BEARER_TOKEN
valueFrom:
secretKeyRef:
name: certmate-secrets
key: api-token
volumeMounts:
- name: certificates
mountPath: /app/certificates
volumes:
- name: certificates
persistentVolumeClaim:
claimName: certmate-certificatesFor system-wide installation on Linux distributions.
# Install system dependencies (Ubuntu/Debian)
sudo apt update
sudo apt install python3 python3-pip python3-venv certbot openssl
# Clone and install
git clone https://github.com/fabriziosalmi/certmate.git
sudo mv certmate /opt/
cd /opt/certmate
sudo pip3 install -r requirements.txt
# Create systemd service (see Service Setup section below for detailed instructions)
sudo cp certmate.service /etc/systemd/system/
sudo systemctl enable certmate
sudo systemctl start certmateπ Detailed Instructions: See INSTALLATION.md for complete setup guides for each method.
For production deployments, CertMate should run as a system service. This section provides comprehensive instructions for setting up CertMate with systemd on Linux distributions.
- Linux system with systemd
- Python 3.9 or higher
- Root/sudo access
Create a dedicated user for running CertMate:
# Create system user and group
sudo useradd --system --shell /bin/false --home-dir /opt/certmate --create-home certmate
# Set proper ownership
sudo chown -R certmate:certmate /opt/certmateSet up the application in /opt/certmate:
# If not already done, clone the repository
git clone https://github.com/fabriziosalmi/certmate.git
sudo mv certmate /opt/
cd /opt/certmate
# Create Python virtual environment
sudo -u certmate python3 -m venv venv
sudo -u certmate ./venv/bin/pip install -r requirements.txt
# Create necessary directories
sudo -u certmate mkdir -p certificates dataCreate environment file for the service:
# Create environment file
sudo tee /opt/certmate/.env > /dev/null <<EOF
# π SECURITY: Change this token!
API_BEARER_TOKEN=your_super_secure_api_token_here_change_this
# Optional: Set specific host/port
HOST=127.0.0.1
PORT=8000
# Optional: Enable debug mode (not recommended for production)
FLASK_DEBUG=false
EOF
# Set proper permissions
sudo chown certmate:certmate /opt/certmate/.env
sudo chmod 600 /opt/certmate/.envInstall and configure the systemd service:
# Copy service file
sudo cp /opt/certmate/certmate.service /etc/systemd/system/
# Reload systemd configuration
sudo systemctl daemon-reload
# Enable service to start on boot
sudo systemctl enable certmate
# Start the service
sudo systemctl start certmateCheck that the service is running correctly:
# Check service status
sudo systemctl status certmate
# View recent logs
sudo journalctl -u certmate --lines=50
# Follow logs in real-time
sudo journalctl -u certmate -fCommon commands for managing the CertMate service:
# Start service
sudo systemctl start certmate
# Stop service
sudo systemctl stop certmate
# Restart service
sudo systemctl restart certmate
# Reload service configuration
sudo systemctl reload certmate
# Check if service is enabled
sudo systemctl is-enabled certmate
# Check if service is active
sudo systemctl is-active certmate
# Disable service from starting on boot
sudo systemctl disable certmateEnsure proper file permissions for security:
# Set ownership
sudo chown -R certmate:certmate /opt/certmate
# Set directory permissions
sudo chmod 755 /opt/certmate
sudo chmod 750 /opt/certmate/certificates /opt/certmate/data
# Set file permissions
sudo chmod 644 /opt/certmate/*.py /opt/certmate/*.md
sudo chmod 600 /opt/certmate/.env
sudo chmod 755 /opt/certmate/venv/bin/*- API Bearer Token: Always change the default API bearer token in
/opt/certmate/.env - File Permissions: The service runs with restricted permissions and limited filesystem access
- Network Access: The service binds to
0.0.0.0:8000by default - consider using a reverse proxy for production - Environment File: The
.envfile contains sensitive data and should be readable only by thecertmateuser - Certificates: Generated certificates are stored in
/opt/certmate/certificateswith restricted access
If the service fails to start:
- Check service status:
sudo systemctl status certmate - View logs:
sudo journalctl -u certmate --lines=100 - Verify permissions: Ensure the
certmateuser can read all necessary files - Test manually:
sudo -u certmate /opt/certmate/venv/bin/python /opt/certmate/app.py - Check dependencies:
sudo -u certmate /opt/certmate/venv/bin/python validate_dependencies.py
For more detailed installation instructions, see INSTALLATION.md.
CertMate provides a comprehensive REST API for programmatic certificate management. All endpoints require Bearer token authentication.
Include the Authorization header in all API requests:
Authorization: Bearer your_api_token_here# Health check
GET /health
# API documentation
GET /docs/ # Swagger UI
GET /redoc/ # ReDoc documentation
# Prometheus/OpenMetrics monitoring
GET /metrics # Prometheus-compatible metrics
GET /api/metrics # JSON metrics summary# Get current settings
GET /api/settings
Authorization: Bearer your_token_here
# Update settings
POST /api/settings
Authorization: Bearer your_token_here
Content-Type: application/json
{
"dns_provider": "cloudflare",
"dns_providers": {
"cloudflare": {
"api_token": "your_cloudflare_token"
}
},
"domains": [
{
"domain": "example.com",
"dns_provider": "cloudflare"
}
],
"email": "[email protected]",
"auto_renew": true
}# List all certificates
GET /api/certificates
Authorization: Bearer your_token_here
# Create new certificate
POST /api/certificates/create
Authorization: Bearer your_token_here
Content-Type: application/json
{
"domain": "example.com",
"dns_provider": "cloudflare", # Optional, uses default from settings
"account_id": "production" # Optional, specify which account to use
}
# Create certificate with specific account
POST /api/certificates/create
Authorization: Bearer your_token_here
Content-Type: application/json
{
"domain": "staging.example.com",
"dns_provider": "cloudflare",
"account_id": "staging"
}
# Renew certificate
POST /api/certificates/example.com/renew
Authorization: Bearer your_token_here
# Download certificate (ZIP format)
GET /api/certificates/example.com/download
Authorization: Bearer your_token_here
# Check certificate deployment status
GET /api/certificates/example.com/deployment-status
Authorization: Bearer your_token_here# Add multiple accounts for a provider
POST /api/settings/dns-providers/cloudflare/accounts
Authorization: Bearer your_token_here
Content-Type: application/json
{
"account_id": "production",
"config": {
"name": "Production Environment",
"description": "Main production Cloudflare account",
"api_token": "your_production_token_here"
}
}
# List all accounts for a provider
GET /api/settings/dns-providers/cloudflare/accounts
Authorization: Bearer your_token_here
# Set default account for a provider
PUT /api/settings/dns-providers/cloudflare/default-account
Authorization: Bearer your_token_here
Content-Type: application/json
{
"account_id": "production"
}
# Update account configuration
PUT /api/settings/dns-providers/cloudflare/accounts/staging
Authorization: Bearer your_token_here
Content-Type: application/json
{
"config": {
"name": "Staging & Testing",
"description": "Updated staging environment",
"api_token": "new_staging_token_here"
}
}# Get current storage backend information
GET /api/storage/info
Authorization: Bearer your_token_here
# Update storage backend configuration
POST /api/storage/config
Authorization: Bearer your_token_here
Content-Type: application/json
{
"backend": "azure_keyvault",
"azure_keyvault": {
"vault_url": "https://yourvault.vault.azure.net/",
"tenant_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"client_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"client_secret": "your_client_secret"
}
}
# Test storage backend connectivity
POST /api/storage/test
Authorization: Bearer your_token_here
Content-Type: application/json
{
"backend": "aws_secrets_manager",
"config": {
"region": "us-east-1",
"access_key_id": "AKIAIOSFODNN7EXAMPLE",
"secret_access_key": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
}
}
# Migrate certificates between storage backends
POST /api/storage/migrate
Authorization: Bearer your_token_here
Content-Type: application/json
{
"source_backend": "local_filesystem",
"target_backend": "azure_keyvault",
"source_config": {
"cert_dir": "certificates"
},
"target_config": {
"vault_url": "https://yourvault.vault.azure.net/",
"tenant_id": "...",
"client_id": "...",
"client_secret": "..."
}
}# List all available backups
GET /api/backups
Authorization: Bearer your_token_here
# Create new backup
POST /api/backups/create
Authorization: Bearer your_token_here
Content-Type: application/json
{
"reason": "manual_backup"
}
# Download specific backup
GET /api/backups/download/unified/{filename}
Authorization: Bearer your_token_here
# Example:
GET /api/backups/download/unified/unified_backup_20241225_120000.zip
# Restore from backup
POST /api/backups/restore/unified
Authorization: Bearer your_token_here
Content-Type: application/json
{
"filename": "unified_backup_20241225_120000.zip",
"create_backup_before_restore": true
}The most powerful feature for infrastructure automation:
# Download certificates via simple URL pattern
GET /{domain}/tls
Authorization: Bearer your_token_hereThis endpoint returns a ZIP file containing all certificate files:
cert.pem- Server certificatechain.pem- Intermediate certificate chainfullchain.pem- Full certificate chain (cert + chain)privkey.pem- Private key
curl -H "Authorization: Bearer your_token_here" \
-o example.com-tls.zip \
https://your-certmate-server.com/example.com/tlsimport requests
import zipfile
from pathlib import Path
class CertMateClient:
def __init__(self, base_url, token):
self.base_url = base_url.rstrip('/')
self.headers = {"Authorization": f"Bearer {token}"}
def download_certificate(self, domain, extract_to=None):
"""Download and optionally extract certificate for domain"""
url = f"{self.base_url}/{domain}/tls"
response = requests.get(url, headers=self.headers)
response.raise_for_status()
zip_path = f"{domain}-tls.zip"
with open(zip_path, 'wb') as f:
f.write(response.content)
if extract_to:
Path(extract_to).mkdir(parents=True, exist_ok=True)
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
zip_ref.extractall(extract_to)
return zip_path
def list_certificates(self):
"""List all managed certificates"""
response = requests.get(f"{self.base_url}/api/certificates",
headers=self.headers)
response.raise_for_status()
return response.json()
def create_certificate(self, domain, dns_provider=None):
"""Create new certificate for domain"""
data = {"domain": domain}
if dns_provider:
data["dns_provider"] = dns_provider
response = requests.post(f"{self.base_url}/api/certificates/create",
json=data, headers=self.headers)
response.raise_for_status()
return response.json()
def renew_certificate(self, domain):
"""Renew existing certificate"""
response = requests.post(f"{self.base_url}/api/certificates/{domain}/renew",
headers=self.headers)
response.raise_for_status()
return response.json()
class CertMateClient:
def __init__(self, base_url, token):
self.base_url = base_url
self.token = token
self.headers = {"Authorization": f"Bearer {token}"}
def create_certificate(self, domain, dns_provider=None, account_id=None):
"""Create a new certificate with optional account specification"""
data = {"domain": domain}
if dns_provider:
data["dns_provider"] = dns_provider
if account_id:
data["account_id"] = account_id
response = requests.post(
f"{self.base_url}/api/certificates/create",
headers=self.headers,
json=data
)
return response.json()
def add_dns_account(self, provider, account_id, config):
"""Add a new DNS provider account"""
response = requests.post(
f"{self.base_url}/api/settings/dns-providers/{provider}/accounts",
headers=self.headers,
json={"account_id": account_id, "config": config}
)
return response.json()
def list_dns_accounts(self, provider):
"""List all accounts for a DNS provider"""
response = requests.get(
f"{self.base_url}/api/settings/dns-providers/{provider}/accounts",
headers=self.headers
)
return response.json()
def download_certificate(self, domain, extract_to=None):
"""Download certificate as ZIP file"""
response = requests.get(
f"{self.base_url}/{domain}/tls",
headers=self.headers
)
if extract_to and response.status_code == 200:
with zipfile.ZipFile(io.BytesIO(response.content)) as zip_file:
zip_file.extractall(extract_to)
print(f"Certificate extracted to {extract_to}")
return response
# Usage example
client = CertMateClient("https://certmate.company.com", "your_token_here")
# Add multiple Cloudflare accounts
client.add_dns_account("cloudflare", "production", {
"name": "Production Environment",
"description": "Main production account",
"api_token": "prod_token_here"
})
client.add_dns_account("cloudflare", "staging", {
"name": "Staging Environment",
"description": "Development and testing",
"api_token": "staging_token_here"
})
# Create certificates with specific accounts
client.create_certificate("api.company.com", "cloudflare", "production")
client.create_certificate("staging.company.com", "cloudflare", "staging")
# Download certificate
client.download_certificate("api.company.com", extract_to="/etc/ssl/certs/api/")Terraform Provider Example:
# Configure the CertMate provider
terraform {
required_providers {
certmate = {
source = "local/certmate"
version = "~> 1.0"
}
}
}
provider "certmate" {
endpoint = "https://certmate.company.com"
token = var.certmate_token
}
# Create certificates for multiple domains with different accounts
resource "certmate_certificate" "api" {
domain = "api.company.com"
dns_provider = "cloudflare"
account_id = "production"
}
resource "certmate_certificate" "web" {
domain = "web.company.com"
dns_provider = "route53"
account_id = "main-aws"
}
resource "certmate_certificate" "staging" {
domain = "staging.company.com"
dns_provider = "cloudflare"
account_id = "staging"
}
# Download certificates to local files
data "certmate_certificate_download" "api" {
domain = certmate_certificate.api.domain
}
# Use in nginx configuration
resource "kubernetes_secret" "api_tls" {
metadata {
name = "api-tls"
namespace = "default"
}
type = "kubernetes.io/tls"
data = {
"tls.crt" = data.certmate_certificate_download.api.fullchain_pem
"tls.key" = data.certmate_certificate_download.api.private_key_pem
}
}Bash Automation Script:
#!/bin/bash
set -euo pipefail
# Configuration
CERTMATE_URL="https://certmate.company.com"
API_TOKEN="${CERTMATE_TOKEN}"
DOMAIN="${1:-example.com}"
CERT_DIR="/etc/ssl/certs/${DOMAIN}"
BACKUP_DIR="/backup/certs/${DOMAIN}/$(date +%Y%m%d_%H%M%S)"
# Functions
log() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $*" >&2
}
create_backup() {
if [[ -d "$CERT_DIR" ]]; then
log "Creating backup of existing certificates"
mkdir -p "$BACKUP_DIR"
cp -r "$CERT_DIR"/* "$BACKUP_DIR/" || true
fi
}
download_certificate() {
log "Downloading certificate for ${DOMAIN}"
# Download with retry logic
for i in {1..3}; do
if curl -f -H "Authorization: Bearer $API_TOKEN" \
-o "${DOMAIN}-tls.zip" \
"$CERTMATE_URL/$DOMAIN/tls"; then
log "Certificate downloaded successfully"
return 0
else
log "Download attempt $i failed, retrying..."
sleep 5
fi
done
log "Failed to download certificate after 3 attempts"
return 1
}
extract_certificate() {
log "Extracting certificate to ${CERT_DIR}"
mkdir -p "$CERT_DIR"
unzip -o "${DOMAIN}-tls.zip" -d "$CERT_DIR"
# Set proper permissions
chmod 600 "$CERT_DIR"/*.pem
chown root:ssl-cert "$CERT_DIR"/*.pem
}
reload_services() {
log "Reloading web services"
systemctl reload nginx || log "Failed to reload nginx"
systemctl reload apache2 || log "Failed to reload apache2"
systemctl reload haproxy || log "Failed to reload haproxy"
}
cleanup() {
rm -f "${DOMAIN}-tls.zip"
}
# Main execution
main() {
log "Starting certificate update for ${DOMAIN}"
create_backup
download_certificate
extract_certificate
reload_services
cleanup
log "Certificate update completed for ${DOMAIN}"
}
# Trap cleanup on exit
trap cleanup EXIT
# Run main function
main "$@"Advanced Ansible Playbook:
---
- name: Manage SSL certificates with CertMate multi-account support
hosts: web_servers
vars:
certmate_url: "https://certmate.company.com"
certmate_token: "{{ vault_certmate_token }}"
tasks:
- name: Configure Cloudflare accounts
uri:
url: "{{ certmate_url }}/api/settings/dns-providers/cloudflare/accounts"
method: POST
headers:
Authorization: "Bearer {{ certmate_token }}"
Content-Type: "application/json"
body_format: json
body:
account_id: "{{ item.account_id }}"
config:
name: "{{ item.name }}"
description: "{{ item.description }}"
api_token: "{{ item.api_token }}"
loop:
- account_id: "production"
name: "Production Environment"
description: "Main production Cloudflare account"
api_token: "{{ vault_cloudflare_prod_token }}"
- account_id: "staging"
name: "Staging Environment"
description: "Development and testing account"
api_token: "{{ vault_cloudflare_staging_token }}"
- name: Create certificates with specific accounts
uri:
url: "{{ certmate_url }}/api/certificates/create"
method: POST
headers:
Authorization: "Bearer {{ certmate_token }}"
Content-Type: "application/json"
body_format: json
body:
domain: "{{ item.domain }}"
dns_provider: "{{ item.provider }}"
account_id: "{{ item.account_id }}"
loop:
- domain: "api.company.com"
provider: "cloudflare"
account_id: "production"
- domain: "staging.company.com"
provider: "cloudflare"
account_id: "staging"
- domain: "test.company.com"
provider: "route53"
account_id: "backup-aws"
- name: Download and deploy certificates
block:
- name: Download certificate
uri:
url: "{{ certmate_url }}/{{ item }}/tls"
headers:
Authorization: "Bearer {{ certmate_token }}"
dest: "/tmp/{{ item }}-tls.zip"
- name: Extract certificate
unarchive:
src: "/tmp/{{ item }}-tls.zip"
dest: "/etc/ssl/certs/{{ item }}/"
remote_src: yes
owner: root
group: ssl-cert
mode: '0640'
loop:
- "api.company.com"
- "staging.company.com"
- "test.company.com"Production-Ready Ansible Playbook:
---
- name: Enterprise SSL certificate management with CertMate
hosts: web_servers
become: yes
vars:
certmate_url: "https://certmate.company.com"
api_token: "{{ vault_certmate_token }}"
certificate_domains:
- name: "api.company.com"
dns_provider: "cloudflare"
nginx_sites: ["api"]
services_to_reload: ["nginx"]
- name: "web.company.com"
dns_provider: "route53"
nginx_sites: ["web", "admin"]
services_to_reload: ["nginx", "haproxy"]
tasks:
- name: Create certificate directories
file:
path: "/etc/ssl/certs/{{ item.name }}"
state: directory
owner: root
group: ssl-cert
mode: '0750'
loop: "{{ certificate_domains }}"
- name: Check certificate expiry
uri:
url: "{{ certmate_url }}/api/certificates/{{ item.name }}/deployment-status"
method: GET
headers:
Authorization: "Bearer {{ api_token }}"
register: cert_status
loop: "{{ certificate_domains }}"
- name: Create new certificates if needed
uri:
url: "{{ certmate_url }}/api/certificates/create"
method: POST
headers:
Authorization: "Bearer {{ api_token }}"
Content-Type: "application/json"
body_format: json
body:
domain: "{{ item.name }}"
dns_provider: "{{ item.dns_provider }}"
loop: "{{ certificate_domains }}"
when: cert_status.results[ansible_loop.index0].json.needs_renewal | default(false)
- name: Download certificates
uri:
url: "{{ certmate_url }}/{{ item.name }}/tls"
method: GET
headers:
Authorization: "Bearer {{ api_token }}"
dest: "/tmp/{{ item.name }}-tls.zip"
creates: "/tmp/{{ item.name }}-tls.zip"
loop: "{{ certificate_domains }}"
- name: Extract certificates
unarchive:
src: "/tmp/{{ item.name }}-tls.zip"
dest: "/etc/ssl/certs/{{ item.name }}/"
owner: root
group: ssl-cert
mode: '0640'
remote_src: yes
loop: "{{ certificate_domains }}"
notify:
- reload nginx
- reload haproxy
- restart services
- name: Verify certificate installation
openssl_certificate:
path: "/etc/ssl/certs/{{ item.name }}/fullchain.pem"
provider: assertonly
has_expired: no
valid_in: 86400 # Valid for at least 1 day
loop: "{{ certificate_domains }}"
- name: Update nginx SSL configuration
template:
src: "nginx-ssl.conf.j2"
dest: "/etc/nginx/sites-available/{{ item.1 }}"
backup: yes
loop: "{{ certificate_domains | subelements('nginx_sites') }}"
notify: reload nginx
- name: Cleanup temporary files
file:
path: "/tmp/{{ item.name }}-tls.zip"
state: absent
loop: "{{ certificate_domains }}"
handlers:
- name: reload nginx
systemd:
name: nginx
state: reloaded
- name: reload haproxy
systemd:
name: haproxy
state: reloaded
- name: restart services
systemd:
name: "{{ item }}"
state: restarted
loop: "{{ services_to_restart | default([]) }}"| Variable | Required | Default | Description |
|---|---|---|---|
API_BEARER_TOKEN |
β | - | Bearer token for API authentication |
SECRET_KEY |
β | auto-generated | Flask secret key for sessions |
HOST |
β | 127.0.0.1 |
Server bind address |
PORT |
β | 8000 |
Server port |
FLASK_ENV |
β | production |
Flask environment |
FLASK_DEBUG |
β | false |
Enable debug mode |
- Go to Cloudflare API Tokens
- Click "Create Token" β "Custom token"
- Set permissions:
- Zone:
DNS:Edit+Zone:Read - Zone Resources: Include specific zones or all zones
- Zone:
- Copy the generated token
# Environment variable
CLOUDFLARE_TOKEN=your_cloudflare_api_token_here- Create IAM user with Route53 permissions
- Attach policy:
Route53FullAccessor custom policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"route53:ListHostedZones",
"route53:GetChange",
"route53:ChangeResourceRecordSets"
],
"Resource": "*"
}
]
}# Environment variables
AWS_ACCESS_KEY_ID=your_access_key_id
AWS_SECRET_ACCESS_KEY=your_secret_access_key
AWS_DEFAULT_REGION=us-east-1- Create Service Principal:
az ad sp create-for-rbac --name "CertMate" --role "DNS Zone Contributor" --scopes "/subscriptions/{subscription-id}/resourceGroups/{resource-group}"# Environment variables
AZURE_SUBSCRIPTION_ID=your_subscription_id
AZURE_RESOURCE_GROUP=your_resource_group_name
AZURE_TENANT_ID=your_tenant_id
AZURE_CLIENT_ID=your_client_id
AZURE_CLIENT_SECRET=your_client_secret- Create service account with DNS Administrator role
- Download JSON key file
# Environment variables
GOOGLE_PROJECT_ID=your_project_id
GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json# Environment variables
POWERDNS_API_URL=https://your-powerdns-server:8081
POWERDNS_API_KEY=your_api_keyCertMate supports multiple storage backends for certificates, providing flexibility for different deployment scenarios and security requirements. By default, certificates are stored locally on the filesystem, but you can configure enterprise-grade storage backends for enhanced security and compliance.
π‘ Choosing the Right Storage Backend:
- Local Filesystem: Perfect for development, testing, and small deployments
- Azure Key Vault: Best for Azure-native environments and Microsoft ecosystem integration
- AWS Secrets Manager: Ideal for AWS infrastructure and cross-region deployments
- HashiCorp Vault: Excellent for multi-cloud environments and advanced secret management
- Infisical: Great for teams wanting open-source secret management with collaboration features
The default storage backend stores certificates in the local filesystem with secure permissions:
# Default certificate directory
certificates/
βββ example.com/
βββ cert.pem # Server certificate
βββ chain.pem # Certificate chain
βββ fullchain.pem # Full chain
βββ privkey.pem # Private key (600 permissions)Configuration:
- Directory:
certificates(configurable) - Permissions:
600for private keys,644for certificates - Backup: Included in automatic backups
- Use Cases: Development, testing, single-server deployments
Benefits:
- β Zero configuration required
- β No external dependencies
- β Fast access and operations
- β Perfect for getting started
Store certificates securely in Azure Key Vault for enterprise-grade secret management:
Required Dependencies:
pip install -r requirements-azure-storage.txtConfiguration:
{
"certificate_storage": {
"backend": "azure_keyvault",
"azure_keyvault": {
"vault_url": "https://yourvault.vault.azure.net/",
"tenant_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"client_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"client_secret": "your_client_secret"
}
}
}Benefits:
- β Azure-native secret management
- β Compliance and audit capabilities (SOC 2, ISO 27001, FIPS 140-2)
- β Hardware security module (HSM) protection
- β Azure RBAC integration and managed identity support
- β Automatic backup and disaster recovery
Use Cases:
- Azure-based infrastructure
- Enterprise compliance requirements
- Multi-region Azure deployments
- Integration with Azure DevOps and ARM templates
Integrate with AWS Secrets Manager for scalable secret storage:
Required Dependencies:
pip install -r requirements-aws-storage.txtConfiguration:
{
"certificate_storage": {
"backend": "aws_secrets_manager",
"aws_secrets_manager": {
"region": "us-east-1",
"access_key_id": "AKIAIOSFODNN7EXAMPLE",
"secret_access_key": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
}
}
}Benefits:
- β AWS-native secret management
- β Automatic encryption at rest with AWS KMS
- β Cross-region replication for high availability
- β IAM-based access control and fine-grained permissions
- β Integration with AWS CloudTrail for audit logging
- β Automatic rotation capabilities
Use Cases:
- AWS-based infrastructure
- Multi-region deployments
- Integration with ECS, EKS, Lambda
- Compliance with AWS security best practices
Use industry-standard HashiCorp Vault for advanced secret management:
Required Dependencies:
pip install -r requirements-vault-storage.txtConfiguration:
{
"certificate_storage": {
"backend": "hashicorp_vault",
"hashicorp_vault": {
"vault_url": "https://vault.example.com:8200",
"vault_token": "hvs.xxxxxxxxxxxxxxxxxxxx",
"mount_point": "secret",
"engine_version": "v2"
}
}
}Benefits:
- β Industry-standard secret management
- β Secret versioning and rollback capabilities
- β Comprehensive audit logging and monitoring
- β Fine-grained access policies and dynamic secrets
- β Multi-cloud and hybrid cloud support
- β Advanced authentication methods (LDAP, Kubernetes, AWS IAM)
Use Cases:
- Multi-cloud environments
- Complex organizational security requirements
- Dynamic secret generation
- Integration with CI/CD pipelines and Kubernetes
Modern open-source secret management with team collaboration:
Required Dependencies:
pip install -r requirements-infisical-storage.txtConfiguration:
{
"certificate_storage": {
"backend": "infisical",
"infisical": {
"site_url": "https://app.infisical.com",
"client_id": "your_client_id",
"client_secret": "your_client_secret",
"project_id": "your_project_id",
"environment": "prod"
}
}
}Benefits:
- β Open-source secret management with transparency
- β End-to-end encryption for maximum security
- β Team collaboration features and role-based access
- β Multi-environment support (dev, staging, prod)
- β Git-like versioning for secrets
- β Self-hostable for complete control
Use Cases:
- Team-based development workflows
- Open-source preference
- Self-hosted secret management
- Multi-environment certificate management Integrate with AWS Secrets Manager for scalable secret storage:
Required Dependencies:
pip install -r requirements-aws-storage.txtConfiguration:
{
"certificate_storage": {
"backend": "aws_secrets_manager",
"aws_secrets_manager": {
"region": "us-east-1",
"access_key_id": "AKIAIOSFODNN7EXAMPLE",
"secret_access_key": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
}
}
}Benefits:
- AWS-native secret management
- Automatic encryption at rest
- Cross-region replication
- IAM-based access control
Use industry-standard HashiCorp Vault for advanced secret management:
Required Dependencies:
pip install -r requirements-vault-storage.txtConfiguration:
{
"certificate_storage": {
"backend": "hashicorp_vault",
"hashicorp_vault": {
"vault_url": "https://vault.example.com:8200",
"vault_token": "hvs.xxxxxxxxxxxxxxxxxxxx",
"mount_point": "secret",
"engine_version": "v2"
}
}
}Benefits:
- β Industry-standard secret management
- β Secret versioning and rollback capabilities
- β Comprehensive audit logging and monitoring
- β Fine-grained access policies and dynamic secrets
- β Multi-cloud and hybrid cloud support
- β Advanced authentication methods (LDAP, Kubernetes, AWS IAM)
Use Cases:
- Multi-cloud environments
- Complex organizational security requirements
- Dynamic secret generation
- Integration with CI/CD pipelines and Kubernetes
Modern open-source secret management with team collaboration:
Required Dependencies:
pip install -r requirements-infisical-storage.txtConfiguration:
{
"certificate_storage": {
"backend": "infisical",
"infisical": {
"site_url": "https://app.infisical.com",
"client_id": "your_client_id",
"client_secret": "your_client_secret",
"project_id": "your_project_id",
"environment": "prod"
}
}
}Benefits:
- β Open-source secret management with transparency
- β End-to-end encryption for maximum security
- β Team collaboration features and role-based access
- β Multi-environment support (dev, staging, prod)
- β Git-like versioning for secrets
- β Self-hostable for complete control
Use Cases:
- Team-based development workflows
- Open-source preference
- Self-hosted secret management
- Multi-environment certificate management
Install All Storage Backends:
# Install all storage backends at once
pip install -r requirements-storage-all.txtInstall Individual Storage Backends:
# Azure Key Vault only
pip install -r requirements-azure-storage.txt
# AWS Secrets Manager only
pip install -r requirements-aws-storage.txt
# HashiCorp Vault only
pip install -r requirements-vault-storage.txt
# Infisical only
pip install -r requirements-infisical-storage.txtRequirements File Overview:
requirements-storage-all.txt- All storage backends (recommended for production)requirements-azure-storage.txt- Azure Key Vault dependenciesrequirements-aws-storage.txt- AWS Secrets Manager dependenciesrequirements-vault-storage.txt- HashiCorp Vault dependenciesrequirements-infisical-storage.txt- Infisical dependenciesrequirements-minimal.txt- Base CertMate without storage backends
Via Web Interface:
- Navigate to Settings β Certificate Storage Backend
- Select your preferred backend from the dropdown
- Configure the required credentials and settings
- Test the connection to verify configuration
- Save settings and optionally migrate existing certificates
Via API:
# Test storage backend connectivity before switching
curl -X POST "http://localhost:8000/api/storage/test" \
-H "Authorization: Bearer your_token" \
-H "Content-Type: application/json" \
-d '{
"backend": "azure_keyvault",
"config": {
"vault_url": "https://yourvault.vault.azure.net/",
"tenant_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"client_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"client_secret": "your_client_secret"
}
}'
# Get current storage backend information
curl -X GET "http://localhost:8000/api/storage/info" \
-H "Authorization: Bearer your_token"
# Update storage backend configuration
curl -X POST "http://localhost:8000/api/storage/config" \
-H "Authorization: Bearer your_token" \
-H "Content-Type: application/json" \
-d '{
"backend": "hashicorp_vault",
"config": {
"vault_url": "https://vault.example.com:8200",
"vault_token": "hvs.xxxxxxxxxxxxxxxxxxxx",
"mount_point": "secret",
"engine_version": "v2"
}
}'π Migrating Between Backends:
Zero-Downtime Migration Process:
- Configure the new storage backend
- Test connectivity and verify access
- Use the migration tool in Settings or API
- Verify all certificates are accessible in new backend
- Optionally clean up old storage
Migration via API:
# Migrate all certificates from current backend to new backend
curl -X POST "http://localhost:8000/api/storage/migrate" \
-H "Authorization: Bearer your_token" \
-H "Content-Type: application/json" \
-d '{
"target_backend": "aws_secrets_manager",
"target_config": {
"region": "us-east-1",
"access_key_id": "AKIAIOSFODNN7EXAMPLE",
"secret_access_key": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
},
"verify_migration": true
}'Migration Benefits:
- β Zero downtime during migration
- β Automatic verification of migrated certificates
- β Rollback capability if issues are detected
- β
Preservation of certificate metadata and permissions
curl -X POST "http://localhost:8000/api/storage/migrate"
-H "Authorization: Bearer your_token"
-H "Content-Type: application/json"
-d '{ "source_backend": "local_filesystem", "target_config": { "backend": "azure_keyvault", "azure_keyvault": {...} } }'
**Backward Compatibility:**
- Existing installations continue working without changes
- New storage backends are opt-in
- Migration is non-destructive (copies certificates)
- Local filesystem remains the default backend
### π Directory Structure
certmate/ βββ π app.py # Main Flask application βββ π requirements.txt # Python dependencies βββ π docker-compose.yml # Docker Compose configuration βββ π Dockerfile # Container build instructions βββ π nginx.conf # Nginx reverse proxy config βββ π .env.example # Environment template βββ π README.md # This documentation βββ π INSTALLATION.md # Detailed installation guide βββ π DNS_PROVIDERS.md # DNS provider configuration βββ π CONTRIBUTING.md # Contribution guidelines βββ π certificates/ # Certificate storage β βββ π {domain}/ β βββ π cert.pem # Server certificate β βββ π chain.pem # Certificate chain β βββ π fullchain.pem # Full chain β βββ π privkey.pem # Private key βββ π data/ # Application data β βββ βοΈ settings.json # Persistent settings βββ π logs/ # Application logs βββ π letsencrypt/ # Let's Encrypt working directory β βββ π config/ # Certbot configuration β βββ π work/ # Certbot working files β βββ π logs/ # Certbot logs βββ π templates/ # Web interface templates βββ π index.html # Main dashboard βββ βοΈ settings.html # Settings page
## π Security & Best Practices
### π‘οΈ Security Considerations
#### Authentication & Authorization
- **Strong Bearer Tokens**: Use cryptographically secure tokens (32+ characters)
- **Token Rotation**: Regularly rotate API tokens
- **Environment Variables**: Never commit tokens to version control
- **HTTPS Only**: Always use HTTPS in production environments
- **IP Restrictions**: Implement firewall rules to restrict access
#### Certificate Security
- **File Permissions**: Private keys stored with `600` permissions
- **Directory Permissions**: Certificate directories with `700` permissions
- **Backup Encryption**: Encrypt certificate backups
- **Access Logging**: Monitor certificate access patterns
#### Network Security
```bash
# Example firewall rules (iptables)
# Allow only specific IPs to access CertMate
iptables -A INPUT -p tcp --dport 8000 -s 10.0.0.0/8 -j ACCEPT
iptables -A INPUT -p tcp --dport 8000 -s 192.168.0.0/16 -j ACCEPT
iptables -A INPUT -p tcp --dport 8000 -j DROP
# docker-compose.prod.yml
version: '3.8'
services:
certmate:
image: certmate:latest
deploy:
replicas: 2
resources:
limits:
cpus: '1.0'
memory: 512M
reservations:
cpus: '0.5'
memory: 256M
environment:
- FLASK_ENV=production
- GUNICORN_WORKERS=4
- GUNICORN_THREADS=2
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60supstream certmate_backend {
server certmate1:8000;
server certmate2:8000;
server certmate3:8000;
}
server {
listen 443 ssl http2;
server_name certmate.company.com;
ssl_certificate /etc/ssl/certs/certmate.company.com/fullchain.pem;
ssl_certificate_key /etc/ssl/certs/certmate.company.com/privkey.pem;
location / {
proxy_pass http://certmate_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# API rate limiting
limit_req zone=api burst=10 nodelay;
}
}CertMate provides comprehensive backup and recovery capabilities built directly into the application, ensuring your certificates and configuration data are always protected.
What is Unified Backup?
- Atomic Operation: Creates a single ZIP file containing both settings and certificates
- Data Consistency: Ensures settings and certificates are always in sync
- Prevents Corruption: Eliminates configuration/certificate mismatches
- Simplified Management: One backup file contains everything needed for complete restoration
Automatic Backups:
- Unified Snapshots - Automatically created when DNS providers, domains, certificates, or application settings are modified
- Retention Management - Configurable retention policy (default: 10 most recent backups)
- Automatic Cleanup - Old backups are automatically removed based on retention settings
Manual Backups:
- On-Demand Creation - Create backups anytime via the web interface or API
- Download Support - Export backups for external storage and disaster recovery
- Comprehensive Coverage - Includes all DNS configurations, certificates, and application settings
Access backup features from the Settings page:
<!-- Create backup -->
<button onclick="createBackup('unified', this)">Create Backup</button>
<!-- View and manage existing backups -->
- Download backups for external storage
- Restore from any backup point with atomic consistency
- View backup contents and metadata
- Delete specific backups manuallyCreate Backup:
# Create backup (settings + certificates)
curl -X POST "http://localhost:8000/api/backups/create" \
-H "Authorization: Bearer your_token" \
-H "Content-Type: application/json" \
-d '{"reason": "manual_backup"}'
# Response includes backup file information
{
"success": true,
"backup_file": "unified_backup_20241225_120000.zip",
"size": "2.5MB",
"contents": {
"settings": true,
"certificates": 15
}
}List and Download Backups:
# List all backups
curl -H "Authorization: Bearer your_token" \
"http://localhost:8000/api/backups"
# Download backup
curl -H "Authorization: Bearer your_token" \
"http://localhost:8000/api/backups/download/unified/unified_backup_20241225_120000.zip" \
-o backup.zipBackup File (ZIP):
unified_backup_20241225_120000.zip
βββ settings.json # Complete application settings
β βββ timestamp: "2024-12-25T12:00:00Z"
β βββ version: "1.2.0"
β βββ dns_providers: {...}
β βββ domains: [...]
β βββ settings: {...}
βββ certificates/ # All certificate files
βββ domain1.com/
β βββ cert.pem
β βββ chain.pem
β βββ fullchain.pem
β βββ privkey.pem
βββ domain2.com/
βββ cert.pem
βββ chain.pem
βββ fullchain.pem
βββ privkey.pem
Backup Restoration:
Web Interface:
- Navigate to Settings β Backup Management
- Select the backup to restore from
- Confirm restoration (restores both settings and certificates atomically)
- Application will restart to apply new settings
- Verify all certificates and configurations are working
API Restoration:
# Restore from backup
curl -X POST "http://localhost:8000/api/backups/restore/unified" \
-H "Authorization: Bearer your_token" \
-H "Content-Type: application/json" \
-d '{"filename": "unified_backup_20241225_120000.zip", "create_backup_before_restore": true}'For additional protection, integrate with external backup systems:
Automated External Backup Script:
#!/bin/bash
# /opt/scripts/backup-certmate-external.sh
BACKUP_DIR="/backup/certmate/$(date +%Y%m%d_%H%M%S)"
CERT_DIR="/opt/certmate/certificates"
DATA_DIR="/opt/certmate/data"
RETENTION_DAYS=30
# Create backup directory
mkdir -p "$BACKUP_DIR"
# Download latest backup via API
curl -H "Authorization: Bearer $API_TOKEN" \
"http://localhost:8000/api/backups/download/unified/latest" \
-o "$BACKUP_DIR/certmate_backup.zip"
# Backup certificates directory
tar -czf "$BACKUP_DIR/certificates.tar.gz" "$CERT_DIR"
# Backup application data
tar -czf "$BACKUP_DIR/data.tar.gz" "$DATA_DIR"
# Encrypt backups (optional)
gpg --cipher-algo AES256 --compress-algo 1 --symmetric \
--output "$BACKUP_DIR/certmate_backup.zip.gpg" \
"$BACKUP_DIR/certmate_backup.zip"
# Cleanup old backups
find /backup/certmate -type d -mtime +$RETENTION_DAYS -exec rm -rf {} \;
echo "External backup completed: $BACKUP_DIR"#!/bin/bash
# Recovery from backup
BACKUP_DATE="20241225_120000"
BACKUP_DIR="/backup/certmate/$BACKUP_DATE"
# Stop services
docker-compose down
# Restore certificates
tar -xzf "$BACKUP_DIR/certificates.tar.gz" -C /opt/certmate/
# Restore data
tar -xzf "$BACKUP_DIR/data.tar.gz" -C /opt/certmate/
# Set permissions
chown -R 1000:1000 /opt/certmate/certificates
chmod -R 700 /opt/certmate/certificates
# Start services
docker-compose up -d
echo "Recovery completed from backup: $BACKUP_DATE"Web Interface Recovery:
- Navigate to Settings β Backup Management
- Select the backup to restore from
- Choose restore type (settings, certificates, or both)
- Confirm restoration
- Application will restart if settings are restored
API Recovery:
# Restore settings from backup
curl -X POST "http://localhost:8000/api/backup/restore/settings" \
-H "Authorization: Bearer your_token" \
-H "Content-Type: application/json" \
-d '{"backup_id": "settings_20241225_120000"}'
# Restore certificates from backup
curl -X POST "http://localhost:8000/api/backup/restore/certificates" \
-H "Authorization: Bearer your_token" \
-H "Content-Type: application/json" \
-d '{"backup_id": "certificates_20241225_120000"}'Manual Recovery from External Backup:
#!/bin/bash
# /opt/scripts/backup-certmate.sh
BACKUP_DIR="/backup/certmate/$(date +%Y%m%d_%H%M%S)"
CERT_DIR="/opt/certmate/certificates"
DATA_DIR="/opt/certmate/data"
RETENTION_DAYS=30
# Create backup directory
mkdir -p "$BACKUP_DIR"
# Backup certificates
tar -czf "$BACKUP_DIR/certificates.tar.gz" "$CERT_DIR"
# Backup application data
tar -czf "$BACKUP_DIR/data.tar.gz" "$DATA_DIR"
# Backup database if using external DB
# mysqldump certmate > "$BACKUP_DIR/database.sql"
# Encrypt backups
gpg --cipher-algo AES256 --compress-algo 1 --symmetric \
--output "$BACKUP_DIR/certificates.tar.gz.gpg" \
"$BACKUP_DIR/certificates.tar.gz"
# Cleanup old backups
find /backup/certmate -type d -mtime +$RETENTION_DAYS -exec rm -rf {} \;
echo "Backup completed: $BACKUP_DIR"#!/bin/bash
# Recovery from backup
BACKUP_DATE="20241225_120000"
BACKUP_DIR="/backup/certmate/$BACKUP_DATE"
# Stop services
docker-compose down
# Restore certificates
gpg --decrypt "$BACKUP_DIR/certificates.tar.gz.gpg" | \
tar -xzf - -C /opt/certmate/
# Restore data
tar -xzf "$BACKUP_DIR/data.tar.gz" -C /opt/certmate/
# Set permissions
chown -R 1000:1000 /opt/certmate/certificates
chmod -R 700 /opt/certmate/certificates
# Start services
docker-compose up -d
echo "Recovery completed from backup: $BACKUP_DATE"# Basic health check
curl -f http://localhost:8000/health
# Detailed health with auth
curl -H "Authorization: Bearer your_token" \
http://localhost:8000/api/certificates# Add to app.py for Prometheus monitoring
from prometheus_client import Counter, Histogram, generate_latest
# Metrics
certificate_requests = Counter('certmate_certificate_requests_total',
'Total certificate requests', ['domain', 'status'])
certificate_expiry = Histogram('certmate_certificate_expiry_days',
'Days until certificate expiry', ['domain'])
@app.route('/metrics')
def metrics():
return generate_latest()# docker-compose.logging.yml
version: '3.8'
services:
certmate:
logging:
driver: "fluentd"
options:
fluentd-address: localhost:24224
tag: certmate
fluentd-async-connect: "true"
fluentd:
image: fluent/fluentd:v1.14
volumes:
- ./fluentd/conf:/fluentd/etc
- ./logs:/var/log/fluentd
ports:
- "24224:24224"
- "24224:24224/udp"{
"dashboard": {
"title": "CertMate SSL Certificate Monitoring",
"panels": [
{
"title": "Certificate Expiry Status",
"targets": [
{
"expr": "certmate_certificate_expiry_days < 30",
"legendFormat": "Expiring Soon ({{domain}})"
}
],
"alert": {
"conditions": [
{
"query": {"queryType": "", "refId": "A"},
"reducer": {"type": "last", "params": []},
"evaluator": {"params": [30], "type": "lt"}
}
],
"executionErrorState": "alerting",
"frequency": "1h",
"handler": 1,
"name": "Certificate Expiring",
"noDataState": "no_data",
"notifications": []
}
}
]
}
}# Add webhook notification support
import requests
def send_webhook_notification(domain, event_type, details):
webhook_url = os.getenv('WEBHOOK_URL')
if not webhook_url:
return
payload = {
'domain': domain,
'event': event_type,
'timestamp': datetime.now().isoformat(),
'details': details
}
try:
requests.post(webhook_url, json=payload, timeout=10)
except Exception as e:
logger.error(f"Failed to send webhook: {e}")# Slack notification script
#!/bin/bash
SLACK_WEBHOOK="your_slack_webhook_url"
DOMAIN="$1"
STATUS="$2"
MESSAGE="Certificate for $DOMAIN: $STATUS"
curl -X POST -H 'Content-type: application/json' \
--data "{\"text\":\"π $MESSAGE\"}" \
"$SLACK_WEBHOOK"Issue: DNS validation failed
# Check DNS propagation
dig TXT _acme-challenge.example.com @8.8.8.8
# Verify DNS provider credentials
curl -H "Authorization: Bearer cf_token" \
"https://api.cloudflare.com/client/v4/user/tokens/verify"Issue: Rate limit exceeded
# Let's Encrypt rate limits:
# - 50 certificates per registered domain per week
# - 5 duplicate certificates per week
# - 300 new orders per account per 3 hours
# Check rate limit status
curl "https://crt.sh/?q=example.com&output=json" | jq lengthIssue: Permission denied accessing certificate files
# Fix file permissions
sudo chown -R certmate:certmate /opt/certmate/certificates
sudo chmod -R 700 /opt/certmate/certificates
sudo chmod 600 /opt/certmate/certificates/*/privkey.pemIssue: 401 Unauthorized
# Verify token format
curl -H "Authorization: Bearer your_token_here" \
http://localhost:8000/api/certificates
# Check token in settings
docker exec certmate cat /app/data/settings.json | jq .api_bearer_tokenIssue: Token not found
# Reset API token
docker exec -it certmate python -c "
import json
with open('/app/data/settings.json', 'r+') as f:
data = json.load(f)
data['api_bearer_token'] = 'new_secure_token_here'
f.seek(0)
json.dump(data, f, indent=2)
f.truncate()
"Issue: Container won't start
# Check logs
docker-compose logs certmate
# Verify environment variables
docker-compose config
# Check port conflicts
netstat -tulpn | grep :8000Issue: Volume mount issues
# Fix volume permissions
sudo chown -R 1000:1000 ./certificates ./data ./logs
# Check volume mounts
docker inspect certmate | jq '.[0].Mounts'Cloudflare:
# Verify API token permissions
curl -X GET "https://api.cloudflare.com/client/v4/user/tokens/verify" \
-H "Authorization: Bearer your_token_here"
# Check zone access
curl -X GET "https://api.cloudflare.com/client/v4/zones" \
-H "Authorization: Bearer your_token_here"AWS Route53:
# Test AWS credentials
aws sts get-caller-identity
# Check Route53 permissions
aws route53 list-hosted-zonesAzure DNS:
# Verify service principal
az login --service-principal \
-u $AZURE_CLIENT_ID \
-p $AZURE_CLIENT_SECRET \
--tenant $AZURE_TENANT_ID
# Check DNS zone access
az network dns zone listEnable debug logging for troubleshooting:
# Environment variable
FLASK_DEBUG=true
FLASK_ENV=development
# Or in Docker Compose
docker-compose -f docker-compose.yml -f docker-compose.debug.yml upIssue: Some API endpoints may fail during rapid testing
Workaround: Add delays between API calls in automated tests:
# Add delay between test calls
sleep 0.1Before seeking help, please provide:
- CertMate version/commit hash
- DNS provider being used
- Error messages from logs
- Steps to reproduce the issue
- Environment details (Docker, Python version, OS)
# Collect system information
echo "=== CertMate Debug Info ==="
echo "Version: $(docker exec certmate python -c 'import app; print(getattr(app, "__version__", "unknown"))')"
echo "Python: $(docker exec certmate python --version)"
echo "OS: $(docker exec certmate cat /etc/os-release | head -2)"
echo "Certbot: $(docker exec certmate certbot --version)"
echo "DNS Plugins: $(docker exec certmate pip list | grep certbot-dns)"
echo "Certificates: $(docker exec certmate ls -la /app/certificates)"
echo "Settings: $(docker exec certmate cat /app/data/settings.json | jq .)"| Document | Description | Target Audience |
|---|---|---|
| README.md | Main documentation and quick start | All users |
| INSTALLATION.md | Detailed installation instructions | System administrators |
| DNS_PROVIDERS.md | DNS provider setup guides | DevOps engineers |
| CA_PROVIDERS.md | Certificate Authority configuration | Enterprise users |
| CONTRIBUTING.md | Development and contribution guide | Developers |
| CODE_OF_CONDUCT.md | Community guidelines | Contributors |
- API Documentation: http://your-server:8000/docs/ (Swagger UI)
- Alternative API Docs: http://your-server:8000/redoc/ (ReDoc)
- GitHub Repository: https://github.com/fabriziosalmi/certmate
- Docker Hub: https://hub.docker.com/r/certmate/certmate
- Issue Tracker: https://github.com/fabriziosalmi/certmate/issues
Check out our examples repository for:
- Production deployment configurations
- Integration scripts for popular tools
- Terraform modules
- Kubernetes manifests
- CI/CD pipeline examples
We welcome contributions! Areas where we need help:
- Documentation - Tutorials, use cases, translations
- Testing - DNS provider testing, edge cases
- Integrations - New DNS providers, monitoring tools
- Features - UI improvements, API enhancements
We love contributions! CertMate is an open-source project and we welcome:
- π Bug Reports - Help us identify and fix issues
- π‘ Feature Requests - Suggest new functionality
- π Documentation - Improve guides and examples
- π§ͺ Testing - Test new features and edge cases
- π» Code - Submit pull requests with improvements
# Fork and clone the repository
git clone https://github.com/fabriziosalmi/certmate.git
cd certmate
# Create development environment
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
pip install -r requirements-dev.txt
# Set up pre-commit hooks
pre-commit install
# Run tests
pytest
# Start development server
python app.py- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
Made with β€οΈ by Fabrizio Salmi
β Star us on GitHub β’ π Report Bug β’ π‘ Request Feature