Skip to content

Commit

Permalink
Merge pull request #114 from omertuc/extfix
Browse files Browse the repository at this point in the history
"user-ca-bundle" should be consider external regardless of proxy
  • Loading branch information
omertuc authored Mar 1, 2024
2 parents c95e571 + 6c1bd2f commit 208f14f
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 90 deletions.
91 changes: 2 additions & 89 deletions src/cluster_crypto/scanning.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@ use crate::{
cluster_crypto::{crypto_objects::process_unknown_value, json_crawl},
config::ConfigPath,
file_utils::{self, read_file_to_string},
k8s_etcd::{get_etcd_json, InMemoryK8sEtcd},
k8s_etcd::InMemoryK8sEtcd,
recert::timing::RunTime,
};
use anyhow::{bail, ensure, Context, Error, Result};
use futures_util::future::join_all;
use itertools::Itertools;
use serde_json::Value;
use std::{
collections::HashSet,
Expand All @@ -21,94 +20,8 @@ use std::{
sync::Arc,
};
use tokio::task::JoinHandle;
use x509_certificate::X509Certificate;

pub(crate) async fn discover_external_certs(in_memory_etcd_client: Arc<InMemoryK8sEtcd>) -> Result<HashSet<String>> {
let trusted_certs = vec![get_openshift_trusted_certs(&in_memory_etcd_client)
.await
.context("openshift trusted certs")?];
let image_trusted_certs = get_openshift_image_trusted_certs(&in_memory_etcd_client)
.await
.context("image trusted certs")?;

let all_certs_bundled = trusted_certs.into_iter().chain(image_trusted_certs).join("\n");

pem::parse_many(all_certs_bundled)
.context("parsing")?
.into_iter()
.map(|pem| match pem.tag() {
"CERTIFICATE" => Ok({
let crt = X509Certificate::from_der(pem.contents()).context("from der")?;
let cn = crt.subject_name().user_friendly_str().unwrap_or("undecodable".to_string());

log::trace!("Found external certificate: {}", cn);

cn.to_string()
}),
_ => bail!("unexpected tag"),
})
.collect::<Result<HashSet<_>>>()
}

async fn get_openshift_image_trusted_certs(in_memory_etcd_client: &Arc<InMemoryK8sEtcd>) -> Result<Vec<String>> {
let mut pem_strings = vec![];

let image_config = get_etcd_json(
in_memory_etcd_client,
&(K8sResourceLocation::new(None, "Image", "cluster", "config.openshift.io")),
)
.await
.context("getting image config")?
.context("image config not found")?;

if let Some(additional_trusted_ca) = image_config.pointer("/spec/additionalTrustedCA/name") {
let user_image_ca_configmap = get_etcd_json(
in_memory_etcd_client,
&(K8sResourceLocation {
namespace: Some("openshift-config".into()),
kind: "ConfigMap".into(),
apiversion: "v1".into(),
name: additional_trusted_ca.as_str().context("must be string")?.into(),
}),
)
.await
.context("getting user image ca configmap")?
.context("user image ca configmap not found")?;

for (k, v) in user_image_ca_configmap
.pointer("/data")
.context("parsing registry-cas")?
.as_object()
.context("must be object")?
{
pem_strings.push(v.as_str().context(format!("must be string ({k})"))?.to_string());
}
}

Ok(pem_strings)
}

async fn get_openshift_trusted_certs(in_memory_etcd_client: &Arc<InMemoryK8sEtcd>) -> Result<String> {
let trusted_ca_bundle_configmap = get_etcd_json(
in_memory_etcd_client,
&(K8sResourceLocation {
namespace: Some("openshift-config-managed".into()),
kind: "ConfigMap".into(),
apiversion: "v1".into(),
name: "trusted-ca-bundle".into(),
}),
)
.await
.context("getting trusted-ca-bundle")?
.context("trusted-ca-bundle not found")?;

Ok(trusted_ca_bundle_configmap
.pointer("/data/ca-bundle.crt")
.context("parsing ca-bundle.crt")?
.as_str()
.context("must be string")?
.to_string())
}
pub(crate) mod external_certs;

pub(crate) async fn crypto_scan(
in_memory_etcd_client: Arc<InMemoryK8sEtcd>,
Expand Down
135 changes: 135 additions & 0 deletions src/cluster_crypto/scanning/external_certs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
use super::super::locations::K8sResourceLocation;
use crate::k8s_etcd::get_etcd_json;
use crate::k8s_etcd::InMemoryK8sEtcd;
use anyhow::{bail, Context, Result};
use itertools::Itertools;
use std::collections::HashSet;
use std::sync::Arc;
use x509_certificate::X509Certificate;

pub(crate) async fn discover_external_certs(in_memory_etcd_client: Arc<InMemoryK8sEtcd>) -> Result<HashSet<String>> {
let proxy_trusted_certs = vec![get_openshift_proxy_trusted_certs(&in_memory_etcd_client)
.await
.context("openshift trusted certs")?];
let ocp_trusted_certs = match get_openshift_user_ca_bundle(&in_memory_etcd_client)
.await
.context("openshift trusted certs")?
{
Some(certs) => vec![certs],
None => vec![],
};
let image_trusted_certs = get_openshift_image_trusted_certs(&in_memory_etcd_client)
.await
.context("image trusted certs")?;

let all_certs_bundled = proxy_trusted_certs
.into_iter()
.chain(image_trusted_certs)
.chain(ocp_trusted_certs)
.join("\n");

pem::parse_many(all_certs_bundled)
.context("parsing")?
.into_iter()
.map(|pem| match pem.tag() {
"CERTIFICATE" => Ok({
let crt = X509Certificate::from_der(pem.contents()).context("from der")?;
let cn = crt.subject_name().user_friendly_str().unwrap_or("undecodable".to_string());

log::trace!("Found external certificate: {}", cn);

cn.to_string()
}),
_ => bail!("unexpected tag"),
})
.collect::<Result<HashSet<_>>>()
}

pub(crate) async fn get_openshift_image_trusted_certs(in_memory_etcd_client: &Arc<InMemoryK8sEtcd>) -> Result<Vec<String>> {
let mut pem_strings = vec![];

let image_config = get_etcd_json(
in_memory_etcd_client,
&(K8sResourceLocation::new(None, "Image", "cluster", "config.openshift.io")),
)
.await
.context("getting image config")?
.context("image config not found")?;

if let Some(additional_trusted_ca) = image_config.pointer("/spec/additionalTrustedCA/name") {
let user_image_ca_configmap = get_etcd_json(
in_memory_etcd_client,
&(K8sResourceLocation {
namespace: Some("openshift-config".into()),
kind: "ConfigMap".into(),
apiversion: "v1".into(),
name: additional_trusted_ca.as_str().context("must be string")?.into(),
}),
)
.await
.context("getting user image ca configmap")?
.context("user image ca configmap not found")?;

for (k, v) in user_image_ca_configmap
.pointer("/data")
.context("parsing registry-cas")?
.as_object()
.context("must be object")?
{
pem_strings.push(v.as_str().context(format!("must be string ({k})"))?.to_string());
}
}

Ok(pem_strings)
}

pub(crate) async fn get_openshift_proxy_trusted_certs(in_memory_etcd_client: &Arc<InMemoryK8sEtcd>) -> Result<String> {
let trusted_ca_bundle_configmap = get_etcd_json(
in_memory_etcd_client,
&(K8sResourceLocation {
namespace: Some("openshift-config-managed".into()),
kind: "ConfigMap".into(),
apiversion: "v1".into(),
name: "trusted-ca-bundle".into(),
}),
)
.await
.context("getting trusted-ca-bundle")?
.context("trusted-ca-bundle not found")?;

Ok(trusted_ca_bundle_configmap
.pointer("/data/ca-bundle.crt")
.context("parsing ca-bundle.crt")?
.as_str()
.context("must be string")?
.to_string())
}

/// MCO reads the user-ca-bundle from the openshift-config namespace directly regardless of whether
/// the Proxy CR points at it or not, so we should consider the certs in that configmap to be
/// external.
pub(crate) async fn get_openshift_user_ca_bundle(in_memory_etcd_client: &Arc<InMemoryK8sEtcd>) -> Result<Option<String>> {
let trusted_ca_bundle_configmap = get_etcd_json(
in_memory_etcd_client,
&(K8sResourceLocation {
namespace: Some("openshift-config".into()),
kind: "ConfigMap".into(),
apiversion: "v1".into(),
name: "user-ca-bundle".into(),
}),
)
.await
.context("getting trusted-ca-bundle")?;

match trusted_ca_bundle_configmap {
None => Ok(None),
Some(trusted_ca_bundle_configmap) => Ok(Some(
trusted_ca_bundle_configmap
.pointer("/data/ca-bundle.crt")
.context("parsing ca-bundle.crt")?
.as_str()
.context("must be string")?
.to_string(),
)),
}
}
2 changes: 1 addition & 1 deletion src/recert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ async fn recertify(
crypto_customizations: &CryptoCustomizations,
) -> Result<RecertifyTiming> {
let external_certs = if in_memory_etcd_client.etcd_client.is_some() {
scanning::discover_external_certs(Arc::clone(&in_memory_etcd_client))
scanning::external_certs::discover_external_certs(Arc::clone(&in_memory_etcd_client))
.await
.context("discovering external certs to ignore")?
} else {
Expand Down

0 comments on commit 208f14f

Please sign in to comment.