The allow-list
ForgejoNamespacePolicy decides which namespace may create what, in which organization, for whom.
When several teams share a cluster, you don't want team A's namespace
creating repositories in team B's organization, or minting a token for the
admin account. ForgejoNamespacePolicy (short name fjnp) is the allow-list
that prevents this.
What it governs
| Kinds | Governed by |
|---|---|
| Cluster-scoped: instances, namespace policies | Kubernetes RBAC only |
Namespaced, bound to a shared ForgejoInstance: users, user keys, organizations, teams, repositories, webhooks, deploy keys, OAuth2 applications, access tokens | RBAC and the allow-list |
Namespaced, bound to a ForgejoLocalInstance | RBAC only: the namespace's own token bounds them |
No policy means everything is allowed
While no ForgejoNamespacePolicy exists, every namespace may create any
namespaced kind, referencing any organization or user. As soon as one
policy exists, the default becomes deny.
Administrators always need an explicit rule
An access token, an SSH key or an OAuth2 application for a site admin acts
with site-admin rights, and a ForgejoUser with admin: true makes one.
Such a CR is refused unless a rule explicitly lists that user in
allowedUsers for the namespace and kind, even while no policy exists. An
unset allowedUsers doesn't count. The reconcilers also ask Forgejo whether
the account is a site admin, whatever its CR says.
See Security model.
Write a policy
With at least one policy in place, a CR is accepted only if:
- an
Allowrule matches its namespace and kind; - that rule covers every organization, user and template the CR references; and
- no
Denyrule matches. ADenyalways wins.
apiVersion: forgejo.weebo.io/v1alpha1
kind: ForgejoNamespacePolicy
metadata:
name: teams
spec:
rules:
# Every team-* namespace may manage repos, hooks and deploy keys in "weebo".
- namespaces: ["team-*"] # exact name, "*", or a prefix glob
allowedKinds: [ForgejoRepository, ForgejoWebhook, ForgejoDeployKey]
allowedOrganizations: [weebo] # unset = any organization
# Only team-a may mint tokens, and only for its own bot.
- namespaces: [team-a]
allowedKinds: [ForgejoAccessToken]
allowedUsers: [team-a-bot] # unset = ANY user. Set it.
# Nobody may mint tokens or OAuth2 apps for admin-bot.
- namespaces: ["*"]
allowedKinds: [ForgejoAccessToken, ForgejoOAuth2Application]
allowedUsers: [admin-bot]
effect: DenyallowedOrganizations and allowedUsers list Forgejo names — an
organization's name, a user's login — not CR names: a CR name is only unique
in its namespace, and a remoteName reference has none. They cover what a
CR manages (a ForgejoUser's own login, a ForgejoOrganization's own name)
and everything it references (organizationRef, userRef, ownerRef,
memberRefs), whether through a CR or a remoteName. A user referenced by
remoteName is treated as a site admin by the administrators rule above:
the operator can't read its flags there, so its credentials need an
explicit allowedUsers entry.
Templates
allowedTemplates restricts the template repositories a namespace may
generate repositories from by Forgejo name
(fromTemplate.repository). It lists owner/name patterns: an exact name,
*, or a prefix glob such as batlehub/*. Unset means any template.
- namespaces: ["team-*"]
allowedKinds: [ForgejoRepository]
allowedOrganizations: [weebo]
allowedTemplates: ["batlehub/*"] # only the shared templatesA template referenced as a ForgejoRepository of the same namespace
(fromTemplate.repositoryRef) isn't governed by this list: the namespace
already manages it.
Restrict templates on a shared cluster
The operator generates repositories with its admin token, which can read
every repository, private ones included. Without allowedTemplates, a
namespace allowed to create repositories can copy the content of any
repository marked as a template into its own. While no policy exists, that
is the case for every namespace.
What each kind references
A rule must cover what the CR points at:
| Kind | References |
|---|---|
ForgejoRepository | Its owner: the organization (organizationRef) or the user (userRef); and the template it is generated from by name (fromTemplate.repository) |
ForgejoWebhook on an organization | That organization |
ForgejoWebhook on a repository | Nothing beyond the repository, which must be in the same namespace and is checked itself |
ForgejoDeployKey | Nothing beyond the repository, as above |
ForgejoOAuth2Application | Its owner (ownerRef), or @instance-admin without one |
ForgejoAccessToken | Its user (userRef) |
@instance-admin. An OAuth2 application without ownerRef belongs to
the operator's own admin account. For the allow-list, it references the
special user @instance-admin. A rule that restricts allowedUsers only
allows such an application if it lists @instance-admin.
Site-admin credentials. A token, SSH key or OAuth2 application of a
site admin — a ForgejoUser with admin: true, a user observed
(managementPolicy: Observe) or referenced by remoteName, an account
Forgejo reports as admin, or @instance-admin — and a ForgejoUser with
admin: true itself, always need an Allow rule naming that user in
allowedUsers — even while no policy exists. A token, OAuth2 application
or SSH key already issued for an account that became a site admin is
revoked (deleted in Forgejo). See
Security model.
Where it's enforced
- At apply time, by the admission webhook, on create and update.
- It never blocks a delete. It also always allows an update that leaves
specunchanged, such as a label edit or the operator removing its finalizer. So revoking a namespace's access never prevents the cleanup of what it already created. - With the chart's default
failurePolicy: Fail, these kinds can't be written while the webhook is down.
- It never blocks a delete. It also always allows an update that leaves
- Before every change, by each reconciler. Tightening a policy also
stops existing CRs (
Ready: False, reasonNamespaceNotAllowed), and a policy change re-checks them right away.
A refused kubectl apply reads (the message uses kind names without the
Forgejo prefix):
admission webhook "validate.forgejo.weebo.io" denied the request:
no ForgejoNamespacePolicy rule allows AccessToken in namespace "team-b" with users ["admin-bot"]