mirror of
https://github.com/turnstonelabs/turnstone.git
synced 2026-08-12 23:12:23 -06:00
62ff3217d0
* fix: TLS Docker end-to-end testing fixes Fixes discovered during Docker Compose TLS integration testing: - Dockerfile: use --extra all (prevents missing optional deps) - lacme 1.0.3: fixes CACertificateIssued event logging crash - chmod PermissionError: guard for Docker volume mounts - socket import: moved to top of main() (was inside TLS conditional, caused NameError in _default_node_id) - redis.SSLConnection: ConnectionPool needs explicit connection_class, not ssl=True (which only works on Redis() directly) - Empty redis password: pass None instead of "" to avoid AUTH error - TURNSTONE_CONSOLE_URL: env var for Docker service discovery (0.0.0.0 bind address isn't reachable from other containers) - HTTP01Handler: ACME client needs a challenge handler even when server auto-approves - Docker overlay: tls-init as root with chmod, Redis conditional password, console Redis TLS flags, TURNSTONE_CONSOLE_URL * feat: full mTLS end-to-end with lacme 1.0.4 Completes the mTLS chain across all services: lacme 1.0.4: - Dual EKU certs (serverAuth + clientAuth) — fixes mTLS rejection - Configurable CA name (name="turnstone") — consistent store key Bootstrap CA import: - Console imports bootstrap CA from /certs volume on first boot - Single trust root: bootstrap CA → console → all service certs Bridge mTLS: - TLSClient init when TURNSTONE_TLS_ENABLED set - Auto-upgrades server URL from http:// to https:// - SSLContext passed to all 3 httpx clients via verify= Console collector mTLS: - upgrade_tls() method replaces httpx client with mTLS context - Called in lifespan after cert issuance alongside proxy upgrade - Fixes "Failed to poll node" when server serves HTTPS Docker overlay: - TURNSTONE_TLS_SANS on all services (Docker service names as SANs) - TURNSTONE_TLS_ENABLED on bridge - Channel service with Redis TLS flags - TURNSTONE_CONSOLE_URL for service discovery - Server healthcheck disabled (mTLS healthcheck deferred) - Redis conditional password from env Verified end-to-end: bootstrap → console CA → server HTTPS → bridge mTLS → Redis TLS → channel Redis TLS → console collector polls server over mTLS → workstream creation works through bridge * fix: lint + copilot feedback on TLS Docker e2e - SIM105: contextlib.suppress(PermissionError) for chmod - F401: remove unused get_storage import in bridge - Redis healthcheck: pass password when REDIS_PASSWORD is set * fix: sort imports in admin.py and bridge.py * fix: tls-init key permissions, healthcheck env, collector race - tls-init: add set -e, chown to turnstone:turnstone with restrictive perms (keys 0600, certs 0640, dirs 0750) instead of world-readable - Redis healthcheck: use container runtime $$REDIS_PASSWORD instead of Compose-time interpolation for consistency with --requirepass block - collector upgrade_tls(): don't close old httpx client while concurrent poll threads may still be using it — let GC handle cleanup
147 lines
4.2 KiB
YAML
147 lines
4.2 KiB
YAML
# TLS overlay — enables mTLS across the turnstone cluster.
|
|
#
|
|
# Usage (requires base compose.yaml with production profile):
|
|
# docker compose -f compose.yaml -f deploy/docker-compose.tls.yml --profile production up
|
|
#
|
|
# The tls-init service bootstraps a CA and issues a cert for Redis.
|
|
# All turnstone services auto-provision their own certs via the
|
|
# console's ACME endpoint.
|
|
|
|
services:
|
|
# Bootstrap: create CA + Redis cert before anything starts.
|
|
# Runs as root to create directories in the volume, then chowns
|
|
# to turnstone:turnstone with restrictive perms (keys 0600).
|
|
tls-init:
|
|
build: .
|
|
user: root
|
|
command:
|
|
- sh
|
|
- -c
|
|
- |
|
|
set -e
|
|
turnstone-admin tls-bootstrap --out /certs --issue redis
|
|
chown -R turnstone:turnstone /certs
|
|
find /certs -type d -exec chmod 750 {} +
|
|
find /certs -type f -name '*key.pem' -exec chmod 600 {} +
|
|
find /certs -type f ! -name '*key.pem' -exec chmod 640 {} +
|
|
volumes:
|
|
- tls-certs:/certs
|
|
networks:
|
|
- turnstone-net
|
|
restart: "no"
|
|
|
|
# Console: runs the internal CA + ACME server
|
|
console:
|
|
depends_on:
|
|
tls-init:
|
|
condition: service_completed_successfully
|
|
volumes:
|
|
- tls-certs:/certs:ro
|
|
environment:
|
|
TURNSTONE_TLS_ENABLED: "true"
|
|
TURNSTONE_TLS_SANS: "console"
|
|
TURNSTONE_CONSOLE_URL: "http://console:8090"
|
|
command:
|
|
- turnstone-console
|
|
- --host=0.0.0.0
|
|
- --port=8090
|
|
- --redis-host=redis
|
|
- --redis-port=6379
|
|
- --poll-interval=${CONSOLE_POLL_INTERVAL:-10}
|
|
- --redis-tls
|
|
- --redis-tls-ca=/certs/ca.pem
|
|
|
|
# Server: auto-provisions certs via console ACME, serves HTTPS
|
|
server:
|
|
depends_on:
|
|
console:
|
|
condition: service_healthy
|
|
volumes:
|
|
- tls-certs:/certs:ro
|
|
environment:
|
|
TURNSTONE_TLS_ENABLED: "true"
|
|
TURNSTONE_TLS_SANS: "server"
|
|
# Disable healthcheck — server serves HTTPS with mTLS which the
|
|
# stdlib healthcheck script can't satisfy. The base compose
|
|
# healthcheck uses plain HTTP which won't work on an HTTPS listener.
|
|
# TODO: wire healthcheck with client cert from /certs volume
|
|
healthcheck:
|
|
disable: true
|
|
|
|
# Bridge: mTLS to server + Redis TLS
|
|
bridge:
|
|
depends_on:
|
|
console:
|
|
condition: service_healthy
|
|
server:
|
|
condition: service_started
|
|
redis:
|
|
condition: service_healthy
|
|
volumes:
|
|
- tls-certs:/certs:ro
|
|
environment:
|
|
TURNSTONE_TLS_ENABLED: "true"
|
|
TURNSTONE_TLS_SANS: "bridge"
|
|
command:
|
|
- turnstone-bridge
|
|
- --server-url=http://server:8080
|
|
- --redis-host=redis
|
|
- --redis-port=6379
|
|
- --heartbeat-ttl=${HEARTBEAT_TTL:-60}
|
|
- --approval-timeout=${APPROVAL_TIMEOUT:-3600}
|
|
- --redis-tls
|
|
- --redis-tls-ca=/certs/ca.pem
|
|
|
|
# Channel: Redis TLS
|
|
channel:
|
|
depends_on:
|
|
console:
|
|
condition: service_healthy
|
|
redis:
|
|
condition: service_healthy
|
|
volumes:
|
|
- tls-certs:/certs:ro
|
|
environment:
|
|
TURNSTONE_TLS_ENABLED: "true"
|
|
TURNSTONE_TLS_SANS: "channel"
|
|
command:
|
|
- sh
|
|
- -c
|
|
- >-
|
|
turnstone-channel
|
|
--redis-host=redis
|
|
--redis-port=6379
|
|
--redis-tls
|
|
--redis-tls-ca=/certs/ca.pem
|
|
--http-host=0.0.0.0
|
|
$${TURNSTONE_DISCORD_GUILD:+--discord-guild $$TURNSTONE_DISCORD_GUILD}
|
|
|
|
# Redis: TLS with certs from bootstrap
|
|
redis:
|
|
depends_on:
|
|
tls-init:
|
|
condition: service_completed_successfully
|
|
volumes:
|
|
- tls-certs:/certs:ro
|
|
command:
|
|
- sh
|
|
- -c
|
|
- |
|
|
ARGS="--tls-port 6379 --port 0 \
|
|
--tls-cert-file /certs/certs/redis/cert.pem \
|
|
--tls-key-file /certs/certs/redis/key.pem \
|
|
--tls-ca-cert-file /certs/ca.pem \
|
|
--tls-auth-clients no"
|
|
if [ -n "$$REDIS_PASSWORD" ]; then
|
|
ARGS="$$ARGS --requirepass $$REDIS_PASSWORD"
|
|
fi
|
|
exec redis-server $$ARGS
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "if [ -n \"$$REDIS_PASSWORD\" ]; then redis-cli --tls --cacert /certs/ca.pem -a $$REDIS_PASSWORD ping; else redis-cli --tls --cacert /certs/ca.pem ping; fi"]
|
|
interval: 5s
|
|
timeout: 3s
|
|
retries: 5
|
|
|
|
volumes:
|
|
tls-certs:
|