Files
computer-playbook/group_vars/all/17_resource.yml
kevinveenbirkenbachandClaude Opus 5.5 d89c74feab feat(security): harden PHP services and proxy response headers
Proxy:
- server_tokens off, X-Powered-By stripped from upstream responses,
  HSTS max-age raised to one year.
- No global X-Content-Type-Options: under nosniff, ORB (step 3.4)
  blocks no-cors cross-origin responses typed text/html, e.g. tracking
  beacons answered with an empty 204.

PHP (CVE-2026-109618, CVE-2026-109619):
- New role sys-svc-compose-php installs a shared ini (expose_php Off,
  session.upload_progress.enabled 0, fatal_error_backtraces 0 for
  PHP 8.5, interim memory_limit -1) and a PHP-FPM [www] drop-in
  (pm.max_requests = PHP_FPM_MAX_REQUESTS) once per stack host,
  before 04_files renders any compose file.
- The host file names carry a sha1 prefix of the controller-side
  source, so a content change alters every PHP app's compose.yml and
  recreates it, also on remote deploys where compose_volumes cannot
  read the stack host file and hashes the path instead.
- 20 PHP roles mount the ini as a compose/swarm config into their
  image's scan dir; moodle, nextcloud and shopware also mount the FPM
  drop-in. Magento keeps its own pool: its php-fpm.conf has no
  include= and is rendered without notify.
- Joomla's php-upload.ini now loads as zzz-joomla-uploads.ini so its
  own memory_limit wins: the media API compares Content-Length with
  toBytes(memory_limit) and rejects every upload at -1.

TODOs record the memory_limit revert condition and accepted
trade-offs, Akaunting on PHP 8.1, FusionDirectory on PHP 7.4 and the
public Keycloak admin console.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 21:03:38 +02:00

55 lines
1.6 KiB
YAML

---
# Host resources
RESOURCE_HOST_CPUS: "{{ ansible_facts['processor_vcpus'] | int }}"
RESOURCE_HOST_MEM: "{{ (ansible_facts['memtotal_mb'] | int) // 1024 }}"
RESOURCE_HOST_RESERVE_CPU: 2
RESOURCE_HOST_RESERVE_MEM: 4
RESOURCE_AVAIL_CPUS: "{{ (RESOURCE_HOST_CPUS | int) - (RESOURCE_HOST_RESERVE_CPU | int) }}"
RESOURCE_AVAIL_MEM: "{{ (RESOURCE_HOST_MEM | int) - (RESOURCE_HOST_RESERVE_MEM | int) }}"
RESOURCE_ACTIVE_DOCKER_CONTAINER_COUNT: >-
{{
applications
| active_docker_container_count(group_names, '^(web-|svc-).*', ensure_min_one=True)
}}
RESOURCE_CPUS_NUM: >-
{{
[
(
((RESOURCE_AVAIL_CPUS | float) / (RESOURCE_ACTIVE_DOCKER_CONTAINER_COUNT | float))
| round(2)
),
0.5
] | max
}}
RESOURCE_MEM_RESERVATION_NUM: >-
{{
(((RESOURCE_AVAIL_MEM | float) / (RESOURCE_ACTIVE_DOCKER_CONTAINER_COUNT | float)) * 0.7)
| round(1)
}}
RESOURCE_MEM_LIMIT_NUM: >-
{{
(((RESOURCE_AVAIL_MEM | float) / (RESOURCE_ACTIVE_DOCKER_CONTAINER_COUNT | float)) * 1.0)
| round(1)
}}
RESOURCE_CPUS: "{{ RESOURCE_CPUS_NUM }}"
RESOURCE_MEM_RESERVATION: "{{ RESOURCE_MEM_RESERVATION_NUM }}g"
RESOURCE_MEM_LIMIT: "{{ RESOURCE_MEM_LIMIT_NUM }}g"
RESOURCE_PIDS_LIMIT: 512
POSTGRES_VCPUS: "{{ ansible_facts['processor_vcpus'] | int }}"
POSTGRES_MAX_CONNECTIONS: "{{ [ ((POSTGRES_VCPUS | int) * 30 + 50), 400 ] | min }}"
POSTGRES_ALLOWED_AVG_CONNECTIONS: "{{ (POSTGRES_MAX_CONNECTIONS | split_postgres_connections(lookup('path_absolute', 'roles'))) | int }}"
POSTGRES_SUPERUSER_RESERVED_CONNECTIONS: 3
POSTGRES_MAX_LOCKS_PER_TRANSACTION: 256
POSTGRES_DELAY: 2
PHP_FPM_MAX_REQUESTS: 200
TIMEOUT_FACTOR: 1