I'm trying to understand how to implement https with grafana/caddy in docker compose without a domain name.
Currently, I access grafana via http://xx.xxx.xx.xx:3000/
I would like this to be https, but am struggling to understand how to generate the cert and have it work as expected. I think letsencrypt requires a domain which I don't have.
version: "3"
networks:
monitor-net:
driver: bridge
volumes:
grafana_data: {}
services:
grafana:
image: grafana/grafana:8.4.4
container_name: grafana
volumes:
- grafana_data:/var/lib/grafana
- ./grafana/provisioning/dashboards:/etc/grafana/provisioning/dashboards
- ./grafana/provisioning/datasources:/etc/grafana/provisioning/datasources
environment:
- GF_SECURITY_ADMIN_USER=${GF_ADMIN_USER}
- GF_SECURITY_ADMIN_PASSWORD=${GF_ADMIN_PASS}
- GF_USERS_ALLOW_SIGN_UP=false
restart: unless-stopped
expose:
- 3000
networks:
- monitor-net
labels:
org.label-schema.group: "monitoring"
caddy:
image: caddy:2.3.0
container_name: caddy
ports:
- "3000:3000"
- "9090:9090"
- "9093:9093"
- "9091:9091"
volumes:
- ./caddy:/etc/caddy
environment:
- ADMIN_USER=${GF_ADMIN_USER}
- ADMIN_PASSWORD=${GF_ADMIN_PASS}
- ADMIN_PASSWORD_HASH=${ADMIN_PASS_HASH}
restart: unless-stopped
networks:
- monitor-net
labels:
org.label-schema.group: "monitoring"
I'm assuming I would create a volume on /etc/caddy/certs where I'd store the certificates, but don't know how to generate it for IP only or how it gets recognized by caddy.
Caddy for IP with SSL
By default, Caddy serves all sites over HTTPS.
Caddy serves IP addresses and local/internal hostnames over HTTPS using self-signed certificates that are automatically trusted locally (if permitted).
Examples: localhost, 127.0.0.1
Offical Docs Here
in your Caddyfile you have to add something like this
http://192.168.1.25:3000 {
reverse_proxy grafana_ip:3000
}
It looks like Caddy does not support generating HTTPS certificates for IP addresses. Additionally, Let's Encrypt does not currently support issuing certificates for bare IP addresses.
However, it does appear that ZeroSSL supports generating certificates for IPs. You could try using these instructions to change one or all of your sites to use ZeroSSL, but I wasn't able to get this to work on my test server.
The best option is probably to get a domain that you can point at your server, and then serve it from there.
Related
I was looking for a software like No-IP to dynamically update my IP using a free domain from them like <domain>.zapto.org, but this time for setting up with docker containers. So I found about duckdns and tried setting it up.
Well, perhaps I got it wrong, but as per what I understood, I can create a service within my docker-compose services setting up the linuxserver/duckdns. When I do that, I suppose that I can then access my other services from that same compose using the domain created on duckdns, is that right?
For instance, I got this docker-compose:
version: "3.9"
services:
dns_server:
image: linuxserver/duckdns:version-13f609b7
restart: always
environment:
TOKEN: ${DUCKDNS_TOKEN}
TZ: ${TZ}
SUBDOMAINS: ${DUCKDNS_SUBDOMAINS}
depends_on:
- server
- db
- phpmyadmin
server:
# ...
restart: always
ports:
- "7171:7171"
- "7172:7172"
# ...
command: sh -c "/wait && screen -S tfs ./tfs"
# Database
db:
image: bitnami/mariadb:10.8.7-debian-11-r1
restart: always
ports:
- "3306:3306"
# ...
# phpmyadmin
phpmyadmin:
# ...
image: bitnami/phpmyadmin:5.2.1-debian-11-r1
restart: always
ports:
- "8080:8080"
- "8443:8443"
# ...
That compose gives me these containers running:
When I try to reach my server service by using 127.0.0.1:7171 or localhost:7171, and also access my phpmyadmin by 127.0.0.1:8080, it works, but it doesn't when I try using <mydomain>.duckdns.org:7171 or <mydomain>.duckdns.org:8080
What is wrong?
As I know, when you define the port - "7171:7171" like this it will bound to your localhost 127.0.0.1, which you can access. If you want to allow public access try something like
server:
ports:
- "0.0.0.0:7171:7171"
- "0.0.0.0:7172:7172"
And you can access the port via your Public IP address or hostname of duckDNS.
FYI: Beware of the security risks of exposing the code to the public.
The minimal example from https://doc.traefik.io/traefik/user-guides/docker-compose/basic-example/ works on my local machine. However, when I try to adapt this to use TLS I run into an issue. I'm a Traefik newbie, so I might be doing a stupid mistake.
This is my attempt:
version: "3.3"
services:
traefik:
image: "traefik:v2.8"
container_name: "traefik"
command:
- "--log.level=DEBUG"
- "--accesslog=true"
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
ports:
- "443:443"
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
whoami:
image: "traefik/whoami"
container_name: "simple-service"
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`127.0.0.1`)"
- "traefik.http.routers.whoami.entrypoints=websecure"
So the major modification is to use "traefik.http.routers.whoami.entrypoints=websecure" instead of "traefik.http.routers.whoami.entrypoints=web"
Running
$ curl -k https://127.0.0.1
I get
404 page not found
The traefik log shows no routing related issues and the internal traefik setup for routing etc shown using curl https://127.0.0.1:8080/api/rawdata | jq . looks the same as the one of the working example, except the changed port.
So I opted for new answer instead of just editing the old answer. (Reason being even incorrect answers teach something).
My reference is this great post by Marc Mogdanz (link: https://marcmogdanz.de/posts/infrastructure-with-traefik-and-cloudflare/).
The direct answer to your query is:
Expose port 8080 but do not publish it
Add a host name rule. This will allow Traefik to route a URL request to its own port 8080.
The affected part of the compose file would be as follows (assuming that the URL https://dashboard.example.com is the desired URL to reach the dashboard):
expose:
- 8080
...
labels:
- "traefik.enable=true"
- "traefik.http.routers.traefik.rule=Host(`dashboard.example.com`)"
- "traefik.http.routers.traefik.tls=true"
- "traefik.http.services.traefik.loadbalancer.server.port=8080"
Finally, I noticed you are testing on localhost. If you are testing on a local machine, use localhost for the dashboard and keep 127.0.0.1 for whoami.
Or, alternately, add a static entry for a subdomain (see https://stackoverflow.com/a/19016600).
Either way, Traefik is looking at the SNI requested - not necessarily the IP address - when matching the Host rule.
Request ----> Docker:443 ---> {Traefik}-"SNI?"---"127.0.0.1"---> {whoami}
| \
| \
8080<---"dashboard.localhost"
Add the following entry to your Traefik:
"--entrypoints.websecure.address=:8080"
Normally it would be 8080 for http and 8443 for https alternative ports, but since your example specifically states https://~:8080, I have adapted it accordingly.
Hello traefik friends.
I just started to look into traefik. All tutorials show how to run one docker-compose.yml file with traefik togather with other containers. I most often have many separate docker-compose.yml files and very much would like to use them with traefik.
so here is my code for traefik container:
version: "3.3"
services:
traefik:
image: "traefik:v2.5"
container_name: "traefik"
command:
#- "--log.level=DEBUG"
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.myresolver.acme.tlschallenge=true"
#- "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
- "--certificatesresolvers.myresolver.acme.email=xxxxxxxxx#gmail.com"
- "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
ports:
- "443:443"
- "8080:8080"
networks:
- "traefik"
- "external"
volumes:
- "./letsencrypt:/letsencrypt"
- "/var/run/docker.sock:/var/run/docker.sock:ro"
and the other exemplary docker-compose I would like to use with traefik:
version: '3.1'
services:
php:
image: php:7.4-apache
ports:
- 8081:80
volumes:
- ./php/www:/var/www/html/
labels:
- "traefik.enable=true"
- "traefik.http.routers.php.rule=host(`php.xxxxxx.com`)"
- "traefik.http.routers.php.entrypoints=websecure"
- "traefik.http.routers.php.tls.certresolver=myresolver"
unfortunately that doesnt seem to work (when I concat theese to files into one big docker-compose.yml file - it works fine. Could you point me in the right direction?
Traefik needs to be part of the networks for all the services it connects to. For me it works when I set network: host for Traefik. (And then you have to remove ports part.)
I do wonder how safe that is, I can't seem to access the admin interface from another machine, so that's good.
Each docker-compose.yml by default create its own network. So traefik from the traefik network can't access PHP server from some other "php-default" network.
see Compose Networking docs
We have to add the PHP server to the traefik network:
php/docker-compose.yml:
services:
php:
image: php:7.4-apache
# we need to tell the traefik what port is the container listening to
expose:
- 80
volumes:
- ./php/www:/var/www/html/
labels:
- "traefik.enable=true"
- "traefik.http.routers.php.rule=host(`php.xxxxxx.com`)"
- "traefik.http.routers.php.entrypoints=websecure"
- "traefik.http.routers.php.tls.certresolver=myresolver"
networks:
default:
name: traefik
external: true
OR if you want to have other networks
services:
php:
...
networks:
- traefik
...
networks:
traefik:
external: true
Note there is not defined port property, instead there is expose. The port exposes ports on the host, the expose act as mere documentation (see this Q) but the traefik read it.
And because of that, I think that in your traefik/docker-compose.yml the external network is unnecessary.
I'm trying to deploy a React + FastApi + Postgres application on docker compose with Traefik as the reverse proxy. I'm running into issues with Bad Gateway errors. Running my FastAPI locally runs it on port 8888 and exposes the path /docs to view the api documentation. I'd like to eventually have the application running on example.local with the docs available on example.local/api/docs. My docker-compose.yaml is as follows (loosely based on this one):
version: '3.8'
services:
proxy:
image: traefik:v2.4
networks:
- web
volumes:
- /var/run/docker.sock:/var/run/docker.sock
ports:
- '80:80'
- '8080:8080'
- '443:443'
command:
- --providers.docker
- --api.insecure=true
- --providers.docker.exposedbydefault=false
- --providers.docker.network=web
- --entrypoints.web.address=:80
labels:
- traefik.enable=true
- traefik.http.routers.example-proxy-http.rule=Host(`example.local`)
- traefik.http.routers.example-proxy-http.entrypoints=web
- traefik.http.services.example-proxy.loadbalancer.server.port=80
backend:
build:
context: ./backend
dockerfile: Dockerfile
command: python app/main.py
volumes:
- ./backend/app:/app
env_file:
- .env
networks:
- web
- backend
labels:
- traefik.enable=true
- traefik.http.routers.example-backend-http.rule=PathPrefix(`api/docs`)
- traefik.http.routers.example-backend-http.entrypoints=web
- traefik.http.services.example-backend.loadbalancer.server.port=8888
networks:
web:
external: true
backend:
external: false
I've added 127.0.0.1 example.local to my /etc/hosts file.
From reading around it seems like Bad Gateway errors tend to occur from traefik and related services not being on the same network, or traefik routing traffic to the wrong port on the service container. However if I set ports: - '8888:8888' in my backend service I can access the docs from localhost:8888/docs so I'm pretty sure 8888 is the correct port for the backend loadbalancer. From what I can see traefik and the backend service are on the same network too and I've set it as the default traefik network with --providers.docker.network=web. Interestingly if I visit localhost/api/docs in my browser I'm served up a page from FastAPI. So it could be an issue with my traefik http router labels? I'm quite new to traefik and proxies so would appreciate any help or guidance, thanks!
UPDATE
If I specify the host for the backend by adding
- traefik.http.routers.infilmation-backend-http.rule=Host(`example.local`) && PathPrefix(`/docs`)
to the backend service labels, then visiting example.local/docs does serve up page from FastApi. So I guess my question would be what is the best way of setting up a host for this application? Is there a way I can specify a default host for all services then any PathPrefix rules would be in relation to that host?
So I have the following docker-compose.yml
version: "3.7"
services:
roundclinic-mysql:
image: mysql:5.7
networks:
- spring-boot-mysql-network
environment:
- MYSQL_DATABASE=
- MYSQL_USER=
- MYSQL_PASSWORD=
- MYSQL_ROOT_PASSWORD=
volumes:
- ./mysqldata:/var/lib/mysql:rw,delegated
ports:
- "3306:3306"
web-service:
image: roundclinic/roundclinic:latest
networks:
- spring-boot-mysql-network
- traefik-network
depends_on:
- roundclinic-mysql
ports:
- 8080:8080
environment:
- "SPRING_PROFILES_ACTIVE=dev"
links:
- roundclinic-mysql
labels:
- "--providers.docker.network=traefik_default"
- "traefik.enable=true"
- "traefik.http.routers.roundclinic.rule=Host(`api-dev.roundclinic.app`)"
- "traefik.http.routers.roundclinic.entrypoints=web"
- "traefik.http.services.cal.loadbalancer.server.port=8080"
traefik:
image: "traefik:v2.2"
container_name: "traefik"
command:
- "--log.level=DEBUG"
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "traefik.docker.network=traefik-network"
ports:
- "80:80"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
networks:
traefik-network:
driver: bridge
external: true
spring-boot-mysql-network:
driver: bridge
volumes:
my-db:
Spring boot starts up fine and can connect to mysql.
When I connect to http://api-dev.roundclinic.app:8080/../ I can hit my application just fine
When I connect to http://api-dev.roundclinic.app/../ I get a gateway timeout. I can see in the traefik logs that it's forwarding the request to what seems to be the correct IP and port, but nothing hits the actual application. I'm not sure what's going on here. Any help?
When accessing port 8080 you are bypassing Traefik and directly access your application, correct?
Generally speaking the Traefik labels look good. Entrypoint, Port and Host are defined, router and service port are present. These are usually all the requirements for Docker-based setups.
One thing that I noticed is that the traefik container uses "traefik.docker.network=traefik-network", but your web app uses:
"--providers.docker.network=traefik_default".
I am not sure if traefik_default is something that traefik provides but that mismatch in network names might be the issue.
I can't test if that is the problem but that would be the first thing to check.
One way would be to simplify your config but just always using the networks key from docker compose instead of mixing it with labels and arguments.