forked from security/hakurei
This should hopefully provide good separation between the artifact curing backend implementation and the (still work in progress) language. Making the IR parseable also guarantees uniqueness of the representation. Signed-off-by: Ophestra <cat@gensokyo.uk>
269 lines
6.4 KiB
Go
269 lines
6.4 KiB
Go
//go:build testtool
|
|
|
|
package main
|
|
|
|
import (
|
|
"encoding/gob"
|
|
"log"
|
|
"net"
|
|
"os"
|
|
"path"
|
|
"reflect"
|
|
"slices"
|
|
"strings"
|
|
|
|
"hakurei.app/container/check"
|
|
"hakurei.app/container/fhs"
|
|
"hakurei.app/container/vfs"
|
|
)
|
|
|
|
func main() {
|
|
log.SetFlags(0)
|
|
log.SetPrefix("testtool: ")
|
|
|
|
var hostNet, layers, promote bool
|
|
if len(os.Args) == 2 && os.Args[0] == "testtool" {
|
|
switch os.Args[1] {
|
|
case "net":
|
|
hostNet = true
|
|
log.SetPrefix("testtool(net): ")
|
|
break
|
|
|
|
case "layers":
|
|
layers = true
|
|
log.SetPrefix("testtool(layers): ")
|
|
break
|
|
|
|
case "promote":
|
|
promote = true
|
|
log.SetPrefix("testtool(promote): ")
|
|
|
|
default:
|
|
log.Fatalf("Args: %q", os.Args)
|
|
return
|
|
}
|
|
} else if wantArgs := []string{"testtool"}; !slices.Equal(os.Args, wantArgs) {
|
|
log.Fatalf("Args: %q, want %q", os.Args, wantArgs)
|
|
}
|
|
|
|
var overlayRoot bool
|
|
wantEnv := []string{"HAKUREI_TEST=1"}
|
|
if len(os.Environ()) == 2 {
|
|
overlayRoot = true
|
|
if !layers && !promote {
|
|
log.SetPrefix("testtool(overlay root): ")
|
|
}
|
|
wantEnv = []string{"HAKUREI_TEST=1", "HAKUREI_ROOT=1"}
|
|
}
|
|
if !slices.Equal(wantEnv, os.Environ()) {
|
|
log.Fatalf("Environ: %q, want %q", os.Environ(), wantEnv)
|
|
}
|
|
|
|
var overlayWork bool
|
|
const (
|
|
wantExec = "/opt/bin/testtool"
|
|
wantExecWork = "/work/bin/testtool"
|
|
)
|
|
var iftPath string
|
|
if got, err := os.Executable(); err != nil {
|
|
log.Fatalf("Executable: error = %v", err)
|
|
} else {
|
|
iftPath = path.Join(path.Dir(path.Dir(got)), "ift")
|
|
|
|
if got != wantExec {
|
|
switch got {
|
|
case wantExecWork:
|
|
overlayWork = true
|
|
log.SetPrefix("testtool(overlay work): ")
|
|
|
|
default:
|
|
log.Fatalf("Executable: %q, want %q", got, wantExec)
|
|
}
|
|
}
|
|
}
|
|
|
|
wantHostname := "cure"
|
|
if hostNet {
|
|
wantHostname += "-net"
|
|
}
|
|
|
|
if hostname, err := os.Hostname(); err != nil {
|
|
log.Fatalf("Hostname: error = %v", err)
|
|
} else if hostname != wantHostname {
|
|
log.Fatalf("Hostname: %q, want %q", hostname, wantHostname)
|
|
}
|
|
|
|
var m *vfs.MountInfo
|
|
if f, err := os.Open(fhs.Proc + "self/mountinfo"); err != nil {
|
|
log.Fatalf("Open: error = %v", err)
|
|
} else {
|
|
err = vfs.NewMountInfoDecoder(f).Decode(&m)
|
|
closeErr := f.Close()
|
|
if err != nil {
|
|
log.Fatalf("Decode: error = %v", err)
|
|
}
|
|
if closeErr != nil {
|
|
log.Fatalf("Close: error = %v", err)
|
|
}
|
|
}
|
|
|
|
if ift, err := net.Interfaces(); err != nil {
|
|
log.Fatal(err)
|
|
} else if !hostNet {
|
|
if len(ift) != 1 || ift[0].Name != "lo" {
|
|
log.Fatalln("got interfaces", strings.Join(slices.Collect(func(yield func(ifn string) bool) {
|
|
for _, ifi := range ift {
|
|
if !yield(ifi.Name) {
|
|
break
|
|
}
|
|
}
|
|
}), ", "))
|
|
}
|
|
} else {
|
|
var iftParent []net.Interface
|
|
|
|
var r *os.File
|
|
if r, err = os.Open(iftPath); err != nil {
|
|
log.Fatal(err)
|
|
} else {
|
|
err = gob.NewDecoder(r).Decode(&iftParent)
|
|
closeErr := r.Close()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
if closeErr != nil {
|
|
log.Fatal(closeErr)
|
|
}
|
|
}
|
|
|
|
if !reflect.DeepEqual(ift, iftParent) {
|
|
log.Fatalf("Interfaces: %#v, want %#v", ift, iftParent)
|
|
}
|
|
}
|
|
|
|
const checksumEmptyDir = "MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU"
|
|
ident := "dztPS6jRjiZtCF4_p8AzfnxGp6obkhrgFVsxdodbKWUoAEVtDz3MykepJB4kI_ks"
|
|
log.Println(m)
|
|
next := func() { m = m.Next; log.Println(m) }
|
|
|
|
if overlayRoot {
|
|
ident = "RdMA-mubnrHuu3Ky1wWyxauSYCO0ZH_zCPUj3uDHqkfwv5sGcByoF_g5PjlGiClb"
|
|
|
|
if m.Root != "/" || m.Target != "/" ||
|
|
m.Source != "overlay" || m.FsType != "overlay" {
|
|
log.Fatal("unexpected root mount entry")
|
|
}
|
|
var lowerdir string
|
|
for _, o := range strings.Split(m.FsOptstr, ",") {
|
|
const lowerdirKey = "lowerdir="
|
|
if strings.HasPrefix(o, lowerdirKey) {
|
|
lowerdir = o[len(lowerdirKey):]
|
|
}
|
|
}
|
|
if !layers {
|
|
if path.Base(lowerdir) != checksumEmptyDir {
|
|
log.Fatal("unexpected artifact checksum")
|
|
}
|
|
} else {
|
|
ident = "p1t_drXr34i-jZNuxDMLaMOdL6tZvQqhavNafGynGqxOZoXAUTSn7kqNh3Ovv3DT"
|
|
|
|
lowerdirsEscaped := strings.Split(lowerdir, ":")
|
|
lowerdirs := lowerdirsEscaped[:0]
|
|
// ignore the option separator since it does not appear in ident
|
|
for i, e := range lowerdirsEscaped {
|
|
if len(e) > 0 &&
|
|
e[len(e)-1] == check.SpecialOverlayEscape[0] &&
|
|
(len(e) == 1 || e[len(e)-2] != check.SpecialOverlayEscape[0]) {
|
|
// ignore escaped pathname separator since it does not
|
|
// appear in ident
|
|
|
|
e = e[:len(e)-1]
|
|
if len(lowerdirsEscaped) != i {
|
|
lowerdirsEscaped[i+1] = e + lowerdirsEscaped[i+1]
|
|
continue
|
|
}
|
|
}
|
|
lowerdirs = append(lowerdirs, e)
|
|
}
|
|
|
|
if len(lowerdirs) != 2 ||
|
|
path.Base(lowerdirs[0]) != "MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU" ||
|
|
path.Base(lowerdirs[1]) != "nY_CUdiaUM1OL4cPr5TS92FCJ3rCRV7Hm5oVTzAvMXwC03_QnTRfQ5PPs7mOU9fK" {
|
|
log.Fatalf("unexpected lowerdirs %s", strings.Join(lowerdirs, ", "))
|
|
}
|
|
}
|
|
} else {
|
|
if hostNet {
|
|
ident = "G8qPxD9puvvoOVV7lrT80eyDeIl3G_CCFoKw12c8mCjMdG1zF7NEPkwYpNubClK3"
|
|
}
|
|
|
|
if m.Root != "/sysroot" || m.Target != "/" {
|
|
log.Fatal("unexpected root mount entry")
|
|
}
|
|
|
|
next()
|
|
if path.Base(m.Root) != "OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb" {
|
|
log.Fatal("unexpected file artifact checksum")
|
|
}
|
|
|
|
next()
|
|
if path.Base(m.Root) != checksumEmptyDir {
|
|
log.Fatal("unexpected artifact checksum")
|
|
}
|
|
}
|
|
|
|
if promote {
|
|
ident = "xXTIYcXmgJWNLC91c417RRrNM9cjELwEZHpGvf8Fk_GNP5agRJp_SicD0w9aMeLJ"
|
|
}
|
|
|
|
next() // testtool artifact
|
|
|
|
next()
|
|
if overlayWork {
|
|
ident = "5hlaukCirnXE4W_RSLJFOZN47Z5RiHnacXzdFp_70cLgiJUGR6cSb_HaFftkzi0-"
|
|
if m.Root != "/" || m.Target != "/work" ||
|
|
m.Source != "overlay" || m.FsType != "overlay" {
|
|
log.Fatal("unexpected work mount entry")
|
|
}
|
|
} else {
|
|
if path.Base(m.Root) != ident || m.Target != "/work" {
|
|
log.Fatal("unexpected work mount entry")
|
|
}
|
|
}
|
|
|
|
next()
|
|
if path.Base(m.Root) != ident || m.Target != "/tmp" {
|
|
log.Fatal("unexpected temp mount entry")
|
|
}
|
|
|
|
next()
|
|
if m.Root != "/" || m.Target != "/proc" || m.Source != "proc" || m.FsType != "proc" {
|
|
log.Fatal("unexpected proc mount entry")
|
|
}
|
|
|
|
next()
|
|
if m.Root != "/" || m.Target != "/dev" || m.Source != "devtmpfs" || m.FsType != "tmpfs" {
|
|
log.Fatal("unexpected dev mount entry")
|
|
}
|
|
|
|
for i := 0; i < 9; i++ { // private /dev entries
|
|
next()
|
|
}
|
|
|
|
if m.Next != nil {
|
|
log.Println("unexpected extra mount entries")
|
|
for m.Next != nil {
|
|
next()
|
|
}
|
|
os.Exit(1)
|
|
}
|
|
|
|
checkData := []byte{0}
|
|
if hostNet {
|
|
checkData = []byte("net")
|
|
}
|
|
if err := os.WriteFile("check", checkData, 0400); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|