From fea2728cc23f3e9c96d1103196a75bac1776642e Mon Sep 17 00:00:00 2001 From: Julius Date: Tue, 17 Mar 2026 18:22:22 -0700 Subject: [PATCH 1/2] feat: adding `https` mode to credentialConfig Signed-off-by: Julius --- README.md | 29 ++++++++++++++++++++++++++- src/config.h | 3 +++ src/image_service.cpp | 46 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a7a9d741..5f935cab 100644 --- a/README.md +++ b/README.md @@ -193,8 +193,11 @@ Default configure file `overlaybd.json` is installed to `/etc/overlaybd/`. | gzipCacheConfig.cacheSizeGB | The max size of cache, in GB. | | gzipCacheConfig.refillSize | The refill size from source, in byte. `262144` is default (256 KB). | | credentialFilePath(legacy) | The credential used for fetching images on registry. `/opt/overlaybd/cred.json` is the default value. | -| credentialConfig.mode | Authentication mode for lazy-loading.
- `file` means reading credential from `credentialConfig.path`.
- `http` means sending an http request to `credentialConfig.path` | +| credentialConfig.mode | Authentication mode for lazy-loading.
- `file` means reading credential from `credentialConfig.path`.
- `http` means sending an http request to `credentialConfig.path`
- `https` means sending an https request with mTLS (mutual TLS) to `credentialConfig.path` | | credentialConfig.path | credential file path or url which is determined by `mode` | +| credentialConfig.client_cert_path | Path to the client certificate file for mTLS (used by `https` mode) | +| credentialConfig.client_key_path | Path to the client private key file for mTLS (used by `https` mode) | +| credentialConfig.server_ca_path | Path to the CA certificate used to verify the server for mTLS (used by `https` mode) | | download.enable | Whether background downloading is enabled or not. | | download.delay | The seconds waiting to start downloading task after the overlaybd device launched. | | download.delayExtra | A random extra delay is attached to delay, avoiding too many tasks started at the same time. | @@ -293,6 +296,30 @@ Overlaybd supports serveral credential mode. Here are some example `credentialCo ``` we write a sample http server in `test/simple_auth_server.cpp` +- mode **https** + + the `credentialConfig.path` should be an HTTPS server listening address. This mode uses mTLS (mutual TLS) for secure communication with the auth server. The client presents its certificate and key, and verifies the server against a trusted CA certificate. + +```json +#### /etc/overlaybd/config.json #### +{ + "logLevel": 1, + "logPath": "/var/log/overlaybd.log", + ... + "credentialConfig": { + "mode": "https", + "path": "https://localhost:19876/auth", + "client_cert_path": "/etc/overlaybd/client.crt", + "client_key_path": "/etc/overlaybd/client.key", + "server_ca_path": "/etc/overlaybd/ca.crt" + }, + ... +} +``` + overlaybd will send an https request with mTLS to the server with `remote_url` like this: +> GET "https://localhost:19876/auth?remote_url=https://hub.docker.com/v2/overlaybd/ubuntu/blobs/sha256:47e63559a8487efb55b2f1ccea9cfc04110a185c49785fdf1329d1ea462ce5f0" + the server response format is the same as the `http` mode. + ## Usage diff --git a/src/config.h b/src/config.h index d2640194..d89a210a 100644 --- a/src/config.h +++ b/src/config.h @@ -97,6 +97,9 @@ struct CredentialConfig : public ConfigUtils::Config { APPCFG_PARA(mode, std::string, ""); APPCFG_PARA(path, std::string, ""); APPCFG_PARA(timeout, int, 1); + APPCFG_PARA(client_cert_path, std::string, ""); + APPCFG_PARA(client_key_path, std::string, ""); + APPCFG_PARA(server_ca_path, std::string, ""); }; struct CacheConfig : public ConfigUtils::Config { diff --git a/src/image_service.cpp b/src/image_service.cpp index 3d39c57a..be090655 100644 --- a/src/image_service.cpp +++ b/src/image_service.cpp @@ -166,6 +166,45 @@ int load_cred_from_http(const std::string addr /* http server */, const std::str return parse_auths(response.data().auths(), remote_path, username, password); } +int load_cred_from_https(const std::string addr /* https server */, const std::string &remote_path, + std::string &username, std::string &password, int timeout, + const std::string &client_cert_path, const std::string &client_key_path, + const std::string &server_ca_path) { + + auto request = new photon::net::cURL(); + DEFER({ delete request; }); + + // Configure mTLS: client certificate, client key, and server CA verification + if (!server_ca_path.empty()) { + request->set_cafile(server_ca_path.c_str()); + request->setopt(CURLOPT_SSL_VERIFYPEER, 1L).setopt(CURLOPT_SSL_VERIFYHOST, 2L); + } + if (!client_cert_path.empty() && !client_key_path.empty()) { + request->setopt(CURLOPT_SSLCERT, client_cert_path.c_str()); + request->setopt(CURLOPT_SSLKEY, client_key_path.c_str()); + } + + auto request_url = addr + "?remote_url=" + remote_path; + LOG_INFO("request url: `", request_url); + photon::net::StringWriter writer; + auto ret = request->GET(request_url.c_str(), &writer, (int64_t)timeout * 1000000); + if (ret != 200) { + LOG_ERRNO_RETURN(0, -1, "connect to auth component failed. http response code: `", ret); + } + LOG_DEBUG(writer.string); + ImageAuthResponse response; + LOG_DEBUG("response size: `", writer.string.size()); + if (response.ParseJSONStream(writer.string) == false) { + LOG_ERRNO_RETURN(0, -1, "parse http response message failed: `", writer.string); + } + LOG_INFO("traceId: `, succ: `", response.traceId(), response.success()); + if (response.success() == false) { + LOG_ERRNO_RETURN(0, -1, "http request failed."); + } + ImageConfigNS::AuthConfig cfg; + return parse_auths(response.data().auths(), remote_path, username, password); +} + int ImageService::read_global_config_and_set() { LOG_INFO("using config `", m_config_path); if (!global_conf.ParseJSON(m_config_path)) { @@ -241,6 +280,13 @@ ImageService::reload_auth(const char *remote_path) { } else if (mode == "http") { auto timeout = global_conf.credentialConfig().timeout(); res = load_cred_from_http(path, std::string(remote_path), username, password, timeout); + } else if (mode == "https") { + auto timeout = global_conf.credentialConfig().timeout(); + auto client_cert = global_conf.credentialConfig().client_cert_path(); + auto client_key = global_conf.credentialConfig().client_key_path(); + auto server_ca = global_conf.credentialConfig().server_ca_path(); + res = load_cred_from_https(path, std::string(remote_path), username, password, + timeout, client_cert, client_key, server_ca); } else { LOG_ERROR("invalid mode for authentication."); return std::make_pair("",""); From 448b599411582f72478580d06bb1df33a361a1da Mon Sep 17 00:00:00 2001 From: Julius Date: Tue, 24 Mar 2026 15:14:32 -0700 Subject: [PATCH 2/2] chore: polish settings and docs Signed-off-by: Julius --- README.md | 15 ++++++++++----- src/image_service.cpp | 6 +++++- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 5f935cab..0927eef1 100644 --- a/README.md +++ b/README.md @@ -193,11 +193,11 @@ Default configure file `overlaybd.json` is installed to `/etc/overlaybd/`. | gzipCacheConfig.cacheSizeGB | The max size of cache, in GB. | | gzipCacheConfig.refillSize | The refill size from source, in byte. `262144` is default (256 KB). | | credentialFilePath(legacy) | The credential used for fetching images on registry. `/opt/overlaybd/cred.json` is the default value. | -| credentialConfig.mode | Authentication mode for lazy-loading.
- `file` means reading credential from `credentialConfig.path`.
- `http` means sending an http request to `credentialConfig.path`
- `https` means sending an https request with mTLS (mutual TLS) to `credentialConfig.path` | +| credentialConfig.mode | Authentication mode for lazy-loading.
- `file` means reading credential from `credentialConfig.path`.
- `http` means sending an http request to `credentialConfig.path`
- `https` means sending an https request to `credentialConfig.path`, with optional client certificate authentication and CA pinning | | credentialConfig.path | credential file path or url which is determined by `mode` | -| credentialConfig.client_cert_path | Path to the client certificate file for mTLS (used by `https` mode) | -| credentialConfig.client_key_path | Path to the client private key file for mTLS (used by `https` mode) | -| credentialConfig.server_ca_path | Path to the CA certificate used to verify the server for mTLS (used by `https` mode) | +| credentialConfig.client_cert_path | Optional. Path to the client certificate file (`https` mode). May contain the private key in the same PEM file. | +| credentialConfig.client_key_path | Optional. Path to the client private key file (`https` mode). Only needed when the key is separate from the certificate. | +| credentialConfig.server_ca_path | Optional. Path to the CA certificate used to verify the server (`https` mode). If omitted, the system CA bundle is used. When set, **only** this CA file is trusted. | | download.enable | Whether background downloading is enabled or not. | | download.delay | The seconds waiting to start downloading task after the overlaybd device launched. | | download.delayExtra | A random extra delay is attached to delay, avoiding too many tasks started at the same time. | @@ -298,7 +298,7 @@ we write a sample http server in `test/simple_auth_server.cpp` - mode **https** - the `credentialConfig.path` should be an HTTPS server listening address. This mode uses mTLS (mutual TLS) for secure communication with the auth server. The client presents its certificate and key, and verifies the server against a trusted CA certificate. + the `credentialConfig.path` should be an HTTPS server listening address. Unlike `http` mode, the `https://` scheme prefix must be included in the path (e.g. `https://localhost:19876/auth`). The optional `client_cert_path`/`client_key_path` fields enable client certificate authentication, and `server_ca_path` pins trust to a specific CA. For a local auth server, providing all three fields secures communication exclusively with that server (mutual TLS). ```json #### /etc/overlaybd/config.json #### @@ -320,6 +320,11 @@ we write a sample http server in `test/simple_auth_server.cpp` > GET "https://localhost:19876/auth?remote_url=https://hub.docker.com/v2/overlaybd/ubuntu/blobs/sha256:47e63559a8487efb55b2f1ccea9cfc04110a185c49785fdf1329d1ea462ce5f0" the server response format is the same as the `http` mode. + All three TLS fields are optional and independently configured: + - `client_cert_path` sets the client certificate. If the PEM file also contains the private key, `client_key_path` can be omitted. + - `client_key_path` sets the client private key. Only needed when the key is in a separate file from the certificate. + - If `server_ca_path` is omitted, the system CA bundle is used to verify the server certificate. When `server_ca_path` is set, **only** the specified CA file is used — the system CA bundle is not consulted. + ## Usage diff --git a/src/image_service.cpp b/src/image_service.cpp index be090655..bb79f97c 100644 --- a/src/image_service.cpp +++ b/src/image_service.cpp @@ -179,8 +179,12 @@ int load_cred_from_https(const std::string addr /* https server */, const std::s request->set_cafile(server_ca_path.c_str()); request->setopt(CURLOPT_SSL_VERIFYPEER, 1L).setopt(CURLOPT_SSL_VERIFYHOST, 2L); } - if (!client_cert_path.empty() && !client_key_path.empty()) { + if (!client_cert_path.empty()) { request->setopt(CURLOPT_SSLCERT, client_cert_path.c_str()); + } + // When CURLOPT_SSLKEY is not set, libcurl expects the private key to be + // bundled in the same PEM file as the client certificate. + if (!client_key_path.empty()) { request->setopt(CURLOPT_SSLKEY, client_key_path.c_str()); }