Skip to content

Commit 1aec117

Browse files
authored
Merge pull request #323 from superfly/basic-auth-upstream
Support container registries using Basic Auth
2 parents 654b344 + d5778dc commit 1aec117

File tree

1 file changed

+48
-14
lines changed

1 file changed

+48
-14
lines changed

src/overlaybd/registryfs/registryfs_v2.cpp

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ using namespace photon::net::http;
5151
static const estring kDockerRegistryAuthChallengeKeyValuePrefix = "www-authenticate";
5252
static const estring kAuthHeaderKey = "Authorization";
5353
static const estring kBearerAuthPrefix = "Bearer ";
54+
static const estring kBasicAuthPrefix = "Basic ";
5455
static const uint64_t kMinimalTokenLife = 30L * 1000 * 1000; // token lives atleast 30s
5556
static const uint64_t kMinimalAUrlLife = 300L * 1000 * 1000; // actual_url lives atleast 300s
5657
static const uint64_t kMinimalMetaLife = 300L * 1000 * 1000; // actual_url lives atleast 300s
@@ -84,6 +85,13 @@ struct UrlInfo {
8485
estring info;
8586
};
8687

88+
enum class AuthType {
89+
Unknown,
90+
None,
91+
Basic,
92+
Bearer
93+
};
94+
8795
class RegistryFSImpl_v2 : public RegistryFS {
8896
public:
8997
UNIMPLEMENTED_POINTER(IFile *creat(const char *, mode_t) override);
@@ -197,10 +205,12 @@ class RegistryFSImpl_v2 : public RegistryFS {
197205
Timeout tmo(timeout);
198206
estring authurl, scope;
199207
estring *token = nullptr;
200-
if (get_scope_auth(url, &authurl, &scope, tmo.timeout()) < 0)
208+
209+
auto authtype = get_scope_auth(url, &authurl, &scope, tmo.timeout());
210+
if (authtype == AuthType::Unknown)
201211
return nullptr;
202212

203-
if (!scope.empty()) {
213+
if (authtype == AuthType::Bearer && !scope.empty()) {
204214
token = m_scope_token.acquire(scope, [&]() -> estring * {
205215
estring *token = new estring();
206216
if (get_token(url, authurl, *token, tmo.timeout()) < 0) {
@@ -217,9 +227,15 @@ class RegistryFSImpl_v2 : public RegistryFS {
217227
HTTP_OP op(m_client, Verb::GET, url);
218228
op.follow = 0;
219229
op.retry = 0;
220-
if (token && !token->empty()) {
230+
if (authtype == AuthType::Bearer && token && !token->empty()) {
221231
op.req.headers.insert(kAuthHeaderKey, "Bearer ");
222232
op.req.headers.value_append(*token);
233+
} else if (authtype == AuthType::Basic) {
234+
auto auth = m_callback(url.data());
235+
estring userpwd_b64;
236+
photon::net::Base64Encode(estring().appends(auth.first, ":", auth.second), userpwd_b64);
237+
op.req.headers.insert(kAuthHeaderKey, "Basic ");
238+
op.req.headers.value_append(userpwd_b64);
223239
}
224240
op.timeout = tmo.timeout();
225241
op.call();
@@ -236,8 +252,15 @@ class RegistryFSImpl_v2 : public RegistryFS {
236252
}
237253
if (code == 200) {
238254
UrlInfo *info = new UrlInfo{UrlMode::Self, ""};
239-
if (token && !token->empty())
255+
if (authtype == AuthType::Bearer && token && !token->empty())
240256
info->info = kBearerAuthPrefix + *token;
257+
else if (authtype == AuthType::Basic) {
258+
auto auth = m_callback(url.data());
259+
estring userpwd_b64;
260+
photon::net::Base64Encode(estring().appends(auth.first, ":", auth.second), userpwd_b64);
261+
info->info = kBasicAuthPrefix + userpwd_b64;
262+
}
263+
241264
if (!scope.empty())
242265
m_scope_token.release(scope);
243266
return info;
@@ -268,8 +291,11 @@ class RegistryFSImpl_v2 : public RegistryFS {
268291
int refresh_token(const estring &url, estring &token) {
269292
estring authurl, scope;
270293
Timeout tmo(m_timeout);
271-
if (get_scope_auth(url, &authurl, &scope, tmo.timeout(), true) < 0)
294+
auto authtype = get_scope_auth(url, &authurl, &scope, tmo.timeout(), true);
295+
if (authtype == AuthType::Unknown)
272296
return -1;
297+
if (authtype == AuthType::Basic || authtype == AuthType::None)
298+
return 0;
273299
if (!scope.empty()) {
274300
get_token(url, authurl, token, tmo.timeout());
275301
if (token.empty()) {
@@ -290,7 +316,7 @@ class RegistryFSImpl_v2 : public RegistryFS {
290316
ObjectCache<estring, estring *> m_scope_token;
291317
ObjectCache<estring, UrlInfo *> m_url_info;
292318

293-
int get_scope_auth(const estring &url, estring *authurl, estring *scope, uint64_t timeout,
319+
AuthType get_scope_auth(const estring &url, estring *authurl, estring *scope, uint64_t timeout,
294320
bool push = false) {
295321
Timeout tmo(timeout);
296322
auto verb = push ? Verb::POST : Verb::GET;
@@ -305,32 +331,40 @@ class RegistryFSImpl_v2 : public RegistryFS {
305331
op.timeout = tmo.timeout();
306332
op.call();
307333
if (op.status_code == -1)
308-
LOG_ERROR_RETURN(ENOENT, -1, "connection failed");
334+
LOG_ERROR_RETURN(ENOENT, AuthType::Unknown, "connection failed");
309335

310336
// going to challenge
311337
if (op.status_code != 401 && op.status_code != 403) {
312338
// no token request accepted, seems token not need;
313-
return 0;
339+
return AuthType::None;
314340
}
315341

316342
auto it = op.resp.headers.find(kDockerRegistryAuthChallengeKeyValuePrefix);
317343
if (it == op.resp.headers.end())
318-
LOG_ERROR_RETURN(EINVAL, -1, "no auth header in response");
344+
LOG_ERROR_RETURN(EINVAL, AuthType::Unknown, "no auth header in response");
319345
estring challengeLine = it.second();
320-
if (!challengeLine.starts_with(kBearerAuthPrefix))
321-
LOG_ERROR_RETURN(EINVAL, -1, "auth string shows not bearer auth, ",
346+
347+
estring prefix = "";
348+
349+
if (challengeLine.starts_with(kBearerAuthPrefix))
350+
prefix = kBearerAuthPrefix;
351+
else if (challengeLine.starts_with(kBasicAuthPrefix))
352+
return AuthType::Basic;
353+
else
354+
LOG_ERROR_RETURN(EINVAL, AuthType::Unknown, "auth string shows not bearer or basic auth, ",
322355
VALUE(challengeLine));
323-
challengeLine = challengeLine.substr(kBearerAuthPrefix.length());
356+
357+
challengeLine = challengeLine.substr(prefix.length());
324358
auto kv = str_to_kvmap(challengeLine);
325359
if (kv.find("realm") == kv.end() || kv.find("service") == kv.end() ||
326360
kv.find("scope") == kv.end()) {
327-
LOG_ERROR_RETURN(EINVAL, -1, "authentication challenge failed with `",
361+
LOG_ERROR_RETURN(EINVAL, AuthType::Unknown, "authentication challenge failed with `",
328362
challengeLine);
329363
}
330364
*scope = estring(kv["scope"]);
331365
*authurl = estring().appends(kv["realm"], "?service=", kv["service"],
332366
"&scope=", kv["scope"]);
333-
return 0;
367+
return AuthType::Bearer;
334368
}
335369

336370
int parse_token(const estring &jsonStr, estring *token) {

0 commit comments

Comments
 (0)