Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Implement CA certificate renewal #242

Merged
merged 27 commits into from
Sep 24, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
dda9286
Handle CA expiry and private key rotation
saltiyazan Sep 11, 2024
7d5b2e5
Fixes tests
saltiyazan Sep 11, 2024
333a3c9
Merge branch 'main' into TLSENG-343
saltiyazan Sep 12, 2024
f82dc34
Fetch latest lib
saltiyazan Sep 12, 2024
cfc2730
Adds unit tests
saltiyazan Sep 12, 2024
b3b93c6
Minor improvements
saltiyazan Sep 12, 2024
0d019cb
Fixes and adds integration tests
saltiyazan Sep 12, 2024
3a8b2b3
Fix revoke
saltiyazan Sep 13, 2024
a3a0b55
Fixes expiry in _generate_root_certificate
saltiyazan Sep 13, 2024
a98fad6
Use set_juju_secret
saltiyazan Sep 13, 2024
529330a
Track latest revision of secret
saltiyazan Sep 13, 2024
3dc62fc
Ensure expiry is set on secret
saltiyazan Sep 13, 2024
4f7e86f
Adds a workaround for the ops issue and addresses review comments
saltiyazan Sep 14, 2024
51e0cc7
Uses unit as part of validity string
saltiyazan Sep 14, 2024
f445de0
Fix itest typo
saltiyazan Sep 14, 2024
7dcda2c
Fix docs
saltiyazan Sep 14, 2024
e3aa0df
Test with latest requirer
saltiyazan Sep 14, 2024
4ee5f7b
Remove private key rotation to a separate pr
saltiyazan Sep 17, 2024
5adad14
Merge branch 'main' into TLSENG-343
saltiyazan Sep 17, 2024
621d8e3
Fixes config option documentation
saltiyazan Sep 17, 2024
ff4139b
Improves docstring of _renew_root_certificate
saltiyazan Sep 17, 2024
a53558e
Deploy specific revision of requirer charm
saltiyazan Sep 17, 2024
9fb803f
Lint fix
saltiyazan Sep 17, 2024
8685b3c
Specify channel with the revision
saltiyazan Sep 17, 2024
50102d8
Use minutes instead of seconds for cert validity
saltiyazan Sep 19, 2024
6d68e3c
Fix tests not to use seconds for validity
saltiyazan Sep 19, 2024
392fda8
Tests CA renewal in integration test
saltiyazan Sep 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 17 additions & 10 deletions charmcraft.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -76,37 +76,44 @@ config:
ca-common-name:
type: string
default: self-signed-certificates-operator
description: Common name to be used by the Certificate Authority.
description: Common name to be used by the Certificate Authority. Changing this value will trigger generation of a new CA certificate, revoking all previously issued certificates.
ca-organization:
type: string
description: Organization name to be used by the Certificate Authority.
description: Organization name to be used by the Certificate Authority. Changing this value will trigger generation of a new CA certificate, revoking all previously issued certificates.
ca-organizational-unit:
type: string
description: Organizational unit to be used by the Certificate Authority.
description: Organizational unit to be used by the Certificate Authority. Changing this value will trigger generation of a new CA certificate, revoking all previously issued certificates.
ca-email-address:
type: string
description: Email address to be used by the Certificate Authority.
description: Email address to be used by the Certificate Authority. Changing this value will trigger generation of a new CA certificate, revoking all previously issued certificates.
ca-country-name:
type: string
description: Country name to be used by the Certificate Authority.
description: Country name to be used by the Certificate Authority. Changing this value will trigger generation of a new CA certificate, revoking all previously issued certificates.
ca-state-or-province-name:
type: string
description: State or province name to be used by the Certificate Authority.
description: State or province name to be used by the Certificate Authority. Changing this value will trigger generation of a new CA certificate, revoking all previously issued certificates.
ca-locality-name:
type: string
description: Locality name to be used by the Certificate Authority.
description: Locality name to be used by the Certificate Authority. Changing this value will trigger generation of a new CA certificate, revoking all previously issued certificates.
root-ca-validity:
type: int
default: 365
description: RootCA certificate validity (in days).
description: RootCA certificate validity (in days), should longer than twice of certificate-validity. Changing this value will trigger generation of a new CA certificate, revoking all previously issued certificates.
gruyaume marked this conversation as resolved.
Show resolved Hide resolved
certificate-validity:
type: int
default: 365
description: Certificate validity (in days).
default: 90
description: Certificate validity (in days) must be shorter than half the root-ca-validity. Changing this value will trigger generation of a new CA certificate, revoking all previously issued certificates.
gruyaume marked this conversation as resolved.
Show resolved Hide resolved
validity-unit:
type: string
default: days
description: Validity unit weeks, days, hours, minutes or seconds, it is days by default and will be applied to both root-ca-validity and certificate-validity. Changing this value will trigger generation of a new CA certificate, revoking all previously issued certificates.
gruyaume marked this conversation as resolved.
Show resolved Hide resolved

actions:
get-ca-certificate:
description: Outputs the CA cert.

get-issued-certificates:
description: Outputs the certificates issued by the charm.

rotate-private-key:
description: Generates a new private key, new CA certificate and revokes previously issued certificates.
30 changes: 23 additions & 7 deletions lib/charms/tls_certificates_interface/v4/tls_certificates.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ def _get_config_locality_name(self) -> Optional[str]:

# Increment this PATCH version before using `charmcraft publish-lib` or reset
# to 0 if you are raising the major API version
LIBPATCH = 6
LIBPATCH = 7

PYDEPS = ["cryptography", "pydantic"]

Expand Down Expand Up @@ -862,7 +862,7 @@ def generate_csr( # noqa: C901

def generate_ca(
private_key: PrivateKey,
validity: int,
validity: timedelta,
common_name: str,
sans_dns: Optional[FrozenSet[str]] = frozenset(),
sans_ip: Optional[FrozenSet[str]] = frozenset(),
Expand All @@ -878,7 +878,7 @@ def generate_ca(

Args:
private_key (PrivateKey): Private key
validity (int): Certificate validity time (in days)
validity (timedelta): Certificate validity time
common_name (str): Common Name that can be an IP or a Full Qualified Domain Name (FQDN).
sans_dns (FrozenSet[str]): DNS Subject Alternative Names
sans_ip (FrozenSet[str]): IP Subject Alternative Names
Expand Down Expand Up @@ -945,7 +945,7 @@ def generate_ca(
.public_key(private_key_object.public_key())
.serial_number(x509.random_serial_number())
.not_valid_before(datetime.now(timezone.utc))
.not_valid_after(datetime.now(timezone.utc) + timedelta(days=validity))
.not_valid_after(datetime.now(timezone.utc) + validity)
.add_extension(x509.SubjectAlternativeName(set(_sans)), critical=False)
.add_extension(x509.SubjectKeyIdentifier(digest=subject_identifier), critical=False)
.add_extension(
Expand All @@ -971,7 +971,7 @@ def generate_certificate(
csr: CertificateSigningRequest,
ca: Certificate,
ca_private_key: PrivateKey,
validity: int,
validity: timedelta,
is_ca: bool = False,
) -> Certificate:
"""Generate a TLS certificate based on a CSR.
Expand All @@ -980,7 +980,7 @@ def generate_certificate(
csr (CertificateSigningRequest): CSR
ca (Certificate): CA Certificate
ca_private_key (PrivateKey): CA private key
validity (int): Certificate validity (in days)
validity (timedelta): Certificate validity time
is_ca (bool): Whether the certificate is a CA certificate

Returns:
Expand All @@ -999,7 +999,7 @@ def generate_certificate(
.public_key(csr_object.public_key())
.serial_number(x509.random_serial_number())
.not_valid_before(datetime.now(timezone.utc))
.not_valid_after(datetime.now(timezone.utc) + timedelta(days=validity))
.not_valid_after(datetime.now(timezone.utc) + validity)
)
extensions = _get_certificate_request_extensions(
authority_key_identifier=ca_pem.extensions.get_extension_for_class(
Expand Down Expand Up @@ -1793,6 +1793,22 @@ def get_provider_certificates(
certificates.append(certificate.to_provider_certificate(relation_id=relation.id))
return certificates

def get_unsolicited_certificates(
self, relation_id: Optional[int] = None
) -> List[ProviderCertificate]:
"""Return provider certificates for which no certificate requests exists.

Those certificates should be revoked.
"""
unsolicited_certificates: List[ProviderCertificate] = []
provider_certificates = self.get_provider_certificates(relation_id=relation_id)
requirer_csrs = self.get_certificate_requests(relation_id=relation_id)
list_of_csrs = [csr.certificate_signing_request for csr in requirer_csrs]
for certificate in provider_certificates:
if certificate.certificate_signing_request not in list_of_csrs:
unsolicited_certificates.append(certificate)
return unsolicited_certificates

def get_outstanding_certificate_requests(
self, relation_id: Optional[int] = None
) -> List[RequirerCSR]:
Expand Down
Loading
Loading