{% import "caucase" as caucase with context %} {%- set TRUE_VALUES = ['y', 'yes', '1', 'true'] -%} [buildout] extends = {{ software_parameter_dict['profile_common'] }} {{ software_parameter_dict['profile_monitor'] }} {{ software_parameter_dict['profile_logrotate_base'] }} parts = directory logrotate-entry-caddy caddy-frontend software-py switch-frontend-softwaretype caucase-updater caucase-updater-promise backend-client-caucase-updater backend-client-caucase-updater-promise frontend-caddy-graceful port-redirection promise-frontend-caddy-configuration promise-caddy-frontend-v4-https promise-caddy-frontend-v4-http promise-caddy-frontend-v6-https promise-caddy-frontend-v6-http promise-logrotate-setup trafficserver-launcher trafficserver-reload trafficserver-configuration-directory trafficserver-records-config trafficserver-remap-config trafficserver-plugin-config trafficserver-storage-config trafficserver-ip-allow-config trafficserver-logging-yaml trafficserver-promise-listen-port trafficserver-promise-cache-availability cron-entry-logrotate-trafficserver ## Monitor for Caddy monitor-base monitor-ats-cache-stats-wrapper monitor-traffic-summary-last-stats-wrapper monitor-caddy-server-status-wrapper monitor-verify-re6st-connectivity backend-haproxy-rsyslogd-configuration backend-haproxy-rsyslogd logrotate-entry-backend-haproxy backend-haproxy backend-haproxy-graceful promise-backend-haproxy-http promise-backend-haproxy-https promise-backend-haproxy-configuration slave-introspection-frontend slave-introspection-graceful promise-slave-introspection-https promise-slave-introspection-configuration logrotate-entry-slave-introspection backend-haproxy-statistic-frontend-promise [software-py] recipe = software [frontend-node-id] # Store id file in top of hierarchy, so it does not depend on directory creation file = ${buildout:directory}/.frontend-node-id.txt recipe = slapos.recipe.build init = import os import secrets if not os.path.exists(options['file']): with open(options['file'], 'w') as fh: fh.write(secrets.token_urlsafe(4)) with open(options['file'], 'r') as fh: options['value'] = fh.read() [frontend-node-private-salt] # Private, not communicated, stable hash, which can be used to salt other # hashes, so their values are connected to the node, but practicaly impossible # to crack (until the node is hacked itself, but then those values are # stolen anyway) recipe = slapos.recipe.build init = import os import uuid if not os.path.exists(options['file']): with open(options['file'], 'w') as fh: fh.write(uuid.uuid4().hex) with open(options['file'], 'r') as fh: options['value'] = fh.read() file = ${buildout:directory}/.frontend-node-private-salt.txt [version-hash] recipe = slapos.recipe.build software-release-url = ${slap-connection:software-release-url} hash-salt = ${frontend-node-private-salt:value} init = import hashlib import base64 options['value'] = base64.urlsafe_b64encode(hashlib.md5(''.join([options['software-release-url'].strip(), options['hash-salt']]).encode()).digest()).decode() [frontend-node-information] recipe = slapos.recipe.build file = ${buildout:directory}/.frontend-node-information.json node-id = ${frontend-node-id:value} current-hash = ${version-hash:value} current-software-release-url = ${version-hash:software-release-url} init = import json changed = False try: with open(options['file'], 'r') as fh: data = json.load(fh) except Exception: changed = True data = { 'node-id': options['node-id'], 'version-hash-history': {options['current-hash']: options['current-software-release-url']} } if 'node-id' not in data: data['node-id'] = options['node-id'] changed = True if 'version-hash-history' not in data: data['version-hash-history'] = {} changed = True if options['current-hash'] not in data['version-hash-history']: data['version-hash-history'][options['current-hash']] = options['current-software-release-url'] changed = True if changed: with open(options['file'], 'w') as fh: json.dump(data, fh) options['value'] = data # Create all needed directories [directory] recipe = slapos.cookbook:mkdirectory bin = ${buildout:directory}/bin/ etc = ${buildout:directory}/etc/ srv = ${buildout:directory}/srv/ var = ${buildout:directory}/var/ tmp = ${:var}/tmp template = ${buildout:directory}/template/ backup = ${:srv}/backup log = ${:var}/log run = ${:var}/run backend-haproxy-rsyslogd-spool = ${:run}/backend-haproxy-rsyslogd-spool service = ${:etc}/service etc-run = ${:etc}/run ca-dir = ${:srv}/ssl backend-client-dir = ${:srv}/backend-client # BBB: SlapOS Master non-zero knowledge BEGIN bbb-ssl-dir = ${:srv}/bbb-ssl # BBB: SlapOS Master non-zero knowledge END frontend_cluster = ${:var}/frontend_cluster # CSR publication expose-csr = ${:srv}/expose-csr expose-csr-etc = ${:etc}/expose-csr expose-csr-var = ${:var}/expose-csr # slave introspection slave-introspection-var = ${:var}/slave-introspection [switch-frontend-softwaretype] recipe = slapos.cookbook:switch-softwaretype default = dynamic-custom-personal-profile-slave-list:output RootSoftwareInstance = ${:default} single-default = dynamic-custom-personal-profile-slave-list:output single-custom-personal = dynamic-custom-personal-profile-slave-list:output [frontend-configuration] ip-access-certificate = ${self-signed-ip-access:certificate} caddy-ipv6 = {{ instance_parameter_dict['ipv6-random'] }} caddy-https-port = ${configuration:port} slave-introspection-configuration = ${directory:etc}/slave-introspection-httpd-nginx.conf slave-introspection-https-port = ${configuration:slave-introspection-https-port} slave-introspection-secure_access = ${slave-introspection-frontend:connection-secure_access} slave-introspection-domain = ${slave-introspection-frontend:connection-domain} [self-signed-ip-access] # Self Signed certificate for HTTPS IP accesses to the frontend recipe = plone.recipe.command update-command = ${:command} ipv6 = ${slap-configuration:ipv6-random} ipv4 = {{instance_parameter_dict['ipv4-random']}} certificate = ${caddy-directory:master-autocert-dir}/ip-access-${:ipv6}-${:ipv4}.crt {#- Can be stopped on error, as does not rely on self provided service #} stop-on-error = True command = [ -f ${:certificate} ] && exit 0 rm -f ${:certificate} /bin/bash -c ' \ {{ software_parameter_dict['openssl'] }} req \ -new -newkey rsa:2048 -sha256 \ -nodes -x509 -days 36500 \ -keyout ${:certificate} \ -subj "/CN=Self Signed IP Access" \ -reqexts SAN \ -extensions SAN \ -config <(cat {{ software_parameter_dict['openssl_cnf'] }} \ <(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-configuration:ipv6-random} ipv4 = {{instance_parameter_dict['ipv4-random']}} certificate = ${caddy-directory:master-autocert-dir}/fallback-access.crt {#- Can be stopped on error, as does not rely on self provided service #} stop-on-error = True command = [ -f ${:certificate} ] && exit 0 rm -f ${:certificate} /bin/bash -c ' \ {{ software_parameter_dict['openssl'] }} req \ -new -newkey rsa:2048 -sha256 \ -nodes -x509 -days 36500 \ -keyout ${:certificate} \ -subj "/CN=Fallback certificate/OU={{ instance_parameter_dict['configuration.frontend-name'] }}" \ -out ${:certificate}' [jinja2-template-base] recipe = slapos.recipe.template:jinja2 output = ${buildout:directory}/${:filename} extensions = jinja2.ext.do extra-context = slapparameter_dict = {{ dumps(slapparameter_dict) }} slap_software_type = {{ dumps(instance_parameter_dict['slap-software-type']) }} context = import json_module json raw profile_common {{ software_parameter_dict['profile_common'] }} raw profile_logrotate_base {{ software_parameter_dict['profile_logrotate_base'] }} raw profile_monitor {{ software_parameter_dict['profile_monitor'] }} key slap_software_type :slap_software_type key slapparameter_dict :slapparameter_dict section directory directory ${:extra-context} [software-release-path] template-empty = {{ software_parameter_dict['template_empty'] }} template-default-slave-virtualhost = {{ software_parameter_dict['template_default_slave_virtualhost'] }} template-backend-haproxy-configuration = {{ software_parameter_dict['template_backend_haproxy_configuration'] }} template-backend-haproxy-rsyslogd-conf = {{ software_parameter_dict['template_backend_haproxy_rsyslogd_conf'] }} template-expose-csr-nginx-conf = {{ software_parameter_dict['template_expose_csr_nginx_conf'] }} [kedifa-login-config] d = ${directory:ca-dir} template-csr = ${:d}/kedifa-login-template-csr.pem key = ${:d}/kedifa-login-certificate.pem certificate = ${:key} ca-certificate = ${:d}/kedifa-caucase-ca.pem cas-ca-certificate = ${:d}/kedifa-cas-caucase-ca.pem crl = ${:d}/kedifa-login-crl.pem [kedifa-login-csr] recipe = plone.recipe.command organization = {{ slapparameter_dict['cluster-identification'] }} organizational_unit = {{ instance_parameter_dict['configuration.frontend-name'] }} command = {% if slapparameter_dict['kedifa-caucase-url'] %} if [ ! -f ${:template-csr} ] && [ ! -f ${:key} ] ; then {{ software_parameter_dict['openssl'] }} req -new -sha256 \ -newkey rsa:2048 -nodes -keyout ${:key} \ -subj "/O=${:organization}/OU=${:organizational_unit}" \ -out ${:template-csr} fi {% endif %} test -f ${:key} && test -f ${:template-csr} update-command = ${:command} template-csr = ${kedifa-login-config:template-csr} key = ${kedifa-login-config:key} {#- Can be stopped on error, as does not rely on self provided service #} stop-on-error = True {{ caucase.updater( prefix='caucase-updater', buildout_bin_directory=software_parameter_dict['bin_directory'], updater_path='${directory:service}/kedifa-login-certificate-caucase-updater', url=slapparameter_dict['kedifa-caucase-url'], data_dir='${directory:srv}/caucase-updater', crt_path='${kedifa-login-config:certificate}', ca_path='${kedifa-login-config:ca-certificate}', crl_path='${kedifa-login-config:crl}', key_path='${kedifa-login-csr:key}', template_csr='${kedifa-login-csr:template-csr}' )}} [kedifa-configuration] caucase-url = {{ slapparameter_dict['kedifa-caucase-url'] }} ca-certificate = ${kedifa-login-config:ca-certificate} certificate = ${kedifa-login-config:certificate} cas-ca-certificate = ${kedifa-login-config:cas-ca-certificate} csr = ${caucase-updater-csr:csr} crl = ${kedifa-login-config:crl} kedifa-updater-mapping-file = ${directory:etc}/kedifa_updater_mapping.txt kedifa-updater-state-file = ${directory:srv}/kedifa_updater_state.json slave_kedifa_information = {{ dumps(slapparameter_dict['slave-kedifa-information']) }} [backend-client-login-config] d = ${directory:backend-client-dir} template-csr = ${:d}/csr.pem key = ${:d}/certificate.pem certificate = ${:key} ca-certificate = ${:d}/ca.pem cas-ca-certificate = ${:d}/cas-ca.pem crl = ${:d}/crl.pem [backend-client-login-csr] recipe = plone.recipe.command organization = {{ slapparameter_dict['cluster-identification'] }} organizational_unit = {{ instance_parameter_dict['configuration.frontend-name'] }} command = {% if slapparameter_dict['backend-client-caucase-url'] %} if [ ! -f ${:template-csr} ] && [ ! -f ${:key} ] ; then {{ software_parameter_dict['openssl'] }} req -new -sha256 \ -newkey rsa:2048 -nodes -keyout ${:key} \ -subj "/O=${:organization}/OU=${:organizational_unit}" \ -out ${:template-csr} fi {% endif %} test -f ${:key} && test -f ${:template-csr} update-command = ${:command} template-csr = ${backend-client-login-config:template-csr} key = ${backend-client-login-config:key} {#- Can be stopped on error, as does not rely on self provided service #} stop-on-error = True {{ caucase.updater( prefix='backend-client-caucase-updater', buildout_bin_directory=software_parameter_dict['bin_directory'], updater_path='${directory:service}/backend-client-login-certificate-caucase-updater', url=slapparameter_dict['backend-client-caucase-url'], data_dir='${directory:srv}/backend-client-caucase-updater', crt_path='${backend-client-login-config:certificate}', ca_path='${backend-client-login-config:ca-certificate}', crl_path='${backend-client-login-config:crl}', key_path='${backend-client-login-csr:key}', template_csr='${backend-client-login-csr:template-csr}' )}} [dynamic-custom-personal-profile-slave-list] < = jinja2-template-base depends = ${software-py:recipe} url = {{ software_parameter_dict['profile_slave_list'] }} filename = instance-slave-list.cfg master_key_download_url = {{ dumps(slapparameter_dict['master-key-download-url']) }} software_type = single-custom-personal organization = {{ slapparameter_dict['cluster-identification'] }} organizational-unit = {{ instance_parameter_dict['configuration.frontend-name'] }} backend-client-caucase-url = {{ slapparameter_dict['backend-client-caucase-url'] }} partition_ipv6 = ${slap-configuration:ipv6-random} url-ready-file = ${directory:var}/url-ready.txt extra-context = key caddy_configuration_directory caddy-directory:slave-configuration key backend_client_caucase_url :backend-client-caucase-url import furl_module furl import urllib_module urllib import operator_module operator key master_key_download_url :master_key_download_url key autocert caddy-directory:autocert key caddy_log_directory caddy-directory:slave-log key url_ready_file :url-ready-file key expose_csr_organization :organization key expose_csr_organizational_unit :organizational-unit key global_ipv6 slap-configuration:ipv6-random key empty_template software-release-path:template-empty key template_default_slave_configuration software-release-path:template-default-slave-virtualhost key template_expose_csr_nginx_conf software-release-path:template-expose-csr-nginx-conf key software_type :software_type key frontend_lazy_graceful_reload frontend-caddy-lazy-graceful:output key monitor_base_url monitor-instance-parameter:monitor-base-url key node_id frontend-node-id:value key version_hash version-hash:value key software_release_url version-hash:software-release-url key node_information frontend-node-information:value key custom_ssl_directory caddy-directory:custom-ssl-directory # BBB: SlapOS Master non-zero knowledge BEGIN key apache_certificate apache-certificate:output # BBB: SlapOS Master non-zero knowledge END ## backend haproxy key template_backend_haproxy_configuration software-release-path:template-backend-haproxy-configuration ## Configuration passed by section section configuration configuration section backend_haproxy_configuration backend-haproxy-configuration section instance_parameter_dict instance-parameter-section section frontend_configuration frontend-configuration section caddy_configuration caddy-configuration section kedifa_configuration kedifa-configuration section software_parameter_dict software-parameter-section # Deploy Caddy Frontend with Jinja power [dynamic-caddy-frontend-template] < = jinja2-template-base url = {{ software_parameter_dict['template_caddy_frontend_configuration'] }} output = ${caddy-configuration:frontend-configuration} local_ipv4 = {{ dumps(instance_parameter_dict['ipv4-random']) }} extra-context = key instance_home buildout:directory key master_certificate caddy-configuration:master-certificate key access_log caddy-configuration:access-log key slave_configuration_directory caddy-directory:slave-configuration section frontend_configuration frontend-configuration key http_port configuration:plain_http_port key https_port configuration:port key global_ipv6 slap-configuration:ipv6-random key local_ipv4 :local_ipv4 key error_log caddy-configuration:error-log key not_found_file caddy-configuration:not-found-file key username monitor-instance-parameter:username key password monitor-htpasswd:passwd # BBB: SlapOS Master non-zero knowledge BEGIN key apache_certificate apache-certificate:output # BBB: SlapOS Master non-zero knowledge END [caddy-wrapper] recipe = slapos.recipe.template:jinja2 inline = #!/bin/sh export CADDYPATH=${directory:frontend_cluster} ulimit -n $(ulimit -Hn) exec {{ software_parameter_dict['caddy'] }} \ -conf ${dynamic-caddy-frontend-template:output} \ -log ${caddy-configuration:error-log} \ -log-roll-mb 0 \ -http2=true \ -grace {{ instance_parameter_dict['configuration.mpm-graceful-shutdown-timeout'] }}s \ -disable-http-challenge \ -disable-tls-alpn-challenge \ "$@" output = ${directory:bin}/caddy-wrapper [caddy-frontend] recipe = slapos.cookbook:wrapper command-line = ${caddy-wrapper:output} -pidfile ${caddy-configuration:pid-file} wrapper-path = ${directory:service}/frontend_caddy hash-existing-files = ${buildout:directory}/software_release/buildout.cfg hash-files = ${caddy-wrapper:output} [not-found-html] recipe = plone.recipe.command update-command = ${:command} filename = notfound.html command = ln -sf {{ software_parameter_dict['template_not_found_html'] }} ${caddy-directory:document-root}/${:filename} [caddy-directory] recipe = slapos.cookbook:mkdirectory document-root = ${directory:srv}/htdocs slave-configuration = ${directory:etc}/caddy-slave-conf.d/ slave-log = ${directory:log}/httpd autocert = ${directory:srv}/autocert master-autocert-dir = ${:autocert}/master-autocert custom-ssl-directory = ${:slave-configuration}/ssl [caddy-configuration] frontend-configuration = ${directory:etc}/Caddyfile access-log = ${directory:log}/frontend-access.log error-log = ${directory:log}/frontend-error.log pid-file = ${directory:run}/httpd.pid frontend-graceful-command = ${frontend-caddy-validate:output} && kill -USR1 $(cat ${:pid-file}) not-found-file = ${caddy-directory:document-root}/${not-found-html:filename} master-certificate = ${caddy-directory:master-autocert-dir}/master.pem # Communication with ATS cache-port = ${trafficserver-variable:input-port} # slave instrspection slave-introspection-access-log = ${directory:log}/slave-introspection-access.log slave-introspection-error-log = ${directory:log}/slave-introspection-error.log slave-introspection-pid-file = ${directory:run}/slave-introspection.pid slave-introspection-graceful-command = ${slave-introspection-validate:output} && kill -HUP $(cat ${:slave-introspection-pid-file}) # BBB: SlapOS Master non-zero knowledge BEGIN [get-self-signed-fallback-access] recipe = slapos.recipe.build certificate-file = ${self-signed-fallback-access:certificate} init = import os options['certificate'] = '' if os.path.exists(options['certificate-file']): with open(options['certificate-file'], 'r') as fh: options['certificate'] = fh.read() [apache-certificate] recipe = slapos.recipe.template:jinja2 inline = {% raw %} {{ 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 output = ${directory:bbb-ssl-dir}/frontend.crt # BBB: SlapOS Master non-zero knowledge END [logrotate-entry-caddy] <= logrotate-entry-base name = caddy log = ${caddy-configuration:error-log} ${caddy-configuration:access-log} rotate-num = ${configuration:rotate-num} # Note: Slaves do not define their own reload, as this would be repeated, # because sharedscripts work per entry, and each slave needs its own # olddir # Here we trust that there will be something to be rotated with error # or access log, and that this will trigger postrotate script. post = ${frontend-caddy-lazy-graceful:output} & delaycompress = ################# # Trafficserver ################# [trafficserver-directory] recipe = slapos.cookbook:mkdirectory configuration = ${directory:etc}/trafficserver local-state = ${directory:var}/trafficserver bin_path = {{ software_parameter_dict['trafficserver'] }}/bin log = ${directory:log}/trafficserver cache-path = ${directory:srv}/ats_cache logrotate-backup = ${logrotate-directory:logrotate-backup}/trafficserver [trafficserver-variable] wrapper-path = ${directory:service}/trafficserver reload-path = ${directory:etc-run}/trafficserver-reload local-ip = {{ instance_parameter_dict['ipv4-random'] }} input-port = 23432 hostname = ${configuration:frontend-name} plugin-config = ip-allow-config = src_ip=0.0.0.0-255.255.255.255 action=ip_allow cache-path = ${trafficserver-directory:cache-path} disk-cache-size = ${configuration:disk-cache-size} ram-cache-size = ${configuration:ram-cache-size} templates-dir = {{ software_parameter_dict['trafficserver'] }}/etc/trafficserver/body_factory request-timeout = ${configuration:request-timeout} version-hash = ${version-hash:value} node-id = ${frontend-node-id:value} [trafficserver-configuration-directory] recipe = plone.recipe.command command = cp -rn {{ software_parameter_dict['trafficserver'] }}/etc/trafficserver/* ${:target} target = ${trafficserver-directory:configuration} [trafficserver-launcher] recipe = slapos.cookbook:wrapper command-line = {{ software_parameter_dict['trafficserver'] }}/bin/traffic_manager wrapper-path = ${trafficserver-variable:wrapper-path} environment = TS_ROOT=${buildout:directory} PROXY_CONFIG_CONFIG_DIR=${trafficserver-directory:configuration} hash-existing-files = ${buildout:directory}/software_release/buildout.cfg [trafficserver-reload] recipe = slapos.cookbook:wrapper command-line = {{ software_parameter_dict['trafficserver'] }}/bin/traffic_ctl config reload wrapper-path = ${trafficserver-variable:reload-path} environment = TS_ROOT=${buildout:directory} # XXX Dedicated Jinja Section without slapparameter [trafficserver-jinja2-template-base] recipe = slapos.recipe.template:jinja2 output = ${trafficserver-directory:configuration}/${:filename} extra-context = context = section ats_directory trafficserver-directory section ats_configuration trafficserver-variable ${:extra-context} [trafficserver-records-config] < = trafficserver-jinja2-template-base url = {{ software_parameter_dict['template_trafficserver_records_config'] }} filename = records.config extra-context = import os_module os [trafficserver-storage-config] < = trafficserver-jinja2-template-base url = {{ software_parameter_dict['template_trafficserver_storage_config'] }} filename = storage.config [trafficserver-logging-yaml] < = trafficserver-jinja2-template-base url = {{ software_parameter_dict['template_trafficserver_logging_yaml'] }} filename = logging.yaml [trafficserver-remap-config] <= trafficserver-jinja2-template-base {%- raw %} inline = map /HTTPS/ http://{{ ipv4 }}:{{ https_port }} map /HTTP/ http://{{ ipv4 }}:{{ http_port }} {%- endraw %} extra-context = raw ipv4 {{ instance_parameter_dict['ipv4-random'] }} key https_port backend-haproxy-configuration:https-port key http_port backend-haproxy-configuration:http-port filename = remap.config [trafficserver-plugin-config] < = trafficserver-jinja2-template-base url = {{ software_parameter_dict['template_empty'] }} filename = plugin.config context = key content trafficserver-variable:plugin-config [trafficserver-ip-allow-config] < = trafficserver-jinja2-template-base url = {{ software_parameter_dict['template_empty'] }} filename = ip_allow.config context = key content trafficserver-variable:ip-allow-config [trafficserver-promise-listen-port] <= monitor-promise-base promise = check_socket_listening name = trafficserver-port-listening.py config-host = ${trafficserver-variable:local-ip} config-port = ${trafficserver-variable:input-port} [trafficserver-ctl] recipe = slapos.cookbook:wrapper command-line = {{ software_parameter_dict['trafficserver'] }}/bin/traffic_ctl wrapper-path = ${directory:bin}/traffic_ctl environment = TS_ROOT=${buildout:directory} [trafficserver-promise-cache-availability] <= monitor-promise-base promise = trafficserver_cache_availability name = trafficserver-cache-availability.py config-wrapper-path = ${trafficserver-ctl:wrapper-path} [trafficserver-rotate-script] < = jinja2-template-base url = {{ software_parameter_dict['template_rotate_script'] }} output = ${directory:bin}/trafficserver-rotate xz_binary = {{ software_parameter_dict['xz_location'] ~ '/bin/xz' }} pattern = *.old # days to keep log files keep_days = 365 extra-context = key log_dir trafficserver-directory:log key rotate_dir trafficserver-directory:logrotate-backup key xz_binary :xz_binary key keep_days :keep_days key pattern :pattern [cron-entry-logrotate-trafficserver] recipe = slapos.cookbook:cron.d cron-entries = ${directory:etc}/cron.d name = trafficserver-logrotate frequency = 0 0 * * * command = ${trafficserver-rotate-script:output} ### End of ATS sections ### Caddy Graceful and promises [frontend-caddy-configuration-state] < = jinja2-template-base url = {{ software_parameter_dict['template_configuration_state_script'] }} output = ${directory:bin}/${:_buildout_section_name_} path_list = ${caddy-configuration:frontend-configuration} ${caddy-directory:slave-configuration}/*.conf ${caddy-directory:master-autocert-dir}/*.key ${caddy-directory:master-autocert-dir}/*.crt ${caddy-directory:master-autocert-dir}/*.pem ${caddy-directory:autocert}/*.pem ${caddy-directory:custom-ssl-directory}/*.proxy_ca_crt ${directory:bbb-ssl-dir}/*.crt sha256sum = {{ software_parameter_dict['sha256sum'] }} extra-context = key path_list :path_list key sha256sum :sha256sum key signature_file :signature_file [frontend-caddy-configuration-state-graceful] < = frontend-caddy-configuration-state signature_file = ${directory:run}/graceful_configuration_state_signature [frontend-caddy-configuration-state-validate] < = frontend-caddy-configuration-state signature_file = ${directory:run}/validate_configuration_state_signature [frontend-caddy-graceful] < = jinja2-template-base url = {{ software_parameter_dict['template_graceful_script'] }} output = ${directory:etc-run}/frontend-caddy-safe-graceful extra-context = key graceful_reload_command caddy-configuration:frontend-graceful-command key configuration_state frontend-caddy-configuration-state-graceful:output [frontend-caddy-validate] < = jinja2-template-base url = {{ software_parameter_dict['template_validate_script'] }} output = ${directory:bin}/frontend-caddy-validate last_state_file = ${directory:run}/caddy_configuration_last_state validate_command = ${caddy-wrapper:output} -validate extra-context = key validate_command :validate_command key configuration_state_command frontend-caddy-configuration-state-validate:output key last_state_file :last_state_file [frontend-caddy-lazy-graceful] < = jinja2-template-base url = {{ software_parameter_dict['template_lazy_script_call'] }} output = ${directory:bin}/frontend-caddy-lazy-graceful pid-file = ${directory:run}/lazy-graceful.pid wait_time = 60 extra-context = key pid_file :pid-file key wait_time :wait_time key lazy_command caddy-configuration:frontend-graceful-command # Promises checking configuration: [promise-helper-last-configuration-state] < = jinja2-template-base url = {{ software_parameter_dict['template_empty'] }} output = ${directory:bin}/frontend-read-last-configuration-state content = #!/bin/sh exit `cat ${frontend-caddy-validate:last_state_file}` context = key content :content [promise-frontend-caddy-configuration] <= monitor-promise-base promise = validate_frontend_configuration name = frontend-caddy-configuration-promise.py config-verification-script = ${promise-helper-last-configuration-state:output} [promise-caddy-frontend-v4-https] <= monitor-promise-base promise = check_socket_listening name = caddy_frontend_ipv4_https.py config-host = {{ instance_parameter_dict['ipv4-random'] }} config-port = ${configuration:port} [promise-caddy-frontend-v4-http] <= monitor-promise-base promise = check_socket_listening name = caddy_frontend_ipv4_http.py config-host = {{ instance_parameter_dict['ipv4-random'] }} config-port = ${configuration:plain_http_port} [promise-caddy-frontend-v6-https] <= monitor-promise-base promise = check_socket_listening name = caddy_frontend_ipv6_https.py config-host = {{ instance_parameter_dict['ipv6-random'] }} config-port = ${configuration:port} [promise-caddy-frontend-v6-http] <= monitor-promise-base promise = check_socket_listening name = caddy_frontend_ipv6_http.py config-host = {{ instance_parameter_dict['ipv6-random'] }} config-port = ${configuration:plain_http_port} [promise-backend-haproxy-http] <= monitor-promise-base promise = check_socket_listening name = backend_haproxy_http.py config-host = {{ instance_parameter_dict['ipv4-random'] }} config-port = ${backend-haproxy-configuration:http-port} [promise-backend-haproxy-https] <= monitor-promise-base promise = check_socket_listening name = backend_haproxy_https.py config-host = {{ instance_parameter_dict['ipv4-random'] }} config-port = ${backend-haproxy-configuration:https-port} [backend-haproxy-configuration] file = ${directory:etc}/backend-haproxy.cfg pid-file = ${directory:run}/backend-haproxy.pid log-socket = ${backend-haproxy-rsyslogd-config:log-socket} graceful-command = ${backend-haproxy-validate:output} && kill -USR2 $(cat ${:pid-file}) http-port = ${configuration:backend-haproxy-http-port} https-port = ${configuration:backend-haproxy-https-port} # Caucase related configuration caucase-url = {{ slapparameter_dict['backend-client-caucase-url'] }} ca-certificate = ${backend-client-login-config:ca-certificate} certificate = ${backend-client-login-config:certificate} cas-ca-certificate = ${backend-client-login-config:cas-ca-certificate} csr = ${backend-client-caucase-updater-csr:csr} crl = ${backend-client-login-config:crl} # the statistic page statistic-certificate = ${self-signed-ip-access:certificate} statistic-port = ${configuration:backend-haproxy-statistic-port} statistic-username = ${monitor-instance-parameter:username} statistic-password = ${monitor-htpasswd:passwd} statistic-identification = {{ instance_parameter_dict['configuration.frontend-name'] + ' @ ' + slapparameter_dict['cluster-identification'] }} statistic-frontend-secure_access = ${backend-haproxy-statistic-frontend:connection-secure_access} [backend-haproxy] recipe = slapos.cookbook:wrapper command-line = {{ software_parameter_dict['haproxy_executable'] }} -f ${backend-haproxy-configuration:file} wrapper-path = ${directory:service}/backend-haproxy hash-existing-files = ${buildout:directory}/software_release/buildout.cfg [backend-haproxy-rsyslogd-lazy-graceful] < = jinja2-template-base url = {{ software_parameter_dict['template_lazy_script_call'] }} output = ${directory:bin}/backend-haproxy-rsyslogd-lazy-graceful pid-file = ${directory:run}/backend-haproxy-rsyslogd-lazy-graceful.pid wait_time = 60 extra-context = key pid_file :pid-file key wait_time :wait_time key lazy_command backend-haproxy-rsyslogd-config:graceful-command [logrotate-entry-backend-haproxy] <= logrotate-entry-base name = backend-haproxy log = ${backend-haproxy-rsyslogd-config:log-file} rotate-num = ${configuration:rotate-num} # Note: Slaves do not define their own reload, as this would be repeated, # because sharedscripts work per entry, and each slave needs its own # olddir # Here we trust that there will be something to be rotated with error # or access log, and that this will trigger postrotate script. post = ${backend-haproxy-rsyslogd-lazy-graceful:output} & delaycompress = [backend-haproxy-configuration-state] <= jinja2-template-base url = {{ software_parameter_dict['template_configuration_state_script'] }} output = ${directory:bin}/${:_buildout_section_name_} path_list = ${backend-haproxy-configuration:file} ${backend-client-login-config:certificate} sha256sum = {{ software_parameter_dict['sha256sum'] }} extra-context = key path_list :path_list key sha256sum :sha256sum key signature_file :signature_file [backend-haproxy-configuration-state-graceful] <= backend-haproxy-configuration-state signature_file = ${directory:run}/backend_haproxy_graceful_configuration_state_signature [backend-haproxy-configuration-state-validate] <= backend-haproxy-configuration-state signature_file = ${directory:run}/backend_haproxy_validate_configuration_state_signature [backend-haproxy-graceful] < = jinja2-template-base url = {{ software_parameter_dict['template_graceful_script'] }} output = ${directory:etc-run}/backend-haproxy-safe-graceful extra-context = key graceful_reload_command backend-haproxy-configuration:graceful-command key configuration_state backend-haproxy-configuration-state-graceful:output [backend-haproxy-validate] <= jinja2-template-base url = {{ software_parameter_dict['template_validate_script'] }} output = ${directory:bin}/backend-haproxy-validate last_state_file = ${directory:run}/backend_haproxy_configuration_last_state validate_command = {{ software_parameter_dict['haproxy_executable'] }} -f ${backend-haproxy-configuration:file} -c extra-context = key validate_command :validate_command key configuration_state_command backend-haproxy-configuration-state-validate:output key last_state_file :last_state_file [promise-backend-haproxy-configuration] <= monitor-promise-base promise = validate_frontend_configuration name = backend-haproxy-configuration.py config-verification-script = ${promise-backend-haproxy-configuration-helper:output} [promise-backend-haproxy-configuration-helper] < = jinja2-template-base url = {{ software_parameter_dict['template_empty'] }} output = ${directory:bin}/backend-haproxy-read-last-configuration-state content = #!/bin/sh exit `cat ${backend-haproxy-validate:last_state_file}` context = key content :content [backend-haproxy-rsyslogd-config] log-socket = ${directory:run}/bhlog.sck log-file = ${directory:log}/backend-haproxy.log pid-file = ${directory:run}/backend-haproxy-rsyslogd.pid spool-directory = ${directory:backend-haproxy-rsyslogd-spool} graceful-command = kill -HUP $(cat ${:pid-file}) log-directory = ${caddy-directory:slave-log} [backend-haproxy-rsyslogd-configuration] <= jinja2-template-base url = ${software-release-path:template-backend-haproxy-rsyslogd-conf} output = ${directory:etc}/backend-haproxy-rsyslogd.conf extra-context = section configuration backend-haproxy-rsyslogd-config [backend-haproxy-rsyslogd] recipe = slapos.cookbook:wrapper command-line = {{ software_parameter_dict['rsyslogd_executable'] }} -i ${backend-haproxy-rsyslogd-config:pid-file} -n -f ${backend-haproxy-rsyslogd-configuration:output} wrapper-path = ${directory:service}/backend-haproxy-rsyslogd hash-existing-files = ${buildout:directory}/software_release/buildout.cfg ####### # Monitoring sections # [monitor-instance-parameter] # Note: Workaround for monitor stack, which uses monitor-httpd-port parameter # directly, and in our case it can come from the network, thus resulting # with need to strip !py!'u' monitor-httpd-port = {{ instance_parameter_dict['configuration.monitor-httpd-port'] | int }} password = {{ instance_parameter_dict['configuration.monitor-password'] | string }} [monitor-conf-parameters] private-path-list += ${logrotate-directory:logrotate-backup} [monitor-traffic-summary-last-stats-wrapper] < = jinja2-template-base url = {{ software_parameter_dict['template_wrapper'] }} output = ${directory:bin}/traffic-summary-last-stats_every_1_hour command = export TS_ROOT=${buildout:directory} && echo "<pre>$({{ software_parameter_dict['trafficserver'] }}/bin/traffic_logstats -f ${trafficserver-directory:log}/squid.blog)</pre>" extra-context = key content monitor-traffic-summary-last-stats-wrapper:command # Produce ATS Cache stats [monitor-ats-cache-stats-wrapper] < = jinja2-template-base url = {{ software_parameter_dict['template_wrapper'] }} output = ${directory:bin}/ats-cache-stats_every_1_hour command = export TS_ROOT=${buildout:directory} && echo "<pre>$({{ software_parameter_dict['trafficserver'] }}/bin/traffic_shell ${monitor-ats-cache-stats-config:output})</pre>" extra-context = key content monitor-ats-cache-stats-wrapper:command [monitor-caddy-server-status-wrapper] < = jinja2-template-base url = {{ software_parameter_dict['template_wrapper'] }} output = ${directory:bin}/monitor-caddy-server-status-wrapper command = {{ software_parameter_dict['curl'] }}/bin/curl -s http://{{ instance_parameter_dict['ipv4-random'] }}:${configuration:plain_http_port}/server-status -u ${monitor-instance-parameter:username}:${monitor-htpasswd:passwd} 2>&1 extra-context = key content monitor-caddy-server-status-wrapper:command [monitor-ats-cache-stats-config] < = jinja2-template-base url = {{ software_parameter_dict['template_empty'] }} output = ${trafficserver-configuration-directory:target}/cache-config.stats context = raw content show:cache-stats [monitor-verify-re6st-connectivity] <= monitor-promise-base promise = check_url_available name = re6st-connectivity.py config-url = ${configuration:re6st-verification-url} [port-redirection] <= jinja2-template-base inline = [{"srcPort": 80, "destPort": {{ '{{' }} http_port {{ '}}' }}}, {"srcPort": 443, "destPort": {{ '{{' }} https_port {{ '}}' }}}] output = ${buildout:directory}/.slapos-port-redirect extra-context = key http_port configuration:plain_http_port key https_port configuration:port [slave-introspection-frontend] <= slap-connection recipe = slapos.cookbook:requestoptional name = Slave Introspection Frontend {{ instance_parameter_dict['configuration.frontend-name'] }} software-url = http://git.erp5.org/gitweb/slapos.git/blob_plain/HEAD:/software/apache-frontend/software.cfg slave = true config-url = https://[${slap-configuration:ipv6-random}]:{{ instance_parameter_dict['configuration.slave-introspection-https-port'] }}/ config-https-only = true return = domain secure_access [backend-haproxy-statistic-frontend] <= slap-connection recipe = slapos.cookbook:requestoptional name = Backend Haproxy Statistic Frontend {{ instance_parameter_dict['configuration.frontend-name'] }} software-url = http://git.erp5.org/gitweb/slapos.git/blob_plain/HEAD:/software/apache-frontend/software.cfg slave = true config-url = https://[${slap-configuration:ipv6-random}]:{{ instance_parameter_dict['configuration.backend-haproxy-statistic-port'] }}/ config-https-only = true return = domain secure_access [backend-haproxy-statistic-frontend-promise] <= monitor-promise-base promise = check_url_available name = backend-haproxy-statistic-frontend.py config-http-code = 401 config-url = ${backend-haproxy-statistic-frontend:connection-secure_access} [slave-introspection-configuration-state] <= jinja2-template-base url = {{ software_parameter_dict['template_configuration_state_script'] }} output = ${directory:bin}/${:_buildout_section_name_} path_list = ${frontend-configuration:slave-introspection-configuration} ${frontend-configuration:ip-access-certificate} sha256sum = {{ software_parameter_dict['sha256sum'] }} extra-context = key path_list :path_list key sha256sum :sha256sum key signature_file :signature_file [slave-introspection-configuration-state-graceful] <= slave-introspection-configuration-state signature_file = ${directory:run}/slave_introspection_graceful_configuration_state_signature [slave-introspection-configuration-state-validate] <= slave-introspection-configuration-state signature_file = ${directory:run}/slave_introspection_validate_configuration_state_signature [slave-introspection-graceful] < = jinja2-template-base url = {{ software_parameter_dict['template_graceful_script'] }} output = ${directory:etc-run}/slave-introspection-safe-graceful extra-context = key graceful_reload_command caddy-configuration:slave-introspection-graceful-command key configuration_state slave-introspection-configuration-state-graceful:output [slave-introspection-validate] <= jinja2-template-base url = {{ software_parameter_dict['template_validate_script'] }} output = ${directory:bin}/slave-introspection-validate last_state_file = ${directory:run}/slave_introspection_configuration_last_state validate_command = {{ software_parameter_dict['nginx'] }} -c ${frontend-configuration:slave-introspection-configuration} -t extra-context = key validate_command :validate_command key configuration_state_command slave-introspection-configuration-state-validate:output key last_state_file :last_state_file [promise-slave-introspection-configuration] <= monitor-promise-base promise = validate_frontend_configuration name = slave-introspection-configuration.py config-verification-script = ${promise-slave-introspection-configuration-helper:output} [promise-slave-introspection-configuration-helper] < = jinja2-template-base url = {{ software_parameter_dict['template_empty'] }} output = ${directory:bin}/slave-introspection-read-last-configuration-state content = #!/bin/sh exit `cat ${slave-introspection-validate:last_state_file}` context = key content :content [promise-slave-introspection-https] <= monitor-promise-base promise = check_socket_listening name = slave_introspection_https.py config-host = {{ instance_parameter_dict['ipv6-random'] }} config-port = ${frontend-configuration:slave-introspection-https-port} [logrotate-entry-slave-introspection] <= logrotate-entry-base name = slave-introspection log = ${caddy-configuration:slave-introspection-access-log} ${caddy-configuration:slave-introspection-error-log} rotate-num = ${configuration:rotate-num} post = kill -USR1 $(cat ${caddy-configuration:slave-introspection-pid-file}) delaycompress = [promise-logrotate-setup] <= monitor-promise-base promise = check_command_execute name = ${:_buildout_section_name_}.py config-command = ${logrotate:wrapper-path} -d [configuration] {%- for key, value in instance_parameter_dict.items() -%} {%- if key.startswith('configuration.') %} {{ key.replace('configuration.', '') }} = {{ dumps(value) }} {%- endif -%} {%- endfor %} [instance-parameter-section] {#- There are dangerous keys like recipe, etc #} {#- XXX: Some other approach would be useful #} {%- set DROP_KEY_LIST = ['recipe', '__buildout_signature__', 'computer', 'partition', 'url', 'key', 'cert'] %} {%- for key, value in instance_parameter_dict.items() -%} {%- if not key.startswith('configuration.') and key not in DROP_KEY_LIST %} {{ key }} = {{ dumps(value) }} {%- endif -%} {%- endfor %} [software-parameter-section] {%- for key, value in software_parameter_dict.items() %} {{ key }} = {{ dumps(value) }} {%- endfor %}