github.com/coreos/mantle@v0.13.0/kola/tests/misc/verity.go (about) 1 // Copyright 2016 CoreOS, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package misc 16 17 import ( 18 "bytes" 19 "fmt" 20 "strings" 21 22 "github.com/coreos/mantle/kola/cluster" 23 "github.com/coreos/mantle/kola/register" 24 "github.com/coreos/mantle/kola/tests/util" 25 "github.com/coreos/mantle/platform" 26 ) 27 28 func init() { 29 register.Register(®ister.Test{ 30 Run: Verity, 31 ClusterSize: 1, 32 Name: "cl.verity", 33 Distros: []string{"cl"}, 34 }) 35 } 36 37 func Verity(c cluster.TestCluster) { 38 c.Run("verify", VerityVerify) 39 // modifies disk; must run last 40 c.Run("corruption", VerityCorruption) 41 } 42 43 // Verity verification tests. 44 // TODO(mischief): seems like a good candidate for kolet. 45 46 // VerityVerify asserts that the filesystem mounted on /usr matches the 47 // dm-verity hash that is embedded in the CoreOS kernel. 48 func VerityVerify(c cluster.TestCluster) { 49 m := c.Machines()[0] 50 51 // extract verity hash from kernel 52 hash := c.MustSSH(m, "dd if=/boot/coreos/vmlinuz-a skip=64 count=64 bs=1 status=none") 53 54 // find /usr dev 55 usrdev := util.GetUsrDeviceNode(c, m) 56 57 // figure out partition size for hash dev offset 58 offset := c.MustSSH(m, "sudo e2size "+usrdev) 59 offset = bytes.TrimSpace(offset) 60 61 c.MustSSH(m, fmt.Sprintf("sudo veritysetup verify --verbose --hash-offset=%s %s %s %s", offset, usrdev, usrdev, hash)) 62 } 63 64 // VerityCorruption asserts that a machine will fail to read a file from a 65 // verify filesystem whose blocks have been modified. 66 func VerityCorruption(c cluster.TestCluster) { 67 m := c.Machines()[0] 68 // skip unless we are actually using verity 69 skipUnlessVerity(c, m) 70 71 // assert that dm shows verity is in use and the device is valid (V) 72 out := c.MustSSH(m, "sudo dmsetup --target verity status usr") 73 74 fields := strings.Fields(string(out)) 75 if len(fields) != 4 { 76 c.Fatalf("failed checking dmsetup status of usr: not enough fields in output (got %d)", len(fields)) 77 } 78 79 if fields[3] != "V" { 80 c.Fatalf("dmsetup status usr reports verity is not valid!") 81 } 82 83 // corrupt a file on disk and flush disk caches. 84 // try setting NAME=CoreOS to NAME=LulzOS in /usr/lib/os-release 85 86 // get usr device, probably vda3 87 usrdev := util.GetUsrDeviceNode(c, m) 88 89 // poke bytes into /usr/lib/os-release 90 c.MustSSH(m, fmt.Sprintf(`echo NAME=LulzOS | sudo dd of=%s seek=$(expr $(sudo debugfs -R "blocks /lib/os-release" %s 2>/dev/null) \* 4096) bs=1 status=none`, usrdev, usrdev)) 91 92 // make sure we flush everything so cat has to go through to the device backing verity. 93 c.MustSSH(m, "sudo /bin/sh -c 'sync; echo -n 3 >/proc/sys/vm/drop_caches'") 94 95 // read the file back. if we can read it successfully, verity did not do its job. 96 out, stderr, err := m.SSH("cat /usr/lib/os-release") 97 if err == nil { 98 c.Fatalf("verity did not prevent reading a corrupted file!") 99 } 100 for _, line := range strings.Split(string(stderr), "\n") { 101 // cat is expected to fail on EIO; report other errors 102 if line != "cat: /usr/lib/os-release: Input/output error" { 103 c.Log(line) 104 } 105 } 106 107 // assert that dm shows verity device is now corrupted (C) 108 out = c.MustSSH(m, "sudo dmsetup --target verity status usr") 109 110 fields = strings.Fields(string(out)) 111 if len(fields) != 4 { 112 c.Fatalf("failed checking dmsetup status of usr: not enough fields in output (got %d)", len(fields)) 113 } 114 115 if fields[3] != "C" { 116 c.Fatalf("dmsetup status usr reports verity is valid after corruption!") 117 } 118 } 119 120 func skipUnlessVerity(c cluster.TestCluster, m platform.Machine) { 121 // figure out if we are actually using verity 122 out, err := c.SSH(m, "sudo veritysetup status usr") 123 if err != nil && bytes.Equal(out, []byte("/dev/mapper/usr is inactive.")) { 124 // verity not in use, so skip. 125 c.Skip("verity is not enabled") 126 } else if err != nil { 127 c.Fatalf("failed checking verity status: %s: %v", out, err) 128 } 129 }