Skip to content

Commit

Permalink
Define structured secret formats
Browse files Browse the repository at this point in the history
  • Loading branch information
nightkr committed Jun 21, 2023
1 parent 248781c commit 45c1de4
Show file tree
Hide file tree
Showing 12 changed files with 361 additions and 44 deletions.
27 changes: 25 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions rust/operator-binary/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ tracing = "0.1.36"
h2 = "=0.3.18" # Need to keep this in sync with our patched h2 build
uuid = { version = "1.2.2", features = ["v4"] }
stackable-krb5-provision-keytab = { path = "../krb5-provision-keytab" }
strum = { version = "0.25.0", features = ["derive"] }


[dev-dependencies]
Expand Down
6 changes: 3 additions & 3 deletions rust/operator-binary/src/backend/k8s_search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use stackable_operator::{
kube::api::ListParams,
};

use crate::crd::SearchNamespace;
use crate::{crd::SearchNamespace, format::SecretData};

use super::{
pod_info::PodInfo, scope::SecretScope, SecretBackend, SecretBackendError, SecretContents,
Expand Down Expand Up @@ -80,14 +80,14 @@ impl SecretBackend for K8sSearch {
.into_iter()
.next()
.context(NoSecretSnafu { label_selector })?;
Ok(SecretContents::new(
Ok(SecretContents::new(SecretData::Unknown(
secret
.data
.unwrap_or_default()
.into_iter()
.map(|(k, v)| (k.into(), v.0))
.collect(),
))
)))
}

async fn get_qualified_node_names(
Expand Down
20 changes: 9 additions & 11 deletions rust/operator-binary/src/backend/kerberos_keytab.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::path::PathBuf;

use async_trait::async_trait;
use snafu::{OptionExt, ResultExt, Snafu};
use stackable_krb5_provision_keytab::provision_keytab;
Expand All @@ -10,8 +8,9 @@ use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
};

use crate::crd::{
Hostname, InvalidKerberosPrincipal, KerberosKeytabBackendAdmin, KerberosPrincipal,
use crate::{
crd::{Hostname, InvalidKerberosPrincipal, KerberosKeytabBackendAdmin, KerberosPrincipal},
format::{well_known, SecretData, WellKnownSecretData},
};

use super::{pod_info::Address, SecretBackend, SecretBackendError, SecretContents};
Expand Down Expand Up @@ -235,12 +234,11 @@ cluster.local = {realm_name}
.read_to_end(&mut keytab_data)
.await
.context(ReadKeytabSnafu)?;
Ok(SecretContents::new(
[
(PathBuf::from("keytab"), keytab_data),
(PathBuf::from("krb5.conf"), profile.into_bytes()),
]
.into(),
))
Ok(SecretContents::new(SecretData::WellKnown(
WellKnownSecretData::KerberosKeytab(well_known::KerberosKeytab {
keytab: keytab_data,
krb5_conf: profile.into_bytes(),
}),
)))
}
}
12 changes: 7 additions & 5 deletions rust/operator-binary/src/backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ pub use tls::TlsGenerate;
use pod_info::Address;
use scope::SecretScope;

use crate::format::{SecretData, SecretFormat};

