Commit 860b4dbe authored by Jérome Perrin's avatar Jérome Perrin

Revert "stack/erp5: stop using caucase managed certificate for balancer"

This reverts commit 620c9332 and add a
caucase service for balancer in the balancer partition. The caucase
service from the root partition (that was not used) is remvoed.

TODO:
 - fix json (undo a change)
 - test external caucase in test_erp5
 - test switching from internal caucase to external caucase
parent 48c853fb
Pipeline #31850 failed with stage
in 0 seconds
......@@ -90,7 +90,6 @@ This software release assigns the following port ranges by default:
balancer 2150-2199
zope 2200-*
jupyter 8888
caucase 8890,8891
==================== ==========
Non-zope partitions are unique in an ERP5 cluster, so you shouldn't have to
......
......@@ -508,22 +508,15 @@
},
"caucase": {
"description": "Caucase certificate authority parameters",
"allOf": [
{
"properties": {
"url": {
"title": "Caucase URL",
"description": "URL of existing caucase instance to use. If empty, a new caucase instance will be deployed. If not empty, other properties in this section will be ignored.",
"description": "URL of existing caucase instance to use. If empty, caucase instances will be deployed inside partitions.",
"default": "",
"type": "string",
"format": "uri"
}
}
},
{
"$ref": "../caucase/instance-caucase-input-schema.json"
}
],
"type": "object"
},
"test-runner": {
......@@ -722,13 +715,13 @@
},
"caucase-url": {
"title": "Caucase URL",
"description": "URL of caucase service to use. If not set, global setting will be used.",
"description": "URL of caucase service to use. If not set, a caucase service will be instanciated.",
"type": "string",
"format": "uri"
},
"csr": {
"title": "csr",
"description": "PEM-encoded certificate signature request to request server certificate with. If not provided, HTTPS will be disabled.",
"description": "PEM-encoded certificate signature request to request server certificate with.",
"type": "string"
},
"max-crl-update-delay": {
......
......@@ -69,7 +69,7 @@
"type": "string"
},
"caucase-http-url": {
"description": "Caucase url on HTTP. For HTTPS URL, uses https scheme, if port is explicitely specified in http URL, take that port and add 1 and use it as https port. If it is not specified.",
"description": "Caucase url on HTTP. For HTTPS URL, uses https scheme, if port is explicitely specified in http URL, take that port and add 1 and use it as https port.",
"pattern": "^http://",
"type": "string"
}
......
import glob
import hashlib
import ipaddress
import json
import logging
import os
import re
import shutil
import socket
import subprocess
import sqlite3
import tempfile
import time
import urllib.parse
from http.server import BaseHTTPRequestHandler
from unittest import mock
import idna
import OpenSSL.SSL
import pexpect
import psutil
import requests
......@@ -21,6 +26,7 @@ from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.x509.oid import NameOID
from slapos.proxy.db_version import DB_VERSION
from slapos.testing.testcase import ManagedResource
from slapos.testing.utils import CrontabMixin, ManagedHTTPServer, findFreeTCPPort
......@@ -124,6 +130,19 @@ class CaucaseService(ManagedResource):
self._caucased_process.stdout.close()
shutil.rmtree(self.directory)
@property
def ca_crt_path(self) -> str:
"""Path of the CA certificate from this caucase.
"""
ca_crt_path = os.path.join(self.directory, 'ca.crt.pem')
if not os.path.exists(ca_crt_path):
with open(ca_crt_path, 'w') as f:
f.write(
requests.get(urllib.parse.urljoin(
self.url,
'/cas/crt/ca.crt.pem',
)).text)
return ca_crt_path
class BalancerTestCase(ERP5InstanceTestCase):
......@@ -165,9 +184,7 @@ class BalancerTestCase(ERP5InstanceTestCase):
'default': '',
},
'ssl-authentication-dict': {'default': False},
'ssl': {
'caucase-url': cls.getManagedResource("caucase", CaucaseService).url,
},
'ssl': {},
'timeout-dict': {'default': None},
'family-path-routing-dict': {},
'path-routing-list': [],
......@@ -557,6 +574,155 @@ class TestHTTP(BalancerTestCase):
])
class TestServerTLSEmbeddedCaucase(BalancerTestCase):
"""Check Server TLS with embedded caucase
"""
__partition_reference__ = 's'
def _getCaucaseCACertificatePath(self) -> str:
"""Returns the path of the caucase certificate on file system.
"""
ca_cert = tempfile.NamedTemporaryFile(
prefix="ca.crt.pem",
mode="w",
delete=False,
)
ca_cert.write(
requests.get(
urllib.parse.urljoin(
self.getRootPartitionConnectionParameterDict()['caucase-http-url'],
'/cas/crt/ca.crt.pem',
)).text)
ca_cert.flush()
self.addCleanup(os.unlink, ca_cert.name)
return ca_cert.name
def _getServerCertificate(self, hostname: str, port: int) -> x509.base.Certificate:
hostname_idna = idna.encode(hostname)
sock = socket.socket()
sock.connect((hostname, port))
ctx = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
sock_ssl = OpenSSL.SSL.Connection(ctx, sock)
sock_ssl.set_connect_state()
sock_ssl.set_tlsext_host_name(hostname_idna)
sock_ssl.do_handshake()
cert = sock_ssl.get_peer_certificate()
crypto_cert = cert.to_cryptography()
sock_ssl.close()
sock.close()
return crypto_cert
def test_certificate_validates_with_caucase_ca(self) -> None:
requests.get(self.default_balancer_url, verify=self._getCaucaseCACertificatePath())
def test_certificate_renewal(self) -> None:
balancer_parsed_url = urllib.parse.urlparse(self.default_balancer_url)
certificate_before_renewal = self._getServerCertificate(
balancer_parsed_url.hostname,
balancer_parsed_url.port)
# run caucase updater in the future, so that certificate is renewed
caucase_updater = os.path.join(
self.computer_partition_root_path,
'etc',
'service',
'haproxy-certificate-caucase-updater',
)
process = pexpect.spawnu("faketime +90days " + caucase_updater)
logger = self.logger
class DebugLogFile:
def write(self, msg):
logger.info("output from caucase_updater: %s", msg)
def flush(self):
pass
process.logfile = DebugLogFile()
process.expect("Renewing .*\nNext wake-up.*")
process.terminate()
process.wait()
# wait for server to use new certificate
for _ in range(30):
certificate_after_renewal = self._getServerCertificate(
balancer_parsed_url.hostname,
balancer_parsed_url.port)
if certificate_after_renewal.not_valid_before > certificate_before_renewal.not_valid_before:
break
time.sleep(.5)
self.assertGreater(
certificate_after_renewal.not_valid_before,
certificate_before_renewal.not_valid_before,
)
# requests are served properly after certificate renewal
self.test_certificate_validates_with_caucase_ca()
class TestServerTLSExternalCaucase(TestServerTLSEmbeddedCaucase):
"""Check Server TLS with external caucase
"""
@classmethod
def _getInstanceParameterDict(cls) -> dict:
parameter_dict = super()._getInstanceParameterDict()
parameter_dict['ssl']['caucase-url'] = cls.getManagedResource(
"caucase", CaucaseService).url
return parameter_dict
def test_published_caucase_http_url_parameter(self) -> None:
self.assertEqual(
self.getRootPartitionConnectionParameterDict()['caucase-http-url'],
self.getManagedResource("caucase", CaucaseService).url,
)
class TestServerTLSCSRTemplateParameter(TestServerTLSExternalCaucase):
"""Check Server TLS with a CSR template passed as parameter
"""
@classmethod
def _getInstanceParameterDict(cls) -> dict:
# use a CSR template with this subject, we'll assert that the
# certificate used by haproxy has same subject.
cls.csr_subject = subject = x509.Name(
[x509.NameAttribute(NameOID.COMMON_NAME, cls.__name__)])
# Add all IPs of the computer in SubjectAlternativeName, we don't
# know what will be the IP of the balancer partition.
with sqlite3.connect(cls.slap._proxy_database) as db:
ip_address_list = [
x509.IPAddress(ipaddress.ip_address(r)) for (r, ) in db.execute(
f"SELECT address FROM partition_network{DB_VERSION}").fetchall()
]
assert ip_address_list
csr = x509.CertificateSigningRequestBuilder().subject_name(
subject).add_extension(
x509.SubjectAlternativeName(ip_address_list),
critical=True,
).sign(
rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend(),
),
hashes.SHA256(),
default_backend(),
)
parameter_dict = super()._getInstanceParameterDict()
parameter_dict['ssl']['csr'] = csr.public_bytes(serialization.Encoding.PEM).decode()
return parameter_dict
def test_certificate_validates_with_caucase_ca(self) -> None:
super().test_certificate_validates_with_caucase_ca()
balancer_parsed_url = urllib.parse.urlparse(self.default_balancer_url)
cert = self._getServerCertificate(
balancer_parsed_url.hostname,
balancer_parsed_url.port,
)
self.assertEqual(
cert.subject.rfc4514_string(),
self.csr_subject.rfc4514_string())
class ContentTypeHTTPServer(ManagedHTTPServer):
"""An HTTP/1.1 Server which reply with content type from path.
......
......@@ -98,6 +98,22 @@ class TestPublishedURLIsReachableMixin:
self.assertEqual(r.status_code, requests.codes.ok)
self.assertIn("ERP5", r.text)
def _getCaucaseServiceCACertificate(self):
ca_cert = tempfile.NamedTemporaryFile(
prefix="ca.crt.pem",
mode="w",
delete=False,
)
ca_cert.write(
requests.get(
urllib.parse.urljoin(
self.getRootPartitionConnectionParameterDict()['caucase-http-url'],
'/cas/crt/ca.crt.pem',
)).text)
ca_cert.flush()
self.addCleanup(os.unlink, ca_cert.name)
return ca_cert.name
def test_published_family_default_v6_is_reachable(self):
"""Tests the IPv6 URL published by the root partition is reachable.
"""
......@@ -105,7 +121,7 @@ class TestPublishedURLIsReachableMixin:
self._checkERP5IsReachable(
param_dict['family-default-v6'],
param_dict['site-id'],
verify=False,
self._getCaucaseServiceCACertificate(),
)
def test_published_family_default_v4_is_reachable(self):
......@@ -115,7 +131,7 @@ class TestPublishedURLIsReachableMixin:
self._checkERP5IsReachable(
param_dict['family-default'],
param_dict['site-id'],
verify=False,
self._getCaucaseServiceCACertificate(),
)
def test_published_frontend_default_is_reachable(self):
......@@ -125,7 +141,7 @@ class TestPublishedURLIsReachableMixin:
self._checkERP5IsReachable(
param_dict['url-frontend-default'],
param_dict['site-id'],
verify=False,
self._getCaucaseServiceCACertificate(),
)
......
......@@ -74,7 +74,7 @@ md5sum = 55232eae0bcdb68a7cb2598d2ba9d60c
[template-erp5]
filename = instance-erp5.cfg.in
md5sum = a85867f7d05c13e576b5453c49bd8997
md5sum = 251662dae6df4a4938072c36a482e231
[template-zeo]
filename = instance-zeo.cfg.in
......@@ -86,11 +86,11 @@ md5sum = 0ac4b74436f554cd677f19275d18d880
[template-zope]
filename = instance-zope.cfg.in
md5sum = 4ab71c2cb50310b948828a07c521e0b7
md5sum = f9a68cd6f12bc2d576d50662d83ee480
[template-balancer]
filename = instance-balancer.cfg.in
md5sum = b0751d3d12cfcc8934cb1027190f5e5e
md5sum = 98beaa8d3fca6f143b8c60b9ebaca390
[template-haproxy-cfg]
filename = haproxy.cfg.in
......
......@@ -2,6 +2,7 @@
{% set part_list = [] -%}
{% macro section(name) %}{% do part_list.append(name) %}{{ name }}{% endmacro -%}
{% set ssl_parameter_dict = slapparameter_dict['ssl'] -%}
{% set caucase_url = ssl_parameter_dict.get('caucase-url') %}
{% set frontend_caucase_url_list = ssl_parameter_dict.get('frontend-caucase-url-list', []) -%}
{#
XXX: This template only supports exactly one IPv4 and (if ipv6 is used) one IPv6
......@@ -15,25 +16,112 @@ per partition. No more (undefined result), no less (IndexError).
[jinja2-template-base]
recipe = slapos.recipe.template:jinja2
[simplefile]
< = jinja2-template-base
inline = {{ '{{ content }}' }}
{% macro simplefile(section_name, file_path, content, mode='') -%}
{% set content_section_name = section_name ~ '-content' -%}
[{{ content_section_name }}]
content = {{ dumps(content) }}
[{{ section(section_name) }}]
< = simplefile
output = {{ file_path }}
context = key content {{content_section_name}}:content
mode = {{ mode }}
{%- endmacro %}
{% if not caucase_url -%}
{% if ipv6_set -%}
{% set caucase_host = '[' ~ (ipv6_set | list)[0] ~ ']' %}
{%- else -%}
{% set caucase_host = (ipv4_set | list)[0] %}
{%- endif %}
{% set caucase_port = 2199 -%}
{% set caucase_netloc = caucase_host ~ ':' ~ caucase_port -%}
{% set caucase_url = 'http://' ~ caucase_netloc %}
{{ caucase.caucased(
prefix='caucased-haproxy-certificate',
buildout_bin_directory=bin_directory,
caucased_path='${directory:services-on-watch}/caucased',
backup_dir='${directory:backup-caucased}',
data_dir='${directory:srv}/caucased',
netloc=caucase_netloc,
tmp='${directory:tmp}',
service_auto_approve_count=ssl_parameter_dict.get('service-auto-approve-amount', 1),
user_auto_approve_count=ssl_parameter_dict.get('user-auto-approve-amount', 0),
key_len=ssl_parameter_dict.get('key-length', 2048),
)}}
{% do section('caucased-haproxy-certificate') -%}
{% do section('caucased-haproxy-certificate-promise') -%}
{% endif -%}
[haproxy-certificate]
cert-and-key-file = ${directory:etc}/${:_buildout_section_name_}-cert-and-key.pem
ca-file = ${directory:etc}/${:_buildout_section_name_}.ca.crt
crl-file = ${directory:etc}/${:_buildout_section_name_}.crl
[haproxy-certificate-csr-config]
recipe = slapos.recipe.template
inline =
[req]
prompt = no
req_extensions = req_ext
distinguished_name = dn
[ dn ]
CN = haproxy
[ req_ext ]
subjectAltName = @alt_names
[ alt_names ]
IP.1 = {{ ipv4 }}
{% if ipv6_set %}
IP.2 = {{ ipv6 }}
{% endif %}
output = ${buildout:parts-directory}/${:_buildout_section_name_}/${:_buildout_section_name_}.txt
[haproxy-certificate-csr]
recipe = plone.recipe.command
command =
if [ ! -f '${:csr}' ] ; then
{{ parameter_dict['openssl'] }}/bin/openssl req \
-newkey rsa \
-batch \
-new \
-nodes \
-keyout /dev/null \
-config '${haproxy-certificate-csr-config:output}' \
-out '${:csr}'
fi
stop-on-error = true
csr = ${directory:srv}/${:_buildout_section_name_}.csr.pem
{% if ssl_parameter_dict.get('csr') %}
{{ simplefile(
'haproxy-certificate-csr-from-parameters',
'${directory:etc}/haproxy-certificate-csr-from-parameters.pem',
ssl_parameter_dict['csr']) }}
[haproxy-certificate-csr]
csr = ${haproxy-certificate-csr-from-parameters:output}
{% endif %}
{{ caucase.updater(
prefix='caucase-updater',
prefix='caucase-updater-haproxy-certificate',
buildout_bin_directory=parameter_dict['bin-directory'],
updater_path='${directory:services-on-watch}/caucase-updater',
url=ssl_parameter_dict['caucase-url'],
data_dir='${directory:srv}/caucase-updater',
crt_path='${apache-conf-ssl:caucase-cert}',
ca_path='${directory:srv}/caucase-updater/ca.crt',
crl_path='${directory:srv}/caucase-updater/crl.pem',
key_path='${apache-conf-ssl:caucase-key}',
updater_path='${directory:services-on-watch}/caucase-updater-haproxy-certificate',
url=caucase_url,
data_dir='${directory:caucase-updater-haproxy-certificate}',
crt_path='${haproxy-certificate:cert-and-key-file}',
ca_path='${haproxy-certificate:ca-file}',
crl_path='${haproxy-certificate:crl-file}',
key_path='${haproxy-certificate:cert-and-key-file}',
on_renew='${haproxy-reload:output}',
max_sleep=ssl_parameter_dict.get('max-crl-update-delay', 1.0),
template_csr_pem=ssl_parameter_dict.get('csr'),
template_csr='${haproxy-certificate-csr:csr}',
openssl=parameter_dict['openssl'] ~ '/bin/openssl',
)}}
{# XXX we don't use caucase yet.
{% do section('caucase-updater') -%}
{% do section('caucase-updater-promise') -%}
#}
{% do section('caucase-updater-haproxy-certificate') -%}
{% set frontend_caucase_url_hash_list = [] -%}
{% for frontend_caucase_url in frontend_caucase_url_list -%}
......@@ -228,22 +316,6 @@ hash-files = ${haproxy-cfg:output}
caucase-cert = ${directory:apache-conf}/apache-caucase.crt
caucase-key = ${directory:apache-conf}/apache-caucase.pem
[simplefile]
< = jinja2-template-base
inline = {{ '{{ content }}' }}
{% macro simplefile(section_name, file_path, content, mode='') -%}
{% set content_section_name = section_name ~ '-content' -%}
[{{ content_section_name }}]
content = {{ dumps(content) }}
[{{ section(section_name) }}]
< = simplefile
output = {{ file_path }}
context = key content {{content_section_name}}:content
mode = {{ mode }}
{%- endmacro %}
[{{ section('haproxy-socat-stats')}}]
recipe = slapos.cookbook:wrapper
wrapper-path = ${directory:bin}/${:_buildout_section_name_}
......@@ -277,7 +349,7 @@ config-command = test -S ${rsyslogd-cfg-parameter-dict:log-socket}
[haproxy-conf-ssl]
certificate = ${build-certificate-and-key:certificate-and-key}
certificate = ${haproxy-certificate:cert-and-key-file}
{% if frontend_caucase_url_list -%}
ca-cert = ${directory:etc}/frontend-ca.pem
ca-cert-dir = ${directory:ca-cert}
......@@ -286,18 +358,15 @@ crl-dir = ${directory:crl}
depends = ${caucase-updater-housekeeper-run:recipe}
{%- endif %}
[build-certificate-and-key]
# BBB cert and key as ssl parameters. Use caucase instead.
{% if ssl_parameter_dict.get('key') -%}
certificate-and-key = ${tls-certificate-and-key-from-parameters:output}
{{ simplefile(
'tls-certificate-and-key-from-parameters',
'haproxy-conf-ssl-certificate-and-key-from-parameters',
'${directory:etc}/certificate-and-key-from-parameters.pem',
ssl_parameter_dict['cert'] ~ "\n" ~ ssl_parameter_dict['key']) }}
{% else %}
recipe = plone.recipe.command
command = "{{ parameter_dict['openssl'] }}/bin/openssl" req -newkey rsa -batch -new -x509 -days 3650 -nodes -keyout "${:certificate-and-key}" -out "${:certificate-and-key}"
certificate-and-key = ${directory:etc}/certificate-and-key-generated.pem
{%- endif %}
[haproxy-conf-ssl]
certificate = ${haproxy-conf-ssl-certificate-and-key-from-parameters:output}
{% endif %}
[{{ section('haproxy-promise') }}]
<= monitor-promise-base
......@@ -316,6 +385,7 @@ recipe = slapos.cookbook:publish.serialised
{% for family_name, test_runner_url_list in test_runner_url_dict.items() -%}
{{ family_name ~ '-test-runner-url-list' }} = {{ dumps(test_runner_url_list) }}
{% endfor -%}
caucase-http-url = {{ caucase_url }}
monitor-base-url = ${monitor-publish-parameters:monitor-base-url}
[{{ section('logrotate-rsyslogd') }}]
......@@ -335,6 +405,9 @@ var = ${buildout:directory}/var
run = ${:var}/run
log = ${:var}/log
srv = ${buildout:directory}/srv
backup-caucased = ${:srv}/backup/caucased/
caucase-updater-haproxy-certificate = ${:srv}/caucase-updater-haproxy-certificate
tmp = ${buildout:directory}/tmp
apachedex = ${monitor-directory:private}/apachedex
rsyslogd-spool = ${:run}/rsyslogd-spool
{% if frontend_caucase_url_list -%}
......
......@@ -44,6 +44,7 @@
<= request-common-base
config-use-ipv6 = {{ dumps(slapparameter_dict.get('use-ipv6', False)) }}
config-computer-memory-percent-threshold = {{ dumps(monitor_dict.get('computer-memory-percent-threshold', 80)) }}
# TODO: user can pass a caucase URL
{% set caucase_dict = slapparameter_dict.get('caucase', {}) -%}
{% set caucase_url = caucase_dict.get('url', '') -%}
......@@ -79,35 +80,13 @@ bin = ${buildout:directory}/bin
service-on-watch = ${buildout:directory}/etc/service
srv = ${buildout:directory}/srv
tmp = ${buildout:directory}/tmp
backup-caucased = ${:srv}/backup/caucased
{% if not caucase_url -%}
{% if use_ipv6 -%}
{% set caucase_host = '[' ~ (ipv6_set | list)[0] ~ ']' %}
{%- else -%}
{% set caucase_host = (ipv4_set | list)[0] %}
{%- endif %}
{% set caucase_port = caucase_dict.get('base-port', 8890) -%}
{% set caucase_netloc = caucase_host ~ ':' ~ caucase_port -%}
{% set caucase_url = 'http://' ~ caucase_netloc -%}
{{ caucase.caucased(
prefix='caucased',
buildout_bin_directory=bin_directory,
caucased_path='${directory:service-on-watch}/caucased',
backup_dir='${directory:backup-caucased}',
data_dir='${directory:srv}/caucased',
netloc=caucase_netloc,
tmp='${directory:tmp}',
service_auto_approve_count=caucase_dict.get('service-auto-approve-amount', 1),
user_auto_approve_count=caucase_dict.get('user-auto-approve-amount', 0),
key_len=caucase_dict.get('key-length', 2048),
)}}
{% do root_common.section('caucased') -%}
{% do root_common.section('caucased-promise') -%}
{% endif -%}
{% do publish_dict.__setitem__('caucase-http-url', caucase_url) -%}
{% set balancer_dict = slapparameter_dict.setdefault('balancer', {}) -%}
{% do balancer_dict.setdefault('ssl', {}).setdefault('caucase-url', caucase_url) -%}
{% do balancer_dict.setdefault('ssl', {}) %}
{% if caucase_url %}
{% do balancer_dict['ssl'].setdefault('caucase-url', caucase_url) -%}
{% endif %}
{% do balancer_dict.setdefault('tcpv4-port', 2150) -%}
{% do balancer_dict.__setitem__('haproxy-server-check-path', balancer_dict.get('haproxy-server-check-path', '/') % {'site-id': site_id}) -%}
{% set routing_path_template_field_dict = {"site-id": site_id} -%}
......@@ -241,7 +220,6 @@ return =
config-bt5 = {{ dumps(slapparameter_dict.get('bt5', ' '.join(bt5_default_list))) }}
config-bt5-repository-url = {{ dumps(slapparameter_dict.get('bt5-repository-url', local_bt5_repository)) }}
config-cloudooo-url-list = {{ dumps(slapparameter_dict.get('cloudooo-url-list', default_cloudooo_url_list)) }}
config-caucase-url = {{ dumps(caucase_url) }}
config-deadlock-debugger-password = ${publish-early:deadlock-debugger-password}
config-developer-list = {{ dumps(slapparameter_dict.get('developer-list', [inituser_login])) }}
config-selenium-server-configuration-dict = {{ dumps(slapparameter_dict.get('selenium-server-configuration-dict', {})) }}
......@@ -447,7 +425,7 @@ config-allow-redirects = 0
{%- endif %}
{% set balancer_ret_dict = {'monitor-base-url': False} -%}
{% set balancer_ret_dict = {'monitor-base-url': False, 'caucase-http-url': False} -%}
{% for family in zope_family_dict -%}
{% do balancer_ret_dict.__setitem__(family, False) -%}
{% do balancer_ret_dict.__setitem__(family + '-v6', False) -%}
......@@ -496,7 +474,7 @@ config-allow-redirects = 0
ret=balancer_ret_dict,
key_config=balancer_key_config_dict,
) }}
{% do publish_dict.__setitem__('caucase-http-url', '${request-balancer:connection-caucase-http-url}' ) -%}
{% endif -%}{# if zope_partition_dict -#}
......
......@@ -81,7 +81,6 @@ environment +=
MATPLOTLIBRC={{ parameter_dict['matplotlibrc'] }}
PYTHONUNBUFFERED=1
INSTANCE_HOME=${:instance-home}
CAUCASE={{ slapparameter_dict['caucase-url'] }}
FONTCONFIG_FILE=${fontconfig-conf:output}
JUPYTER_PATH=${directory:jupyter-dir}
JUPYTER_CONFIG_DIR=${directory:jupyter-config-dir}
......
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