This commit is contained in:
SkyperTHC 2023-02-27 17:19:33 +00:00
commit 4f6895da0a
No known key found for this signature in database
GPG Key ID: A9BD386DF9113CD6
21 changed files with 106 additions and 67 deletions

@ -2,7 +2,7 @@ VER := 0.4.3rc2
all:
make -C router
make -C cleaner/cg
make -C tools/cg
make -C master
make -C host
make -C tor
@ -123,12 +123,12 @@ FILES_ROOT += "segfault-$(VER)/sfbin/sf"
FILES_ROOT += "segfault-$(VER)/sfbin/loginmsg-new.sh-example"
FILES_ROOT += "segfault-$(VER)/sfbin/loginmsg-all.sh-example"
FILES_CLEANER += "segfault-$(VER)/cleaner/cg/Dockerfile"
FILES_CLEANER += "segfault-$(VER)/cleaner/cg/go.mod"
FILES_CLEANER += "segfault-$(VER)/cleaner/cg/go.sum"
FILES_CLEANER += "segfault-$(VER)/cleaner/cg/main.go"
FILES_CLEANER += "segfault-$(VER)/cleaner/cg/Makefile"
FILES_CLEANER += "segfault-$(VER)/cleaner/cg/sysinfo_linux.go"
FILES_CLEANER += "segfault-$(VER)/tools/cg/Dockerfile"
FILES_CLEANER += "segfault-$(VER)/tools/cg/go.mod"
FILES_CLEANER += "segfault-$(VER)/tools/cg/go.sum"
FILES_CLEANER += "segfault-$(VER)/tools/cg/main.go"
FILES_CLEANER += "segfault-$(VER)/tools/cg/Makefile"
FILES_CLEANER += "segfault-$(VER)/tools/cg/sysinfo_linux.go"
FILES += $(FILES_CLEANER) $(FILES_MASTER) $(FILES_ROOT) $(FILES_GSNC) $(FILES_CONFIG) $(FILES_ROUTER) $(FILES_TOR) $(FILES_ENCFSD) $(FILES_GUEST) $(FILES_HOST) $(FILES_PROVISION)
TARX = $(shell command -v gtar 2>/dev/null)

@ -1,11 +0,0 @@
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

Binary file not shown.

@ -89,15 +89,17 @@ services:
- "${SF_BASEDIR:-.}/sfbin:/sf/bin:ro"
sf-containerguard:
build: cleaner/cg
build: tools/cg
image: sf-containerguard
container_name: sf-containerguard
restart: ${SF_RESTART:-on-failure}
cgroup_parent: sf.slice
pid: "host"
cap_add:
- SYS_PTRACE # access to /proc/<PID>/root/dev/pts/* to send messages to user.
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
- "/var/run/containerd/io.containerd.runtime.v2.task:/var/run/containerd/io.containerd.runtime.v2.task:ro"
- "/var/run/containerd/io.containerd.runtime.v2.task:/var/run/containerd/io.containerd.runtime.v2.task"
- "${SF_BASEDIR:-.}/config:${SF_BASEDIR:-.}/config"
- "/sys/fs/cgroup:/sys/fs/cgroup"

0
cleaner/cg/Makefile → tools/cg/Makefile Executable file → Normal file

BIN
tools/cg/cg.tgz Normal file

Binary file not shown.

