Traefik is the reverse proxy at the heart of the homelab's Docker infrastructure. It handles routing of all HTTP/HTTPS requests to the appropriate containers, with automatic SSL certificate management and security integration via CrowdSec.
This configuration allows Traefik containers to access the Docker host via the name `host.docker.internal`, useful for proxying services running directly on the host.
## traefik-public Instance
### Role and Usage
The **traefik-public** instance manages all services **accessible from the Internet**:
- Public web applications
- Exposed APIs
- Authenticated services accessible from outside
### Network Binding
```yaml
ports:
- "192.168.1.2:80:80"
- "192.168.1.2:443:443"
```
Traefik listens only on IP **192.168.1.2**, corresponding to the VM's first NIC.
### Entry Points
**Port 80 (HTTP)**:
```yaml
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
```
Automatic HTTP to HTTPS redirection for all requests.
**Port 443 (HTTPS)**:
```yaml
websecure:
address: ":443"
http:
middlewares:
- crowdsec-bouncer@file
- secheaders@file
- ratelimit@file
transport:
respondingTimeouts:
idleTimeout: 300s
```
Three middlewares applied by default to all public services:
1.**crowdsec-bouncer**: Blocking malicious IPs detected by CrowdSec
**CrowdSec** is a community-based intrusion detection and prevention system. Traefik-public integrates the **CrowdSec bouncer** to automatically block malicious IPs.
**CrowdSec Middleware** (applied to websecure):
```yaml
middlewares:
- crowdsec-bouncer@file
```
The bouncer queries the local CrowdSec API to check if the source IP is banned. On match, the request is blocked with HTTP 403.
### SSL Certificates
Certificate storage:
```yaml
volumes:
- ./letsencrypt-public:/letsencrypt
```
ACME configuration in `traefik-public.yml`:
```yaml
certificatesResolvers:
cloudflare:
acme:
email: your-email@example.com
storage: /letsencrypt/cloudflare_acme.json
keyType: EC256
dnsChallenge:
provider: cloudflare
resolvers:
- "1.1.1.1:53"
- "8.8.8.8:53"
```
Certificates are automatically renewed 30 days before expiration.
### Logging
```yaml
log:
level: DEBUG
filePath: "/var/log/traefik/traefik.log"
accessLog:
filePath: "/var/log/traefik/access.log"
format: json
```
Logs stored in `/var/log/traefik/` on the Docker host:
- **traefik.log**: Traefik system logs (startup, errors, reloads)
- **access.log**: Access logs in JSON format (HTTP requests)
### Docker Provider Configuration
```yaml
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
network: traefik_network
```
- **exposedByDefault: false**: Containers are not automatically exposed, Traefik labels must be explicitly added
- **network: traefik_network**: Traefik will use this network to communicate with containers
- **Monitoring**: Check logs to detect renewal failures
### Logging and Monitoring
- **Accessible logs**: Mounted as volumes on the host for analysis
- **JSON format**: Facilitates parsing and integration with monitoring tools
- **DEBUG level**: Useful for troubleshooting, can be reduced in production
## Configuration Limitations
While functional and secure, this architecture has certain limitations to be aware of:
### Shared Docker Network
**Issue**: Both Traefik instances (public and private) use the **same Docker network** (`traefik_network`). This means all containers connected to this network can potentially communicate with each other, whether exposed publicly or locally.
**Impact**:
- A container exposed via traefik-public can technically access a container exposed via traefik-private
- Network isolation is incomplete, relying only on IP bindings (192.168.1.2 vs 192.168.1.3)
**Possible Improvement**:
- Create two distinct Docker networks: `traefik_public_network` and `traefik_private_network`
- Connect each Traefik instance only to its dedicated network
- Ensure complete network isolation at the Docker level
### Lack of VLAN Segmentation
**Issue**: Both VM NICs share the same physical network (192.168.1.0/24) without VLAN segmentation.
**Impact**:
- The NIC for traefik-private (192.168.1.3) technically has Internet access via the network gateway, when it doesn't need it
- No network isolation at L2/L3 level between public and private interfaces
- In case of compromise, an attacker could potentially pivot between both networks
**Possible Improvement**:
- **Public VLAN**: Place the traefik-public NIC (192.168.1.2) in a VLAN with Internet access
- **Private VLAN**: Place the traefik-private NIC (192.168.1.3) in an isolated VLAN without Internet access
- Configure strict firewall rules between VLANs
- This segmentation would significantly strengthen isolation and limit attack surface
### Docker Socket Access
**Issue**: Both Traefik instances have **direct and complete** access to the Docker socket (`/var/run/docker.sock`). The Docker socket is Docker's administration API, giving full control over the host.
**Security Impact**:
- A compromised Traefik container could control all host containers
- Possibility of privilege escalation (launching containers in privileged mode, mounting sensitive volumes, etc.)
- Read-only access (`ro`) limits damage, but still allows extracting sensitive information (environment variables, secrets, etc.)
**Possible Improvement**:
- Use a **Docker socket proxy** like [Tecnativa/docker-socket-proxy](https://github.com/Tecnativa/docker-socket-proxy)
- This proxy allows fine-grained filtering of allowed operations (e.g., only read containers and their labels)
- Reduce attack surface by limiting API access to endpoints strictly necessary for Traefik
Configuration example:
```yaml
docker-socket-proxy:
image: tecnativa/docker-socket-proxy
environment:
CONTAINERS: 1 # Allow container reading
NETWORKS: 1 # Allow network reading
SERVICES: 0 # Deny access to Swarm services
TASKS: 0 # Deny access to Swarm tasks
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
```
These improvements are not critical for a homelab but would be **strongly recommended in production environments**.