From 36171bcda49dbd3215ea94773ff260419e8ac632 Mon Sep 17 00:00:00 2001 From: Viktor Barzin Date: Sun, 22 Mar 2026 22:10:10 +0200 Subject: [PATCH] add htpasswd auth to private docker registry + expose at registry.viktorbarzin.me MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add auth.htpasswd section to config-private.yml - Mount htpasswd file in registry-private container, fix healthcheck for 401 - Rename registry UI from registry.viktorbarzin.me → docker.viktorbarzin.me - Add Docker CLI ingress at registry.viktorbarzin.me (HTTPS backend, no rate-limit, unlimited body) - Add docker to cloudflare_proxied_names (registry stays non-proxied) - Add Kyverno ClusterPolicy to sync registry-credentials secret to all namespaces - Update infra provisioning to install apache2-utils and generate htpasswd from Vault --- config.tfvars | Bin 9875 -> 9885 bytes modules/docker-registry/config-private.yml | 4 + modules/docker-registry/docker-compose.yml | 4 +- stacks/infra/main.tf | 14 ++- .../modules/kyverno/registry-credentials.tf | 83 ++++++++++++++++++ .../modules/reverse_proxy/main.tf | 23 ++++- 6 files changed, 123 insertions(+), 5 deletions(-) create mode 100644 stacks/kyverno/modules/kyverno/registry-credentials.tf diff --git a/config.tfvars b/config.tfvars index 804ef7b8029985093982ff226602c906006f54cc..9ec90c1f01c7c683f53dbe3967b365f3bf4ac04a 100644 GIT binary patch literal 9885 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 PBXJzlAImwsW~|rTOXFT0 literal 9875 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/dev/null 2>&1"] + # 401 is expected (auth required) — any HTTP response means the registry is healthy + test: ["CMD", "sh", "-c", "wget -qS -O /dev/null http://localhost:5000/v2/ 2>&1 | grep -q 'HTTP/'"] interval: 30s timeout: 10s retries: 3 diff --git a/stacks/infra/main.tf b/stacks/infra/main.tf index 8fd1d899..a27778c2 100644 --- a/stacks/infra/main.tf +++ b/stacks/infra/main.tf @@ -21,6 +21,11 @@ data "vault_kv_secret_v2" "secrets" { name = "infra" } +data "vault_kv_secret_v2" "viktor" { + mount = "secret" + name = "viktor" +} + # --------------------------------------------------------------------------- # Locals # --------------------------------------------------------------------------- @@ -176,8 +181,8 @@ module "docker-registry-template" { # Setup registry config and start container provision_cmds = [ - # Install and enable QEMU guest agent for remote management - "apt-get install -y qemu-guest-agent", + # Install dependencies (QEMU guest agent + htpasswd for registry auth) + "apt-get install -y qemu-guest-agent apache2-utils", "systemctl enable qemu-guest-agent", "systemctl start qemu-guest-agent", # Stop host nginx — we run nginx inside Docker instead @@ -185,6 +190,11 @@ module "docker-registry-template" { "systemctl disable nginx || true", # Create directory structure "mkdir -p /opt/registry/data/dockerhub /opt/registry/data/ghcr /opt/registry/data/quay /opt/registry/data/k8s /opt/registry/data/kyverno /opt/registry/data/private /opt/registry/tls", + # Generate htpasswd file for private registry authentication + format("htpasswd -Bbn %s %s > /opt/registry/htpasswd", + data.vault_kv_secret_v2.viktor.data["registry_user"], + data.vault_kv_secret_v2.viktor.data["registry_password"] + ), # Write Docker Compose file format("echo %s | base64 -d > /opt/registry/docker-compose.yml", base64encode(file("${path.root}/../../modules/docker-registry/docker-compose.yml")) diff --git a/stacks/kyverno/modules/kyverno/registry-credentials.tf b/stacks/kyverno/modules/kyverno/registry-credentials.tf new file mode 100644 index 00000000..feded5b3 --- /dev/null +++ b/stacks/kyverno/modules/kyverno/registry-credentials.tf @@ -0,0 +1,83 @@ + +# ============================================================================= +# Private Docker Registry Credentials — Auto-sync to all namespaces +# ============================================================================= +# Source secret in kyverno namespace, cloned by ClusterPolicy into every NS. +# Pods use imagePullSecrets: [{name: registry-credentials}] to pull from +# registry.viktorbarzin.me (or 10.0.20.10:5050 internally). + +data "vault_kv_secret_v2" "viktor" { + mount = "secret" + name = "viktor" +} + +resource "kubernetes_secret" "registry_credentials" { + metadata { + name = "registry-credentials" + namespace = kubernetes_namespace.kyverno.metadata[0].name + } + type = "kubernetes.io/dockerconfigjson" + data = { + ".dockerconfigjson" = jsonencode({ + auths = { + "registry.viktorbarzin.me" = { + auth = base64encode("${data.vault_kv_secret_v2.viktor.data["registry_user"]}:${data.vault_kv_secret_v2.viktor.data["registry_password"]}") + } + "10.0.20.10:5050" = { + auth = base64encode("${data.vault_kv_secret_v2.viktor.data["registry_user"]}:${data.vault_kv_secret_v2.viktor.data["registry_password"]}") + } + } + }) + } +} + +resource "kubernetes_manifest" "sync_registry_credentials" { + manifest = { + apiVersion = "kyverno.io/v1" + kind = "ClusterPolicy" + metadata = { + name = "sync-registry-credentials" + } + spec = { + rules = [ + { + name = "sync-registry-secret" + match = { + any = [ + { + resources = { + kinds = ["Namespace"] + } + } + ] + } + exclude = { + any = [ + { + resources = { + namespaces = ["kube-system", "kube-public", "kube-node-lease"] + } + } + ] + } + generate = { + apiVersion = "v1" + kind = "Secret" + name = "registry-credentials" + namespace = "{{request.object.metadata.name}}" + synchronize = true + clone = { + namespace = "kyverno" + name = "registry-credentials" + } + } + } + ] + } + } + + depends_on = [ + helm_release.kyverno, + kubernetes_secret.registry_credentials, + ] +} diff --git a/stacks/reverse-proxy/modules/reverse_proxy/main.tf b/stacks/reverse-proxy/modules/reverse_proxy/main.tf index e6dcc34b..c30aa01c 100644 --- a/stacks/reverse-proxy/modules/reverse_proxy/main.tf +++ b/stacks/reverse-proxy/modules/reverse_proxy/main.tf @@ -186,10 +186,10 @@ module "proxmox" { } } -# https://registry.viktorbarzin.me/ +# https://docker.viktorbarzin.me/ (registry web UI) module "docker-registry-ui" { source = "./factory" - name = "registry" + name = "docker" external_name = "docker-registry.viktorbarzin.lan" port = 8080 tls_secret_name = var.tls_secret_name @@ -206,6 +206,25 @@ module "docker-registry-ui" { } } +# https://registry.viktorbarzin.me/ (Docker CLI push/pull endpoint) +module "docker-registry-cli" { + source = "./factory" + name = "registry" + external_name = "docker-registry.viktorbarzin.lan" + port = 5050 + backend_protocol = "HTTPS" + tls_secret_name = var.tls_secret_name + protected = false # Docker CLI uses htpasswd, NOT Authentik + max_body_size = "0" # unlimited - Docker layers can be large + depends_on = [kubernetes_namespace.reverse-proxy] + extra_annotations = { + # Skip rate-limit (Docker push/pull generates many rapid requests) + # Keep CrowdSec for L7 protection + "traefik.ingress.kubernetes.io/router.middlewares" = "traefik-csp-headers@kubernetescrd,traefik-crowdsec@kubernetescrd" + "gethomepage.dev/enabled" = "false" + } +} + # https://valchedrym.viktorbarzin.me/ module "valchedrym" { source = "./factory"