1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
#include "lix/libstore/store-api.hh"
#include "lix/libutil/config.hh"
#include "lix/libstore/http-binary-cache-store.hh"
#include <stdlib.h>
#include <curl/curl.h>

namespace nix {
struct mTLSBinaryCacheStoreConfig : HttpBinaryCacheStoreConfig
{
    using HttpBinaryCacheStoreConfig::HttpBinaryCacheStoreConfig;

    const std::string name() override
    {
        return "mTLS HTTP Binary Cache Store";
    }

    std::string doc() override
    {
        return
#include "mtls-http-binary-cache-store.md"
            ;
    }

    PathsSetting<nix::Path> tlsCertificate{
        this,
        "",
        "tls-certificate",
        "Path of the TLS client certificate in PEM format as expected by CURLOPT_SSLCERT"
    };

    PathsSetting<nix::Path> tlsKey{
        this,
        "",
        "tls-private-key",
        "Path of the TLS client certificate private key in PEM format as expected by CURLOPT_SSLKEY"
    };
};

struct mTLSBinaryCacheStoreImpl : public HttpBinaryCacheStore
{
    struct Keyring
    {
        nix::Path tlsCertificate;
        nix::Path tlsKey;
    };

    mTLSBinaryCacheStoreConfig config_;
    std::shared_ptr<Keyring> keyring;

    mTLSBinaryCacheStoreConfig & config() override
    {
        return config_;
    }
    const mTLSBinaryCacheStoreConfig & config() const override
    {
        return config_;
    }

    mTLSBinaryCacheStoreImpl(
        const std::string & uriScheme, const Path & _cacheUri, mTLSBinaryCacheStoreConfig config
    )
        : Store(config)
        , HttpBinaryCacheStore("https", _cacheUri, config)
        , config_(std::move(config))
        , keyring(std::make_shared<Keyring>(config_.tlsCertificate.get(), config_.tlsKey.get()))
    {
    }

    FileTransferOptions makeOptions(Headers && headers = {}) override
    {
        auto options = HttpBinaryCacheStore::makeOptions(std::move(headers));
        auto baseExtraSetup = std::move(options.extraSetup);
        auto keyring = this->keyring;

        options.extraSetup = [keyring, baseExtraSetup{std::move(baseExtraSetup)}](CURL * req) {
            if (baseExtraSetup) {
                baseExtraSetup(req);
            }

            const bool haveCert = !keyring->tlsCertificate.empty();
            const bool haveKey = !keyring->tlsKey.empty();
            if (!(haveCert && haveKey)) {
                throw Error("https+mtls requires both tls-certificate and tls-private-key");
            }
            curl_easy_setopt(req, CURLOPT_SSLCERT, keyring->tlsCertificate.c_str());
            curl_easy_setopt(req, CURLOPT_SSLKEY, keyring->tlsKey.c_str());
        };

        return options;
    }

    static std::set<std::string> uriSchemes()
    {
        return {"https+mtls"};
    }
};
}

extern "C" void nix_plugin_entry()
{
    nix::StoreImplementations::add<nix::mTLSBinaryCacheStoreImpl, nix::mTLSBinaryCacheStoreConfig>();
}