Μία πόρτα εισόδου για κάθε εσωτερικό εργαλείο: ένα μοτίβο self-hosting
Πώς τρέχετε task boards, dashboards, ένα password vault και άλλα πίσω από έναν reverse proxy: wildcard DNS, containers υπό systemd, πολυεπίπεδη πρόσβαση, ασφαλείς μετονομασίες.

Κάθε ομάδα λειτουργιών συσσωρεύει μικρά εργαλεία. Ένα task board, έναν server για dashboards, μια κονσόλα containers, ένα password vault, κάνα δυο εσωτερικές web εφαρμογές που έγραψε κάποιος. Καθένα ξεκινά τη ζωή του στη δική του πόρτα, με το δικό του πιστοποιητικό και τη δική του αντίληψη περί ταυτοποίησης, και μέσα σε έναν χρόνο κανείς δεν είναι σίγουρος ποια από αυτά είναι εκτεθειμένα, ποια έχουν ενημερωθεί ή ποια έχουν ακόμα έγκυρο πιστοποιητικό. Αυτό είναι το μοτίβο που χρησιμοποιούμε για να τρέχουμε ένα ολόκληρο σύνολο εσωτερικών εργαλείων ως μία καλοσυντηρημένη υπηρεσία: μία πόρτα εισόδου, ένας τρόπος εκκίνησης, μία πολιτική πρόσβασης.
Η μορφή του stack
Το μοτίβο έχει τέσσερα επίπεδα, και καθένα έχει μία δουλειά:
- DNS: ένα wildcard όνομα, όπως
*.tools.example.com, που δείχνει σε έναν host. - Reverse proxy: Traefik σε αυτόν τον host, που τερματίζει το TLS και δρομολογεί κάθε hostname στο σωστό container.
- Containers: κάθε εργαλείο στο δικό του Podman container σε ένα ιδιωτικό δίκτυο containers, χωρίς καμία πόρτα δημοσιευμένη στον host εκτός από αυτή του proxy.
- Επίβλεψη: το systemd ξεκινά, επανεκκινεί και βάζει σε σειρά τα πάντα, ώστε το stack να επιβιώνει από μια επανεκκίνηση χωρίς ανθρώπινη παρέμβαση.
Με αυτό στη θέση του, η προσθήκη ενός εργαλείου είναι μια σύντομη checklist και όχι έργο: ξεκινάτε ένα container στο δίκτυο, προσθέτετε μια διαδρομή, τέλος. Δεν υπάρχει νέα τρύπα στο firewall, νέα εγγραφή DNS ή νέα αίτηση πιστοποιητικού.
DNS: ένα wildcard, και μετά το ξεχνάτε
Μια wildcard εγγραφή σημαίνει ότι τα tasks.tools.example.com, grafana.tools.example.com και vault.tools.example.com αναλύονται όλα στον host του proxy χωρίς κανείς να αγγίξει ξανά το DNS. Συνδυάστε την με ένα wildcard πιστοποιητικό για το ίδιο όνομα, και κάθε νέο εργαλείο γεννιέται με έγκυρο TLS.
Κρατήστε το wildcard σε ένα αποκλειστικό subdomain. Δεν θέλετε το anything.example.com να καταλήγει στον host των εργαλείων σας.
Ο proxy: διαδρομές ως αρχεία που διαβάζονται
Το Traefik μπορεί να ανακαλύπτει διαδρομές από labels των containers ή από απλά αρχεία ρυθμίσεων. Για ένα μικρό stack με μεγάλη διάρκεια ζωής προτιμούμε τα αρχεία: ελέγχονται εύκολα, μπαίνουν εύκολα σε version control, και δεν αλλάζουν επειδή κάποιος επανεκκίνησε ένα container με διαφορετικά labels. Μια διαδρομή για έναν task manager μοιάζει κάπως έτσι:
http:
routers:
tasks:
rule: "Host(`tasks.tools.example.com`)"
entryPoints: [websecure]
service: tasks
middlewares: [mgmt-only]
tls: {}
services:
tasks:
loadBalancer:
servers: [{ url: "http://tasks:3456" }]
middlewares:
mgmt-only:
ipAllowList:
sourceRange: ["192.0.2.0/24"]
Το middleware είναι το σημαντικό κομμάτι. Ο proxy είναι το ένα και μοναδικό σημείο όπου αποφασίζετε ποιος μπορεί να φτάσει σε ένα εργαλείο, οπότε βάλτε την απόφαση εκεί.
Πρόσβαση: δύο κλειδαριές, όχι μία
Τα εσωτερικά εργαλεία έχουν δικά τους logins, και η ποιότητά τους ποικίλλει. Μην βασίζεστε μόνο σε αυτά. Βάλτε δύο ελέγχους μπροστά τους:
- Μια λίστα επιτρεπόμενων πηγών στον proxy, περιορισμένη στα δίκτυα από τα οποία δουλεύει πραγματικά η ομάδα σας. Αναπτύξτε κάθε CIDR πριν το γράψετε και διαβάστε το ξανά μετά. Ένα λάθος μήκος prefix μπορεί να μετατρέψει το εύρος ενός γραφείου σε μεγάλο κομμάτι του internet:
python3 -c "import ipaddress,sys; n=ipaddress.ip_network(sys.argv[1],strict=False); print(n, n[0], '->', n[-1], n.num_addresses)" 192.0.2.0/24
- Ένα middleware ταυτοποίησης (basic auth πάνω από TLS κατ' ελάχιστο, ή forward authentication προς τον πάροχο ταυτότητάς σας) για εργαλεία που δεν πρέπει να είναι προσβάσιμα ούτε από μέσα χωρίς δεύτερο διαπιστευτήριο.
Το password vault αξίζει ιδιαίτερη σκέψη. Ένας server συμβατός με Bitwarden, όπως το Vaultwarden, αποθηκεύει τα περιεχόμενα του vault κρυπτογραφημένα από τους clients, οπότε ο server δεν τα κρατά ποτέ σε αναγνώσιμη μορφή. Αυτή είναι μια ισχυρή ιδιότητα, αλλά δεν καταργεί την ανάγκη να περιορίζετε ποιος μπορεί να φτάσει στον server.
Containers υπό systemd
Το να τρέχετε containers με το χέρι δουλεύει μέχρι την πρώτη επανεκκίνηση. Το Podman ενσωματώνεται με το systemd μέσω του Quadlet: περιγράφετε το container σε ένα μικρό unit file και το systemd το χειρίζεται όπως κάθε άλλη υπηρεσία.
# /etc/containers/systemd/tasks.container
[Unit]
Description=Task manager
After=network-online.target
[Container]
Image=docker.io/vikunja/vikunja:<pinned-tag>
Network=tools.network
Volume=/srv/tools/tasks:/app/vikunja/files:Z
[Service]
Restart=always
[Install]
WantedBy=multi-user.target
Μετά το systemctl daemon-reload, το systemctl start tasks το τρέχει, και από εκεί και πέρα ξεκινά στο boot. Καρφώστε τα image tags αντί να ακολουθείτε το latest, ώστε μια αναβάθμιση να είναι απόφαση που παίρνετε εσείς, με ένα dump και ένα rollback έτοιμα, και όχι κάτι που σας συμβαίνει στην επόμενη επανεκκίνηση.
Κρατήστε το SELinux enforcing. Βάλτε label σε κάθε volume για το container που του ανήκει (:Z για ιδιωτικό volume, :z για ένα που πρέπει να μοιράζονται περισσότερα από ένα containers).
Μετονομασία χωρίς να σπάσει τίποτα
Αργά ή γρήγορα ένα εργαλείο μετακινείται: νέος host, νέο domain, ένα πιο τακτοποιημένο όνομα. Κρατήστε το παλιό όνομα λειτουργικό βάζοντας τον proxy να απαντά σε αυτό με μόνιμη ανακατεύθυνση προς το νέο. Χρησιμοποιήστε 308 Permanent Redirect αντί για 301, γιατί το 308 λέει στους clients να επαναλάβουν την ίδια μέθοδο και το ίδιο body στη νέα διεύθυνση.
Υπάρχει μια παγίδα που πιάνει scripts, όχι browsers. Οι browsers ακολουθούν τις ανακατευθύνσεις. Πολλοί clients γραμμής εντολών και βιβλιοθήκες δεν το κάνουν, αν δεν τους το πείτε. Ένα script που στέλνει POST στο παλιό όνομα παίρνει πίσω ένα 308, δεν το ακολουθεί, και προχωρά. Τίποτα δεν δημιουργήθηκε, τίποτα δεν απέτυχε ηχηρά, και αν το script ελέγχει μόνο ότι κάτι απάντησε, αναφέρει επιτυχία. Με το curl, χρησιμοποιήστε -L για να ακολουθήσει, και πάντα ελέγχετε τον κωδικό κατάστασης και το body:
curl -s -o out.json -w '%{http_code}\n' -X POST https://tasks.tools.example.com/api/v1/...
Ένα body απάντησης με τις λέξεις «Permanent Redirect» εκεί που περιμένατε JSON είναι το σημάδι. Όταν μετονομάζετε, ψάξτε στα scripts και τις ενσωματώσεις σας για το παλιό όνομα και ενημερώστε τα, και κρατήστε την ανακατεύθυνση μόνο ως δίχτυ ασφαλείας.
Backups, παρακολούθηση και ενημερώσεις
Παρακολουθείτε κάθε διαδρομή από έξω από τον host για την αναμενόμενη σελίδα, όχι απλώς για μια απάντηση, και ενημερώνετε ένα εργαλείο τη φορά, κρατώντας το προηγούμενο image μέχρι το νέο να έχει τρέξει για λίγο.
Μια checklist για τα δικά σας εργαλεία
- Μπορείτε να απαριθμήσετε κάθε εσωτερικό εργαλείο, το hostname του και ποιος μπορεί να το φτάσει;
- Δημοσιεύει κάποιο εργαλείο τη δική του πόρτα στον host ή στο internet, παρακάμπτοντας τον proxy;
- Είναι όλα τα εύρη της λίστας επιτρεπόμενων τόσο στενά όσο νομίζετε; Αναπτύξτε τα και κοιτάξτε.
- Επανέρχονται τα containers σας μόνα τους μετά από επανεκκίνηση;
- Ελέγχουν τα scripts σας κωδικούς κατάστασης, ή μόνο ότι κάτι απάντησε;
- Πότε κάνατε τελευταία φορά restore το password vault σας;
Μία πόρτα εισόδου, μία πολιτική και ένας τρόπος εκκίνησης μετατρέπουν ένα συρτάρι με σκόρπια εργαλεία σε μια υπηρεσία για την οποία μπορείτε να σκεφτείτε καθαρά.
CTA
Αν θέλετε τα εργαλεία της ομάδας σας να τρέχουν έτσι, στο δικό σας instance, οι μηχανικοί μας μπορούν να βοηθήσουν. Μιλήστε με έναν μηχανικό
Related
Σχετικά: Managed self-hosted εργαλεία λειτουργιών · Το hardening baseline σε κάθε host
Δημοσιεύθηκε 24 Αυγούστου 2026.
Όλες οι σημειώσεις