Checkpoint Lab — Kubernetes Agent, GitOps Workflows, Cluster Access, Environments, and Deployment Integration
Design and optionally exercise a complete disposable GitLab-to-Kubernetes trust path, prove one denied operation, correlate GitLab and cluster identities, and verify that cleanup removes residual access.
Learning objectives
- Design a complete GitLab-to-Kubernetes trust path with named actors, tokens, scopes, and authorization decisions.
- Predict allowed and denied operations before testing them.
- Correlate GitLab environment/deployment evidence with immutable Kubernetes workload identity and GitOps revision.
- Exercise a safe denial without mutating valuable cluster resources.
- Remove agent/token/local-cluster resources and verify there is no residual access.
gitops.manifest_projects functionality was removed in
GitLab 17.0; current GitOps guidance uses
Flux plus the GitLab Agent. Therefore the mandatory
chapter path is manifest/identity simulation with no real cluster or
paid feature; any live cluster exercise is optional and disposable.
1. Checkpoint scenario and operating constraints
You are designing deployment access for
training/platform/app. Review and staging workloads
share a disposable non-production cluster. Production is out of
scope. The mandatory checkpoint remains a fixture simulation. An
optional live extension may use only a local disposable cluster and
disposable GitLab project.
2. Draw the actor/token/network/authorization flow
Your diagram must contain these nodes: developer, GitLab project/ref, CI job, runner, KAS, agentk, agent token, Kubernetes API, service account or impersonated CI identity, Role/RoleBinding, namespace, workload, GitLab environment, and Flux controller. Label every boundary where an authorization decision occurs.
flowchart TD D[Developer] --> R[GitLab ref / commit] R --> P[Pipeline / job] P --> RN[Trusted runner] RN --> KAS[KAS] AT[Agent token] --> AK[agentk] AK <-->|authenticated channel| KAS KAS --> AK AK --> API[Kubernetes API] API --> RB[RBAC] RB --> NS[ch28-lab namespace] NS --> W[Workload digest] P --> E[GitLab environment] F[Flux] -->|reconcile desired revision| W R --> F
3. Preflight and baseline evidence
Record the following before changing anything:
{
"gitlab_version_baseline": "19.3",
"offering": "fixture / optional disposable GitLab",
"tier_required_for_mandatory_path": "Free",
"project": "training/platform/app",
"agent_config_project": "training/platform/agent-config",
"environment": "staging",
"namespace": "ch28-lab",
"allowed_operation": "get/list pods in ch28-lab",
"denied_operation": "delete namespaces",
"gitops_controller": "Flux"
}
Prediction A: a staging job in the authorized project should receive
an agent context. Prediction B: the effective Kubernetes identity
may read Pods in ch28-lab but must not delete
namespaces.
4. Build the fixture trust path
Create the agent configuration, RBAC, read-only CI, and Flux fixtures from Lesson 2. Add a tiny workload manifest that uses an immutable synthetic digest:
apiVersion: apps/v1
kind: Deployment
metadata:
name: ch28-demo
namespace: ch28-lab
spec:
replicas: 1
selector:
matchLabels: {app: ch28-demo}
template:
metadata:
labels: {app: ch28-demo}
spec:
containers:
- name: app
image: registry.example.invalid/training/app@sha256:1111111111111111111111111111111111111111111111111111111111111111
The invalid registry prevents accidental execution in the mandatory path. The purpose is to preserve the identity chain.
5. Validate policy intent before execution
Use local checks to prove the intended configuration contains the narrow project/environment scope and least-privilege verbs:
python - <<'PY'
from pathlib import Path
c=Path('.gitlab/agents/lab-agent/config.yaml').read_text()
r=Path('fixtures/rbac.yaml').read_text()
assert 'training/platform/app' in c
assert 'staging' in c and 'review/*' in c
assert 'verbs: ["get", "list"]' in r
assert 'delete' not in r
print('scope/RBAC predictions encoded: PASS')
PY
sha256sum .gitlab/agents/lab-agent/config.yaml fixtures/rbac.yaml manifests/deployment.yaml
git rev-parse HEAD
6. Optional live exercise: prove one allow and one deny
In a disposable local cluster with a disposable GitLab Agent connection, select the agent context and run read-only authorization tests:
kubectl config use-context "$AGENT_CONTEXT"
kubectl auth can-i get pods -n ch28-lab
# expected: yes
kubectl auth can-i delete namespaces
# expected: no
kubectl -n ch28-lab get deployment ch28-demo -o jsonpath='{.metadata.name}{" "}{.spec.template.spec.containers[0].image}{"\n"}'
If the denied action prints yes, stop. Do not perform
the delete. Preserve the authorization evidence, tighten the
Role/service account/impersonation binding, and rerun
auth can-i.
7. Prove GitLab and cluster identity independently
A complete evidence packet should be able to answer all of these questions without secrets:
| Question | Evidence |
|---|---|
| Which source revision initiated delivery? | Git commit SHA / manifest revision |
| Which GitLab job/environment was involved? | Pipeline ID, job ID, environment name/tier |
| Which agent connection was selected? | Agent project:name and numeric agent ID |
| Which exact workload artifact is desired? | OCI digest in manifest |
| Which exact workload is observed? | Kubernetes Deployment/Pod image identity |
| Is GitOps converged? | Flux Ready status and applied revision |
| What can the CI identity do? | kubectl auth can-i allow/deny results |
8. Inject one safe failure and diagnose it
Remove staging from the fixture environment allowlist
or change the project ID to a non-matching synthetic project.
Predict that the job should no longer receive/use that agent
connection. Preserve the changed config SHA and expected denial.
Restore the original fixture and verify the prediction is once again
satisfied.
On a live optional sandbox, avoid using a real production agent to test this. Configuration propagation can take one or two minutes; distinguish propagation delay from a syntax or RBAC failure.
9. Cleanup and prove no residual access
Mandatory fixture cleanup: keep only sanitized evidence, then remove the disposable local repository. Optional live cleanup:
- List the lab agent and active token IDs.
- Revoke the lab agent token.
- Delete the GitLab agent registration.
- Uninstall agentk from the local cluster.
- Remove the lab namespace and any Flux objects created only for the exercise.
- Verify the agent is absent from the GitLab API, the token cannot be retrieved/used, the cluster has no lab agent/namespace resources, and no CI variable contains a copied kubeconfig/token.
10. Production operating model added by Chapter 28
Your GitLab operating model now includes an external infrastructure trust boundary. The production rule is: GitLab project/ref/runner authorization decides who may request access; Kubernetes identity/RBAC decides what the request may do; GitOps/controller evidence decides whether declared state is actually converged; environment/deployment records connect those facts back to delivery history.
Knowledge check
Why must the checkpoint predict a denied operation before testing?
It defines the intended least-privilege contract. A surprising “yes” becomes a clear policy failure instead of an ambiguous observation.
What evidence proves the deployed bytes most precisely?
An immutable OCI image digest, correlated with the manifest and observed workload—not a mutable tag.
Why are GitLab environment and Flux/Kubernetes status both required?
They answer different questions: GitLab records delivery history; Flux/Kubernetes show current desired/observed cluster state.
After deleting the GitLab agent object, what remains to do?
Revoke/verify tokens as applicable and explicitly uninstall/remove cluster-side agent/namespace/controller resources; GitLab deletion does not clean Kubernetes automatically.
What is the safer production GitOps direction in current GitLab?
Flux-based pull reconciliation plus the GitLab Agent for integration/access, rather than the removed legacy agent manifest sync or direct production CI pushes.
What is Chapter 29 about?
REST and GraphQL APIs, webhooks/system hooks, pagination, rate limits, and resilient GitLab automation.
11. Chapter close and bridge to Chapter 29
Chapter 28 completes the platform-to-cluster trust model without turning GitLab into Kubernetes itself. You can now reason about agent identity, cluster authorization, GitOps reconciliation, environment evidence, and cleanup independently. Chapter 29 returns to GitLab as an integration platform and teaches how to automate those resources safely through REST, GraphQL, webhooks, pagination, rate limits, and idempotent clients.
Primary sources and version notes
These lessons were finalized against current official GitLab documentation on 2026-08-22. Kubernetes, Flux, agentk, glab, KAS, and cluster security evolve independently, so re-check current GitLab version/tier, supported agent/Kubernetes versions, CLI syntax, and cluster policy before production use.
- GitLab 19.3 release
- Connecting a Kubernetes cluster with GitLab
- Install the GitLab Agent for Kubernetes
- Using GitLab CI/CD with a Kubernetes cluster
- Grant users Kubernetes access
- Using GitOps with a Kubernetes cluster
- Migrate legacy agent GitOps to Flux
- Managing agent instances and tokens
- Kubernetes Agent REST API
- Kubernetes managed resources and environments
- GitLab deprecations and removals
- GitLab token security overview
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.