Commit 1d271f4d by Łukasz Nowak Committed by Łukasz Nowak

caddy-frontend: Re-do zero-SSL BBB

Instead of complex architecture in the profiles, reuse kedifa-updater
capability to do backward compatibility certificate management thanks to its
fall-back mechanism.

kedifa-updater uses state file to know, if it ever succeed to download
certificate from KeDiFa, and so it really makes it that pushing at least once
certificate to KeDiFa, even if it is sometimes unresponsive, will switch to
it.

Fallback certificate is used, thus each slave listens immediately on HTTP and
HTTPS. Thanks to this, asynchronous updates do not need to communicate with
slapos node instance, and slapos node instance does not care about the
certificates anymore.
1 parent 25902c06
......@@ -22,7 +22,7 @@ md5sum = c801b7f9f11f0965677c22e6bbe9281b
[template-apache-frontend]
filename = instance-apache-frontend.cfg.in
md5sum = 0e96242cac04534435b07e27bd9d5096
md5sum = 1d39842e07e6a8674f3157ffc3f7a042
[template-apache-replicate]
filename = instance-apache-replicate.cfg.in
......@@ -30,7 +30,7 @@ md5sum = 0f5af15a0cc024ff181c15e946d92808
[template-slave-list]
filename = templates/apache-custom-slave-list.cfg.in
md5sum = 121e46e105f350c2995d5188cf340ad1
md5sum = e681b41535dcd676bb971743ad0bb27f
[template-slave-configuration]
filename = templates/custom-virtualhost.conf.in
......@@ -42,7 +42,7 @@ md5sum = 38e9994be01ea1b8a379f8ff7aa05438
[template-caddy-frontend-configuration]
filename = templates/Caddyfile.in
md5sum = 9bd48b38abc48825388dd6ec6ab19b9a
md5sum = 7ba0f98ce1692cbd34d98c79488bf240
[caddy-backend-url-validator]
filename = templates/caddy-backend-url-validator.in
......@@ -58,7 +58,7 @@ md5sum = f20d6c3d2d94fb685f8d26dfca1e822b
[template-default-slave-virtualhost]
filename = templates/default-virtualhost.conf.in
md5sum = 1d3c7dc1c0d6261c6fd4cc9d756eb19a
md5sum = ab4a32b9cc463628403fa67fa6b38507
[template-cached-slave-virtualhost]
filename = templates/cached-virtualhost.conf.in
......@@ -90,7 +90,7 @@ md5sum = cd6bb9bd0734f17469b0ca88f8b1a531
[template-nginx-configuration]
filename = templates/nginx.cfg.in
md5sum = b3a971c3700cb5175f6c1e388cb88775
md5sum = 1f185e17d91bfba5a80b8359821ebce9
[template-nginx-eventsource-slave-virtualhost]
filename = templates/nginx-eventsource-slave.conf.in
......@@ -98,7 +98,7 @@ md5sum = 217a6c801b8330b0b825f7b8b4c77184
[template-nginx-notebook-slave-virtualhost]
filename = templates/nginx-notebook-slave.conf.in
md5sum = b864b17a304f0fad6d8dcb0f1893145b
md5sum = 67604880521600b0913cde6948f184de
[template-caddy-lazy-script-call]
filename = templates/apache-lazy-script-call.sh.in
......
......@@ -127,6 +127,25 @@ command =
<(printf "\n[SAN]\nsubjectAltName=IP:${:ipv6},IP:${:ipv4}")) \
-out ${:certificate}'
[self-signed-fallback-access]
# Self Signed certificate for HTTPS access to the frontend with fallback certificate
recipe = plone.recipe.command
update-command = ${:command}
ipv6 = ${slap-network-information:global-ipv6}
ipv4 = {{instance_parameter['ipv4-random']}}
certificate = ${caddy-directory:master-autocert-dir}/fallback-access.crt
stop-on-error = True
command =
[ -f ${:certificate} ] && exit 0
rm -f ${:certificate}
/bin/bash -c ' \
{{ parameter_dict['openssl'] }} req \
-new -newkey rsa:2048 -sha256 \
-nodes -x509 -days 36500 \
-keyout ${:certificate} \
-subj "/CN=Fallback certificate/OU={{ instance_parameter['configuration.frontend-name'] }}" \
-out ${:certificate}'
[jinja2-template-base]
recipe = slapos.recipe.template:jinja2
rendered = ${buildout:directory}/${:filename}
......@@ -136,7 +155,6 @@ slapparameter_dict = {{ dumps(instance_parameter['configuration']) }}
slap_software_type = {{ dumps(instance_parameter['slap-software-type']) }}
context =
import json_module json
import os_module os
raw common_profile {{ parameter_dict['common_profile'] }}
raw logrotate_base_instance {{ parameter_dict['logrotate_base_instance'] }}
key slap_software_type :slap_software_type
......@@ -394,16 +412,22 @@ command-line = ${frontend-caddy-validate:rendered}
wrapper-path = ${directory:bin}/caddy-configtest
# BBB: SlapOS Master non-zero knowledge BEGIN
[get-self-signed-fallback-access]
recipe = collective.recipe.shelloutput
commands =
certificate = cat ${self-signed-fallback-access:certificate}
[apache-certificate]
recipe = slapos.recipe.template:jinja2
template = inline:
{% raw %}
{{ certificate }}
{{ key }}
{{ certificate or fallback_certificate }}
{{ key or '' }}
{% endraw %}
context =
key certificate configuration:apache-certificate
key key configuration:apache-key
key fallback_certificate get-self-signed-fallback-access:certificate
rendered = ${directory:bbb-ssl-dir}/frontend.crt
# BBB: SlapOS Master non-zero knowledge END
......
......@@ -4,20 +4,8 @@ import {{frontend_configuration.get('log-access-configuration')}}
import {{ slave_configuration_directory }}/*.conf
import {{ slave_with_cache_configuration_directory }}/*.conf
{%- set ssl = {} -%}
{%- if os_module.path.exists(master_certificate) -%}
{%- do ssl.__setitem__('certificate', master_certificate) -%}
{%- do ssl.__setitem__('key', master_certificate) -%}
{#- BBB: SlapOS Master non-zero knowledge BEGIN -#}
{%- elif os_module.path.getsize(apache_certificate) > 0 -%}
{%- do ssl.__setitem__('certificate', apache_certificate) -%}
{%- do ssl.__setitem__('key', apache_certificate) -%}
{%- endif -%}
{#- BBB: SlapOS Master non-zero knowledge END #}
# Catch-all and 404 for not configured instances
{% if 'key' in ssl %}
:{{ https_port }} {
tls {{ ssl['certificate'] }} {{ ssl['key'] }}
tls {{ master_certificate }} {{ master_certificate }}
bind {{ local_ipv4 }}
# Compress the output
gzip
......@@ -27,7 +15,6 @@ import {{ slave_with_cache_configuration_directory }}/*.conf
* {{ not_found_file }}
}
}
{% endif %}
:{{ http_port }} {
bind {{ local_ipv4 }}
......
......@@ -21,7 +21,6 @@ recipe = slapos.recipe.template:jinja2
extensions = jinja2.ext.do
extra-context =
context =
import os_module os
raw common_profile {{ common_profile }}
${:extra-context}
......@@ -33,7 +32,7 @@ notifempty = true
create = true
{% if master_key_download_url %}
{% do kedifa_updater_mapping.append((master_key_download_url, master_certificate)) %}
{% do kedifa_updater_mapping.append((master_key_download_url, master_certificate, apache_certificate)) %}
{% endif %}
{% if slave_kedifa_information %}
......@@ -174,7 +173,6 @@ bytes = 8
{% set cert_name = slave_reference.replace('-','.') + '.pem' %}
{% set certificate = '%s/%s' % (autocert, cert_name) %}
{% do slave_parameter_dict.__setitem__('certificate', certificate )%}
{% do kedifa_updater_mapping.append((key_download_url, certificate)) %}
{# Set ssl certificates for each slave #}
{% for cert_name in ('ssl_csr', 'ssl_proxy_ca_crt')%}
......@@ -199,13 +197,12 @@ value = {{ dumps(slave_instance.get(cert_name)) }}
{% endfor %}
{#- Set Up Certs #}
{% do slave_instance.__setitem__('apache_certificate', apache_certificate) %}
{% if 'ssl_key' in slave_instance and 'ssl_crt' in slave_instance %}
{% set cert_title = '%s-crt' % (slave_reference) %}
{% set cert_file = '/'.join([bbb_ssl_directory, cert_title.replace('-','.')]) %}
{% do kedifa_updater_mapping.append((key_download_url, certificate, cert_file)) %}
{% do part_list.append(cert_title) %}
{% do slave_parameter_dict.__setitem__("ssl_crt", cert_file) %}
{% do slave_instance.__setitem__('path_to_ssl_crt', cert_file) %}
[{{cert_title}}]
< = jinja2-template-base
......@@ -214,7 +211,9 @@ rendered = {{ cert_file }}
cert-content = {{ dumps(slave_instance.get('ssl_crt') + '\n' + slave_instance.get('ssl_ca_crt', '') + '\n' + slave_instance.get('ssl_key')) }}
extra-context =
key content :cert-content
{% endif %}
{% else %}
{% do kedifa_updater_mapping.append((key_download_url, certificate, master_certificate)) %}
{% endif %}
# BBB: SlapOS Master non-zero knowledge END
{# ########################################## #}
......@@ -471,7 +470,7 @@ recipe = slapos.recipe.template:jinja2
file = {{ kedifa_updater_mapping_file }}
template = inline:
{% for mapping in kedifa_updater_mapping %}
{{ mapping[0] }} {{ mapping[1] }}
{{ mapping[0] }} {{ mapping[1] }} {{ mapping[2] }}
{% endfor %}
rendered = ${:file}
......
......@@ -26,25 +26,11 @@
{%- set default_path = slave_parameter.get('default-path', '').strip('/') | urlencode %}
# SSL enabled hosts
{% set ssl = {} %}
{% if os_module.path.exists(slave_parameter['certificate']) %}
{% do ssl.__setitem__('certificate', slave_parameter['certificate']) %}
{% do ssl.__setitem__('key', slave_parameter['certificate']) %}
{#- BBB: SlapOS Master non-zero knowledge BEGIN -#}
{% elif 'path_to_ssl_crt' in slave_parameter %}
{% do ssl.__setitem__('certificate', slave_parameter['path_to_ssl_crt']) %}
{% do ssl.__setitem__('key', slave_parameter['path_to_ssl_crt']) %}
{% elif os_module.path.getsize(slave_parameter['apache_certificate']) > 0 %}
{% do ssl.__setitem__('certificate', slave_parameter['apache_certificate']) %}
{% do ssl.__setitem__('key', slave_parameter['apache_certificate']) %}
{% endif %}
{#- BBB: SlapOS Master non-zero knowledge END -#}
{% if 'key' in ssl %}
{{ https_host_list|join(', ') }} {
bind {{ slave_parameter['local_ipv4'] }}
# Compress the output
gzip
tls {{ ssl['certificate'] }} {{ ssl['key'] }} {
tls {{ slave_parameter['certificate'] }} {{ slave_parameter['certificate'] }} {
{%- if enable_h2 %}
# Allow HTTP2
alpn h2 http/1.1
......@@ -189,7 +175,6 @@
{%- endif %} {#- if backend_url #}
{%- endif %} {#- if slave_type == 'zope' and backend_url #}
} {# https_host_list|join(', ') #}
{% endif %}
# SSL-disabled hosts
{{ http_host_list|join(', ') }} {
......
......@@ -5,20 +5,6 @@
{%- set https_upstream = https_url.split("/")[2] %}
# SSL-enabled
{% set ssl = {} %}
{% if os_module.path.exists(slave_parameter['certificate']) %}
{% do ssl.__setitem__('certificate', slave_parameter['certificate']) %}
{% do ssl.__setitem__('key', slave_parameter['certificate']) %}
{#- BBB: SlapOS Master non-zero knowledge BEGIN -#}
{% elif 'path_to_ssl_crt' in slave_parameter %}
{% do ssl.__setitem__('certificate', slave_parameter['path_to_ssl_crt']) %}
{% do ssl.__setitem__('key', slave_parameter['path_to_ssl_crt']) %}
{% elif os_module.path.getsize(slave_parameter['apache_certificate']) > 0 %}
{% do ssl.__setitem__('certificate', slave_parameter['apache_certificate']) %}
{% do ssl.__setitem__('key', slave_parameter['apache_certificate']) %}
{% endif %}
{#- BBB: SlapOS Master non-zero knowledge END -#}
{% if 'key' in ssl %}
https://{{ slave_parameter.get('custom_domain') }}:{{ slave_parameter['nginx_https_port'] }} {
bind {{ slave_parameter['local_ipv4'] }}
# Compress the output
......@@ -26,7 +12,7 @@ https://{{ slave_parameter.get('custom_domain') }}:{{ slave_parameter['nginx_htt
log / {{ slave_parameter.get('access_log') }} "{remote} - {>REMOTE_USER} [{when}] \"{method} {uri} {proto}\" {status} {size} \"{>Referer}\" \"{>User-Agent}\" {latency_ms}"
errors {{ slave_parameter.get('error_log') }}
tls {{ ssl['certificate'] }} {{ ssl['key'] }} {
tls {{ slave_parameter['certificate'] }} {{ slave_parameter['certificate'] }} {
alpn http/1.1
}
......@@ -50,7 +36,6 @@ https://{{ slave_parameter.get('custom_domain') }}:{{ slave_parameter['nginx_htt
insecure_skip_verify
}
}
{% endif %}
# SSL-disabled
http://{{ slave_parameter.get('custom_domain') }}:{{ slave_parameter['nginx_http_port'] }} {
......
......@@ -58,20 +58,8 @@
import {{ slave_configuration_directory }}/*.conf
# Catch-all and 404 for not configured instances
{%- set ssl = {} -%}
{%- if os_module.path.exists(master_certificate) -%}
{%- do ssl.__setitem__('certificate', master_certificate) -%}
{%- do ssl.__setitem__('key', master_certificate) -%}
{#- BBB: SlapOS Master non-zero knowledge BEGIN -#}
{%- elif os_module.path.getsize(apache_certificate) > 0 -%}
{%- do ssl.__setitem__('certificate', apache_certificate) -%}
{%- do ssl.__setitem__('key', apache_certificate) -%}
{%- endif -%}
{#- BBB: SlapOS Master non-zero knowledge END -#}
# Catch-all and 404 for not configured instances
{% if 'key' in ssl %}
:{{ port }} {
tls {{ ssl['certificate'] }} {{ ssl['key'] }}
tls {{ master_certificate }} {{ master_certificate }}
bind {{ local_ip }}
# Serve an error 204 (No Content) for favicon.ico
status 204 /favicon.ico
......@@ -81,7 +69,6 @@ import {{ slave_configuration_directory }}/*.conf
* {{ not_found_file }}
}
}
{% endif %}
:{{ plain_port }} {
bind {{ local_ip }}
......
......@@ -750,6 +750,10 @@ class SlaveHttpFrontendTestCase(HttpFrontendTestCase):
cls.runComputerPartitionUntil(
cls.untilSlavePartitionReady)
cls.runKedifaUpdater()
# run once more slapos node instance, as kedifa-updater sets up
# certificates needed for caddy-frontend, and on this moment it can be
# not started yet
cls.runComputerPartition(max_quantity=1)
for slave_reference, partition_parameter_kw in cls\
.getSlaveParameterDictDict().items():
slave_instance = request(
......@@ -5139,23 +5143,15 @@ class TestSlaveSlapOSMasterCertificateCompatibility(
self.assertEqualResultJson(result, 'Path', '/test-path')
certificate_file_list = glob.glob(os.path.join(
self.instance_path, '*', 'etc', 'caddy-slave-conf.d', 'ssl',
self.instance_path, '*', 'srv', 'bbb-ssl',
'_custom_domain_ssl_crt_ssl_key_ssl_ca_crt.crt'))
self.assertEqual(1, len(certificate_file_list))
certificate_file = certificate_file_list[0]
with open(certificate_file) as out:
expected = self.customdomain_ca_certificate_pem + '\n' + \
self.ca.certificate_pem + '\n' + self.customdomain_ca_key_pem
self.assertEqual(
self.customdomain_ca_certificate_pem + '\n' + self.ca.certificate_pem,
out.read()
)
key_file_list = glob.glob(os.path.join(
self.instance_path, '*', 'etc', 'caddy-slave-conf.d', 'ssl',
'_custom_domain_ssl_crt_ssl_key_ssl_ca_crt.key'))
self.assertEqual(1, len(key_file_list))
key_file = key_file_list[0]
with open(key_file) as out:
self.assertEqual(
self.customdomain_ca_key_pem,
expected,
out.read()
)
......@@ -5182,6 +5178,7 @@ class TestSlaveSlapOSMasterCertificateCompatibility(
)
self.runComputerPartition(max_quantity=1)
self.runKedifaUpdater()
result = self.fakeHTTPSResult(
parameter_dict['domain'], parameter_dict['public-ipv4'], 'test-path')
......@@ -5192,23 +5189,15 @@ class TestSlaveSlapOSMasterCertificateCompatibility(
self.assertEqualResultJson(result, 'Path', '/test-path')
certificate_file_list = glob.glob(os.path.join(
self.instance_path, '*', 'etc', 'caddy-slave-conf.d', 'ssl',
self.instance_path, '*', 'srv', 'bbb-ssl',
'_custom_domain_ssl_crt_ssl_key_ssl_ca_crt.crt'))
self.assertEqual(1, len(certificate_file_list))
certificate_file = certificate_file_list[0]
with open(certificate_file) as out:
expected = customdomain_ca_certificate_pem + '\n' + ca.certificate_pem \
+ '\n' + customdomain_ca_key_pem
self.assertEqual(
customdomain_ca_certificate_pem + '\n' + ca.certificate_pem,
out.read()
)
key_file_list = glob.glob(os.path.join(
self.instance_path, '*', 'etc', 'caddy-slave-conf.d', 'ssl',
'_custom_domain_ssl_crt_ssl_key_ssl_ca_crt.key'))
self.assertEqual(1, len(key_file_list))
key_file = key_file_list[0]
with open(key_file) as out:
self.assertEqual(
customdomain_ca_key_pem,
expected,
out.read()
)
......@@ -5267,23 +5256,15 @@ class TestSlaveSlapOSMasterCertificateCompatibility(
der2pem(result.peercert))
certificate_file_list = glob.glob(os.path.join(
self.instance_path, '*', 'etc', 'caddy-slave-conf.d', 'ssl',
self.instance_path, '*', 'srv', 'bbb-ssl',
'_ssl_ca_crt_does_not_match.crt'))
self.assertEqual(1, len(certificate_file_list))
certificate_file = certificate_file_list[0]
with open(certificate_file) as out:
expected = self.certificate_pem + '\n' + self.ca.certificate_pem + \
'\n' + self.key_pem
self.assertEqual(
self.certificate_pem + '\n' + self.ca.certificate_pem,
out.read()
)
key_file_list = glob.glob(os.path.join(
self.instance_path, '*', 'etc', 'caddy-slave-conf.d', 'ssl',
'_ssl_ca_crt_does_not_match.key'))
self.assertEqual(1, len(key_file_list))
key_file = key_file_list[0]
with open(key_file) as out:
self.assertEqual(
self.key_pem,
expected,
out.read()
)
......
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!