Восстановление репозитория
This commit is contained in:
371
alarmaction.go
Normal file
371
alarmaction.go
Normal file
@@ -0,0 +1,371 @@
|
||||
//----------------------------------------------------------------------------
|
||||
// Получение и обработка данных о пользовательских сессиях (авторизация)
|
||||
// при соединении с почтовой системой. И блокирование нежелательных адресов.
|
||||
// ---------------------------------------------------------------------------
|
||||
// Версия 2 (ipset)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strings"
|
||||
"net/netip"
|
||||
"time"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
|
||||
// "github.com/coreos/go-iptables/iptables"
|
||||
"github.com/gonetx/ipset"
|
||||
"github.com/jackc/pgx/v5"
|
||||
// "github.com/jackc/pgtype"
|
||||
)
|
||||
|
||||
// проверяем наличие ipset системе
|
||||
func CheckIPSet() {
|
||||
if err := ipset.Check(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
type Alarm struct {
|
||||
Id int32
|
||||
Mail_account_id int32
|
||||
Remote_ip netip.Prefix
|
||||
Country_code string
|
||||
Last_time string
|
||||
Last_check_timestamp time.Time
|
||||
Action_timestamp time.Time
|
||||
Alarm_type int
|
||||
Alarm_action bool
|
||||
Hostname string
|
||||
}
|
||||
|
||||
// PIDFile stored the process id
|
||||
type PIDFile struct {
|
||||
path string
|
||||
}
|
||||
|
||||
var PID_file_path = "/var/run/alarmaction.pid"
|
||||
|
||||
// Remove the pid file
|
||||
// func (file PIDFile) PIDRemove() error {
|
||||
// return os.Remove(file.path)
|
||||
// }
|
||||
|
||||
func ProcessExit(exit_code int) {
|
||||
if exit_code == 0 {
|
||||
os.Remove(PID_file_path)
|
||||
}
|
||||
os.Exit(exit_code)
|
||||
}
|
||||
|
||||
// Выборка записей по конкретному IP
|
||||
// запросы разные в зависимости от типа тревоги
|
||||
func PgSelectAlarmRemoteIP(alarm Alarm) pgx.Rows {
|
||||
var query string
|
||||
|
||||
conn, err := pgx.Connect(context.Background(), os.Getenv("DATABASE_URL"))
|
||||
// fmt.Println(*conn)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Unable to connect to database: %v\n", err)
|
||||
ProcessExit(1)
|
||||
}
|
||||
|
||||
switch alarm.Alarm_type {
|
||||
case 4:
|
||||
query = fmt.Sprintf("select id, remote_ip from alarm where alarm_type = %v and \"%v_action_timestamp\" is NULL", alarm.Alarm_type, alarm.Hostname)
|
||||
}
|
||||
log.Println(query)
|
||||
rows, err_select := conn.Query(context.Background(), query)
|
||||
|
||||
if err_select != nil {
|
||||
log.Println("Query alarms failed PgSelectAlarmRemoteIP:", err_select)
|
||||
ProcessExit(1)
|
||||
}
|
||||
if len(rows.FieldDescriptions()) == 0 {
|
||||
ProcessExit(1)
|
||||
}
|
||||
|
||||
// log.Println("Selected an:",len(rows.FieldDescriptions()), "records")
|
||||
|
||||
defer conn.Close(context.Background())
|
||||
return rows
|
||||
}
|
||||
// Проверка на соответствие адреса записи в filter
|
||||
func PgSelectFilterIP(ip netip.Prefix) int32 {
|
||||
var query string
|
||||
var id int32
|
||||
conn, err := pgx.Connect(context.Background(), os.Getenv("DATABASE_URL"))
|
||||
// fmt.Println(*conn)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Unable to connect to database: %v\n", err)
|
||||
ProcessExit(1)
|
||||
}
|
||||
|
||||
// проверяем адрес на совпадение с фильтром (для исключения)
|
||||
// select * from filter where remote_ip >>= inet '209.85.161.20/32'
|
||||
query = fmt.Sprintf("select id from filter where remote_ip >>= inet '%v'", ip)
|
||||
log.Println(query)
|
||||
err_select := conn.QueryRow(context.Background(), query).Scan(&id)
|
||||
|
||||
if err_select != nil {
|
||||
log.Println("Select filter for remote ip", ip, "error:", err)
|
||||
if err_select.Error() == "no rows in result set" {
|
||||
id = 0
|
||||
} else {
|
||||
ProcessExit(1)
|
||||
}
|
||||
}
|
||||
return id
|
||||
}
|
||||
func PgUpdateAlarm(conn *pgx.Conn, alarm Alarm, id int32) int32 {
|
||||
log.Println("Update alarm data", alarm)
|
||||
t := time.Now().Format("2006-01-02 15:04:05")
|
||||
query := fmt.Sprintf("update alarm set \"%v_action_timestamp\"='%v' where id=%v returning id", alarm.Hostname, t, id)
|
||||
log.Println("Update alarm query:", query)
|
||||
err := conn.QueryRow(context.Background(), query).Scan(&id)
|
||||
if err != nil {
|
||||
log.Printf("Update alarm data failed: %v\n", err)
|
||||
}
|
||||
|
||||
return id
|
||||
}
|
||||
|
||||
// func IpTablesCreateChain (sChain string) bool {
|
||||
// ipt, err := iptables.New()
|
||||
// if err != nil {
|
||||
// log.Printf("Failed to new up an IPtables intance. ERROR: %v", err)
|
||||
// ProcessExit(1)
|
||||
// }
|
||||
// var chain_exists bool
|
||||
// chain_exists, err = ipt.ChainExists("filter", sChain)
|
||||
// if err != nil {
|
||||
// log.Printf("Failed to checking chain (%v). ERROR: %v", sChain , err)
|
||||
// return false
|
||||
// }
|
||||
// if chain_exists {
|
||||
// return true
|
||||
// } else {
|
||||
// err = ipt.NewChain("filter", sChain)
|
||||
// if err != nil {
|
||||
// log.Printf("Failed to creating chain (%v). ERROR: %v", sChain , err)
|
||||
// return false
|
||||
// }
|
||||
// err = ipt.AppendUnique("filter", "INPUT", "-j", sChain)
|
||||
// if err != nil {
|
||||
// log.Printf("Failed to append chain. ERROR: %v", err)
|
||||
// return false
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return true
|
||||
//
|
||||
// }
|
||||
|
||||
// func BlockIP(ip netip.Prefix, sChain string, dChain string) bool {
|
||||
// // Some default chain names
|
||||
//
|
||||
// // Get a new iptables interface
|
||||
// ipt, err := iptables.New()
|
||||
// if err != nil {
|
||||
// log.Printf("Failed to new up an IPtables intance. ERROR: %v", err)
|
||||
// ProcessExit(1)
|
||||
// }
|
||||
// // Build out the ipstring(add /32 to the end)
|
||||
// ipstr := fmt.Sprintf("%s", ip)
|
||||
// res, _ := ipt.Exists("filter", sChain, "-s", ipstr, "-j", dChain)
|
||||
// if !res {
|
||||
// // Use the appendUnique method to put this in iptables, but only once
|
||||
// err = ipt.AppendUnique("filter", sChain, "-s", ipstr, "-j", dChain)
|
||||
// if err != nil {
|
||||
// log.Printf("Failed to ban an ip(%v). ERROR: %v", ipstr , err)
|
||||
// return false
|
||||
// }
|
||||
// } else {
|
||||
// log.Printf("Failed to ban an ip(%v). ERROR: Address already banned", ipstr)
|
||||
// }
|
||||
//
|
||||
// // Since we made it here, we won
|
||||
// return true
|
||||
// }
|
||||
func BlockIP(ip netip.Prefix, sChain string, dChain string) bool {
|
||||
// create test set even it's exist
|
||||
// set, err := ipset.New("auto_blocked", ipset.HashIp, ipset.Exist(true), ipset.Timeout(time.Hour))
|
||||
ip_address := ip.String()
|
||||
set, err := ipset.New("auto-blocked-ipset", ipset.HashIp, ipset.Exist(true))
|
||||
// output: test
|
||||
if err != nil {
|
||||
log.Printf("Failed to create new ipset rule. ERROR: %v", err)
|
||||
// return false
|
||||
} else {
|
||||
log.Printf("New ipset rule %v was created", set.Name())
|
||||
}
|
||||
|
||||
// _ = set.Flush()
|
||||
|
||||
// _ = set.Add("1.1.1.1", ipset.Timeout(time.Hour))
|
||||
err = set.Add(ip_address)
|
||||
if err != nil {
|
||||
log.Printf("Failed to added new %v to set. ERROR: %v", ip_address, err)
|
||||
return false
|
||||
}
|
||||
|
||||
ok, err := set.Test(ip_address)
|
||||
// output: true
|
||||
log.Println(ok)
|
||||
if err != nil {
|
||||
log.Printf("Failed to added new %v to set. ERROR: %v", ip_address, err)
|
||||
return false
|
||||
}
|
||||
|
||||
// ok, _ = set.Test("1.1.1.2")
|
||||
// output: false
|
||||
// log.Println(ok)
|
||||
|
||||
// info, _ := set.List()
|
||||
// output: &{test hash:ip 4 family inet hashsize 1024 maxelem 65536 timeout 3600 216 0 [1.1.1.1 timeout 3599]}
|
||||
// log.Println(info)
|
||||
|
||||
// _ = set.Del("1.1.1.1")
|
||||
|
||||
// _ = set.Destroy()
|
||||
return true
|
||||
}
|
||||
|
||||
// just suit for linux
|
||||
func processExists(pid string) bool {
|
||||
if _, err := os.Stat(filepath.Join("/proc", pid)); err == nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func checkPIDFILEAlreadyExists(path string) error {
|
||||
if pidByte, err := ioutil.ReadFile(path); err == nil {
|
||||
pid := strings.TrimSpace(string(pidByte))
|
||||
if processExists(pid) {
|
||||
return fmt.Errorf("ensure the process:%s is not running pid file:%s", pid, path)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewPIDFile create the pid file
|
||||
// path specified under production pidfile, file content pid
|
||||
func NewPIDFile(path string) (*PIDFile, error) {
|
||||
if err := checkPIDFILEAlreadyExists(path); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(path), os.FileMode(0755)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := ioutil.WriteFile(path, []byte(fmt.Sprintf("%d", os.Getpid())), 0644); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &PIDFile{path: path}, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
CheckIPSet()
|
||||
// Создадим и откроем на запись файл для логов
|
||||
var alarm_log_file string
|
||||
if os.Getenv("ALARM_LOG_FILE") == "" {
|
||||
alarm_log_file = "alarmaction.log"
|
||||
} else {
|
||||
alarm_log_file = os.Getenv("ALARM_LOG_FILE")
|
||||
}
|
||||
|
||||
f, err := os.OpenFile(alarm_log_file, os.O_RDWR | os.O_CREATE | os.O_APPEND, 0666)
|
||||
if err != nil {
|
||||
log.Fatalf("error opening file: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
log.SetOutput(f)
|
||||
|
||||
// Создание PID файла
|
||||
_, err = NewPIDFile(PID_file_path)
|
||||
if err != nil {
|
||||
log.Println("error to create the pid file failed:", err.Error())
|
||||
ProcessExit(1)
|
||||
}
|
||||
|
||||
// Проверка переменных окружения
|
||||
if os.Getenv("PGHOST") == "" {
|
||||
fmt.Println("Send error: make sure environment variables `PGHOST` was set")
|
||||
ProcessExit(1)
|
||||
}
|
||||
if os.Getenv("PGUSER") == "" {
|
||||
fmt.Println("Send error: make sure environment variables `PGUSER` was set")
|
||||
ProcessExit(1)
|
||||
}
|
||||
if os.Getenv("PGPASSWORD") == "" {
|
||||
fmt.Println("Send error: make sure environment variables `PGPASSWORD` was set")
|
||||
ProcessExit(1)
|
||||
}
|
||||
if os.Getenv("PGDATABASE") == "" {
|
||||
fmt.Println("Send error: make sure environment variables `PGDATABASE` was set")
|
||||
ProcessExit(1)
|
||||
}
|
||||
// if os.Getenv("HOSTNAME") == "" {
|
||||
// fmt.Println("Send error: make sure environment variables `HOSTNAME` was set")
|
||||
// ProcessExit(1)
|
||||
// }
|
||||
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
ProcessExit(1)
|
||||
}
|
||||
|
||||
conn, err := pgx.Connect(context.Background(), os.Getenv("DATABASE_URL"))
|
||||
// fmt.Println(*conn)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Unable to connect to database: %v\n", err)
|
||||
ProcessExit(1)
|
||||
}
|
||||
|
||||
sChain := "auto_blocked"
|
||||
dChain := "DROP"
|
||||
|
||||
// if !IpTablesCreateChain(sChain) {
|
||||
// log.Println("Iptables chain", sChain, "does not exists")
|
||||
// ProcessExit(1)
|
||||
// }
|
||||
|
||||
var alarm Alarm
|
||||
|
||||
alarm.Hostname = strings.Split(hostname, ".")[0]
|
||||
alarm.Action_timestamp = time.Unix(time.Now().Unix(), 0)
|
||||
alarm.Alarm_type = 4
|
||||
|
||||
res := PgSelectAlarmRemoteIP(alarm)
|
||||
var id int32
|
||||
var ip netip.Prefix
|
||||
var filter_id int32
|
||||
for res.Next() {
|
||||
values, _ := res.Values()
|
||||
id = values[0].(int32)
|
||||
ip = values[1].(netip.Prefix)
|
||||
filter_id = PgSelectFilterIP(ip)
|
||||
log.Println(filter_id)
|
||||
if filter_id > 0 {
|
||||
log.Println("The ip address", ip, "is conained in filter table")
|
||||
log.Println(PgUpdateAlarm(conn, alarm, id))
|
||||
// continue
|
||||
} else {
|
||||
fmt.Println(id, ip)
|
||||
if BlockIP(ip, sChain, dChain) {
|
||||
log.Println("The ip address", ip, "was blocked")
|
||||
alarm.Alarm_action = true
|
||||
log.Println(PgUpdateAlarm(conn, alarm, id))
|
||||
}
|
||||
}
|
||||
}
|
||||
ProcessExit(0)
|
||||
}
|
Reference in New Issue
Block a user