Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.git
.venv
build
dist
*.egg-info
**/__pycache__
.local
.env
etc/certlord/credentials.yml
77 changes: 77 additions & 0 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
name: Docker image and Compose
on:
pull_request:
push:
branches: [main]
release:
types: [published]
workflow_dispatch:
inputs:
publish:
description: Publish validated image to Docker Hub
type: boolean
default: false
permissions:
contents: read
concurrency:
group: docker-${{ github.ref }}
cancel-in-progress: false
jobs:
compose:
strategy:
fail-fast: false
matrix:
runner: [ubuntu-24.04, ubuntu-24.04-arm]
runs-on: ${{ matrix.runner }}
timeout-minutes: 25
env:
CERTLORD_IMAGE: certlord-compose-test:local
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262
- name: Validate Compose and build image
run: |
docker compose config --quiet
docker compose build certlord
- name: Start installed stack and verify restart persistence
run: sh docker/smoke.sh
- name: Installed image dependency check
run: docker compose exec -T certlord python -m pip check
- name: Sanitized service state
if: always()
run: |
docker compose ps -a
docker compose logs --tail 15 vault certlord auton
- name: Remove disposable stack
if: always()
run: docker compose down -v --remove-orphans
publish:
needs: compose
if: >-
github.repository == 'decryptus/certlord' &&
((github.event_name == 'workflow_dispatch' && inputs.publish && github.ref == 'refs/heads/main') ||
(github.event_name == 'release' && !github.event.release.prerelease))
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262
- name: Validate stable image version
id: version
run: |
version=$(cat VERSION)
python -c 'import re; from pathlib import Path; assert re.fullmatch(r"[0-9]+\.[0-9]+\.[0-9]+", Path("VERSION").read_text().strip())'
echo "version=$version" >> "$GITHUB_OUTPUT"
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
username: decryptus
password: ${{ secrets.DOCKERHUB_TOKEN }}
- uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
build-args: VERSION=${{ steps.version.outputs.version }}
tags: |
decryptus/certlord:${{ steps.version.outputs.version }}
decryptus/certlord:latest
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ build/
dist/
*.egg-info/
etc/certlord/credentials.yml

