From: Daan De Meyer Date: Thu, 21 Mar 2024 10:32:27 +0000 (+0100) Subject: Add proxy settings X-Git-Tag: v23~68^2~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ec2588d75db358bacc5bd6d2333787e869d19b8c;p=thirdparty%2Fmkosi.git Add proxy settings These allow using mkosi behind a proxy that requires proxy authentication. Only dnf seems to allow specifying these certificates as individual settings so other package managers are not fully supported for now. We mount the proxy certificates and keys to /proxy.xxx in the sandbox because otherwise they might end up being mounted at the same location as the certificates from the tools tree, which means those wouldn't be used. --- diff --git a/mkosi/__init__.py b/mkosi/__init__.py index ef06b8591..bd9017600 100644 --- a/mkosi/__init__.py +++ b/mkosi/__init__.py @@ -1665,6 +1665,10 @@ def finalize_default_initrd( *([f"--environment={k}='{v}'" for k, v in config.environment.items()]), *(["--tools-tree", str(config.tools_tree)] if config.tools_tree else []), *([f"--extra-search-path={p}" for p in config.extra_search_paths]), + *(["--proxy-url", config.proxy_url] if config.proxy_url else []), + *(["--proxy-peer-certificate", str(p)] if (p := config.proxy_peer_certificate) else []), + *(["--proxy-client-certificate", str(p)] if (p := config.proxy_client_certificate) else []), + *(["--proxy-client-key", str(p)] if (p := config.proxy_client_key) else []), *(["-f"] * args.force), ] @@ -3932,6 +3936,10 @@ def finalize_default_tools(args: Args, config: Config, *, resources: Path) -> Co *(["--source-date-epoch", str(config.source_date_epoch)] if config.source_date_epoch is not None else []), *([f"--environment={k}='{v}'" for k, v in config.environment.items()]), *([f"--extra-search-path={p}" for p in config.extra_search_paths]), + *(["--proxy-url", config.proxy_url] if config.proxy_url else []), + *(["--proxy-peer-certificate", str(p)] if (p := config.proxy_peer_certificate) else []), + *(["--proxy-client-certificate", str(p)] if (p := config.proxy_client_certificate) else []), + *(["--proxy-client-key", str(p)] if (p := config.proxy_client_key) else []), *(["-f"] * args.force), ] diff --git a/mkosi/config.py b/mkosi/config.py index d100dd346..c2e6e2a4d 100644 --- a/mkosi/config.py +++ b/mkosi/config.py @@ -677,6 +677,21 @@ def config_default_source_date_epoch(namespace: argparse.Namespace) -> Optional[ return config_parse_source_date_epoch(s, None) +def config_default_proxy_url(namespace: argparse.Namespace) -> Optional[str]: + names = ("http_proxy", "https_proxy", "HTTP_PROXY", "HTTPS_PROXY") + + for env in namespace.environment: + k, _, v = env.partition("=") + if k in names: + return cast(str, v) + + for k, v in os.environ.items(): + if k in names: + return cast(str, v) + + return None + + def config_default_kernel_command_line(namespace: argparse.Namespace) -> list[str]: return [f"console={namespace.architecture.default_serial_tty()}"] @@ -1366,6 +1381,10 @@ class Config: sign: bool key: Optional[str] + proxy_url: Optional[str] + proxy_peer_certificate: Optional[Path] + proxy_client_certificate: Optional[Path] + proxy_client_key: Optional[Path] incremental: bool nspawn_settings: Optional[Path] extra_search_paths: list[Path] @@ -1572,6 +1591,9 @@ class Config: ) -> list[PathString]: mounts = [ *[Mount(d, d, ro=True) for d in self.extra_search_paths if not relaxed and not self.tools_tree], + *([Mount(p, "/proxy.cacert", ro=True)] if (p := self.proxy_peer_certificate) else []), + *([Mount(p, "/proxy.clientcert", ro=True)] if (p := self.proxy_client_certificate) else []), + *([Mount(p, "/proxy.clientkey", ro=True)] if (p := self.proxy_client_key) else []), *mounts, ] @@ -2477,6 +2499,38 @@ SETTINGS = ( help="GPG key to use for signing", ), + ConfigSetting( + dest="proxy_url", + section="Host", + default_factory=config_default_proxy_url, + default_factory_depends=("environment",), + metavar="URL", + help="Set the proxy to use", + ), + ConfigSetting( + dest="proxy_peer_certificate", + section="Host", + parse=config_make_path_parser(), + paths=( + "/etc/pki/tls/certs/ca-bundle.crt", + "/etc/ssl/certs/ca-certificates.crt", + ), + help="Set the proxy peer certificate", + ), + ConfigSetting( + dest="proxy_client_certificate", + section="Host", + parse=config_make_path_parser(secret=True), + help="Set the proxy client certificate", + ), + ConfigSetting( + dest="proxy_client_key", + section="Host", + default_factory=lambda ns: ns.proxy_client_certificate, + default_factory_depends=("proxy_client_certificate",), + parse=config_make_path_parser(secret=True), + help="Set the proxy client key", + ), ConfigSetting( dest="incremental", short="-i", @@ -3493,10 +3547,16 @@ def load_environment(args: argparse.Namespace) -> dict[str, str]: env["IMAGE_VERSION"] = args.image_version if args.source_date_epoch is not None: env["SOURCE_DATE_EPOCH"] = str(args.source_date_epoch) - if proxy := os.getenv("http_proxy"): - env["http_proxy"] = proxy - if proxy := os.getenv("https_proxy"): - env["https_proxy"] = proxy + if args.proxy_url is not None: + for e in ("http_proxy", "https_proxy"): + env[e] = args.proxy_url + env[e.upper()] = args.proxy_url + if args.proxy_peer_certificate: + env["GIT_PROXY_SSL_CAINFO"] = "/proxy.cacert" + if args.proxy_client_certificate: + env["GIT_PROXY_SSL_CERT"] = "/proxy.clientcert" + if args.proxy_client_key: + env["GIT_PROXY_SSL_KEY"] = "/proxy.clientkey" if dnf := os.getenv("MKOSI_DNF"): env["MKOSI_DNF"] = dnf @@ -3737,6 +3797,10 @@ def summary(config: Config) -> str: summary += f"""\ {bold("HOST CONFIGURATION")}: + Proxy URL: {none_to_none(config.proxy_url)} + Proxy Peer Certificate: {none_to_none(config.proxy_peer_certificate)} + Proxy Client Certificate: {none_to_none(config.proxy_client_certificate)} + Proxy Client Key: {none_to_none(config.proxy_client_key)} Incremental: {yes_no(config.incremental)} NSpawn Settings: {none_to_none(config.nspawn_settings)} Extra Search Paths: {line_join_list(config.extra_search_paths)} diff --git a/mkosi/distributions/opensuse.py b/mkosi/distributions/opensuse.py index 8451e2230..19dc7e930 100644 --- a/mkosi/distributions/opensuse.py +++ b/mkosi/distributions/opensuse.py @@ -164,6 +164,10 @@ def fetch_gpgurls(context: Context, repourl: str) -> tuple[str, ...]: "--remote-name", "--no-progress-meter", "--fail", + *(["--proxy", context.config.proxy_url] if context.config.proxy_url else []), + *(["--proxy-capath", "/proxy.cacert"] if context.config.proxy_peer_certificate else []), + *(["--proxy-cert", "/proxy.clientcert"] if context.config.proxy_client_certificate else []), + *(["--proxy-key", "/proxy.clientkey"] if context.config.proxy_client_key else []), f"{repourl}/repodata/repomd.xml", ], sandbox=context.sandbox( diff --git a/mkosi/installer/apt.py b/mkosi/installer/apt.py index c8c007857..8f9c1a8a9 100644 --- a/mkosi/installer/apt.py +++ b/mkosi/installer/apt.py @@ -172,6 +172,12 @@ class Apt(PackageManager): "-o", "DPkg::Options::=--path-exclude=/usr/share/info/*", ] + if context.config.proxy_url: + cmdline += [ + "-o", f"Acquire::http::Proxy={context.config.proxy_url}", + "-o", f"Acquire::https::Proxy={context.config.proxy_url}", + ] + return cmdline @classmethod diff --git a/mkosi/installer/dnf.py b/mkosi/installer/dnf.py index 0c17e8776..1544722f3 100644 --- a/mkosi/installer/dnf.py +++ b/mkosi/installer/dnf.py @@ -148,6 +148,15 @@ class Dnf(PackageManager): "--setopt=varsdir=/etc/dnf/vars", ] + if context.config.proxy_url: + cmdline += [f"--setopt=proxy={context.config.proxy_url}"] + if context.config.proxy_peer_certificate: + cmdline += ["--setopt=proxy_sslcacert=/proxy.cacert"] + if context.config.proxy_client_certificate: + cmdline += ["--setopt=proxy_sslclientcert=/proxy.clientcert"] + if context.config.proxy_client_key: + cmdline += ["--setopt=proxy_sslclientkey=/proxy.clientkey"] + return cmdline @classmethod diff --git a/mkosi/resources/mkosi.md b/mkosi/resources/mkosi.md index 0e5217c65..d16611f8b 100644 --- a/mkosi/resources/mkosi.md +++ b/mkosi/resources/mkosi.md @@ -1417,6 +1417,34 @@ boolean argument: either `1`, `yes`, or `true` to enable, or `0`, `no`, ### [Host] Section +`ProxyUrl=`, `--proxy-url=` + +: Configure a proxy to be used for all outgoing network connections. + Various tools that mkosi invokes and for which the proxy can be + configured are configured to use this proxy. mkosi also sets various + well-known environment variables to specify the proxy to use for any + programs it invokes that may need internet access. + +`ProxyPeerCertificate=`, `--proxy-peer-certificate=` + +: Configure a file containing certificates used to verify the proxy. + Defaults to the system-wide certificate store. Note that not all + package managers that mkosi uses have support for specifying a proxy + peer certificate. + +`ProxyClientCertificate=`, `--proxy-client-certificate=` + +: Configure a file containing the certificate used to authenticate the + client with the proxy. Note that not all package managers that mkosi + uses have support for specifying a proxy client certificate. + +`ProxyClientKey=`, `--proxy-client-key=` + +: Configure a file containing the private key used to authenticate the + client with the proxy. Defaults to the proxy client certificate if one + is provided. Note that not all package managers that mkosi uses have + support for specifying a proxy client key. + `Incremental=`, `--incremental=`, `-i` : Enable incremental build mode. In this mode, a copy of the OS image is diff --git a/tests/test_json.py b/tests/test_json.py index 0b0859c21..3cd112935 100644 --- a/tests/test_json.py +++ b/tests/test_json.py @@ -203,6 +203,10 @@ def test_config() -> None: "/run/foo" ], "Profile": "profile", + "ProxyClientCertificate": "/my/client/cert", + "ProxyClientKey": "/my/client/key", + "ProxyPeerCertificate": "/my/peer/cert", + "ProxyUrl": "https://my/proxy", "QemuArgs": [], "QemuCdrom": false, "QemuDrives": [ @@ -393,6 +397,10 @@ def test_config() -> None: postinst_scripts = [Path("/bar/qux")], prepare_scripts = [Path("/run/foo")], profile = "profile", + proxy_client_certificate = Path("/my/client/cert"), + proxy_client_key = Path("/my/client/key"), + proxy_peer_certificate = Path("/my/peer/cert"), + proxy_url = "https://my/proxy", qemu_args = [], qemu_cdrom = False, qemu_drives = [QemuDrive("abc", 200, Path("/foo/bar"), "abc,qed"), QemuDrive("abc", 200, None, "")],