Created
August 10, 2025 11:30
-
-
Save horacioibrahim/bb1fdf3db3fa8dc1891f0ef9af9264bb to your computer and use it in GitHub Desktop.
harden-nginx-drop-ip.sh
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # curl -fsSL https://example.com/harden-nginx-drop-ip.sh -o harden-nginx-drop-ip.sh | |
| # sudo bash harden-nginx-drop-ip.sh | |
| set -euo pipefail | |
| SITES_AVAIL="/etc/nginx/sites-available" | |
| SITES_ENABL="/etc/nginx/sites-enabled" | |
| SELF_DIR="/etc/nginx/selfsigned" | |
| WEBROOT="/var/www/certbot" | |
| HTTP_DROP="${SITES_AVAIL}/00-drop-ip-http" | |
| HTTPS_DROP="${SITES_AVAIL}/00-drop-ip-https" | |
| SELF_KEY="${SELF_DIR}/default.key" | |
| SELF_CRT="${SELF_DIR}/default.crt" | |
| log(){ printf "\033[1;34m›\033[0m %s\n" "$*"; } | |
| ok(){ printf "\033[1;32m✔\033[0m %s\n" "$*"; } | |
| err(){ printf "\033[1;31m✖ %s\033[0m\n" "$*" >&2; } | |
| require_root(){ | |
| if [[ ${EUID:-$(id -u)} -ne 0 ]]; then | |
| err "Rode como root: sudo $0" | |
| exit 1 | |
| fi | |
| } | |
| ensure_pkgs(){ | |
| # nginx e openssl costumam já estar, mas garantimos | |
| apt-get update -y >/dev/null | |
| apt-get install -y nginx openssl >/dev/null | |
| } | |
| disable_default_site(){ | |
| if [[ -e "${SITES_ENABL}/default" ]]; then | |
| log "Removendo sites-enabled/default…" | |
| rm -f "${SITES_ENABL}/default" | |
| ok "default desabilitado." | |
| else | |
| log "default já não está habilitado — ok." | |
| fi | |
| } | |
| write_http_drop(){ | |
| log "Escrevendo ${HTTP_DROP}…" | |
| cat > "${HTTP_DROP}" <<'EOF' | |
| server { | |
| listen 80 default_server; | |
| server_name _; | |
| # ACME webroot (Let's Encrypt HTTP-01) | |
| location ^~ /.well-known/acme-challenge/ { | |
| root /var/www/certbot; | |
| default_type "text/plain"; | |
| } | |
| # Demais requisições para IP cru: derruba | |
| location / { return 444; } | |
| } | |
| EOF | |
| ln -sf "${HTTP_DROP}" "${SITES_ENABL}/00-drop-ip-http" | |
| ok "HTTP drop criado/atualizado." | |
| } | |
| gen_selfsigned(){ | |
| mkdir -p "${SELF_DIR}" | |
| if [[ -s "${SELF_KEY}" && -s "${SELF_CRT}" ]]; then | |
| log "Self-signed já existe — pulando geração." | |
| return | |
| fi | |
| log "Gerando certificado self-signed (RSA 2048, 825 dias)…" | |
| openssl req -x509 -nodes -days 825 -newkey rsa:2048 \ | |
| -keyout "${SELF_KEY}" -out "${SELF_CRT}" \ | |
| -subj "/CN=localhost" >/dev/null 2>&1 | |
| chmod 600 "${SELF_KEY}" | |
| ok "Self-signed gerado em ${SELF_DIR}." | |
| } | |
| write_https_drop(){ | |
| log "Escrevendo ${HTTPS_DROP}…" | |
| cat > "${HTTPS_DROP}" <<EOF | |
| server { | |
| listen 443 ssl default_server; | |
| http2 on; | |
| server_name _; | |
| ssl_certificate ${SELF_CRT}; | |
| ssl_certificate_key ${SELF_KEY}; | |
| # Derruba imediatamente conexões no IP cru | |
| return 444; | |
| } | |
| EOF | |
| ln -sf "${HTTPS_DROP}" "${SITES_ENABL}/00-drop-ip-https" | |
| ok "HTTPS drop criado/atualizado." | |
| } | |
| harden_server_tokens(){ | |
| # Desliga o banner "nginx" nas respostas | |
| if grep -q '^\s*server_tokens' /etc/nginx/nginx.conf; then | |
| sed -i 's/^\s*server_tokens\s\+.*;/server_tokens off;/' /etc/nginx/nginx.conf | |
| else | |
| # insere no bloco http { } se não existir | |
| awk ' | |
| BEGIN{inserted=0} | |
| /http\s*{/ && !inserted { print; print " server_tokens off;"; inserted=1; next } | |
| { print } | |
| ' /etc/nginx/nginx.conf > /etc/nginx/nginx.conf.tmp && mv /etc/nginx/nginx.conf.tmp /etc/nginx/nginx.conf | |
| fi | |
| ok "server_tokens off configurado." | |
| } | |
| reload_nginx(){ | |
| log "Validando configuração…" | |
| nginx -t | |
| log "Recarregando nginx…" | |
| systemctl reload nginx | |
| ok "Nginx recarregado." | |
| } | |
| ensure_webroot(){ | |
| mkdir -p "${WEBROOT}/.well-known/acme-challenge" | |
| ok "Webroot ACME pronto em ${WEBROOT}." | |
| } | |
| ######## main | |
| require_root | |
| ensure_pkgs | |
| disable_default_site | |
| ensure_webroot | |
| write_http_drop | |
| gen_selfsigned | |
| write_https_drop | |
| harden_server_tokens | |
| reload_nginx | |
| echo | |
| ok "Pronto! IP em 80/443 agora não serve conteúdo (80 só ACME; 443 drop com self-signed)." |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment