Standing-Access Scan
Overview
Per-run pipeline credentials remove the pipeline’s standing access, but every namespace still has bindings that persist: built-in service accounts, the broker’s own binding, and anything granted by hand. A Resource Manager scan runs against each namespace resource and reports those subjects as identities and the roles they hold as groups, with privileged roles flagged.
What you’ll accomplish:
- Grant the broker read-only access for scanning
- Upload a scan script and enable scanning on the resource type
- Run a scan and read the inventory
How It Works
A scan runs per resource. The broker receives the resource’s fields upper-cased with a RESOURCE_ prefix (RESOURCE_CLUSTER_NAME, RESOURCE_NAMESPACE) and the path to write results to (BROKER_INJECTED_SCAN_OUTPUT_PATH). No variable mapping is needed. The broker uploads the JSON, and Britive imports it.
| Scan payload | Contents |
|---|---|
data.identities | Every subject bound in the namespace: its service accounts, plus users, groups and service accounts from other namespaces named in its RoleBindings |
data.groups | One per Role or ClusterRole bound in the namespace; members are identity ids; privileged for admin, edit, cluster-admin and roles with wildcard verbs |
data.permissions, data.permission_mapping | Empty; access is expressed as group membership |
Britive’s per-run gha-* service accounts and bindings are excluded: they are short-lived checkouts, not standing access.
A scan reports what is inside existing resources; it does not create resources.
Before You Begin
- The
aro-namespaceresource type and resources from Namespace Resource Type - Access Broker 3.x with configuration version 2 (
BRITIVE_BROKER_CONFIG_VERSION=2), which enables scans jqin the broker image
Steps
Grant the broker read-only scan access
Cluster-wide get and list on service accounts and RBAC objects. No Secrets.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: britive-broker-scan
rules:
- apiGroups: [""]
resources: ["namespaces", "serviceaccounts"]
verbs: ["get", "list"]
- apiGroups: ["rbac.authorization.k8s.io"]
resources: ["roles", "rolebindings", "clusterroles", "clusterrolebindings"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: britive-broker-scan
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: britive-broker-scan
subjects:
- kind: ServiceAccount
name: britive-broker
namespace: britive-systemoc apply -f rbac-scan.yamlWrite the scan script
The script writes lists to files before jq reads them (OpenShift’s ClusterRole list is too large to pass as an argument), builds the payload, and validates it against the import’s rules before writing:
- every identity has non-empty
email,first_nameandlast_name - identity and group
idandnameare at most 50 characters (longer ids are shortened with a checksum suffix) - groups carry
type: "User group"andis_active - every group member is an identity id
OUT=${BROKER_INJECTED_SCAN_OUTPUT_PATH:?}
CLUSTER=${RESOURCE_CLUSTER_NAME:?}
NS=${RESOURCE_NAMESPACE:?}
KC="/etc/britive/kubeconfigs/${CLUSTER}.kubeconfig"
TMP=$(mktemp -d); trap 'rm -rf "$TMP"' EXIT
oc --kubeconfig "$KC" -n "$NS" get serviceaccounts -o json >"$TMP/sas.json"
oc --kubeconfig "$KC" -n "$NS" get rolebindings -o json >"$TMP/rbs.json"
oc --kubeconfig "$KC" -n "$NS" get roles -o json >"$TMP/roles.json"
oc --kubeconfig "$KC" get clusterroles -o json \
| jq '[.items[] | {key: "ClusterRole/\(.metadata.name)",
value: ([.rules[]? | (.verbs // []) | index("*")] | any)}] | from_entries' \
>"$TMP/cwild.json"
# jq -n --slurpfile ... builds {data: {identities, groups, permissions: [], permission_mapping: []}, metadata}
# identity: {id, name, type: "User", description, created_on, is_active,
# attributes: {email, first_name, last_name, kind, username, ...}}
# group: {id, name, type: "User group", description, created_on, is_active, members, attributes}The full script is kept on the platform, in the configuration of the resource type, where you upload it in the next step.
Enable scanning on the resource type
resource "britive_resource_manager_resource_type" "aro_namespace" {
name = "aro-namespace"
scan_enabled = true
# parameters as before
}
resource "britive_resource_manager_resource_type_scan_settings" "aro_namespace" {
resource_type_id = britive_resource_manager_resource_type.aro_namespace.id
template_type = "FilePath"
script_file_path = "${path.module}/scripts/scan.sh"
time_limit = 5 # minutes
}The scan resources need the Britive Terraform provider 3.0.3 or later.
Schedule a daily scan
resource "britive_resource_manager_resource_type_schedule_scan" "daily" {
resource_type_id = britive_resource_manager_resource_type.aro_namespace.id
name = "aro-namespace-daily"
frequency_type = "Daily"
start_time = "06:00"
resource_labels {
label_key = "britive-managed"
values = ["true"]
}
depends_on = [britive_resource_manager_resource_type_scan_settings.aro_namespace]
}Run a scan
In the Britive console, open the aro-namespace resource type and click Scan Resources.
Verify
A clean namespace with only OpenShift defaults and the broker’s binding reports 5 identities and 4 roles, none privileged:
| Group (role) | Members |
|---|---|
ClusterRole/britive-broker-ns | sa:britive-system/britive-broker |
ClusterRole/system:deployer | sa:deployer |
ClusterRole/system:image-builder | sa:builder |
ClusterRole/system:image-puller | group:system:serviceaccounts:<namespace> |
The broker log shows the scan and the upload:
msg="broker response" response.action=scan response.status=SUCCESS
msg="broker response" response.action=upload response.status=SUCCESSGrant someone edit in the namespace and scan again: the role appears with privileged=true.
Troubleshoot
| Symptom | Cause | Fix |
|---|---|---|
/usr/bin/jq: Argument list too long | Large lists passed with --argjson | Write lists to files and use --slurpfile |
Console: “An unknown error has occurred” while the broker logs upload SUCCESS | The import rejected the payload | Check required identity fields, 50-character ids and group type; validate before writing |
forbidden: ... cannot list resource "clusterroles" | Scan role missing or not bound | Apply britive-broker-scan and its ClusterRoleBinding |
| Scan request never reaches the broker | Scanning not enabled, or config version 1 | scan_enabled = true; BRITIVE_BROKER_CONFIG_VERSION=2 |