-
Notifications
You must be signed in to change notification settings - Fork 907
/
apparmor.go
151 lines (126 loc) · 4.28 KB
/
apparmor.go
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
//go:build linux && cgo && !agent
package sys
import (
"os"
"os/exec"
"strconv"
"strings"
"github.com/syndtr/gocapability/capability"
"github.com/canonical/lxd/lxd/db/cluster"
"github.com/canonical/lxd/lxd/db/warningtype"
"github.com/canonical/lxd/lxd/util"
"github.com/canonical/lxd/shared"
"github.com/canonical/lxd/shared/logger"
)
// Initialize AppArmor-specific attributes.
func (s *OS) initAppArmor() []cluster.Warning {
var dbWarnings []cluster.Warning
/* Detect AppArmor availability */
_, err := exec.LookPath("apparmor_parser")
if shared.IsFalse(os.Getenv("LXD_SECURITY_APPARMOR")) {
logger.Warnf("AppArmor support has been manually disabled")
dbWarnings = append(dbWarnings, cluster.Warning{
TypeCode: warningtype.AppArmorNotAvailable,
LastMessage: "Manually disabled",
})
} else if !shared.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 shared.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 LXD is running in an unprivileged container without stacking")
}
} else {
s.AppArmorAdmin = true
}
/* Detect AppArmor confinment */
profile := util.AppArmorProfile()
/* if AppArmor is enabled on the system but there is no profile then
the application has the profile name "unconfined", or if AppArmor
is not supported then the label will be empty and finally newer
AppArmor releases support a profile mode "unconfined" where an
application can have a profile defined for it which effectively
is equivalent to the traditional "unconfined" label - in this case
the label will have the suffix " (unconfined)" */
if profile != "unconfined" && profile != "" && !strings.HasSuffix(profile, "(unconfined)") {
if s.AppArmorAvailable {
logger.Warnf("Per-container AppArmor profiles are disabled because LXD is already protected by AppArmor via profile %q", profile)
}
s.AppArmorConfined = true
}
return dbWarnings
}
func haveMacAdmin() bool {
c, err := capability.NewPid2(0)
if err != nil {
return false
}
err = c.Load()
if err != nil {
return false
}
if c.Get(capability.EFFECTIVE, capability.CAP_MAC_ADMIN) {
return true
}
return false
}
// 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
}