Write a policy
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.
{ "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:adminonpolicy/<name>: the managed policyadminincludes it,readwritedoes not (writing policies is a privilege escalation).
The document
{
"Version": "1",
"Statement": [
{ "Effect": "Allow", "Actions": ["<action>", "..."], "Resources": ["<ressource>", "..."] }
]
}
| Key | Value | Rule |
|---|---|---|
Version | "1" | only accepted version |
Statement | array | 1 to 50 entries |
Effect | Allow or Deny | case-sensitive |
Actions | array | 1 to 64 action patterns |
Resources | array | 0 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.
| Family | Values | Wildcard |
|---|---|---|
| Services | ssm:, iam:, deploy:, env:, policy:, kms:, cdn:, tier:, registry:, oidctrust: | *:verb |
| Verbs | read, list, describe, write, delete, rotate, declare, admin, wrap, unwrap, rewrap, assumerole, upload, purge, logs, pull, push | service:* |
| HTTP methods (generated APIs of your applications) | GET, HEAD, POST, PUT, PATCH, DELETE | none |
| 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
| Form | Example | Wildcard |
|---|---|---|
| Everything | * | — |
Logical type/name | secret/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 URL | https://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:
- matching explicit
Deny→ denial; - otherwise first matching
Allow→ allowed; - 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.
| Name | Exact statements | Attached by default to |
|---|---|---|
assume-role | Allow iam:assumerole on tenant/<tenant> | the tenant's creator, and the platform for the administration of your tenant |
readonly | Allow *:read, *:list, *:describe, deploy:logs, registry:pull, GET, HEAD on * | nobody |
readwrite | Allow 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 |
admin | Allow * 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:
readonlyandreadwritedo not includeiam:assumerole: for a user to enter the tenant, also attachassume-roleto them (--with-assume).readwriteincludes none ofpolicy:*,tier:writeorauth:ReadCredentials: managing policies and the tier, or reading an application's credentials, requiresadmin.
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).
| Status | Meaning |
|---|---|
applied | the requested document is the one in force |
pending | created or modified, not in force yet (a few minutes) |
deleting | deletion 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]
createrefuses a policy that already exists;updaterefuses a missing policy.-faccepts a JSON or YAML document, the output ofpolicy get --json, or anIAMPolicymanifest (-f -reads standard input). With a manifest,spec.descriptionserves as the description.- The name is a DNS label (lowercase letters, digits,
-, 63 characters at most), neither reserved nor prefixedbind-.
Declarative equivalent of the opening example:
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
sng apply for policiesThe 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 responds409 policies_pendingwithRetry-After: 10;sng tenant usewaits on its own. -
Write cycle:
sng tenant policy …callsPUT /v1/iam/policies/<name>, which validates the document and responds202 Accepted; the policy switches topending, thenappliedwithin a few minutes. The binding of a policy is always calledbind-<policy>. -
Optimistic locking: each application increments
version.sng tenant policy listdisplays 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-*"] } ] }