add modsec

Signed-off-by: Zoey <zoey@z0ey.de>

Update Dockerfile
This commit is contained in:
Zoey
2023-05-18 17:03:35 +02:00
parent 73842be397
commit 7e6612467f
13 changed files with 151 additions and 114 deletions

View File

@@ -28,12 +28,14 @@ RUN apk add --no-cache ca-certificates nodejs-current yarn && \
node-prune && \
yarn cache clean --all
FROM python:3.11.3-alpine3.18 as certbot
RUN apk add --no-cache ca-certificates build-base libffi-dev && \
python3 -m venv /usr/local/certbot && \
. /usr/local/certbot/bin/activate && \
pip install --no-cache-dir certbot
FROM --platform="$BUILDPLATFORM" alpine:3.18.0 as crowdsec
RUN apk add --no-cache ca-certificates git build-base && \
git clone --recursive https://github.com/crowdsecurity/cs-nginx-bouncer /src && \
@@ -50,17 +52,55 @@ RUN apk add --no-cache ca-certificates git build-base && \
sed -i "s|BAN_TEMPLATE_PATH=.*|BAN_TEMPLATE_PATH=/data/etc/crowdsec/ban.html|g" lua-mod/config_example.conf && \
sed -i "s|CAPTCHA_TEMPLATE_PATH=.*|CAPTCHA_TEMPLATE_PATH=/data/etc/crowdsec/crowdsec.conf|g" lua-mod/config_example.conf
FROM zoeyvid/nginx-quic:126
FROM zoeyvid/nginx-quic:142
COPY rootfs /
RUN apk add --no-cache ca-certificates tzdata \
lua5.1-lzlib \
nodejs-current \
luarocks5.1 wget lua5.1-dev build-base \
openssl apache2-utils \
coreutils grep jq curl shadow sudo && \
coreutils grep jq curl shadow sudo \
luarocks5.1 wget lua5.1-dev build-base git && \
wget https://raw.githubusercontent.com/SpiderLabs/ModSecurity/v3/master/modsecurity.conf-recommended -O /usr/local/nginx/conf/conf.d/include/modsecurity.conf && \
wget https://raw.githubusercontent.com/SpiderLabs/ModSecurity/v3/master/unicode.mapping -O /usr/local/nginx/conf/conf.d/include/unicode.mapping && \
sed -i "s|SecRuleEngine .*|SecRuleEngine On|g" /usr/local/nginx/conf/conf.d/include/modsecurity.conf && \
echo "Include /data/etc/modsecurity/modsecurity.conf" | tee -a /usr/local/nginx/conf/conf.d/include/modsecurity.conf && \
cp /usr/local/nginx/conf/conf.d/include/modsecurity.conf /usr/local/nginx/conf/conf.d/include/modsecurity-crs.conf && \
echo "Include /data/etc/modsecurity/crs-setup.conf" | tee -a /usr/local/nginx/conf/conf.d/include/modsecurity-crs.conf && \
echo "Include /usr/local/nginx/conf/conf.d/include/coreruleset/crs-setup.conf" | tee -a /usr/local/nginx/conf/conf.d/include/modsecurity-crs.conf && \
echo "#Include /usr/local/nginx/conf/conf.d/include/coreruleset/plugins/*-config.conf" | tee -a /usr/local/nginx/conf/conf.d/include/modsecurity-crs.conf && \
echo "#Include /usr/local/nginx/conf/conf.d/include/coreruleset/plugins/*-before.conf" | tee -a /usr/local/nginx/conf/conf.d/include/modsecurity-crs.conf && \
echo "Include /usr/local/nginx/conf/conf.d/include/coreruleset/rules/*.conf" | tee -a /usr/local/nginx/conf/conf.d/include/modsecurity-crs.conf && \
echo "#Include /usr/local/nginx/conf/conf.d/include/coreruleset/plugins/*-after.conf" | tee -a /usr/local/nginx/conf/conf.d/include/modsecurity-crs.conf && \
git clone https://github.com/coreruleset/coreruleset /tmp/coreruleset && \
wget https://patch-diff.githubusercontent.com/raw/coreruleset/coreruleset/pull/3218.patch -O /tmp/coreruleset/http3.patch && \
cd /tmp/coreruleset && \
git apply /tmp/coreruleset/http3.patch && \
mkdir /usr/local/nginx/conf/conf.d/include/coreruleset && \
cp /tmp/coreruleset/crs-setup.conf.example /usr/local/nginx/conf/conf.d/include/coreruleset/crs-setup.conf.example && \
sed -i '/#/!d' /usr/local/nginx/conf/conf.d/include/coreruleset/crs-setup.conf.example && \
mv /tmp/coreruleset/crs-setup.conf.example /usr/local/nginx/conf/conf.d/include/coreruleset/crs-setup.conf && \
mv /tmp/coreruleset/rules /usr/local/nginx/conf/conf.d/include/coreruleset/rules && \
git clone --recursive https://github.com/coreruleset/phpmyadmin-rule-exclusions-plugin /tmp/phpmyadmin-rule-exclusions-plugin && \
git clone --recursive https://github.com/coreruleset/nextcloud-rule-exclusions-plugin /tmp/nextcloud-rule-exclusions-plugin && \
git clone --recursive https://github.com/coreruleset/wordpress-rule-exclusions-plugin /tmp/wordpress-rule-exclusions-plugin && \
git clone --recursive https://github.com/coreruleset/cpanel-rule-exclusions-plugin /tmp/cpanel-rule-exclusions-plugin && \
git clone --recursive https://github.com/coreruleset/body-decompress-plugin /tmp/body-decompress-plugin && \
git clone --recursive https://github.com/coreruleset/auto-decoding-plugin /tmp/auto-decoding-plugin && \
git clone --recursive https://github.com/coreruleset/google-oauth2-plugin /tmp/google-oauth2-plugin && \
mv /tmp/coreruleset/plugins /usr/local/nginx/conf/conf.d/include/coreruleset/plugins && \
mv /tmp/phpmyadmin-rule-exclusions-plugin/plugins/* /usr/local/nginx/conf/conf.d/include/coreruleset/plugins && \
mv /tmp/nextcloud-rule-exclusions-plugin/plugins/* /usr/local/nginx/conf/conf.d/include/coreruleset/plugins && \
mv /tmp/wordpress-rule-exclusions-plugin/plugins/* /usr/local/nginx/conf/conf.d/include/coreruleset/plugins && \
mv /tmp/cpanel-rule-exclusions-plugin/plugins/* /usr/local/nginx/conf/conf.d/include/coreruleset/plugins && \
mv /tmp/body-decompress-plugin/plugins/* /usr/local/nginx/conf/conf.d/include/coreruleset/plugins && \
mv /tmp/auto-decoding-plugin/plugins/* /usr/local/nginx/conf/conf.d/include/coreruleset/plugins && \
mv /tmp/google-oauth2-plugin/plugins/* /usr/local/nginx/conf/conf.d/include/coreruleset/plugins && \
rm -r /tmp/* && \
luarocks-5.1 install lua-resty-http && \
luarocks-5.1 install lua-cjson && \
apk del --no-cache luarocks5.1 wget lua5.1-dev build-base
apk del --no-cache luarocks5.1 wget lua5.1-dev build-base git
COPY rootfs /
COPY --from=backend /build/backend /app
COPY --from=frontend /build/frontend/dist /app/frontend
COPY --from=certbot /usr/local/certbot /usr/local/certbot

View File

@@ -45,8 +45,9 @@ so that the barrier for entry here is low.
# List of new features
- Supports HTTP/3 (QUIC) protocol
- Supports HTTP/3 (QUIC) protocol aviable
- Supports Crowdsec. Please read below for instructions on how to use it.
- Supports ModSecurity, with coreruleset as an option. You can configure ModSecurity/coreruleset by editing the files in the `/opt/npm/etc/modsecurity` folder.
- Darkmode button in the footer for comfortable viewing
- Fixes proxy to https origin when the origin only accepts TLSv1.3
- Only enables TLSv1.2 and TLSv1.3 protocols
@@ -178,9 +179,9 @@ services:
# - "CLEAN=false" # Clean folders, default true
# - "FULLCLEAN=true" # Clean unused config folders, default false
# - "PHP81=true" # Activate PHP81, default false
# - "PHP81_APKS=php81-curl php-81-curl" # Add php extensions, see aviable packages here: https://pkgs.alpinelinux.org/packages?branch=v3.17&repo=community&arch=x86_64&name=php81-*, default none
# - "PHP81_APKS=php81-curl php-81-curl" # Add php extensions, see available packages here: https://pkgs.alpinelinux.org/packages?branch=v3.17&repo=community&arch=x86_64&name=php81-*, default none
# - "PHP82=true" # Activate PHP82, default false
# - "PHP82_APKS=php82-curl php-82-curl" # Add php extensions, see aviable packages here: https://pkgs.alpinelinux.org/packages?branch=v3.17&repo=community&arch=x86_64&name=php82-*, default none
# - "PHP82_APKS=php82-curl php-82-curl" # Add php extensions, see available packages here: https://pkgs.alpinelinux.org/packages?branch=v3.17&repo=community&arch=x86_64&name=php82-*, default none
```
3. Bring up your stack by running (or deploy your portainer stack)

View File

@@ -87,7 +87,7 @@
"letsencrypt_agree": false,
"dns_challenge": false,
"nginx_online": false,
"nginx_err": "Command failed: /usr/sbin/nginx -t -g \"error_log off;\"\nnginx: [emerg] unknown directive \"sdfsdfsdf\" in /data/nginx/proxy_host/1.conf:37\nnginx: configuration file /etc/nginx/nginx.conf test failed\n"
"nginx_err": "Command failed: nginx -t -g \"error_log off;\"\nnginx: [emerg] unknown directive \"sdfsdfsdf\" in /data/nginx/proxy_host/1.conf:37\nnginx: configuration file /urs/local/nginx/conf/nginx.conf test failed\n"
},
"allow_websocket_upgrade": 0,
"http2_support": 0,

View File

@@ -10,7 +10,6 @@ const certificateModel = require('../models/certificate');
const dnsPlugins = require('../certbot-dns-plugins');
const internalAuditLog = require('./audit-log');
const internalNginx = require('./nginx');
const internalHost = require('./host');
const archiver = require('archiver');
const path = require('path');
const { isArray } = require('lodash');
@@ -126,112 +125,65 @@ const internalCertificate = {
})
.then((certificate) => {
if (certificate.provider === 'letsencrypt') {
// Request a new Cert using Certbot. Let the fun begin.
// 1. Find out any hosts that are using any of the hostnames in this cert
// 2. Disable them in nginx temporarily
// 3. Generate the Certbot config
// 4. Request cert
// 5. Remove Certbot config
// 6. Re-instate previously disabled hosts
// 1. Find out any hosts that are using any of the hostnames in this cert
return internalHost.getHostsWithDomains(certificate.domain_names)
.then((in_use_result) => {
// 2. Disable them in nginx temporarily
return internalCertificate.disableInUseHosts(in_use_result)
.then(() => {
return in_use_result;
});
})
.then((in_use_result) => {
// With DNS challenge no config is needed, so skip 3 and 5.
if (certificate.meta.dns_challenge) {
return internalNginx.reload().then(() => {
// 4. Request cert
return internalCertificate.requestLetsEncryptSslWithDnsChallenge(certificate);
// Request a new Cert using Certbot. Let the fun begin.
if (certificate.meta.dns_challenge) {
return internalCertificate.requestLetsEncryptSslWithDnsChallenge(certificate)
.then(() => {
return certificate;
})
.catch((err) => {
// In the event of failure, throw err back
throw err;
});
} else {
return internalCertificate.requestLetsEncryptSsl(certificate)
.then(() => {
return certificate;
})
.catch((err) => {
// In the event of failure, throw err back
throw err;
});
}
} else {
return certificate;
}
})
.then((certificate) => {
if (certificate.provider === 'letsencrypt') {
// At this point, the certbot cert should exist on disk.
// Lets get the expiry date from the file and update the row silently
return internalCertificate
.getCertificateInfoFromFile(
'/data/tls/certbot/live/npm-' + certificate.id + '/fullchain.pem'
)
.then((cert_info) => {
return certificateModel
.query()
.patchAndFetchById(certificate.id, {
expires_on: moment(cert_info.dates.to, 'X').format(
'YYYY-MM-DD HH:mm:ss'
),
})
.then(internalNginx.reload)
.then(() => {
// 6. Re-instate previously disabled hosts
return internalCertificate.enableInUseHosts(in_use_result);
})
.then(() => {
return certificate;
})
.catch((err) => {
// In the event of failure, revert things and throw err back
return internalCertificate.enableInUseHosts(in_use_result)
.then(internalNginx.reload)
.then(() => {
throw err;
});
.then((saved_row) => {
// Add cert data for audit log
saved_row.meta = _.assign({}, saved_row.meta, {
letsencrypt_certificate: cert_info,
});
} else {
// 3. Generate the Certbot config
return internalNginx.generateLetsEncryptRequestConfig(certificate)
.then(internalNginx.reload)
.then(async() => await new Promise((r) => setTimeout(r, 5000)))
.then(() => {
// 4. Request cert
return internalCertificate.requestLetsEncryptSsl(certificate);
})
.then(() => {
// 5. Remove Certbot config
return internalNginx.deleteLetsEncryptRequestConfig(certificate);
})
.then(internalNginx.reload)
.then(() => {
// 6. Re-instate previously disabled hosts
return internalCertificate.enableInUseHosts(in_use_result);
})
.then(() => {
return certificate;
})
.catch((err) => {
// In the event of failure, revert things and throw err back
return internalNginx.deleteLetsEncryptRequestConfig(certificate)
.then(() => {
return internalCertificate.enableInUseHosts(in_use_result);
})
.then(internalNginx.reload)
.then(() => {
throw err;
});
});
}
})
.then(() => {
// At this point, the certbot cert should exist on disk.
// Lets get the expiry date from the file and update the row silently
return internalCertificate.getCertificateInfoFromFile('/data/tls/certbot/live/npm-' + certificate.id + '/fullchain.pem')
.then((cert_info) => {
return certificateModel
.query()
.patchAndFetchById(certificate.id, {
expires_on: moment(cert_info.dates.to, 'X').format('YYYY-MM-DD HH:mm:ss')
})
.then((saved_row) => {
// Add cert data for audit log
saved_row.meta = _.assign({}, saved_row.meta, {
letsencrypt_certificate: cert_info
});
return saved_row;
});
return saved_row;
});
}).catch(async (error) => {
// Delete the certificate from the database if it was not created successfully
await certificateModel
.query()
.deleteById(certificate.id);
await certificateModel.query().deleteById(certificate.id);
throw error;
});
} else {
return certificate;
}
}).then((certificate) => {
})
.then((certificate) => {
data.meta = _.assign({}, data.meta || {}, certificate.meta);
@@ -248,6 +200,7 @@ const internalCertificate = {
});
},
/**
* @param {Access} access
* @param {Object} data

View File

@@ -115,7 +115,7 @@ const internalNginx = {
return internalNginx.test()
.then(() => {
logger.info('Reloading Nginx');
return utils.exec('nginx -s reload');
return utils.exec('kill $(cat /usr/local/nginx/logs/nginx.pid); nginx');
});
},

View File

@@ -13,6 +13,15 @@ server {
{% include "_brotli.conf" %}
{% include "_access.conf" %}
{% if block_exploits == 1 or block_exploits == true %}
modsecurity on;
{% if caching_enabled == 1 or caching_enabled == true -%}
modsecurity_rules_file /usr/local/nginx/conf/conf.d/include/modsecurity-crs.conf;
{% else %}
modsecurity_rules_file /usr/local/nginx/conf/conf.d/include/modsecurity.conf;
{% endif %}
{% endif %}
include conf.d/include/acme-challenge.conf;
include conf.d/include/block-exploits.conf;

View File

@@ -28,6 +28,6 @@ services:
# - "CLEAN=false" # Clean folders, default true
# - "FULLCLEAN=true" # Clean unused config folders, default false
# - "PHP81=true" # Activate PHP81, default false
# - "PHP81_APKS=php81-curl php-81-curl" # Add php extensions, see aviable packages here: https://pkgs.alpinelinux.org/packages?branch=v3.17&repo=community&arch=x86_64&name=php81-*, default none
# - "PHP81_APKS=php81-curl php-81-curl" # Add php extensions, see available packages here: https://pkgs.alpinelinux.org/packages?branch=v3.17&repo=community&arch=x86_64&name=php81-*, default none
# - "PHP82=true" # Activate PHP82, default false
# - "PHP82_APKS=php82-curl php-82-curl" # Add php extensions, see aviable packages here: https://pkgs.alpinelinux.org/packages?branch=v3.17&repo=community&arch=x86_64&name=php82-*, default none
# - "PHP82_APKS=php82-curl php-82-curl" # Add php extensions, see available packages here: https://pkgs.alpinelinux.org/packages?branch=v3.17&repo=community&arch=x86_64&name=php82-*, default none

View File

@@ -54,7 +54,7 @@
<input name="forward_port" type="number" class="form-control text-monospace" placeholder="80" value="<%- forward_port %>" required>
</div>
</div>
<div class="col-sm-12 col-md-12">
<div class="col-sm-6 col-md-6">
<div class="form-group">
<label class="custom-switch">
<input type="checkbox" class="custom-switch-input" name="allow_websocket_upgrade" value="1"<%- allow_websocket_upgrade ? ' checked' : '' %>>
@@ -63,7 +63,6 @@
</label>
</div>
</div>
<!--
<div class="col-sm-6 col-md-6">
<div class="form-group">
<label class="custom-switch">
@@ -73,7 +72,7 @@
</label>
</div>
</div>
<div class="col-sm-6 col-md-6">
<div class="col-sm-12 col-md-12">
<div class="form-group">
<label class="custom-switch">
<input type="checkbox" class="custom-switch-input" name="caching_enabled" value="1"<%- caching_enabled ? ' checked' : '' %>>
@@ -82,7 +81,6 @@
</label>
</div>
</div>
-->
<div class="col-sm-12 col-md-12">
<div class="form-group">

View File

@@ -75,8 +75,8 @@
"http2-support": "Enable Brotli",
"domain-names": "Domain Names",
"cert-provider": "Certificate Provider",
"block-exploits": "Block Common Exploits",
"caching-enabled": "Cache Assets",
"block-exploits": "Enable ModSecurity",
"caching-enabled": "Enable CoreRuleSet (Requires ModSecurity)",
"ssl-certificate": "TLS Certificate",
"none": "None",
"new-cert": "Request a new TLS Certificate",

View File

@@ -31,7 +31,7 @@
"nodemon": "2.0.22",
"numeral": "2.0.6",
"sass-loader": "10.4.1",
"style-loader": "3.3.2",
"style-loader": "3.3.3",
"tabler-ui": "git+https://github.com/tabler/tabler.git#00f78ad823311bc3ad974ac3e5b0126198f0a813",
"underscore": "1.13.6",
"webpack": "4.46.0",

View File

@@ -188,6 +188,7 @@ mkdir -vp /data/tls/certbot/renewal \
/data/etc/html \
/data/etc/access \
/data/etc/crowdsec \
/data/etc/modsecurity \
/data/nginx/redirection_host \
/data/nginx/proxy_host \
/data/nginx/dead_host \
@@ -318,6 +319,7 @@ find /data/nginx -type f -name '*.conf' -exec sed -i "/ssl_stapling/d" {} \;
find /data/nginx -type f -name '*.conf' -exec sed -i "/ssl_stapling_verify/d" {} \;
touch /data/etc/html/index.html \
/data/etc/modsecurity/modsecurity.conf \
/data/nginx/default.conf \
/data/nginx/ip_ranges.conf \
/data/nginx/custom/root.conf \
@@ -332,6 +334,9 @@ touch /data/etc/html/index.html \
/data/nginx/custom/server_stream_tcp.conf \
/data/nginx/custom/server_stream_udp.conf
cp -vn /usr/local/nginx/conf/conf.d/include/coreruleset/crs-setup.conf.example /data/etc/modsecurity/crs-setup.conf
cp -v /usr/local/nginx/conf/conf.d/include/coreruleset/crs-setup.conf.example /data/etc/modsecurity/crs-setup.conf.example
if [ -z "$NPM_CERT_ID" ]; then
export NPM_CERT=/data/tls/dummycert.pem
export NPM_KEY=/data/tls/dummykey.pem

View File

@@ -131,6 +131,34 @@ if ($http_user_agent ~ "GrabNet") {
set $block_user_agents 1;
}
if ($http_user_agent ~ "Amazonbot") {
set $block_user_agents 1;
}
if ($http_user_agent ~ "Applebot") {
set $block_user_agents 1;
}
if ($http_user_agent ~ "Bingbot") {
set $block_user_agents 1;
}
if ($http_user_agent ~ "Facebookbot") {
set $block_user_agents 1;
}
if ($http_user_agent ~ "Googlebot") {
set $block_user_agents 1;
}
if ($http_user_agent ~ "LinkedInBot") {
set $block_user_agents 1;
}
if ($http_user_agent ~ "Twitterbot") {
set $block_user_agents 1;
}
if ($block_user_agents = 1) {
return 403;
}

View File

@@ -12,6 +12,9 @@ server {
include conf.d/include/force-tls.conf;
include conf.d/include/tls-ciphers.conf;
include conf.d/include/block-exploits.conf;
modsecurity on;
modsecurity_rules_file /usr/local/nginx/conf/conf.d/include/modsecurity.conf;
#ssl_certificate ;
#ssl_certificate_key ;