From de0882bd7ed919099c7713ce93d19953887741ce Mon Sep 17 00:00:00 2001 From: RobHooper Date: Thu, 30 Jul 2026 10:50:53 +0100 Subject: [PATCH 01/22] refactor: remove ubuntu 20.04 --- salt/apache/init.sls | 15 -------------- salt/core/firewall/files/firewall_reset.sh | 4 ++-- salt/core/sshd/files/customization.conf | 5 +++++ salt/core/sshd/init.sls | 24 ---------------------- salt/core/systemd/ntp.sls | 5 ----- 5 files changed, 7 insertions(+), 46 deletions(-) diff --git a/salt/apache/init.sls b/salt/apache/init.sls index f084344cb..456e5ba55 100644 --- a/salt/apache/init.sls +++ b/salt/apache/init.sls @@ -8,25 +8,10 @@ {{ unset_firewall('PUBLIC_HTTPS') }} {% endif %} -# ondrej/apache2 is still needed on Ubuntu 20.04 for MDContactEmail. -# https://github.com/icing/mod_md/issues/203 apache2: - {% if grains.osmajorrelease in ('18', '20') %} - pkgrepo.managed: - - ppa: ondrej/apache2 - {% endif %} pkg.installed: - pkgs: - apache2 - {% if grains.osmajorrelease in ('18', '20') %} - # Avoid "AH01882: Init: this version of mod_ssl was compiled against a newer library (OpenSSL 1.1.1g 21 Apr 2020, - # version currently loaded is OpenSSL 1.1.1 11 Sep 2018) - may result in undefined or erroneous behavior" - # https://github.com/open-contracting/deploy/issues/66#issuecomment-742898193 - - libssl1.1 - - openssl - - require: - - pkgrepo: apache2 - {% endif %} service.running: - name: apache2 - enable: True diff --git a/salt/core/firewall/files/firewall_reset.sh b/salt/core/firewall/files/firewall_reset.sh index ca1cfb04e..d0fa6821b 100644 --- a/salt/core/firewall/files/firewall_reset.sh +++ b/salt/core/firewall/files/firewall_reset.sh @@ -34,11 +34,11 @@ fi echo_verbose "Get iptables location" case "${ID}_${VERSION_ID}" in -ubuntu_22.04 | ubuntu_20.04 | ubuntu_18.04 | debian_10 | debian_9 | debian_8) +ubuntu_26.04 | ubuntu_24.04 | ubuntu_22.04 | debian_13 | debian_12 ) IPTABLESSAVLOC=/etc/iptables/rules.v4 IP6TABLESSAVLOC=/etc/iptables/rules.v6 ;; -centos_7 | redhat-derivative_) +redhat-derivative_) IPTABLESSAVLOC=/etc/sysconfig/iptables IP6TABLESSAVLOC=/etc/sysconfig/ip6tables ;; diff --git a/salt/core/sshd/files/customization.conf b/salt/core/sshd/files/customization.conf index 526b487f5..015294214 100644 --- a/salt/core/sshd/files/customization.conf +++ b/salt/core/sshd/files/customization.conf @@ -1,3 +1,8 @@ +# We set both PermitRootLogin and PasswordAuthentication for two reasons: +# +# - PermitRootLogin adds a layer of security in case PasswordAuthentication is toggled on. +# - While PermitRootLogin is set to "no" or "without-password", we can monitor it with our intrusion detection software. + PasswordAuthentication no PermitRootLogin without-password X11Forwarding no diff --git a/salt/core/sshd/init.sls b/salt/core/sshd/init.sls index face34a33..3df1ccfc4 100644 --- a/salt/core/sshd/init.sls +++ b/salt/core/sshd/init.sls @@ -1,34 +1,10 @@ # We'll only be using SSH key authentication. -{% if grains['osrelease'] >= '20.04' %} /etc/ssh/sshd_config.d/customization.conf: file.managed: - source: salt://core/sshd/files/customization.conf - watch_in: - service: ssh_service -{% else %} -# We set both PermitRootLogin and PasswordAuthentication for two reasons: -# -# - PermitRootLogin adds a layer of security in case PasswordAuthentication is toggled on. -# - While PermitRootLogin is set to "no" or "without-password", we can monitor it with our intrusion detection software. -harden ssh configuration: - file.keyvalue: - - name: /etc/ssh/sshd_config - - key_values: - # Disable password authentication. - PasswordAuthentication: 'no' - # Force root logins with SSH keys. - PermitRootLogin: without-password - # Disable X11 forwarding. - X11Forwarding: 'no' - - separator: ' ' - - uncomment: '# ' - - key_ignore_case: True - - append_if_not_found: True - - watch_in: - - service: ssh_service -{% endif %} - # Restart the SSH service if the config changes. ssh_service: service.running: diff --git a/salt/core/systemd/ntp.sls b/salt/core/systemd/ntp.sls index 8b4ab9c78..00c968f36 100644 --- a/salt/core/systemd/ntp.sls +++ b/salt/core/systemd/ntp.sls @@ -1,17 +1,12 @@ # Configure an SNTP service. systemd-timesyncd: - {% if grains['osrelease'] >= '20.04' %} - # timesyncd is built into systemd on older Ubuntu releases. pkg.installed: - name: systemd-timesyncd - {% endif %} service.running: - name: systemd-timesyncd - enable: True - {% if grains['osrelease'] >= '20.04' %} - require: - pkg: systemd-timesyncd - {% endif %} /etc/systemd/timesyncd.conf.d/customization.conf: file.managed: From 3ea5d03f0abdd67f38358e12fb1bf73c8797c818 Mon Sep 17 00:00:00 2001 From: RobHooper Date: Mon, 3 Aug 2026 15:36:48 +0100 Subject: [PATCH 02/22] feat: Update default MySQL version --- salt/mysql/init.sls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/mysql/init.sls b/salt/mysql/init.sls index 9af6de975..d58511e8f 100644 --- a/salt/mysql/init.sls +++ b/salt/mysql/init.sls @@ -1,4 +1,4 @@ -{% set mysql_version = pillar.mysql.version|default('8.0')|quote %} +{% set mysql_version = pillar.mysql.version|default('8.4')|quote %} # https://docs.saltproject.io/en/latest/ref/states/all/salt.states.mysql_database.html mysql dependencies: From 750d09b651842feee060e668ed996305fa0a744d Mon Sep 17 00:00:00 2001 From: RobHooper Date: Mon, 3 Aug 2026 15:43:17 +0100 Subject: [PATCH 03/22] feat: Update default PHP version --- salt/php-fpm/init.sls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/php-fpm/init.sls b/salt/php-fpm/init.sls index 8acfe17f8..c685ae318 100644 --- a/salt/php-fpm/init.sls +++ b/salt/php-fpm/init.sls @@ -1,4 +1,4 @@ -{% set php_version = pillar.php.version|default('8.1')|quote %} +{% set php_version = pillar.php.version|default('8.5')|quote %} include: - apache.modules.proxy_fcgi From 002aaa0da2630f5dc8bbc246a84aa63a9978dfa5 Mon Sep 17 00:00:00 2001 From: RobHooper Date: Mon, 3 Aug 2026 15:47:08 +0100 Subject: [PATCH 04/22] refactor: remove legacy apt repo configuration --- salt/docker/init.sls | 4 ---- salt/postgres/init.sls | 4 ---- 2 files changed, 8 deletions(-) diff --git a/salt/docker/init.sls b/salt/docker/init.sls index f67bc0757..7e9cb9bb6 100644 --- a/salt/docker/init.sls +++ b/salt/docker/init.sls @@ -4,12 +4,8 @@ docker: pkgrepo.managed: - humanname: Docker Official Repository - {% if grains.osmajorrelease|string in ('18', '20') %} - - name: deb [arch={{ grains.osarch }}] https://download.docker.com/{{ grains.kernel|lower }}/{{ grains.os|lower }} {{ grains.oscodename }} stable - {% else %} - name: deb [arch={{ grains.osarch }} signed-by=/usr/share/keyrings/docker-keyring.gpg] https://download.docker.com/{{ grains.kernel|lower }}/{{ grains.os|lower }} {{ grains.oscodename }} stable - aptkey: False - {% endif %} - dist: {{ grains.oscodename }} - file: /etc/apt/sources.list.d/docker.list - key_url: https://download.docker.com/{{ grains.kernel|lower }}/{{ grains.os|lower }}/gpg diff --git a/salt/postgres/init.sls b/salt/postgres/init.sls index 28274cecd..4b92e130f 100644 --- a/salt/postgres/init.sls +++ b/salt/postgres/init.sls @@ -79,12 +79,8 @@ pgbadger: postgresql: pkgrepo.managed: - humanname: PostgreSQL Official Repository - {% if grains.osmajorrelease|string in ('18', '20') %} - - name: deb https://apt.postgresql.org/pub/repos/apt {{ grains.oscodename }}-pgdg main - {% else %} - name: deb [signed-by=/usr/share/keyrings/postgresql-keyring.gpg] https://apt.postgresql.org/pub/repos/apt {{ grains.oscodename }}-pgdg main - aptkey: False - {% endif %} - dist: {{ grains.oscodename }}-pgdg - file: /etc/apt/sources.list.d/psql.list - key_url: https://www.postgresql.org/media/keys/ACCC4CF8.asc From 36ee7b82b9671e07a258eddf55bdcf5747bee344 Mon Sep 17 00:00:00 2001 From: RobHooper Date: Tue, 4 Aug 2026 10:43:00 +0100 Subject: [PATCH 05/22] chore: fix linting --- salt/core/firewall/files/firewall_reset.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/core/firewall/files/firewall_reset.sh b/salt/core/firewall/files/firewall_reset.sh index d0fa6821b..a3e56bdcb 100644 --- a/salt/core/firewall/files/firewall_reset.sh +++ b/salt/core/firewall/files/firewall_reset.sh @@ -34,7 +34,7 @@ fi echo_verbose "Get iptables location" case "${ID}_${VERSION_ID}" in -ubuntu_26.04 | ubuntu_24.04 | ubuntu_22.04 | debian_13 | debian_12 ) +ubuntu_26.04 | ubuntu_24.04 | ubuntu_22.04 | debian_13 | debian_12) IPTABLESSAVLOC=/etc/iptables/rules.v4 IP6TABLESSAVLOC=/etc/iptables/rules.v6 ;; From 044886dcd5df605eb4039f663035c03882e80df8 Mon Sep 17 00:00:00 2001 From: RobHooper Date: Tue, 4 Aug 2026 14:06:08 +0100 Subject: [PATCH 06/22] docs: New Linode interface --- docs/deploy/create_server.rst | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/docs/deploy/create_server.rst b/docs/deploy/create_server.rst index e97d26cfa..dfe3ad2a7 100644 --- a/docs/deploy/create_server.rst +++ b/docs/deploy/create_server.rst @@ -34,6 +34,10 @@ Create the server via the :ref:`host`'s interface. #. Set *Linode Label* to the server's FQDN (e.g. ``ocp99.open-contracting.org``) #. Set *Add Tags* to either *Production* or *Development* #. Set *Root Password* to a `strong password `__, and save it to OCP's `LastPass `__ account + #. Set *Public Interface Firewall* to *No firewall*, unless the server uses Docker. + + #. If using Docker, select *Create Firewall* and :ref:`configure an external firewall starting at step 3`. + #. Check *Backups* #. Click *Create Linode* and wait a few minutes for the server to power on @@ -58,11 +62,11 @@ Create the server via the :ref:`host`'s interface. #. Rename the "Swap Image" disk to "### MB Swap Image" - #. On the *Configurations* tab: + #. On the *Network* tab: - #. Click *Edit* for the "My Ubuntu ##.04 LTS Disk Profile" (or similar) configuration - #. Uncheck *Auto-configure networking* (skip if configuring a non-OCP server) - #. Click *Save Changes* + #. Click *Interface Settings* + #. Uncheck *Enable Network Helper* (skip if configuring a non-OCP server) + #. Click *Save* #. Click *Power On* #. Copy *SSH Access* to your clipboard @@ -79,8 +83,6 @@ Create the server via the :ref:`host`'s interface. Linode can take a day to close the ticket. In the meantime, proceed with the instructions below. Once the ticket is closed, assign a specific address within the /64 block in the :doc:`network configuration<../develop/update/network>`. - #. If using Docker, :ref:`configure an external firewall`. - .. tab-item:: Hetzner Cloud :sync: hetzner-cloud From 79c2b97914216a4ad8e0bcb46ea59ba28771cfe9 Mon Sep 17 00:00:00 2001 From: RobHooper Date: Tue, 4 Aug 2026 16:35:11 +0100 Subject: [PATCH 07/22] feat: update locale on new servers --- pillar/common.sls | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pillar/common.sls b/pillar/common.sls index 4f138a2aa..4713950ac 100644 --- a/pillar/common.sls +++ b/pillar/common.sls @@ -23,7 +23,11 @@ ssh: - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDG8dhMVvgH/tt9+VoyokyUg/iKVcZKMku8pYN6o8RoT8XKoyP/iyrUIl5HxolqIt+PJTpomYkA40eJ/0mN4/kRhr+tctZ+tUdo8/G8H42FG3McklL6XlwOdXRGIYC+NynF8YGws57J8YkM2oL9linkUZYpGpVkNew2aEg916HWWfGZktwuQa7knIwIhFr9FlvxxaZhdcQ7VJjnJOP0fLLr5WCVaiWDGjQ5cHJURcTBL+j+eTRpKFvk9BMKCAQyLkSEluT0QeESDMtR7sRHA54to1LDXRX0ky9cAQ6mxXWgpSpmHCuPVYpzOfoSd7b8aczDLUGBxq9EWOTS3UMUWJBX Yohanna (OCP) # The default locale is en_GB rather than en_US for accidental, historical reasons. +{% if grains.osmajorrelease|int >= 26 %} +locale: en_US +{% else %} locale: en_GB +{% endif %} ntp: - 0.uk.pool.ntp.org From 9f39f95a6b1803eabc76f01cad798589a98569ce Mon Sep 17 00:00:00 2001 From: RobHooper Date: Tue, 4 Aug 2026 16:36:45 +0100 Subject: [PATCH 08/22] refactor(firewall.sh): remove ubuntu 20.04 --- salt/core/firewall/files/firewall.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/salt/core/firewall/files/firewall.sh b/salt/core/firewall/files/firewall.sh index 0ba2db5ad..0fb836ded 100644 --- a/salt/core/firewall/files/firewall.sh +++ b/salt/core/firewall/files/firewall.sh @@ -61,11 +61,11 @@ fi echo_verbose "Get iptables location" case "${ID}_${VERSION_ID}" in -ubuntu_24.04 | ubuntu_22.04 | ubuntu_20.04 | ubuntu_18.04 | debian_10 | debian_9 | debian_8) +ubuntu_26.04 | ubuntu_24.04 | ubuntu_22.04 | debian_13 | debian_12) IPTABLESSAVLOC=/etc/iptables/rules.v4 IP6TABLESSAVLOC=/etc/iptables/rules.v6 ;; -centos_7 | redhat-derivative_) +redhat-derivative_) IPTABLESSAVLOC=/etc/sysconfig/iptables IP6TABLESSAVLOC=/etc/sysconfig/ip6tables ;; From 7942ba1ed7de848f07ec61fed17273db0d38dad1 Mon Sep 17 00:00:00 2001 From: RobHooper Date: Tue, 4 Aug 2026 16:39:44 +0100 Subject: [PATCH 09/22] feat: add chrony support for Ubuntu 26+ --- salt/core/ntp/files/ntp-pools.sources | 3 +++ .../{systemd => ntp}/files/timesyncd.conf | 0 salt/core/{systemd/ntp.sls => ntp/init.sls} | 27 ++++++++++++++++++- salt/top.sls | 2 +- 4 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 salt/core/ntp/files/ntp-pools.sources rename salt/core/{systemd => ntp}/files/timesyncd.conf (100%) rename salt/core/{systemd/ntp.sls => ntp/init.sls} (50%) diff --git a/salt/core/ntp/files/ntp-pools.sources b/salt/core/ntp/files/ntp-pools.sources new file mode 100644 index 000000000..661d6b40c --- /dev/null +++ b/salt/core/ntp/files/ntp-pools.sources @@ -0,0 +1,3 @@ +{% for source in pillar.ntp %} +pool {{ source }} iburst maxsources 4 +{% endfor %} diff --git a/salt/core/systemd/files/timesyncd.conf b/salt/core/ntp/files/timesyncd.conf similarity index 100% rename from salt/core/systemd/files/timesyncd.conf rename to salt/core/ntp/files/timesyncd.conf diff --git a/salt/core/systemd/ntp.sls b/salt/core/ntp/init.sls similarity index 50% rename from salt/core/systemd/ntp.sls rename to salt/core/ntp/init.sls index 00c968f36..969cdd832 100644 --- a/salt/core/systemd/ntp.sls +++ b/salt/core/ntp/init.sls @@ -1,4 +1,28 @@ # Configure an SNTP service. +{% if grains.osmajorrelease|int >= 26 %} +chrony: + service.running: + - name: chrony + +chrony-reload: + cmd.wait: + - name: chronyc reload sources + +/etc/chrony/sources.d/ntp-pools.sources: + file.managed: + - source: salt://core/ntp/files/ntp-pools.sources + - template: jinja + - watch_in: + - cmd: chrony-reload + +/etc/chrony/sources.d/ubuntu-ntp-pools.sources: + file.comment: + - regex: "^pool " + - backup: False + - watch_in: + - cmd: chrony-reload + +{% else %} systemd-timesyncd: pkg.installed: - name: systemd-timesyncd @@ -10,7 +34,7 @@ systemd-timesyncd: /etc/systemd/timesyncd.conf.d/customization.conf: file.managed: - - source: salt://core/systemd/files/timesyncd.conf + - source: salt://core/ntp/files/timesyncd.conf - template: jinja - makedirs: True - watch_in: @@ -22,6 +46,7 @@ systemd-timesyncd: - backup: False - watch_in: - service: systemd-timesyncd +{% endif %} set timezone to utc: timezone.system: diff --git a/salt/top.sls b/salt/top.sls index bcd1f4248..a9a1b57a7 100644 --- a/salt/top.sls +++ b/salt/top.sls @@ -13,13 +13,13 @@ base: - core.mail - core.motd - core.network + - core.ntp - core.reboot - core.rsyslog - core.sshd - core.swap - core.sysctl - core.systemd.logind - - core.systemd.ntp 'cms': - cms From 764fae617dd930a75003dcb05f3e50e0318179a5 Mon Sep 17 00:00:00 2001 From: RobHooper Date: Tue, 4 Aug 2026 17:14:20 +0100 Subject: [PATCH 10/22] chore: remove legacy comment --- salt/core/apt/init.sls | 1 - 1 file changed, 1 deletion(-) diff --git a/salt/core/apt/init.sls b/salt/core/apt/init.sls index fd45dd15b..dd7362906 100644 --- a/salt/core/apt/init.sls +++ b/salt/core/apt/init.sls @@ -12,7 +12,6 @@ needrestart: pkg.removed: - name: needrestart -# https://www.phusionpassenger.com/library/install/apache/install/oss/bionic/ # gnupg depends on dirmngr. gnupg2 is a dummy package for gnupg. secure ppa: pkg.installed: From d02ec59edb0bf16e00aa3a04cdeb438f2d028bbb Mon Sep 17 00:00:00 2001 From: RobHooper Date: Thu, 6 Aug 2026 11:09:52 +0100 Subject: [PATCH 11/22] feat: depreciate smtp_use_tls --- salt/core/mail.sls | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/salt/core/mail.sls b/salt/core/mail.sls index 36ff126e8..a71d24382 100644 --- a/salt/core/mail.sls +++ b/salt/core/mail.sls @@ -33,7 +33,11 @@ postfix: smtp_sasl_auth_enable: "yes" smtp_sasl_security_options: "noanonymous" smtp_sasl_password_maps: "hash:/etc/postfix/sasl_passwd" +{% if grains.osmajorrelease|int >= 26 %} + # Depreciated in Postfix 3.9, instead specify 'smtp_tls_security_level' smtp_use_tls: "yes" +{% endif %} + smtp_tls_security_level: "may" smtp_tls_note_starttls_offer: "yes" {%- if "relay_address" in pillar.smtp %} smtp_generic_maps: "hash:/etc/postfix/generic" From 2e137aa0317569953e5c7f203d2f583bc45a76f6 Mon Sep 17 00:00:00 2001 From: RobHooper Date: Thu, 6 Aug 2026 15:24:52 +0100 Subject: [PATCH 12/22] feat: set MDStoreDir --- salt/apache/files/conf/letsencrypt.conf | 3 +++ 1 file changed, 3 insertions(+) diff --git a/salt/apache/files/conf/letsencrypt.conf b/salt/apache/files/conf/letsencrypt.conf index eff23c2b3..f0ac9a431 100644 --- a/salt/apache/files/conf/letsencrypt.conf +++ b/salt/apache/files/conf/letsencrypt.conf @@ -2,6 +2,9 @@ MDContactEmail sysadmin@open-contracting.org MDCertificateAgreement accepted MDCAChallenges http-01 +{% if grains.osmajorrelease|int >= 26 %} + MDStoreDir /var/lib/apache2/md +{% endif %} {%- for directive, value in salt['pillar.get']('apache:modules:mod_md', {})|items %} {{ directive }} {{ value }} {%- endfor %} From 5a6c77d8d503520f145cd1c668ba79f3de856f5d Mon Sep 17 00:00:00 2001 From: RobHooper Date: Thu, 6 Aug 2026 15:47:35 +0100 Subject: [PATCH 13/22] feat: Update SSL cipher suites --- salt/apache/files/conf/letsencrypt.conf | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/salt/apache/files/conf/letsencrypt.conf b/salt/apache/files/conf/letsencrypt.conf index f0ac9a431..68e696137 100644 --- a/salt/apache/files/conf/letsencrypt.conf +++ b/salt/apache/files/conf/letsencrypt.conf @@ -10,12 +10,22 @@ {%- endfor %} -# generated 2023-06-28, Mozilla Guideline v5.7, Apache 2.4.52, OpenSSL 3.0.2, intermediate configuration -# https://ssl-config.mozilla.org/#server=apache&version=2.4.52&config=intermediate&openssl=3.0.2&guideline=5.7 -# Omitted DHE-RSA-CHACHA20-POLY1305 (lowest priority). - SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1 - SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 +{%- if grains.osmajorrelease|int >= 26 %} + # generated 2026-08-06, TLSRef Guideline v6.0, Apache 2.4.66, OpenSSL 3.5.5, intermediate config, gitrev=1b22dc6 + # https://configurator.tlsref.org/#server=apache&version=2.4.66&config=intermediate&openssl=3.5.5&guideline=6.0 + SSLProtocol -all +TLSv1.2 +TLSv1.3 + SSLOpenSSLConfCmd Curves X25519MLKEM768:X25519:prime256v1:secp384r1 + SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305 + SSLCipherSuite TLSv1.3 TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256 +{%- else %} + # generated 2026-08-06, TLSRef Guideline v6.0, Apache 2.4.52, OpenSSL 3.0.2 (OLD: missing PQC hybrid MLKEMs), intermediate config, gitrev=1b22dc6 + # https://configurator.tlsref.org/#server=apache&version=2.4.52&config=intermediate&openssl=3.0.2&guideline=6.0 + SSLProtocol -all +TLSv1.2 +TLSv1.3 + SSLOpenSSLConfCmd Curves X25519:prime256v1:secp384r1 + SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305 + SSLCipherSuite TLSv1.3 TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256 +{%- endif %} SSLHonorCipherOrder off SSLSessionTickets off From 6efe6961ff50136a973d98d1c55155ef9559c8bf Mon Sep 17 00:00:00 2001 From: RobHooper Date: Thu, 6 Aug 2026 15:53:59 +0100 Subject: [PATCH 14/22] feat: add fail2ban requirements --- salt/core/fail2ban/init.sls | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/salt/core/fail2ban/init.sls b/salt/core/fail2ban/init.sls index ce4e06c84..01752ce2a 100644 --- a/salt/core/fail2ban/init.sls +++ b/salt/core/fail2ban/init.sls @@ -23,6 +23,8 @@ fail2ban: [Definition] failregex = ^ .* ".*" 404 ignoreregex = + - require: + - pkg: apache2 - require_in: - file: /etc/fail2ban/jail.local - watch_in: @@ -37,6 +39,8 @@ fail2ban: [Definition] failregex = ^ .* FATAL: password authentication failed for user ".*"$ ignoreregex = + - require: + - pkg: postgresql - require_in: - file: /etc/fail2ban/jail.local - watch_in: From 13c19e0586a1be72b24ece766128ba409705ec58 Mon Sep 17 00:00:00 2001 From: RobHooper Date: Thu, 6 Aug 2026 15:54:22 +0100 Subject: [PATCH 15/22] feat: Support new Percona install name format --- salt/mysql/init.sls | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/salt/mysql/init.sls b/salt/mysql/init.sls index d58511e8f..35156a67b 100644 --- a/salt/mysql/init.sls +++ b/salt/mysql/init.sls @@ -13,8 +13,14 @@ percona-release: - sources: - percona-release: https://repo.percona.com/apt/percona-release_latest.{{ salt['grains.get']('lsb_distrib_codename') }}_all.deb cmd.run: +{% if pillar.mysql.version == '8.0' %} + # Legacy naming format - name: percona-release setup ps{{ mysql_version|replace('.', '') }} - creates: /etc/apt/sources.list.d/percona-ps-{{ mysql_version|replace('.', '') }}-release.list +{% else %} + - name: percona-release setup ps{{ mysql_version|replace('.', '') }}-lts --scheme https + - creates: /etc/apt/sources.list.d/percona-ps-{{ mysql_version|replace('.', '') }}-lts-release.list +{% endif %} - require: - pkg: percona-release From 3691326ac45f6417e3d2714b2c2a9e77cefe0806 Mon Sep 17 00:00:00 2001 From: RobHooper Date: Thu, 6 Aug 2026 16:03:57 +0100 Subject: [PATCH 16/22] docs: note MDStoreDir change --- salt/apache/files/conf/letsencrypt.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/salt/apache/files/conf/letsencrypt.conf b/salt/apache/files/conf/letsencrypt.conf index 68e696137..b0b31e68d 100644 --- a/salt/apache/files/conf/letsencrypt.conf +++ b/salt/apache/files/conf/letsencrypt.conf @@ -3,6 +3,7 @@ MDCertificateAgreement accepted MDCAChallenges http-01 {% if grains.osmajorrelease|int >= 26 %} + # Store mod_md data outside /etc due to systemd filesystem sandboxing. MDStoreDir /var/lib/apache2/md {% endif %} {%- for directive, value in salt['pillar.get']('apache:modules:mod_md', {})|items %} From 9dde2544b57339384e361b3163c9cc5f13b5c6dc Mon Sep 17 00:00:00 2001 From: RobHooper Date: Thu, 6 Aug 2026 17:22:11 +0100 Subject: [PATCH 17/22] fix: handle non-zero exit from firewall.sh --- salt/core/firewall/init.sls | 3 +++ 1 file changed, 3 insertions(+) diff --git a/salt/core/firewall/init.sls b/salt/core/firewall/init.sls index 1b9aeab28..c3b77052d 100644 --- a/salt/core/firewall/init.sls +++ b/salt/core/firewall/init.sls @@ -38,6 +38,9 @@ save iptables rules: - onchanges: - file: /home/sysadmin-tools/firewall-settings.local - file: /home/sysadmin-tools/bin/firewall.sh + - success_retcodes: + - 0 + - 3 # Docker install detected, exiting early. /home/sysadmin-tools/bin/firewall_reset.sh: file.managed: From 9f2361f1fa3eff0ac379eb827110c14f2c3829c7 Mon Sep 17 00:00:00 2001 From: RobHooper Date: Fri, 7 Aug 2026 10:39:21 +0100 Subject: [PATCH 18/22] feat: Implement NTS time syncronisation --- pillar/common.sls | 11 +++++++++++ salt/core/ntp/files/ntp-pools.sources | 6 +++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/pillar/common.sls b/pillar/common.sls index 4713950ac..1438f3cdc 100644 --- a/pillar/common.sls +++ b/pillar/common.sls @@ -35,6 +35,17 @@ ntp: - 2.uk.pool.ntp.org - 3.uk.pool.ntp.org +# NTS used over NTP on Ubuntu 26.04+ +nts: + time.cloudflare.com: + 1.ntp.ubuntu.com: + 2.ntp.ubuntu.com: + 3.ntp.ubuntu.com: + 4.ntp.ubuntu.com: + ntp-bootstrap.ubuntu.com: + context: iburst maxsources 1 nts certset 1 + + smtp: relay: True relay_address: noreply@noreply.open-contracting.org diff --git a/salt/core/ntp/files/ntp-pools.sources b/salt/core/ntp/files/ntp-pools.sources index 661d6b40c..23763d513 100644 --- a/salt/core/ntp/files/ntp-pools.sources +++ b/salt/core/ntp/files/ntp-pools.sources @@ -1,3 +1,3 @@ -{% for source in pillar.ntp %} -pool {{ source }} iburst maxsources 4 -{% endfor %} +{%- for source, entry in pillar.nts | items %} +pool {{ source }} {{ entry.context | default("iburst maxsources 1 nts prefer") }} +{%- endfor %} From 82ee86da83326135681f07b9f96f94f954c6f0c5 Mon Sep 17 00:00:00 2001 From: "Bob H." <57331626+RobHooper@users.noreply.github.com> Date: Fri, 7 Aug 2026 15:12:25 +0100 Subject: [PATCH 19/22] Apply suggestions from code review Co-authored-by: James McKinney <26463+jpmckinney@users.noreply.github.com> --- salt/apache/files/conf/letsencrypt.conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/salt/apache/files/conf/letsencrypt.conf b/salt/apache/files/conf/letsencrypt.conf index b0b31e68d..a6077c3f9 100644 --- a/salt/apache/files/conf/letsencrypt.conf +++ b/salt/apache/files/conf/letsencrypt.conf @@ -2,10 +2,10 @@ MDContactEmail sysadmin@open-contracting.org MDCertificateAgreement accepted MDCAChallenges http-01 -{% if grains.osmajorrelease|int >= 26 %} +{%- if grains.osmajorrelease|int >= 26 %} # Store mod_md data outside /etc due to systemd filesystem sandboxing. MDStoreDir /var/lib/apache2/md -{% endif %} +{%- endif %} {%- for directive, value in salt['pillar.get']('apache:modules:mod_md', {})|items %} {{ directive }} {{ value }} {%- endfor %} From 5346b2a0e6e075162b2433ddaf57c0e5762a6285 Mon Sep 17 00:00:00 2001 From: RobHooper Date: Fri, 7 Aug 2026 15:25:42 +0100 Subject: [PATCH 20/22] Apply suggestions from code review --- docs/deploy/create_server.rst | 5 +---- pillar/common.sls | 1 - salt/apache/files/conf/letsencrypt.conf | 2 +- salt/core/apt/init.sls | 1 - salt/core/firewall/init.sls | 6 +++--- salt/core/mail.sls | 2 +- salt/core/ntp/init.sls | 1 - salt/core/sshd/files/customization.conf | 10 +++++----- salt/core/sshd/init.sls | 1 + 9 files changed, 12 insertions(+), 17 deletions(-) diff --git a/docs/deploy/create_server.rst b/docs/deploy/create_server.rst index dfe3ad2a7..99011f3b4 100644 --- a/docs/deploy/create_server.rst +++ b/docs/deploy/create_server.rst @@ -34,10 +34,7 @@ Create the server via the :ref:`host`'s interface. #. Set *Linode Label* to the server's FQDN (e.g. ``ocp99.open-contracting.org``) #. Set *Add Tags* to either *Production* or *Development* #. Set *Root Password* to a `strong password `__, and save it to OCP's `LastPass `__ account - #. Set *Public Interface Firewall* to *No firewall*, unless the server uses Docker. - - #. If using Docker, select *Create Firewall* and :ref:`configure an external firewall starting at step 3`. - + #. If using Docker, under *Public Interface Firewall* select *Create Firewall* and :ref:`configure an external firewall starting at step 3`. Otherwise, set to *No firewall*. #. Check *Backups* #. Click *Create Linode* and wait a few minutes for the server to power on diff --git a/pillar/common.sls b/pillar/common.sls index 1438f3cdc..d973a680d 100644 --- a/pillar/common.sls +++ b/pillar/common.sls @@ -45,7 +45,6 @@ nts: ntp-bootstrap.ubuntu.com: context: iburst maxsources 1 nts certset 1 - smtp: relay: True relay_address: noreply@noreply.open-contracting.org diff --git a/salt/apache/files/conf/letsencrypt.conf b/salt/apache/files/conf/letsencrypt.conf index a6077c3f9..26488364a 100644 --- a/salt/apache/files/conf/letsencrypt.conf +++ b/salt/apache/files/conf/letsencrypt.conf @@ -3,7 +3,7 @@ MDCertificateAgreement accepted MDCAChallenges http-01 {%- if grains.osmajorrelease|int >= 26 %} - # Store mod_md data outside /etc due to systemd filesystem sandboxing. + {#- Store mod_md data outside /etc due to systemd filesystem sandboxing. #} MDStoreDir /var/lib/apache2/md {%- endif %} {%- for directive, value in salt['pillar.get']('apache:modules:mod_md', {})|items %} diff --git a/salt/core/apt/init.sls b/salt/core/apt/init.sls index dd7362906..be3d9d35b 100644 --- a/salt/core/apt/init.sls +++ b/salt/core/apt/init.sls @@ -12,7 +12,6 @@ needrestart: pkg.removed: - name: needrestart -# gnupg depends on dirmngr. gnupg2 is a dummy package for gnupg. secure ppa: pkg.installed: - pkgs: diff --git a/salt/core/firewall/init.sls b/salt/core/firewall/init.sls index c3b77052d..c7dcf5ac0 100644 --- a/salt/core/firewall/init.sls +++ b/salt/core/firewall/init.sls @@ -35,12 +35,12 @@ iptables-persistent: save iptables rules: cmd.run: - name: /home/sysadmin-tools/bin/firewall.sh - - onchanges: - - file: /home/sysadmin-tools/firewall-settings.local - - file: /home/sysadmin-tools/bin/firewall.sh - success_retcodes: - 0 - 3 # Docker install detected, exiting early. + - onchanges: + - file: /home/sysadmin-tools/firewall-settings.local + - file: /home/sysadmin-tools/bin/firewall.sh /home/sysadmin-tools/bin/firewall_reset.sh: file.managed: diff --git a/salt/core/mail.sls b/salt/core/mail.sls index a71d24382..6c42ddc11 100644 --- a/salt/core/mail.sls +++ b/salt/core/mail.sls @@ -33,7 +33,7 @@ postfix: smtp_sasl_auth_enable: "yes" smtp_sasl_security_options: "noanonymous" smtp_sasl_password_maps: "hash:/etc/postfix/sasl_passwd" -{% if grains.osmajorrelease|int >= 26 %} +{% if grains.osmajorrelease|int < 26 %} # Depreciated in Postfix 3.9, instead specify 'smtp_tls_security_level' smtp_use_tls: "yes" {% endif %} diff --git a/salt/core/ntp/init.sls b/salt/core/ntp/init.sls index 969cdd832..396ecec96 100644 --- a/salt/core/ntp/init.sls +++ b/salt/core/ntp/init.sls @@ -21,7 +21,6 @@ chrony-reload: - backup: False - watch_in: - cmd: chrony-reload - {% else %} systemd-timesyncd: pkg.installed: diff --git a/salt/core/sshd/files/customization.conf b/salt/core/sshd/files/customization.conf index 015294214..b7718ce9f 100644 --- a/salt/core/sshd/files/customization.conf +++ b/salt/core/sshd/files/customization.conf @@ -1,8 +1,8 @@ -# We set both PermitRootLogin and PasswordAuthentication for two reasons: -# -# - PermitRootLogin adds a layer of security in case PasswordAuthentication is toggled on. -# - While PermitRootLogin is set to "no" or "without-password", we can monitor it with our intrusion detection software. - +{#- +We set both PermitRootLogin and PasswordAuthentication for two reasons: + - PermitRootLogin adds a layer of security in case PasswordAuthentication is toggled on. + - While PermitRootLogin is set to "no" or "without-password", we can monitor it with our intrusion detection software. +-#} PasswordAuthentication no PermitRootLogin without-password X11Forwarding no diff --git a/salt/core/sshd/init.sls b/salt/core/sshd/init.sls index 3df1ccfc4..2862d730e 100644 --- a/salt/core/sshd/init.sls +++ b/salt/core/sshd/init.sls @@ -2,6 +2,7 @@ /etc/ssh/sshd_config.d/customization.conf: file.managed: - source: salt://core/sshd/files/customization.conf + - template: jinja - watch_in: - service: ssh_service From 4066a693b8de69f8e26ddfd9d425086561e7d2b4 Mon Sep 17 00:00:00 2001 From: RobHooper Date: Fri, 7 Aug 2026 15:28:16 +0100 Subject: [PATCH 21/22] chore: fix typo --- salt/core/mail.sls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/core/mail.sls b/salt/core/mail.sls index 6c42ddc11..838ce95b5 100644 --- a/salt/core/mail.sls +++ b/salt/core/mail.sls @@ -34,7 +34,7 @@ postfix: smtp_sasl_security_options: "noanonymous" smtp_sasl_password_maps: "hash:/etc/postfix/sasl_passwd" {% if grains.osmajorrelease|int < 26 %} - # Depreciated in Postfix 3.9, instead specify 'smtp_tls_security_level' + # Deprecated in Postfix 3.9, instead specify 'smtp_tls_security_level' smtp_use_tls: "yes" {% endif %} smtp_tls_security_level: "may" From 895301004f8e051916dd2af86553f01f1dfabec5 Mon Sep 17 00:00:00 2001 From: James McKinney <26463+jpmckinney@users.noreply.github.com> Date: Fri, 7 Aug 2026 10:57:17 -0400 Subject: [PATCH 22/22] docs(create_server): Align with order and labels on Linode --- docs/deploy/create_server.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/deploy/create_server.rst b/docs/deploy/create_server.rst index 99011f3b4..188d655d4 100644 --- a/docs/deploy/create_server.rst +++ b/docs/deploy/create_server.rst @@ -28,13 +28,13 @@ Create the server via the :ref:`host`'s interface. #. `Log into Linode `__ #. Click *Create Linode* - #. Set *Linux Distribution* to the latest Ubuntu LTS version #. Set *Region* to *London, UK (eu-west)* + #. Set *Linux Distribution* to the latest Ubuntu LTS version #. Select a *Linode Plan* #. Set *Linode Label* to the server's FQDN (e.g. ``ocp99.open-contracting.org``) #. Set *Add Tags* to either *Production* or *Development* #. Set *Root Password* to a `strong password `__, and save it to OCP's `LastPass `__ account - #. If using Docker, under *Public Interface Firewall* select *Create Firewall* and :ref:`configure an external firewall starting at step 3`. Otherwise, set to *No firewall*. + #. Under *Firewall*, if using Docker, click *Create Firewall* and :ref:`configure an external firewall from step 3`. Otherwise, select *No firewall - traffic is unprotected (not recommended)* from the dropdown. #. Check *Backups* #. Click *Create Linode* and wait a few minutes for the server to power on