@ -15,13 +15,13 @@ require (
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/go-cmp v0.5.8 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.2 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/stretchr/testify v1.8.0 // indirect
github.com/stretchr/testify v1.8.1 // indirect
golang.org/x/mod v0.8.0 // indirect
golang.org/x/net v0.7.0 // indirect
golang.org/x/term v0.5.0 // indirect

@ -19,8 +19,8 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae h1:O4SWKdcHVCvYqyDV+9CJA1fcDN2L11Bule0iFy3YlAI=
@ -41,10 +41,12 @@ github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVs
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=

@ -12,7 +12,6 @@ import (
"path/filepath"
"runtime"
"strconv"
"strings"
"sync"
"time"
@ -37,7 +36,7 @@ var (
debugFlag = flag.Bool("debug", false, "activate debug mode")
)
func init() {
func main() {
flag.Parse()
if *debugFlag {
log.SetLevel(log.DebugLevel)
@ -47,21 +46,12 @@ func init() {
log.SetFormatter(&log.TextFormatter{
ForceColors: true,
})
}
func main() {
hostname, _ := os.Hostname()
log.Infof("ContainerGuard (CG) started protecting [%v]", hostname)
log.Infof("compiled on %v from commit %v", Buildtime, Version)
// docker client
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
if err != nil {
panic(err)
}
log.Debugf("connected to docker client v%v", cli.ClientVersion())
// number of virtual cores
var numCPU = runtime.NumCPU()
// MAX_LOAD defines the maximum amount of `strain` each CPU can have
@ -97,11 +87,20 @@ func main() {
}
log.Warnf("[TRIGGER] load (%.2f) on cpu (%v) higher than max_load (%v)", sysLoad1mAvg(), numCPU, MAX_LOAD)
LAST_LOAD = sysLoad1mAvg()
// docker client
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
if err != nil {
panic(err)
}
log.Debugf("connected to docker client v%v", cli.ClientVersion())
err = stopContainersBasedOnUsage(cli)
if err != nil {
log.Error(err)
}
LAST_LOAD = sysLoad1mAvg()
}
}
@ -139,6 +138,7 @@ func stopContainersBasedOnUsage(cli *client.Client) error {
highestUsage = usage
mu.Unlock()
}
log.Infof("[%v] usage (%.2f%%)", c.Names[0][1:], usage)
}(c)
}
@ -152,20 +152,25 @@ func stopContainersBasedOnUsage(cli *client.Client) error {
intPtr := func(v int) *int { return &v }
var killTimeout = container.StopOptions{
Signal: "SIGTERM",
Timeout: intPtr(10),
Timeout: intPtr(5),
}
var killThreshold = highestUsage * 0.8 // 80% of highestUsage
const action = "STOP (2s) || KILL"
const abuseMessage = "Your server was shut down because it consumed to many resources. If you feel that this was a mistake then please contact us 💙"
const actionMessage = "STOP (5s) || KILL"
const abuseMessage = "Your server was shut down because it consumed too many resources. If you feel that this was a mistake then please contact us 💙"
// stop all containers where usage > `highestUsage` * 0.8
if usage > killThreshold {
log.Warnf("[%v] usage (%.2f%%) > threshold (%.2f%%) | action %v", c.Names[0][1:], usage, killThreshold, action)
if killThreshold < 10.0 {
return fmt.Errorf("StopContainer: operation aborted: threshold too low %.5f", killThreshold)
}
log.Warnf("[%v] usage (%.2f%%) > threshold (%.2f%%) | action %v", c.Names[0][1:], usage, killThreshold, actionMessage)
// message user that he's being abusive
err = sendMessage(c.ID, abuseMessage)
if err != nil {
log.Errorf("unable to message the user: %v", err)
log.Errorf("unable to message container: %v", err)
}
err = printProcs(c.ID, c.Names[0])
@ -187,7 +192,7 @@ func stopContainersBasedOnUsage(cli *client.Client) error {
usage: usage,
threshold: killThreshold,
load: sysLoad1mAvg(),
action: action,
action: actionMessage,
}
if err := logData.save(*resultFlag); err != nil {
log.Error(err)
@ -232,7 +237,7 @@ func sendMessage(cID string, message string) error {
pid, err := os.ReadFile(pidPath)
if err != nil {
return err
return fmt.Errorf("readfile: %v", err)
}
// keeps track of how many FDs we've walked past.
@ -243,14 +248,15 @@ func sendMessage(cID string, message string) error {
return err
}
fdCount++
_, err = strconv.Atoi(d.Name())
if err != nil {
log.Debugf("not a number: %v", path)
log.Debugf("not a number: %v", d.Name())
return nil
}
fdCount++
log.Debugf("MESSAGE to: %v", path)
err = _sendMessage(path, message)
if err != nil {
return err
@ -263,7 +269,7 @@ func sendMessage(cID string, message string) error {
return nil
})
if err != nil {
return err
return fmt.Errorf("walkpath: %v", err)
}
return nil
@ -289,9 +295,10 @@ func _sendMessage(fd, message string) error {
return fmt.Errorf("%v is a symlink! dodging attack...", file.Name())
}
if info.Mode().Type() != os.ModeSocket {
return fmt.Errorf("%v is NOT a socket! dodging attack...", file.Name())
}
// removed this check as pts/0 is not detected as socket and the message won't be sent.
// if info.Mode().Type() != os.ModeSocket {
// return fmt.Errorf("%v is NOT a socket! dodging attack...", file.Name())
// }
if !terminal.IsTerminal(int(file.Fd())) {
return fmt.Errorf("unable to write to %v: not a tty", file.Name())
@ -343,18 +350,20 @@ func (a LogData) save(path string) error {
func sanitize(s string) string {
clean := func(s []byte) string {
j := 0
for _, b := range s {
for i, b := range s {
if b == '\x00' {
s[i] = '\x20'
}
if ('a' <= b && b <= 'z') ||
('A' <= b && b <= 'Z') ||
('0' <= b && b <= '9') ||
b == '#' {
b == '#' || b == ' ' || b == '-' {
s[j] = b
j++
}
}
return string(s[:j])
}
s = strings.ToValidUTF8(s, "#")
s = clean([]byte(s))
return s
}
@ -367,8 +376,14 @@ func printProcs(cid, cname string) error {
if err != nil {
return err
}
var count int
scanner := bufio.NewScanner(file)
for scanner.Scan() {
if count > 12 {
return fmt.Errorf("reached max commands, skipping extra output")
}
count++
cmdline := fmt.Sprintf("/proc/%s/cmdline", scanner.Text())
// limit each command string to max 255 characters
@ -381,14 +396,8 @@ func printProcs(cid, cname string) error {
return err
}
var count int
_scanner := bufio.NewScanner(file)
for _scanner.Scan() {
if count > 128 {
return fmt.Errorf("reached 128 commands, skipping extra output")
}
count++
data := sanitize(_scanner.Text())
log.Warnf("[%s] proc: %v", cname, data)
}

25
tools/cg/main_test.go Normal file

@ -0,0 +1,25 @@
package main
import (
"os"
"testing"
)
func TestSanitize(t *testing.T) {
from := "/dev/urandom"
file, err := os.Open(from)
if err != nil {
t.Fatalf("unable to open file %v", err)
}
defer file.Close()
data := make([]byte, 4096)
n, err := file.Read(data)
if err != nil {
t.Fatalf("unable to read bytes: %v", err)
}
t.Logf("read %v bytes from %v", n, from)
r1 := sanitize(string(data))
t.Logf("result1: %v", r1)
}

14
tools/monitor_ssh/go.mod Normal file

@ -0,0 +1,14 @@
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.6.0
)
require (
github.com/stretchr/testify v1.8.1 // indirect
golang.org/x/sys v0.5.0 // indirect
)

@ -6,16 +6,14 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
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/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
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/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
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=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
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=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=