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 (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"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 {
|
||||
Condition *string `yaml:"condition"`
|
||||
Delay *string `yaml:"delay"`
|
||||
@@ -24,7 +37,9 @@ type ServiceConfig struct {
|
||||
MemLimit *string `yaml:"mem_limit"`
|
||||
Cpus *float64 `yaml:"cpus"`
|
||||
Ports *[]string `yaml:"ports"`
|
||||
User *string `yaml:"user"`
|
||||
Deploy *DeployConfig `yaml:"deploy"`
|
||||
SecOpts *[]string `yaml:"security_opt"`
|
||||
}
|
||||
|
||||
type Compose struct {
|
||||
@@ -38,28 +53,44 @@ func main() {
|
||||
}
|
||||
f, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
fd, err := io.ReadAll(f)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var data Compose
|
||||
err = yaml.Unmarshal(fd, &data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
type report struct {
|
||||
Name string
|
||||
PolicyPresent bool
|
||||
PortSafety bool
|
||||
PortIssues []string
|
||||
ResourceSafety bool
|
||||
Name string //service name
|
||||
|
||||
//restart policy
|
||||
PolicyPresent bool //is it present
|
||||
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
|
||||
ResourceLevel IssueLevel
|
||||
//proper security options
|
||||
SecurityOptSafe bool
|
||||
SecurityOptLevel IssueLevel
|
||||
SecurityOptIssues []string
|
||||
//are users nonroot
|
||||
UserSafe bool
|
||||
UserIssues []string
|
||||
UserIssueLevel IssueLevel
|
||||
}
|
||||
|
||||
var issues []report
|
||||
@@ -71,36 +102,132 @@ func main() {
|
||||
if serviceConf.Ports != nil {
|
||||
matched, err := regexp.Match(`.*:.*:.*`, []byte((*serviceConf.Ports)[0]))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
portMatched, err := regexp.Match(`.*:.*`, []byte((*serviceConf.Ports)[0]))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if !matched {
|
||||
r.PortSafety = false
|
||||
r.PortIssues = append(r.PortIssues, "host port does not have an IP addressed and is fully opened")
|
||||
r.PortSafe = false
|
||||
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 {
|
||||
r.PortSafety = true
|
||||
r.PortSafe = true
|
||||
r.PortLevel = PASSED
|
||||
}
|
||||
} else {
|
||||
r.PortSafety = true
|
||||
r.PortSafe = true
|
||||
r.PortLevel = PASSED
|
||||
}
|
||||
|
||||
r.ResourceSafety = true
|
||||
r.ResourceSafe = true
|
||||
r.ResourceLevel = PASSED
|
||||
if serviceConf.MemLimit == nil {
|
||||
r.ResourceSafety = false
|
||||
r.ResourceIssues = append(r.ResourceIssues, "no memory limit set")
|
||||
r.ResourceSafe = false
|
||||
r.ResourceIssues = append(r.ResourceIssues, "no memory limit has been set for the container")
|
||||
r.ResourceLevel = WARNING
|
||||
}
|
||||
if serviceConf.Cpus == nil {
|
||||
r.ResourceSafety = false
|
||||
r.ResourceIssues = append(r.ResourceIssues, "no cpu limit set")
|
||||
r.ResourceSafe = false
|
||||
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 {
|
||||
r.PolicyPresent = true
|
||||
r.PolicyLevel = PASSED
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
|
||||
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