Skip to content

squadramunter/CertDax

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

36 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CertDax — SSL Certificate Management Dashboard

A complete SSL certificate management platform with web dashboard, ACME integration, CA signing, deploy agents, and automated renewal.

CertDax Dashboard

Features

  • Dashboard — Overview of all certificates with status, expiry timeline, and statistics
  • ACME Protocol — Request certificates from Let's Encrypt and other ACME-compatible CAs (DNS-01 and HTTP-01 challenges)
  • Self-Signed & CA Signing — Generate self-signed certificates, create internal CAs, and sign certificates with your own CA
  • Auto-Renewal — Automatic certificate renewal with configurable per-certificate thresholds
  • Deploy Agents — Lightweight Go agents for automated certificate deployment to servers (amd64, arm64, arm, 386)
  • Agent Groups — Group agents and share certificates across multiple servers
  • DNS Providers — Cloudflare, TransIP, Hetzner, DigitalOcean, Vultr, OVH, AWS Route 53, Google Cloud DNS, and manual validation
  • Multi-Tenant — Group-based resource isolation with inter-group sharing
  • SSO / OIDC — Single sign-on via Authentik, Keycloak, Entra ID, or any OpenID Connect provider with auto-provisioning and admin group mapping
  • Email Notifications — Customizable HTML email templates for certificate events (expiry, renewal, creation, deletion)
  • API — Full REST API with API key authentication for scripts, CI/CD, and automation
  • Password Reset — Secure token-based password recovery via email
  • Security — Private keys and credentials encrypted at rest, JWT authentication, hashed agent tokens, CORS configuration, Swagger disabled in production

Tech Stack

Layer Technology
Frontend React, TypeScript, Tailwind CSS, Recharts
Backend Python, FastAPI, SQLAlchemy, cryptography
Database PostgreSQL (production) / SQLite (development)
Agent Go (statically linked, zero dependencies)
Infrastructure Docker, Docker Compose, Nginx

Quick Start (Docker Compose)

# 1. Clone and configure
cp .env.example .env

# 2. Generate required secrets
python3 -c "import secrets; print('SECRET_KEY=' + secrets.token_urlsafe(64))"
python3 -c "import base64, os; print('ENCRYPTION_KEY=' + base64.urlsafe_b64encode(os.urandom(32)).decode())"
python3 -c "import secrets; print('DB_PASSWORD=' + secrets.token_urlsafe(32))"

# 3. Edit .env with the generated values and your domain
nano .env

# 4. Start the application
docker compose up -d

# 5. Open your browser and create the first admin account

Development Setup

Backend:

cd backend
python3 -m venv venv
source venv/bin/activate
pip3 install -r requirements.txt

# Create a .env for development
cat > .env << EOF
SECRET_KEY=$(python -c "import secrets; print(secrets.token_urlsafe(64))")
CORS_ORIGINS=http://localhost:5173
FRONTEND_URL=http://localhost:5173
DEBUG=true
EOF

uvicorn app.main:app --reload --port 8000

Frontend:

cd frontend
npm install
npm run dev

Open http://localhost:5173 in your browser.

First Use

  1. Open the application and create the first admin account
  2. Go to Settings → configure SMTP for email notifications (optional)
  3. Go to Settings → configure OIDC/SSO for single sign-on (optional)
  4. Go to Providers and add a DNS provider (e.g. Cloudflare) and/or Certificate Authority
  5. Go to CertificatesNew certificate and request your first ACME certificate
  6. Go to Self-Signed to generate internal certificates or create a CA
  7. (Optional) Set up Agents and install the deploy agent on your servers
  8. (Optional) Go to API to create API keys for scripting and automation

Environment Variables

Variable Required Default Description
SECRET_KEY Yes JWT signing key. Must be identical across all replicas
ENCRYPTION_KEY Recommended Auto-generated Encryption key for private keys and secrets. Must be identical across replicas
DB_PASSWORD Docker only PostgreSQL password (Docker Compose sets up the database automatically)
DATABASE_URL No sqlite:///./data/certdax.db Database connection string. Use PostgreSQL for production
ACME_CONTACT_EMAIL No admin@example.com Contact email for ACME certificate requests
JWT_EXPIRY_MINUTES No 1440 JWT token lifetime in minutes
RENEWAL_CHECK_HOURS No 12 How often to check for certificates needing renewal
RENEWAL_THRESHOLD_DAYS No 30 Default days before expiry to trigger auto-renewal
CORS_ORIGINS Yes Comma-separated list of allowed frontend origins
API_BASE_URL No Auto-detected Public backend URL (used in agent install scripts)
FRONTEND_URL Yes Public frontend URL (used in password reset emails)
AGENT_BINARIES_DIR No agent-dist Directory containing agent binaries
DEBUG No false Enable Swagger/OpenAPI docs at /docs and /redoc

