1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
|
//go:build linux && cgo && !agent
package sys
import (
"os"
"os/exec"
"strconv"
"strings"
"golang.org/x/sys/unix"
"github.com/lxc/incus/v6/internal/server/db/cluster"
"github.com/lxc/incus/v6/internal/server/db/warningtype"
localUtil "github.com/lxc/incus/v6/internal/server/util"
internalUtil "github.com/lxc/incus/v6/internal/util"
"github.com/lxc/incus/v6/shared/logger"
"github.com/lxc/incus/v6/shared/util"
)
// Initialize AppArmor-specific attributes.
func (s *OS) initAppArmor() []cluster.Warning {
var dbWarnings []cluster.Warning
/* Detect AppArmor availability */
_, err := exec.LookPath("apparmor_parser")
if util.IsFalse(os.Getenv("INCUS_SECURITY_APPARMOR")) {
logger.Warnf("AppArmor support has been manually disabled")
dbWarnings = append(dbWarnings, cluster.Warning{
TypeCode: warningtype.AppArmorNotAvailable,
LastMessage: "Manually disabled",
})
} else if !internalUtil.IsDir("/sys/kernel/security/apparmor") {
logger.Warnf("AppArmor support has been disabled because of lack of kernel support")
dbWarnings = append(dbWarnings, cluster.Warning{
TypeCode: warningtype.AppArmorNotAvailable,
LastMessage: "Disabled because of lack of kernel support",
})
} else if err != nil {
logger.Warnf("AppArmor support has been disabled because 'apparmor_parser' couldn't be found")
dbWarnings = append(dbWarnings, cluster.Warning{
TypeCode: warningtype.AppArmorNotAvailable,
LastMessage: "Disabled because 'apparmor_parser' couldn't be found",
})
} else {
s.AppArmorAvailable = true
}
/* Detect AppArmor stacking support */
s.AppArmorStacking = appArmorCanStack()
/* Detect existing AppArmor stack */
if util.PathExists("/sys/kernel/security/apparmor/.ns_stacked") {
contentBytes, err := os.ReadFile("/sys/kernel/security/apparmor/.ns_stacked")
if err == nil && string(contentBytes) == "yes\n" {
s.AppArmorStacked = true
}
}
/* Detect AppArmor admin support */
if !haveMacAdmin() {
if s.AppArmorAvailable {
logger.Warnf("Per-container AppArmor profiles are disabled because the mac_admin capability is missing")
}
} else if s.RunningInUserNS && !s.AppArmorStacked {
if s.AppArmorAvailable {
logger.Warnf("Per-container AppArmor profiles are disabled because Incus is running in an unprivileged container without stacking")
}
} else {
s.AppArmorAdmin = true
}
/* Detect AppArmor confinment */
profile := localUtil.AppArmorProfile()
if profile != "unconfined" && profile != "" {
if s.AppArmorAvailable {
logger.Warnf("Per-container AppArmor profiles are disabled because Incus is already protected by AppArmor")
}
s.AppArmorConfined = true
}
return dbWarnings
}
func haveMacAdmin() bool {
hdr := unix.CapUserHeader{Pid: 0}
// Get hdr version to check.
err := unix.Capget(&hdr, nil)
if err != nil {
return false
}
// Return false if not version 3.
if hdr.Version != unix.LINUX_CAPABILITY_VERSION_3 {
return false
}
var data [2]unix.CapUserData
err = unix.Capget(&hdr, &data[0])
if err != nil {
return false
}
idx := unix.CAP_MAC_ADMIN / 32
bit := uint(unix.CAP_MAC_ADMIN % 32)
return ((1 << bit) & data[idx].Effective) != 0
}
// Returns true if AppArmor stacking support is available.
func appArmorCanStack() bool {
contentBytes, err := os.ReadFile("/sys/kernel/security/apparmor/features/domain/stack")
if err != nil {
return false
}
if string(contentBytes) != "yes\n" {
return false
}
contentBytes, err = os.ReadFile("/sys/kernel/security/apparmor/features/domain/version")
if err != nil {
return false
}
content := string(contentBytes)
parts := strings.Split(strings.TrimSpace(content), ".")
if len(parts) == 0 {
logger.Warn("Unknown apparmor domain version", logger.Ctx{"version": content})
return false
}
major, err := strconv.Atoi(parts[0])
if err != nil {
logger.Warn("Unknown apparmor domain version", logger.Ctx{"version": content})
return false
}
minor := 0
if len(parts) == 2 {
minor, err = strconv.Atoi(parts[1])
if err != nil {
logger.Warn("Unknown apparmor domain version", logger.Ctx{"version": content})
return false
}
}
return major >= 1 && minor >= 2
}
|