Self-hosted web interface for Smallstep step-ca — manage your private PKI from a browser.
🇬🇧 English · 🇷🇺 Русский
A small-team-friendly web UI on top of
smallstep/step-ca. No SaaS, no telemetry, no vendor lock-in — runs entirely on your own server in three Docker containers.
Latest stable: v1.7.0
Highlights:
- restricted admin web console with predefined diagnostic commands, timeout, output limit and audit log
- password recovery UX cleanup: neutral result message, no account enumeration and cleared identifier field after submit
- password recovery with SMTP, expiring reset tokens, rate limiting and audit events
- admin audit hardening for user actions, key downloads, backups and settings changes
- responsive top navigation with dropdown groups for narrow windows
- TOTP 2FA with authenticator app enrollment, QR code and recovery codes
- login 2FA challenge after password verification
- polished main page, 2FA page and certificate list layout
- responsive certificate list for desktop, laptop and narrow browser windows
- 📋 Certificate management — issue, renew, revoke and import X.509 certificates
- 👥 Role-based access —
admin/manager/viewer - ⏱️ Temporary users — short-lived guest accounts with automatic expiration (new in v1.4.0)
- 📅 Custom date picker — site-themed, no native browser widget (new in v1.4.0)
- 🌍 Timezone-aware — configurable with the
TZenvironment variable - 🎨 4 themes — dark, light, blue, auto (follows OS)
- 🧭 Admin workspace — polished admin UI with matching dark, light and blue themes (new in v1.4.11)
- 🛡️ Built-in security — CSRF tokens, rate limiting, IP blocking, security log
- 🌐 Provisioner inspection — list and edit step-ca provisioners
- 💾 Backup export — admin UI and CLI backup bundles with manifest checksums (new in v1.4.9)
- 🔎 CA integrity checks — root/intermediate chain, provisioner claims, password sync and pinned step-ca image (new in v1.5.0)
- 🔬 Certificate details — SANs, fingerprints, key usage, cert/key pair and chain validation (new in v1.5.1)
- 🧩 Certificate templates — server, internal service, wildcard and client identity presets (new in v1.5.2)
- 🔔 Webhook notifications — test webhook, failed issue/renew alerts, login burst alerts and expiry watcher (new in v1.5.3)
- 🔐 TOTP 2FA — authenticator app enrollment, QR code login challenge and recovery codes (new in v1.6.0)
- 🔁 Password recovery — SMTP reset links with TTL, rate limit, audit trail and neutral public responses (updated in v1.6.3)
- 🖥️ Restricted admin console — allowlisted diagnostics inside the UI container with audit logging (new in v1.7.0)
git clone https://github.com/UncleFi1/step-ca-ui.git
cd step-ca-ui
sudo ./install.shThe installer can run in Russian or English and supports both clean installs and safe updates:
sudo ./install.sh --mode install --lang en
sudo ./install.sh --mode update --lang enUpdate mode creates a backup first, preserves .env and Docker volumes, then
runs docker compose up -d --build. It does not run docker compose down -v.
That's it. The installer:
- Detects your OS and installs Docker if needed
- Auto-detects your server IP (with confirmation)
- Generates strong passwords for everything
- Writes
.envandcredentials.txt(chmod 600) - Builds and starts the containers
- Prints the URL and admin password
The whole thing takes 2–4 minutes on a fresh VM.
| Minimum | Recommended | High-load | |
|---|---|---|---|
| CPU | 1 vCPU | 2 vCPU | 4+ vCPU |
| RAM | 1 GB | 2 GB | 4+ GB |
| Disk | 5 GB | 20 GB SSD | 50+ GB NVMe |
| Network | 10 Mbit/s | 100 Mbit/s | 1 Gbit/s |
| Users | up to 50 | up to 500 | 500+ |
| Certificates | up to 500 | up to 10k | 10k+ |
Software:
- Linux kernel 4.4+ (Ubuntu 20.04+, Debian 11+, CentOS Stream 9+, Rocky 9+, Alma 9+)
- Docker Engine 20.10+ with Compose plugin v2+
- Open ports:
443/tcp(HTTPS UI), optionally9000/tcp(step-ca API)
Untested but should work: macOS / Windows via Docker Desktop (development only).
Not supported: shared hosting without Docker, Raspberry Pi Zero (insufficient RAM).
| Layer | Technology |
|---|---|
| Backend | Go 1.22, chi router |
| Frontend | Server-rendered HTML + vanilla JS, no build step |
| Database | PostgreSQL 16 |
| CA | smallstep/step-ca |
| Deploy | Docker Compose |
| Container OS | Alpine 3.19 + tzdata |
┌────────────┐
Browser ─── HTTPS ───►│ step-ui │ Go web app, port 8443
│ (chi) │
└──┬─────┬───┘
│ │
SQL ◄──────┘ └──────► HTTPS API
│ │
┌──▼──┐ ┌▼──────────┐
│ pg │ │ step-ca │ port 9000
│ 16 │ │ (PKI) │
└─────┘ └───────────┘
step-ui exposes :443 → internally redirects to :8443
step-ca exposes :9000 → internal-only by default
| Role | View | Issue/Import | Revoke | Manage Users |
|---|---|---|---|---|
| viewer | ✅ | ❌ | ❌ | ❌ |
| manager | ✅ | ✅ | ❌ | ❌ |
| admin | ✅ | ✅ | ✅ | ✅ |
Temporary users can have any role; they're auto-blocked when expires_at passes (a goroutine checks every minute).
- ✅ CSRF protection — tokens on every form and server-side checks on POST routes
- ✅ Rate limiting — 5 failed login attempts → 15-minute IP block
- ✅ Security headers — CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, optional HSTS
- ✅ Session timeout — 8 hours, sliding
- ✅ Login audit log — every login attempt is recorded with IP and User-Agent
- ✅ Self-signed TLS — auto-generated on first boot, 10-year validity
- ✅ Password hashing — bcrypt for new/updated passwords, with transparent migration from legacy SHA-256 hashes on next successful login
🔒 Production tip: put step-ui behind a reverse proxy (Caddy/nginx) with a real TLS certificate, restrict access via VPN/Tailscale, and back up the
step-ca-datavolume regularly.
All configuration lives in .env. The installer creates this file for you, but you can edit it manually:
HOST_IP=192.168.1.100 # SAN in self-signed cert; step-ca DNS
UI_HTTPS_PORT=443 # external HTTPS port
PROVISIONER=admin # step-ca provisioner identifier
CA_PASSWORD=<generated> # step-ca provisioner password
STEP_CA_IMAGE=smallstep/step-ca:0.30.2 # pinned step-ca image
SECRET_KEY=<generated> # session/CSRF signing key
SESSION_SECURE=true # secure session cookie over HTTPS
ENABLE_HSTS=false # enable only when using a trusted TLS certificate
POSTGRES_PASSWORD=<generated> # database password
TZ=UTC # container timezone
STEPCA_DEFAULT_TLS_CERT_DURATION=8760h
STEPCA_MAX_TLS_CERT_DURATION=87600hAfter changing .env, recreate the containers:
sudo docker compose up -d --force-recreateHow do I change the HTTPS port from 443?
Edit docker-compose.yml:
services:
step-ui:
ports:
- "8443:8443" # was "443:8443"Then restart: sudo docker compose up -d --force-recreate step-ui.
How do I back up and restore the data?
Use the admin UI: Admin -> Backup -> Download backup bundle.
CLI export is also supported:
sudo ./install.sh --mode backup --lang enBackups include PostgreSQL, step-ca-data, Step-CA UI data/certs/uploads and
manifest.json with SHA-256 checksums. Restore is manual by design; follow
BACKUP_RESTORE.md.
How do I reset the admin password?
sudo docker compose exec postgres psql -U stepui -d stepui -c \
"UPDATE users SET password_hash = encode(sha256('newpass'::bytea), 'hex') WHERE username='admin';"Then log in with admin / newpass and change it from the UI.
The legacy SHA-256 reset value is accepted for recovery and is rehashed to bcrypt after login.
The browser warns about a self-signed certificate. How do I use my own?
Replace step-ui-go/ssl/server.crt and server.key with your own cert + key (e.g. from Let's Encrypt or your internal CA), then restart step-ui. Make sure the cert covers your HOST_IP or hostname.
Can I run this behind Cloudflare / Caddy / nginx?
Yes. Point your reverse proxy at step-ui:8443 (HTTPS upstream) or change step-ui to plain HTTP and put TLS termination on the proxy. Set X-Forwarded-Proto: https so step-ui generates correct URLs.
How do I update to a new version?
sudo ./install.sh --mode update --lang enThe update mode creates a backup first, keeps existing Docker volumes, optionally checks out a selected tag, and runs migrations automatically on startup. Always check the release notes first — major versions may have breaking changes.
Pull requests are welcome. For major changes, please open an issue first to discuss what you'd like to change.
git clone https://github.com/UncleFi1/step-ca-ui.git
cd step-ca-ui/step-ui-go
go mod download
go run . # requires running postgres + step-caWhen submitting:
- Run
gofmt -w .andgo vet ./... - Update relevant tests
- Keep commits focused and descriptive
.
├── docker-compose.yml # 3 services: postgres, step-ca, step-ui
├── .env.example # configuration template
├── install.sh # one-shot installer
├── LICENSE # GPL-3.0
├── README.md # this file (English)
├── README.ru.md # Russian translation
└── step-ui-go/
├── main.go # entry point, router setup
├── config/ # env-based config loader
├── db/ # all SQL queries
├── handlers/ # HTTP handlers (one file per area)
├── middleware/ # auth, security headers, CSRF
├── models/ # data structs
├── security/ # password hashing, rate limiting, CSRF
├── templates/ # HTML templates (Go html/template)
├── static/ # CSS, JS, favicon, images
├── Dockerfile # multi-stage Alpine build
└── entrypoint.sh # waits for deps, generates SSL, starts app
This project is licensed under the GNU General Public License v3.0 — see the LICENSE file for details.
In short: you can use, modify, and distribute this software, but any derivative work must also be released under GPLv3.