gitee.com/mysnapcore/mysnapd@v0.1.0/interfaces/kmod/backend.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 // Package kmod implements a backend which loads kernel modules on behalf of 21 // interfaces. 22 // 23 // Interfaces may request kernel modules to be loaded by providing snippets via 24 // their respective "*Snippet" methods for interfaces.SecurityKMod security 25 // system. The snippet should contain a newline-separated list of requested 26 // kernel modules. The KMod backend stores all the modules needed by given 27 // snap in /etc/modules-load.d/snap.<snapname>.conf file ensuring they are 28 // loaded when the system boots and also loads these modules via modprobe. 29 // If a snap is uninstalled or respective interface gets disconnected, the 30 // corresponding /etc/modules-load.d/ config file gets removed, however no 31 // kernel modules are unloaded. This is by design. 32 // 33 // Note: this mechanism should not be confused with kernel-module-interface; 34 // kmod only loads a well-defined list of modules provided by interface definition 35 // and doesn't grant any special permissions related to kernel modules to snaps, 36 // in contrast to kernel-module-interface. 37 package kmod 38 39 import ( 40 "bytes" 41 "fmt" 42 "os" 43 "sort" 44 45 "gitee.com/mysnapcore/mysnapd/dirs" 46 "gitee.com/mysnapcore/mysnapd/interfaces" 47 "gitee.com/mysnapcore/mysnapd/osutil" 48 "gitee.com/mysnapcore/mysnapd/snap" 49 "gitee.com/mysnapcore/mysnapd/timings" 50 ) 51 52 // Backend is responsible for maintaining kernel modules 53 type Backend struct { 54 preseed bool 55 } 56 57 // Initialize does nothing. 58 func (b *Backend) Initialize(opts *interfaces.SecurityBackendOptions) error { 59 if opts != nil && opts.Preseed { 60 b.preseed = true 61 } 62 return nil 63 } 64 65 // Name returns the name of the backend. 66 func (b *Backend) Name() interfaces.SecuritySystem { 67 return "kmod" 68 } 69 70 // setupModules creates a conf file with list of kernel modules required by 71 // given snap, writes it in /etc/modules-load.d/ directory and immediately 72 // loads the modules using /sbin/modprobe. The devMode is ignored. 73 func (b *Backend) setupModules(snapInfo *snap.Info, spec *Specification) error { 74 content, modules := deriveContent(spec, snapInfo) 75 // synchronize the content with the filesystem 76 glob := interfaces.SecurityTagGlob(snapInfo.InstanceName()) 77 dir := dirs.SnapKModModulesDir 78 if err := os.MkdirAll(dir, 0755); err != nil { 79 return fmt.Errorf("cannot create directory for kmod files %q: %s", dir, err) 80 } 81 82 changed, _, err := osutil.EnsureDirState(dirs.SnapKModModulesDir, glob, content) 83 if err != nil { 84 return err 85 } 86 87 if len(changed) > 0 { 88 b.loadModules(modules) 89 } 90 return nil 91 } 92 93 // setupModprobe creates a configuration file under /etc/modprobe.d/ according 94 // to the specification: this allows to either specify the load parameters for 95 // a module, or prevent it from being loaded. 96 // TODO: consider whether 97 // - a newly blocklisted module should get unloaded 98 // - a module whose option change should get reloaded 99 func (b *Backend) setupModprobe(snapInfo *snap.Info, spec *Specification) error { 100 dir := dirs.SnapKModModprobeDir 101 if err := os.MkdirAll(dir, 0755); err != nil { 102 return fmt.Errorf("cannot create directory for kmod files %q: %s", dir, err) 103 } 104 105 glob := interfaces.SecurityTagGlob(snapInfo.InstanceName()) 106 dirContents := prepareModprobeDirContents(spec, snapInfo) 107 _, _, err := osutil.EnsureDirState(dirs.SnapKModModprobeDir, glob, dirContents) 108 if err != nil { 109 return err 110 } 111 112 return nil 113 } 114 115 // Setup will make the kmod backend generate the needed system files (such as 116 // those under /etc/modules-load.d/ and /etc/modprobe.d/) and call the 117 // appropriate system commands so that the desired kernel module configuration 118 // will be applied. 119 // The devMode is ignored. 120 // 121 // If the method fails it should be re-tried (with a sensible strategy) by the caller. 122 func (b *Backend) Setup(snapInfo *snap.Info, confinement interfaces.ConfinementOptions, repo *interfaces.Repository, tm timings.Measurer) error { 123 snapName := snapInfo.InstanceName() 124 // Get the snippets that apply to this snap 125 spec, err := repo.SnapSpecification(b.Name(), snapName) 126 if err != nil { 127 return fmt.Errorf("cannot obtain kmod specification for snap %q: %s", snapName, err) 128 } 129 130 err = b.setupModprobe(snapInfo, spec.(*Specification)) 131 if err != nil { 132 return err 133 } 134 135 err = b.setupModules(snapInfo, spec.(*Specification)) 136 if err != nil { 137 return err 138 } 139 140 return nil 141 } 142 143 // Remove removes modules config file specific to a given snap. 144 // 145 // This method should be called after removing a snap. 146 // 147 // If the method fails it should be re-tried (with a sensible strategy) by the caller. 148 func (b *Backend) Remove(snapName string) error { 149 glob := interfaces.SecurityTagGlob(snapName) 150 var errors []error 151 if _, _, err := osutil.EnsureDirState(dirs.SnapKModModulesDir, glob, nil); err != nil { 152 errors = append(errors, err) 153 } 154 155 if _, _, err := osutil.EnsureDirState(dirs.SnapKModModprobeDir, glob, nil); err != nil { 156 errors = append(errors, err) 157 } 158 159 if len(errors) > 0 { 160 return fmt.Errorf("cannot remove kernel modules config files: %v", errors) 161 } 162 163 return nil 164 } 165 166 func deriveContent(spec *Specification, snapInfo *snap.Info) (map[string]osutil.FileState, []string) { 167 if len(spec.modules) == 0 { 168 return nil, nil 169 } 170 content := make(map[string]osutil.FileState) 171 var modules []string 172 for k := range spec.modules { 173 modules = append(modules, k) 174 } 175 sort.Strings(modules) 176 177 var buffer bytes.Buffer 178 buffer.WriteString("# This file is automatically generated.\n") 179 for _, module := range modules { 180 buffer.WriteString(module) 181 buffer.WriteRune('\n') 182 } 183 content[fmt.Sprintf("%s.conf", snap.SecurityTag(snapInfo.InstanceName()))] = &osutil.MemoryFileState{ 184 Content: buffer.Bytes(), 185 Mode: 0644, 186 } 187 return content, modules 188 } 189 190 func prepareModprobeDirContents(spec *Specification, snapInfo *snap.Info) map[string]osutil.FileState { 191 disallowedModules := spec.DisallowedModules() 192 if len(disallowedModules) == 0 && len(spec.moduleOptions) == 0 { 193 return nil 194 } 195 196 contents := "# Generated by snapd. Do not edit\n\n" 197 // First, write down the list of disallowed modules 198 for _, module := range disallowedModules { 199 contents += fmt.Sprintf("blacklist %s\n", module) 200 } 201 // Then, write down the module options 202 for module, options := range spec.moduleOptions { 203 contents += fmt.Sprintf("options %s %s\n", module, options) 204 } 205 206 fileName := fmt.Sprintf("%s.conf", snap.SecurityTag(snapInfo.InstanceName())) 207 return map[string]osutil.FileState{ 208 fileName: &osutil.MemoryFileState{ 209 Content: []byte(contents), 210 Mode: 0644, 211 }, 212 } 213 } 214 215 func (b *Backend) NewSpecification() interfaces.Specification { 216 return &Specification{} 217 } 218 219 // SandboxFeatures returns the list of features supported by snapd for loading kernel modules. 220 func (b *Backend) SandboxFeatures() []string { 221 return []string{"mediated-modprobe"} 222 }