OpenShift: pull runtime image from the internal ATT GitLab registry

- runtime/03-deployment.yaml: image is now
  att-gitlab.apps.evd.geaerospace.net:5050/att/infrastructure/vshell:latest with
  imagePullSecrets; the internal ImageStream trigger is gone
- runtime/00-serviceaccount.yaml: carries att-gitlab-pull
- runtime/06-att-gitlab-pull-secret.yaml: registry credentials (placeholders)
- cluster/00-insecure-registry.yaml: Image/cluster insecureRegistries entry, the
  cluster-side equivalent of 'podman pull -k'
- 02/03 build manifests marked optional, with the GitLab push-output variant
  documented inline
- openshift/README.md split into run-from-registry and build-in-cluster paths
- all 14 manifests decode cleanly with 'oc create --dry-run=client'
This commit is contained in:
mward4 committed 2026-10-05 14:24:29 -04:00
1 parent 9dbe3749ba
commit eab245f61d
8 files changed
+164 -71

No files matched your search

+5 -3
View File
@@ -98,9 +98,11 @@ HTTP→HTTPS redirect, after checking `-version`, `-test_config` and `-license`.
## OpenShift
`openshift/` holds drop-in manifests that build this image in-cluster
(`BuildConfig` + `ImageStream` + Gitea source secret) and, optionally, run it
(custom SCC, PVC, Deployment, Service, passthrough Route). See
`openshift/` holds drop-in manifests. The runtime Deployment pulls
`att-gitlab.apps.evd.geaerospace.net:5050/att/infrastructure/vshell:latest` from
the internal GitLab registry (pull secret, custom SCC, PVC, Service, passthrough
Route, plus a cluster-scoped insecure-registry entry for the `-k` equivalent);
the `BuildConfig`/`ImageStream` chain for building in-cluster is optional. See
[openshift/README.md](openshift/README.md) for the apply order.
## Licensing
+3 -2
View File
@@ -1,5 +1,6 @@
# Output image stream for the built image. The build pushes vshell:latest here
# and the runtime Deployment pulls from it.
# Output image stream for in-cluster builds (03-buildconfig.yaml).
# Only needed if OpenShift builds the image; the runtime Deployment pulls
# att-gitlab.apps.evd.geaerospace.net:5050/att/infrastructure/vshell:latest instead.
apiVersion: image.openshift.io/v1
kind: ImageStream
metadata:
+20 -4
View File
@@ -1,12 +1,18 @@
# In-cluster build of the VShell image.
#
# The repository already vendors the vendor .deb, so the only things the build
# pod fetches from the network are:
# OPTIONAL when you already have the image in the internal GitLab registry:
# runtime/03-deployment.yaml pulls
# att-gitlab.apps.evd.geaerospace.net:5050/att/infrastructure/vshell:latest
# so this BuildConfig and 02-vshell-imagestream.yaml are only needed if you want
# OpenShift to build the image too.
#
# The repository vendors the vendor .deb, so the only things the build pod
# fetches from the network are:
# - gitea.milesward.dev (this git repo, authenticated by gitea-vshell-source)
# - registry-1.docker.io (base image ubuntu:24.04)
# - archive.ubuntu.com (apt-get install openssl in the Dockerfile)
# On an air-gapped cluster, mirror ubuntu:24.04 and openssl into an internal
# registry first; see 04-base-imagestream.yaml and openshift/README.md.
# On an air-gapped cluster, mirror ubuntu:24.04 and openssl first; see
# 04-base-imagestream.yaml.
apiVersion: build.openshift.io/v1
kind: BuildConfig
metadata:
@@ -32,10 +38,20 @@ spec:
name: docker.io/library/ubuntu:24.04
# Pin the base by digest instead of tag when you need reproducible builds:
# name: docker.io/library/ubuntu@sha256:a60ef8ec184edb5ed91f5983f00261359fb2fe20f603e8d488b9477d32fcad70
# Default: push the build into the in-cluster image stream (02-vshell-imagestream.yaml).
output:
to:
kind: ImageStreamTag
name: vshell:latest
# Alternative: push the build straight to the internal GitLab registry, so the
# Deployment picks it up with imagePullPolicy: Always. Needs push credentials
# on the builder service account, and the nodes must trust the registry
# (cluster/00-insecure-registry.yaml):
# oc create secret docker-registry att-gitlab-push -n vshell \
# --docker-server=att-gitlab.apps.evd.geaerospace.net:5050 \
# --docker-username=<user> --docker-password=<token>
# oc secrets link builder att-gitlab-push -n vshell
# oc patch bc/vshell -n vshell --type=merge -p '{"spec":{"output":{"to":{"kind":"DockerImage","name":"att-gitlab.apps.evd.geaerospace.net:5050/att/infrastructure/vshell:latest"}}}}'
resources:
requests:
cpu: 500m
+67 -53
View File
@@ -1,76 +1,90 @@
# OpenShift manifests for VShell
Everything needed to build `vshell:latest` inside an OpenShift project and, if you
want, to run it there. Write-tested against the OpenShift 4.22 client scheme and
matching the 4.21 cluster described in `/workspace/openshift-win11/FINDINGS.md`.
Two independent halves:
## Build (required)
- **run** (the normal path): pull `att-gitlab.apps.evd.geaerospace.net:5050/att/infrastructure/vshell:latest`
from the internal ATT GitLab registry and run it in the `vshell` project.
- **build** (optional): build the image inside OpenShift from the private Gitea
repo, for when you want the cluster to produce the image itself.
All manifests have been decode-tested with `oc create --dry-run=client` against
the OpenShift 4.22 client scheme.
## Run from the internal registry
| File | What it does |
|---|---|
| [`00-namespace.yaml`](00-namespace.yaml) | Project `vshell`. |
| [`cluster/00-insecure-registry.yaml`](cluster/00-insecure-registry.yaml) | Cluster-scoped: mark `att-gitlab.apps.evd.geaerospace.net:5050` insecure (the `-k` equivalent). |
| [`runtime/00-serviceaccount.yaml`](runtime/00-serviceaccount.yaml) | `ServiceAccount/vshell`, carries the pull secret. |
| [`runtime/01-scc.yaml`](runtime/01-scc.yaml) | Least-privilege SCC allowing UID 0 + Role/RoleBinding so only this SA may use it. |
| [`runtime/02-pvc.yaml`](runtime/02-pvc.yaml) | `PersistentVolumeClaim/vshell-data` for config + TLS certificate. |
| [`runtime/03-deployment.yaml`](runtime/03-deployment.yaml) | Deployment pulling from the GitLab registry. |
| [`runtime/04-service.yaml`](runtime/04-service.yaml) | ClusterIP service for ports 22/21/990/80/443. |
| [`runtime/05-route.yaml`](runtime/05-route.yaml) | HTTPS Route, **passthrough** termination. |
| [`runtime/06-att-gitlab-pull-secret.yaml`](runtime/06-att-gitlab-pull-secret.yaml) | Registry credentials (placeholders - create from the CLI). |
```sh
# 1. registry credentials (deploy token / PAT with read_registry)
oc create secret docker-registry att-gitlab-pull -n vshell \
--docker-server=att-gitlab.apps.evd.geaerospace.net:5050 \
--docker-username=<user> --docker-password=<token>
# 2. let the nodes pull from it over plain/insecure TLS (cluster-admin; rolls all nodes)
oc apply -f cluster/00-insecure-registry.yaml
# 3. project + workload
oc apply -f 00-namespace.yaml
oc apply -f runtime/
oc get pods,route -n vshell
```
`cluster/00-insecure-registry.yaml` is the pod-world replacement for
`podman pull -k`: a pod spec cannot opt out of TLS verification, so the node CRI
must be told the registry is exempt. Editing `Image/cluster` makes the Machine
Config Operator drain and reboot every node — do it in a maintenance window, and
remember the rollback rolls them again.
If the cluster sets `registrySources.allowedRegistries`, add
`att-gitlab.apps.evd.geaerospace.net:5050` **and** the internal registry
`image-registry.openshift-image-registry.svc:5000` to that list, or other pulls
start failing. If the registry has a private CA, prefer
`spec.additionalTrustedCA` over `insecureRegistries`.
`restricted-v2` forbids UID 0 and VShell needs root (PAM reads `/etc/shadow`,
writes `/var/run/*.pid`, creates root-owned host keys), hence the SCC — or
`oc adm policy add-scc-to-user anyuid -z vshell -n vshell` for the stock one.
The Route is passthrough because `vshell-httpsd` terminates TLS itself with the
PKCS#12 certificate the entrypoint generates; SSH (22), explicit FTPS (21) and
implicit FTPS (990) cannot use a Route, so expose the Service with a
NodePort/LoadBalancer. Never scale the Deployment past one replica: fixed ports
and a single host key.
## Build in OpenShift (optional)
| File | What it does |
|---|---|
| [`01-gitea-source-secret.yaml`](01-gitea-source-secret.yaml) | `kubernetes.io/basic-auth` secret for the private Gitea repo. |
| [`02-vshell-imagestream.yaml`](02-vshell-imagestream.yaml) | Output `ImageStream/vshell` (`latest`). |
| [`03-buildconfig.yaml`](03-buildconfig.yaml) | `BuildConfig/vshell` — Docker strategy, git source, `ubuntu:24.04` base. |
| [`04-base-imagestream.yaml`](04-base-imagestream.yaml) | Optional: keep the Ubuntu base inside the cluster instead of pulling Docker Hub on every build. |
| [`03-buildconfig.yaml`](03-buildconfig.yaml) | `BuildConfig/vshell` — Docker strategy, git source, `ubuntu:24.04` base. Can also be pointed at the GitLab registry (see the comments in the file). |
| [`04-base-imagestream.yaml`](04-base-imagestream.yaml) | Optional: keep the Ubuntu base inside the cluster instead of pulling Docker Hub each build. |
| [`05-dockerhub-pull-secret.yaml`](05-dockerhub-pull-secret.yaml) | Optional: Docker Hub credentials so base pulls are not rate limited. |
```sh
oc apply -f 00-namespace.yaml
# password = the Gitea token from ~/.dsh/AGENTS.md (do not commit it into 01-*.yaml)
oc create secret generic gitea-vshell-source -n vshell \
--type=kubernetes.io/basic-auth \
--from-literal=username=mward4 \
--from-literal=password=<gitea-token>
oc apply -f 02-vshell-imagestream.yaml
oc apply -f 03-buildconfig.yaml
oc apply -f 02-vshell-imagestream.yaml -f 03-buildconfig.yaml
oc start-build vshell -n vshell --follow
oc get is vshell -n vshell
```
The repository vendors the vendor `.deb`, so the build pod only needs egress to
`gitea.milesward.dev`, `registry-1.docker.io` and `archive.ubuntu.com`
(`apt-get install openssl`). Air-gapped clusters: import `ubuntu:24.04` with
`04-base-imagestream.yaml`, point `strategy.dockerStrategy.from` at
`ImageStreamTag ubuntu:24.04`, and add an internal apt mirror.
`replicas: 1` is not a build concern but a runtime one: the daemons own fixed
ports and a single host key, so never scale the Deployment.
## Runtime (optional)
| File | What it does |
|---|---|
| [`runtime/00-serviceaccount.yaml`](runtime/00-serviceaccount.yaml) | `ServiceAccount/vshell`. |
| [`runtime/01-scc.yaml`](runtime/01-scc.yaml) | Least-privilege SCC that allows UID 0 + Role/RoleBinding so only this SA may use it. |
| [`runtime/02-pvc.yaml`](runtime/02-pvc.yaml) | `PersistentVolumeClaim/vshell-data` for config + TLS certificate. |
| [`runtime/03-deployment.yaml`](runtime/03-deployment.yaml) | Deployment, auto-updated by the `image.openshift.io/triggers` annotation. |
| [`runtime/04-service.yaml`](runtime/04-service.yaml) | ClusterIP service for ports 22/21/990/80/443. |
| [`runtime/05-route.yaml`](runtime/05-route.yaml) | HTTPS Route, **passthrough** termination. |
```sh
oc apply -f runtime/
oc get route vshell-https -n vshell
```
`restricted-v2` forbids UID 0, so the SCC (or `oc adm policy add-scc-to-user anyuid -z vshell -n vshell`)
is required — VShell reads `/etc/shadow` through PAM, writes `/var/run/*.pid`
and creates root-owned host keys. The custom SCC grants no extra capabilities
and no host access.
The Route is passthrough because `vshell-httpsd` terminates TLS itself with the
PKCS#12 certificate the entrypoint generated; clients therefore see a
self-signed certificate until you mount a real one at `$VSHELL_TLS_P12`. SSH
(22), explicit FTPS (21) and implicit FTPS (990) cannot go through a Route —
expose the Service with a NodePort/LoadBalancer or an external load balancer.
The Deployment points `VSHELL_CONFIG` and `VSHELL_TLS_P12` at the PVC-mounted
`/data`, which keeps the vendor defaults and the `HttpsFiles/` web assets
visible. The SSH host key stays at `/usr/local/etc/vshelld_hostkey` unless you
set `HostKeyFilenames /data/vshelld_hostkey` in the generated config.
The repository vendors the vendor `.deb`, so a build pod needs egress only to
`gitea.milesward.dev`, `registry-1.docker.io` and `archive.ubuntu.com`. For
air-gapped clusters import `ubuntu:24.04` with `04-base-imagestream.yaml`, point
`strategy.dockerStrategy.from` at `ImageStreamTag ubuntu:24.04`, and add an
internal apt mirror.
## Caveats
@@ -0,0 +1,28 @@
# Insecure-registry configuration for the internal ATT GitLab registry.
#
# This is the cluster equivalent of `podman pull -k` / `skopeo --src-tls-verify=false`.
# A pod spec cannot skip TLS verification, so the node container runtime has to be
# told the registry is exempt. Cluster-scoped: requires cluster-admin.
#
# WARNING: editing spec.registrySources makes the Machine Config Operator roll
# every node (drain + reboot) to apply the new CRI config. Do it in a maintenance
# window. Reverting this file rolls the nodes again.
#
# If your cluster also sets spec.registrySources.allowedRegistries (a whitelist),
# that list must then contain att-gitlab.apps.evd.geaerospace.net:5050 AND the
# OpenShift internal registry (image-registry.openshift-image-registry.svc:5000)
# AND any other registry the cluster pulls from, or pulls start failing.
#
# Preferred over insecure TLS when the registry has a private CA: keep the CA in
# a ConfigMap in the openshift-config namespace and reference it instead:
# spec:
# additionalTrustedCA:
# name: att-gitlab-ca
apiVersion: config.openshift.io/v1
kind: Image
metadata:
name: cluster
spec:
registrySources:
insecureRegistries:
- att-gitlab.apps.evd.geaerospace.net:5050
+4 -1
View File
@@ -1,6 +1,7 @@
# Service account the VShell pod runs as. It is bound to the custom SCC in
# 01-scc.yaml, because VShell must run as root (PAM reads /etc/shadow, the
# daemons write /var/run/*.pid and generate host keys owned by root).
# daemons write /var/run/*.pid and generate host keys owned by root), and it
# carries the pull secret for the internal ATT GitLab registry.
apiVersion: v1
kind: ServiceAccount
metadata:
@@ -8,3 +9,5 @@ metadata:
namespace: vshell
labels:
app.kubernetes.io/name: vshell
imagePullSecrets:
- name: att-gitlab-pull
+13 -8
View File
@@ -1,8 +1,14 @@
# VShell runtime. Single replica: the daemons own fixed ports and one host key,
# so they are not horizontally scalable.
# VShell runtime, pulling the image from the internal ATT GitLab registry.
#
# The image tag is kept in sync by the image.openshift.io/triggers annotation,
# which fires whenever a new vshell:latest build finishes.
# Single replica: the daemons own fixed ports and one host key, so they are not
# horizontally scalable.
#
# This registry is outside OpenShift, so three things make the pull work:
# 1. 06-att-gitlab-pull-secret.yaml (credentials, linked via the ServiceAccount)
# 2. cluster/00-insecure-registry.yaml on the nodes - the registry equivalent
# of `podman pull -k`, since a pod spec cannot opt out of TLS verification
# 3. DNS/route reachability of att-gitlab.apps.evd.geaerospace.net:5050 from
# every node
apiVersion: apps/v1
kind: Deployment
metadata:
@@ -10,9 +16,6 @@ metadata:
namespace: vshell
labels:
app.kubernetes.io/name: vshell
annotations:
image.openshift.io/triggers: |
[{"from":{"kind":"ImageStreamTag","name":"vshell:latest"},"fieldPath":"spec.template.spec.containers[0].image"}]
spec:
replicas: 1
strategy:
@@ -26,13 +29,15 @@ spec:
app.kubernetes.io/name: vshell
spec:
serviceAccountName: vshell
imagePullSecrets:
- name: att-gitlab-pull
securityContext:
runAsUser: 0
runAsGroup: 0
fsGroup: 0
containers:
- name: vshell
image: vshell:latest
image: att-gitlab.apps.evd.geaerospace.net:5050/att/infrastructure/vshell:latest
imagePullPolicy: Always
env:
- name: VSHELL_CONFIG
@@ -0,0 +1,24 @@
# Credentials for the internal ATT GitLab container registry.
#
# Create it with your own GitLab credentials (deploy token, PAT with read_registry,
# or a robot account) - do not commit them:
#
# oc create secret docker-registry att-gitlab-pull -n vshell \
# --docker-server=att-gitlab.apps.evd.geaerospace.net:5050 \
# --docker-username=<user> \
# --docker-password=<token>
#
# Already referenced by runtime/00-serviceaccount.yaml and runtime/03-deployment.yaml,
# so nothing else needs to be linked.
apiVersion: v1
kind: Secret
metadata:
name: att-gitlab-pull
namespace: vshell
labels:
app.kubernetes.io/name: vshell
app.kubernetes.io/component: registry-auth
type: kubernetes.io/dockerconfigjson
stringData:
.dockerconfigjson: |
{"auths":{"att-gitlab.apps.evd.geaerospace.net:5050":{"username":"REPLACE","password":"REPLACE","auth":"REPLACE_BASE64_USER_COLON_TOKEN"}}}