├── .gitignore ├── README.md ├── conf └── app.ini ├── settings └── settings.go ├── ssh_firewall ├── ssh_firewall.go └── util ├── attack_check.go ├── iptables.go └── schedule.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | .idea -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # xsec-ssh-firweall 2 | 3 | xsec-ssh-firewall为一个简易的SSH密码防暴力破解程序,通过读取SSH的`auth.log`判断恶意IP,执行`iptables`进行封杀。本项目采用Go语言开发。 4 | 5 | ## 使用方法 6 | 7 | 设置好配置文件`conf/app.ini`后直接启动`./ssh_firewall`即可。 8 | 9 | 建议使用`supvervisor`或`nohup`将其跑在后台。 10 | 11 | ```ini 12 | INTERFACE = eth0 13 | BLOCKTIME = 60 14 | WHITE_IPLIST = 127.0.0.1,8.8.8.8,4.4.4.4,10.10.10.20 15 | 16 | [logs] 17 | sshd_log = /var/log/ 18 | ``` 19 | 20 | ### 配置文件说明: 21 | 22 | 1. INTERFACE表示外网的网卡名 23 | 1. BLOCKTIME表示对暴力破解来源IP的封禁时间,单位为分钟 24 | 1. WHITE_IPLIST为白名单,防止自己连续3次输错密码后无法登录服务器 25 | 1. sshd_log为ssh log的位置,保持默认即可 26 | 27 | ## 使用效果截图: 28 | 29 | ![](https://docs.xsec.io/images/ssh-firewall/ssh-firewall.png) 30 | -------------------------------------------------------------------------------- /conf/app.ini: -------------------------------------------------------------------------------- 1 | INTERFACE = eth0 2 | BLOCKTIME = 60 3 | WHITE_IPLIST = 127.0.0.1,8.8.8.8,4.4.4.4,10.10.10.20 4 | 5 | [logs] 6 | sshd_log = /var/log/ 7 | -------------------------------------------------------------------------------- /settings/settings.go: -------------------------------------------------------------------------------- 1 | package settings 2 | 3 | import ( 4 | "fmt" 5 | 6 | "log" 7 | "os" 8 | "path" 9 | "path/filepath" 10 | 11 | "github.com/patrickmn/go-cache" 12 | "gopkg.in/ini.v1" 13 | "strings" 14 | "time" 15 | ) 16 | 17 | var ( 18 | Cfg *ini.File 19 | Interface string 20 | BlockTime time.Duration 21 | WhiteIPlist map[string]bool 22 | 23 | SshLog string 24 | Cache map[string]*cache.Cache 25 | ) 26 | 27 | func init() { 28 | log.SetPrefix("[xsec-ssh-firewall] ") 29 | var err error 30 | source := "conf/app.ini" 31 | Cfg, err = ini.Load(source) 32 | //log.Println(Cfg, err) 33 | if err != nil { 34 | log.Panicln(err) 35 | } 36 | 37 | Interface = Cfg.Section("").Key("INTERFACE").MustString("eth0") 38 | blockTime := Cfg.Section("").Key("BLOCKTIME").MustInt64(60) 39 | BlockTime = time.Duration(blockTime) 40 | whiteIps := Cfg.Section("").Key("WHITE_IPLIST").MustString("127.0.0.1") 41 | WhiteIPlist = make(map[string]bool) 42 | for _, ip := range strings.Split(whiteIps, ",") { 43 | WhiteIPlist[ip] = true 44 | } 45 | 46 | curDir, _ := filepath.Abs(".") 47 | secLogs := Cfg.Section("logs") 48 | SshLog = secLogs.Key("sshd_log").MustString(fmt.Sprintf("%v/logs/openssh/", curDir)) 49 | 50 | if _, err := os.Stat(SshLog); !os.IsExist(err) { 51 | logPath := path.Dir(SshLog) 52 | os.MkdirAll(logPath, 0755) 53 | os.Create(path.Join(SshLog, "auth.log")) 54 | os.Create(path.Join(SshLog, "syslog")) 55 | } 56 | Cache = make(map[string]*cache.Cache) 57 | log.Printf("SSHLogPath: %v, Interface: %v, BlockTime: %v, WhiteIplist: %v\n", SshLog, Interface, 58 | BlockTime*time.Minute, WhiteIPlist) 59 | } 60 | -------------------------------------------------------------------------------- /ssh_firewall: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netxfly/xsec-ssh-firewall/dc2222e00e834fa5601db0c5770638407ad5f192/ssh_firewall -------------------------------------------------------------------------------- /ssh_firewall.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "xsec-ssh-firewall/settings" 5 | "xsec-ssh-firewall/util" 6 | ) 7 | 8 | func main() { 9 | go util.MonitorLog(settings.SshLog) 10 | 11 | util.Schedule(30) 12 | 13 | } 14 | -------------------------------------------------------------------------------- /util/attack_check.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "regexp" 7 | "time" 8 | 9 | "github.com/hpcloud/tail" 10 | "github.com/patrickmn/go-cache" 11 | 12 | "xsec-ssh-firewall/settings" 13 | ) 14 | 15 | func MonitorLog(SShLogPath string) { 16 | logName := fmt.Sprintf("%v/auth.log", SShLogPath) 17 | t, err := tail.TailFile(logName, tail.Config{Follow: true}) 18 | // fmt.Println(t, err) 19 | if err == nil { 20 | for line := range t.Lines { 21 | //fmt.Printf("%v, %v, %v\n", line.Time.Format("2006-01-02 15:04:05"), line.Text, line.Err) 22 | CheckSSH(line) 23 | } 24 | } 25 | } 26 | 27 | func CheckSSH(logContent *tail.Line) { 28 | content := logContent.Text 29 | re, _ := regexp.Compile(`.* Failed password for (.+?) from (.+?) port .*`) 30 | ret := re.FindStringSubmatch(content) 31 | if len(ret) > 0 { 32 | ip := ret[2] 33 | user := ret[1] 34 | if _, ok := settings.WhiteIPlist[ip]; ok { 35 | log.Printf("%v in White list, ignore\n", ip) 36 | 37 | } else { 38 | log.Printf("%v, [ %v ] try to crack %v 's password\n", logContent.Time.Format("2006-01-02 15:04:05"), ip, user) 39 | if c, ok := settings.Cache[ip]; ok { 40 | if _, ok := c.Get("times"); ok { 41 | c.IncrementInt("times", 1) 42 | times, _ := c.Get("times") 43 | log.Printf("%v crack times:%v\n", ip, times) 44 | t := times.(int) 45 | if t >= 3 { 46 | // Refresh the traffic transfer policy 47 | RefreshPolicy() 48 | } 49 | } else { 50 | c.Set("times", 1, cache.DefaultExpiration) 51 | } 52 | } else { 53 | c := cache.New(settings.BlockTime*time.Minute, 30*time.Second) 54 | 55 | c.Set("times", 1, cache.DefaultExpiration) 56 | settings.Cache[ip] = c 57 | } 58 | } 59 | 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /util/iptables.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "log" 5 | "os/exec" 6 | 7 | "xsec-ssh-firewall/settings" 8 | ) 9 | 10 | func SetIptables() { 11 | 12 | for ip := range settings.Cache { 13 | if ip == "127.0.0.1" { 14 | log.Printf("Local ip: %v", ip) 15 | continue 16 | } else { 17 | log.Printf("Block ip: %v\n", ip) 18 | exec.Command("/sbin/iptables", "-t", "filter", "-A", "WHITELIST", "-i", settings.Interface, "-s", ip, "-j", "DROP").Output() 19 | 20 | } 21 | 22 | } 23 | } 24 | 25 | // Init iptables policy 26 | func InitPolicy() { 27 | // set white list chain in filter table 28 | exec.Command("/sbin/iptables", "-t", "filter", "-N", "WHITELIST").Run() 29 | exec.Command("/sbin/iptables", "-t", "filter", "-F", "WHITELIST").Run() 30 | exec.Command("/sbin/iptables", "-t", "filter", "-A", "INPUT", "-j", "WHITELIST").Run() 31 | } 32 | 33 | // Delete Policy 34 | func DeletePolicy() { 35 | // Flush rule 36 | exec.Command("/sbin/iptables", "-t", "filter", "-F").Run() 37 | // delete chain 38 | exec.Command("/sbin/iptables", "-t", "filter", "-X", "WHITELIST").Run() 39 | 40 | } 41 | 42 | func RefreshPolicy() { 43 | Stop() 44 | InitPolicy() 45 | SetIptables() 46 | } 47 | 48 | func Stop() { 49 | DeletePolicy() 50 | } 51 | -------------------------------------------------------------------------------- /util/schedule.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | func Schedule(seconds time.Duration) { 8 | for { 9 | go RefreshPolicy() 10 | time.Sleep(seconds * time.Second) 11 | } 12 | } 13 | --------------------------------------------------------------------------------