Docker (μέρος 7) | Στήνουμε έναν reverse proxy για τα container μας με το Traefik

Traefik Proxy

Αν πάμε να στήσουμε σε έναν 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 είναι εύκολος στην εγκατάσταση και τη συντήρηση, κάτι που θα δούμε στην πράξη ευθύς αμέσως.

How does reverse proxy work?

Προϋποθέσεις


Πριν ξεκινήσουμε όμως θα χρειαστεί να κάνουμε μία προεταοιμασία. Συγκεκριμένα θα πρέπει:

  1. να έχουμε εγκαταστήσει το Docker και το Docker Compose στο server μας,
  2. να έχουμε ανοίξει τις πόρτες 80 και 443 στο router και να τις έχουμε προωθήσει στη διεύθυνση IP του server μας,
  3. να έχουμε αποκτήσει το δικό μας 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 = true
  network = "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-utils
htpasswd -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.json
chmod 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: heimdall
      environment:
        - PUID=1000
        - PGID=1000
        - TZ=europe/athens
      volumes:
        - ./heimdall:/config
      labels:
      - 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=80
      networks:
      - internal
        - web
      restart: unless-stopped
      image: linuxserver/heimdall

networks:
  web:
    external: true
  internal:
  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: mariadb
    container_name: nextcloud_db
    command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW
    restart: always
    volumes:
      - ./database:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=password
      - MYSQL_PASSWORD=password
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=doctor
    networks:
      - internal
    labels:
      - traefik.enable=false

  app:
    image: nextcloud
    container_name: nextcloud_app
    restart: always
    labels:
     - 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=80
    networks:
      - internal
      - web
    volumes:
      - ./html:/var/www/html
    depends_on:
      - db

networks:
  web:
    external: true
  internal:
    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 θα βρείτε στην επίσημη σελίδα του.

Σχόλια

  1. Ευχαριστώ πολύ @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(συσκευής μας) και αν ναι, ποια είναι τα πλεονεκτήματα σε αυτή την περίπτωση;

    ΑπάντησηΔιαγραφή
    Απαντήσεις
    1. Γεια σου Κωνσταντίνε (τώρα είδα το σχόλιο!).

      Για να φτάσω στο Traefik δοκίμασα και ένα σωρό άλλες λύσεις, μεταξύ των οποίων και το swag. ΤΟ πρόβλημά μου ήταν κυρίως ότι δε μου έφερναν τα πιστοποιητικά SSL από το Let's Encrypt. Επίσης πολλές από τις λύσεις που προτείνονται στο ίντερνετ, περιέχουν container που δεν παίζουν σε ARM επεξεργαστές. Νομίζω το Traefik είναι ότι καλύτερο και πιο εύκολο έχω δοκιμάσει, για αυτό και έγραψα το άρθρο.

      Όσον αφορά το port forwarding, στο router ορίζεις απλά την IP του server σου. Εσύ μετά στο server μπορείς να βάλεις όποια εσωτερική πόρτα θέλεις.

      Ευχαριστώ για τα καλά σου λόγια. Μακάρι να μπορούσα να γράφω πιο συχνά.

      Διαγραφή
  2. Ευχαριστώ πολύ για το άρθρο σου.
    Δώσε docker όσο ποιο πολύ γίνεται. Καλή δύναμη.

    ΑπάντησηΔιαγραφή
  3. Καλησπέρα! Πολύ χρήσιμα τα άρθρα για το docker!

    Αδημονούμε και για επόμενα docker - άρθρα ! :-)

    ΑπάντησηΔιαγραφή
  4. Ευχαριστώ για την βοήθεια.

    Μπορείς να προσθέσεις το σενάριο να δρομολογείς απο το Traefic προς κάποια φυσική συσκευή του δικτύου σου; πχ να βλέπεις το nas σου στο nas.doctorandroid.gr

    Route external services through Traefik

    ΑπάντησηΔιαγραφή
    Απαντήσεις
    1. Σε ευχαριστώ για την πρόταση.

      Σκοπεύω να κάνω ένα άρθρο για το Caddy, έναν διαφορετικό proxy που είναι ακόμα πιο εύκολος στη χρήση.

      Εκεί θα πούμε περισσότερα.

      Διαγραφή
    2. Οντως φαίνεται πολυ καλό και πιο εύκολο.
      Εχω ξεκινησει την αλλαγή σε μένα και με έχει πολυ λιγότερο χρόνο να το στήσω απο ότι το να με παιξει σωστά το routing του traefic προς μή docker service.
      Δείτε και αυτό σχετικά.
      https://p1ngouin.com/posts/why-i-migrated-from-traefik-to-caddy

      Σε ευχαριστώ και πάλι.

      Διαγραφή

Δημοσίευση σχολίου

Πες την άποψή σου ή κάνε την ερώτησή σου ελεύθερα, ακολουθώντας όμως τους στοιχειώδεις κανόνες ευγένειας.

Δείτε επίσης...

Android | Γιατί δε θα αγόραζα ποτέ smartphone της OnePlus

Φεύγω από την Google (μέρος 15) | ''Ξηλώνουμε'' τα Google apps από το Android

Ιδιωτικό απόρρητο | Το αφελές επιχείρημα του ''Δεν έχω τίποτα να κρύψω''

Μάθε παιδί μου Linux (μέρος 11) | Δίσκοι, κατατμήσεις και σύστημα αρχείων (filesystem)

Ubuntu Touch 2020 review | Μια πραγματική mobile GNU/Linux διανομή έτοιμη για καθημερινή χρήση

Απόρρητο | ''Μπερδεύουμε'' τον αλγόριθμο Google και Facebook με ψεύτικες πληροφορίες

Android | Περιόρισε την παρακολούθηση των εφαρμογών και κόψε τις διαφημίσεις με το TrackerControl

Docker (μέρος 1) | Τι είναι και πώς το εγκαθιστούμε στον υπολογιστή μας;

Android | Βάλε στην ''απομόνωση'' τις εφαρμογές που δεν εμπιστεύεσαι με το Shelter