Deploy Agent

The deploy agent is a statically compiled Go binary that runs on any Linux distribution without dependencies. Available for amd64, arm64, arm and 386 architectures.

Quick Install

# On the target server (as root)
cd agent/
sudo ./install.sh

This automatically detects the architecture, copies the binary to /usr/local/bin/certdax-agent, creates the config directory and installs the systemd service.

Manual Installation

# Choose the correct binary for your architecture
# Options: certdax-agent-linux-amd64, -arm64, -arm, -386
sudo install -m 755 dist/certdax-agent-linux-amd64 /usr/local/bin/certdax-agent

# Create config directory and configure
sudo mkdir -p /etc/certdax
sudo cp config.example.yaml /etc/certdax/config.yaml
sudo chmod 600 /etc/certdax/config.yaml
sudo nano /etc/certdax/config.yaml

# Install systemd service
sudo cp certdax-agent.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now certdax-agent

Usage Without Config File

certdax-agent --api-url https://certdax.example.com --token YOUR_AGENT_TOKEN

# Or via environment variables
export CERTDAX_API_URL=https://certdax.example.com
export CERTDAX_AGENT_TOKEN=your-token
certdax-agent

Building From Source

cd agent/

# Build for all platforms
make all

# Or build for current platform only
make build

# Binaries are in dist/
ls -la dist/

DNS Provider Configuration

Cloudflare

{
  "api_token": "your-cloudflare-api-token"
}

Create an API token in Cloudflare with Zone:DNS:Edit permissions.

TransIP

{
  "login": "your-transip-login",
  "private_key": "-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----"
}

Generate a key pair in the TransIP control panel.

Hetzner

{
  "api_token": "your-hetzner-dns-api-token"
}

Create an API token in the Hetzner DNS Console.

DigitalOcean

{
  "api_token": "your-digitalocean-api-token"
}

Create a personal access token in the DigitalOcean control panel with read/write scope.

Vultr

{
  "api_key": "your-vultr-api-key"
}

Create an API key in the Vultr customer portal.

OVH

{
  "endpoint": "ovh-eu",
  "application_key": "your-app-key",
  "application_secret": "your-app-secret",
  "consumer_key": "your-consumer-key"
}

Generate credentials at https://api.ovh.com/createToken/.

AWS Route 53

{
  "access_key_id": "AKIAIOSFODNN7EXAMPLE",
  "secret_access_key": "your-secret-access-key",
  "region": "us-east-1"
}

Create an IAM user with route53:ChangeResourceRecordSets and route53:ListHostedZones permissions.

Google Cloud DNS

{
  "project_id": "your-gcp-project-id",
  "service_account_json": "{...}"
}

Create a service account with the DNS Administrator role and export the JSON key.

Manual

{}

With manual validation, DNS records are shown in the server logs.

Reverse Proxy

By default CertDax listens on port 80 (HTTP). Place a reverse proxy in front for SSL termination. The examples below assume CertDax runs on 127.0.0.1:80.

Nginx

server {
    listen 443 ssl http2;
    server_name certdax.example.com;

    ssl_certificate     /etc/ssl/certs/certdax.pem;
    ssl_certificate_key /etc/ssl/private/certdax.key;

    location / {
        proxy_pass http://127.0.0.1:80;
        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;
        proxy_read_timeout 300s;
        client_max_body_size 10m;
    }
}

server {
    listen 80;
    server_name certdax.example.com;
    return 301 https://$host$request_uri;
}

Apache2

Enable the required modules first:

sudo a2enmod proxy proxy_http ssl rewrite headers
sudo systemctl restart apache2
<VirtualHost *:443>
    ServerName certdax.example.com

    SSLEngine On
    SSLCertificateFile    /etc/ssl/certs/certdax.pem
    SSLCertificateKeyFile /etc/ssl/private/certdax.key

    ProxyPreserveHost On
    ProxyPass / http://127.0.0.1:80/
    ProxyPassReverse / http://127.0.0.1:80/

    RequestHeader set X-Forwarded-Proto "https"
    RequestHeader set X-Forwarded-Port "443"
</VirtualHost>

<VirtualHost *:80>
    ServerName certdax.example.com
    RewriteEngine On
    RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]
</VirtualHost>

HAProxy

frontend https_in
    bind *:443 ssl crt /etc/haproxy/certs/certdax.pem
    bind *:80
    http-request redirect scheme https unless { ssl_fc }

    default_backend certdax

backend certdax
    option httpchk GET /health
    http-request set-header X-Forwarded-Proto https if { ssl_fc }
    server certdax 127.0.0.1:80 check

