// Setup creates a conf file with list of kernel modules required by given snap, // writes it in /etc/modules-load.d/ directory and immediately loads the modules // using /sbin/modprobe. The devMode is ignored. // // If the method fails it should be re-tried (with a sensible strategy) by the caller. func (b *Backend) Setup(snapInfo *snap.Info, confinement interfaces.ConfinementOptions, repo *interfaces.Repository) error { snapName := snapInfo.Name() // Get the snippets that apply to this snap snippets, err := repo.SecuritySnippetsForSnap(snapInfo.Name(), interfaces.SecurityKMod) if err != nil { return fmt.Errorf("cannot obtain kmod security snippets for snap %q: %s", snapName, err) } // Get the files that this snap should have glob := interfaces.SecurityTagGlob(snapName) content, modules, err := b.combineSnippets(snapInfo, snippets) if err != nil { return fmt.Errorf("cannot obtain expected security files for snap %q: %s", snapName, err) } dir := dirs.SnapKModModulesDir if err := os.MkdirAll(dir, 0755); err != nil { return fmt.Errorf("cannot create directory for kmod files %q: %s", dir, err) } changed, _, err := osutil.EnsureDirState(dirs.SnapKModModulesDir, glob, content) if err != nil { return err } if len(changed) > 0 { return loadModules(modules) } return nil }
// Remove removes dbus configuration files of a given snap. // // This method should be called after removing a snap. func (b *Backend) Remove(snapName string) error { glob := fmt.Sprintf("%s.conf", interfaces.SecurityTagGlob(snapName)) _, _, err := osutil.EnsureDirState(dirs.SnapBusPolicyDir, glob, nil) if err != nil { return fmt.Errorf("cannot synchronize DBus configuration files for snap %q: %s", snapName, err) } return nil }
// Remove removes seccomp profiles of a given snap. func (b *Backend) Remove(snapName string) error { glob := interfaces.SecurityTagGlob(snapName) _, _, err := osutil.EnsureDirState(dirs.SnapSeccompDir, glob, nil) if err != nil { return fmt.Errorf("cannot synchronize security files for snap %q: %s", snapName, err) } return nil }
// Remove removes and unloads apparmor profiles of a given snap. func (b *Backend) Remove(snapName string) error { glob := interfaces.SecurityTagGlob(snapName) _, removed, errEnsure := osutil.EnsureDirState(dirs.SnapAppArmorDir, glob, nil) errUnload := unloadProfiles(removed) if errEnsure != nil { return fmt.Errorf("cannot synchronize security files for snap %q: %s", snapName, errEnsure) } return errUnload }
func (b *Backend) Setup(snapInfo *snap.Info, confinement interfaces.ConfinementOptions, repo *interfaces.Repository) error { snapName := snapInfo.Name() rawSnippets, err := repo.SecuritySnippetsForSnap(snapInfo.Name(), interfaces.SecuritySystemd) if err != nil { return fmt.Errorf("cannot obtain systemd security snippets for snap %q: %s", snapName, err) } snippets, err := unmarshalRawSnippetMap(rawSnippets) if err != nil { return fmt.Errorf("cannot unmarshal systemd snippets for snap %q: %s", snapName, err) } snippet, err := mergeSnippetMap(snippets) if err != nil { return fmt.Errorf("cannot merge systemd snippets for snap %q: %s", snapName, err) } content, err := renderSnippet(snippet) if err != nil { return fmt.Errorf("cannot render systemd snippets for snap %q: %s", snapName, err) } dir := dirs.SnapServicesDir if err := os.MkdirAll(dir, 0755); err != nil { return fmt.Errorf("cannot create directory for systemd services %q: %s", dir, err) } glob := interfaces.InterfaceServiceName(snapName, "*") systemd := sysd.New(dirs.GlobalRootDir, &dummyReporter{}) // We need to be carefully here and stop all removed service units before // we remove their files as otherwise systemd is not able to disable/stop // them anymore. if err := disableRemovedServices(systemd, dir, glob, content); err != nil { logger.Noticef("cannot stop removed services: %s", err) } changed, removed, errEnsure := osutil.EnsureDirState(dir, glob, content) // Reload systemd whenever something is added or removed if len(changed) > 0 || len(removed) > 0 { err := systemd.DaemonReload() if err != nil { logger.Noticef("cannot reload systemd state: %s", err) } } // Ensure the service is running right now and on reboots for _, service := range changed { if err := systemd.Enable(service); err != nil { logger.Noticef("cannot enable service %q: %s", service, err) } // If we have a new service here which isn't started yet the restart // operation will start it. if err := systemd.Restart(service, 10*time.Second); err != nil { logger.Noticef("cannot restart service %q: %s", service, err) } } return errEnsure }
func (s *EnsureDirStateSuite) TestRemovesUnexpectedFiless(c *C) { name := filepath.Join(s.dir, "evil.snap") err := ioutil.WriteFile(name, []byte(`evil text`), 0600) c.Assert(err, IsNil) changed, removed, err := osutil.EnsureDirState(s.dir, s.glob, map[string]*osutil.FileState{}) c.Assert(err, IsNil) // Removed file is reported c.Assert(changed, HasLen, 0) c.Assert(removed, DeepEquals, []string{"evil.snap"}) // The file is removed _, err = os.Stat(name) c.Assert(os.IsNotExist(err), Equals, true) }
func (s *EnsureDirStateSuite) TestIgnoresUnrelatedFiles(c *C) { name := filepath.Join(s.dir, "unrelated") err := ioutil.WriteFile(name, []byte(`text`), 0600) c.Assert(err, IsNil) changed, removed, err := osutil.EnsureDirState(s.dir, s.glob, map[string]*osutil.FileState{}) c.Assert(err, IsNil) // Report says that nothing has changed c.Assert(changed, HasLen, 0) c.Assert(removed, HasLen, 0) // The file is still there _, err = os.Stat(name) c.Assert(err, IsNil) }
func (b *Backend) Setup(snapInfo *snap.Info, devMode bool, repo *interfaces.Repository) error { snapName := snapInfo.Name() rawSnippets, err := repo.SecuritySnippetsForSnap(snapInfo.Name(), interfaces.SecuritySystemd) if err != nil { return fmt.Errorf("cannot obtain systemd security snippets for snap %q: %s", snapName, err) } snippets, err := unmarshalRawSnippetMap(rawSnippets) if err != nil { return fmt.Errorf("cannot unmarshal systemd snippets for snap %q: %s", snapName, err) } snippet, err := mergeSnippetMap(snippets) if err != nil { return fmt.Errorf("cannot merge systemd snippets for snap %q: %s", snapName, err) } content, err := renderSnippet(snippet) if err != nil { return fmt.Errorf("cannot render systemd snippets for snap %q: %s", snapName, err) } dir := dirs.SnapServicesDir if err := os.MkdirAll(dir, 0755); err != nil { return fmt.Errorf("cannot create directory for systemd services %q: %s", dir, err) } glob := interfaces.InterfaceServiceName(snapName, "*") changed, removed, errEnsure := osutil.EnsureDirState(dir, glob, content) systemd := sysd.New(dirs.GlobalRootDir, &dummyReporter{}) // Reload systemd whenever something is added or removed if len(changed) > 0 || len(removed) > 0 { err := systemd.DaemonReload() if err != nil { logger.Noticef("cannot reload systemd state: %s", err) } } // Start any new services for _, service := range changed { err := systemd.Start(service) if err != nil { logger.Noticef("cannot start service %q: %s", service, err) } } // Stop any removed services for _, service := range removed { err := systemd.Stop(service, 10*time.Second) if err != nil { logger.Noticef("cannot stop service %q: %s", service, err) } } return errEnsure }
func (s *EnsureDirStateSuite) TestCreatesMissingFiles(c *C) { name := filepath.Join(s.dir, "missing.snap") changed, removed, err := osutil.EnsureDirState(s.dir, s.glob, map[string]*osutil.FileState{ "missing.snap": {Content: []byte(`content`), Mode: 0600}, }) c.Assert(err, IsNil) // Created file is reported c.Assert(changed, DeepEquals, []string{"missing.snap"}) c.Assert(removed, HasLen, 0) // The content is correct content, err := ioutil.ReadFile(name) c.Assert(err, IsNil) c.Assert(content, DeepEquals, []byte("content")) // The permissions are correct stat, err := os.Stat(name) c.Assert(err, IsNil) c.Assert(stat.Mode().Perm(), Equals, os.FileMode(0600)) }
func (b *Backend) Remove(snapName string) error { systemd := sysd.New(dirs.GlobalRootDir, &dummyReporter{}) // Remove all the files matching snap glob glob := interfaces.InterfaceServiceName(snapName, "*") _, removed, errEnsure := osutil.EnsureDirState(dirs.SnapServicesDir, glob, nil) for _, service := range removed { if err := systemd.DisableNow(service); err != nil { logger.Noticef("cannot disable service %q: %s", service, err) } } // Reload systemd whenever something is removed if len(removed) > 0 { err := systemd.DaemonReload() if err != nil { logger.Noticef("cannot reload systemd state: %s", err) } } return errEnsure }
func (s *EnsureDirStateSuite) TestVerifiesExpectedFiles(c *C) { name := filepath.Join(s.dir, "expected.snap") err := ioutil.WriteFile(name, []byte("expected"), 0600) c.Assert(err, IsNil) changed, removed, err := osutil.EnsureDirState(s.dir, s.glob, map[string]*osutil.FileState{ "expected.snap": {Content: []byte("expected"), Mode: 0600}, }) c.Assert(err, IsNil) // Report says that nothing has changed c.Assert(changed, HasLen, 0) c.Assert(removed, HasLen, 0) // The content is correct content, err := ioutil.ReadFile(path.Join(s.dir, "expected.snap")) c.Assert(err, IsNil) c.Assert(content, DeepEquals, []byte("expected")) // The permissions are correct stat, err := os.Stat(name) c.Assert(err, IsNil) c.Assert(stat.Mode().Perm(), Equals, os.FileMode(0600)) }
func (s *EnsureDirStateSuite) TestCorrectsFilesWithSameSize(c *C) { name := filepath.Join(s.dir, "differing.snap") err := ioutil.WriteFile(name, []byte("evil"), 0600) c.Assert(err, IsNil) changed, removed, err := osutil.EnsureDirState(s.dir, s.glob, map[string]*osutil.FileState{ "differing.snap": {Content: []byte("good"), Mode: 0600}, }) c.Assert(err, IsNil) // changed file is reported c.Assert(changed, DeepEquals, []string{"differing.snap"}) c.Assert(removed, HasLen, 0) // The content is changed content, err := ioutil.ReadFile(name) c.Assert(err, IsNil) c.Assert(content, DeepEquals, []byte("good")) // The permissions are what we expect stat, err := os.Stat(name) c.Assert(err, IsNil) c.Assert(stat.Mode().Perm(), Equals, os.FileMode(0600)) }
func (s *EnsureDirStateSuite) TestRemovesAllManagedFilesOnError(c *C) { // Create a "prior.snap" file prior := filepath.Join(s.dir, "prior.snap") err := ioutil.WriteFile(prior, []byte("data"), 0600) c.Assert(err, IsNil) // Create a "clash.snap" directory to simulate failure clash := filepath.Join(s.dir, "clash.snap") err = os.Mkdir(clash, 0000) c.Assert(err, IsNil) // Try to ensure directory state changed, removed, err := osutil.EnsureDirState(s.dir, s.glob, map[string]*osutil.FileState{ "prior.snap": {Content: []byte("data"), Mode: 0600}, "clash.snap": {Content: []byte("data"), Mode: 0600}, }) c.Assert(changed, HasLen, 0) c.Assert(removed, DeepEquals, []string{"clash.snap", "prior.snap"}) c.Assert(err, ErrorMatches, "rename .* .*/clash.snap: is a directory") // The clashing file is removed _, err = os.Stat(clash) c.Assert(os.IsNotExist(err), Equals, true) }
func (s *EnsureDirStateSuite) TestFixesFilesWithBadPermissions(c *C) { name := filepath.Join(s.dir, "sensitive.snap") // NOTE: the existing file is currently wide-open for everyone" err := ioutil.WriteFile(name, []byte("password"), 0666) c.Assert(err, IsNil) changed, removed, err := osutil.EnsureDirState(s.dir, s.glob, map[string]*osutil.FileState{ // NOTE: we want the file to be private "sensitive.snap": {Content: []byte("password"), Mode: 0600}, }) c.Assert(err, IsNil) // changed file is reported c.Assert(changed, DeepEquals, []string{"sensitive.snap"}) c.Assert(removed, HasLen, 0) // The content is still the same content, err := ioutil.ReadFile(name) c.Assert(err, IsNil) c.Assert(content, DeepEquals, []byte("password")) // The permissions are changed stat, err := os.Stat(name) c.Assert(err, IsNil) c.Assert(stat.Mode().Perm(), Equals, os.FileMode(0600)) }
// Setup creates seccomp profiles specific to a given snap. // The snap can be in developer mode to make security violations non-fatal to // the offending application process. // // This method should be called after changing plug, slots, connections between // them or application present in the snap. func (b *Backend) Setup(snapInfo *snap.Info, devMode bool, repo *interfaces.Repository) error { snapName := snapInfo.Name() // Get the snippets that apply to this snap snippets, err := repo.SecuritySnippetsForSnap(snapInfo.Name(), interfaces.SecuritySecComp) if err != nil { return fmt.Errorf("cannot obtain security snippets for snap %q: %s", snapName, err) } // Get the files that this snap should have content, err := b.combineSnippets(snapInfo, devMode, snippets) if err != nil { return fmt.Errorf("cannot obtain expected security files for snap %q: %s", snapName, err) } glob := interfaces.SecurityTagGlob(snapName) dir := dirs.SnapSeccompDir if err := os.MkdirAll(dir, 0755); err != nil { return fmt.Errorf("cannot create directory for seccomp profiles %q: %s", dir, err) } _, _, err = osutil.EnsureDirState(dir, glob, content) if err != nil { return fmt.Errorf("cannot synchronize security files for snap %q: %s", snapName, err) } return nil }
// Setup creates dbus configuration files specific to a given snap. // // DBus has no concept of a complain mode so confinment type is ignored. func (b *Backend) Setup(snapInfo *snap.Info, opts interfaces.ConfinementOptions, repo *interfaces.Repository) error { snapName := snapInfo.Name() // Get the snippets that apply to this snap snippets, err := repo.SecuritySnippetsForSnap(snapInfo.Name(), interfaces.SecurityDBus) if err != nil { return fmt.Errorf("cannot obtain DBus security snippets for snap %q: %s", snapName, err) } // Get the files that this snap should have content, err := b.combineSnippets(snapInfo, snippets) if err != nil { return fmt.Errorf("cannot obtain expected DBus configuration files for snap %q: %s", snapName, err) } glob := fmt.Sprintf("%s.conf", interfaces.SecurityTagGlob(snapName)) dir := dirs.SnapBusPolicyDir if err := os.MkdirAll(dir, 0755); err != nil { return fmt.Errorf("cannot create directory for DBus configuration files %q: %s", dir, err) } _, _, err = osutil.EnsureDirState(dir, glob, content) if err != nil { return fmt.Errorf("cannot synchronize DBus configuration files for snap %q: %s", snapName, err) } return nil }
// Setup creates and loads apparmor profiles specific to a given snap. // The snap can be in developer mode to make security violations non-fatal to // the offending application process. // // This method should be called after changing plug, slots, connections between // them or application present in the snap. func (b *Backend) Setup(snapInfo *snap.Info, opts interfaces.ConfinementOptions, repo *interfaces.Repository) error { snapName := snapInfo.Name() // Get the snippets that apply to this snap snippets, err := repo.SecuritySnippetsForSnap(snapName, interfaces.SecurityAppArmor) if err != nil { return fmt.Errorf("cannot obtain security snippets for snap %q: %s", snapName, err) } // Get the files that this snap should have content, err := b.combineSnippets(snapInfo, opts, snippets) if err != nil { return fmt.Errorf("cannot obtain expected security files for snap %q: %s", snapName, err) } glob := interfaces.SecurityTagGlob(snapInfo.Name()) dir := dirs.SnapAppArmorDir if err := os.MkdirAll(dir, 0755); err != nil { return fmt.Errorf("cannot create directory for apparmor profiles %q: %s", dir, err) } _, removed, errEnsure := osutil.EnsureDirState(dir, glob, content) // NOTE: load all profiles instead of just the changed profiles. We're // relying on apparmor cache to make this efficient. This gives us // certainty that each call to Setup ends up with working profiles. all := make([]string, 0, len(content)) for name := range content { all = append(all, name) } sort.Strings(all) errReload := reloadProfiles(all) errUnload := unloadProfiles(removed) if errEnsure != nil { return fmt.Errorf("cannot synchronize security files for snap %q: %s", snapName, errEnsure) } if errReload != nil { return errReload } return errUnload }
func (s *EnsureDirStateSuite) TestReportsAbnormalFileLocation(c *C) { c.Assert(func() { osutil.EnsureDirState(s.dir, s.glob, map[string]*osutil.FileState{"subdir/file.snap": {}}) }, PanicMatches, `EnsureDirState got filename "subdir/file.snap" which has a path component`) }
func (s *EnsureDirStateSuite) TestReportsAbnormalFileName(c *C) { c.Assert(func() { osutil.EnsureDirState(s.dir, s.glob, map[string]*osutil.FileState{"without-namespace": {}}) }, PanicMatches, `EnsureDirState got filename "without-namespace" which doesn't match the glob pattern "\*\.snap"`) }
func (s *EnsureDirStateSuite) TestReportsAbnormalPatterns(c *C) { c.Assert(func() { osutil.EnsureDirState(s.dir, "[", nil) }, PanicMatches, `EnsureDirState got invalid pattern "\[": syntax error in pattern`) }
// Remove removes modules config file specific to a given snap. // // This method should be called after removing a snap. // // If the method fails it should be re-tried (with a sensible strategy) by the caller. func (b *Backend) Remove(snapName string) error { glob := interfaces.SecurityTagGlob(snapName) _, _, err := osutil.EnsureDirState(dirs.SnapKModModulesDir, glob, nil) return err }