IAM-åtkomstpolicyer
En åtkomstpolicy beviljar eller nekar en maskinprincipal — en API-nyckel eller en Workload Identity-bindning — behörighet att anropa Frostmoln-API:et. Den är Frostmolns motsvarighet till en AWS IAM-policy, GCP IAM eller Azure RBAC, men skriven i Frostmolns egen vokabulär.
En policy låter dig uttrycka minsta behörighet som en platt scope-lista inte kan: en enskild operation i stället för hela compute:write, en resurs, ett käll-IP-intervall, ett tidsfönster — och explicit neka. Till exempel: en CI-nyckel som får skapa instanser men aldrig ta bort dem; en Terraform-nyckel som bara fungerar från ditt kontorsnät; en SDK-nyckel som bara får röra resurser taggade env=staging.
Pilot — åtkomst beviljas per tenant
Författande av åtkomstpolicyer rullas ut som en pilot. Det är endast tillgängligt för tenanter som funktionen har aktiverats för. Om du inte ser IAM i portalen eller om fm iam returnerar not available är det inte aktiverat för din tenant ännu — kontakta supporten för att begära åtkomst. Dina befintliga API-nycklar och deras scopes fortsätter fungera oförändrat under tiden.
Vem författar policyer
Åtkomstpolicyer styr maskinprincipaler, men de författas av en människa inloggad med OIDC (portalen, fm-CLI:t efter fm login, eller Terraform med en OIDC-backad provider). En API-nyckel eller ett workload-identity-token kan inte skapa, ändra eller koppla en policy — styrplanet kan aldrig låsas ute av en policy det själv omfattas av. Detta är avsiktligt: även en deny *-policy på din egen nyckel går att återställa, eftersom du åtgärdar den som människa.
Så fungerar utvärderingen
Utvärderingen är default-deny:
- Om någon
deny-regel matchar nekas begäran — ett explicit deny vinner alltid, oavsett regelordning eller vilken policy det kom från. - Annars, om någon
allow-regel matchar, tillåts begäran. - Annars nekas begäran (implicit deny).
En begäran matchar en regel när dess operation matchar en av regelns operations, dess mål-FRN matchar en av regelns targets, och alla regelns constraints håller.
Policydokumentet
En policy är ett JSON-dokument: en schemaVersion och en lista av rules.
{
"schemaVersion": "1",
"rules": [
{
"name": "compute-read-only",
"access": "allow",
"operations": ["compute:instances:read", "compute:instances:list"],
"targets": ["frn:compute:*:*:instances/*"]
}
]
}Varje regel har dessa fält:
| Fält | Obligatoriskt | Värde |
|---|---|---|
name | nej | En läsbar etikett för regeln. Fritext; påverkar inte utvärderingen. |
access | ja | allow eller deny. |
operations | ja | Icke-tom lista av operationsmönster (se Operationer). |
targets | ja | Icke-tom lista av FRN-mönster (se Mål — FRN). |
constraints | nej | Extra villkor som måste hålla (se Villkor). |
schemaVersion är strängen "1". Schemat är append-only och versionshanterat som resten av plattformskontraktet — nya funktioner läggs till, aldrig döps om eller tas bort, så en policy du skriver idag fortsätter fungera.
Operationer
En operation namnger en åtgärd i formatet service:resource:action, till exempel compute:instances:create eller storage:volumes:delete. Varje segment tar emot jokertecknet *, och jokertecken kollapsar på varje nivå:
compute:instances:create— en exakt operationcompute:instances:*— varje åtgärd på compute-instansercompute:*— varje compute-operation*:*:delete— varje delete, över alla tjänster
Mängden konkreta operationer är en serverägd, append-only-katalog — hårdkoda den inte, eftersom den växer över tid. Hämta den aktuella katalogen:
fm iam catalog # all operations
fm iam catalog --filter compute # only those containing "compute"eller över HTTP:
curl -H "Authorization: Bearer $TOKEN" \
https://api.frostmoln.cloud/api/v1/iam/catalog
# → { "operations": ["billing:invoices:create", "compute:instances:create", ... ] }Portalens policybyggare presenterar samma katalog som en väljare.
Mål — FRN
Mål matchas mot ett Frostmoln Resource Name (FRN) — plattformens resursnamnschema, analogt med ett AWS ARN:
frn:<service>:<region>:<tenant>:<type>/<id>I en regels targets skriver du FRN-mönster, med * för att jokerteckna vilket segment som helst:
*— matchar varje resurs (använd sparsamt)frn:compute:*:*:instances/*— vilken compute-instans som helst, denna tenantsfrn:storage:*:*:volumes/*— vilken volym som helst, denna tenantsfrn:compute:*:*:instances/i-0a1b2c3d— en specifik instans
Segmentet <region> måste vara *
Regionsbegränsade policyer stöds inte ännu: en begäran bär ingen upplöst region, så ett mål som namnger en region kan inte matchas tillförlitligt. Skriv alltid * där — ett mål med något annat avvisas när du sparar policyn. Alla övriga segment fungerar som beskrivs ovan.
Segmentet <tenant> är din tenant
Låt <tenant> vara *: eftersom varje begäran bär med sig din egen tenant syftar * redan på din tenant och ingen annan. Du kan även skriva ditt eget tenant-id explicit, men ett mål som namnger en annan tenant avvisas när du sparar — det skulle aldrig kunna matchas, så regeln skulle inte ha någon effekt.
Villkor
Villkor lägger till förutsättningar på en regel. En regels constraints är en map med nyckel på operator, sedan på villkorsnyckel:
{
"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" }
}
}Villkorsnycklar
| Nyckel | Betydelse |
|---|---|
frn:region | Går inte att använda ännu. En begäran bär ingen upplöst region, så nyckeln kan aldrig lösas upp: ett allow som använder den beviljar aldrig, och ett deny som använder den fyras i varje region. Utelämna den. |
frn:sourceIp | Anroparens käll-IP. |
frn:tenant | Tenant-id:t. |
frn:principalType | Principalens typ: api_key eller workload_identity. |
frn:currentTime | Begärans tidpunkt (för before/after). |
frn:requestTag/<k> | Värdet på request-taggen <k>. |
frn:resourceTag/<k> | Värdet på taggen <k> på målresursen. |
Operatorer och värdeformer
| Operator | Värdeform | Anteckningar |
|---|---|---|
equals | en enskild sträng | Exakt matchning. |
notEquals | en enskild sträng | Negerad exakt matchning. |
like | en enskild sträng | Glob-matchning (jokertecken *). |
ipInRange | en lista av CIDR-strängar | Används med frn:sourceIp. |
before | en enskild RFC3339-tidsstämpel | Används med frn:currentTime. |
after | en enskild RFC3339-tidsstämpel | Används med frn:currentTime. |
ipInRange är den enda operatorn som tar en lista; varje annan operator tar en enskild skalär sträng. En okänd nyckel eller operator, eller ett värde ett villkor inte kan resolva vid begärans tidpunkt, failar closed — villkoret håller inte (så ett allow beviljar inte), och ett deny fyras fortfarande.
⚠️ Ett villkor på ett deny smalnar av det
Detta är det enskilt viktigaste att få rätt. Villkor tillämpas på samma sätt på allow- och deny-regler: en regel matchar bara när dess villkor håller. Så ett villkor på en deny-regel gör att neket fyras endast när villkoret är definitivt sant — vilket lämnar operationen tillåten närhelst villkoret är definitivt falskt. (Ett olöst eller okänt villkor fyrar ändå neket — fail-closed.)
- För att förbjuda en operation ovillkorligt, skriv en
deny-regel utan villkor. - För att begränsa en beviljning (till ett käll-IP, ett tidsfönster, en resurstagg), lägg villkoret på
allow-regeln.
{
"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 är nätverksbegränsat av sitt villkor; deny är ovillkorligt, så delete är förbjudet alltid. Ett villkor på det deny skulle ha tillåtit deletes närhelst villkoret var falskt.
Författa en policy
Författa i portalen, med fm-CLI:t eller med Terraform. En policy skrivs en gång och kopplas sedan till en eller flera principaler.
Portal
Öppna IAM → Access Policies, välj New policy och använd den guidade byggaren — välj operationer från katalogen, lägg till mål och villkor, och använd den inbyggda testaren för att kontrollera en begäran innan du sparar. Öppna sedan en principal (API-nyckel eller workload identity) och koppla policyn.
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
Komponera dokumentet med datakällan frostmoln_iam_policy_document (inbyggd vokabulär — rule / access / operations / targets / constraint), skapa sedan policyn och koppla den:
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 lista av CIDR:er; varje annan operators values är ett enskilt element. Datakällan avvisar en lista med flera värden på någon annan operator.
Grupper
För att koppla samma policy till flera principaler, skapa en grupp, lägg till principaler i den och koppla policyn till 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>En medlem är en api_key eller en workload_identity; en policy kopplas till en api_key, en workload_identity eller en group.
Testa innan du sparar
En felförfattad policy har verkliga tänder i produktion, så testa den först. Testaren (portalbyggaren, eller fm iam simulate) kör ett kandidatdokument mot en hypotetisk begäran och returnerar allow eller deny — den persisterar eller upprätthåller aldrig något:
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
# → allowFlaggor för villkorskontext: --source-ip, --tenant, --principal-type, --request-tag key=value, --resource-tag key=value. Ett utelämnat villkorsfält behandlas som unresolved / fail-closed, precis som i produktion. (--region finns också, men frn:region går inte att använda ännu — se Villkorsnycklar.)
Migrera från scopes
Dina befintliga API-nyckel-scopes (compute:read, storage:write, …) fungerar fortfarande — varje äldre scope utvärderas som en likvärdig syntetiserad allow- policy, så det finns noll påtvingad migrering. compute:read beter sig som ett allow över compute read/list-operationerna på frn:compute:*.
Du migrerar en nyckel till en riktig policy med minsta behörighet när du vill ha något scopes inte kan uttrycka. Till exempel att ersätta ett brett compute:write-scope:
compute:write beviljar create och update och delete på varje compute- resurs, från var som helst. En ersättning med minsta behörighet kan vara:
{
"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:
- Författa policyn (ovan) och testa den med
fm iam simulatemot de operationer din arbetsbelastning faktiskt utför. - Koppla den till nyckeln.
- Smalna av scopen på nyckeln (policyn är additiv; både den syntetiserade scope-policyn och din kopplade policy utvärderas, och varje explicit
denyvinner). Ta bort det bredacompute:write-scopet när den kopplade policyn täcker det nyckeln behöver. - Kör
fm iam simulateigen (eller följ nyckeln i portalen) för att bekräfta att beviljningarna och nekningarna är vad du förväntar dig.
Eftersom ett explicit deny åsidosätter varje allow håller regeln never-delete ovan även medan det äldre scopet fortfarande finns kvar — ett säkert sätt att strama åt en nyckel innan du är klar med att ta bort dess scopes.