summaryrefslogtreecommitdiffstats
path: root/recipes-containers
diff options
context:
space:
mode:
authorBruce Ashfield <bruce.ashfield@gmail.com>2026-02-09 03:17:24 +0000
committerBruce Ashfield <bruce.ashfield@gmail.com>2026-02-09 03:34:12 +0000
commit52fc4ca7c75594fe8b3c92a9f88df19f8f4d0944 (patch)
treef003083cf14ecb0543303ab0023d39fd3457eb78 /recipes-containers
parent092aa81983335b2346a725eebd2a75fc785bb42b (diff)
downloadmeta-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')
-rw-r--r--recipes-containers/container-registry/container-oci-registry-config.bb100
-rw-r--r--recipes-containers/container-registry/container-registry-ca.bb98
-rw-r--r--recipes-containers/container-registry/docker-registry-config.bb120
3 files changed, 278 insertions, 40 deletions
diff --git a/recipes-containers/container-registry/container-oci-registry-config.bb b/recipes-containers/container-registry/container-oci-registry-config.bb
index ba8cf4c3..294defc3 100644
--- a/recipes-containers/container-registry/container-oci-registry-config.bb
+++ b/recipes-containers/container-registry/container-oci-registry-config.bb
@@ -17,21 +17,27 @@
17# See: docker-registry-config.bb for Docker configuration 17# See: docker-registry-config.bb for Docker configuration
18# 18#
19# This recipe creates a drop-in configuration file for accessing a custom 19# This recipe creates a drop-in configuration file for accessing a custom
20# container registry. It is completely OPT-IN and does not modify any 20# container registry. It supports both insecure (HTTP) and secure (HTTPS with TLS)
21# existing configuration files. 21# modes. It is completely OPT-IN and does not modify any existing configuration files.
22# 22#
23# IMPORTANT: This recipe: 23# IMPORTANT: This recipe:
24# - Does NOT modify docker-distribution or container-host-config 24# - Does NOT modify docker-distribution or container-host-config
25# - Does NOT clobber public registry access (docker.io, quay.io, etc.) 25# - Does NOT clobber public registry access (docker.io, quay.io, etc.)
26# - Uses drop-in files in /etc/containers/registries.conf.d/ 26# - Uses drop-in files in /etc/containers/registries.conf.d/
27# - Skips entirely if CONTAINER_REGISTRY_URL is not set 27# - Skips entirely if CONTAINER_REGISTRY_URL is not set
28# - In secure mode: installs CA cert to /etc/containers/certs.d/{registry}/
28# 29#
29# Usage: 30# Usage:
30# # In local.conf or image recipe: 31# # Insecure mode (HTTP):
31# CONTAINER_REGISTRY_URL = "localhost:5000" 32# CONTAINER_REGISTRY_URL = "localhost:5000"
32# CONTAINER_REGISTRY_INSECURE = "1" 33# CONTAINER_REGISTRY_INSECURE = "1"
33# IMAGE_FEATURES += "container-registry" 34# IMAGE_FEATURES += "container-registry"
34# 35#
36# # Secure mode (HTTPS with TLS):
37# CONTAINER_REGISTRY_SECURE = "1"
38# CONTAINER_REGISTRY_URL = "localhost:5000"
39# IMAGE_FEATURES += "container-registry"
40#
35# The IMAGE_FEATURES mechanism auto-selects this recipe for Podman/CRI-O 41# The IMAGE_FEATURES mechanism auto-selects this recipe for Podman/CRI-O
36# or docker-registry-config for Docker based on VIRTUAL-RUNTIME_container_engine. 42# or docker-registry-config for Docker based on VIRTUAL-RUNTIME_container_engine.
37# 43#
@@ -40,6 +46,7 @@
40SUMMARY = "Configure custom container registry for Podman/Skopeo/Buildah (opt-in)" 46SUMMARY = "Configure custom container registry for Podman/Skopeo/Buildah (opt-in)"
41DESCRIPTION = "Adds drop-in configuration for Podman, Skopeo, Buildah, and CRI-O. \ 47DESCRIPTION = "Adds drop-in configuration for Podman, Skopeo, Buildah, and CRI-O. \
42NOT for Docker (use docker-registry-config for Docker). \ 48NOT for Docker (use docker-registry-config for Docker). \
49Supports both insecure (HTTP) and secure (HTTPS with TLS) modes. \
43Does NOT modify existing registries.conf - creates a separate file in \ 50Does NOT modify existing registries.conf - creates a separate file in \
44registries.conf.d/ that is merged at runtime. Public registries remain accessible. \ 51registries.conf.d/ that is merged at runtime. Public registries remain accessible. \
45This recipe is opt-in: requires CONTAINER_REGISTRY_URL to be set. \ 52This recipe is opt-in: requires CONTAINER_REGISTRY_URL to be set. \
@@ -48,12 +55,14 @@ Use IMAGE_FEATURES container-registry to auto-select based on container engine."
48LICENSE = "MIT" 55LICENSE = "MIT"
49LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302" 56LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
50 57
58inherit allarch container-registry
59
51# User MUST set these - recipe skips otherwise 60# User MUST set these - recipe skips otherwise
52CONTAINER_REGISTRY_URL ?= ""
53CONTAINER_REGISTRY_INSECURE ?= "0"
54CONTAINER_REGISTRY_SEARCH_FIRST ?= "1" 61CONTAINER_REGISTRY_SEARCH_FIRST ?= "1"
55 62
56inherit allarch 63# Path to OCI auth config (for baked credentials)
64# NOT stored in bitbake - should point to external file
65CONTAINER_REGISTRY_AUTHFILE ?= ""
57 66
58# Skip recipe entirely if not configured 67# Skip recipe entirely if not configured
59# User must explicitly set CONTAINER_REGISTRY_URL to enable 68# User must explicitly set CONTAINER_REGISTRY_URL to enable
@@ -61,20 +70,54 @@ python() {
61 registry = d.getVar('CONTAINER_REGISTRY_URL') 70 registry = d.getVar('CONTAINER_REGISTRY_URL')
62 if not registry: 71 if not registry:
63 raise bb.parse.SkipRecipe("CONTAINER_REGISTRY_URL not set - recipe is opt-in only") 72 raise bb.parse.SkipRecipe("CONTAINER_REGISTRY_URL not set - recipe is opt-in only")
73
74 # Check for conflicting settings
75 secure = d.getVar('CONTAINER_REGISTRY_SECURE') == '1'
76 insecure = d.getVar('CONTAINER_REGISTRY_INSECURE') == '1'
77
78 if secure and insecure:
79 bb.fatal("CONTAINER_REGISTRY_SECURE='1' and CONTAINER_REGISTRY_INSECURE='1' cannot both be set. "
80 "Use secure mode (TLS+auth) OR insecure mode (HTTP), not both.")
81
82 # In secure mode, depend on PKI generation
83 if secure:
84 d.appendVarFlag('do_install', 'depends', ' container-registry-index:do_generate_registry_script')
64} 85}
65 86
66python do_install() { 87python do_install() {
67 import os 88 import os
89 import shutil
68 90
69 registry = d.getVar('CONTAINER_REGISTRY_URL') 91 registry = d.getVar('CONTAINER_REGISTRY_URL')
70 insecure = d.getVar('CONTAINER_REGISTRY_INSECURE') == "1" 92 insecure = d.getVar('CONTAINER_REGISTRY_INSECURE') == "1"
93 secure = d.getVar('CONTAINER_REGISTRY_SECURE') == "1"
71 search_first = d.getVar('CONTAINER_REGISTRY_SEARCH_FIRST') == "1" 94 search_first = d.getVar('CONTAINER_REGISTRY_SEARCH_FIRST') == "1"
95 ca_cert = d.getVar('CONTAINER_REGISTRY_CA_CERT')
96 authfile = d.getVar('CONTAINER_REGISTRY_AUTHFILE') or ''
97
98 # Extract registry host (strip any path)
99 registry_host = registry.split('/')[0] if '/' in registry else registry
72 100
73 dest = d.getVar('D') 101 dest = d.getVar('D')
74 confdir = os.path.join(dest, d.getVar('sysconfdir').lstrip('/'), 102 confdir = os.path.join(dest, d.getVar('sysconfdir').lstrip('/'),
75 'containers', 'registries.conf.d') 103 'containers', 'registries.conf.d')
76 os.makedirs(confdir, exist_ok=True) 104 os.makedirs(confdir, exist_ok=True)
77 105
106 # Install CA cert in secure mode
107 if secure:
108 if os.path.exists(ca_cert):
109 cert_dir = os.path.join(dest, 'etc/containers/certs.d', registry_host)
110 os.makedirs(cert_dir, exist_ok=True)
111 shutil.copy(ca_cert, os.path.join(cert_dir, 'ca.crt'))
112 bb.note(f"Installed CA certificate for registry: {registry_host}")
113 else:
114 bb.warn(f"Secure mode enabled but CA certificate not found at {ca_cert}")
115 bb.warn("Run 'container-registry.sh start' to generate PKI, then rebuild this package")
116
117 # In secure mode, insecure should be false
118 if secure:
119 insecure = False
120
78 # Generate drop-in config 121 # Generate drop-in config
79 # Filename starts with 50- so it's processed after base config but 122 # Filename starts with 50- so it's processed after base config but
80 # can be overridden by higher-numbered files 123 # can be overridden by higher-numbered files
@@ -82,11 +125,17 @@ python do_install() {
82 125
83 with open(config_path, 'w') as f: 126 with open(config_path, 'w') as f:
84 f.write(f"# Custom container registry: {registry}\n") 127 f.write(f"# Custom container registry: {registry}\n")
85 f.write(f"# Generated by container-registry-config recipe\n") 128 f.write(f"# Generated by container-oci-registry-config recipe\n")
86 f.write(f"# This is ADDITIVE - base registries.conf is unchanged\n") 129 f.write(f"# This is ADDITIVE - base registries.conf is unchanged\n")
87 f.write(f"# Public registries (docker.io, quay.io) remain accessible\n") 130 f.write(f"# Public registries (docker.io, quay.io) remain accessible\n")
88 f.write(f"#\n") 131 f.write(f"#\n")
89 f.write(f"# To remove: uninstall container-registry-config package\n") 132 if secure:
133 f.write(f"# Mode: secure (TLS with CA certificate verification)\n")
134 f.write(f"# CA cert: /etc/containers/certs.d/{registry_host}/ca.crt\n")
135 else:
136 f.write(f"# Mode: insecure (HTTP or untrusted TLS)\n")
137 f.write(f"#\n")
138 f.write(f"# To remove: uninstall container-oci-registry-config package\n")
90 f.write(f"# or delete this file\n\n") 139 f.write(f"# or delete this file\n\n")
91 140
92 if search_first: 141 if search_first:
@@ -95,17 +144,40 @@ python do_install() {
95 f.write(f"# Search this registry for unqualified image names\n") 144 f.write(f"# Search this registry for unqualified image names\n")
96 f.write(f'unqualified-search-registries = ["{registry}"]\n\n') 145 f.write(f'unqualified-search-registries = ["{registry}"]\n\n')
97 146
147 # Always create registry entry to set insecure flag explicitly
148 f.write(f'[[registry]]\n')
149 f.write(f'location = "{registry_host}"\n')
98 if insecure: 150 if insecure:
99 # Mark registry as insecure (HTTP or self-signed TLS)
100 f.write(f"# Registry uses HTTP or has untrusted TLS certificate\n")
101 f.write(f'[[registry]]\n')
102 f.write(f'location = "{registry}"\n')
103 f.write(f'insecure = true\n') 151 f.write(f'insecure = true\n')
152 else:
153 f.write(f'insecure = false\n')
104 154
105 bb.note(f"Created registry config for {registry} (insecure={insecure})") 155 # Install authfile if provided (for baked credentials)
156 if authfile and os.path.exists(authfile):
157 containers_dir = os.path.join(dest, 'etc/containers')
158 os.makedirs(containers_dir, exist_ok=True)
159 auth_json = os.path.join(containers_dir, 'auth.json')
160 shutil.copy(authfile, auth_json)
161 os.chmod(auth_json, 0o600)
162 bb.note(f"Installed OCI auth config from {authfile}")
163
164 mode = "secure" if secure else ("insecure" if insecure else "default")
165 bb.note(f"Created registry config for {registry} (mode={mode})")
106} 166}
107 167
108FILES:${PN} = "${sysconfdir}/containers/registries.conf.d" 168FILES:${PN} = " \
169 ${sysconfdir}/containers/registries.conf.d \
170 ${sysconfdir}/containers/certs.d/*/ca.crt \
171 ${sysconfdir}/containers/auth.json \
172"
173
174# Ensure proper permissions on auth file
175pkg_postinst:${PN}() {
176#!/bin/sh
177if [ -f $D/etc/containers/auth.json ]; then
178 chmod 600 $D/etc/containers/auth.json
179fi
180}
109 181
110# Soft dependency - works with or without container-host-config 182# Soft dependency - works with or without container-host-config
111# If container-host-config is installed, our drop-in extends it 183# If container-host-config is installed, our drop-in extends it
diff --git a/recipes-containers/container-registry/container-registry-ca.bb b/recipes-containers/container-registry/container-registry-ca.bb
new file mode 100644
index 00000000..85bb206f
--- /dev/null
+++ b/recipes-containers/container-registry/container-registry-ca.bb
@@ -0,0 +1,98 @@
1# SPDX-FileCopyrightText: Copyright (C) 2025 Bruce Ashfield
2#
3# SPDX-License-Identifier: MIT
4#
5# container-registry-ca.bb
6# ============================================================================
7# Install CA certificate for secure container registry on target images.
8#
9# This recipe installs the CA certificate generated during
10# container-registry-index:do_generate_registry_script to the appropriate
11# locations for Docker, Podman/CRI-O, and system trust.
12#
13# Prerequisites:
14# 1. Enable secure mode: CONTAINER_REGISTRY_SECURE = "1"
15# 2. PKI is auto-generated when building this package
16#
17# Usage:
18# IMAGE_INSTALL:append = " container-registry-ca"
19#
20# Installed files:
21# /etc/docker/certs.d/{registry}/ca.crt - Docker daemon trust
22# /etc/containers/certs.d/{registry}/ca.crt - Podman/CRI-O trust
23# /usr/local/share/ca-certificates/container-registry-ca.crt - System trust
24#
25# ============================================================================
26
27SUMMARY = "CA certificate for secure container registry"
28DESCRIPTION = "Installs the CA certificate for TLS verification when pulling from the local container registry"
29LICENSE = "MIT"
30LIC_FILES_CHKSUM = "file://${COREBASE}/meta/COPYING.MIT;md5=3da9cfbcb788c80a0384361b4de20420"
31
32inherit container-registry
33
34# Only build if secure mode is enabled
35python () {
36 secure = d.getVar('CONTAINER_REGISTRY_SECURE')
37 if secure != '1':
38 raise bb.parse.SkipRecipe("CONTAINER_REGISTRY_SECURE is not '1' - secure mode not enabled")
39}
40
41# No source files - we use the generated CA cert
42SRC_URI = ""
43
44do_configure[noexec] = "1"
45do_compile[noexec] = "1"
46
47# Ensure PKI is generated before we try to install the CA cert
48do_install[depends] += "container-registry-index:do_generate_registry_script"
49
50python do_install() {
51 import os
52 import shutil
53
54 d_dir = d.getVar('D')
55 ca_cert = d.getVar('CONTAINER_REGISTRY_CA_CERT')
56 registry_url = d.getVar('CONTAINER_REGISTRY_URL')
57
58 # Extract registry host (strip port)
59 registry_host = registry_url.split('/')[0] if '/' in registry_url else registry_url
60
61 if not os.path.exists(ca_cert):
62 bb.fatal(f"CA certificate not found at {ca_cert}. "
63 "This should have been auto-generated. Check container-registry-index:do_generate_registry_script logs.")
64
65 # Install for Docker: /etc/docker/certs.d/{registry}/ca.crt
66 docker_cert_dir = os.path.join(d_dir, 'etc/docker/certs.d', registry_host)
67 os.makedirs(docker_cert_dir, exist_ok=True)
68 shutil.copy(ca_cert, os.path.join(docker_cert_dir, 'ca.crt'))
69
70 # Install for Podman/CRI-O: /etc/containers/certs.d/{registry}/ca.crt
71 containers_cert_dir = os.path.join(d_dir, 'etc/containers/certs.d', registry_host)
72 os.makedirs(containers_cert_dir, exist_ok=True)
73 shutil.copy(ca_cert, os.path.join(containers_cert_dir, 'ca.crt'))
74
75 # Install for system trust: /usr/local/share/ca-certificates/
76 system_ca_dir = os.path.join(d_dir, 'usr/local/share/ca-certificates')
77 os.makedirs(system_ca_dir, exist_ok=True)
78 shutil.copy(ca_cert, os.path.join(system_ca_dir, 'container-registry-ca.crt'))
79
80 bb.note(f"Installed CA certificate for registry: {registry_host}")
81}
82
83# Package files
84FILES:${PN} = " \
85 ${sysconfdir}/docker/certs.d/*/ca.crt \
86 ${sysconfdir}/containers/certs.d/*/ca.crt \
87 /usr/local/share/ca-certificates/container-registry-ca.crt \
88"
89
90# Run update-ca-certificates after install if available
91pkg_postinst:${PN}() {
92#!/bin/sh
93if [ -x /usr/sbin/update-ca-certificates ]; then
94 /usr/sbin/update-ca-certificates 2>/dev/null || true
95fi
96}
97
98RDEPENDS:${PN} = ""
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
38SUMMARY = "Configure insecure container registries for Docker daemon (opt-in)" 43SUMMARY = "Configure container registry for Docker daemon (opt-in)"
39DESCRIPTION = "Creates /etc/docker/daemon.json with insecure-registries config. \ 44DESCRIPTION = "Creates /etc/docker/daemon.json with registry config. \
40FOR DOCKER ONLY - not for Podman/Skopeo (use container-oci-registry-config for those). \ 45FOR DOCKER ONLY - not for Podman/Skopeo (use container-oci-registry-config for those). \
41This recipe is opt-in: requires DOCKER_REGISTRY_INSECURE to be set. \ 46Supports both insecure (HTTP) and secure (HTTPS with TLS) modes. \
42Use IMAGE_FEATURES container-registry to auto-select based on container engine." 47Use IMAGE_FEATURES container-registry to auto-select based on container engine."
43 48
44LICENSE = "MIT" 49LICENSE = "MIT"
45LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302" 50LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
46 51
52inherit 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
50DOCKER_REGISTRY_INSECURE ?= "" 57DOCKER_REGISTRY_INSECURE ?= ""
51CONTAINER_REGISTRY_URL ?= "" 58
52CONTAINER_REGISTRY_INSECURE ?= "" 59# Path to Docker auth config (for baked credentials)
60# NOT stored in bitbake - should point to external file
61CONTAINER_REGISTRY_AUTHFILE ?= ""
53 62
54def get_insecure_registries(d): 63def 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
67inherit allarch 76def 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
70python() { 81python() {
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
76python do_install() { 98python 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
100FILES:${PN} = "${sysconfdir}/docker/daemon.json" 156FILES:${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
163pkg_postinst:${PN}() {
164#!/bin/sh
165if [ -f $D/root/.docker/config.json ]; then
166 chmod 600 $D/root/.docker/config.json
167fi
168}
101 169
102# Docker must be installed for this to be useful 170# Docker must be installed for this to be useful
103RDEPENDS:${PN} = "docker" 171RDEPENDS:${PN} = "docker"