mirror of
https://github.com/hackerschoice/segfault.git
synced 2024-06-30 18:51:22 +00:00
Merge pull request #33 from theaog/kc-kill-container-tool
feat: add ContainerGuard tool
This commit is contained in:
commit
5a6118345a
2
cleaner/cg/.gitignore
vendored
Normal file
2
cleaner/cg/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
cg
|
||||
todo.md
|
9
cleaner/cg/Dockerfile
Normal file
9
cleaner/cg/Dockerfile
Normal file
@ -0,0 +1,9 @@
|
||||
FROM golang:1.19 as BUILD
|
||||
WORKDIR /app
|
||||
COPY *.go go.mod go.sum /app/
|
||||
RUN go mod tidy && go mod vendor
|
||||
RUN CGO_ENABLED=0 go build -ldflags='-s -w' -o cg
|
||||
|
||||
FROM scratch
|
||||
COPY --from=BUILD /app/cg /app/cg
|
||||
ENTRYPOINT ["/app/cg"]
|
21
cleaner/cg/Makefile
Normal file
21
cleaner/cg/Makefile
Normal file
@ -0,0 +1,21 @@
|
||||
build: pre
|
||||
GOOS=linux GOARCH=amd64 go build
|
||||
|
||||
upload:
|
||||
scp -P64222 cg root@bh.segfault.net:/usr/bin/cg.new
|
||||
ssh -p64222 root@bh.segfault.net mv /usr/bin/cg.new /usr/bin/cg
|
||||
|
||||
build-arm: pre
|
||||
GOOS=linux GOARCH=arm go build -o cg-arm
|
||||
|
||||
pre:
|
||||
go mod tidy
|
||||
|
||||
docker:
|
||||
docker built -t cg .
|
||||
docker run -it --rm cg
|
||||
|
||||
release: build
|
||||
sha256sum cg | tee cg.sum
|
||||
tar czvf cg.tgz cg cg.sum
|
||||
rm -f cg cg.sum
|
BIN
cleaner/cg/cg.tgz
Normal file
BIN
cleaner/cg/cg.tgz
Normal file
Binary file not shown.
29
cleaner/cg/go.mod
Normal file
29
cleaner/cg/go.mod
Normal file
@ -0,0 +1,29 @@
|
||||
module cg
|
||||
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/docker/docker v20.10.20+incompatible
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
golang.org/x/sys v0.1.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Microsoft/go-winio v0.6.0 // indirect
|
||||
github.com/docker/distribution v2.8.1+incompatible // indirect
|
||||
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/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
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b // indirect
|
||||
golang.org/x/time v0.1.0 // indirect
|
||||
golang.org/x/tools v0.1.12 // indirect
|
||||
gotest.tools/v3 v3.4.0 // indirect
|
||||
)
|
97
cleaner/cg/go.sum
Normal file
97
cleaner/cg/go.sum
Normal file
@ -0,0 +1,97 @@
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg=
|
||||
github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
|
||||
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
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/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
|
||||
github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v20.10.20+incompatible h1:kH9tx6XO+359d+iAkumyKDc5Q1kOwPuAUaeri48nD6E=
|
||||
github.com/docker/docker v20.10.20+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
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/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=
|
||||
github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw=
|
||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
|
||||
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
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/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/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/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=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY=
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA=
|
||||
golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
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=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
|
||||
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
|
||||
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
|
271
cleaner/cg/main.go
Normal file
271
cleaner/cg/main.go
Normal file
@ -0,0 +1,271 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/sirupsen/logrus"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if *debugFlag {
|
||||
log.SetLevel(log.DebugLevel)
|
||||
log.SetReportCaller(true)
|
||||
}
|
||||
log.SetFormatter(&logrus.TextFormatter{
|
||||
ForceColors: true,
|
||||
})
|
||||
}
|
||||
|
||||
// CLI flags
|
||||
var (
|
||||
strainFlag = flag.Float64("strain", 20, "maximum amount of strain per CPU core")
|
||||
pathFlag = flag.String("path", "/sf/config/db/cg", "directory path where action logs are stored")
|
||||
debugFlag = flag.Bool("debug", false, "activate debug mode")
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
log.Infof("ContainerGuard (CG) started protecting your Segfault.Net instance...")
|
||||
|
||||
// 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
|
||||
// before triggering our cleanup tasks.
|
||||
var MAX_LOAD = *strainFlag * float64(numCPU)
|
||||
// last recorded loadavg after a trigger event
|
||||
var LAST_LOAD float64 // default value 0.0
|
||||
var loop_time_s = 5
|
||||
|
||||
var count int
|
||||
for range time.Tick(time.Second * time.Duration(loop_time_s)) {
|
||||
|
||||
// protect legitimate users
|
||||
if LAST_LOAD != 0.0 { // we got a trigger event
|
||||
// after 60s stop protecting
|
||||
if count > 60/loop_time_s {
|
||||
LAST_LOAD = 0.0
|
||||
count = 0
|
||||
continue
|
||||
}
|
||||
|
||||
if sysLoad1mAvg() <= LAST_LOAD {
|
||||
LAST_LOAD = sysLoad1mAvg()
|
||||
count++
|
||||
continue
|
||||
}
|
||||
|
||||
// if load doesn't go down every 5s
|
||||
LAST_LOAD = 0.0 // reset
|
||||
}
|
||||
|
||||
if sysLoad1mAvg() <= MAX_LOAD {
|
||||
continue
|
||||
}
|
||||
|
||||
log.Warnf("[TRIGGER] load (%.2f) on cpu (%v) higher than max_load (%v)", sysLoad1mAvg(), numCPU, MAX_LOAD)
|
||||
LAST_LOAD = sysLoad1mAvg()
|
||||
err = stopContainersBasedOnUsage(cli)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// stopContainersBasedOnUsage iterates through all the containers on the system
|
||||
// to find abusive ones and stops them, but only if their name starts w/ lg-*
|
||||
func stopContainersBasedOnUsage(cli *client.Client) error {
|
||||
const filterPrefix = "/lg-*"
|
||||
opts := types.ContainerListOptions{}
|
||||
opts.All = false // list only running containers
|
||||
opts.Filters = filters.NewArgs()
|
||||
opts.Filters.Add("name", filterPrefix)
|
||||
|
||||
ctx := context.Background()
|
||||
list, err := cli.ContainerList(ctx, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// mu protects `_largestUsage`
|
||||
var mu sync.Mutex
|
||||
var highestUsage float64
|
||||
|
||||
// used to synchronize goroutines
|
||||
var wg = &sync.WaitGroup{}
|
||||
|
||||
// check all containers usage and keep largest value in `largestUsage` var
|
||||
for _, c := range list {
|
||||
wg.Add(1)
|
||||
go func(c types.Container) {
|
||||
defer wg.Done()
|
||||
|
||||
usage := containerUsage(cli, c.ID)
|
||||
if usage > highestUsage {
|
||||
mu.Lock()
|
||||
highestUsage = usage
|
||||
mu.Unlock()
|
||||
}
|
||||
log.Infof("[%v] usage (%.2f%%)", c.Names[0][1:], usage)
|
||||
}(c)
|
||||
}
|
||||
wg.Wait()
|
||||
log.Infof("[HIGHEST USAGE] %.2f%%", highestUsage)
|
||||
|
||||
for _, c := range list {
|
||||
wg.Add(1)
|
||||
go func(c types.Container) {
|
||||
defer wg.Done()
|
||||
|
||||
usage := containerUsage(cli, c.ID)
|
||||
log.Debugf("allowed to kill %v with usage %v", c.Names[0], usage)
|
||||
|
||||
var killTimeout = time.Second * 2
|
||||
var killThreshold = highestUsage * 0.8
|
||||
const action = "STOP in 2s or KILL"
|
||||
|
||||
// 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)
|
||||
|
||||
ctx := context.Background()
|
||||
err := cli.ContainerStop(ctx, c.ID, &killTimeout)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// log stopped containers to disk
|
||||
logData := LogData{
|
||||
name: c.Names[0],
|
||||
usage: usage,
|
||||
threshold: killThreshold,
|
||||
load: sysLoad1mAvg(),
|
||||
action: action,
|
||||
}
|
||||
if err := logData.save(*pathFlag); err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
}(c)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// containerUsage calculates the CPU usage of a container.
|
||||
func containerUsage(cli *client.Client, cID string) float64 {
|
||||
ctx := context.Background()
|
||||
stats, err := cli.ContainerStats(ctx, cID, false)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return 0
|
||||
}
|
||||
defer stats.Body.Close()
|
||||
|
||||
var result ContainerStatsData
|
||||
err = json.NewDecoder(stats.Body).Decode(&result)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
b, _ := ioutil.ReadAll(stats.Body)
|
||||
log.Error(b)
|
||||
}
|
||||
|
||||
// https://github.com/docker/cli/blob/53f8ed4bec07084db4208f55987a2ea94b7f01d6/cli/command/container/stats_helpers.go#L166
|
||||
// calculations
|
||||
cpu_delta := float64(result.CPUStats.CPUUsage.TotalUsage) - float64(result.PrecpuStats.CPUUsage.TotalUsage)
|
||||
system_cpu_delta := result.CPUStats.SystemCPUUsage - result.PrecpuStats.SystemCPUUsage
|
||||
number_cpus := result.CPUStats.OnlineCpus
|
||||
usage := (float64(cpu_delta) / float64(system_cpu_delta)) * float64(number_cpus) * 100.0
|
||||
|
||||
return usage
|
||||
}
|
||||
|
||||
type LogData struct {
|
||||
name string
|
||||
usage float64
|
||||
threshold float64
|
||||
load float64
|
||||
action string
|
||||
}
|
||||
|
||||
// run mkdir only once
|
||||
var mkdirOnce = sync.Once{}
|
||||
|
||||
func (a LogData) save(path string) error {
|
||||
|
||||
var err error
|
||||
mkdirOnce.Do(func() {
|
||||
err = os.MkdirAll(path, 0770)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t := time.Now().UTC().Unix()
|
||||
// example: 1666389757 usage=95.71 threshold=28.61 load=200.23 action=SIGKILL
|
||||
data := fmt.Sprintf("%v usage=%.2f threshold=%.2f load=%.2f action=%s ", t, a.usage, a.threshold, a.load, a.action)
|
||||
filePath := filepath.Join(path, a.name+".txt")
|
||||
|
||||
if err := os.WriteFile(filePath, []byte(data), 0660); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("[LOG FILE] %v", filePath)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type ContainerStatsData struct {
|
||||
CPUStats struct {
|
||||
CPUUsage struct {
|
||||
UsageInUsermode int `json:"usage_in_usermode"`
|
||||
TotalUsage int `json:"total_usage"`
|
||||
UsageInKernelmode int `json:"usage_in_kernelmode"`
|
||||
} `json:"cpu_usage"`
|
||||
SystemCPUUsage int `json:"system_cpu_usage"`
|
||||
OnlineCpus int `json:"online_cpus"`
|
||||
ThrottlingData struct {
|
||||
Periods int `json:"periods"`
|
||||
ThrottledPeriods int `json:"throttled_periods"`
|
||||
ThrottledTime int `json:"throttled_time"`
|
||||
} `json:"throttling_data"`
|
||||
} `json:"cpu_stats"`
|
||||
PrecpuStats struct {
|
||||
CPUUsage struct {
|
||||
PercpuUsage []int `json:"percpu_usage"`
|
||||
UsageInUsermode int `json:"usage_in_usermode"`
|
||||
TotalUsage int `json:"total_usage"`
|
||||
UsageInKernelmode int `json:"usage_in_kernelmode"`
|
||||
} `json:"cpu_usage"`
|
||||
SystemCPUUsage int `json:"system_cpu_usage"`
|
||||
OnlineCpus int `json:"online_cpus"`
|
||||
ThrottlingData struct {
|
||||
Periods int `json:"periods"`
|
||||
ThrottledPeriods int `json:"throttled_periods"`
|
||||
ThrottledTime int `json:"throttled_time"`
|
||||
} `json:"throttling_data"`
|
||||
} `json:"precpu_stats"`
|
||||
}
|
10
cleaner/cg/readme.md
Normal file
10
cleaner/cg/readme.md
Normal file
@ -0,0 +1,10 @@
|
||||
# CG stands for ContainerGuard
|
||||
|
||||
### What is this for?
|
||||
it's a container guard(monitoring) service aimed at identifying and stopping abuse
|
||||
|
||||
### What does it do?
|
||||
atm, it only monitors container CPU usage and kills misbehaving containers
|
||||
|
||||
### What will it do in the future?
|
||||
all sort of guarding(monitoring) and punishment behaviors
|
17
cleaner/cg/sysinfo_linux.go
Normal file
17
cleaner/cg/sysinfo_linux.go
Normal file
@ -0,0 +1,17 @@
|
||||
//go:build linux
|
||||
|
||||
// build +linux
|
||||
|
||||
package main
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
func sysLoad1mAvg() float64 {
|
||||
var info unix.Sysinfo_t
|
||||
unix.Sysinfo(&info)
|
||||
|
||||
const si_load_shift = 16
|
||||
load := float64(info.Loads[0]) / float64(1<<si_load_shift)
|
||||
|
||||
return load
|
||||
}
|
@ -66,6 +66,15 @@ services:
|
||||
- "/var/run/docker.sock:/var/run/docker.sock"
|
||||
- "${SF_BASEDIR:-.}/sfbin:/sf/bin:ro"
|
||||
|
||||
sf-containerguard:
|
||||
build: cleaner/cg
|
||||
image: sf-containerguard
|
||||
container_name: sf-containerguard
|
||||
restart: ${SF_RESTART:-on-failure}
|
||||
volumes:
|
||||
- "/var/run/docker.sock:/var/run/docker.sock"
|
||||
- ${SF_BASEDIR:-.}/config:${SF_BASEDIR:-.}/config
|
||||
|
||||
sf-portd:
|
||||
build: encfsd
|
||||
image: sf-encfsd
|
||||
@ -262,7 +271,7 @@ services:
|
||||
vpn-net:
|
||||
ipv4_address: 172.20.0.111
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
- NET_ADMIN
|
||||
restart: ${SF_RESTART:-on-failure}
|
||||
dns: 172.20.0.53
|
||||
depends_on:
|
||||
@ -407,4 +416,3 @@ networks:
|
||||
config:
|
||||
- subnet: 10.11.0.0/16
|
||||
# default gw is always 10.11.0.1 and is the host side of the bridge (?)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user