Permission Service Configuration#
Goal: Deploy the Permission Service in Kubernetes. The service supports two modes: config-file mode (read-only, no database required) for quick testing, and Postgres mode for full policy management. This guide starts with config-file mode as the simplest way to get running, then covers upgrading to Postgres.
Service Ports#
Port |
Protocol |
Description |
|---|---|---|
3000 |
HTTP/REST |
REST API endpoint |
3010 |
gRPC |
gRPC API endpoint |
Pulling the Permission Service Helm Chart#
Pull and unpack the Permission Service Helm chart from the NGC catalog. Replace {NGC_API_KEY} with your NGC API
key.
# pull the chart from NGC
helm fetch https://helm.ngc.nvidia.com/nvidia/omniverse/charts/permission-service-{VERSION}.tgz --username='$oauthtoken' --password=${NGC_API_KEY}
# unpack the chart and cd into the directory
tar -xvf permission-service-{VERSION}.tgz
cd permission-service
Config File Mode#
Config-file mode is the simplest way to start the Permission Service. Policies are defined as YAML directly in Helm values and mounted into the container — no external database is required.
Setting Up Default Policies#
Create a permission-values.yaml file to configure the service in config-file mode with default policies.
# create and edit the local values file, we're using VS Code for this example
code permission-values.yaml
Add the image-pull secret and configure the database type:
image:
pullSecrets:
- name: ngcpull-secret
database:
type: "config-file"
init:
policies:
# Permit all requests — useful for initial testing
- policy: "permit(principal, action, resource);"
permission-values.yaml
Policies are written in Cedar policy language. Each entry under
database.init.policies must contain exactly one Cedar policy statement in the policy field. See the
official Cedar documentation for the full language reference.
Note
In config-file mode, policy files are automatically reloaded when their modification time changes — no service restart is required.
Granting Administrative Permissions#
Access to the policy management and metadata APIs is gated by meta-permissions — Cedar actions on the reserved
permissions service that every authenticated caller is evaluated against. If none of these actions are granted
to anyone, the service starts up correctly but no caller can list, create, or delete policies, change service
metadata, or inspect other users’ effective permissions.
Action |
Protects |
|---|---|
|
Reading policies through |
|
Creating and deleting policies through |
|
Reading and writing service metadata through every endpoint under |
|
Calling |
Note
Meta-permissions are only enforced when authentication is enabled (auth.enabled: true). When authentication is
disabled, every endpoint is reachable without a token and these actions are not evaluated.
Meta-permissions are granted the same way as any other permission — by writing Cedar policies that permit them
for the principals who should have access. Seed those policies through database.init.policies alongside the
application policies for the services you deploy. For example, to give the single user alice full
administrative access:
database:
init:
policies:
# Administrative access for one user
- policy: 'permit(principal == Principal::"alice", action == Action::"permissions:view", resource);'
- policy: 'permit(principal == Principal::"alice", action == Action::"permissions:edit", resource);'
- policy: 'permit(principal == Principal::"alice", action == Action::"permissions:meta", resource);'
- policy: 'permit(principal == Principal::"alice", action == Action::"permissions:diagnostics", resource);'
In practice, most deployments grant these actions to a group rather than a specific user, for example:
database:
init:
policies:
# Administrative access for anyone whose token carries the "permission-admins" group claim
- policy: >-
permit(principal, action == Action::"permissions:view", resource)
when { principal.groups.contains("permission-admins") };
- policy: >-
permit(principal, action == Action::"permissions:edit", resource)
when { principal.groups.contains("permission-admins") };
- policy: >-
permit(principal, action == Action::"permissions:meta", resource)
when { principal.groups.contains("permission-admins") };
- policy: >-
permit(principal, action == Action::"permissions:diagnostics", resource)
when { principal.groups.contains("permission-admins") };
The principal.groups attribute used above, along with any other claim-based attributes referenced in policies,
is populated from the caller’s bearer token claims at request time.
Warning
The fully-open starter policy permit(principal, action, resource); shown above grants all actions, including
the four meta-permissions, to every authenticated caller. It is convenient for initial testing but should be
replaced with scoped policies before exposing the service to real users.
Setting Up Service Metadata#
In addition to policies, the Helm value database.init.services seeds service metadata — the catalog of services
that integrate with authorization, the actions each exposes, the resource types those actions operate on, and the
per-service idClaim and per-resource-type evaluationPriority settings the service consumes at evaluation time.
The chart renders the list into a ConfigMap and mounts it at /etc/permission-config/ so the service can load it
on startup.
The example below registers a single storage-service with two actions and two resource types:
database:
init:
services:
- name: "storage-service"
principal:
idClaim: "sub"
actions:
- "read"
- "write"
resourceTypes:
- type: "object"
evaluationPriority: "permit"
- type: "folder"
evaluationPriority: "permit"
Schema notes:
nameis the service identifier; actions are referenced from Cedar policies asAction::"<name>:<action>".principal.idClaimis optional. When omitted, the service falls back to the deployment-widePRINCIPAL_ID_CLAIM(defaultsub). It names the bearer-token claim used as the principal id for requests targeting this service.actionsare plain action names; each becomes addressable asAction::"<service>:<name>"in Cedar.resourceTypes[].typeis the Cedar entity type used as<type>::"<id>"in policies.resourceTypes[].evaluationPriorityis optional and defaults to"forbid"; the alternative is"permit". It controls how competingpermit/forbidpolicies are combined for resources of that type.A service that registers actions only (no resource types) simply omits
resourceTypes; Cedar policies for those actions leave theresourceslot unconstrained.
In Postgres mode, seeded entries are written into the database on startup and can be changed afterwards through
the metadata REST API under /v1beta/services/.
Limitations#
Config-file mode is read-only. All write operations (add_policy, update_policy, remove_policy,
set_service_meta, etc.) return a “not supported” error. This mode is suitable for testing and static deployments
where policies do not change at runtime.
To manage policies dynamically through the API, use Postgres mode.
Install the Permission Service (Config File Mode)#
Validate and install the chart:
# validate the chart
helm template . -f permission-values.yaml
# dry-run the install
helm upgrade --install permission-service . -f permission-values.yaml --namespace storage-apis --dry-run --debug
If everything looks good, install the Permission Service:
# install the permission service
helm upgrade --install permission-service . -f permission-values.yaml --namespace storage-apis
# validate the pod is running, this may take a few minutes to start up
kubectl get pods -n storage-apis
Verify the service is responding:
# test the permission service health endpoint
curl http://permission-service.storage-apis.svc.cluster.local:3000/health
Postgres Mode#
Postgres mode unlocks full policy management through the API: create, update, and delete policies and service
metadata at runtime. Policies defined in database.init.policies are loaded as system policies on startup, and
service metadata defined in database.init.services is written into the database on startup and can be changed
afterwards through the metadata REST API exposed under /v1beta/services/.
Note
The meta-permissions that gate the policy management, metadata, and
diagnostics APIs apply in Postgres mode as well. Seed them through database.init.policies on first deployment so
that administrators can reach those APIs once the service is up; otherwise the APIs are inaccessible even with a
valid bearer token.
Setting Up PostgreSQL with CloudNativePG#
CloudNativePG is a Kubernetes operator that manages the full lifecycle of PostgreSQL clusters. It provides a convenient and easy-to-set-up way to run PostgreSQL directly in a Kubernetes cluster.
Install the CloudNativePG Operator#
# add the CloudNativePG Helm repository
helm repo add cnpg https://cloudnative-pg.github.io/charts
# install the operator
helm upgrade --install cnpg-operator cnpg/cloudnative-pg \
--namespace cnpg-system \
--create-namespace
Verify the operator is running:
kubectl get pods -n cnpg-system
Create a PostgreSQL Cluster#
Create a Cluster custom resource to provision a PostgreSQL instance. Save the following as
permission-pg-cluster.yaml:
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: permission-pg
namespace: storage-apis
spec:
instances: 1
bootstrap:
initdb:
database: permissions
owner: permission-service
storage:
size: 1Gi
Apply the resource:
kubectl apply -f permission-pg-cluster.yaml
# wait for the cluster to become ready
kubectl get cluster -n storage-apis
CloudNativePG automatically creates a secret named permission-pg-app in the same namespace containing the
connection credentials (host, port, user, password, dbname). You can inspect it:
kubectl get secret permission-pg-app -n storage-apis -o yaml
Prepare Secrets for the Permission Service#
The Permission Service expects a secret containing POSTGRES_PASSWORD. You can create one from the
CloudNativePG-generated credentials:
# extract the password from the CNPG-generated secret
PGPASSWORD=$(kubectl get secret permission-pg-app -n storage-apis -o jsonpath='{.data.password}' | base64 -d)
# create the permission service secret
kubectl create secret generic permission-service-secret \
--from-literal=POSTGRES_PASSWORD="$PGPASSWORD" \
--namespace storage-apis
Alternatively, if you manage PostgreSQL outside of CloudNativePG, create the secret directly with your database password:
kubectl create secret generic permission-service-secret \
--from-literal=POSTGRES_PASSWORD="{YOUR_POSTGRES_PASSWORD}" \
--namespace storage-apis
CloudNativePG Alternatives#
Any PostgreSQL-compatible database works with the Permission Service. If you prefer not to run PostgreSQL inside the cluster, consider a cloud-managed service:
When using a managed service, provide the connection details (host, port, user, dbname, sslMode) in
the Helm values and create a Kubernetes secret with the password as described above.
Deploy in Postgres Mode#
Create or update permission-values.yaml with Postgres settings. Replace the host with the service name of
your PostgreSQL instance (CloudNativePG creates a service named permission-pg-rw by default):
image:
pullSecrets:
- name: ngcpull-secret
database:
type: "postgres"
init:
policies:
# System policies loaded on startup — optional
- policy: "permit(principal, action, resource);"
postgres:
host: "permission-pg-rw"
port: 5432
user: "permission-service"
dbname: "permissions"
sslMode: ""
connections: 16
workerThreads: 32
secret:
name: permission-service-secret
create: false
permission-values.yaml
Note
Set secret.create: false when you have already created the permission-service-secret manually. If
secret.create is true (the default), the chart creates the secret from database.postgres.password in
the values file.
Validate and install:
# validate the chart
helm template . -f permission-values.yaml
# dry-run the install
helm upgrade --install permission-service . -f permission-values.yaml --namespace storage-apis --dry-run --debug
If everything looks good, install the Permission Service:
# install the permission service
helm upgrade --install permission-service . -f permission-values.yaml --namespace storage-apis
# validate the pod is running
kubectl get pods -n storage-apis
Verify the service is responding:
# test the permission service health endpoint
curl http://permission-service.storage-apis.svc.cluster.local:3000/health
Ingress Access#
To expose the Permission Service outside the cluster, enable the httpProxy section in your values file. The
chart creates Contour HTTPProxy resources for both REST and gRPC endpoints.
httpProxy:
enabled: true
fqdn:
host: "permissions"
domain: "{DNS_URL}"
permission-values.yaml
Redeploy the Permission Service with the updated values:
helm upgrade --install permission-service . -f permission-values.yaml --namespace storage-apis
Verify the HTTPProxy resources were created:
kubectl get httpproxy -n storage-apis
For full details on setting up Contour as a load balancer, configuring DNS records, and enabling TLS, see the Ingress Configuration guide.
Clean Up#
To clean up: uninstall the Helm release, delete secrets, and remove the namespace.
# uninstall the permission service
helm uninstall permission-service -n storage-apis
# delete the secrets
kubectl delete secret permission-service-secret -n storage-apis
# if using CloudNativePG, delete the cluster
kubectl delete cluster permission-pg -n storage-apis
# delete the namespace
kubectl delete namespace storage-apis
# validate the namespace was deleted
kubectl get namespace storage-apis
References#
Cedar Policy Language — product home for Cedar
Cedar documentation — language reference and guides
CloudNativePG Documentation — operator for running PostgreSQL in Kubernetes
Contour HTTPProxy — ingress resource used by the Helm chart
Ingress Configuration — full guide for Contour, DNS, and TLS setup
Amazon RDS for PostgreSQL — managed PostgreSQL on AWS
Azure Database for PostgreSQL — managed PostgreSQL on Azure
Cloud SQL for PostgreSQL — managed PostgreSQL on GCP
What’s Next?#
Continue your deployment by configuring the Discovery Service so clients can find and connect to all services via a single endpoint.
See the official Cedar documentation for the complete language specification.