Skip to content

Commit

Permalink
feat: TLS provider
Browse files Browse the repository at this point in the history
  • Loading branch information
kayra1 committed Aug 27, 2024
1 parent ac985c0 commit cb713b2
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 0 deletions.
41 changes: 41 additions & 0 deletions src/charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from charms.prometheus_k8s.v0.prometheus_scrape import MetricsEndpointProvider
from charms.tls_certificates_interface.v4.tls_certificates import (
Certificate,
ProviderCertificate,
TLSCertificatesProvidesV4,
generate_ca,
generate_certificate,
Expand Down Expand Up @@ -112,6 +113,7 @@ def configure(self, event: ops.EventBase):
self._configure_gocert_config_file()
self._configure_access_certificates()
self._configure_charm_authorization()
self._configure_certificate_requirers()

def _on_collect_status(self, event: ops.CollectStatusEvent):
if not self.unit.is_leader():
Expand Down Expand Up @@ -170,6 +172,45 @@ def _configure_charm_authorization(self):
login_details_secret = self.model.get_secret(label=GOCERT_LOGIN_SECRET_LABEL)
login_details_secret.set_content(login_details.to_dict())

def _configure_certificate_requirers(self):
"""Get all CSR's and certs from databags and Gocert, compare differences and update requirers if needed."""
login_details = self._get_or_create_admin_account()
logger.warning(login_details)
if not login_details.token:
logger.warning("couldn't distribute certificates: not logged in")
return

databag_csrs = self.tls.get_certificate_requests()
gocert_csrs_table = self.client.get_certificate_requests_table(login_details.token)
if not gocert_csrs_table:
logger.warning("couldn't distribute certificates: couldn't get table from GoCert")
return
for request in databag_csrs:
matching_rows = list(
filter(
lambda x: x.csr == request.certificate_signing_request.raw,
gocert_csrs_table.rows,
)
)
if len(matching_rows) < 1:
self.client.post_csr(request.certificate_signing_request.raw, login_details.token)
continue
matching_row = matching_rows[0]
provided_certificates = self.tls.get_issued_certificates(request.relation_id)
if matching_row.certificate != "" and matching_row.certificate not in [
obj.certificate.raw for obj in provided_certificates
]:
certificate = Certificate().from_string(matching_row.certificate)
self.tls.set_relation_certificate(
ProviderCertificate(
relation_id=request.relation_id,
certificate_signing_request=request.certificate_signing_request,
certificate=certificate,
ca=Certificate(),
chain=[certificate],
)
)

## Properties ##
@property
def _pebble_layer(self) -> ops.pebble.LayerDict:
Expand Down
61 changes: 61 additions & 0 deletions src/gocert.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
"""Library for interacting with the GoCert application."""

import logging
from dataclasses import dataclass
from typing import List

import requests

Expand All @@ -14,6 +16,22 @@ class GoCertClientError(Exception):
"""Base class for exceptions raised by the GoCert client."""


@dataclass(frozen=True)
class CertificateRequest:
"""The certificate request that's stored in GoCert."""

id: int
csr: str
certificate: str


@dataclass
class CertificateRequests:
"""The table of certificate requests in GoCert."""

rows: List[CertificateRequest]


class GoCert:
"""Class to interact with GoCert."""

Expand Down Expand Up @@ -113,3 +131,46 @@ def create_first_user(self, username: str, password: str) -> int | None:
logger.info("created the first user in GoCert.")
id = req.json().get("id")
return int(id) if id else None

def get_certificate_requests_table(self, token: str) -> CertificateRequests | None:
"""Get all certificate requests table from GoCert."""
try:
req = requests.get(
f"{self.url}/api/{self.API_VERSION}/certificate_requests",
verify=self.ca_path if self.ca_path else None,
headers={"Authorization": f"Bearer {token}"},
)
req.raise_for_status()
except (requests.RequestException, OSError):
logger.warning(
"couldn't retrieve certificate requests table: code %s, %s",
req.status_code,
req.text,
)
return None
table = req.json()
return CertificateRequests(
rows=[
CertificateRequest(csr.get("id"), csr.get("csr"), csr.get("certificate"))
for csr in table
]
if table
else []
)

def post_csr(self, csr: str, token: str) -> None:
"""Post a new CSR to GoCert."""
try:
req = requests.post(
f"{self.url}/api/{self.API_VERSION}/certificate_requests",
verify=self.ca_path if self.ca_path else None,
headers={"Authorization": f"Bearer {token}"},
data=csr,
)
req.raise_for_status()
except (requests.RequestException, OSError):
logger.error(
"couldn't post new certificate requests: code %s, %s",
req.status_code,
req.text,
)

0 comments on commit cb713b2

Please sign in to comment.