CertMate - Certificate Lifecycle Management

Snapshot 2026-08-03 23:56:39 UTC · version 1

published
INDEXABLE MARKDOWN SNAPSHOT

Research document

Open canonical .md

CertMate - Certificate Lifecycle Management

CertMate is a self-hosted certificate lifecycle management platform: it issues and renews TLS certificates, discovers the ones you did not issue, keeps a single inventory of what exists across your estate — what is served where, who issued it, when it expires, which cryptography it uses — and deploys renewed certificates to where they are needed. It supports 29 DNS providers, runs its own private CA for internal names, keeps a tamper-evident audit trail of every operation, and exposes all of it through a REST API.

Quick StartCLIDocumentationInstallationDNS ProvidersCA ProvidersStorage BackendsBackup and RecoveryAPI Reference


Ecosystem

CertMate is the open-source core of a small, focused toolset:

  • certmate-tools — free, privacy-first, client-side TLS / certificate / ACME diagnostics (runs entirely in your browser).
  • certmate-agent — conversational assistant: a local LLM mapped 1:1 to CertMate's REST API, with RAG over the docs.
  • nis2-public — NIS2 continuous posture management & remediation.

Enterprise / high-scale — multi-tenant, mTLS, white-label and NIS2-aligned deployments are available through CertMate-ng (source-available, BSL 1.1, EU-built). For access or a deployment discussion, email fabrizio.salmi@gmail.com.


Command-line interface

The whole certificate lifecycle from your terminal — pip install certmate-cli:

pip install certmate-cli

export CERTMATE_URL=https://certmate.example.com
export CERTMATE_TOKEN=...                 # omit on a fresh (setup-mode) instance

certmate health
certmate cert create app.example.com --dns cloudflare --wait   # issue, block until live
certmate cert ls
certmate cert info app.example.com
certmate audit verify

certmate-cli is a thin layer over certmate-sdk (pip install certmate-sdk) — a small, httpx-based Python client for the same REST API the web UI and MCP server drive. Both are first-party, live in clients/, and are published to PyPI. The clip above is a real issuance over DNS-01 (Let's Encrypt staging); see demo/.


AI agents (MCP server)

CertMate ships a first-party Model Context Protocol server, so an assistant like Claude can drive the same REST API the web UI and the CLI use — with the same auth and the same audit trail. It lives in mcp/, is Node.js (>= 20), and exposes 16 tools: inventory and status (certmate_list_certificates, certmate_get_certificate, certmate_diagnostics, certmate_get_activity, …), lifecycle operations (certmate_create_certificate, certmate_renew_certificate, certmate_get_job, certmate_set_auto_renew, …), and delivery (certmate_download_certificate, certmate_deploy_certificate).

cd mcp && npm install
// claude_desktop_config.json — or any MCP-capable client
{
  "mcpServers": {
    "certmate": {
      "command": "node",
      "args": ["/path/to/certmate/mcp/index.js"],
      "env": {
        "CERTMATE_URL": "https://certmate.example.com",
        "CERTMATE_TOKEN": "<a scoped API key with is_agent: true>"
      }
    }
  }
}

Give it a scoped key, not your admin token. A key created with is_agent: true (a checkbox under Settings → API Keys, or is_agent in POST /api/keys) makes every action the agent takes land in the audit chain as actor.kind="agent" rather than being indistinguishable from a human operator — which is the difference between an audit trail and a rumour. Scope the key to the domains the agent is allowed to touch.

Full tool reference, attribution model and safety notes: docs/mcp.md.


Why CertMate?

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, with deploy hooks to reload services
  • Multi-Cloud Support - Works with two dozen+ DNS providers (Cloudflare, AWS, Azure, GCP, Akamai Edge DNS, Hetzner, Porkbun, GoDaddy, and more — see docs/dns-providers.md for the full list)
  • Enterprise-Ready - RBAC, scoped API keys, Docker, Kubernetes, REST API, and monitoring built-in
  • Simple Integration - One-URL certificate downloads for easy automation
  • Security-First - Role-based access control, scoped API keys, audit logging, HMAC-signed webhooks
  • Unified Backup System - Atomic backups of settings and certificates ensuring data consistency
  • Real-Time Dashboard - SSE-powered live updates, command palette, keyboard shortcuts, dark mode

Key Features

Certificate Management

  • Multiple CA Providers - Support for Let's Encrypt, ZeroSSL, Google Trust Services, Actalis, DigiCert ACME, SSL.com, and Private CAs
  • Let's Encrypt Integration - Free, automated SSL certificates, with the staging environment available as a dedicated CA entry for testing
  • DigiCert ACME Support - Enterprise-grade certificates with External Account Binding (EAB)
  • Actalis Support - Free 90-day DV certificates from a European CA via ACME with EAB
  • Private CA Support - Internal/corporate CAs with custom trust bundles and ACME compatibility
  • Wildcard Support - Single certificate for *.example.com and example.com
  • Multi-Domain Certificates - SAN certificates for multiple domains
  • DNS Alias via CNAME Delegation - Delegate ACME DNS validation to an alternative domain using standard CNAME records
  • 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
  • Zombie Certificate Scanner - filesystem scanner to identify and clean up orphan ("zombie") certificates no longer tracked in the active configuration

Multi-DNS Provider Support

  • 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)

Enterprise Features

  • Role-Based Access Control - Three-tier RBAC with viewer, operator, and admin roles
  • Scoped API Keys - Create, revoke, and manage API keys with per-key role and optional expiration
  • 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 and Alpine.js
  • Setup Wizard - Guided first-run configuration for DNS, CA, and authentication
  • Real-Time Updates - Server-Sent Events (SSE) push live status to the dashboard
  • Docker Ready - Full containerization with Docker Compose
  • Kubernetes Compatible - Deploy in any Kubernetes cluster
  • Monitoring Integration - Health checks, Prometheus metrics, and structured JSON logging

Backup and Recovery

  • 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

Certificate Storage Backends

  • 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
  • S3-Compatible Object Storage - One backend for any S3 endpoint via a configurable endpoint URL (Hetzner, Contabo, OVHcloud, Scaleway, Exoscale, Wasabi, MinIO, AWS) — ideal for EU-sovereign object storage; no extra dependency
  • 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

Notifications & Automation

  • Multi-Channel Notifications - Email (SMTP), Slack, Discord, Telegram, ntfy, Gotify, and generic webhooks
  • Webhook HMAC Signatures - SHA-256 signed payloads for secure webhook verification
  • Deploy Hooks - Post-issuance shell commands to reload Nginx/Apache or run custom scripts
  • Weekly Digest - Scheduled email summary of certificate status and upcoming renewals
  • SSE Real-Time Events - Live push updates for certificate operations and deploy hook results

Security & Compliance

  • Role-Based Access Control - Viewer, operator, and admin roles with hierarchical permissions
  • Scoped API Keys - Create keys with specific roles and optional expiration dates
  • Bearer Token Authentication - Secure API access control
  • File Permissions - Proper certificate file security (600/700)
  • Audit Logging - Complete certificate lifecycle tracking with timeline view
  • Environment Variables - Secure credential management
  • Rate Limit Handling - Let's Encrypt rate limit awareness
  • Log Sanitizer - Automatically redacts sensitive parameters, private keys, and API tokens from application logs

User Interface

  • Command Palette - Cmd+K / Ctrl+K quick search and navigation
  • Keyboard Shortcuts - Power-user shortcuts for navigation and common actions
  • Dark Mode - System-aware dark/light theme toggle
  • Mobile-Friendly - Responsive layout with bottom tab bar on small screens
  • Activity Timeline - Chronological view of all certificate and system events

Developer Experience

  • One-URL Downloads - Simple certificate retrieval for automation (/{domain}/tls)
  • Individual Component Downloads - Fetch cert, key, chain, or fullchain separately
  • Multiple Output Formats - PEM, ZIP, individual files
  • SDK Examples - Python, Bash, Ansible, Terraform examples
  • Webhook Support - Certificate lifecycle notifications with HMAC verification
  • Deploy Hook API - Configure and test post-issuance hooks via REST API
  • Backup API - Programmatic backup creation and restoration
  • Swagger & ReDoc - Interactive API documentation at /docs/ and /redoc/
  • Model Context Protocol (MCP) Server - Built-in Node.js MCP server providing tools for agentic AI assistants to manage certificates and run diagnostics

