First version in Galaxy
This commit is contained in:
5
seb4itik/byow/roles/caddy/README.md
Normal file
5
seb4itik/byow/roles/caddy/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# BYOW - Build Your Own WAF
|
||||
|
||||
A WAF _à la carte_.
|
||||
|
||||
## Caddy
|
||||
408
seb4itik/byow/roles/caddy/defaults/main.yml
Normal file
408
seb4itik/byow/roles/caddy/defaults/main.yml
Normal file
@@ -0,0 +1,408 @@
|
||||
# Defaults
|
||||
caddy_config_only: false
|
||||
caddy_go_version: "1.25.5"
|
||||
caddy_xcaddy_version: "0.4.5"
|
||||
caddy_caddy_version: "2.10.2"
|
||||
caddy_coraza_caddy_version: "2.1.0" # Coraza v3.3.3
|
||||
caddy_owasp_crs_version: "4.22.0"
|
||||
caddy_system_user: "byow"
|
||||
caddy_system_group: "byow"
|
||||
caddy_home_dir: "/opt/{{ caddy_system_user }}"
|
||||
caddy_config_dir: "/etc/{{ caddy_system_user }}"
|
||||
caddy_log_dir: "/var/log/{{ caddy_system_user }}"
|
||||
|
||||
# Cf.: https://github.com/coreruleset/plugin-registry
|
||||
caddy_crs_plugins:
|
||||
- name: "drupal-rule-exclusions"
|
||||
version: "1.0.0"
|
||||
- name: "wordpress-rule-exclusions"
|
||||
version: "1.2.0"
|
||||
- name: "nextcloud-rule-exclusions"
|
||||
version: "1.5.0"
|
||||
- name: "dokuwiki-rule-exclusions"
|
||||
version: "1.0.0"
|
||||
- name: "phpmyadmin-rule-exclusions"
|
||||
version: "1.0.0"
|
||||
has_after_config: true
|
||||
- name: "roundcube-rule-exclusions"
|
||||
version: "1.0.4"
|
||||
provider: "EsadCetiner"
|
||||
- name: "sogo-rule-exclusions"
|
||||
version: "1.0.4"
|
||||
provider: "EsadCetiner"
|
||||
|
||||
# Required unless not site.custom_cert
|
||||
caddy_email: null
|
||||
|
||||
# Required if site.bot_barrier
|
||||
caddy_bot_barrier_secret: null
|
||||
|
||||
# Optionnal
|
||||
caddy_local_ca_name: null
|
||||
caddy_ca_root: null
|
||||
|
||||
caddy_sites: []
|
||||
# - name: ""
|
||||
# site: ""
|
||||
# paths:
|
||||
# - path: "*"
|
||||
# addrs:
|
||||
# - ""
|
||||
# blacklist:
|
||||
# - ""
|
||||
# aliases:
|
||||
# - ""
|
||||
# crs_exceptions:
|
||||
# before_request:
|
||||
# - ""
|
||||
# after_response:
|
||||
# - ""
|
||||
|
||||
caddy_waf_defaults:
|
||||
redirect_from_aliases: true
|
||||
custom_cert: false
|
||||
custom_cert_file: ""
|
||||
custom_key_file: ""
|
||||
|
||||
# Security headers
|
||||
header_content_security_policy:
|
||||
|
||||
# https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html#detailed-csp-directives
|
||||
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy#directives
|
||||
## Fetch directives
|
||||
child-src: "'self'"
|
||||
connect-src: "'self'"
|
||||
font-src: "'self'"
|
||||
img-src: "'self'"
|
||||
manifest-src: "'self'"
|
||||
media-src: "'self'"
|
||||
# Deprecated: prefetch-src: "'self'"
|
||||
object-src: "'none'"
|
||||
script-src: "'none'"
|
||||
script-src-attr: "'none'"
|
||||
script-src-elem: "'none'"
|
||||
style-src: "'self'"
|
||||
style-src-attr: "'self'"
|
||||
style-src-elem: "'self'"
|
||||
default-src: "'self'"
|
||||
## Document directives
|
||||
# base-uri
|
||||
# plugin-types
|
||||
# sandbox
|
||||
## Navigation directives
|
||||
form-action: "'self'"
|
||||
frame-ancestors: "'self'"
|
||||
# https://developer.mozilla.org/fr/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/frame-src
|
||||
frame-src: "'self'"
|
||||
# https://developer.mozilla.org/fr/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/worker-src
|
||||
worker-src: "'self'"
|
||||
# https://developer.mozilla.org/fr/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/block-all-mixed-content
|
||||
block-all-mixed-content:
|
||||
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/fenced-frame-src
|
||||
fenced-frame-src: "'self'"
|
||||
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/upgrade-insecure-requests
|
||||
upgrade-insecure-requests:
|
||||
|
||||
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cross-Origin-Embedder-Policy
|
||||
header_cross_origin_embedder_policy: "require-corp"
|
||||
|
||||
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cross-Origin-Opener-Policy
|
||||
header_cross_origin_opener_policy: "same-origin"
|
||||
|
||||
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cross-Origin-Resource-Policy
|
||||
header_cross_origin_resource_policy: "same-site"
|
||||
|
||||
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Permissions-Policy
|
||||
header_permissions_policy:
|
||||
accelerometer: "()"
|
||||
ambient-light-sensor: "()"
|
||||
aria-notify: "()"
|
||||
attribution-reporting: "()"
|
||||
autoplay: "()"
|
||||
bluetooth: "()"
|
||||
browsing-topics: "()"
|
||||
camera: "()"
|
||||
captured-surface-control: "()"
|
||||
compute-pressure: "()"
|
||||
cross-origin-isolated: "()"
|
||||
deferred-fetch: "()"
|
||||
deferred-fetch-minimal: "()"
|
||||
display-capture: "()"
|
||||
encrypted-media: "()"
|
||||
fullscreen: "()"
|
||||
gamepad: "()"
|
||||
geolocation: "()"
|
||||
gyroscope: "()"
|
||||
hid: "()"
|
||||
identity-credentials-get: "()"
|
||||
idle-detection: "()"
|
||||
language-detector: "()"
|
||||
local-fonts: "()"
|
||||
magnetometer: "()"
|
||||
microphone: "()"
|
||||
midi: "()"
|
||||
on-device-speech-recognition: "()"
|
||||
otp-credentials: "()"
|
||||
payment: "()"
|
||||
picture-in-picture: "()"
|
||||
private-state-token-issuance: "()"
|
||||
private-state-token-redemption: "()"
|
||||
publickey-credentials-create: "()"
|
||||
publickey-credentials-get: "()"
|
||||
screen-wake-lock: "()"
|
||||
serial: "()"
|
||||
speaker-selection: "()"
|
||||
storage-access: "()"
|
||||
translator: "()"
|
||||
summarizer: "()"
|
||||
usb: "()"
|
||||
web-share: "()"
|
||||
window-management: "()"
|
||||
xr-spatial-tracking: "()"
|
||||
|
||||
# https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#floc-federated-learning-of-cohorts
|
||||
interest-cohort: "()"
|
||||
|
||||
# ???
|
||||
battery: "()"
|
||||
document-domain: "()"
|
||||
execution-while-not-rendered: "()"
|
||||
execution-while-out-of-viewport: "()"
|
||||
navigation-override: "()"
|
||||
|
||||
# https://www.geeksforgeeks.org/computer-networks/http-headers-feature-policy/
|
||||
header_feature_policy:
|
||||
accelerometer: "'none'"
|
||||
ambient-light-sensor: "'none'"
|
||||
autoplay: "'none'"
|
||||
battery: "'none'"
|
||||
camera: "'none'"
|
||||
display-capture: "'none'"
|
||||
document-domain: "'none'"
|
||||
encrypted-media: "'none'"
|
||||
fullscreen: "'none'"
|
||||
geolocation: "'none'"
|
||||
gyroscope: "'none'"
|
||||
layout-animation: "'none'"
|
||||
legacy-image-formats: "'none'"
|
||||
magnetometer: "'none'"
|
||||
microphone: "'none'"
|
||||
midi: "'none'"
|
||||
oversized-images: "'none'"
|
||||
payment: "'none'"
|
||||
picture-in-picture: "'none'"
|
||||
publickey-credentials-get: "'none'"
|
||||
sync-xhr: "'none'"
|
||||
usb: "'none'"
|
||||
screen-wake-lock: "'none'"
|
||||
xr-spatial-tracking: "'none'"
|
||||
|
||||
# ???
|
||||
execution-while-not-rendered: "'none'"
|
||||
execution-while-out-of-viewport: "'none'"
|
||||
navigation-override: "'none'"
|
||||
speaker-selection: "'none'"
|
||||
unoptimized-images: "'none'"
|
||||
unsized-media: "'none'"
|
||||
web-share: "'none'"
|
||||
|
||||
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Referrer-Policy
|
||||
header_referrer_policy: "strict-origin-when-cross-origin"
|
||||
|
||||
# https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Strict_Transport_Security_Cheat_Sheet.html
|
||||
header_strict_transport_security: "max-age=2635200; includeSubDomains"
|
||||
|
||||
# https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-content-type-options
|
||||
header_x_content_type_options: "nosniff"
|
||||
|
||||
# https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-frame-options
|
||||
header_x_frame_options: "DENY"
|
||||
|
||||
# https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-robots-tag
|
||||
header_x_robots_tag: "noindex, nofollow"
|
||||
|
||||
# https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#content-type
|
||||
default_content_type: "text/html; charset=UTF-8"
|
||||
|
||||
# https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#set-cookie
|
||||
cookies_attributes: "SameSite=None; Secure;"
|
||||
|
||||
# https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-dns-prefetch-control
|
||||
# FIXME
|
||||
|
||||
# https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#server
|
||||
# https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-aspnet-version
|
||||
# https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-aspnetmvc-version
|
||||
# https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-powered-by
|
||||
delete_headers:
|
||||
- "Server"
|
||||
- "Via"
|
||||
- "X-AspNet-Version"
|
||||
- "X-AspNetMvc-Version"
|
||||
- "X-Powered-By"
|
||||
|
||||
custom_headers: {}
|
||||
# - name: <string>
|
||||
# value: <string>
|
||||
|
||||
# caddy-ratelimit
|
||||
rate_events: 1000
|
||||
rate_window: "1m"
|
||||
|
||||
# steffenbusch/caddy-bot-barrier
|
||||
bot_barrier: false
|
||||
|
||||
# corazawaf/coraza-caddy
|
||||
enable_crs: true
|
||||
crs_plugins: []
|
||||
log_only: false
|
||||
paranoia_level: 1
|
||||
detection_paranoia_level: null
|
||||
enforce_bodyproc_urlencoded: 1
|
||||
critical_anomaly_score: 5
|
||||
error_anomaly_score: 4
|
||||
warning_anomaly_score: 3
|
||||
notice_anomaly_score: 2
|
||||
inbound_anomaly_score_threshold: 5
|
||||
outbound_anomaly_score_threshold: 4
|
||||
reporting_level: 4
|
||||
max_num_args: 255
|
||||
arg_name_length: 100
|
||||
arg_length: 1000
|
||||
total_arg_length: 64000
|
||||
max_file_size: 10485760 # 10 Mo
|
||||
combined_file_sizes: 20970520 # 20 Mo
|
||||
sampling_percentage: 100
|
||||
allowed_methods:
|
||||
- "GET"
|
||||
- "HEAD"
|
||||
- "POST"
|
||||
- "OPTIONS"
|
||||
allowed_request_content_type:
|
||||
- "|application/csp-report|"
|
||||
- "|application/json|"
|
||||
- "|application/reports+json|"
|
||||
- "|application/soap+xml|"
|
||||
- "|application/x-www-form-urlencoded|"
|
||||
- "|application/xml|"
|
||||
- "|multipart/form-data|"
|
||||
- "|text/xml|"
|
||||
restricted_extensions:
|
||||
- ".ani/"
|
||||
- ".asa/"
|
||||
- ".asax/"
|
||||
- ".ascx/"
|
||||
- ".back/"
|
||||
- ".backup/"
|
||||
- ".bak/"
|
||||
- ".bat/"
|
||||
- ".bck/"
|
||||
- ".bk/"
|
||||
- ".bkp/"
|
||||
- ".cdx/"
|
||||
- ".cer/"
|
||||
- ".cfg/"
|
||||
- ".cmd/"
|
||||
- ".cnf/"
|
||||
- ".com/"
|
||||
- ".compositefont/"
|
||||
- ".conf/"
|
||||
- ".config/"
|
||||
- ".copy/"
|
||||
- ".crt/"
|
||||
- ".cs/"
|
||||
- ".csproj/"
|
||||
- ".csr/"
|
||||
- ".dat/"
|
||||
- ".db/"
|
||||
- ".dbf/"
|
||||
- ".dist/"
|
||||
- ".dll/"
|
||||
- ".dos/"
|
||||
- ".dpkg-dist/"
|
||||
- ".drv/"
|
||||
- ".gadget/"
|
||||
- ".hta/"
|
||||
- ".htr/"
|
||||
- ".htw/"
|
||||
- ".ida/"
|
||||
- ".idc/"
|
||||
- ".idq/"
|
||||
- ".inc/"
|
||||
- ".inf/"
|
||||
- ".ini/"
|
||||
- ".jse/"
|
||||
- ".key/"
|
||||
- ".licx/"
|
||||
- ".lnk/"
|
||||
- ".log/"
|
||||
- ".mdb/"
|
||||
- ".msc/"
|
||||
- ".ocx/"
|
||||
- ".old/"
|
||||
- ".pass/"
|
||||
- ".pdb/"
|
||||
- ".pem/"
|
||||
- ".pfx/"
|
||||
- ".pif/"
|
||||
- ".pol/"
|
||||
- ".prf/"
|
||||
- ".printer/"
|
||||
- ".pwd/"
|
||||
- ".rdb/"
|
||||
- ".rdp/"
|
||||
- ".reg/"
|
||||
- ".resources/"
|
||||
- ".resx/"
|
||||
- ".sav/"
|
||||
- ".save/"
|
||||
- ".scr/"
|
||||
- ".sct/"
|
||||
- ".sh/"
|
||||
- ".shs/"
|
||||
- ".sql/"
|
||||
- ".sqlite/"
|
||||
- ".sqlite3/"
|
||||
- ".swp/"
|
||||
- ".sys/"
|
||||
- ".temp/"
|
||||
- ".tlb/"
|
||||
- ".tmp/"
|
||||
- ".vb/"
|
||||
- ".vbe/"
|
||||
- ".vbproj/"
|
||||
- ".vbs/"
|
||||
- ".vsdisco/"
|
||||
- ".vxd/"
|
||||
- ".webinfo/"
|
||||
- ".ws/"
|
||||
- ".wsc/"
|
||||
- ".wsf/"
|
||||
- ".wsh/"
|
||||
- ".xsd/"
|
||||
- ".xsx/"
|
||||
restricted_headers_basic:
|
||||
- "/content-encoding/"
|
||||
- "/content-range/"
|
||||
- "/expect/"
|
||||
- "/if/"
|
||||
- "/lock-token/"
|
||||
- "/proxy/"
|
||||
- "/x-http-method-override/"
|
||||
- "/x-http-method/"
|
||||
- "/x-method-override/"
|
||||
- "/x-middleware-subrequest/"
|
||||
restricted_headers_extended:
|
||||
- "/accept-charset/"
|
||||
allowed_http_versions:
|
||||
- "HTTP/1.0"
|
||||
- "HTTP/1.1"
|
||||
- "HTTP/2"
|
||||
- "HTTP/2.0"
|
||||
- "HTTP/3"
|
||||
- "HTTP/3.0"
|
||||
allowed_request_content_type_charset:
|
||||
- "|utf-8|"
|
||||
- "|iso-8859-1|"
|
||||
- "|iso-8859-15|"
|
||||
- "|windows-1252|"
|
||||
71
seb4itik/byow/roles/caddy/files/bot_barrier_template.html
Normal file
71
seb4itik/byow/roles/caddy/files/bot_barrier_template.html
Normal file
@@ -0,0 +1,71 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Security Check</title>
|
||||
<style nonce="{{ .CSPNonce }}">
|
||||
html, body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: Arial, sans-serif;
|
||||
background-color: #f4f4f9;
|
||||
color: #333;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
margin: 0.4em 0 0.3em;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 1.2em;
|
||||
margin: 0.6em 0;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
margin: 2em 0;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border: 5px solid #ccc;
|
||||
border-top: 5px solid #333;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<!-- English Section -->
|
||||
<h1>Just a moment...</h1>
|
||||
<p>A small mathematical challenge is running<br>
|
||||
in the background - fully automatic to confirm<br>
|
||||
you're not a bot 🔒.</p>
|
||||
|
||||
<!-- Spinner -->
|
||||
<div class="spinner"></div>
|
||||
|
||||
<p>⏳ You'll be redirected shortly.</p>
|
||||
</div>
|
||||
|
||||
<script nonce="{{ .CSPNonce }}">{{ .Script }}</script>
|
||||
</body>
|
||||
</html>
|
||||
21
seb4itik/byow/roles/caddy/filter_plugins/filters.py
Normal file
21
seb4itik/byow/roles/caddy/filter_plugins/filters.py
Normal file
@@ -0,0 +1,21 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
class FilterModule(object):
|
||||
def filters(self):
|
||||
return {
|
||||
'dict2str': self.dict2str
|
||||
}
|
||||
|
||||
def dict2str(self, d, sep1='=', sep2=';', sep3=''):
|
||||
return sep2.join(
|
||||
[ str(k) + (sep1+str(v) if v!=None and v!='' else '') for k, v in d.items() ]
|
||||
) + sep3
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Tests
|
||||
filters = FilterModule()
|
||||
print(filters.filters())
|
||||
print(filters.dict2str(
|
||||
{"un": 1, "deux": 2, "str": "a string", "rien": None, "bool": True}
|
||||
))
|
||||
4
seb4itik/byow/roles/caddy/handlers/main.yml
Normal file
4
seb4itik/byow/roles/caddy/handlers/main.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
- name: Restart Caddy
|
||||
ansible.builtin.service:
|
||||
name: "{{ caddy_system_user }}"
|
||||
state: "restarted"
|
||||
73
seb4itik/byow/roles/caddy/tasks/caddy_config.yml
Normal file
73
seb4itik/byow/roles/caddy/tasks/caddy_config.yml
Normal file
@@ -0,0 +1,73 @@
|
||||
- name: Create sites config directories
|
||||
loop: "{{ caddy_sites }}"
|
||||
ansible.builtin.file:
|
||||
path: "{{ caddy_config_dir }}/sites/{{ item.name }}"
|
||||
state: "directory"
|
||||
group: "{{ caddy_system_group }}"
|
||||
mode: "0750"
|
||||
|
||||
- name: Generate sites coraza.conf config file
|
||||
loop: "{{ caddy_sites }}"
|
||||
loop_control:
|
||||
loop_var: "site"
|
||||
ansible.builtin.template:
|
||||
src: "templates/coraza.conf"
|
||||
dest: "{{ caddy_config_dir }}/sites/{{ site.name }}/coraza.conf"
|
||||
group: "{{ caddy_system_group }}"
|
||||
mode: "0640"
|
||||
notify:
|
||||
- "Restart Caddy"
|
||||
|
||||
- name: Generate sites crs-setup config file
|
||||
loop: "{{ caddy_sites }}"
|
||||
loop_control:
|
||||
loop_var: "site"
|
||||
ansible.builtin.template:
|
||||
src: "templates/crs-setup.conf"
|
||||
dest: "{{ caddy_config_dir }}/sites/{{ site.name }}/crs-setup.conf"
|
||||
group: "{{ caddy_system_group }}"
|
||||
mode: "0640"
|
||||
notify:
|
||||
- "Restart Caddy"
|
||||
|
||||
- name: Generate sites exclusions before request file
|
||||
loop: "{{ caddy_sites }}"
|
||||
loop_control:
|
||||
loop_var: "site"
|
||||
ansible.builtin.template:
|
||||
src: "templates/exclusions-request-before.conf"
|
||||
dest: "{{ caddy_config_dir }}/sites/{{ site.name }}/exclusions-request-before.conf"
|
||||
group: "{{ caddy_system_group }}"
|
||||
mode: "0640"
|
||||
notify:
|
||||
- "Restart Caddy"
|
||||
|
||||
- name: Generate sites exclusions after response file
|
||||
loop: "{{ caddy_sites }}"
|
||||
loop_control:
|
||||
loop_var: "site"
|
||||
ansible.builtin.template:
|
||||
src: "templates/exclusions-response-after.conf"
|
||||
dest: "{{ caddy_config_dir }}/sites/{{ site.name }}/exclusions-response-after.conf"
|
||||
group: "{{ caddy_system_group }}"
|
||||
mode: "0640"
|
||||
notify:
|
||||
- "Restart Caddy"
|
||||
|
||||
- name: Generate Caddyfile
|
||||
ansible.builtin.template:
|
||||
src: "templates/Caddyfile"
|
||||
dest: "{{ caddy_config_dir }}/Caddyfile"
|
||||
group: "{{ caddy_system_group }}"
|
||||
mode: "0640"
|
||||
notify:
|
||||
- "Restart Caddy"
|
||||
|
||||
- name: Copy bot barrier template
|
||||
ansible.builtin.copy:
|
||||
src: "files/bot_barrier_template.html"
|
||||
dest: "{{ caddy_config_dir }}/bot_barrier_template.html"
|
||||
group: "{{ caddy_system_group }}"
|
||||
mode: "0640"
|
||||
notify:
|
||||
- "Restart Caddy"
|
||||
135
seb4itik/byow/roles/caddy/tasks/caddy_install.yml
Normal file
135
seb4itik/byow/roles/caddy/tasks/caddy_install.yml
Normal file
@@ -0,0 +1,135 @@
|
||||
- name: Remove packaged Go
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- "golang"
|
||||
state: "absent"
|
||||
autoremove: true
|
||||
purge: true
|
||||
|
||||
- name: Install requirements
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- "git"
|
||||
- "libnss3-tools"
|
||||
- "ssl-cert"
|
||||
|
||||
- name: Create Go directory
|
||||
ansible.builtin.file:
|
||||
path: "/usr/local/go-{{ caddy_go_version }}"
|
||||
state: "directory"
|
||||
mode: "0755"
|
||||
|
||||
- name: Install Go
|
||||
ansible.builtin.unarchive:
|
||||
src: "https://go.dev/dl/go{{ caddy_go_version }}.linux-amd64.tar.gz"
|
||||
dest: "/usr/local/go-{{ caddy_go_version }}"
|
||||
remote_src: true
|
||||
|
||||
- name: Link Go
|
||||
ansible.builtin.file:
|
||||
src: "/usr/local/go-{{ caddy_go_version }}/go/bin/go"
|
||||
dest: "/usr/local/bin/go"
|
||||
state: "link"
|
||||
|
||||
- name: Create xcaddy directory
|
||||
ansible.builtin.file:
|
||||
path: "/usr/local/xcaddy-{{ caddy_xcaddy_version }}"
|
||||
state: "directory"
|
||||
mode: "0755"
|
||||
|
||||
- name: Install xcaddy
|
||||
ansible.builtin.unarchive:
|
||||
src: "{{ caddy_xcaddy_download }}/v{{ caddy_xcaddy_version }}/xcaddy_{{ caddy_xcaddy_version }}_linux_amd64.tar.gz"
|
||||
dest: "/usr/local/xcaddy-{{ caddy_xcaddy_version }}"
|
||||
remote_src: true
|
||||
|
||||
- name: Link xcaddy
|
||||
ansible.builtin.file:
|
||||
src: "/usr/local/xcaddy-{{ caddy_xcaddy_version }}/xcaddy"
|
||||
dest: "/usr/local/bin/xcaddy"
|
||||
state: "link"
|
||||
|
||||
- name: Create Caddy build command
|
||||
ansible.builtin.set_fact:
|
||||
caddy_build_command: >
|
||||
xcaddy build v{{ caddy_caddy_version }}
|
||||
--with github.com/pteich/caddy-tlsconsul
|
||||
--with github.com/corazawaf/coraza-caddy/v2@v{{ caddy_coraza_caddy_version }}
|
||||
--with github.com/mholt/caddy-ratelimit
|
||||
--with github.com/steffenbusch/caddy-bot-barrier
|
||||
--output /usr/local/bin/caddy
|
||||
|
||||
- name: Test Caddy version
|
||||
ansible.builtin.copy:
|
||||
dest: "/usr/local/bin/.caddy.build"
|
||||
content: "{{ caddy_build_command }}"
|
||||
mode: "0644"
|
||||
register: caddy_test_caddy_version
|
||||
|
||||
- name: Build Caddy
|
||||
# noqa: no-handler
|
||||
when: "caddy_test_caddy_version.changed"
|
||||
ansible.builtin.command:
|
||||
cmd: "{{ caddy_build_command }}"
|
||||
creates: "/usr/local/bin/caddy"
|
||||
notify:
|
||||
- "Restart Caddy"
|
||||
|
||||
- name: Create Caddy system group
|
||||
ansible.builtin.group:
|
||||
name: "{{ caddy_system_group }}"
|
||||
system: true
|
||||
|
||||
- name: Create Caddy system user
|
||||
ansible.builtin.user:
|
||||
name: "{{ caddy_system_user }}"
|
||||
group: "{{ caddy_system_group }}"
|
||||
groups: ["ssl-cert"]
|
||||
password: "!"
|
||||
system: true
|
||||
home: "{{ caddy_home_dir }}"
|
||||
|
||||
- name: Create Caddy config directories
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: "directory"
|
||||
group: "{{ caddy_system_group }}"
|
||||
mode: "0750"
|
||||
loop:
|
||||
- "{{ caddy_config_dir }}"
|
||||
- "{{ caddy_config_dir }}/crs-plugins"
|
||||
- "{{ caddy_config_dir }}/sites"
|
||||
|
||||
- name: Create Caddy log dir
|
||||
ansible.builtin.file:
|
||||
path: "{{ caddy_log_dir }}"
|
||||
state: "directory"
|
||||
group: "{{ caddy_system_group }}"
|
||||
mode: "0770"
|
||||
|
||||
- name: Install OWASP CRS
|
||||
ansible.builtin.unarchive:
|
||||
src: "https://github.com/coreruleset/coreruleset/archive/refs/tags/v{{ caddy_owasp_crs_version }}.tar.gz"
|
||||
dest: "{{ caddy_config_dir }}"
|
||||
group: "{{ caddy_system_group }}"
|
||||
remote_src: true
|
||||
|
||||
- name: Install plugins
|
||||
loop: "{{ caddy_crs_plugins }}"
|
||||
loop_control:
|
||||
loop_var: "plugin"
|
||||
ansible.builtin.include_tasks: "plugin_install.yml"
|
||||
|
||||
- name: Deploy caddy service file
|
||||
ansible.builtin.template:
|
||||
src: "templates/caddy.service"
|
||||
dest: "/etc/systemd/system/{{ caddy_system_user }}.service"
|
||||
mode: "0644"
|
||||
notify:
|
||||
- "Restart Caddy"
|
||||
|
||||
- name: Enable Caddy
|
||||
ansible.builtin.service:
|
||||
name: "{{ caddy_system_user }}"
|
||||
enabled: true
|
||||
daemon_reload: true
|
||||
6
seb4itik/byow/roles/caddy/tasks/main.yml
Normal file
6
seb4itik/byow/roles/caddy/tasks/main.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
- name: Install Caddy
|
||||
when: "not(caddy_config_only)"
|
||||
ansible.builtin.include_tasks: "caddy_install.yml"
|
||||
|
||||
- name: Config Caddy
|
||||
ansible.builtin.include_tasks: "caddy_config.yml"
|
||||
55
seb4itik/byow/roles/caddy/tasks/plugin_install.yml
Normal file
55
seb4itik/byow/roles/caddy/tasks/plugin_install.yml
Normal file
@@ -0,0 +1,55 @@
|
||||
- name: Download plugin {{ plugin.name }}
|
||||
ansible.builtin.get_url:
|
||||
url: "https://github.com/{{ plugin.provider | default('coreruleset') }}/{{ plugin.name }}-plugin/archive/refs/tags/v{{ plugin.version }}.tar.gz"
|
||||
dest: "/usr/local/src/"
|
||||
mode: "0644"
|
||||
register: "caddy_plugin_downloaded"
|
||||
|
||||
- name: Deploy plugin {{ plugin.name }}
|
||||
# noqa: no-handler
|
||||
when: "caddy_plugin_downloaded.changed"
|
||||
block:
|
||||
|
||||
- name: Get temp dir {{ plugin.name }}
|
||||
ansible.builtin.tempfile:
|
||||
state: "directory"
|
||||
register: "caddy_temp_plugin_dir"
|
||||
|
||||
- name: Unarchive plugin {{ plugin.name }}
|
||||
ansible.builtin.unarchive:
|
||||
src: "/usr/local/src/{{ plugin.name }}-plugin-{{ plugin.version }}.tar.gz"
|
||||
dest: "{{ caddy_temp_plugin_dir.path }}"
|
||||
remote_src: true
|
||||
|
||||
- name: Copy plugin files {{ plugin.name }}
|
||||
ansible.builtin.copy:
|
||||
src: "{{ caddy_temp_plugin_dir.path }}/{{ plugin.name }}-plugin-{{ plugin.version }}/plugins/"
|
||||
dest: "{{ caddy_config_dir }}/crs-plugins/"
|
||||
mode: "0644"
|
||||
# directory_mode: "0750"
|
||||
group: "{{ caddy_system_group }}"
|
||||
remote_src: true
|
||||
notify:
|
||||
- "Restart Caddy"
|
||||
|
||||
- name: Create after config {{ plugin.name }}
|
||||
when: "not(plugin.has_after_config | default(false))"
|
||||
ansible.builtin.copy:
|
||||
content: ""
|
||||
dest: "{{ caddy_config_dir }}/crs-plugins/{{ plugin.name }}-after.conf"
|
||||
mode: "0644"
|
||||
group: "{{ caddy_system_group }}"
|
||||
|
||||
# FIXME: bad hack, why is it required?
|
||||
- name: Adjust plugins diretory permissions
|
||||
ansible.builtin.file:
|
||||
path: "{{ caddy_config_dir }}/crs-plugins"
|
||||
state: "directory"
|
||||
group: "{{ caddy_system_group }}"
|
||||
mode: "0750"
|
||||
|
||||
- name: Delete temp dir {{ plugin.name }}
|
||||
when: "caddy_temp_plugin_dir.path is defined"
|
||||
ansible.builtin.file:
|
||||
path: "{{ caddy_temp_plugin_dir.path }}"
|
||||
state: "absent"
|
||||
154
seb4itik/byow/roles/caddy/templates/Caddyfile
Normal file
154
seb4itik/byow/roles/caddy/templates/Caddyfile
Normal file
@@ -0,0 +1,154 @@
|
||||
{
|
||||
grace_period 10s
|
||||
order coraza_waf first
|
||||
{% if caddy_email | default(None) != None %}
|
||||
email "{{ caddy_email }}"
|
||||
{% endif %}
|
||||
|
||||
{% if caddy_ca_root | default(None) != None %}
|
||||
tls {
|
||||
ca_root "{{ caddy_ca_root }}"
|
||||
}
|
||||
|
||||
{% endif %}
|
||||
log waf {
|
||||
format json
|
||||
include "http.handlers.waf"
|
||||
output file {{ caddy_log_dir }}/waf.log
|
||||
}
|
||||
{% if caddy_local_ca_name | default(None) != None %}
|
||||
|
||||
pki {
|
||||
ca local {
|
||||
name "{{ caddy_local_ca_name }}"
|
||||
}
|
||||
}
|
||||
{% endif %}
|
||||
}
|
||||
|
||||
{% for site in caddy_sites %}
|
||||
{% if (site.redirect_from_aliases | default(true)) and (site.aliases | default([]) | length) > 0 %}
|
||||
{{ site.aliases | join(', ') }} {
|
||||
redir https://{{ site.site }}{uri}
|
||||
}
|
||||
|
||||
{% else %}
|
||||
{% for alias in site.aliases | default([]) %}
|
||||
{{ alias }},
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{{ site.site }} {
|
||||
encode gzip zstd
|
||||
|
||||
{% for path in site.paths %}
|
||||
handle {{ path.path | default('*') }} {
|
||||
reverse_proxy {
|
||||
to {{ path.addrs | join(' ') }}
|
||||
header_up X-Real-IP {remote}
|
||||
lb_policy ip_hash
|
||||
# Active checks
|
||||
# health_uri /
|
||||
# health_interval 10s
|
||||
# health_timeout 5s
|
||||
# health_status 200
|
||||
# Passive checks
|
||||
fail_duration 30s
|
||||
max_fails 3
|
||||
unhealthy_latency 2000ms
|
||||
transport http {
|
||||
# tls_server_name {host}
|
||||
# tls_insecure_skip_verify
|
||||
}
|
||||
}
|
||||
}
|
||||
{% endfor %}
|
||||
|
||||
{% if site.custom_cert | default (false) %}
|
||||
tls {{ site.custom_cert_file }} {{ site.custom_key_file }}
|
||||
|
||||
{% endif %}
|
||||
{% if site.blacklist | default([]) | length > 0 %}
|
||||
@blocked {
|
||||
{% for ip in site.blacklist | default([]) %}
|
||||
remote_ip {{ ip }}
|
||||
{% endfor %}
|
||||
}
|
||||
respond @blocked "Access Denied" 403
|
||||
|
||||
{% endif %}
|
||||
{% if site.bot_barrier | default (false) %}
|
||||
bot_barrier {
|
||||
secret {{ caddy_bot_barrier_secret }}
|
||||
complexity 18
|
||||
valid_for 30m
|
||||
seed_cookie_name __chall_{{ site.name }}_seed
|
||||
solution_cookie_name __chall_{{ site.name }}_solution
|
||||
mac_cookie_name __chall_{{ site.name }}_mac
|
||||
template {{ caddy_config_dir }}/bot_barrier_template.html
|
||||
}
|
||||
|
||||
{% endif %}
|
||||
rate_limit {
|
||||
# distributed
|
||||
zone remote_ip {
|
||||
key {remote.ip}
|
||||
events {{ site.rate_events | default('1000') }}
|
||||
window {{ site.rate_window | default('1m') }}
|
||||
}
|
||||
}
|
||||
{% if site.enable_crs | default(caddy_waf_defaults.enable_crs) %}
|
||||
|
||||
coraza_waf {
|
||||
load_owasp_crs
|
||||
directives `
|
||||
Include "{{ caddy_config_dir }}/sites/{{ site.name }}/coraza.conf"
|
||||
Include "{{ caddy_config_dir }}/sites/{{ site.name }}/crs-setup.conf"
|
||||
{% for plugin in site.crs_plugins | default(caddy_waf_defaults.crs_plugins) %}
|
||||
Include "{{ caddy_config_dir }}/crs-plugins/{{ plugin }}-config.conf"
|
||||
Include "{{ caddy_config_dir }}/crs-plugins/{{ plugin }}-before.conf"
|
||||
{% endfor %}
|
||||
Include "{{ caddy_config_dir }}/sites/{{ site.name }}/exclusions-request-before.conf"
|
||||
Include "{{ caddy_config_dir }}/coreruleset-{{ caddy_owasp_crs_version }}/rules/*.conf"
|
||||
Include "{{ caddy_config_dir }}/sites/{{ site.name }}/exclusions-response-after.conf"
|
||||
{% for plugin in site.crs_plugins | default(caddy_waf_defaults.crs_plugins) %}
|
||||
Include "{{ caddy_config_dir }}/crs-plugins/{{ plugin }}-after.conf"
|
||||
{% endfor %}
|
||||
SecRuleEngine On
|
||||
`
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
header {
|
||||
Content-Security-Policy "{{ caddy_waf_defaults.header_content_security_policy | combine(site.header_content_security_policy | default({})) | dict2str(sep1=' ', sep2='; ', sep3=';') }}"
|
||||
Cross-Origin-Embedder-Policy "{{ site.header_cross_origin_embedder_policy | default(caddy_waf_defaults.header_cross_origin_embedder_policy) }}"
|
||||
Cross-Origin-Opener-Policy "{{ site.header_cross_origin_opener_policy | default(caddy_waf_defaults.header_cross_origin_opener_policy) }}"
|
||||
Cross-Origin-Resource-Policy "{{ site.header_cross_origin_resource_policy | default(caddy_waf_defaults.header_cross_origin_resource_policy) }}"
|
||||
Feature-Policy "{{ caddy_waf_defaults.header_feature_policy | combine(site.header_feature_policy | default({})) | dict2str(sep1=' ', sep2='; ', sep3=';') }}"
|
||||
Permissions-Policy "{{ caddy_waf_defaults.header_permissions_policy | combine(site.header_permissions_policy | default({})) | dict2str(sep2=', ') }}"
|
||||
Referrer-Policy "{{ site.header_referrer_policy | default(caddy_waf_defaults.header_referrer_policy) }}"
|
||||
Strict-Transport-Security "{{ site.header_strict_transport_security | default(caddy_waf_defaults.header_strict_transport_security) }}"
|
||||
X-Content-Type-Options "{{ site.header_x_content_type_options | default(caddy_waf_defaults.header_x_content_type_options) }}"
|
||||
X-Frame-Options "{{ site.header_x_frame_options | default(caddy_waf_defaults.header_x_frame_options) }}"
|
||||
X-Robots-Tag "{{ site.header_x_robots_tag | default(caddy_waf_defaults.header_x_robots_tag) }}"
|
||||
?Content-Type "{{ site.default_content_type | default(caddy_waf_defaults.default_content_type) }}"
|
||||
>Set-Cookie "(.*)" "$1; {{ site.cookies_attributes | default(caddy_waf_defaults.cookies_attributes) }}"
|
||||
{% for header in site.delete_headers | default(caddy_waf_defaults.delete_headers) %}
|
||||
-{{ header }}
|
||||
{% endfor %}
|
||||
{% for header in site.custom_headers | default(caddy_waf_defaults.custom_headers) %}
|
||||
{{ header.name }} "{{ header.value }}"
|
||||
{% endfor %}
|
||||
}
|
||||
|
||||
handle_errors 403 {
|
||||
header X-Blocked "true"
|
||||
respond "Your request was blocked."
|
||||
}
|
||||
|
||||
log {
|
||||
format json
|
||||
output file {{ caddy_log_dir }}/site_{{ site.name }}.log
|
||||
}
|
||||
}
|
||||
|
||||
{% endfor %}
|
||||
20
seb4itik/byow/roles/caddy/templates/caddy.service
Normal file
20
seb4itik/byow/roles/caddy/templates/caddy.service
Normal file
@@ -0,0 +1,20 @@
|
||||
[Unit]
|
||||
Description=Caddy
|
||||
Documentation=https://caddyserver.com/docs/
|
||||
After=network.target network-online.target
|
||||
Requires=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=notify
|
||||
User={{ caddy_system_user }}
|
||||
Group={{ caddy_system_group }}
|
||||
ExecStart=/usr/local/bin/caddy run --environ --config {{ caddy_config_dir }}/Caddyfile
|
||||
ExecReload=/usr/local/bin/caddy reload --config {{ caddy_config_dir }}/Caddyfile --force
|
||||
TimeoutStopSec=15s
|
||||
LimitNOFILE=1048576
|
||||
PrivateTmp=true
|
||||
ProtectSystem=full
|
||||
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
158
seb4itik/byow/roles/caddy/templates/coraza.conf
Normal file
158
seb4itik/byow/roles/caddy/templates/coraza.conf
Normal file
@@ -0,0 +1,158 @@
|
||||
# -- Rule engine initialization ----------------------------------------------
|
||||
|
||||
# Enable Coraza, attaching it to every transaction. Use detection
|
||||
# only to start with, because that minimises the chances of post-installation
|
||||
# disruption.
|
||||
SecRuleEngine DetectionOnly
|
||||
|
||||
|
||||
# -- Request body handling ---------------------------------------------------
|
||||
|
||||
# Allow Coraza to access request bodies. If you don't, Coraza
|
||||
# won't be able to see any POST parameters, which opens a large security
|
||||
# hole for attackers to exploit.
|
||||
SecRequestBodyAccess On
|
||||
|
||||
# Enable XML request body parser.
|
||||
# Initiate XML Processor in case of xml content-type
|
||||
SecRule REQUEST_HEADERS:Content-Type "^(?:application(?:/soap\+|/)|text/)xml" \
|
||||
"id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML"
|
||||
|
||||
# Enable JSON request body parser.
|
||||
# Initiate JSON Processor in case of JSON content-type; change accordingly
|
||||
# if your application does not use 'application/json'
|
||||
SecRule REQUEST_HEADERS:Content-Type "^application/json" \
|
||||
"id:'200001',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON"
|
||||
|
||||
# Enable JSON request body parser for more subtypes.
|
||||
# Adapt this rule if you want to engage the JSON Processor for "+json" subtypes
|
||||
SecRule REQUEST_HEADERS:Content-Type "^application/[a-z0-9.-]+[+]json" \
|
||||
"id:'200006',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON"
|
||||
|
||||
# Maximum request body size we will accept for buffering. If you support
|
||||
# file uploads, this value must has to be as large as the largest file
|
||||
# you are willing to accept.
|
||||
SecRequestBodyLimit 13107200
|
||||
|
||||
# Maximum request body size that Coraza will store in memory. If the body
|
||||
# size exceeds this value, it will be saved to a temporary file on disk.
|
||||
SecRequestBodyInMemoryLimit 131072
|
||||
|
||||
# Maximum request body size we will accept for buffering, with files excluded.
|
||||
# You want to keep that value as low as practical.
|
||||
# Note: SecRequestBodyNoFilesLimit is currently NOT supported by Coraza
|
||||
# SecRequestBodyNoFilesLimit 131072
|
||||
|
||||
# What to do if the request body size is above our configured limit.
|
||||
# Keep in mind that this setting will automatically be set to ProcessPartial
|
||||
# when SecRuleEngine is set to DetectionOnly mode in order to minimize
|
||||
# disruptions when initially deploying Coraza.
|
||||
# Warning: Setting this directive to ProcessPartial introduces a potential bypass
|
||||
# risk, as attackers could prepend junk data equal to or greater than the inspected body size.
|
||||
SecRequestBodyLimitAction Reject
|
||||
|
||||
# Verify that we've correctly processed the request body.
|
||||
# As a rule of thumb, when failing to process a request body
|
||||
# you should reject the request (when deployed in blocking mode)
|
||||
# or log a high-severity alert (when deployed in detection-only mode).
|
||||
SecRule REQBODY_ERROR "!@eq 0" \
|
||||
"id:'200002', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2"
|
||||
|
||||
# By default be strict with what we accept in the multipart/form-data
|
||||
# request body. If the rule below proves to be too strict for your
|
||||
# environment consider changing it to detection-only.
|
||||
# Do NOT remove it, as it will catch many evasion attempts.
|
||||
SecRule MULTIPART_STRICT_ERROR "!@eq 0" \
|
||||
"id:'200003',phase:2,t:none,log,deny,status:400, \
|
||||
msg:'Multipart request body failed strict validation.'"
|
||||
|
||||
# -- Response body handling --------------------------------------------------
|
||||
|
||||
# Allow Coraza to access response bodies.
|
||||
# You should have this directive enabled in order to identify errors
|
||||
# and data leakage issues.
|
||||
# Do keep in mind that enabling this directive does increases both
|
||||
# memory consumption and response latency.
|
||||
SecResponseBodyAccess On
|
||||
|
||||
# Which response MIME types do you want to inspect? You should adjust the
|
||||
# configuration below to catch documents but avoid static files
|
||||
# (e.g., images and archives).
|
||||
SecResponseBodyMimeType text/plain text/html text/xml
|
||||
|
||||
# Buffer response bodies of up to 512 KB in length.
|
||||
SecResponseBodyLimit 524288
|
||||
|
||||
# What happens when we encounter a response body larger than the configured
|
||||
# limit? By default, we process what we have and let the rest through.
|
||||
# That's somewhat less secure, but does not break any legitimate pages.
|
||||
SecResponseBodyLimitAction ProcessPartial
|
||||
|
||||
|
||||
# -- Filesystem configuration ------------------------------------------------
|
||||
|
||||
# The location where Coraza will keep its persistent data. This default setting
|
||||
# is chosen due to all systems have /tmp available however, it
|
||||
# too should be updated to a place that other users can't access.
|
||||
SecDataDir /tmp/
|
||||
|
||||
|
||||
# -- File uploads handling configuration -------------------------------------
|
||||
|
||||
# The location where Coraza stores intercepted uploaded files. This
|
||||
# location must be private to Coraza. You don't want other users on
|
||||
# the server to access the files, do you?
|
||||
#SecUploadDir /opt/coraza/var/upload/
|
||||
|
||||
# If On, the WAF will store the uploaded files in the SecUploadDir
|
||||
# directory.
|
||||
# Note: SecUploadKeepFiles is currently NOT supported by Coraza
|
||||
#SecUploadKeepFiles Off
|
||||
|
||||
# Uploaded files are by default created with permissions that do not allow
|
||||
# any other user to access them. You may need to relax that if you want to
|
||||
# interface Coraza to an external program (e.g., an anti-virus).
|
||||
# Note: SecUploadFileMode is currently NOT supported by Coraza
|
||||
#SecUploadFileMode 0600
|
||||
|
||||
|
||||
# -- Debug log configuration -------------------------------------------------
|
||||
|
||||
# Default debug log path
|
||||
# Debug levels:
|
||||
# 0: No logging (least verbose)
|
||||
# 1: Error
|
||||
# 2: Warn
|
||||
# 3: Info
|
||||
# 4-8: Debug
|
||||
# 9: Trace (most verbose)
|
||||
SecDebugLog {{ caddy_log_dir }}/coraza-debug.log
|
||||
SecDebugLogLevel 4
|
||||
|
||||
|
||||
# -- Audit log configuration -------------------------------------------------
|
||||
|
||||
# Log the transactions that are marked by a rule, as well as those that
|
||||
# trigger a server error (determined by a 5xx or 4xx, excluding 404,
|
||||
# level response status codes).
|
||||
SecAuditEngine RelevantOnly
|
||||
SecAuditLogRelevantStatus "^(?:(5|4)(0|1)[0-9])$"
|
||||
|
||||
# Define which parts of the transaction are going to be recorded in the audit log
|
||||
SecAuditLogParts ABIJDEFHZ
|
||||
|
||||
# Use a single file for logging. This is much easier to look at, but
|
||||
# assumes that you will use the audit log only occasionally.
|
||||
SecAuditLogType Serial
|
||||
|
||||
# The format used to write the audit log.
|
||||
# Can be one of JSON|JsonLegacy|Native|OCSF
|
||||
SecAuditLogFormat JSON
|
||||
|
||||
# The following settings are not supported by Coraza
|
||||
# SecCookieFormat 0
|
||||
# SecArgumentSeparator &
|
||||
# SecRule MULTIPART_UNMATCHED_BOUNDARY "@eq 1" \
|
||||
# "id:'200004',phase:2,t:none,log,deny,msg:'Multipart parser detected a possible unmatched boundary.'"
|
||||
# SecRule TX:/^COR_/ "!@streq 0" \
|
||||
# "id:'200005',phase:2,t:none,deny,msg:'Coraza internal error flagged: %{MATCHED_VAR_NAME}'"
|
||||
305
seb4itik/byow/roles/caddy/templates/crs-setup.conf
Normal file
305
seb4itik/byow/roles/caddy/templates/crs-setup.conf
Normal file
@@ -0,0 +1,305 @@
|
||||
## Log destination
|
||||
SecDefaultAction "phase:1,log,auditlog,pass"
|
||||
SecDefaultAction "phase:2,log,auditlog,pass"
|
||||
|
||||
## Paranoia level
|
||||
SecAction \
|
||||
"id:900000,\
|
||||
phase:1,\
|
||||
pass,\
|
||||
t:none,\
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\
|
||||
setvar:tx.blocking_paranoia_level={{ site.paranoia_level | default(caddy_waf_defaults.paranoia_level) }}"
|
||||
{% if site.detection_paranoia_level | default(caddy_waf_defaults.detection_paranoia_level) != None %}
|
||||
|
||||
## Detection paranoia level
|
||||
SecAction \
|
||||
"id:900001,\
|
||||
phase:1,\
|
||||
pass,\
|
||||
t:none,\
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\
|
||||
setvar:tx.detection_paranoia_level={{ site.detection_paranoia_level | default(caddy_waf_defaults.detection_paranoia_level) }}"
|
||||
{% endif %}
|
||||
|
||||
## Enforce Body Processor URLENCODED
|
||||
SecAction \
|
||||
"id:900010,\
|
||||
phase:1,\
|
||||
pass,\
|
||||
t:none,\
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\
|
||||
setvar:tx.enforce_bodyproc_urlencoded={{ site.enforce_bodyproc_urlencoded | default(caddy_waf_defaults.enforce_bodyproc_urlencoded) }}"
|
||||
|
||||
## Anomaly Scoring Mode Severity Levels
|
||||
SecAction \
|
||||
"id:900100,\
|
||||
phase:1,\
|
||||
pass,\
|
||||
t:none,\
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\
|
||||
setvar:tx.critical_anomaly_score={{ site.critical_anomaly_score | default(caddy_waf_defaults.critical_anomaly_score) }},\
|
||||
setvar:tx.error_anomaly_score={{ site.error_anomaly_score | default(caddy_waf_defaults.error_anomaly_score) }},\
|
||||
setvar:tx.warning_anomaly_score={{ site.warning_anomaly_score | default(caddy_waf_defaults.warning_anomaly_score) }},\
|
||||
setvar:tx.notice_anomaly_score={{ site.notice_anomaly_score | default(caddy_waf_defaults.notice_anomaly_score) }}"
|
||||
|
||||
## Anomaly Scoring Mode Blocking Threshold Levels
|
||||
SecAction \
|
||||
"id:900110,\
|
||||
phase:1,\
|
||||
pass,\
|
||||
t:none,\
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\
|
||||
setvar:tx.inbound_anomaly_score_threshold={% if site.log_only | default(caddy_waf_defaults.log_only) %}1000{% else %}{{ site.inbound_anomaly_score_threshold | default(caddy_waf_defaults.inbound_anomaly_score_threshold) }}{% endif %},\
|
||||
setvar:tx.outbound_anomaly_score_threshold={% if site.log_only | default(caddy_waf_defaults.log_only) %}1000{% else %}{{ site.outbound_anomaly_score_threshold | default(caddy_waf_defaults.outbound_anomaly_score_threshold) }}{% endif %}"
|
||||
|
||||
## Application Specific Rule Exclusions
|
||||
# FIXME
|
||||
# In CRS 4, these are no longer part of the CRS itself, but they are available
|
||||
# as "CRS plugins". Some plugins improve support for web applications, and others
|
||||
# may bring new functionality. Plugins are not installed by default, but can be
|
||||
# downloaded from the plugin registry:
|
||||
# https://github.com/coreruleset/plugin-registry
|
||||
# For detailed information about using and installing plugins, please see:
|
||||
# https://coreruleset.org/docs/concepts/plugins/
|
||||
|
||||
## Anomaly Score Reporting Level
|
||||
# 0 - Reporting disabled
|
||||
# 1 - Reporting for requests with a blocking anomaly score >= a threshold
|
||||
# 2 - Reporting for requests with a detection anomaly score >= a threshold
|
||||
# 3 - Reporting for requests with a blocking anomaly score greater than 0
|
||||
# 4 - Reporting for requests with a detection anomaly score greater than 0
|
||||
# 5 - Reporting for all requests
|
||||
SecAction \
|
||||
"id:900115,\
|
||||
phase:1,\
|
||||
pass,\
|
||||
t:none,\
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\
|
||||
setvar:tx.reporting_level={{ site.reporting_level | default(caddy_waf_defaults.reporting_level) }}"
|
||||
|
||||
## Early Anomaly Scoring Mode Blocking
|
||||
SecAction \
|
||||
"id:900120,\
|
||||
phase:1,\
|
||||
pass,\
|
||||
t:none,\
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\
|
||||
setvar:tx.early_blocking=0"
|
||||
|
||||
## Initialize Default Collections
|
||||
SecAction \
|
||||
"id:900130,\
|
||||
phase:1,\
|
||||
pass,\
|
||||
t:none,\
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\
|
||||
setvar:tx.enable_default_collections=1"
|
||||
|
||||
## HTTP Policy Settings
|
||||
SecAction \
|
||||
"id:900200,\
|
||||
phase:1,\
|
||||
pass,\
|
||||
t:none,\
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\
|
||||
setvar:'tx.allowed_methods={{ site.allowed_methods | default(caddy_waf_defaults.allowed_methods) | join(" ") }}'"
|
||||
|
||||
## Content-Types that a client is allowed to send in a request
|
||||
SecAction \
|
||||
"id:900220,\
|
||||
phase:1,\
|
||||
pass,\
|
||||
t:none,\
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\
|
||||
setvar:'tx.allowed_request_content_type={{ site.allowed_request_content_type | default(caddy_waf_defaults.allowed_request_content_type) | join(" ") }}'"
|
||||
|
||||
## Allowed HTTP versions
|
||||
SecAction \
|
||||
"id:900230,\
|
||||
phase:1,\
|
||||
pass,\
|
||||
t:none,\
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\
|
||||
setvar:'tx.allowed_http_versions={{ site.allowed_http_versions | default(caddy_waf_defaults.allowed_http_versions) | join(" ") }}'"
|
||||
|
||||
## Forbidden file extensions
|
||||
SecAction \
|
||||
"id:900240,\
|
||||
phase:1,\
|
||||
pass,\
|
||||
t:none,\
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\
|
||||
setvar:'tx.restricted_extensions={{ site.restricted_extensions | default(caddy_waf_defaults.restricted_extensions) | join(" ") }}'"
|
||||
|
||||
## Restricted request headers
|
||||
SecAction \
|
||||
"id:900250,\
|
||||
phase:1,\
|
||||
pass,\
|
||||
t:none,\
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\
|
||||
setvar:'tx.restricted_headers_basic={{ site.restricted_headers_basic | default(caddy_waf_defaults.restricted_headers_basic) | join(" ") }}'"
|
||||
|
||||
## Extended restricted request headers (forbidden at a higher paranoia level)
|
||||
SecAction \
|
||||
"id:900255,\
|
||||
phase:1,\
|
||||
pass,\
|
||||
t:none,\
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\
|
||||
setvar:'tx.restricted_headers_extended={{ site.restricted_headers_extended | default(caddy_waf_defaults.restricted_headers_extended) | join(" ") }}'"
|
||||
|
||||
## Content-Types charsets that a client is allowed to send in a request
|
||||
SecAction \
|
||||
"id:900280,\
|
||||
phase:1,\
|
||||
pass,\
|
||||
t:none,\
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\
|
||||
setvar:'tx.allowed_request_content_type_charset={{ caddy_waf_defaults.allowed_request_content_type_charset | default(caddy_waf_defaults.allowed_request_content_type_charset) | join(" ") }}'"
|
||||
|
||||
## Block request if number of arguments is too high
|
||||
SecAction \
|
||||
"id:900300,\
|
||||
phase:1,\
|
||||
pass,\
|
||||
t:none,\
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\
|
||||
setvar:tx.max_num_args={{ site.max_num_args | default(caddy_waf_defaults.max_num_args) }}"
|
||||
|
||||
## Block request if the length of any argument name is too high
|
||||
SecAction \
|
||||
"id:900310,\
|
||||
phase:1,\
|
||||
pass,\
|
||||
t:none,\
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\
|
||||
setvar:tx.arg_name_length={{ site.arg_name_length | default(caddy_waf_defaults.arg_name_length) }}"
|
||||
|
||||
## Block request if the length of any argument value is too high
|
||||
SecAction \
|
||||
"id:900320,\
|
||||
phase:1,\
|
||||
pass,\
|
||||
t:none,\
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\
|
||||
setvar:tx.arg_length={{ site.arg_length | default(caddy_waf_defaults.arg_length) }}"
|
||||
|
||||
## Block request if the total length of all combined arguments is too high
|
||||
SecAction \
|
||||
"id:900330,\
|
||||
phase:1,\
|
||||
pass,\
|
||||
t:none,\
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\
|
||||
setvar:tx.total_arg_length={{ site.total_arg_length | default(caddy_waf_defaults.total_arg_length) }}"
|
||||
|
||||
## Block request if the file size of any individual uploaded file is too high
|
||||
SecAction \
|
||||
"id:900340,\
|
||||
phase:1,\
|
||||
pass,\
|
||||
t:none,\
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\
|
||||
setvar:tx.max_file_size={{ site.max_file_size | default(caddy_waf_defaults.max_file_size) }}"
|
||||
|
||||
## Block request if the total size of all combined uploaded files is too high
|
||||
SecAction \
|
||||
"id:900350,\
|
||||
phase:1,\
|
||||
pass,\
|
||||
t:none,\
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\
|
||||
setvar:tx.combined_file_sizes={{ site.combined_file_sizes | default(caddy_waf_defaults.combined_file_sizes) }}"
|
||||
|
||||
## Easing In / Sampling Percentage
|
||||
SecAction \
|
||||
"id:900400,\
|
||||
phase:1,\
|
||||
pass,\
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\
|
||||
setvar:tx.sampling_percentage={{ site.sampling_percentage | default(caddy_waf_defaults.sampling_percentage) }}"
|
||||
|
||||
## Check UTF-8 encoding
|
||||
SecAction \
|
||||
"id:900950,\
|
||||
phase:1,\
|
||||
pass,\
|
||||
t:none,\
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\
|
||||
setvar:tx.crs_validate_utf8_encoding=1"
|
||||
|
||||
## Skip Checking Responses
|
||||
# CRS will perform analysis of the response contents if this is enabled and you have
|
||||
# the directive `SecResponseBodyAccess On`.
|
||||
# Warning: this feature is _enabled_ by default, but depending on your applications
|
||||
# you might be targeted in a Request Filter Denial of Service (RFDoS) attack.
|
||||
# References: https://blog.sicuranext.com/response-filter-denial-of-service-a-new-way-to-shutdown-a-website/
|
||||
# Uncomment this rule to _skip checking responses_.
|
||||
#SecAction \
|
||||
# "id:900500,\
|
||||
# phase:1,\
|
||||
# pass,\
|
||||
# t:none,\
|
||||
# nolog,\
|
||||
# tag:'OWASP_CRS',\
|
||||
# ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\
|
||||
# setvar:tx.crs_skip_response_analysis=1"
|
||||
|
||||
## End of setup
|
||||
SecAction \
|
||||
"id:900990,\
|
||||
phase:1,\
|
||||
pass,\
|
||||
t:none,\
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\
|
||||
setvar:tx.crs_setup_version={{ caddy_owasp_crs_version | regex_replace('\.', '') }}"
|
||||
@@ -0,0 +1,3 @@
|
||||
{% for exclusion in site.crs_exceptions.before_request | default([]) %}
|
||||
{{ exclusion }}
|
||||
{% endfor %}
|
||||
@@ -0,0 +1,3 @@
|
||||
{% for exclusion in site.crs_exceptions.after_response | default([]) %}
|
||||
{{ exclusion }}
|
||||
{% endfor %}
|
||||
1
seb4itik/byow/roles/caddy/vars/main.yml
Normal file
1
seb4itik/byow/roles/caddy/vars/main.yml
Normal file
@@ -0,0 +1 @@
|
||||
caddy_xcaddy_download: "https://github.com/caddyserver/xcaddy/releases/download"
|
||||
5
seb4itik/byow/roles/common/README.md
Normal file
5
seb4itik/byow/roles/common/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# BYOW - Build Your Own WAF
|
||||
|
||||
A WAF _à la carte_.
|
||||
|
||||
## Common
|
||||
2
seb4itik/byow/roles/common/defaults/main.yml
Normal file
2
seb4itik/byow/roles/common/defaults/main.yml
Normal file
@@ -0,0 +1,2 @@
|
||||
common_default_packages: []
|
||||
common_proxy_server: null
|
||||
7
seb4itik/byow/roles/common/tasks/main.yml
Normal file
7
seb4itik/byow/roles/common/tasks/main.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
- name: Setup proxy client
|
||||
when: "common_proxy_server != None"
|
||||
ansible.builtin.include_tasks: "setup_proxy_client.yml"
|
||||
|
||||
- name: Setup packages
|
||||
when: "common_default_packages | length > 0"
|
||||
ansible.builtin.include_tasks: "setup_packages.yml"
|
||||
9
seb4itik/byow/roles/common/tasks/setup_packages.yml
Normal file
9
seb4itik/byow/roles/common/tasks/setup_packages.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
- name: Keep packages up to date
|
||||
ansible.builtin.apt:
|
||||
update_cache: true
|
||||
upgrade: "safe"
|
||||
|
||||
- name: Install default packages
|
||||
ansible.builtin.apt:
|
||||
name: "{{ common_default_packages }}"
|
||||
autoclean: true
|
||||
24
seb4itik/byow/roles/common/tasks/setup_proxy_client.yml
Normal file
24
seb4itik/byow/roles/common/tasks/setup_proxy_client.yml
Normal file
@@ -0,0 +1,24 @@
|
||||
- name: Create 00proxy.conf file
|
||||
ansible.builtin.template:
|
||||
src: "templates/apt_proxy.conf"
|
||||
dest: "/etc/apt/apt.conf.d/00proxy.conf"
|
||||
mode: "644"
|
||||
owner: "root"
|
||||
group: "root"
|
||||
|
||||
- name: Add http_proxy and https_proxy variable to file /etc/environment
|
||||
ansible.builtin.lineinfile:
|
||||
path: "/etc/environment"
|
||||
regexp: '^{{ item.key }}='
|
||||
line: '{{ item.key }}="{{ item.value }}"'
|
||||
create: true
|
||||
mode: "644"
|
||||
owner: "root"
|
||||
group: "root"
|
||||
with_items:
|
||||
- key: 'http_proxy'
|
||||
value: '{{ common_proxy_server }}'
|
||||
- key: 'https_proxy'
|
||||
value: '{{ common_proxy_server }}'
|
||||
- key: 'no_proxy'
|
||||
value: 'localhost,127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16'
|
||||
2
seb4itik/byow/roles/common/templates/apt_proxy.conf
Normal file
2
seb4itik/byow/roles/common/templates/apt_proxy.conf
Normal file
@@ -0,0 +1,2 @@
|
||||
Acquire::http::Proxy "http://{{ common_proxy_server }}";
|
||||
Acquire::https::Proxy "https://{{ common_proxy_server }}";
|
||||
5
seb4itik/byow/roles/consul/README.md
Normal file
5
seb4itik/byow/roles/consul/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# BYOW - Build Your Own WAF
|
||||
|
||||
A WAF _à la carte_.
|
||||
|
||||
## Consul
|
||||
22
seb4itik/byow/roles/consul/defaults/main.yml
Normal file
22
seb4itik/byow/roles/consul/defaults/main.yml
Normal file
@@ -0,0 +1,22 @@
|
||||
# Required
|
||||
consul_datacenter: null
|
||||
consul_domain: null
|
||||
consul_secret_key: null
|
||||
consul_server: false
|
||||
consul_servers: []
|
||||
|
||||
# Required for registering services
|
||||
consul_services: []
|
||||
|
||||
# Defaults
|
||||
consul_version: "1.22.2"
|
||||
consul_key_file: "/etc/ssl/private/server.{{ consul_datacenter }}.{{ consul_domain }}.key"
|
||||
consul_cert_file: "/etc/ssl/certs/server.{{ consul_datacenter }}.{{ consul_domain }}.crt"
|
||||
consul_ca_file: "/etc/ssl/certs/local-ca.crt"
|
||||
consul_client_addr: "0.0.0.0"
|
||||
consul_bind_addr: "{{ ansible_default_ipv4.address }}"
|
||||
consul_advertise_addr: "{{ ansible_default_ipv4.address }}"
|
||||
consul_default_check_interval: "15s"
|
||||
consul_default_check_timeout: "2s"
|
||||
|
||||
consul_acl_bootstraped: false
|
||||
5
seb4itik/byow/roles/consul/handlers/main.yml
Normal file
5
seb4itik/byow/roles/consul/handlers/main.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
- name: Restart Consul
|
||||
ansible.builtin.service:
|
||||
name: "consul"
|
||||
state: "restarted"
|
||||
daemon_reload: true
|
||||
131
seb4itik/byow/roles/consul/tasks/main.yml
Normal file
131
seb4itik/byow/roles/consul/tasks/main.yml
Normal file
@@ -0,0 +1,131 @@
|
||||
- name: Install requirements
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- "unzip"
|
||||
|
||||
# Not required for Ubuntu 24, was a test for Debian 13
|
||||
- name: Disable IPv6 on all interfaces
|
||||
ansible.posix.sysctl:
|
||||
name: "net.ipv6.conf.all.disable_ipv6"
|
||||
value: "1"
|
||||
sysctl_set: true
|
||||
|
||||
- name: Create Consul directory
|
||||
ansible.builtin.file:
|
||||
path: "/usr/local/consul-{{ consul_version }}"
|
||||
state: "directory"
|
||||
mode: "0755"
|
||||
|
||||
- name: Install Consul
|
||||
ansible.builtin.unarchive:
|
||||
src: "https://releases.hashicorp.com/consul/{{ consul_version }}/consul_{{ consul_version }}_linux_amd64.zip"
|
||||
dest: "/usr/local/consul-{{ consul_version }}"
|
||||
remote_src: true
|
||||
notify:
|
||||
- "Restart Consul"
|
||||
|
||||
- name: Link Consul
|
||||
ansible.builtin.file:
|
||||
src: "/usr/local/consul-{{ consul_version }}/consul"
|
||||
dest: "/usr/local/bin/consul"
|
||||
state: "link"
|
||||
notify:
|
||||
- "Restart Consul"
|
||||
|
||||
- name: Create consul system group
|
||||
ansible.builtin.group:
|
||||
name: "consul"
|
||||
system: true
|
||||
|
||||
- name: Create consul system user
|
||||
ansible.builtin.user:
|
||||
name: "consul"
|
||||
group: "consul"
|
||||
groups: ["ssl-cert"]
|
||||
password: "!"
|
||||
system: true
|
||||
home: "/opt/consul"
|
||||
|
||||
- name: Create consul config directory
|
||||
ansible.builtin.file:
|
||||
path: "/etc/consul"
|
||||
state: "directory"
|
||||
group: "consul"
|
||||
mode: "0750"
|
||||
|
||||
- name: Create consul log dir
|
||||
ansible.builtin.file:
|
||||
path: "/var/log/consul"
|
||||
state: "directory"
|
||||
group: "consul"
|
||||
mode: "0770"
|
||||
|
||||
- name: Generate Consul config
|
||||
ansible.builtin.template:
|
||||
src: "templates/config.json"
|
||||
dest: "/etc/consul/config.json"
|
||||
group: "consul"
|
||||
mode: "0640"
|
||||
notify:
|
||||
- "Restart Consul"
|
||||
|
||||
- name: Deploy Consul service file
|
||||
ansible.builtin.template:
|
||||
src: "templates/consul.service"
|
||||
dest: "/etc/systemd/system/consul.service"
|
||||
mode: "0644"
|
||||
notify:
|
||||
- "Restart Consul"
|
||||
|
||||
- name: Enable and start Consul
|
||||
ansible.builtin.service:
|
||||
name: "consul"
|
||||
state: "started"
|
||||
enabled: true
|
||||
daemon_reload: true
|
||||
|
||||
# consul acl bootstrap -format=json
|
||||
# {
|
||||
# "CreateIndex": 1042,
|
||||
# "ModifyIndex": 1042,
|
||||
# "AccessorID": "50f8bfb6-b259-4c08-f9aa-2dc0eeb08e99",
|
||||
# "SecretID": "b7f16a8a-0b4f-4661-fea2-f79d95dd011b",
|
||||
# "Description": "Bootstrap Token (Global Management)",
|
||||
# "Policies": [
|
||||
# {
|
||||
# "ID": "00000000-0000-0000-0000-000000000001",
|
||||
# "Name": "global-management"
|
||||
# }
|
||||
# ],
|
||||
# "Local": false,
|
||||
# "CreateTime": "2026-01-19T18:41:13.294800712+03:00",
|
||||
# "Hash": "X2AgaFhnQGRhSSF/h0m6qpX1wj/HJWbyXcxkEM/5GrY="
|
||||
# }
|
||||
|
||||
- name: Register services to consul
|
||||
loop: "{{ consul_services }}"
|
||||
community.general.consul_agent_service:
|
||||
name: "{{ item.name }}"
|
||||
service_port: "{{ item.port }}"
|
||||
tags: "{{ item.tags | default([]) }}"
|
||||
|
||||
- name: Register HTTP checks to consul
|
||||
loop: "{{ consul_services }}"
|
||||
when: "item.check_http | default(None) != None"
|
||||
community.general.consul_agent_check:
|
||||
name: "{{ item.name }}_check"
|
||||
service_id: "{{ item.name }}"
|
||||
interval: "{{ item.check_interval | default(consul_default_check_interval) }}"
|
||||
timeout: "{{ item.check_timeout | default(consul_default_check_timeout) }}"
|
||||
http: "{{ item.check_http }}"
|
||||
scheme: "{{ item.check_scheme | default('http') }}"
|
||||
|
||||
- name: Register TCP checks to consul
|
||||
loop: "{{ consul_services }}"
|
||||
when: "item.check_tcp | default(None) != None"
|
||||
community.general.consul_agent_check:
|
||||
name: "{{ item.name }}_check"
|
||||
service_id: "{{ item.name }}"
|
||||
interval: "{{ item.check_interval | default(consul_default_check_interval) }}"
|
||||
timeout: "{{ item.check_timeout | default(consul_default_check_timeout) }}"
|
||||
tcp: "{{ item.check_tcp }}"
|
||||
28
seb4itik/byow/roles/consul/templates/config.json
Normal file
28
seb4itik/byow/roles/consul/templates/config.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"log_level": "INFO",
|
||||
"domain": "{{ consul_domain }}",
|
||||
"datacenter": "{{ consul_datacenter | mandatory }}",
|
||||
"key_file": "{{ consul_key_file }}",
|
||||
"cert_file": "{{ consul_cert_file }}",
|
||||
"ca_file": "{{ consul_ca_file }}",
|
||||
"verify_incoming": true,
|
||||
"verify_outgoing": true,
|
||||
"verify_server_hostname": true,
|
||||
"encrypt": "{{ consul_secret_key }}",
|
||||
"data_dir": "/opt/consul",
|
||||
{% if consul_server %}
|
||||
"node_name": "{{ ansible_hostname }}",
|
||||
"server": true,
|
||||
"ui": true,
|
||||
"leave_on_terminate": true,
|
||||
"client_addr": "{{ consul_client_addr }}",
|
||||
"bind_addr": "{{ consul_bind_addr }}",
|
||||
"advertise_addr": "{{ consul_advertise_addr }}",
|
||||
"bootstrap_expect": {{ (consul_servers | length) - 1 }},
|
||||
"enable_syslog": true,
|
||||
"performance": {
|
||||
"raft_multiplier": 1
|
||||
},
|
||||
{% endif %}
|
||||
"retry_join": ["{{ consul_servers | join('", "') }}"]
|
||||
}
|
||||
18
seb4itik/byow/roles/consul/templates/consul.service
Normal file
18
seb4itik/byow/roles/consul/templates/consul.service
Normal file
@@ -0,0 +1,18 @@
|
||||
[Unit]
|
||||
Description="HashiCorp Consul - A service mesh solution"
|
||||
Documentation=https://www.consul.io/
|
||||
Requires=network-online.target
|
||||
After=network-online.target
|
||||
ConditionFileNotEmpty=/etc/consul/config.json
|
||||
|
||||
[Service]
|
||||
Type=notify
|
||||
User=consul
|
||||
Group=consul
|
||||
ExecStart=/usr/local/bin/consul agent -config-file=/etc/consul/config.json
|
||||
KillMode=process
|
||||
Restart=on-failure
|
||||
LimitNOFILE=65536
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
5
seb4itik/byow/roles/keepalived/README.md
Normal file
5
seb4itik/byow/roles/keepalived/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# BYOW - Build Your Own WAF
|
||||
|
||||
A WAF _à la carte_.
|
||||
|
||||
## Keepalived
|
||||
16
seb4itik/byow/roles/keepalived/defaults/main.yml
Normal file
16
seb4itik/byow/roles/keepalived/defaults/main.yml
Normal file
@@ -0,0 +1,16 @@
|
||||
# Required
|
||||
keepalived_script_user: null
|
||||
keepalived_monitored_process: null
|
||||
keepalived_vrrp_password: null # Max 8 characters
|
||||
keepalived_vrrp_ips: null
|
||||
# - master: <string>
|
||||
# virtual_router_id: <int>
|
||||
# addr: <string>
|
||||
|
||||
# Optionnal
|
||||
keepalived_alerts_to: []
|
||||
keepalived_unicast_peers: []
|
||||
|
||||
# Required if length keepalived_alerts_to > 0
|
||||
keepalived_smtp_from: null
|
||||
keepalived_smtp_relay: null
|
||||
4
seb4itik/byow/roles/keepalived/handlers/main.yml
Normal file
4
seb4itik/byow/roles/keepalived/handlers/main.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
- name: Restart keepalived
|
||||
ansible.builtin.service:
|
||||
name: "keepalived"
|
||||
state: "restarted"
|
||||
28
seb4itik/byow/roles/keepalived/tasks/main.yml
Normal file
28
seb4itik/byow/roles/keepalived/tasks/main.yml
Normal file
@@ -0,0 +1,28 @@
|
||||
- name: Installation des paquetages
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- "keepalived"
|
||||
notify:
|
||||
- "Restart keepalived"
|
||||
|
||||
- name: Tell kernel to allow binding non-local IP
|
||||
ansible.posix.sysctl:
|
||||
name: "net.ipv4.ip_nonlocal_bind"
|
||||
value: "1"
|
||||
sysctl_set: true
|
||||
notify:
|
||||
- "Restart keepalived"
|
||||
|
||||
- name: Generate keepalived.conf file
|
||||
ansible.builtin.template:
|
||||
src: "templates/keepalived.conf"
|
||||
dest: "/etc/keepalived/keepalived.conf"
|
||||
mode: "0640"
|
||||
notify:
|
||||
- "Restart keepalived"
|
||||
|
||||
- name: Activate and start keepalived
|
||||
ansible.builtin.service:
|
||||
name: "keepalived"
|
||||
state: "started"
|
||||
enabled: true
|
||||
58
seb4itik/byow/roles/keepalived/templates/keepalived.conf
Normal file
58
seb4itik/byow/roles/keepalived/templates/keepalived.conf
Normal file
@@ -0,0 +1,58 @@
|
||||
# FIXME: NOTICE: setting config option max_auto_priority should result in better keepalived performance
|
||||
|
||||
global_defs {
|
||||
enable_script_security
|
||||
script_user "{{ keepalived_script_user }}"
|
||||
{% if keepalived_alerts_to | length > 0 %}
|
||||
|
||||
notification_email {
|
||||
{% for dest in keepalived_alerts_to %}
|
||||
{{ dest }}
|
||||
{% endfor %}
|
||||
}
|
||||
notification_email_from {{ keepalived_smtp_from }}
|
||||
smtp_server {{ keepalived_smtp_relay }}
|
||||
smtp_connect_timeout 30
|
||||
router_id {{ ansible_hostname }}
|
||||
{% endif %}
|
||||
}
|
||||
|
||||
vrrp_script chk_{{ keepalived_monitored_process }} {
|
||||
script "/usr/bin/killall -0 {{ keepalived_monitored_process }}" # Check if process is running
|
||||
interval 2 # Check every 2 seconds
|
||||
weight 2 # Weight to influence master election
|
||||
}
|
||||
{% for ip in keepalived_vrrp_ips %}
|
||||
|
||||
# master: {{ ip.master }}
|
||||
vrrp_instance VI_{{ ip.virtual_router_id }} {
|
||||
state {{ (ansible_hostname==ip.master) | ternary('MASTER', 'BACKUP') }}
|
||||
interface {{ ansible_default_ipv4.interface }}
|
||||
virtual_router_id {{ ip.virtual_router_id }}
|
||||
priority {{ (ansible_hostname==ip.master) | ternary('101', '100') }}
|
||||
advert_int 1
|
||||
promote_secondaries
|
||||
{% if keepalived_alerts_to | length > 0 %}
|
||||
smtp_alert true
|
||||
{% endif %}
|
||||
authentication {
|
||||
auth_type PASS
|
||||
auth_pass {{ keepalived_vrrp_password }}
|
||||
}
|
||||
virtual_ipaddress {
|
||||
{{ ip.addr }}
|
||||
}
|
||||
{% if keepalived_unicast_peers | length > 0 %}
|
||||
unicast_peer {
|
||||
{% for peer in keepalived_unicast_peers %}
|
||||
{% if peer != ansible_default_ipv4.address %}
|
||||
{{ peer }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
}
|
||||
{% endif %}
|
||||
track_script {
|
||||
chk_{{ keepalived_monitored_process }}
|
||||
}
|
||||
}
|
||||
{% endfor %}
|
||||
5
seb4itik/byow/roles/local_ca/README.md
Normal file
5
seb4itik/byow/roles/local_ca/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# BYOW - Build Your Own WAF
|
||||
|
||||
A WAF _à la carte_.
|
||||
|
||||
## Local CA
|
||||
11
seb4itik/byow/roles/local_ca/defaults/main.yml
Normal file
11
seb4itik/byow/roles/local_ca/defaults/main.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
# Defaults
|
||||
local_ca_key_size: 4096
|
||||
local_ca_key_type: "RSA"
|
||||
|
||||
# Required
|
||||
local_ca_common_name: null
|
||||
local_ca_passphrase: null
|
||||
local_ca_country_name: null
|
||||
local_ca_locality_name: null
|
||||
local_ca_organization_name: null
|
||||
local_ca_email_address: null
|
||||
51
seb4itik/byow/roles/local_ca/tasks/main.yml
Normal file
51
seb4itik/byow/roles/local_ca/tasks/main.yml
Normal file
@@ -0,0 +1,51 @@
|
||||
- name: Create CA directory
|
||||
delegate_to: "localhost"
|
||||
run_once: true
|
||||
ansible.builtin.file:
|
||||
path: "ca"
|
||||
state: "directory"
|
||||
mode: "0700"
|
||||
|
||||
- name: Create private key with password protection
|
||||
delegate_to: "localhost"
|
||||
run_once: true
|
||||
community.crypto.openssl_privatekey:
|
||||
path: "ca/ca-certificate.key"
|
||||
passphrase: "{{ local_ca_passphrase }}"
|
||||
size: "{{ local_ca_key_size }}"
|
||||
type: "{{ local_ca_key_type }}"
|
||||
mode: "0640"
|
||||
|
||||
- name: Create certificate signing request (CSR) for CA certificate
|
||||
delegate_to: "localhost"
|
||||
run_once: true
|
||||
community.crypto.openssl_csr:
|
||||
path: "ca/ca-certificate.csr"
|
||||
privatekey_path: "ca/ca-certificate.key"
|
||||
privatekey_passphrase: "{{ local_ca_passphrase }}"
|
||||
common_name: "{{ local_ca_common_name }}"
|
||||
use_common_name_for_san: false # since we do not specify SANs, don't use CN as a SAN
|
||||
country_name: "{{ local_ca_country_name }}"
|
||||
locality_name: "{{ local_ca_locality_name }}"
|
||||
organization_name: "{{ local_ca_organization_name }}"
|
||||
email_address: "{{ local_ca_email_address }}"
|
||||
basic_constraints:
|
||||
- "CA:TRUE"
|
||||
basic_constraints_critical: true
|
||||
key_usage:
|
||||
- "keyCertSign"
|
||||
key_usage_critical: true
|
||||
return_content: true
|
||||
register: local_ca_csr
|
||||
|
||||
- name: Create self-signed CA certificate from CSR
|
||||
delegate_to: "localhost"
|
||||
run_once: true
|
||||
# noqa: no-handler
|
||||
when: "local_ca_csr.changed"
|
||||
community.crypto.x509_certificate:
|
||||
path: "ca/ca-certificate.crt"
|
||||
csr_content: "{{ local_ca_csr.csr }}"
|
||||
privatekey_path: "ca/ca-certificate.key"
|
||||
privatekey_passphrase: "{{ local_ca_passphrase }}"
|
||||
provider: "selfsigned"
|
||||
5
seb4itik/byow/roles/local_ca_certs/README.md
Normal file
5
seb4itik/byow/roles/local_ca_certs/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# BYOW - Build Your Own WAF
|
||||
|
||||
A WAF _à la carte_.
|
||||
|
||||
## Local CA certificates
|
||||
19
seb4itik/byow/roles/local_ca_certs/defaults/main.yml
Normal file
19
seb4itik/byow/roles/local_ca_certs/defaults/main.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
# Defaults
|
||||
local_ca_certs_default_country_name: null
|
||||
local_ca_certs_default_locality_name: null
|
||||
local_ca_certs_default_organization_name: null
|
||||
local_ca_certs_default_email_address: null
|
||||
local_ca_certs_default_not_after: "+365d" # valid for one year
|
||||
local_ca_certs_default_not_before: "-1d" # valid since yesterday
|
||||
|
||||
# Required
|
||||
local_ca_certs_ca_passphrase: null
|
||||
local_ca_certs_list: {}
|
||||
# - cn: <string>
|
||||
# domains: <array of strings>
|
||||
# country_name: <string>
|
||||
# locality_name: <string>
|
||||
# organization_name: <string>
|
||||
# email_address: <string>
|
||||
# not_after: <string>
|
||||
# not_before: <string>
|
||||
2
seb4itik/byow/roles/local_ca_certs/handlers/main.yml
Normal file
2
seb4itik/byow/roles/local_ca_certs/handlers/main.yml
Normal file
@@ -0,0 +1,2 @@
|
||||
- name: Reboot host
|
||||
ansible.builtin.reboot:
|
||||
42
seb4itik/byow/roles/local_ca_certs/tasks/create_cert.yml
Normal file
42
seb4itik/byow/roles/local_ca_certs/tasks/create_cert.yml
Normal file
@@ -0,0 +1,42 @@
|
||||
- name: Create private key for new certificate
|
||||
community.crypto.openssl_privatekey:
|
||||
path: "/etc/ssl/private/{{ cert.cn }}.key"
|
||||
mode: "0640"
|
||||
group: "ssl-cert"
|
||||
|
||||
- name: Create CSR for new certificate
|
||||
community.crypto.openssl_csr:
|
||||
path: "/etc/ssl/certs/{{ cert.cn }}.csr"
|
||||
privatekey_path: "/etc/ssl/private/{{ cert.cn }}.key"
|
||||
common_name: "{{ cert.cn }}"
|
||||
subject_alt_name: "{{ cert.domains }}"
|
||||
country_name: "{{ cert.country_name | default(local_ca_certs_default_country_name) | mandatory }}"
|
||||
locality_name: "{{ cert.locality_name | default(local_ca_certs_default_locality_name) | mandatory }}"
|
||||
organization_name: "{{ cert.organization_name | default(local_ca_certs_default_organization_name) | mandatory }}"
|
||||
email_address: "{{ cert.email_address | default(local_ca_certs_default_email_address) | mandatory }}"
|
||||
return_content: true
|
||||
register: local_ca_certs_csr
|
||||
|
||||
- name: Sign certificate with our CA
|
||||
delegate_to: "localhost"
|
||||
# noqa: no-handler
|
||||
when: "local_ca_certs_csr.changed"
|
||||
community.crypto.x509_certificate_pipe:
|
||||
csr_content: "{{ local_ca_certs_csr.csr }}"
|
||||
provider: "ownca"
|
||||
ownca_path: "ca/ca-certificate.crt"
|
||||
ownca_privatekey_path: "ca/ca-certificate.key"
|
||||
ownca_privatekey_passphrase: "{{ local_ca_certs_ca_passphrase }}"
|
||||
ownca_not_after: "{{ cert.not_after | default(local_ca_certs_default_not_after) }}"
|
||||
ownca_not_before: "{{ cert.not_before | default(local_ca_certs_default_not_before) }}"
|
||||
register: local_ca_certs_certificate
|
||||
|
||||
- name: Write certificate file on server
|
||||
# noqa: no-handler
|
||||
when: "local_ca_certs_csr.changed"
|
||||
ansible.builtin.copy:
|
||||
dest: "/etc/ssl/certs/{{ cert.cn }}.crt"
|
||||
content: "{{ local_ca_certs_certificate.certificate }}"
|
||||
mode: "0644"
|
||||
notify:
|
||||
- "Reboot host"
|
||||
16
seb4itik/byow/roles/local_ca_certs/tasks/main.yml
Normal file
16
seb4itik/byow/roles/local_ca_certs/tasks/main.yml
Normal file
@@ -0,0 +1,16 @@
|
||||
- name: Install requirements
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- "ssl-cert"
|
||||
|
||||
- name: Copy CA certificate file on server
|
||||
ansible.builtin.copy:
|
||||
src: "ca/ca-certificate.crt"
|
||||
dest: "/etc/ssl/certs/local-ca.crt"
|
||||
mode: "0644"
|
||||
|
||||
- name: Create certs
|
||||
loop: "{{ local_ca_certs_list }}"
|
||||
loop_control:
|
||||
loop_var: "cert"
|
||||
ansible.builtin.include_tasks: "create_cert.yml"
|
||||
5
seb4itik/byow/roles/mysql/README.md
Normal file
5
seb4itik/byow/roles/mysql/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# BYOW - Build Your Own WAF
|
||||
|
||||
A WAF _à la carte_.
|
||||
|
||||
## MySQL
|
||||
6
seb4itik/byow/roles/mysql/defaults/main.yml
Normal file
6
seb4itik/byow/roles/mysql/defaults/main.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
# Required
|
||||
mysql_databases: []
|
||||
mysql_users: []
|
||||
# - name: <string
|
||||
# password: <string>
|
||||
# priv: <string>
|
||||
5
seb4itik/byow/roles/mysql/handlers/main.yml
Normal file
5
seb4itik/byow/roles/mysql/handlers/main.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
- name: Restart MySQL
|
||||
ansible.builtin.service:
|
||||
name: "mysql"
|
||||
state: "restarted"
|
||||
daemon_reload: true
|
||||
31
seb4itik/byow/roles/mysql/tasks/main.yml
Normal file
31
seb4itik/byow/roles/mysql/tasks/main.yml
Normal file
@@ -0,0 +1,31 @@
|
||||
- name: Installation de MySQL
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- "mysql-server"
|
||||
- "python3-pymysql"
|
||||
notify:
|
||||
- "Restart MySQL"
|
||||
|
||||
- name: Set MySQL bind_address parameter
|
||||
ansible.builtin.lineinfile:
|
||||
path: "/etc/mysql/mysql.conf.d/mysqld.cnf"
|
||||
regexp: "^bind-address\\s+="
|
||||
line: "bind-address = *"
|
||||
notify:
|
||||
- "Restart MySQL"
|
||||
|
||||
- name: Création des bases de données MySQL
|
||||
loop: "{{ mysql_databases }}"
|
||||
community.mysql.mysql_db:
|
||||
login_unix_socket: "/var/run/mysqld/mysqld.sock"
|
||||
name: "{{ item }}"
|
||||
encoding: "utf8"
|
||||
|
||||
- name: Création des utilisateur MySQL
|
||||
loop: "{{ mysql_users }}"
|
||||
community.mysql.mysql_user:
|
||||
login_unix_socket: "/var/run/mysqld/mysqld.sock"
|
||||
name: "{{ item.name }}"
|
||||
password: "{{ item.password }}"
|
||||
priv: "{{ item.priv }}"
|
||||
host: "{{ item.host }}"
|
||||
5
seb4itik/byow/roles/nfs_client/README.md
Normal file
5
seb4itik/byow/roles/nfs_client/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# BYOW - Build Your Own WAF
|
||||
|
||||
A WAF _à la carte_.
|
||||
|
||||
## NFS client
|
||||
8
seb4itik/byow/roles/nfs_client/defaults/main.yml
Normal file
8
seb4itik/byow/roles/nfs_client/defaults/main.yml
Normal file
@@ -0,0 +1,8 @@
|
||||
# Required
|
||||
nfs_client_mounts: []
|
||||
# - mount_point: <string>
|
||||
# server: <string>
|
||||
# options: <string> (default "rw,sync,hard")
|
||||
# owner: <int>
|
||||
# group: <int>
|
||||
# mode: <string> (default "0750")
|
||||
23
seb4itik/byow/roles/nfs_client/tasks/main.yml
Normal file
23
seb4itik/byow/roles/nfs_client/tasks/main.yml
Normal file
@@ -0,0 +1,23 @@
|
||||
- name: Installation de NFS Common
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- "nfs-common"
|
||||
|
||||
- name: Création des points de montage
|
||||
loop: "{{ nfs_client_mounts }}"
|
||||
ansible.builtin.file:
|
||||
path: "{{ item.mount_point }}"
|
||||
state: "directory"
|
||||
owner: "{{ item.owner }}"
|
||||
group: "{{ item.group }}"
|
||||
mode: "{{ item.mode | default('0750') }}"
|
||||
|
||||
- name: Mount filesystems
|
||||
loop: "{{ nfs_client_mounts }}"
|
||||
ansible.posix.mount:
|
||||
path: "{{ item.mount_point }}"
|
||||
src: "{{ item.server }}"
|
||||
fstype: "nfs4"
|
||||
opts: "{{ item.options | default('rw,sync,hard') }}"
|
||||
state: "mounted"
|
||||
boot: true
|
||||
5
seb4itik/byow/roles/nfs_server/README.md
Normal file
5
seb4itik/byow/roles/nfs_server/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# BYOW - Build Your Own WAF
|
||||
|
||||
A WAF _à la carte_.
|
||||
|
||||
## NFS Server
|
||||
9
seb4itik/byow/roles/nfs_server/defaults/main.yml
Normal file
9
seb4itik/byow/roles/nfs_server/defaults/main.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
# Required
|
||||
nfs_server_exports: []
|
||||
# - path: <string>
|
||||
# owner: <int>
|
||||
# group: <int>
|
||||
# mode: <string> (default "0750")
|
||||
# clients:
|
||||
# - address: <string>
|
||||
# options: <string> (default "ro")
|
||||
5
seb4itik/byow/roles/nfs_server/handlers/main.yml
Normal file
5
seb4itik/byow/roles/nfs_server/handlers/main.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
- name: Restart NFS-Server
|
||||
ansible.builtin.service:
|
||||
name: "nfs-kernel-server"
|
||||
enabled: true
|
||||
state: "restarted"
|
||||
36
seb4itik/byow/roles/nfs_server/tasks/main.yml
Normal file
36
seb4itik/byow/roles/nfs_server/tasks/main.yml
Normal file
@@ -0,0 +1,36 @@
|
||||
- name: Installation du serveur NFS
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- "nfs-kernel-server"
|
||||
notify:
|
||||
- "Restart NFS-Server"
|
||||
|
||||
- name: Activation de NFS 4
|
||||
ansible.builtin.lineinfile:
|
||||
dest: "/etc/nfs.conf"
|
||||
regex: "{{ item.regex }}"
|
||||
line: "{{ item.line }}"
|
||||
loop:
|
||||
- regex: "vers3=y"
|
||||
line: "vers3=n"
|
||||
- regex: "vers4=y"
|
||||
line: "vers4=y"
|
||||
notify:
|
||||
- "Restart NFS-Server"
|
||||
|
||||
- name: Création des répertoires exportés
|
||||
loop: "{{ nfs_server_exports }}"
|
||||
ansible.builtin.file:
|
||||
path: "{{ item.path }}"
|
||||
state: "directory"
|
||||
owner: "{{ item.owner }}"
|
||||
group: "{{ item.group }}"
|
||||
mode: "{{ item.mode | default('750') }}"
|
||||
|
||||
- name: Copie du fichier exports
|
||||
ansible.builtin.template:
|
||||
src: "templates/exports"
|
||||
dest: "/etc/exports"
|
||||
mode: "0644"
|
||||
notify:
|
||||
- "Restart NFS-Server"
|
||||
3
seb4itik/byow/roles/nfs_server/templates/exports
Normal file
3
seb4itik/byow/roles/nfs_server/templates/exports
Normal file
@@ -0,0 +1,3 @@
|
||||
{% for export in nfs_server_exports %}
|
||||
{{ export.path }}{% for c in export.clients %} {{ c.address }}({{ c.options | default ("ro") }}){% endfor %}
|
||||
{% endfor %}
|
||||
5
seb4itik/byow/roles/ovh_ldp/README.md
Normal file
5
seb4itik/byow/roles/ovh_ldp/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# BYOW - Build Your Own WAF
|
||||
|
||||
A WAF _à la carte_.
|
||||
|
||||
## OVH Log Data Platform
|
||||
6
seb4itik/byow/roles/ovh_ldp/defaults/main.yml
Normal file
6
seb4itik/byow/roles/ovh_ldp/defaults/main.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
# Required
|
||||
ovh_ldp_token: null
|
||||
ovh_ldp_cluster: null
|
||||
|
||||
# Defaults
|
||||
ovh_ldp_tls_syslog_port: 6514
|
||||
5
seb4itik/byow/roles/ovh_ldp/handlers/main.yml
Normal file
5
seb4itik/byow/roles/ovh_ldp/handlers/main.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
- name: Restart syslog-ng
|
||||
ansible.builtin.service:
|
||||
name: "syslog-ng"
|
||||
state: "restarted"
|
||||
daemon_reload: true
|
||||
13
seb4itik/byow/roles/ovh_ldp/tasks/main.yml
Normal file
13
seb4itik/byow/roles/ovh_ldp/tasks/main.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
- name: Install syslog-ng
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- "syslog-ng"
|
||||
- "ca-certificates"
|
||||
|
||||
- name: Generate syslog-ng config file for OVH LDP
|
||||
ansible.builtin.template:
|
||||
src: "templates/ldp-ovh.conf"
|
||||
dest: "/etc/syslog-ng/conf.d/ldp-ovh.conf"
|
||||
mode: "0640"
|
||||
notify:
|
||||
- "Restart syslog-ng"
|
||||
22
seb4itik/byow/roles/ovh_ldp/templates/ldp-ovh.conf
Normal file
22
seb4itik/byow/roles/ovh_ldp/templates/ldp-ovh.conf
Normal file
@@ -0,0 +1,22 @@
|
||||
rewrite ovh-token {
|
||||
set("{{ ovh_ldp_token }}", value(".SDATA.token@29084.X-OVH-TOKEN"));
|
||||
};
|
||||
|
||||
destination ovhPaaSLogs {
|
||||
network("{{ ovh_ldp_cluster }}"
|
||||
port({{ ovh_ldp_tls_syslog_port }})
|
||||
transport("tls")
|
||||
flags(syslog-protocol)
|
||||
ts_format("iso")
|
||||
frac-digits(6)
|
||||
tls(peer-verify("required-trusted") ca_dir("/etc/ssl/certs/"))
|
||||
keep-alive(yes)
|
||||
so_keepalive(yes)
|
||||
);
|
||||
};
|
||||
|
||||
log {
|
||||
source(s_src);
|
||||
rewrite(ovh-token);
|
||||
destination(ovhPaaSLogs);
|
||||
};
|
||||
5
seb4itik/byow/roles/unbound/README.md
Normal file
5
seb4itik/byow/roles/unbound/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# BYOW - Build Your Own WAF
|
||||
|
||||
A WAF _à la carte_.
|
||||
|
||||
## Unbound
|
||||
14
seb4itik/byow/roles/unbound/defaults/main.yml
Normal file
14
seb4itik/byow/roles/unbound/defaults/main.yml
Normal file
@@ -0,0 +1,14 @@
|
||||
unbound_listen_interfaces:
|
||||
- "0.0.0.0"
|
||||
|
||||
unbound_access_control:
|
||||
- "0.0.0.0/0 allow"
|
||||
|
||||
unbound_forward_zones:
|
||||
- name: "."
|
||||
forward_to: "8.8.8.8"
|
||||
|
||||
unbound_stub_zones: []
|
||||
# - name: <string>
|
||||
# stub_to: <string>
|
||||
# no_cache: no (default yes)
|
||||
4
seb4itik/byow/roles/unbound/handlers/main.yml
Normal file
4
seb4itik/byow/roles/unbound/handlers/main.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
- name: Restart unbound
|
||||
ansible.builtin.service:
|
||||
name: "unbound"
|
||||
state: "restarted"
|
||||
14
seb4itik/byow/roles/unbound/tasks/main.yml
Normal file
14
seb4itik/byow/roles/unbound/tasks/main.yml
Normal file
@@ -0,0 +1,14 @@
|
||||
- name: Install Unbound
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- "unbound"
|
||||
notify:
|
||||
- "Restart unbound"
|
||||
|
||||
- name: Deploy unbounf config file
|
||||
ansible.builtin.template:
|
||||
src: "templates/unbound.conf"
|
||||
dest: "/etc/unbound/unbound.conf.d/unbound.conf"
|
||||
mode: "0644"
|
||||
notify:
|
||||
- "Restart unbound"
|
||||
21
seb4itik/byow/roles/unbound/templates/unbound.conf
Normal file
21
seb4itik/byow/roles/unbound/templates/unbound.conf
Normal file
@@ -0,0 +1,21 @@
|
||||
server:
|
||||
do-not-query-localhost: no
|
||||
{% for interface in unbound_listen_interfaces %}
|
||||
interface: {{ interface }}
|
||||
{% endfor %}
|
||||
{% for access in unbound_access_control %}
|
||||
access-control: {{ access }}
|
||||
{% endfor %}
|
||||
{% for zone in unbound_stub_zones %}
|
||||
|
||||
stub-zone:
|
||||
name: "{{ zone.name }}"
|
||||
stub-addr: {{ zone.stub_to }}
|
||||
stub-no-cache: {{ zone.no_cache | default('yes') }}
|
||||
{% endfor %}
|
||||
{% for zone in unbound_forward_zones %}
|
||||
|
||||
forward-zone:
|
||||
name: "{{ zone.name }}"
|
||||
forward-addr: {{ zone.forward_to }}
|
||||
{% endfor %}
|
||||
5
seb4itik/byow/roles/wordpress/README.md
Normal file
5
seb4itik/byow/roles/wordpress/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# BYOW - Build Your Own WAF
|
||||
|
||||
A WAF _à la carte_.
|
||||
|
||||
## Wordpress
|
||||
24
seb4itik/byow/roles/wordpress/defaults/main.yml
Normal file
24
seb4itik/byow/roles/wordpress/defaults/main.yml
Normal file
@@ -0,0 +1,24 @@
|
||||
# Required
|
||||
wordpress_url: null
|
||||
wordpress_site: null
|
||||
wordpress_site_title: null
|
||||
wordpress_admin_user: null
|
||||
wordpress_admin_password: null
|
||||
wordpress_admin_email: null
|
||||
wordpress_db_pass: null
|
||||
wordpress_themes: []
|
||||
wordpress_active_theme: null
|
||||
|
||||
# Defaults
|
||||
wordpress_user: "wordpress"
|
||||
wordpress_plugins: []
|
||||
wordpress_locale: "fr_FR"
|
||||
wordpress_db_host: "localhost"
|
||||
wordpress_db_name: "{{ wordpress_user }}"
|
||||
wordpress_db_user: "{{ wordpress_user }}"
|
||||
wordpress_db_prefix: "wp_"
|
||||
wordpress_db_charset: "utf8mb4"
|
||||
|
||||
# Optionnal
|
||||
wordpress_import_ocdi_indexes: []
|
||||
wordpress_create_master: null
|
||||
17
seb4itik/byow/roles/wordpress/handlers/main.yml
Normal file
17
seb4itik/byow/roles/wordpress/handlers/main.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
- name: Restart Nginx
|
||||
ansible.builtin.systemd:
|
||||
name: "nginx"
|
||||
state: "restarted"
|
||||
|
||||
- name: Flush Wordpress
|
||||
become: true
|
||||
become_user: "{{ wordpress_user }}"
|
||||
ansible.builtin.shell:
|
||||
executable: "/usr/bin/bash"
|
||||
chdir: "/var/www/{{ wordpress_user }}/{{ wordpress_site }}"
|
||||
cmd: |
|
||||
set -o pipefail
|
||||
wp cache flush
|
||||
wp transient delete --all
|
||||
wp rewrite flush --hard
|
||||
changed_when: true
|
||||
12
seb4itik/byow/roles/wordpress/tasks/install_plugins.yml
Normal file
12
seb4itik/byow/roles/wordpress/tasks/install_plugins.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
- name: Installation des plugins Wordpress
|
||||
become: true
|
||||
become_user: "{{ wordpress_user }}"
|
||||
loop: "{{ wordpress_plugins_to_install }}"
|
||||
ansible.builtin.command:
|
||||
chdir: "/var/www/{{ wordpress_user }}/{{ wordpress_site }}"
|
||||
cmd: "wp plugin install {{ item }} --activate"
|
||||
creates: "/var/www/{{ wordpress_user }}/{{ wordpress_site }}/wp-content/plugins/{{ item | basename | splitext | first }}"
|
||||
register: "wordpress_out_plugin_install"
|
||||
changed_when: "'Success: Installed 1 of 1 plugins' in wordpress_out_plugin_install.stdout"
|
||||
notify:
|
||||
- "Flush Wordpress"
|
||||
214
seb4itik/byow/roles/wordpress/tasks/main.yml
Normal file
214
seb4itik/byow/roles/wordpress/tasks/main.yml
Normal file
@@ -0,0 +1,214 @@
|
||||
- name: Installation des pré-requis
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- "nginx"
|
||||
- "php-fpm"
|
||||
- "php-mysql"
|
||||
- "php-curl"
|
||||
- "php-json"
|
||||
- "php-xml"
|
||||
- "php-mbstring"
|
||||
- "php-zip"
|
||||
- "php-igbinary"
|
||||
- "php-imagick"
|
||||
- "php-intl"
|
||||
- "php-apcu"
|
||||
- "zip"
|
||||
|
||||
- name: Installation de wp-cli
|
||||
when: "wordpress_create_master != None and wordpress_create_master == ansible_hostname"
|
||||
ansible.builtin.get_url:
|
||||
url: "https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar"
|
||||
dest: "/usr/local/bin/wp"
|
||||
owner: "root"
|
||||
group: "root"
|
||||
mode: "755"
|
||||
|
||||
- name: Création de l'utilisateur système pour Wordpress
|
||||
ansible.builtin.user:
|
||||
name: "{{ wordpress_user | mandatory }}"
|
||||
home: "/var/www/{{ wordpress_user }}"
|
||||
group: "www-data"
|
||||
shell: "/usr/bin/bash"
|
||||
password_lock: true
|
||||
create_home: true
|
||||
|
||||
- name: Création du répertoire de Wordpress
|
||||
ansible.builtin.file:
|
||||
path: "/var/www/{{ wordpress_user }}/{{ wordpress_site }}"
|
||||
state: "directory"
|
||||
owner: "{{ wordpress_user }}"
|
||||
group: "www-data"
|
||||
mode: "0750"
|
||||
|
||||
- name: Installation de Wordpress
|
||||
when: "wordpress_create_master != None and wordpress_create_master == ansible_hostname"
|
||||
become: true
|
||||
become_user: "{{ wordpress_user }}"
|
||||
ansible.builtin.command:
|
||||
chdir: "/var/www/{{ wordpress_user }}/{{ wordpress_site }}"
|
||||
cmd: "wp core download --locale='{{ wordpress_locale }}' --skip-content"
|
||||
creates: "/var/www/{{ wordpress_user }}/{{ wordpress_site }}/wp-config-sample.php"
|
||||
|
||||
# TODO: https://make.wordpress.org/cli/handbook/references/config/#config-files
|
||||
- name: Configuration de Wordpress
|
||||
when: "wordpress_create_master != None and wordpress_create_master == ansible_hostname"
|
||||
become: true
|
||||
become_user: "{{ wordpress_user }}"
|
||||
ansible.builtin.shell:
|
||||
chdir: "/var/www/{{ wordpress_user }}/{{ wordpress_site }}"
|
||||
executable: "/usr/bin/bash"
|
||||
cmd: |
|
||||
set -o pipefail
|
||||
wp config create \
|
||||
--dbhost='{{ wordpress_db_host }}' \
|
||||
--dbname='{{ wordpress_db_name }}' \
|
||||
--dbuser='{{ wordpress_db_user }}' \
|
||||
--dbpass='{{ wordpress_db_pass }}' \
|
||||
--dbprefix='{{ wordpress_db_prefix }}' \
|
||||
--dbcharset='{{ wordpress_db_charset }}' \
|
||||
--extra-php <<PHP
|
||||
define('FS_METHOD', 'direct');
|
||||
define('FORCE_SSL_ADMIN', false);
|
||||
define('WP_HOME', 'https://{{ wordpress_site }}');
|
||||
define('WP_SITEURL', 'https://{{ wordpress_site }}');
|
||||
|
||||
error_reporting(0);
|
||||
@ini_set('display_errors', 0);
|
||||
define('DISALLOW_FILE_EDIT', true);
|
||||
|
||||
if (isset(\$_SERVER['HTTP_X_FORWARDED_PROTO']) && \$_SERVER['HTTP_X_FORWARDED_PROTO']=='https')
|
||||
\$_SERVER['HTTPS'] = 'on';
|
||||
PHP
|
||||
creates: "/var/www/{{ wordpress_user }}/{{ wordpress_site }}/wp-config.php"
|
||||
notify:
|
||||
- "Flush Wordpress"
|
||||
|
||||
- name: Create Wordpress database tables
|
||||
when: "wordpress_create_master != None and wordpress_create_master == ansible_hostname"
|
||||
become: true
|
||||
become_user: "{{ wordpress_user }}"
|
||||
ansible.builtin.command:
|
||||
chdir: "/var/www/{{ wordpress_user }}/{{ wordpress_site }}"
|
||||
cmd: >
|
||||
wp core install
|
||||
--url='{{ wordpress_url }}'
|
||||
--title='{{ wordpress_site_title }}'
|
||||
--admin_user='{{ wordpress_admin_user }}'
|
||||
--admin_password='{{ wordpress_admin_password }}'
|
||||
--admin_email='{{ wordpress_admin_email }}'
|
||||
--locale='{{ wordpress_locale }}'
|
||||
--skip-email
|
||||
creates: "/var/www/{{ wordpress_user }}/{{ wordpress_site }}/wp-content/uploads/ }}"
|
||||
register: "wordpress_out_core_install"
|
||||
changed_when: "'Success: WordPress installed successfully.' in wordpress_out_core_install.stdout"
|
||||
notify:
|
||||
- "Flush Wordpress"
|
||||
|
||||
- name: Installation des thèmes Wordpress
|
||||
when: "wordpress_create_master != None and wordpress_create_master == ansible_hostname"
|
||||
become: true
|
||||
become_user: "{{ wordpress_user }}"
|
||||
loop: "{{ wordpress_themes }}"
|
||||
ansible.builtin.command:
|
||||
chdir: "/var/www/{{ wordpress_user }}/{{ wordpress_site }}"
|
||||
cmd: "wp theme install '{{ item }}'"
|
||||
creates: "/var/www/{{ wordpress_user }}/{{ wordpress_site }}/wp-content/themes/{{ item | basename | splitext | first }}"
|
||||
register: "wordpress_out_theme_install"
|
||||
changed_when: "'Success: Installed 1 of 1 themes' in wordpress_out_theme_install.stdout"
|
||||
notify:
|
||||
- "Flush Wordpress"
|
||||
|
||||
- name: Activation du thème Wordpress
|
||||
when: "wordpress_create_master != None and wordpress_create_master == ansible_hostname"
|
||||
become: true
|
||||
become_user: "{{ wordpress_user }}"
|
||||
ansible.builtin.command:
|
||||
chdir: "/var/www/{{ wordpress_user }}/{{ wordpress_site }}"
|
||||
cmd: "wp theme activate '{{ wordpress_active_theme }}'"
|
||||
register: "wordpress_out_theme_activate"
|
||||
changed_when: "'Success: Switched to ' in wordpress_out_theme_activate.stdout"
|
||||
notify:
|
||||
- "Flush Wordpress"
|
||||
|
||||
- name: Installation des plugins wordpress-importer et one-click-demo-import
|
||||
when: "wordpress_create_master != None and wordpress_create_master == ansible_hostname"
|
||||
vars:
|
||||
wordpress_plugins_to_install:
|
||||
- "wordpress-importer"
|
||||
- "one-click-demo-import"
|
||||
ansible.builtin.include_tasks: "install_plugins.yml"
|
||||
|
||||
- name: Importation des plugins OCDI
|
||||
when: "wordpress_create_master != None and wordpress_create_master == ansible_hostname"
|
||||
become: true
|
||||
become_user: "{{ wordpress_user }}"
|
||||
loop: "{{ wordpress_import_ocdi_indexes }}"
|
||||
ansible.builtin.shell:
|
||||
chdir: "/var/www/{{ wordpress_user }}/{{ wordpress_site }}"
|
||||
executable: "/usr/bin/bash"
|
||||
cmd: |
|
||||
set -o pipefail
|
||||
wp ocdi import --predefined='{{ item }}' \
|
||||
&& touch /var/www/{{ wordpress_user }}/{{ wordpress_site }}/.ocdi_index{{ item }}_imported
|
||||
creates: "/var/www/{{ wordpress_user }}/{{ wordpress_site }}/.ocdi_index{{ item }}_imported"
|
||||
register: "wordpress_out_ocdi_import"
|
||||
changed_when: "'Success: Content import finished!' in wordpress_out_ocdi_import.stdout"
|
||||
notify:
|
||||
- "Flush Wordpress"
|
||||
|
||||
- name: Installation des plugins Wordpress
|
||||
when: "wordpress_create_master != None and wordpress_create_master == ansible_hostname"
|
||||
vars:
|
||||
wordpress_plugins_to_install: "{{ wordpress_plugins }}"
|
||||
ansible.builtin.include_tasks: "install_plugins.yml"
|
||||
|
||||
- name: Création du répertoire cache de Wordpress
|
||||
when: "wordpress_create_master != None and wordpress_create_master == ansible_hostname"
|
||||
ansible.builtin.file:
|
||||
path: "/var/www/{{ wordpress_user }}/{{ wordpress_site }}/wp-content/cache"
|
||||
state: "directory"
|
||||
owner: "{{ wordpress_user }}"
|
||||
group: "www-data"
|
||||
mode: "0750"
|
||||
|
||||
- name: Apply permissions
|
||||
when: "wordpress_create_master != None and wordpress_create_master == ansible_hostname"
|
||||
loop:
|
||||
- "cache"
|
||||
- "upgrade"
|
||||
- "uploads"
|
||||
ansible.builtin.file:
|
||||
path: "/var/www/{{ wordpress_user }}/{{ wordpress_site }}/wp-content/{{ item }}/"
|
||||
owner: "{{ wordpress_user }}"
|
||||
group: "www-data"
|
||||
mode: "u=rwX,g=rwX,o=rX"
|
||||
recurse: true
|
||||
|
||||
- name: Configuration de Nginx
|
||||
ansible.builtin.template:
|
||||
src: "templates/nginx_vhost.conf"
|
||||
dest: "/etc/nginx/sites-available/{{ wordpress_site }}.conf"
|
||||
owner: "root"
|
||||
group: "root"
|
||||
mode: "0644"
|
||||
notify:
|
||||
- "Restart Nginx"
|
||||
|
||||
- name: Link Nginx vhost config
|
||||
ansible.builtin.file:
|
||||
src: "/etc/nginx/sites-available/{{ wordpress_site }}.conf"
|
||||
dest: "/etc/nginx/sites-enabled/{{ wordpress_site }}.conf"
|
||||
state: "link"
|
||||
notify:
|
||||
- "Restart Nginx"
|
||||
|
||||
- name: Unlink Nginx default vhost config
|
||||
ansible.builtin.file:
|
||||
path: "/etc/nginx/sites-enabled/default"
|
||||
state: "absent"
|
||||
notify:
|
||||
- "Restart Nginx"
|
||||
|
||||
# FIXME: php. ini config
|
||||
# Mainly: upload_max_filesize, post_max_size, max_execution_time
|
||||
50
seb4itik/byow/roles/wordpress/templates/nginx_vhost.conf
Normal file
50
seb4itik/byow/roles/wordpress/templates/nginx_vhost.conf
Normal file
@@ -0,0 +1,50 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name {{ wordpress_site }} www.{{ wordpress_site }};
|
||||
root /var/www/{{ wordpress_user }}/{{ wordpress_site }};
|
||||
index index.html index.htm index.php;
|
||||
client_max_body_size 50M;
|
||||
|
||||
location /nginx_status {
|
||||
stub_status on;
|
||||
access_log off;
|
||||
allow 127.0.0.1;
|
||||
deny all;
|
||||
}
|
||||
|
||||
location ~ /\.ht {
|
||||
deny all;
|
||||
}
|
||||
|
||||
location ~ /wp-config\.php\$ {
|
||||
deny all;
|
||||
}
|
||||
|
||||
location ~ /wp-content/uploads/.*\.php\$ {
|
||||
deny all;
|
||||
}
|
||||
|
||||
location ~ \.php$ {
|
||||
include snippets/fastcgi-php.conf;
|
||||
fastcgi_pass unix:/var/run/php/php-fpm.sock;
|
||||
}
|
||||
|
||||
# Réécritures pour WordPress
|
||||
location ~ ^/index\.php$ { }
|
||||
location / {
|
||||
if (!-e $request_filename){
|
||||
rewrite ^/(.*)$ /index.php last;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
# location / {
|
||||
# try_files \$uri \$uri/ /index.php?\$args;
|
||||
# }
|
||||
|
||||
# location ~ \.php\$ {
|
||||
# include snippets/fastcgi-php.conf;
|
||||
# fastcgi_pass unix:/var/run/php/php-fpm.sock;
|
||||
# fastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name;
|
||||
# }
|
||||
}
|
||||
Reference in New Issue
Block a user