Skip to content

feat: Integration with Cumulocity CA#3259

Merged
didier-wenzek merged 18 commits intothin-edge:mainfrom
didier-wenzek:feat/integration-with-cumulocity-ca
Apr 1, 2025
Merged

feat: Integration with Cumulocity CA#3259
didier-wenzek merged 18 commits intothin-edge:mainfrom
didier-wenzek:feat/integration-with-cumulocity-ca

Conversation

@didier-wenzek
Copy link
Copy Markdown
Contributor

@didier-wenzek didier-wenzek commented Nov 21, 2024

Proposed changes

  • Impl tedge cert download c8y
  • Impl tedge cert renew c8y
  • Enhance tedge config with setting for certificate validity period
  • Enhance tedge cert show to display number of days before expiration
  • Enhance tedge cert show to display certificate serial number
  • Enhance tedge cert show to display any cert
  • Get and renew certificates using CSRs that have been generated elsewhere (typically an HSM)
  • Fix tedge cert renew arguments: cloud vs ca
  • Make sure the current certificate is not erased on renewal This will be done by an external tool (wrapping the renewal with a reconnect and a rollback on error)

Types of changes

  • Bugfix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Improvement (general improvements like code refactoring that doesn't explicitly fix a bug or add any new functionality)
  • Documentation Update (if none of the other choices apply)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)

Paste Link to the issue

#3248

Checklist

  • I have read the CONTRIBUTING doc
  • I have signed the CLA (in all commits with git commit -s)
  • I ran cargo fmt as mentioned in CODING_GUIDELINES
  • I used cargo clippy as mentioned in CODING_GUIDELINES
  • I have added tests that prove my fix is effective or that my feature works
  • I have added necessary documentation (if appropriate)

Further comments

@didier-wenzek didier-wenzek added feature Change request theme:c8y Theme: Cumulocity related topics theme:certificates Theme: Device certificate topics labels Nov 21, 2024
@didier-wenzek didier-wenzek force-pushed the feat/integration-with-cumulocity-ca branch from 844dc34 to 284c8ee Compare November 22, 2024 10:16
@didier-wenzek didier-wenzek force-pushed the feat/integration-with-cumulocity-ca branch from 284c8ee to fba8ed6 Compare November 22, 2024 16:00
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Nov 28, 2024

Robot Results

✅ Passed ❌ Failed ⏭️ Skipped Total Pass % ⏱️ Duration
603 0 3 603 100 1h45m24.206169999s

@reubenmiller
Copy link
Copy Markdown
Contributor

Cert renewal

To start out, the certificate renewal will be facilitated by an external process (default using system timer service) to actively renew the certificate.

In order to make it easy for external processes to trigger a certificate renewal, there needs to be a way a process can check if the certificate is about to expire (with the minimum value as defined in certificate.validity.minimum_duration in the tedge.toml).

The command used to check the validity of the certificate should produce the following exit code, so that the calling process can respond accordingly. There are several options where the above certificate validity can be placed, below are some options.

Inspiration for this type of renewal is taken from smallstep

Option 1: New subcommand to check if the cert needs renewal or not

tedge cert needs-renewal c8y

