This project is production ready. It is intended for production deployments with the hardening and operational guidance documented in this repository. Review the deployment, security, backup, and environment configuration sections before rollout.
Web-based PKI management for root, intermediate, and end-entity certificates (server, client, email, OCSP responder) with policy enforcement, RBAC, CRL generation, audit logging, and multi-organization isolation.
This application provides a complete, self-hosted certificate authority (CA) management platform for organizations managing their own PKI infrastructure. It enables teams to create, issue, renew, and revoke X.509 certificates across multiple organizations with role-based access control, encrypted storage, and cryptographic audit trails.
- Certificate Lifecycle Management β Create root CAs, intermediate CAs, and end-entity certificates (TLS servers, client auth, S/MIME, OCSP responders) with configurable validity periods and cryptographic algorithms.
- Multi-Organization Isolation β Manage separate certificate hierarchies for different organizations in a single deployment with complete data isolation.
- Role-Based Access Control β Three roles (admin, manager, user) with granular permissions for creation, renewal, revocation, and downloads.
- Certificate Revocation Lists β Automatic CRL generation and distribution when certificates are revoked; public endpoints for external validators.
- Encrypted Storage β Certificate private keys and sensitive data encrypted at rest; decryption only on demand.
- Audit Logging β Track all certificate operations (creation, renewal, revocation, downloads) with user attribution, role, client IP address, and timestamps. The Recent Operations panel on the dashboard displays IP alongside each event.
- Backup & Restore β Export and restore full backups (database + certificate storage) from the Toolbox without server restart; atomic swaps ensure consistency; cross-platform restore supported (e.g., Windows β Linux) with matching encryption keys.
- Policy-Driven Defaults β Enforce certificate constraints (validity periods, key algorithms, SAN validation, and root password minimum length) via centralized policy configuration.
- Internal TLS Infrastructure β Issue and manage TLS certificates for internal services, microservices, and APIs without external CA dependencies.
- Client Certificate Authentication β Deploy mTLS for secure service-to-service communication or employee device authentication.
- Email & Document Signing β Issue S/MIME certificates for encrypted email and digitally-signed document workflows.
- IoT & Embedded Systems β Manage device certificates for IoT deployments with automated renewal and revocation.
- Development & Testing β Create test certificates on-demand for development environments without managing external CA integrations.
You can run the application directly from the published container image without building it locally.
For Docker deployment details, example Compose files, and container-specific setup, refer to /docker.
mkdir pki
cd pki
# copy docker/docker-compose.yml from this repository and adapt it if needed
nano docker-compose.yml
# create .env from this repository's .env.example and edit it
# or generate it from the project root with:
# python utils/generate_env.py --env production --output .env
# see utils/README.md for details
docker compose up -dThe default Compose image is ghcr.io/entr0-pi/pki-certificates:latest. See docker/README.md for Docker deployment details and utils/README.md for .env generation.
- Python 3.11+
uv- Node.js 18+ and
npmfor rebuilding frontend CSS
uv venv
# activate the virtual environment
# PowerShell: .venv\Scripts\Activate.ps1
# bash/zsh: source .venv/bin/activate
uv pip install -r requirements.txt
npm installIf you also plan to run tests locally:
uv pip install -r tests/requirements-dev.txtGenerate a .env file with all secrets auto-filled:
python utils/generate_env.py --env devThis creates .env from .env.example with cryptographically-generated values for all required secrets (PKI_ENCRYPTION_KEY, PKI_ENCRYPTION_SALT, PKI_API_KEY_*, PKI_JWT_SECRET).
Review and adjust key settings as needed:
PKI_BASE_URLβ used in generated CRL distribution URLsPKI_HOSTβ bind address (default0.0.0.0)PKI_PORTβ FastAPI port (default8000)PKI_COOKIE_SECUREβ set tofalsefor local HTTP development,truefor HTTPS
For a full list of all environment variables and options (including --env production, --output, --dry-run), see utils/README.md.
Encryption keys can be rotated later without losing access to existing encrypted artifacts by using python utils/encryption_manager.py prepare and python utils/encryption_manager.py rotate.
See utils/README.md for the full key rotation workflow and cautions before promoting the new .env.
python scripts/init_db.pynpm run build:cssThis compiles frontend/static/src/input.css into frontend/static/vendor/bundle.css.
python backend/app.pyOr use the provided launcher script:
# Linux/macOS
./scripts/start_webapp.sh
# Windows (combines DB init + app in one step)
scripts\start_webapp.batOpen http://localhost:8000 and sign in with one of the configured API keys.
The frontend is server-rendered with Jinja templates and a compiled Tailwind CSS bundle.
npm installinstalls Tailwind CSS and DaisyUI locallynpm run build:cssrebuilds the production CSS bundlenpm run watch:csswatches templates and regenerates CSS during UI work
If you only run the app and do not change templates or frontend dependencies, the committed CSS bundle is sufficient.
backend/
app.py FastAPI entrypoint and route handlers
auth.py Session auth and RBAC helpers
db.py Database access layer (schema v3)
path_config.py Path resolution for data/db locations
file_crypto.py AES-256-GCM encryption/decryption for all certificate artifacts
cert_crypto.py Shared certificate utilities
root_ca_create_crypto.py Root CA generation
intermediate_ca_create_crypto.py Intermediate CA generation
end_entity_create_crypto.py End-entity certificate generation (server, client, email, OCSP)
revoke_cert_crypto.py Revocation and CRL generation
root_ca_validate.py Certificate/CSR/key loading and signature verification helpers
folder.py PKI directory layout management and workspace initialization
helpers.py Shared utilities (dirs, permissions, password gen, OpenSSL wrappers)
create_cert.py CLI entrypoint for certificate creation
logging_config.py Centralized ISO 8601 logging configuration
uvicorn_log_config.py Reference documentation for Uvicorn logging config structure
config/
policy.json Certificate policy, defaults, and root password length requirements
rbac.json Route-to-role authorization map (single source of truth for all routes)
openssl/
config.txt OpenSSL config assets (for reference only, not used)
schema/
pki_schema.sql SQL schema
frontend/
templates/ Jinja HTML templates
static/
src/input.css Tailwind source file
vendor/bundle.css Built CSS served by FastAPI
*.js Small UI behaviors
docs/
*.md Operational, security, API, and frontend docs
tests/
check_routes.py Route enforcement smoke test (RBAC verification on live app)
conftest.py Pytest fixtures for in-process testing
requirements-dev.txt Test-only Python dependencies
test_*.py Backend and UI workflow coverage
scripts/
init_db.py Database initialization helper
utils/
generate_env.py Creates `.env` from `.env.example` and generates secret values
encryption_manager.py CLI tool to decrypt or rotate encrypted PKI artifacts
.env.example Environment file (example)
package.json npm scripts for frontend asset builds
tailwind.config.js Tailwind/DaisyUI configuration
requirements.txt Python runtime dependencies
All HTTP route permissions are centrally configured in backend/config/rbac.json without requiring Python code changes:
{
"GET /auth/login": ["public"], // Public endpoint (no auth required)
"GET /toolbox": ["admin"], // Admin only
"GET /organizations": ["admin", "manager", "user"], // Multiple roles allowed
"GET /static/*": ["public"] // Prefix pattern (wildcard)
}Key Features:
- Use
["public"]sentinel for unauthenticated routes - List allowed roles to restrict access:
["admin"],["admin", "manager"], etc. - Routes without explicit role list allow any authenticated user
- Supports path parameters:
/organizations/{org_id}/... - Supports prefix patterns:
/static/*,/api/* - Comprehensive validation at startup ensures all routes are configured
- Invalid config fails application launch with clear error messages
- No Python code changes needed to adjust permissions
For details, see docs/ROUTES.md - Access Control Configuration.
| Document | Purpose |
|---|---|
| docs/ROUTES.md | HTTP routes, auth requirements, and RBAC matrix |
| docs/DB_SCHEMA.md | SQLite schema and table relationships |
| docs/SECURITY.md | Deployment hardening, secret handling, and operational security notes |
| docs/FRONTEND.md | Tailwind/DaisyUI frontend build workflow and asset pipeline |
| docs/CRON_JOBS.md | Scheduling guidance for consistency checks and maintenance tasks |
| docs/TEST_PLAN.md | Test scope and verification strategy |
| docs/DATA_TREE.md | Project skeleton/reference layout |
pytest -vInstall optional test dependencies from tests/requirements-dev.txt if your environment does not already have them.
Test RBAC enforcement on a live running app:
# Start the app
python backend/app.py
# In another terminal, test all routes
python tests/check_routes.py --base-url http://localhost:8000
# Run with verbose output (show all routes)
python tests/check_routes.py --base-url http://localhost:8000 --verbose
# Test specific routes
python tests/check_routes.py --base-url http://localhost:8000 --filter toolbox --verbose
# Test with specific fixture IDs
python tests/check_routes.py --base-url http://localhost:8000 --org-id 1 --cert-id 3 --issuer-name root-caThe check_routes.py tool verifies that every route in rbac.json correctly enforces role-based access control by making real HTTP requests to the app with different authentication levels (unauthenticated, admin, manager, user). It tests all 32 routes across 4 identities (128 checks total). See docs/TEST_PLAN.md for details.
- Keep
.env, API keys, JWT secrets, and encryption material out of version control. - Use HTTPS in production and set
PKI_COOKIE_SECURE=true. - Back up both the database and encrypted certificate storage regularly using the Backup and Restore features in the Toolbox (admin only). You can download backups and restore them at any time without requiring a server restart. Alternatively, manually archive the
database/anddata/directories. - Review docs/SECURITY.md before deploying outside local development.
Last update: 2026-03-22 β All routes centralized in rbac.json with startup validation for public and private routes; ["public"] sentinel for unauthenticated endpoints; middleware configuration-driven with zero hardcoded exemptions; operators adjust access via config without code changes