Supported DNS Providers

CertMate supports a wide range of DNS providers through Let's Encrypt DNS-01 challenge via individual certbot plugins that provide reliable, well-tested DNS challenge support. The complete list is in the table below. 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
EfficientIP SOLIDserver Host, API Credentials Yes Enterprise DDI / Smart Architecture Stable
RFC2136 Nameserver, TSIG Key/Secret Yes Standard DNS update protocol Stable
Linode (Akamai Connected Cloud) API Key Single Cloud hosting Stable
Akamai Edge DNS EdgeGrid (.edgerc) credentials Single Enterprise managed DNS 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 (legacy DNS) API Token Single European cloud hosting Stable
Hetzner Cloud API Token Single Hetzner Cloud DNS (hcloud) 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
ArvanCloud API Key Single Iranian cloud provider Stable
Infomaniak API Token Single Swiss ISP & cloud provider Stable
ACME-DNS JSON Config Single Generic ACME-DNS server Stable
Scaleway API Token (secret key) Single European cloud (EU-sovereign) Stable
deSEC API Token Single Free, non-profit DNSSEC DNS Stable
DuckDNS Token Single Free dynamic DNS Stable
Custom Script User-provided hook scripts Single Any provider via custom hooks Stable

Provider Categories

  • 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, Akamai Edge DNS, Vultr, Hetzner
  • Enterprise DNS: Cloudflare, DNS Made Easy, NS1, PowerDNS, EfficientIP SOLIDserver
  • 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)

Multi-Account Benefits

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 Guide for provider-specific configuration. Step-by-Step Installation: See Installation Guide for complete setup guide. Multi-Account Examples: See DNS Providers Guide for enterprise configuration examples.

Quick Start with Docker

Get CertMate running in under 5 minutes with Docker Compose:

Prerequisites

  • Docker 20.10+
  • Docker Compose 2.0+
  • Domain with DNS managed by supported provider

1. Clone and Setup

# Clone the repository
git clone https://github.com/fabriziosalmi/certmate.git
cd certmate

# Copy environment template
cp .env.example .env

2. Configure Environment

Edit .env file with your credentials:

# Recommended for any network-exposed deployment: API Security.
# Auto-generated if unset, but a not-yet-onboarded instance serves the
# first-run setup bypass to anyone who can reach it — set this (or bind to
# localhost) before exposing CertMate. When set, the first-run screen asks
# you to paste this same token once to create the initial admin.
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
PORT=8000
# Note: there is no HOST variable. The container binds 0.0.0.0 in its own
# namespace — publish it as 127.0.0.1:8000:8000 to reach it on loopback only,
# and front it with a reverse proxy for external access.

Storage Backends: By default, certificates are stored locally. For enterprise deployments, you can configure Azure Key Vault, AWS Secrets Manager, HashiCorp Vault, Infisical, or any S3-compatible object storage via the web interface after startup. See Storage Backends for details.

Backup Best Practices: CertMate includes a unified backup system that creates atomic snapshots of both settings and certificates. After setup, create your first backup from Settings → Backup Management.

3. Deploy

# Start all services
docker-compose up -d

# Check status
docker-compose ps

# View logs
docker-compose logs -f certmate

4. Access 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

5. Create Your First Certificate

Using the Web Interface:

  1. Navigate to http://localhost:8000
  2. Go to Settings and configure your DNS provider
  3. Add your domain (e.g. example.com)
  4. 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"}'

Installation Methods

Choose the installation method that best fits your environment:

