sish: Ngrok na vlastním železe

sish: Ngrok na vlastním železe

Představení projektu sish jako alternativy k ngroku pro bezpečné tunelování na vlastním serveru s vlastní doménou.

sish: Ngrok na vlastním železe

Nejsem nijak spojen s projektem sish. Jen mi přijde jako skvělé řešení pro bezpečné tunelování a chtěl jsem se o něj podělit. Už ho nějakou dobu používám a jsem s ním velmi spokojený. Je to skvělá alternativa k ngroku, která nabízí plnou kontrolu nad vašimi daty a provozem.

sish Představení

sish je open-source projekt, který vytvořil Antonio Mika. Umožňuje vývojářům bezpečně vystavit svoje lokální prostředí do internetu bez nutnosti úpravy Firewallu, podobně jako ngrok, ale bez nutnosti mít nainstalovaného lokálního klienta, jelikož využívá standardní SSH protokol pro vytváření tunelů.

V moment, kdy vývojáři potřebují sdílet rozpracované změny s kolegy nebo zákazníky, tak narazí na ngrok jelikož je to první výsledek, který se objeví ve vyhledávačích. Nicméně ngrok je proprietární řešení, skrze které potečou vaše potenciálně citlivá data. Bezplatná verze má omezení, která nemusí vyhovovat všem případům použití.

Nejětší problém, který s ngrokem mám, je ten, že veškerý provoz je směrován přes jejich servery, přičemž TLS terminace se děje na jejich infrastruktuře, což představuje riziko nešifrovaného provozu mezi ngrok servery a lokálními službami. To může být významný bezpečnostní problém. Minimálně je to riziko, který by se měl brát v úvahu v rámci Risk Assessmentu.

V poslední době vzniklo dost alternativ k ngroku, které jsou open-source, ale nepřijdou mi tak flexibilní a jednoduché jako sish. Většina z nich vyžaduje instalaci klientského software na vývojářských strojích, což může být překážkou pro adopci, nebo nejsou snadno self-hostovatelné. sish vyniká tím, že využívá standardní SSH protokol. Už i Windows ve výchozí instalaci obsahuje ssh klienta, což usnadňuje použití SSH protokolu bez nutnosti dalšího softwaru.

Pokud můžete tolerovat občasné výpadky (kvůli údržbě), můžete mít plně funkční tunelovací řešení za minimální náklady a s maximální kontrolou nad daty. Obvykle můžete použít nejlevnější VM na jakémkoli cloudovém poskytovateli.

Pro více informací o tom, jak sish funguje, se podívejte do oficiální dokumentace: https://docs.ssi.sh/how-it-works

Použití

Abyste měli představu o tom, jak sish funguje v praxi a jestli vyhovuje vašim potřebám, tak vám ho ukážu na jednoduchém příkladu. Pro pokročilejší funkce se můžete podívat do oficiálního cheatsheetu: https://docs.ssi.sh/cheatsheet

Pro demonstraci použiju sish server, který jsem vytvořil jako součást tohoto tutoriálu.

Lokální službu můžu vystavit pomocí tohoto příkazu:

1
2
3
4
5
6
7
8
ssh -R usage-demo:80:localhost:9090 dt
    ^       ^      ^      ^     ^   ^
    |       |      |      |     |   +-- SSH Alias pro připojení k `sish` serveru, který je definovaný v `~/.ssh/config`
    |       |      |      |     +------ Port lokálně běžící služby, kterou chcete vystavit do internetu
    |       |      |      +------------ Ve většině případů to bude vždy `localhost`
    |       |      +------------------- Port HTTP služby v rámci `sish` serveru
    |       +-------------------------- Subdoména, pod kterou bude služba vystavena. V méme případě bude vypadata takto: `https://usage-demo.dt.smtl.cz`
    +---------------------------------- Reverse SSH shell připojení

Výstup bude vypadat nějak takto:

