├── .travis.yml ├── LICENSE ├── README.md ├── binaries ├── config-shield └── shield ├── config-shield.go ├── example ├── dynamic-vhost.conf ├── le.conf ├── nshield.conf └── ssl.conf ├── install.sh ├── nshield-scheme.png ├── old └── nshield-main.py └── shield.go /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | sudo: false 3 | go: 4 | - 1.9.x 5 | 6 | git: 7 | depth: 3 8 | 9 | before_install: 10 | - sudo apt-get -qq update 11 | - go get github.com/BurntSushi/toml 12 | 13 | 14 | install: true 15 | 16 | 17 | go_import_path: github.com/fnzv/net-Shield 18 | 19 | script: 20 | - go build shield.go 21 | - go build config-shield.go 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Sami 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## net-Shield 2 | [![Build Status](https://travis-ci.org/fnzv/net-Shield.svg?branch=master)](https://travis-ci.org/fnzv/net-Shield)
3 | An Easy and Simple Anti-DDoS solution for VPS,Dedicated Servers and IoT devices based on iptables/ipsets 4 | 5 | ![](nshield-scheme.png?raw=true) 6 | 7 | ----------------- 8 | 9 | ## Requirements 10 | 11 | - Linux System with golang, iptables/ipsets 12 | - Nginx 13 | 14 | 15 | ### Quickstart 16 | 17 | Run the bash script (install.sh) to install all the required dependencies. 18 | 19 | ```bash install.sh``` 20 | 21 |
22 | You will be prompted to insert a domain and the real IP address associated to it so net-Shield will configure for you the first proxydomain (you can see the changes on /etc/nshield/nshield.conf). 23 |
24 | 25 | 26 | ### Proxy Domains 27 | 28 | To configure proxydomains you need to enable the proxy option on /etc/nshield/nshield.conf (proxy = 1) and be sure that the proxydomain list (on the same conf file) is correct:
29 |
30 | ``` 31 | proxydomains = [ 32 | "sami.pw 8.8.8.8", 33 | "example.org 1.2.3.4" 34 | ] 35 | ``` 36 |
37 | 38 | ### Usage 39 | 40 | After you completed the install with the quickstart script you can call the "config-nshield" commad that will read the nshield.conf and re-configure shield rules based on the new configuration. 41 | 42 | Example: 43 | I want to enable SSL on sami.pw that i just configured as above: 44 | 1) Edit /etc/nshield/nshield.conf and set autossl = 1 45 | 2) On your terminal run: ```# config-shield ``` 46 | 3) You can now see the changes on the Nginx configuration 47 | 48 | The domain must point to the net-Shield instance otherwise will fail let's encrypt verification. 49 | 50 | Logs are diplayed on: /var/log/nshield.log 51 | 52 | ## How it works 53 | Basically this script is set by default to run every 30 minutes and execute these operations: 54 | 55 | - Get latest Bot,Spammers,Bad IP/Net reputation lists and blocks if those Bad guys are attacking your server (Thank you FireHol http://iplists.firehol.org/ ) 56 | - Enable basic Anti-DDoS methods to deny unwanted/malicious traffic 57 | - Rate limits when under attack 58 | - Allows HTTP(S) Proxying to protect your site 59 | 60 | ## Demo 61 | [![asciicast](https://asciinema.org/a/zozehdooPDbvem9tCDLI321Hp.png)](https://asciinema.org/a/zozehdooPDbvem9tCDLI321Hp) 62 | 63 | Tested on Ubuntu 16.04 and 14.04 LTS 64 | 65 | ## Contributors 66 | 67 | Feel free to open issues or send me an email 68 | 69 | ## Binaries 70 | 71 | In case you cannot compile it your self and/or run the install.sh you can find the binaries on: 72 | https://github.com/fnzv/net-Shield/tree/master/binaries 73 | 74 | 75 | ## License 76 | 77 | Code distributed under MIT licence. 78 | -------------------------------------------------------------------------------- /binaries/config-shield: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnzv/net-Shield/4198a6d62d7982f6ba1bc2f299506aca111a27ef/binaries/config-shield -------------------------------------------------------------------------------- /binaries/shield: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnzv/net-Shield/4198a6d62d7982f6ba1bc2f299506aca111a27ef/binaries/shield -------------------------------------------------------------------------------- /config-shield.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | "log" 7 | "os/exec" 8 | 9 | "github.com/BurntSushi/toml" 10 | 11 | ) 12 | 13 | var ( 14 | 15 | dryrun int 16 | proxy int 17 | underattack int 18 | basicddos int 19 | autossl int 20 | whitelist []string 21 | proxydomains []string 22 | whitelist_text string 23 | 24 | ) 25 | 26 | // Info from config file 27 | type Config struct { 28 | DryRun int 29 | BasicDdos int 30 | UnderAttack int 31 | Proxy int 32 | Autossl int 33 | Whitelist []string 34 | ProxyDomains []string 35 | } 36 | // Reads info from config file 37 | func ReadConfig() Config { 38 | var configfile = "/etc/nshield/nshield.conf" 39 | _, err := os.Stat(configfile) 40 | if err != nil { 41 | log.Fatal("Config file is missing: ", configfile) 42 | } 43 | 44 | var config Config 45 | if _, err := toml.DecodeFile(configfile, &config); err != nil { 46 | log.Fatal(err) 47 | } 48 | //log.Print(config.Index) 49 | return config 50 | } 51 | 52 | func exec_shell(command string) string { 53 | out, err := exec.Command("/bin/bash","-c",command).Output() 54 | if err != nil { 55 | log.Fatal(err) 56 | } 57 | return string(out) 58 | } 59 | 60 | 61 | 62 | func main() { 63 | 64 | var config = ReadConfig() 65 | basicddos = config.BasicDdos 66 | underattack = config.UnderAttack 67 | dryrun = config.DryRun 68 | proxy = config.Proxy 69 | autossl = config.Autossl 70 | whitelist = config.Whitelist 71 | proxydomains = config.ProxyDomains 72 | 73 | 74 | f, err := os.OpenFile("/var/log/nshield.log", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) 75 | if err != nil { 76 | log.Fatal(err) 77 | } 78 | defer f.Close() 79 | log.SetOutput(f) 80 | 81 | 82 | log.Println("Loading nshield with these settings:") 83 | log.Println("BasicDdos: ",basicddos) 84 | log.Println("UnderAttack: ",underattack) 85 | log.Println("DryRun: ",dryrun) 86 | log.Println("Proxy: ",proxy) 87 | 88 | 89 | exec_shell("wget -O /etc/nshield/ipsets/firehol_level1.netset https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/firehol_level1.netset >/dev/null 2>&1") 90 | exec_shell("wget -O /etc/nshield/ipsets/botscout_1d.ipset https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/botscout_1d.ipset >/dev/null 2>&1") 91 | exec_shell("wget -O /etc/nshield/ipsets/bi_any_2_30d.ipset https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/bi_any_2_30d.ipset >/dev/null 2>&1") 92 | exec_shell("wget -O /etc/nshield/snort_ipfilter.ipset https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/snort_ipfilter.ipset >/dev/null 2>&1") 93 | 94 | exec_shell("iptables -F") 95 | log.Println("Cleaning iptables..") 96 | // check if already exists 97 | check_ipset := exec_shell("ipset list | grep -e rate -e block | awk '{print $2}'") 98 | if strings.Contains(check_ipset,"rate") { 99 | log.Println("ipset already existing") 100 | } else { 101 | log.Println("creating ipset") 102 | exec_shell("ipset create ratelimit hash:ip hashsize 4096") 103 | } 104 | 105 | if strings.Contains(check_ipset,"block") { 106 | log.Println("ipset already existing") 107 | } else { 108 | log.Println("creating ipset") 109 | exec_shell("ipset create block hash:ip timeout 0") 110 | } 111 | exec_shell("iptables -A INPUT -m set --match-set ratelimit src -m hashlimit --hashlimit 25/sec --hashlimit-name ratelimithash -j DROP") 112 | exec_shell("iptables -A INPUT -m set --match-set block src -j DROP") 113 | 114 | 115 | 116 | 117 | i1 := 0 118 | for range whitelist { 119 | log.Println("Adding to whitelist: "+whitelist[i1]) 120 | whitelist_text = whitelist_text + whitelist[i1] 121 | //fmt.Printf("%s", out) 122 | if dryrun == 0 { 123 | exec_shell("iptables -I INPUT -s "+whitelist[i1]+" -j ACCEPT -m comment --comment 'WHITELISTED IP - NSHIELD'") 124 | } 125 | i1++ 126 | } 127 | 128 | 129 | 130 | log.Println("Setting ipt logs..") 131 | exec_shell(`iptables -I INPUT -m limit --limit 40/min -j LOG --log-prefix "nShield: " --log-level 7`) 132 | 133 | if (basicddos == 1 && dryrun == 0) { 134 | log.Println("Setting up Basic DDos Protection") 135 | exec_shell("iptables -A INPUT -p tcp ! --syn -m state --state NEW -j DROP") 136 | exec_shell("iptables -A INPUT -p tcp --tcp-flags ALL ALL -j DROP") 137 | exec_shell("iptables -A INPUT -p icmp -m icmp --icmp-type timestamp-request -j DROP && iptables -A INPUT -p icmp -m limit --limit 1/second -j ACCEPT") 138 | exec_shell("/sbin/sysctl -w net/netfilter/nf_conntrack_tcp_loose=0") 139 | exec_shell("echo 1000000 > /sys/module/nf_conntrack/parameters/hashsize && /sbin/sysctl -w net/netfilter/nf_conntrack_max=2000000 && /sbin/sysctl -w net.ipv4.tcp_syn_retries=2 && /sbin/sysctl -w net.ipv4.tcp_rfc1337=1 && /sbin/sysctl -w net.ipv4.tcp_synack_retries=1") 140 | 141 | } 142 | 143 | 144 | if (underattack == 1 && dryrun ==0) { 145 | exec_shell("iptables -A INPUT -p tcp --syn -m hashlimit --hashlimit 15/s --hashlimit-burst 30 --hashlimit-mode srcip --hashlimit-name synattack -j ACCEPT && iptables -A INPUT -p tcp --syn -j DROP") 146 | } 147 | 148 | 149 | if (proxy == 1 && dryrun ==0) { 150 | log.Println("Setting up nShield proxy for domains found in configuration") 151 | i:= 0 152 | for range proxydomains { 153 | log.Println(proxydomains[i]) 154 | s := strings.Split(proxydomains[i], " ") 155 | domain, ip := s[0], s[1] 156 | log.Println("Generating Nginx conf for "+domain+" on IP "+ip) 157 | //fmt.Printf("%s", out) 158 | if strings.Contains(exec_shell("cat /etc/nginx/sites-enabled/dynamic-vhost.conf"),domain) == false { 159 | if dryrun == 0 { 160 | exec_shell(`echo 'server { 161 | listen 80; 162 | root /var/www/html; 163 | index index.html index.htm index.nginx-debian.html; 164 | server_name `+domain+`; 165 | location / { 166 | proxy_pass http://`+ip+`; 167 | proxy_set_header Host $host; 168 | proxy_set_header X-Real-IP $remote_addr; 169 | } 170 | } 171 | ' >> /etc/nginx/sites-enabled/dynamic-vhost.conf`) 172 | exec_shell("service nginx reload") 173 | } 174 | 175 | } else { log.Println("Domain already present") } 176 | i++ 177 | } 178 | } 179 | 180 | if (autossl ==1 && dryrun == 0) { 181 | 182 | i2 :=0 183 | for range proxydomains { 184 | log.Println(proxydomains[i2]) 185 | s := strings.Split(proxydomains[i2], " ") 186 | domain, ip := s[0], s[1] 187 | if strings.Contains(exec_shell("cat /etc/nginx/sites-enabled/dynamic-ssl-vhost.conf"),domain) == false { 188 | log.Println("i will generate SSL cert with certbot for ",domain) 189 | //call certbot 190 | exec_shell("certbot certonly --webroot --agree-tos --no-eff-email -n -w /var/www/letsencrypt -d "+domain) 191 | 192 | exec_shell(`echo 'server { 193 | listen 443 ssl; 194 | root /var/www/html; 195 | index index.html index.htm index.nginx-debian.html; 196 | ssl_certificate /etc/letsencrypt/live/`+domain+`/cert.pem; 197 | ssl_certificate_key /etc/letsencrypt/live/`+domain+`/privkey.pem; 198 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 199 | ssl_ciphers HIGH:!aNULL:!MD5; 200 | server_name `+domain+`; 201 | location / { 202 | proxy_pass http://`+ip+`; 203 | proxy_set_header Host $host; 204 | proxy_set_header X-Real-IP $remote_addr; 205 | } 206 | } 207 | ' >> /etc/nginx/sites-enabled/dynamic-ssl-vhost.conf && service nginx reload`) 208 | 209 | } 210 | 211 | i2++ 212 | } 213 | 214 | } 215 | 216 | log.Println("Top current connections: \n ",exec_shell(`netstat -atun | grep -v "Addr" | grep -v "and" | awk '{print $5}' | cut -d: -f1 | sed -e '/^$/d' |sort | uniq -c | sort -n`)) 217 | 218 | } 219 | -------------------------------------------------------------------------------- /example/dynamic-vhost.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80 default_server; 3 | 4 | root /var/www/html; 5 | index index.html; 6 | 7 | server_name _; 8 | 9 | location / { 10 | proxy_pass http://domain; 11 | proxy_set_header Host $host; 12 | proxy_set_header X-Real-IP $remote_addr; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /example/le.conf: -------------------------------------------------------------------------------- 1 | location ^~ /.well-known/acme-challenge/ { 2 | default_type "text/plain"; 3 | root /var/www/letsencrypt; 4 | } 5 | -------------------------------------------------------------------------------- /example/nshield.conf: -------------------------------------------------------------------------------- 1 | dryrun= 0 2 | basicddos = 1 3 | underattack = 1 4 | proxy = 1 5 | autossl = 0 6 | whitelist = [ 7 | "192.168.0.0/24", 8 | "127.0.0.1/8" 9 | ] 10 | proxydomains = [ 11 | "example.org 1.2.3.4" 12 | ] 13 | -------------------------------------------------------------------------------- /example/ssl.conf: -------------------------------------------------------------------------------- 1 | ssl_session_timeout 1d; 2 | ssl_session_cache shared:SSL:50m; 3 | ssl_session_tickets off; 4 | 5 | ssl_protocols TLSv1.2; 6 | ssl_ciphers EECDH+AESGCM:EECDH+AES; 7 | ssl_ecdh_curve secp384r1; 8 | ssl_prefer_server_ciphers on; 9 | 10 | ssl_stapling on; 11 | ssl_stapling_verify on; 12 | 13 | add_header Strict-Transport-Security "max-age=15768000; includeSubdomains; preload"; 14 | add_header X-Frame-Options DENY; 15 | add_header X-Content-Type-Options nosniff; 16 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Downloading & Installing Golang.. \n" 4 | 5 | # Golang quick install 6 | 7 | apt-get update 8 | wget https://storage.googleapis.com/golang/go1.9.2.linux-amd64.tar.gz 9 | sudo tar -xvf go1.9.2.linux-amd64.tar.gz 10 | sudo mv go /usr/local 11 | echo "export GOROOT=/usr/local/go" >> /root/.bashrc 12 | echo "export GOPATH=$HOME/Projects" >> /root/.bashrc 13 | echo "export PATH=$GOPATH/bin:$GOROOT/bin:$PATH" >> /root/.bashrc 14 | echo "Checking Golang version\n" 15 | ln -s /usr/local/go/bin/go /usr/bin/go 16 | go get github.com/BurntSushi/toml 17 | rm -rf go/ 18 | 19 | rm -rf go1.9.2.linux-amd64.tar.gz 20 | mkdir -p /etc/nshield/ipsets/ 21 | 22 | apt-get install -y jq software-properties-common ipset 23 | sudo add-apt-repository -y ppa:certbot/certbot 24 | sudo apt-get update 25 | sudo apt-get install python-certbot-nginx -y 26 | touch /etc/nginx/sites-enabled/dynamic-ssl-vhost.conf 27 | touch /etc/nginx/sites-enabled/dynamic-vhost.conf 28 | 29 | echo 'Setting ipt new log file..' 30 | # Configures ipt connection logging to separate file 31 | echo '# Log kernel generated iptables log messages to file 32 | :msg,contains,"nShield" /var/log/iptables.log 33 | & ~' >> /etc/rsyslog.d/10-ipt.conf && service rsyslog restart 34 | 35 | echo "/var/log/iptables.log { 36 | maxsize 100M 37 | hourly 38 | missingok 39 | rotate 4 40 | compress 41 | notifempty 42 | nocreate 43 | }" > /etc/logrotate.d/nshield 44 | 45 | 46 | echo "Installing Nginx for nShield proxy..\n" 47 | apt install -y nginx 48 | 49 | 50 | 51 | echo "Copying example configuration... \n" 52 | 53 | wget -O /etc/nshield/nshield.conf https://raw.githubusercontent.com/fnzv/nShield/master/example/nshield.conf 54 | wget -O /etc/nginx/snippet/ssl.conf https://raw.githubusercontent.com/fnzv/nShield/master/example/ssl.conf 55 | wget -O /etc/nginx/snippet/le.conf https://raw.githubusercontent.com/fnzv/nShield/master/example/le.conf 56 | 57 | read -p "Enter a domain to protect: " domain 58 | 59 | read -p "Enter the real IP address of the website: " ip 60 | 61 | sed -i s"/example.org/$domain/"g /etc/nshield/nshield.conf 62 | 63 | sed -i s"/1.2.3.4/$ip/"g /etc/nshield/nshield.conf 64 | 65 | 66 | echo "Compiling source code.." 67 | 68 | go build shield.go 69 | 70 | go build config-shield.go 71 | 72 | cp config-shield /bin/config-shield 73 | cp shield /bin/shield 74 | 75 | echo "30 * * * * /bin/shield" >> /etc/crontab 76 | -------------------------------------------------------------------------------- /nshield-scheme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnzv/net-Shield/4198a6d62d7982f6ba1bc2f299506aca111a27ef/nshield-scheme.png -------------------------------------------------------------------------------- /old/nshield-main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Author: Sami Yessou - samiii@protonmail.com 3 | # nShield - An Easy and Simple Anti-DDoS solution for VPS,Dedicated Servers and IoT devices 4 | # (To be)*Features: Blocks known attackers from the Web and allows users to CDN/Proxying their site with an offsite VPS/Servers 5 | # Still in beta 6 | 7 | 8 | import os,argparse,ConfigParser,sys 9 | 10 | config = ConfigParser.ConfigParser() 11 | 12 | config.read("/etc/nshield/nshield.conf") 13 | 14 | 15 | # Log check 16 | os.popen('find /var/log/syslog -type f -size +500k -delete >/dev/null 2>&1') 17 | 18 | #read conf and save variables 19 | dryrun = int(config.get("conf","dryrun")) 20 | basic_ddos = int(config.get("conf","basic_ddos")) 21 | under_attack = int(config.get("conf","under_attack")) 22 | nshield_proxy = int(config.get("conf","nshield_proxy")) 23 | 24 | #print "Configuration loaded: \n" 25 | #print "dryrun: "+dryrun 26 | #print "\nbasic_ddos: "+basic_ddos 27 | #print "\nunder_attack: "+under_attack 28 | #print "\nnshield_proxy: "+nshield_proxy 29 | 30 | 31 | parser = argparse.ArgumentParser() 32 | 33 | parser.add_argument('-ssl', action='store_true', default=False,dest='autossl', help='Enable SSL on proxy domains') 34 | 35 | parser.add_argument('-dry', action='store_true', default=False,dest='standalone', help='Standalone mode') 36 | 37 | results = parser.parse_args() 38 | 39 | 40 | 41 | if results.standalone: 42 | dryrun = 1 43 | print "Running nShield in standalone mode.. Dryrun is now enabled for safety\n" 44 | autossl = results.autossl 45 | 46 | 47 | 48 | 49 | 50 | #Update firehol ip/netsets 51 | 52 | os.popen("wget -O firehol_level1.netset https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/firehol_level1.netset >/dev/null 2>&1") 53 | os.popen("wget -O botscout_1d.ipset https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/botscout_1d.ipset >/dev/null 2>&1") 54 | os.popen("wget -O bi_any_2_30d.ipset https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/bi_any_2_30d.ipset >/dev/null 2>&1") 55 | os.popen("wget -O snort_ipfilter.ipset https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/snort_ipfilter.ipset >/dev/null 2>&1") 56 | 57 | #Load all sets from cwd 58 | blocklist_ipset=os.popen("cat *.ipset").read() 59 | blocklist_netset=os.popen("cat *.netset").read() 60 | whitelist=os.popen("cat /etc/nshield/whitelist").read() 61 | 62 | # Get top 10 Nginx reqs 63 | nginx_iplist=os.popen("cat /var/log/nginx/access.log | awk ' { print $1}' | sort -n | uniq -c | sort -rn | head").read() 64 | 65 | 66 | splitted_nginx_iplist = nginx_iplist.split() 67 | 68 | 69 | # For every IP check ASN & Reputation 70 | 71 | print "Top 10 NGINX Requests are coming from these IPs : \n"+nginx_iplist 72 | 73 | 74 | print "Top 10 ASN by NGINX Requests: \n" 75 | for ip in splitted_nginx_iplist: 76 | if "." in ip: 77 | print ip+" - MNT BY: "+os.popen("curl -s ipinfo.io/"+ip+"/org").read() 78 | if dryrun is 1: 79 | os.popen("iptables -F") 80 | os.popen('iptables -I INPUT -j LOG --log-prefix "nShield: " --log-level 7') 81 | ipt_iplist=os.popen("cat /var/log/nshield.log | awk '{ print $12 }' | sed s'/SRC=//' | sort -n | uniq -c | grep -v DST").read() 82 | top_ipt_iplist=os.popen("cat /var/log/nshield.log | awk '{ print $12 }' | sed s'/SRC=//' | sort -n | uniq -c | sort -rn | grep -v DST | head").read() 83 | splitted_ipt_iplist=ipt_iplist.split() 84 | splitted_top_ipt_iplist=top_ipt_iplist.split() 85 | 86 | print "Top 10 TCP Requests are coming from these IPs : \n"+top_ipt_iplist 87 | 88 | print "Top 10 ASN of ipt logged Requests: \n" 89 | for ip in splitted_top_ipt_iplist: 90 | if "." in ip: 91 | print ip+" - MNT BY: "+os.popen("curl -s ipinfo.io/"+ip+"/org").read() 92 | 93 | 94 | 95 | for ip in splitted_ipt_iplist: 96 | if "." in ip: 97 | if ip in blocklist_ipset and conns >= 10: 98 | print "Blocking "+ip+" because found in ipsets and more than 10 reqs" 99 | iptblock="iptables -I INPUT -s "+ip+" -m comment --comment nShield-Blocked-from-ipset+10reqs -j DROP" 100 | if dryrun is 1: 101 | print "Dry Run.." 102 | else: 103 | os.popen(iptblock) 104 | subnet=ip.split('.') 105 | netset=subnet[0]+"."+subnet[1]+"."+subnet[2] 106 | if netset in blocklist_netset: 107 | print "Blocking "+ip+" because found in netsets" 108 | iptblock="iptables -I INPUT -s "+netset+".0/24 -m comment --comment nShield-Blocked-from-netsets -j DROP" 109 | if dryrun is 1: 110 | print "Dry Run.." 111 | else: 112 | os.popen(iptblock) 113 | else: 114 | conns=ip 115 | 116 | 117 | 118 | 119 | if dryrun is not 1: 120 | print "Setting up whitelist .." 121 | os.popen("iptables -I INPUT -i lo -j ACCEPT && iptables -I INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT") 122 | #check if its real ip before 123 | for ip in whitelist.split(): 124 | print ip 125 | os.popen("iptables -I INPUT -s "+ip+" -j ACCEPT -m comment --comment nShield-whitelist") 126 | 127 | 128 | 129 | if basic_ddos is 1 and dryrun is 0: 130 | print "Setting up Basic DDoS Protection" 131 | 132 | # Block SYN FLOOD 133 | os.popen("iptables -A INPUT -p tcp ! --syn -m state --state NEW -j DROP") 134 | # Block XMAS Scan 135 | os.popen("iptables -A INPUT -p tcp --tcp-flags ALL ALL -j DROP") 136 | # Smurf attack protection 137 | os.popen("iptables -A INPUT -p icmp -m icmp --icmp-type timestamp-request -j DROP && iptables -A INPUT -p icmp -m limit --limit 1/second -j ACCEPT") 138 | 139 | os.popen("/sbin/sysctl -w net/netfilter/nf_conntrack_tcp_loose=0") 140 | 141 | os .popen("echo 1000000 > /sys/module/nf_conntrack/parameters/hashsize && /sbin/sysctl -w net/netfilter/nf_conntrack_max=2000000 && /sbin/sysctl -w net.ipv4.tcp_syn_retries=2 && /sbin/sysctl -w net.ipv4.tcp_rfc1337=1 && /sbin/sysctl -w net.ipv4.tcp_synack_retries=1") 142 | 143 | print "\nBlocking XMAS scan, Smurf, ICMP attacks & SYN flood" 144 | 145 | 146 | 147 | if under_attack is 1 and dryrun is 0: 148 | # burst connections and add rate limits 149 | os.popen('iptables -A INPUT -p tcp --syn -m hashlimit --hashlimit 15/s --hashlimit-burst 30 --hashlimit-mode srcip --hashlimit-name synattack -j ACCEPT && iptables -A INPUT -p tcp --syn -j DROP') 150 | 151 | 152 | 153 | if nshield_proxy is 1 and dryrun is 0: 154 | print "Setting up nShield proxy for domains found in /etc/nshield/proxydomains\n" 155 | # Generates nginx proxy_pass from /etc/nshield/proxydomains and checks if already present in nginx conf 156 | with open("/etc/nshield/proxydomains") as f: 157 | for line in f: 158 | print "LINE" 159 | line = line.split(' ') 160 | domain = str(line[0]) 161 | ip = line[1] 162 | if domain not in os.popen("cat /etc/nginx/sites-enabled/dynamic-vhost.conf").read(): 163 | print "I Will generate proxy configuration for site "+domain+" on IP: "+ip 164 | os.popen("""echo 'server { 165 | listen 80; 166 | 167 | root /var/www/html; 168 | index index.html index.htm index.nginx-debian.html; 169 | 170 | server_name """+domain+"""; 171 | 172 | location / { 173 | proxy_pass http://"""+ip+"""; 174 | proxy_set_header Host $host; 175 | proxy_set_header X-Real-IP $remote_addr; 176 | } 177 | } 178 | ' >> /etc/nginx/sites-enabled/dynamic-vhost.conf""") 179 | os.popen('service nginx restart') 180 | else: 181 | print "Domain already configured" 182 | 183 | print "Now you can test that your site is reachable via nShield proxy by changing the domain DNS or via your PC hosts file or directly DNS A record" 184 | 185 | 186 | # Is triggered only if run from commandline and not cron 187 | if autossl and dryrun is 0: 188 | with open("/etc/nshield/proxydomains") as f: 189 | content = f.readlines() 190 | content1 = content[0].split(' ') 191 | ip = content1[1].strip('\n') 192 | domain = content1[0] 193 | if domain not in os.popen("cat /etc/nginx/sites-enabled/dynamic-ssl-vhost.conf").read(): 194 | print "I Will generate SSL certs for "+domain+" with Let's Encrypt DNS challenge" 195 | email = str(raw_input("Insert your email address? (Used for cert Expiration and Let's Encrypt TOS agreement \n")) 196 | os.system("certbot --text --agree-tos --email "+email+" -d "+domain+" --manual --preferred-challenges dns --expand --renew-by-default --manual-public-ip-logging-ok certonly") 197 | print "Setting up Nginx configuration...\n" 198 | os.popen("""echo 'server { 199 | listen 443 ssl; 200 | 201 | root /var/www/html; 202 | index index.html index.htm index.nginx-debian.html; 203 | ssl_certificate /etc/letsencrypt/live/"""+domain+"""/cert.pem; 204 | ssl_certificate_key /etc/letsencrypt/live/"""+domain+"""/privkey.pem; 205 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 206 | ssl_ciphers HIGH:!aNULL:!MD5; 207 | server_name """+domain+"""; 208 | 209 | location / { 210 | proxy_pass http://"""+ip+"""; 211 | proxy_set_header Host $host; 212 | proxy_set_header X-Real-IP $remote_addr; 213 | } 214 | } 215 | ' >> /etc/nginx/sites-enabled/dynamic-ssl-vhost.conf && service nginx restart""") 216 | 217 | print "TOP Current Connections by IP \n" 218 | 219 | print os.popen("""netstat -atun | grep -v "Addr" | grep -v "and" | awk '{print $5}' | cut -d: -f1 | sed -e '/^$/d' |sort | uniq -c | sort -n""").read() 220 | -------------------------------------------------------------------------------- /shield.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | "log" 7 | "os/exec" 8 | 9 | "github.com/BurntSushi/toml" 10 | 11 | ) 12 | 13 | var ( 14 | 15 | dryrun int 16 | proxy int 17 | underattack int 18 | basicddos int 19 | autossl int 20 | whitelist []string 21 | proxydomains []string 22 | whitelist_text string 23 | 24 | ) 25 | 26 | // Info from config file 27 | type Config struct { 28 | DryRun int 29 | BasicDdos int 30 | UnderAttack int 31 | Proxy int 32 | Autossl int 33 | Whitelist []string 34 | ProxyDomains []string 35 | } 36 | // Reads info from config file 37 | func ReadConfig() Config { 38 | var configfile = "/etc/nshield/nshield.conf" 39 | _, err := os.Stat(configfile) 40 | if err != nil { 41 | log.Fatal("Config file is missing: ", configfile) 42 | } 43 | 44 | var config Config 45 | if _, err := toml.DecodeFile(configfile, &config); err != nil { 46 | log.Fatal(err) 47 | } 48 | //log.Print(config.Index) 49 | return config 50 | } 51 | 52 | func exec_shell(command string) string { 53 | out, err := exec.Command("/bin/bash","-c",command).Output() 54 | if err != nil { 55 | log.Fatal(err) 56 | } 57 | return string(out) 58 | } 59 | 60 | 61 | 62 | func main() { 63 | 64 | var config = ReadConfig() 65 | basicddos = config.BasicDdos 66 | underattack = config.UnderAttack 67 | dryrun = config.DryRun 68 | proxy = config.Proxy 69 | autossl = config.Autossl 70 | whitelist = config.Whitelist 71 | proxydomains = config.ProxyDomains 72 | exec_shell("wget -O /etc/nshield/ipsets/firehol_level1.netset https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/firehol_level1.netset >/dev/null 2>&1") 73 | exec_shell("wget -O /etc/nshield/ipsets/botscout_1d.ipset https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/botscout_1d.ipset >/dev/null 2>&1") 74 | exec_shell("wget -O /etc/nshield/ipsets/bi_any_2_30d.ipset https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/bi_any_2_30d.ipset >/dev/null 2>&1") 75 | exec_shell("wget -O /etc/nshield/snort_ipfilter.ipset https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/snort_ipfilter.ipset >/dev/null 2>&1") 76 | 77 | f, err := os.OpenFile("/var/log/nshield.log", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) 78 | if err != nil { 79 | log.Fatal(err) 80 | } 81 | defer f.Close() 82 | log.SetOutput(f) 83 | 84 | 85 | blocklist_ipset:=exec_shell("cat /etc/nshield/ipsets/*.ipset") 86 | blocklist_netset:=exec_shell("cat /etc/nshield/ipsets/*.netset") 87 | 88 | iplist:=exec_shell("cat /var/log/iptables.log | awk ' { print $11 } ' | sed s'/SRC=//'g | sort -n | uniq -c | sort -rn | head -n25 | grep -v 192.168. | awk '{ print $2 }'") 89 | 90 | 91 | log.Println("Top incoming requests are: \n"+iplist) 92 | 93 | // resolve with curl https://api.iptoasn.com/v1/as/ip/8.8.8.8 | jq '.as_description' 94 | 95 | // Split on comma. 96 | splitted_iplist := strings.Split(iplist, " ") 97 | 98 | // Display all elements. 99 | for i := range splitted_iplist { 100 | ip:=splitted_iplist[i] 101 | if strings.Contains(blocklist_ipset,ip) { 102 | log.Println("Banning "+ip+" because found in IP blocklists") 103 | exec_shell("ipset add block "+ip+" timeout 300") 104 | } 105 | network:=exec_shell(`echo "+ip+" | awk -F '.' '{ print $1"."$2"."$3 }'`) 106 | if strings.Contains(blocklist_netset,network) { 107 | log.Println("Banning "+ip+" because found in Net blocklists") 108 | exec_shell("ipset add block "+ip+" timeout 300") 109 | } 110 | // 111 | 112 | 113 | 114 | } 115 | } 116 | --------------------------------------------------------------------------------