Tailscale na Ubuntu Serveru: bezpečná instalace a SSH přístup

Návod co nejbezpečněji nainstaluje a nastaví Tailscale na Ubuntu Serveru (LTS) a zpřístupní přes něj SSH. Z internetu SSH na konci zavře; u serveru v lokální síti volitelně ponechá SSH z LAN jako záložní cestu. Tailscale tu slouží jen jako privátní kanál pro SSH: žádný exit node, subnet router, Serve, Funnel ani Tailscale SSH. Pokud na serveru zároveň běží Mullvad VPN s kill switchem, navazuje doplněk na konci článku.

Ověřeno proti oficiální dokumentaci k 2. 10. 2026: Tailscale 1.102.x (stable), Ubuntu 24.04 LTS (noble) a 26.04 LTS (resolute), stránka Mullvadu o split tunnelingu na Linuxu (aktualizovaná 25. 4. 2024).

Vladimír NeporAutorIng. Vladimír Nepor
Publikováno

Placeholdery

V příkazech nahraďte tyto hodnoty svými.

PlaceholderVýznamPříklad / kde ho zjistit
<ADMIN_USER>Linuxový uživatel, pod kterým se přihlašujete přes SSHvladimir
<TAILNET_USER>E-mail účtu, kterým se přihlašujete do tailnetu; v tagOwners ho může nahradit autogroup:adminvy@example.com, admin konzole → Users
<SERVER_TS_NAME>Jméno serveru v tailnetu (MagicDNS)srv01
<TAILSCALE_IP_SERVER>Tailscale IPv4 serverutailscale ip -4 na serveru
<TAILSCALE_IP_PC>Tailscale IPv4 vašeho PCtailscale ip -4 na PC
<TAILSCALE_IP_NB>Tailscale IPv4 notebookutailscale ip -4 na notebooku
<SSH_PORT>Port, na kterém běží sshd (výchozí 22, změna viz průvodce Ubuntu Serverem)sudo sshd -T | grep -i '^port'
<PUBLIC_IP>Veřejná IP serveru, ve scénáři B veřejná IP routeru (pro test, že SSH port je z internetu zavřený)panel poskytovatele / router
<LAN_SUBNET>Lokální síť pod vaší správou, ze které zůstane SSH povolené (jen scénář B; žádný široký rozsah, viz kapitola 6)192.168.88.0/24, ip -4 route na serveru
<LAN_IP_SERVER>IP serveru v lokální síti (jen scénář B)ip -4 addr show na serveru
<CODENAME>Codename Ubuntulsb_release -cs → jammy, noble, resolute
<ZEME>Kód země pro výběr Mullvad exit nodu (jen doplněk)CZ, DE
<MULLVAD_EXIT_NODE>Hostname Mullvad exit nodu (jen doplněk)tailscale exit-node list
<COMMIT>Commit nástroje pro výjimku z kill switche, jehož soubory jste prošli (jen doplněk)GitHub → History, např. d452d22

1. Přehled architektury

SSH na serveru zůstává běžný OpenSSH na portu <SSH_PORT>; Tailscale jen dopraví TCP spojení z vašeho PC přes šifrovaný WireGuard tunel na rozhraní tailscale0. Paket pak prochází třemi nezávislými vrstvami a zahodit ho může kterákoli z nich:

  1. Tailscale policy (grants/ACL) – vynucuje ji tailscaled na serveru ještě před vložením paketu do tailscale0. Povolí jen vašeho uživatele na tcp:<SSH_PORT>.
  2. UFW – lokální firewall v tabulkách ip filter / ip6 filter (iptables-nft). Povolí SSH port jen na tailscale0, ve scénáři B navíc z lokální sítě.
  3. OpenSSH – přihlášení pouze klíčem, bez roota, bez hesla (SSH klíče).

SSH port je na konci z internetu zavřený, takže server z internetu SSH vůbec nenabízí. U serveru v lokální síti pod vaší správou zůstává SSH z LAN jako záložní cesta (scénáře v kapitole 2). Spojení na internet navazuje jen tailscaled (koordinační server, DERP relay, UDP 41641), a to odchozí.

PC / notebook → (WireGuard UDP, přímo nebo přes DERP) → server: eth0 → tailscaled (dešifrování + policy) → (jen SSH port od vašeho uživatele) → rozhraní tailscale0 → UFW (allow in on tailscale0, SSH port) → sshd (jen klíče). Internet → SSH port z internetu: default deny (UFW zahodí). Scénář B: LAN → UFW (allow from LAN_SUBNET) → sshd.PC / notebooktailscale klientWireGuard UDP (přímo nebo přes DERP)server: eth0tailscaleddešifrování + policy (grants)jen SSH port od vašeho uživatelerozhraní tailscale0UFWallow in on tailscale0, SSH portsshdjen klíčeinternetSSH port z internetu:default denyLAN (scénář B)allow from <LAN_SUBNET>

Tailscale tu slouží jen jako privátní kanál pro SSH: žádný exit node, subnet router, Serve, Funnel ani Tailscale SSH. Pokud na serveru zároveň běží Mullvad VPN s kill switchem, potřebujete navíc doplněk na konci článku – bez něj Mullvad provoz z tailscale0 zahodí.

2. Předpoklady, scénáře a záložní přístup

Předpoklady:

  • Ubuntu Server 22.04, 24.04 nebo 26.04 LTS, uživatel <ADMIN_USER> se sudo (správa uživatelů).
  • Funkční přihlášení SSH klíčem pro <ADMIN_USER> a vypnuté přihlášení heslem i rootem podle článku SSH klíče (ověřené, ne jen nahrané do authorized_keys).
  • SSH na portu <SSH_PORT> – změnu výchozího portu 22 popisuje průvodce Ubuntu Serverem. Pokud port neměníte, dosaďte 22.
  • UFW se základními politikami podle průvodce, kapitola Nastavení Firewallu.
  • Tailscale účet (tailnet) s vaším uživatelem přihlášeným přes identity provider; na PC/notebooku nainstalovaný a přihlášený klient.
  • Během celé konfigurace mějte otevřenou jednu stávající SSH relaci a každou změnu testujte v druhé, nové relaci.

Scénáře nasazení

Co se na konci stane s SSH mimo Tailscale, záleží na tom, kde server běží a jaká mu zbude záložní cesta:

ScénářTypickyZáložní přístupSSH mimo tailscale0
A – mimo váš dosahVPS, server v datacentru, server v cizí sítikonzole poskytovatele (VNC, KVM, IPMI)zavřít úplně
B – lokální síť pod vaší správouserver doma nebo ve firmě za vlastním routeremSSH z LAN, fyzický přístuppovolit jen z <LAN_SUBNET>, z internetu zavřít

Ve scénáři A nepovolujte SSH z privátní sítě datacentra – není vaše. Ve scénáři B nesmí být SSH port na routeru přesměrovaný do internetu (viz NAT na MikroTiku).

Pojistka: automatický rollback

Před rizikovým krokem si naplánujte „dead man's switch“, který za 10 minut SSH port znovu otevře. Když vše funguje, časovač zrušíte.

# naplánuje znovuotevření SSH portu za 10 minut (transientní systemd timer)
sudo systemd-run --unit=rollback-ssh --on-active=10min /usr/sbin/ufw allow <SSH_PORT>/tcp

# vše funguje → pojistku zrušte (jinak se za 10 minut obecné pravidlo vrátí)
sudo systemctl stop rollback-ssh.timer

Ruční rollback

Pokud přístup ztratíte, z konzole poskytovatele (ve scénáři B z LAN nebo u fyzické klávesnice) vraťte změny v tomto pořadí (každý krok jednotlivě, po každém zkuste SSH):

# 1) znovu otevřít SSH port
sudo ufw allow <SSH_PORT>/tcp

# 2) pokud je problém v přihlášení: vrátit poslední změnu v /etc/ssh/sshd_config
#    nebo /etc/ssh/sshd_config.d/ a ověřit syntaxi
sudo sshd -t && sudo systemctl restart ssh

# 3) poslední možnost: vypnout firewall úplně
sudo ufw disable

Se zapnutým Mullvadem viz rollback v doplňku.

3. Instalace Tailscale z APT repozitáře

Instalujeme ze stable repozitáře pkgs.tailscale.com bez curl | sh: klíč i .list stáhneme do dočasných souborů, zkontrolujeme a teprve pak nainstalujeme. Repozitář má k 2. 10. 2026 větve pro jammy (22.04), noble (24.04) i resolute (26.04), aktuální stable je 1.102.x.

Zjistěte codename systému (použije se v URL repozitáře):

CODENAME="$(lsb_release -cs)"; echo "$CODENAME"

Stáhněte podpisový klíč repozitáře do dočasného souboru (formát noarmor = binární keyring, který APT čte přímo):

curl -fsSL "https://pkgs.tailscale.com/stable/ubuntu/${CODENAME}.noarmor.gpg" -o /tmp/tailscale-archive-keyring.gpg

Zkontrolujte obsah klíče bez importu do keyringu; long ID má končit 458CA832957F5868 a UID je „Tailscale Inc. (Package repository signing key) <info@tailscale.com>“:

gpg --show-keys --keyid-format long /tmp/tailscale-archive-keyring.gpg

Nainstalujte klíč do /usr/share/keyrings/ s vlastníkem root a právy 0644 (adresář nepatří do globální důvěry APT, klíč platí jen pro repozitář, který na něj ukáže přes signed-by):

sudo install -d -m 0755 /usr/share/keyrings
sudo install -m 0644 -o root -g root /tmp/tailscale-archive-keyring.gpg /usr/share/keyrings/tailscale-archive-keyring.gpg

Vytvořte .list soubor s vazbou signed-by na tento klíč (stejný obsah, jaký publikuje Tailscale):

printf '# Tailscale packages for ubuntu %s\ndeb [signed-by=/usr/share/keyrings/tailscale-archive-keyring.gpg] https://pkgs.tailscale.com/stable/ubuntu %s main\n' \
  "$CODENAME" "$CODENAME" | sudo tee /etc/apt/sources.list.d/tailscale.list

Zkontrolujte výsledné soubory:

cat /etc/apt/sources.list.d/tailscale.list
ls -l /usr/share/keyrings/tailscale-archive-keyring.gpg

Očekávaný obsah .list (pro noble):

# Tailscale packages for ubuntu noble
deb [signed-by=/usr/share/keyrings/tailscale-archive-keyring.gpg] https://pkgs.tailscale.com/stable/ubuntu noble main

Nainstalujte balíček a ověřte, že běží démon:

sudo apt update
sudo apt install tailscale
systemctl status tailscaled --no-pager
tailscale version

Po upgradu Ubuntu na další LTS změňte codename v .list i klíč (stáhněte <CODENAME>.noarmor.gpg pro nové vydání).

4. Připojení do tailnetu a tag tag:server

Server připojíme rovnou s tagem: tagované zařízení nepatří žádnému uživateli, má vypnutou expiraci klíče a v policy na něj lze cílit jako na roli. Tag jde přidělit jen tehdy, když je definovaný v tagOwners.

Krok 1 – definujte tag v policy. V admin konzoli otevřete Access controls a přepněte na JSON editor; výchozí vizuální editor JSON nezobrazuje (tag v něm lze přidat v Definitions → Tags). Výchozí policy už obsahuje zakomentovaný příklad tagOwners s tag:example. Odkomentujte ho a upravte; druhou sekci tagOwners nevkládejte, editor by ohlásil duplicitní klíč. Výchozí pravidla zatím ponechte, zpřísníme je v kapitole 7.

"tagOwners": {
  "tag:server": ["<TAILNET_USER>"],
},

<TAILNET_USER> je e-mail účtu, kterým se do tailnetu přihlašujete. Místo něj můžete použít autogroup:admin (všichni admini tailnetu), který používá i výchozí příklad.

Krok 2 – připojte server. Příkaz vypíše přihlašovací URL; otevřete ji na PC a přihlaste se jako <TAILNET_USER> (vlastník tagu).

sudo tailscale up --accept-dns=false --accept-routes=false --advertise-tags=tag:server

Význam flagů:

  • --accept-dns=false – Tailscale nepřepíše /etc/resolv.conf / systemd-resolved; server neřeší MagicDNS jména a jeho DNS zůstává pod vaší (případně Mullvadovou) kontrolou.
  • --accept-routes=false – server nepřijme subnet routy jiných uzlů; na Linuxu je to výchozí stav, explicitně ho uvádíme kvůli čitelnosti.
  • --advertise-tags=tag:server – identita zařízení bude tag, ne váš uživatel.

Flagy tailscale up se mezi spuštěními neukládají jako „diff“: pozdější tailscale up bez nich ohlásí chybu nebo je resetuje. Dodatečné změny dělejte přes tailscale set (např. sudo tailscale set --auto-update).

Krok 3 – pokud máte zapnutý Device approval, schvalte nový uzel v admin konzoli (Machines). Bez schválení server do tailnetu nepustí žádný provoz.

Krok 4 – ověřte stav a poznamenejte si IP:

tailscale status
tailscale ip -4      # → <TAILSCALE_IP_SERVER>

V admin konzoli u serveru zkontrolujte, že má tag tag:server a u Key expiry je „Disabled“. Na PC ověřte SSH přes Tailscale (SSH port je zatím otevřený i mimo Tailscale). PC musí být přihlášené do stejného tailnetu: tailscale status na serveru má vypsat server i PC. Pokud vypíše jen jeden řádek, PC v tailnetu není a test selže z tohoto důvodu, ne kvůli serveru.

ssh -p <SSH_PORT> <ADMIN_USER>@<TAILSCALE_IP_SERVER>

Pokud na serveru běží Mullvad s kill switchem, tento test selže – nejdřív nastavte výjimku z doplňku a pak se sem vraťte.

5. OpenSSH hardening

Přihlášení pouze klíčem, zákaz hesla a roota včetně přepisů v /etc/ssh/sshd_config.d/ (např. 50-cloud-init.conf) nastavte podle článku SSH klíče › SSHD konfigurace. Tady jen ověříme výsledek a doplníme omezení na jednoho uživatele.

Doplněk – přihlásit se smí jen <ADMIN_USER>. Soubory v /etc/ssh/sshd_config.d/ Ubuntu načítá přes Include na začátku sshd_config; pokud už někde AllowUsers máte, upravte raději ten záznam:

echo 'AllowUsers <ADMIN_USER>' | sudo tee /etc/ssh/sshd_config.d/10-allowusers.conf

Ověřte syntaxi a efektivní hodnoty (sshd -T vypíše výslednou konfiguraci po zpracování všech include):

sudo sshd -t && echo OK
sudo sshd -T | grep -Ei '^(port|permitrootlogin|passwordauthentication|kbdinteractiveauthentication|authenticationmethods|allowusers)'

Restartujte službu (restart ssh.service stávající relace neukončí). Na Ubuntu 24.04+ je sshd socket-aktivovaný, na 22.04 běží jako běžná služba; příkaz je v obou případech stejný:

sudo systemctl restart ssh

V nové relaci ověřte, že přihlášení klíčem funguje a heslo se nenabízí:

ssh -p <SSH_PORT> <ADMIN_USER>@<TAILSCALE_IP_SERVER>
ssh -p <SSH_PORT> -o PubkeyAuthentication=no -o PreferredAuthentications=password <ADMIN_USER>@<TAILSCALE_IP_SERVER>   # má skončit „Permission denied (publickey)“

sshd záměrně neomezujeme přes ListenAddress <TAILSCALE_IP_SERVER>: při startu systému tailscale0 ještě nemusí existovat a sshd by se nespustil. Omezení na rozhraní řeší UFW a Tailscale policy.

6. UFW a uzavření SSH mimo Tailscale

Výchozí politiky UFW (příchozí zakázat, odchozí povolit) nastavte podle průvodce, kapitola Aplikování výchozího nastavení pro UFW. Tady přidáme pravidlo pro tailscale0, ve scénáři B pravidlo pro LAN, a obecné pravidlo pro SSH port odebereme až po ověření.

Zjistěte současný stav (uvidíte existující pravidla, která možná chcete zachovat):

sudo ufw status verbose

Povolte SSH přes Tailscale:

sudo ufw allow in on tailscale0 to any port <SSH_PORT> proto tcp comment 'SSH via Tailscale'

Pokud UFW ještě neběží, dočasně ponechte SSH odevšad, aby zapnutí UFW nepřerušilo stávající relaci:

sudo ufw allow <SSH_PORT>/tcp comment 'TEMP SSH'
sudo ufw enable
sudo ufw status verbose

Z PC ověřte SSH přes Tailscale (ssh -p <SSH_PORT> <ADMIN_USER>@<TAILSCALE_IP_SERVER>).

Jen scénář B – povolte SSH z lokální sítě jako záložní cestu a z jiného zařízení v LAN ji ověřte:

sudo ufw allow from <LAN_SUBNET> to any port <SSH_PORT> proto tcp comment 'SSH from LAN (fallback)'

# z PC v LAN:
ssh -p <SSH_PORT> <ADMIN_USER>@<LAN_IP_SERVER>

Se zapnutým Mullvadem na serveru nejdřív povolte LAN podle kapitoly 11.4, jinak tento test selže.

Teprve pak odeberte obecné pravidlo.

# nejdřív nanečisto: vypíše, co by se smazalo
sudo ufw --dry-run delete allow <SSH_PORT>/tcp

sudo ufw delete allow <SSH_PORT>/tcp
# pokud jste dřív povolili profil OpenSSH:
sudo ufw delete allow OpenSSH
sudo ufw status numbered

Ve výpisu má zůstat <SSH_PORT>/tcp on tailscale0 (IPv4 a v6), ve scénáři B navíc <SSH_PORT>/tcp ALLOW IN <LAN_SUBNET>, plus případná pravidla jiných služeb, která jste zachovali.

Mazání podle čísla (sudo ufw delete <N>): IPv4 a IPv6 jsou samostatná pravidla, každé s vlastním číslem, a čísla se po každém smazání posunou. Před dalším smazáním proto znovu spusťte ufw status numbered.

Pokud SSH přes Tailscale (a ve scénáři B z LAN) funguje i v nové relaci, zrušte pojistku z kapitoly 2. Když ji nezastavíte, za 10 minut se obecné pravidlo vrátí a SSH port bude znovu otevřený odevšad:

sudo systemctl stop rollback-ssh.timer
systemctl list-timers rollback-ssh.timer   # → 0 timers listed

Jak spolu UFW a Tailscale souvisí

tailscaled v režimu --netfilter-mode=on (výchozí) vkládá do ip filter na začátek řetězce INPUT skok do ts-input, který obsahuje iifname "tailscale0" accept. Tím je provoz z tailscale0 přijat ještě před řetězci UFW, takže na tomto rozhraní ve skutečnosti filtruje Tailscale policy (kapitola 7), ne UFW. Ověření:

sudo iptables -S INPUT | head -5
sudo iptables -S ts-input

Výchozí chování odpovídá doporučení Tailscale a s restriktivní policy je dostatečné. Pokud chcete, aby UFW bylo na tailscale0 druhou nezávislou bránou, spusťte Tailscale s --netfilter-mode=off (pak firewall pro Tailscale spravujete sami) a povolte UDP 41641 pro přímá P2P spojení:

sudo tailscale set --netfilter-mode=off
sudo ufw allow 41641/udp comment 'Tailscale direct'

7. Tailscale admin konzole

Policy: jen můj uživatel na tag:server:<SSH_PORT>

Výchozí policy povoluje vše pravidlem v sekci acls ("src": ["*"], "dst": ["*:*"]). Sekce acls a grants se sčítají, takže přidáním grantu se nic nezpřísní. Pravidlo allow-all v acls proto odstraňte; nejjednodušší je nahradit celý soubor ukázkou níže (vynechává i výchozí sekci ssh). Pokud allow-all zůstane, neprojde ani test deny.

Policy používá grants (nástupce acls; port je v poli ip, ne v dst). Výchozí stav bez grantu je deny, takže server jako tag:server nemá žádné povolení a nemůže iniciovat spojení nikam; odpovědi na povolená spojení Tailscale propouští stavově. Sekce ssh chybí, takže Tailscale SSH je vypnuté; chybí i autoApprovers, takže server nemůže inzerovat routy ani exit node.

{
  // Kdo smí přidělit tag:server (Owner/Admin smí vždy).
  "tagOwners": {
    "tag:server": ["<TAILNET_USER>"],
  },

  // Jediné povolení v tailnetu: zařízení mého uživatele -> SSH na servery.
  "grants": [
    {
      "src": ["<TAILNET_USER>"],
      "dst": ["tag:server"],
      "ip":  ["tcp:<SSH_PORT>"],
    },
  ],

  // Bez sekce "ssh" (Tailscale SSH vypnuto) a bez "autoApprovers".

  "tests": [
    {
      "src":    "<TAILNET_USER>",
      "accept": ["tag:server:<SSH_PORT>"],
      "deny":   ["tag:server:80", "tag:server:443"],
    },
    {
      // Server nesmí iniciovat spojení na klienty.
      "src":  "tag:server",
      "deny": ["<TAILSCALE_IP_PC>:22", "<TAILSCALE_IP_NB>:22"],
    },
  ],
}

Grant se zdrojem <TAILNET_USER> pokrývá každé zařízení přihlášené tímto účtem, i to, které přidáte později. Záložní notebook proto změnu policy nevyžaduje a IP adresy zařízení v grantech nejsou potřeba; v ukázce jsou jen v testech, které o nové zařízení můžete doplnit.

Běžný ping na <TAILSCALE_IP_SERVER> neprojde (ICMP není povolené), tailscale ping ano (pracuje na úrovni Tailscale, ne OS).

Dopad na celý tailnet

Policy platí pro všechna zařízení v tailnetu, ne jen pro server. Po uložení ukázky smí každé zařízení jen SSH na tag:server. Zkontrolujte, co dalšího v tailnetu používáte, a granty doplňte:

  • Vlastní zařízení mezi sebou (soubory, vzdálená plocha mezi PC a notebookem): grant autogroup:member → autogroup:self.
  • Tailscale SSH na jiných zařízeních: pravidla v sekci ssh bez síťového grantu na port 22 nefungují. Grant autogroup:self níže port 22 pokrývá.
  • Exit node (vlastní nebo Mullvad add-on): zařízení, která ho používají, potřebují grant na autogroup:internet.
  • Staré nodeAttrs: zkontrolujte záznamy s mullvad (licence Mullvad add-onu) a odstraňte ty, které míří na zařízení, která už neexistují.
  // do "grants" podle potřeby přidejte:
    {
      // Moje zařízení mezi sebou (všechny porty, včetně 22 pro Tailscale SSH).
      "src": ["autogroup:member"],
      "dst": ["autogroup:self"],
      "ip":  ["*"],
    },
    {
      // Moje zařízení smí používat exit node.
      "src": ["autogroup:member"],
      "dst": ["autogroup:internet"],
      "ip":  ["*"],
    },

Tagovaný server do autogroup:member nepatří, takže tyto granty jeho omezení nemění a testy výše dál platí.

Device approval, nebo Tailnet Lock

Podle dokumentace Tailscale nelze mít zapnuté Device approval a Tailnet Lock současně, jde o vzájemně výlučné funkce. Vyberte jednu:

FunkceCo chráníKdy ji zvolit
Tailnet LockNový uzel musí podepsat váš důvěryhodný uzel; nevěříte ani koordinačnímu serveru Tailscale.Doporučeno, pokud máte aspoň 2 desktopová zařízení (Linux/macOS/Windows) jako podepisující uzly.
Device approvalNový uzel musí schválit admin v konzoli.Když nemáte dva podepisující uzly nebo nechcete spravovat disablement secrets.

Device approval: admin konzole → Settings → Device management → zapněte ruční schvalování nových zařízení. Nová zařízení pak čekají ve stavu „Needs approval“ (Machines).

Tailnet Lock

Podepisující uzly budou vaše PC a notebook (Tailscale ≥ 1.46.1; Android podepisovat neumí). Server podepisujícím uzlem není: na stroji dostupném z internetu nemají být klíče, které smí do tailnetu přidávat další zařízení.

  1. Admin konzole → Settings → Device management → Enable Tailnet Lock.
  2. V sekci Add signing nodes vyberte PC a notebook (minimum jsou dva uzly; třetí, např. záložní stroj, výrazně usnadní obnovu).
  3. Volitelně zapněte odeslání jednoho disablement secret podpoře Tailscale (záchrana, pokud své ztratíte).
  4. Zkopírujte vygenerovaný příkaz tailscale lock init … a spusťte ho na jednom z podepisujících uzlů.
  5. Příkaz vypíše 10 disablement secrets. Uložte je offline (správce hesel, tisk do trezoru); znovu se nezobrazí.

Po init jsou všechny existující uzly včetně serveru podepsané. Ověřte na serveru, že vidí stejné důvěryhodné klíče jako PC:

tailscale lock status

Nový nebo přeinstalovaný server se do zamčeného tailnetu dostane až po podpisu z podepisujícího uzlu (příkaz zjistíte na serveru přes tailscale lock status nebo v konzoli u uzlu „Locked out“ → Sign node):

# na PC nebo notebooku
tailscale lock sign nodekey:<NODEKEY_SERVERU>

Ztráta zařízení:

  • Ztracený, ale nekompromitovaný podepisující uzel odeberte ze zbývajícího: tailscale lock remove tlpub:<KLIC_ZTRACENEHO>. Uzly, které podepsal, se automaticky přepodepíší.
  • Kompromitovaný klíč zneplatněte přes tailscale lock revoke-keys tlpub:<KLIC> s co-signováním dalšími uzly. Potřebujete o jedno co-signování víc, než kolik klíčů revokujete; se dvěma podepisujícími uzly to po kompromitaci jednoho nezvládnete, proto třetí uzel.
  • Ztráta všech podepisujících uzlů: Tailnet Lock vypněte v konzoli (Device management → Disable Tailnet Lock) jedním disablement secret a inicializujte ho znovu. Bez secretu (a bez kopie u podpory) tailnet obnovit nelze.

MFA a key expiry

Tailscale přihlašování deleguje na identity provider (Google, Microsoft, GitHub, OIDC…). Zapněte na účtu <TAILNET_USER> u IdP vícefaktorové ověření, ideálně passkey nebo hardwarový klíč; kdo ovládne tento účet, může přidat zařízení a upravit policy.

Server má jako tagované zařízení expiraci klíče vypnutou automaticky (ověřte v Machines). Vaše PC a notebook expirují podle nastavení tailnetu (výchozí 180 dní) a čas od času se znovu přihlásí přes IdP, což je žádoucí.

Úklid zařízení

Po nasazení odeberte v Machines staré a nepoužívané stroje (Remove). Každé zařízení v tailnetu je potenciální vstupní bod: platí pro něj granty jeho uživatele, takže kdo ho ovládne, má stejný přístup jako vy.

8. Aktualizace

Tailscale aktualizujte jedním mechanismem, ne dvěma: oba používají APT a mohou si navzájem blokovat dpkg zámek. Pro server doporučuji unattended-upgrades, protože běží v okně řízeném systémovým timerem, loguje do /var/log/unattended-upgrades/ a řeší i ostatní balíčky. Kdo chce restarty tailscaled plánovat sám, zvolí ruční údržbu.

Ubuntu Server má unattended-upgrades nainstalované a zapnuté pro bezpečnostní aktualizace Ubuntu už po instalaci (soubor /etc/apt/apt.conf.d/20auto-upgrades). Ověření:

cat /etc/apt/apt.conf.d/20auto-upgrades     # APT::Periodic::Unattended-Upgrade "1";
systemctl is-enabled unattended-upgrades apt-daily-upgrade.timer

Varianta A (doporučená): unattended-upgrades

Instalaci a základní nastavení popisuje článek Unattended upgrades (EN). Pak přidejte repozitář Tailscale mezi povolené originy (klíčové slovo site porovnává hostname repozitáře):

sudo tee /etc/apt/apt.conf.d/52unattended-upgrades-tailscale >/dev/null <<'EOF'
Unattended-Upgrade::Origins-Pattern {
        "site=pkgs.tailscale.com";
};
EOF

Ověřte, že APT repozitář vidí a unattended-upgrades by Tailscale zahrnul (dry-run nic neinstaluje):

apt-cache policy tailscale
sudo unattended-upgrade --dry-run --debug 2>&1 | grep -i tailscale

Vypněte vestavěný auto-update Tailscale, aby se mechanismy nepraly:

sudo tailscale set --auto-update=false

Varianta B: tailscale set --auto-update

Tailscale si aktualizace stahuje sám přes APT podle vlastního plánu (plánování nelze nastavit). Funguje jen se souborem /etc/apt/sources.list.d/tailscale.list ve formátu z kapitoly 3.

sudo tailscale set --auto-update
tailscale get auto-update

Varianta C: ruční údržba

Bezpečnostní aktualizace Ubuntu běží automaticky (výchozí stav výše). Tailscale, případně Mullvad, aktualizujete ručně: pravidelně, například jednou měsíčně, a navíc hned po zveřejnění bezpečnostního bulletinu. tailscaled se pak restartuje jen ve chvíli, kterou zvolíte. Varianta se hodí pro server, kde výpadky a restarty služeb plánujete sami (produkční aplikace, databáze, dlouhé úlohy).

Sledujte Tailscale security bulletins, vypněte vestavěný auto-update a repozitář Tailscale do Origins-Pattern nepřidávejte:

sudo tailscale set --auto-update=false

# jednou měsíčně nebo po bulletinu:
sudo apt update
apt list --upgradable 2>/dev/null | grep -E 'tailscale|mullvad'
sudo apt install --only-upgrade tailscale

Co ověřit po aktualizaci

Upgrade balíčku tailscale restartuje tailscaled, takže SSH relace přes Tailscale na pár sekund spadne; dlouhé práce spouštějte v tmux. Po upgradu zkontrolujte:

tailscale version
tailscale status

9. Ověření funkčnosti

Projděte checklist po dokončení konfigurace a znovu po každém rebootu nebo upgradu Tailscale. Se zapnutým Mullvadem doplňte kontroly z doplňku.

Na serveru:

tailscale status                              # server online, PC/NB v seznamu
tailscale ping <TAILSCALE_IP_PC>              # odpověď přes DERP nebo direct
sudo ufw status verbose                       # deny incoming, SSH port jen na tailscale0 (B: + LAN)
sudo ss -tlnp | grep ':<SSH_PORT> '           # sshd (u socket aktivace vlastník systemd) na 0.0.0.0 a [::]
sudo sshd -T | grep -Ei '^(port|permitrootlogin|passwordauthentication)'

Na PC (klient v tailnetu):

ssh -p <SSH_PORT> <ADMIN_USER>@<TAILSCALE_IP_SERVER>        # musí projít
nc -vz -w 5 <PUBLIC_IP> <SSH_PORT>                          # musí skončit timeoutem
tailscale down && nc -vz -w 5 <TAILSCALE_IP_SERVER> <SSH_PORT>; tailscale up   # bez Tailscale nedostupné

Jen scénář B, z PC v LAN:

ssh -p <SSH_PORT> <ADMIN_USER>@<LAN_IP_SERVER>              # záložní cesta musí projít

Test po rebootu:

sudo reboot
# z PC po ~1–2 minutách:
ssh -p <SSH_PORT> <ADMIN_USER>@<TAILSCALE_IP_SERVER>

U serveru s databázemi nebo jinými službami, které drží stav na disku, otestujte i studený start: vypněte ho příkazem sudo shutdown -h now a zapněte z konzole poskytovatele nebo tlačítkem. Ze zásuvky ho nevypínejte, hrozí poškození dat. Studený start je dobrý test pořadí služeb.

Výsledek:

  • SSH přes Tailscale funguje z PC i notebooku
  • SSH z internetu (<PUBLIC_IP>) končí timeoutem
  • (scénář B) SSH z LAN na <LAN_IP_SERVER> funguje
  • Heslo se nenabízí, root se nepřihlásí
  • Policy testy v admin konzoli prošly
  • Po rebootu vše funguje bez ručního zásahu
  • Pojistka rollback-ssh.timer je zrušená
  • V Machines nejsou staré a nepoužívané stroje

10. Troubleshooting

SSH přes tailnet nejde

Postupujte od klienta k serveru. Každý krok zúží, kde je problém:

  1. tailscale status na serveru: vidíte jen server? Klient není v tailnetu (není přihlášený, je v jiném tailnetu, nebo čeká na schválení).
  2. tailscale ping <TAILSCALE_IP_SERVER> z klienta: neprojde? Problém je na klientovi, typicky Mullvad nebo jiná VPN na PC.
  3. tcpdump na serveru během pokusu o SSH ukáže, kam paket dojde.
sudo tcpdump -ni tailscale0 tcp port <SSH_PORT>
Výpis tcpdumpPříčina
SYN ([S]) bez odpovědipaket zahodí firewall na serveru (UFW), nebo Mullvad (kapitola 11)
na SYN odpoví [R] (reset)sshd na portu neposlouchá: zkontrolujte ss -tlnp a <SSH_PORT>
nepřijde nicpaket na server nedorazil: problém je na straně klienta, nebo ho zahodila policy (kapitola 7, testy v admin konzoli)

Spojení jde přes DERP místo přímého P2P

tailscale status          # u peeru „relay "fra"“ místo „direct <ip:port>“
tailscale netcheck        # UDP, MappingVariesByDestIP, nejbližší DERP
tailscale ping <TAILSCALE_IP_PC>

Ověřte, že netcheck hlásí UDP: true; MappingVariesByDestIP: true znamená obtížný NAT na straně serveru nebo klienta. Při --netfilter-mode=off musí UFW povolovat UDP 41641 (kapitola 6). SSH přes DERP funguje spolehlivě, jen s vyšší latencí. Se zapnutým Mullvadem je relay očekávaný stav (doplněk).

Logy

journalctl -u tailscaled -b --no-pager | tail -50
journalctl -u ssh -b --no-pager | tail -50

11. Doplněk: Tailscale současně s Mullvad VPN

Tato kapitola platí jen tehdy, když je na serveru aplikace Mullvad (mullvad-daemon) připojená s kill switchem. Instalaci a nastavení Mullvadu popisuje článek Mullvad VPN na Ubuntu Serveru. Bez výjimky níže SSH přes Tailscale nefunguje.

11.1 Proč Mullvad blokuje Tailscale

Mullvad vytváří tabulku inet mullvad s řetězci input a output (priorita 0, policy drop). Propustí jen provoz přes wg0-mullvad, loopback, spojení na relay Mullvadu a spojení s connection-tracking markem 0x00000f41. Dále přidává ip rule, které vše bez meta marku 0x6d6f6c65 posílají do jeho routovací tabulky (výchozí cesta přes wg0-mullvad). Obě hodnoty platí k 2. 10. 2026: ct mark 0x00000f41 (split tunnel MARK v kódu aplikace) a meta mark 0x6d6f6c65 (fwmark tunelu).

Na hooku input jádro projde base chainy všech tabulek podle priority (UFW v ip filter, Mullvad v inet mullvad) a accept v jedné tabulce znamená jen „pokračuj do další“; drop kdekoli je konečný. Pro SSH přes Tailscale to znamená dva problémy:

  1. Příchozí SYN z tailscale0 zahodí input chain Mullvadu. Řešení: spojení označit ct mark 0x00000f41 v řetězci s nižší prioritou (−100), než má Mullvad.
  2. Odpověď sshd nemá žádný mark, takže ji ip rule Mullvadu pošle do wg0-mullvad, kde ji Mullvad zahodí. Řešení: odpovědím nastavit meta mark 0x6d6f6c65 v řetězci typu route (nástroj z 11.2 to dělá sám pro všechny odpovědi na spojení přijatá z tailscale0, pravidlem ct direction reply ct mark 0x00000f41); jádro pak cestu přepočítá a paket najde v tabulce 52 Tailscale přes tailscale0.
Příchozí SYN z PC na tailscale0: výjimka v hooku input (priorita -100, ct mark 0x00000f41) → ip filter / INPUT (ts-input + UFW, priorita 0) → inet mullvad / input (priorita 0: ct mark 0xf41 = accept) → sshd. Odpověď sshd na PC: výjimka v route output (priorita -100, odpověď na spojení s ct mark 0x00000f41: meta mark 0x6d6f6c65) → reroute: ip rule Mullvadu se přeskočí, table 52 Tailscale = tailscale0 → inet mullvad / output (ct mark 0xf41 = accept).Příchozí SYN z PC na tailscale0Odpověď sshd na PCvýjimka: hook inputpriorita -100ct mark 0x00000f41ip filter / INPUTts-input + UFW, priorita 0inet mullvad / inputpriorita 0: ct mark 0xf41 = acceptsshdvýjimka: route outputpriorita -100, odpověď s ct mark 0xf41→ meta mark 0x6d6f6c65reroute: ip rule Mullvadu se přeskočítable 52 Tailscale = tailscale0inet mullvad / outputct mark 0xf41 = accept

11.2 Výjimka z kill switche

Výjimku nastavíte hotovým nástrojem Stakers-space/staking-scripts – mullvad/enable_tailscale. Instalaci, použití a bezpečné nasazení na vzdáleném serveru popisuje jeho README; tady jen to, co souvisí s nastavením z tohoto článku.

Soubory stahujte z konkrétního commitu, ne z větve main: dostanete přesně obsah, který jste prošli, a pozdější změna v repozitáři se na server nedostane bez vašeho vědomí. Článek popisuje stav commitu d452d22 (13. 10. 2026). Projděte všech pět souborů, včetně watchdog .service a .timer. Pokud neměníte cesty, služby upravovat nemusíte.

COMMIT=<COMMIT>
BASE="https://raw.githubusercontent.com/Stakers-space/staking-scripts/$COMMIT/mullvad/enable_tailscale"
mkdir -p /tmp/enable_tailscale && cd /tmp/enable_tailscale
for f in netfilter_manager.sh mullvad-tailscale_netfilters.rules mullvad-enable_tailscale.service \
         mullvad-enable_tailscale-watchdog.service mullvad-enable_tailscale-watchdog.timer; do
  curl -fsSLO "$BASE/$f"
done
less netfilter_manager.sh mullvad-*

Při instalaci podle README pak místo curl z main použijte tyto prošlé soubory.

  • Tabulka inet mullvad-tailscale (soubor /usr/local/etc/mullvad-tailscale_netfilters.rules) se načítá výhradně přes netfilter_manager.sh add-rules – atomicky, bez duplicit, a při chybném souboru zůstanou původní pravidla. Soubor nenačítejte přímo přes nft -f.
  • Příchozí provoz: řetězec s prioritou −100 (před firewallem Mullvadu) označí veškerý příchozí provoz z tailscale0. Co z tailnetu na server skutečně projde, určuje policy z kapitoly 7 (jen tcp:<SSH_PORT>) a UFW.
  • Odpovědi: odpovědi na spojení přijatá z tailscale0 směruje mimo tunel řádek ct direction reply ct mark 0x00000f41 …, pro celý tailnet, IPv4 i IPv6. Seznam IP klientů k tomu není potřeba.
  • EXCLUDED_IPS slouží jen pro spojení, která do tailnetu otevírá server (např. zálohy na PC). Policy z kapitoly 7 serveru otevírat spojení do tailnetu zakazuje, takže v tomto nastavení seznam nepotřebujete: zakomentujte define EXCLUDED_IPS i řádek ip daddr $EXCLUDED_IPS … (ukázka níže). Prázdnou množinu { } nft nepřijme. Alternativou je ponechat jako zástupnou hodnotu vlastní Tailscale IP serveru s komentářem. Ukázkové hodnoty 100.101.102.103 a 100.104.105.106 v souboru nenechávejte. Nikdy sem nedávejte celý rozsah 100.64.0.0/10 – CGNAT používají i poskytovatelé a provoz by obešel kill switch.
  • DNS výjimka pro 100.100.100.100 je potřeba jen s --accept-dns=true. Server z tohoto článku běží s --accept-dns=false, takže řetězec excludeDns nic nedělá a můžete ho v souboru pravidel zakomentovat.
  • Služby: mullvad-enable_tailscale.service načte pravidla při startu ještě před Mullvadem a Tailscale a znovu po každém restartu nftables.service; zastavení služby pravidla neodstraní. Watchdog mullvad-enable_tailscale-watchdog.timer každou minutu kontroluje, že tabulka existuje.
# Server neotevírá spojení do tailnetu (policy z kapitoly 7), seznam není potřeba.
# define EXCLUDED_IPS = {
#     <TAILSCALE_IP_PC>
# }

    chain excludeOutgoing {
        type route hook output priority -100; policy accept;
        ct direction reply ct mark 0x00000f41 meta mark set 0x6d6f6c65;
        # ip daddr $EXCLUDED_IPS ct mark set 0x00000f41 meta mark set 0x6d6f6c65;
    }

Ověření:

/usr/local/bin/netfilter_manager.sh status                  # tabulka inet mullvad-tailscale (exit 1 = chybí)
systemctl list-timers mullvad-enable_tailscale-watchdog.timer
journalctl -u mullvad-enable_tailscale-watchdog.service      # [WARN] Table ... is missing = někdo pravidla smazal

Novou SSH relaci přes Tailscale otevřete z druhého terminálu – už otevřená relace nic nedokazuje, protože si mark drží v conntracku.

11.3 Pořadí ip rule: Mullvad musí být před Tailscale

Tailscale přidává pravidla s pevnými prioritami 5210–5270. Mullvad přidává svá pravidla při každém připojení bez explicitní priority, takže je jádro umístí před první existující pravidlo. Pokud se Mullvad připojí dřív než tailscaled, jeho pravidla skončí na 32764/32765 (za Tailscale), provoz tailscaled jde přímo na eth0, Mullvad ho zahodí a Tailscale je offline. Správný stav (pravidla Mullvadu 5208/5209 před 5210):

0:      from all lookup local
5208:   from all lookup main suppress_prefixlength 0
5209:   not from all fwmark 0x6d6f6c65 lookup 1836018789
5210:   from all fwmark 0x80000/0xff0000 lookup main
...
5270:   from all lookup 52

Okamžitá oprava je mullvad reconnect. Aby tailscaled při bootu startoval před Mullvadem, přidejte drop-in. Příkaz otevře editor; text napište mezi dva horní komentáře, jinak se neuloží:

sudo systemctl edit mullvad-daemon.service
### Editing /etc/systemd/system/mullvad-daemon.service.d/override.conf
### Anything between here and the comment below will become …

[Unit]
After=tailscaled.service

### … below this comment will be discarded

Ověřte, že výpis obsahuje tailscaled.service:

systemctl show mullvad-daemon -p After

Mechanismus je odvozený ze zdrojového kódu Mullvadu a chování jádra; ověřte ho na svém stroji po rebootu (ip rule show). Watchdog z 11.2 hlídá jen existenci nft tabulky, pořadí ip rule nekontroluje. Pokud by After= nestačilo, použijte místo pevného zpoždění podmíněné čekání na pravidlo Tailscale. Do stejného drop-inu přidejte:

