Merge ~pjdc/charm-k8s-mattermost/+git/charm-k8s-mattermost:control-source into charm-k8s-mattermost:master

Proposed by Paul Collins
Status: Merged
Approved by: Tom Haddon
Approved revision: 8a738d3c395373c83e9fc47b8d65628e7c54210e
Merged at revision: 627cbc0db4e21161ca05bea83bb5b5373035d9cf
Proposed branch: ~pjdc/charm-k8s-mattermost/+git/charm-k8s-mattermost:control-source
Merge into: charm-k8s-mattermost:master
Diff against target: 164 lines (+70/-5)
3 files modified
config.yaml (+7/-0)
src/charm.py (+42/-4)
tests/unit/test_charm.py (+21/-1)
Reviewer Review Type Date Requested Status
Tom Haddon Approve
Paul Collins Needs Resubmitting
Canonical IS Reviewers Pending
Review via email: mp+384601@code.launchpad.net

Commit message

add ingress_whitelist_source_range setting, set nginx.ingress.kubernetes.io/ssl-redirect to false when site_url scheme is "http"

To post a comment you must log in.
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

This merge proposal is being monitored by mergebot. Change the status to Approved to merge.

Revision history for this message
Tom Haddon (mthaddon) wrote :

Do we think we're going to need this option? Happy to approve if so (although some config validation would be good), but I'm not sure when we'd be using this, and any code/config we can avoid is a good thing.

review: Needs Information
Revision history for this message
Paul Collins (pjdc) wrote :

I'm certainly planning to use it while our deployment is being validated -- due to the shared ingress there's no other practical way (unless Mattermost has an internal setting) to restrict access.

As for validation, I suppose we could do something like split the value on commas and confirm that each element is a valid CIDR before updating the pod spec.

Revision history for this message
Tom Haddon (mthaddon) wrote :

> I'm certainly planning to use it while our deployment is being validated --
> due to the shared ingress there's no other practical way (unless Mattermost
> has an internal setting) to restrict access.

Ack.

> As for validation, I suppose we could do something like split the value on
> commas and confirm that each element is a valid CIDR before updating the pod
> spec.

Yeah, let's add that in please.

Revision history for this message
Paul Collins (pjdc) wrote :

> > As for validation, I suppose we could do something like split the value on
> > commas and confirm that each element is a valid CIDR before updating the pod
> > spec.
>
> Yeah, let's add that in please.

Pushed. Results in the following:

Unit Workload Agent Address Ports Message
mattermost/2* blocked idle 10.1.1.142 8065/TCP ingress_whitelist_source_range: invalid network(s): 10.2.4.9/24

review: Needs Resubmitting
Revision history for this message
Tom Haddon (mthaddon) wrote :

Two comments inline, neither of them are blocking.

Revision history for this message
Paul Collins (pjdc) wrote :

> Two comments inline, neither of them are blocking.

I've rebased on origin with S3 landed, reworked the check based on your suggestions, integrated the S3 required-settings check into the check, and added some tests for check_ranges.

review: Needs Resubmitting
Revision history for this message
Paul Collins (pjdc) wrote :

Yielding the following:

Unit Workload Agent Address Ports Message
mattermost/1* blocked idle 10.1.1.23 8065/TCP required setting(s) empty: s3_bucket; ingress_whitelist_source_range: invalid network(s): 91.189.92.242/25

Revision history for this message
Tom Haddon (mthaddon) wrote :

LGTM, thx

review: Approve
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

