Skip to content

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:

  1. Om någon deny-regel matchar nekas begäran — ett explicit deny vinner alltid, oavsett regelordning eller vilken policy det kom från.
  2. Annars, om någon allow-regel matchar, tillåts begäran.
  3. 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.

json
{
  "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ältObligatorisktVärde
namenejEn läsbar etikett för regeln. Fritext; påverkar inte utvärderingen.
accessjaallow eller deny.
operationsjaIcke-tom lista av operationsmönster (se Operationer).
targetsjaIcke-tom lista av FRN-mönster (se Mål — FRN).
constraintsnejExtra 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 operation
  • compute:instances:* — varje åtgärd på compute-instanser
  • compute:* — 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:

bash
fm iam catalog                 # all operations
fm iam catalog --filter compute   # only those containing "compute"

eller över HTTP:

bash
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 tenants
  • frn:storage:*:*:volumes/* — vilken volym som helst, denna tenants
  • frn: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:

json
{
  "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

NyckelBetydelse
frn:regionGå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:sourceIpAnroparens käll-IP.
frn:tenantTenant-id:t.
frn:principalTypePrincipalens typ: api_key eller workload_identity.
frn:currentTimeBegä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

OperatorVärdeformAnteckningar
equalsen enskild strängExakt matchning.
notEqualsen enskild strängNegerad exakt matchning.
likeen enskild strängGlob-matchning (jokertecken *).
ipInRangeen lista av CIDR-strängarAnvänds med frn:sourceIp.
beforeen enskild RFC3339-tidsstämpelAnvänds med frn:currentTime.
afteren enskild RFC3339-tidsstämpelAnvä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.
json
{
  "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

bash
# 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:

hcl
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:

bash
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:

bash
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
# → allow

Flaggor 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:

json
{
  "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:

  1. Författa policyn (ovan) och testa den med fm iam simulate mot de operationer din arbetsbelastning faktiskt utför.
  2. Koppla den till nyckeln.
  3. Smalna av scopen på nyckeln (policyn är additiv; både den syntetiserade scope-policyn och din kopplade policy utvärderas, och varje explicit deny vinner). Ta bort det breda compute:write-scopet när den kopplade policyn täcker det nyckeln behöver.
  4. Kör fm iam simulate igen (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.