Cybersecurity-Projects/PROJECTS/intermediate/docker-security-audit/internal/proc/capabilities.go

284 lines
5.8 KiB
Go

/*
CarterPerez-dev | 2026
capabilities.go
*/
package proc
import (
"fmt"
"github.com/CarterPerez-dev/docksec/internal/finding"
"github.com/CarterPerez-dev/docksec/internal/rules"
)
type CapabilitySet struct {
Effective uint64
Permitted uint64
Inheritable uint64
Bounding uint64
Ambient uint64
}
var capabilityBits = map[int]string{
0: "CAP_CHOWN",
1: "CAP_DAC_OVERRIDE",
2: "CAP_DAC_READ_SEARCH",
3: "CAP_FOWNER",
4: "CAP_FSETID",
5: "CAP_KILL",
6: "CAP_SETGID",
7: "CAP_SETUID",
8: "CAP_SETPCAP",
9: "CAP_LINUX_IMMUTABLE",
10: "CAP_NET_BIND_SERVICE",
11: "CAP_NET_BROADCAST",
12: "CAP_NET_ADMIN",
13: "CAP_NET_RAW",
14: "CAP_IPC_LOCK",
15: "CAP_IPC_OWNER",
16: "CAP_SYS_MODULE",
17: "CAP_SYS_RAWIO",
18: "CAP_SYS_CHROOT",
19: "CAP_SYS_PTRACE",
20: "CAP_SYS_PACCT",
21: "CAP_SYS_ADMIN",
22: "CAP_SYS_BOOT",
23: "CAP_SYS_NICE",
24: "CAP_SYS_RESOURCE",
25: "CAP_SYS_TIME",
26: "CAP_SYS_TTY_CONFIG",
27: "CAP_MKNOD",
28: "CAP_LEASE",
29: "CAP_AUDIT_WRITE",
30: "CAP_AUDIT_CONTROL",
31: "CAP_SETFCAP",
32: "CAP_MAC_OVERRIDE",
33: "CAP_MAC_ADMIN",
34: "CAP_SYSLOG",
35: "CAP_WAKE_ALARM",
36: "CAP_BLOCK_SUSPEND",
37: "CAP_AUDIT_READ",
38: "CAP_PERFMON",
39: "CAP_BPF",
40: "CAP_CHECKPOINT_RESTORE",
}
var capabilityNames = func() map[string]int {
m := make(map[string]int, len(capabilityBits))
for bit, name := range capabilityBits {
m[name] = bit
}
return m
}()
func (c *CapabilitySet) HasCapability(name string) bool {
bit, ok := capabilityNames[name]
if !ok {
return false
}
return (c.Effective & (1 << bit)) != 0
}
func (c *CapabilitySet) HasPermitted(name string) bool {
bit, ok := capabilityNames[name]
if !ok {
return false
}
return (c.Permitted & (1 << bit)) != 0
}
func (c *CapabilitySet) HasBounding(name string) bool {
bit, ok := capabilityNames[name]
if !ok {
return false
}
return (c.Bounding & (1 << bit)) != 0
}
func (c *CapabilitySet) HasAmbient(name string) bool {
bit, ok := capabilityNames[name]
if !ok {
return false
}
return (c.Ambient & (1 << bit)) != 0
}
func (c *CapabilitySet) ListEffective() []string {
return c.listCaps(c.Effective)
}
func (c *CapabilitySet) ListPermitted() []string {
return c.listCaps(c.Permitted)
}
func (c *CapabilitySet) ListBounding() []string {
return c.listCaps(c.Bounding)
}
func (c *CapabilitySet) ListAmbient() []string {
return c.listCaps(c.Ambient)
}
func (c *CapabilitySet) ListInheritable() []string {
return c.listCaps(c.Inheritable)
}
func (c *CapabilitySet) listCaps(mask uint64) []string {
var caps []string
for bit, name := range capabilityBits {
if (mask & (1 << bit)) != 0 {
caps = append(caps, name)
}
}
return caps
}
func (c *CapabilitySet) IsFullyPrivileged() bool {
return c.Effective == 0x1ffffffffff || c.Effective == 0xffffffffffffffff
}
func (c *CapabilitySet) HasDangerousCapabilities() bool {
for _, cap := range c.ListEffective() {
if rules.IsDangerousCapability(cap) {
return true
}
}
return false
}
func (c *CapabilitySet) HasCriticalCapabilities() bool {
for _, cap := range c.ListEffective() {
if rules.IsCriticalCapability(cap) {
return true
}
}
return false
}
func (c *CapabilitySet) GetDangerousCapabilities() []string {
var dangerous []string
for _, cap := range c.ListEffective() {
if rules.IsDangerousCapability(cap) {
dangerous = append(dangerous, cap)
}
}
return dangerous
}
func (c *CapabilitySet) GetCriticalCapabilities() []string {
var critical []string
for _, cap := range c.ListEffective() {
if rules.IsCriticalCapability(cap) {
critical = append(critical, cap)
}
}
return critical
}
func (c *CapabilitySet) GetCapabilitiesBySeverity(
minSeverity finding.Severity,
) []string {
var result []string
for _, cap := range c.ListEffective() {
severity := rules.GetCapabilitySeverity(cap)
if severity >= minSeverity {
result = append(result, cap)
}
}
return result
}
func (c *CapabilitySet) EffectiveCount() int {
return countBits(c.Effective)
}
func (c *CapabilitySet) PermittedCount() int {
return countBits(c.Permitted)
}
func (c *CapabilitySet) BoundingCount() int {
return countBits(c.Bounding)
}
func countBits(n uint64) int {
count := 0
for n != 0 {
count += int(n & 1)
n >>= 1
}
return count
}
func ParseCapabilityMask(hex string) (uint64, error) {
var mask uint64
_, err := fmt.Sscanf(hex, "%x", &mask)
return mask, err
}
func CapabilityNameToBit(name string) (int, bool) {
bit, ok := capabilityNames[name]
return bit, ok
}
func CapabilityBitToName(bit int) (string, bool) {
name, ok := capabilityBits[bit]
return name, ok
}
func AllCapabilityNames() []string {
names := make([]string, 0, len(capabilityBits))
for i := 0; i <= 40; i++ {
if name, ok := capabilityBits[i]; ok {
names = append(names, name)
}
}
return names
}
var defaultDockerCaps = map[string]struct{}{
"CAP_CHOWN": {},
"CAP_DAC_OVERRIDE": {},
"CAP_FSETID": {},
"CAP_FOWNER": {},
"CAP_MKNOD": {},
"CAP_NET_RAW": {},
"CAP_SETGID": {},
"CAP_SETUID": {},
"CAP_SETFCAP": {},
"CAP_SETPCAP": {},
"CAP_NET_BIND_SERVICE": {},
"CAP_SYS_CHROOT": {},
"CAP_KILL": {},
"CAP_AUDIT_WRITE": {},
}
func (c *CapabilitySet) GetAddedCapabilities() []string {
var added []string
for _, cap := range c.ListEffective() {
if _, isDefault := defaultDockerCaps[cap]; !isDefault {
added = append(added, cap)
}
}
return added
}
func (c *CapabilitySet) GetDroppedDefaultCapabilities() []string {
var dropped []string
for cap := range defaultDockerCaps {
if !c.HasCapability(cap) {
dropped = append(dropped, cap)
}
}
return dropped
}
func (c *CapabilitySet) HasOnlyDefaultCapabilities() bool {
for _, cap := range c.ListEffective() {
if _, isDefault := defaultDockerCaps[cap]; !isDefault {
return false
}
}
return true
}