SSH-policy és jóváhagyás
Amit az átjáró egy operátornak a célgépen enged, ha már tudja, ki ő: egy parancs-szabály alakja és amire illeszkedik, amit a policy nem tud elolvasni, mi van az átjáró által aláírt tanúsítványokban, és a kapuk, amiken egy connect átmegy. Hogy ki az operátor, és melyik csoport mit kapuz, az identitások, csoportok és zárolás lapon áll.
Parancs-policy
Egy szabály alakja
A policy.yaml szabályok listáját tartalmazza. Mindegyik ez:
| Kulcs | Jelentés |
|---|---|
name |
A szabály azonosítója. Megjelenik az elutasítási üzenetben és az auditbejegyzésben |
pattern |
Egy reguláris kifejezés. A teljes sorra és a benne lévő minden parancsra illesztődik — lásd Mire illeszkednek a szabályok |
category |
Egy karbantartott minta név alatt (script_execution, interactive_pty) nyers regex helyett. A pattern-nel kölcsönösen kizáró |
action |
deny vagy require_approval. Ha hiányzik, az deny. Bármi más betöltési hiba |
reason |
Az operátornak megmutatott és az auditnaplóba írt indoklás |
timeout_seconds |
Meddig vár egy jóváhagyási kérés. require_approval szabálynál alapból 300 |
min_level |
A szabály inaktív az ennél alacsonyabb lockdown szintű hosztokon |
roles |
A szabály csak ezekre a szerepkörökre vonatkozik. Az üres minden szerepkört jelent |
Két akció van, nem három. Nincs allow, és nem is kell: az a parancs, amelyik
egyetlen szabályra sem illeszkedik, lefut. Tehát minden parancs a három osztály
egyikébe esik — egy illeszkedő szabály megtagadta, egy illeszkedő szabály egy
második személyhez küldte, vagy azzal engedélyeződött, hogy semmire nem illeszkedett.
Egy szövegdarabon a kiértékelés első-találat három blokkon, rögzített
sorrendben: előbb a beépített
tiltó szabályok, aztán a policy.yaml szabályai, végül a beépített jóváhagyási
szabályok. Ettől a sorrendtől viselkedik jól az unió. A tiltások jönnek elsőként,
tehát semmilyen fájl nem gyengítheti őket; a jóváhagyási alapértelmezések jönnek
utoljára, tehát egy saját, szűkebb szabály beelőzhet egyet és felülírhatja.
A beépített tiltó szabályok minden policy részei, beleértve egy olyan átjáró
policyját is, amelynek egyáltalán nincs policy.yaml-ja:
| Szabály | Mit tilt |
|---|---|
block_interactive_root_shell |
sudo -i, sudo -s, és sudo <shell> -c nélkül, a célprogram alapnevével összevetve |
block_sudo_shell_wrapper |
sudo <shell> -c …, ugyanaz az alapnév-összevetés — egy shell-burok elrejti a valódi parancsot a policy-motor elől |
block_destructive_root_rm |
rm -rf / |
block_passwd_changes |
passwd, sudo-val vagy anélkül — a jelszóváltoztatás a címtáron keresztül megy |
block_sudo_not_allowlisted |
Csak akkor van jelen, ha a policy.yaml beállítja a sudo_allowed_commands-ot: minden olyan sudo-cél, amelynek az alapneve nincs a listán |
A két sudo-szabály úgy illeszt, hogy a sudo saját flagjeit értelmezi és a célprogram
alapnevét hasonlítja össze, nem úgy, hogy a sudo szó utáni szövegre illeszt — a
sudo /bin/bash és a sudo bash tehát ugyanaz a tény a számukra. Az engedélylista
opt-in, de ha egyszer bekapcsolták, ugyanabból a fájlból nem írható felül: a
beépített tiltó blokkon belül ül.
A két beépített jóváhagyási szabály a 4-es lockdown szinttől felfelé érvényes:
approve_script_execution_on_prod (egy szkript futtatása megkerüli a
parancsonkénti policyt) és approve_interactive_pty_on_prod (interaktív
terminálprogram).
Egy pillantásra: mi fut, mi vár, mi tiltott
Minden sor, amit egy operátor beír, a három kimenet egyikével végződik: lefut, egy második emberre vár, vagy tiltott. Ez a táblázat a teljes döntés egy helyen — az utána következő szakaszok a részletek.
| Mit tartalmaz a sor | Kimenet | Miért | Hol állítható |
|---|---|---|---|
| Parancs, amire egyetlen szabály sem illeszkedik | Lefut | Nincs allow akció: amit semmi nem kifogásol, az engedett |
Egy szabály a policy.yaml-ba (deny vagy require_approval, szűkíthető roles-szal és min_level-lel) |
| Parancs, amire egy saját szabályod illeszkedik | A szabály akciója | A te szabályod, a te döntésed | policy.yaml |
Beépített tiltás: root shell sudo-val, sudo shell-burkoló, rm -rf /, passwd |
Tiltott | Ez a padló, ami minden policyben megmarad — fájl nem gyengítheti | Nem állítható. A sudo_allowed_commands csak bővítheti |
| Beépített jóváhagyás: script futtatása, interaktív terminál-program, keményített hoszton | Jóváhagyásra vár | Egy script vagy teljes képernyős program elrejti a policy elől a benne futó parancsokat | Egy elé álló, szűkebb saját szabály (más időkorlát, más roles) |
Több parancs egy sorban (;, &&, &, vagy-lista, cső, ( ), { }, ciklus- vagy feltétel-törzs) |
Minden parancs értékelődik; a legszigorúbb kimenet nyer | Egy második parancs nem utazhat az első döntésén | Nem állítható — így működik az illesztés |
Előtag mögötti parancs (sudo, env, timeout, …), vagy idézőjellel, escape-pel írt (up\time) |
A ténylegesen futó parancsként értékelődik | Egy burkoló vagy egy escape nem rejtheti el a programot a szabályok elől | Nem állítható |
Csak futáskor eldőlő szerkezet: $(...), backtick, eval, bash -c, heredoc, függvény-definíció, változóból jövő parancsnév |
Alapból tiltott | Előre egyetlen parse sem tudja megmondani, mi fog futni | A policy.yaml dynamic_construct_action kulcsa: deny (alap), require_approval, vagy allow (laborbeállítás) |
| Sor, amit a bash nem tud parse-olni | Tiltott | Senki nem tudja megmondani, mit jelent | Nem állítható (block_unparseable_line) |
exit más parancsokkal egy sorban |
Tiltott | Egy soron belül nem zárná a munkamenetet, vagyis nem azt tenné, aminek látszik | Nem állítható (block_exit_in_compound_line) — az exit külön sorba kerüljön |
Ami egy engedélyezett vi, less vagy python belsejében íródik |
Nem értékelődik, rögzül | Az átjáró a programot megnyitó sort látja, nem azt, ami benne történik | A célgépen: a fiók jogai és a sudo beállítása |
Egy sor kimenete még a futtatása előtt megnézhető: isg policy check.

