IAM-tilgangspolicyer
En tilgangspolicy gir eller nekter en maskinprincipal — en API-nøkkel eller en Workload Identity-binding — tillatelse til å kalle Frostmoln-API-et. Den er Frostmolns motstykke til en AWS IAM-policy, GCP IAM eller Azure RBAC, men skrevet i Frostmolns eget vokabular.
En policy lar deg uttrykke minste privilegium som en flat scope-liste ikke kan: en enkelt operasjon i stedet for alle compute:write, én ressurs, et kilde-IP-område, et tidsvindu — og eksplisitt deny. For eksempel: en CI-nøkkel som kan opprette instanser, men aldri slette dem; en Terraform-nøkkel som bare virker fra kontornettet ditt; en SDK-nøkkel som bare kan berøre ressurser merket env=staging.
Pilot — tilgang gis per tenant
Forfatting av tilgangspolicyer rulles ut som en pilot. Den er kun tilgjengelig for tenanter der funksjonen er aktivert. Hvis du ikke ser IAM i portalen eller fm iam returnerer not available, er den ennå ikke aktivert for din tenant — kontakt support for å be om tilgang. Dine eksisterende API-nøkler og deres scoper fortsetter å fungere uendret i mellomtiden.
Hvem forfatter policyer
Tilgangspolicyer styrer maskin-principaler, men de forfattes av et menneske innlogget med OIDC (portalen, fm-CLI-en etter fm login, eller Terraform med en OIDC-basert provider). En API-nøkkel eller et workload-identity-token kan ikke opprette, endre eller knytte til en policy — styringsplanet kan aldri låses ute av en policy det selv er underlagt. Dette er bevisst: selv en deny *-policy på din egen nøkkel er gjenopprettbar, fordi du fikser den som et menneske.
Slik fungerer evalueringen
Evalueringen er default-deny:
- Hvis en
deny-regel matcher, nektes forespørselen — en eksplisitt deny vinner alltid, uavhengig av regelrekkefølge eller hvilken policy den kom fra. - Ellers, hvis en
allow-regel matcher, tillates forespørselen. - Ellers nektes forespørselen (implisitt deny).
En forespørsel matcher en regel når dens operasjon matcher en av regelens operations, dens mål-FRN matcher en av regelens targets, og alle regelens constraints holder.
Policy-dokumentet
En policy er et JSON-dokument: en schemaVersion og en liste med rules.
{
"schemaVersion": "1",
"rules": [
{
"name": "compute-read-only",
"access": "allow",
"operations": ["compute:instances:read", "compute:instances:list"],
"targets": ["frn:compute:*:*:instances/*"]
}
]
}Hver regel har disse feltene:
| Felt | Påkrevd | Verdi |
|---|---|---|
name | nei | En menneskelig etikett for regelen. Fritekst; har ingen effekt på evalueringen. |
access | ja | allow eller deny. |
operations | ja | Ikke-tom liste med operasjonsmønstre (se Operasjoner). |
targets | ja | Ikke-tom liste med FRN-mønstre (se Mål — FRN). |
constraints | nei | Ekstra betingelser som må holde (se Betingelser). |
schemaVersion er strengen "1". Skjemaet er append-only og versjonert som resten av plattformkontrakten — nye funksjoner legges til, aldri omdøpt eller fjernet, slik at en policy du skriver i dag fortsetter å fungere.
Operasjoner
En operasjon navngir én handling i formatet service:resource:action, for eksempel compute:instances:create eller storage:volumes:delete. Hvert segment godtar *-jokertegnet, og jokertegn kollapser på hvert nivå:
compute:instances:create— én eksakt operasjoncompute:instances:*— alle handlinger på compute-instansercompute:*— alle compute-operasjoner*:*:delete— alle sletthandlinger, på tvers av alle tjenester
Settet av konkrete operasjoner er en server-eid, append-only-katalog — ikke hardkod det, fordi det vokser over tid. Hent den live katalogen:
fm iam catalog # all operations
fm iam catalog --filter compute # only those containing "compute"eller over HTTP:
curl -H "Authorization: Bearer $TOKEN" \
https://api.frostmoln.cloud/api/v1/iam/catalog
# → { "operations": ["billing:invoices:create", "compute:instances:create", ... ] }Portalens policy-bygger presenterer den samme katalogen som en velger.
Mål — FRN
Mål matches mot et Frostmoln Resource Name (FRN) — plattformens ressursnavngivingsskjema, analogt med et AWS ARN:
frn:<service>:<region>:<tenant>:<type>/<id>I en regels targets skriver du FRN-mønstre, ved å bruke * for å bruke jokertegn på et hvilket som helst segment:
*— matcher alle ressurser (bruk sparsomt)frn:compute:*:*:instances/*— enhver compute-instans, denne tenantensfrn:storage:*:*:volumes/*— ethvert volum, denne tenantensfrn:compute:*:*:instances/i-0a1b2c3d— én bestemt instans
<region>-segmentet må være *
Regionsavgrensede policyer støttes ennå ikke: en forespørsel bærer ingen oppslått region, så et mål som navngir én kan ikke matches pålitelig. Skriv alltid * der — et mål med noe annet avvises når du lagrer policyen. Alle andre segmenter fungerer som beskrevet over.
<tenant>-segmentet er din tenant
La <tenant> stå som *: fordi hver forespørsel bærer din egen tenant, viser * allerede til din tenant og ingen annen. Du kan også skrive din egen tenant-id eksplisitt, men et mål som navngir en annen tenant avvises når du lagrer — det kunne aldri matche, så regelen ville i praksis ikke ha noen effekt.
Betingelser
Betingelser legger vilkår til en regel. En regels constraints er et map nøklet på operator, deretter på betingelsesnøkkel:
{
"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" }
}
}Betingelsesnøkler
| Nøkkel | Betydning |
|---|---|
frn:region | Kan ikke brukes ennå. En forespørsel bærer ingen oppslått region, så nøkkelen kan aldri løses: et allow som bruker den gir aldri tilgang, og et deny som bruker den utløses i alle regioner. La den stå ubrukt. |
frn:sourceIp | Kallerens kilde-IP. |
frn:tenant | Tenant-id-en. |
frn:principalType | Principal-typen: api_key eller workload_identity. |
frn:currentTime | Forespørselstidspunktet (for before/after). |
frn:requestTag/<k> | Verdien av forespørselstaggen <k>. |
frn:resourceTag/<k> | Verdien av taggen <k> på målressursen. |
Operatorer og verdiformer
| Operator | Verdiform | Merknader |
|---|---|---|
equals | en enkelt streng | Eksakt match. |
notEquals | en enkelt streng | Negert eksakt match. |
like | en enkelt streng | Glob-match (*-jokertegn). |
ipInRange | en liste med CIDR-strenger | Brukes med frn:sourceIp. |
before | et enkelt RFC3339-tidsstempel | Brukes med frn:currentTime. |
after | et enkelt RFC3339-tidsstempel | Brukes med frn:currentTime. |
ipInRange er den eneste operatoren som tar en liste; alle andre operatorer tar en enkelt skalar streng. En ukjent nøkkel eller operator, eller en verdi en betingelse ikke kan løse på forespørselstidspunktet, feiler lukket — betingelsen holder ikke (så et allow gir ikke tilgang), og et deny utløses fortsatt.
⚠️ En betingelse på et deny innsnevrer det
Dette er det aller viktigste å få riktig. Betingelser anvendes på samme måte på allow- og deny-regler: en regel matcher bare når dens betingelser holder. Så en betingelse på en deny-regel gjør at deny-en utløses bare når betingelsen er definitivt sann — og lar operasjonen være tillatt når betingelsen er definitivt usann. (En uavklart eller ukjent betingelse utløser likevel deny-en — fail-closed.)
- For å forby en operasjon ubetinget, skriv en
deny-regel uten betingelse. - For å begrense en tildeling (til en kilde-IP, et tidsvindu, en ressurstagg), plasser betingelsen på
allow-regelen.
{
"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-en er nettverksbegrenset av sin betingelse; deny-en er ubetinget, så sletting er alltid forbudt. En betingelse på det deny-et ville ha tillatt sletting når betingelsen var usann.
Forfatte en policy
Forfatt i portalen, med fm-CLI-en, eller med Terraform. En policy skrives én gang og deretter knyttes til en eller flere principaler.
Portal
Åpne IAM → Access Policies, velg New policy, og bruk den veiledede byggeren — velg operasjoner fra katalogen, legg til mål og betingelser, og bruk den innebygde testeren for å sjekke en forespørsel før du lagrer. Åpne deretter en principal (API-nøkkel eller workload identity) og knytt til policyen.
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
Sett sammen dokumentet med frostmoln_iam_policy_document-datakilden (nativt vokabular — rule / access / operations / targets / constraint), opprett deretter policyen og knytt den til:
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 tar en liste med CIDR-er; hver annen operators values er et enkelt element. Datakilden avviser en flerverdiliste på enhver annen operator.
Grupper
For å knytte den samme policyen til flere principaler, opprett en gruppe, legg principaler til den, og knytt policyen til gruppen:
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>Et medlem er en api_key eller en workload_identity; en policy knyttes til en api_key, en workload_identity, eller en group.
Test før du lagrer
En feilforfattet policy har reelle tenner i produksjon, så test den først. Testeren (portalbyggeren, eller fm iam simulate) kjører et kandidatdokument mot en hypotetisk forespørsel og returnerer allow eller deny — den lagrer eller håndhever aldri noe:
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
# → allowFlagg for betingelseskontekst: --source-ip, --tenant, --principal-type, --request-tag key=value, --resource-tag key=value. Et utelatt betingelsesfelt behandles som uløst / feiler lukket, akkurat som i produksjon. (--region finnes også, men frn:region kan ikke brukes ennå — se Betingelsesnøkler.)
Migrere fra scoper
Dine eksisterende API-nøkkel-scoper (compute:read, storage:write, …) fungerer fortsatt — hver eldre scope evalueres som en tilsvarende syntetisert allow-policy, så det er null tvungen migrering. compute:read oppfører seg som et allow over compute read/list-operasjonene på frn:compute:*.
Du migrerer en nøkkel til en ekte minste-privilegium-policy når du ønsker noe scoper ikke kan uttrykke. For eksempel, ved å erstatte en bred compute:write-scope:
compute:write gir create og update og delete på enhver compute- ressurs, fra hvor som helst. En minste-privilegium-erstatning kan være:
{
"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": ["*"]
}
]
}Steg:
- Forfatt policyen (over) og test den med
fm iam simulatemot operasjonene arbeidslasten din faktisk utfører. - Knytt den til nøkkelen.
- Innsnevre scopene på nøkkelen (policyen er additiv; både den syntetiserte scope-policyen og din tilknyttede policy evalueres, og ethvert eksplisitt
denyvinner). Fjern den bredecompute:write-scopen når den tilknyttede policyen dekker det nøkkelen trenger. - Kjør
fm iam simulatepå nytt (eller følg nøkkelen i portalen) for å bekrefte at tildelingene og nektelsene er som du forventer.
Fordi et eksplisitt deny overstyrer ethvert allow, holder never-delete-regelen over selv mens den eldre scopen fortsatt er til stede — en trygg måte å stramme inn en nøkkel før du er ferdig med å fjerne scopene dens.