Merge ~tshinn/snap-store-proxy-charm:fix_https_cert_config into snap-store-proxy-charm:main

Proposed by Tong Shinn
Status: Merged
Approved by: Tong Shinn
Approved revision: 5ee7c4a10ae3c00a82d98b6a5875d1cab6bb09e7
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~tshinn/snap-store-proxy-charm:fix_https_cert_config
Merge into: snap-store-proxy-charm:main
Diff against target: 96 lines (+29/-11)
4 files modified
src/charm.py (+2/-7)
src/helpers.py (+6/-0)
tests/test_charm.py (+2/-4)
tests/test_helpers.py (+19/-0)
Reviewer Review Type Date Requested Status
Wouter van Bommel (community) Approve
Odysseus Kaziolas Approve
Review via email: mp+436362@code.launchpad.net

Commit message

snap-store-proxy CLI has the import-certificate option. Update the charm to use it instead of setting proxy.tls.cert and proxy.tls.key directly, which doesn't fully work.

Description of the change

After deploying the charm, the following commands can be used to configure snap-store-proxy to support HTTPS:

juju config snap-store-proxy certificate="$(cat my.cert)"
juju config snap-store-proxy private_key="$(cat my.key)"

where my.cert and my.key are PEM formatted key/cert pair.

To post a comment you must log in.
Revision history for this message
Odysseus Kaziolas (odysseus-k) wrote :

Haven't used snap-store-proxy but changes LGTM plus tests pass. Maybe @suligap should take a look here as well as he's more experienced with snap store proxy.

Do we also need to have an entry in our docs with the commands above?

review: Approve
Revision history for this message
Przemysław Suliga (suligap) wrote :

This looks good to me.

But looking at snap-store-proxy code it also looks like a possible issue in snap-store-proxy itself that cert validation will fail during `snap-proxy config ...` when the passed in proxy.tls.cert is actually a chain eg. intermediate and leaf cert.

This validation is done properly in case of `import-certificate` command. But I don't see a reason why `snap-proxy config` wouldn't do the same.

But also:

1. From your description I'm not sure - is that the issue you're trying to solve?
2. It probably won't fully solve this issue because the charm only expects one cert to be present as `certificate` and does validation against that and that might fail first in those cases?

Revision history for this message
Tong Shinn (tshinn) wrote :

1. No, I treated snap-store-proxy itself as a black box while getting to this point. My focus was solely on fixing the charm. The charm didn't work as is because instead of using `import-certificate` it configured `proxy.tls.cert` and `proxy.tls.key` configurations directly, which didn't result in nginx getting configured properly (was unable to get to the bottom of exactly why). When I tried `import-certificate` it started working, hence the MP.

2. Yes I think you are right that the charm itself only supports a single certificate in the PEM file. When the snap-store-proxy supports it, the charm should be updated also! :)

Revision history for this message
Wouter van Bommel (woutervb) wrote :

Left one question, probably doesn't hurt that it's there, just not sure it's needed.

review: Approve
Revision history for this message
Przemysław Suliga (suligap) wrote :

> Yes I think you are right that the charm itself only
> supports a single certificate in the PEM file. When the
> snap-store-proxy supports it, the charm
> should be updated also! :)

`snap-store-proxy import-certificate` already supports that correctly. `snap-store-proxy config proxy.tls.cert=...` does not I think. So if this change is merged, then it's just the charm's cert validation logic that would need to be updated. Or perhaps it should be removed since the proxy does the validation anyway? But there's probably a reason why it's in the charm too.

Revision history for this message
Tong Shinn (tshinn) wrote :

Will merge this MP as is and work on supporting chained certs in a seperate MP.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/charm.py b/src/charm.py
2index ac50311..f71f20b 100755
3--- a/src/charm.py
4+++ b/src/charm.py
5@@ -22,6 +22,7 @@ from ops.model import ActiveStatus, BlockedStatus, MaintenanceStatus, ModelError
6 from exceptions import ConfigException
7 from helpers import (
8 all_config_options,
9+ configure_cert,
10 configure_proxy,
11 configure_self_signed_cert,
12 default_values,
13@@ -338,13 +339,7 @@ class SnapstoreProxyCharm(CharmBase):
14
15 # Okay, the certificate and private key do make sense and match this host,
16 # so let's install them
17- configure_proxy(
18- ["proxy.tls.cert", "proxy.tls.key"],
19- [
20- getattr(self._stored, "certificate"),
21- getattr(self._stored, "private_key"),
22- ],
23- )
24+ configure_cert(raw_key, raw_certificate)
25 logger.info("Successfully installed the tls certificate on the proxy")
26
27 def handle_pgsql_dsn_change(self):
28diff --git a/src/helpers.py b/src/helpers.py
29index 8b92f42..3088c2b 100644
30--- a/src/helpers.py
31+++ b/src/helpers.py
32@@ -35,6 +35,12 @@ def configure_proxy(option, value, force=False):
33 run(command)
34
35
36+def configure_cert(key, cert):
37+ logger.info("Importing and configuring certificate for nginx")
38+ run(f'{PROXY_CLI} config proxy.tls.cert="" proxy.tls.key=""'.split())
39+ run(f"{PROXY_CLI} import-certificate".split(), input=f"{key}\n{cert}", text=True)
40+
41+
42 def configure_self_signed_cert():
43 logger.info("Generating and configuring self signed certificate for nginx")
44 run(f'{PROXY_CLI} config proxy.tls.cert="" proxy.tls.key=""'.split())
45diff --git a/tests/test_charm.py b/tests/test_charm.py
46index e36b9a3..02f03c6 100644
47--- a/tests/test_charm.py
48+++ b/tests/test_charm.py
49@@ -631,15 +631,13 @@ class TestTLSCertificate(CharmTest):
50 cert, key = self.create_x509_certificate("test.domain.just.invented", san)
51
52 # Since valid values will be pushed on to the proxy, we need to stub that here
53- with patch("charm.configure_proxy") as m_configure, patch(
54+ with patch("charm.configure_cert") as m_configure, patch(
55 "charm.open_port"
56 ) as m_open_port:
57 # Ensure that the default values are set, as expected
58 harness.update_config({"certificate": cert, "private_key": key})
59
60- m_configure.assert_called_once_with(
61- ["proxy.tls.cert", "proxy.tls.key"], [cert, key]
62- )
63+ m_configure.assert_called_once_with(key, cert)
64 m_open_port.assert_called_once_with(ssl=True)
65
66 assert len(harness.charm.errors) == 0
67diff --git a/tests/test_helpers.py b/tests/test_helpers.py
68index 15fac97..2747726 100644
69--- a/tests/test_helpers.py
70+++ b/tests/test_helpers.py
71@@ -47,6 +47,25 @@ def test_configure_proxy_list_forced():
72 )
73
74
75+def test_configure_cert():
76+ key = "private_key_in_pem_format"
77+ cert = "certificate_in_pem_format"
78+ with mock.patch("helpers.run") as mocked_run:
79+ helpers.configure_cert(key, cert)
80+
81+ mocked_run.assert_has_calls(
82+ [
83+ mock.call(f'{PROXY_CLI} config proxy.tls.cert="" proxy.tls.key=""'.split()),
84+ mock.call(
85+ f"{PROXY_CLI} import-certificate".split(),
86+ input=f"{key}\n{cert}",
87+ text=True,
88+ ),
89+ ],
90+ any_order=False,
91+ )
92+
93+
94 def test_configure_self_signed_cert():
95 with mock.patch("helpers.run") as mocked_run:
96 helpers.configure_self_signed_cert()

Subscribers

People subscribed via source and target branches

to all changes: