Signed-off-by: Brian Goff <cpuguy83@gmail.com>
| ... | ... |
@@ -743,6 +743,11 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo |
| 743 | 743 |
} |
| 744 | 744 |
|
| 745 | 745 |
sysInfo := sysinfo.New(false) |
| 746 |
+ // Check if Devices cgroup is mounted, it is hard requirement for container security. |
|
| 747 |
+ if !sysInfo.CgroupDevicesEnabled {
|
|
| 748 |
+ return nil, fmt.Errorf("Devices cgroup isn't mounted")
|
|
| 749 |
+ } |
|
| 750 |
+ |
|
| 746 | 751 |
ed, err := execdrivers.NewDriver(config.ExecDriver, config.ExecOptions, config.ExecRoot, config.Root, sysInitPath, sysInfo) |
| 747 | 752 |
if err != nil {
|
| 748 | 753 |
return nil, err |
| ... | ... |
@@ -3,13 +3,22 @@ package sysinfo |
| 3 | 3 |
// SysInfo stores information about which features a kernel supports. |
| 4 | 4 |
// TODO Windows: Factor out platform specific capabilities. |
| 5 | 5 |
type SysInfo struct {
|
| 6 |
- MemoryLimit bool |
|
| 7 |
- SwapLimit bool |
|
| 8 |
- CpuCfsPeriod bool |
|
| 9 |
- CpuCfsQuota bool |
|
| 6 |
+ AppArmor bool |
|
| 7 |
+ *cgroupMemInfo |
|
| 8 |
+ *cgroupCpuInfo |
|
| 10 | 9 |
IPv4ForwardingDisabled bool |
| 11 |
- AppArmor bool |
|
| 12 |
- OomKillDisable bool |
|
| 13 | 10 |
BridgeNfCallIptablesDisabled bool |
| 14 | 11 |
BridgeNfCallIp6tablesDisabled bool |
| 12 |
+ CgroupDevicesEnabled bool |
|
| 13 |
+} |
|
| 14 |
+ |
|
| 15 |
+type cgroupMemInfo struct {
|
|
| 16 |
+ MemoryLimit bool |
|
| 17 |
+ SwapLimit bool |
|
| 18 |
+ OomKillDisable bool |
|
| 19 |
+} |
|
| 20 |
+ |
|
| 21 |
+type cgroupCpuInfo struct {
|
|
| 22 |
+ CpuCfsPeriod bool |
|
| 23 |
+ CpuCfsQuota bool |
|
| 15 | 24 |
} |
| ... | ... |
@@ -4,7 +4,6 @@ import ( |
| 4 | 4 |
"io/ioutil" |
| 5 | 5 |
"os" |
| 6 | 6 |
"path" |
| 7 |
- "strconv" |
|
| 8 | 7 |
"strings" |
| 9 | 8 |
|
| 10 | 9 |
"github.com/Sirupsen/logrus" |
| ... | ... |
@@ -14,81 +13,78 @@ import ( |
| 14 | 14 |
// New returns a new SysInfo, using the filesystem to detect which features the kernel supports. |
| 15 | 15 |
func New(quiet bool) *SysInfo {
|
| 16 | 16 |
sysInfo := &SysInfo{}
|
| 17 |
- if cgroupMemoryMountpoint, err := cgroups.FindCgroupMountpoint("memory"); err != nil {
|
|
| 18 |
- if !quiet {
|
|
| 19 |
- logrus.Warnf("Your kernel does not support cgroup memory limit: %v", err)
|
|
| 20 |
- } |
|
| 21 |
- } else {
|
|
| 22 |
- // If memory cgroup is mounted, MemoryLimit is always enabled. |
|
| 23 |
- sysInfo.MemoryLimit = true |
|
| 17 |
+ sysInfo.cgroupMemInfo = checkCgroupMem(quiet) |
|
| 18 |
+ sysInfo.cgroupCpuInfo = checkCgroupCpu(quiet) |
|
| 24 | 19 |
|
| 25 |
- _, err1 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.memsw.limit_in_bytes")) |
|
| 26 |
- sysInfo.SwapLimit = err1 == nil |
|
| 27 |
- if !sysInfo.SwapLimit && !quiet {
|
|
| 28 |
- logrus.Warn("Your kernel does not support swap memory limit.")
|
|
| 29 |
- } |
|
| 20 |
+ _, err := cgroups.FindCgroupMountpoint("devices")
|
|
| 21 |
+ sysInfo.CgroupDevicesEnabled = err == nil |
|
| 30 | 22 |
|
| 31 |
- _, err = ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.oom_control")) |
|
| 32 |
- sysInfo.OomKillDisable = err == nil |
|
| 33 |
- if !sysInfo.OomKillDisable && !quiet {
|
|
| 34 |
- logrus.Warnf("Your kernel does not support oom control.")
|
|
| 35 |
- } |
|
| 23 |
+ sysInfo.IPv4ForwardingDisabled = !readProcBool("/proc/sys/net/ipv4/ip_forward")
|
|
| 24 |
+ sysInfo.BridgeNfCallIptablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-iptables")
|
|
| 25 |
+ sysInfo.BridgeNfCallIp6tablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-ip6tables")
|
|
| 26 |
+ |
|
| 27 |
+ // Check if AppArmor is supported. |
|
| 28 |
+ if _, err := os.Stat("/sys/kernel/security/apparmor"); !os.IsNotExist(err) {
|
|
| 29 |
+ sysInfo.AppArmor = true |
|
| 36 | 30 |
} |
| 37 | 31 |
|
| 38 |
- if cgroupCpuMountpoint, err := cgroups.FindCgroupMountpoint("cpu"); err != nil {
|
|
| 32 |
+ return sysInfo |
|
| 33 |
+} |
|
| 34 |
+ |
|
| 35 |
+func checkCgroupMem(quiet bool) *cgroupMemInfo {
|
|
| 36 |
+ info := &cgroupMemInfo{}
|
|
| 37 |
+ mountPoint, err := cgroups.FindCgroupMountpoint("memory")
|
|
| 38 |
+ if err != nil {
|
|
| 39 | 39 |
if !quiet {
|
| 40 |
- logrus.Warnf("%v", err)
|
|
| 41 |
- } |
|
| 42 |
- } else {
|
|
| 43 |
- _, err := ioutil.ReadFile(path.Join(cgroupCpuMountpoint, "cpu.cfs_period_us")) |
|
| 44 |
- sysInfo.CpuCfsPeriod = err == nil |
|
| 45 |
- if !sysInfo.CpuCfsPeriod && !quiet {
|
|
| 46 |
- logrus.Warn("Your kernel does not support cgroup cfs period")
|
|
| 47 |
- } |
|
| 48 |
- _, err = ioutil.ReadFile(path.Join(cgroupCpuMountpoint, "cpu.cfs_quota_us")) |
|
| 49 |
- sysInfo.CpuCfsQuota = err == nil |
|
| 50 |
- if !sysInfo.CpuCfsQuota && !quiet {
|
|
| 51 |
- logrus.Warn("Your kernel does not support cgroup cfs quotas")
|
|
| 40 |
+ logrus.Warnf("Your kernel does not support cgroup memory limit: %v", err)
|
|
| 52 | 41 |
} |
| 42 |
+ return nil |
|
| 53 | 43 |
} |
| 44 |
+ info.MemoryLimit = true |
|
| 54 | 45 |
|
| 55 |
- // Checek if ipv4_forward is disabled. |
|
| 56 |
- if data, err := ioutil.ReadFile("/proc/sys/net/ipv4/ip_forward"); os.IsNotExist(err) {
|
|
| 57 |
- sysInfo.IPv4ForwardingDisabled = true |
|
| 58 |
- } else {
|
|
| 59 |
- if enabled, _ := strconv.Atoi(strings.TrimSpace(string(data))); enabled == 0 {
|
|
| 60 |
- sysInfo.IPv4ForwardingDisabled = true |
|
| 61 |
- } else {
|
|
| 62 |
- sysInfo.IPv4ForwardingDisabled = false |
|
| 63 |
- } |
|
| 46 |
+ info.SwapLimit = cgroupEnabled(mountPoint, "memory.memsw.limit_in_bytes") |
|
| 47 |
+ if !quiet && !info.SwapLimit {
|
|
| 48 |
+ logrus.Warn("Your kernel does not support swap memory limit.")
|
|
| 64 | 49 |
} |
| 65 |
- |
|
| 66 |
- // Check if bridge-nf-call-iptables is disabled. |
|
| 67 |
- if data, err := ioutil.ReadFile("/proc/sys/net/bridge/bridge-nf-call-iptables"); os.IsNotExist(err) {
|
|
| 68 |
- sysInfo.BridgeNfCallIptablesDisabled = true |
|
| 69 |
- } else {
|
|
| 70 |
- enabled, _ := strconv.Atoi(strings.TrimSpace(string(data))) |
|
| 71 |
- sysInfo.BridgeNfCallIptablesDisabled = enabled == 0 |
|
| 50 |
+ info.OomKillDisable = cgroupEnabled(mountPoint, "memory.oom_control") |
|
| 51 |
+ if !quiet && !info.OomKillDisable {
|
|
| 52 |
+ logrus.Warnf("Your kernel does not support oom control.")
|
|
| 72 | 53 |
} |
| 73 |
- // Check if bridge-nf-call-ip6tables is disabled. |
|
| 74 |
- if data, err := ioutil.ReadFile("/proc/sys/net/bridge/bridge-nf-call-ip6tables"); os.IsNotExist(err) {
|
|
| 75 |
- sysInfo.BridgeNfCallIp6tablesDisabled = true |
|
| 76 |
- } else {
|
|
| 77 |
- enabled, _ := strconv.Atoi(strings.TrimSpace(string(data))) |
|
| 78 |
- sysInfo.BridgeNfCallIp6tablesDisabled = enabled == 0 |
|
| 54 |
+ |
|
| 55 |
+ return info |
|
| 56 |
+} |
|
| 57 |
+ |
|
| 58 |
+func checkCgroupCpu(quiet bool) *cgroupCpuInfo {
|
|
| 59 |
+ info := &cgroupCpuInfo{}
|
|
| 60 |
+ mountPoint, err := cgroups.FindCgroupMountpoint("cpu")
|
|
| 61 |
+ if err != nil {
|
|
| 62 |
+ if !quiet {
|
|
| 63 |
+ logrus.Warn(err) |
|
| 64 |
+ } |
|
| 65 |
+ return nil |
|
| 79 | 66 |
} |
| 80 | 67 |
|
| 81 |
- // Check if AppArmor is supported. |
|
| 82 |
- if _, err := os.Stat("/sys/kernel/security/apparmor"); os.IsNotExist(err) {
|
|
| 83 |
- sysInfo.AppArmor = false |
|
| 84 |
- } else {
|
|
| 85 |
- sysInfo.AppArmor = true |
|
| 68 |
+ info.CpuCfsPeriod = cgroupEnabled(mountPoint, "cpu.cfs_period_us") |
|
| 69 |
+ if !quiet && !info.CpuCfsPeriod {
|
|
| 70 |
+ logrus.Warn("Your kernel does not support cgroup cfs period")
|
|
| 86 | 71 |
} |
| 87 | 72 |
|
| 88 |
- // Check if Devices cgroup is mounted, it is hard requirement for container security. |
|
| 89 |
- if _, err := cgroups.FindCgroupMountpoint("devices"); err != nil {
|
|
| 90 |
- logrus.Fatalf("Error mounting devices cgroup: %v", err)
|
|
| 73 |
+ info.CpuCfsQuota = cgroupEnabled(mountPoint, "cpu.cfs_quota_us") |
|
| 74 |
+ if !quiet && !info.CpuCfsQuota {
|
|
| 75 |
+ logrus.Warn("Your kernel does not support cgroup cfs quotas")
|
|
| 91 | 76 |
} |
| 77 |
+ return info |
|
| 78 |
+} |
|
| 92 | 79 |
|
| 93 |
- return sysInfo |
|
| 80 |
+func cgroupEnabled(mountPoint, name string) bool {
|
|
| 81 |
+ _, err := os.Stat(path.Join(mountPoint, name)) |
|
| 82 |
+ return err == nil |
|
| 83 |
+} |
|
| 84 |
+ |
|
| 85 |
+func readProcBool(path string) bool {
|
|
| 86 |
+ val, err := ioutil.ReadFile(path) |
|
| 87 |
+ if err != nil {
|
|
| 88 |
+ return false |
|
| 89 |
+ } |
|
| 90 |
+ return strings.TrimSpace(string(val)) == "1" |
|
| 94 | 91 |
} |
| 95 | 92 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,58 @@ |
| 0 |
+package sysinfo |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "io/ioutil" |
|
| 4 |
+ "os" |
|
| 5 |
+ "path" |
|
| 6 |
+ "path/filepath" |
|
| 7 |
+ "testing" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+func TestReadProcBool(t *testing.T) {
|
|
| 11 |
+ tmpDir, err := ioutil.TempDir("", "test-sysinfo-proc")
|
|
| 12 |
+ if err != nil {
|
|
| 13 |
+ t.Fatal(err) |
|
| 14 |
+ } |
|
| 15 |
+ defer os.RemoveAll(tmpDir) |
|
| 16 |
+ |
|
| 17 |
+ procFile := filepath.Join(tmpDir, "read-proc-bool") |
|
| 18 |
+ if err := ioutil.WriteFile(procFile, []byte("1"), 644); err != nil {
|
|
| 19 |
+ t.Fatal(err) |
|
| 20 |
+ } |
|
| 21 |
+ |
|
| 22 |
+ if !readProcBool(procFile) {
|
|
| 23 |
+ t.Fatal("expected proc bool to be true, got false")
|
|
| 24 |
+ } |
|
| 25 |
+ |
|
| 26 |
+ if err := ioutil.WriteFile(procFile, []byte("0"), 644); err != nil {
|
|
| 27 |
+ t.Fatal(err) |
|
| 28 |
+ } |
|
| 29 |
+ if readProcBool(procFile) {
|
|
| 30 |
+ t.Fatal("expected proc bool to be false, got false")
|
|
| 31 |
+ } |
|
| 32 |
+ |
|
| 33 |
+ if readProcBool(path.Join(tmpDir, "no-exist")) {
|
|
| 34 |
+ t.Fatal("should be false for non-existent entry")
|
|
| 35 |
+ } |
|
| 36 |
+ |
|
| 37 |
+} |
|
| 38 |
+ |
|
| 39 |
+func TestCgroupEnabled(t *testing.T) {
|
|
| 40 |
+ cgroupDir, err := ioutil.TempDir("", "cgroup-test")
|
|
| 41 |
+ if err != nil {
|
|
| 42 |
+ t.Fatal(err) |
|
| 43 |
+ } |
|
| 44 |
+ defer os.RemoveAll(cgroupDir) |
|
| 45 |
+ |
|
| 46 |
+ if cgroupEnabled(cgroupDir, "test") {
|
|
| 47 |
+ t.Fatal("cgroupEnabled should be false")
|
|
| 48 |
+ } |
|
| 49 |
+ |
|
| 50 |
+ if err := ioutil.WriteFile(path.Join(cgroupDir, "test"), []byte{}, 644); err != nil {
|
|
| 51 |
+ t.Fatal(err) |
|
| 52 |
+ } |
|
| 53 |
+ |
|
| 54 |
+ if !cgroupEnabled(cgroupDir, "test") {
|
|
| 55 |
+ t.Fatal("cgroupEnabled should be true")
|
|
| 56 |
+ } |
|
| 57 |
+} |