Change successfully merged at revision 627cbc0db4e21161ca05bea83bb5b5373035d9cf

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/config.yaml b/config.yaml
2index 4799770..2d6bf21 100644
3--- a/config.yaml
4+++ b/config.yaml
5@@ -3,6 +3,13 @@ options:
6 type: boolean
7 description: Set the Mattermost log level to DEBUG, otherwise INFO.
8 default: false
9+ ingress_whitelist_source_range:
10+ type: string
11+ description: |
12+ A comma-separated list of CIDRs to store in the ingress.kubernetes.io/whitelist-source-range annotation.
13+
14+ This can be used to lock down access to Mattermost based on source IP address.
15+ default: ''
16 open_server:
17 type: boolean
18 description: Allows users to sign up from the root page without an invite
19diff --git a/src/charm.py b/src/charm.py
20index 888969f..52e7034 100755
21--- a/src/charm.py
22+++ b/src/charm.py
23@@ -3,6 +3,8 @@
24 import sys
25 sys.path.append('lib') # noqa: E402
26
27+from ipaddress import ip_network
28+
29 from urllib.parse import urlparse
30
31 from ops.charm import (
32@@ -17,6 +19,7 @@ from ops.framework import (
33 from ops.main import main
34 from ops.model import (
35 ActiveStatus,
36+ BlockedStatus,
37 MaintenanceStatus,
38 WaitingStatus,
39 )
40@@ -42,6 +45,19 @@ class MattermostCharmEvents(CharmEvents):
41 db_master_available = EventSource(MattermostDBMasterAvailableEvent)
42
43
44+def check_ranges(ranges, name):
45+ if ranges:
46+ networks = ranges.split(',')
47+ invalid_networks = []
48+ for network in networks:
49+ try:
50+ ip_network(network)
51+ except ValueError:
52+ invalid_networks.append(network)
53+ if invalid_networks:
54+ return '{}: invalid network(s): {}'.format(name, ', '.join(invalid_networks))
55+
56+
57 class MattermostK8sCharm(CharmBase):
58
59 state = StoredState()
60@@ -101,6 +117,19 @@ class MattermostK8sCharm(CharmBase):
61
62 # TODO(pjdc): Emit event when we add support for read replicas.
63
64+ def _check_for_config_problems(self):
65+ problems = []
66+
67+ missing = self._missing_charm_settings()
68+ if missing:
69+ problems.append('required setting(s) empty: {}'.format(', '.join(sorted(missing))))
70+
71+ ranges = self.model.config['ingress_whitelist_source_range']
72+ if ranges:
73+ problems.append(check_ranges(ranges, 'ingress_whitelist_source_range'))
74+
75+ return '; '.join(filter(None, problems))
76+
77 def _make_pod_spec(self):
78 mattermost_image_details = self.mattermost_image.fetch()
79 pod_config = self._make_pod_config()
80@@ -177,6 +206,7 @@ class MattermostK8sCharm(CharmBase):
81 if not site_url:
82 return None
83 parsed = urlparse(site_url)
84+ annotations = {}
85
86 if parsed.scheme.startswith('http'):
87 ingress = {
88@@ -205,6 +235,15 @@ class MattermostK8sCharm(CharmBase):
89 tls_secret_name = self.model.config['tls_secret_name']
90 if tls_secret_name:
91 ingress['spec']['tls'][0]['secretName'] = tls_secret_name
92+ else:
93+ annotations['nginx.ingress.kubernetes.io/ssl-redirect'] = 'false'
94+
95+ ingress_whitelist_source_range = self.model.config['ingress_whitelist_source_range']
96+ if ingress_whitelist_source_range:
97+ annotations['nginx.ingress.kubernetes.io/whitelist-source-range'] = ingress_whitelist_source_range
98+
99+ if annotations:
100+ ingress['annotations'] = annotations
101
102 return {
103 "kubernetesResources": {
104@@ -222,13 +261,12 @@ class MattermostK8sCharm(CharmBase):
105 self.unit.status = ActiveStatus()
106 return
107
108- missing = self._missing_charm_settings()
109- if missing:
110- self.unit.status = WaitingStatus('Some required settings are empty: {}'.format(', '.join(sorted(missing))))
111+ problems = self._check_for_config_problems()
112+ if problems:
113+ self.unit.status = BlockedStatus(problems)
114 return
115
116 self.unit.status = MaintenanceStatus('Configuring pod')
117-
118 pod_spec = self._make_pod_spec()
119
120 # Due to https://github.com/canonical/operator/issues/293 we
121diff --git a/tests/unit/test_charm.py b/tests/unit/test_charm.py
122index 97fbede..17e7464 100644
123--- a/tests/unit/test_charm.py
124+++ b/tests/unit/test_charm.py
125@@ -7,7 +7,11 @@ import sys
126 sys.path.append('lib') # noqa: E402
127 sys.path.append('src') # noqa: E402
128
129-from charm import MattermostK8sCharm
130+from charm import (
131+ MattermostK8sCharm,
132+ check_ranges,
133+)
134+
135 from ops import testing
136
137 CONFIG_NO_S3_SETTINGS_S3_ENABLED = {
138@@ -23,6 +27,10 @@ CONFIG_NO_S3_SETTINGS_S3_DISABLED_NO_DEFAULTS = {
139 's3_enabled': False,
140 }
141
142+RANGE_BAD = '10.242.0.0/8,91.189.92.242/25'
143+RANGE_GOOD = '10.0.0.0/8,91.189.92.128/25'
144+RANGE_MIXED = '10.242.0.0/8,91.189.92.128/25'
145+
146
147 class TestMattermostK8sCharm(unittest.TestCase):
148
149@@ -39,3 +47,15 @@ class TestMattermostK8sCharm(unittest.TestCase):
150 self.harness.charm.model.config = copy.deepcopy(CONFIG_NO_S3_SETTINGS_S3_DISABLED_NO_DEFAULTS)
151 expected = []
152 self.assertEqual(sorted(self.harness.charm._missing_charm_settings()), expected)
153+
154+ def test_check_ranges_bad(self):
155+ expected = 'range_bad: invalid network(s): 10.242.0.0/8, 91.189.92.242/25'
156+ self.assertEqual(check_ranges(RANGE_BAD, 'range_bad'), expected)
157+
158+ def test_check_ranges_good(self):
159+ expected = None
160+ self.assertEqual(check_ranges(RANGE_GOOD, 'range_good'), expected)
161+
162+ def test_check_ranges_mixed(self):
163+ expected = 'range_mixed: invalid network(s): 10.242.0.0/8'
164+ self.assertEqual(check_ranges(RANGE_MIXED, 'range_mixed'), expected)

Subscribers

People subscribed via source and target branches