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

https redirect config block #246

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
52 changes: 41 additions & 11 deletions src/charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import re
import socket
import typing
from string import Template
from typing import Any, Dict, List, Optional, Union
from urllib.parse import urlparse

Expand Down Expand Up @@ -85,7 +86,7 @@
_SERVER_KEY_PATH = f"{_DYNAMIC_CONFIG_DIR}/server.key"
_CA_CERTS_PATH = "/usr/local/share/ca-certificates"
_CA_CERT_PATH = f"{_CA_CERTS_PATH}/traefik-ca.crt"

_RECV_CA_TEMPLATE = Template(f"{_CA_CERTS_PATH}/receive-ca-cert-$rel_id-ca.crt")

BIN_PATH = "/usr/bin/traefik"

Expand Down Expand Up @@ -251,13 +252,35 @@ def __init__(self, *args):

def _on_recv_ca_cert_available(self, event: CertificateTransferAvailableEvent):
# Assuming only one cert per relation (this is in line with the original lib design).
target = f"{_CA_CERTS_PATH}/receive-ca-cert-{event.relation_id}-ca.crt"
self.container.push(target, event.ca, make_dirs=True)
self._update_received_ca_certs(event)

def _update_received_ca_certs(self, event: Optional[CertificateTransferAvailableEvent] = None):
"""Push the cert attached to the event, if it is given; otherwise push all certs.

This function is needed because relation events are not emitted on upgrade, and because we
do not have (nor do we want) persistent storage for certs.
Calling this function from upgrade-charm might be too early though. Pebble-ready is
preferred.
"""
if event:
self.container.push(
_RECV_CA_TEMPLATE.substitute(rel_id=event.relation_id), event.ca, make_dirs=True
)
else:
for relation in self.model.relations.get(self.recv_ca_cert.relationship_name, []):
# For some reason, relation.units includes our unit and app. Need to exclude them.
for unit in set(relation.units).difference([self.app, self.unit]):
# Note: this nested loop handles the case of multi-unit CA, each unit providing
# a different ca cert, but that is not currently supported by the lib itself.
cert_path = _RECV_CA_TEMPLATE.substitute(rel_id=relation.id)
if cert := relation.data[unit].get("ca"):
self.container.push(cert_path, cert, make_dirs=True)

self._update_system_certs()

def _on_recv_ca_cert_removed(self, event: CertificateTransferRemovedEvent):
# Assuming only one cert per relation (this is in line with the original lib design).
target = f"{_CA_CERTS_PATH}/receive-ca-cert-{event.relation_id}-ca.crt"
target = _RECV_CA_TEMPLATE.substitute(rel_id=event.relation_id)
self.container.remove_path(target, recursive=True)
self._update_system_certs()

Expand Down Expand Up @@ -421,13 +444,19 @@ def _push_config(self):
tcp_entrypoints = self._tcp_entrypoints()
logger.debug(f"Statically configuring traefik with tcp entrypoints: {tcp_entrypoints}.")

web_config = {
"address": f":{self._port}",
# http -> https redirect can be enabled by default
"http": {"redirections": {"entryPoint": {"to": "websecure", "scheme": "https"}}},
}

traefik_config = {
"log": {
"level": "DEBUG",
},
"entryPoints": {
"diagnostics": {"address": f":{self._diagnostics_port}"},
"web": {"address": f":{self._port}"},
"web": web_config,
"websecure": {"address": f":{self._tls_port}"},
**tcp_entrypoints,
},
Expand Down Expand Up @@ -514,6 +543,7 @@ def _on_traefik_pebble_ready(self, _: PebbleReadyEvent):
self._clear_all_configs_and_restart_traefik()
# push the (fresh new) configs.
self._process_status_and_configurations()
self._update_received_ca_certs()
self._set_workload_version()

def _clear_all_configs_and_restart_traefik(self):
Expand Down Expand Up @@ -993,14 +1023,14 @@ def _generate_config_block(
transports = {}

elif is_end_to_end:
# We cannot assume traefik's CA is the same CA that signed the proxied apps.
# Since we use the update_ca_certificates machinery, we don't need to specify the
# "rootCAs" entry.
# Keeping the serverTransports section anyway because it is informative ("endToEndTLS"
# vs "reverseTerminationTransport") when inspecting the config file in production.
transport_name = "endToEndTLS"
lb_def["serversTransport"] = transport_name
transports = {
transport_name: {
# Note: assuming traefik's CA is the same CA that signed the proxied apps.
"rootCAs": [_CA_CERT_PATH],
}
}
transports = {transport_name: {"insecureSkipVerify": False}}

else:
transports = {}
Expand Down
2 changes: 2 additions & 0 deletions tests/unit/test_charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ def setUp(self):
self.harness: Harness[TraefikIngressCharm] = Harness(TraefikIngressCharm)
self.harness.set_model_name("test-model")
self.addCleanup(self.harness.cleanup)
self.harness.handle_exec("traefik", ["update-ca-certificates", "--fresh"], result=0)

patcher = patch.object(TraefikIngressCharm, "version", property(lambda *_: "0.0.0"))
self.mock_version = patcher.start()
Expand Down Expand Up @@ -416,6 +417,7 @@ def setUp(self):
self.harness: Harness[TraefikIngressCharm] = Harness(TraefikIngressCharm)
self.harness.set_model_name("test-model")
self.addCleanup(self.harness.cleanup)
self.harness.handle_exec("traefik", ["update-ca-certificates", "--fresh"], result=0)

patcher = patch.object(TraefikIngressCharm, "version", property(lambda *_: "0.0.0"))
self.mock_version = patcher.start()
Expand Down
1 change: 1 addition & 0 deletions tests/unit/test_route.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
def harness() -> Harness[TraefikIngressCharm]:
harness = Harness(TraefikIngressCharm)
harness.set_model_name(MODEL_NAME)
harness.handle_exec("traefik", ["update-ca-certificates", "--fresh"], result=0)

patcher = patch.object(TraefikIngressCharm, "version", property(lambda *_: "0.0.0"))
patcher.start()
Expand Down
1 change: 1 addition & 0 deletions tests/unit/test_tls_certificates.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def setUp(self):
self.harness: Harness[TraefikIngressCharm] = Harness(TraefikIngressCharm)
self.harness.set_model_name("test-model")
self.addCleanup(self.harness.cleanup)
self.harness.handle_exec("traefik", ["update-ca-certificates", "--fresh"], result=0)

patcher = patch.object(TraefikIngressCharm, "version", property(lambda *_: "0.0.0"))
self.mock_version = patcher.start()
Expand Down
Loading