- Published on
Securing internal and external traffic with two Traefik instances
Introduction
Many enterprises, startups, and individuals managing their own homelab encounter the challenge of simultaneously exposing public services to the internet while maintaining exclusive access to certain internal services through VPN connections. As discussed in a previous article where I detailed my setup for cloudflared + traefik + docker, enabling web hosting without exposing ports, this article goes into the process of integrating a secondary Traefik instance into your setup. This second instance efficiently manages traffic for services designated solely for internal usage. Functioning as a gatekeeper, this Traefik instance not only oversees the internal SSL certificate administration but also ensures the proper routing of requests to internal services.
Configuration
Two Traefik instances
The strategy behind employing two Traefik instances hinges on leveraging the power of Traefik's Label
-constraint feature, a mechanism that allows selective filtration of containers based on Docker labels.
To implement this, include the following code snippet in the traefik.yaml
configuration of the Traefik instance responsible for handling external traffic:
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
constraints: "Label(`dev.gero.expose-externally`,`true`)"
This configuration instructs the external Traefik instance to exclusively route traffic to containers carrying the dev.gero.expose-externally=true label. It's worth noting that you can customize the label according to your specific requirements. However, remember to apply this label to your external services prior to restarting the external Traefik instance, as failing to do so could result in the loss of service detection by the external Traefik instance.
With the label-based routing established for external traffic, you can proceed to integrate a second Traefik instance that manages internal traffic.
version: '3'
services:
traefik-internal:
image: traefik:latest
container_name: traefik-internal
restart: unless-stopped
security_opt:
- no-new-privileges:true
networks:
- proxy-internal
ports:
# Bind on secure IP (local, VPN) only
- 100.100.100.100:80:80
- 100.100.100.100:443:443
environment:
- "[email protected]"
- "CF_API_KEY=yourcfapitoken1234"
volumes:
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./data/traefik.yml:/traefik.yml:ro
- ./acme.json:/acme.json
labels:
# Expose Internally
- "dev.gero.expose-internally=true"
- "traefik.enable=true"
- "traefik.http.routers.traefik-internal.entrypoints=http"
- "traefik.http.routers.traefik-internal.rule=Host(`traefik-internal-dashboard.local.my.tld`)"
- "traefik.http.middlewares.traefik-internal-https-redirect.redirectscheme.scheme=https"
- "traefik.http.middlewares.sslheader.headers.customrequestheaders.X-Forwarded-Proto=https"
- "traefik.http.routers.traefik-internal.middlewares=traefik-internal-https-redirect"
- "traefik.http.routers.traefik-internal-secure.entrypoints=https"
- "traefik.http.routers.traefik-internal-secure.rule=Host(`traefik-internal-dashboard.local.my.tld`)"
- "traefik.http.routers.traefik-internal-secure.middlewares=traefik-internal-auth"
- "traefik.http.routers.traefik-internal-secure.tls=true"
- "traefik.http.routers.traefik-internal-secure.tls.certresolver=cloudflare"
- "traefik.http.routers.traefik-internal-secure.tls.domains[0].main=local.my.tld"
- "traefik.http.routers.traefik-internal-secure.tls.domains[0].sans=*.local.my.tld"
- "traefik.http.routers.traefik-internal-secure.service=api@internal"
networks:
proxy-internal:
external: true
Although this looks quite complicated, it's only a couple of components mixed together. The second Traefik instance binds on a designated IP address (like an internal address from 192.168.10.0/24
or from an overlay VPN like Tailscale), ensuring the confinement of internal services within your network and preventing any inadvertent external exposure. Additional labels are assigned to the container to enable the Traefik dashboard with HTTPS redirection and a wildcard certificate. This design ensures that only a certificate for local.my.tld
and *.local.my.tld
is requested from LetsEncrypt, thereby maintaining confidentiality about your internal services and preventing leaks through Certificate Transparency Logs (like crt.sh).
To ensure that the internal Traefik instance solely forwards traffic to internal services, a Label-constraint is also employed. The complete traefik.yaml configuration for the internal Traefik instance takes the form as presented below:
api:
dashboard: true
debug: true
entryPoints:
http:
address: ":80"
https:
address: ":443"
serversTransport:
insecureSkipVerify: true
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
constraints: "Label(`dev.gero.expose-internally`,`true`)"
file:
filename: /config.yml
certificatesResolvers:
cloudflare:
acme:
email: [email protected]
storage: acme.json
dnsChallenge:
provider: cloudflare
resolvers:
- "1.1.1.1:53"
- "1.0.0.1:53"
Conclusion
In the ever-evolving landscape of networking and security, finding solutions to efficiently manage both internal and external services can be a daunting challenge. The approach of employing two Traefik instances, each tailored to distinct traffic needs, emerges as a practical solution to this problem.
By utilizing the Traefik Label
-constraint feature, you can seamlessly integrate two Traefik instances into your infrastructure. The primary Traefik instance, responsible for external traffic, selectively forwards requests based on the presence of a designated label. This ensures that only services with the appropriate label are exposed to the internet, maintaining a secure and controlled external presence.
The secondary Traefik instance handles internal services, providing a comprehensive SSL certificate management system and efficient forwarding of requests within your internal network. The strategic use of IP filtering, HTTPS redirection, and wildcard certificates not only enhances security but also promotes streamlined management of your internal services. This approach safeguards sensitive internal resources from inadvertent exposure while maintaining the flexibility and scalability required in modern networking setups.
In conclusion, the incorporation of two Traefik instances, each attuned to distinct traffic requirements, offers a pragmatic solution for organizations seeking to strike a balance between external accessibility and internal security. This approach showcases the adaptability of Traefik as a tool to facilitate efficient and secure traffic management, regardless of the complexities posed by diverse service environments.