generated from bisco/codex-bootstrap
Compare commits
8 Commits
786fa78ebd
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 1470b49ed9 | |||
| 27b3b7624f | |||
| 741ed4764d | |||
| 86680f71f3 | |||
| 1509c1e5fd | |||
| f3759e76ea | |||
| b5beb27623 | |||
| 7dfc23d69e |
+4
-7
@@ -4,9 +4,9 @@ Edit this file for each repository.
|
||||
|
||||
## Project identity
|
||||
|
||||
Project name: `CHANGE_ME`
|
||||
Project description: `CHANGE_ME`
|
||||
Primary language/runtime: `CHANGE_ME`
|
||||
Project name: `traefik-tcp-loadbalancer`
|
||||
Project description: `Dockerized Traefik TCP load balancer with TLS passthrough`
|
||||
Primary language/runtime: `Docker Compose / Traefik`
|
||||
|
||||
## Project mode
|
||||
|
||||
@@ -14,7 +14,6 @@ Choose one:
|
||||
|
||||
```text
|
||||
project_mode: personal
|
||||
project_mode: work
|
||||
```
|
||||
|
||||
Rules:
|
||||
@@ -29,8 +28,6 @@ Enable only the profiles that apply to this repository:
|
||||
```text
|
||||
enabled_profiles:
|
||||
- docker
|
||||
- ansible
|
||||
- python
|
||||
```
|
||||
|
||||
## Branching model
|
||||
@@ -77,7 +74,7 @@ All tests MUST be executed inside Docker containers.
|
||||
Configure the canonical test command for this repository:
|
||||
|
||||
```bash
|
||||
CHANGE_ME
|
||||
docker compose config
|
||||
```
|
||||
|
||||
Examples:
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
*.log
|
||||
.env
|
||||
config/**/*.bak
|
||||
config/**/*.tmp
|
||||
@@ -1,73 +1,105 @@
|
||||
# codex-bootstrap
|
||||
# Traefik TCP Load Balancer
|
||||
|
||||
A repository template for AI-assisted development with Codex.
|
||||
Docker Compose deployment for Traefik used as a TCP load balancer with TLS passthrough.
|
||||
|
||||
This template defines a repeatable workflow for using Codex as an autonomous coding agent that can create branches, modify code, run Docker-based tests, update documentation, write ADRs, and commit changes using Conventional Commits.
|
||||
## What it does
|
||||
|
||||
## Purpose
|
||||
- publishes `443/tcp` on the Docker host;
|
||||
- publishes `80/tcp` on the Docker host for HTTP-01 certificate challenges;
|
||||
- forwards TCP TLS traffic to backend IP addresses and ports;
|
||||
- leaves SSL certificates and private keys on the backends;
|
||||
- stores Traefik configuration in the host `./config` directory;
|
||||
- uses the Traefik file provider, without mounting the Docker socket.
|
||||
|
||||
Use this template to bootstrap repositories where Codex must operate with clear rules, minimal changes, pragmatic TDD, security guardrails, and explicit documentation requirements.
|
||||
## Configuration
|
||||
|
||||
## Repository structure
|
||||
Add or edit one file per site under `config/dynamic/`. For example, `config/dynamic/lab-younerd.yml` contains:
|
||||
|
||||
```text
|
||||
.
|
||||
├── AGENTS.md
|
||||
├── README.md
|
||||
├── .codex/
|
||||
│ ├── project.md
|
||||
│ ├── workflow.md
|
||||
│ ├── security.md
|
||||
│ ├── quality.md
|
||||
│ ├── orchestration.md
|
||||
│ ├── prompts/
|
||||
│ │ ├── task.md
|
||||
│ │ ├── bugfix.md
|
||||
│ │ ├── refactor.md
|
||||
│ │ ├── security-review.md
|
||||
│ │ └── documentation.md
|
||||
│ ├── agents/
|
||||
│ │ ├── architect.md
|
||||
│ │ ├── developer.md
|
||||
│ │ ├── reviewer.md
|
||||
│ │ ├── security-reviewer.md
|
||||
│ │ ├── test-engineer.md
|
||||
│ │ └── documentation-writer.md
|
||||
│ └── profiles/
|
||||
│ ├── docker.md
|
||||
│ ├── ansible.md
|
||||
│ └── python.md
|
||||
└── docs/
|
||||
├── adr/
|
||||
│ └── 0000-template.md
|
||||
├── architecture.md
|
||||
├── deployment.md
|
||||
├── operations.md
|
||||
├── security.md
|
||||
├── testing.md
|
||||
└── runbook.md
|
||||
```yaml
|
||||
http:
|
||||
routers:
|
||||
lab-yourdomain-http:
|
||||
entryPoints:
|
||||
- web
|
||||
rule: "Host(`lab.yourdomain.tld`)"
|
||||
service: lab-yourdomain-http
|
||||
|
||||
services:
|
||||
lab-yourdomain-http:
|
||||
loadBalancer:
|
||||
healthCheck:
|
||||
path: "/"
|
||||
hostname: "lab.yourdomain.tld"
|
||||
interval: "10s"
|
||||
timeout: "3s"
|
||||
servers:
|
||||
- url: "http://127.0.0.1:8080"
|
||||
|
||||
tcp:
|
||||
routers:
|
||||
lab-yourdomain-tls:
|
||||
entryPoints:
|
||||
- tls
|
||||
rule: "HostSNI(`lab.yourdomain.tld`)"
|
||||
service: lab-yourdomain-tls
|
||||
tls:
|
||||
passthrough: true
|
||||
|
||||
services:
|
||||
lab-yourdomain-tls:
|
||||
loadBalancer:
|
||||
healthCheck:
|
||||
interval: "10s"
|
||||
timeout: "3s"
|
||||
servers:
|
||||
- address: "127.0.0.1:8443"
|
||||
```
|
||||
|
||||
## How to use
|
||||
`HostSNI(...)` matches the hostname sent by the client during the TLS handshake. This deployment uses host networking so Traefik can reach services bound to the Docker host loopback address, such as `127.0.0.1:8443`.
|
||||
|
||||
1. Copy this template into a new or existing repository.
|
||||
2. Edit `.codex/project.md` and configure:
|
||||
- project mode;
|
||||
- enabled profiles;
|
||||
- Docker-based test command;
|
||||
- branch naming rules if needed.
|
||||
3. Add project-specific details to the documentation under `docs/`.
|
||||
4. When asking Codex to work on a task, use one of the prompt templates under `.codex/prompts/`.
|
||||
HTTP-01 certificate challenges use normal HTTP routing on port `80`, not SNI. The included example forwards:
|
||||
|
||||
## Core rules
|
||||
```text
|
||||
lab.yourdomain.tld -> 127.0.0.1:8080
|
||||
www.yourdomain.tld -> 127.0.0.1:9080
|
||||
```
|
||||
|
||||
Codex must:
|
||||
Adjust `8080` and `9080` to the local ports where the backend HTTP service is listening. The full HTTP request is forwarded so the backend can handle redirects and ACME challenge paths.
|
||||
|
||||
- start work from `develop`;
|
||||
- create a dedicated `feature/`, `fix/`, or `hotfix/` branch;
|
||||
- use pragmatic TDD;
|
||||
- keep changes minimal and focused;
|
||||
- run the configured Docker-based test command before completion;
|
||||
- update documentation and ADRs when needed;
|
||||
- produce a final report with summary, tests, risks, and rollback notes;
|
||||
- commit using Conventional Commits.
|
||||
Each backend service includes health checks. TCP checks verify that the TLS port accepts connections; HTTP checks call `/` with the configured hostname and treat successful or redirect responses as healthy.
|
||||
|
||||
## Run
|
||||
|
||||
Validate the Compose file:
|
||||
|
||||
```bash
|
||||
docker compose config
|
||||
```
|
||||
|
||||
Start Traefik:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
View logs:
|
||||
|
||||
```bash
|
||||
docker compose logs -f traefik
|
||||
```
|
||||
|
||||
Stop the service:
|
||||
|
||||
```bash
|
||||
docker compose down
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
- Architecture: `docs/architecture.md`
|
||||
- Deployment: `docs/deployment.md`
|
||||
- Operations: `docs/operations.md`
|
||||
- Security: `docs/security.md`
|
||||
- Testing: `docs/testing.md`
|
||||
- Runbook: `docs/runbook.md`
|
||||
- ADR: `docs/adr/0001-traefik-tcp-tls-passthrough.md`
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
services:
|
||||
traefik:
|
||||
image: traefik:v3.7.5
|
||||
container_name: tcp-loadbalancer-traefik
|
||||
restart: unless-stopped
|
||||
command:
|
||||
- --configFile=/etc/traefik/traefik.yml
|
||||
network_mode: host
|
||||
volumes:
|
||||
- ./config:/etc/traefik:ro
|
||||
read_only: true
|
||||
cap_drop:
|
||||
- ALL
|
||||
cap_add:
|
||||
- NET_BIND_SERVICE
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
healthcheck:
|
||||
test: ["CMD", "traefik", "healthcheck", "--ping"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
@@ -0,0 +1,37 @@
|
||||
http:
|
||||
routers:
|
||||
azionelab-http:
|
||||
entryPoints:
|
||||
- web
|
||||
rule: "Host(`azionelab.org`) || Host(`www.azionelab.org`)"
|
||||
service: azionelab-http
|
||||
|
||||
services:
|
||||
azionelab-http:
|
||||
loadBalancer:
|
||||
healthCheck:
|
||||
path: "/"
|
||||
hostname: "azionelab.org"
|
||||
interval: "10s"
|
||||
timeout: "3s"
|
||||
servers:
|
||||
- url: "http://127.0.0.1:9080"
|
||||
|
||||
tcp:
|
||||
routers:
|
||||
azionelab-tls:
|
||||
entryPoints:
|
||||
- tls
|
||||
rule: "HostSNI(`azionelab.org`) || HostSNI(`www.azionelab.org`)"
|
||||
service: azionelab-tls
|
||||
tls:
|
||||
passthrough: true
|
||||
|
||||
services:
|
||||
azionelab-tls:
|
||||
loadBalancer:
|
||||
healthCheck:
|
||||
interval: "10s"
|
||||
timeout: "3s"
|
||||
servers:
|
||||
- address: "127.0.0.1:9443"
|
||||
@@ -0,0 +1,37 @@
|
||||
http:
|
||||
routers:
|
||||
lab-younerd-http:
|
||||
entryPoints:
|
||||
- web
|
||||
rule: "Host(`lab.younerd.org`)"
|
||||
service: lab-younerd-http
|
||||
|
||||
services:
|
||||
lab-younerd-http:
|
||||
loadBalancer:
|
||||
healthCheck:
|
||||
path: "/"
|
||||
hostname: "lab.younerd.org"
|
||||
interval: "10s"
|
||||
timeout: "3s"
|
||||
servers:
|
||||
- url: "http://127.0.0.1:8080"
|
||||
|
||||
tcp:
|
||||
routers:
|
||||
lab-younerd-tls:
|
||||
entryPoints:
|
||||
- tls
|
||||
rule: "HostSNI(`lab.younerd.org`)"
|
||||
service: lab-younerd-tls
|
||||
tls:
|
||||
passthrough: true
|
||||
|
||||
services:
|
||||
lab-younerd-tls:
|
||||
loadBalancer:
|
||||
healthCheck:
|
||||
interval: "10s"
|
||||
timeout: "3s"
|
||||
servers:
|
||||
- address: "127.0.0.1:8443"
|
||||
@@ -0,0 +1,23 @@
|
||||
global:
|
||||
checkNewVersion: false
|
||||
sendAnonymousUsage: false
|
||||
|
||||
entryPoints:
|
||||
web:
|
||||
address: ":80"
|
||||
tls:
|
||||
address: ":443"
|
||||
ping:
|
||||
address: ":8082"
|
||||
|
||||
providers:
|
||||
file:
|
||||
directory: /etc/traefik/dynamic
|
||||
watch: true
|
||||
|
||||
ping:
|
||||
entryPoint: ping
|
||||
|
||||
log:
|
||||
level: INFO
|
||||
format: common
|
||||
@@ -0,0 +1,41 @@
|
||||
# ADR-0001: Traefik TCP TLS passthrough load balancer
|
||||
|
||||
Date: 2026-06-30
|
||||
|
||||
Status: Accepted
|
||||
|
||||
## Context
|
||||
|
||||
The load balancer must run as a Docker container and forward TCP TLS traffic to backend services reachable by local IP address and port. Backend services already expose their own SSL certificates, so the load balancer must not terminate TLS or store backend private keys. Runtime configuration must be kept in a host-based volume.
|
||||
|
||||
## Decision
|
||||
|
||||
Use Traefik Proxy in Docker with the file provider and TCP TLS passthrough routing. Static and dynamic configuration live under the host `./config` directory and are mounted read-only at `/etc/traefik`.
|
||||
|
||||
Traefik uses host networking so it can reach backends bound to host loopback addresses such as `127.0.0.1:8443`. The container is granted only `NET_BIND_SERVICE` so Traefik can bind host ports `80` and `443`.
|
||||
|
||||
Traefik routes incoming TLS connections with `HostSNI(...)` to the configured TCP load-balancer services. HTTP routes on port `80` use `Host(...)` and forward the full request to the selected backend because SNI exists only in TLS handshakes and backend nginx instances are responsible for redirects and ACME challenge handling.
|
||||
|
||||
## Consequences
|
||||
|
||||
TLS certificates, private keys, and certificate renewal remain the responsibility of each backend. Traefik can route TLS traffic based on the TLS SNI value but cannot inspect HTTP headers or paths because TLS is passed through unchanged.
|
||||
|
||||
Backend health checks are configured per site. TCP health checks verify backend port reachability; HTTP health checks call `/` with the configured hostname. Configuration changes in `config/dynamic/` are watched by Traefik and can be applied without recreating the container.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
TLS termination at Traefik was rejected because the requested backend-owned certificate model would require moving certificates or keys to Traefik.
|
||||
|
||||
The Docker provider was not selected because the backends are addressed directly by IP and port rather than discovered from Docker labels.
|
||||
|
||||
## Security impact
|
||||
|
||||
The container does not mount the Docker socket, does not run privileged, drops Linux capabilities except `NET_BIND_SERVICE`, uses `no-new-privileges`, and mounts configuration read-only. No private keys or credentials are required in this repository. Host networking exposes Traefik directly on the host network namespace, so host firewall rules should restrict non-public ports such as the ping entrypoint.
|
||||
|
||||
## Operational impact
|
||||
|
||||
Operators update backend IP addresses and ports in per-site files under `config/dynamic/`. Traefik listens directly on host `80/tcp` and `443/tcp`; the ping entrypoint is used by the healthcheck.
|
||||
|
||||
## Rollback
|
||||
|
||||
Revert the commit that introduced the Traefik configuration and restore any previous load-balancer service. If deployed, stop the Compose stack with `docker compose down`.
|
||||
+32
-9
@@ -1,13 +1,36 @@
|
||||
# Architecture
|
||||
|
||||
Describe the project architecture here.
|
||||
This project runs Traefik Proxy as a Dockerized TCP load balancer.
|
||||
|
||||
Include:
|
||||
## Components
|
||||
|
||||
- main components;
|
||||
- runtime dependencies;
|
||||
- data flow;
|
||||
- persistence;
|
||||
- external integrations;
|
||||
- deployment topology;
|
||||
- relevant ADRs.
|
||||
- `compose.yaml`: defines the Traefik container, host networking, host-based configuration volume, container hardening, and healthcheck.
|
||||
- `config/traefik.yml`: static Traefik configuration for HTTP, TCP TLS, ping, file provider, and logging.
|
||||
- `config/dynamic/*.yml`: dynamic per-site HTTP routers, TCP routers, and backend load-balancer services.
|
||||
|
||||
## Data flow
|
||||
|
||||
1. A client opens a TLS connection to the Docker host on `443/tcp`.
|
||||
2. Traefik receives the connection directly through host networking on `443/tcp`.
|
||||
3. Traefik matches the TCP router using `HostSNI(...)`.
|
||||
4. Traefik forwards the unchanged TCP stream to one configured backend IP address and port.
|
||||
5. The selected backend terminates TLS and presents its own SSL certificate.
|
||||
|
||||
For HTTP-01 certificate validation:
|
||||
|
||||
1. Let's Encrypt opens an HTTP connection to the Docker host on `80/tcp`.
|
||||
2. Traefik receives the request directly through host networking on `80/tcp`.
|
||||
3. Traefik matches the HTTP `Host(...)` router.
|
||||
4. Traefik forwards the full HTTP request to the configured local backend so that backend redirects and ACME challenge handling remain in one place.
|
||||
|
||||
## Persistence
|
||||
|
||||
Traefik configuration is stored in the host `./config` directory and mounted read-only at `/etc/traefik`.
|
||||
|
||||
No certificates, private keys, application data, or Docker socket are mounted into the Traefik container.
|
||||
|
||||
## Deployment topology
|
||||
|
||||
Traefik runs as one Docker container on the load-balancer host with `network_mode: host`. Backends are external to this Compose stack and can be reached on host loopback addresses such as `127.0.0.1:8443`.
|
||||
|
||||
See `docs/adr/0001-traefik-tcp-tls-passthrough.md` for the design decision.
|
||||
|
||||
+73
-11
@@ -1,15 +1,77 @@
|
||||
# Deployment
|
||||
|
||||
Describe how this project is deployed.
|
||||
## Requirements
|
||||
|
||||
Include:
|
||||
- Docker Engine with Docker Compose plugin.
|
||||
- Backend services reachable from the Docker host or container by IP address and port.
|
||||
- Backend services must terminate TLS and present valid SSL certificates for the expected client names.
|
||||
|
||||
- environments;
|
||||
- Docker/Compose usage;
|
||||
- required configuration;
|
||||
- secrets handling;
|
||||
- exposed ports;
|
||||
- volumes;
|
||||
- networks;
|
||||
- deployment commands;
|
||||
- rollback procedure.
|
||||
## Configuration
|
||||
|
||||
Add or update a per-site file under `config/dynamic/` with the required hostnames and backend addresses:
|
||||
|
||||
```yaml
|
||||
tcp:
|
||||
routers:
|
||||
example:
|
||||
rule: "HostSNI(`example.org`)"
|
||||
service: example
|
||||
tls:
|
||||
passthrough: true
|
||||
|
||||
services:
|
||||
example:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- address: "127.0.0.1:8443"
|
||||
```
|
||||
|
||||
This deployment uses host networking so Traefik can reach services bound to host loopback addresses such as `127.0.0.1:8443`.
|
||||
|
||||
HTTP traffic is routed separately with HTTP `Host(...)` rules on port `80`. Configure the HTTP service in the same per-site file to point at the local backend port that should handle redirects and ACME challenge paths.
|
||||
|
||||
Keep each site's HTTP and TCP routers in the same dynamic file, for example `config/dynamic/lab-younerd.yml` or `config/dynamic/azionelab.yml`.
|
||||
|
||||
The `./config` directory is the host-based configuration volume. It is mounted read-only into the container:
|
||||
|
||||
```yaml
|
||||
volumes:
|
||||
- ./config:/etc/traefik:ro
|
||||
```
|
||||
|
||||
## Ports
|
||||
|
||||
- Host `80/tcp`: public HTTP entrypoint handled directly by Traefik through host networking.
|
||||
- Host `443/tcp`: public TCP TLS passthrough entrypoint handled directly by Traefik through host networking.
|
||||
- Host `8082/tcp`: Traefik ping entrypoint for the container healthcheck; restrict this port with host firewall rules if needed.
|
||||
|
||||
## Deploy
|
||||
|
||||
Validate the Compose configuration:
|
||||
|
||||
```bash
|
||||
docker compose config
|
||||
```
|
||||
|
||||
Start the stack:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Check status:
|
||||
|
||||
```bash
|
||||
docker compose ps
|
||||
docker compose logs traefik
|
||||
```
|
||||
|
||||
## Rollback
|
||||
|
||||
Stop the stack:
|
||||
|
||||
```bash
|
||||
docker compose down
|
||||
```
|
||||
|
||||
Then revert the deployment commit or restore the previous load-balancer configuration.
|
||||
|
||||
+59
-9
@@ -1,13 +1,63 @@
|
||||
# Operations
|
||||
|
||||
Describe operational procedures.
|
||||
## Startup and shutdown
|
||||
|
||||
Include:
|
||||
Start Traefik:
|
||||
|
||||
- startup and shutdown;
|
||||
- health checks;
|
||||
- logs;
|
||||
- monitoring;
|
||||
- backup and restore;
|
||||
- routine maintenance;
|
||||
- known operational risks.
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Stop Traefik:
|
||||
|
||||
```bash
|
||||
docker compose down
|
||||
```
|
||||
|
||||
Restart after static configuration changes in `config/traefik.yml`:
|
||||
|
||||
```bash
|
||||
docker compose restart traefik
|
||||
```
|
||||
|
||||
Dynamic configuration changes under `config/dynamic/` are watched by Traefik and normally do not require a restart.
|
||||
|
||||
## Health checks
|
||||
|
||||
The Traefik container healthcheck runs:
|
||||
|
||||
```bash
|
||||
traefik healthcheck --ping
|
||||
```
|
||||
|
||||
Check health with:
|
||||
|
||||
```bash
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
Backend services also define Traefik load-balancer health checks in each file under `config/dynamic/`. HTTP checks call `/` with the configured hostname; TCP checks verify that the local TLS port accepts connections.
|
||||
|
||||
## Logs
|
||||
|
||||
Traefik logs to container stdout:
|
||||
|
||||
```bash
|
||||
docker compose logs -f traefik
|
||||
```
|
||||
|
||||
## Backend maintenance
|
||||
|
||||
To remove a backend from rotation, edit that site's file under `config/dynamic/` and remove or comment out its `address` entry. Keep at least one reachable backend configured before applying the change.
|
||||
|
||||
This deployment uses host networking. For backends running on the Docker host, use `127.0.0.1:PORT` when the service is bound to host loopback.
|
||||
|
||||
For HTTP on port `80`, update the HTTP services in the relevant site file under `config/dynamic/` to match the local backend ports that handle redirects and ACME challenge paths.
|
||||
|
||||
## Known operational risks
|
||||
|
||||
- TLS passthrough prevents Traefik from inspecting HTTP paths, headers, or certificate contents.
|
||||
- Backend certificates and renewal jobs must be monitored on each backend.
|
||||
- SNI routing depends on clients sending the expected hostname during the TLS handshake. Clients that connect by IP address or omit SNI will not match hostname-specific routers.
|
||||
- Public port `80/tcp` forwards all HTTP paths for configured hostnames to the matching backend.
|
||||
- TCP health checks verify reachability, not certificate validity or application-level correctness.
|
||||
|
||||
+61
-7
@@ -1,19 +1,73 @@
|
||||
# Runbook
|
||||
|
||||
Operational runbook for this project.
|
||||
|
||||
## Common tasks
|
||||
|
||||
Document routine operational tasks here.
|
||||
### Start the load balancer
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Stop the load balancer
|
||||
|
||||
```bash
|
||||
docker compose down
|
||||
```
|
||||
|
||||
### Update backend targets
|
||||
|
||||
1. Edit the relevant site file under `config/dynamic/`.
|
||||
2. Run `docker compose config` to catch Compose-level errors.
|
||||
3. Watch Traefik logs:
|
||||
|
||||
```bash
|
||||
docker compose logs -f traefik
|
||||
```
|
||||
|
||||
Dynamic file-provider changes are watched by Traefik.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
Document known issues, symptoms, checks, and remediation steps.
|
||||
### Container is unhealthy
|
||||
|
||||
Check container status and logs:
|
||||
|
||||
```bash
|
||||
docker compose ps
|
||||
docker compose logs traefik
|
||||
```
|
||||
|
||||
Confirm that `config/traefik.yml` is mounted and that the ping entrypoint is enabled.
|
||||
|
||||
### Backend is unhealthy
|
||||
|
||||
Check Traefik logs and verify that the local backend ports respond from the host:
|
||||
|
||||
```bash
|
||||
curl -H 'Host: lab.younerd.org' http://127.0.0.1:8080/
|
||||
openssl s_client -connect 127.0.0.1:8443 -servername lab.younerd.org
|
||||
```
|
||||
|
||||
Repeat with the hostname and ports from the relevant file under `config/dynamic/`.
|
||||
|
||||
### Clients cannot connect
|
||||
|
||||
Check that the host is listening on `80/tcp` and `443/tcp`, firewall rules allow inbound traffic, and backend IP addresses are reachable from Traefik. For services bound to host loopback, configure backends as `127.0.0.1:PORT`.
|
||||
|
||||
### Certificate HTTP-01 challenge fails
|
||||
|
||||
Check that port `80/tcp` is reachable from the internet and that the matching HTTP service points to the local backend that handles ACME challenge paths.
|
||||
|
||||
### Wrong certificate is presented
|
||||
|
||||
Because TLS is passed through, the certificate comes from the selected backend. Check backend certificate configuration and SNI routing rules in the relevant file under `config/dynamic/`.
|
||||
|
||||
## Rollback
|
||||
|
||||
Document rollback procedures here.
|
||||
Stop the Compose stack:
|
||||
|
||||
## Emergency contacts
|
||||
```bash
|
||||
docker compose down
|
||||
```
|
||||
|
||||
Document project-specific escalation paths if appropriate.
|
||||
Revert the deployment commit or restore the previous load-balancer configuration.
|
||||
|
||||
+37
-12
@@ -1,16 +1,41 @@
|
||||
# Security
|
||||
|
||||
Describe security assumptions and controls.
|
||||
## TLS and certificates
|
||||
|
||||
Include:
|
||||
Traefik is configured for TCP TLS passthrough. It does not terminate TLS and does not require certificate files or private keys.
|
||||
|
||||
- authentication;
|
||||
- authorization;
|
||||
- network exposure;
|
||||
- TLS/certificates;
|
||||
- secrets management;
|
||||
- logging of sensitive data;
|
||||
- container privileges;
|
||||
- filesystem permissions;
|
||||
- dependency management;
|
||||
- relevant ADRs.
|
||||
Backend services are responsible for:
|
||||
|
||||
- presenting SSL certificates;
|
||||
- protecting private keys;
|
||||
- renewing certificates;
|
||||
- enforcing any application-layer authentication and authorization.
|
||||
|
||||
## Network exposure
|
||||
|
||||
Traefik uses host networking and listens directly on host ports `80/tcp` for HTTP traffic and `443/tcp` for TLS passthrough traffic. The Traefik ping entrypoint listens on host port `8082/tcp` for healthchecks; restrict this port with host firewall rules if needed.
|
||||
|
||||
The HTTP routers match configured hostnames and forward the full HTTP request to the selected backend. Backend services are responsible for redirects and ACME challenge handling.
|
||||
|
||||
## Container hardening
|
||||
|
||||
The Traefik service:
|
||||
|
||||
- does not mount the Docker socket;
|
||||
- is not privileged;
|
||||
- drops Linux capabilities except `NET_BIND_SERVICE`, which is required to bind host ports `80` and `443`;
|
||||
- enables `no-new-privileges`;
|
||||
- mounts configuration read-only;
|
||||
- uses an explicit Traefik image tag instead of `latest`.
|
||||
|
||||
## Secrets
|
||||
|
||||
No secrets should be stored in this repository. Do not add certificates, private keys, API tokens, or passwords to `config/` or documentation.
|
||||
|
||||
## Logging
|
||||
|
||||
Traefik logs operational events to stdout. Do not enable debug logging in production unless needed for a time-limited investigation.
|
||||
|
||||
## Relevant ADRs
|
||||
|
||||
- `docs/adr/0001-traefik-tcp-tls-passthrough.md`
|
||||
|
||||
+35
-13
@@ -1,23 +1,45 @@
|
||||
# Testing
|
||||
|
||||
Describe how tests are executed.
|
||||
|
||||
All tests should run inside Docker containers.
|
||||
All project validation should run through Docker-based commands.
|
||||
|
||||
## Canonical test command
|
||||
|
||||
The canonical project validation command is:
|
||||
|
||||
```bash
|
||||
CHANGE_ME
|
||||
docker compose config
|
||||
```
|
||||
|
||||
## Smoke test
|
||||
|
||||
After replacing the example backend addresses and starting the stack, verify HTTP routing:
|
||||
|
||||
```bash
|
||||
curl -H 'Host: lab.younerd.org' http://LOAD_BALANCER_HOST/
|
||||
curl -H 'Host: azionelab.org' http://LOAD_BALANCER_HOST/
|
||||
curl -H 'Host: www.azionelab.org' http://LOAD_BALANCER_HOST/
|
||||
```
|
||||
|
||||
Then verify TCP TLS connectivity:
|
||||
|
||||
```bash
|
||||
openssl s_client -connect LOAD_BALANCER_HOST:443 -servername EXPECTED_DNS_NAME
|
||||
```
|
||||
|
||||
The certificate shown by `openssl` should be the backend certificate, not a certificate stored by Traefik.
|
||||
|
||||
For the included SNI example:
|
||||
|
||||
```bash
|
||||
openssl s_client -connect LOAD_BALANCER_HOST:443 -servername lab.younerd.org
|
||||
openssl s_client -connect LOAD_BALANCER_HOST:443 -servername azionelab.org
|
||||
openssl s_client -connect LOAD_BALANCER_HOST:443 -servername www.azionelab.org
|
||||
```
|
||||
|
||||
## Test categories
|
||||
|
||||
Describe applicable categories:
|
||||
|
||||
- unit tests;
|
||||
- integration tests;
|
||||
- linting;
|
||||
- formatting checks;
|
||||
- Ansible syntax checks;
|
||||
- Docker/Compose validation;
|
||||
- smoke tests.
|
||||
- Docker/Compose validation: `docker compose config`.
|
||||
- Runtime health: `docker compose ps`.
|
||||
- Backend health checks: Traefik load-balancer health checks configured per site.
|
||||
- HTTP route smoke test: `curl`.
|
||||
- TLS passthrough smoke test: `openssl s_client`.
|
||||
|
||||
Reference in New Issue
Block a user