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
- Let's Encrypt Integration - Free, automated SSL certificates
- Wildcard Support - Single certificate for
*.example.com
andexample.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
- Cloudflare - Global CDN with edge locations worldwide
- AWS Route53 - Amazon's scalable DNS service
- Azure DNS - Microsoft's cloud DNS solution
- Google Cloud DNS - Google's high-performance DNS
- PowerDNS - Open-source DNS server with REST API
- 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
- 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
- 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:
Provider | Credentials Required | Use Case | Status |
---|---|---|---|
🔶 Cloudflare | API Token | Global CDN, Free tier available | ✅ Stable |
🟠 AWS Route53 | Access Key, Secret Key | AWS infrastructure, Enterprise | ✅ Stable |
🔵 Azure DNS | Service Principal credentials | Microsoft ecosystem | ✅ Stable |
🟢 Google Cloud DNS | Service Account JSON | Google Cloud Platform | ✅ Stable |
⚫ PowerDNS | API URL, API Key | Self-hosted, On-premises | ✅ Stable |
🔷 DigitalOcean | API Token | Cloud infrastructure | ✅ Stable |
🟣 Linode | API Key | Cloud hosting | ✅ Stable |
🟡 Gandi | API Token | Domain registrar | ✅ Stable |
🔴 OVH | API Credentials | European hosting | ✅ Stable |
🟢 Namecheap | Username, API Key | Domain registrar | ✅ Stable |
🟦 Vultr | API Key | Global cloud infrastructure | ✅ Stable |
🔺 DNS Made Easy | API Key, Secret Key | Enterprise DNS management | ✅ Stable |
🟣 NS1 | API Key | Intelligent DNS platform | ✅ Stable |
🔷 RFC2136 | Nameserver, TSIG Key/Secret | Standard DNS update protocol | ✅ Stable |
🟠 Hetzner | API Token | European cloud hosting | ✅ Stable |
🟡 Porkbun | API Key, Secret Key | Domain registrar with DNS | ✅ Stable |
🟢 GoDaddy | API Key, Secret | Popular domain registrar | ✅ Stable |
🔵 Hurricane Electric | Username, Password | Free DNS hosting | ✅ Stable |
🔶 Dynu | API Token | Dynamic DNS service | ✅ Stable |
- 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)
📚 Detailed Setup Instructions: See DNS_PROVIDERS.md for provider-specific configuration.
🔧 Step-by-Step Installation: See INSTALLATION.md for complete setup guide.
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 .env
Edit .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
# Start all services
docker-compose up -d
# Check status
docker-compose ps
# View logs
docker-compose logs -f certmate
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 -d
Multi-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.py
For 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-certificates
For 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/certmate
Set 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 data
Create 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/.env
Install 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 certmate
Check 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 -f
Common 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 certmate
Ensure 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:8000
by default - consider using a reverse proxy for production - Environment File: The
.env
file contains sensitive data and should be readable only by thecertmate
user - Certificates: Generated certificates are stored in
/opt/certmate/certificates
with 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
certmate
user 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
# 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
}
# 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
The most powerful feature for infrastructure automation:
# Download certificates via simple URL pattern
GET /{domain}/tls
Authorization: Bearer your_token_here
This 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/tls
import 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()
# Usage example
client = CertMateClient("https://certmate.company.com", "your_token_here")
# Download certificate
client.download_certificate("api.company.com", extract_to="/etc/ssl/certs/api/")
# List all certificates
certs = client.list_certificates()
for cert in certs:
print(f"Domain: {cert['domain']}, Expires: {cert['expiry_date']}")
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
resource "certmate_certificate" "api" {
domain = "api.company.com"
dns_provider = "cloudflare"
}
resource "certmate_certificate" "web" {
domain = "web.company.com"
dns_provider = "route53"
}
# 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
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:
Route53FullAccess
or 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_key
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
- 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
- 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
# 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: 60s
upstream 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;
}
}
#!/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 length
Issue: 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.pem
Issue: 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_token
Issue: 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 :8000
Issue: 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-zones
Azure 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 list
Enable 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 up
Before seeking help, please provide:
# 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 .)"
Check out our examples repository for:
- Production deployment configurations
- Integration scripts for popular tools
- Terraform modules
- Kubernetes manifests
- CI/CD pipeline examples
- 🔄 Certificate Templates - Predefined certificate configurations
- 📊 Advanced Analytics - Certificate usage and performance metrics
- 🔐 ACME v2 Features - External account binding, certificate transparency
- 🌍 Multi-Region Support - Geographic certificate distribution
- 📱 Mobile App - iOS/Android certificate monitoring
- 🤖 AI-Powered Renewal - Intelligent renewal scheduling
- 🔌 Plugin System - Custom DNS provider plugins
- 📈 Enterprise Dashboard - Multi-tenant certificate management
Version | Release Date | Key Features |
---|---|---|
v2.0.0 | 2024-12-25 | Multi-DNS provider support, enhanced API |
v1.5.0 | 2024-11-15 | Docker improvements, monitoring features |
v1.0.0 | 2024-10-01 | Initial release with Cloudflare support |
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
Thanks to all contributors who make CertMate better!
This project is licensed under the MIT License - see the LICENSE file for details.
- ✅ Commercial Use - Use in commercial projects
- ✅ Modification - Modify and distribute changes
- ✅ Distribution - Distribute the software
- ✅ Private Use - Use privately
- ❌ Liability - No warranty or liability
- ❌ Patent Grant - No patent rights granted