Browse code

move default apparmor policy into package

Signed-off-by: Jessica Frazelle <acidburn@docker.com>

Jessica Frazelle authored on 2016/01/20 06:54:42
Showing 4 changed files
1 1
deleted file mode 100644
... ...
@@ -1,161 +0,0 @@
1
-// +build linux
2
-
3
-package native
4
-
5
-import (
6
-	"bufio"
7
-	"io"
8
-	"os"
9
-	"os/exec"
10
-	"path"
11
-	"strings"
12
-	"text/template"
13
-
14
-	"github.com/docker/docker/pkg/aaparser"
15
-	"github.com/opencontainers/runc/libcontainer/apparmor"
16
-)
17
-
18
-const (
19
-	apparmorProfilePath = "/etc/apparmor.d/docker"
20
-)
21
-
22
-type data struct {
23
-	Name         string
24
-	ExecPath     string
25
-	Imports      []string
26
-	InnerImports []string
27
-	MajorVersion int
28
-	MinorVersion int
29
-}
30
-
31
-const baseTemplate = `
32
-{{range $value := .Imports}}
33
-{{$value}}
34
-{{end}}
35
-
36
-profile {{.Name}} flags=(attach_disconnected,mediate_deleted) {
37
-{{range $value := .InnerImports}}
38
-  {{$value}}
39
-{{end}}
40
-
41
-  network,
42
-  capability,
43
-  file,
44
-  umount,
45
-
46
-  deny @{PROC}/* w,   # deny write for all files directly in /proc (not in a subdir)
47
-  # deny write to files not in /proc/<number>/** or /proc/sys/**
48
-  deny @{PROC}/{[^1-9],[^1-9][^0-9],[^1-9s][^0-9y][^0-9s],[^1-9][^0-9][^0-9][^0-9]*}/** w,
49
-  deny @{PROC}/sys/[^k]** w,  # deny /proc/sys except /proc/sys/k* (effectively /proc/sys/kernel)
50
-  deny @{PROC}/sys/kernel/{?,??,[^s][^h][^m]**} w,  # deny everything except shm* in /proc/sys/kernel/
51
-  deny @{PROC}/sysrq-trigger rwklx,
52
-  deny @{PROC}/mem rwklx,
53
-  deny @{PROC}/kmem rwklx,
54
-  deny @{PROC}/kcore rwklx,
55
-
56
-  deny mount,
57
-
58
-  deny /sys/[^f]*/** wklx,
59
-  deny /sys/f[^s]*/** wklx,
60
-  deny /sys/fs/[^c]*/** wklx,
61
-  deny /sys/fs/c[^g]*/** wklx,
62
-  deny /sys/fs/cg[^r]*/** wklx,
63
-  deny /sys/firmware/efi/efivars/** rwklx,
64
-  deny /sys/kernel/security/** rwklx,
65
-
66
-{{if ge .MajorVersion 2}}{{if ge .MinorVersion 8}}
67
-  # suppress ptrace denials when using 'docker ps' or using 'ps' inside a container
68
-  ptrace (trace,read) peer=docker-default,
69
-{{end}}{{end}}
70
-{{if ge .MajorVersion 2}}{{if ge .MinorVersion 9}}
71
-  # docker daemon confinement requires explict allow rule for signal
72
-  signal (receive) set=(kill,term) peer={{.ExecPath}},
73
-{{end}}{{end}}
74
-}
75
-`
76
-
77
-func generateProfile(out io.Writer) error {
78
-	compiled, err := template.New("apparmor_profile").Parse(baseTemplate)
79
-	if err != nil {
80
-		return err
81
-	}
82
-	data := &data{
83
-		Name: "docker-default",
84
-	}
85
-	if tunablesExists() {
86
-		data.Imports = append(data.Imports, "#include <tunables/global>")
87
-	} else {
88
-		data.Imports = append(data.Imports, "@{PROC}=/proc/")
89
-	}
90
-	if abstractionsExists() {
91
-		data.InnerImports = append(data.InnerImports, "#include <abstractions/base>")
92
-	}
93
-	data.MajorVersion, data.MinorVersion, err = aaparser.GetVersion()
94
-	if err != nil {
95
-		return err
96
-	}
97
-	data.ExecPath, err = exec.LookPath("docker")
98
-	if err != nil {
99
-		return err
100
-	}
101
-	if err := compiled.Execute(out, data); err != nil {
102
-		return err
103
-	}
104
-	return nil
105
-}
106
-
107
-// check if the tunables/global exist
108
-func tunablesExists() bool {
109
-	_, err := os.Stat("/etc/apparmor.d/tunables/global")
110
-	return err == nil
111
-}
112
-
113
-// check if abstractions/base exist
114
-func abstractionsExists() bool {
115
-	_, err := os.Stat("/etc/apparmor.d/abstractions/base")
116
-	return err == nil
117
-}
118
-
119
-func installAppArmorProfile() error {
120
-	if !apparmor.IsEnabled() {
121
-		return nil
122
-	}
123
-
124
-	// Make sure /etc/apparmor.d exists
125
-	if err := os.MkdirAll(path.Dir(apparmorProfilePath), 0755); err != nil {
126
-		return err
127
-	}
128
-
129
-	f, err := os.OpenFile(apparmorProfilePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
130
-	if err != nil {
131
-		return err
132
-	}
133
-	if err := generateProfile(f); err != nil {
134
-		f.Close()
135
-		return err
136
-	}
137
-	f.Close()
138
-
139
-	if err := aaparser.LoadProfile(apparmorProfilePath); err != nil {
140
-		return err
141
-	}
142
-
143
-	return nil
144
-}
145
-
146
-func hasAppArmorProfileLoaded(profile string) error {
147
-	file, err := os.Open("/sys/kernel/security/apparmor/profiles")
148
-	if err != nil {
149
-		return err
150
-	}
151
-	r := bufio.NewReader(file)
152
-	for {
153
-		p, err := r.ReadString('\n')
154
-		if err != nil {
155
-			return err
156
-		}
157
-		if strings.HasPrefix(p, profile+" ") {
158
-			return nil
159
-		}
160
-	}
161
-}
... ...
@@ -21,6 +21,7 @@ import (
21 21
 	"github.com/docker/docker/pkg/reexec"
22 22
 	sysinfo "github.com/docker/docker/pkg/system"
23 23
 	"github.com/docker/docker/pkg/term"
24
+	aaprofile "github.com/docker/docker/profiles/apparmor"
24 25
 	"github.com/opencontainers/runc/libcontainer"
25 26
 	"github.com/opencontainers/runc/libcontainer/apparmor"
26 27
 	"github.com/opencontainers/runc/libcontainer/cgroups/systemd"
... ...
@@ -33,6 +34,8 @@ import (
33 33
 const (
34 34
 	DriverName = "native"
35 35
 	Version    = "0.2"
36
+
37
+	defaultApparmorProfile = "docker-default"
36 38
 )
37 39
 
38 40
 // Driver contains all information for native driver,
... ...
@@ -57,13 +60,13 @@ func NewDriver(root string, options []string) (*Driver, error) {
57 57
 	}
58 58
 
59 59
 	if apparmor.IsEnabled() {
60
-		if err := installAppArmorProfile(); err != nil {
61
-			apparmorProfiles := []string{"docker-default"}
60
+		if err := aaprofile.InstallDefault(defaultApparmorProfile); err != nil {
61
+			apparmorProfiles := []string{defaultApparmorProfile}
62 62
 
63 63
 			// Allow daemon to run if loading failed, but are active
64 64
 			// (possibly through another run, manually, or via system startup)
65 65
 			for _, policy := range apparmorProfiles {
66
-				if err := hasAppArmorProfileLoaded(policy); err != nil {
66
+				if err := aaprofile.IsLoaded(policy); err != nil {
67 67
 					return nil, fmt.Errorf("AppArmor enabled on system but the %s profile could not be loaded.", policy)
68 68
 				}
69 69
 			}
70 70
new file mode 100644
... ...
@@ -0,0 +1,110 @@
0
+// +build linux
1
+
2
+package apparmor
3
+
4
+import (
5
+	"bufio"
6
+	"io"
7
+	"os"
8
+	"path"
9
+	"strings"
10
+	"text/template"
11
+
12
+	"github.com/docker/docker/pkg/aaparser"
13
+)
14
+
15
+var (
16
+	// profileDirectory is the file store for apparmor profiles and macros.
17
+	profileDirectory = "/etc/apparmor.d"
18
+	// defaultProfilePath is the default path for the apparmor profile to be saved.
19
+	defaultProfilePath = path.Join(profileDirectory, "docker")
20
+)
21
+
22
+// profileData holds information about the given profile for generation.
23
+type profileData struct {
24
+	// Name is profile name.
25
+	Name string
26
+	// ExecPath is the path to the docker binary.
27
+	ExecPath string
28
+	// Imports defines the apparmor functions to import, before defining the profile.
29
+	Imports []string
30
+	// InnerImports defines the apparmor functions to import in the profile.
31
+	InnerImports []string
32
+	// MajorVersion is the apparmor_parser major version.
33
+	MajorVersion int
34
+	// MinorVersion is the apparmor_parser minor version.
35
+	MinorVersion int
36
+}
37
+
38
+// generateDefault creates an apparmor profile from ProfileData.
39
+func (p *profileData) generateDefault(out io.Writer) error {
40
+	compiled, err := template.New("apparmor_profile").Parse(baseTemplate)
41
+	if err != nil {
42
+		return err
43
+	}
44
+	if macroExists("tunables/global") {
45
+		p.Imports = append(p.Imports, "#include <tunables/global>")
46
+	} else {
47
+		p.Imports = append(p.Imports, "@{PROC}=/proc/")
48
+	}
49
+	if macroExists("abstractions/base") {
50
+		p.InnerImports = append(p.InnerImports, "#include <abstractions/base>")
51
+	}
52
+	if err := compiled.Execute(out, p); err != nil {
53
+		return err
54
+	}
55
+	return nil
56
+}
57
+
58
+// macrosExists checks if the passed macro exists.
59
+func macroExists(m string) bool {
60
+	_, err := os.Stat(path.Join(profileDirectory, m))
61
+	return err == nil
62
+}
63
+
64
+// InstallDefault generates a default profile and installs it in the
65
+// ProfileDirectory with `apparmor_parser`.
66
+func InstallDefault(name string) error {
67
+	// Make sure the path where they want to save the profile exists
68
+	if err := os.MkdirAll(profileDirectory, 0755); err != nil {
69
+		return err
70
+	}
71
+
72
+	p := profileData{
73
+		Name: name,
74
+	}
75
+
76
+	f, err := os.OpenFile(defaultProfilePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
77
+	if err != nil {
78
+		return err
79
+	}
80
+	if err := p.generateDefault(f); err != nil {
81
+		f.Close()
82
+		return err
83
+	}
84
+	f.Close()
85
+
86
+	if err := aaparser.LoadProfile(defaultProfilePath); err != nil {
87
+		return err
88
+	}
89
+
90
+	return nil
91
+}
92
+
93
+// IsLoaded checks if a passed profile as been loaded into the kernel.
94
+func IsLoaded(name string) error {
95
+	file, err := os.Open("/sys/kernel/security/apparmor/profiles")
96
+	if err != nil {
97
+		return err
98
+	}
99
+	r := bufio.NewReader(file)
100
+	for {
101
+		p, err := r.ReadString('\n')
102
+		if err != nil {
103
+			return err
104
+		}
105
+		if strings.HasPrefix(p, name+" ") {
106
+			return nil
107
+		}
108
+	}
109
+}
0 110
new file mode 100644
... ...
@@ -0,0 +1,50 @@
0
+// +build linux
1
+
2
+package apparmor
3
+
4
+// baseTemplate defines the default apparmor profile for containers.
5
+const baseTemplate = `
6
+{{range $value := .Imports}}
7
+{{$value}}
8
+{{end}}
9
+
10
+profile {{.Name}} flags=(attach_disconnected,mediate_deleted) {
11
+{{range $value := .InnerImports}}
12
+  {{$value}}
13
+{{end}}
14
+
15
+  network,
16
+  capability,
17
+  file,
18
+  umount,
19
+
20
+  deny @{PROC}/* w,   # deny write for all files directly in /proc (not in a subdir)
21
+  # deny write to files not in /proc/<number>/** or /proc/sys/**
22
+  deny @{PROC}/{[^1-9],[^1-9][^0-9],[^1-9s][^0-9y][^0-9s],[^1-9][^0-9][^0-9][^0-9]*}/** w,
23
+  deny @{PROC}/sys/[^k]** w,  # deny /proc/sys except /proc/sys/k* (effectively /proc/sys/kernel)
24
+  deny @{PROC}/sys/kernel/{?,??,[^s][^h][^m]**} w,  # deny everything except shm* in /proc/sys/kernel/
25
+  deny @{PROC}/sysrq-trigger rwklx,
26
+  deny @{PROC}/mem rwklx,
27
+  deny @{PROC}/kmem rwklx,
28
+  deny @{PROC}/kcore rwklx,
29
+
30
+  deny mount,
31
+
32
+  deny /sys/[^f]*/** wklx,
33
+  deny /sys/f[^s]*/** wklx,
34
+  deny /sys/fs/[^c]*/** wklx,
35
+  deny /sys/fs/c[^g]*/** wklx,
36
+  deny /sys/fs/cg[^r]*/** wklx,
37
+  deny /sys/firmware/efi/efivars/** rwklx,
38
+  deny /sys/kernel/security/** rwklx,
39
+
40
+{{if ge .MajorVersion 2}}{{if ge .MinorVersion 8}}
41
+  # suppress ptrace denials when using 'docker ps' or using 'ps' inside a container
42
+  ptrace (trace,read) peer=docker-default,
43
+{{end}}{{end}}
44
+{{if ge .MajorVersion 2}}{{if ge .MinorVersion 9}}
45
+  # docker daemon confinement requires explict allow rule for signal
46
+  signal (receive) set=(kill,term) peer={{.ExecPath}},
47
+{{end}}{{end}}
48
+}
49
+`