mirror of
https://github.com/hackerschoice/segfault.git
synced 2024-06-30 18:51:22 +00:00
Merge pull request #54 from theaog/monitor_functional_ssh
mfs: add monitor functional ssh tool - GOOD WORK.
This commit is contained in:
commit
29c559f44c
2
admin/monitor_ssh/.gitignore
vendored
Normal file
2
admin/monitor_ssh/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
mfs
|
||||
run.sh
|
13
admin/monitor_ssh/Makefile
Normal file
13
admin/monitor_ssh/Makefile
Normal file
@ -0,0 +1,13 @@
|
||||
VERSION := $(shell git rev-parse --short HEAD)
|
||||
BUILDTIME := $(shell date -u '+%Y-%m-%dT%H:%M:%SZ')
|
||||
|
||||
GOLDFLAGS += -s -w
|
||||
GOLDFLAGS += -X main.Version=$(VERSION)
|
||||
GOLDFLAGS += -X main.Buildtime=$(BUILDTIME)
|
||||
GOFLAGS = -ldflags "$(GOLDFLAGS)"
|
||||
|
||||
build: pre
|
||||
GOOS=linux GOARCH=amd64 go build $(GOFLAGS)
|
||||
|
||||
pre:
|
||||
go mod tidy
|
11
admin/monitor_ssh/go.mod
Normal file
11
admin/monitor_ssh/go.mod
Normal file
@ -0,0 +1,11 @@
|
||||
module mfs
|
||||
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
github.com/yanzay/tbot/v2 v2.2.0
|
||||
golang.org/x/crypto v0.1.0
|
||||
)
|
||||
|
||||
require golang.org/x/sys v0.2.0 // indirect
|
21
admin/monitor_ssh/go.sum
Normal file
21
admin/monitor_ssh/go.sum
Normal file
@ -0,0 +1,21 @@
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/yanzay/tbot/v2 v2.2.0 h1:bK1+XTwY59IskXpwtHc/ItfU2uELTTqYP3pajfBaPeM=
|
||||
github.com/yanzay/tbot/v2 v2.2.0/go.mod h1:q0+8JblBq9tLAnKHdBIZsHwDvMS9TfO6mNfaAk1VrHg=
|
||||
golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
|
||||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
205
admin/monitor_ssh/main.go
Normal file
205
admin/monitor_ssh/main.go
Normal file
@ -0,0 +1,205 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/yanzay/tbot/v2"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
// set during compilation using ldflags
|
||||
var Version string
|
||||
var Buildtime string
|
||||
|
||||
// stores `server:secret` from -s flag
|
||||
var servers = map[string]string{}
|
||||
|
||||
// flags
|
||||
var timerFlag = flag.Duration("timer", time.Minute, "how often to connect to segfault servers")
|
||||
var versionFlag = flag.Bool("version", false, "print program version")
|
||||
var debugFlag = flag.Bool("debug", false, "print debug logs")
|
||||
|
||||
func init() {
|
||||
|
||||
flag.Func("s", "server:secret", func(s string) error {
|
||||
|
||||
split := strings.Split(s, ":")
|
||||
if len(split) < 2 {
|
||||
return fmt.Errorf("must provide server:secret")
|
||||
}
|
||||
servers[split[0]] = split[1]
|
||||
return nil
|
||||
})
|
||||
flag.Parse()
|
||||
|
||||
log.SetFormatter(&log.TextFormatter{
|
||||
ForceColors: true,
|
||||
})
|
||||
|
||||
if *debugFlag {
|
||||
log.SetLevel(log.DebugLevel)
|
||||
log.SetReportCaller(true)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
// telegram bot secret key
|
||||
tgKEY = mustEnv("TG_KEY")
|
||||
// telegram chat id
|
||||
tgCHATID = mustEnv("TG_CHATID")
|
||||
)
|
||||
|
||||
func main() {
|
||||
if *versionFlag {
|
||||
fmt.Printf("%v compiled on %v from commit %v\n", os.Args[0], Buildtime, Version)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
log.Debugf("Telegram chat ID: %v", tgCHATID)
|
||||
bot := tbot.New(tgKEY)
|
||||
bot.HandleMessage("cowsay .+", func(m *tbot.Message) {
|
||||
// we use cowsay to confirm supergroup chat ids
|
||||
log.Printf("chat id: %v", m.Chat.ID)
|
||||
|
||||
text := strings.TrimPrefix(m.Text, "cowsay ")
|
||||
cow := fmt.Sprintf("```\n%s\n```", cowsay(text))
|
||||
bot.Client().SendMessage(m.Chat.ID, cow, tbot.OptParseModeMarkdown)
|
||||
})
|
||||
go func() { // run bot listener on his own thread
|
||||
err := bot.Start()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
// collects errors and sends them as message via telegram bot
|
||||
var msgC = make(chan string, len(servers)) // buffered
|
||||
defer close(msgC)
|
||||
go func() {
|
||||
for msg := range msgC {
|
||||
log.Warnf("%v", msg)
|
||||
_, err := bot.Client().SendMessage(tgCHATID, msg)
|
||||
if err != nil {
|
||||
log.Errorf("%v: %+v", err, "bah")
|
||||
continue
|
||||
}
|
||||
}
|
||||
log.Debugf("exiting...")
|
||||
}()
|
||||
|
||||
var (
|
||||
wg = &sync.WaitGroup{}
|
||||
|
||||
// protects `connTracker` from concurrent r/w
|
||||
mu sync.Mutex
|
||||
connTracker int
|
||||
)
|
||||
|
||||
// program main loop
|
||||
for {
|
||||
for server, secret := range servers {
|
||||
|
||||
wg.Add(1)
|
||||
go func(server, secret string) {
|
||||
defer func() {
|
||||
wg.Done()
|
||||
mu.Lock()
|
||||
connTracker++
|
||||
mu.Unlock()
|
||||
}()
|
||||
|
||||
err := checkServer(server, secret)
|
||||
if err != nil {
|
||||
log.Debug(err)
|
||||
msgC <- err.Error()
|
||||
}
|
||||
}(server, secret)
|
||||
}
|
||||
|
||||
log.Debug("waiting for routines to return")
|
||||
wg.Wait()
|
||||
|
||||
time.Sleep(*timerFlag)
|
||||
|
||||
mu.Lock()
|
||||
log.Infof("[#%v] successful connections", connTracker)
|
||||
mu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
var SSH_CLIENT_CONF = &ssh.ClientConfig{
|
||||
Timeout: time.Second * 5,
|
||||
ClientVersion: "SSH-2.0-Segfault",
|
||||
User: "root",
|
||||
Auth: []ssh.AuthMethod{ssh.Password("segfault")},
|
||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||
BannerCallback: func(message string) error {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func checkServer(server, secret string) error {
|
||||
const SSH_PORT = "22"
|
||||
|
||||
client, err := ssh.Dial("tcp", server+":"+SSH_PORT, SSH_CLIENT_CONF)
|
||||
if err != nil {
|
||||
return fmt.Errorf("[%v] connection failed: %v", server, err)
|
||||
}
|
||||
|
||||
session, err := client.NewSession()
|
||||
if err != nil {
|
||||
return fmt.Errorf("[%v] SSH session failed: %v", server, err)
|
||||
}
|
||||
defer session.Close()
|
||||
session.Setenv("SECRET", secret)
|
||||
|
||||
// configure terminal mode
|
||||
modes := ssh.TerminalModes{
|
||||
ssh.ECHO: 0, // supress echo
|
||||
}
|
||||
// run terminal session
|
||||
if err := session.RequestPty("xterm", 50, 80, modes); err != nil {
|
||||
return fmt.Errorf("[%v] SSH request PTY failed: %v", server, err)
|
||||
}
|
||||
|
||||
out, err := session.CombinedOutput("uptime 2>&1")
|
||||
if err != nil {
|
||||
return fmt.Errorf("[%v] SSH `uptime` command failed: %v", server, err)
|
||||
}
|
||||
|
||||
log.Infof("[%v] %v", server, string(out))
|
||||
return nil
|
||||
}
|
||||
|
||||
// mustEnv panics if our required envs are not present.
|
||||
func mustEnv(s string) string {
|
||||
env := os.Getenv(s)
|
||||
if env == "" {
|
||||
log.Fatalf("must provide environment variable: `export %v=`", s)
|
||||
}
|
||||
return env
|
||||
}
|
||||
|
||||
// for fun.
|
||||
func cowsay(text string) string {
|
||||
lineLen := utf8.RuneCountInString(text) + 2
|
||||
topLine := fmt.Sprintf(" %s ", strings.Repeat("_", lineLen))
|
||||
textLine := fmt.Sprintf("< %s >", text)
|
||||
bottomLine := fmt.Sprintf(" %s ", strings.Repeat("-", lineLen))
|
||||
cow := `
|
||||
\ ^__^
|
||||
\ (oo)\_______
|
||||
(__)\ )\/\
|
||||
||----w |
|
||||
|| ||
|
||||
`
|
||||
resp := fmt.Sprintf("%s\n%s\n%s%s", topLine, textLine, bottomLine, cow)
|
||||
return resp
|
||||
}
|
19
admin/monitor_ssh/readme.md
Normal file
19
admin/monitor_ssh/readme.md
Normal file
@ -0,0 +1,19 @@
|
||||
# Monitor Functional SSH (mfs)
|
||||
|
||||
Attempts to start a SSH session (concurrently) to each of our segfault.net instances and reports on telegram any failures to connect or login.
|
||||
|
||||
Admins must provide `TG_KEY` and `TG_CHATID` env variables to start the tool.
|
||||
```bash
|
||||
$ export TG_KEY="key"
|
||||
$ export TG_CHATID="12345678"
|
||||
```
|
||||
|
||||
The servers list must be supplied via CLI flags, e.g.:
|
||||
```bash
|
||||
$ ./mfs \
|
||||
-s de.segfault.net:secret \
|
||||
-s us.segfault.net:secret \
|
||||
-s it.segfault.net:secret
|
||||
```
|
||||
|
||||
By default it checks all servers every 1 minute, you can tweak the timer e.g. `-timer 5m`
|
Loading…
Reference in New Issue
Block a user