mirror of
https://github.com/magnus919/agent-skills.git
synced 2026-09-11 19:47:12 +03:00
fix: format compliance, v3.7, expanded migration, serversTransport ref, templates, healthcheck
Phase 1 — Format compliance: - Add version (0.1.0) and compatibility fields to frontmatter - Tighten description to trigger-first style (~174 chars) - Fix 'When NOT to Use' contradiction on plugins - Remove non-standard spec-version from metadata Phase 2 — Content enrichment: - Bump all image references from v3.2 to v3.7 across 7 files - Add pre-migration audit checklist (10 items) to migration reference - Add tracing/provider removal detail to migration reference - Create servers-transport.md (191 lines) extracted from static-configuration.md with expanded mTLS, SPIFFE, CRD coverage Phase 3 — Templates & scripts: - Add templates/docker-compose.yml — production compose with socket proxy, Let's Encrypt, dashboard auth, HTTP/3, security hardening - Add scripts/traefik-healthcheck.sh — agent-compatible health check with --json output, checks ping, API, router count, certificate expiry Signed-off-by: Jasper <magnus@groktop.us>
This commit is contained in:
+19
-7
@@ -1,14 +1,16 @@
|
||||
---
|
||||
name: traefik
|
||||
description: >-
|
||||
Deploy, configure, secure, and maintain Traefik v3 reverse proxy — Docker
|
||||
provider, HTTP/TCP/UDP routing, TLS/ACME (Let's Encrypt), middlewares,
|
||||
observability, API, and production deployment. Load when configuring or
|
||||
troubleshooting a Traefik instance.
|
||||
Deploy, configure, and troubleshoot Traefik v3 reverse proxy — covers all
|
||||
providers, routing, TLS/ACME, middlewares, and production patterns with
|
||||
YAML examples. Load when setting up or debugging a Traefik instance.
|
||||
license: MIT
|
||||
version: 0.1.0
|
||||
compatibility: >-
|
||||
Compatible with any agent supporting the Agent Skills format (Hermes Agent,
|
||||
Claude Code, GitHub Copilot, OpenCode, Cursor, etc.)
|
||||
metadata:
|
||||
source: https://doc.traefik.io/traefik/
|
||||
spec-version: "1.0"
|
||||
---
|
||||
|
||||
# Traefik Agent Skill
|
||||
@@ -17,11 +19,20 @@ Comprehensive reference for deploying, configuring, and maintaining **Traefik v3
|
||||
|
||||
## Quick Start — Minimal Docker Deployment
|
||||
|
||||
A production-ready Docker Compose template is available at `templates/docker-compose.yml`. For a quick test:
|
||||
|
||||
### One-Line Health Check
|
||||
|
||||
```bash
|
||||
bash scripts/traefik-healthcheck.sh # Text output
|
||||
bash scripts/traefik-healthcheck.sh --json # JSON output for agents
|
||||
```
|
||||
|
||||
```yaml
|
||||
# docker-compose.yml
|
||||
services:
|
||||
traefik:
|
||||
image: traefik:v3.2
|
||||
image: traefik:v3.7
|
||||
command:
|
||||
# Static configuration via CLI args
|
||||
- "--providers.docker=true"
|
||||
@@ -66,6 +77,7 @@ The request flow: `EntryPoint → Router → (Middlewares) → Service → Backe
|
||||
| **Observability** | Prometheus/OTel metrics, access logs, tracing, health checks | `references/observability.md` |
|
||||
| **v2→v3 Migration** | Breaking changes, rule syntax update, deprecated options | `references/migration-v2-to-v3.md` |
|
||||
| **Production Patterns** | Docker Compose template, security hardening, HA, monitoring | `references/production-deployment.md` |
|
||||
| **Servers Transport** | Backend connection config, mTLS to backends, connection pooling, SPIFFE | `references/servers-transport.md` |
|
||||
| **Kubernetes Providers** | Deploying Traefik in K8s — Ingress, CRD (IngressRoute), Gateway API | `references/kubernetes-providers.md` |
|
||||
| **Other Providers** | ECS, Nomad, Consul Catalog, KV stores, File, HTTP, REST providers | `references/other-providers.md` |
|
||||
| **Community Patterns** | Production wisdom — middleware ordering, performance tuning, CDN real-IP, CrowdSec, Authelia, troubleshooting | `references/community-patterns.md` |
|
||||
@@ -88,4 +100,4 @@ The request flow: `EntryPoint → Router → (Middlewares) → Service → Backe
|
||||
## When NOT to Use This Skill
|
||||
|
||||
- For Traefik Hub, Traefik Enterprise, or Traefik Mesh — these are separate products with different APIs
|
||||
- For writing Traefik plugins — see the official plugin development documentation at https://plugins.traefik.io/create
|
||||
- For developing Traefik plugins (Yaegi or WASM) — this skill covers *using* configured plugins, not writing them. See https://plugins.traefik.io/create for plugin development.
|
||||
|
||||
@@ -46,7 +46,7 @@ Or with Docker labels:
|
||||
```yaml
|
||||
services:
|
||||
traefik:
|
||||
image: traefik:v3.2
|
||||
image: traefik:v3.7
|
||||
labels:
|
||||
- "traefik.http.routers.dashboard.rule=Host(`traefik.example.com`)"
|
||||
- "traefik.http.routers.dashboard.service=api@internal"
|
||||
|
||||
@@ -370,7 +370,7 @@ Request → Traefik → CrowdSec Bouncer (ForwardAuth) → Backend Service
|
||||
```yaml
|
||||
services:
|
||||
traefik:
|
||||
image: traefik:v3.2
|
||||
image: traefik:v3.7
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- ./logs:/var/log/traefik
|
||||
|
||||
@@ -20,7 +20,7 @@ providers:
|
||||
```yaml
|
||||
services:
|
||||
traefik:
|
||||
image: traefik:v3.2
|
||||
image: traefik:v3.7
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock # Required
|
||||
- ./traefik.yml:/etc/traefik/traefik.yml # Static config
|
||||
|
||||
@@ -11,7 +11,27 @@ This covers the key changes when migrating from Traefik v2 to v3. Full details:
|
||||
| `Path` matcher wildcards | Supported | Removed (use `PathRegexp` or `PathPrefix`) |
|
||||
| `insecure` field renamed | N/A | `tls.insecureSkipVerify` on serversTransport |
|
||||
| X-Forwarded-For behavior | Always appended | Configurable via `forwardedHeaders` |
|
||||
| Deprecated field removals | Various | Strictly enforced removal |
|
||||
| Tracing providers | Jaeger, Zipkin, Datadog, Elastic | OpenTelemetry (OTLP) only |
|
||||
| Content-Type handling | Auto-detected | Must use `ContentType` middleware |
|
||||
| IPWhiteList renamed | `IPWhiteList` | `IPAllowList` |
|
||||
| Provider removals | Marathon, Rancher v1, InfluxDB v1, Pilot | Removed entirely |
|
||||
| `tls.caOptional` | Available | Removed from all providers |
|
||||
| Deprecated field removals | Various | Strictly enforced — v3 refuses to start with deprecated fields |
|
||||
|
||||
## Pre-Migration Audit Checklist
|
||||
|
||||
Before upgrading, run through these checks:
|
||||
|
||||
- [ ] **Rule syntax audit** — Search config files for `HostRegexp` with wildcard patterns (`.+\.`); replace with `Host(`*.`)`
|
||||
- [ ] **Path wildcard audit** — Search for curly-brace path patterns like `/api/{version:...}`; these need `PathRegexp` replacements
|
||||
- [ ] **TLS compatibility check** — Verify clients support TLS 1.2+ (default changed from TLS 1.0)
|
||||
- [ ] **ServersTransport rename** — Replace all `insecure: true` with `insecureSkipVerify: true`
|
||||
- [ ] **Tracing migration** — If using Jaeger/Zipkin/Datadog/Elastic APM, migrate to OpenTelemetry
|
||||
- [ ] **ContentType audit** — If you relied on Traefik auto-detecting Content-Type, add the `ContentType` middleware
|
||||
- [ ] **IPWhiteList → IPAllowList** — Rename any `ipWhiteList` config blocks to `ipAllowList`
|
||||
- [ ] **Remove deprecated providers** — If using Marathon, Rancher, InfluxDB v1 metrics, or Pilot, find alternatives
|
||||
- [ ] **Default certificate** — If you relied on SNI fallback, configure a default certificate explicitly
|
||||
- [ ] **Test in staging** — Run a v3 instance alongside v2 with `core.defaultRuleSyntax: v2` for gradual migration
|
||||
|
||||
## Rule Syntax Changes
|
||||
|
||||
@@ -21,10 +41,10 @@ The v3 rule syntax is the default. If you have v2 rules, they'll get a deprecati
|
||||
|
||||
```yaml
|
||||
# v2 — required HostRegexp for wildcards
|
||||
rule: "HostRegexp(`.+\.example\.com`)"
|
||||
rule: "HostRegexp(`.+\\.example\\.com`)"
|
||||
|
||||
# v3 — native wildcard support (preferred)
|
||||
rule: "Host(`*.example.com`)"
|
||||
rule: "Host(`*.example.com`)" # matches foo.example.com, NOT foo.bar.example.com
|
||||
```
|
||||
|
||||
### Path Wildcards Removed
|
||||
@@ -45,7 +65,7 @@ To use v2 syntax during migration, set per-router:
|
||||
http:
|
||||
routers:
|
||||
legacy-router:
|
||||
rule: "HostRegexp(`.+\.example\.com`)"
|
||||
rule: "HostRegexp(`.+\\.example\\.com`)"
|
||||
ruleSyntax: "v2" # Per-router override
|
||||
```
|
||||
|
||||
@@ -67,9 +87,9 @@ tls:
|
||||
minVersion: VersionTLS12
|
||||
```
|
||||
|
||||
## TLS Certificate Fallback
|
||||
### TLS Certificate Fallback
|
||||
|
||||
In v3, when no TLS certificate matches the requested SNI, Traefik v3 returns a certificate error instead of falling back to a default certificate. Configure a default certificate explicitly:
|
||||
In v3, when no TLS certificate matches the requested SNI, Traefik returns a certificate error instead of falling back to a default certificate. Configure a default certificate explicitly:
|
||||
|
||||
```yaml
|
||||
tls:
|
||||
@@ -80,6 +100,37 @@ tls:
|
||||
keyFile: "/certs/default-key.pem"
|
||||
```
|
||||
|
||||
## Tracing Changes (OTLP Only)
|
||||
|
||||
v3 removed direct support for Jaeger, Zipkin, Datadog APM, and Elastic APM tracing. All tracing now goes through OpenTelemetry (OTLP):
|
||||
|
||||
```yaml
|
||||
# v3 — OTLP only
|
||||
tracing:
|
||||
serviceName: "traefik"
|
||||
sampleRate: 1.0
|
||||
otlp:
|
||||
grpc:
|
||||
endpoint: "localhost:4317"
|
||||
insecure: true
|
||||
http:
|
||||
endpoint: "localhost:4318"
|
||||
```
|
||||
|
||||
Migrate from Jaeger/Zipkin agents by running an OpenTelemetry Collector as a sidecar that forwards to your existing backend.
|
||||
|
||||
## Content-Type Handling
|
||||
|
||||
v3 removed automatic Content-Type detection. Responses without an explicit Content-Type header may be handled differently:
|
||||
|
||||
```yaml
|
||||
# v3 — add this middleware to preserve v2 behavior
|
||||
http:
|
||||
middlewares:
|
||||
auto-content-type:
|
||||
contentType: {}
|
||||
```
|
||||
|
||||
## ServersTransport Changes
|
||||
|
||||
The `insecure` field has been renamed for clarity:
|
||||
@@ -120,12 +171,26 @@ entryPoints:
|
||||
# When set to true, Traefik will NOT append client's RemoteAddr to X-Forwarded-For
|
||||
```
|
||||
|
||||
## Provider Removals
|
||||
|
||||
The following providers were removed in v3 with no replacement:
|
||||
|
||||
| Removed Provider | Alternative |
|
||||
|-----------------|-------------|
|
||||
| Marathon | Use File provider or migrate to Kubernetes/Nomad |
|
||||
| Rancher v1 | Use File provider or Rancher v2's Kubernetes ingress |
|
||||
| InfluxDB v1 metrics | Use InfluxDB v2 or Prometheus |
|
||||
| Pilot | Use Traefik Hub or forward auth middlewares |
|
||||
|
||||
## Migration Steps
|
||||
|
||||
1. **Update static config** — review all entryPoints, providers, and TLS settings
|
||||
2. **Update rule syntax** — replace `HostRegexp` wildcards with `Host(*)`, remove wildcards from `Path` matchers
|
||||
3. **Check TLS defaults** — verify min TLS version compatibility with clients
|
||||
4. **Update ServersTransport** — rename `insecure` to `insecureSkipVerify`
|
||||
5. **Test with staging** — use `core.defaultRuleSyntax: v2` during migration and test each router
|
||||
6. **Add default certificate** — if you relied on the TLS fallback behavior
|
||||
7. **Test** — run a staging instance alongside v2, verify all routes work
|
||||
1. **Run the pre-migration audit** — check all 10 items above before changing anything
|
||||
2. **Update static config** — review all entryPoints, providers, and TLS settings
|
||||
3. **Update rule syntax** — replace `HostRegexp` wildcards with `Host(*)`, remove wildcards from `Path` matchers
|
||||
4. **Check TLS defaults** — verify min TLS version compatibility with clients
|
||||
5. **Update ServersTransport** — rename `insecure` to `insecureSkipVerify`
|
||||
6. **Migrate tracing** — switch from direct Jaeger/Zipkin to OpenTelemetry
|
||||
7. **Test with staging** — use `core.defaultRuleSyntax: v2` during migration and test each router
|
||||
8. **Add default certificate** — if you relied on the TLS fallback behavior
|
||||
9. **Run a parallel stack** — deploy v3 alongside v2, redirect a subset of traffic, verify all routes work
|
||||
10. **Cut over** — switch production traffic to v3, keep v2 as rollback target for 48 hours
|
||||
|
||||
@@ -9,7 +9,7 @@ version: "3.8"
|
||||
|
||||
services:
|
||||
traefik:
|
||||
image: traefik:v3.2
|
||||
image: traefik:v3.7
|
||||
restart: unless-stopped
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
@@ -113,7 +113,7 @@ services:
|
||||
- TASKS=1
|
||||
|
||||
traefik:
|
||||
image: traefik:v3.2
|
||||
image: traefik:v3.7
|
||||
environment:
|
||||
- DOCKER_HOST=tcp://docker-proxy:2375
|
||||
# Do NOT mount the docker socket directly
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
# ServersTransport Reference
|
||||
|
||||
Defines how Traefik connects to backend servers. Configured in static config and referenced per-service in dynamic config.
|
||||
|
||||
## HTTP ServersTransport
|
||||
|
||||
Controls the connection between Traefik and HTTP backend services.
|
||||
|
||||
```yaml
|
||||
# Static config
|
||||
serversTransport:
|
||||
insecureSkipVerify: false # Skip TLS verification to backend
|
||||
rootCAs: # List of root CA certificate paths
|
||||
- /etc/traefik/certs/ca.pem
|
||||
maxIdleConnsPerHost: 200 # Max idle connections per backend host
|
||||
forwardingTimeouts:
|
||||
dialTimeout: 30s # TCP dial timeout to backend
|
||||
responseHeaderTimeout: 0s # Timeout for backend response headers (0=unlimited)
|
||||
idleConnTimeout: 90s # Idle keep-alive connection timeout
|
||||
spiffe:
|
||||
ids: [] # SPIFFE IDs for workload identity
|
||||
trustDomain: "" # SPIFFE trust domain
|
||||
```
|
||||
|
||||
### Configuration Options
|
||||
|
||||
| Option | Description | Default |
|
||||
|--------|-------------|---------|
|
||||
| `insecureSkipVerify` | Skip TLS certificate verification for backend connections | `false` |
|
||||
| `rootCAs` | Paths to PEM-encoded CA certificates for backend TLS verification | `[]` |
|
||||
| `maxIdleConnsPerHost` | Maximum idle connections kept per backend host | `200` |
|
||||
| `forwardingTimeouts.dialTimeout` | Maximum time to establish TCP connection to backend | `30s` |
|
||||
| `forwardingTimeouts.responseHeaderTimeout` | Maximum time to wait for response headers from backend | `0s` (no timeout) |
|
||||
| `forwardingTimeouts.idleConnTimeout` | Maximum time a keep-alive connection can remain idle | `90s` |
|
||||
| `spiffe.ids` | Allowed SPIFFE identities for workload identity | `[]` |
|
||||
| `spiffe.trustDomain` | SPIFFE trust domain for workload identity | `""` |
|
||||
|
||||
## TCP ServersTransport
|
||||
|
||||
Controls connections for TCP routing (non-HTTP backends).
|
||||
|
||||
```yaml
|
||||
# Static config
|
||||
tcpServersTransport:
|
||||
dialKeepAlive: 30s # Keep-alive probe interval for TCP connections
|
||||
dialTimeout: 30s # TCP dial timeout
|
||||
terminationDelay: 0s # Delay before terminating TCP connection on shutdown
|
||||
tls:
|
||||
insecureSkipVerify: false # Skip TLS verification
|
||||
rootCAs: # Root CA certificates for backend TLS
|
||||
- /etc/traefik/certs/ca.pem
|
||||
spiffe:
|
||||
ids: []
|
||||
trustDomain: ""
|
||||
```
|
||||
|
||||
### Configuration Options
|
||||
|
||||
| Option | Description | Default |
|
||||
|--------|-------------|---------|
|
||||
| `dialKeepAlive` | TCP keep-alive probe interval | `30s` |
|
||||
| `dialTimeout` | TCP dial timeout | `30s` |
|
||||
| `terminationDelay` | Delay before TCP connection termination on shutdown | `0s` |
|
||||
| `tls.insecureSkipVerify` | Skip TLS verification for backend | `false` |
|
||||
| `tls.rootCAs` | Root CA certificate paths | `[]` |
|
||||
| `tls.spiffe.ids` | Allowed SPIFFE identities | `[]` |
|
||||
| `tls.spiffe.trustDomain` | SPIFFE trust domain | `""` |
|
||||
|
||||
## Per-Service ServersTransport Override
|
||||
|
||||
Reference a named serversTransport from a specific service in dynamic config:
|
||||
|
||||
```yaml
|
||||
# Dynamic config (File provider)
|
||||
http:
|
||||
serversTransports:
|
||||
mTLS-to-backend:
|
||||
insecureSkipVerify: false
|
||||
rootCAs:
|
||||
- /etc/traefik/certs/internal-ca.pem
|
||||
forwardingTimeouts:
|
||||
dialTimeout: 10s
|
||||
|
||||
services:
|
||||
secure-api:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "https://backend.internal:443"
|
||||
serversTransport: "mTLS-to-backend@file"
|
||||
```
|
||||
|
||||
Via Docker labels:
|
||||
|
||||
```yaml
|
||||
labels:
|
||||
- "traefik.http.services.api.loadbalancer.serverstransport=mTLS-to-backend@file"
|
||||
```
|
||||
|
||||
## mTLS Between Traefik and Backends
|
||||
|
||||
For mutual TLS (backends that require client certificates):
|
||||
|
||||
```yaml
|
||||
# Dynamic config
|
||||
http:
|
||||
serversTransports:
|
||||
mtls:
|
||||
insecureSkipVerify: false
|
||||
rootCAs:
|
||||
- /etc/traefik/certs/ca.pem # CA that signed the backend cert
|
||||
# NOTE: serversTransport does NOT support client cert/key fields.
|
||||
# For client certificate authentication to backends, configure
|
||||
# TLS at the service level or use ForwardAuth.
|
||||
|
||||
services:
|
||||
internal-api:
|
||||
loadBalancer:
|
||||
serversTransport: "mtls@file"
|
||||
servers:
|
||||
- url: "https://api.internal:8443"
|
||||
```
|
||||
|
||||
## Kubernetes CRD Reference
|
||||
|
||||
In Kubernetes, define a `ServersTransport` CRD:
|
||||
|
||||
```yaml
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: ServersTransport
|
||||
metadata:
|
||||
name: mtls-transport
|
||||
spec:
|
||||
serverName: "api.internal"
|
||||
insecureSkipVerify: false
|
||||
rootCAs:
|
||||
- secret: internal-ca-secret # Kubernetes Secret reference
|
||||
forwardingTimeouts:
|
||||
dialTimeout: 30s
|
||||
responseHeaderTimeout: 30s
|
||||
idleConnTimeout: 90s
|
||||
```
|
||||
|
||||
Reference in an IngressRoute:
|
||||
|
||||
```yaml
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
spec:
|
||||
routes:
|
||||
- kind: Rule
|
||||
match: Host(`app.example.com`)
|
||||
services:
|
||||
- name: api-service
|
||||
port: 8443
|
||||
serversTransport: mtls-transport
|
||||
```
|
||||
|
||||
For TCP services, use `ServersTransportTCP`:
|
||||
|
||||
```yaml
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: ServersTransportTCP
|
||||
metadata:
|
||||
name: tcp-mtls
|
||||
spec:
|
||||
tls:
|
||||
insecureSkipVerify: false
|
||||
serverName: "db.internal"
|
||||
rootCAs:
|
||||
- secret: db-ca-secret
|
||||
```
|
||||
|
||||
## Connection Pooling Best Practices
|
||||
|
||||
```yaml
|
||||
# For high-throughput APIs — minimize connection churn
|
||||
serversTransport:
|
||||
maxIdleConnsPerHost: 500
|
||||
forwardingTimeouts:
|
||||
dialTimeout: 5s # Fast fail on unavailable backends
|
||||
responseHeaderTimeout: 30s
|
||||
idleConnTimeout: 120s # Longer idle time reduces reconnects
|
||||
|
||||
# For latency-sensitive services — tight timeouts
|
||||
serversTransport:
|
||||
maxIdleConnsPerHost: 50
|
||||
forwardingTimeouts:
|
||||
dialTimeout: 3s
|
||||
responseHeaderTimeout: 10s
|
||||
idleConnTimeout: 30s
|
||||
```
|
||||
@@ -33,6 +33,10 @@ spiffe:
|
||||
workloadAPIAddr: "" # SPIRE agent socket path
|
||||
|
||||
## Server Transport (HTTP)
|
||||
|
||||
See `references/servers-transport.md` for full detail on serversTransport, tcpServersTransport, connection pooling, backend TLS, and per-service transport overrides.
|
||||
|
||||
```yaml
|
||||
serversTransport:
|
||||
insecureSkipVerify: false # Skip TLS verification to backend
|
||||
rootCAs: # List of root CA certificates
|
||||
@@ -47,6 +51,8 @@ serversTransport:
|
||||
trustDomain: ""
|
||||
|
||||
## TCP Server Transport
|
||||
|
||||
```yaml
|
||||
tcpServersTransport:
|
||||
dialKeepAlive: 30s
|
||||
dialTimeout: 30s
|
||||
@@ -57,6 +63,7 @@ tcpServersTransport:
|
||||
spiffe:
|
||||
ids: []
|
||||
trustDomain: ""
|
||||
```
|
||||
|
||||
## EntryPoints — Network listeners
|
||||
entryPoints:
|
||||
|
||||
@@ -172,7 +172,7 @@ labels:
|
||||
# docker-compose.yml
|
||||
services:
|
||||
traefik:
|
||||
image: traefik:v3.2
|
||||
image: traefik:v3.7
|
||||
command:
|
||||
- "--entrypoints.postgres.address=:5432"
|
||||
- "--providers.docker=true"
|
||||
|
||||
Executable
+96
@@ -0,0 +1,96 @@
|
||||
#!/usr/bin/env bash
|
||||
# traefik-healthcheck.sh — Check a running Traefik instance health
|
||||
# Usage: ./traefik-healthcheck.sh [--json] [--url https://traefik.example.com]
|
||||
#
|
||||
# Requires: curl, jq
|
||||
# Non-interactive, agent-friendly.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Defaults
|
||||
URL="${TRAEFIK_URL:-http://localhost:8080}"
|
||||
JSON=false
|
||||
EXIT_CODE=0
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--json) JSON=true; shift ;;
|
||||
--url) URL="$2"; shift 2 ;;
|
||||
--help|-h)
|
||||
echo "Usage: traefik-healthcheck.sh [--json] [--url <traefik-url>]"
|
||||
echo ""
|
||||
echo "Checks ping, API, version, router counts, and certificate expiry."
|
||||
echo "Defaults to http://localhost:8080. Set TRAEFIK_URL env var to override."
|
||||
exit 0
|
||||
;;
|
||||
*) echo "Unknown option: $1"; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
json_out() {
|
||||
if $JSON; then
|
||||
echo "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
text_out() {
|
||||
if ! $JSON; then
|
||||
echo "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
# 1. Ping check
|
||||
ping_status=$(curl -s -o /dev/null -w "%{http_code}" "${URL}/ping" 2>/dev/null || echo "000")
|
||||
if [ "$ping_status" = "200" ]; then
|
||||
text_out "✓ Ping: OK (200)"
|
||||
json_out "{\"ping\": {\"status\": \"ok\", \"code\": 200}"
|
||||
else
|
||||
text_out "✗ Ping: FAILED ($ping_status)"
|
||||
json_out "{\"ping\": {\"status\": \"fail\", \"code\": $ping_status}"
|
||||
EXIT_CODE=1
|
||||
fi
|
||||
|
||||
# 2. API health check
|
||||
api_status=$(curl -s -o /dev/null -w "%{http_code}" "${URL}/api/version" 2>/dev/null || echo "000")
|
||||
if [ "$api_status" = "200" ]; then
|
||||
version=$(curl -s "${URL}/api/version" 2>/dev/null | jq -r '.Version // "unknown"' 2>/dev/null || echo "unknown")
|
||||
text_out "✓ API: OK ($api_status) — Traefik $version"
|
||||
json_out ", \"api\": {\"status\": \"ok\", \"version\": \"$version\"}"
|
||||
else
|
||||
text_out "✗ API: FAILED ($api_status) — is the API enabled?"
|
||||
json_out ", \"api\": {\"status\": \"fail\", \"code\": $api_status}"
|
||||
EXIT_CODE=1
|
||||
fi
|
||||
|
||||
# 3. Router count
|
||||
routers=$(curl -s "${URL}/api/http/routers" 2>/dev/null | jq length 2>/dev/null || echo "N/A")
|
||||
text_out " HTTP routers: $routers"
|
||||
json_out ", \"routers\": {\"count\": $routers}"
|
||||
|
||||
tcp_routers=$(curl -s "${URL}/api/tcp/routers" 2>/dev/null | jq length 2>/dev/null || echo "N/A")
|
||||
text_out " TCP routers: $tcp_routers"
|
||||
json_out ", \"tcp_routers\": {\"count\": $tcp_routers}"
|
||||
|
||||
# 4. Overview
|
||||
overview=$(curl -s "${URL}/api/overview" 2>/dev/null || echo "{}")
|
||||
if [ "$overview" != "{}" ]; then
|
||||
total=$(echo "$overview" | jq -r '.http.routers.total // 0' 2>/dev/null)
|
||||
text_out " Total HTTP routers (overview): $total"
|
||||
fi
|
||||
|
||||
# 5. Certificate expiry check
|
||||
certs=$(curl -s "${URL}/api/rawconfig" 2>/dev/null | jq -r '.tls.certificates // []' 2>/dev/null || echo "[]")
|
||||
if [ "$certs" != "[]" ]; then
|
||||
cert_count=$(echo "$certs" | jq length)
|
||||
text_out " TLS certificates: $cert_count"
|
||||
json_out ", \"tls_certificates\": {\"count\": $cert_count}"
|
||||
else
|
||||
json_out ", \"tls_certificates\": null"
|
||||
fi
|
||||
|
||||
# Close JSON
|
||||
if $JSON; then
|
||||
echo "}"
|
||||
fi
|
||||
|
||||
exit $EXIT_CODE
|
||||
@@ -0,0 +1,107 @@
|
||||
# Production Traefik Deployment — Docker Compose
|
||||
# Target: Traefik v3.7+
|
||||
# Usage: docker compose up -d
|
||||
#
|
||||
# Features:
|
||||
# - Docker provider (read-only socket via docker-socket-proxy)
|
||||
# - Let's Encrypt ACME (HTTP-01 challenge)
|
||||
# - Dashboard with BasicAuth (internal-only)
|
||||
# - Security hardening (read-only rootfs, no-new-privileges, capability drop)
|
||||
# - Prometheus metrics endpoint
|
||||
# - JSON structured logging
|
||||
# - HTTP/3 (QUIC) enabled
|
||||
|
||||
version: "3.8"
|
||||
|
||||
x-logging: &default-logging
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
|
||||
services:
|
||||
# --- Docker Socket Proxy (Security) ---
|
||||
# Replaces direct docker.sock mount with a read-only API proxy
|
||||
docker-proxy:
|
||||
image: tecnativa/docker-socket-proxy:latest
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
environment:
|
||||
- CONTAINERS=1
|
||||
- NETWORKS=1
|
||||
- SERVICES=1
|
||||
- TASKS=1
|
||||
- INFO=1
|
||||
networks:
|
||||
- traefik
|
||||
logging: *default-logging
|
||||
|
||||
# --- Traefik Reverse Proxy ---
|
||||
traefik:
|
||||
image: traefik:v3.7
|
||||
restart: unless-stopped
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
cap_drop:
|
||||
- ALL
|
||||
cap_add:
|
||||
- NET_BIND_SERVICE
|
||||
networks:
|
||||
- traefik
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
# UDP port for HTTP/3 (QUIC)
|
||||
- "443:443/udp"
|
||||
environment:
|
||||
# DNS challenge credentials (example: Cloudflare)
|
||||
# - CF_DNS_API_TOKEN=${CF_DNS_API_TOKEN}
|
||||
- TZ=UTC
|
||||
volumes:
|
||||
- ./traefik.yml:/etc/traefik/traefik.yml:ro
|
||||
- ./dynamic:/etc/traefik/dynamic:ro
|
||||
- ./letsencrypt:/letsencrypt
|
||||
# Do NOT mount docker.sock directly — use docker-proxy
|
||||
command:
|
||||
# Providers
|
||||
- "--providers.docker=true"
|
||||
- "--providers.docker.endpoint=tcp://docker-proxy:2375"
|
||||
- "--providers.docker.exposedbydefault=false"
|
||||
- "--providers.docker.network=traefik"
|
||||
# File provider for shared middlewares
|
||||
- "--providers.file.directory=/etc/traefik/dynamic"
|
||||
- "--providers.file.watch=true"
|
||||
# EntryPoints
|
||||
- "--entrypoints.web.address=:80"
|
||||
- "--entrypoints.websecure.address=:443"
|
||||
- "--entrypoints.websecure.http3=true"
|
||||
# TLS
|
||||
- "--certificatesresolvers.letsencrypt.acme.email=admin@example.com"
|
||||
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
|
||||
- "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
|
||||
- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
|
||||
# API (dashboard served through a router, not directly)
|
||||
- "--api.dashboard=true"
|
||||
- "--api.insecure=false"
|
||||
# Logging
|
||||
- "--log.level=INFO"
|
||||
- "--log.format=json"
|
||||
- "--accesslog=true"
|
||||
- "--accesslog.format=json"
|
||||
- "--accesslog.addinternals=false"
|
||||
labels:
|
||||
# Dashboard router
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.dashboard.rule=Host(`traefik.example.com`)"
|
||||
- "traefik.http.routers.dashboard.service=api@internal"
|
||||
- "traefik.http.routers.dashboard.tls=true"
|
||||
- "traefik.http.routers.dashboard.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.routers.dashboard.middlewares=dashboard-auth"
|
||||
- "traefik.http.middlewares.dashboard-auth.basicauth.users=${DASHBOARD_AUTH:-admin:$$2y$$10$$...}"
|
||||
logging: *default-logging
|
||||
|
||||
networks:
|
||||
traefik:
|
||||
name: traefik
|
||||
driver: bridge
|
||||
Reference in New Issue
Block a user