1use std::str::FromStr;
11
12use anyhow::bail;
13use serde::Deserialize;
14
15use mz_cloud_resources::crd::{
16 ManagedResource, MaterializeCertSpec,
17 generated::cert_manager::certificates::{
18 Certificate, CertificatePrivateKey, CertificatePrivateKeyAlgorithm,
19 CertificatePrivateKeyEncoding, CertificatePrivateKeyRotationPolicy, CertificateSpec,
20 },
21};
22
23#[derive(Clone, Deserialize, Default)]
24#[serde(rename_all = "camelCase")]
25pub struct DefaultCertificateSpecs {
26 pub balancerd_external: Option<MaterializeCertSpec>,
27 pub console_external: Option<MaterializeCertSpec>,
28 pub internal: Option<MaterializeCertSpec>,
29}
30
31impl FromStr for DefaultCertificateSpecs {
32 type Err = serde_json::Error;
33
34 fn from_str(s: &str) -> Result<Self, Self::Err> {
35 serde_json::from_str(s)
36 }
37}
38
39pub fn create_certificate(
40 default_spec: Option<MaterializeCertSpec>,
41 resource: &impl ManagedResource,
42 mz_cert_spec: Option<MaterializeCertSpec>,
43 cert_name: String,
44 secret_name: String,
45 additional_dns_names: Option<Vec<String>>,
46 algorithm_hint: CertificatePrivateKeyAlgorithm,
47 size_hint: Option<i64>,
48) -> anyhow::Result<Option<Certificate>> {
49 let default_spec = default_spec.unwrap_or_else(MaterializeCertSpec::default);
50 let mz_cert_spec = mz_cert_spec.unwrap_or_else(MaterializeCertSpec::default);
51 let Some(issuer_ref) = mz_cert_spec.issuer_ref.or(default_spec.issuer_ref) else {
52 return Ok(None);
53 };
54 let mut secret_template = mz_cert_spec
55 .secret_template
56 .or(default_spec.secret_template)
57 .unwrap_or_default();
58 secret_template.labels = Some(
59 secret_template
60 .labels
61 .unwrap_or_default()
62 .into_iter()
63 .chain(resource.default_labels())
64 .collect(),
65 );
66 let mut dns_names = mz_cert_spec
67 .dns_names
68 .or(default_spec.dns_names)
69 .unwrap_or_default();
70 if let Some(names) = additional_dns_names {
71 dns_names.extend(names);
72 }
73 let private_key_algorithm = mz_cert_spec
74 .private_key_algorithm
75 .or(default_spec.private_key_algorithm)
76 .unwrap_or(algorithm_hint);
77 let private_key_size = match private_key_algorithm {
78 CertificatePrivateKeyAlgorithm::Rsa => {
79 let size = mz_cert_spec
80 .private_key_size
81 .or(default_spec.private_key_size)
82 .unwrap_or_else(|| size_hint.unwrap_or(4096));
83 if size < 2048 {
84 bail!("RSA key size must be at least 2048 bits");
85 }
86 Some(size)
87 }
88 CertificatePrivateKeyAlgorithm::Ecdsa => {
89 let size = mz_cert_spec
90 .private_key_size
91 .or(default_spec.private_key_size)
92 .unwrap_or_else(|| size_hint.unwrap_or(256));
93 if ![256, 384, 521].contains(&size) {
94 bail!("ECDSA key size must be one of 256, 384, or 521 bits");
95 }
96 Some(size)
97 }
98 CertificatePrivateKeyAlgorithm::Ed25519 => None,
99 };
100 Ok(Some(Certificate {
101 metadata: resource.managed_resource_meta(cert_name),
102 spec: CertificateSpec {
103 dns_names: Some(dns_names),
104 duration: mz_cert_spec.duration.or(default_spec.duration),
105 issuer_ref,
106 private_key: Some(CertificatePrivateKey {
107 algorithm: Some(private_key_algorithm),
108 encoding: Some(CertificatePrivateKeyEncoding::Pkcs8),
109 rotation_policy: Some(CertificatePrivateKeyRotationPolicy::Always),
110 size: private_key_size,
111 }),
112 renew_before: mz_cert_spec.renew_before.or(default_spec.renew_before),
113 secret_name,
114 secret_template: Some(secret_template),
115 ..Default::default()
116 },
117 status: None,
118 }))
119}
120
121pub fn issuer_ref_defined(
122 defaults: &Option<MaterializeCertSpec>,
123 overrides: &Option<MaterializeCertSpec>,
124) -> bool {
125 overrides
126 .as_ref()
127 .and_then(|spec| spec.issuer_ref.as_ref())
128 .is_some()
129 || defaults
130 .as_ref()
131 .and_then(|spec| spec.issuer_ref.as_ref())
132 .is_some()
133}