Skip to content

Commit

Permalink
fixup! Impl tedge cert renew c8y
Browse files Browse the repository at this point in the history
  • Loading branch information
didier-wenzek committed Nov 22, 2024
1 parent fba8ed6 commit 5f284b5
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 38 deletions.
44 changes: 8 additions & 36 deletions crates/core/tedge/src/cli/certificate/c8y/download.rs
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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);
Expand All @@ -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!(
Expand Down Expand Up @@ -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<String, CertError> {
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,
Expand All @@ -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(())
}
}
45 changes: 43 additions & 2 deletions crates/core/tedge/src/cli/certificate/c8y/mod.rs
Original file line number Diff line number Diff line change
@@ -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<String, CertError> {
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(())
}
74 changes: 74 additions & 0 deletions crates/core/tedge/src/cli/certificate/c8y/renew.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -37,6 +46,71 @@ impl Command for RenewCertCmd {
}

fn execute(&self) -> Result<(), MaybeFancy<Error>> {
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<Response, reqwest::Error> {
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!()
}
}

0 comments on commit 5f284b5

Please sign in to comment.