a lot of progress, WIP

This commit is contained in:
2025-11-11 12:58:06 -05:00
parent b7389117ac
commit 09b33762ec

390
main.go
View File

@@ -16,6 +16,7 @@ const (
ROOT_USER_GROUP = "1000:1000" ROOT_USER_GROUP = "1000:1000"
ROOT_USER = "1000" ROOT_USER = "1000"
ROOT_GROUP = "1000" ROOT_GROUP = "1000"
IM_SO_SORRY = `^(\${{\s*(vars|secrets)\..*}}|[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}):(\${{\s*(vars|secrets)\..*}}|[0-9]+):(\${{\s*(vars|secrets)\..*}}|[0-9]+)`
FATAL IssueLevel = "FATAL" FATAL IssueLevel = "FATAL"
WARNING IssueLevel = "WARNING" WARNING IssueLevel = "WARNING"
@@ -45,14 +46,6 @@ func (i *Issue) Fatal() {
i.Safe = false i.Safe = false
} }
//have functions that take the service configuration and they check certain fields then
//will return an issue if there is one, which we will append.
//then loop through to print
//this should be a struct w methods like Passed() that handles the true/FATAL stuff
//then the printing and stuff can just be loops it doesn't have to be so hardcoded
type RestartConfig struct { type RestartConfig struct {
Condition *string `yaml:"condition"` Condition *string `yaml:"condition"`
Delay *string `yaml:"delay"` Delay *string `yaml:"delay"`
@@ -65,12 +58,13 @@ type DeployConfig struct {
} }
type ServiceConfig struct { 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"` User *string `yaml:"user"`
Deploy *DeployConfig `yaml:"deploy"` Deploy *DeployConfig `yaml:"deploy"`
SecOpts *[]string `yaml:"security_opt"` SecOpts *[]string `yaml:"security_opt"`
Privileged *bool `yaml:"privileged"`
} }
type Compose struct { type Compose struct {
@@ -99,48 +93,44 @@ func main() {
log.Fatal(err) log.Fatal(err)
} }
type report struct { // type report struct {
Name string //service name // Name string //service name
//restart policy // //restart policy
PolicyPresent bool //is it present // PolicyPresent bool //is it present
PolicyLevel IssueLevel //how serious // PolicyLevel IssueLevel //how serious
PolicyIssues []string // PolicyIssues []string
//are ports behind a proxy? // //are ports behind a proxy?
PortSafe bool // PortSafe bool
PortIssues []string // PortIssues []string
PortLevel IssueLevel // PortLevel IssueLevel
//memory limits and cpu // //memory limits and cpu
ResourceSafe bool // ResourceSafe bool
ResourceIssues []string // ResourceIssues []string
ResourceLevel IssueLevel // ResourceLevel IssueLevel
//proper security options // //proper security options
SecurityOptSafe bool // SecurityOptSafe bool
SecurityOptLevel IssueLevel // SecurityOptLevel IssueLevel
SecurityOptIssues []string // SecurityOptIssues []string
//are users nonroot // //are users nonroot
UserSafe bool // UserSafe bool
UserIssues []string // UserIssues []string
UserIssueLevel IssueLevel // UserIssueLevel IssueLevel
} // }
type NewReport struct { type NewReport struct {
Name string Name string
Issues []*Issue Issues []*Issue
} }
// var issues []report
var issues []NewReport var issues []NewReport
for name, serviceConf := range data.Services { for name, serviceConf := range data.Services {
// var i NewReport
rprt := NewReport{ rprt := NewReport{
Name: name, Name: name,
} }
// i.Name = name
// _ = serviceConf
i := ResourceCheck(serviceConf) i := ResourceCheck(serviceConf)
if i != nil { if i != nil {
// issues = append(issues, NewReport{Name: name, Issues: []*Issue{i}})
rprt.Issues = append(rprt.Issues, i) rprt.Issues = append(rprt.Issues, i)
} }
@@ -157,130 +147,20 @@ func main() {
rprt.Issues = append(rprt.Issues, k) rprt.Issues = append(rprt.Issues, k)
} }
l, err := PortCheck(serviceConf)
if err != nil {
log.Fatal(err)
}
if len(l) > 0 {
rprt.Issues = append(rprt.Issues, l...)
}
m := PolicyCheck(serviceConf)
if m != nil {
rprt.Issues = append(rprt.Issues, m)
}
issues = append(issues, rprt) issues = append(issues, rprt)
// if serviceConf.MemLimit == nil {
// i.Issues = append(i.Issues, Issue{
// Level: WARNING,
// Safe: true,
// })
// // 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.ResourceSafe = false
// r.ResourceIssues = append(r.ResourceIssues, "no cpu limit set for the container")
// r.ResourceLevel = WARNING
// }
// r := report{
// Name: name,
// }
// if serviceConf.Ports != nil {
// matched, err := regexp.Match(`.*:.*:.*`, []byte((*serviceConf.Ports)[0]))
// if err != nil {
// log.Fatal(err)
// }
// portMatched, err := regexp.Match(`.*:.*`, []byte((*serviceConf.Ports)[0]))
// if err != nil {
// log.Fatal(err)
// }
// if !matched {
// 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.PortSafe = true
// r.PortLevel = PASSED
// }
// } else {
// r.PortSafe = true
// r.PortLevel = PASSED
// }
// r.ResourceSafe = true
// r.ResourceLevel = PASSED
// if serviceConf.MemLimit == nil {
// 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.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, "no user is set in the compose file")
// r.UserIssueLevel = WARNING
// }
// issues = append(issues, r)
} }
for _, p := range issues { for _, p := range issues {
@@ -289,30 +169,6 @@ func main() {
fmt.Println(*x) fmt.Println(*x)
} }
} }
// 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"))
// }
} }
func ResourceCheck(srv ServiceConfig) *Issue { func ResourceCheck(srv ServiceConfig) *Issue {
@@ -333,6 +189,26 @@ func ResourceCheck(srv ServiceConfig) *Issue {
} }
i.Passed() i.Passed()
i.Messages = append(i.Messages, "resource limits configuration is safe")
return i
}
func PrivilegedCheck(srv ServiceConfig) *Issue {
i := &Issue{}
if srv.Privileged == nil {
i.Passed()
i.Messages = append(i.Messages, "no privileged flag set")
return i
}
if *srv.Privileged == true {
i.Fatal()
i.Messages = append(i.Messages, "privileged flag is set to true")
return i
}
i.Passed()
i.Messages = append(i.Messages, "privileged flag is set to false")
return i return i
} }
@@ -347,6 +223,7 @@ func SecurityOptCheck(srv ServiceConfig) *Issue {
for _, opt := range *srv.SecOpts { for _, opt := range *srv.SecOpts {
if strings.EqualFold(opt, PRIVILEGE_OPT) { if strings.EqualFold(opt, PRIVILEGE_OPT) {
i.Passed() i.Passed()
i.Messages = append(i.Messages, "security option are safe")
return i return i
} }
} }
@@ -356,6 +233,51 @@ func SecurityOptCheck(srv ServiceConfig) *Issue {
return i return i
} }
func PortCheck(srv ServiceConfig) ([]*Issue, error) {
il := []*Issue{}
if srv.Ports == nil {
i := &Issue{}
i.Passed()
i.Messages = append(i.Messages, "no ports exposed")
il = append(il, i)
return il, nil
}
for _, prt := range *srv.Ports {
i := &Issue{}
m, err := regexp.Match(`^[0-9]*:[0-9]*`, []byte(prt))
if err != nil {
return nil, err
}
if m {
i.Fatal()
i.Messages = append(i.Messages, fmt.Sprintf("%s is fully exposed", prt))
il = append(il, i)
continue
}
m2, err := regexp.Match(IM_SO_SORRY, []byte(prt))
if err != nil {
return nil, err
}
if m2 {
i.Passed()
i.Messages = append(i.Messages, fmt.Sprintf("%s is safe", prt))
il = append(il, i)
continue
} else {
i.Warning()
i.Messages = append(i.Messages, fmt.Sprintf("unable to parse port configuration for %s", prt))
il = append(il, i)
continue
}
}
return il, nil
}
func UserCheck(srv ServiceConfig) (*Issue, error) { func UserCheck(srv ServiceConfig) (*Issue, error) {
i := &Issue{} i := &Issue{}
if srv.User == nil { if srv.User == nil {
@@ -364,7 +286,7 @@ func UserCheck(srv ServiceConfig) (*Issue, error) {
return i, nil return i, nil
} }
mtch, err := regexp.Match("[0-9].*:[0-9].*", []byte(*srv.User)) mtch, err := regexp.Match("[0-9]*:[0-9]*", []byte(*srv.User))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -375,6 +297,7 @@ func UserCheck(srv ServiceConfig) (*Issue, error) {
i.Messages = append(i.Messages, "a root user set in the compose file") i.Messages = append(i.Messages, "a root user set in the compose file")
} else { } else {
i.Passed() i.Passed()
i.Messages = append(i.Messages, "user configuration is safe")
} }
return i, nil return i, nil
} else { } else {
@@ -396,6 +319,7 @@ func UserCheck(srv ServiceConfig) (*Issue, error) {
if leftUser && rightUser { if leftUser && rightUser {
i.Passed() i.Passed()
i.Messages = append(i.Messages, "user configuration is safe")
return i, nil return i, nil
} }
@@ -405,6 +329,7 @@ func UserCheck(srv ServiceConfig) (*Issue, error) {
i.Messages = append(i.Messages, "a root user is present in configuration") i.Messages = append(i.Messages, "a root user is present in configuration")
} else { } else {
i.Passed() i.Passed()
i.Messages = append(i.Messages, "user configuration is safe")
} }
return i, nil return i, nil
} }
@@ -415,6 +340,7 @@ func UserCheck(srv ServiceConfig) (*Issue, error) {
i.Messages = append(i.Messages, "a root user is present in configuration") i.Messages = append(i.Messages, "a root user is present in configuration")
} else { } else {
i.Passed() i.Passed()
i.Messages = append(i.Messages, "user configuration is safe")
} }
return i, nil return i, nil
} }
@@ -425,59 +351,33 @@ func UserCheck(srv ServiceConfig) (*Issue, error) {
return i, nil return i, nil
} }
// func PortCheck(srv ServiceConfig) (*Issue, error) { func PolicyCheck(srv ServiceConfig) *Issue {
// i := &Issue{} i := &Issue{}
// if srv.Ports == nil { if srv.Deploy != nil {
// i.Passed() if srv.Deploy.Policy != nil {
// return i, nil if srv.Deploy.Policy.Condition != nil {
// } if srv.Deploy.Policy.MaxAttempts != nil {
i.Passed()
i.Messages = append(i.Messages, "restart policy configuration is safe")
return i
} else {
i.Warning()
i.Messages = append(i.Messages, "no max_attempts value set on restart policy container could death loop")
return i
}
} else {
i.Fatal()
i.Messages = append(i.Messages, "no restart condition is set on the container")
return i
}
} else {
i.Fatal()
i.Messages = append(i.Messages, "no restart policy is set on the container")
return i
}
}
// for _, p := range *srv.Ports { i.Fatal()
// mtc, err := regexp.Match(`.*:.*:.*`, []byte(p)) i.Messages = append(i.Messages, "no restart policy is set on the container")
// if err != nil { return i
// return nil, err }
// }
// if mtc {
// continue
// }
// pmtc, err := regexp.Match(`.*:.*`, []byte(p))
// if err != nil {
// return nil, err
// }
// if pmtc {
// i.Warning()
// i.Messages = append(i.Messages, "ports are fully exposed, use an IP for the host port to move behind the proxy")
// }
// }
// // if serviceConf.Ports != nil {
// // matched, err := regexp.Match(`.*:.*:.*`, []byte((*serviceConf.Ports)[0]))
// // if err != nil {
// // log.Fatal(err)
// // }
// // portMatched, err := regexp.Match(`.*:.*`, []byte((*serviceConf.Ports)[0]))
// // if err != nil {
// // log.Fatal(err)
// // }
// // if !matched {
// // 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.PortSafe = true
// // r.PortLevel = PASSED
// // }
// // } else {
// // r.PortSafe = true
// // r.PortLevel = PASSED
// // }
// }