/// Configuration provided by the `Volume` selecting what secret data should be provided
///
/// Fields beginning with `csi.storage.k8s.io/` are provided by the Kubelet
Expand Down Expand Up @@ -108,17 +110,17 @@ impl SecretVolumeSelector {

type SecretFiles = HashMap<PathBuf, Vec<u8>>;

#[derive(Default, Debug)]
#[derive(Debug)]
pub struct SecretContents {
pub files: SecretFiles,
pub data: SecretData,
pub expires_after: Option<DateTime<FixedOffset>>,
}

impl SecretContents {
fn new(files: SecretFiles) -> Self {
fn new(data: SecretData) -> Self {
Self {
files,
..Self::default()
data,
expires_after: None,
}
}

Expand Down
33 changes: 14 additions & 19 deletions rust/operator-binary/src/backend/tls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ use stackable_operator::{
};
use time::{Duration, OffsetDateTime};

use crate::format::{well_known, SecretData, WellKnownSecretData};

use super::{
pod_info::{Address, PodInfo},
SecretBackend, SecretBackendError, SecretContents,
Expand Down Expand Up @@ -315,32 +317,25 @@ impl SecretBackend for TlsGenerate {
})
.context(BuildCertificateSnafu { tpe: CertType::Pod })?
.build();
Ok(SecretContents::new(
[
(
"ca.crt".into(),
self.ca_cert
Ok(
SecretContents::new(SecretData::WellKnown(WellKnownSecretData::PemCertificate(
well_known::PemCertificate {
ca_pem: self
.ca_cert
.to_pem()
.context(SerializeCertificateSnafu { tpe: CertType::Pod })?,
),
(
"tls.crt".into(),
pod_cert
certificate_pem: pod_cert
.to_pem()
.context(SerializeCertificateSnafu { tpe: CertType::Pod })?,
),
(
"tls.key".into(),
pod_key
key_pem: pod_key
.private_key_to_pem_pkcs8()
.context(SerializeCertificateSnafu { tpe: CertType::Pod })?,
),
]
.into(),
},
)))
.expires_after(
time_datetime_to_chrono(expire_pod_after).context(InvalidCertLifetimeSnafu)?,
),
)
.expires_after(
time_datetime_to_chrono(expire_pod_after).context(InvalidCertLifetimeSnafu)?,
))
}
}

Expand Down
8 changes: 4 additions & 4 deletions rust/operator-binary/src/csi_server/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ impl SecretProvisionerNode {
async fn save_secret_data(
&self,
target_path: &Path,
data: &SecretContents,
data: SecretContents,
) -> Result<(), PublishError> {
let create_secret = {
let mut opts = OpenOptions::new();
Expand All @@ -197,7 +197,7 @@ impl SecretProvisionerNode {
.mode(0o640);
opts
};
for (k, v) in &data.files {
for (k, v) in data.data.into_files() {
let item_path = target_path.join(k);
if let Some(item_path_parent) = item_path.parent() {
create_dir_all(item_path_parent)
Expand All @@ -210,7 +210,7 @@ impl SecretProvisionerNode {
.open(&item_path)
.await
.context(publish_error::CreateFileSnafu { path: &item_path })?
.write_all(v)
.write_all(&v)
.await
.context(publish_error::WriteFileSnafu { path: item_path })?;
}
Expand Down Expand Up @@ -348,7 +348,7 @@ impl Node for SecretProvisionerNode {
self.tag_pod(&self.client, &request.volume_id, &selector, &data)
.await?;
self.prepare_secret_dir(&target_path).await?;
self.save_secret_data(&target_path, &data).await?;
self.save_secret_data(&target_path, data).await?;
Ok(Response::new(NodePublishVolumeResponse {}))
}
.await,
Expand Down
82 changes: 82 additions & 0 deletions rust/operator-binary/src/format/convert.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use openssl::{
error::ErrorStack as OpensslError, pkcs12::Pkcs12, pkey::PKey, stack::Stack, x509::X509,
};
use snafu::{ResultExt, Snafu};

use crate::format::utils::split_pem_certificates;

use super::{
well_known::{PemCertificate, Pkcs12Certificate, WellKnownSecretData},
SecretFormat,
};

pub fn convert(
from: WellKnownSecretData,
to: SecretFormat,
) -> Result<WellKnownSecretData, ConvertError> {
match (from, to) {
// Converting into the current format is always a no-op
(from, to) if SecretFormat::from(&from) == to => Ok(from),

(WellKnownSecretData::PemCertificate(pem), SecretFormat::Pkcs12Certificate) => Ok(
WellKnownSecretData::Pkcs12Certificate(convert_pem_cert_to_pkcs12(pem)?),
),

(from, to) => NoValidConversionSnafu { from, to }.fail(),
}
}

#[derive(Snafu, Debug)]
pub enum ConvertError {
#[snafu(display("no conversion defined from {from:?} to {to:?}"))]
NoValidConversion {
from: SecretFormat,
to: SecretFormat,
},
#[snafu(
display("failed to convert from PEM certificate to PKCS#12"),
context(false)
)]
PemCertToPkcs12 { source: PemCertToPkcs12Error },
}

pub fn convert_pem_cert_to_pkcs12(
pem: PemCertificate,
) -> Result<Pkcs12Certificate, PemCertToPkcs12Error> {
use pem_cert_to_pkcs12_error::*;
let cert = X509::from_pem(&pem.certificate_pem).context(LoadCertSnafu)?;
let key = PKey::private_key_from_pem(&pem.key_pem).context(LoadKeySnafu)?;

let mut ca_stack = Stack::<X509>::new().context(LoadCaSnafu)?;
for ca in split_pem_certificates(&pem.ca_pem) {
X509::from_pem(ca)
.and_then(|ca| ca_stack.push(ca))
.context(LoadCertSnafu)?;
}

let mut pkcs_builder = Pkcs12::builder();

Ok(Pkcs12Certificate {
truststore: pkcs_builder
.ca(ca_stack)
.build2("")
.and_then(|store| store.to_der())
.context(BuildTruststoreSnafu)?,
keystore: pkcs_builder
.cert(&cert)
.pkey(&key)
.build2("")
.and_then(|store| store.to_der())
.context(BuildKeystoreSnafu)?,
})
}

#[derive(Snafu, Debug)]
#[snafu(module)]
pub enum PemCertToPkcs12Error {
LoadCert { source: OpensslError },
LoadKey { source: OpensslError },
LoadCa { source: OpensslError },
BuildKeystore { source: OpensslError },
BuildTruststore { source: OpensslError },
}
33 changes: 33 additions & 0 deletions rust/operator-binary/src/format/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use std::collections::HashMap;

pub use self::{
convert::ConvertError,
well_known::{FromFilesError as ParseError, SecretFormat, WellKnownSecretData},
};

mod convert;
mod utils;
pub mod well_known;

pub type SecretFiles = HashMap<String, Vec<u8>>;

#[derive(Debug)]
pub enum SecretData {
WellKnown(well_known::WellKnownSecretData),
Unknown(SecretFiles),
}
impl SecretData {
pub fn into_files(self) -> SecretFiles {
match self {
SecretData::WellKnown(data) => data.into_files(),
SecretData::Unknown(files) => files,
}
}

pub fn parse(self) -> Result<WellKnownSecretData, ParseError> {
match self {
Self::WellKnown(x) => Ok(x),
Self::Unknown(files) => WellKnownSecretData::from_files(files),
}
}
}
Loading

0 comments on commit 45c1de4

Please sign in to comment.