package e2e_test import ( "fmt" "os" "os/exec" "path" "strconv" "syscall" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" . "github.com/spectrocloud/peg/matcher" ) var installationOutput string var vm VM var _ = Describe("local encrypted passphrase", func() { var config string BeforeEach(func() { RegisterFailHandler(printInstallationOutput) vm = startVM() vm.EventuallyConnects(1200) }) JustBeforeEach(func() { out, err := vm.Sudo(fmt.Sprintf(`cat << EOF > config.yaml %s `, config)) Expect(err).ToNot(HaveOccurred(), out) installationOutput, err = vm.Sudo("set -o pipefail && kairos-agent manual-install --device auto config.yaml 2>&1 | tee manual-install.txt") Expect(err).ToNot(HaveOccurred(), installationOutput) vm.Reboot() }) AfterEach(func() { err := vm.Destroy(func(vm VM) { // Stop TPM emulator tpmPID, err := os.ReadFile(path.Join(vm.StateDir, "tpm", "pid")) Expect(err).ToNot(HaveOccurred()) if len(tpmPID) != 0 { pid, err := strconv.Atoi(string(tpmPID)) Expect(err).ToNot(HaveOccurred()) syscall.Kill(pid, syscall.SIGKILL) } }) Expect(err).ToNot(HaveOccurred()) }) // https://kairos.io/docs/advanced/partition_encryption/#offline-mode When("doing local encryption", func() { BeforeEach(func() { config = `#cloud-config install: encrypted_partitions: - COS_PERSISTENT reboot: false # we will reboot manually hostname: metal-{{ trunc 4 .MachineID }} users: - name: kairos passwd: kairos EOF` }) It("boots and has an encrypted partition", func() { vm.EventuallyConnects(1200) out, err := vm.Sudo("blkid") Expect(err).ToNot(HaveOccurred(), out) Expect(out).To(MatchRegexp("TYPE=\"crypto_LUKS\" PARTLABEL=\"persistent\""), out) }) }) //https://kairos.io/docs/advanced/partition_encryption/#online-mode When("using a remote key management server (automated passphrase generation)", func() { var tpmHash string var err error BeforeEach(func() { tpmHash, err = vm.Sudo("/system/discovery/kcrypt-discovery-challenger") Expect(err).ToNot(HaveOccurred(), tpmHash) kubectlApplyYaml(fmt.Sprintf(`--- apiVersion: keyserver.kairos.io/v1alpha1 kind: SealedVolume metadata: name: %[1]s namespace: default spec: TPMHash: "%[1]s" partitions: - label: COS_PERSISTENT quarantined: false `, tpmHash)) config = fmt.Sprintf(`#cloud-config hostname: metal-{{ trunc 4 .MachineID }} users: - name: kairos passwd: kairos install: encrypted_partitions: - COS_PERSISTENT grub_options: extra_cmdline: "rd.neednet=1" reboot: false # we will reboot manually kcrypt: challenger: challenger_server: "http://%s" nv_index: "" c_index: "" tpm_device: "" EOF`, os.Getenv("KMS_ADDRESS")) }) AfterEach(func() { cmd := exec.Command("kubectl", "delete", "sealedvolume", tpmHash) out, err := cmd.CombinedOutput() Expect(err).ToNot(HaveOccurred(), out) }) It("creates a passphrase and a key/pair to decrypt it", func() { // Expect a LUKS partition vm.EventuallyConnects(1200) out, err := vm.Sudo("blkid") Expect(err).ToNot(HaveOccurred(), out) Expect(out).To(MatchRegexp("TYPE=\"crypto_LUKS\" PARTLABEL=\"persistent\""), out) // Expect a secret to be created cmd := exec.Command("kubectl", "get", "secrets", fmt.Sprintf("%s-cos-persistent", tpmHash), "-o=go-template='{{.data.generated_by|base64decode}}'", ) secretOut, err := cmd.CombinedOutput() Expect(err).ToNot(HaveOccurred()) Expect(string(secretOut)).To(MatchRegexp("tpm")) }) }) // https://kairos.io/docs/advanced/partition_encryption/#scenario-static-keys When("using a remote key management server (static keys)", func() { var tpmHash string var err error BeforeEach(func() { tpmHash, err = vm.Sudo("/system/discovery/kcrypt-discovery-challenger") Expect(err).ToNot(HaveOccurred(), tpmHash) kubectlApplyYaml(fmt.Sprintf(`--- apiVersion: v1 kind: Secret metadata: name: %[1]s namespace: default type: Opaque stringData: pass: "awesome-plaintext-passphrase" `, tpmHash)) kubectlApplyYaml(fmt.Sprintf(`--- apiVersion: keyserver.kairos.io/v1alpha1 kind: SealedVolume metadata: name: %[1]s namespace: default spec: TPMHash: "%[1]s" partitions: - label: COS_PERSISTENT secret: name: %[1]s path: pass quarantined: false `, tpmHash)) config = fmt.Sprintf(`#cloud-config hostname: metal-{{ trunc 4 .MachineID }} users: - name: kairos passwd: kairos install: encrypted_partitions: - COS_PERSISTENT grub_options: extra_cmdline: "rd.neednet=1" reboot: false # we will reboot manually kcrypt: challenger: challenger_server: "http://%s" nv_index: "" c_index: "" tpm_device: "" EOF`, os.Getenv("KMS_ADDRESS")) }) AfterEach(func() { cmd := exec.Command("kubectl", "delete", "sealedvolume", tpmHash) out, err := cmd.CombinedOutput() Expect(err).ToNot(HaveOccurred(), out) cmd = exec.Command("kubectl", "delete", "secret", tpmHash) out, err = cmd.CombinedOutput() Expect(err).ToNot(HaveOccurred(), out) }) It("creates uses the existing passphrase to decrypt it", func() { // Expect a LUKS partition vm.EventuallyConnects(1200) out, err := vm.Sudo("blkid") Expect(err).ToNot(HaveOccurred(), out) Expect(out).To(MatchRegexp("TYPE=\"crypto_LUKS\" PARTLABEL=\"persistent\""), out) Expect(out).To(MatchRegexp("/dev/mapper.*LABEL=\"COS_PERSISTENT\""), out) }) }) }) func printInstallationOutput(message string, callerSkip ...int) { fmt.Printf("This is the installation output in case it's useful:\n%s\n", installationOutput) // Ensures the correct line numbers are reported Fail(message, callerSkip[0]+1) } func kubectlApplyYaml(yamlData string) { yamlFile, err := os.CreateTemp("", "") Expect(err).ToNot(HaveOccurred()) defer os.Remove(yamlFile.Name()) err = os.WriteFile(yamlFile.Name(), []byte(yamlData), 0744) Expect(err).ToNot(HaveOccurred()) cmd := exec.Command("kubectl", "apply", "-f", yamlFile.Name()) out, err := cmd.CombinedOutput() Expect(err).ToNot(HaveOccurred(), out) }