A GitHub Actions nem éri el a VPN-t: saját runner vagy alagút
A hosztolt runner egy friss VM a hálózatodon kívül. Vagy tegyél runnert abba a hálózatba, vagy nyiss alagutat a jobból, és várj egy valódi útvonalra.
A deploy lépésed egy belső hosztnéven akad el, miközben ugyanaz a parancs a laptopodról azonnal válaszol, és a kliens buildje mindkét gépen azonos. A job a GitHub által hosztolt runneren fut: egy virtuális gép, amelyet épp ehhez az egy jobhoz hoztak létre a GitHub felhőjében, útvonal nélkül a privát címtartományodba, resolver-bejegyzés nélkül a belső zónához, és olyan tanúsítvány nélkül, amelyet a privát endpoint elfogadna. Erre a korlátra csak két terv illik, és egyiket sem lehet később bekapcsolni a workflow újraépítése nélkül. Még az első YAML-sor előtt válassz közöttük, mert az utólagos átállás többe kerül, mint az első próbálkozás.
A választás aközött van, hogy a runnert oda teszed, ahol a hálózat már véget ér, vagy egy alagutat nyitsz, amely pont egy job idejéig él. Mindkettő évek óta visz éles deployokat, és különböző módokon hibázik.
Hol húzódik valójában a határ
Minden, amit a laptopra telepítesz, a laptopnál megáll, és minden, amit egy job alatt telepítesz, eltűnik a runnerrel. Nincs állandó hálózati névtér, amelyhez a kliens a futások között kapcsolódva maradhatna, ezért minden futás ugyanabból az elérhetetlen állapotból indul. Az Azure dokumentálja ezt a határt a Microsoft által hosztolt agenteknél: a privát hálózatokba menő VPN- és ExpressRoute-kapcsolatok nem támogatottak, és a GitHub által hosztolt runnerek ugyanezt a korlátozásformát hordozzák, nem külön mechanizmust. Olvasd ezt a termék tulajdonságaként, nem pedig kikerülendő hibaként.
Egyetlen repository-beállítás sem lazít rajta. A job címkéje dönti el, melyik pool veszi fel a munkát, és a hosztolt címke azt jelenti, hogy a teljes futás alatt a peremhálózatodon kívüli gép dolgozik.
A privát elérhetőségnek tehát olyasvalamiből kell jönnie, ami a tiéd. Egy runner, amely ott van, ahol a hálózat már véget ér, vagy egy alagút, amely egy jobot él. A cikk hátralévő része arról szól, hogy tudatosan fizess valamelyikért, ahelyett hogy éles környezetben fedeznéd fel a számlát.
A futások gyakorisága alapján dönts, ne a megszokás alapján
Használj saját runnert a VPC-n vagy az irodai hálózaton belül, ha a deployok naponta többször érkeznek, ha a job egynél több privát portot érint, vagy ha a platformcsapat már a foltozást és a hitelesítő adatok rotációját viszi. Azon a gépen a job úgy old fel és irányít, mint bármely más hoszt a tűzfal mögött. A hálózati munka egyszer, a telepítéskor történik meg, nem minden futásnál.
Építsd az alagutat a jobon belül, ha a hozzáférés alkalmi, és nem akarsz hosszú életű agentet, amely hitelesítő adatot tart a hálózatodba. A job néhány percre a saját hálózati kliense lesz, és nem hagy maga után semmit, ami gyakran az egész lényeg.
Becsüld meg az alagút költségét, mielőtt kiválasztod. Minden futás fizet a profil létrehozásáért, a kliens indításáért, a készenlétért és a bontásért. Az az alagút, amely feljön, de soha nem ellenőrzik, olyan deployt eredményez, amely félúton elbukik, és a privát állapotot olyan formában hagyja, amelyet senki nem tervezett. Az utána következő újrapróbálkozás ritkán tiszta, és a második hibát általában a kliens verziójára fogják.
Úgy írd meg a profilt, hogy a lépés ne szakadhasson meg
Tartsd a profilokat és hitelesítő adatokat a repositoryn kívül, és a szükséges lépésben hozd létre őket secretből. A sorrend dönti el, túléli-e a job: a sudo alatt létrehozott fájl a rooté, és a lépés jogosultság nélküli shellje már nem tud beleírni.
- name: Bring up OpenVPN
run: |
umask 077
profile="$RUNNER_TEMP/client.ovpn"
printf '%s\n' "$VPN_PROFILE" > "$profile"
: > "$RUNNER_TEMP/openvpn.log"
printf '%s\n' "$VPN_CREDENTIALS" > "$RUNNER_TEMP/credentials.txt"
sudo openvpn --daemon openvpn-job \
--config "$profile" \
--auth-user-pass "$RUNNER_TEMP/credentials.txt" \
--writepid "$RUNNER_TEMP/openvpn.pid" \
--log "$RUNNER_TEMP/openvpn.log"A blokk két részlete a későbbi lépéseket szolgálja. umask 077 esetén a profil 0600 módban, a runner felhasználója tulajdonában landol, a sudo még olvashatja, és a kliens a naplóját olyan fájlba írja, amely már a runneré, így a készenléti ciklus második sudo nélkül olvashatja azt a naplót. --writepid rögzíti azt a PID-et, amelyet a bontás megcéloz.
Az ügyféltanúsítvánnyal hitelesítő profilnak ugyanígy kiírt kulcsra van szüksége, a static-challenge profilnak pedig a megfelelő auth-módra. A DNS-opciókat pusholó profiloknak Ubuntu és Debian alatt openvpn-systemd-resolved kell, mert a push önmagában semmit nem változtat az alap image-en.
A készenlét egy próba, nem szünet
A fix sleep pontosan addig tart, amíg a koncentrátor le nem lassul, és akkor a deploy lépés már az útvonal létezése előtt lefut. A hiba aztán a deploy eszközön belül a privát hoszt felé irányuló timeoutként jelenik meg, ami semmit nem mond az alagútról.
- name: Await private endpoint
run: |
for attempt in $(seq 1 30); do
if getent hosts private.example.internal > /dev/null && \
timeout 3 bash -c 'exec 3<>/dev/tcp/private.example.internal/8443' 2>/dev/null; then
echo "endpoint answered after $attempt attempts"
exit 0
fi
sleep 2
done
grep -Ei 'error|verify|auth' "$RUNNER_TEMP/openvpn.log" | tail -40
exit 1A ciklus két dolgot figyel, mert bármelyik lehet a törött fél. getent hosts bizonyítja, hogy a pusholt resolver válaszol arra a névre, a socket megnyílása pedig bizonyítja, hogy valami figyel azon a porton, amelyet a deploy használni fog; a csak útvonallal rendelkező profilnak szó szerinti címre van szüksége, és a getent sort törölni kell.
Amikor a ciklus lejár, a napló az egyetlen, ami három különböző hibát elválaszt: egy elutasított tanúsítványt, egy rossz auth-módot és egy útvonalat, amely soha nem érkezett meg. A tail előtti szűrés elég rövidre tartja azt a kimenetet, hogy olvasható legyen, miközben a job még mindig elbukik. Az első hibát olvasd, ne az utolsó sort, mert az a kliens, amely nem tud hitelesíteni, ismétli magát, amíg a ciklus fel nem adja. ip -4 route show dev tun0 az egyszerűbb kérdésre válaszol, hogy egyáltalán pusholtak-e útvonalat, és ugyanabban a ciklusban egy sorba kerül.
Bontás és az artefakt kérdése
Zárd le a jobot egy if: always() által védett lépéssel, amely sudo kill "$(sudo cat "$RUNNER_TEMP/openvpn.pid")" futtat és semmi mást. Megosztott saját runneren soha ne ölj folyamatnév alapján: egy másik jobnak lehet ugyanazzal a binárisval és hasonló argumentumokkal rendelkező kliense. A hiba akkor arra a jobra száll, amelyik másodikként futott, és a naplója egy olyan workflow-ra mutat, amely semmit nem tett rosszul.
A védelem azért számít, mert egy elbukott deploy is élő alagutat hagy. always() nélkül a takarító lépés pontosan akkor marad ki, amikor szükség van rá, és a következő futás egy már csatlakozott kliens ellen indul.
Egy kérdés marad, mielőtt bármelyik tervet elfogadod: kell-e egyáltalán a build fázisnak privát útvonal? Az a deployment agent, amely már a hálózaton belül ül, le tud húzni egy jóváhagyott artefaktot, és ez távol tartja a szokásos buildeket a privát úttól. Kevesebb alagutat tartó job kevesebb helyet jelent, ahol egy elavult útvonal elbújhat.