diff --git a/.github/files/wordpress_sites.yml b/.github/files/wordpress_sites.yml index 8fa390b191..c005628990 100644 --- a/.github/files/wordpress_sites.yml +++ b/.github/files/wordpress_sites.yml @@ -1,6 +1,3 @@ -letsencrypt_contact_emails: - - admin@example.com - wordpress_sites: example.com: site_hosts: diff --git a/LICENSE.md b/LICENSE.md index 1b5cd27da6..9e47289f33 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,4 +1,4 @@ -Copyright (c) Roots Software Foundation LLC +Copyright (c) Roots Software LLC Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/README.md b/README.md index c3089ee80b..452555c19a 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Trellis is an open source project and completely free to use. If you've benefited from our projects and would like to support our future endeavors, please consider [sponsoring Roots](https://github.com/sponsors/roots).
## Overview diff --git a/deploy-hooks/build-before.yml b/deploy-hooks/build-before.yml index 06d3980fa7..f437cc01c5 100644 --- a/deploy-hooks/build-before.yml +++ b/deploy-hooks/build-before.yml @@ -1,14 +1,14 @@ # Placeholder `deploy_build_before` hook for building theme assets on the # host machine and then copying the files to the remote server # -# ⚠️ This example assumes your theme is using Sage 10 +# ⚠️ This example assumes your theme is using Sage 11 # -# Uncomment the lines below if you are using Sage 10 +# Uncomment the lines below if you are using Sage 11 # and replace `sage` with your theme folder # # --- # - name: Install npm dependencies -# command: yarn +# command: npm install # delegate_to: localhost # args: # chdir: "{{ project_local_path }}/web/app/themes/sage" @@ -18,24 +18,24 @@ # args: # chdir: "{{ deploy_helper.new_release_path }}/web/app/themes/sage" # -# - name: Compile assets for production -# command: yarn build +# - name: Compile assets +# command: npm run build # delegate_to: localhost # args: # chdir: "{{ project_local_path }}/web/app/themes/sage" # -# - name: Check for entrypoints +# - name: Check for manifest # stat: -# path: "{{ project_local_path }}/web/app/themes/sage/public/entrypoints.json" +# path: "{{ project_local_path }}/web/app/themes/sage/public/build/manifest.json" # delegate_to: localhost # register: entrypoints_data # - name: Entrypoints missing # ansible.builtin.fail: -# msg: "The theme is missing the public/entrypoints.json file" +# msg: "The theme is missing the build manifest file" # when: not entrypoints_data.stat.exists # -# - name: Copy production assets +# - name: Copy compiled assets # synchronize: # src: "{{ project_local_path }}/web/app/themes/sage/public" # dest: "{{ deploy_helper.new_release_path }}/web/app/themes/sage" diff --git a/galaxy.yml b/galaxy.yml index 5a869fa7c2..574332d4d3 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -6,7 +6,7 @@ roles: - name: ntp src: geerlingguy.ntp - version: 2.5.0 + version: 2.7.0 - name: logrotate src: nickhammond.logrotate @@ -14,7 +14,7 @@ roles: - name: swapfile src: oefenweb.swapfile - version: v2.0.38 + version: v2.0.42 - name: mailpit src: roots.mailpit diff --git a/group_vars/all/helpers.yml b/group_vars/all/helpers.yml index 29d2c29d8a..7db67b83f8 100644 --- a/group_vars/all/helpers.yml +++ b/group_vars/all/helpers.yml @@ -16,7 +16,6 @@ site_hosts_redirects: "{{ item.value.site_hosts | selectattr('redirects', 'defin site_hosts: "{{ site_hosts_canonical | union(site_hosts_redirects) }}" multisite_subdomains_wildcards: "{{ item.value.multisite.subdomains | default(false) | ternary( site_hosts_canonical | map('regex_replace', '^(www\\.)?(.*)$', '*.\\2') | list, [] ) }}" ssl_enabled: "{{ item.value.ssl is defined and item.value.ssl.enabled | default(false) }}" -ssl_stapling_enabled: "{{ item.value.ssl is defined and item.value.ssl.stapling_enabled | default(true) }}" cron_enabled: "{{ site_env.disable_wp_cron and (not item.value.multisite.enabled | default(false) or (item.value.multisite.enabled | default(false) and item.value.multisite.cron | default(true))) }}" sites_use_ssl: "{{ wordpress_sites.values() | map(attribute='ssl') | selectattr('enabled') | list | count > 0 }}" diff --git a/lib/trellis/plugins/callback/vars.py b/lib/trellis/plugins/callback/vars.py index e30b59687c..1a754d908d 100644 --- a/lib/trellis/plugins/callback/vars.py +++ b/lib/trellis/plugins/callback/vars.py @@ -47,8 +47,8 @@ def raw_vars(self, play, host, hostvars): if not isinstance(raw_vars, list): raise AnsibleError('The `raw_vars` variable must be defined as a list.') - patterns = [re.sub(r'\*', '(.)*', re.sub(r'\.', '\.', var)) for var in raw_vars if var.split('.')[0] in hostvars] - keys = set(pattern.split('\.')[0] for pattern in patterns) + patterns = [re.sub(r'\*', '(.)*', re.sub(r'\.', r'\.', var)) for var in raw_vars if var.split('.')[0] in hostvars] + keys = set(pattern.split(r'\.')[0] for pattern in patterns) for key in keys: if key in play.vars: play.vars[key] = self.raw_triage(key, play.vars[key], patterns) diff --git a/roles/common/tasks/main.yml b/roles/common/tasks/main.yml index cadc613586..6cb1fce1d8 100644 --- a/roles/common/tasks/main.yml +++ b/roles/common/tasks/main.yml @@ -41,8 +41,8 @@ vars: params: files: - - '{{ php_version }}.yml' - - '7.4.yml' + - '{{ php_version }}.yml' # e.g. 8.2.yml + - version-specific-defaults.yml paths: - "{{ playbook_dir }}/roles/php/vars/" diff --git a/roles/deploy/defaults/main.yml b/roles/deploy/defaults/main.yml index a2f2247f26..d5d05d7f91 100644 --- a/roles/deploy/defaults/main.yml +++ b/roles/deploy/defaults/main.yml @@ -49,6 +49,9 @@ project_current_path: "{{ project.current_path | default('current') }}" # Whether to run `wp core update-db` at end of each deploy update_db_on_deploy: true +# Whether to flush rewrite rules at end of each deploy +flush_rewrite_rules_on_deploy: true + # Most scripts are used in development instead of remote servers. Use with caution. composer_no_scripts: true # Whether to run `composer check-platform-reqs`. diff --git a/roles/deploy/hooks/finalize-after.yml b/roles/deploy/hooks/finalize-after.yml index c1c54c66e5..5a4809b659 100644 --- a/roles/deploy/hooks/finalize-after.yml +++ b/roles/deploy/hooks/finalize-after.yml @@ -31,6 +31,12 @@ chdir: "{{ deploy_helper.current_path }}" when: project.update_db_on_deploy | default(update_db_on_deploy) + - name: Flush rewrite rules + command: wp rewrite flush + args: + chdir: "{{ deploy_helper.current_path }}" + when: project.flush_rewrite_rules_on_deploy | default(flush_rewrite_rules_on_deploy) + when: wp_installed.rc == 0 - name: Reload php-fpm diff --git a/roles/letsencrypt/tasks/setup.yml b/roles/letsencrypt/tasks/setup.yml index 72664d0f42..b78a7b0d76 100644 --- a/roles/letsencrypt/tasks/setup.yml +++ b/roles/letsencrypt/tasks/setup.yml @@ -1,26 +1,4 @@ --- -- name: Fail if letsencrypt_contact_emails is not defined - fail: - msg: > - Error: the required `letsencrypt_contact_emails` variable is not defined or invalid. - - - Please define it in `groups_vars/all/main.yml` with at least one email (as a list/array, *not* a string): - - letsencrypt_contact_emails: - - changeme@example.com - - The contact email is used by Let's Encrypt to send expiry notices when a certificate is coming up for renewal. - - - See https://letsencrypt.org/docs/expiration-emails/ for more information. - - - Since Trellis attempts to renew certificates after {{ letsencrypt_min_renewal_age }} days ({{ 90 - letsencrypt_min_renewal_age }} days before renewal), - getting an expiry notice email means something has gone wrong giving you enough notice to fix the problem. - - when: (letsencrypt_contact_emails is not defined) or (letsencrypt_contact_emails is string) - - name: Create directories and set permissions file: mode: "{{ item.mode | default(omit) }}" diff --git a/roles/letsencrypt/templates/renew-certs.py b/roles/letsencrypt/templates/renew-certs.py index b13ed8efa6..7b19561649 100644 --- a/roles/letsencrypt/templates/renew-certs.py +++ b/roles/letsencrypt/templates/renew-certs.py @@ -32,7 +32,6 @@ '--ca {{ letsencrypt_ca }} ' '--account-key {{ letsencrypt_account_key }} ' '--csr {} ' - '--contact {{ letsencrypt_contact_emails | map('regex_replace', '(^.*$)', 'mailto:\\1') | join (' ') }} ' '--acme-dir {{ acme_tiny_challenges_directory }}' ).format(csr_path) diff --git a/roles/mariadb/tasks/main.yml b/roles/mariadb/tasks/main.yml index 58d8ea1717..723bf0e2da 100644 --- a/roles/mariadb/tasks/main.yml +++ b/roles/mariadb/tasks/main.yml @@ -8,6 +8,10 @@ apt_repository: repo: "{{ mariadb_ppa }}" update_cache: yes + register: result + until: result is success + retries: 3 + delay: 5 - name: Install MySQL client ansible.builtin.apt: diff --git a/roles/nginx/tasks/main.yml b/roles/nginx/tasks/main.yml index 8b8641826d..12686d5334 100644 --- a/roles/nginx/tasks/main.yml +++ b/roles/nginx/tasks/main.yml @@ -8,6 +8,10 @@ apt_repository: repo: "{{ nginx_ppa }}" update_cache: yes + register: result + until: result is success + retries: 3 + delay: 5 - name: Install Nginx ansible.builtin.apt: diff --git a/roles/nginx/templates/h5bp/directive-only/extra-security.conf b/roles/nginx/templates/h5bp/directive-only/extra-security.conf index eb10e06ca4..2274628166 100644 --- a/roles/nginx/templates/h5bp/directive-only/extra-security.conf +++ b/roles/nginx/templates/h5bp/directive-only/extra-security.conf @@ -10,8 +10,27 @@ add_header X-Content-Type-Options nosniff always; # The header instructs IE to enable its inbuilt anti-cross-site scripting filter. add_header X-XSS-Protection "1; mode=block" always; -# with Content Security Policy (CSP) enabled (and a browser that supports it (http://caniuse.com/#feat=contentsecuritypolicy), -# you can tell the browser that it can only download content from the domains you explicitly allow -# CSP can be quite difficult to configure, and cause real issues if you get it wrong -# There is website that helps you generate a policy here http://cspisawesome.com/ +# Mitigate the risk of cross-site scripting and other content-injection +# attacks. +# +# This can be done by setting a Content Security Policy which permits +# trusted sources of content for your website. +# +# There is no policy that fits all websites, you will have to modify the +# `Content-Security-Policy` directives in the example depending on your needs. +# +# To make your CSP implementation easier, you can use an online CSP header +# generator such as: +# https://report-uri.com/home/generate/ +# +# It is encouraged that you validate your CSP header using a CSP validator +# such as: +# https://csp-evaluator.withgoogle.com +# +# https://www.w3.org/TR/CSP/ +# https://owasp.org/www-project-secure-headers/#content-security-policy +# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy +# https://developers.google.com/web/fundamentals/security/csp +# https://content-security-policy.com/ + # add_header Content-Security-Policy "default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' https://www.google-analytics.com;" always; diff --git a/roles/nginx/templates/h5bp/directive-only/ssl-stapling.conf b/roles/nginx/templates/h5bp/directive-only/ssl-stapling.conf deleted file mode 100644 index 95cc175ce4..0000000000 --- a/roles/nginx/templates/h5bp/directive-only/ssl-stapling.conf +++ /dev/null @@ -1,34 +0,0 @@ -# ---------------------------------------------------------------------- -# | Online Certificate Status Protocol stapling | -# ---------------------------------------------------------------------- - -# OCSP is a lightweight, only one record to help clients verify the validity of -# the server certificate. -# OCSP stapling allows the server to send its cached OCSP record during the TLS -# handshake, without the need of 3rd party OCSP responder. -# -# https://wiki.mozilla.org/Security/Server_Side_TLS#OCSP_Stapling -# https://tools.ietf.org/html/rfc6066#section-8 -# https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_stapling -# -# (1) Use Cloudflare 1.1.1.1 DNS resolver -# https://developers.cloudflare.com/1.1.1.1/setting-up-1.1.1.1/ -# -# (2) Use Google 8.8.8.8 DNS resolver -# https://developers.google.com/speed/public-dns/docs/using -# -# (3) Use Dyn DNS resolver -# https://help.dyn.com/internet-guide-setup/ - -ssl_stapling on; -ssl_stapling_verify on; - -resolver - # (1) - 1.1.1.1 1.0.0.1 [2606:4700:4700::1111] [2606:4700:4700::1001] - # (2) - 8.8.8.8 8.8.4.4 [2001:4860:4860::8888] [2001:4860:4860::8844] - # (3) - # 216.146.35.35 216.146.36.36 - valid=60s; -resolver_timeout 2s; diff --git a/roles/nginx/templates/h5bp/directive-only/ssl.conf b/roles/nginx/templates/h5bp/directive-only/ssl.conf index 20d98766b3..19e62f03b7 100644 --- a/roles/nginx/templates/h5bp/directive-only/ssl.conf +++ b/roles/nginx/templates/h5bp/directive-only/ssl.conf @@ -39,5 +39,3 @@ keepalive_timeout 300s; # up from 75 secs default # Make it a symlink to the most important certificate you have, so that users of IE 8 and below on WinXP can see your main site without SSL errors. #ssl_certificate /etc/nginx/default_ssl.crt; #ssl_certificate_key /etc/nginx/default_ssl.key; - -# Consider using OCSP Stapling as shown in ssl-stapling.conf diff --git a/roles/nginx/templates/nginx.conf.j2 b/roles/nginx/templates/nginx.conf.j2 index 6b60169863..b62f4ef3ff 100644 --- a/roles/nginx/templates/nginx.conf.j2 +++ b/roles/nginx/templates/nginx.conf.j2 @@ -66,8 +66,8 @@ http { fastcgi_cache_path {{ nginx_cache_path }} levels=1:2 keys_zone=wordpress:{{ nginx_cache_key_storage_size }} max_size={{ nginx_cache_size }} inactive={{ nginx_cache_inactive }}; fastcgi_cache_use_stale updating error timeout invalid_header http_500; fastcgi_cache_lock on; - fastcgi_cache_key $realpath_root$scheme$host$request_uri$request_method$http_origin; - fastcgi_ignore_headers Cache-Control Expires Set-Cookie; + fastcgi_cache_key $realpath_root$scheme$host$request_uri$request_method$http_origin$http_x_http_method_override; + fastcgi_ignore_headers Expires Set-Cookie; fastcgi_pass_header Set-Cookie; fastcgi_pass_header Cookie; {% endblock %} diff --git a/roles/php/tasks/main.yml b/roles/php/tasks/main.yml index 107a102047..164aeffdb3 100644 --- a/roles/php/tasks/main.yml +++ b/roles/php/tasks/main.yml @@ -3,6 +3,10 @@ apt_repository: repo: "ppa:ondrej/php" update_cache: yes + register: result + until: result is success + retries: 3 + delay: 5 - name: Install PHP and extensions apt: diff --git a/roles/php/vars/7.4.yml b/roles/php/vars/7.4.yml deleted file mode 100644 index 61ab906904..0000000000 --- a/roles/php/vars/7.4.yml +++ /dev/null @@ -1,18 +0,0 @@ -php_extensions_default: - php7.4-bcmath: "{{ apt_package_state }}" - php7.4-cli: "{{ apt_package_state }}" - php7.4-curl: "{{ apt_package_state }}" - php7.4-dev: "{{ apt_package_state }}" - php7.4-fpm: "{{ apt_package_state }}" - php7.4-imagick: "{{ apt_package_state }}" - php7.4-intl: "{{ apt_package_state }}" - php7.4-mbstring: "{{ apt_package_state }}" - php7.4-mysql: "{{ apt_package_state }}" - php7.4-xml: "{{ apt_package_state }}" - php7.4-xmlrpc: "{{ apt_package_state }}" - php7.4-zip: "{{ apt_package_state }}" - -php_memcached_packages: - php7.4-memcached: "{{ apt_package_state }}" - -php_xdebug_package: php7.4-xdebug diff --git a/roles/php/vars/8.0.yml b/roles/php/vars/8.0.yml deleted file mode 100644 index 02eb376120..0000000000 --- a/roles/php/vars/8.0.yml +++ /dev/null @@ -1,18 +0,0 @@ -php_extensions_default: - php8.0-bcmath: "{{ apt_package_state }}" - php8.0-cli: "{{ apt_package_state }}" - php8.0-curl: "{{ apt_package_state }}" - php8.0-dev: "{{ apt_package_state }}" - php8.0-fpm: "{{ apt_package_state }}" - php8.0-imagick: "{{ apt_package_state }}" - php8.0-intl: "{{ apt_package_state }}" - php8.0-mbstring: "{{ apt_package_state }}" - php8.0-mysql: "{{ apt_package_state }}" - php8.0-xml: "{{ apt_package_state }}" - php8.0-xmlrpc: "{{ apt_package_state }}" - php8.0-zip: "{{ apt_package_state }}" - -php_memcached_packages: - php8.0-memcached: "{{ apt_package_state }}" - -php_xdebug_package: php8.0-xdebug diff --git a/roles/php/vars/8.1.yml b/roles/php/vars/8.1.yml deleted file mode 100644 index 1b15ae2d4e..0000000000 --- a/roles/php/vars/8.1.yml +++ /dev/null @@ -1,18 +0,0 @@ -php_extensions_default: - php8.1-bcmath: "{{ apt_package_state }}" - php8.1-cli: "{{ apt_package_state }}" - php8.1-curl: "{{ apt_package_state }}" - php8.1-dev: "{{ apt_package_state }}" - php8.1-fpm: "{{ apt_package_state }}" - php8.1-imagick: "{{ apt_package_state }}" - php8.1-intl: "{{ apt_package_state }}" - php8.1-mbstring: "{{ apt_package_state }}" - php8.1-mysql: "{{ apt_package_state }}" - php8.1-xml: "{{ apt_package_state }}" - php8.1-xmlrpc: "{{ apt_package_state }}" - php8.1-zip: "{{ apt_package_state }}" - -php_memcached_packages: - php8.1-memcached: "{{ apt_package_state }}" - -php_xdebug_package: php8.1-xdebug diff --git a/roles/php/vars/8.2.yml b/roles/php/vars/8.2.yml deleted file mode 100644 index 9fd5b87761..0000000000 --- a/roles/php/vars/8.2.yml +++ /dev/null @@ -1,18 +0,0 @@ -php_extensions_default: - php8.2-bcmath: "{{ apt_package_state }}" - php8.2-cli: "{{ apt_package_state }}" - php8.2-curl: "{{ apt_package_state }}" - php8.2-dev: "{{ apt_package_state }}" - php8.2-fpm: "{{ apt_package_state }}" - php8.2-imagick: "{{ apt_package_state }}" - php8.2-intl: "{{ apt_package_state }}" - php8.2-mbstring: "{{ apt_package_state }}" - php8.2-mysql: "{{ apt_package_state }}" - php8.2-xml: "{{ apt_package_state }}" - php8.2-xmlrpc: "{{ apt_package_state }}" - php8.2-zip: "{{ apt_package_state }}" - -php_memcached_packages: - php8.2-memcached: "{{ apt_package_state }}" - -php_xdebug_package: php8.2-xdebug diff --git a/roles/php/vars/8.3.yml b/roles/php/vars/8.3.yml deleted file mode 100644 index 557f9726a5..0000000000 --- a/roles/php/vars/8.3.yml +++ /dev/null @@ -1,18 +0,0 @@ -php_extensions_default: - php8.3-bcmath: "{{ apt_package_state }}" - php8.3-cli: "{{ apt_package_state }}" - php8.3-curl: "{{ apt_package_state }}" - php8.3-dev: "{{ apt_package_state }}" - php8.3-fpm: "{{ apt_package_state }}" - php8.3-imagick: "{{ apt_package_state }}" - php8.3-intl: "{{ apt_package_state }}" - php8.3-mbstring: "{{ apt_package_state }}" - php8.3-mysql: "{{ apt_package_state }}" - php8.3-xml: "{{ apt_package_state }}" - php8.3-xmlrpc: "{{ apt_package_state }}" - php8.3-zip: "{{ apt_package_state }}" - -php_memcached_packages: - php8.3-memcached: "{{ apt_package_state }}" - -php_xdebug_package: php8.3-xdebug diff --git a/roles/php/vars/version-specific-defaults.yml b/roles/php/vars/version-specific-defaults.yml new file mode 100644 index 0000000000..b3bc73a075 --- /dev/null +++ b/roles/php/vars/version-specific-defaults.yml @@ -0,0 +1,29 @@ +# +# Set php version in group_vars/all/main.yml +# +# To override these defaults for a specific php version, duplicate this +# file to roles/php/vars/ and rename with your specific php version +# e.g. roles/php/vars/8.2.yml. +# +# You can then use e.g. +# php_extensions_default: +# php8.2-bcmath: "{{ apt_package_state }}" +# +php_extensions_default: + "php{{ php_version }}-bcmath": "{{ apt_package_state }}" + "php{{ php_version }}-cli": "{{ apt_package_state }}" + "php{{ php_version }}-curl": "{{ apt_package_state }}" + "php{{ php_version }}-dev": "{{ apt_package_state }}" + "php{{ php_version }}-fpm": "{{ apt_package_state }}" + "php{{ php_version }}-imagick": "{{ apt_package_state }}" + "php{{ php_version }}-intl": "{{ apt_package_state }}" + "php{{ php_version }}-mbstring": "{{ apt_package_state }}" + "php{{ php_version }}-mysql": "{{ apt_package_state }}" + "php{{ php_version }}-xml": "{{ apt_package_state }}" + "php{{ php_version }}-xmlrpc": "{{ apt_package_state }}" + "php{{ php_version }}-zip": "{{ apt_package_state }}" + +php_memcached_packages: + "php{{ php_version }}-memcached": "{{ apt_package_state }}" + +php_xdebug_package: "php{{ php_version }}-xdebug" diff --git a/roles/wordpress-setup/templates/wordpress-site.conf.j2 b/roles/wordpress-setup/templates/wordpress-site.conf.j2 index 95b406439a..2cf97593dc 100644 --- a/roles/wordpress-setup/templates/wordpress-site.conf.j2 +++ b/roles/wordpress-setup/templates/wordpress-site.conf.j2 @@ -79,9 +79,6 @@ server { {% if ssl_enabled -%} # SSL configuration include h5bp/directive-only/ssl.conf; - {% if ssl_stapling_enabled -%} - include h5bp/directive-only/ssl-stapling.conf; - {% endif -%} ssl_buffer_size 1400; # 1400 bytes to fit in one MTU @@ -255,6 +252,7 @@ server { {% block fastcgi_basic -%} include fastcgi_params; + fastcgi_param SERVER_NAME $host; fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; fastcgi_param DOCUMENT_ROOT $realpath_root; fastcgi_pass unix:/var/run/php-fpm-wordpress.sock; diff --git a/roles/xdebug/tasks/main.yml b/roles/xdebug/tasks/main.yml index 7b6248d1c0..f4d1d3acef 100644 --- a/roles/xdebug/tasks/main.yml +++ b/roles/xdebug/tasks/main.yml @@ -1,6 +1,13 @@ --- -- name: Include php{{ php_version }} related vars - include_vars: 'roles/php/vars/{{ php_version }}.yml' +- name: Import PHP version specific vars + include_vars: "{{ lookup('first_found', params) }}" + vars: + params: + files: + - '{{ php_version }}.yml' # e.g. 8.2.yml + - version-specific-defaults.yml + paths: + - "{{ playbook_dir }}/roles/php/vars/" - name: Install Xdebug apt: