4.3 Restrict build-pod network access

Your source now names internal inputs, and your pipeline is wired to prepare a Maven cache. Before running it, replace the build namespace’s allow-all egress policy with explicit internal destinations. Continue in the Showroom terminal; the policy applies to build pods, not to your terminal.

Why this control exists

Source configuration expresses the intended fetch path. NetworkPolicy adds a separate control over pod traffic, so a command cannot silently use an unlisted public endpoint. The workshop allows Nexus for prefetch and uses Maven’s offline flag during the image build. This is a constrained workshop build path, not a physically air-gapped cluster.

NetworkPolicies are additive: any policy selecting a pod can grant egress. Tightening build-egress is insufficient if another selected policy still allows everything. Inspect all policies in the build namespace as part of your review.

Build pods may reach DNS

Learning objectives

By the end of this module, you will be able to:

  • Replace the build namespace’s allow-all egress with explicit destinations.

  • Match namespace selectors and destination ports to the cluster configuration.

  • Discover the Kubernetes API addresses needed by the build Task.

  • Explain the policy’s scope and inspect other policies that could grant access.

Record the starting policy

Discover the build namespace and save a new evidence directory for this attempt. Expect the initial build-egress to contain egress: [{}], which allows all destinations. If you already tightened it, preserve your work and compare it with the criteria below.

BUILD_NS="$(oc -n gitea get configmap lab-gitea -o jsonpath='{.data.student_build_namespace}')"
mkdir -p ~/lightwell-build-evidence
NP_EVIDENCE="$(mktemp -d "$HOME/lightwell-build-evidence/network.XXXXXX")"
oc -n "$BUILD_NS" get networkpolicy build-egress -o yaml > "$NP_EVIDENCE/before.yaml"
cat "$NP_EVIDENCE/before.yaml"
oc -n "$BUILD_NS" get configmap lab-networkpolicy -o yaml
oc -n "$BUILD_NS" get networkpolicy

Exercise

Identify the allowed destinations

Use ConfigMap lab-networkpolicy for the namespaces and ports. The table lists the workshop defaults and why each destination is needed. OpenShift matches destination pod ports after service translation; for example, Central’s Service port 443 maps to pod port 8443.

Destination Protocol and port Purpose

openshift-dns

UDP and TCP 5353

Resolve service names

openshift-image-registry

TCP 5000

Access internal images

lightwell-repo

TCP 8081, 5000

Nexus Maven and destination registry

gitea

TCP 3000

Clone application source internally

Kubernetes API ClusterIP and endpoints

TCP 443 and 6443, respectively

Start and inspect BuildConfig builds

stackrox

TCP 8443

Pipeline ACS checks

trusted-artifact-signer

TCP 8080, 5555, 5554, 3000

TUF, Fulcio HTTP/gRPC, and Rekor

trusted-profile-analyzer

TCP 8080

Later TPA access

Discover the API ClusterIP and endpoint addresses. These vary by claim. The API endpoints are host-network control-plane addresses, so a namespace selector alone does not cover them.

oc -n default get svc kubernetes \
  -o jsonpath='clusterIP={.spec.clusterIP} port={.spec.ports[0].port}{"\n"}'
oc -n default get endpoints kubernetes \
  -o jsonpath='{.subsets[*].addresses[*].ip}{" "}{.subsets[*].ports[*].port}{"\n"}'

Author the build policy

Create ~/lightwell-build-evidence/build-egress.yaml in your editor. Begin with the following structure. Set metadata.namespace to your discovered build namespace. This is a partial policy: add all remaining table destinations before applying it.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: build-egress
  namespace: lw-poc-build
spec:
  podSelector: {}
  policyTypes: [Egress]
  egress:
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: openshift-dns
      ports:
        - protocol: UDP
          port: 5353
        - protocol: TCP
          port: 5353

For each remaining named namespace, add another egress entry with its own namespaceSelector and TCP ports from the table. Keep each namespace paired with its ports. For the API, add one entry with an ipBlock.cidr for the ClusterIP and port 443, then entries for each endpoint address and port 6443. Use /32 for each IPv4 address (/128 for IPv6). Do not add a broad address range or an empty rule.

Namespace selectors allow matching pods in the named namespace on the listed ports; they do not identify one specific Service. Keep podSelector: {} so the policy selects every pod in the build namespace. Remove the seed’s empty egress rule entirely. Keep public registries, public DNS, and the ingress namespace off this allow-list. Build tasks use the internal Gitea, Nexus, and signing-service URLs.

Apply and inspect your policy

Validate the authored object with the API before applying it. A successful dry run checks the object’s schema, not whether every required connection will work. Apply only after comparing each destination with the table and discovered values.

oc -n "$BUILD_NS" apply --dry-run=server -f ~/lightwell-build-evidence/build-egress.yaml
oc -n "$BUILD_NS" apply -f ~/lightwell-build-evidence/build-egress.yaml
oc -n "$BUILD_NS" get networkpolicy build-egress -o yaml > "$NP_EVIDENCE/after.yaml"
diff -u "$NP_EVIDENCE/before.yaml" "$NP_EVIDENCE/after.yaml"

diff returns status 1 when it finds differences; that is expected here. The meaningful change is the explicit destination and port list replacing allow-all egress.

Review

Inspect all policies in the build namespace, including their selectors. Confirm there is no remaining allow-all rule that selects build pods. Check that build-egress has not been copied into staging or production and that their app-operate policies remain present. Track 6 owns application egress.

oc -n "$BUILD_NS" get networkpolicy -o yaml
oc -n lw-poc-staging get networkpolicy
oc -n lw-poc-prod get networkpolicy

These observations establish the policy configuration. Module 4.4 tests the intended build path and records a negative build attempt. If clone or prefetch fails there, check the internal URL, matching namespace, and pod port before changing a rule.

Kubelet image pulls occur on nodes and are outside this pod-egress policy. Task images can therefore still come from public registries. A public image-pull failure alone does not prove NetworkPolicy enforcement; inspect the failing operation and its logs. The Dockerfile’s internal references and Maven offline mode remain separate controls.

Carry the result into module 4.4

Keep the before/after policy evidence. Next run the committed pipeline and correlate its task results with the produced image and SBOM.

Official documentation