1
2
3
4
5
Press Ctrl-C to close the session.

Starting SSH Forwarding service for http:80. Forwarded connections can be accessed via the following methods:
HTTP: http://usage-demo.dt.smtl.cz
HTTPS: https://usage-demo.dt.smtl.cz

Pokud zmiňovanou URL otevřeme v prohlížeči, měli bychom vidět službu běžící na našem lokálním počítači, ale přístupnou přes sish tunel a zabezpečenou certifikátem. demonstrace použití

Subdoména nemusí být specifickovaná. Pokud se nezadá, sish použije náhodnou hodnotu:

1
2
3
4
5
6
7
$ ssh -R 80:localhost:9090 dt
Press Ctrl-C to close the session.

The subdomain localhost.dt.smtl.cz is unavailable. Assigning a random subdomain.
Starting SSH Forwarding service for http:80. Forwarded connections can be accessed via the following methods:
HTTP: http://k9x.dt.smtl.cz
HTTPS: https://k9x.dt.smtl.cz

Ke zjednodušení používání, můžete vytvořit vlastní alias ve vašem zsh/bash rc souboru:

1
2
3
4
5
6
7
8
sish() {
    port="${1:-80}"
    domain="${2:-${HOST}}"
    domain="${domain//./-}:" # Replace dot with dash and add colon at the end
    
    echo "Opening local tunnel on port ${port}"
    ssh -R "${domain}80:localhost:${port}" dt
}

Díky této funkci lze spustit tunel jednoduše takto:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
$ sish 9090
Opening local tunnel on port 9090
Press Ctrl-C to close the session.

Starting SSH Forwarding service for http:80. Forwarded connections can be accessed via the following methods:
HTTP: http://mctrouba.dt.smtl.cz
HTTPS: https://mctrouba.dt.smtl.cz

Connection to localhost closed by remote host.
Connection to localhost closed.
$ sish 9090 my-awesome-service
Opening local tunnel on port 9090
Press Ctrl-C to close the session.

Starting SSH Forwarding service for http:80. Forwarded connections can be accessed via the following methods:
HTTP: http://my-awesome-service.dt.smtl.cz
HTTPS: https://my-awesome-service.dt.smtl.cz

Pokud se funkce bude jmenovat ngrok, tak můžete použít stejný příkaz jako předtím, ale bude používat váš self-hosted sish server místo ngrok.

Nasazení

Pro toto demo použiju nejlevnější VM v Azuru s manuálně vyžádaným wildcard certifikátem skrze Let’s Encrypt. Lze použít jakýkoliv cloud nebo vlastní železo.

Kdykoli uvidíte dt.smtl.cz, nahraďte jej vlastní doménou.

Krok 1: Příprava VM

Na internetu už je spousta návodů, jak vytvořit novou VM a nainstalovat na ni Docker, takže nevidím důvod, proč to rozepisovat do detailu. Postupujte podle svých osvědčených postupů nebo návodu na internetu a vytvote novou Ubuntu VM a nainstalujte Dockerem a Docker Compose.

Jakmile bude VM připravená, povolte následující porty ve firewallu VM:

  • 22 - Obvyklý SSH port pro správu VM
    • Měl by být omezen pouze na vaše IP adresy nebo VPN, aby se zabránilo neautorizovanému přístupu
  • 2222 - sish SSH port pro správu tunelů
    • Měl by být omezen pouze pro IP adresy vývojářů nebo VPN, aby se zabránilo zneužití
  • 80 - HTTP port pro normální HTTP komunikaci
    • Měl by být dostupný veřejně
  • 443 - HTTPS port pro zabezpečenou HTTPs komunikaci
    • Měl by být dostupný veřejně

Krok 2: Nastavení domény

