The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/7866
This e-mail was sent by the LXC bot, direct replies will not reach the author unless they happen to be subscribed to this list. === Description (from pull-request) ===
From ec0e232d3fef873b7441df2c75249e73bec397f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Tue, 15 Sep 2020 15:41:25 -0400 Subject: [PATCH 1/7] shared/subprocess: Set err on non-zero MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- shared/subprocess/proc.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/shared/subprocess/proc.go b/shared/subprocess/proc.go index f78b1a1f70..27ee69d7e8 100644 --- a/shared/subprocess/proc.go +++ b/shared/subprocess/proc.go @@ -177,6 +177,9 @@ func (p *Process) start(fds []*os.File) error { exitcode := int64(procstate.ExitCode()) p.exitCode = exitcode + if p.exitCode != 0 { + p.exitErr = fmt.Errorf("Process exited with a non-zero value") + } close(p.chExit) }() From 2bc7219b6078710382c8a8526bc7828737729a49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Tue, 15 Sep 2020 15:25:13 -0400 Subject: [PATCH 2/7] lxd/instances/qemu: Use subprocess MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- lxd/instance/drivers/driver_qemu.go | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go index c3b3d80d35..bab4b84d61 100644 --- a/lxd/instance/drivers/driver_qemu.go +++ b/lxd/instance/drivers/driver_qemu.go @@ -57,6 +57,7 @@ import ( "github.com/lxc/lxd/shared/logger" "github.com/lxc/lxd/shared/logging" "github.com/lxc/lxd/shared/osarch" + "github.com/lxc/lxd/shared/subprocess" "github.com/lxc/lxd/shared/termios" "github.com/lxc/lxd/shared/units" ) @@ -827,13 +828,14 @@ func (vm *qemu) Start(stateful bool) error { forkLimitsCmd = append(forkLimitsCmd, fmt.Sprintf("fd=%d", 3+i)) } - cmd := exec.Command(vm.state.OS.ExecPath, append(forkLimitsCmd, qemuCmd...)...) - var stdout bytes.Buffer - var stderr bytes.Buffer - cmd.Stdout = &stdout - cmd.Stderr = &stderr + // Setup background process. + p, err := subprocess.NewProcess(vm.state.OS.ExecPath, append(forkLimitsCmd, qemuCmd...), vm.EarlyLogFilePath(), vm.EarlyLogFilePath()) + if err != nil { + return err + } // Open any extra files and pass their file handles to qemu command. + files := []*os.File{} for _, file := range fdFiles { info, err := os.Stat(file) if err != nil { @@ -870,12 +872,18 @@ func (vm *qemu) Start(stateful bool) error { defer f.Close() // Close file after qemu has started. } - cmd.ExtraFiles = append(cmd.ExtraFiles, f) + files = append(files, f) } - err = cmd.Run() + err = p.StartWithFiles(files) if err != nil { - err = errors.Wrapf(err, "Failed to run: %s: %s", strings.Join(cmd.Args, " "), strings.TrimSpace(string(stderr.Bytes()))) + return err + } + + _, err = p.Wait() + if err != nil { + stderr, _ := ioutil.ReadFile(vm.EarlyLogFilePath()) + err = errors.Wrapf(err, "Failed to run: %s: %s", strings.Join(p.Args, " "), string(stderr)) op.Done(err) return err } @@ -4389,6 +4397,11 @@ func (vm *qemu) LogPath() string { return shared.LogPath(name) } +// EarlyLogFilePath returns the instance's early log path. +func (vm *qemu) EarlyLogFilePath() string { + return filepath.Join(vm.LogPath(), "qemu.early.log") +} + // LogFilePath returns the instance's log path. func (vm *qemu) LogFilePath() string { return filepath.Join(vm.LogPath(), "qemu.log") From 85ddc644cf42b184aeac11d1b47cce11c503cb1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Tue, 15 Sep 2020 17:44:48 -0400 Subject: [PATCH 3/7] lxd/instance: Add DevPaths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- lxd/instance/drivers/driver_common.go | 9 +++++++++ lxd/instance/drivers/driver_qemu.go | 7 +++++++ lxd/instance/instance_interface.go | 3 +++ 3 files changed, 19 insertions(+) diff --git a/lxd/instance/drivers/driver_common.go b/lxd/instance/drivers/driver_common.go index 27d0fd1c81..7f3e9da6dd 100644 --- a/lxd/instance/drivers/driver_common.go +++ b/lxd/instance/drivers/driver_common.go @@ -11,6 +11,7 @@ import ( // common provides structure common to all instance types. type common struct { dbType instancetype.Type + devPaths []string expandedConfig map[string]string expandedDevices deviceConfig.Devices localConfig map[string]string @@ -77,3 +78,11 @@ func (c *common) expandDevices(profiles []api.Profile) error { return nil } + +// DevPaths() returns a list of /dev devices which the instance requires access to. +// This is function is only safe to call from within the security +// packages as called during instance startup, the rest of the time this +// will likely return nil. +func (c *common) DevPaths() []string { + return c.devPaths +} diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go index bab4b84d61..6a5f7cd2f7 100644 --- a/lxd/instance/drivers/driver_qemu.go +++ b/lxd/instance/drivers/driver_qemu.go @@ -640,6 +640,9 @@ func (vm *qemu) Start(stateful bool) error { revert := revert.New() defer revert.Fail() + // Start accumulating device paths. + vm.devPaths = []string{} + // Mount the instance's config volume. _, err = vm.mount() if err != nil { @@ -2059,6 +2062,10 @@ func (vm *qemu) addDriveConfig(sb *strings.Builder, bootIndexes map[string]int, } } + if !strings.HasPrefix(driveConf.DevPath, "rbd:") { + vm.devPaths = append(vm.devPaths, driveConf.DevPath) + } + return qemuDrive.Execute(sb, map[string]interface{}{ "devName": driveConf.DevName, "devPath": driveConf.DevPath, diff --git a/lxd/instance/instance_interface.go b/lxd/instance/instance_interface.go index f0c117f6e7..bfe9054a70 100644 --- a/lxd/instance/instance_interface.go +++ b/lxd/instance/instance_interface.go @@ -69,6 +69,9 @@ type Instance interface { Delete() error Export(w io.Writer, properties map[string]string) (api.ImageMetadata, error) + // Used for security. + DevPaths() []string + // Live configuration. CGroupSet(key string, value string) error VolatileSet(changes map[string]string) error From 20581ad6585878588f3d9827d90a21be6610e0cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Tue, 15 Sep 2020 17:42:02 -0400 Subject: [PATCH 4/7] lxd/apparmor: Fix unload/delete MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- lxd/apparmor/apparmor.go | 14 +++++++------- lxd/apparmor/instance.go | 4 ++-- lxd/apparmor/instance_forkproxy.go | 4 ++-- lxd/apparmor/network.go | 8 ++++---- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/lxd/apparmor/apparmor.go b/lxd/apparmor/apparmor.go index ffb9491f56..2231964633 100644 --- a/lxd/apparmor/apparmor.go +++ b/lxd/apparmor/apparmor.go @@ -83,9 +83,9 @@ func deleteNamespace(state *state.State, name string) error { // hasProfile checks if the profile is already loaded. func hasProfile(state *state.State, name string) (bool, error) { - mangled := strings.Replace(name, "/", ".", -1) + mangled := strings.Replace(strings.Replace(strings.Replace(name, "/", ".", -1), "<", "", -1), ">", "", -1) - profilesPath := "/sys/kernel/security/apaprmor/policy/profiles" + profilesPath := "/sys/kernel/security/apparmor/policy/profiles" if shared.PathExists(profilesPath) { entries, err := ioutil.ReadDir(profilesPath) if err != nil { @@ -94,7 +94,7 @@ func hasProfile(state *state.State, name string) (bool, error) { for _, entry := range entries { fields := strings.Split(entry.Name(), ".") - if mangled == strings.Join(fields[0:len(fields)-2], ".") { + if mangled == strings.Join(fields[0:len(fields)-1], ".") { return true, nil } } @@ -122,12 +122,12 @@ func loadProfile(state *state.State, name string) error { } // unloadProfile removes the profile from the kernel. -func unloadProfile(state *state.State, name string) error { +func unloadProfile(state *state.State, fullName string, name string) error { if !state.OS.AppArmorAvailable { return nil } - ok, err := hasProfile(state, name) + ok, err := hasProfile(state, fullName) if err != nil { return err } @@ -140,7 +140,7 @@ func unloadProfile(state *state.State, name string) error { } // deleteProfile unloads and delete profile and cache for a profile. -func deleteProfile(state *state.State, name string) error { +func deleteProfile(state *state.State, fullName string, name string) error { if !state.OS.AppArmorAdmin { return nil } @@ -150,7 +150,7 @@ func deleteProfile(state *state.State, name string) error { return err } - err = unloadProfile(state, name) + err = unloadProfile(state, fullName, name) if err != nil { return err } diff --git a/lxd/apparmor/instance.go b/lxd/apparmor/instance.go index 1ae6f64b80..bccd46bf57 100644 --- a/lxd/apparmor/instance.go +++ b/lxd/apparmor/instance.go @@ -93,7 +93,7 @@ func InstanceUnload(state *state.State, inst instance) error { return err } - err = unloadProfile(state, instanceProfileFilename(inst)) + err := unloadProfile(state, InstanceProfileName(inst), instanceProfileFilename(inst)) if err != nil { return err } @@ -108,7 +108,7 @@ func InstanceParse(state *state.State, inst instance) error { // InstanceDelete removes the policy from cache/disk. func InstanceDelete(state *state.State, inst instance) error { - return deleteProfile(state, instanceProfileFilename(inst)) + return deleteProfile(state, InstanceProfileName(inst), instanceProfileFilename(inst)) } // instanceProfile generates the AppArmor profile template from the given instance. diff --git a/lxd/apparmor/instance_forkproxy.go b/lxd/apparmor/instance_forkproxy.go index 3bdb79ed11..96215cd77e 100644 --- a/lxd/apparmor/instance_forkproxy.go +++ b/lxd/apparmor/instance_forkproxy.go @@ -185,10 +185,10 @@ func ForkproxyLoad(state *state.State, inst instance, dev device) error { // ForkproxyUnload ensures that the instances's policy namespace is unloaded to free kernel memory. // This does not delete the policy from disk or cache. func ForkproxyUnload(state *state.State, inst instance, dev device) error { - return unloadProfile(state, forkproxyProfileFilename(inst, dev)) + return unloadProfile(state, ForkproxyProfileName(inst, dev), forkproxyProfileFilename(inst, dev)) } // ForkproxyDelete removes the policy from cache/disk. func ForkproxyDelete(state *state.State, inst instance, dev device) error { - return deleteProfile(state, forkproxyProfileFilename(inst, dev)) + return deleteProfile(state, ForkproxyProfileName(inst, dev), forkproxyProfileFilename(inst, dev)) } diff --git a/lxd/apparmor/network.go b/lxd/apparmor/network.go index f3efe516e5..e5b2698c79 100644 --- a/lxd/apparmor/network.go +++ b/lxd/apparmor/network.go @@ -85,14 +85,14 @@ func NetworkLoad(state *state.State, n network) error { // This does not delete the policy from disk or cache. func NetworkUnload(state *state.State, n network) error { // dnsmasq - err := unloadProfile(state, dnsmasqProfileFilename(n)) + err := unloadProfile(state, DnsmasqProfileName(n), dnsmasqProfileFilename(n)) if err != nil { return err } // forkdns if n.Config()["bridge.mode"] == "fan" { - err := unloadProfile(state, forkdnsProfileFilename(n)) + err := unloadProfile(state, ForkdnsProfileName(n), forkdnsProfileFilename(n)) if err != nil { return err } @@ -103,13 +103,13 @@ func NetworkUnload(state *state.State, n network) error { // NetworkDelete removes the profiles from cache/disk. func NetworkDelete(state *state.State, n network) error { - err := deleteProfile(state, dnsmasqProfileFilename(n)) + err := deleteProfile(state, DnsmasqProfileName(n), dnsmasqProfileFilename(n)) if err != nil { return err } if n.Config()["bridge.mode"] == "fan" { - err := deleteProfile(state, forkdnsProfileFilename(n)) + err := deleteProfile(state, ForkdnsProfileName(n), forkdnsProfileFilename(n)) if err != nil { return err } From 2ece5ff996cf549401dd2871fb2fcb0a5e5f3671 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Tue, 15 Sep 2020 17:45:34 -0400 Subject: [PATCH 5/7] lxd/apparmor/instance: Sort context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- lxd/apparmor/instance.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lxd/apparmor/instance.go b/lxd/apparmor/instance.go index bccd46bf57..d6459e7262 100644 --- a/lxd/apparmor/instance.go +++ b/lxd/apparmor/instance.go @@ -131,15 +131,15 @@ func instanceProfile(state *state.State, inst instance) (string, error) { // Render the profile. var sb *strings.Builder = &strings.Builder{} err = lxcProfileTpl.Execute(sb, map[string]interface{}{ - "feature_unix": unixSupported, "feature_cgns": state.OS.CGInfo.Namespacing, "feature_cgroup2": state.OS.CGInfo.Layout == cgroup.CgroupsUnified || state.OS.CGInfo.Layout == cgroup.CgroupsHybrid, "feature_stacking": state.OS.AppArmorStacking && !state.OS.AppArmorStacked, + "feature_unix": unixSupported, + "name": InstanceProfileName(inst), "namespace": InstanceNamespaceName(inst), "nesting": shared.IsTrue(inst.ExpandedConfig()["security.nesting"]), - "name": InstanceProfileName(inst), - "unprivileged": !shared.IsTrue(inst.ExpandedConfig()["security.privileged"]) || state.OS.RunningInUserNS, "raw": rawContent, + "unprivileged": !shared.IsTrue(inst.ExpandedConfig()["security.privileged"]) || state.OS.RunningInUserNS, }) if err != nil { return "", err From fd082cf26bf1c220a1a146576fc421b8d54a2783 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Tue, 15 Sep 2020 14:04:15 -0400 Subject: [PATCH 6/7] lxd/apparmor: Prepare for qemu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- lxd/apparmor/instance.go | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/lxd/apparmor/instance.go b/lxd/apparmor/instance.go index d6459e7262..ab9880a26f 100644 --- a/lxd/apparmor/instance.go +++ b/lxd/apparmor/instance.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/lxc/lxd/lxd/cgroup" + "github.com/lxc/lxd/lxd/instance/instancetype" "github.com/lxc/lxd/lxd/project" "github.com/lxc/lxd/lxd/state" "github.com/lxc/lxd/shared" @@ -18,6 +19,7 @@ type instance interface { Project() string Name() string ExpandedConfig() map[string]string + Type() instancetype.Type } // InstanceProfileName returns the instance's AppArmor profile name. @@ -43,9 +45,11 @@ func instanceProfileFilename(inst instance) string { // InstanceLoad ensures that the instances's policy is loaded into the kernel so the it can boot. func InstanceLoad(state *state.State, inst instance) error { - err := createNamespace(state, InstanceNamespaceName(inst)) - if err != nil { - return err + if inst.Type() == instancetype.Container { + err := createNamespace(state, InstanceNamespaceName(inst)) + if err != nil { + return err + } } /* In order to avoid forcing a profile parse (potentially slow) on @@ -88,9 +92,11 @@ func InstanceLoad(state *state.State, inst instance) error { // InstanceUnload ensures that the instances's policy namespace is unloaded to free kernel memory. // This does not delete the policy from disk or cache. func InstanceUnload(state *state.State, inst instance) error { - err := deleteNamespace(state, InstanceNamespaceName(inst)) - if err != nil { - return err + if inst.Type() == instancetype.Container { + err := deleteNamespace(state, InstanceNamespaceName(inst)) + if err != nil { + return err + } } err := unloadProfile(state, InstanceProfileName(inst), instanceProfileFilename(inst)) From de3e3680ff59dd1db0b016bf23616d5d13612586 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Tue, 15 Sep 2020 14:04:25 -0400 Subject: [PATCH 7/7] lxd/apparmor: Add qemu profile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- lxd/apparmor/instance.go | 66 ++++++++++++++++++++----- lxd/apparmor/instance_qemu.go | 74 +++++++++++++++++++++++++++++ lxd/instance/drivers/driver_qemu.go | 26 ++++++++++ 3 files changed, 153 insertions(+), 13 deletions(-) create mode 100644 lxd/apparmor/instance_qemu.go diff --git a/lxd/apparmor/instance.go b/lxd/apparmor/instance.go index ab9880a26f..77d266befd 100644 --- a/lxd/apparmor/instance.go +++ b/lxd/apparmor/instance.go @@ -11,6 +11,7 @@ import ( "github.com/lxc/lxd/lxd/instance/instancetype" "github.com/lxc/lxd/lxd/project" "github.com/lxc/lxd/lxd/state" + "github.com/lxc/lxd/lxd/util" "github.com/lxc/lxd/shared" ) @@ -20,6 +21,9 @@ type instance interface { Name() string ExpandedConfig() map[string]string Type() instancetype.Type + LogPath() string + Path() string + DevPaths() []string } // InstanceProfileName returns the instance's AppArmor profile name. @@ -136,19 +140,55 @@ func instanceProfile(state *state.State, inst instance) (string, error) { // Render the profile. var sb *strings.Builder = &strings.Builder{} - err = lxcProfileTpl.Execute(sb, map[string]interface{}{ - "feature_cgns": state.OS.CGInfo.Namespacing, - "feature_cgroup2": state.OS.CGInfo.Layout == cgroup.CgroupsUnified || state.OS.CGInfo.Layout == cgroup.CgroupsHybrid, - "feature_stacking": state.OS.AppArmorStacking && !state.OS.AppArmorStacked, - "feature_unix": unixSupported, - "name": InstanceProfileName(inst), - "namespace": InstanceNamespaceName(inst), - "nesting": shared.IsTrue(inst.ExpandedConfig()["security.nesting"]), - "raw": rawContent, - "unprivileged": !shared.IsTrue(inst.ExpandedConfig()["security.privileged"]) || state.OS.RunningInUserNS, - }) - if err != nil { - return "", err + if inst.Type() == instancetype.Container { + err = lxcProfileTpl.Execute(sb, map[string]interface{}{ + "feature_cgns": state.OS.CGInfo.Namespacing, + "feature_cgroup2": state.OS.CGInfo.Layout == cgroup.CgroupsUnified || state.OS.CGInfo.Layout == cgroup.CgroupsHybrid, + "feature_stacking": state.OS.AppArmorStacking && !state.OS.AppArmorStacked, + "feature_unix": unixSupported, + "name": InstanceProfileName(inst), + "namespace": InstanceNamespaceName(inst), + "nesting": shared.IsTrue(inst.ExpandedConfig()["security.nesting"]), + "raw": rawContent, + "unprivileged": !shared.IsTrue(inst.ExpandedConfig()["security.privileged"]) || state.OS.RunningInUserNS, + }) + if err != nil { + return "", err + } + } else { + rootPath := "" + if shared.InSnap() { + rootPath = "/var/lib/snapd/hostfs" + } + + // AppArmor requires deref of all paths. + path, err := filepath.EvalSymlinks(inst.Path()) + if err != nil { + return "", err + } + + devPaths := inst.DevPaths() + for i := range devPaths { + devPaths[i], err = filepath.EvalSymlinks(devPaths[i]) + if err != nil { + return "", err + } + } + + err = qemuProfileTpl.Execute(sb, map[string]interface{}{ + "devPaths": inst.DevPaths(), + "exePath": util.GetExecPath(), + "libraryPath": strings.Split(os.Getenv("LD_LIBRARY_PATH"), ":"), + "logPath": inst.LogPath(), + "name": InstanceProfileName(inst), + "path": path, + "raw": rawContent, + "rootPath": rootPath, + "snap": shared.InSnap(), + }) + if err != nil { + return "", err + } } return sb.String(), nil diff --git a/lxd/apparmor/instance_qemu.go b/lxd/apparmor/instance_qemu.go new file mode 100644 index 0000000000..7ceb02dc52 --- /dev/null +++ b/lxd/apparmor/instance_qemu.go @@ -0,0 +1,74 @@ +package apparmor + +import ( + "text/template" +) + +var qemuProfileTpl = template.Must(template.New("qemuProfile").Parse(`#include <tunables/global> +profile "{{ .name }}" flags=(attach_disconnected,mediate_deleted) { + #include <abstractions/base> + #include <abstractions/consoles> + #include <abstractions/nameservice> + + capability dac_override, + capability dac_read_search, + capability setgid, + capability setuid, + capability sys_chroot, + capability sys_resource, + + # Needed by qemu + /{,usr/}bin/qemu* mrix, + /dev/hugepages/** w, + /dev/kvm w, + /dev/net/tun w, + /dev/ptmx w, + /dev/vfio/** w, + /dev/vhost-net w, + /dev/vhost-vsock w, + /etc/ceph/** r, + /usr/share/OVMF/OVMF_CODE.fd kr, + owner @{PROC}/@{pid}/task/@{tid}/comm rw, + + # Instance specific paths + {{ .logPath }}/** rwk, + {{ .path }}/qemu.nvram rwk, +{{range $index, $element := .devPaths}} + {{$element}} rwk, +{{- end }} + + # Needed for lxd fork commands + {{ .exePath }} mr, + @{PROC}/@{pid}/cmdline r, + {{ .rootPath }}/{etc,lib,usr/lib}/os-release r, + + # Things that we definitely don't need + deny @{PROC}/@{pid}/cgroup r, + deny /sys/module/apparmor/parameters/enabled r, + deny /sys/kernel/mm/transparent_hugepage/hpage_pmd_size r, + +{{- if .snap }} + # The binary itself (for nesting) + /var/snap/lxd/common/lxd.debug mr, + /snap/lxd/*/bin/lxd mr, + /snap/lxd/*/bin/qemu* mrix, + /snap/lxd/*/share/qemu/OVMF_CODE.fd kr, + + # Snap-specific libraries + /snap/lxd/*/lib/**.so* mr, +{{- end }} + +{{if .libraryPath -}} + # Entries from LD_LIBRARY_PATH +{{range $index, $element := .libraryPath}} + {{$element}}/** mr, +{{- end }} +{{- end }} + +{{- if .raw }} + + ### Configuration: raw.apparmor +{{ .raw }} +{{- end }} +} +`)) diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go index 6a5f7cd2f7..fc1a8ac5cc 100644 --- a/lxd/instance/drivers/driver_qemu.go +++ b/lxd/instance/drivers/driver_qemu.go @@ -27,6 +27,7 @@ import ( "gopkg.in/yaml.v2" lxdClient "github.com/lxc/lxd/client" + "github.com/lxc/lxd/lxd/apparmor" "github.com/lxc/lxd/lxd/backup" "github.com/lxc/lxd/lxd/cluster" "github.com/lxc/lxd/lxd/db" @@ -505,6 +506,12 @@ func (vm *qemu) Freeze() error { // onStop is run when the instance stops. func (vm *qemu) onStop(target string) error { + ctxMap := log.Ctx{ + "project": vm.project, + "name": vm.name, + "ephemeral": vm.ephemeral, + } + // Pick up the existing stop operation lock created in Stop() function. op := operationlock.Get(vm.id) if op != nil && op.Action() != "stop" { @@ -526,6 +533,13 @@ func (vm *qemu) onStop(target string) error { return err } + // Unload the apparmor profile + err = apparmor.InstanceUnload(vm.state, vm) + if err != nil { + ctxMap["err"] = err + logger.Error("Failed to unload AppArmor profile", ctxMap) + } + if target == "reboot" { err = vm.Start(false) } else if vm.ephemeral { @@ -837,6 +851,15 @@ func (vm *qemu) Start(stateful bool) error { return err } + // Load the AppArmor profile + err = apparmor.InstanceLoad(vm.state, vm) + if err != nil { + op.Done(err) + return err + } + + p.SetApparmor(apparmor.InstanceProfileName(vm)) + // Open any extra files and pass their file handles to qemu command. files := []*os.File{} for _, file := range fdFiles { @@ -3230,6 +3253,9 @@ func (vm *qemu) cleanup() { vm.removeUnixDevices() vm.removeDiskDevices() + // Remove the security profiles + apparmor.InstanceDelete(vm.state, vm) + // Remove the devices path os.Remove(vm.DevicesPath())
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel