https://github.com/glueops/gatekeeper.platform.glueops.dev
Managed by github-org-manager
https://github.com/glueops/gatekeeper.platform.glueops.dev
Last synced: 5 months ago
JSON representation
Managed by github-org-manager
- Host: GitHub
- URL: https://github.com/glueops/gatekeeper.platform.glueops.dev
- Owner: GlueOps
- Created: 2026-02-19T14:42:58.000Z (5 months ago)
- Default Branch: main
- Last Pushed: 2026-02-21T00:03:13.000Z (5 months ago)
- Last Synced: 2026-02-21T01:49:01.550Z (5 months ago)
- Language: Go
- Size: 15.7 MB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- Agents: AGENTS.md
Awesome Lists containing this project
README
# GlueOps Core Gatekeeper

GlueOps Core Gatekeeper is a small Go HTTP service that acts as a **deployment dependency gate** for Kubernetes.
It lets applications define their own dependency checks (via a `Gate` custom resource), and provides a simple API that returns:
- **200 OK** when all checks pass (dependencies are ready)
- **409 Conflict** when any check is not ready yet (safe to retry)
- **4xx** for invalid specs or authorization issues
This is especially useful when you need ordering logic across a mix of:
- Argo CD-managed apps (sync waves / hooks)
- Helm-managed components (hooks, Jobs, controllers)
- “Out-of-band” resources not represented as Argo Applications
A common pattern is to use an **Argo CD PreSync hook Job** to block deployment of an app until the Gatekeeper reports that prerequisites (DB, migrations, external dependencies) are ready.
---
## Key features
- **Per-app dependency definition**: each app owns a `Gate` object in its namespace.
- **Safe-by-default multi-tenancy**:
- Requests authenticate via Kubernetes **ServiceAccount tokens**.
- Access is authorized using Kubernetes **SubjectAccessReview** (delegated authorization).
- **Namespace policy modes** controlled by a namespace label:
- `customer` (default): gate lookup and checks are namespace-local
- `platform`: optionally allows cross-namespace checks and/or gate lookups (restricted by allowlists)
- **Simple HTTP API** designed for hook-based retries.
---
## How it works
1. A caller (usually an Argo CD hook Job) calls Gatekeeper with its ServiceAccount token:
- `GET /check?gate=`
2. Gatekeeper performs a `TokenReview` to authenticate the token.
3. Gatekeeper determines the caller namespace from the ServiceAccount identity.
4. Gatekeeper loads the `Gate` CR and evaluates each check.
5. Each check is authorized with a `SubjectAccessReview` for the caller.
6. Gatekeeper responds with JSON and appropriate HTTP status.
---
## Installation
### 1) Install the CRD
Apply the `Gate` CRD:
```bash
kubectl apply -f crd.yaml
```
### 2) Create Gatekeeper RBAC
Apply Gatekeeper RBAC (TokenReview + SubjectAccessReview + read-only allowlisted resources + Gate status patch):
```bash
kubectl apply -f rbac.yml
```
### 3) Deploy the Gatekeeper service
Your deployment should run with the Gatekeeper ServiceAccount and expose port 8080.
(Provide your own deployment YAML, Helm chart, or Kustomize overlay.)
## Configuration
Gatekeeper supports configuration via environment variables.
### Gate CRD location
| Env var | Default | Description |
| -------------------------- | ------------------------------ | ------------------------- |
| `GATEKEEPER_GATE_GROUP` | `platform.onglueopshosted.com` | API group of the Gate CRD |
| `GATEKEEPER_GATE_VERSION` | `v1alpha1` | API version |
| `GATEKEEPER_GATE_RESOURCE` | `gates` | plural resource name |
### Platform mode can be restricted to specific namespaces.
| Env var | Default | Description |
| ------------------------------------------------ | ---------------------- | ------------------------------------------------------------------------------------- |
| `GATEKEEPER_PLATFORM_ALLOWED_NAMESPACES` | `glueops-core,nonprod` | CSV list of namespaces allowed for platform cross-namespace access |
| `GATEKEEPER_PLATFORM_ALLOWED_NAMESPACE_PREFIXES` | `glueops-core-` | CSV list of namespace prefixes allowed (e.g. `glueops-core-` allows `glueops-core-*`) |
### HTTP
| Env var | Default | Description |
| ------- | ------- | ---------------- |
| `PORT` | `8080` | HTTP listen port |
## Namespace policy modes
Gatekeeper chooses the mode based on a label on the caller’s namespace:
Label key:
```yaml
gatekeeper.platform.onglueopshosted.com/mode: customer|platform
```
### customer mode (default)
- Gate lookup must be in the caller namespace.
- Checks must be in the same namespace as the Gate.
This is intended for customer/self-service namespaces.
### platform mode
- Cross-namespace Gate lookup is allowed via ?ns=... only if the namespace is allowed by:
- GATEKEEPER_PLATFORM_ALLOWED_NAMESPACES OR
- GATEKEEPER_PLATFORM_ALLOWED_NAMESPACE_PREFIXES
- Cross-namespace checks are allowed using the same allow rules.
This is intended for platform-controlled automation and core namespaces.
## The Gate Custom Resource
A Gate defines a list of checks. Each check must set exactly one check type.
Example:
```yaml
apiVersion: platform.onglueopshosted.com/v1alpha1
kind: Gate
metadata:
name: keycloak-prod
namespace: nonprod
spec:
strict: true
checks:
- id: postgres
deploymentAvailable:
name: keycloak-pg-database-prod
minAvailableReplicas: 1
```
### Supported check types
`deploymentAvailable`
Checks a Deployment is Available and has at least N available replicas.
```yaml
- id: api
deploymentAvailable:
name: my-api
minAvailableReplicas: 2
```
`statefulSetReady`
Checks a StatefulSet has at least N ready replicas (and optionally is fully updated).
```yaml
- id: postgres
statefulSetReady:
name: postgres
minReadyReplicas: 1
requireUpdatedRevision: true
```
`jobComplete`
Checks a Job has completed successfully.
```yaml
- id: migrate
jobComplete:
name: my-app-migrate
```
`serviceReadyEndpoints`
Checks a Service has at least N ready endpoint addresses (via EndpointSlices).
```yaml
- id: redis
serviceReadyEndpoints:
name: redis
minReadyAddresses: 1
```
`podLabelReady`
Checks at least N Pods matching a label selector are Ready.
```yaml
- id: workers
podLabelReady:
selector: "app=my-worker"
minReadyPods: 2
```
> Note: If you allow very broad selectors, this may list many pods. Prefer selectors that are specific to the app.
## HTTP API
### GET /healthz
Simple health endpoint.
```bash
curl -i http://gatekeeper:8080/healthz
```
### GET /check?gate=
Evaluates the Gate in the caller namespace (customer mode), or by default in the caller namespace (platform mode too).
- 200 OK → all checks passed
- 409 Conflict → at least one check is blocking (safe to retry)
Example:
```bash
TOKEN="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)"
curl -sS -H "Authorization: Bearer $TOKEN" \
"http://gatekeeper:8080/check?gate=keycloak-prod"
```
### GET /check?gate=&ns= (platform mode)
In platform mode only, allows evaluating a Gate in another namespace if allowed by allowlists.
```bash
TOKEN="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)"
curl -sS -H "Authorization: Bearer $TOKEN" \
"http://gatekeeper:8080/check?gate=keycloak-prod&ns=nonprod"
```
### GET /explain?gate=[&ns=]
Same evaluation logic as /check, but always returns JSON with 200 OK (useful for debugging).
```
curl -sS -H "Authorization: Bearer $TOKEN" \
"http://gatekeeper:8080/explain?gate=keycloak-prod&ns=nonprod" | jq .
```
### JSON response format
```json
{
"gate": "keycloak-prod",
"namespace": "nonprod",
"ready": false,
"mode": "platform",
"results": [
{
"id": "postgres",
"ready": false,
"message": "availableReplicas 0 < 1"
}
]
}
```
## Argo CD usage (recommended)
The most common integration is an Argo CD PreSync hook Job that blocks until Gatekeeper returns 200.
Example: PreSync hook Job
```yaml
apiVersion: batch/v1
kind: Job
metadata:
name: gate-wait
annotations:
argocd.argoproj.io/hook: PreSync
argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
backoffLimit: 0
template:
spec:
serviceAccountName: gate-waiter
restartPolicy: Never
containers:
- name: wait
image: curlimages/curl:8.5.0
command: ["/bin/sh", "-c"]
args:
- |
set -e
TOKEN="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)"
until curl -sf -H "Authorization: Bearer ${TOKEN}" \
"http://glueops-core-gatekeeper.glueops-core-gatekeeper.svc.cluster.local:8080/check?gate=keycloak-prod"; do
echo "dependencies not ready yet"; sleep 5
done
echo "dependencies ready"
```
This pattern makes Argo CD wait until prerequisites are ready before continuing the sync.
## RBAC / Authorization model
Gatekeeper uses delegated authorization:
- The caller’s ServiceAccount must have RBAC permissions to read the resources referenced in the Gate checks.
- Gatekeeper verifies permissions with SubjectAccessReview before reading.
Example: minimal namespace Role for a hook SA
In many cases, you can give the PreSync hook SA a minimal Role in its namespace:
```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: gate-waiter-read
namespace: nonprod
rules:
- apiGroups: ["apps"]
resources: ["deployments","statefulsets"]
verbs: ["get"]
- apiGroups: ["batch"]
resources: ["jobs"]
verbs: ["get"]
- apiGroups: [""]
resources: ["services"]
verbs: ["get"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["list"]
- apiGroups: ["discovery.k8s.io"]
resources: ["endpointslices"]
verbs: ["list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: gate-waiter-read
namespace: nonprod
subjects:
- kind: ServiceAccount
name: gate-waiter
namespace: nonprod
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: gate-waiter-read
```
### Local development
Run locally against a cluster
update / create .env file
```text
GATEKEEPER_PLATFORM_ALLOWED_NAMESPACE_PREFIXES="glueops-core-"
GATEKEEPER_PLATFORM_ALLOWED_NAMESPACES="glueops-core,nonprod"
KUBECONFIG=/home/vscode/.kube/config
```
```bash
go run .
```
### Test with a ServiceAccount token
For example, to test platform mode from a platform namespace:
```bash
NS=glueops-core-gatekeeper
SA=glueops-core-gatekeeper
TOKEN=$(kubectl -n "$NS" create token "$SA" --duration=10m)
curl -sS -H "Authorization: Bearer $TOKEN" \
"http://localhost:8080/check?gate=keycloak-prod&ns=nonprod" | jq .
```
If you get a 403:
- verify the caller SA has RBAC to read the referenced resources (or intentionally doesn’t)
- verify the target namespace is allowed by GATEKEEPER_PLATFORM_ALLOWED_NAMESPACES/..._PREFIXES
### Troubleshooting
jq: parse error
You are likely receiving a plain-text error response. Run with -i to see the status code:
```bash
curl -i -H "Authorization: Bearer $TOKEN" \
"http://localhost:8080/check?gate=keycloak-prod&ns=nonprod"
```
#### 403 forbidden with SubjectAccessReview
The caller ServiceAccount does not have permission to read a resource referenced by a check.
Fix by granting minimal RBAC in the namespace for that SA (see RBAC example above).
#### 404 gate not found
The Gate name or namespace is wrong, or you are calling without &ns= in platform mode.