GitHub Actions nepasiekia jūsų VPN: savas runner arba tunelis
Hostinamas runner yra nauja VM už jūsų tinklo ribų. Įdėkite runner į tą tinklą arba atidarykite tunelį iš job ir laukite tikro maršruto.
Jūsų diegimo žingsnis užstringa ties vidiniu hostname, nors ta pati komanda iš nešiojamojo kompiuterio atsako akimirksniu, o kliento build abiejose mašinose identiškas. Job vykdomas GitHub hostinamame runner: virtualioje mašinoje, sukurtoje būtent tam vienam job GitHub debesyje, be maršruto į jūsų privatų adresų erdvę, be resolver įrašo vidinei zonai ir be sertifikato, kurį priimtų privatus endpoint. Šiam apribojimui tinka tik du sprendimai, ir nė vieno negalima įjungti vėliau neperstatant workflow. Pasirinkite prieš pirmą YAML eilutę, nes perkėlimas vėliau kainuoja daugiau nei pirmas bandymas.
Pasirinkimas yra tarp runner, pastatyto ten, kur tinklas jau baigiasi, ir tunelio, kuris gyvena lygiai vieną job. Abu jau metus neša produkcinius diegimus, ir jie žlunga skirtingai.
Kur iš tikrųjų yra riba
Viskas, ką įdiegiate nešiojamajame, sustoja nešiojamajame, o viskas, ką įdiegiate job metu, dingsta kartu su runner. Nėra nuolatinės tinklo vardų erdvės, prie kurios klientas liktų prisirišęs tarp paleidimų, todėl kiekvienas paleidimas prasideda toje pačioje nepasiekiamoje būsenoje. Azure dokumentuoja šią ribą Microsoft hostinamiems agentams: VPN ir ExpressRoute jungtys į privačius tinklus ten nepalaikomos, o GitHub hostinami runner neša tą pačią apribojimo formą, o ne atskirą mechanizmą. Skaitykite tai kaip produkto savybę, o ne kaip defektą, kurį reikia apeiti.
Jokia saugyklos nuostata to nesušvelnina. Job etiketė nusprendžia, kuris pool paims darbą, o hostinama etiketė reiškia mašiną už jūsų perimetro viso paleidimo metu.
Todėl privatus pasiekiamumas turi ateiti iš to, kas priklauso jums. Runner, pastatytas ten, kur tinklas jau baigiasi, arba tunelis, gyvenantis vieną job. Likusi straipsnio dalis apie sąmoningą mokėjimą už vieną iš šių, o ne sąskaitos atradimą produkcijoje.
Spręskite pagal paleidimų dažnį, o ne pagal įpratimą
Naudokite savo runner VPC ar biuro tinkle, kai diegimai ateina kelis kartus per dieną, kai job paliečia daugiau nei vieną privatų portą arba kai platformos komanda jau tvarko pataisymus ir kredencialų rotaciją. Toje mašinoje job sprendžia vardus ir maršrutizuoja kaip bet kuris kitas host už ugniasienės. Tinklo darbas atliekamas vieną kartą, diegiant, o ne kiekvieno paleidimo metu.
Statykite tunelį job viduje, kai prieiga reta ir nenorite ilgaamžio agento, laikančio kredencialą į jūsų tinklą. Job kelioms minutėms tampa savo tinklo klientu ir nieko nepalieka, o tai dažnai ir yra visas tikslas.
Įvertinkite tunelio kainą prieš jį rinkdamiesi. Kiekvienas paleidimas moka už profilio sukūrimą, kliento paleidimą, pasirengimą ir išardymą. Tunelis, kuris pakyla, bet niekada nepatikrinamas, sukuria diegimą, kuris žlunga pusiaukelėje ir palieka privačią būseną tokia forma, kokios niekas neplanavo. Pakartotinis bandymas po to retai būna švarus, o antroji nesėkmė dažniausiai priskiriama kliento versijai.
Rašyti profilį taip, kad žingsnis negalėtų nutrikti
Laikykite profilius ir kredencialus už saugyklos ribų ir sukurkite juos iš secret tame žingsnyje, kuriam jų reikia. Tvarka nusprendžia, ar job išgyvens: failas, sukurtas po sudo, priklauso root, o žingsnio neturintis privilegijų shell nebegali į jį rašyti.
- 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"Abi to bloko detalės skirtos vėlesniems žingsniams. Su umask 077 profilis nusileidžia 0600 režimu, priklausydamas runner naudotojui, sudo vis dar gali jį skaityti, o klientas rašo savo žurnalą į failą, kurį runner jau turi, todėl pasirengimo ciklas gali skaityti tą žurnalą be antro sudo. --writepid įrašo PID, į kurį nukreips išardymas.
Profilis, kuris autentifikuojasi kliento sertifikatu, turi turėti raktą įrašytą taip pat, o static-challenge profiliui reikia atitinkamo auth režimo. Profiliams, kurie stumia DNS parinktis, Ubuntu ir Debian reikia openvpn-systemd-resolved, nes vien stūmimas baziniame atvaizde nieko nekeičia.
Pasirengimas yra zondas, o ne pauzė
Fiksuotas sleep išlieka lygiai iki to momento, kai koncentratorius sulėtėja, o tada diegimo žingsnis paleidžiamas anksčiau, nei atsiranda maršrutas. Klaida tada iškyla diegimo įrankio viduje kaip timeout privačiam host, o tai nieko nesako apie tunelį.
- 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 1Ciklas stebi du dalykus, nes bet kuris iš jų gali būti sugedusi pusė. getent hosts įrodo, kad stumtas resolver atsako už tą vardą, o socket atsidarymas įrodo, kad kažkas klausosi prievado, kurį naudos diegimas; profiliui su vien maršrutu reikia pažodinio adreso ir ištrintos getent eilutės.
Kai ciklas baigiasi, žurnalas yra vienintelis dalykas, skiriantis tris skirtingus gedimus: atmestą sertifikatą, neteisingą auth režimą ir maršrutą, kuris taip ir neatėjo. Filtravimas prieš tail išlaiko tą išvestį pakankamai trumpą, kad būtų galima perskaityti, kol job dar žlunga. Skaitykite pirmą klaidą, o ne paskutinę eilutę, nes klientas, kuris negali autentifikuotis, kartoja save, kol ciklas pasiduoda. ip -4 route show dev tun0 atsako į paprastesnį klausimą, ar maršrutas apskritai buvo stumtas, ir tame pačiame cikle kainuoja vieną eilutę.
Išardymas ir artefakto klausimas
Uždarykite job žingsniu, kurį saugo if: always() ir kuris vykdo sudo kill "$(sudo cat "$RUNNER_TEMP/openvpn.pid")" ir nieko daugiau. Bendrai naudojamame savo runner niekada nežudykite pagal proceso vardą: kitas job gali turėti klientą su tuo pačiu binaru ir panašiais argumentais. Tada klaida tenka tam job, kuris paleistas antras, ir jo žurnalas rodo į workflow, kuris nieko nepadarė blogai.
Apsauga svarbi todėl, kad nepavykęs diegimas vis tiek palieka gyvą tunelį. Be always() valymo žingsnis praleidžiamas būtent tada, kai jo reikia, ir kitas paleidimas prasideda prieš jau prisijungusį klientą.
Lieka vienas klausimas prieš priimant bet kurį sprendimą: ar build etapui apskritai reikia privataus maršruto? Deployment agentas, kuris jau sėdi tinkle, gali atsisiųsti patvirtintą artefaktą, ir tai laiko įprastus build toliau nuo privataus kelio. Mažiau job, laikančių tunelį, reiškia mažiau vietų, kur pasenęs maršrutas gali pasislėpti.