Note: Set CORS_ORIGINS and FRONTEND_URL in your .env to the public URL (e.g. https://certdax.example.com).

API Endpoints

Endpoint Method Description
/api/auth/register POST Create first admin account
/api/auth/login POST Login
/api/certificates GET List ACME certificates
/api/certificates/request POST Request new ACME certificate
/api/certificates/{id}/renew POST Renew ACME certificate
/api/providers/cas GET List Certificate Authorities
/api/providers/dns GET/POST Manage DNS providers
/api/self-signed GET List self-signed certificates (filter: ?is_ca=true, ?search=)
/api/self-signed POST Create self-signed or CA-signed certificate
/api/self-signed/{id} GET Get certificate details (incl. PEM)
/api/self-signed/{id} DELETE Delete certificate (?force=true to force)
/api/self-signed/{id}/renew POST Renew certificate (?validity_days=365)
/api/self-signed/{id}/parsed GET Parsed X.509 certificate details
/api/self-signed/{id}/download/zip GET Download cert + key as ZIP
/api/self-signed/{id}/download/pem/{type} GET Download PEM (type: certificate, privatekey, combined, chain, ca)
/api/self-signed/{id}/download/pfx GET Download as PFX/PKCS#12
/api/agents GET/POST Manage deploy agents
/api/agent-groups GET/POST Manage agent groups
/api/agent/poll GET Agent: fetch pending deployments
/api/agent/heartbeat POST Agent: heartbeat

Self-Signed Certificate API Examples

Create a self-signed certificate:

curl -X POST https://certdax.example.com/api/self-signed \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "common_name": "myserver.local",
    "san_domains": ["*.myserver.local"],
    "organization": "MyCompany",
    "country": "NL",
    "key_type": "rsa",
    "key_size": 4096,
    "validity_days": 365
  }'

Create a CA certificate:

curl -X POST https://certdax.example.com/api/self-signed \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "common_name": "My Internal CA",
    "organization": "MyCompany",
    "country": "NL",
    "key_type": "rsa",
    "key_size": 4096,
    "validity_days": 3650,
    "is_ca": true
  }'

Sign a certificate with an existing CA:

curl -X POST https://certdax.example.com/api/self-signed \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "common_name": "app.internal",
    "san_domains": ["app.internal", "api.internal"],
    "organization": "MyCompany",
    "key_type": "rsa",
    "key_size": 4096,
    "validity_days": 365,
    "ca_id": 1
  }'

Note: Set ca_id to the ID of a certificate created with is_ca: true. The certificate will be signed by that CA instead of being self-signed. The CA chain is automatically included in ZIP and PFX downloads.

Scaling (Docker Swarm / Kubernetes)

CertDax supports horizontal scaling with multiple backend replicas. The following mechanisms ensure cluster safety:

  • Distributed locking — Scheduled tasks (renewal checks, expiry checks) use database-backed locks so only one instance executes them at a time
  • Atomic status transitions — Certificate processing uses atomic database updates to prevent race conditions between replicas
  • Stateless API — JWT authentication is stateless; any replica can serve any request
  • PostgreSQL required — SQLite only supports single-node; use PostgreSQL for multi-node

Requirements for multi-node

Setting Why
ENCRYPTION_KEY Must be identical across all replicas. Without it, each node generates its own key and encrypted data becomes unreadable across nodes
SECRET_KEY Must be identical across all replicas for JWT validation
DATABASE_URL Must point to a shared PostgreSQL instance
Agent binaries Built into the Docker image (backend/agent-dist/). Copy them from agent/dist/ before building

Docker Swarm example

# Build and push images
docker compose build
docker tag certdax-backend registry.example.com/certdax-backend:latest
docker tag certdax-frontend registry.example.com/certdax-frontend:latest
docker push registry.example.com/certdax-backend:latest
docker push registry.example.com/certdax-frontend:latest

# Deploy as a stack (scales backend replicas)
docker stack deploy -c docker-compose.yml certdax
docker service scale certdax_backend=3

Kubernetes

Use the Docker images with a standard deployment. Key points:

  • Store SECRET_KEY, ENCRYPTION_KEY, DB_PASSWORD in a K8s Secret
  • Use a Deployment with multiple replicas for the backend
  • Point DATABASE_URL to a managed PostgreSQL (e.g. CloudSQL, RDS, or an in-cluster instance)
  • Copy agent binaries into backend/agent-dist/ before building the image

Security

Mechanism Details
Private key encryption Fernet (AES-128-CBC + HMAC) at rest
Credential encryption DNS provider and OIDC secrets encrypted at rest
Password hashing bcrypt
Agent tokens SHA-256 hashed
API keys SHA-256 hashed, 25 keys per user limit
Authentication JWT tokens (configurable expiry) + API key fallback
CORS Configurable per environment
OpenAPI/Swagger Disabled in production
Container Non-root user for backend
Cluster safety Database-backed distributed locking for scheduled tasks

About

Certificate Management Dashboard with auto deploy agents

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors