Auth registry for authenticated pull - #112
Conversation
…rage Docker Distribution requires a shared REGISTRY_HTTP_SECRET when multiple registry instances access the same storage backend. Add the secret to the existing unauthenticated registry in preparation for the authenticated registry that will share the same volume. Signed-off-by: HarshwardhanPatil07 <harshpat@redhat.com>
79f9d79 to
93f3137
Compare
Add a second registry instance (bink-auth-registry) that shares the same bink-registry-data volume as the unauthenticated registry. The auth registry mounts the volume read-only and requires htpasswd authentication with fixed test credentials (testuser/testpassword). This enables bootc-operator milestone 5b (pull secret propagation) e2e tests that need a pull-secret-protected registry without breaking the existing development workflow on the unauthenticated registry. The auth registry listens on port 5001 with static IP 10.88.0.3 on the podman default network. Volume sharing between two registry:2 instances is safe: the filesystem driver uses atomic renames with no file locks, and the read-only instance sees new images immediately. Credentials are configured via htpasswd with bcrypt hashing, injected at container startup through an entrypoint override. The bcrypt hash is generated at runtime using golang.org/x/crypto/bcrypt. Assisted-by: AI Signed-off-by: HarshwardhanPatil07 <harshpat@redhat.com>
93f3137 to
0fc3c05
Compare
Johan-Liebert1
left a comment
There was a problem hiding this comment.
Overall looks sane
| return nil | ||
| } | ||
|
|
||
| if err := mgr.StopAuthRegistry(cmd.Context()); err != nil { |
There was a problem hiding this comment.
Also, we're stopping the AuthRegistry regardless it was created or not. Are we sure this won't just error out if the auth registry doesn't exist?
There was a problem hiding this comment.
both registries are created by default. it doesn't error out.
Experimentation :
harshpat@harshpat-thinkpadp1gen4i:~/Downloads/repos/bink$ podman rm -f bink-auth-registry
harshpat@harshpat-thinkpadp1gen4i:~/Downloads/repos/bink$ ./bink registry info
Registry (unauthenticated): running
IP: 10.88.0.2
Host port: 5000
Push: podman push --tls-verify=false localhost:5000/<image>:<tag>
Pull: registry.cluster.local:5000/<image>:<tag>
Registry (authenticated): stopped
IP: 10.88.0.3
Host port: 5001
Pull: auth-registry.cluster.local:5001/<image>:<tag>
Credentials: testuser / testpassword
harshpat@harshpat-thinkpadp1gen4i:~/Downloads/repos/bink$ ./bink registry stop
harshpat@harshpat-thinkpadp1gen4i:~/Downloads/repos/bink$ ./bink registry info
Registry (unauthenticated): stopped
IP: 10.88.0.2
Host port: 5000
Push: podman push --tls-verify=false localhost:5000/<image>:<tag>
Pull: registry.cluster.local:5000/<image>:<tag>
Registry (authenticated): stopped
IP: 10.88.0.3
Host port: 5001
Pull: auth-registry.cluster.local:5001/<image>:<tag>
Credentials: testuser / testpassword
harshpat@harshpat-thinkpadp1gen4i:~/Downloads/repos/bink$ ./bink registry start
harshpat@harshpat-thinkpadp1gen4i:~/Downloads/repos/bink$ ./bink registry info
Registry (unauthenticated): running
IP: 10.88.0.2
Host port: 5000
Push: podman push --tls-verify=false localhost:5000/<image>:<tag>
Pull: registry.cluster.local:5000/<image>:<tag>
Registry (authenticated): running
IP: 10.88.0.3
Host port: 5001
Pull: auth-registry.cluster.local:5001/<image>:<tag>
Credentials: testuser / testpassword
harshpat@harshpat-thinkpadp1gen4i:~/Downloads/repos/bink$ podman rm -f bink-auth-registry
bink-auth-registry
harshpat@harshpat-thinkpadp1gen4i:~/Downloads/repos/bink$ ./bink registry info
Registry (unauthenticated): running
IP: 10.88.0.2
Host port: 5000
Push: podman push --tls-verify=false localhost:5000/<image>:<tag>
Pull: registry.cluster.local:5000/<image>:<tag>
Registry (authenticated): stopped
IP: 10.88.0.3
Host port: 5001
Pull: auth-registry.cluster.local:5001/<image>:<tag>
Credentials: testuser / testpassword
| return fmt.Errorf("ensuring registry image: %w", err) | ||
| } | ||
|
|
||
| if err := m.podman.VolumeCreate(ctx, config.RegistryVolume, nil); err != nil { |
There was a problem hiding this comment.
Is VolumeCreate idempotent? Would this fail if the container already exists?
There was a problem hiding this comment.
No, it doesn't fail
Experimentation:
harshpat@harshpat-thinkpadp1gen4i:~/Downloads/repos/bink$ ./bink registry start
harshpat@harshpat-thinkpadp1gen4i:~/Downloads/repos/bink$ podman volume inspect bink-registry-data --format "{{.Name}}"
bink-registry-data
harshpat@harshpat-thinkpadp1gen4i:~/Downloads/repos/bink$ ./bink registry start
harshpat@harshpat-thinkpadp1gen4i:~/Downloads/repos/bink$ ./bink registry stop --auth
harshpat@harshpat-thinkpadp1gen4i:~/Downloads/repos/bink$ ./bink registry start --auth
There was a problem hiding this comment.
Yes it is idempotent. more info: https://github.com/bootc-dev/bink/blob/main/internal/podman/client.go#L739-L741
Add DNS resolution for auth-registry.cluster.local and configure CRI-O and containers/registries.conf inside VMs to treat the authenticated registry at port 5001 as an insecure (HTTP) registry. The DNS entry is baked into the DNS container image and requires a rebuild via make build-dns-image. Assisted-by: AI Signed-off-by: HarshwardhanPatil07 <harshpat@redhat.com>
Start the authenticated registry automatically during cluster start alongside the unauthenticated one. Update the registry CLI subcommands: - bink registry start: starts both registries (--auth for auth only) - bink registry stop: stops both registries and removes data (--auth to stop only the authenticated registry) - bink registry info: shows status of both registries with credentials - bink cluster stop --remove-data: updated note mentioning both registries Assisted-by: AI Signed-off-by: HarshwardhanPatil07 <harshpat@redhat.com>
The REGISTRY_STORAGE_MAINTENANCE_READONLY_ENABLED env var causes a panic in Docker Distribution because its config parser cannot handle the deeply nested storage.maintenance.readonly.enabled key via environment variable flattening. The volume is already mounted read-only (ro option), so push attempts will fail at the filesystem level regardless. Assisted-by: AI Signed-off-by: HarshwardhanPatil07 <harshpat@redhat.com>
0fc3c05 to
e72f8a4
Compare
| return fmt.Errorf("ensuring registry image: %w", err) | ||
| } | ||
|
|
||
| if err := m.podman.VolumeCreate(ctx, config.RegistryVolume, nil); err != nil { |
There was a problem hiding this comment.
what if this volume already exist? Will this error out?
There was a problem hiding this comment.
VolumeCreate is idempotent — it catches "volume already exists" errors and returns nil (podman/client.go:738). Verified locally: calling bink registry start twice in a row completes without error.
There was a problem hiding this comment.
https://github.com/bootc-dev/bink/blob/main/internal/podman/client.go#L739-L741
So if the volume exists podman does returns the error "volume already exists", our code catches that specific error and returns nil
There was a problem hiding this comment.
Experimentation:
harshpat@harshpat-thinkpadp1gen4i:~/Downloads/repos/bink$ ./bink registry start
harshpat@harshpat-thinkpadp1gen4i:~/Downloads/repos/bink$ podman volume inspect bink-registry-data --format "{{.Name}}"
bink-registry-data
harshpat@harshpat-thinkpadp1gen4i:~/Downloads/repos/bink$ ./bink registry start
harshpat@harshpat-thinkpadp1gen4i:~/Downloads/repos/bink$ ./bink registry stop --auth
harshpat@harshpat-thinkpadp1gen4i:~/Downloads/repos/bink$ ./bink registry start --auth
|
@HarshwardhanPatil07 thanks for the work, would you mind to add an integration test. Like push an image and try to pull it using the secret registry and authentication. Thanks! |
If a container is removed between the ContainerExists check and the ContainerRemove call (e.g. by another process or a parallel test), the remove returns a "no such container" error. Treat this as a no-op instead of a hard failure, so the stop flow completes gracefully. Assisted-by: AI Signed-off-by: HarshwardhanPatil07 <harshpat@redhat.com>
e72f8a4 to
9ab9335
Compare
|
Thank you very much for the amazing reviews @alicefr @Johan-Liebert1. If you have more questions please do let me know |
|
Yes @alicefr, I will create test in follow up PR to keep it clean |
Please, include it in this PR so we can check if the code is correct |
|
@alicefr Yes |
Adds a second registry instance (
bink-auth-registry) configured with htpasswd authentication. This registry shares the same storage volume as the existing unauthenticated registry, ensuring that images pushed tolocalhost:5000are immediately available onlocalhost:5001with credentials, without any data duplication.Verification steps:
1. Build and start
make build-bink
./bink cluster start --cluster-name test --api-port 0
2. Push an image to the unauthenticated registry
podman pull quay.io/libpod/busybox:latest
podman tag quay.io/libpod/busybox:latest localhost:5000/busybox:test
podman push --tls-verify=false localhost:5000/busybox:test
3. Verify auth works
curl -s http://localhost:5001/v2/_catalog # → 401 (blocked)
curl -s -u testuser:testpassword http://localhost:5001/v2/_catalog # → {"repositories":["busybox"]}
4. Clean up
./bink cluster stop --cluster-name test --remove-data
closes #101