From 049d0914422430985ed010da9193e5c260fbdfe9 Mon Sep 17 00:00:00 2001 From: George Shammas Date: Sun, 21 Apr 2024 14:51:19 -0400 Subject: [PATCH] libstore: Add the ability to use different auth methods with Http Binary Caches --- maintainers/flake-module.nix | 1 + src/libstore/filetransfer.cc | 12 +++++++++++- src/libstore/filetransfer.hh | 4 ++++ src/libstore/http-binary-cache-store.cc | 24 ++++++++++++++++++++--- src/libstore/http-binary-cache-store.hh | 26 +++++++++++++++++++++++++ src/libstore/package.nix | 1 + 6 files changed, 64 insertions(+), 4 deletions(-) diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index c3eaf671c..54efa8919 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -164,6 +164,7 @@ ''^src/libstore/globals\.cc$'' ''^src/libstore/globals\.hh$'' ''^src/libstore/http-binary-cache-store\.cc$'' + ''^src/libstore/http-binary-cache-store\.hh$'' ''^src/libstore/legacy-ssh-store\.cc$'' ''^src/libstore/legacy-ssh-store\.hh$'' ''^src/libstore/length-prefixed-protocol-helper\.hh$'' diff --git a/src/libstore/filetransfer.cc b/src/libstore/filetransfer.cc index 5ea8b6f96..4c1bfb0e9 100644 --- a/src/libstore/filetransfer.cc +++ b/src/libstore/filetransfer.cc @@ -320,7 +320,9 @@ struct curlFileTransfer : public FileTransfer curl_easy_setopt(req, CURLOPT_PIPEWAIT, 1); #endif #if LIBCURL_VERSION_NUM >= 0x072f00 - if (fileTransferSettings.enableHttp2) + // Our writeCallbackWrapper does not support rewinding which breaks + // negotiate/kerberos auth over http/2. + if (fileTransferSettings.enableHttp2 && request.authmethod != HttpAuthMethod::NEGOTIATE) curl_easy_setopt(req, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS); else curl_easy_setopt(req, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); @@ -357,6 +359,14 @@ struct curlFileTransfer : public FileTransfer curl_easy_setopt(req, CURLOPT_SSL_VERIFYHOST, 0); } + curl_easy_setopt(req, CURLOPT_HTTPAUTH, request.authmethod); + if (request.authmethod != HttpAuthMethod::NEGOTIATE) { + curl_easy_setopt(req, CURLOPT_USERNAME, ""); + curl_easy_setopt(req, CURLOPT_PASSWORD, ""); + } else if (request.authmethod != HttpAuthMethod::BEARER) { + curl_easy_setopt(req, CURLOPT_XOAUTH2_BEARER, request.bearer_token.c_str()); + } + curl_easy_setopt(req, CURLOPT_CONNECTTIMEOUT, fileTransferSettings.connectTimeout.get()); curl_easy_setopt(req, CURLOPT_LOW_SPEED_LIMIT, 1L); diff --git a/src/libstore/filetransfer.hh b/src/libstore/filetransfer.hh index d836ab2c4..a541b9e28 100644 --- a/src/libstore/filetransfer.hh +++ b/src/libstore/filetransfer.hh @@ -1,6 +1,8 @@ #pragma once ///@file +#include "http-binary-cache-store.hh" + #include #include @@ -64,6 +66,8 @@ struct FileTransferRequest std::string expectedETag; bool verifyTLS = true; bool head = false; + HttpAuthMethod authmethod = HttpAuthMethod::BASIC; + std::string bearer_token; size_t tries = fileTransferSettings.tries; unsigned int baseRetryTimeMs = 250; ActivityId parentAct; diff --git a/src/libstore/http-binary-cache-store.cc b/src/libstore/http-binary-cache-store.cc index b15ef4e4c..704a15d5c 100644 --- a/src/libstore/http-binary-cache-store.cc +++ b/src/libstore/http-binary-cache-store.cc @@ -8,6 +8,23 @@ namespace nix { MakeError(UploadToHTTP, Error); +HttpAuthMethod parseHttpAuthMethod(const std::string &str) { + static const std::map map = { + {"none", HttpAuthMethod::NONE}, + {"basic", HttpAuthMethod::BASIC}, + {"digest", HttpAuthMethod::DIGEST}, + {"negotiate", HttpAuthMethod::NEGOTIATE}, + {"ntlm", HttpAuthMethod::NTLM}, + {"bearer", HttpAuthMethod::BEARER}, + {"any", HttpAuthMethod::ANY}, + {"anysafe", HttpAuthMethod::ANYSAFE}}; + auto it = map.find(str); + if (it == map.end()) { + throw UsageError("option authmethod has invalid value '%s'", str); + } + return it->second; +} + HttpBinaryCacheStoreConfig::HttpBinaryCacheStoreConfig( std::string_view scheme, @@ -34,7 +51,6 @@ std::string HttpBinaryCacheStoreConfig::doc() ; } - class HttpBinaryCacheStore : public virtual HttpBinaryCacheStoreConfig, public virtual BinaryCacheStore { private: @@ -143,11 +159,13 @@ protected: FileTransferRequest makeRequest(const std::string & path) { - return FileTransferRequest( + auto request = FileTransferRequest( hasPrefix(path, "https://") || hasPrefix(path, "http://") || hasPrefix(path, "file://") ? path : cacheUri + "/" + path); - + request.authmethod = parseHttpAuthMethod(authmethod); + request.bearer_token = bearer_token; + return request; } void getFile(const std::string & path, Sink & sink) override diff --git a/src/libstore/http-binary-cache-store.hh b/src/libstore/http-binary-cache-store.hh index d2fc43210..cdcd57ca0 100644 --- a/src/libstore/http-binary-cache-store.hh +++ b/src/libstore/http-binary-cache-store.hh @@ -1,7 +1,24 @@ +#pragma once +///@file + +#include "types.hh" +#include + #include "binary-cache-store.hh" namespace nix { +enum struct HttpAuthMethod : unsigned long { + NONE = CURLAUTH_NONE, + BASIC = CURLAUTH_BASIC, + DIGEST = CURLAUTH_DIGEST, + NEGOTIATE = CURLAUTH_NEGOTIATE, + NTLM = CURLAUTH_NTLM, + BEARER = CURLAUTH_BEARER, + ANY = CURLAUTH_ANY, + ANYSAFE = CURLAUTH_ANYSAFE +}; + struct HttpBinaryCacheStoreConfig : virtual BinaryCacheStoreConfig { using BinaryCacheStoreConfig::BinaryCacheStoreConfig; @@ -24,6 +41,15 @@ struct HttpBinaryCacheStoreConfig : virtual BinaryCacheStoreConfig return ret; } + const Setting authmethod{this, "basic", "authmethod", + R"( + libcurl auth method to use (`none`, `basic`, `digest`, `bearer`, `negotiate`, `ntlm`, `any`, or `anysafe`). + See https://curl.se/libcurl/c/CURLOPT_HTTPAUTH.html for more info. + )"}; + + const Setting bearer_token{this, "", "bearer-token", + "Bearer token to use for authentication. Requires `authmethod` to be set to `bearer`."}; + std::string doc() override; }; diff --git a/src/libstore/package.nix b/src/libstore/package.nix index 4582ba0d2..4c423684c 100644 --- a/src/libstore/package.nix +++ b/src/libstore/package.nix @@ -72,6 +72,7 @@ mkMesonDerivation (finalAttrs: { propagatedBuildInputs = [ nix-util nlohmann_json + curl ]; preConfigure =