[Service]
# čeká nejvýš 30 s na pravidlo Tailscale (lookup 52); Mullvad se spustí v každém případě
ExecStartPre=/bin/sh -c 'for i in $(seq 30); do ip rule show | grep -q "lookup 52" && exit 0; sleep 1; done; exit 0'

11.4 Scénář B: SSH z LAN se zapnutým Mullvadem

Kill switch Mullvadu zahazuje i provoz z lokální sítě, takže záložní SSH z LAN (scénář B) se zapnutým Mullvadem nefunguje. Povolte LAN v Mullvadu:

mullvad lan set allow
mullvad lan get

Mullvad pak propustí provoz z a do privátních rozsahů; které porty jsou z LAN dostupné, dál určuje UFW. Odpovědi do LAN jdou přes eth0 díky pravidlu 5208 (lookup main suppress_prefixlength 0). Ve scénáři A LAN v Mullvadu nepovolujte.

11.5 Aktualizace a ověření

Pokud máte Mullvad z jeho APT repozitáře a používáte unattended-upgrades, přidejte do Origins-Pattern z kapitoly 8 i jeho hostname (zjistíte z apt-cache policy mullvad-vpn). Upgrade mullvad-vpn restartuje daemon, který znovu vytvoří inet mullvad a svá ip rule; tabulka výjimky má zůstat. Po upgradu a po rebootu ověřte:

mullvad status                                # Connected
sudo nft list tables                          # inet mullvad + inet mullvad-tailscale
ip rule show                                  # 5208/5209 před 5210 (kapitola 11.3)
curl https://am.i.mullvad.net/connected       # provoz serveru jde přes Mullvad

11.6 DERP místo P2P

S Mullvadem je relay očekávaný stav: tailscaled posílá svůj UDP provoz tunelem Mullvadu, peer vidí jako zdroj exit IP Mullvadu a Mullvad nenabízí přesměrování portů, takže hole punching obvykle selže. Spojení „opravovat“ výjimkou pro UDP 41641 mimo tunel nedoporučuji: odhalili byste peerům skutečnou IP serveru.

11.7 Alternativa: Mullvad exit nodes přes Tailscale

Místo aplikace Mullvad na serveru lze použít Mullvad VPN add-on v Tailscale: server pošle veškerý internetový provoz přes Mullvad server, který se v tailnetu tváří jako exit node. Odpadne výjimka z kill switche i hlídání pořadí ip rule, protože na serveru běží jen jeden VPN klient. Funkce je k 2. 10. 2026 v betě (Tailscale ≥ 1.48.2).

Dává smysl, když server neposkytuje žádné veřejné služby na své veřejné IP (odpovědi by odcházely přes Mullvad), chcete jednodušší správu a nepotřebujete funkce aplikace Mullvad, které add-on nenabízí (vlastní kill switch, nastavení aplikace na serveru).

Policy z kapitoly 7 rozšíříte o přidělení licence přes nodeAttrs a grant na autogroup:internet. Server tím smí iniciovat spojení do internetu přes exit node, do tailnetu stále nesmí nikam:

  // Licence Mullvad add-onu pro server.
  "nodeAttrs": [
    {
      "target": ["tag:server"],
      "attr":   ["mullvad"],
    },
  ],

  // do "grants" přidejte:
    {
      // Server smí používat exit node (internet), ne zařízení v tailnetu.
      "src": ["tag:server"],
      "dst": ["autogroup:internet"],
      "ip":  ["*"],
    },

Licence spravujte buď v admin konzoli (Settings → General → Mullvad VPN → Configure), nebo přes nodeAttrs v policy, ne oběma způsoby současně. Základní add-on obsahuje 5 licencí.

Nejdřív vypněte aplikaci Mullvad a odstraňte výjimku (zastavení služby pravidla neodstraní), odinstalaci Mullvadu popisuje článek Mullvad VPN › Odstranění. Pak vyberte exit node (seznam se po prvním přidělení licence může načítat až 2 minuty):

mullvad lockdown-mode set off
mullvad disconnect
sudo systemctl disable --now mullvad-enable_tailscale-watchdog.timer mullvad-enable_tailscale.service
sudo /usr/local/bin/netfilter_manager.sh remove-rules

tailscale exit-node list --filter=<ZEME>
sudo tailscale set --exit-node=<MULLVAD_EXIT_NODE>
curl https://am.i.mullvad.net/connected

Ve scénáři B nebo když server potřebuje přístup do vlastní LAN přidejte --exit-node-allow-lan-access, jinak by odpovědi do LAN odcházely přes exit node. DNS: s --accept-dns=false server používá svůj lokální resolver a dotazy odcházejí přes exit node; pro jistotu proti DNS leakům nastavte jako resolver Mullvad DNS nebo přepněte na --accept-dns=true s globálním nameserverem v admin konzoli. S Tailnet Lock musíte každý Mullvad exit node podepsat, a to ze zařízení, které má samo licenci Mullvad.

OblastAplikace Mullvad na serveruMullvad exit node přes Tailscale
Kill switchVlastní firewall Mullvadu, volitelně i když daemon neběží (lockdown mode).Nezávislý kill switch není; po tailscale down nebo pádu tailscaled jde provoz přímo s veřejnou IP.
NezávislostVPN funguje i při výpadku nebo zrušení Tailscale.VPN i SSH přístup závisí na jednom klientovi a jednom účtu.
Anonymita vůči TailscaleTailscale o Mullvad účtu neví.Mullvad účty spravuje Tailscale a ví, který uživatel k nim patří; z logů může vidět, ke kterému Mullvad serveru se zařízení připojuje.
Veřejné služby serveruFungují podle nastavení split tunnelu.Odpovědi odcházejí přes exit node, veřejné služby na IP serveru prakticky nejdou.
SložitostVýjimka z kill switche, hlídání pořadí ip rule.Jen policy a tailscale set --exit-node.

12. Zdroje

Oficiální dokumentace a zdroje ověřené k 2. 10. 2026.

Související návody na Vrealmatic

Tailscale

Mullvad

Potřebujete bezpečně nastavit server?

Navrhneme, zabezpečíme a zdokumentujeme sítě i servery.

Servery a sítě