Skip to main content

Write a policy

Available

Deployed and supported. The fields and commands described here are stable within version v1alpha1.

A policy says who can do what in your tenant: a short JSON document, inspired by cloud providers' IAM policies, which you then attach to users, roles or machines. Each tenant is born with four managed policies at your disposal (assume-role, readonly, readwrite, admin); you write your own to refine.

ops-read.json
{ "Version": "1",
"Statement": [
{ "Effect": "Allow", "Actions": ["ssm:read", "ssm:list", "ssm:describe"], "Resources": ["secret/*"] },
{ "Effect": "Deny", "Actions": ["ssm:read"], "Resources": ["secret/prod-*"] } ] }
sng tenant policy create ops-read -f ops-read.json --description "lecture des secrets hors production"
policy "ops-read" created (pending: applied in a few minutes)

Prerequisites​

  • A session on the tenant: sng tenant use acme (The model).
  • The action policy:admin on policy/<name>: the managed policy admin includes it, readwrite does not (writing policies is a privilege escalation).

The document​

{
"Version": "1",
"Statement": [
{ "Effect": "Allow", "Actions": ["<action>", "..."], "Resources": ["<ressource>", "..."] }
]
}
KeyValueRule
Version"1"only accepted version
Statementarray1 to 50 entries
EffectAllow or Denycase-sensitive
Actionsarray1 to 64 action patterns
Resourcesarray0 to 64 patterns; absent or empty = ["*"]

Validation is strict: an unknown key, or a malformed effect, action or resource give 400. That way a typo in a Deny cannot silently disable it. A document weighs 64 KiB at most.

Actions​

An action is written service:verb (case-insensitive, normalized to lowercase) or is an HTTP method.

FamilyValuesWildcard
Servicesssm:, iam:, deploy:, env:, policy:, kms:, cdn:, tier:, registry:, oidctrust:*:verb
Verbsread, list, describe, write, delete, rotate, declare, admin, wrap, unwrap, rewrap, assumerole, upload, purge, logs, pull, pushservice:*
HTTP methods (generated APIs of your applications)GET, HEAD, POST, PUT, PATCH, DELETEnone
Everything*covers both families

Service verbs and HTTP methods are two distinct families: *:* does not cover GET. Only * covers everything. The dns service is added to this list where DNS management is enabled.

Resources​

