Refactor client-cert generator to use rustls/rcgen.

This refactors the last bit of code that depends on the OpenSSL C
library. Note that the openssl-probe does not depend on OpenSSL, it only
tries to detect the CA certificate directory on the host system.
This commit is contained in:
Orne Brocaar 2023-12-11 10:55:15 +00:00
parent fe98673bbb
commit 2774c51ea2
9 changed files with 228 additions and 227 deletions

207
Cargo.lock generated
View File

@ -181,6 +181,45 @@ dependencies = [
"term",
]
[[package]]
name = "asn1-rs"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0"
dependencies = [
"asn1-rs-derive",
"asn1-rs-impl",
"displaydoc",
"nom",
"num-traits",
"rusticata-macros",
"thiserror",
"time",
]
[[package]]
name = "asn1-rs-derive"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
"synstructure",
]
[[package]]
name = "asn1-rs-impl"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "assert-json-diff"
version = "2.0.2"
@ -787,7 +826,6 @@ dependencies = [
"lrwn",
"mime_guess",
"openidconnect",
"openssl",
"pbjson-types",
"pbkdf2",
"petgraph",
@ -797,11 +835,13 @@ dependencies = [
"prost-types",
"rand",
"rand_core",
"rcgen",
"rdkafka",
"redis",
"regex",
"reqwest",
"rquickjs",
"rsa",
"rumqttc",
"rust-embed",
"rustls",
@ -1135,6 +1175,12 @@ dependencies = [
"syn 2.0.39",
]
[[package]]
name = "data-encoding"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"
[[package]]
name = "dbl"
version = "0.3.2"
@ -1197,6 +1243,20 @@ dependencies = [
"zeroize",
]
[[package]]
name = "der-parser"
version = "8.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e"
dependencies = [
"asn1-rs",
"displaydoc",
"nom",
"num-bigint",
"num-traits",
"rusticata-macros",
]
[[package]]
name = "deranged"
version = "0.3.9"
@ -1318,6 +1378,17 @@ dependencies = [
"winapi",
]
[[package]]
name = "displaydoc"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.39",
]
[[package]]
name = "doc-comment"
version = "0.3.3"
@ -1568,21 +1639,6 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foreign-types"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
dependencies = [
"foreign-types-shared",
]
[[package]]
name = "foreign-types-shared"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "form_urlencoded"
version = "1.2.1"
@ -2222,7 +2278,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378"
dependencies = [
"base64 0.21.5",
"pem",
"pem 1.1.1",
"ring 0.16.20",
"serde",
"serde_json",
@ -2636,6 +2692,15 @@ dependencies = [
"memchr",
]
[[package]]
name = "oid-registry"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff"
dependencies = [
"asn1-rs",
]
[[package]]
name = "once_cell"
version = "1.18.0"
@ -2674,50 +2739,12 @@ dependencies = [
"url",
]
[[package]]
name = "openssl"
version = "0.10.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79a4c6c3a2b158f7f8f2a2fc5a969fa3a068df6fc9dbb4a43845436e3af7c800"
dependencies = [
"bitflags 2.4.1",
"cfg-if",
"foreign-types",
"libc",
"once_cell",
"openssl-macros",
"openssl-sys",
]
[[package]]
name = "openssl-macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.39",
]
[[package]]
name = "openssl-probe"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-sys"
version = "0.9.96"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3812c071ba60da8b5677cc12bcb1d42989a65553772897a7e0355545a819838f"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "ordered-float"
version = "2.10.1"
@ -2878,6 +2905,16 @@ dependencies = [
"base64 0.13.1",
]
[[package]]
name = "pem"
version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3163d2912b7c3b52d651a055f2c7eec9ba5cd22d26ef75b8dd3a59980b185923"
dependencies = [
"base64 0.21.5",
"serde",
]
[[package]]
name = "pem-rfc7468"
version = "0.7.0"
@ -3303,6 +3340,18 @@ dependencies = [
"cipher",
]
[[package]]
name = "rcgen"
version = "0.12.0"
source = "git+https://github.com/rustls/rcgen.git?rev=5ed5fccd3effd4da391492f5f01f98c955b9a4c4#5ed5fccd3effd4da391492f5f01f98c955b9a4c4"
dependencies = [
"pem 3.0.2",
"ring 0.17.5",
"time",
"x509-parser",
"yasna",
]
[[package]]
name = "rdkafka"
version = "0.34.0"
@ -3652,6 +3701,15 @@ dependencies = [
"semver",
]
[[package]]
name = "rusticata-macros"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632"
dependencies = [
"nom",
]
[[package]]
name = "rustix"
version = "0.37.27"
@ -4195,6 +4253,18 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
[[package]]
name = "synstructure"
version = "0.12.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
"unicode-xid",
]
[[package]]
name = "system-configuration"
version = "0.5.1"
@ -4848,12 +4918,6 @@ version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a72e1902dde2bd6441347de2b70b7f5d59bf157c6c62f0c44572607a1d55bbe"
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "version_check"
version = "0.9.4"
@ -5150,11 +5214,32 @@ dependencies = [
"windows-sys",
]
[[package]]
name = "x509-parser"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7069fba5b66b9193bd2c5d3d4ff12b839118f6bcbef5328efafafb5395cf63da"
dependencies = [
"asn1-rs",
"data-encoding",
"der-parser",
"lazy_static",
"nom",
"oid-registry",
"ring 0.16.20",
"rusticata-macros",
"thiserror",
"time",
]
[[package]]
name = "yasna"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd"
dependencies = [
"time",
]
[[package]]
name = "zeroize"

