made progress
- better printing - more checks - tests
This commit is contained in:
165
main.go
165
main.go
@@ -3,12 +3,25 @@ package main
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PRIVILEGE_OPT = "no-new-privileges=true"
|
||||||
|
ROOT_USER = "1000:1000"
|
||||||
|
|
||||||
|
FATAL IssueLevel = "FATAL"
|
||||||
|
WARNING IssueLevel = "WARNING"
|
||||||
|
PASSED IssueLevel = "PASSED"
|
||||||
|
)
|
||||||
|
|
||||||
|
type IssueLevel string
|
||||||
|
|
||||||
type RestartConfig struct {
|
type RestartConfig struct {
|
||||||
Condition *string `yaml:"condition"`
|
Condition *string `yaml:"condition"`
|
||||||
Delay *string `yaml:"delay"`
|
Delay *string `yaml:"delay"`
|
||||||
@@ -24,7 +37,9 @@ type ServiceConfig struct {
|
|||||||
MemLimit *string `yaml:"mem_limit"`
|
MemLimit *string `yaml:"mem_limit"`
|
||||||
Cpus *float64 `yaml:"cpus"`
|
Cpus *float64 `yaml:"cpus"`
|
||||||
Ports *[]string `yaml:"ports"`
|
Ports *[]string `yaml:"ports"`
|
||||||
|
User *string `yaml:"user"`
|
||||||
Deploy *DeployConfig `yaml:"deploy"`
|
Deploy *DeployConfig `yaml:"deploy"`
|
||||||
|
SecOpts *[]string `yaml:"security_opt"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Compose struct {
|
type Compose struct {
|
||||||
@@ -38,28 +53,44 @@ func main() {
|
|||||||
}
|
}
|
||||||
f, err := os.Open(filePath)
|
f, err := os.Open(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
fd, err := io.ReadAll(f)
|
fd, err := io.ReadAll(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var data Compose
|
var data Compose
|
||||||
err = yaml.Unmarshal(fd, &data)
|
err = yaml.Unmarshal(fd, &data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
type report struct {
|
type report struct {
|
||||||
Name string
|
Name string //service name
|
||||||
PolicyPresent bool
|
|
||||||
PortSafety bool
|
//restart policy
|
||||||
PortIssues []string
|
PolicyPresent bool //is it present
|
||||||
ResourceSafety bool
|
PolicyLevel IssueLevel //how serious
|
||||||
|
PolicyIssues []string
|
||||||
|
//are ports behind a proxy?
|
||||||
|
PortSafe bool
|
||||||
|
PortIssues []string
|
||||||
|
PortLevel IssueLevel
|
||||||
|
//memory limits and cpu
|
||||||
|
ResourceSafe bool
|
||||||
ResourceIssues []string
|
ResourceIssues []string
|
||||||
|
ResourceLevel IssueLevel
|
||||||
|
//proper security options
|
||||||
|
SecurityOptSafe bool
|
||||||
|
SecurityOptLevel IssueLevel
|
||||||
|
SecurityOptIssues []string
|
||||||
|
//are users nonroot
|
||||||
|
UserSafe bool
|
||||||
|
UserIssues []string
|
||||||
|
UserIssueLevel IssueLevel
|
||||||
}
|
}
|
||||||
|
|
||||||
var issues []report
|
var issues []report
|
||||||
@@ -71,36 +102,132 @@ func main() {
|
|||||||
if serviceConf.Ports != nil {
|
if serviceConf.Ports != nil {
|
||||||
matched, err := regexp.Match(`.*:.*:.*`, []byte((*serviceConf.Ports)[0]))
|
matched, err := regexp.Match(`.*:.*:.*`, []byte((*serviceConf.Ports)[0]))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
portMatched, err := regexp.Match(`.*:.*`, []byte((*serviceConf.Ports)[0]))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !matched {
|
if !matched {
|
||||||
r.PortSafety = false
|
r.PortSafe = false
|
||||||
r.PortIssues = append(r.PortIssues, "host port does not have an IP addressed and is fully opened")
|
r.PortIssues = append(r.PortIssues, "host port is fully exposed, consider adding an IP address to keep it behind the proxy")
|
||||||
|
r.PortLevel = WARNING
|
||||||
|
if !portMatched {
|
||||||
|
r.PortIssues = append(r.PortIssues, "ports seem misconfigured validate ports in compose file")
|
||||||
|
r.PortLevel = FATAL
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
r.PortSafety = true
|
r.PortSafe = true
|
||||||
|
r.PortLevel = PASSED
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
r.PortSafety = true
|
r.PortSafe = true
|
||||||
|
r.PortLevel = PASSED
|
||||||
}
|
}
|
||||||
|
|
||||||
r.ResourceSafety = true
|
r.ResourceSafe = true
|
||||||
|
r.ResourceLevel = PASSED
|
||||||
if serviceConf.MemLimit == nil {
|
if serviceConf.MemLimit == nil {
|
||||||
r.ResourceSafety = false
|
r.ResourceSafe = false
|
||||||
r.ResourceIssues = append(r.ResourceIssues, "no memory limit set")
|
r.ResourceIssues = append(r.ResourceIssues, "no memory limit has been set for the container")
|
||||||
|
r.ResourceLevel = WARNING
|
||||||
}
|
}
|
||||||
if serviceConf.Cpus == nil {
|
if serviceConf.Cpus == nil {
|
||||||
r.ResourceSafety = false
|
r.ResourceSafe = false
|
||||||
r.ResourceIssues = append(r.ResourceIssues, "no cpu limit set")
|
r.ResourceIssues = append(r.ResourceIssues, "no cpu limit set for the container")
|
||||||
|
r.ResourceLevel = WARNING
|
||||||
|
}
|
||||||
|
|
||||||
|
if serviceConf.Cpus == nil && serviceConf.MemLimit == nil {
|
||||||
|
r.ResourceLevel = FATAL
|
||||||
|
r.ResourceIssues = append(r.ResourceIssues, "no resource limits set, its the wild west")
|
||||||
}
|
}
|
||||||
|
|
||||||
if serviceConf.Deploy != nil && serviceConf.Deploy.Policy != nil {
|
if serviceConf.Deploy != nil && serviceConf.Deploy.Policy != nil {
|
||||||
r.PolicyPresent = true
|
r.PolicyPresent = true
|
||||||
|
r.PolicyLevel = PASSED
|
||||||
} else {
|
} else {
|
||||||
r.PolicyPresent = false
|
r.PolicyPresent = false
|
||||||
|
r.PolicyLevel = WARNING
|
||||||
|
//this should check fields bettera and suggest better conditions
|
||||||
|
r.PolicyIssues = append(r.PolicyIssues, "set a restart policy for the container")
|
||||||
|
}
|
||||||
|
|
||||||
|
r.SecurityOptSafe = false
|
||||||
|
if serviceConf.SecOpts != nil {
|
||||||
|
for _, opt := range *serviceConf.SecOpts {
|
||||||
|
if opt == PRIVILEGE_OPT {
|
||||||
|
r.SecurityOptSafe = true
|
||||||
|
r.SecurityOptLevel = PASSED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !r.SecurityOptSafe {
|
||||||
|
r.SecurityOptLevel = FATAL
|
||||||
|
r.SecurityOptIssues = append(r.SecurityOptIssues, "set no-new-privileges=true on the container security_opts")
|
||||||
|
}
|
||||||
|
|
||||||
|
r.UserSafe = true
|
||||||
|
r.UserIssueLevel = PASSED
|
||||||
|
if serviceConf.User != nil {
|
||||||
|
matchnum, err := regexp.Match("[0-9].*:[0-9].*", []byte(*serviceConf.User))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if matchnum {
|
||||||
|
if strings.EqualFold(*serviceConf.User, ROOT_USER) {
|
||||||
|
r.UserSafe = false
|
||||||
|
r.UserIssueLevel = FATAL
|
||||||
|
r.UserIssues = append(r.UserIssues, "a root user is set in the compose file")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match, err := regexp.Match(".*:.*", []byte(*serviceConf.User))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !match {
|
||||||
|
r.UserIssues = append(r.UserIssues, "user configuration seems off, check compose file")
|
||||||
|
r.UserIssueLevel = WARNING
|
||||||
|
r.UserSafe = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
r.UserSafe = false
|
||||||
|
r.UserIssues = append(r.UserIssues, "a nonroot user should be set")
|
||||||
|
r.UserIssueLevel = FATAL
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%+v\n", r)
|
|
||||||
issues = append(issues, r)
|
issues = append(issues, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, p := range issues {
|
||||||
|
fmt.Println("##########################################")
|
||||||
|
fmt.Println(p.Name)
|
||||||
|
fmt.Println("------------------------------------------")
|
||||||
|
fmt.Println("[SECURITY_OPTS]")
|
||||||
|
fmt.Println(p.SecurityOptLevel)
|
||||||
|
fmt.Printf("safe: %t\n\n", p.SecurityOptSafe)
|
||||||
|
fmt.Println(strings.Join(p.SecurityOptIssues, "\n"))
|
||||||
|
fmt.Println("\n[USER]")
|
||||||
|
fmt.Println(p.UserIssueLevel)
|
||||||
|
fmt.Printf("safe: %t\n\n", p.UserSafe)
|
||||||
|
fmt.Println(strings.Join(p.UserIssues, "\n"))
|
||||||
|
fmt.Println("\n[RESOURCE]")
|
||||||
|
fmt.Println(p.ResourceLevel)
|
||||||
|
fmt.Printf("safe: %t\n\n", p.ResourceSafe)
|
||||||
|
fmt.Println(strings.Join(p.ResourceIssues, "\n"))
|
||||||
|
fmt.Println("\n[PORT]")
|
||||||
|
fmt.Println(p.PortLevel)
|
||||||
|
fmt.Printf("safe: %t\n\n", p.PortSafe)
|
||||||
|
fmt.Println(strings.Join(p.PortIssues, "\n"))
|
||||||
|
fmt.Println("\n[POLICY]")
|
||||||
|
fmt.Println(p.PolicyLevel)
|
||||||
|
fmt.Printf("safe: %t\n\n", p.PolicyPresent)
|
||||||
|
fmt.Println(strings.Join(p.PolicyIssues, "\n"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user