diff --git a/docker/cameleer-traefik/Dockerfile b/docker/cameleer-traefik/Dockerfile new file mode 100644 index 0000000..f4b733f --- /dev/null +++ b/docker/cameleer-traefik/Dockerfile @@ -0,0 +1,7 @@ +FROM traefik:v3 +RUN apk add --no-cache openssl +COPY traefik.yml /etc/traefik/traefik.yml +COPY traefik-dynamic.yml /etc/traefik/dynamic.yml +COPY entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh +ENTRYPOINT ["/entrypoint.sh"] diff --git a/docker/cameleer-traefik/entrypoint.sh b/docker/cameleer-traefik/entrypoint.sh new file mode 100644 index 0000000..bf2c0b8 --- /dev/null +++ b/docker/cameleer-traefik/entrypoint.sh @@ -0,0 +1,60 @@ +#!/bin/sh +set -e + +CERTS_DIR="/certs" + +# Skip if certs already exist (idempotent) +if [ ! -f "$CERTS_DIR/cert.pem" ]; then + mkdir -p "$CERTS_DIR" + + if [ -n "$CERT_FILE" ] && [ -n "$KEY_FILE" ]; then + # User-supplied certificate + echo "[certs] Installing user-supplied certificate..." + cp "$CERT_FILE" "$CERTS_DIR/cert.pem" + cp "$KEY_FILE" "$CERTS_DIR/key.pem" + if [ -n "$CA_FILE" ]; then + cp "$CA_FILE" "$CERTS_DIR/ca.pem" + fi + # Validate key matches cert + CERT_MOD=$(openssl x509 -noout -modulus -in "$CERTS_DIR/cert.pem" 2>/dev/null | md5sum) + KEY_MOD=$(openssl rsa -noout -modulus -in "$CERTS_DIR/key.pem" 2>/dev/null | md5sum) + if [ "$CERT_MOD" != "$KEY_MOD" ]; then + echo "[certs] ERROR: Certificate and key do not match!" + rm -f "$CERTS_DIR/cert.pem" "$CERTS_DIR/key.pem" "$CERTS_DIR/ca.pem" + exit 1 + fi + SELF_SIGNED=false + echo "[certs] Installed user-supplied certificate." + else + # Generate self-signed certificate + HOST="${PUBLIC_HOST:-localhost}" + echo "[certs] Generating self-signed certificate for $HOST..." + openssl req -x509 -newkey rsa:4096 \ + -keyout "$CERTS_DIR/key.pem" -out "$CERTS_DIR/cert.pem" \ + -days 365 -nodes \ + -subj "/CN=$HOST" \ + -addext "subjectAltName=DNS:$HOST,DNS:*.$HOST" + SELF_SIGNED=true + echo "[certs] Generated self-signed certificate for $HOST." + fi + + # Write metadata for SaaS app to seed DB + SUBJECT=$(openssl x509 -noout -subject -in "$CERTS_DIR/cert.pem" 2>/dev/null | sed 's/subject=//') + FINGERPRINT=$(openssl x509 -noout -fingerprint -sha256 -in "$CERTS_DIR/cert.pem" 2>/dev/null | sed 's/.*=//') + NOT_BEFORE=$(openssl x509 -noout -startdate -in "$CERTS_DIR/cert.pem" 2>/dev/null | sed 's/notBefore=//') + NOT_AFTER=$(openssl x509 -noout -enddate -in "$CERTS_DIR/cert.pem" 2>/dev/null | sed 's/notAfter=//') + HAS_CA=false + [ -f "$CERTS_DIR/ca.pem" ] && HAS_CA=true + cat > "$CERTS_DIR/meta.json" </dev/null || true +else + echo "[certs] Certificates already exist, skipping generation." +fi + +# Start Traefik +exec traefik "$@" diff --git a/docker/cameleer-traefik/traefik-dynamic.yml b/docker/cameleer-traefik/traefik-dynamic.yml new file mode 100644 index 0000000..d20cd3e --- /dev/null +++ b/docker/cameleer-traefik/traefik-dynamic.yml @@ -0,0 +1,24 @@ +http: + routers: + root-redirect: + rule: "Path(`/`)" + priority: 100 + entryPoints: + - websecure + tls: {} + middlewares: + - root-to-platform + service: saas@docker + middlewares: + root-to-platform: + redirectRegex: + regex: "^(https?://[^/]+)/?$" + replacement: "${1}/platform/" + permanent: false + +tls: + stores: + default: + defaultCertificate: + certFile: /etc/traefik/certs/cert.pem + keyFile: /etc/traefik/certs/key.pem diff --git a/docker/cameleer-traefik/traefik.yml b/docker/cameleer-traefik/traefik.yml new file mode 100644 index 0000000..10de04c --- /dev/null +++ b/docker/cameleer-traefik/traefik.yml @@ -0,0 +1,23 @@ +api: + dashboard: false + +entryPoints: + web: + address: ":80" + http: + redirections: + entryPoint: + to: websecure + scheme: https + websecure: + address: ":443" + admin-console: + address: ":3002" + +providers: + docker: + endpoint: "unix:///var/run/docker.sock" + exposedByDefault: false + network: cameleer + file: + filename: /etc/traefik/dynamic.yml