206 lines
5.3 KiB
Go
206 lines
5.3 KiB
Go
/*
|
|
AngelaMos | 2026
|
|
daemon.go
|
|
*/
|
|
|
|
package analyzer
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
|
|
"github.com/CarterPerez-dev/docksec/internal/benchmark"
|
|
"github.com/CarterPerez-dev/docksec/internal/docker"
|
|
"github.com/CarterPerez-dev/docksec/internal/finding"
|
|
)
|
|
|
|
type DaemonAnalyzer struct {
|
|
client *docker.Client
|
|
}
|
|
|
|
func NewDaemonAnalyzer(client *docker.Client) *DaemonAnalyzer {
|
|
return &DaemonAnalyzer{client: client}
|
|
}
|
|
|
|
func (a *DaemonAnalyzer) Name() string {
|
|
return "daemon"
|
|
}
|
|
|
|
func (a *DaemonAnalyzer) Analyze(
|
|
ctx context.Context,
|
|
) (finding.Collection, error) {
|
|
info, err := a.client.Info(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
target := finding.Target{
|
|
Type: finding.TargetDaemon,
|
|
Name: "docker-daemon",
|
|
ID: info.ID,
|
|
}
|
|
|
|
var findings finding.Collection
|
|
|
|
findings = append(
|
|
findings,
|
|
a.checkSeccompDefault(target, info.SecurityOptions)...)
|
|
findings = append(
|
|
findings,
|
|
a.checkLiveRestore(target, info.LiveRestoreEnabled)...)
|
|
findings = append(
|
|
findings,
|
|
a.checkExperimental(target, info.ExperimentalBuild)...)
|
|
findings = append(
|
|
findings,
|
|
a.checkUsernsRemap(target, info.SecurityOptions)...)
|
|
findings = append(
|
|
findings,
|
|
a.checkLoggingDriver(target, info.LoggingDriver)...)
|
|
findings = append(
|
|
findings,
|
|
a.checkCgroupDriver(target, info.CgroupDriver)...)
|
|
|
|
return findings, nil
|
|
}
|
|
|
|
func (a *DaemonAnalyzer) checkSeccompDefault(
|
|
target finding.Target,
|
|
securityOpts []string,
|
|
) finding.Collection {
|
|
var findings finding.Collection
|
|
|
|
seccompEnabled := false
|
|
for _, opt := range securityOpts {
|
|
if strings.HasPrefix(opt, "seccomp") {
|
|
seccompEnabled = true
|
|
if strings.Contains(opt, "unconfined") {
|
|
control, _ := benchmark.Get("2.7")
|
|
f := finding.New("CIS-2.7", control.Title, finding.SeverityHigh, target).
|
|
WithDescription(control.Description).
|
|
WithCategory(string(CategoryDaemon)).
|
|
WithRemediation(control.Remediation).
|
|
WithReferences(control.References...).
|
|
WithCISControl(control.ToCISControl())
|
|
findings = append(findings, f)
|
|
}
|
|
break
|
|
}
|
|
}
|
|
|
|
if !seccompEnabled {
|
|
control, _ := benchmark.Get("2.7")
|
|
f := finding.New("CIS-2.7", "Seccomp not enabled on daemon", finding.SeverityMedium, target).
|
|
WithDescription("Docker daemon is not configured with seccomp support.").
|
|
WithCategory(string(CategoryDaemon)).
|
|
WithRemediation(control.Remediation).
|
|
WithReferences(control.References...).
|
|
WithCISControl(control.ToCISControl())
|
|
findings = append(findings, f)
|
|
}
|
|
|
|
return findings
|
|
}
|
|
|
|
func (a *DaemonAnalyzer) checkLiveRestore(
|
|
target finding.Target,
|
|
enabled bool,
|
|
) finding.Collection {
|
|
var findings finding.Collection
|
|
|
|
if !enabled {
|
|
control, _ := benchmark.Get("2.14")
|
|
f := finding.New("CIS-2.14", control.Title, finding.SeverityLow, target).
|
|
WithDescription(control.Description).
|
|
WithCategory(string(CategoryDaemon)).
|
|
WithRemediation(control.Remediation).
|
|
WithReferences(control.References...).
|
|
WithCISControl(control.ToCISControl())
|
|
findings = append(findings, f)
|
|
}
|
|
|
|
return findings
|
|
}
|
|
|
|
func (a *DaemonAnalyzer) checkExperimental(
|
|
target finding.Target,
|
|
enabled bool,
|
|
) finding.Collection {
|
|
var findings finding.Collection
|
|
|
|
if enabled {
|
|
control, _ := benchmark.Get("2.8")
|
|
f := finding.New("CIS-2.8", control.Title, finding.SeverityLow, target).
|
|
WithDescription(control.Description).
|
|
WithCategory(string(CategoryDaemon)).
|
|
WithRemediation(control.Remediation).
|
|
WithReferences(control.References...).
|
|
WithCISControl(control.ToCISControl())
|
|
findings = append(findings, f)
|
|
}
|
|
|
|
return findings
|
|
}
|
|
|
|
func (a *DaemonAnalyzer) checkUsernsRemap(
|
|
target finding.Target,
|
|
securityOpts []string,
|
|
) finding.Collection {
|
|
var findings finding.Collection
|
|
|
|
usernsEnabled := false
|
|
for _, opt := range securityOpts {
|
|
if strings.HasPrefix(opt, "userns") {
|
|
usernsEnabled = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !usernsEnabled {
|
|
f := finding.New("CIS-2.8", "User namespace remapping not enabled", finding.SeverityInfo, target).
|
|
WithDescription("User namespace remapping provides additional isolation by mapping container users to unprivileged host users.").
|
|
WithCategory(string(CategoryDaemon)).
|
|
WithRemediation("Configure --userns-remap in daemon.json for additional container isolation.")
|
|
findings = append(findings, f)
|
|
}
|
|
|
|
return findings
|
|
}
|
|
|
|
func (a *DaemonAnalyzer) checkLoggingDriver(
|
|
target finding.Target,
|
|
driver string,
|
|
) finding.Collection {
|
|
var findings finding.Collection
|
|
|
|
if driver == "" || driver == "none" {
|
|
control, _ := benchmark.Get("2.3")
|
|
f := finding.New("CIS-2.3", control.Title, finding.SeverityMedium, target).
|
|
WithDescription(control.Description).
|
|
WithCategory(string(CategoryDaemon)).
|
|
WithRemediation(control.Remediation).
|
|
WithReferences(control.References...).
|
|
WithCISControl(control.ToCISControl())
|
|
findings = append(findings, f)
|
|
}
|
|
|
|
return findings
|
|
}
|
|
|
|
func (a *DaemonAnalyzer) checkCgroupDriver(
|
|
target finding.Target,
|
|
driver string,
|
|
) finding.Collection {
|
|
var findings finding.Collection
|
|
|
|
if driver == "" {
|
|
f := finding.New("DS-CGROUP-001", "No cgroup driver configured", finding.SeverityInfo, target).
|
|
WithDescription("Docker daemon has no explicit cgroup driver configured. This is informational.").
|
|
WithCategory(string(CategoryDaemon)).
|
|
WithRemediation("Consider explicitly configuring cgroup driver for consistency.")
|
|
findings = append(findings, f)
|
|
}
|
|
|
|
return findings
|
|
}
|