Exit codes:

  • 0 - certificate needs renewal as it is no longer valid, or it will expire within the duration, certificate.validity.minimum_duration
  • 1 - certificate is still valid and does not need renewal
  • 2 - unexpected error (e.g. certificate does not exist, or can't be read)

Option 2: Flag in renew command

tedge cert renew c8y --needs-renewal

Exit codes:

  • 0 - certificate needs renewal as it is no longer valid, or it will expire within the duration, certificate.validity.minimum_duration
  • 1 - certificate is still valid and does not need renewal
  • 2 - unexpected error (e.g. certificate does not exist, or can't be read)

@didier-wenzek
Copy link
Copy Markdown
Contributor Author

didier-wenzek commented Mar 24, 2025

Exit codes:

  • 0 - certificate needs renewal as it is no longer valid, or it will expire within the duration, certificate.validity.minimum_duration
  • 1 - certificate is still valid and does not need renewal
  • 2 - unexpected error (e.g. certificate does not exist, or can't be read)

I would return a non 0 value when the certificate is already expired - because it can no more be renew then. But maybe, this is a design choice to proceed in such in a case (maybe device time is not correctly set and the certificate is actually still valid). Isn't it?

@reubenmiller
Copy link
Copy Markdown
Contributor

reubenmiller commented Mar 24, 2025

Exit codes:

  • 0 - certificate needs renewal as it is no longer valid, or it will expire within the duration, certificate.validity.minimum_duration
  • 1 - certificate is still valid and does not need renewal
  • 2 - unexpected error (e.g. certificate does not exist, or can't be read)

I would return a non 0 value when the certificate is already expired - because it can no more be renew then. But maybe, this is a design choice to proceed in such in a case (maybe device time is not correctly set and the certificate is actually still valid). Isn't it??

Yes basically. The renewal might not work, but at least it will be attempted. This choice was also taken from the smallstep cli command, step certificate needs-renewal

Below is the example of the exist code description from their docs:

This command returns '0' if the X509 certificate needs renewal, '1' if the X509 certificate does not need renewal, '2' if the X509 certificate file does not exist, and '255' for any other error.

@didier-wenzek
Copy link
Copy Markdown
Contributor Author

Cert renewal

Option 1: New subcommand to check if the cert needs renewal or not

Implemented with ea03cc5

}
};

if is_self_signed {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be useful if we allow users to migrate from a self-signed certificate to a certificate issued by the Cumulocity CA, as this is probably the position that a lot of thin-edge.io c8y users are currently in.

So it would be useful to be able to control whether we're re-creating a new self-signed certificate, or doing the default action to request a new certificate via the new Cumulocity CA service.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Indeed, directly asking c8y CA for a renewal should work in such a case despite the current certificate having not been issued by v8y.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I can confirm that Cumulocity can issue a new certificate to the device (as it is using the JWT for validation and not the certificate, which works nicely)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see two options, my preference being for the second one.

implicit:

When the certificate is used to connect c8y, try first to post the renewal request to c8y CA and fallback to a self-signed certificate when the response is a 404 and the current certificate is self-signed.

  • Warn the user when the new certificate is self-signed: this certificate has to be uploaded on the tenant to be trusted.
  • Pros: No new command line options
  • Cons: the command seems simpler to use, but it is not, as a second critical step might be required.

explicit:

Add a --self-signed option (which default is false).

  • When --self-signed is false and the endpoint is c8y, then request the renewal to c8y CA. Let the current certificate unchanged if this fails.
  • Return an error if --self-signed is true but the certificate is not self-signed.
  • Return an error if --self-signed is false and the endpoint is not c8y.
  • Cons: This changes how tedge cert renew behaves
  • Pros: Make self-signed certificates second-class citizens

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be useful if we allow users to migrate from a self-signed certificate to a certificate issued by the Cumulocity CA, as this is probably the position that a lot of thin-edge.io c8y users are currently in.

Similarly, we can support users moving away from device passwords.

So it would be useful to be able to control whether we're re-creating a new self-signed certificate, or doing the default action to request a new certificate via the new Cumulocity CA service.

And, along the same lines, it would be useful to improve tedge cert download or tedge cert renew so a very first certificate can be retrieved using the renewal API and authenticating via JWT.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a --self-signed option (which default is false).

Implemented: db15855

Copy link
Copy Markdown
Member

@rina23q rina23q left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes are very clear and easy to read. I was not able to test with c8y fully since my tenant is missing something for now. I'll test again once my tenant issue is solved.

Copy link
Copy Markdown
Member

@rina23q rina23q left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It works like a charm, really nice feature! Now it's super easy to register a device in c8y!

Copy link
Copy Markdown
Contributor

@reubenmiller reubenmiller left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved (from a functional level). It has all of the fundamental elements to build a renewal service on top.

Copy link
Copy Markdown
Member

@Bravo555 Bravo555 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As for the code itself, LGTM, only managed to find a single typo. But are documentation additions, like a reference or a user guide planned in this PR or in a follow-up?

Cargo.toml Outdated
proptest = "1.0"
quote = "1"
rand = "0.8"
rasn = "0.18.0"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: Is there a specific reason why we're not using the latest version?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding crate versions, long time ago, we decided to specify until minor version (like 0.18), but I see now our Cargo.toml is mixed of major.minor.patch and major.minor. I'm also curious why here is 3 digits.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because not compatible with our current MSRV.

Added a comment and removed the patch suffix: e777d3e

/// This can be used to bypass the default behavior
/// which is to forward the renewal request to the cloud CA
/// even if the current certificate has not been signed by this CA.
/// In most case, the default behavior is what you want:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo:

Suggested change
/// In most case, the default behavior is what you want:
/// In most cases, the default behavior is what you want:

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed bf2f748

Signed-off-by: Didier Wenzek <didier.wenzek@free.fr>
Copy link
Copy Markdown
Contributor

@albinsuresh albinsuresh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

#[tedge_config(example = "365d", default(from_str = "365d"))]
requested_duration: SecondsOrHumanTime,

/// Minimum validity duration bellow which a new certificate should be requested
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Minimum validity duration bellow which a new certificate should be requested
/// Minimum validity duration below which a new certificate should be requested

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: ff6c58f

#[clap(verbatim_doc_comment)]
/// Request and download the device certificate from Cumulocity
///
/// - Generate a private key and Signing Certificate Request (CSR) for the device
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// - Generate a private key and Signing Certificate Request (CSR) for the device
/// - Generate a private key and Certificate Signing Request (CSR) for the device

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/// - Generate a private key and Signing Certificate Request (CSR) for the device
/// - Upload this CSR on Cumulocity, using the provided device identifier and security token
/// - Loop till the device is registered by an administrator and the CSR accepted
/// - Store the certificate created by Cumulocity
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// - Store the certificate created by Cumulocity
/// - Download and store the certificate created by Cumulocity

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

didier-wenzek and others added 17 commits April 1, 2025 14:25
Signed-off-by: Didier Wenzek <didier.wenzek@free.fr>
Signed-off-by: Didier Wenzek <didier.wenzek@free.fr>
Signed-off-by: Didier Wenzek <didier.wenzek@free.fr>
Signed-off-by: Didier Wenzek <didier.wenzek@free.fr>
Signed-off-by: Didier Wenzek <didier.wenzek@free.fr>
Signed-off-by: Didier Wenzek <didier.wenzek@free.fr>
Signed-off-by: Didier Wenzek <didier.wenzek@free.fr>
Previously, this command was only able to show
the device certificate (for some cloud profile)

Signed-off-by: Didier Wenzek <didier.wenzek@free.fr>
Signed-off-by: Didier Wenzek <didier.wenzek@free.fr>
Signed-off-by: Didier Wenzek <didier.wenzek@free.fr>
The current implementation is simplified knowing that `tedge cert renew`
only supports self-signed certificates and certificates signed by c8y
and more precisely by the CA of the c8y tenant the device is connected to.

Signed-off-by: Didier Wenzek <didier.wenzek@free.fr>
Signed-off-by: Didier Wenzek <didier.wenzek@free.fr>
Signed-off-by: Didier Wenzek <didier.wenzek@free.fr>
This licence is indirectly required by `rasn-cms`
the crate used to decode pkcs#7 certificates received from EST.

Signed-off-by: Didier Wenzek <didier.wenzek@free.fr>
Signed-off-by: Didier Wenzek <didier.wenzek@free.fr>
…gned

Signed-off-by: Didier Wenzek <didier.wenzek@free.fr>

impl DownloadCertCmd {
async fn download_device_certificate(&self) -> Result<(), Error> {
let (common_name, security_token) = self.get_registration_data()?;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably use spawn_blocking

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has been addressed by ec2bf68

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature Change request theme:c8y Theme: Cumulocity related topics theme:certificates Theme: Device certificate topics

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants