Compare commits

...

12 Commits

Author SHA1 Message Date
Jérémy Lecour 5b4ea19350 cleanup
gitea/ansible-roles/pipeline/head This commit looks good Details
2022-11-27 14:59:40 +01:00
Jérémy Lecour d03910d5f8 boost-proxy: comments in english
gitea/ansible-roles/pipeline/head This commit looks good Details
2022-11-27 14:23:12 +01:00
Jérémy Lecour 071ac2bafb boost-proxy: speedup update-config tags
gitea/ansible-roles/pipeline/head This commit looks good Details
2022-11-27 13:14:04 +01:00
Jérémy Lecour 13877be26d boost-proxy: custom file example
gitea/ansible-roles/pipeline/head This commit looks good Details
2022-11-27 13:04:04 +01:00
Jérémy Lecour 7c8de544ef boost-proxy: better example 2022-11-27 13:03:47 +01:00
Jérémy Lecour 450cb58c6f boost-proxy: copy whatever static content is available 2022-11-27 13:03:29 +01:00
Jérémy Lecour 5663ebcecf boost-proxy: speedup update-config tags 2022-11-27 13:00:39 +01:00
Jérémy Lecour c28cb1ee72 boost-proxy: custom error pages are optional
gitea/ansible-roles/pipeline/head This commit looks good Details
2022-11-27 00:35:49 +01:00
Jérémy Lecour c0e0ba3562 boost-proxy: add some variables/templates examples 2022-11-27 00:35:49 +01:00
Jérémy Lecour 717926a216 boost-proxy: fix missing http error pages 2022-11-27 00:35:49 +01:00
Jérémy Lecour 5921d24f4b boost-proxy: add handlers for varnish, haproxy 2022-11-27 00:35:49 +01:00
Jérémy Lecour f8715078f6 boost-proxy: new role, extracted from internal use, to make a Boost server 2022-11-27 00:35:49 +01:00
42 changed files with 1758 additions and 1 deletions

View File

@ -12,6 +12,7 @@ The **patch** part changes is incremented if multiple releases happen the same m
### Added
* boost-proxy: new role, extracted from internal use, to make a Boost server
* evolinux-base: replace regular kernel by cloud kernel on virtual servers
* nagios-nrpe: check_haproxy_stats supports DRAIN status
* lxc-php: set php-fpm umask to 007

View File

@ -0,0 +1,45 @@
###
# This is list of variables that should copy to your inventory when using this role
# Those values are sensible, for a dedicated Boost Proxy server.
#
---
# minifirewall_protected_ports_tcp: []
# minifirewall_protected_ports_udp: []
# minifirewall_public_ports_tcp: [80, 443]
# minifirewall_public_ports_udp: []
# minifirewall_semipublic_ports_tcp: [22, "{{ haproxy_stats_port }}"]
# minifirewall_semipublic_ports_udp: []
# minifirewall_private_ports_tcp: [5666]
# minifirewall_private_ports_udp: []
# minifirewall_http_sites: ['0.0.0.0/0']
# minifirewall_https_sites: ['0.0.0.0/0']
# haproxy_socket: /run/haproxy/admin.sock
# haproxy_chroot: /var/lib/haproxy
# haproxy_server_state_file: /var/lib/haproxy/server_state.txt
# haproxy_stats_access_ips: "{{ trusted_ips | union(['127.0.0.1']) }}"
# haproxy_stats_admin_ips: "{{ trusted_ips }}"
# haproxy_maintenance_ips: []
# haproxy_deny_ips: []
# haproxy_stats_enable: True
# haproxy_stats_port: "8080"
# nginx_package_name: "nginx-light"
# nginx_force_default_template: True
# nginx_default_template_regular: "nginx/evolinux-default.conf.j2"
# varnish_addresses:
# - "{{ haproxy_chroot }}{{ boost_varnish_proxy_socket }},user=vcache,group=varnish,mode=666,PROXY"
# - 127.0.0.1:82
# varnish_tmp_dir: /var/tmp-vcache
# boost_sites_enabled_for_all:
# - 000-example
# Set this to Tur if you have multiple servers that need to communicate with a root SSH access
# boost_allow_root_ssh_between_servers: False

View File

@ -0,0 +1,34 @@
---
boost_sysctl_file_path: /etc/sysctl.d/boost.conf
boost_sysctl_config:
- { key: "net.ipv4.ip_local_port_range", value: "20000 65023" }
- { key: "net.ipv4.tcp_max_syn_backlog", value: "60000" }
- { key: "net.ipv4.tcp_fin_timeout", value: "30" }
- { key: "net.ipv4.ip_nonlocal_bind", value: "1" }
- { key: "net.core.somaxconn", value: "60000" }
- { key: "net.nf_conntrack_max", value: "524288" }
- { key: "net.unix.max_dgram_qlen", value: "100" }
- { key: "net.ipv4.conf.default.rp_filter", value: "0" }
- { key: "net.ipv4.conf.all.rp_filter", value: "0" }
- { key: "net.ipv4.conf.all.arp_ignore", value: "1" }
- { key: "net.ipv4.conf.all.arp_announce", value: "2" }
boost_allow_root_ssh_between_servers: False
other_servers_from_group_ips: []
boost_sites_enabled: []
boost_sites_enabled_for_all: []
boost_sites_enabled_for_group: []
boost_sites_enabled_for_host: []
boost_validate_haproxy: True
boost_validate_varnish: True
boost_nginx_check_url: "/nginxcheck"
boost_haproxy_check_url: "/haproxycheck"
boost_varnish_check_url: "/varnishcheck"
boost_nginx_proxy_port: 81
boost_haproxy_proxy_socket: /run/haproxy-frontend-default.sock
boost_varnish_proxy_socket: "/run/varnish.sock"

View File

@ -0,0 +1,31 @@
---
- name: reload sshd
systemd:
name: ssh
state: reloaded
- name: reload systemd
command: systemctl daemon-reload
- name: reload varnish
systemd:
name: varnish
state: reloaded
daemon_reload: yes
- name: restart varnish
systemd:
name: varnish
state: restarted
daemon_reload: yes
- name: reload haproxy
systemd:
name: haproxy
state: reloaded
- name: restart haproxy
systemd:
name: haproxy
state: restarted

View File

@ -0,0 +1,58 @@
---
- name: URL for HAProxy admin page is on default page
lineinfile:
path: "/var/www/index.html"
line: ' <li><a href="{{ haproxy_stats_external_url }}">HAProxy</a></li>'
regexp: '>HAProxy<'
insertafter: ">Stats système<"
tags:
- haproxy
- config
- name: HAproxy run directory in chroot
file:
dest: "/var/lib/haproxy/run"
owner: root
group: root
mode: "0755"
state: directory
tags:
- haproxy
- config
- name: HAproxy errors directory is present
file:
dest: "/etc/haproxy/errors"
owner: root
group: root
mode: "0755"
state: directory
tags:
- haproxy
- config
- name: "Copy error pages"
copy:
src: "templates/haproxy/static/"
dest: "/etc/haproxy/errors/"
owner: root
group: root
mode: "0644"
notify: reload haproxy
tags:
- haproxy
- config
- update-config
- name: 2048 bits DHparam file is present
get_url:
url: https://ssl-config.mozilla.org/ffdhe2048.txt
dest: /etc/ssl/dhparam-haproxy
mode: '0600'
owner: root
group: root
force: no
tags:
- haproxy
- config

View File

@ -0,0 +1,48 @@
---
#######################
# System configuration
#######################
# Merge variables from group_vars and host_vars
- set_fact:
boost_sites_enabled: "{{ boost_sites_enabled_for_all | union(boost_sites_enabled_for_group) | union(boost_sites_enabled_for_host) | unique }}"
tags: always
- debug:
var: boost_sites_enabled
tags: always
- include: haproxy.yml
- include: sshd.yml
- include: sysctl.yml
######################
# Sites configuration
######################
- import_tasks: sites.yml
#################
# external roles
#################
- import_role:
name: haproxy
- import_role:
name: varnish
- import_role:
name: nginx
- import_role:
name: certbot
##############
# validations
##############
- include: validate.yml

View File

@ -0,0 +1,30 @@
---
# HAProxy
- name: "Copy HAProxy static elements for {{ site }}"
copy:
src: "templates/boost-sites/{{ site }}/haproxy/static/"
dest: "/etc/haproxy/sites/{{ site }}/"
owner: root
group: root
mode: "0644"
tags:
- haproxy
- config
- update-config
# Varnish
- name: "Copy Varnish custom VCL for {{ site }}"
template:
src: "templates/boost-sites/{{ site }}/varnish/default.vcl.j2"
dest: "/etc/varnish/sites/{{ site }}.vcl"
owner: root
group: root
mode: "0644"
notify: reload varnish
tags:
- varnish
- config
- update-config

View File

@ -0,0 +1,41 @@
---
# HAProxy
- name: Create HAProxy sites parent directory
file:
dest: "/etc/haproxy/sites"
owner: root
group: root
mode: "0755"
state: directory
tags:
- haproxy
- config
# Varnish
- name: Create Varnish sites parent directory
file:
dest: "/etc/varnish/sites"
owner: root
group: root
mode: "0755"
state: directory
tags:
- varnish
- config
# Each site
- name: Create site configuration
include: site.yml
loop: "{{ boost_sites_enabled }}"
loop_control:
loop_var: site
when:
- boost_sites_enabled | length > 0
tags:
- haproxy
- config
- update-config

View File

@ -0,0 +1,27 @@
---
- name: "root can connect over SSH from other servers"
blockinfile:
dest: /etc/ssh/sshd_config
marker: "# {mark} ROOT AUTHORIZATION"
block: |
Match User root Address {{ other_servers_from_group_ips | join(',') }}
AllowGroups root
PubkeyAuthentication yes
PasswordAuthentication no
PermitRootLogin without-password
state: present
notify: reload sshd
when: (boost_allow_root_ssh_between_servers | bool) and (other_servers_from_group_ips | length > 0)
tags:
- ssh
- name: "root can connect over SSH from other servers"
blockinfile:
dest: /etc/ssh/sshd_config
marker: "# {mark} ROOT AUTHORIZATION"
state: absent
notify: reload sshd
when: not (boost_allow_root_ssh_between_servers | bool) or (other_servers_from_group_ips | length <= 0)
tags:
- ssh

View File

@ -0,0 +1,12 @@
---
- name: Boost optimization for sysctl
sysctl:
sysctl_file: "{{ boost_sysctl_file_path }}"
name: "{{ item.key }}"
value: "{{ item.value }}"
reload: yes
sysctl_set: yes
loop: "{{ boost_sysctl_config }}"
tags:
- sysctl

View File

@ -0,0 +1,24 @@
---
- name: check if HAProxy configuration is valid
shell:
cmd: "haproxy -c -f /etc/haproxy/haproxy.cfg"
changed_when: false
check_mode: no
register: haproxy_validate
when: boost_validate_haproxy
tags:
- always
- name: check if Varnish configuration is valid
shell:
cmd: "sudo -u vcache TMPDIR={{ varnish_tmp_dir }} varnishd -C -f /etc/varnish/default.vcl > /dev/null"
args:
warn: False
changed_when: false
check_mode: no
register: varnish_validate
when: boost_validate_varnish
tags:
- always

View File

@ -0,0 +1,7 @@
backend example
errorfile 503 /etc/haproxy/sites/000-example/maintenance.http
server example-hostmane 1.2.3.4:443 check observe layer4 ssl verify none
backend example_maintenance
http-request set-log-level silent
errorfile 503 /etc/haproxy/sites/000-example/maintenance.http

View File

@ -0,0 +1,2 @@
# If you keep this file, it will be added after the global section, before frontends.
# Remove it if you don't need it, to keep the configuration tidy.

View File

@ -0,0 +1,31 @@
acl example_domains var(req.hdr_host) -m str example.com
acl example_domains2 var(req.hdr_host) -m str example.org www.example.org
### acl example_maintenance_ips src -f /etc/haproxy/sites/{{ site }}/maintenance_ips
# Redirect to HTTPS without Let's Encrypt certificate
### http-request redirect scheme https code 301 if example_domains !{ ssl_fc }
# Redirect to HTTPS with Let's Encrypt certificate (exclude LE challenge from redirection)
### http-request redirect scheme https code 301 if example_domains !{ ssl_fc } !letsencrypt
# Redirect a domain to another without Let's encrypt certificate
### http-request redirect prefix https://example-to.org code 301 if { var(req.hdr_host) -m str example-from.org }
# Redirect a domain to another with a Let's encrypt certificate (exclude LE challenge from redirection)
### http-request redirect prefix https://example-to.org code 301 if { var(req.hdr_host) -m str example-from.org } !letsencrypt
# HSTS (31536000 seconds = 1 year)
### http-response set-header Strict-Transport-Security max-age=31536000 if example_domains
# Custom directives, all conditionned by at least the "example_domains" ACL
# Example: reject request from a specific IP
### http-request deny if example_domains { src 203.0.113.1 }
# routing directives, all conditionned by (at least) the "example_domains" ACL
# Maintenance mode (### -> uncomment BUT define example_maintenance_ips acl before)
### use_backend example_maintenance if example_domains !example_maintenance_ips !maintenance_ips
# Use Varnish if available
use_backend varnish if example_domains varnish_available varnish_http_verb
# … or use normal backend
use_backend example if example_domains

View File

@ -0,0 +1,5 @@
acl example_domains var(req.hdr_host) -m str example.com
# routing directives, all conditionned by (at least) the "example_domains" ACL
use_backend example if example_domains

View File

@ -0,0 +1,15 @@
HTTP/1.0 503 Service Unavailable
Cache-Control: no-cache
Connection: close
Content-Type: text/html
<!doctype html>
<html lang="fr">
<head>
<title>Site Maintenance</title>
<meta charset="UTF-8">
</head>
<body>
<h1>Maintenance en cours</h1>
</body>

View File

@ -0,0 +1,150 @@
sub vcl_recv {
# Happens before we check if we have this in cache already.
#
# Typically you clean up the request here, removing cookies you don't need,
# rewriting the request, etc.
if (req.http.host == "example.com" || req.http.host == "www.example.com") {
# Accept PURGE requests from whitelisted IPs
# Uncomment to enable
### if (req.method == "PURGE") {
### # Allow from monitoring & hosting08
### if (client.ip == "31.170.9.129" || client.ip == "31.170.11.159") {
### if (req.url == "/_purge_all") {
### ban("req.http.host == "+req.http.host+" && req.url ~ .");
### return (synth(200, "ALL purge cache done"));
### }
### ban("req.http.host == "+req.http.host+" && req.url ~ "+req.url);
### return (synth(200, "purge cache done"));
### } else {
### return (synth(403, "permission denied"));
### }
### }
# return (pass) when Cache-Control: no-cache, private etc. from client
include "/etc/varnish/conf.d/respect_cache_request_headers.recv.vcl";
# unset cookie and auth headers for static files (jpg, png, pdf...)
include "/etc/varnish/conf.d/cleanup_requests_static.recv.vcl";
# Wordpress : return (pass) when WP cookie or "^/wp-(login|admin)" url
### include "/etc/varnish/conf.d/wordpress.recv.vcl";
# Uncomment if your site uses Prestashop
### include "/etc/varnish/conf.d/prestashop.recv.vcl";
# Uncomment to use devide detection
### call devicedetect;
# builtin configuration
### include "/etc/varnish/conf.d/builtin.recv.vcl";
if (req.method == "PRI") {
/* This will never happen in properly formed traffic (see: RFC7540) */
return (synth(405));
}
if (!req.http.host &&
req.esi_level == 0 &&
req.proto ~ "^(?i)HTTP/1.1") {
/* In HTTP/1.1, Host is required. */
return (synth(400));
}
if (req.method != "GET" &&
req.method != "HEAD" &&
req.method != "PUT" &&
req.method != "POST" &&
req.method != "TRACE" &&
req.method != "OPTIONS" &&
req.method != "DELETE" &&
req.method != "PATCH") {
/* Non-RFC2616 or CONNECT which is weird. */
return (pipe);
}
if (req.method != "GET" && req.method != "HEAD") {
/* We only deal with GET and HEAD by default */
return (pass);
}
if (req.http.Authorization || req.http.Cookie) {
/* Not cacheable by default */
return (pass);
}
return (hash);
}
}
sub vcl_backend_response {
# Happens after we have read the response headers from the backend.
#
# Here you clean the response headers, removing silly Set-Cookie headers
# and other mistakes your backend does..
if (bereq.http.host == "example.com" || bereq.http.host == "www.example.com") {
# Low TTL for objects with an error response code.
if (beresp.status == 403 || beresp.status == 404 || beresp.status >= 500) {
set beresp.ttl = 10s;
# mark as "hit_for_pass" for 10s
### set beresp.uncacheable = false;
return (deliver);
}
# Default TTL if the backend does not send Expires or max-age/s-max-age headers
if (!beresp.http.expires && beresp.http.cache-control !~ "max-age=") {
set beresp.ttl = 4h;
}
# grace time
### set beresp.grace = 1d;
# Exceptions
if (bereq.url ~ "\.(rss|xml|atom)(\?.*|)$") {
set beresp.ttl = 2h;
}
# Wordpress : no cache when WP cookie or "^/wp-(login|admin)" url
### include "/etc/varnish/conf.d/wordpress.backend_response.vcl";
# Uncomment if your site uses Prestashop
### include "/etc/varnish/conf.d/prestashop.backend_response.vcl";
# Uncomment if you want to do device detection
### include "/etc/varnish/conf.d/devicedetect.backend_response.vcl";
# builtin configuration
### include "/etc/varnish/conf.d/builtin.backend_response.vcl";
if (bereq.uncacheable) {
return (deliver);
} else if (beresp.ttl <= 0s ||
beresp.http.Set-Cookie ||
beresp.http.Surrogate-control ~ "no-store" ||
(!beresp.http.Surrogate-Control &&
beresp.http.Cache-Control ~ "no-cache|no-store|private") ||
beresp.http.Vary == "*") {
# Mark as "Hit-For-Miss" for the next 2 minutes
set beresp.ttl = 120s;
set beresp.uncacheable = true;
}
return (deliver);
}
}
sub vcl_deliver {
# Happens when we have all the pieces we need, and are about to send the
# response to the client.
#
# You can do accounting or modifying the final object here.
if (req.http.host == "example.com" || req.http.host == "www.example.com") {
# Uncomment if you want to do device detection
### include "/etc/varnish/conf.d/devicedetect.deliver.vcl";
# Tell wich config file has been used
{% if 'preprod' in group_names %}
set resp.http.X-Varnish-Config = "{{ site }}";
{% else %}
# Uncomment to enable
### set resp.http.X-Varnish-Config = "{{ site }}";
{% endif %}
return (deliver);
}
}

View File

@ -0,0 +1,293 @@
# {{ ansible_managed }}
#
# Inspired by https://gist.github.com/haproxytechblog/dc5c3b5e2801d36b79e00f07b2309c14
#
# This is an example HAProxy config for a Boost Proxy.
# You should copy it into your projetct and customize it.
######################################################################################
global
log /dev/log local0
log /dev/log local1 notice
chroot {{ haproxy_chroot }}
stats socket {{ haproxy_socket }} mode 660 level admin
stats timeout 30s
user haproxy
group haproxy
daemon
node {{ ansible_hostname }}
server-state-file {{ haproxy_server_state_file }}
maxconn 60000
# Default SSL material locations
ca-base /etc/ssl/certs
crt-base /etc/ssl/private
# generated 2021-05-03, Mozilla Guideline v5.6, HAProxy 2.2, OpenSSL 1.1.1d, intermediate configuration
# https://ssl-config.mozilla.org/#server=haproxy&version=2.2&config=intermediate&openssl=1.1.1d&guideline=5.6
ssl-default-bind-ciphers 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
ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
ssl-default-bind-options prefer-client-ciphers no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets
ssl-default-server-ciphers 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
ssl-default-server-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
ssl-default-server-options no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets
# curl https://ssl-config.mozilla.org/ffdhe2048.txt > /etc/ssl/dhparam-haproxy
ssl-dh-param-file /etc/ssl/dhparam-haproxy
defaults
log global
mode http
option httplog
option dontlognull
option log-separate-errors
option httpclose
option abortonclose
option http-server-close
option redispatch
timeout connect 5s
timeout client 300s
timeout server 300s
timeout queue 60s
timeout http-request 15s
load-server-state-from-file global
maxconn 50000
# Default options for "server" directive in backends
default-server maxconn 2000 on-error fail-check slowstart 60s inter 5s fastinter 1s downinter 1s weight 100 fall 3 rise 2
http-errors boost-default-errors
errorfile 400 /etc/haproxy/errors/400.http
errorfile 403 /etc/haproxy/errors/403.http
errorfile 408 /etc/haproxy/errors/408.http
errorfile 500 /etc/haproxy/errors/500.http
errorfile 502 /etc/haproxy/errors/502.http
errorfile 503 /etc/haproxy/errors/503.http
errorfile 504 /etc/haproxy/errors/504.http
resolvers local_dns
parse-resolv-conf
resolve_retries 3
timeout resolve 1s
timeout retry 1s
hold other 30s
hold refused 30s
hold nx 30s
hold timeout 30s
hold valid 10s
hold obsolete 30s
{% if haproxy_stats_enable %}
# BEGIN Stats section
{% if haproxy_stats_users %}
userlist stats_users
{% for user in haproxy_stats_users | default([]) %}
user {{ user.login }} password {{ user.password }}
{% endfor %}
{% endif %}
listen stats
mode http
bind {{ haproxy_stats_bind_directive }}
{% if haproxy_stats_internal_enable %}
bind {{ haproxy_stats_internal_host }}:{{ haproxy_stats_internal_port }}
{% endif %}
acl stats_access_ips src -f /etc/haproxy/stats_access_ips
acl stats_admin_ips src -f /etc/haproxy/stats_admin_ips
stats enable
stats refresh 10s
stats uri {{ haproxy_stats_path }}
stats show-legends
stats show-node
stats admin if stats_admin_ips
{% if haproxy_stats_users %}
acl stats_users http_auth(stats_users)
stats http-request auth realm "HAProxy admin" if !stats_access_ips !stats_users
{% else %}
stats http-request deny if !stats_access_ips
{% endif %}
http-request set-log-level silent
# END Stats section
{% endif %}
# This frontend is the main entry point from "origin" http clients
frontend external
bind 0.0.0.0:80,:::80
bind 0.0.0.0:443,:::443 ssl strict-sni alpn h2,http/1.1 crt /etc/haproxy/ssl
option forwardfor
errorfiles boost-default-errors
# Reject the request at the TCP level if source is in the denylist
tcp-request connection reject if { src -f /etc/haproxy/deny_ips }
# Remove a possible x-forwarded-for header already present
http-request del-header x-forwarded-for if { req.hdr(x-forwarded-for) -m found }
# Store the Host header in lowercase, to speedup ACL later
http-request set-var(req.hdr_host) req.hdr(host),lower
# Capture host header in logs
http-request capture var(req.hdr_host) len 32
# Is the request coming for the server itself (stats…)
acl server_hostname var(req.hdr_host) -m str {{ ansible_fqdn }} {{ ansible_hostname }}
acl munin var(req.hdr_host) -m str munin
# List of IP that will not go the maintenance backend
acl maintenance_ips src -f /etc/haproxy/maintenance_ips
# Detect Let's Encrypt challenge requests
acl letsencrypt path_dir -i /.well-known/acme-challenge
# Determine if the request is routable to Varnish
acl varnish_available nbsrv(varnish) gt 0
acl varnish_detected res.hdr(x-varnish) -m found
acl varnish_http_verb method GET HEAD PURGE
# Reject the request at the TCP level if source is in the denylist
tcp-request connection reject if { src -f /etc/haproxy/deny_ips }
http-request set-header x-forwarded-port %[dst_port]
http-request set-header x-forwarded-proto http if !{ ssl_fc }
http-request set-header x-forwarded-proto https if { ssl_fc }
# BEGIN HTTP tagging
acl xid_req_exists req.hdr(x-request-id) -m found
http-request set-var(txn.xid) req.hdr(x-request-id) if xid_req_exists
http-request set-var(txn.xid) uuid() unless xid_req_exists
http-request capture var(txn.xid) len 64
http-request set-header x-request-id %[var(txn.xid)] unless xid_req_exists
acl xid_res_exists res.hdr(x-request-id) -m found
http-after-response set-header x-request-id %[var(txn.xid)] unless xid_res_exists
http-request set-header x-boost-step1 "haproxy-external"
http-response set-header x-boost-step1 "haproxy-external; ssl-frontend" if { ssl_fc }
http-response set-header x-boost-step1 "haproxy-external; no-ssl-frontend" if !{ ssl_fc }
http-response set-header x-boost-server {{ ansible_hostname }}
# Full log line added in header to help debug
{% if 'preprod' in group_names %}
http-response set-header x-haproxy-log-external "%ci:%cp [%tr] %ft %b/%s %TR/%Tw/%Tc/%Tr/%Ta %ST %B %CC %CS %tsc %ac/%fc/%bc/%sc/%rc %sq/%bq %hr %hs %{+Q}r"
{% else %}
# Uncomment to enable
### http-response set-header x-haproxy-log-external "%ci:%cp [%tr] %ft %b/%s %TR/%Tw/%Tc/%Tr/%Ta %ST %B %CC %CS %tsc %ac/%fc/%bc/%sc/%rc %sq/%bq %hr %hs %{+Q}r"
{% endif %}
# END HTTP tagging
# Global maintenance mode
# Uncomment to enable
### use_backend maintenance unless maintenance_ips
use_backend local if server_hostname
use_backend local if munin
# "letsencrypt" must stay after "local"
use_backend letsencrypt if letsencrypt
{% for site in boost_sites_enabled %}
# BEGIN frontend_external section for site '{{ site }}'
{% include "templates/boost-sites/" + site + "/haproxy/frontend_external.j2" %}
{# Do not remove the blank line below #}
# END frontend_external section for site '{{ site }}'
{% endfor %}
default_backend goto_internal
# This is used as a fallback to go to the internal frontend
# This is not supposed to happen
backend goto_internal
server haproxy-internal {{ boost_haproxy_proxy_socket }} send-proxy-v2
# This frontend is only used when returning from varnish
frontend internal
bind {{ boost_haproxy_proxy_socket }} user root mode 666 accept-proxy
capture request header Host len 32
option forwardfor
# Check URL (used by Varnish)
monitor-uri {{ boost_haproxy_check_url | mandatory }}
# Store the Host header in lowercase, to speedup ACL later
http-request set-var(req.hdr_host) req.hdr(host),lower
# Capture host header in logs
http-request capture var(req.hdr_host) len 32
acl varnish_from req.hdr(x-varnish) -m found
acl forwarded_proto req.hdr(x-forwarded-proto) -m found
# Keep header if present and coming from Varnish
http-request set-header x-forwarded-proto %[req.hdr(x-forwarded-proto)] if forwarded_proto varnish_from
# BEGIN HTTP tagging
http-request set-header x-boost-step3 "haproxy-internal"
http-response set-header x-boost-step3 "haproxy-internal; ssl-backend" if { ssl_bc }
http-response set-header x-boost-step3 "haproxy-internal; no-ssl-backend" if !{ ssl_bc }
# Full log line added in header to help debug
{% if 'preprod' in group_names %}
http-response set-header x-haproxy-log-internal "%ci:%cp [%tr] %ft %b/%s %TR/%Tw/%Tc/%Tr/%Ta %ST %B %CC %CS %tsc %ac/%fc/%bc/%sc/%rc %sq/%bq %hr %hs %{+Q}r"
{% else %}
# Uncomment to enable
### http-response set-header x-haproxy-log-internal "%ci:%cp [%tr] %ft %b/%s %TR/%Tw/%Tc/%Tr/%Ta %ST %B %CC %CS %tsc %ac/%fc/%bc/%sc/%rc %sq/%bq %hr %hs %{+Q}r"
{% endif %}
# END HTTP tagging
{% for site in boost_sites_enabled %}
# BEGIN frontend_internal section for site '{{ site }}'
{% include "templates/boost-sites/" + site + "/haproxy/frontend_internal.j2" %}
{# Do not remove the blank line below #}
# END frontend_internal section for site '{{ site }}'
{% endfor %}
backend varnish
option httpchk HEAD {{ boost_varnish_check_url | mandatory }}
server varnish_sock {{ boost_varnish_proxy_socket }} check observe layer7 maxconn 3000 inter 1s send-proxy-v2
{% for site in boost_sites_enabled %}
# BEGIN backend section for site '{{ site }}'
{% include "templates/boost-sites/" + site + "/haproxy/backend.j2" %}
{# Do not remove the blank line below #}
# END backend section for site '{{ site }}'
{% endfor %}
backend letsencrypt
{% if boost_certificates_server is not defined or boost_certificates_server == inventory_hostname %}
server localhost 127.0.0.1:{{ boost_nginx_proxy_port | mandatory }} send-proxy-v2 maxconn 10
{% else %}
server {{ boost_certificates_server }} {{ hostvars[boost_certificates_server].ansible_host }}:{{ boost_nginx_proxy_port | mandatory }} maxconn 10
{% endif %}
backend local
option httpchk HEAD {{ boost_nginx_check_url | mandatory }}
server localhost 127.0.0.1:{{ boost_nginx_proxy_port | mandatory }} send-proxy-v2 maxconn 10
backend maintenance
http-request set-log-level silent
errorfile 503 /etc/haproxy/errors/maintenance.http

View File

@ -0,0 +1,15 @@
HTTP/1.0 400 Bad Request
Cache-Control: no-cache, no-store
Connection: close
Content-Type: text/html
<!doctype html>
<html lang="fr">
<head>
<title>400 Bad Request</title>
<meta charset="UTF-8">
</head>
<body>
<h1>400 Bad Request</h1>
</body>

View File

@ -0,0 +1,15 @@
HTTP/1.0 403 Forbidden
Cache-Control: no-cache, no-store
Connection: close
Content-Type: text/html
<!doctype html>
<html lang="fr">
<head>
<title>403 Forbidden</title>
<meta charset="UTF-8">
</head>
<body>
<h1>403 Forbidden</h1>
</body>

View File

@ -0,0 +1,15 @@
HTTP/1.0 404 Not found
Cache-Control: no-cache, no-store
Connection: close
Content-Type: text/html
<!doctype html>
<html lang="fr">
<head>
<title>404 Not found</title>
<meta charset="UTF-8">
</head>
<body>
<h1>404 Not found</h1>
</body>

View File

@ -0,0 +1,15 @@
HTTP/1.0 500 Internal Server Error
Cache-Control: no-cache, no-store
Connection: close
Content-Type: text/html
<!doctype html>
<html lang="fr">
<head>
<title>500 Internal Server Error</title>
<meta charset="UTF-8">
</head>
<body>
<h1>500 Internal Server Error</h1>
</body>

View File

@ -0,0 +1,15 @@
HTTP/1.0 502 Bad Gateway
Cache-Control: no-cache, no-store
Connection: close
Content-Type: text/html
<!doctype html>
<html lang="fr">
<head>
<title>502 Bad Gateway</title>
<meta charset="UTF-8">
</head>
<body>
<h1>502 Bad Gateway</h1>
</body>

View File

@ -0,0 +1,15 @@
HTTP/1.0 503 Service Unavailable
Cache-Control: no-cache, no-store
Connection: close
Content-Type: text/html
<!doctype html>
<html lang="fr">
<head>
<title>503 Service Unavailable</title>
<meta charset="UTF-8">
</head>
<body>
<h1>503 Service Unavailable</h1>
</body>

View File

@ -0,0 +1,15 @@
HTTP/1.0 504 Gateway Timeout
Cache-Control: no-cache, no-store
Connection: close
Content-Type: text/html
<!doctype html>
<html lang="fr">
<head>
<title>504 Gateway Timeout</title>
<meta charset="UTF-8">
</head>
<body>
<h1>504 Gateway Timeout</h1>
</body>

View File

@ -0,0 +1,15 @@
HTTP/1.0 503 Service Unavailable
Cache-Control: no-cache
Connection: close
Content-Type: text/html
<!doctype html>
<html lang="fr">
<head>
<title>Site Maintenance</title>
<meta charset="UTF-8">
</head>
<body>
<h1>Maintenance en cours</h1>
</body>

View File

@ -0,0 +1,59 @@
server {
listen {{ boost_nginx_proxy_port | mandatory }} proxy_protocol default_server;
set_real_ip_from 127.0.0.1;
real_ip_header proxy_protocol;
server_name {{ ansible_fqdn }};
index index.htm index.html;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
error_page 403 {{ nginx_default_redirect_url }};
root /var/www;
# Auth.
satisfy any;
include /etc/nginx/snippets/ipaddr_whitelist;
deny all;
auth_basic "Reserved {{ ansible_fqdn }}";
auth_basic_user_file /etc/nginx/snippets/private_htpasswd;
location / {
index index.html index.htm;
}
location /munin/ {
alias /var/cache/munin/www/;
add_header X-Frame-Options "SAMEORIGIN";
}
location = {{ boost_nginx_check_url | mandatory }} {
echo_status 200;
echo 'OK';
}
location ^~ /munin-cgi/munin-cgi-graph/ {
fastcgi_split_path_info ^(/munin-cgi/munin-cgi-graph)(.*);
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_pass unix:/var/run/munin/spawn-fcgi-munin-graph.sock;
include fastcgi_params;
}
include /etc/nginx/snippets/letsencrypt.conf;
}
server {
listen {{ boost_nginx_proxy_port | mandatory }} proxy_protocol;
server_name munin;
set_real_ip_from 127.0.0.1;
real_ip_header proxy_protocol;
location /server-status-{{ nginx_serverstatus_suffix | mandatory }} {
stub_status on;
access_log off;
}
}

View File

@ -0,0 +1,12 @@
if (bereq.uncacheable) {
return (deliver);
} else if (beresp.ttl <= 0s ||
beresp.http.Set-Cookie ||
beresp.http.Surrogate-control ~ "no-store" ||
(!beresp.http.Surrogate-Control &&
beresp.http.Cache-Control ~ "no-cache|no-store|private") ||
beresp.http.Vary == "*") {
# Mark as "Hit-For-Miss" for the next 2 minutes
set beresp.ttl = 120s;
set beresp.uncacheable = true;
}

View File

@ -0,0 +1,200 @@
/*-
* Copyright (c) 2006 Verdens Gang AS
* Copyright (c) 2006-2015 Varnish Software AS
* All rights reserved.
*
* Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This is the builtin VCL code
*/
vcl 4.0;
#######################################################################
# Client side
sub vcl_recv {
if (req.method == "PRI") {
/* This will never happen in properly formed traffic (see: RFC7540) */
return (synth(405));
}
if (!req.http.host &&
req.esi_level == 0 &&
req.proto ~ "^(?i)HTTP/1.1") {
/* In HTTP/1.1, Host is required. */
return (synth(400));
}
if (req.method != "GET" &&
req.method != "HEAD" &&
req.method != "PUT" &&
req.method != "POST" &&
req.method != "TRACE" &&
req.method != "OPTIONS" &&
req.method != "DELETE" &&
req.method != "PATCH") {
/* Non-RFC2616 or CONNECT which is weird. */
return (pipe);
}
if (req.method != "GET" && req.method != "HEAD") {
/* We only deal with GET and HEAD by default */
return (pass);
}
if (req.http.Authorization || req.http.Cookie) {
/* Not cacheable by default */
return (pass);
}
return (hash);
}
sub vcl_pipe {
# By default Connection: close is set on all piped requests, to stop
# connection reuse from sending future requests directly to the
# (potentially) wrong backend. If you do want this to happen, you can undo
# it here.
# unset bereq.http.connection;
return (pipe);
}
sub vcl_pass {
return (fetch);
}
sub vcl_hash {
hash_data(req.url);
if (req.http.host) {
hash_data(req.http.host);
} else {
hash_data(server.ip);
}
return (lookup);
}
sub vcl_purge {
return (synth(200, "Purged"));
}
sub vcl_hit {
if (obj.ttl >= 0s) {
// A pure unadulterated hit, deliver it
return (deliver);
}
if (obj.ttl + obj.grace > 0s) {
// Object is in grace, deliver it
// Automatically triggers a background fetch
return (deliver);
}
// fetch & deliver once we get the result
return (miss);
}
sub vcl_miss {
return (fetch);
}
sub vcl_deliver {
return (deliver);
}
/*
* We can come here "invisibly" with the following errors: 500 & 503
*/
sub vcl_synth {
set resp.http.Content-Type = "text/html; charset=utf-8";
set resp.http.Retry-After = "5";
set resp.body = {"<!DOCTYPE html>
<html>
<head>
<title>"} + resp.status + " " + resp.reason + {"</title>
</head>
<body>
<h1>Error "} + resp.status + " " + resp.reason + {"</h1>
<p>"} + resp.reason + {"</p>
<h3>Guru Meditation:</h3>
<p>XID: "} + req.xid + {"</p>
<hr>
<p>Varnish cache server</p>
</body>
</html>
"};
return (deliver);
}
#######################################################################
# Backend Fetch
sub vcl_backend_fetch {
if (bereq.method == "GET") {
unset bereq.body;
}
return (fetch);
}
sub vcl_backend_response {
if (bereq.uncacheable) {
return (deliver);
} else if (beresp.ttl <= 0s ||
beresp.http.Set-Cookie ||
beresp.http.Surrogate-control ~ "no-store" ||
(!beresp.http.Surrogate-Control &&
beresp.http.Cache-Control ~ "no-cache|no-store|private") ||
beresp.http.Vary == "*") {
# Mark as "Hit-For-Miss" for the next 2 minutes
set beresp.ttl = 120s;
set beresp.uncacheable = true;
}
return (deliver);
}
sub vcl_backend_error {
set beresp.http.Content-Type = "text/html; charset=utf-8";
set beresp.http.Retry-After = "5";
set beresp.body = {"<!DOCTYPE html>
<html>
<head>
<title>"} + beresp.status + " " + beresp.reason + {"</title>
</head>
<body>
<h1>Error "} + beresp.status + " " + beresp.reason + {"</h1>
<p>"} + beresp.reason + {"</p>
<h3>Guru Meditation:</h3>
<p>XID: "} + bereq.xid + {"</p>
<hr>
<p>Varnish cache server</p>
</body>
</html>
"};
return (deliver);
}
#######################################################################
# Housekeeping
sub vcl_init {
return (ok);
}
sub vcl_fini {
return (ok);
}

View File

@ -0,0 +1,29 @@
if (req.method == "PRI") {
/* This will never happen in properly formed traffic (see: RFC7540) */
return (synth(405));
}
if (!req.http.host &&
req.esi_level == 0 &&
req.proto ~ "^(?i)HTTP/1.1") {
/* In HTTP/1.1, Host is required. */
return (synth(400));
}
if (req.method != "GET" &&
req.method != "HEAD" &&
req.method != "PUT" &&
req.method != "POST" &&
req.method != "TRACE" &&
req.method != "OPTIONS" &&
req.method != "DELETE" &&
req.method != "PATCH") {
/* Non-RFC2616 or CONNECT which is weird. */
return (pipe);
}
if (req.method != "GET" && req.method != "HEAD") {
/* We only deal with GET and HEAD by default */
return (pass);
}
if (req.http.Authorization || req.http.Cookie) {
/* Not cacheable by default */
return (pass);
}

View File

@ -0,0 +1,7 @@
# Cleanup requests on static binary files and force serving from cache.
if (req.url ~ "\.(jpe?g|png|gif|ico|swf|gz|zip|rar|bz2|tgz|tbz|pdf|pls|torrent|mp4)(\?.*|)$") {
unset req.http.Authenticate;
unset req.http.POSTDATA;
unset req.http.cookie;
### set req.method = "GET";
}

View File

@ -0,0 +1,19 @@
# Low TTL for objects with an error response code.
if (beresp.status == 403 || beresp.status == 404 || beresp.status >= 500) {
set beresp.ttl = 10s;
# mark as "hit_for_pass" for 10s
### set beresp.uncacheable = false;
return (deliver);
}
set beresp.http.foo-bar "BAZ"
# Default TTL if the backend does not send any header.
if (!beresp.http.Cache-Control) {
set beresp.ttl = 1d;
}
# Exceptions
if (bereq.url ~ "\.(rss|xml|atom)(\?.*|)$") {
set beresp.ttl = 2h;
}

View File

@ -0,0 +1,18 @@
# so, this is a bit counterintuitive. The backend creates content based on
# the normalized User-Agent, but we use Vary on X-UA-Device so Varnish will
# use the same cached object for all U-As that map to the same X-UA-Device.
#
# If the backend does not mention in Vary that it has crafted special
# content based on the User-Agent (==X-UA-Device), add it.
# If your backend does set Vary: User-Agent, you may have to remove that here.
if (bereq.http.X-UA-Device) {
if (!beresp.http.Vary) { # no Vary at all
set beresp.http.Vary = "X-UA-Device";
} elseif (beresp.http.Vary !~ "X-UA-Device") { # add to existing Vary
set beresp.http.Vary = beresp.http.Vary + ", X-UA-Device";
}
}
# comment this out if you don't want the client to know your
# classification
set beresp.http.X-UA-Device = bereq.http.X-UA-Device;

View File

@ -0,0 +1,5 @@
# to keep any caches in the wild from serving wrong content to client #2
# behind them, we need to transform the Vary on the way out.
if ((req.http.X-UA-Device) && (resp.http.Vary)) {
set resp.http.Vary = regsub(resp.http.Vary, "X-UA-Device", "User-Agent");
}

View File

@ -0,0 +1,113 @@
#
# Copyright (c) 2016-2018 Varnish Cache project
# Copyright (c) 2012-2016 Varnish Software AS
#
# SPDX-License-Identifier: BSD-2-Clause
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# detectdevice.vcl - regex based device detection for Varnish
# https://github.com/varnishcache/varnish-devicedetect/
#
# Original author: Lasse Karstensen <lkarsten@varnish-software.com>
sub devicedetect {
unset req.http.X-UA-Device;
set req.http.X-UA-Device = "pc";
# Handle that a cookie may override the detection alltogether.
if (req.http.Cookie ~ "(?i)X-UA-Device-force") {
/* ;?? means zero or one ;, non-greedy to match the first. */
set req.http.X-UA-Device = regsub(req.http.Cookie, "(?i).*X-UA-Device-force=([^;]+);??.*", "\1");
/* Clean up our mess in the cookie header */
set req.http.Cookie = regsuball(req.http.Cookie, "(^|; ) *X-UA-Device-force=[^;]+;? *", "\1");
/* If the cookie header is now empty, or just whitespace, unset it. */
if (req.http.Cookie ~ "^ *$") { unset req.http.Cookie; }
} else {
if (req.http.User-Agent ~ "\(compatible; Googlebot-Mobile/2.1; \+http://www.google.com/bot.html\)" ||
(req.http.User-Agent ~ "(Android|iPhone)" && req.http.User-Agent ~ "\(compatible.?; Googlebot/2.1.?; \+http://www.google.com/bot.html") ||
(req.http.User-Agent ~ "(iPhone|Windows Phone)" && req.http.User-Agent ~ "\(compatible; bingbot/2.0; \+http://www.bing.com/bingbot.htm")) {
set req.http.X-UA-Device = "mobile-bot"; }
elsif (req.http.User-Agent ~ "(?i)(ads|google|bing|msn|yandex|baidu|ro|career|seznam|)bot" ||
req.http.User-Agent ~ "(?i)(baidu|jike|symantec)spider" ||
req.http.User-Agent ~ "(?i)pingdom" ||
req.http.User-Agent ~ "(?i)facebookexternalhit" ||
req.http.User-Agent ~ "(?i)scanner" ||
req.http.User-Agent ~ "(?i)slurp" ||
req.http.User-Agent ~ "(?i)(web)crawler") {
set req.http.X-UA-Device = "bot"; }
elsif (req.http.User-Agent ~ "(?i)ipad") { set req.http.X-UA-Device = "tablet-ipad"; }
elsif (req.http.User-Agent ~ "(?i)ip(hone|od)") { set req.http.X-UA-Device = "mobile-iphone"; }
/* how do we differ between an android phone and an android tablet?
http://stackoverflow.com/questions/5341637/how-do-detect-android-tablets-in-general-useragent */
elsif (req.http.User-Agent ~ "(?i)android.*(mobile|mini)") { set req.http.X-UA-Device = "mobile-android"; }
// android 3/honeycomb was just about tablet-only, and any phones will probably handle a bigger page layout.
elsif (req.http.User-Agent ~ "(?i)android 3") { set req.http.X-UA-Device = "tablet-android"; }
/* Opera Mobile */
elsif (req.http.User-Agent ~ "Opera Mobi") { set req.http.X-UA-Device = "mobile-smartphone"; }
// May very well give false positives towards android tablets. Suggestions welcome.
elsif (req.http.User-Agent ~ "(?i)android") { set req.http.X-UA-Device = "tablet-android"; }
elsif (req.http.User-Agent ~ "PlayBook; U; RIM Tablet") { set req.http.X-UA-Device = "tablet-rim"; }
elsif (req.http.User-Agent ~ "hp-tablet.*TouchPad") { set req.http.X-UA-Device = "tablet-hp"; }
elsif (req.http.User-Agent ~ "Kindle/3") { set req.http.X-UA-Device = "tablet-kindle"; }
elsif (req.http.User-Agent ~ "Touch.+Tablet PC" ||
req.http.User-Agent ~ "Windows NT [0-9.]+; ARM;" ) {
set req.http.X-UA-Device = "tablet-microsoft";
}
elsif (req.http.User-Agent ~ "Mobile.+Firefox") { set req.http.X-UA-Device = "mobile-firefoxos"; }
elsif (req.http.User-Agent ~ "^HTC" ||
req.http.User-Agent ~ "Fennec" ||
req.http.User-Agent ~ "IEMobile" ||
req.http.User-Agent ~ "BlackBerry" ||
req.http.User-Agent ~ "BB10.*Mobile" ||
req.http.User-Agent ~ "GT-.*Build/GINGERBREAD" ||
req.http.User-Agent ~ "SymbianOS.*AppleWebKit") {
set req.http.X-UA-Device = "mobile-smartphone";
}
elsif (req.http.User-Agent ~ "(?i)symbian" ||
req.http.User-Agent ~ "(?i)^sonyericsson" ||
req.http.User-Agent ~ "(?i)^nokia" ||
req.http.User-Agent ~ "(?i)^samsung" ||
req.http.User-Agent ~ "(?i)^lg" ||
req.http.User-Agent ~ "(?i)bada" ||
req.http.User-Agent ~ "(?i)blazer" ||
req.http.User-Agent ~ "(?i)cellphone" ||
req.http.User-Agent ~ "(?i)iemobile" ||
req.http.User-Agent ~ "(?i)midp-2.0" ||
req.http.User-Agent ~ "(?i)u990" ||
req.http.User-Agent ~ "(?i)netfront" ||
req.http.User-Agent ~ "(?i)opera mini" ||
req.http.User-Agent ~ "(?i)palm" ||
req.http.User-Agent ~ "(?i)nintendo wii" ||
req.http.User-Agent ~ "(?i)playstation portable" ||
req.http.User-Agent ~ "(?i)portalmmm" ||
req.http.User-Agent ~ "(?i)proxinet" ||
req.http.User-Agent ~ "(?i)windows\ ?ce" ||
req.http.User-Agent ~ "(?i)winwap" ||
req.http.User-Agent ~ "(?i)eudoraweb" ||
req.http.User-Agent ~ "(?i)htc" ||
req.http.User-Agent ~ "(?i)240x320" ||
req.http.User-Agent ~ "(?i)avantgo") {
set req.http.X-UA-Device = "mobile-generic";
}
}
}

View File

@ -0,0 +1,48 @@
## From https://github.com/CleverCloud/varnish-examples/blob/master/prestashop.vcl
# Remove some headers we never want to see
unset beresp.http.Server;
unset beresp.http.X-Powered-By;
# For static content strip all backend cookies
if (bereq.url ~ "\.(css|js|png|gif|jp(e?)g)|swf|ico|woff") {
unset beresp.http.cookie;
}
# Don't store backend
if (bereq.url ~ "admin70" || bereq.url ~ "preview=true") {
set beresp.uncacheable = true;
set beresp.ttl = 30s;
return (deliver);
}
if (bereq.method == "GET" && (bereq.url ~ "^/?mylogout=")) {
set beresp.ttl = 0s;
unset beresp.http.Set-Cookie;
set beresp.uncacheable = true;
return (deliver);
}
# don't cache response to posted requests or those with basic auth
if ( bereq.method == "POST" || bereq.http.Authorization ) {
set beresp.uncacheable = true;
set beresp.ttl = 120s;
return (deliver);
}
# don't cache search results
if ( bereq.url ~ "\?s=" ){
set beresp.uncacheable = true;
set beresp.ttl = 120s;
return (deliver);
}
# only cache status ok
if ( beresp.status != 200 ) {
set beresp.uncacheable = true;
set beresp.ttl = 120s;
return (deliver);
}
# A TTL of 2h
set beresp.ttl = 2h;
# Define the default grace period to serve cached content
set beresp.grace = 30s;

View File

@ -0,0 +1,87 @@
## From https://github.com/CleverCloud/varnish-examples/blob/master/prestashop.vcl
# Normalize the header, remove the port (in case you're testing this on various TCP ports)
set req.http.Host = regsub(req.http.Host, ":[0-9]+", "");
# Remove has_js and CloudFlare/Google Analytics __* cookies.
set req.http.Cookie = regsuball(req.http.Cookie, "(^|;\s*)(_[_a-z]+|has_js)=[^;]*", "");
# Remove a ";" prefix, if present.
set req.http.Cookie = regsub(req.http.Cookie, "^;\s*", "");
# Allow purging from ACL
if (req.method == "PURGE") {
# If not allowed then a error 405 is returned
if (client.ip != "127.0.0.1" ) {
return (synth(405, "This IP is not allowed to send PURGE requests."));
}
# If allowed, do a cache_lookup -> vlc_hit() or vlc_miss()
return (purge);
}
# Post requests will not be cached
if (req.http.Authorization || req.method == "POST") {
return (pass);
}
if (req.method == "GET" && (req.url ~ "^/?mylogout=")) {
unset req.http.Cookie;
return (pass);
}
#we should not cache any page for Prestashop backend
if (req.method == "GET" && (req.url ~ "^/admin70")) {
return (pass);
}
#we should not cache any page for customers
if (req.method == "GET" && (req.url ~ "^/authentification" || req.url ~ "^/my-account")) {
return (pass);
}
#we should not cache any page for customers
if (req.method == "GET" && (req.url ~ "^/identity" || req.url ~ "^/my-account.php")) {
return (pass);
}
#we should not cache any page for sales
if (req.method == "GET" && (req.url ~ "^/cart.php" || req.url ~ "^/order.php")) {
return (pass);
}
#we should not cache any page for sales
if (req.method == "GET" && (req.url ~ "^/addresses.php" || req.url ~ "^/order-detail.php")) {
return (pass);
}
#we should not cache any page for sales
if (req.method == "GET" && (req.url ~ "^/order-confirmation.php" || req.url ~ "^/order-return.php")) {
return (pass);
}
if (req.method != "GET" && req.method != "HEAD") {
return (pass);
}
# Remove the "has_js" cookie
set req.http.Cookie = regsuball(req.http.Cookie, "has_js=[^;]+(; )?", "");
# Remove any Google Analytics based cookies
# set req.http.Cookie = regsuball(req.http.Cookie, "__utm.=[^;]+(; )?", "");
# removes all cookies named __utm? (utma, utmb...) - tracking thing
set req.http.Cookie = regsuball(req.http.Cookie, "(^|(?<=; )) *__utm.=[^;]+;? *", "\1");
# Remove a ";" prefix, if present.
set req.http.Cookie = regsub(req.http.Cookie, "^;\s*", "");
# Are there cookies left with only spaces or that are empty?
if (req.http.Cookie ~ "^ *$") {
unset req.http.Cookie;
}
# Cache the following files extensions
if (req.url ~ "\.(css|js|png|gif|jp(e)?g|swf|ico|woff)") {
unset req.http.Cookie;
}
# Normalize Accept-Encoding header and compression
# https://www.varnish-cache.org/docs/3.0/tutorial/vary.html
if (req.http.Accept-Encoding) {
# Do no compress compressed files...
if (req.url ~ "\.(jpg|png|gif|gz|tgz|bz2|tbz|mp3|ogg)$") {
unset req.http.Accept-Encoding;
} elsif (req.http.Accept-Encoding ~ "gzip") {
set req.http.Accept-Encoding = "gzip";
} elsif (req.http.Accept-Encoding ~ "deflate") {
set req.http.Accept-Encoding = "deflate";
} else {
unset req.http.Accept-Encoding;
}
}
# Did not cache HTTP authentication and HTTP Cookie
if (req.http.Authorization) {
# Not cacheable by default
return (pass);
}

View File

@ -0,0 +1,4 @@
# respect Cache-Control from client
if (req.http.Cache-Control ~ "(private|no-cache|no-store)" || req.http.Pragma == "no-cache") {
return (pass);
}

View File

@ -0,0 +1,4 @@
if (bereq.url ~ "wp-(login|admin)" || bereq.url ~ "preview=true" || bereq.http.Cookie ~ "wordpress_logged_in_" ) {
set beresp.uncacheable = true;
set beresp.ttl = 0s;
}

View File

@ -0,0 +1,3 @@
if (req.url ~ "^/wp-(login|admin)" || req.url ~ "preview=true" || req.http.Cookie ~ "wordpress_logged_in_" ) {
return (pass);
}

View File

@ -0,0 +1,176 @@
vcl 4.1;
probe haproxycheck {
.request =
"HEAD {{ boost_haproxy_check_url | mandatory }} HTTP/1.1"
"Connection: close";
.timeout = 1s;
.interval = 3s;
.window = 3;
.threshold = 2;
}
backend default {
.path = "{{ boost_haproxy_proxy_socket }}";
.proxy_header = 1;
.connect_timeout = 3s;
.first_byte_timeout = 300s;
.between_bytes_timeout = 300s;
.probe = haproxycheck;
}
# Uncomment if you want to do device detection
# cf. https://varnish-cache.org/docs/6.0/users-guide/devicedetection.html
### include "/etc/varnish/conf.d/devicedetect.functions.vcl";
# Routines appliquées dans tous les cas
# A modifier avec précaution
# Ne pas mettre de "return" dans ces routines communes
sub vcl_recv {
# Health check from HAProxy
if (req.url == "{{ boost_varnish_check_url | mandatory }}") {
return (synth(200, "Hi HAProxy, I'm fine!"));
}
# Normalize encoding, and unset it on yet-compressed formats.
if (req.http.Accept-Encoding) {
if (req.url ~ "\.(jpg|jpeg|png|gif|gz|tgz|bz2|lzma|tbz|zip|rar)(\?.*|)$") {
unset req.http.Accept-Encoding;
}
# use gzip when possible, otherwise use deflate
if (req.http.Accept-Encoding ~ "gzip") {
set req.http.Accept-Encoding = "gzip";
}
elsif (req.http.Accept-Encoding ~ "deflate") {
set req.http.Accept-Encoding = "deflate";
}
else {
# unknown algorithm, unset accept-encoding header
unset req.http.Accept-Encoding;
}
}
# Remove known cookies used only on client side (by JavaScript).
if (req.http.cookie) {
set req.http.Cookie = regsuball(req.http.Cookie, "_gat=[^;]+(; )?", ""); # Google Analytics
set req.http.Cookie = regsuball(req.http.Cookie, "_ga=[^;]+(; )?", ""); # Google Analytics
set req.http.Cookie = regsuball(req.http.Cookie, "_gaq=[^;]+(; )?", ""); # Google Analytics
set req.http.Cookie = regsuball(req.http.Cookie, "__utm[^=]*=[^;]+(; )?", ""); # Google Analytics
set req.http.Cookie = regsuball(req.http.Cookie, "__gads=[^;]+(; )?", ""); # Google Doubleclick
set req.http.Cookie = regsuball(req.http.Cookie, "__auc=[^;]+(; )?", ""); # Alexa Analytics
if (req.http.cookie ~ "^ *$") {
unset req.http.cookie;
}
}
# BEGIN HTTP tagging
# TODO: ajouter un en-tête pour marquer le passage dans les 3 composants
# avec une artie permanente et une partie optionnelle commentée par défaut
set req.http.X-Boost-Layer = "varnish";
# END HTTP tagging
}
sub vcl_backend_response {
if (beresp.uncacheable) {
set beresp.http.X-Cacheable = "FALSE";
} else {
set beresp.http.X-Cacheable = "TRUE";
}
# our default TTL is 60s instead of 86400s
if (beresp.http.cache-control !~ "max-age=") {
set beresp.ttl = 60s;
}
# Grace mode (Stale content delivery)
# abandon when 5xx errors to keep grace mode even 503 from HAProxy
if (beresp.status >= 500 && bereq.is_bgfetch) {
return (abandon);
}
set beresp.grace = 4h;
}
sub vcl_deliver {
unset resp.http.Via;
# BEGIN HTTP tagging
# TODO: ajouter un en-tête pour marquer le passage dans les 3 composants
# avec une partie permanente et une partie optionnelle commentée par défaut
if (resp.http.Set-Cookie && resp.http.Cache-Control) {
set resp.http.X-Boost-Step2 = "varnish; set-cookie; cache-control";
} elseif (resp.http.Set-Cookie) {
set resp.http.X-Boost-Step2 = "varnish; set-cookie; no-cache-control";
} elseif (resp.http.Cache-Control) {
set resp.http.X-Boost-Step2 = "varnish; no-set-cookie; cache-control";
} else {
set resp.http.X-Boost-Step2 = "varnish; no-set-cookie; no-cache-control";
}
# END HTTP tagging
if (resp.http.X-Varnish ~ "[0-9]+ +[0-9]+") {
set resp.http.X-Cache = "HIT";
unset resp.http.X-Boost-Step3;
} else {
set resp.http.X-Cache = "MISS";
}
# DEBUG infos
## désactivation pour test
## https://github.com/varnishcache/varnish-cache/issues/3765
# set resp.http.X-Varnish-Client-Ip = client.ip;
set resp.http.X-Varnish-Client-Method = req.method;
set resp.http.X-Varnish-Client-Url = req.url;
set resp.http.X-Varnish-Client-Proto = req.proto;
set resp.http.X-Varnish-Object-Ttl = obj.ttl;
{% if 'preprod' in group_names %}
set resp.http.X-Varnish-Client-Cache-Control = req.http.cache-control;
set resp.http.X-Varnish-Client-Cookie = req.http.cookie;
set resp.http.X-Varnish-Client-Ua = req.http.user-agent;
set resp.http.X-Varnish-Object-Grace = obj.grace;
set resp.http.X-Varnish-Object-Keep = obj.keep;
set resp.http.X-Varnish-Object-Storage = obj.storage;
{% else %}
### set resp.http.X-Varnish-Client-Cache-Control = req.http.cache-control;
### set resp.http.X-Varnish-Client-Cookie = req.http.cookie;
### set resp.http.X-Varnish-Client-Ua = req.http.user-agent;
### set resp.http.X-Varnish-Object-Grace = obj.grace;
### set resp.http.X-Varnish-Object-Keep = obj.keep;
### set resp.http.X-Varnish-Object-Storage = obj.storage;
{% endif %}
}
# BEGIN sites
{% for site_name in boost_sites_enabled %}
include "/etc/varnish/sites/{{ site_name }}.vcl";
{% endfor %}
# END sites
# Routines personnalisées, appliquées en "fallback"
# TODO: mieux expliquer !
sub vcl_backend_error {
set beresp.http.Content-Type = "text/html; charset=utf-8";
set beresp.http.Retry-After = "5";
set beresp.body = {"<!DOCTYPE html>
<html>
<head>
<title>"} + beresp.status + " " + beresp.reason + {"</title>
</head>
<body>
<h1>Error "} + beresp.status + " " + beresp.reason + {"</h1>
<p>"} + beresp.reason + {"</p>
<h3>EvoGuru Meditation:</h3>
<p>XID: "} + bereq.xid + {"</p>
<hr>
<p>Varnish cache server</p>
</body>
</html>
"};
return (deliver);
}
# Les routines internes de Varnish sont visibles avec la commande
# varnishd -x builtin

View File

@ -162,7 +162,6 @@
tags:
- varnish
- config
- update-config
- name: Copy included Varnish config
template: