|
|
|
@@ -21,7 +21,8 @@ const (
|
|
|
|
ROOT_USER = "1000"
|
|
|
|
ROOT_USER = "1000"
|
|
|
|
ROOT_GROUP = "1000"
|
|
|
|
ROOT_GROUP = "1000"
|
|
|
|
//this is an insane regex to detect IP:PORT:PORT in port configuration but also supports the ability to detect secrets.* and vars.* from workflows
|
|
|
|
//this is an insane regex to detect IP:PORT:PORT in port configuration but also supports the ability to detect secrets.* and vars.* from workflows
|
|
|
|
IM_SO_SORRY = `^(\${{\s*(vars|secrets)\.[[:alnum:]]+\s*}}|[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}){1}:(\${{\s*(vars|secrets)\.[[:alnum:]]+\s*}}|[0-9]+){1}:(\${{\s*(vars|secrets)\.[[:alnum:]]+\s*}}|[0-9]+){1}$`
|
|
|
|
IP_PORT_PORT_REGEX = `^(\${\s*\w+\s*}|[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}){1}:(\${\s*\w+\s*}|[0-9]+){1}:(\${\s*\w+\s*}|[0-9]+){1}$`
|
|
|
|
|
|
|
|
ENV_PORT_PORT = `^\${\s*\w+\s*}{1}:\${\s*\w+\s*}{1}$`
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
func main() {
|
|
|
|
@@ -57,17 +58,17 @@ func main() {
|
|
|
|
Name: name,
|
|
|
|
Name: name,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
i := ResourceCheck(serviceConf)
|
|
|
|
i := ResourceCheck(&serviceConf)
|
|
|
|
if i != nil {
|
|
|
|
if i != nil {
|
|
|
|
rprt.Issues = append(rprt.Issues, i)
|
|
|
|
rprt.Issues = append(rprt.Issues, i)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
j := SecurityOptCheck(serviceConf)
|
|
|
|
j := SecurityOptCheck(&serviceConf)
|
|
|
|
if j != nil {
|
|
|
|
if j != nil {
|
|
|
|
rprt.Issues = append(rprt.Issues, j)
|
|
|
|
rprt.Issues = append(rprt.Issues, j)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
k, err := UserCheck(serviceConf)
|
|
|
|
k, err := UserCheck(&serviceConf)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -75,7 +76,7 @@ func main() {
|
|
|
|
rprt.Issues = append(rprt.Issues, k)
|
|
|
|
rprt.Issues = append(rprt.Issues, k)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
l, err := PortCheck(serviceConf)
|
|
|
|
l, err := PortCheck(&serviceConf)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -83,12 +84,12 @@ func main() {
|
|
|
|
rprt.Issues = append(rprt.Issues, l...)
|
|
|
|
rprt.Issues = append(rprt.Issues, l...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
m := PolicyCheck(serviceConf)
|
|
|
|
m := PolicyCheck(&serviceConf)
|
|
|
|
if m != nil {
|
|
|
|
if m != nil {
|
|
|
|
rprt.Issues = append(rprt.Issues, m)
|
|
|
|
rprt.Issues = append(rprt.Issues, m)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
n := PrivilegedCheck(serviceConf)
|
|
|
|
n := PrivilegedCheck(&serviceConf)
|
|
|
|
if n != nil {
|
|
|
|
if n != nil {
|
|
|
|
rprt.Issues = append(rprt.Issues, n)
|
|
|
|
rprt.Issues = append(rprt.Issues, n)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -96,13 +97,16 @@ func main() {
|
|
|
|
issues = append(issues, rprt)
|
|
|
|
issues = append(issues, rprt)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//this is better printing, it should probably group up the port issues in a better printing. Not sure how
|
|
|
|
|
|
|
|
lvl := os.Getenv("LOG_LEVEL")
|
|
|
|
lvl := os.Getenv("LOG_LEVEL")
|
|
|
|
|
|
|
|
fatalCount := 0
|
|
|
|
for _, p := range issues {
|
|
|
|
for _, p := range issues {
|
|
|
|
fmt.Println()
|
|
|
|
fmt.Println()
|
|
|
|
fmt.Println("----------------------------------------------------------------------------")
|
|
|
|
fmt.Println("----------------------------------------------------------------------------")
|
|
|
|
fmt.Println(p.Name)
|
|
|
|
fmt.Println(p.Name)
|
|
|
|
for _, x := range p.Issues {
|
|
|
|
for _, x := range p.Issues {
|
|
|
|
|
|
|
|
if x.Level == issue.FATAL {
|
|
|
|
|
|
|
|
fatalCount++
|
|
|
|
|
|
|
|
}
|
|
|
|
if lvl == "all" {
|
|
|
|
if lvl == "all" {
|
|
|
|
fmt.Printf("\tsafe: %t\n\tlevel: %s\n\tMessages:\n\t\t%s\n\n", x.Safe, x.Level, strings.Join(x.Messages, "\n\t\t"))
|
|
|
|
fmt.Printf("\tsafe: %t\n\tlevel: %s\n\tMessages:\n\t\t%s\n\n", x.Safe, x.Level, strings.Join(x.Messages, "\n\t\t"))
|
|
|
|
} else if lvl == "fatal" {
|
|
|
|
} else if lvl == "fatal" {
|
|
|
|
@@ -112,33 +116,41 @@ func main() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if strings.EqualFold(os.Getenv("FAIL_ON_FATAL"), "yes") && fatalCount > 0 {
|
|
|
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ensure cpus and mem_limit are set on the service
|
|
|
|
// ensure cpus and mem_limit are set on the service
|
|
|
|
func ResourceCheck(srv compose.ServiceConfig) *issue.Issue {
|
|
|
|
func ResourceCheck(srv *compose.ServiceConfig) *issue.Issue {
|
|
|
|
i := &issue.Issue{}
|
|
|
|
i := &issue.Issue{}
|
|
|
|
if srv.Cpus == nil {
|
|
|
|
if srv.Cpus == nil {
|
|
|
|
i.Warning()
|
|
|
|
i.Warning()
|
|
|
|
i.Messages = append(i.Messages, "there is no cpu limit set for the service")
|
|
|
|
i.Messages = append(i.Messages, "there is no cpu limit set for the service, set cpus to define the number of (potentially virtual) CPUs to allocate to service containers")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if srv.MemLimit == nil {
|
|
|
|
if srv.MemLimit == nil {
|
|
|
|
i.Warning()
|
|
|
|
i.Warning()
|
|
|
|
i.Messages = append(i.Messages, "there is no memory limit set for the service")
|
|
|
|
i.Messages = append(i.Messages, "there is no memory limit set for the service, set mem_limit to configure a limit on the amount of memory a container can allocate")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if srv.Cpus == nil && srv.MemLimit == nil {
|
|
|
|
if srv.Cpus == nil && srv.MemLimit == nil {
|
|
|
|
i.Fatal()
|
|
|
|
i.Fatal()
|
|
|
|
i.Messages = append(i.Messages, "there are no resource limits set for the service")
|
|
|
|
i.Messages = append(i.Messages, "there are no resource limits set for the service, its the wild west")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if len(i.Messages) > 0 {
|
|
|
|
|
|
|
|
return i
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
i.Passed()
|
|
|
|
i.Passed()
|
|
|
|
i.Messages = append(i.Messages, "resource limits configuration is safe")
|
|
|
|
i.Messages = append(i.Messages, "resource limits are configured properly")
|
|
|
|
return i
|
|
|
|
return i
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// make sure service is not ran as privileged
|
|
|
|
// make sure service is not ran as privileged
|
|
|
|
func PrivilegedCheck(srv compose.ServiceConfig) *issue.Issue {
|
|
|
|
func PrivilegedCheck(srv *compose.ServiceConfig) *issue.Issue {
|
|
|
|
i := &issue.Issue{}
|
|
|
|
i := &issue.Issue{}
|
|
|
|
if srv.Privileged == nil {
|
|
|
|
if srv.Privileged == nil {
|
|
|
|
i.Passed()
|
|
|
|
i.Passed()
|
|
|
|
@@ -148,7 +160,7 @@ func PrivilegedCheck(srv compose.ServiceConfig) *issue.Issue {
|
|
|
|
|
|
|
|
|
|
|
|
if *srv.Privileged == true {
|
|
|
|
if *srv.Privileged == true {
|
|
|
|
i.Fatal()
|
|
|
|
i.Fatal()
|
|
|
|
i.Messages = append(i.Messages, "privileged flag is set to true")
|
|
|
|
i.Messages = append(i.Messages, "privileged flag is set to true, remove it to prevent the service container from running with elevated privileges")
|
|
|
|
return i
|
|
|
|
return i
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -158,29 +170,29 @@ func PrivilegedCheck(srv compose.ServiceConfig) *issue.Issue {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// checks for specific security options
|
|
|
|
// checks for specific security options
|
|
|
|
func SecurityOptCheck(srv compose.ServiceConfig) *issue.Issue {
|
|
|
|
func SecurityOptCheck(srv *compose.ServiceConfig) *issue.Issue {
|
|
|
|
i := &issue.Issue{}
|
|
|
|
i := &issue.Issue{}
|
|
|
|
if srv.SecOpts == nil {
|
|
|
|
if srv.SecOpts == nil {
|
|
|
|
i.Fatal()
|
|
|
|
i.Fatal()
|
|
|
|
i.Messages = append(i.Messages, "set no-new-privileges=true on the service security_opts")
|
|
|
|
i.Messages = append(i.Messages, "set no-new-privileges=true on the service security_opts to disable container processes from gaining new privileges")
|
|
|
|
return i
|
|
|
|
return i
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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")
|
|
|
|
i.Messages = append(i.Messages, "security options are safe")
|
|
|
|
return i
|
|
|
|
return i
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
i.Fatal()
|
|
|
|
i.Fatal()
|
|
|
|
i.Messages = append(i.Messages, "set no-new-privileges=true on the container security_opts")
|
|
|
|
i.Messages = append(i.Messages, "set no-new-privileges=true on the container security_opts to disable container processes from gaining new privileges")
|
|
|
|
return i
|
|
|
|
return i
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// checking port to ensure it is behind a reverse proxy
|
|
|
|
// checking port to ensure it is behind a reverse proxy
|
|
|
|
func PortCheck(srv compose.ServiceConfig) ([]*issue.Issue, error) {
|
|
|
|
func PortCheck(srv *compose.ServiceConfig) ([]*issue.Issue, error) {
|
|
|
|
il := []*issue.Issue{}
|
|
|
|
il := []*issue.Issue{}
|
|
|
|
if srv.Ports == nil {
|
|
|
|
if srv.Ports == nil {
|
|
|
|
i := &issue.Issue{}
|
|
|
|
i := &issue.Issue{}
|
|
|
|
@@ -204,7 +216,7 @@ func PortCheck(srv compose.ServiceConfig) ([]*issue.Issue, error) {
|
|
|
|
continue
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ms, err := regexp.Match(`^\${{\s*(vars|secrets)\.[[:alnum:]]+\s*}}{1}:\${{\s*(vars|secrets)\.[[:alnum:]]+\s*}}{1}$`, []byte(prt))
|
|
|
|
ms, err := regexp.Match(ENV_PORT_PORT, []byte(prt))
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -216,7 +228,7 @@ func PortCheck(srv compose.ServiceConfig) ([]*issue.Issue, error) {
|
|
|
|
continue
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
m2, err := regexp.Match(IM_SO_SORRY, []byte(prt))
|
|
|
|
m2, err := regexp.Match(IP_PORT_PORT_REGEX, []byte(prt))
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -238,7 +250,7 @@ func PortCheck(srv compose.ServiceConfig) ([]*issue.Issue, error) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ensures the user value is set and nonroot in the service
|
|
|
|
// ensures the user value is set and nonroot in the service
|
|
|
|
func UserCheck(srv compose.ServiceConfig) (*issue.Issue, error) {
|
|
|
|
func UserCheck(srv *compose.ServiceConfig) (*issue.Issue, error) {
|
|
|
|
i := &issue.Issue{}
|
|
|
|
i := &issue.Issue{}
|
|
|
|
if srv.User == nil {
|
|
|
|
if srv.User == nil {
|
|
|
|
i.Warning()
|
|
|
|
i.Warning()
|
|
|
|
@@ -311,7 +323,7 @@ func UserCheck(srv compose.ServiceConfig) (*issue.Issue, error) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ensures a restart policy is set in the service configuration with at least max_attempts and restart condition set
|
|
|
|
// ensures a restart policy is set in the service configuration with at least max_attempts and restart condition set
|
|
|
|
func PolicyCheck(srv compose.ServiceConfig) *issue.Issue {
|
|
|
|
func PolicyCheck(srv *compose.ServiceConfig) *issue.Issue {
|
|
|
|
i := &issue.Issue{}
|
|
|
|
i := &issue.Issue{}
|
|
|
|
if srv.Deploy != nil {
|
|
|
|
if srv.Deploy != nil {
|
|
|
|
if srv.Deploy.Policy != nil {
|
|
|
|
if srv.Deploy.Policy != nil {
|
|
|
|
|