Merge lp:~erik-lonroth/charm-haproxy/certdirs into lp:charm-haproxy

Proposed by Tom Haddon
Status: Merged
Approved by: Laurent Sesquès
Approved revision: 168
Merged at revision: 164
Proposed branch: lp:~erik-lonroth/charm-haproxy/certdirs
Merge into: lp:charm-haproxy
Diff against target: 122 lines (+75/-17)
2 files modified
README.md (+49/-0)
hooks/hooks.py (+26/-17)
To merge this branch: bzr merge lp:~erik-lonroth/charm-haproxy/certdirs
Reviewer Review Type Date Requested Status
Tom Haddon Approve
Canonical IS Reviewers Pending
Review via email: mp+454355@code.launchpad.net

Commit message

Add support for directory with SSL pem certs

Description of the change

Add support for directory with SSL pem certs

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 :

LGTM, thx

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

Change cannot be self approved, setting status to needs review.

Revision history for this message
Tom Haddon (mthaddon) :
168. By Erik Lönroth

Indent bind_stanza

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

Change successfully merged at revision 164

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'README.md'
2--- README.md 2022-02-04 23:08:05 +0000
3+++ README.md 2023-10-24 08:46:12 +0000
4@@ -145,6 +145,55 @@
5 the `source` configuration option to `backports` or to whatever PPA/archive you
6 wish to use.
7
8+## SSL Termination with directory for certs
9+
10+You can configure services to use a filesystem path in the *crts*. This is a good fit when you might have certbot or acme.sh to maintain certificates outside of haproxy itself.
11+
12+This is an example of this:
13+
14+Create a services.yaml file for the microsample service:
15+
16+```
17+- service_name: microsample
18+ service_host: "0.0.0.0"
19+ service_port: 443
20+ service_options:
21+ - mode http
22+ - balance leastconn
23+ crts: [/var/lib/haproxy/certs]
24+```
25+
26+```
27+juju deploy microsample
28+juju deploy haproxy
29+juju relate haproxy microsample
30+```
31+
32+Create the directory and self signed certificate in haproxy/0
33+```
34+juju exec --unit haproxy/0 -- sudo mkdir -p /var/lib/haproxy/certs
35+openssl genpkey -algorithm RSA -out example.key
36+openssl req -x509 -new -key example.key -out example.crt
37+cat example.key example.crt > /var/lib/haproxy/certs/example.pem
38+```
39+
40+Configure haproxy with the services.yaml content.
41+
42+`juju config haproxy services="$(cat services.yaml)"`
43+
44+This produces a stanza in haproxy.cfg as:
45+
46+```
47+frontend haproxy-0-443
48+ bind 0.0.0.0:443 ssl crt /var/lib/haproxy/certs no-sslv3
49+ default_backend microsample
50+ mode http
51+```
52+
53+Once you have valid certs in the directory, they will be used.
54+
55+You can validate with `curl -k https://haproxy/` which should now reply with "Online".
56+
57 ## Monitoring
58
59 Telegraf is recommended for monitoring HAProxy. To do so, deploy the
60
61=== modified file 'hooks/hooks.py'
62--- hooks/hooks.py 2023-08-30 07:16:33 +0000
63+++ hooks/hooks.py 2023-10-24 08:46:12 +0000
64@@ -416,14 +416,19 @@
65 # Enable SSL termination for this frontend, using the given
66 # certificates.
67 bind_stanza += " ssl"
68- for i, crt in enumerate(service_crts):
69- if crt == "DEFAULT":
70- path = os.path.join(default_haproxy_lib_dir, "default.pem")
71- else:
72- path = os.path.join(default_haproxy_lib_dir,
73- "service_%s" % service_name, "%d.pem" % i)
74- # SSLv3 is always off, since it's vulnerable to POODLE attacks
75+ if len(service_crts) == 1 and os.path.isdir(service_crts[0]):
76+ log("Service configured to use '%s' for certificates in haproxy.cfg." % service_crts[0])
77+ path = service_crts[0]
78 bind_stanza += " crt %s no-sslv3" % path
79+ else:
80+ for i, crt in enumerate(service_crts):
81+ if crt == "DEFAULT":
82+ path = os.path.join(default_haproxy_lib_dir, "default.pem")
83+ else:
84+ path = os.path.join(default_haproxy_lib_dir,
85+ "service_%s" % service_name, "%d.pem" % i)
86+ # SSLv3 is always off, since it's vulnerable to POODLE attacks
87+ bind_stanza += " crt %s no-sslv3" % path
88 service_config.append(bind_stanza)
89 service_config.append(" default_backend %s" % (service_name,))
90 service_config.extend(" %s" % service_option.strip()
91@@ -836,17 +841,21 @@
92 f.write(base64.b64decode(errorfile["content"]))
93
94 # Write to disk the content of the given SSL certificates
95+ # or use a single path element to search for them.
96 crts = service_config.get('crts', [])
97- for i, crt in enumerate(crts):
98- if crt == "DEFAULT" or crt == "EXTERNAL":
99- continue
100- content = base64.b64decode(crt)
101- path = get_service_lib_path(service_name)
102- full_path = os.path.join(path, "%d.pem" % i)
103- write_ssl_pem(full_path, content)
104- with open(full_path, 'w') as f:
105- f.write(content.decode('utf-8'))
106-
107+ if len(crts) == 1 and os.path.isdir(crts[0]):
108+ log("Service configured to use path to look for certificates in haproxy.cfg.")
109+ else:
110+ for i, crt in enumerate(crts):
111+ if crt == "DEFAULT" or crt == "EXTERNAL":
112+ continue
113+ content = base64.b64decode(crt)
114+ path = get_service_lib_path(service_name)
115+ full_path = os.path.join(path, "%d.pem" % i)
116+ write_ssl_pem(full_path, content)
117+ with open(full_path, 'w') as f:
118+ f.write(content.decode('utf-8'))
119+
120 if not os.path.exists(default_haproxy_service_config_dir):
121 os.mkdir(default_haproxy_service_config_dir, 0o600)
122 with open(os.path.join(default_haproxy_service_config_dir,

Subscribers

People subscribed via source and target branches

to all changes: