Subdomény na localhoste
Programovanie webových aplikácií a prevádzka lokálnych webových aplikácií zvyčajne vyžaduje vysoké čísla portov, napríklad :8080, ktoré sa ťažko pamätajú a sú náchylné na konflikty. Pozrime sa preto na lepší prístup: využitie subdomén localhostu namiesto čísel portov.
Žiadne štandardné riešenie
Neexistuje žiadne systémové API na pridelenie subdomén localhostu tak, ako je to pri portoch.
V minulosti som namapoval subdoménu mojej verejnej domény (*.test.machinezoo.com
) na 127.0.0.1,
ale toto riešenie má niekoľko nevýhod: nie je dostupné pre programátorov bez vlastnej domény,
je potenciálne nebezpečné, nefunguje pri výpadku internetu a stále vyžaduje porty v URL adresách.
Lepším riešením je spustiť reverzný proxy server na porte 80, ktorý presmeruje virtuálne domény na konkrétne porty. Toto riešenie funguje dobre nielen pre vývoj webových aplikácií, ale aj pre bežné lokálne webové aplikácie, ako sú Syncthing a Open WebUI.
Konfigurácia DNS
Na mojom systéme (Fedora) subdomény ako app1.localhost
smerujú na rovnakú adresu ako samotný localhost
(::1 pre IPv6 a 127.0.0.1 pre IPv4),
čo je zrejme vďaka systemd-resolved
.
Rýchly test ukazuje, že to funguje tak s ping, ako aj vo Firefoxe.
Hoci to nemusí fungovať automaticky na všetkých operačných systémoch, zvyčajne je to jednoduché nakonfigurovať.
Ak nastavujete systémový resolver domén manuálne, môžete na localhost nasmerovať akúkoľvek
rezervovanú doménu.
Hoci .test
je dobrá voľba a .local
sa často (aj keď nesprávne) používa na tento účel,
ja preferujem .localhost
doménu, pretože je určená na tento účel a názov sa hodí pre akúkoľvek aplikáciu.
Výber reverzného proxy servera
Pokiaľ ide o reverzné proxy servery, sú tu traja hlavní kandidáti: Caddy, Traefik a Nginx. Nginx som vylúčil, pretože je písaný v C (potenciálne bezpečnostné riziko), je prepojený na Rusko prostredníctvom niektorých jeho najaktívnejších vývojárov a niektoré kľúčové funkcie sú k dispozícii len pre platiacich zákazníkov.
Z dvojice Caddy a Traefik som uprednostnil Caddy. Konfigurácia Traefiku je podstatne rozsiahlejšia a zložitejšia než Caddyfile. Caddy je navrhnutý tak, aby sa dal ľahko konfigurovať, pričom lokálne servery sú jedným z jeho zamýšľaných využití. Hoci Caddy má menej funkcií než Traefik, pre naše potreby je viac než postačujúci.
Stratégia výberu portov
Aj pri použití subdomén stále každá webová aplikácia potrebuje port, na ktorý môže reverzný proxy server preposielať dotazy. Namiesto konfigurovateľných portov, z čoho by bola extra práca pre programátorov a starosti pre používateľov, odporúčam zvoliť si pevný port náhodne v rozsahu medzi 1 024 a 32 000. Privilegovaným portom pod 1 024 a dynamickým portom nad 32 000 je lepšie sa vyhnúť. Konflikty s inými aplikáciami sú nepravdepodobné, ale ak ich niekto nahlási, proste zvoľte iný náhodný port. Porty by mali byť konfigurovateľné iba v populárnych lokálnych serveroroch, kde je to opodstatnené.
Konfigurácia Caddy
Caddy používa jediný konfiguračný súbor s názvom Caddyfile. Vďaka rozumným predvoľbám môže konfigurácia zostať pomerne stručná. Tu je základný príklad:
{ default_bind 127.0.0.1 admin off } http://app1.localhost { reverse_proxy localhost:8080 } http://*.app2.localhost { reverse_proxy localhost:8081 }
Nie som si istý, či je default_bind
striktne potrebný
(Caddy by to mohol odvodiť zo zakončenia domény na .localhost
), ale je lepšie špecifikovať to explicitne.
Vypol som admin API, aby som nemusel analyzovať jeho dopad na bezpečnosť,
aj keď niektorí používatelia by ho mohli chcieť, aby mohli znovu načítať Caddyfile bez reštartovania Caddy.
Predpona http://
v adrese zaručuje, že Caddy počúva iba na porte 80 a nesnaží sa získať TLS certifikáty.
Caddy podporuje long-poll a websocket automaticky bez dodatočnej konfigurácie.
Caddyfile môže byť trochu repetitívny. Aj keď Caddy podporuje šablóny (snippets) pre stručnú definíciu viacerých domén, manipulácia s parametrami šablón mi príde trochu ťažkopádna. Namiesto toho používam krátky Python skript na vygenerovanie zoznamu domén z dvojíc subdoména-port:
import textwrap mappings = { 8080: "app1", 8081: "*.app2", } print(textwrap.dedent(''' { default_bind 127.0.0.1 admin off } ''').strip()) for port, subdomain in mappings.items(): print(textwrap.dedent(f''' http://{subdomain}.localhost {{ reverse_proxy localhost:{port} }} ''').rstrip())
Spúšťanie Caddy servera
Hoci Caddy môže bežať priamo ako systémová služba (Caddyfile vyššie je na to navrhnutý), kontajnerizácia poskytuje čistejšie riešenie so zníženým bezpečnostným rizikom. Takto ho spustíte s Podmanom:
sudo podman run -d --name caddy \ --replace \ --pull=always \ --restart=always \ --network=host \ --stop-signal=SIGKILL \ -v /path/to/Caddyfile:/etc/caddy/Caddyfile:z \ docker.io/caddy sudo systemctl enable podman-restart
Parameter --network=host
je nevyhnutný, aby mohol Caddy preposlielať dotazy na lokálne porty.
Kontajner možno aktualizovať jednoducho opätovným spustením tohoto príkazu
a podman-restart
zabezpečí, že Caddy sa spustí po štarte systému.
Keďže admin API je vypnuté a TLS sa nepoužíva, nie je potrebné pripojiť dátový ani konfiguračný zväzok.
Stop signál SIGKILL
zaručuje rýchle vypnutie/reštart, čím sa vyhneme predvolenému správaniu SIGTERM
,
kde Caddy čaká na ukončenie spojení (čo môže trvať dlho pri aktívnych long-poll dotazoch).
Stojí to za to?
Nastavenie subdomén na localhoste má zmysel v niekoľkých prípadoch:
- Ak ste sa predtým museli uchýliť k presmerovaniu verejných subdomén na localhost.
- Pri prevádzkovaní viacerých virtuálnych domén na jednom lokálnom porte, aj keď by sa dalo argumentovať, že v tomto prípade je lepšie použiť viacero portov.
- Pri častom vývoji, kde automatické dopĺňanie URL v prehliadači šetrí čas.
- Pri prevádzkovaní viacerých lokálnych webových aplikácií s ťažko zapamätateľnými portami.
Pre občasné lokálne testovanie webových aplikácií je však toto riešenie zbytočne zložité.