trz_gateway_server/server/
certificate.rs1use 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 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}