Skip to main content

trz_gateway_server/server/
certificate.rs

1use std::sync::Arc;
2use std::time::Duration;
3use std::time::SystemTime;
4
5use axum::Json;
6use axum::http::StatusCode;
7use nameth::NamedEnumValues as _;
8use nameth::NamedType as _;
9use nameth::nameth;
10use openssl::x509::X509Extension;
11use pem::PemError;
12use tracing::debug;
13use tracing::info;
14use trz_gateway_common::api::tunnel::GetCertificateRequest;
15use trz_gateway_common::http_error::HttpError;
16use trz_gateway_common::http_error::IsHttpError;
17use trz_gateway_common::is_global::IsGlobalError;
18use trz_gateway_common::x509::PemAsStringError;
19use trz_gateway_common::x509::PemString as _;
20use trz_gateway_common::x509::cert::MakeCertError;
21use trz_gateway_common::x509::cert::make_cert;
22use trz_gateway_common::x509::name::CertitficateName;
23use trz_gateway_common::x509::signed_extension::MakeSignedExtensionError;
24use trz_gateway_common::x509::signed_extension::make_signed_extension;
25use trz_gateway_common::x509::time::Asn1ToSystemTimeError;
26use trz_gateway_common::x509::validity::Validity;
27use trz_gateway_common::x509::validity::ValidityError;
28
29use super::IssuerConfig;
30use super::Server;
31use crate::auth_code::AuthCode;
32
33static CERTIFICATE_VALIDITY: Duration = Duration::from_secs(3600 * 24 * 90);
34
35impl Server {
36    /// API to issue client certificates.
37    ///
38    /// Endpoint: "/remote/certificate"
39    pub async fn get_certificate(
40        self: Arc<Self>,
41        Json(request): Json<GetCertificateRequest<AuthCode>>,
42    ) -> Result<String, HttpError<GetCertificateError>> {
43        if !request.auth_code.is_valid() {
44            info!(
45                "Invalid auth code. Got '{}' expected '{}'",
46                request.auth_code,
47                AuthCode::current()
48            );
49            return Err(GetCertificateError::InvalidAuthCode)?;
50        }
51        Ok(self.make_pem_cert(request)?)
52    }
53
54    fn make_pem_cert(
55        &self,
56        request: GetCertificateRequest<AuthCode>,
57    ) -> Result<String, GetCertificateError> {
58        let issuer_config = self
59            .issuer_config
60            .get()
61            .map_err(GetCertificateError::IssuerConfig)?;
62        let now = SystemTime::now();
63        let mut validity = issuer_config.validity;
64        validity.from = now;
65        validity.to = SystemTime::min(validity.to, now + CERTIFICATE_VALIDITY);
66        debug!(
67            "Issuing client certificate for {} valid until {:?}",
68            request.name, validity.to
69        );
70        let signed_extension = self.make_signed_extension(&issuer_config, &request, validity)?;
71        Ok(self.assemble_pem_cert(request, validity, signed_extension)?)
72    }
73
74    fn make_signed_extension(
75        &self,
76        issuer_config: &IssuerConfig,
77        request: &GetCertificateRequest<AuthCode>,
78        validity: Validity,
79    ) -> Result<X509Extension, GetCertificateError> {
80        Ok(make_signed_extension(
81            &request.name,
82            validity,
83            pem::parse(&request.public_key)
84                .map_err(GetCertificateError::InvalidPublicKeyPem)?
85                .contents(),
86            Some(&issuer_config.intermediates),
87            (*issuer_config.signer).as_ref(),
88        )?)
89    }
90
91    fn assemble_pem_cert(
92        &self,
93        request: GetCertificateRequest<AuthCode>,
94        validity: Validity,
95        signed_extension: X509Extension,
96    ) -> Result<String, MakePemCertificateError> {
97        let certificate = make_cert(
98            (*self.root_ca).as_ref(),
99            CertitficateName {
100                common_name: Some(&request.name),
101                ..CertitficateName::default()
102            },
103            validity,
104            &request.public_key,
105            vec![signed_extension],
106        )?;
107        Ok(certificate.to_pem().pem_string()?)
108    }
109}
110
111#[nameth]
112#[derive(thiserror::Error, Debug)]
113pub enum GetCertificateError {
114    #[error("[{n}] {t} is invalid", n = self.name(), t = AuthCode::type_name())]
115    InvalidAuthCode,
116
117    #[error("[{n}] {0}", n = self.name())]
118    Validity(#[from] ValidityError<Asn1ToSystemTimeError>),
119
120    #[error("[{n}] {0}", n = self.name())]
121    InvalidPublicKeyPem(PemError),
122
123    #[error("[{n}] {0}", n = self.name())]
124    MakeSignedExtension(#[from] MakeSignedExtensionError),
125
126    #[error("[{n}] {0}", n = self.name())]
127    MakeCert(#[from] MakePemCertificateError),
128
129    #[error("[{n}] {0}", n = self.name())]
130    IssuerConfig(Arc<dyn IsGlobalError>),
131}
132
133#[nameth]
134#[derive(thiserror::Error, Debug)]
135pub enum MakePemCertificateError {
136    #[error("[{n}] {0}", n = self.name())]
137    MakeCert(#[from] MakeCertError),
138
139    #[error("[{n}] Failed to convert certificate to PEM: {0}", n = self.name())]
140    PemString(#[from] PemAsStringError),
141}
142
143impl IsHttpError for GetCertificateError {
144    fn status_code(&self) -> StatusCode {
145        match self {
146            Self::InvalidAuthCode => StatusCode::FORBIDDEN,
147            Self::MakeCert(error) => error.status_code(),
148            Self::Validity { .. } => StatusCode::INTERNAL_SERVER_ERROR,
149            Self::InvalidPublicKeyPem { .. } => StatusCode::BAD_REQUEST,
150            Self::MakeSignedExtension { .. } => StatusCode::INTERNAL_SERVER_ERROR,
151            Self::IssuerConfig { .. } => StatusCode::INTERNAL_SERVER_ERROR,
152        }
153    }
154}
155
156impl IsHttpError for MakePemCertificateError {
157    fn status_code(&self) -> StatusCode {
158        match self {
159            Self::MakeCert(error) => error.status_code(),
160            Self::PemString { .. } => StatusCode::INTERNAL_SERVER_ERROR,
161        }
162    }
163}