Skip to content

Commit 569dcf9

Browse files
Raito BezariusMic92vlaci
committed
contrib/plugins: init with mTLS store example
This is a collection of Lix plugins that showcase how to write one for various usecases. The first is a mTLS store plugin that enable mTLS cache URIs (`https+mtls://`). We enable meson build system support for this plugin but we are not going to distribute it in the official packaging of Lix, we will repackage each relevant plugin downstream in Nixpkgs. These plugins have *NO* guarantee support, they are provided as useful references and are possibly production-ready if your usecase is simple enough. Reference: NixOS/nix#13030 (this change has resemblances but our APIs are different, the tests harness is mostly from CppNix). Change-Id: Ib354271981b35dff6c134b12c4748c3eaf743fcb Co-authored-by: Jörg Thalheim <[email protected]> Co-authored-by: László Vaskó <[email protected]> Signed-off-by: Raito Bezarius <[email protected]>
1 parent 2d0c1e2 commit 569dcf9

File tree

13 files changed

+421
-5
lines changed

13 files changed

+421
-5
lines changed

contrib/plugins/meson.build

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
plugin_mtls_store = shared_module(
2+
'plugin_mtls_store',
3+
'plugin_mtls_store.cc',
4+
dependencies : [
5+
liblixutil,
6+
liblixstore,
7+
liblixexpr,
8+
liblixfetchers,
9+
curl,
10+
],
11+
install : false,
12+
build_by_default : true,
13+
link_args : strict_shared_module_link_args,
14+
)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
R"(
2+
3+
**Store URL format**: `https+mtls://...`
4+
5+
This store allows a binary cache to be accessed via the HTTPS
6+
protocol with mutual TLS mandated.
7+
8+
Two parameters can be passed to the query string:
9+
10+
- `tls-certificate`, a path to the TLS client certificate (optional)
11+
- `tls-private-key`, a path to the TLS private key backing the client certificate (required)
12+
13+
)"
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
#include "lix/libstore/store-api.hh"
2+
#include "lix/libutil/config.hh"
3+
#include "lix/libstore/http-binary-cache-store.hh"
4+
#include <stdlib.h>
5+
#include <curl/curl.h>
6+
7+
namespace nix {
8+
struct mTLSBinaryCacheStoreConfig : HttpBinaryCacheStoreConfig
9+
{
10+
using HttpBinaryCacheStoreConfig::HttpBinaryCacheStoreConfig;
11+
12+
const std::string name() override
13+
{
14+
return "mTLS HTTP Binary Cache Store";
15+
}
16+
17+
std::string doc() override
18+
{
19+
return
20+
#include "mtls-http-binary-cache-store.md"
21+
;
22+
}
23+
24+
PathsSetting<nix::Path> tlsCertificate{
25+
this,
26+
"",
27+
"tls-certificate",
28+
"Path of an optional TLS client certificate in PEM format as expected by CURLOPT_SSLCERT"
29+
};
30+
31+
PathsSetting<nix::Path> tlsKey{
32+
this,
33+
"",
34+
"tls-private-key",
35+
"Path of an TLS client certificate private key in PEM format as expected by CURLOPT_SSLKEY"
36+
};
37+
};
38+
39+
struct mTLSBinaryCacheStoreImpl : public HttpBinaryCacheStore
40+
{
41+
struct Keyring
42+
{
43+
nix::Path tlsCertificate;
44+
nix::Path tlsKey;
45+
};
46+
47+
mTLSBinaryCacheStoreConfig config_;
48+
std::shared_ptr<Keyring> keyring;
49+
50+
mTLSBinaryCacheStoreConfig & config() override
51+
{
52+
return config_;
53+
}
54+
const mTLSBinaryCacheStoreConfig & config() const override
55+
{
56+
return config_;
57+
}
58+
59+
mTLSBinaryCacheStoreImpl(
60+
const std::string & uriScheme, const Path & _cacheUri, mTLSBinaryCacheStoreConfig config
61+
)
62+
: Store(config)
63+
, HttpBinaryCacheStore("https", _cacheUri, config)
64+
, config_(std::move(config))
65+
, keyring(std::make_shared<Keyring>(config_.tlsCertificate.get(), config_.tlsKey.get()))
66+
{
67+
}
68+
69+
FileTransferOptions makeOptions(Headers && headers = {}) override
70+
{
71+
auto options = HttpBinaryCacheStore::makeOptions(std::move(headers));
72+
auto baseExtraSetup = std::move(options.extraSetup);
73+
auto keyring = this->keyring;
74+
75+
options.extraSetup =
76+
[keyring, baseExtraSetup{std::move(baseExtraSetup)}](CURL * req) {
77+
if (baseExtraSetup) {
78+
baseExtraSetup(req);
79+
}
80+
81+
if (!keyring->tlsCertificate.empty()) {
82+
curl_easy_setopt(req, CURLOPT_SSLCERT, keyring->tlsCertificate.c_str());
83+
}
84+
85+
curl_easy_setopt(req, CURLOPT_SSLKEY, keyring->tlsKey.c_str());
86+
};
87+
88+
return options;
89+
}
90+
91+
static std::set<std::string> uriSchemes()
92+
{
93+
return {"https+mtls"};
94+
}
95+
};
96+
}
97+
98+
99+
extern "C" void nix_plugin_entry()
100+
{
101+
nix::StoreImplementations::add<nix::mTLSBinaryCacheStoreImpl, nix::mTLSBinaryCacheStoreConfig>();
102+
}

doc/manual/change-authors.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,9 @@ ma27:
156156
matthewbauer:
157157
github: matthewbauer
158158

159+
mic92:
160+
github: Mic92
161+
159162
midnightveil:
160163
display_name: julia
161164
forgejo: midnightveil
@@ -243,6 +246,9 @@ vigress8:
243246
forgejo: vigress8
244247
github: vigress8
245248