Nastavení se bude lišit v závislosti na vašem poskytovateli DNS. Je potřeba vytvořit dva DNS záznamy:

  1. Záznam typu A
    • Název: dt.smtl.cz
    • Hodnota: <Public_IP_Of_VM>
  2. Záznam typu CNAME
    • Název: *.dt.smtl.cz
    • Hodnota: dt.smtl.cz
Konfigurace DNS záznamů Konfigurace DNS záznamů

Krok 3: Získání wildcard certifikátu

Používám WEDOS jako svého DNS poskytovatele, tak použiju DNS-01 challenge pro získání certifikátu. Zjistěte si, jaké možnosti pro DNS-01 challenge nabízí váš DNS poskytovatel.

K získání certifikátu použiju lego klienta v Docker kontejneru. Potřebné lego parametry pro všechny DNS poskytovatele najdete zde: https://go-acme.github.io/lego/dns/index.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
docker run --rm -ti -v "${PWD}:/data" -w /data \
  -e WEDOS_USERNAME="<EMAIL>" \
  -e WEDOS_WAPI_PASSWORD="<WAPI_PASSWORD>" \
  goacme/lego \
    --accept-tos \
    --email='Your mail' \
    --dns="wedos" \
    --domains='*.dt.smtl.cz' \
    --domains='dt.smtl.cz' \
    run

Výsledek by měl vypadat takto: lego výstup

Krok 4: Vytvoření potřebných konfigurací

Jakmile je certifikát připraven, můžeme přejít k dalšímu kroku a vytvořit potřebné konfigurační soubory a složky.

Představenou strukturu nemusíte dodržovat. Pokud vám nevyhovuje, tak si ji můžete upravit podle svých představ, ale ujistěte se, že aktualizujete cesty v konfiguračních souborech odpovídajícím způsobem.

Připojte se k VM a vytvořte následující strukturu adresářů:

mkdir -p /srv/sish/{certs,keys,pubkeys,ssh-keys}

1
2
3
4
5
6
7
8
/srv/
└── sish/
    ├── certs/
    ├── keys/
    ├── pubkeys/
    ├── ssh-keys/
    ├── compose.yml
    └── config.yml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
services:
  sish:
    image: antoniomika/sish:latest
    container_name: sish
    restart: unless-stopped
    volumes:
      - ./pubkeys:/pubkeys
      - ./keys:/keys
      - ./certs:/ssl
      - ./config.yml:/app/config.yml
    ports:
      - "2222:2222"
      - "80:80"
      - "443:443"
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
ssh-address: ":2222"
http-address: ":80"
https-address: ":443"
https: true
https-certificate-directory: /ssl
authentication-keys-directory: /pubkeys
private-keys-directory: /keys
bind-random-ports: false
bind-random-subdomains: false  # Configuring this to false will allow users to specify custom domain
domain: dt.smtl.cz
authentication: true

append-user-to-subdomain: false # Configuring this to false will allow users to specify custom domain
append-user-to-subdomain-separator: "-"

Zkopírujte získaný certifikát a privátní klíč do certs adresáře.

1
cp '.lego/certificates/'*'dt.smtl.cz.'{crt,key} './certs/'

Do složky pubkeys přidejte veřejné klíče všech uživatelů, kteří budou mít přístup k vytváření tunelů. Každý klíč by měl být v samostatném souboru s .pub příponou.

Krok 5: Spuštění serveru

Ve složce /srv/sish spusťte docker compose up -d. Ověřte, že server běží bez chyb: docker logs -f sish

docker compose logs -f docker compose logs -f

K ověření že server běží a naslouchá na správných portech, můžete použít netcat:

1
2
3
4
$ nc -zv localhost 80; nc -zv localhost 443; nc -zv localhost 2222
Connection to localhost (127.0.0.1) 80 port [tcp/http] succeeded!
Connection to localhost (127.0.0.1) 443 port [tcp/https] succeeded!
Connection to localhost (127.0.0.1) 2222 port [tcp/*] succeeded!

Ten samý test můžete provést z vašeho lokálního počítače, abyste ověřili, že porty jsou otevřené a přístupné z vaší lokace:

1
2
3
4
$ nc -zv dt.smtl.cz 80; nc -zv dt.smtl.cz 443; nc -zv dt.smtl.cz 2222
Connection to dt.smtl.cz port 80 [tcp/http] succeeded!
Connection to dt.smtl.cz port 443 [tcp/https] succeeded!
Connection to dt.smtl.cz port 2222 [tcp/rockwell-csp2] succeeded!

Krok 6: Ověření funkčnosti

Abychom otestovali, že tunelování funguje správně, vytvoříme jednoduchý tunel pro službu whoami, která běží lokálně na vašem počítači. Tato služba vrátí detaily požadavku, takže můžeme ověřit, že provoz je správně směrován skrze tunel.

  1. Spusťte kontejner whoami:
    • docker run -d -p 9090:80 --name iamfoo traefik/whoami
  2. Otevřete prohlížeč a navštivte http://localhost:9090, whoami stránka by měla být zobrazena.
    • whoami lokálně
  3. Vytvořte tunel pomocí:
    • ssh -R whoami-test-jc:80:localhost:9090 -p 2222 dt.smtl.cz
    • Výstup by měl vypadat takto:
    • výstup z testovacího tunelu
  4. Otevřete zobrazenou URL v prohlížeči: https://whoami-test-jc.dt.smtl.cz
    • Ta samá stránka, jako v kroku 2, by se měla zobrazit. Tentokrát ale pomocí sish tunelu a s validním certifikátem.
    • whoami skrze sish

Pokud něco nefunguje, tak zkontrolujte sish logy pomocí docker logs -f sish, jestli se neobjevují nějaké chyby nebo varování.

Závěr

Úspěšně jste nakonfigurovali self-hosted alternativu k ngrok pomocí sish. Nyní můžete vytvářet tunely k vašim lokálním službám a přistupovat k nim bezpečně přes internet. Nezapomeňte monitorovat server a udržovat jej aktuální, aby byla zajištěna bezpečnost a stabilita.

Podívejte se na kompletní příklad konfiguračního souboru a přizpůsobte jej svým potřebám.

✨ Bonus: Autentizace pomocí OS uživatelů (smallstep SSH certifikáty)

Jelikož sish je dostupný skrze SSH protokol, lze k jeho zabezpečení použít různé metody (Bastion/Jump servery, VPN, Firewall, Krátkodobé ssh klíče, …)

Pokud už máte existující autentizační systém pro připojování k Ubuntu VM, můžete jej využít pro autentizaci k sish serveru. Tím pádem nemusíte spravovat samostatné klíče pro sish v lokálním adresáři (/pubkeys).

Abyste mohli využít autentizaci na úrovni OS, udělejte následující:

  1. Použijte možnost authentication: false v sish konfiguračním souboru
  2. Upravte compose.yml a vystavte sish službu pouze na localhostu:
  • 1
    2
    
      ports:
          - "127.0.0.1:2222:2222"

Tímto způsobem nebude interní SSH port sish server dostupný zvenčí. Aby bylo možné vytvořit tunely, je potřeba se nejdříve připojit k Docker host systému a poté se připojit k sish serveru skrze localhost.

Již zmíněný příkaz pro vytvoření tunelu by se změnil v toto:

1
2
3
ssh -J ansible@dt.smtl.cz -R whoami-test-jc:80:localhost:9090 -p 2222 localhost
# Předtím:
# ssh -R whoami-test-jc:80:localhost:9090 -p 2222 dt.smtl.cz

Tento příkaz se nejdříve připojí k Docker host systému (dt.smtl.cz) pomocí OS uživatele ansible, a poté se připojí k sish službě běžící na localhostu přes port 2222. Tímto způsobem můžete využít svůj stávající autentizační systém založený na OS uživatelích, aniž byste museli spravovat samostatné klíče pro sish.

SSH připojení skrze proxy SSH připojení skrze proxy

Ke zjednodušení můžete použít následující SSH konfiguraci:

1
2
3
4
5
6
7
8
Host dt-jump
    HostName dt.smtl.cz
    User ansible
Host dt
    ProxyJump dt-jump
    HostName localhost
    User CouldBeAnything # Will help to identify the connection in sish logs
    Port 2222

Díky této konfiguraci se příkaz zjednoduší na:

ssh -R whoami-test-jc:80:localhost:9090 dt

Připojení skrze SSH aliasu Připojení skrze SSH aliasu

✨ Bonus 2: Automatické obnovování certifikátu

Manuálně generovat certifikát a kopírovat ho do sish serveru je v pořádku pro testování, ale není to udržitelné řešení pro produkční prostředí. Certifikáty Let’s Encrypt jsou platné pouze 90 dní (brzy 45 dní), takže je potřeba mít nastavený proces pro jejich pravidelné obnovování.

Hlavním cílem je mít cron job, který bude pravidelně spouštět příkaz pro obnovu certifikátu a aktualizovat certifikáty v certs adresáři. Jelikož je potřeba restartovat sish kontejner po obnově certifikátu, je jednodušší mít lego přístupný na Docker hostu a vytvořit skript, který bude řešit obnovu a restartování kontejneru.

Nejprve nainstalujte lego na Docker hostu.

Pro demo preferuji získání binárky z Docker image, ale měli byste dodržovat otestované postupy pro instalaci (a udržování aktuálnosti) softwaru na vašich serverech.

docker run --rm -ti -v "${PWD}:/data" -w /data --entrypoint sh goacme/lego -c 'cp /lego /data/lego'

Tento příkaz zkopíruje lego binárku do aktuálního adresáře, takže ji můžete přesunout na více standardní místo.

mv ./lego /usr/local/bin/lego

Skript, který obnový certifikát, by mohl vypadat nějak takto:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/sh

cd "/srv/sish" || exit 10

# Follow your best-practices for loading a secret values (ideally from some keyvault)
export WEDOS_USERNAME="..."
export WEDOS_WAPI_PASSWORD="..."
export LEGO_EMAIL="..."
DOMAIN="..."
PROVIDER="wedos"
RESOLVER="ns.${PROVIDER}.com:53"

# Renew the cert and restart the container using docker client when renewed
lego \
    --accept-tos \
    --dns="${PROVIDER}" \
    --domains="*.${DOMAIN}" \
    --domains="${DOMAIN}" \
    --dns.resolvers="${RESOLVER}" \
    renew \
    --renew-hook='./hooks/copy-restart.sh'

Kopírovaní certifikátu a restartování kontejneru je definovaný v následujícím skriptu:

1
2
3
4
5
6
7
8
9
#!/usr/bin/env bash

cd '/srv/sish' || exit 10

# You can adjust it by adopting the environment variables as specified here: https://go-acme.github.io/lego/usage/cli/obtain-a-certificate/index.html#running-a-script-afterward 
cp '.lego/certificates/'*'.cz.'{crt,key} './certs/'
docker compose restart sish

echo 'Certificate renewal successful, sish container restarted'

Nastavte oba skripty jako spustitelné: chmod +x /srv/sish/hooks/*.sh

Nakonec vytvořte cron job, který bude pravidelně spouštět skript pro obnovu certifikátu.

Vždy vyberte náhodnou hodinu a minutu pro spuštění obnovy, aby nedošlo k přetížení ACME serverů. Více informací naleznete na https://go-acme.github.io/lego/usage/cli/renew-a-certificate/index.html#automatic-renewal.

29 2 * * * /srv/sish/hooks/renew-cert.sh >> /srv/sish/renew.log 2>&1

Tento příspěvek je licencován pod CC BY 4.0 autorem.