github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/interfaces/system_key_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2018 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 interfaces_test 21 22 import ( 23 "bytes" 24 "encoding/json" 25 "fmt" 26 "io/ioutil" 27 "os" 28 "path/filepath" 29 "reflect" 30 "strings" 31 32 . "gopkg.in/check.v1" 33 34 "github.com/snapcore/snapd/dirs" 35 "github.com/snapcore/snapd/interfaces" 36 "github.com/snapcore/snapd/osutil" 37 "github.com/snapcore/snapd/sandbox/apparmor" 38 "github.com/snapcore/snapd/sandbox/cgroup" 39 "github.com/snapcore/snapd/sandbox/seccomp" 40 "github.com/snapcore/snapd/testutil" 41 ) 42 43 type systemKeySuite struct { 44 testutil.BaseTest 45 46 tmp string 47 apparmorFeatures string 48 buildID string 49 seccompCompilerVersion seccomp.VersionInfo 50 } 51 52 var _ = Suite(&systemKeySuite{}) 53 54 func (s *systemKeySuite) SetUpTest(c *C) { 55 s.BaseTest.SetUpTest(c) 56 57 s.tmp = c.MkDir() 58 dirs.SetRootDir(s.tmp) 59 err := os.MkdirAll(filepath.Dir(dirs.SnapSystemKeyFile), 0755) 60 c.Assert(err, IsNil) 61 err = os.MkdirAll(dirs.DistroLibExecDir, 0755) 62 c.Assert(err, IsNil) 63 err = os.Symlink("/proc/self/exe", filepath.Join(dirs.DistroLibExecDir, "snapd")) 64 c.Assert(err, IsNil) 65 66 s.apparmorFeatures = filepath.Join(s.tmp, "/sys/kernel/security/apparmor/features") 67 s.buildID = "this-is-my-build-id" 68 69 s.seccompCompilerVersion = seccomp.VersionInfo("123 2.3.3 abcdef123 -") 70 testutil.MockCommand(c, filepath.Join(dirs.DistroLibExecDir, "snap-seccomp"), fmt.Sprintf(` 71 if [ "$1" = "version-info" ]; then echo "%s"; exit 0; fi 72 exit 1 73 `, s.seccompCompilerVersion)) 74 75 s.AddCleanup(seccomp.MockActions([]string{"allow", "errno", "kill", "log", "trace", "trap"})) 76 } 77 78 func (s *systemKeySuite) TearDownTest(c *C) { 79 s.BaseTest.TearDownTest(c) 80 81 dirs.SetRootDir("/") 82 } 83 84 func (s *systemKeySuite) testInterfaceWriteSystemKey(c *C, nfsHome, overlayRoot bool) { 85 var overlay string 86 if overlayRoot { 87 overlay = "overlay" 88 } 89 restore := interfaces.MockIsHomeUsingNFS(func() (bool, error) { return nfsHome, nil }) 90 defer restore() 91 92 restore = interfaces.MockReadBuildID(func(p string) (string, error) { 93 c.Assert(p, Equals, filepath.Join(dirs.DistroLibExecDir, "snapd")) 94 return s.buildID, nil 95 }) 96 defer restore() 97 98 restore = interfaces.MockIsRootWritableOverlay(func() (string, error) { return overlay, nil }) 99 defer restore() 100 101 restore = cgroup.MockVersion(1, nil) 102 defer restore() 103 104 err := interfaces.WriteSystemKey() 105 c.Assert(err, IsNil) 106 107 systemKey, err := ioutil.ReadFile(dirs.SnapSystemKeyFile) 108 c.Assert(err, IsNil) 109 110 kernelFeatures, _ := apparmor.KernelFeatures() 111 112 apparmorFeaturesStr, err := json.Marshal(kernelFeatures) 113 c.Assert(err, IsNil) 114 115 apparmorParserMtime, err := json.Marshal(apparmor.ParserMtime()) 116 c.Assert(err, IsNil) 117 118 parserFeatures, _ := apparmor.ParserFeatures() 119 apparmorParserFeaturesStr, err := json.Marshal(parserFeatures) 120 c.Assert(err, IsNil) 121 122 seccompActionsStr, err := json.Marshal(seccomp.Actions()) 123 c.Assert(err, IsNil) 124 125 compiler, err := seccomp.NewCompiler(func(name string) (string, error) { 126 return filepath.Join(dirs.DistroLibExecDir, "snap-seccomp"), nil 127 }) 128 c.Assert(err, IsNil) 129 seccompCompilerVersion, err := compiler.VersionInfo() 130 c.Assert(err, IsNil) 131 c.Assert(seccompCompilerVersion, Equals, s.seccompCompilerVersion) 132 133 c.Check(string(systemKey), testutil.EqualsWrapped, fmt.Sprintf(`{"version":%d,"build-id":"%s","apparmor-features":%s,"apparmor-parser-mtime":%s,"apparmor-parser-features":%s,"nfs-home":%v,"overlay-root":%q,"seccomp-features":%s,"seccomp-compiler-version":"%s","cgroup-version":"1"}`, 134 interfaces.SystemKeyVersion, 135 s.buildID, 136 apparmorFeaturesStr, 137 apparmorParserMtime, 138 apparmorParserFeaturesStr, 139 nfsHome, 140 overlay, 141 seccompActionsStr, 142 seccompCompilerVersion, 143 )) 144 } 145 146 func (s *systemKeySuite) TestInterfaceWriteSystemKeyNoNFS(c *C) { 147 s.testInterfaceWriteSystemKey(c, false, false) 148 } 149 150 func (s *systemKeySuite) TestInterfaceWriteSystemKeyWithNFS(c *C) { 151 s.testInterfaceWriteSystemKey(c, true, false) 152 } 153 154 func (s *systemKeySuite) TestInterfaceWriteSystemKeyWithOverlayRoot(c *C) { 155 s.testInterfaceWriteSystemKey(c, false, true) 156 } 157 158 // bonus points to someone who actually runs this 159 func (s *systemKeySuite) TestInterfaceWriteSystemKeyWithNFSWithOverlayRoot(c *C) { 160 s.testInterfaceWriteSystemKey(c, true, true) 161 } 162 163 func (s *systemKeySuite) TestInterfaceWriteSystemKeyErrorOnBuildID(c *C) { 164 restore := interfaces.MockIsHomeUsingNFS(func() (bool, error) { return false, nil }) 165 defer restore() 166 167 restore = interfaces.MockReadBuildID(func(p string) (string, error) { 168 c.Assert(p, Equals, filepath.Join(dirs.DistroLibExecDir, "snapd")) 169 return "", fmt.Errorf("no build ID for you") 170 }) 171 defer restore() 172 173 err := interfaces.WriteSystemKey() 174 c.Assert(err, ErrorMatches, "no build ID for you") 175 } 176 177 func (s *systemKeySuite) TestInterfaceSystemKeyMismatchHappy(c *C) { 178 s.AddCleanup(interfaces.MockSystemKey(` 179 { 180 "build-id": "7a94e9736c091b3984bd63f5aebfc883c4d859e0", 181 "apparmor-features": ["caps", "dbus"] 182 } 183 `)) 184 185 // no system-key yet -> Error 186 c.Assert(osutil.FileExists(dirs.SnapSystemKeyFile), Equals, false) 187 _, err := interfaces.SystemKeyMismatch() 188 c.Assert(err, Equals, interfaces.ErrSystemKeyMissing) 189 190 // create a system-key -> no mismatch anymore 191 err = interfaces.WriteSystemKey() 192 c.Assert(err, IsNil) 193 mismatch, err := interfaces.SystemKeyMismatch() 194 c.Assert(err, IsNil) 195 c.Check(mismatch, Equals, false) 196 197 // change our system-key to have more apparmor features 198 s.AddCleanup(interfaces.MockSystemKey(` 199 { 200 "build-id": "7a94e9736c091b3984bd63f5aebfc883c4d859e0", 201 "apparmor-features": ["caps", "dbus", "more", "and", "more"] 202 } 203 `)) 204 mismatch, err = interfaces.SystemKeyMismatch() 205 c.Assert(err, IsNil) 206 c.Check(mismatch, Equals, true) 207 } 208 209 func (s *systemKeySuite) TestInterfaceSystemKeyMismatchParserMtimeHappy(c *C) { 210 s.AddCleanup(interfaces.MockSystemKey(` 211 { 212 "build-id": "7a94e9736c091b3984bd63f5aebfc883c4d859e0", 213 "apparmor-parser-mtime": 1234 214 } 215 `)) 216 217 // no system-key yet -> Error 218 c.Assert(osutil.FileExists(dirs.SnapSystemKeyFile), Equals, false) 219 _, err := interfaces.SystemKeyMismatch() 220 c.Assert(err, Equals, interfaces.ErrSystemKeyMissing) 221 222 // create a system-key -> no mismatch anymore 223 err = interfaces.WriteSystemKey() 224 c.Assert(err, IsNil) 225 mismatch, err := interfaces.SystemKeyMismatch() 226 c.Assert(err, IsNil) 227 c.Check(mismatch, Equals, false) 228 229 // change our system-key to have a different parser mtime 230 s.AddCleanup(interfaces.MockSystemKey(` 231 { 232 "build-id": "7a94e9736c091b3984bd63f5aebfc883c4d859e0", 233 "apparmor-parser-mtime": 5678 234 } 235 `)) 236 mismatch, err = interfaces.SystemKeyMismatch() 237 c.Assert(err, IsNil) 238 c.Check(mismatch, Equals, true) 239 } 240 241 func (s *systemKeySuite) TestInterfaceSystemKeyMismatchVersions(c *C) { 242 // we calculcate v1 243 s.AddCleanup(interfaces.MockSystemKey(` 244 { 245 "version":1, 246 "build-id": "7a94e9736c091b3984bd63f5aebfc883c4d859e0" 247 }`)) 248 // and the on-disk version is v2 249 err := ioutil.WriteFile(dirs.SnapSystemKeyFile, []byte(` 250 { 251 "version":2, 252 "build-id": "7a94e9736c091b3984bd63f5aebfc883c4d859e0" 253 }`), 0644) 254 c.Assert(err, IsNil) 255 256 // when we encounter different versions we get the right error 257 _, err = interfaces.SystemKeyMismatch() 258 c.Assert(err, Equals, interfaces.ErrSystemKeyVersion) 259 } 260 261 func (s *systemKeySuite) TestStaticVersion(c *C) { 262 // this is a static check to ensure we remember to bump the 263 // version when we add fields 264 // 265 // *** IF THIS FAILS, YOU NEED TO BUMP THE VERSION BEFORE "FIXING" THIS *** 266 var sk interfaces.SystemKey 267 268 // XXX: this checks needs to become smarter once we remove or change 269 // existing fields, in which case the version will gets a bump but the 270 // number of fields decreases or remains unchanged 271 c.Check(reflect.ValueOf(sk).NumField(), Equals, interfaces.SystemKeyVersion) 272 273 c.Check(fmt.Sprintf("%+v", sk), Equals, "{"+strings.Join([]string{ 274 "Version:0", 275 "BuildID:", 276 "AppArmorFeatures:[]", 277 "AppArmorParserMtime:0", 278 "AppArmorParserFeatures:[]", 279 "NFSHome:false", 280 "OverlayRoot:", 281 "SecCompActions:[]", 282 "SeccompCompilerVersion:", 283 "CgroupVersion:", 284 }, " ")+"}") 285 } 286 287 func (s *systemKeySuite) TestRecordedSystemKey(c *C) { 288 _, err := interfaces.RecordedSystemKey() 289 c.Check(err, Equals, interfaces.ErrSystemKeyMissing) 290 291 restore := interfaces.MockSystemKey(` 292 { 293 "build-id": "7a94e9736c091b3984bd63f5aebfc883c4d859e0", 294 "apparmor-features": ["caps"] 295 } 296 `) 297 defer restore() 298 299 c.Assert(interfaces.WriteSystemKey(), IsNil) 300 301 // just to ensure we really re-read it from the disk with RecordedSystemKey 302 interfaces.MockSystemKey(`{"build-id":"foo"}`) 303 304 key, err := interfaces.RecordedSystemKey() 305 c.Assert(err, IsNil) 306 307 sysKey, ok := key.(*interfaces.SystemKey) 308 c.Assert(ok, Equals, true) 309 c.Check(sysKey.BuildID, Equals, "7a94e9736c091b3984bd63f5aebfc883c4d859e0") 310 } 311 312 func (s *systemKeySuite) TestCurrentSystemKey(c *C) { 313 restore := interfaces.MockSystemKey(`{"build-id": "7a94e9736c091b3984bd63f5aebfc883c4d859e0"}`) 314 defer restore() 315 316 key, err := interfaces.CurrentSystemKey() 317 c.Assert(err, IsNil) 318 sysKey, ok := key.(*interfaces.SystemKey) 319 c.Assert(ok, Equals, true) 320 c.Check(sysKey.BuildID, Equals, "7a94e9736c091b3984bd63f5aebfc883c4d859e0") 321 } 322 323 func (s *systemKeySuite) TestSystemKeysMatch(c *C) { 324 _, err := interfaces.SystemKeysMatch(nil, nil) 325 c.Check(err, ErrorMatches, `SystemKeysMatch: arguments are not system keys`) 326 327 restore := interfaces.MockSystemKey(`{"build-id": "7a94e9736c091b3984bd63f5aebfc883c4d859e0"}`) 328 defer restore() 329 330 key1, err := interfaces.CurrentSystemKey() 331 c.Assert(err, IsNil) 332 333 _, err = interfaces.SystemKeysMatch(key1, nil) 334 c.Check(err, ErrorMatches, `SystemKeysMatch: arguments are not system keys`) 335 336 _, err = interfaces.SystemKeysMatch(nil, key1) 337 c.Check(err, ErrorMatches, `SystemKeysMatch: arguments are not system keys`) 338 339 interfaces.MockSystemKey(`{"build-id": "8888e9736c091b3984bd63f5aebfc883c4d85988"}`) 340 key2, err := interfaces.CurrentSystemKey() 341 c.Assert(err, IsNil) 342 343 ok, err := interfaces.SystemKeysMatch(key1, key2) 344 c.Assert(err, IsNil) 345 c.Check(ok, Equals, false) 346 347 key3, err := interfaces.CurrentSystemKey() 348 c.Assert(err, IsNil) 349 350 ok, err = interfaces.SystemKeysMatch(key2, key3) 351 c.Assert(err, IsNil) 352 c.Check(ok, Equals, true) 353 } 354 355 func (s *systemKeySuite) TestSystemKeysUnmarshalSame(c *C) { 356 // whitespace here simulates the serialization across HTTP, etc. that should 357 // not trigger any differences 358 // use a full system-key to fully test serialization, etc. 359 systemKeyJSON := ` 360 { 361 "apparmor-features": [ 362 "caps", 363 "dbus", 364 "domain", 365 "file", 366 "mount", 367 "namespaces", 368 "network", 369 "network_v8", 370 "policy", 371 "ptrace", 372 "query", 373 "rlimit", 374 "signal" 375 ], 376 "apparmor-parser-features": [], 377 "apparmor-parser-mtime": 1589907589, 378 "build-id": "cb94e5eeee4cf7ecda53f8308a984cb155b55732", 379 "cgroup-version": "1", 380 "nfs-home": false, 381 "overlay-root": "", 382 "seccomp-compiler-version": "e6e309ad8aee052e5aa695dfaa040328ae1559c5 2.4.3 9b218ef9a4e508dd8a7f848095cb8875d10a4bf28428ad81fdc3f8dac89108f7 bpf-actlog", 383 "seccomp-features": [ 384 "allow", 385 "errno", 386 "kill_process", 387 "kill_thread", 388 "log", 389 "trace", 390 "trap", 391 "user_notif" 392 ], 393 "version": 10 394 }` 395 396 // write the mocked system key to disk 397 restore := interfaces.MockSystemKey(systemKeyJSON) 398 defer restore() 399 err := interfaces.WriteSystemKey() 400 c.Assert(err, IsNil) 401 402 // now unmarshal the specific json to a system key object 403 key1, err := interfaces.UnmarshalJSONSystemKey(bytes.NewBuffer([]byte(systemKeyJSON))) 404 c.Assert(err, IsNil) 405 406 // now read the system key from disk 407 key2, err := interfaces.RecordedSystemKey() 408 c.Assert(err, IsNil) 409 410 // the two system-keys should be the same 411 ok, err := interfaces.SystemKeysMatch(key1, key2) 412 c.Assert(err, IsNil) 413 c.Check(ok, Equals, true, Commentf("key1:%#v key2:%#v", key1, key2)) 414 }