From 5f284b550b7d3dbc315da9842c326aa79764bf2f Mon Sep 17 00:00:00 2001 From: Didier Wenzek Date: Fri, 22 Nov 2024 17:41:17 +0100 Subject: [PATCH] fixup! Impl tedge cert renew c8y --- .../tedge/src/cli/certificate/c8y/download.rs | 44 ++--------- .../core/tedge/src/cli/certificate/c8y/mod.rs | 45 ++++++++++- .../tedge/src/cli/certificate/c8y/renew.rs | 74 +++++++++++++++++++ 3 files changed, 125 insertions(+), 38 deletions(-) diff --git a/crates/core/tedge/src/cli/certificate/c8y/download.rs b/crates/core/tedge/src/cli/certificate/c8y/download.rs index 0b96614aba4..192c13e5389 100644 --- a/crates/core/tedge/src/cli/certificate/c8y/download.rs +++ b/crates/core/tedge/src/cli/certificate/c8y/download.rs @@ -1,25 +1,20 @@ -use crate::bridge::BridgeLocation; +use crate::cli::certificate::c8y::create_device_csr; +use crate::cli::certificate::c8y::store_device_cert; use crate::command::Command; use crate::error; use crate::get_webpki_error_from_reqwest; use crate::log::MaybeFancy; -use crate::read_cert_to_string; use crate::warning; -use crate::CertError; -use crate::CreateCertCmd; use anyhow::Error; use camino::Utf8PathBuf; use certificate::CloudRootCerts; -use certificate::NewCertificateConfig; use hyper::StatusCode; use reqwest::blocking::Response; use reqwest::header::CONTENT_TYPE; -use std::fs::OpenOptions; use std::io::Write; use std::time::Duration; use tedge_config::HostPort; use tedge_config::HTTPS_PORT; -use tedge_utils::paths::set_permission; use url::Url; /// Command to request and download a device certificate from Cumulocity @@ -62,7 +57,11 @@ impl Command for DownloadCertCmd { impl DownloadCertCmd { fn download_device_certificate(&self) -> Result<(), Error> { let (common_name, security_token) = self.get_registration_data()?; - let csr = self.create_device_csr(common_name.clone())?; + let csr = create_device_csr( + common_name.clone(), + self.key_path.clone(), + self.csr_path.clone(), + )?; let http = self.root_certs.blocking_client(); let url = format!("https://{}/.well-known/est/simpleenroll", self.c8y_url); @@ -73,7 +72,7 @@ impl DownloadCertCmd { match result { Ok(response) if response.status() == StatusCode::OK => { if let Ok(cert) = response.text() { - self.store_device_cert(cert)?; + store_device_cert(&self.cert_path, cert)?; return Ok(()); } error!( @@ -126,20 +125,6 @@ impl DownloadCertCmd { Ok((device_id, security_token)) } - /// Create the device private key and CSR - fn create_device_csr(&self, common_name: String) -> Result { - let config = NewCertificateConfig::default(); - let create_cmd = CreateCertCmd { - id: common_name, - cert_path: self.cert_path.clone(), - key_path: self.key_path.clone(), - csr_path: Some(self.csr_path.clone()), - bridge_location: BridgeLocation::BuiltIn, - }; - create_cmd.create_certificate_signing_request(&config)?; - read_cert_to_string(&self.csr_path) - } - /// Post the device CSR fn post_device_csr( &self, @@ -155,17 +140,4 @@ impl DownloadCertCmd { .body(csr.to_string()) .send() } - - fn store_device_cert(&self, cert: String) -> Result<(), CertError> { - let mut file = OpenOptions::new() - .write(true) - .create_new(true) - .open(&self.cert_path)?; - - file.write_all(cert.as_bytes())?; - file.sync_all()?; - - set_permission(&file, 0o444)?; - Ok(()) - } } diff --git a/crates/core/tedge/src/cli/certificate/c8y/mod.rs b/crates/core/tedge/src/cli/certificate/c8y/mod.rs index d0375a41011..290e2ba62e7 100644 --- a/crates/core/tedge/src/cli/certificate/c8y/mod.rs +++ b/crates/core/tedge/src/cli/certificate/c8y/mod.rs @@ -1,7 +1,48 @@ -mod upload; mod download; mod renew; +mod upload; -pub use upload::UploadCertCmd; +use crate::bridge::BridgeLocation; +use crate::read_cert_to_string; +use crate::CertError; +use crate::CreateCertCmd; +use camino::Utf8PathBuf; +use certificate::NewCertificateConfig; pub use download::DownloadCertCmd; pub use renew::RenewCertCmd; +use std::fs::OpenOptions; +use std::io::Write; +use tedge_utils::paths::set_permission; +pub use upload::UploadCertCmd; + +/// Create a device private key and CSR +fn create_device_csr( + common_name: String, + key_path: Utf8PathBuf, + csr_path: Utf8PathBuf, +) -> Result { + let config = NewCertificateConfig::default(); + let create_cmd = CreateCertCmd { + id: common_name, + cert_path: "should-be-unused".into(), + key_path, + csr_path: Some(csr_path.clone()), + bridge_location: BridgeLocation::BuiltIn, + }; + create_cmd.create_certificate_signing_request(&config)?; + read_cert_to_string(&csr_path) +} + +/// Store a device certificate +fn store_device_cert(cert_path: &Utf8PathBuf, cert: String) -> Result<(), CertError> { + let mut file = OpenOptions::new() + .write(true) + .create_new(true) + .open(cert_path)?; + + file.write_all(cert.as_bytes())?; + file.sync_all()?; + + set_permission(&file, 0o444)?; + Ok(()) +} diff --git a/crates/core/tedge/src/cli/certificate/c8y/renew.rs b/crates/core/tedge/src/cli/certificate/c8y/renew.rs index 33d656c647d..ba0b9fc40ef 100644 --- a/crates/core/tedge/src/cli/certificate/c8y/renew.rs +++ b/crates/core/tedge/src/cli/certificate/c8y/renew.rs @@ -1,11 +1,20 @@ +use crate::cli::certificate::c8y::create_device_csr; +use crate::cli::certificate::c8y::store_device_cert; use crate::command::Command; +use crate::error; +use crate::get_webpki_error_from_reqwest; use crate::log::MaybeFancy; use anyhow::Error; use camino::Utf8PathBuf; use certificate::CloudRootCerts; +use hyper::header::AUTHORIZATION; +use hyper::header::CONTENT_TYPE; +use hyper::StatusCode; +use reqwest::blocking::Response; use tedge_config::HostPort; use tedge_config::HTTPS_PORT; use tedge_config::MQTT_TLS_PORT; +use url::Url; /// Command to renew a device certificate from Cumulocity pub struct RenewCertCmd { @@ -37,6 +46,71 @@ impl Command for RenewCertCmd { } fn execute(&self) -> Result<(), MaybeFancy> { + Ok(self.renew_device_certificate()?) + } +} + +impl RenewCertCmd { + fn renew_device_certificate(&self) -> Result<(), Error> { + let csr = create_device_csr( + self.device_id.clone(), + self.key_path.clone(), + self.csr_path.clone(), + )?; + + let jwt_token = self.get_jwt_token(); + + let http = self.root_certs.blocking_client(); + let url = format!("https://{}/.well-known/est/simpleenroll", self.c8y_url); + let url = Url::parse(&url)?; + let result = self.post_device_csr(&http, &url, &jwt_token, &csr); + match result { + Ok(response) if response.status() == StatusCode::OK => { + if let Ok(cert) = response.text() { + store_device_cert(&self.cert_path, cert)?; + return Ok(()); + } + error!( + "Fail to extract a certificate from the response returned by {}", + self.c8y_url + ); + } + Ok(response) => { + error!( + "The device certificate cannot be renewed on {}: {:?}", + self.c8y_url, + response.status() + ); + } + Err(err) => { + error!( + "Fail to connect to {}: {:?}", + self.c8y_url, + get_webpki_error_from_reqwest(err) + ) + } + } + + Ok(()) + } + + /// Post the device CSR + fn post_device_csr( + &self, + http: &reqwest::blocking::Client, + url: &Url, + jwt_token: &str, + csr: &str, + ) -> Result { + http.post(url.clone()) + .header(AUTHORIZATION, format!("Bearer {jwt_token}")) + .header(CONTENT_TYPE, "text/plain") + .body(csr.to_string()) + .send() + } + + fn get_jwt_token(&self) -> String { + let _ = self.c8y_mqtt.clone(); todo!() } }