IAM-käyttöoikeuskäytännöt
Käyttöoikeuskäytäntö myöntää tai epää koneprincipaalille — API-avaimelle tai Workload Identity -sidokselle — oikeuden kutsua Frostmoln-rajapintaa. Se on Frostmolnin vastine AWS IAM -käytännölle, GCP IAM:lle tai Azure RBAC:lle, mutta kirjoitettuna Frostmolnin omalla sanastolla.
Käytännöllä voit ilmaista vähimmät oikeudet, joita litteä scope-lista ei pysty: yksittäisen toiminnon koko compute:write -oikeuden sijaan, yhden resurssin, lähde-IP-alueen, aikaikkunan — ja eksplisiittisen epäämisen. Esimerkiksi: CI-avain, joka saa luoda instansseja mutta ei koskaan poistaa niitä; Terraform-avain, joka toimii vain toimistoverkostasi; SDK-avain, joka saa koskea vain resursseihin, joilla on tunniste env=staging.
Pilotti — käyttöoikeus myönnetään tenanttikohtaisesti
Käyttöoikeuskäytäntöjen laadinta otetaan käyttöön pilottina. Se on saatavilla vain tenanteille, joille ominaisuus on otettu käyttöön. Jos et näe IAM-osiota portaalissa tai fm iam palauttaa not available, sitä ei ole vielä otettu käyttöön tenantillesi — pyydä käyttöoikeutta ottamalla yhteyttä tukeen. Nykyiset API-avaimesi ja niiden scopet toimivat sillä välin muuttumattomina.
Kuka laatii käytäntöjä
Käyttöoikeuskäytännöt hallitsevat koneprincipaaleja, mutta niitä laatii ihminen, joka on kirjautunut sisään OIDC:llä (portaali, fm-komentorivi fm login -kirjautumisen jälkeen tai Terraform OIDC-taustaisella palveluntarjoajalla). API-avain tai workload-identity-token ei voi luoda, muuttaa tai liittää käytäntöä — hallintatasoa ei voi koskaan lukita ulos käytännöllä, jonka alainen se on. Tämä on tarkoituksellista: jopa oman avaimesi deny * -käytännöstä voi palautua, koska korjaat sen ihmisenä.
Miten arviointi toimii
Arviointi on oletuksena epäävä (default-deny):
- Jos jokin
deny-sääntö täsmää, pyyntö evätään — eksplisiittinen epääminen voittaa aina, riippumatta sääntöjen järjestyksestä tai siitä, mistä käytännöstä se tuli. - Muutoin, jos jokin
allow-sääntö täsmää, pyyntö sallitaan. - Muutoin pyyntö evätään (implisiittinen epääminen).
Pyyntö täsmää sääntöön, kun sen toiminto täsmää johonkin säännön operations-arvoista, sen kohde-FRN täsmää johonkin säännön targets-arvoista ja kaikki säännön constraints-ehdot pitävät paikkansa.
Käytäntödokumentti
Käytäntö on JSON-dokumentti: schemaVersion ja lista rules.
{
"schemaVersion": "1",
"rules": [
{
"name": "compute-read-only",
"access": "allow",
"operations": ["compute:instances:read", "compute:instances:list"],
"targets": ["frn:compute:*:*:instances/*"]
}
]
}Jokaisella säännöllä on nämä kentät:
| Kenttä | Pakollinen | Arvo |
|---|---|---|
name | ei | Ihmisluettava nimi säännölle. Vapaata tekstiä; ei vaikuta arviointiin. |
access | kyllä | allow tai deny. |
operations | kyllä | Ei-tyhjä lista toimintomalleja (ks. Toiminnot). |
targets | kyllä | Ei-tyhjä lista FRN-malleja (ks. Kohteet — FRN). |
constraints | ei | Lisäehdot, joiden on pidettävä paikkansa (ks. Ehdot). |
schemaVersion on merkkijono "1". Skeema on vain lisättävä (append-only) ja versioitu kuten muukin alustan sopimus — uusia ominaisuuksia lisätään, ei koskaan nimetä uudelleen tai poisteta, joten tänään kirjoittamasi käytäntö toimii jatkossakin.
Toiminnot
Toiminto nimeää yhden toiminnan muodossa service:resource:action, esimerkiksi compute:instances:create tai storage:volumes:delete. Jokainen segmentti hyväksyy *-jokerimerkin, ja jokerimerkit tiivistyvät kullakin tasolla:
compute:instances:create— yksi tarkka toimintocompute:instances:*— jokainen compute-instanssien toimintacompute:*— jokainen compute-toiminto*:*:delete— jokainen poisto kaikissa palveluissa
Konkreettisten toimintojen joukko on palvelimen omistama, vain lisättävä luettelo — älä koodaa sitä kovakoodauksena, koska se kasvaa ajan myötä. Hae ajantasainen luettelo:
fm iam catalog # all operations
fm iam catalog --filter compute # only those containing "compute"tai HTTP:n kautta:
curl -H "Authorization: Bearer $TOKEN" \
https://api.frostmoln.cloud/api/v1/iam/catalog
# → { "operations": ["billing:invoices:create", "compute:instances:create", ... ] }Portaalin käytäntörakentaja esittää saman luettelon valitsimena.
Kohteet — FRN
Kohteet täsmätään Frostmoln-resurssinimeen (FRN) — alustan resurssien nimeämismalliin, joka vastaa AWS ARN:ää:
frn:<service>:<region>:<tenant>:<type>/<id>Säännön targets-kenttään kirjoitat FRN-malleja käyttäen *-merkkiä minkä tahansa segmentin jokerointiin:
*— täsmää jokaiseen resurssiin (käytä säästeliäästi)frn:compute:*:*:instances/*— mikä tahansa compute-instanssi, tämän tenantinfrn:storage:*:*:volumes/*— mikä tahansa volyymi, tämän tenantinfrn:compute:*:*:instances/i-0a1b2c3d— yksi tietty instanssi
<region>-segmentin on oltava *
Aluekohtaisia käytäntöjä ei tueta vielä: pyyntö ei kanna mukanaan ratkaistua aluetta, joten aluetta nimeävää kohdetta ei voi täsmätä luotettavasti. Kirjoita siihen aina * — kohde, jossa on jotain muuta, hylätään käytäntöä tallennettaessa. Kaikki muut segmentit toimivat yllä kuvatulla tavalla.
<tenant>-segmentti on sinun tenantisi
Jätä <tenant> arvoon *: koska jokainen pyyntö kantaa mukanaan oman tenantisi, * viittaa jo sinun tenantiisi eikä mihinkään muuhun. Voit myös kirjoittaa oman tenant-tunnuksesi eksplisiittisesti, mutta toista tenantia nimeävä kohde hylätään tallennettaessa — se ei voisi koskaan täsmätä, joten säännöllä ei olisi mitään vaikutusta.
Ehdot
Ehdot lisäävät sääntöön edellytyksiä. Säännön constraints on kartta, jonka avaimena on ensin operaattori ja sitten ehtoavain:
{
"name": "read-staging-from-office",
"access": "allow",
"operations": ["compute:instances:read"],
"targets": ["frn:compute:*:*:instances/*"],
"constraints": {
"ipInRange": { "frn:sourceIp": ["203.0.113.0/24"] },
"equals": { "frn:resourceTag/env": "staging" }
}
}Ehtoavaimet
| Avain | Merkitys |
|---|---|
frn:region | Ei vielä käytettävissä. Pyyntö ei kanna mukanaan ratkaistua aluetta, joten avain ei koskaan ratkea: sitä käyttävä allow ei myönnä koskaan, ja sitä käyttävä deny laukeaa jokaisella alueella. Jätä se pois. |
frn:sourceIp | Kutsujan lähde-IP. |
frn:tenant | Tenantin tunnus. |
frn:principalType | Principaalin tyyppi: api_key tai workload_identity. |
frn:currentTime | Pyynnön ajankohta (before/after-operaattoreille). |
frn:requestTag/<k> | Pyyntötunnisteen <k> arvo. |
frn:resourceTag/<k> | Kohderesurssin tunnisteen <k> arvo. |
Operaattorit ja arvomuodot
| Operaattori | Arvomuoto | Huomiot |
|---|---|---|
equals | yksittäinen merkkijono | Tarkka täsmäys. |
notEquals | yksittäinen merkkijono | Negatoitu tarkka täsmäys. |
like | yksittäinen merkkijono | Glob-täsmäys (*-jokerimerkki). |
ipInRange | lista CIDR-merkkijonoja | Käytetään frn:sourceIp-avaimen kanssa. |
before | yksittäinen RFC3339-aikaleima | Käytetään frn:currentTime-avaimen kanssa. |
after | yksittäinen RFC3339-aikaleima | Käytetään frn:currentTime-avaimen kanssa. |
ipInRange on ainoa operaattori, joka ottaa listan; jokainen muu operaattori ottaa yksittäisen skalaarimerkkijonon. Tuntematon avain tai operaattori, tai arvo, jota ehto ei pysty ratkaisemaan pyyntöhetkellä, epäonnistuu turvallisesti (fail closed) — ehto ei pidä paikkaansa (joten allow ei myönnä), ja deny laukeaa silti.
⚠️ deny-säännön ehto kaventaa sitä
Tämä on tärkein asia ymmärtää oikein. Ehtoja sovelletaan samalla tavalla allow- ja deny-sääntöihin: sääntö täsmää vain, kun sen ehdot pitävät paikkansa. Niinpä deny-säännön ehto saa epäämisen laukeamaan vain silloin, kun ehto on varmasti tosi — jolloin toiminto jää sallituksi aina kun ehto on varmasti epätosi. (Ratkaisematon tai tuntematon ehto laukaisee epäämisen silti — fail-closed.)
- Kieltääksesi toiminnon ehdottomasti kirjoita
deny-sääntö ilman ehtoa. - Rajoittaaksesi myöntöä (lähde-IP:hen, aikaikkunaan, resurssitunnisteeseen) aseta ehto
allow-sääntöön.
{
"schemaVersion": "1",
"rules": [
{
"name": "create-read-from-office",
"access": "allow",
"operations": ["compute:instances:create", "compute:instances:read"],
"targets": ["frn:compute:*:*:instances/*"],
"constraints": { "ipInRange": { "frn:sourceIp": ["203.0.113.0/24"] } }
},
{
"name": "never-delete",
"access": "deny",
"operations": ["compute:instances:delete"],
"targets": ["*"]
}
]
}allow on verkkorajoitettu ehtonsa perusteella; deny on ehdoton, joten poisto on aina kielletty. Ehto tuossa deny-säännössä olisi sallinut poistot aina, kun ehto oli epätosi.
Käytännön laatiminen
Laadi käytäntö portaalissa, fm-komentorivillä tai Terraformilla. Käytäntö kirjoitetaan kerran ja liitetään sitten yhteen tai useampaan principaaliin.
Portaali
Avaa IAM → Käyttöoikeuskäytännöt, valitse Uusi käytäntö ja käytä opastettua rakentajaa — valitse toiminnot luettelosta, lisää kohteet ja ehdot ja tarkista pyyntö ennen tallennusta sisäänrakennetulla testerillä. Avaa sitten principaali (API-avain tai workload identity) ja liitä käytäntö.
fm CLI
# Create from a file, stdin, or inline JSON
fm iam policy create --name ci-compute-operator --document @policy.json
cat policy.json | fm iam policy create --name ci-compute-operator --document -
# List / inspect / update / delete
fm iam policy list
fm iam policy get <policy-id>
fm iam policy update <policy-id> --document @policy.json
fm iam policy delete <policy-id>
# Attach to a principal (api_key | workload_identity | group)
fm iam policy attach <policy-id> --type api_key --id <key-id>
fm iam policy detach <policy-id> --type api_key --id <key-id>Terraform
Kokoa dokumentti frostmoln_iam_policy_document -datalähteellä (natiivi sanasto — rule / access / operations / targets / constraint), luo sitten käytäntö ja liitä se:
data "frostmoln_iam_policy_document" "ci" {
# Restrict the grant with a constraint on the ALLOW rule.
rule {
name = "create-read-from-office"
access = "allow"
operations = ["compute:instances:create", "compute:instances:read"]
targets = ["frn:compute:*:*:instances/*"]
constraint {
operator = "ipInRange"
key = "frn:sourceIp"
values = ["203.0.113.0/24"]
}
}
# Forbid delete unconditionally — a deny with NO constraint.
rule {
name = "never-delete"
access = "deny"
operations = ["compute:instances:delete"]
targets = ["*"]
}
}
resource "frostmoln_iam_policy" "ci" {
name = "ci-compute-operator"
description = "CI: create/read compute from the office network, never delete"
document = data.frostmoln_iam_policy_document.ci.json
}
resource "frostmoln_iam_policy_attachment" "ci_key" {
policy_id = frostmoln_iam_policy.ci.id
attachee_type = "api_key" # or "workload_identity" / "group"
attachee_id = frostmoln_api_key.ci.id
}ipInRange ottaa listan CIDR-osoitteita; jokaisen muun operaattorin values on yksittäinen elementti. Datalähde hylkää moniarvoisen listan minkä tahansa muun operaattorin kohdalla.
Ryhmät
Liittääksesi saman käytännön useaan principaaliin luo ryhmä, lisää siihen principaalit ja liitä käytäntö ryhmään:
fm iam group create --name ci-keys
fm iam group member add <group-id> --type api_key --id <key-id>
fm iam policy attach <policy-id> --type group --id <group-id>Jäsen on api_key tai workload_identity; käytäntö liitetään api_key-, workload_identity- tai group-kohteeseen.
Testaa ennen tallennusta
Väärin laadittu käytäntö puree toden teolla tuotannossa, joten testaa se ensin. Testeri (portaalin rakentaja tai fm iam simulate) ajaa ehdokasdokumentin kuvitteellista pyyntöä vastaan ja palauttaa allow tai deny — se ei koskaan tallenna tai pakota mitään:
fm iam simulate --document @policy.json \
--operation compute:instances:delete --target '*'
# → deny
fm iam simulate --document @policy.json \
--operation compute:instances:read --target 'frn:compute:*:*:instances/*' \
--source-ip 203.0.113.10
# → allowEhtokontekstin liput: --source-ip, --tenant, --principal-type, --request-tag key=value, --resource-tag key=value. Pois jätetty ehtokenttä käsitellään ratkaisemattomana / turvallisesti epäävänä (fail-closed), täsmälleen kuten tuotannossa. (Myös --region on olemassa, mutta frn:region ei ole vielä käytettävissä — ks. Ehtoavaimet.)
Siirtyminen scopeista
Nykyiset API-avaimesi scopet (compute:read, storage:write, …) toimivat edelleen — jokainen vanha scope arvioidaan vastaavana synteettisenä allow-käytäntönä, joten pakotettua siirtymää ei ole lainkaan. compute:read käyttäytyy kuin allow compute-lukutoimintojen (read/list) yli kohteessa frn:compute:*.
Siirrät avaimen todelliseen vähimmäisoikeuksien käytäntöön, kun haluat jotain, mitä scopet eivät pysty ilmaisemaan. Esimerkiksi laajan compute:write -scopen korvaaminen:
compute:write myöntää luonnin ja päivityksen ja poiston jokaiselle compute-resurssille, mistä tahansa. Vähimmäisoikeuksien korvaaja voisi olla:
{
"schemaVersion": "1",
"rules": [
{
"name": "create-and-update-from-office",
"access": "allow",
"operations": [
"compute:instances:create",
"compute:instances:update",
"compute:instances:read",
"compute:instances:list"
],
"targets": ["frn:compute:*:*:instances/*"],
"constraints": { "ipInRange": { "frn:sourceIp": ["203.0.113.0/24"] } }
},
{
"name": "never-delete",
"access": "deny",
"operations": ["compute:instances:delete"],
"targets": ["*"]
}
]
}Vaiheet:
- Laadi käytäntö (yllä) ja testaa se
fm iam simulate-komennolla niitä toimintoja vastaan, joita työkuormasi todella suorittaa. - Liitä se avaimeen.
- Kavenna avaimen scopet (käytäntö on additiivinen; sekä synteettinen scope-käytäntö että liittämäsi käytäntö arvioidaan, ja mikä tahansa eksplisiittinen
denyvoittaa). Poista laajacompute:write-scope, kun liitetty käytäntö kattaa sen, mitä avain tarvitsee. - Aja
fm iam simulateuudelleen (tai seuraa avainta portaalissa) varmistaaksesi, että myönnöt ja epäämiset ovat odotustesi mukaisia.
Koska eksplisiittinen deny ohittaa minkä tahansa allow-säännön, yllä oleva never-delete-sääntö pätee, vaikka vanha scope on yhä olemassa — turvallinen tapa tiukentaa avainta ennen kuin poistat sen scopet kokonaan.