FormExampleWildcard
Everything*—
Logical type/namesecret/db-password, app/api, tenant/acme* as a suffix only: secret/*, secret/prod-*, resource/deploy/*
Path (any host of the tenant)/v1/orders/** = one segment in the middle of a path; final * = the whole subtree
Full URLhttps://orders-api.acme.dev.stackngo.app/v1/orders/*same, exact host

Known logical types: secret, resource, release, env, tenant, user, role, group, app, policy, key, cdn, tier, repository, oidctrust (plus dnszone, dnsrecord with DNS). The action catalog gives the exact resource of each route.

Evaluation​

For an action on a resource, StackNGO reads all the policies attached to your subject (you and your roles, or the machine application, or the CI job) in the session's tenant:

  1. matching explicit Deny → denial;
  2. otherwise first matching Allow → allowed;
  3. otherwise → denial.

An operation on a whole collection (listing secrets, creating an app) checks the type/* form: an Allow secret/* covers it; a Deny secret/prod-* does not block it (the service then filters item by item).

Managed policies​

Each tenant has managed policies: created when the tenant is created, cannot be modified, cannot be deleted. Their names are reserved: sng tenant policy create admin responds 409. You can, however, attach them to whoever you want.

NameExact statementsAttached by default to
assume-roleAllow iam:assumerole on tenant/<tenant>the tenant's creator, and the platform for the administration of your tenant
readonlyAllow *:read, *:list, *:describe, deploy:logs, registry:pull, GET, HEAD on *nobody
readwriteAllow all of readonly + ssm:write, ssm:delete, ssm:rotate, ssm:declare, deploy:write, deploy:delete, env:write, cdn:write, cdn:delete, cdn:upload, cdn:purge, registry:push, registry:delete, POST, PUT, PATCH, DELETE on *nobody
adminAllow * on *the tenant's creator, and the platform for the administration of your tenant

Two other managed policies, reserved for the platform, allow it to declare and rotate your secrets; they never grant writing a value. They appear in sng tenant policy list: do not attach them to anyone.

To remember:

  • readonly and readwrite do not include iam:assumerole: for a user to enter the tenant, also attach assume-role to them (--with-assume).
  • readwrite includes none of policy:*, tier:write or auth:ReadCredentials: managing policies and the tier, or reading an application's credentials, requires admin.

Manage your policies​

List​

sng tenant policy list
NAME DEFAULT STATUS VERSION DESCRIPTION
admin yes applied 1 Accès complet au tenant, y compris la gestion des utilisateurs, des rôles et des policies.
assume-role yes applied 1 Permet d'ouvrir une session sur ce tenant.
ops-read pending lecture des secrets hors production
readonly yes applied 1 Lecture seule : consulter les ressources du tenant sans rien modifier.
readwrite yes applied 1 Lecture et écriture des ressources du tenant (secrets, déploiements, sites, images), sans gestion des accès.
…

Each managed policy carries a description (in French) that says what it is for. The list ends with the two managed policies reserved for the platform: their description starts with « Réservée à la plateforme » (reserved to the platform) and ends with « Ne pas attacher » (do not attach).

StatusMeaning
appliedthe requested document is the one in force
pendingcreated or modified, not in force yet (a few minutes)
deletingdeletion requested, policy still in force for a few minutes

Read​

sng tenant policy get ops-read # the JSON document
sng tenant policy get ops-read --json # with name, description, default, status, version, document

Create and replace​

sng tenant policy create NAME -f FILE [--description TEXTE]
sng tenant policy update NAME -f FILE [--description TEXTE]
  • create refuses a policy that already exists; update refuses a missing policy.
  • -f accepts a JSON or YAML document, the output of policy get --json, or an IAMPolicy manifest (-f - reads standard input). With a manifest, spec.description serves as the description.
  • The name is a DNS label (lowercase letters, digits, -, 63 characters at most), neither reserved nor prefixed bind-.

Declarative equivalent of the opening example:

ops-read.yml
apiVersion: iam.stackngo.io/v1alpha1
kind: IAMPolicy
metadata:
name: ops-read
spec:
description: lecture des secrets hors production
statement:
- effect: Allow
actions: ["ssm:read", "ssm:list", "ssm:describe"]
resources: ["secret/*"]
- effect: Deny
actions: ["ssm:read"]
resources: ["secret/prod-*"]
sng tenant policy create ops-read -f ops-read.yml
No sng apply for policies

The IAMPolicy and IAMPolicyBinding kinds go through sng tenant policy, not through sng apply: the API itself stores them in your tenant's desired state after validation. See the Manifest reference.

Delete​

sng tenant policy delete ops-read --yes
policy "ops-read" deletion requested (applied in a few minutes)

Deletion also removes all its attachments. Managed policies cannot be deleted (409).

Verification​

After one to a few minutes:

sng tenant policy list
sng tenant policy principals ops-read

The status switches from pending to applied, and principals displays the subjects actually attached (applied) against the requested subjects (requested).

Going further​

  • 409 policies_pending: when a tenant is created, its managed policies take a few minutes to apply. During this delay (5 min at most), opening a session responds 409 policies_pending with Retry-After: 10; sng tenant use waits on its own.

  • Write cycle: sng tenant policy … calls PUT /v1/iam/policies/<name>, which validates the document and responds 202 Accepted; the policy switches to pending, then applied within a few minutes. The binding of a policy is always called bind-<policy>.

  • Optimistic locking: each application increments version. sng tenant policy list displays it.

  • Policy for a generated API: HTTP methods target the routes of your own applications exposed under <service>-<app>.<tenant>.dev.stackngo.app:

    { "Version": "1", "Statement": [
    { "Effect": "Allow", "Actions": ["GET"], "Resources": ["https://orders-api.acme.dev.stackngo.app/v1/orders/*"] } ] }
  • Policy for a CI that publishes a site without being able to create or delete it:

    { "Version": "1", "Statement": [
    { "Effect": "Allow", "Actions": ["cdn:upload", "cdn:purge"], "Resources": ["cdn/*"] },
    { "Effect": "Deny", "Actions": ["cdn:*"], "Resources": ["cdn/prod-*"] } ] }

See also​