Skip to content

Commit

Permalink
init: add first version
Browse files Browse the repository at this point in the history
  • Loading branch information
Rirush committed Jul 3, 2022
0 parents commit 0fc15dd
Show file tree
Hide file tree
Showing 6 changed files with 221 additions and 0 deletions.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# auth-notify

A small ssh and sudo notification daemon. It parses /var/log/auth.log, searches for successful ssh authentications
and all sudo authentications, and notifies about them both in stdout, and optionally in Telegram.

Currently only tested with OpenSSH and systemd on Debian.

## Installation

Download binary from the release, copy it to `/usr/local/bin/auth-notify`, then copy `auth-notify.conf` into
`/etc/`, and `auth-notify.service` into `/etc/systemd/system/`.

You can start the service by running `systemctl start auth-notify`, and enable automatic start on boot with
`systemctl enable auth-notify`.
2 changes: 2 additions & 0 deletions auth-notify.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
TELEGRAM_CHAT=
TELEGRAM_TOKEN=
11 changes: 11 additions & 0 deletions auth-notify.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[Unit]
Description=auth-notify is a login notification service
Wants=multi-user.target

[Service]
ExecStart=/usr/local/bin/auth-notify
EnvironmentFile=/etc/auth-notify.conf
Restart=on-failure

[Install]
WantedBy=multi-user.target
12 changes: 12 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module auth-notify

go 1.18

require github.com/hpcloud/tail v1.0.0

require (
github.com/fsnotify/fsnotify v1.5.4 // indirect
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect
gopkg.in/fsnotify.v1 v1.4.7 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
)
10 changes: 10 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
172 changes: 172 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
package main

import (
"encoding/json"
"fmt"
"github.com/hpcloud/tail"
"io"
"log"
"net/http"
"net/url"
"os"
"strings"
"time"
)

type TelegramResponse struct {
OK bool `json:"ok"`
Description string `json:"description"`
}

func SendMessage(chat, token, message string) error {
if chat == "" || token == "" {
// If no configuration provided, don't do anything
return nil
}

endpoint := "https://api.telegram.org/bot" + token + "/sendMessage"

params := url.Values{}
params.Set("chat_id", chat)
params.Set("text", message)

c := http.Client{Timeout: 15 * time.Second}
resp, err := c.Get(endpoint + "?" + params.Encode())
if err != nil {
return err
}

defer resp.Body.Close()

data, err := io.ReadAll(resp.Body)
if err != nil {
return err
}

tgResp := &TelegramResponse{}
err = json.Unmarshal(data, tgResp)

if err != nil {
return err
}

if !tgResp.OK {
return fmt.Errorf("telegram returned an error: %v", tgResp.Description)
}

return nil
}

func main() {
telegramChat := os.Getenv("TELEGRAM_CHAT")
telegramToken := os.Getenv("TELEGRAM_TOKEN")

logTail, err := tail.TailFile("/var/log/auth.log", tail.Config{
ReOpen: true,
Follow: true,
})
if err != nil {
log.Fatalln(err)
}

skipping := true
now := time.Now()

for line := range logTail.Lines {
entryFields := strings.SplitN(line.Text, ": ", 2)
if len(entryFields) != 2 {
continue
}

meta, text := entryFields[0], entryFields[1]
metaFields := strings.Fields(meta)
if len(metaFields) != 5 {
continue
}

date := strings.Join(metaFields[:3], " ")
hostname := metaFields[3]
unit := metaFields[4]

t, err := time.Parse("Jan 2 15:04:05", date)
if err != nil {
log.Println("cannot parse time:", err)
continue
}

// Skip all records that exist before starting the tail
if skipping && (t.Day() != now.Day() ||
t.Month() != now.Month() ||
t.Hour() < now.Hour() ||
(t.Hour() == now.Hour() && t.Minute() < now.Minute()) ||
(t.Hour() == now.Hour() && t.Minute() == now.Minute() && t.Second() <= now.Second())) {
continue
}

skipping = false

switch {
case strings.HasPrefix(unit, "sshd"):
if !strings.HasPrefix(text, "Accepted") {
continue
}

sshFields := strings.Fields(text)
if len(sshFields) < 6 {
continue
}

method, user, ip := sshFields[1], sshFields[3], sshFields[5]

fmt.Printf("new ssh session for user %v (from %v; using %v)\n", user, ip, method)
err = SendMessage(telegramChat, telegramToken,
fmt.Sprintf("new ssh session started by user %v\n\nfrom: %v\nmethod: %v\nhostname: %v",
user, ip, method, hostname))
if err != nil {
fmt.Printf("could not send message: %v\n", err)
}
case strings.HasPrefix(unit, "sudo"):
text = strings.TrimSpace(text)
splitSudo := strings.SplitN(text, " : ", 2)
if len(splitSudo) != 2 {
continue
}

user, rest := splitSudo[0], splitSudo[1]

failed := strings.Contains(rest, "incorrect password")

entries := strings.Split(rest, " ; ")
var asUser, command string

for _, entry := range entries {
switch {
case strings.HasPrefix(entry, "USER="):
asUser = entry[5:]
case strings.HasPrefix(entry, "COMMAND="):
command = entry[8:]
}
}

if !failed {
fmt.Printf("sudo executed by %v (became %v; for command %v)\n", user, asUser, command)

err = SendMessage(telegramChat, telegramToken,
fmt.Sprintf("sudo started by %v\n\ntarget: %v\ncommand: %v\nhostname: %v",
user, asUser, command, hostname))
if err != nil {
fmt.Printf("could not send message: %v\n", err)
}
} else {
fmt.Printf("failed attempt to execute sudo by %v (to become %v; for command %v)\n", user, asUser, command)

err = SendMessage(telegramChat, telegramToken,
fmt.Sprintf("failed attempt to start sudo by %v\n\ntarget: %v\ncommand: %v\nhostname: %v",
user, asUser, command, hostname))
if err != nil {
fmt.Printf("could not send message: %v\n", err)
}
}
}
}
}

0 comments on commit 0fc15dd

Please sign in to comment.