Commit 27bd4b8f authored by Jérome Perrin's avatar Jérome Perrin

software/dufs: handle certificate renewal

Because dufs only reads certificate on startup it does not detect when
the certificate was renewed.

The only thing supported by dufs is to restart, but we can not restart
the service, because the partition can not communicate with the
supervisor.
To solve this, use another level of process supervisor: using zdaemon
we can run a process in the foreground, so that it stays under
slapos' supervisor control, but still expose a way to restart the
process using zdaemon API.
parent 27502625
...@@ -15,4 +15,4 @@ ...@@ -15,4 +15,4 @@
[instance.cfg.in] [instance.cfg.in]
filename = instance.cfg.in filename = instance.cfg.in
md5sum = 0cb3cbac5479581985e5446078217686 md5sum = 75f81b613078004304c6a0d39037caf6
...@@ -74,6 +74,7 @@ caucase.updater( ...@@ -74,6 +74,7 @@ caucase.updater(
crl_path='${dufs-certificate:crl-file}', crl_path='${dufs-certificate:crl-file}',
key_path='${dufs-certificate:key-file}', key_path='${dufs-certificate:key-file}',
template_csr='${dufs-certificate-prepare-csr:csr}', template_csr='${dufs-certificate-prepare-csr:csr}',
on_renew='${dufs-server-restart:wrapper-path}',
openssl=openssl_bin, openssl=openssl_bin,
)}} )}}
...@@ -134,25 +135,43 @@ caucase.caucased( ...@@ -134,25 +135,43 @@ caucase.caucased(
recipe = slapos.cookbook:generate.password recipe = slapos.cookbook:generate.password
user = admin user = admin
[dufs-zdaemon-cfg]
recipe = slapos.recipe.template
inline =
<runner>
program ${dufs-server-bin:wrapper-path}
socket-name ${directory:var}/zds
daemon off
</runner>
output = ${directory:etc}/${:_buildout_section_name_}
[dufs-server] [dufs-server]
recipe = slapos.cookbook:wrapper recipe = slapos.cookbook:wrapper
command-line = {{ zdaemon_bin }} -C ${dufs-zdaemon-cfg:output} start
wrapper-path = ${directory:service}/${:_buildout_section_name_}
port = 19080
ip = ${instance-parameter:ipv6-random}
url = https://[${:ip}]:${:port}
[dufs-server-restart]
recipe = slapos.cookbook:wrapper
command-line = {{ zdaemon_bin }} -C ${dufs-zdaemon-cfg:output} restart
wrapper-path = ${directory:bin}/${:_buildout_section_name_}
[dufs-server-bin]
recipe = slapos.cookbook:wrapper
command-line = command-line =
{{ dufs_bin }} {{ dufs_bin }}
--enable-cors --enable-cors
--bind ${:ip} --bind ${dufs-server:ip}
--port ${:port} --port ${dufs-server:port}
--allow-all --allow-all
--auth /@${admin-password:user}:${admin-password:passwd} --auth /@${admin-password:user}:${admin-password:passwd}
--auth /pub@${admin-password:user}:${admin-password:passwd}@* --auth /pub@${admin-password:user}:${admin-password:passwd}@*
--tls-cert ${dufs-certificate:cert-file} --tls-cert ${dufs-certificate:cert-file}
--tls-key ${dufs-certificate:key-file} --tls-key ${dufs-certificate:key-file}
${directory:dufs-data-dir} ${directory:dufs-data-dir}
wrapper-path = ${directory:service}/${:_buildout_section_name_} wrapper-path = ${directory:bin}/${:_buildout_section_name_}
port = 19080
ip = ${instance-parameter:ipv6-random}
url = https://[${:ip}]:${:port}
[dufs-listen-promise] [dufs-listen-promise]
<= check-port-listening-promise <= check-port-listening-promise
......
...@@ -10,6 +10,9 @@ parts = ...@@ -10,6 +10,9 @@ parts =
caucase-eggs caucase-eggs
instance.cfg.in instance.cfg.in
[zdaemon]
recipe = zc.recipe.egg
egg = ${:_buildout_section_name_}
[dufs] [dufs]
recipe = slapos.recipe.cmmi recipe = slapos.recipe.cmmi
...@@ -33,6 +36,7 @@ context = ...@@ -33,6 +36,7 @@ context =
raw dash_bin ${dash:location}/bin/dash raw dash_bin ${dash:location}/bin/dash
raw curl_bin ${curl:location}/bin/curl raw curl_bin ${curl:location}/bin/curl
raw dufs_bin ${dufs:location}/bin/dufs raw dufs_bin ${dufs:location}/bin/dufs
raw zdaemon_bin ${buildout:bin-directory}/${zdaemon:egg}
key template_monitor monitor2-template:output key template_monitor monitor2-template:output
import-list = import-list =
file caucase caucase-jinja2-library:target file caucase caucase-jinja2-library:target
...@@ -25,8 +25,10 @@ ...@@ -25,8 +25,10 @@
# #
############################################################################## ##############################################################################
import contextlib
import io import io
import os import os
import subprocess
import tempfile import tempfile
import urllib.parse import urllib.parse
...@@ -103,3 +105,32 @@ class TestFileServer(SlapOSInstanceTestCase): ...@@ -103,3 +105,32 @@ class TestFileServer(SlapOSInstanceTestCase):
) )
self.assertEqual(resp.text, 'hello') self.assertEqual(resp.text, 'hello')
self.assertEqual(resp.status_code, requests.codes.ok) self.assertEqual(resp.status_code, requests.codes.ok)
def test_renew_cert(self):
def _getpeercert():
# XXX low level way to get get the server certificate
with requests.Session() as session:
pool = session.get(
self.connection_parameters['public-url'],
verify=self.ca_cert,
).raw._pool.pool
with contextlib.closing(pool.get()) as cnx:
return cnx.sock._sslobj.getpeercert()
cert_before = _getpeercert()
# execute certificate updater two month later, when it's time to renew certificate.
# use a timeout, because this service runs forever
subprocess.run(
(
'timeout',
'5',
'faketime',
'+2 months',
os.path.join(
self.computer_partition_root_path,
'etc/service/dufs-certificate-updater'),
),
capture_output=not self._debug,
)
cert_after = _getpeercert()
self.assertNotEqual(cert_before['notAfter'], cert_after['notAfter'])
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment