Hi all,
I'm trying to harden the security of my ghost blog's admin interface. I managed it with the Traefik dashboard but seem unable to do so for the blog. The blog admin url is blog.mydomain.com/ghost
Whatever I do, I seem to be unable to protect "/ghost" with the Traefik authentication. Does anyone have a tip on what I might be doing wrong? The following are sections of the setup I tried
traefik.yaml
http:
routers:
dashboard:
rule: Host(`traefik.mydomain.com`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))
service: api@internal
middlewares:
- auth
rtr-blog-admin:
rule: Host(`blog.mydomain.com`) && PathPrefix(`/ghost`)
service: svc-blog-admin
middlewares:
- auth
services:
svc-blog-admin:
loadBalancer:
servers:
- url: "https://blog.mydomain.com/ghost"
middlewares:
auth:
digestAuth:
users:
- "myuser:traefik:xxxxxxxxxxxxx"
docker-compose.yaml (blog service label section)
labels:
- "traefik.enable=true"
- "traefik.http.routers.blog.rule=Host(`blog.mydomain.com`)"
- "traefik.http.routers.blog.entrypoints=websecure"
- "traefik.http.routers.blog.tls.certresolver=production"
Thanks for any advice you may have.
Hi @ndy3yju1,
Thanks for your interest in Traefik.
To me, your blog admin panel URL is weird in your service. It seems you are pointing to the
same URL than the incoming request which could lead to unexpected behaviors.
Don't you want to use the real service url (e.g: https://my-blog-url-in-docker
)?
If it does not help, could you provide your docker-compose file?
Thanks,
Maxence
Hi @moutoum,
Thanks for your help. I am still in the early stages of learning how traefik works. I got a few things working but I'm still confused about the interaction and syntax of the different "blocks".
In anycase, here is a somewhat redacted copy of my docker file and traefik file.
docker-compose.yaml
services:
blog:
container_name: blog
hostname: blog
image: ghost:5-alpine
restart: always
depends_on:
- blog_db
environment:
# redacted
NODE_ENV: production
labels:
- "traefik.enable=true"
- "traefik.http.routers.blog.rule=Host(`blog.mydomain.com`)"
- "traefik.http.routers.blog.entrypoints=websecure"
- "traefik.http.routers.blog.tls.certresolver=production"
volumes:
- blog-content:/var/lib/ghost/content
networks:
- blog
blog_db:
image: mysql:8.0
command: --default-authentication-plugin=mysql_native_password
container_name: blog_db
restart: always
environment:
# redacted
volumes:
- blog-db:/var/lib/mysql
networks:
- blog
traefik.yml
global:
checkNewVersion: false
sendAnonymousUsage: true
log:
level: INFO # DEBUG, INFO, WARNING, ERROR, CRITICAL
accessLog:
filePath: "/var/log/traefik/access.log"
format: json
api:
dashboard: true
insecure: false
http:
routers:
dashboard:
rule: # redacted
service: api@internal
middlewares:
- auth
rtr-blog-admin:
rule: Host(`blog.mydomain.com`) && PathPrefix(`/ghost`)
service: svc-blog-admin
middlewares:
- auth
services:
svc-blog-admin:
loadBalancer:
servers:
- url: "https://blog/ghost"
entryPoints:
websecure:
address: :443
[...]
providers:
docker:
exposedByDefault: false # Default is true
file:
# watch for dynamic configuration changes
directory: /etc/traefik
watch: true
As you can see I tried to change the service url to the internal name of the pod but that fails so I must have misunderstood.
Thanks for your help!
Hi @ndy3yju1,
Thanks for sharing me your configuration.
I just saw something wrong in your configuration file.
Before pointing the error, I would explain you one or two basics on Traefik
and how it works.
Traefik comes with 2 kinds of configuration:
- The first one, called "Static Configuration", contains the parameters required to start and configure Traefik itself. In this configuration, you can find entry points, providers, logging configuration, traefik dashboard and API, etc... This static configuration can be provided in different ways : it could be from command line arguments, or in a file which is by default in
/etc/traefik/traefik.yml
.
- The second configuration, called "Dynamic Configuration", is the runtime one. This configuration contains the routing configurations as routers, middlewares, services, ... This dynamic configuration can be provided in different manners as well, but keep it mind that this configuration is hot loaded, and is refreshed based on some orchestrator events (docker, k8s) or at some intervals.
To come back to your problem, I see that your "traefik.yml" file contains both static and dynamic configurations, which are not compatible in the same file.
I can suggest you to move the "http" section of your file into a new one (e.g: /etc/dyn-traefik/blog.yml
), and then update the providers.file.directory
to /etc/dyn-traefik/blog.yml
.
Let me know if it helps you.
Good luck,
Maxence
Hi @moutoum ,
I haven't been ignoring you just trying to implement what I think you said and seem to be failing miserably across the line. I can't even get the dashboard working now so I'm concentrating on that part which I know worked before.
Thank you for your explanation of static and dynamic configuration, somehow the documentation didn't do it for me and I could never grasp the difference.
This is what I did. It still fails but I'm working at it when I have a sec to spare.
The dashboard.yaml is a simlink to /etc/traefik/sites/dashboard.yaml
docker-compose.yaml
---
version: "3.3"
services:
traefik:
image: "traefik:v2.7"
container_name: "traefik"
restart: unless-stopped
security_opt:
- no-new-privileges:true
environment:
SOME_TOKEN: "**********"
labels:
- traefik.enable=true
- traefik.http.routers.traefik.rule=Host(`test.mydomain.com`)
- traefik.http.routers.traefik.entrypoints=websecure
- traefik.http.routers.traefik.tls.certresolver=production
- "traefik.docker.network=traefik_proxy"
ports:
- "443:443"
volumes:
- /root/docker/traefik/config:/etc/traefik
- /root/docker/traefik/certs:/certs:ro
- /root/docker/traefik/logs:/var/logs/traefik
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- proxy
traefik.yaml
---
global:
checkNewVersion: false
sendAnonymousUsage: true # true by default
log:
level: INFO # DEBUG, INFO, WARNING, ERROR, CRITICAL
accessLog:
filePath: "/var/log/traefik/access.log"
format: json
api:
dashboard: true
insecure: false
entryPoints:
websecure:
address: :443
certificatesResolvers:
staging:
acme:
[redacted]
production:
acme:
[redacted]
tls:
options:
default:
sniStrict: true
minVersion: VersionTLS12
require-mtls:
clientAuth:
clientAuthType: RequireAndVerifyClientCert
caFiles:
- /certs/ca-chain.cert.pem
providers:
docker:
exposedByDefault: false
file:
directory: /etc/traefik/sites-enabled
watch: true
pilot:
token: "**********"
dashboard.yaml
---
http:
routers:
dashboard:
rule: Host(`test.mydomain.com`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))
service: api@internal
middlewares:
- auth
middlewares:
auth:
digestAuth:
users:
- "jdoe:traefik:**********"
Does the separation between static and dynamic make more sense now? I am still a bit unsure about the mix of entries as docker-compose labels vs yaml files so I tried to move anything dynamic to the file.
Thanks for your help!
Hi @ndy3yju1,
Your configuration is better so far.
I don't understand what you are doing with the labels. To me, it seems to duplicate your dashboard.yaml
file. In any case, there are some tips to improve your configuration and make it, hopefully, working.
- If you want to use the labels to forward to your dashboard, you have to specify the service you targets with the router:
traefik.http.routers.traefik.service=api@internal
.
- In the
docker-compose.yaml
file, I see you are using the traefik_proxy
network instead of the proxy
one in which the traefik
container belongs to. It could lead to a common error 504 Gateway Timeout
.
- I suggest you to not use different dynamic configuration provider as it is more difficult to read and maintain when the config is spread.
- Try to create the configuration step by step. Start by a simple router forwarding with no TLS and no middleware to your application, then add middleware, then add TLS.
Let me know if it helps. If no feed free to post your updates and questions 
Maxence.