249+
vlaci:
250+
github: vlaci
251+
246252
vlinkz:
247253
display_name: Victor Fuentes
248254
forgejo: vlinkz

doc/manual/rl-next/mtls-plugin.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
---
2+
synopsis: "mTLS store connections via a plugin"
3+
issues: []
4+
cls: [3754, 3696, 3697, 3698]
5+
category: Improvements
6+
credits: [raito, horrors, mic92, vlaci]
7+
---
8+
9+
To support use cases requiring mutual TLS (mTLS) authentication when connecting
10+
to remote Nix stores, e.g. private stores, we have introduced a **contributed**
11+
mTLS plugin extending the Lix store interface.
12+
13+
This design follows an extensibility model which was brought up [by a proposal
14+
of making Kerberos authentication possible in Lix
15+
directly](https://gerrit.lix.systems/c/lix/+/3637).
16+
17+
This mTLS plugin serves as a concrete example of how store connection
18+
mechanisms can be modularized through external plugins, without extending Lix
19+
core. This idea can be generalized to integrate automatic certificate renewal
20+
or advanced integrations with secrets engine or posture checks.
21+
22+
It enables custom TLS client certificates to be used for authenticating against
23+
a remote store that enforces mTLS.
24+
25+
To use the plugin, configure Lix manually by setting in your `nix.conf`:
26+
27+
```
28+
plugin-files = /a/path/to/libplugin_mtls_store.so
29+
```
30+
31+
Currently, this must be done explicitly. In the future, Nixpkgs will provide a
32+
mechanism to reference an up-to-date and curated set of plugins automatically.
33+
34+
Making plugins easily consumable outside of Nixpkgs (e.g., from external plugin
35+
registries or binary distributions) remains an open question and will require
36+
further design.
37+
38+
Contributed plugins come with significantly reduced **stability** and
39+
**maintenance** guarantees compared to the Lix core. We encourage users who
40+
depend on a given plugin to take on maintenance responsibilities and apply for
41+
ownership within the Lix mono-repository. These plugins are subject to removal
42+
at any time.

meson.build

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ endif
125125

126126
enable_nix_eval_jobs = get_option('nix-eval-jobs')
127127
enable_tests = get_option('enable-tests')
128+
enable_contrib_plugins = get_option('enable-contrib-plugins')
128129

129130
tests_args = []
130131

@@ -224,12 +225,13 @@ is_x64 = host_machine.cpu_family() == 'x86_64'
224225
# This corresponds to the $(1)_ALLOW_UNDEFINED option from the Make buildsystem.
225226
# Mostly this is load-bearing on the plugin tests defined in tests/functional/plugins/meson.build.
226227
shared_module_link_args = []
228+
# This is a stricter additional set of link flags.
229+
strict_shared_module_link_args = []
227230
if is_darwin
228231
shared_module_link_args += ['-undefined', 'suppress', '-flat_namespace']
232+
strict_shared_module_link_args += ['-flat_namespace']
229233
elif is_linux
230-
# -Wl,-z,defs is the equivalent, but a comment in the Make buildsystem says that breaks
231-
# Clang sanitizers on Linux.
232-
# FIXME(Qyriad): is that true?
234+
strict_shared_module_link_args += ['-Wl,-z,defs']
233235
endif
234236
configdata = { }
235237

@@ -663,6 +665,10 @@ if enable_tests
663665
subdir('tests/functional2')
664666
endif
665667

668+
if enable_contrib_plugins
669+
subdir('contrib/plugins')
670+
endif
671+
666672
subdir('meson/clang-tidy')
667673

668674
subproject('nix-eval-jobs', required : enable_nix_eval_jobs)

meson.options

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ option('enable-tests', type : 'boolean', value : true,
3232
description : 'whether to enable tests or not (requires rapidcheck and gtest)',
3333
)
3434

35+
option('enable-contrib-plugins', type : 'boolean', value : true,
36+
description : 'whether to build contributed plugins'
37+
)
38+
3539
option('tests-color', type : 'boolean', value : true,
3640
description : 'set to false to disable color output in gtest',
3741
)

misc/pre-commit.nix

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ pre-commit-run {
3636
enable = true;
3737
package = pkgs.llvmPackages.libclang.python;
3838
entry = "${pkgs.llvmPackages.libclang.python}/bin/git-clang-format --binary ${pkgs.llvmPackages.clang-tools}/bin/clang-format";
39-
files = "^(lix/|tests/)";
39+
files = "^(lix/|tests/|contrib/plugins/)";
4040
types = [
4141
"c++"
4242
"file"

package.nix

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ stdenv.mkDerivation (finalAttrs: {
232232
./doc
233233
./lix
234234
./misc
235+
./contrib/plugins
235236
./COPYING
236237
]
237238
++ lib.optionals lintInsteadOfBuild [ ./.clang-tidy ]
@@ -309,6 +310,10 @@ stdenv.mkDerivation (finalAttrs: {
309310
jq
310311
yq
311312
lsof
313+
# For mTLS tests.
314+
curl
315+
openssl
316+
python3
312317
]
313318
++ lib.optional hostPlatform.isLinux util-linuxMinimal
314319
++ lib.optional (!officialRelease && buildUnreleasedNotes) build-release-notes

tests/functional/init.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ if test -d "$TEST_ROOT"; then
1010
killDaemon
1111
rm -rf "$TEST_ROOT"
1212
fi
13-
mkdir "$TEST_ROOT"
13+
mkdir -p "$TEST_ROOT"
1414

1515
mkdir "$NIX_STORE_DIR"
1616
mkdir "$NIX_LOCALSTATE_DIR"

0 commit comments

Comments
 (0)