Docker (Recommended)

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 (bound to localhost; put it behind a
# reverse proxy and enable authentication before exposing it externally)
docker run --platform linux/arm64 -d --name certmate --env-file .env -p 127.0.0.1:8000:8000 fabriziosalmi/certmate:latest

Multi-Platform Guide: See Docker Guide for comprehensive multi-architecture setup instructions.

Python Virtual Environment

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

Kubernetes

For container orchestration and high availability deployments.

# Example Kubernetes deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: certmate
spec:
  replicas: 1
  selector:
    matchLabels:
      app: certmate
  template:
    metadata:
      labels:
        app: certmate
    spec:
      containers:
        - name: certmate
          image: certmate:latest
          ports:
            - containerPort: 8000
          resources:
            requests:
              cpu: 250m
              memory: 512Mi
            limits:
              cpu: "1"
              memory: 1536Mi
          env:
            - name: API_BEARER_TOKEN
              valueFrom:
                secretKeyRef:
                  name: certmate-secrets
                  key: api-token
            - name: CERTMATE_CERT_INFO_CACHE_TTL
              value: "60"
          volumeMounts:
            - name: certificates
              mountPath: /app/certificates
      volumes:
        - name: certificates
          persistentVolumeClaim:
            claimName: certmate-certificates

For production sizing, OOM troubleshooting, and a kubectl patch example, see Kubernetes Production Notes.

System Package Installation

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 Guide for complete setup guides for each method.

Service Setup

For production deployments, CertMate should run as a system service. This section provides comprehensive instructions for setting up CertMate with systemd on Linux distributions.

Prerequisites

  • Linux system with systemd
  • Python 3.9 or higher
  • Root/sudo access

1. Create Dedicated System User

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

2. Prepare Application Directory

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

3. Configure Environment Variables

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 the port (the bind address is not configurable here —
# publish the container on 127.0.0.1 if you want loopback only)
PORT=8000
EOF

# Set proper permissions
sudo chown certmate:certmate /opt/certmate/.env
sudo chmod 600 /opt/certmate/.env

4. Install systemd Service

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

5. Verify Service Status

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

6. Service Management Commands

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

7. File Permissions

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/*

Security Notes

  • 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 the certmate user
  • Certificates: Generated certificates are stored in /opt/certmate/certificates with restricted access

Troubleshooting Service Setup

If the service fails to start:

  1. Check service status: sudo systemctl status certmate
  2. View logs: sudo journalctl -u certmate --lines=100
  3. Verify permissions: Ensure the certmate user can read all necessary files
  4. Test manually: sudo -u certmate /opt/certmate/venv/bin/python /opt/certmate/app.py
  5. Check dependencies: sudo -u certmate /opt/certmate/venv/bin/python validate_dependencies.py

For more detailed installation instructions, see the Installation Guide.

Single Sign-On (OIDC/SSO)

CertMate supports authenticating users against an external OpenID Connect provider (Keycloak, Authentik, Okta, Google Workspace, Microsoft Entra, ...) using the Authorization Code + PKCE flow. SSO is additive: local username/password login and API keys keep working alongside it.

Configuring an IdP

  1. Open the CertMate UI as an admin → Settings → SSO.

  2. Set the Issuer URL to the IdP's base URL (the path before /.well-known/openid-configuration). For example:

    • Keycloak: https://idp.example.com/realms/main
    • Authentik: https://idp.example.com/application/o/certmate/
    • Google: https://accounts.google.com
  3. Fill in the Client ID and Client Secret issued by the IdP for the CertMate application.

  4. In the IdP, register CertMate's callback URL as a valid redirect URI:

    https://your-certmate.example.com/api/auth/oidc/callback
    
  5. Pick the claim names your IdP uses for username (preferred_username by default), email (email), and role (groups). Add Role mappings to translate IdP group/role claim values to CertMate roles — first match wins. Anything that doesn't match falls back to the configured Default role (viewer recommended).

  6. Toggle Enable OIDC/SSO on and save. Visit /login in a new browser session to see the Sign in with button.

Role mapping example

For a Keycloak realm that exposes a groups claim, the configuration block in settings.json looks like:

"oidc": {
  "enabled": true,
  "provider_name": "Keycloak",
  "issuer_url": "https://idp.example.com/realms/main",
  "client_id": "certmate",
  "client_secret": "********",
  "scopes": ["openid", "email", "profile", "groups"],
  "role_claim": "groups",
  "role_mappings": [
    { "claim_value": "certmate-admins",    "role": "admin" },
    { "claim_value": "certmate-operators", "role": "operator" }
  ],
  "default_role": "viewer",
  "auto_create_users": true,
  "link_by_email": true,
  "sync_role_on_login": true
}

Provisioning and linking

  • Just-in-time provisioning (auto_create_users) creates a CertMate user row on first login. The row has an empty password hash so JIT-provisioned SSO accounts cannot fall back to local login.
  • Email linking (link_by_email) detects collisions with existing local users and merges identities — the user keeps their existing role and their existing password hash, so a local-then-linked account can still log in either way during a rollout. Disable link_by_email if you want JIT-only provisioning with no local-password fallback.
  • Subject (sub + iss) lookup always wins over email matching, so an already-linked SSO user is never accidentally re-merged when their IdP email changes.
  • Role sync (sync_role_on_login, default true) re-derives the role from the current claims on every login, so removing someone from an admin group in the IdP demotes them in CertMate too. Set it to false when the IdP only authenticates and roles are managed inside CertMate — an admin promoting someone by hand then survives their next login.
  • A disabled CertMate user is refused at SSO login exactly as at local login: disabling an account locks it out regardless of how it authenticates.

Security

  • PKCE (S256) is enforced for every flow regardless of client type.
  • The id_token's signature, audience, issuer, expiry and nonce are validated server-side by Authlib using the IdP's published JWKS.
  • client_secret is masked (********) in every GET response and round-tripped safely through the Settings UI.
  • The oidc settings block is on the bulk-POST reject list — only the dedicated /api/auth/oidc/settings endpoint can mutate it, with full audit.
  • Failed callbacks count against the same per-IP rate limit as local login.

API Usage

CertMate provides a comprehensive REST API for programmatic certificate management. All endpoints require Bearer token authentication.

Authentication

Include the Authorization header in all API requests:

Authorization: Bearer your_api_token_here

Core Endpoints

Health & Status

# 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

Settings Management

# 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": "admin@example.com",
 "auto_renew": true
}

Certificate Management

# 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 SAN certificate (multiple domains)
POST /api/certificates/create
Authorization: Bearer your_token_here
Content-Type: application/json

{
 "domain": "example.com",
 "san_domains": ["www.example.com", "mail.example.com", "api.example.com"],
 "dns_provider": "cloudflare"
}
# This creates a single certificate covering all specified domains.
# The primary domain is "example.com" and san_domains are additional 
# Subject Alternative Names included in the certificate.
# Note: All domains must use the same DNS provider for validation.

# 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"
}

# Create certificate with DNS alias via CNAME delegation
POST /api/certificates/create
Authorization: Bearer your_token_here
Content-Type: application/json

{
 "domain": "example.com",
 "dns_provider": "cloudflare",
 "domain_alias": "validation.example.org"
}
# DNS alias validation works via CNAME delegation. Before issuing, create
# a CNAME record in your DNS zone:
#
#   _acme-challenge.example.com  CNAME  _acme-challenge.validation.example.org
#
# CertMate creates the TXT record on the provider-managed alias name, and
# Let's Encrypt follows the CNAME chain during the DNS-01 challenge.
# Alias mode is supported for CertMate's first-class DNS providers; generic
# fallback providers are rejected until a dedicated adapter exists.
# This is useful when:
# - The primary domain's DNS does not support an API
# - You want to centralize ACME validations on a dedicated domain
# - There are DNS restrictions on the primary zone

# Renew certificate
POST /api/certificates/example.com/renew
Authorization: Bearer your_token_here

# Download certificate bundle as JSON
GET /api/certificates/example.com/download?format=json
Authorization: Bearer your_token_here

# Check certificate deployment status
GET /api/certificates/example.com/deployment-status
Authorization: Bearer your_token_here

# Scan filesystem for orphan ("zombie") certificates no longer tracked by Certbot
POST /api/certificates/zombies/scan
Authorization: Bearer your_token_here

Multi-Account Management

# Add multiple accounts for a provider
POST /api/dns/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/dns/cloudflare/accounts
Authorization: Bearer your_token_here

# Set default account for a provider — there is no dedicated endpoint:
# "set_as_default" travels with the account payload.
PUT /api/dns/cloudflare/accounts/production
Authorization: Bearer your_token_here
Content-Type: application/json

{
 "set_as_default": true
}

# Update account configuration
PUT /api/dns/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"
 }
}

Storage Backend Management

# 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": "..."
 }
}

Backup Management

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

Automation-Friendly Download URL

Certificate downloads 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 certificate
  • chain.pem - Intermediate certificate chain
  • fullchain.pem - Full certificate chain (cert + chain)
  • privkey.pem - Private key

Integration Examples

cURL Download

curl -H "Authorization: Bearer your_token_here" \
 -o example.com-tls.json \
 https://your-certmate-server.com/api/certificates/example.com/download?format=json

Python SDK Example

import requests
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):
 """Download certificate bundle as JSON for domain"""
 url = f"{self.base_url}/api/certificates/{domain}/download?format=json"
 
 response = requests.get(url, headers=self.headers)
 response.raise_for_status()
 return response.json()
 
 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")

# List and download certificates
certs = client.list_certificates()
bundle = client.download_certificate("api.company.com")
Path("/etc/ssl/certs/api").mkdir(parents=True, exist_ok=True)
for name, key in {
    "cert.pem": "cert_pem",
    "chain.pem": "chain_pem",
    "fullchain.pem": "fullchain_pem",
    "privkey.pem": "private_key_pem",
}.items():
    Path("/etc/ssl/certs/api", name).write_text(bundle[key])

The same JSON response shape can be consumed directly by Ansible's uri module or Salt's HTTP helpers without unpacking an archive.

Infrastructure as Code Examples

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.json" \
 "$CERTMATE_URL/api/certificates/$DOMAIN/download?format=json"; 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"
 jq -r '.cert_pem' "${DOMAIN}-tls.json" > "$CERT_DIR/cert.pem"
 jq -r '.chain_pem' "${DOMAIN}-tls.json" > "$CERT_DIR/chain.pem"
 jq -r '.fullchain_pem' "${DOMAIN}-tls.json" > "$CERT_DIR/fullchain.pem"
 jq -r '.private_key_pem' "${DOMAIN}-tls.json" > "$CERT_DIR/privkey.pem"
 
 # 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.json"
}

# 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/dns/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 bundle as JSON
 uri:
 url: "{{ certmate_url }}/api/certificates/{{ item }}/download?format=json"
 headers:
 Authorization: "Bearer {{ certmate_token }}"
 return_content: yes
 register: cert_bundle
 
 - name: Write certificate files
 copy:
 dest: "/etc/ssl/certs/{{ item.0.item }}/{{ item.1.name }}"
 content: "{{ item.0.json[item.1.key] }}"
 owner: root
 group: ssl-cert
 mode: "{{ item.1.mode }}"
 loop: "{{ cert_bundle.results | product(cert_files) | list }}"
 vars:
 cert_files:
 - { name: "cert.pem", key: "cert_pem", mode: "0644" }
 - { name: "chain.pem", key: "chain_pem", mode: "0644" }
 - { name: "fullchain.pem", key: "fullchain_pem", mode: "0644" }
 - { name: "privkey.pem", key: "private_key_pem", mode: "0600" }
 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 }}/api/certificates/{{ item.name }}/download?format=json"
 method: GET
 headers:
 Authorization: "Bearer {{ api_token }}"
 return_content: yes
 register: cert_bundle
 loop: "{{ certificate_domains }}"
 
 - name: Write certificates
 copy:
 dest: "/etc/ssl/certs/{{ item.0.item.name }}/{{ item.1.name }}"
 content: "{{ item.0.json[item.1.key] }}"
 owner: root
 group: ssl-cert
 mode: "{{ item.1.mode }}"
 loop: "{{ cert_bundle.results | product(cert_files) | list }}"
 vars:
 cert_files:
 - { name: "cert.pem", key: "cert_pem", mode: "0644" }
 - { name: "chain.pem", key: "chain_pem", mode: "0644" }
 - { name: "fullchain.pem", key: "fullchain_pem", mode: "0644" }
 - { name: "privkey.pem", key: "private_key_pem", mode: "0600" }
 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.json"
 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([]) }}"

Configuration Guide

Environment Variables

Variable Required Default Description
API_BEARER_TOKEN auto-generated Bearer token for API authentication
API_BEARER_TOKEN_FILE - Path to a file containing the API bearer token (takes precedence over API_BEARER_TOKEN)
SECRET_KEY auto-generated Flask secret key for sessions
SECRET_KEY_FILE - Path to a file containing the Flask secret key (takes precedence over SECRET_KEY)
PORT 8000 Server port (honoured by the container entrypoint)
FLASK_ENV production Flask environment. production refuses --debug
CERTMATE_LOG_FILE - Also write logs to this path. Off by default: the container logs to stdout, which is what docker logs and log shippers expect. Set it (e.g. /app/logs/certmate.log) to keep a file on the mounted volume — it is what the web UI's log stream reads
CERTMATE_LOG_MAX_BYTES 10485760 Rotate the log file at this size (10 MB). File logging is always rotated — there is no way to configure an unbounded one
CERTMATE_LOG_BACKUP_COUNT 5 How many rotated files to keep (~60 MB ceiling with the default size)
CERTMATE_AUDIT_LOG_MAX_BYTES 10485760 Rotate the human-readable audit log (logs/audit/certificate_audit.log) at this size. 0 disables rotation. Does not apply to the tamper-evident hash chain in data/audit/, which is never rotated
CERTMATE_AUDIT_LOG_BACKUP_COUNT 5 How many rotated audit logs to keep. Note the Activity page tails only the active file, so it shows fewer entries immediately after a roll
CERTMATE_LOG_LEVEL INFO DEBUG / INFO / WARNING / ERROR
CERTMATE_LOG_JSON true JSON log lines (set false for human-readable)

Bind address is not an environment variable. The container always binds 0.0.0.0 inside its own network namespace; to expose it only on loopback, publish it that way — -p 127.0.0.1:8000:8000. Running app.py directly (development only) takes --host / --port / --debug as CLI flags. HOST and FLASK_DEBUG are read by nothing, and setting them has never had any effect (#429).

DNS Provider Configuration

Cloudflare Setup

  1. Go to Cloudflare API Tokens
  2. Click "Create Token" → "Custom token"
  3. Set permissions:
  • Zone: DNS:Edit + Zone:Read
  • Zone Resources: Include specific zones or all zones
  1. Copy the generated token
# Environment variable
CLOUDFLARE_TOKEN=your_cloudflare_api_token_here

AWS Route53 Setup

  1. Create IAM user with Route53 permissions
  2. 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

Azure DNS Setup

  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

Google Cloud DNS Setup

  1. Create service account with DNS Administrator role
  2. Download JSON key file
# Environment variables
GOOGLE_PROJECT_ID=your_project_id
GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json

PowerDNS Setup

# Environment variables
POWERDNS_API_URL=https://your-powerdns-server:8081
POWERDNS_API_KEY=your_api_key

Certificate Storage Configuration

CertMate 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

Local Filesystem (Default)

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: 600 for private keys, 644 for 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

Azure Key Vault

Store certificates securely in Azure Key Vault for enterprise-grade secret management:

Required Dependencies:

pip install -r requirements-azure-storage.txt

Configuration:

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

This HTML preview is truncated for page performance. The canonical Markdown file contains the complete snapshot.

MARKDOWN METRICS
6411words
335headings
74links
87code blocks
MDRSS ASSESSMENT
Evidence46/100high confidence
Why MDRSS assigned this score
  • Production catalog audit 2026-08-04
  • Taxonomy classified from title, annotation, source and Markdown signals
  • Agent usefulness evaluated from structure, procedures, examples, evidence and retrieval value
Evidence (1)

Discussion 0

Sign in to join the discussion.