From 4780228c958f8ab845407453a3f5cc635fb032ae Mon Sep 17 00:00:00 2001 From: Adphi Date: Tue, 28 Feb 2023 12:01:18 +0100 Subject: [PATCH] luks: implements support for centos Signed-off-by: Adphi --- builder.go | 33 ++++++++++++++++------------- e2e/e2e_test.go | 42 ++++++++++++++++++++++++++----------- templates/centos.Dockerfile | 12 +++++++++-- 3 files changed, 59 insertions(+), 28 deletions(-) diff --git a/builder.go b/builder.go index d06f952..259b85a 100644 --- a/builder.go +++ b/builder.go @@ -150,10 +150,6 @@ func NewBuilder(ctx context.Context, workdir, imgTag, disk string, size uint64, return nil, err } if luksPassword != "" { - // TODO(adphi): remove this check when we support luks encryption on other distros - if osRelease.ID == ReleaseCentOS { - return nil, fmt.Errorf("luks encryption is not supported on centos") - } if !splitBoot { return nil, fmt.Errorf("luks encryption requires split boot") } @@ -314,11 +310,7 @@ func (b *builder) mountImg(ctx context.Context) error { return err } b.bootPart = fmt.Sprintf("/dev/mapper/%sp1", filepath.Base(b.loDevice)) - if b.splitBoot { - b.rootPart = fmt.Sprintf("/dev/mapper/%sp2", filepath.Base(b.loDevice)) - } else { - b.rootPart = b.bootPart - } + b.rootPart = ifElse(b.splitBoot, fmt.Sprintf("/dev/mapper/%sp2", filepath.Base(b.loDevice)), b.bootPart) if b.isLuksEnabled() { logrus.Infof("encrypting root partition") f, err := os.CreateTemp("", "key") @@ -408,7 +400,10 @@ func diskUUID(ctx context.Context, disk string) (string, error) { func (b *builder) setupRootFS(ctx context.Context) (err error) { logrus.Infof("setting up rootfs") - b.rootUUID, err = diskUUID(ctx, b.rootPart) + b.rootUUID, err = diskUUID(ctx, ifElse(b.isLuksEnabled(), b.mappedCryptRoot, b.rootPart)) + if err != nil { + return err + } var fstab string if b.splitBoot { b.bootUUID, err = diskUUID(ctx, b.bootPart) @@ -503,12 +498,15 @@ func (b *builder) installKernel(ctx context.Context) error { } var cfg string if b.isLuksEnabled() { - if b.osRelease.ID != ReleaseAlpine { - cfg = fmt.Sprintf(sysconfig, b.rootUUID, fmt.Sprintf("%s root=/dev/mapper/root cryptopts=target=root,source=UUID=%s", b.cmdLineExtra, b.cryptUUID)) - cfg = strings.Replace(cfg, "root=UUID="+b.rootUUID, "", 1) - } else { + switch b.osRelease.ID { + case ReleaseAlpine: cfg = fmt.Sprintf(sysconfig, b.rootUUID, fmt.Sprintf("%s root=/dev/mapper/root cryptdm=root", b.cmdLineExtra)) cfg = strings.Replace(cfg, "root=UUID="+b.rootUUID, "cryptroot=UUID="+b.cryptUUID, 1) + case ReleaseCentOS: + cfg = fmt.Sprintf(sysconfig, b.rootUUID, fmt.Sprintf("%s rd.luks.name=UUID=%s rd.luks.uuid=%s rd.luks.crypttab=0", b.cmdLineExtra, b.rootUUID, b.cryptUUID)) + default: + cfg = fmt.Sprintf(sysconfig, b.rootUUID, fmt.Sprintf("%s root=/dev/mapper/root cryptopts=target=root,source=UUID=%s", b.cmdLineExtra, b.cryptUUID)) + cfg = strings.Replace(cfg, "root=UUID="+b.rootUUID, "", 1) } } else { cfg = fmt.Sprintf(sysconfig, b.rootUUID, b.cmdLineExtra) @@ -577,3 +575,10 @@ func checkDependencies() error { func OutputFormats() []string { return formats[:] } + +func ifElse(v bool, t string, f string) string { + if v { + return t + } + return f +} diff --git a/e2e/e2e_test.go b/e2e/e2e_test.go index 9b81b93..5b4dcdd 100644 --- a/e2e/e2e_test.go +++ b/e2e/e2e_test.go @@ -38,11 +38,16 @@ type test struct { args []string } -var images = []string{ - "alpine:3.17", - "ubuntu:20.04", - "debian:11", - "centos:8", +type img struct { + name string + luks string +} + +var images = []img{ + {name: "alpine:3.17", luks: "Enter passphrase for /dev/sda2:"}, + {name: "ubuntu:20.04", luks: "Please unlock disk root:"}, + {name: "debian:11", luks: "Please unlock disk root:"}, + {name: "centos:8", luks: "Please enter passphrase for disk"}, } func TestConvert(t *testing.T) { @@ -55,6 +60,10 @@ func TestConvert(t *testing.T) { name: "split-boot", args: []string{"--split-boot"}, }, + { + name: "luks", + args: []string{"--luks-password=root"}, + }, } for _, tt := range tests { @@ -64,21 +73,21 @@ func TestConvert(t *testing.T) { require.NoError(os.MkdirAll(dir, os.ModePerm)) defer os.RemoveAll(dir) - for _, i := range images { - t.Run(i, func(t *testing.T) { + for _, img := range images { + t.Run(img.name, func(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() // t.Parallel() require := require2.New(t) - out := filepath.Join(dir, strings.NewReplacer(":", "-", ".", "-").Replace(i)+".qcow2") + out := filepath.Join(dir, strings.NewReplacer(":", "-", ".", "-").Replace(img.name)+".qcow2") if _, err := os.Stat(out); err == nil { require.NoError(os.Remove(out)) } - - require.NoError(docker.RunD2VM(ctx, d2vm.Image, d2vm.Version, dir, dir, "convert", append([]string{"-p", "root", "-o", "/out/" + filepath.Base(out), "-v", i}, tt.args...)...)) + + require.NoError(docker.RunD2VM(ctx, d2vm.Image, d2vm.Version, dir, dir, "convert", append([]string{"-p", "root", "-o", "/out/" + filepath.Base(out), "-v", "--keep-cache", img.name}, tt.args...)...)) inr, inw := io.Pipe() defer inr.Close() @@ -93,6 +102,9 @@ func TestConvert(t *testing.T) { password := []byte("Password:") s := bufio.NewScanner(outr) s.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) { + if i := bytes.Index(data, []byte(img.luks)); i >= 0 { + return i + len(img.luks), []byte(img.luks), nil + } if i := bytes.Index(data, login); i >= 0 { return i + len(login), login, nil } @@ -106,8 +118,14 @@ func TestConvert(t *testing.T) { }) for s.Scan() { b := s.Bytes() + if bytes.Contains(b, []byte(img.luks)) { + t.Logf("sending luks password") + if _, err := inw.Write([]byte("root\n")); err != nil { + t.Logf("failed to write luks password: %v", err) + cancel() + } + } if bytes.Contains(b, login) { - t.Logf("vm ready") t.Logf("sending login") if _, err := inw.Write([]byte("root\n")); err != nil { t.Logf("failed to write login: %v", err) @@ -134,7 +152,7 @@ func TestConvert(t *testing.T) { cancel() } }() - if err := qemu.Run(ctx, out, qemu.WithStdin(inr), qemu.WithStdout(io.MultiWriter(outw, os.Stdout)), qemu.WithStderr(io.Discard)); err != nil && !success.Load() { + if err := qemu.Run(ctx, out, qemu.WithStdin(inr), qemu.WithStdout(io.MultiWriter(outw, os.Stdout)), qemu.WithStderr(io.Discard), qemu.WithMemory(2048)); err != nil && !success.Load() { t.Fatalf("failed to run qemu: %v", err) } }) diff --git a/templates/centos.Dockerfile b/templates/centos.Dockerfile index 93a9771..7c9082d 100644 --- a/templates/centos.Dockerfile +++ b/templates/centos.Dockerfile @@ -7,12 +7,20 @@ RUN sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-* && \ RUN yum update -y -RUN yum install -y kernel systemd NetworkManager e2fsprogs sudo && \ +RUN yum install -y \ + kernel \ + systemd \ + NetworkManager \ + e2fsprogs \ + {{- if .Luks }} + cryptsetup \ + {{- end }} + sudo && \ systemctl enable NetworkManager && \ systemctl unmask systemd-remount-fs.service && \ systemctl unmask getty.target -RUN dracut --no-hostonly --regenerate-all --force && \ +RUN dracut --no-hostonly --regenerate-all --force {{ if .Luks }}--install="/usr/sbin/cryptsetup"{{ end }}&& \ cd /boot && \ ln -s $(find . -name 'vmlinuz-*') vmlinuz && \ ln -s $(find . -name 'initramfs-*.img') initrd.img