.local/
.env
25 changes: 25 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
FROM python:3.12-slim-bookworm AS build
RUN apt-get update && apt-get install -y --no-install-recommends build-essential libcurl4-openssl-dev libssl-dev && rm -rf /var/lib/apt/lists/*
WORKDIR /src
COPY . .
RUN python -m pip wheel --wheel-dir /wheels '.[textual]' 'certbot==5.8.0' 'certbot-httpreq==0.0.24' 'acme-http-connector==0.2.1' 'auton==1.2.1' 'autond==1.2.1'

FROM python:3.12-slim-bookworm
ARG VERSION=1.0.0
LABEL org.opencontainers.image.title="CertLord" \
org.opencontainers.image.description="TLS certificate lifecycle automation with ACME, Vault storage and Auton deployment." \
org.opencontainers.image.source="https://github.com/decryptus/certlord" \
org.opencontainers.image.version=$VERSION \
org.opencontainers.image.licenses="GPL-3.0-or-later"
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates libcurl4 libmagic1 openssh-client && rm -rf /var/lib/apt/lists/* \
&& groupadd --gid 10001 certlord && useradd --uid 10001 --gid certlord --create-home certlord
COPY --from=build /wheels /wheels
RUN python -m pip install --no-index --find-links=/wheels certlord certbot certbot-httpreq acme-http-connector auton autond textual && rm -rf /wheels \
&& mkdir -p /var/lib/certlord /var/lib/auton && chown certlord:certlord /var/lib/certlord /var/lib/auton
COPY docker/ /opt/certlord-docker/
ENV PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1
USER 10001:10001
WORKDIR /var/lib/certlord
EXPOSE 8666
STOPSIGNAL SIGTERM
CMD ["certlord", "-f", "-c", "/etc/certlord/certlord.yml", "-p", "/var/lib/certlord/certlord.pid", "--logfile", "/var/lib/certlord/certlord.log"]
3 changes: 3 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,6 @@ include scripts/capture_textual.py
include conf.py index.rst docs/requirements.txt
recursive-include _templates *.html
recursive-include docs *.md *.rst

include Dockerfile .dockerignore compose.yaml
recursive-include docker *.py *.sh *.hcl *.conf *.yml
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,14 @@ are optional; expiry observations are exposed for an external supervision system
It uses DWho, HTTPdis and Sonicprobe.

Redis stores temporary ACME HTTP-01 challenge responses and tracks pending work,
retries and deployment leases. Vault stores certificate material. Both services
must be provisioned separately; see the [installation guide](docs/debian-installation.md).
retries and deployment leases. Vault stores certificate material. Use the [Docker Compose installation](docs/docker-compose.md) to run the services
together, or provision them separately for the [Debian installation](docs/debian-installation.md).

## Docker installation

Use the [Compose guide](docs/docker-compose.md) for initialization, persistent
Vault storage, API access and deployment configuration. The application image is
`decryptus/certlord:1.0.0`.

## Command line and terminal interface

Expand Down
90 changes: 90 additions & 0 deletions compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
name: certlord
x-image: &image
image: ${CERTLORD_IMAGE:-decryptus/certlord:1.0.0}
build:
context: .
services:
vault:
image: hashicorp/vault:1.21.4
command: ["server"]
environment:
VAULT_ADDR: http://127.0.0.1:8200
cap_add: [IPC_LOCK]
volumes:
- vault-data:/vault/file
- ./docker/vault.hcl:/vault/config/server.hcl:ro
networks: [backend]
restart: unless-stopped
redis:
image: redis:7.4-alpine
command: ["redis-server", "--appendonly", "yes", "--appendfsync", "everysec"]
volumes: [redis-data:/data]
networks: [backend]
restart: unless-stopped
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 5
setup:
<<: *image
profiles: [tools]
user: "0:0"
entrypoint: ["python", "/opt/certlord-docker/setup.py"]
volumes:
- ./.local:/bootstrap
networks: [backend]
certlord:
<<: *image
depends_on:
redis:
condition: service_healthy
vault:
condition: service_started
auton:
condition: service_started
ports: ["127.0.0.1:${CERTLORD_PORT:-8666}:8666"]
volumes:
- ./.local/certlord:/etc/certlord:ro
- certlord-data:/var/lib/certlord
networks: [backend, outbound]
restart: unless-stopped
init: true
stop_grace_period: 30s
security_opt: [no-new-privileges:true]
healthcheck:
test: ["CMD", "python", "/opt/certlord-docker/health.py"]
interval: 20s
timeout: 15s
retries: 3
start_period: 30s
auton:
<<: *image
command: ["autond", "-f", "-u", "certlord", "-g", "certlord", "-c", "/etc/auton/auton.yml", "-p", "/var/lib/auton/auton.pid", "--logfile", "/var/lib/auton/auton.log"]
volumes:
- ./.local/auton:/etc/auton:ro
- ./.local/deploy:/deploy:ro
- auton-data:/var/lib/auton
networks: [backend, outbound]
restart: unless-stopped
init: true
stop_grace_period: 30s
security_opt: [no-new-privileges:true]
challenges:
image: nginx:1.28-alpine
profiles: [http01]
ports: ["${HTTP01_BIND:-0.0.0.0}:${HTTP01_PORT:-80}:8080"]
volumes:
- ./docker/challenges.conf:/etc/nginx/conf.d/default.conf:ro
networks: [backend, outbound]
depends_on: [certlord]
restart: unless-stopped
volumes:
vault-data:
redis-data:
certlord-data:
auton-data:
networks:
backend:
internal: true
outbound: {}
43 changes: 43 additions & 0 deletions docker/auton-routes.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
job:
routes:
jobs:
handler: 'job_list'
regexp: '^jobs$'
auth: false
op: 'GET'
job_detail:
handler: 'job_detail'
regexp: '^jobs/(?P<endpoint>[^\/]+)/(?P<id>[a-z0-9][a-z0-9\-]{7,63})$'
auth: false
op: 'GET'
endpoints:
handler: 'endpoint_list'
regexp: '^endpoints$'
auth: false
op: 'GET'
health:
handler: 'daemon_health'
regexp: '^health$'
auth: false
op: 'GET'
maintenance:
handler: 'daemon_maintenance'
regexp: '^maintenance$'
auth: true
op: 'POST'
stats:
handler: 'daemon_stats'
regexp: '^stats$'
auth: false
op: 'GET'
run:
handler: 'job_run'
regexp: '^run/(?P<endpoint>[^\/]+)/(?P<id>[a-z0-9][a-z0-9\-]{7,63})$'
safe_init: true
auth: false
op: 'POST'
status:
handler: 'job_status'
regexp: '^status/(?P<endpoint>[^\/]+)/(?P<id>[a-z0-9][a-z0-9\-]{7,63})$'
auth: false
op: 'GET'
Loading
Loading