Skip to content

Commit d3fab24

Browse files
authored
Merge pull request #397 from juliusl/pr/add-https-auth-mode
Adds `https` mode to credentialConfig
2 parents d5f4b06 + 448b599 commit d3fab24

3 files changed

Lines changed: 86 additions & 1 deletion

File tree

README.md

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,8 +193,11 @@ Default configure file `overlaybd.json` is installed to `/etc/overlaybd/`.
193193
| gzipCacheConfig.cacheSizeGB | The max size of cache, in GB. |
194194
| gzipCacheConfig.refillSize | The refill size from source, in byte. `262144` is default (256 KB). |
195195
| credentialFilePath(legacy) | The credential used for fetching images on registry. `/opt/overlaybd/cred.json` is the default value. |
196-
| credentialConfig.mode | Authentication mode for lazy-loading. <br> - `file` means reading credential from `credentialConfig.path`. <br> - `http` means sending an http request to `credentialConfig.path` |
196+
| credentialConfig.mode | Authentication mode for lazy-loading. <br> - `file` means reading credential from `credentialConfig.path`. <br> - `http` means sending an http request to `credentialConfig.path` <br> - `https` means sending an https request to `credentialConfig.path`, with optional client certificate authentication and CA pinning |
197197
| credentialConfig.path | credential file path or url which is determined by `mode` |
198+
| credentialConfig.client_cert_path | Optional. Path to the client certificate file (`https` mode). May contain the private key in the same PEM file. |
199+
| credentialConfig.client_key_path | Optional. Path to the client private key file (`https` mode). Only needed when the key is separate from the certificate. |
200+
| 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. |
198201
| download.enable | Whether background downloading is enabled or not. |
199202
| download.delay | The seconds waiting to start downloading task after the overlaybd device launched. |
200203
| download.delayExtra | A random extra delay is attached to delay, avoiding too many tasks started at the same time. |
@@ -293,6 +296,35 @@ Overlaybd supports serveral credential mode. Here are some example `credentialCo
293296
```
294297
we write a sample http server in `test/simple_auth_server.cpp`
295298

299+
- mode **https**
300+
301+
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).
302+
303+
```json
304+
#### /etc/overlaybd/config.json ####
305+
{
306+
"logLevel": 1,
307+
"logPath": "/var/log/overlaybd.log",
308+
...
309+
"credentialConfig": {
310+
"mode": "https",
311+
"path": "https://localhost:19876/auth",
312+
"client_cert_path": "/etc/overlaybd/client.crt",
313+
"client_key_path": "/etc/overlaybd/client.key",
314+
"server_ca_path": "/etc/overlaybd/ca.crt"
315+
},
316+
...
317+
}
318+
```
319+
overlaybd will send an https request with mTLS to the server with `remote_url` like this:
320+
> GET "https://localhost:19876/auth?remote_url=https://hub.docker.com/v2/overlaybd/ubuntu/blobs/sha256:47e63559a8487efb55b2f1ccea9cfc04110a185c49785fdf1329d1ea462ce5f0"
321+
the server response format is the same as the `http` mode.
322+
323+
All three TLS fields are optional and independently configured:
324+
- `client_cert_path` sets the client certificate. If the PEM file also contains the private key, `client_key_path` can be omitted.
325+
- `client_key_path` sets the client private key. Only needed when the key is in a separate file from the certificate.
326+
- 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.
327+
296328

297329
## Usage
298330

src/config.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ struct CredentialConfig : public ConfigUtils::Config {
9797
APPCFG_PARA(mode, std::string, "");
9898
APPCFG_PARA(path, std::string, "");
9999
APPCFG_PARA(timeout, int, 1);
100+
APPCFG_PARA(client_cert_path, std::string, "");
101+
APPCFG_PARA(client_key_path, std::string, "");
102+
APPCFG_PARA(server_ca_path, std::string, "");
100103
};
101104

102105
struct CacheConfig : public ConfigUtils::Config {

src/image_service.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,49 @@ int load_cred_from_http(const std::string addr /* http server */, const std::str
166166
return parse_auths(response.data().auths(), remote_path, username, password);
167167
}
168168

169+
int load_cred_from_https(const std::string addr /* https server */, const std::string &remote_path,
170+
std::string &username, std::string &password, int timeout,
171+
const std::string &client_cert_path, const std::string &client_key_path,
172+
const std::string &server_ca_path) {
173+
174+
auto request = new photon::net::cURL();
175+
DEFER({ delete request; });
176+
177+
// Configure mTLS: client certificate, client key, and server CA verification
178+
if (!server_ca_path.empty()) {
179+
request->set_cafile(server_ca_path.c_str());
180+
request->setopt(CURLOPT_SSL_VERIFYPEER, 1L).setopt(CURLOPT_SSL_VERIFYHOST, 2L);
181+
}
182+
if (!client_cert_path.empty()) {
183+
request->setopt(CURLOPT_SSLCERT, client_cert_path.c_str());
184+
}
185+
// When CURLOPT_SSLKEY is not set, libcurl expects the private key to be
186+
// bundled in the same PEM file as the client certificate.
187+
if (!client_key_path.empty()) {
188+
request->setopt(CURLOPT_SSLKEY, client_key_path.c_str());
189+
}
190+
191+
auto request_url = addr + "?remote_url=" + remote_path;
192+
LOG_INFO("request url: `", request_url);
193+
photon::net::StringWriter writer;
194+
auto ret = request->GET(request_url.c_str(), &writer, (int64_t)timeout * 1000000);
195+
if (ret != 200) {
196+
LOG_ERRNO_RETURN(0, -1, "connect to auth component failed. http response code: `", ret);
197+
}
198+
LOG_DEBUG(writer.string);
199+
ImageAuthResponse response;
200+
LOG_DEBUG("response size: `", writer.string.size());
201+
if (response.ParseJSONStream(writer.string) == false) {
202+
LOG_ERRNO_RETURN(0, -1, "parse http response message failed: `", writer.string);
203+
}
204+
LOG_INFO("traceId: `, succ: `", response.traceId(), response.success());
205+
if (response.success() == false) {
206+
LOG_ERRNO_RETURN(0, -1, "http request failed.");
207+
}
208+
ImageConfigNS::AuthConfig cfg;
209+
return parse_auths(response.data().auths(), remote_path, username, password);
210+
}
211+
169212
int ImageService::read_global_config_and_set() {
170213
LOG_INFO("using config `", m_config_path);
171214
if (!global_conf.ParseJSON(m_config_path)) {
@@ -241,6 +284,13 @@ ImageService::reload_auth(const char *remote_path) {
241284
} else if (mode == "http") {
242285
auto timeout = global_conf.credentialConfig().timeout();
243286
res = load_cred_from_http(path, std::string(remote_path), username, password, timeout);
287+
} else if (mode == "https") {
288+
auto timeout = global_conf.credentialConfig().timeout();
289+
auto client_cert = global_conf.credentialConfig().client_cert_path();
290+
auto client_key = global_conf.credentialConfig().client_key_path();
291+
auto server_ca = global_conf.credentialConfig().server_ca_path();
292+
res = load_cred_from_https(path, std::string(remote_path), username, password,
293+
timeout, client_cert, client_key, server_ca);
244294
} else {
245295
LOG_ERROR("invalid mode for authentication.");
246296
return std::make_pair("","");

0 commit comments

Comments
 (0)