Skip to content

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

  1. 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.
  2. Muutoin, jos jokin allow-sääntö täsmää, pyyntö sallitaan.
  3. 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.

json
{
  "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äPakollinenArvo
nameeiIhmisluettava nimi säännölle. Vapaata tekstiä; ei vaikuta arviointiin.
accesskylläallow tai deny.
operationskylläEi-tyhjä lista toimintomalleja (ks. Toiminnot).
targetskylläEi-tyhjä lista FRN-malleja (ks. Kohteet — FRN).
constraintseiLisä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 toiminto
  • compute:instances:* — jokainen compute-instanssien toiminta
  • compute:* — 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:

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

tai HTTP:n kautta:

bash
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 tenantin
  • frn:storage:*:*:volumes/* — mikä tahansa volyymi, tämän tenantin
  • frn: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:

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" }
  }
}

Ehtoavaimet

AvainMerkitys
frn:regionEi 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:sourceIpKutsujan lähde-IP.
frn:tenantTenantin tunnus.
frn:principalTypePrincipaalin tyyppi: api_key tai workload_identity.
frn:currentTimePyynnön ajankohta (before/after-operaattoreille).
frn:requestTag/<k>Pyyntötunnisteen <k> arvo.
frn:resourceTag/<k>Kohderesurssin tunnisteen <k> arvo.

Operaattorit ja arvomuodot

OperaattoriArvomuotoHuomiot
equalsyksittäinen merkkijonoTarkka täsmäys.
notEqualsyksittäinen merkkijonoNegatoitu tarkka täsmäys.
likeyksittäinen merkkijonoGlob-täsmäys (*-jokerimerkki).
ipInRangelista CIDR-merkkijonojaKäytetään frn:sourceIp-avaimen kanssa.
beforeyksittäinen RFC3339-aikaleimaKäytetään frn:currentTime-avaimen kanssa.
afteryksittäinen RFC3339-aikaleimaKä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.
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 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

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

Kokoa dokumentti frostmoln_iam_policy_document -datalähteellä (natiivi sanasto — rule / access / operations / targets / constraint), luo sitten käytäntö ja liitä se:

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

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>

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:

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

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

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": ["*"]
    }
  ]
}

Vaiheet:

  1. Laadi käytäntö (yllä) ja testaa se fm iam simulate -komennolla niitä toimintoja vastaan, joita työkuormasi todella suorittaa.
  2. Liitä se avaimeen.
  3. 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 deny voittaa). Poista laaja compute:write -scope, kun liitetty käytäntö kattaa sen, mitä avain tarvitsee.
  4. Aja fm iam simulate uudelleen (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.