diff --git a/docker/create_networks.sh b/docker/create_networks.sh new file mode 100644 index 0000000..49b2155 --- /dev/null +++ b/docker/create_networks.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +function create_networks() { + docker network create caddy_net + echo "[ DOCKER ]: Created caddy intranet 'caddy_net'" + docker network create monitoring_net + echo "[ DOCKER ]: Created monitoring intranet 'monitoring_net'" +} diff --git a/install.sh b/install.sh index eafa420..16bf7f4 100644 --- a/install.sh +++ b/install.sh @@ -5,6 +5,7 @@ source ./user/ssh_config.sh source ./web/install_caddy.sh source ./web/setup_ufw.sh source ./docker/install_docker.sh +source ./docker/create_networks.sh source ./utils/install_vim.sh source ./utils/install_zsh.sh source ./monitoring/install_prometheus.sh @@ -25,6 +26,7 @@ fi # Docker install_docker +create_networks # Web install_caddy $1 diff --git a/templates/caddy/full/Makefile b/templates/caddy/full/Makefile new file mode 100644 index 0000000..4180df8 --- /dev/null +++ b/templates/caddy/full/Makefile @@ -0,0 +1,18 @@ +# Makefile for managing Caddy + +.PHONY: caddy\:restart caddy\:update-api-key caddy\:logs + +caddy\:restart: + @echo "Formatting and reloading Caddy configuration..." + docker exec caddy caddy fmt --overwrite --config /etc/caddy/Caddyfile + docker exec caddy caddy reload --config /etc/caddy/Caddyfile + @echo "Caddy configuration reloaded successfully." + +caddy\:crowdsec-key + @echo "Generating new CrowdSec API key..." + @docker exec caddy caddy reload --config /etc/caddy/Caddyfile || true + @echo "\n=== IMPORTANT ===\nCopy the API_KEY from the output above and replace the value of CROWDSEC_API_KEY in your .env file." + +caddy\:logs: + @echo "Showing Caddy logs..." + docker compose logs -f caddy diff --git a/templates/caddy/full/caddy/Caddyfile b/templates/caddy/full/caddy/Caddyfile index fc7dd9b..02ca693 100644 --- a/templates/caddy/full/caddy/Caddyfile +++ b/templates/caddy/full/caddy/Caddyfile @@ -2,6 +2,11 @@ # Put Coraza in front of every request order coraza_waf first + # Enable metrics for Prometheus + servers { + metrics + } + # Logging log { level DEBUG @@ -16,24 +21,40 @@ } -# Example static -static.example.com { - coraza_waf { - directives ` - Include /etc/caddy/coraza.conf - ` - } - - root * /src/static/test - file_server -} +# Example: Static file server +# static.example.com { +# coraza_waf { +# directives ` +# Include /etc/caddy/coraza.conf +# ` +# } +# +# root * /src/static/test +# file_server +# } -api.example.com { - coraza_waf { - directives ` - Include /etc/caddy/coraza.conf - ` - } +# Example: Reverse Proxy for service running in docker container (must be under caddy_net) +# api.example.com { +# coraza_waf { +# directives ` +# Include /etc/caddy/coraza.conf +# ` +# } +# +# reverse_proxy * http://{CONTAINER_NAME}:{CONTAINER_PORT} +# } - reverse_proxy * http://{CONTAINER_NAME}:{CONTAINER_PORT} -} +# Example: Bypassing WAF for given API path (Useful for allowing prometheus query) +# api.example2.com { +# @waf { +# not path /api/v1/* +# } +# handle @waf { +# coraza_waf { +# directives ` +# Include /etc/caddy/coraza.conf +# ` +# } +# } +# reverse_proxy * http://prometheus:9090 +# } diff --git a/templates/caddy/full/caddy/coraza/coraza.conf b/templates/caddy/full/caddy/coraza/coraza.conf index f5dcd61..72163b4 100644 --- a/templates/caddy/full/caddy/coraza/coraza.conf +++ b/templates/caddy/full/caddy/coraza/coraza.conf @@ -1,15 +1,186 @@ +# NOTE: Coraza WAF recommended conf: [https://github.com/corazawaf/coraza/blob/main/coraza.conf-recommended] + +# -- 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 then the value given on the first line has to be as large +# as the largest file you are willing to accept. The second value refers +# to the size of data, with files excluded. You want to keep that value as +# low as practical. +# +SecRequestBodyLimit 13107200 + +SecRequestBodyInMemoryLimit 131072 + +# 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. +# +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/ + +# By default, only keep the files that were determined to be unusual +# in some way (by an external inspection script). For this to work you +# will also need at least one file inspection rule. +# +#SecUploadKeepFiles RelevantOnly + +# 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). +# +#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) +# Most logging has not been implemented because it will be replaced with +# advanced rule profiling options +#SecDebugLog /opt/coraza/var/log/debug.log +#SecDebugLogLevel 3 + + +# -- 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])$" + +# Log everything we know about a transaction. +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 Native + +# 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}'" + + +#--------- CUSTOM RULES ---------# + # OWASP CRS rules Include /etc/caddy/coreruleset/crs-setup.conf Include /etc/caddy/coreruleset/rules/*.conf # Custom rules SecRuleEngine On - -# Block SQLi -SecRule ARGS "@detectSQLi" \ - "id:1000,\ - phase:2,\ - deny,\ - status:403,\ - msg:'SQL Injection Detected'" - diff --git a/templates/caddy/full/docker-compose.yml b/templates/caddy/full/docker-compose.yml index 0499a71..730741f 100644 --- a/templates/caddy/full/docker-compose.yml +++ b/templates/caddy/full/docker-compose.yml @@ -10,6 +10,7 @@ services: - COLLECTIONS=crowdsecurity/caddy crowdsecurity/whitelist-good-actors crowdsecurity/http-cve - BOUNCER_KEY_CADDY=${CROWDSEC_API_KEY} networks: + - monitoring_net - caddy_net restart: unless-stopped @@ -30,6 +31,7 @@ services: - caddy_config:/config networks: - caddy_net + - monitoring_net depends_on: - crowdsec restart: unless-stopped @@ -41,3 +43,5 @@ volumes: networks: caddy_net: external: true + monitoring_net: + external: true diff --git a/templates/monitoring/docker-compose.yml b/templates/monitoring/docker-compose.yml index a15cd10..20548d4 100644 --- a/templates/monitoring/docker-compose.yml +++ b/templates/monitoring/docker-compose.yml @@ -20,7 +20,7 @@ services: networks: monitoring_net: - driver: bridge + external: true caddy_net: external: true diff --git a/templates/monitoring/prometheus.yml b/templates/monitoring/prometheus.yml index c49cb8c..3773141 100644 --- a/templates/monitoring/prometheus.yml +++ b/templates/monitoring/prometheus.yml @@ -10,3 +10,11 @@ scrape_configs: - job_name: 'node' static_configs: - targets: ['node-exporter:9100'] + + - job_name: 'crowdsec' + static_configs: + - targets: ['crowdsec:6060'] + + - job_name: 'caddy' + static_configs: + - targets: ['caddy:2019'] diff --git a/utils/install_make.sh b/utils/install_make.sh new file mode 100644 index 0000000..001a740 --- /dev/null +++ b/utils/install_make.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +function install_make() { + echo "[ UTILS ]: Installing Make" + sudo apt update + sudo apt install build-essential + echo "[ UTILS ]: Make installed succesfully" +} diff --git a/web/install_caddy.sh b/web/install_caddy.sh index 4f0f8d6..37a88f3 100644 --- a/web/install_caddy.sh +++ b/web/install_caddy.sh @@ -18,12 +18,11 @@ function install_caddy() { wget "$REPO_URL/$TEMPLATE_PATH/docker-compose.yml" -O "$caddy_dir/docker-compose.yml" wget "$REPO_URL/$TEMPLATE_PATH/.env" -O "$caddy_dir/.env" + wget "$REPO_URL/$TEMPLATE_PATH/Makefile" -O "$caddy_dir/Makefile" wget "$REPO_URL/$TEMPLATE_PATH/caddy/Caddyfile" -O "$caddy_dir/caddy/Caddyfile" wget "$REPO_URL/$TEMPLATE_PATH/caddy/coraza/coraza.conf" -O "$caddy_dir/caddy/coraza/coraza.conf" wget "$REPO_URL/$TEMPLATE_PATH/crowdsec/acquis.yaml" -O "$caddy_dir/crowdsec/acquis.yaml" echo "[ WEB ]: Caddy setup succesfully. You can find the Caddyfile under /home/$username/web_server/caddy" echo "[ WEB ]: Do not forget to update the .env file located under $caddy_dir" - docker network create caddy_net - echo "[ WEB ]: Created caddy intranet 'caddy_net'" }