diff --git a/docs/deploy/create_server.rst b/docs/deploy/create_server.rst index e97d26cfa..188d655d4 100644 --- a/docs/deploy/create_server.rst +++ b/docs/deploy/create_server.rst @@ -28,12 +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 + #. 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 @@ -58,11 +59,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 +80,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 diff --git a/pillar/common.sls b/pillar/common.sls index 4f138a2aa..d973a680d 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 @@ -31,6 +35,16 @@ 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/apache/files/conf/letsencrypt.conf b/salt/apache/files/conf/letsencrypt.conf index eff23c2b3..26488364a 100644 --- a/salt/apache/files/conf/letsencrypt.conf +++ b/salt/apache/files/conf/letsencrypt.conf @@ -2,17 +2,31 @@ MDContactEmail sysadmin@open-contracting.org 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 %} {{ directive }} {{ value }} {%- 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 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/apt/init.sls b/salt/core/apt/init.sls index fd45dd15b..be3d9d35b 100644 --- a/salt/core/apt/init.sls +++ b/salt/core/apt/init.sls @@ -12,8 +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: - pkgs: 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: 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 ;; diff --git a/salt/core/firewall/files/firewall_reset.sh b/salt/core/firewall/files/firewall_reset.sh index ca1cfb04e..a3e56bdcb 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/firewall/init.sls b/salt/core/firewall/init.sls index 1b9aeab28..c7dcf5ac0 100644 --- a/salt/core/firewall/init.sls +++ b/salt/core/firewall/init.sls @@ -35,6 +35,9 @@ iptables-persistent: save iptables rules: cmd.run: - name: /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 diff --git a/salt/core/mail.sls b/salt/core/mail.sls index 36ff126e8..838ce95b5 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 %} + # Deprecated 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" diff --git a/salt/core/ntp/files/ntp-pools.sources b/salt/core/ntp/files/ntp-pools.sources new file mode 100644 index 000000000..23763d513 --- /dev/null +++ b/salt/core/ntp/files/ntp-pools.sources @@ -0,0 +1,3 @@ +{%- for source, entry in pillar.nts | items %} +pool {{ source }} {{ entry.context | default("iburst maxsources 1 nts prefer") }} +{%- 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 8b4ab9c78..396ecec96 100644 --- a/salt/core/systemd/ntp.sls +++ b/salt/core/ntp/init.sls @@ -1,21 +1,39 @@ # 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: - {% 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: - - source: salt://core/systemd/files/timesyncd.conf + - source: salt://core/ntp/files/timesyncd.conf - template: jinja - makedirs: True - watch_in: @@ -27,6 +45,7 @@ systemd-timesyncd: - backup: False - watch_in: - service: systemd-timesyncd +{% endif %} set timezone to utc: timezone.system: diff --git a/salt/core/sshd/files/customization.conf b/salt/core/sshd/files/customization.conf index 526b487f5..b7718ce9f 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..2862d730e 100644 --- a/salt/core/sshd/init.sls +++ b/salt/core/sshd/init.sls @@ -1,34 +1,11 @@ # 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 + - template: jinja - 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/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/mysql/init.sls b/salt/mysql/init.sls index ae5db9f84..fa69b272f 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: @@ -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 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 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 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