Képernyőkép: referencia-környezet (env1), 2026-10-17.
Mire illeszkednek a szabályok
Az operátor egy SORT gépel be, és egy sor egynél több parancsot is futtathat. Az átjáró úgy értelmezi, ahogy a bash értelmezi, és minden parancsot kiértékel, ami abból a sorból lefuthat — nem csak a sor elejét:
- a nyers sort pontosan úgy, ahogy begépelték;
- minden egyszerű parancsot a soron belül — a
;,&&,||,|és&két oldalán, a( )és a{ }belsejében, egy ciklus- vagy feltétel-törzsben, és egy parancs-behelyettesítés belsejében; - mindegyiket még egyszer, a
sudo-előtag és az ismert parancs-előtagok (env,nohup,timeout,stdbuf,setsid,nice,ionice,command,builtin,time) mögötti alakjában.
Az idézőjelezés és az escape-elés előbb feloldódik, tehát a re\boot a
reboot parancs — ez az, amit a bash is futtatna.
A legszigorúbb döntés nyer: a deny üt a require_approval-on, az pedig a
nincs-találaton. Egy saját szabály tehát nem tudja enyhíteni egy beépített tiltást
azzal, hogy ugyanabban a sorban valami másra illeszkedik — nincs is allow
akció, amivel ezt le lehetne írni. Ha a döntés egy részre esett, nem a teljes
sorra, az üzenet megmondja, melyikre:
DENIED by policy: block_passwd_changes (password changes must go through the IdP) — part 2 of 2: "passwd -S peter"
Sorok, amiket a policy nem tud elolvasni
Van olyan szerkezet, ami csak futás közben dől el, és semmilyen elemzés nem tudja
előre megmondani: a parancs-behelyettesítés ($(...) vagy backtick), a
folyamat-behelyettesítés (<(...)), a here-document, a shell-függvény-definíció,
az eval, a source, a ., az exec, az alias, egy shell vagy interpreter,
aminek a programját sztringként adták át (bash -c, python3 -c, perl -e), és
minden olyan parancsnév, ami csak behelyettesítés után áll elő ($X, vagy egy
glob magában a programnévben).
Azt, hogy egy ilyen sorral mi történjen, a policy.yaml
dynamic_construct_action kulcsa dönti el. Értéke deny (az alap, és a hiányzó
kulcs is ezt jelenti), require_approval vagy allow; bármi más betöltési hiba.
Az első kettőnél a döntés a block_dynamic_shell_construct, illetve az
approve_dynamic_shell_construct szabály néven jelenik meg. Az allow
kiértékelés nélkül futtatja a sort, és laborbeállítás — a policy betöltésekor
figyelmeztetést naplóz.
Két sor-szintű szabály egyáltalán nem állítható:
| Szabály | Mikor szól |
|---|---|
block_unparseable_line |
A sor nem olyan shell-sor, amit a bash értelmezni tud — lezáratlan idézőjel, befejezetlen here-document. Nincs olyan olvasata a „futtasd le mégis"-nek egy olyan sornál, aminek a jelentését senki nem tudja megmondani |
block_exit_in_compound_line |
Az exit, a quit vagy a logout olyan sorban szerepel, amiben más parancsok is vannak. Ezek a szavak csak önálló sorként zárják a munkamenetet; máshol a célgépnek küldött parancsok, amik a munkamenettel nem csinálnak semmit, és ezt a különbséget jobb elutasítani, mint csendben a másikat csinálni |
Hogy mindezt egy saját soron is lásd, futtatás nélkül: az isg policy check
kiírja a felbontást, minden jelölt-szöveget, és a döntő szabályt:
isg policy check -- 'id; passwd -S peter'
A kilépési kódja maga a döntés: 0 engedélyezve, 1 tiltva, 2 jóváhagyás
szükséges.
Hol áll meg a policy
Az átjáró policyja nem kemény határ, és nem is annak áruljuk. Azt szűkíti, ami a célgépig eljut, és rögzíti, mit próbáltak. Három dolog eleve kívül esik rajta:
- Egy engedélyezett interpreter, szerkesztő vagy lapozó magából is tud
parancsot futtatni. A
vi:!-ja, aless!-ja, egypythonprompt. Az átjáró azt a sort látja, amelyik a programot megnyitotta; ami azon belül gépelődik, az bájtfolyam a célgép felé — a munkamenet átiratában rögzül, de egyetlen szabályra sem illesztődik. - A futásidejű tények láthatatlanok. Egy alias vagy shell-függvény, ami a
CÉLGÉPEN van definiálva, az
xargs, afind -exec, awatch, és az a fájl, amibe egy átirányítás ír — mindegyik olyan helyen nevez meg programot, amit az átjáró sosem olvas. - Az elemzés nem futtatás. Pontosan ezért utasítjuk el alapból a fenti szerkezeteket, ahelyett hogy találgatnánk róluk.
A kemény határ a célgépen van: egy minimális jogú fiók, és egy olyan sudo
beállítás, ami megnevezi, mire emelkedhet az a fiók. Az átjáró az előtte álló
kapu és az arról szóló nyilvántartás, hogy ki ment át rajta — a kettő együtt, nem
külön-külön. A fiók alakját lásd a Célgép lapon.
Mit tartalmaz egy tanúsítvány
Az átjáró négyféle tanúsítványt ír alá, és a különbségek abban vannak, hogy melyik mit nevez meg, és meddig él.
| Emberi munkamenet | Szolgáltatásfiók | Megújítható identitás | Hoszt | |
|---|---|---|---|---|
| Principalok | Az egyetlen feloldott --as OS-felhasználó |
A ca.yaml service_accounts[].principal egyetlen principalja |
— (X.509 kliens-tanúsítvány) | Minden hosztnév, amin az átjáró tárcsázza azt a gépet |
| Key ID | isg-human:<principal> |
A fiók neve | — | isg-host:<first principal> |
| Kiterjesztések | permit-pty, és semmi más |
permit-pty |
— | nincs |
| Kritikus opciók | nincs | nincs | — | nincs |
| Élettartam | 10 perc, a kiadás köré centrálva | ttl_seconds, alapból 1 óra |
renewable_ttl_days, alapból 30 nap |
host_cert_ttl, alapból 1 év |
| A kulcs eredete | A daemon memóriájában generálva, sosem íródik lemezre | A kérelmező kulcsa | A szolgáltatás gépén generálva | A célgép meglévő hosztkulcsa, amit a CA csak aláír |
A principal az --as kérdésre adott válasz, a feloldás után — az az
OS-felhasználó, akivé a munkamenet válik a célgépen. Semmi más nincs benne: egy
principal, egy tanúsítvány, egy munkamenet.
A permit-pty és semmi más döntés, nem kihagyás. Nincs
permit-port-forwarding, nincs permit-agent-forwarding, nincs
permit-X11-forwarding, nincs permit-user-rc. Egy továbbított port pontosan az a
csatorna, amit a policy-motor nem lát, ezért maga a hitelesítő adat képtelen
megnyitni egyet.
A csoporttagság sosem íródik bele egy tanúsítványba. A csoportok azt kapuzzák, hogy kiadódik-e egy tanúsítvány; nem a tanúsítvány hordozza őket. A szerepkör sem, és annak a kiléte sem, aki a munkamenetet más nevében kérte — ezek az auditnaplóban élnek, ahová egy rájuk vonatkozó kérdés tartozik.
A tíz perces emberi ablak az átjáró és a célgép közti órajel-eltérésre van méretezve a kézfogás alatt, és semmi másra. Az SSH csak a kapcsolat felépítése közben nézi meg a tanúsítványt, tehát az a munkamenet, amelyik az ablakán belül kapcsolódott, utána is tovább működik; a valakit elvágó kar a következő kiadás megtagadása, és pont ezt teszik az alábbi hozzáférési kapuk.
Milyen kapukon megy át egy connect
Mielőtt bármilyen hitelesítő adat létezne, egy kérés rögzített sorrendben dől el, és minden nem-interaktív dolog történik elsőként, hogy egy olyan kérés, amit soha nem engedtek volna át, ne kerüljön az operátornak egy többfaktoros kihívásába.
A ca.yaml human_access szekciója engedélylista, első-találat-nyer
szemantikával. Minden szabály megnevezi a principals-t (azokat az --as
értékeket, amikre kiterjed; itt a * megengedett), és aztán vagy a hosts-ot
(szó szerinti nevek — itt szándékosan nincs *), vagy a host_labels-t (CMDB
kulcs/érték párok halmaza, amelyeknek mind illeszkedniük kell). Egy szabály
ezenfelül követelhet csoportot (require_group), és megnevezheti, hogy ki kérhet
munkamenetet az adott principal nevében (requested_by; ha a kulcs hiányzik, csak
maga a principal). Tiltó szabály nincs: az a kérés, amelyik semmire nem illeszkedik,
no_matching_rule okkal utasítódik el.
Ennek a listának két tulajdonságát érdemes kimondani. Egy host_labels szabály nem
tud elavult vagy elérhetetlen CMDB-címkékre illeszkedni — labels_stale-t vagy
labels_unavailable-t jelent ahelyett, hogy találgatna. És hogy a szekció
kikényszerített-e, az a saját kapcsolója, a human_access.enforce: ha ki van
kapcsolva, minden döntés attól még kiértékelődik és auditálódik, csak egyik sem
blokkol. A requested_by_enforce bekapcsolása enforce nélkül ellentmondásként
utasítódik el.
Egy harmadik tulajdonság arról szól, honnan jönnek a címkék, és ez az, amiért a
daemon szigorú ebben. Egy host_labels szabály csak annyira valódi, amennyire a
mögötte álló CMDB: egy adat nélküli resolver minden hosztra üres címke-halmazt
ad, tehát minden ilyen szabály egyszerre abbahagyja az illeszkedést — csendben,
úgy, hogy a kimenetben semmi nem mondja ki, hogy a címkék eltűntek. Ezért egy
olyan cmdb.yaml, ami megnevez egy nem létező mock_file-t, megtagadja az
indulást ahelyett, hogy címkék nélkül jönne fel, és az isg system check
cmdb.yaml sora ugyanazzal a szöveggel pirosat ad. A kulcs teljes kihagyása
továbbra is megengedett — az a kimondott módja annak, hogy címkék nélkül
fussunk —, és a sor ilyenkor sárga, megnevezve a következményt.
Honnan jönnek ezek a szabályok egy telepített gateway-en. Ha a vezérlő nem
szállít ca.yaml-t, az Ansible-role generál egy minimálisat, és abba két
allow-szabályt ír, nem egy széles listát. Az első az operátoroké
(isg_gateway_operators): ugyanazok az emberek, akik a gateway gépén helyi fiókot
és isg-operators-tagságot is kapnak. A második csak akkor jelenik meg, ha az
isg_gateway_human_access_users megnevez valakit, és azokra a nevesített emberekre
szól, ugyanazokra a hosztokra. Ez a második csoport a gateway-en helyi fiókot nem
kap, és ott csoport-tagságot sem — az útjuk a célgépre a tanúsítvány, a gateway
pedig csak a kiadója. A nevek fióknevek, soha nem *: a role elutasítja az olyan
lista renderelését, amelyben van, mert a principals-ban a * nem egy név, hanem
mindenki. Az isg_gateway_requested_by_enforce az, ami ugyanabban a generált
fájlban bekapcsolja a requested_by_enforce-ot, és mivel egyik szabály sem hordoz
requested_by-t, minden principal csak önmaga nevében kérhet munkamenetet.
A hostgroup.yaml a második, független kapu: hosztonként megkövetelt csoportok,
a hoszt címkéi alapján kiválasztva. Saját enforce kapcsolója van, és kikapcsolva
ugyanígy viselkedik — mérve és auditálva, de nem kikényszerítve.
Aztán a többfaktoros kihívás, amelynek a módját az mfa.yaml mfa_mode-ja adja:
email (SMTP-vel kézbesített véletlen kód, vagy a totp_outbox.txt-be írva, ha
nincs továbbító konfigurálva), totp (RFC 6238, operátoronként egyszer regisztrálva
az isg mfa enroll-lal), vagy totp-with-email-fallback. Az ismételt hibák a
felhasználót és a forráscímet is kizárják arra az időre, amit az mfa.yaml beállít.
A totp-with-email-fallback módban — és csak ott — az az operátor, aki e-mailes
kóddal lépett be, maga regisztrálhat hitelesítő appot, a webUI /profile lapjáról.
A megerősítés két kódot kér, nem egyet: az appét, és egy frissen kiküldöttet arra a
faktorra, amivel a fiók már rendelkezik. Azért, mert ebben a módban a TOTP-rekord
kiváltja az e-mailes faktort, tehát egy eltérített böngésző-session nem
regisztrálhatja be a saját appját. Újraregisztráció ezen az úton nincs — egy
elvesztett hitelesítő app az adminisztrátor MFA_RESET-je, nem önkiszolgáló
lépés.
Ki rendelkezik a második faktor felett email módban. Az átjáró a kódot
arra a mail attribútumra küldi, amit belépéskor a címtár-bejegyzésből olvas
ki, az első faktor pedig a címtárbeli jelszó. Aki a címtárban egy felhasználó
jelszavát visszaállíthatja és az e-mail mezőjét átírhatja, az
mfa_mode: email mellett mindkét faktort uralja — a második faktor ilyenkor
nem független a címtártól. totp mellett a felhasználó egyszer regisztrálja a
titkot, és az átjáró őrzi (mfa.yaml store: fájl az átjáró saját
állapotában, $ISG_HOME/totp, vagy OpenBao store: vault mellett), a címtár
hatókörén kívül. A totp-with-email-fallback a kettő között áll: aki már
regisztrált hitelesítő appot, az a totp helyzetében van, aki még nem, az az
email helyzetében — és egy e-mailben kapott kóddal maga regisztrálhat appot.
Ajánlás: ahol a címtárat nem ugyanazok adminisztrálják, mint az átjárót, a
totp a választás; az email ott illik, ahol a címtárat mindkét faktor
bizalmi horgonyaként elfogadjátok, a totp-with-email-fallback pedig az
átállás útja a kettő között.
Az átjárós bejelentkezésnek (ssh isg-login@gateway) van még egy útja: ha az
sso.yaml-ban cli: optional áll, a promptnál adott üres jelszó elindítja az
RFC 8628 device flow-t az identity providerrel, cli: required mellett pedig
egyáltalán nincs jelszó-prompt. A promptnál begépelt felhasználónévnek egyeznie
kell azzal a névvel, amit a provider visszaad, és utána ugyanaz a címtár-kapu és
ugyanaz a többfaktoros ág következik, mint fentebb. Az isg connect nincs ezen
az úton — a hívója a socketből áll össze, nem egy bejelentkezésből.
És csak ezután íródik alá egy tanúsítvány — és minden utána begépelt parancs a saját policy-döntése és a saját auditbejegyzése.
Amit az átjáró nem nyújt SSH-n
Ezek mért hiányok: olyan dolgok, amiket a kód megtagad, itt felsorolva, hogy egy telepítés ne épüljön rá egyikre sem.
- Nincs jelszavas hitelesítés a célgép felé. A
connect.yamlallow_password_auth: truebeállítása kemény konfigurációs hiba, és egy elutasított tanúsítvány sosem esik vissza jelszókérésre — az a célgép, amelyik még nem bízott meg a CA-ban, lelet, nem olyasmi, amit egy prompt eltakarhatna. Pontosan egy mechanizmus van, ahuman_auth: cert, és ez az egyetlen legális értéke annak a kulcsnak. - Nincs hitelesítőadat-tár a célgépek fiókjaihoz. Az átjáró egyetlen célgép
egyetlen fiókjához sem tart jelszót. Ahol egy parancsnak
sudokell, a jogosultság- emelés a célgépen történik, és a célgép sajátsudo-ja kéri be, egy áteresztő terminálon át: a bróker sem el nem olvassa, sem el nem tárolja a begépeltet, és a munkamenet-rögzítő — ami csak a kimenetet rögzíti — sem tudja elkapni. - Nem marad állandó hitelesítő adat a célgépen. Az emberi munkamenet kulcsa a daemon memóriájában generálódik, és sosem íródik lemezre, sosem kerül az operátor kezébe, és sosem települ a célgépre. Valakinek a hozzáférését elvenni címtár-változtatás, nem minden hoszton takarítandó fájl.
- Nincs munkamenet-közbeni elvágás egy emberi munkamenetnél. Nincs megújítás és
nincs újrakiadás; egy élő SSH-munkamenet nem szakad meg, amikor a tanúsítványa
ablaka elmúlik. Egy munkamenet behatárolására a munkamenet-korlátok valók (a
policy.yamlidle_timeout_secondsésmax_duration_secondskulcsai), a hozzáférés visszavonására pedig a következő kiadás megtagadása. A hosszú életű hitelesítő adatoknál ez fordítva van: azisg ca revokeés azisg ca host-cert revokeegy sorszámot tesz a visszavonási listára, amit minden megújításnál és minden tárcsázásnál megnéz. - Nincs hoszt-helyettesítő karakter egy hozzáférési szabályban. A
principals: ["*"]létezik; ahosts: ["*"]nem. A széles kijelölést ahost_labelsfejezi ki, mert egy címkehalmaz auditálható — egy olvasó látja, mely hosztokra terjed ki egy szabály —, míg egy helyettesítő karakter némán elnyel minden később hozzáadott hosztot. - Nincsenek tiltó szabályok a
human_access-ben. Kizárólag engedélylista. Az engedélyezés és a tiltás keverése egy rendezett listában pontosan az a precedencia-zavar, amiről a parancsonkénti policy-motor már hordoz egy sebhelyet.