Docker (μέρος 7) | Στήνουμε έναν reverse proxy για τα container μας με το Traefik
Αν πάμε να στήσουμε σε έναν server όλες τις υπηρεσίες που έχουμε δει μέχρι
στιγμής στα
"Μαθήματα Docker", θα ανακαλύψουμε ότι πρέπει να ανοίξουμε πολλές πόρτες στο router μας, αν
θέλουμε να έχουμε πρόσβαση σε αυτές εκτός του τοπικού μας δικτύου. Αυτό από
μόνο του είναι ένα τεράστιο πρόβλημα για την ασφάλεια του server μας, το οποίο
πρέπει να αποφύγουμε και ένας εξαιρετικός τρόπος για να το κάνουμε αυτό είναι
να στήσουμε έναν reverse proxy server. Σήμερα θα δούμε πώς γίνεται αυτό
χρησιμοποιώντας το Traefik Proxy.
Τι είναι ο reverse proxy server;
Κάθε υπηρεσία που τρέχουμε στο Docker μας δίνει πρόσβαση μέσα από
συγκεκριμένες πόρτες, οι οποίες για τις web εφαρμογές είναι συνήθως η 80
(http) και η 443 (https). Για παράδειγμα το Nextcloud χρειάζεται την 80 και
την 443, το Home Assistant χρειάζεται την 8123 και το Portainer την 8000 και
την 9000. Για να μην ανοίξουμε λοιπόν όλες αυτές τις πόρτες στο δίκτυό μας,
μπορούμε να στήσουμε έναν reverse proxy server στην 80 και την 443, ο οποίος
θα κατευθύνει τα αιτήματά μας στην υπηρεσία που ζητάμε.
Με απλά λόγια ο reverse proxy είναι ένας πορτιέρης! Στέκεται μπροστά στις
ανοιχτές πόρτες του router, ελέγχει τα αιτήματα σύνδεσης και τα προωθεί στις
σωστές υπηρεσίες. Αν για παράδειγμα γράψουμε στον browser ότι θέλουμε να
μπούμε στο hass.doctorandoid.gr, ο proxy θα μας στείλει στο Home Assistant,
ενώ αν ζητήσουμε το next.doctorandroid.gr, θα μας στείλει αντίστοιχα στο
Nextcloud.
Οι σύγχρονοι reverse proxy όπως ο Traefik μπορούν επίσης να φέρουν
πιστοποιητικά SSL για τις υπηρεσίες που εξυπηρετούν και να ασφαλίσουν τη
σύνδεσή μας, ενώ διαθέτουν και load balancer σε περίπτωση που πολλοί χρήστες
ζητήσουν ταυτόχρονα την ίδια υπηρεσία. Το καλύτερο από όλα όμως είναι ότι
ειδικά ο Traefik Proxy είναι εύκολος στην εγκατάσταση και τη συντήρηση, κάτι
που θα δούμε στην πράξη ευθύς αμέσως.
Προϋποθέσεις
Πριν ξεκινήσουμε όμως θα χρειαστεί να κάνουμε μία προεταοιμασία. Συγκεκριμένα
θα πρέπει:
- να έχουμε εγκαταστήσει το Docker και το Docker Compose στο server μας,
- να έχουμε ανοίξει τις πόρτες 80 και 443 στο router και να τις έχουμε προωθήσει στη διεύθυνση IP του server μας,
- να έχουμε αποκτήσει το δικό μας domain (π.χ. doctorandroid.gr) και να έχουμε προωθήσει τα subdomain που θα χρησιμοποιήσουμε (π.χ. next.doctorandroid.gr, hass.doctorandroid.gr) στην δημόσα IP που βρίσκεται ο server μας.
Pro tip:
Όσον αφορά την προώθηση των subdomain στο δίκτυό μας, μπορούμε να τα
κάνουμε όλα με μία καταχώρηση και να μην ασχοληθούμε ξανά μαζί τους.
Μπαίνουμε στη σελίδα του registar από τον οποίο αγοράσαμε το domain (π.χ.
papaki.gr) και δημιουργούμε ένα A record δίνοντας ως Host την διεύθυνση
*.doctorandroid.gr.
Διαφήμιση
Ρυθμίζουμε το Traefik Proxy
Ο Traefik Proxy χρειάζεται δύο αρχεία ρυθμίσεων για να λειτουργήσει. Το
"traefik.toml" διαθέτει τις στατικές ρυθμίσεις της υπηρεσίας και το
"traefik_dynamic.toml" τις δυναμικές. Δημιουργούμε και ανοίγουμε το πρώτο με
την εντολή:
nano traefik.toml
Κάνουμε αντιγραφή και επικόλληση τα ακόλουθα και αποθηκεύουμε το αρχείο μας:
[entryPoints][entryPoints.web]address = ":80"[entryPoints.web.http.redirections.entryPoint]to = "websecure"scheme = "https"[entryPoints.websecure]address = ":443"[api]dashboard = true[certificatesResolvers.lets-encrypt.acme]email = "[email protected]"storage = "acme.json"[certificatesResolvers.lets-encrypt.acme.tlsChallenge][providers.docker]watch = truenetwork = "web"[providers.file]filename = "traefik_dynamic.toml"
Με λίγα λόγια αυτό που κάναμε με το συγκεκριμένο αρχείο είναι το εξής:
- [entrypoints]: Δηλώσαμε ότι ο proxy μας θα παρακολουθεί την πόρτα 80 (web) και την 443 (websecure) για νέα αιτήματα, ενώ ρυθμίσαμε επίσης τα αιτήματα "http" να προωθούνται αυτόματα σε "https".
- [api]: Ενεργοποιήσαμε το "Dashboard" του Traefik.
- [certificatesResolvers.lets-encrypt.acme]: Ορίσαμε το αρχείο "acme.json" στο οποίο θα αποθηκεύονται οι πληροφορίες των πιστοποιητικών SSL που θα λαμβάνουν οι υπηρεσίες μας από το "Let's Encrypt". Εκεί δηλώσαμε επίσης και το email μας (π.χ. "[email protected]") για τυχόν μελλοντικές ειδοποιήσεις.
- [providers.docker]: Ενημερώσαμε τον proxy μας να εξυπηρετεί τα Docker container που είναι συνδεδεμένα στο δίκτυο "web", το οποίο θα δημιουργήσουμε στη συνέχεια.
- [providers.file]: Δηλώσαμε ότι οι δυναμικές ρυθμίσεις του Traefik βρίσκονται στο αρχείο "traefik_dynamic.toml".
Πάμε τώρα να δημιουργήσουμε το αρχείο "traefik_dynamic.toml". Εκεί περιέχονται
και οι ρυθμίσεις για το "Dashboard" του Traefik, μέσα από το οποίο μπορούμε να
βλέπουμε με μια ματιά την υγεία των υπηρεσιών μας. Θα χρειαστούμε ένα username
(π.χ. "doctor") και ένα password (π.χ. "1234") για να το ασφαλίσουμε, αλλά θα
πρέπει πρώτα να το κρυπτογραφήσουμε, κάτι που γίνεται ως εξής:
sudo apt install apache2-utilshtpasswd -nb doctor 1234
Το τερματικό θα μας απαντήσει με τον κρυπτογραφημένο κωδικό ως εξής:
doctor:$apr1$q9cQzs5M$VsAyJGznYd1HTNY8qiI7C.
Δημιουργούμε και ανοίγουμε το αρχείο με την εντολή:
nano traefik_dynamic.toml
Κάνουμε αντιγραφή και επικόλληση τα ακόλουθα και το αποθηκεύουμε:
[http.middlewares.simpleAuth.basicAuth]users = ["doctor:$apr1$q9cQzs5M$VsAyJGznYd1HTNY8qiI7C."][http.routers.api]rule = "Host(`monitor.doctorandroid.gr`)"entrypoints = ["websecure"]middlewares = ["simpleAuth"]service = "api@internal"[http.routers.api.tls]certResolver = "lets-encrypt"
Αυτό που κάναμε με το συγκεκριμένο αρχείο είναι το εξής:
- [http.middlewares.simpleAuth.basicAuth]: Ορίσαμε ένα username και ένα password για το "Dashboard" του Traefik (είναι αυτό που δημιουργήσαμε πριν με το "htpasswd").
- [http.routers.api]: Ορίσαμε τη διεύθυνση από την οποία θα μπαίνουμε στο "Dashboard" (monitor.doctorandroid.gr). Κάναμε επίσης αυτόματη προώθηση όλων των αιτημάτων από την πόρτα 80 στην ασφαλή 443 (websecure).
- [http.routers.api.tls]: Τέλος ενημερώσαμε τον proxy μας ότι τα πιστοποιητικά SSL θα έρχονται από την υπηρεσία "Let's Encrypt".
Δημιουργούμε το Docker container του Traefik Proxy
Είπαμε πριν ότι τα container μας θα τρέχουν στο δίκτυο "web" του Docker. Ας το
δημιουργήσουμε με την εντολή:
docker network create web
Δημιουργούμε επίσης το αρχείο "acme.json" και του δίνουμε τα σωστά δικαιώματα
στο σύστημα με τις εντολές:
touch acme.jsonchmod 600 acme.json
Η εντολή για σηκώσουμε το container του Traefik Proxy στο Docker είναι η εξής:
docker run -d \-v /var/run/docker.sock:/var/run/docker.sock \-v ./traefik.toml:/traefik.toml \-v ./traefik_dynamic.toml:/traefik_dynamic.toml \-v ./acme.json:/acme.json \-p 80:80 \-p 443:443 \--network web \--name traefik \traefik:v2.2
Δε χρειάζεται να εξηγήσουμε τι κάνει η συγκεκριμένη εντολή, καθώς είμαστε
πλέον έμπειροι από τα προηγούμενα
"Μαθήματα Docker". Τώρα που το Traefik είναι έτοιμο, μπορούμε να μπούμε στην υπηρεσία από τη
διεύθυνση:
https://monitor.doctorandroid.gr/dashboard/
Διαφήμιση
Συνδέουμε το Heimdall στο Traefik Proxy
Αφού τελειώσαμε με τον proxy server μας, ήρθε επιτέλους η στιγμή να συνδέσουμε
τις υπηρεσίες μας επάνω του. Το μόνο που θα χρειαστεί να κάνουμε είναι να
προσθέσουμε κάποιες ετικέτες και κάποια δίκτυα στο αρχείο Docker Compose της
κάθε υπηρεσίας. Θα δούμε μία απλή υπηρεσία όπως το Heimdall, αλλά και μία
εξαρτώμενη από βάση δεδομένων όπως το Nextcloud για να καλύψουμε όλες τις
περιπτώσεις.
Ξεκινάμε με το Heimdall και δημιουργούμε το αρχείο "docker-compose.yml" με την
εντολή:
nano docker-compose.yml
Κάνουμε αντιγραφή και επικόλληση τα εξής:
version: '3'services:heimdall:container_name: heimdallenvironment:- PUID=1000- PGID=1000- TZ=europe/athensvolumes:- ./heimdall:/configlabels:- traefik.http.routers.heimdall.rule=Host(`heimdall.doctorandroid.gr`)- traefik.http.routers.heimdall.tls=true- traefik.http.routers.heimdall.tls.certresolver=lets-encrypt- traefik.port=80networks:- internal- webrestart: unless-stoppedimage: linuxserver/heimdallnetworks:web:external: trueinternal:external: false
Αυτό που αλλάξει εδώ σε σχέση με το επίσημο Docker Compose αρχείο του Heimdall
είναι το εξής:
- Αφαιρέσαμε τα "ports" καθώς τη διαχείρισή τους θα την κάνει πλέον ο proxy.
- Προσθέσαμε τα "networks" στα οποία θα συνδεθεί το container μας. Το "web" είναι το βασικό δίκτυο του proxy, ενώ με το "internal" το container μας μπορεί να επικοινωνεί με άλλα container (δε χρειάζεται στην συγκεκριμένη περίπτωση).
- Προσθέσαμε τα απαραίτητα "labels" στα οποία δηλώσαμε τη διεύθυνση μέσα από την οποία θα έχουμε πρόσβαση (heimdall.doctorandroid.gr), καθώς και ότι θέλουμε ένα SSL πιστοποιητικό από το "Let's Encrypt" για αυτήν.
Το αποθηκεύουμε και το τρέχουμε με την εντολή:
docker-compose up -d
Πλέον μπορούμε να μπούμε στην υπηρεσία του Heimdall από τη διεύθυνση:
https://heimdall.doctorandroid.gr
Συνδέουμε το Nextcloud στο Traefik proxy
Πάμε να ρίξουμε και μια ματιά στην περίπτωση του Nextcloud, το οποίο
χρειάζεται και μία βάση δεδομένων για να λειτουργήσει. Στην περίπτωση αυτή
θέλουμε να στείλουμε στον proxy μόνο την υπηρεσία του Nextcloud και όχι τη
βάση δεδομένων, η οποία θέλουμε να λειτουργεί τοπικά.
Δημιουργούμε το αρχείο "docker-compose.yml" με την εντολή:
nano docker-compose.yml
Κάνουμε αντιγραφή και επικόλληση τα εξής:
version: '3'services:db:image: mariadbcontainer_name: nextcloud_dbcommand: --transaction-isolation=READ-COMMITTED --binlog-format=ROWrestart: alwaysvolumes:- ./database:/var/lib/mysqlenvironment:- MYSQL_ROOT_PASSWORD=password- MYSQL_PASSWORD=password- MYSQL_DATABASE=nextcloud- MYSQL_USER=doctornetworks:- internallabels:- traefik.enable=falseapp:image: nextcloudcontainer_name: nextcloud_apprestart: alwayslabels:- traefik.http.routers.cloud.rule=Host(`cloud.doctorandroid.gr`)- traefik.http.routers.cloud.tls=true- traefik.http.routers.cloud.tls.certresolver=lets-encrypt- traefik.port=80networks:- internal- webvolumes:- ./html:/var/www/htmldepends_on:- dbnetworks:web:external: trueinternal:external: false
Σε αυτό το αρχείο βλέπουμε δύο υπηρεσίες, το "db" που είναι η βάση δεδομένων
και το "app" το οποίο είναι η εφαρμογή του Nextcloud. Το "db" λοιπόν δε
θέλουμε να έχει πρόσβαση στο ίντερνετ οπότε το προσθέσαμε μόνο στο "internal"
network και όχι στο "web". Με το "labels: traefik.enable=false" λέμε επίσης
στο Traefik να μην το λαμβάνει υπ' όψη.
Αντίθετα στο "app" προσθέσαμε τα "labels" και "networks" που χρησιμοποιήσαμε
και στο Heimdall (εδώ το "internal" είναι απαραίτητο για να επικοινωνεί το
"app" με το "db"). Η διαφορά εδώ είναι ότι προσθέσαμε επίσης και το
"depends_on: db", το οποίο σημαίνει ότι το "app" δεν μπορεί να λειτουργήσει
χωρίς το "db", κάτι το οποίο είναι λογικό.
Το αποθηκεύουμε και το τρέχουμε με την εντολή:
docker-compose up -d
Πλέον μπορούμε να μπούμε στην υπηρεσία του Heimdall από τη διεύθυνση:
https://cloud.doctorandroid.gr
Εν κατακλείδι
Η πρώτη προτεραιότητα σε έναν server είναι πάντα η ασφάλεια. Όσο βολικό και να
είναι το self-hosting με το Docker, δεν μπορείς να ανοίγεις πόρτες στο δίκτυό
σου για κάθε υπηρεσία. Αργά ή γρήγορα λοιπόν ένας reverse proxy server θα σου
χρειαστεί και αυτός που μας προσφέρει η Traefik Labs είναι εξαιρετικά εύκολος
στην εγκατάσταση, ενώ δε χρειάζεται σχεδόν καθόλου συντήρηση.
Προσθέστε σε αυτά το γεγονός ότι είναι ανοιχτού κώδικα και ότι διαθέτει ένα
πλήρες documentation, σε περίπτωση που θέλουμε να κάνουμε κάτι πιο εξεζητημένο
στο μέλλον. Περισσότερα για τον Traefik Proxy θα βρείτε
στην επίσημη σελίδα του.
Ευχαριστώ πολύ @Chris Kay για τα άρθρα σου.
ΑπάντησηΔιαγραφήΕίναι πάντα απλά και κατανοητά... και με pro tips επίσης!
Με έχουν βοηθήσει πάρα πολύ με τα container και το linux(απόπειρα zero to hero), για τη δημιουργία home server σε Rpi4!
Η χρήση του swag, https://docs.linuxserver.io/general/swag θα μπορούσε να συγκριθεί με αυτή του Traefik;
Ποιά είναι τα + και τα - ;
Θα μπορούσαμε να κάνουμε port forwarding της 443 και της 80, από το router σε άλλη θύρα του host(συσκευής μας) και αν ναι, ποια είναι τα πλεονεκτήματα σε αυτή την περίπτωση;
Γεια σου Κωνσταντίνε (τώρα είδα το σχόλιο!).
ΔιαγραφήΓια να φτάσω στο Traefik δοκίμασα και ένα σωρό άλλες λύσεις, μεταξύ των οποίων και το swag. ΤΟ πρόβλημά μου ήταν κυρίως ότι δε μου έφερναν τα πιστοποιητικά SSL από το Let's Encrypt. Επίσης πολλές από τις λύσεις που προτείνονται στο ίντερνετ, περιέχουν container που δεν παίζουν σε ARM επεξεργαστές. Νομίζω το Traefik είναι ότι καλύτερο και πιο εύκολο έχω δοκιμάσει, για αυτό και έγραψα το άρθρο.
Όσον αφορά το port forwarding, στο router ορίζεις απλά την IP του server σου. Εσύ μετά στο server μπορείς να βάλεις όποια εσωτερική πόρτα θέλεις.
Ευχαριστώ για τα καλά σου λόγια. Μακάρι να μπορούσα να γράφω πιο συχνά.
Ευχαριστώ πολύ για το άρθρο σου.
ΑπάντησηΔιαγραφήΔώσε docker όσο ποιο πολύ γίνεται. Καλή δύναμη.
Αν και καθυστερημένα σε ευχαριστώ φίλε μου.
ΔιαγραφήΚαλησπέρα! Πολύ χρήσιμα τα άρθρα για το docker!
ΑπάντησηΔιαγραφήΑδημονούμε και για επόμενα docker - άρθρα ! :-)
Ευχαριστώ για την βοήθεια.
ΑπάντησηΔιαγραφήΜπορείς να προσθέσεις το σενάριο να δρομολογείς απο το Traefic προς κάποια φυσική συσκευή του δικτύου σου; πχ να βλέπεις το nas σου στο nas.doctorandroid.gr
Route external services through Traefik
Σε ευχαριστώ για την πρόταση.
ΔιαγραφήΣκοπεύω να κάνω ένα άρθρο για το Caddy, έναν διαφορετικό proxy που είναι ακόμα πιο εύκολος στη χρήση.
Εκεί θα πούμε περισσότερα.
Οντως φαίνεται πολυ καλό και πιο εύκολο.
ΔιαγραφήΕχω ξεκινησει την αλλαγή σε μένα και με έχει πολυ λιγότερο χρόνο να το στήσω απο ότι το να με παιξει σωστά το routing του traefic προς μή docker service.
Δείτε και αυτό σχετικά.
https://p1ngouin.com/posts/why-i-migrated-from-traefik-to-caddy
Σε ευχαριστώ και πάλι.