diff options
| author | Bruce Ashfield <bruce.ashfield@gmail.com> | 2026-02-09 03:17:24 +0000 |
|---|---|---|
| committer | Bruce Ashfield <bruce.ashfield@gmail.com> | 2026-02-09 03:34:12 +0000 |
| commit | 52fc4ca7c75594fe8b3c92a9f88df19f8f4d0944 (patch) | |
| tree | f003083cf14ecb0543303ab0023d39fd3457eb78 /recipes-containers/container-registry/docker-registry-config.bb | |
| parent | 092aa81983335b2346a725eebd2a75fc785bb42b (diff) | |
| download | meta-virtualization-52fc4ca7c75594fe8b3c92a9f88df19f8f4d0944.tar.gz | |
container-registry: add target image TLS integration
Install CA certificates and registry configuration into target images
so they can pull from the secure registry at runtime.
docker-registry-config.bb: When CONTAINER_REGISTRY_SECURE=1, install
the CA cert to /etc/docker/certs.d/{host}/ca.crt instead of adding
insecure-registries to daemon.json. Translates localhost/127.0.0.1 to
10.0.2.2 for QEMU targets where the host registry is accessed via
slirp networking.
container-oci-registry-config.bb: Same secure mode support for
podman/CRI-O with insecure=false in registries.conf.
container-registry-ca.bb: New recipe that installs the CA certificate
to Docker, podman/CRI-O, and system trust store paths on the target.
container-cross-install.bbclass: Auto-add docker-registry-config or
container-oci-registry-config to IMAGE_INSTALL when
CONTAINER_REGISTRY_SECURE=1, based on the configured container engine.
Signed-off-by: Bruce Ashfield <bruce.ashfield@gmail.com>
Diffstat (limited to 'recipes-containers/container-registry/docker-registry-config.bb')
| -rw-r--r-- | recipes-containers/container-registry/docker-registry-config.bb | 120 |
1 files changed, 94 insertions, 26 deletions
diff --git a/recipes-containers/container-registry/docker-registry-config.bb b/recipes-containers/container-registry/docker-registry-config.bb index e03cd3eb..0e8d66ad 100644 --- a/recipes-containers/container-registry/docker-registry-config.bb +++ b/recipes-containers/container-registry/docker-registry-config.bb | |||
| @@ -10,46 +10,55 @@ | |||
| 10 | # FOR DOCKER ONLY - creates /etc/docker/daemon.json | 10 | # FOR DOCKER ONLY - creates /etc/docker/daemon.json |
| 11 | # | 11 | # |
| 12 | # NOT for Podman/Skopeo/Buildah - they use /etc/containers/registries.conf.d/ | 12 | # NOT for Podman/Skopeo/Buildah - they use /etc/containers/registries.conf.d/ |
| 13 | # See: container-registry-config.bb for Podman/Skopeo/Buildah | 13 | # See: container-oci-registry-config.bb for Podman/Skopeo/Buildah |
| 14 | # | 14 | # |
| 15 | # This recipe creates daemon.json for Docker to access insecure registries. | 15 | # This recipe creates daemon.json for Docker to access registries. |
| 16 | # It is completely OPT-IN and requires explicit configuration. | 16 | # It supports both insecure (HTTP) and secure (HTTPS with TLS) modes. |
| 17 | # | 17 | # |
| 18 | # NOTE: Docker does not support "default registry" like our vdkr transform. | 18 | # NOTE: Docker does not support "default registry" like our vdkr transform. |
| 19 | # Users must still use fully qualified image names unless using Docker Hub. | 19 | # Users must still use fully qualified image names unless using Docker Hub. |
| 20 | # This config only handles insecure registry trust. | ||
| 21 | # | 20 | # |
| 22 | # IMPORTANT: This recipe: | 21 | # IMPORTANT: This recipe: |
| 23 | # - Skips entirely if DOCKER_REGISTRY_INSECURE is not set | 22 | # - Skips entirely if neither insecure nor secure registry is configured |
| 24 | # - Creates /etc/docker/daemon.json (will be merged if docker recipe | 23 | # - Creates /etc/docker/daemon.json |
| 25 | # also creates one, or may need RCONFLICTS handling) | 24 | # - In secure mode: installs CA cert to /etc/docker/certs.d/{registry}/ |
| 25 | # - In insecure mode: adds registry to insecure-registries list | ||
| 26 | # | 26 | # |
| 27 | # Usage: | 27 | # Usage: |
| 28 | # # In local.conf or image recipe: | 28 | # # Insecure mode (HTTP): |
| 29 | # DOCKER_REGISTRY_INSECURE = "10.0.2.2:5000 myregistry.local:5000" | 29 | # DOCKER_REGISTRY_INSECURE = "10.0.2.2:5000 myregistry.local:5000" |
| 30 | # IMAGE_FEATURES += "container-registry" | 30 | # IMAGE_FEATURES += "container-registry" |
| 31 | # | 31 | # |
| 32 | # # Secure mode (HTTPS with TLS): | ||
| 33 | # CONTAINER_REGISTRY_SECURE = "1" | ||
| 34 | # CONTAINER_REGISTRY_URL = "localhost:5000" | ||
| 35 | # IMAGE_FEATURES += "container-registry" | ||
| 36 | # | ||
| 32 | # The IMAGE_FEATURES mechanism auto-selects this recipe for Docker | 37 | # The IMAGE_FEATURES mechanism auto-selects this recipe for Docker |
| 33 | # or container-oci-registry-config for Podman/CRI-O based on | 38 | # or container-oci-registry-config for Podman/CRI-O based on |
| 34 | # VIRTUAL-RUNTIME_container_engine. | 39 | # VIRTUAL-RUNTIME_container_engine. |
| 35 | # | 40 | # |
| 36 | # =========================================================================== | 41 | # =========================================================================== |
| 37 | 42 | ||
| 38 | SUMMARY = "Configure insecure container registries for Docker daemon (opt-in)" | 43 | SUMMARY = "Configure container registry for Docker daemon (opt-in)" |
| 39 | DESCRIPTION = "Creates /etc/docker/daemon.json with insecure-registries config. \ | 44 | DESCRIPTION = "Creates /etc/docker/daemon.json with registry config. \ |
| 40 | FOR DOCKER ONLY - not for Podman/Skopeo (use container-oci-registry-config for those). \ | 45 | FOR DOCKER ONLY - not for Podman/Skopeo (use container-oci-registry-config for those). \ |
| 41 | This recipe is opt-in: requires DOCKER_REGISTRY_INSECURE to be set. \ | 46 | Supports both insecure (HTTP) and secure (HTTPS with TLS) modes. \ |
| 42 | Use IMAGE_FEATURES container-registry to auto-select based on container engine." | 47 | Use IMAGE_FEATURES container-registry to auto-select based on container engine." |
| 43 | 48 | ||
| 44 | LICENSE = "MIT" | 49 | LICENSE = "MIT" |
| 45 | LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302" | 50 | LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302" |
| 46 | 51 | ||
| 52 | inherit allarch container-registry | ||
| 53 | |||
| 47 | # Space-separated list of insecure registries | 54 | # Space-separated list of insecure registries |
| 48 | # Example: "10.0.2.2:5000 myregistry.local:5000" | 55 | # Example: "10.0.2.2:5000 myregistry.local:5000" |
| 49 | # Can also use runtime-agnostic CONTAINER_REGISTRY_URL + CONTAINER_REGISTRY_INSECURE | 56 | # Can also use runtime-agnostic CONTAINER_REGISTRY_URL + CONTAINER_REGISTRY_INSECURE |
| 50 | DOCKER_REGISTRY_INSECURE ?= "" | 57 | DOCKER_REGISTRY_INSECURE ?= "" |
| 51 | CONTAINER_REGISTRY_URL ?= "" | 58 | |
| 52 | CONTAINER_REGISTRY_INSECURE ?= "" | 59 | # Path to Docker auth config (for baked credentials) |
| 60 | # NOT stored in bitbake - should point to external file | ||
| 61 | CONTAINER_REGISTRY_AUTHFILE ?= "" | ||
| 53 | 62 | ||
| 54 | def get_insecure_registries(d): | 63 | def get_insecure_registries(d): |
| 55 | """Get insecure registries from either Docker-specific or generic config""" | 64 | """Get insecure registries from either Docker-specific or generic config""" |
| @@ -64,40 +73,99 @@ def get_insecure_registries(d): | |||
| 64 | return registry_url.strip() | 73 | return registry_url.strip() |
| 65 | return "" | 74 | return "" |
| 66 | 75 | ||
| 67 | inherit allarch | 76 | def is_secure_mode(d): |
| 77 | """Check if secure registry mode is enabled""" | ||
| 78 | return d.getVar('CONTAINER_REGISTRY_SECURE') == '1' | ||
| 68 | 79 | ||
| 69 | # Skip recipe entirely if not configured | 80 | # Skip recipe entirely if not configured |
| 70 | python() { | 81 | python() { |
| 82 | secure = is_secure_mode(d) | ||
| 71 | registries = get_insecure_registries(d) | 83 | registries = get_insecure_registries(d) |
| 72 | if not registries: | 84 | |
| 73 | raise bb.parse.SkipRecipe("No insecure registry configured - recipe is opt-in only") | 85 | # Check for conflicting settings |
| 86 | if secure and registries: | ||
| 87 | bb.fatal("CONTAINER_REGISTRY_SECURE='1' conflicts with insecure registry settings. " | ||
| 88 | "Use secure mode (TLS+auth) OR insecure mode (HTTP), not both.") | ||
| 89 | |||
| 90 | if not secure and not registries: | ||
| 91 | raise bb.parse.SkipRecipe("No registry configured - recipe is opt-in only") | ||
| 92 | |||
| 93 | # In secure mode, depend on PKI generation | ||
| 94 | if secure: | ||
| 95 | d.appendVarFlag('do_install', 'depends', ' container-registry-index:do_generate_registry_script') | ||
| 74 | } | 96 | } |
| 75 | 97 | ||
| 76 | python do_install() { | 98 | python do_install() { |
| 77 | import os | 99 | import os |
| 78 | import json | 100 | import json |
| 79 | 101 | import shutil | |
| 80 | registries = get_insecure_registries(d).split() | ||
| 81 | 102 | ||
| 82 | dest = d.getVar('D') | 103 | dest = d.getVar('D') |
| 83 | confdir = os.path.join(dest, d.getVar('sysconfdir').lstrip('/'), 'docker') | 104 | confdir = os.path.join(dest, d.getVar('sysconfdir').lstrip('/'), 'docker') |
| 84 | os.makedirs(confdir, exist_ok=True) | 105 | os.makedirs(confdir, exist_ok=True) |
| 85 | 106 | ||
| 86 | config_path = os.path.join(confdir, 'daemon.json') | 107 | secure = is_secure_mode(d) |
| 108 | registries = get_insecure_registries(d).split() | ||
| 109 | ca_cert = d.getVar('CONTAINER_REGISTRY_CA_CERT') | ||
| 110 | registry_url = d.getVar('CONTAINER_REGISTRY_URL') or '' | ||
| 111 | authfile = d.getVar('CONTAINER_REGISTRY_AUTHFILE') or '' | ||
| 112 | |||
| 113 | config = {} | ||
| 114 | |||
| 115 | if secure: | ||
| 116 | # Secure mode: install CA cert, no insecure-registries | ||
| 117 | # Translate localhost/127.0.0.1 to 10.0.2.2 for QEMU slirp networking | ||
| 118 | qemu_url = registry_url.replace('localhost', '10.0.2.2').replace('127.0.0.1', '10.0.2.2') | ||
| 119 | registry_host = qemu_url.split('/')[0] if '/' in qemu_url else qemu_url | ||
| 120 | |||
| 121 | if os.path.exists(ca_cert): | ||
| 122 | # Install CA cert to /etc/docker/certs.d/{registry}/ca.crt | ||
| 123 | cert_dir = os.path.join(dest, 'etc/docker/certs.d', registry_host) | ||
| 124 | os.makedirs(cert_dir, exist_ok=True) | ||
| 125 | shutil.copy(ca_cert, os.path.join(cert_dir, 'ca.crt')) | ||
| 126 | bb.note(f"Installed CA certificate for registry: {registry_host}") | ||
| 127 | else: | ||
| 128 | bb.warn(f"Secure mode enabled but CA certificate not found at {ca_cert}") | ||
| 129 | bb.warn("Run 'container-registry.sh start' to generate PKI, then rebuild this package") | ||
| 130 | |||
| 131 | # daemon.json can be empty or minimal in secure mode | ||
| 132 | # (no insecure-registries needed when using TLS) | ||
| 133 | bb.note("Secure mode: Docker will use TLS verification with installed CA cert") | ||
| 134 | else: | ||
| 135 | # Insecure mode: add to insecure-registries | ||
| 136 | if registries: | ||
| 137 | config["insecure-registries"] = registries | ||
| 138 | bb.note(f"Created Docker config with insecure registries: {registries}") | ||
| 87 | 139 | ||
| 88 | # Create daemon.json | 140 | # Install authfile if provided (for baked credentials) |
| 89 | config = { | 141 | if authfile and os.path.exists(authfile): |
| 90 | "insecure-registries": registries | 142 | docker_dir = os.path.join(dest, 'root/.docker') |
| 91 | } | 143 | os.makedirs(docker_dir, mode=0o700, exist_ok=True) |
| 144 | config_json = os.path.join(docker_dir, 'config.json') | ||
| 145 | shutil.copy(authfile, config_json) | ||
| 146 | os.chmod(config_json, 0o600) | ||
| 147 | bb.note(f"Installed Docker auth config from {authfile}") | ||
| 92 | 148 | ||
| 149 | # Write daemon.json (may be empty in secure mode) | ||
| 150 | config_path = os.path.join(confdir, 'daemon.json') | ||
| 93 | with open(config_path, 'w') as f: | 151 | with open(config_path, 'w') as f: |
| 94 | json.dump(config, f, indent=2) | 152 | json.dump(config, f, indent=2) |
| 95 | f.write("\n") | 153 | f.write("\n") |
| 96 | |||
| 97 | bb.note(f"Created Docker config with insecure registries: {registries}") | ||
| 98 | } | 154 | } |
| 99 | 155 | ||
| 100 | FILES:${PN} = "${sysconfdir}/docker/daemon.json" | 156 | FILES:${PN} = " \ |
| 157 | ${sysconfdir}/docker/daemon.json \ | ||
| 158 | ${sysconfdir}/docker/certs.d/*/ca.crt \ | ||
| 159 | /root/.docker/config.json \ | ||
| 160 | " | ||
| 161 | |||
| 162 | # Ensure proper permissions on auth file | ||
| 163 | pkg_postinst:${PN}() { | ||
| 164 | #!/bin/sh | ||
| 165 | if [ -f $D/root/.docker/config.json ]; then | ||
| 166 | chmod 600 $D/root/.docker/config.json | ||
| 167 | fi | ||
| 168 | } | ||
| 101 | 169 | ||
| 102 | # Docker must be installed for this to be useful | 170 | # Docker must be installed for this to be useful |
| 103 | RDEPENDS:${PN} = "docker" | 171 | RDEPENDS:${PN} = "docker" |