View File

@ -15,5 +15,11 @@ lto = true
codegen-units = 1
[patch.crates-io]
# Remove if diesel > 2.1.4
diesel = { git = "https://github.com/diesel-rs/diesel.git", rev = "566dcccc6df6adb6ceddef8df5e1806e2a065c40" }
# Remove if diesel-async > 0.4.1
diesel-async = { git = "https://github.com/weiznich/diesel_async.git", rev = "017ebe2fb7a2709ab5db92148dea5ce812a35e09" }
# Remove if rcgen > 0.11.4
rcgen = { git = "https://github.com/rustls/rcgen.git", rev = "5ed5fccd3effd4da391492f5f01f98c955b9a4c4" }

View File

@ -105,7 +105,8 @@ jsonwebtoken = "8.3"
rustls = "0.21"
rustls-native-certs = "0.6"
rustls-pemfile = "1.0"
openssl = { version = "0.10" }
rsa = "0.9"
rcgen = { version = "0.12", features = [ "x509-parser" ] }
openidconnect = { version = "3.3", features = ["accept-rfc3339-timestamps"] }
# MQTT

View File

@ -1,82 +1,57 @@
use std::ops::Add;
use std::time::{SystemTime, UNIX_EPOCH};
use std::time::SystemTime;
use anyhow::{Context, Result};
use openssl::asn1::Asn1Time;
use openssl::bn::{BigNum, MsbOption};
use openssl::ec::{EcGroup, EcKey};
use openssl::hash::MessageDigest;
use openssl::nid::Nid;
use openssl::pkey::{PKey, PKeyRef, Private};
use openssl::x509::extension::{
AuthorityKeyIdentifier, BasicConstraints, KeyUsage, SubjectKeyIdentifier,
use rcgen::{
Certificate, CertificateParams, DnType, ExtendedKeyUsagePurpose, KeyPair, KeyUsagePurpose,
};
use rsa::{
pkcs1::DecodeRsaPrivateKey,
pkcs8::{EncodePrivateKey, LineEnding},
RsaPrivateKey,
};
use openssl::x509::{X509NameBuilder, X509Ref, X509ReqBuilder, X509};
use tokio::fs;
use uuid::Uuid;
use crate::config;
use lrwn::EUI64;
// Based on:
// https://github.com/sfackler/rust-openssl/blob/master/openssl/examples/mk_certs.rs
// TODO: wrap in tokio?
async fn generate_client_certificate(
id: &str,
ttl: SystemTime,
ca_cert: &X509Ref,
ca_key_pair: &PKeyRef<Private>,
) -> Result<(X509, PKey<Private>)> {
let ttl_unix = ttl.duration_since(UNIX_EPOCH)?;
fn gen_client_cert(id: &str, not_before: SystemTime, not_after: SystemTime) -> Result<Certificate> {
let mut params = CertificateParams::new(vec![id.to_string()]);
params
.distinguished_name
.push(DnType::CommonName, id.to_string());
params.use_authority_key_identifier_extension = true;
params.not_before = not_before.into();
params.not_after = not_after.into();
params.key_usages.push(KeyUsagePurpose::DigitalSignature);
params
.extended_key_usages
.push(ExtendedKeyUsagePurpose::ClientAuth);
let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1)?;
let ec = EcKey::generate(&group)?;
let key_pair = PKey::from_ec_key(ec)?;
Ok(Certificate::from_params(params)?)
}
let mut req_builder = X509ReqBuilder::new()?;
req_builder.set_pubkey(&key_pair)?;
async fn get_ca_cert(ca_cert_file: &str, ca_key_file: &str) -> Result<Certificate> {
let ca_cert_s = fs::read_to_string(ca_cert_file)
.await
.context("Read gateway ca_cert")?;
let ca_key_s = fs::read_to_string(ca_key_file)
.await
.context("Read gateway ca_key")?;
let ca_key_s = private_key_to_pkcs8(&ca_key_s)?;
let mut x509_name = X509NameBuilder::new()?;
x509_name.append_entry_by_text("CN", id)?;
let x509_name = x509_name.build();
req_builder.set_subject_name(&x509_name)?;
let ca_key = KeyPair::from_pem(&ca_key_s).context("Parse gateway CA key")?;
let params = CertificateParams::from_ca_cert_pem(&ca_cert_s, ca_key)
.context("Parse gateway CA certificate")?;
req_builder.sign(&key_pair, MessageDigest::sha256())?;
let req = req_builder.build();
// Workaround for:
// https://github.com/rustls/rcgen/issues/193
let ca_key =
KeyPair::from_pem_and_sign_algo(&ca_key_s, params.alg).context("Parse gateway CA key")?;
let params = CertificateParams::from_ca_cert_pem(&ca_cert_s, ca_key)
.context("Parse gateway CA certificate")?;
let mut cert_builder = X509::builder()?;
cert_builder.set_version(2)?;
let serial_number = {
let mut serial = BigNum::new()?;
serial.rand(159, MsbOption::MAYBE_ZERO, false)?;
serial.to_asn1_integer()?
};
cert_builder.set_serial_number(&serial_number)?;
cert_builder.set_subject_name(req.subject_name())?;
cert_builder.set_issuer_name(ca_cert.subject_name())?;
cert_builder.set_pubkey(&key_pair)?;
let not_before = Asn1Time::days_from_now(0)?;
cert_builder.set_not_before(&not_before)?;
let not_after = Asn1Time::from_unix(ttl_unix.as_secs().try_into()?)?;
cert_builder.set_not_after(&not_after)?;
cert_builder.append_extension(BasicConstraints::new().build()?)?;
cert_builder.append_extension(KeyUsage::new().critical().digital_signature().build()?)?;
let subject_key_identifier =
SubjectKeyIdentifier::new().build(&cert_builder.x509v3_context(None, None))?;
cert_builder.append_extension(subject_key_identifier)?;
let auth_key_identifier = AuthorityKeyIdentifier::new()
.keyid(false)
.issuer(false)
.build(&cert_builder.x509v3_context(Some(ca_cert), None))?;
cert_builder.append_extension(auth_key_identifier)?;
cert_builder.sign(ca_key_pair, MessageDigest::sha256())?;
let cert = cert_builder.build();
Ok((cert, key_pair))
Ok(Certificate::from_params(params).context("Init Certificate struct")?)
}
// This returns the CA, certificate and private-key as PEM encoded strings.
@ -84,33 +59,21 @@ pub async fn client_cert_for_gateway_id(
gateway_id: &EUI64,
) -> Result<(SystemTime, String, String, String)> {
let conf = config::get();
let ca_cert = fs::read(&conf.gateway.ca_cert)
let ca_cert = get_ca_cert(&conf.gateway.ca_cert, &conf.gateway.ca_key)
.await
.context("Read gateway ca_cert")?;
let ca_key = fs::read(&conf.gateway.ca_key)
.await
.context("Read gateway ca_key")?;
let ca_cert = X509::from_pem(&ca_cert).context("Parse gateway ca_cert")?;
let ca_key = PKey::private_key_from_pem(&ca_key).context("Parse gateway ca_key")?;
let ttl = SystemTime::now().add(conf.gateway.client_cert_lifetime);
let (cert, p_key) =
generate_client_certificate(&gateway_id.to_string(), ttl, &ca_cert, &ca_key)
.await
.context("Generate client-certificate")?;
let ca_pem = ca_cert.to_pem()?;
let cert_pem = cert.to_pem()?;
let p_key_pem = p_key.private_key_to_pem_pkcs8()?;
.context("Get CA cert")?;
let not_before = SystemTime::now();
let not_after = SystemTime::now() + conf.gateway.client_cert_lifetime;
let gw_cert = gen_client_cert(&gateway_id.to_string(), not_before, not_after)
.context("Generate client certificate")?;
Ok((
ttl,
String::from_utf8(ca_pem).unwrap(),
String::from_utf8(cert_pem).unwrap(),
String::from_utf8(p_key_pem).unwrap(),
not_after,
ca_cert.serialize_pem().context("Serialize CA cert")?,
gw_cert
.serialize_pem_with_signer(&ca_cert)
.context("Serialize client cert")?,
gw_cert.serialize_private_key_pem(),
))
}
@ -118,32 +81,29 @@ pub async fn client_cert_for_application_id(
application_id: &Uuid,
) -> Result<(SystemTime, String, String, String)> {
let conf = config::get();
let ca_cert = fs::read(&conf.integration.mqtt.client.ca_cert)
.await
.context("Read mqtt ca_cert")?;
let ca_key = fs::read(&conf.integration.mqtt.client.ca_key)
.await
.context("Read mqtt ca_key")?;
let ca_cert = X509::from_pem(&ca_cert).context("Parse gateway ca_cert")?;
let ca_key = PKey::private_key_from_pem(&ca_key).context("Parse gateway ca_key")?;
let ttl = SystemTime::now().add(conf.integration.mqtt.client.client_cert_lifetime);
let (cert, p_key) =
generate_client_certificate(&application_id.to_string(), ttl, &ca_cert, &ca_key)
.await
.context("Generate client-certificate")?;
let ca_pem = ca_cert.to_pem()?;
let cert_pem = cert.to_pem()?;
let p_key_pem = p_key.private_key_to_pem_pkcs8()?;
let ca_cert = get_ca_cert(
&conf.integration.mqtt.client.ca_cert,
&conf.integration.mqtt.client.ca_key,
)
.await?;
let not_before = SystemTime::now();
let not_after = SystemTime::now() + conf.integration.mqtt.client.client_cert_lifetime;
let app_cert = gen_client_cert(&application_id.to_string(), not_before, not_after)?;
Ok((
ttl,
String::from_utf8(ca_pem).unwrap(),
String::from_utf8(cert_pem).unwrap(),
String::from_utf8(p_key_pem).unwrap(),
not_after,
ca_cert.serialize_pem()?,
app_cert.serialize_pem_with_signer(&ca_cert)?,
app_cert.serialize_private_key_pem(),
))
}
fn private_key_to_pkcs8(pem: &str) -> Result<String> {
if pem.contains("RSA PRIVATE KEY") {
let pkey = RsaPrivateKey::from_pkcs1_pem(pem).context("Read RSA PKCS#1")?;
let pkcs8_pem = pkey.to_pkcs8_pem(LineEnding::default())?;
Ok(pkcs8_pem.as_str().to_owned())
} else {
Ok(pem.to_string())
}
}

View File

@ -1,8 +1,6 @@
// Required by rust::table macro.
#![recursion_limit = "256"]
extern crate openssl;
#[macro_use]
extern crate lazy_static;
extern crate diesel_migrations;

View File

@ -1,22 +1,6 @@
FROM ghcr.io/cross-rs/aarch64-unknown-linux-musl:latest
ENV OPENSSL_VERSION=3.1.2
ENV OPENSSL_TARGET=linux-aarch64
ENV MUSL_PREFIX=aarch64-linux-musl
RUN apt-get update && \
apt-get --assume-yes install \
protobuf-compiler \
libprotobuf-dev
RUN echo "Building OpenSSL" && \
cd /tmp && \
curl -fLO "https://www.openssl.org/source/openssl-$OPENSSL_VERSION.tar.gz" && \
tar xvzf "openssl-$OPENSSL_VERSION.tar.gz" && cd "openssl-$OPENSSL_VERSION" && \
env CC=$MUSL_PREFIX-gcc ./Configure no-shared no-zlib -fPIC --prefix=/usr/local/$MUSL_PREFIX-target -DOPENSSL_NO_SECURE_MEMORY $OPENSSL_TARGET && \
env C_INCLUDE_PATH=/usr/local/$MUSL_PREFIX/include/ make depend && \
env C_INCLUDE_PATH=/usr/local/$MUSL_PREFIX/include/ make && \
make install_sw && \
rm -r /tmp/*
ENV PKG_CONFIG_PATH=/usr/local/$MUSL_PREFIX-target/lib/pkgconfig

View File

@ -1,22 +1,6 @@
FROM ghcr.io/cross-rs/armv7-unknown-linux-musleabihf:latest
ENV OPENSSL_VERSION=3.1.2
ENV OPENSSL_TARGET=linux-generic32
ENV MUSL_PREFIX=arm-linux-musleabihf
RUN apt-get update && \
apt-get --assume-yes install \
protobuf-compiler \
libprotobuf-dev
RUN echo "Building OpenSSL" && \
cd /tmp && \
curl -fLO "https://www.openssl.org/source/openssl-$OPENSSL_VERSION.tar.gz" && \
tar xvzf "openssl-$OPENSSL_VERSION.tar.gz" && cd "openssl-$OPENSSL_VERSION" && \
env CC=$MUSL_PREFIX-gcc ./Configure no-shared no-zlib -fPIC --prefix=/usr/local/$MUSL_PREFIX-target -DOPENSSL_NO_SECURE_MEMORY $OPENSSL_TARGET && \
env C_INCLUDE_PATH=/usr/local/$MUSL_PREFIX/include/ make depend && \
env C_INCLUDE_PATH=/usr/local/$MUSL_PREFIX/include/ make && \
make install_sw && \
rm -r /tmp/*
ENV PKG_CONFIG_PATH=/usr/local/$MUSL_PREFIX-target/lib/pkgconfig

View File

@ -1,22 +1,6 @@
FROM ghcr.io/cross-rs/x86_64-unknown-linux-musl:latest
ENV OPENSSL_VERSION=3.1.2
ENV OPENSSL_TARGET=linux-x86_64
ENV MUSL_PREFIX=x86_64-linux-musl
RUN apt-get update && \
apt-get --assume-yes install \
protobuf-compiler \
libprotobuf-dev
RUN echo "Building OpenSSL" && \
cd /tmp && \
curl -fLO "https://www.openssl.org/source/openssl-$OPENSSL_VERSION.tar.gz" && \
tar xvzf "openssl-$OPENSSL_VERSION.tar.gz" && cd "openssl-$OPENSSL_VERSION" && \
env CC=$MUSL_PREFIX-gcc ./Configure no-shared no-zlib -fPIC --prefix=/usr/local/$MUSL_PREFIX-target -DOPENSSL_NO_SECURE_MEMORY $OPENSSL_TARGET && \
env C_INCLUDE_PATH=/usr/local/$MUSL_PREFIX/include/ make depend && \
env C_INCLUDE_PATH=/usr/local/$MUSL_PREFIX/include/ make && \
make install_sw && \
rm -r /tmp/*
ENV PKG_CONFIG_PATH=/usr/local/$MUSL_PREFIX-target/lib/pkgconfig:/usr/local/$MUSL_PREFIX-target/lib64/pkgconfig

View File

@ -11,7 +11,6 @@ pkgs.mkShell {
pkgs.perl
pkgs.cmake
pkgs.clang
pkgs.openssl
];
LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib";
BINDGEN_EXTRA_CLANG_ARGS = "-I${pkgs.llvmPackages.libclang.lib}/lib/clang/${pkgs.llvmPackages.libclang.version}/include";