Chapter 28Lesson 02~340 minutes

Kubernetes Agent, GitOps Workflows, Cluster Access, Environments, and Deployment Integration: Guided Hands-On Workflow and Core Operations

Work through a Free-compatible fixture lab for agent configuration, authorization, least-privilege Kubernetes access, environment evidence, and safe cleanup, with an optional disposable local-cluster extension.

Hands-onFree pathAgent fixturesLeast privilegeCleanup

Learning objectives

  • Create a disposable Free-compatible fixture that models agent registration, CI authorization, RBAC, Flux reconciliation, and environment evidence.
  • Predict which project/environment may use an agent and which Kubernetes operations should be allowed or denied.
  • Inspect sanitized agent/API evidence without exposing agent tokens or kubeconfig contents.
  • Optionally exercise the trust path on a local disposable Kubernetes cluster.
  • Remove temporary agent/cluster resources and prove no residual access remains.
Availability baseline — verified 2026-08-22 against GitLab 19.3. The GitLab Agent for Kubernetes, agent REST API, agent registration/token management, and the basic CI/CD cluster workflow are available on Free, Premium, and Ultimate across GitLab.com, Self-Managed, and Dedicated. Project/group CI access and environment filters are Free-compatible. CI job impersonation and user impersonation require Premium/Ultimate. User-access integration is currently Beta. The Agent's old built-in pull-based 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. Preflight: mandatory path needs no Kubernetes cluster

Create a local repository named gitlab-ch28-agent-lab. The required path uses YAML/JSON fixtures and local validation only. It does not create a GitLab agent, cloud cluster, runner, token, or real environment. If you already have a disposable GitLab project and a local kind, k3d, or similar cluster, the optional extension shows how to test the same authorization model safely.

Never paste a real agent token into a repository, shell history, issue, lesson evidence, or CI log. If an agent token is exposed, revoke it first, then rotate/reinstall the affected agent credential and only afterwards clean up leaked copies.
mkdir gitlab-ch28-agent-lab && cd gitlab-ch28-agent-lab
git init && git switch -c main
mkdir -p .gitlab/agents/lab-agent fixtures manifests evidence
printf 'chapter-28 training only
' > README.md

2. Predict the trust path before writing YAML

Write these predictions in evidence/predictions.md:

  1. Only jobs from training/platform/app whose GitLab environment is staging or review/* should be candidates for agent access.
  2. A read-only Kubernetes identity should be able to list Pods in ch28-lab and be denied namespace deletion.
  3. A GitLab environment record and a Kubernetes workload must be verified separately.
  4. A Flux reconciliation revision must match the desired manifest revision before calling the GitOps path converged.

3. Model the agent configuration and authorization

Create the exact configuration path that a real agent uses:

# .gitlab/agents/lab-agent/config.yaml
ci_access:
  projects:
    - id: training/platform/app
      environments:
        - review/*
        - staging
# Premium/Ultimate optional hardening:
#      access_as:
#        ci_job: {}

On Free this models project/environment authorization. The commented impersonation block is Premium/Ultimate and should remain disabled in the mandatory lab. Note the trust boundary: this file belongs to the agent configuration project; a broad group authorization would implicitly extend access to descendant projects and deserves stricter review.

4. Model least-privilege Kubernetes RBAC

Create fixtures/rbac.yaml from the following fixture:

apiVersion: v1
kind: Namespace
metadata:
  name: ch28-lab
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: workload-reader
  namespace: ch28-lab
rules:
  - apiGroups: [""]
    resources: ["pods", "configmaps"]
    verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: workload-reader-binding
  namespace: ch28-lab
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: workload-reader
subjects:
  - kind: Group
    name: gitlab:ci_job
    apiGroup: rbac.authorization.k8s.io

The fixture intentionally grants only get/list for Pods and ConfigMaps in one namespace. The subject gitlab:ci_job only becomes meaningful when Premium/Ultimate CI job impersonation is actually enabled. On the Free simulation, treat it as the desired production RBAC contract rather than an active identity.

5. Model a read-only CI job and its denial

Create fixtures/ci-job.yml:

cluster_read:
  stage: verify
  environment:
    name: staging
  script:
    - kubectl config use-context "$AGENT_CONTEXT"
    - kubectl auth can-i get pods -n ch28-lab
    - kubectl get pods -n ch28-lab -o name
    - kubectl auth can-i delete namespaces

The optional live extension assumes a disposable runner/toolchain that already provides a reviewed, pinned kubectl version compatible with the cluster; the fixture deliberately does not pull a mutable latest image. The first two commands are expected to succeed only if the selected context exists and Kubernetes RBAC allows them. The final kubectl auth can-i delete namespaces should print no under the intended least-privilege model. Do not replace auth can-i with an actual namespace delete merely to prove denial.

6. Model current GitOps with Flux

Create fixtures/flux.yaml:

apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
  name: ch28-manifests
  namespace: flux-system
spec:
  interval: 5m
  url: https://gitlab.example.invalid/training/platform/manifests.git
  ref:
    branch: main
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: ch28-staging
  namespace: flux-system
spec:
  interval: 5m
  path: ./clusters/staging
  prune: true
  sourceRef:
    kind: GitRepository
    name: ch28-manifests

The URL is intentionally non-routable. This fixture teaches the object relationships without contacting any real service. Current GitLab guidance recommends Flux for pull-based GitOps; the GitLab Agent is complementary rather than the reconciler itself.

7. Validate the fixtures locally and preserve provenance

python - <<'PY'
from pathlib import Path
for p in [
  Path('.gitlab/agents/lab-agent/config.yaml'),
  Path('fixtures/rbac.yaml'), Path('fixtures/ci-job.yml'), Path('fixtures/flux.yaml')
]:
    assert p.exists() and p.stat().st_size > 0, p
text=Path('.gitlab/agents/lab-agent/config.yaml').read_text()
assert 'environments:' in text and 'staging' in text
assert 'access_as:' not in '
'.join(line for line in text.splitlines() if not line.lstrip().startswith('#'))
print('fixture structure: PASS')
PY
git add . && git commit -m "Add Chapter 28 Kubernetes trust fixtures"
git rev-parse HEAD
sha256sum .gitlab/agents/lab-agent/config.yaml fixtures/rbac.yaml fixtures/flux.yaml

The Git commit SHA and file hashes make the lab reproducible. They are evidence about configuration identity, not evidence that any cluster accepted the configuration.

8. Optional disposable live extension

Only if you already have a disposable GitLab project and a local cluster:

  1. Create/register a uniquely named agent in the disposable project. Creating an agent/token requires Maintainer/Owner.
  2. Create one token, store it only in the installation mechanism, and never echo it. An agent can have at most two active tokens.
  3. Install agentk with the current supported Helm/glab workflow into the local cluster.
  4. Apply a dedicated ch28-lab namespace and minimal RBAC.
  5. Authorize only the disposable project/environment. Wait for authorization propagation.
  6. Run a tiny read-only job or local access test and record only resource names, auth can-i results, agent ID, job ID, and commit SHA.
  7. Revoke the agent token, delete the GitLab agent registration, uninstall agentk, and remove the namespace. Deleting the GitLab agent does not remove cluster-side resources automatically.
Runner trust: do not route production cluster credentials through a persistent shared or untrusted runner. The Agent reduces kubeconfig distribution but does not make an unsafe runner trustworthy.

9. Correlate GitLab environment and cluster identity

Use a sanitized evidence record rather than copying full kubeconfig or Pod environment variables:

{
  "gitlab": {
    "project": "training/platform/app",
    "commit_sha": "0123456789abcdef...",
    "pipeline_id": 4201,
    "job_id": 4207,
    "environment": "staging",
    "agent": "training/platform/agent-config:lab-agent"
  },
  "kubernetes": {
    "namespace": "ch28-lab",
    "workload": "deployment/ch28-demo",
    "image_digest": "sha256:111111...",
    "observed_generation": 3
  },
  "gitops": {
    "controller": "Flux",
    "desired_revision": "main@sha1:012345...",
    "ready": true
  }
}

The point is causality: source/ref, pipeline/job, environment, image digest, controller revision, and observed workload must align. A UI badge alone is not sufficient.

10. Control-selection challenge

Choose the smallest correct control for each requirement:

Requirement Best surface
Allow only staging/review jobs in one project to obtain agent context ci_access.projects[].environments
Give each CI job a Kubernetes-native identity for RBAC Premium/Ultimate access_as: ci_job + Kubernetes RBAC
Continuously reconcile desired manifests without pipeline push access Flux pull-based GitOps
Prove a job may list Pods but cannot delete namespaces kubectl auth can-i with the effective context

11. Cleanup and verification

For the mandatory local fixture, keep sanitized evidence only if useful, then delete the disposable directory from its parent. For an optional live exercise, cleanup order matters: revoke/rotate credentials first if compromised; otherwise revoke the lab token, remove the GitLab agent registration, uninstall agentk/Flux resources that were created only for the lab, delete the lab namespace, and verify each resource is gone. Do not infer that deleting the agent in GitLab cleaned the cluster.

Knowledge check

Why is the mandatory path fixture-based?

Why use kubectl auth can-i instead of deliberately deleting a protected object?

If you delete the GitLab agent registration, is agentk automatically removed from Kubernetes?

What current controller performs GitOps reconciliation in this chapter?

What should be stored as lab evidence?

12. Summary and next bridge

You have now modeled the entire trust path without requiring a live cluster. Lesson 3 turns those mechanics into architecture choices: push versus pull, broad versus narrow authorization, and shared versus isolated cluster designs.

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.

Next lesson

Configuration, Design Choices, and Tradeoffs

Choose between agent and stored kubeconfig, CI push and Flux GitOps, shared and isolated cluster models, and broad versus narrow access.

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.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this address.