Mirror push failed "unauthorized to access repository: base-images/ golang, action: push" — harbor-robot-dockerconfig is scoped only to the homelab project's robot account, nothing on base-images. Making base-images public only grants anonymous pull, never push. Removed the read-only docker-config secret mount (it would have blocked `docker login` from writing new credentials anyway) and switched to logging in interactively with Harbor admin credentials instead, since this whole mirror is a rarely-run manual task, not worth provisioning a dedicated robot account for.
4.6 KiB
devops-base-images
One-time-setup repo for mirroring minimized base images into Harbor's
base-images project, so language builds (via devops-lib's
buildDocker.groovy fallback templates) never depend on Docker Hub at
build time, and the images that actually ship are the leanest official
variant available for each language.
Not wired into any Jenkins pipeline — this is deliberately a manual,
occasional operation (re-run when images.txt changes: a new language,
a version bump, or picking up an upstream base-image update), using the
same one-off kubectl-based DinD pod pattern devops-lib's
build-tools.Dockerfile bootstrap already uses, since the VM itself may
not have Docker installed directly.
One-time setup
1. Create the Harbor project (public — these are just re-hosted public images, no confidentiality concern, and public avoids needing any pull credentials wired into every build):
Harbor UI → New Project → name base-images → check Public → Create.
Or via API:
curl -X POST -u admin:'<harbor admin password>' -H "Content-Type: application/json" \
-d '{"project_name":"base-images","metadata":{"public":"true"}}' \
"http://harbor.192.168.1.7.nip.io/api/v2.0/projects"
2. Run the mirror, via a throwaway DinD pod (same shape as the
build-tools image bootstrap):
cat <<'EOF' | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: base-images-mirror
namespace: jenkins
data:
images.txt: |
golang:1.22-alpine golang:1.22-alpine
alpine:3.20 alpine:3.20
node:20-alpine node:20-alpine
python:3.12-alpine python:3.12-alpine
maven:3-eclipse-temurin-21-alpine maven:3-eclipse-temurin-21-alpine
eclipse-temurin:21-jre-alpine eclipse-temurin:21-jre-alpine
php:8.3-cli-alpine php:8.3-cli-alpine
mirror.sh: |
#!/bin/sh
set -eu
REGISTRY="harbor.192.168.1.7.nip.io"
PROJECT="base-images"
while read -r src target; do
[ -z "$src" ] && continue
case "$src" in \#*) continue ;; esac
dest="${REGISTRY}/${PROJECT}/${target}"
echo "=== ${src} -> ${dest} ==="
docker pull "$src"
docker tag "$src" "$dest"
docker push "$dest"
done < images.txt
echo "=== done ==="
---
apiVersion: v1
kind: Pod
metadata:
name: base-images-mirror
namespace: jenkins
spec:
restartPolicy: Never
containers:
- name: docker
image: docker:27-dind
securityContext:
privileged: true
args:
- "--insecure-registry=harbor.192.168.1.7.nip.io"
env:
- name: DOCKER_TLS_CERTDIR
value: ""
volumeMounts:
- name: docker-graph-storage
mountPath: /var/lib/docker
- name: docker-cli
image: docker:27-cli
command: ["cat"]
tty: true
env:
- name: DOCKER_HOST
value: tcp://localhost:2375
volumeMounts:
- name: mirror
mountPath: /mirror
volumes:
- name: docker-graph-storage
emptyDir: {}
- name: mirror
configMap:
name: base-images-mirror
defaultMode: 0755
EOF
No harbor-robot-dockerconfig mount here on purpose — that robot
account is scoped only to the homelab project (push+pull there
specifically); it has no grant on base-images at all, and making
base-images public only grants anonymous pull, never push. Push
always needs real credentials scoped to that project regardless. For a
one-off manual task like this, simplest is just logging in with your
Harbor admin account directly inside the pod, rather than provisioning
a whole new robot account for something run this rarely:
kubectl wait --for=condition=Ready pod/base-images-mirror -n jenkins --timeout=120s
kubectl exec -it -n jenkins base-images-mirror -c docker-cli -- sh -c '
for i in $(seq 1 30); do docker info >/dev/null 2>&1 && break; sleep 2; done
docker login harbor.192.168.1.7.nip.io
'
(enter your Harbor admin username/password when prompted)
kubectl exec -n jenkins base-images-mirror -c docker-cli -- sh -c '
cd /mirror && sh mirror.sh
'
kubectl delete pod base-images-mirror -n jenkins
kubectl delete configmap base-images-mirror -n jenkins
3. Verify:
curl -s http://harbor.192.168.1.7.nip.io/v2/_catalog
Should list base-images/golang, base-images/alpine,
base-images/node, base-images/python,
base-images/maven, base-images/eclipse-temurin, base-images/php.
Adding a new image / updating a version
Add a line to images.txt, commit, push, then re-run step 2 above (the
ConfigMap's images.txt needs updating to match — copy the current file
content in, same as the initial setup).