← back to /blog← powrót do /blog
Przypinanie egress IP runnera CI jednym curl-em
Sporo rzeczy opiera się na Twoim egress IP: allowlisty baz danych, firewalle zewnętrznych API, VPN partnera, polityka bucketa S3, relay SMTP przyjmujący tylko znanego nadawcę. Dopóki adres jest stały, nikt o nim nie myśli. W momencie gdy się zmieni (nowy NAT gateway, przetworzony self-hosted runner, dostawca chmury rotujący adres), zadania zaczynają padać głęboko w pipeline z timeoutem połączenia i bez oczywistej przyczyny.
Ciągle się na to natykam przy self-hosted runnerach CI, więc wpiąłem mały check na
początek pipeline. Korzysta z ip.syschecks.com,
serwisu what-is-my-IP, który prowadzę: curl i dostajesz dokładnie ten adres,
który widzi świat zewnętrzny.
Przerwij od razu przy złym egress
Najtańsza wersja to jeden krok na górze zadania. Ustal publiczne IP, porównaj je z adresami z allowlisty i zatrzymaj się, zanim cokolwiek innego ruszy:
- name: Verify runner egress IP
run: |
IP=$(curl -fsS https://ip.syschecks.com)
echo "runner egress: $IP"
case "$IP" in
203.0.113.*|198.51.100.10) echo "allowlisted, continuing" ;;
*) echo "::error::egress IP $IP is not in the allowlist"; exit 1 ;;
esac
Teraz zrotowany adres pada w dziesięć sekund z jasnym komunikatem, zamiast trzydzieści minut później jako „could not connect to database”, które wysyła trzy osoby do grzebania w security groupach.
Więcej niż sam adres
Endpoint tekstowy zwraca samo IP. Dodaj /json, gdy chcesz asertować na czymś
więcej niż adres: kraj, ASN albo to, czy ruch nagle wychodzi przez datacenter lub VPN,
którego nie powinno być na trasie:
curl -fsS https://ip.syschecks.com/json | jq -r '.ip, .country, .asn, .privacy.type'
Przydatny guard dla obciążonych regulacjami workloadów: odmów deployu, jeśli egress
opuszcza oczekiwany kraj albo gdy privacy.type wraca jako vpn /
tor / hosting, a spodziewałeś się konkretnego zakresu firmowego.
- name: Egress sanity
run: |
JSON=$(curl -fsS https://ip.syschecks.com/json)
CC=$(echo "$JSON" | jq -r .country)
TYPE=$(echo "$JSON" | jq -r .privacy.type)
[ "$CC" = "PL" ] || { echo "::error::egress country $CC, expected PL"; exit 1; }
[ "$TYPE" != "tor" ] || { echo "::error::egress via Tor - refusing to deploy"; exit 1; }
Samorejestrujący się dynamiczny runner
Jeśli IP runnera zmienia się celowo (autoskalująca się flota, instancja spot), to samo wywołanie zasila aktualizację allowlisty zamiast twardego błędu. Weź IP, autoryzuj je na security group i cofnij przy teardownie, żeby nie zostawiać otwartej dziury:
IP=$(curl -fsS https://ip.syschecks.com)
aws ec2 authorize-security-group-ingress \
--group-id sg-0123456789abcdef0 \
--protocol tcp --port 5432 --cidr "$IP/32"
# ... uruchom zadanie ...
aws ec2 revoke-security-group-ingress \
--group-id sg-0123456789abcdef0 \
--protocol tcp --port 5432 --cidr "$IP/32"
Dlaczego zewnętrzne echo, a nie metadata instancji
Metadata chmury daje adres instancji, który często nie jest adresem widzianym przez internet. Za NAT gateway, proxy egressowym albo firmowym breakoutem adres z metadata i realny adres egress różnią się, a allowlistę interesuje tylko ten drugi. Zewnętrzne echo odzwierciedla to, co realnie odbiera zdalny serwis, o co w tym check chodzi.
Serwis to zwykły HTTP z CORS, więc działa tak samo z shella, kontenera i przeglądarki, i nie loguje adresów, które odbija.
Gdzie to żyje
- Narzędzie: ip.syschecks.com: czysty tekst,
/json,/all,/rbloraz dokumentacja API pod/docs - Część pakietu monitoringu syschecks.com: uptime, TLS, DNS i sprawdzanie blacklist z alertowaniem
- Paweł