Skip to main content

mz_orchestratord/
tls.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10use 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}