2023-12-12 20:51:55 +00:00
|
|
|
package hook
|
|
|
|
|
|
|
|
import (
|
2024-01-30 13:28:54 +00:00
|
|
|
"fmt"
|
2024-01-24 09:48:33 +00:00
|
|
|
"os"
|
2024-06-12 08:08:19 +00:00
|
|
|
"regexp"
|
2024-01-24 09:48:33 +00:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
2023-12-12 20:51:55 +00:00
|
|
|
"github.com/kairos-io/kairos-agent/v2/pkg/config"
|
|
|
|
"github.com/kairos-io/kairos-agent/v2/pkg/constants"
|
|
|
|
v1 "github.com/kairos-io/kairos-agent/v2/pkg/types/v1"
|
|
|
|
internalutils "github.com/kairos-io/kairos-agent/v2/pkg/utils"
|
|
|
|
fsutils "github.com/kairos-io/kairos-agent/v2/pkg/utils/fs"
|
|
|
|
"github.com/kairos-io/kairos-sdk/machine"
|
|
|
|
"github.com/kairos-io/kairos-sdk/utils"
|
|
|
|
kcrypt "github.com/kairos-io/kcrypt/pkg/lib"
|
|
|
|
)
|
|
|
|
|
|
|
|
type KcryptUKI struct{}
|
|
|
|
|
2024-04-24 14:42:51 +00:00
|
|
|
func (k KcryptUKI) Run(c config.Config, spec v1.Spec) error {
|
2023-12-18 10:38:26 +00:00
|
|
|
// pre-check for systemd version, we need something higher or equal to 252
|
|
|
|
run, err := utils.SH("systemctl --version | head -1 | awk '{ print $2}'")
|
|
|
|
systemdVersion := strings.TrimSpace(string(run))
|
|
|
|
if err != nil {
|
|
|
|
c.Logger.Errorf("could not get systemd version: %s", err)
|
|
|
|
c.Logger.Errorf("could not get systemd version: %s", run)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if systemdVersion == "" {
|
|
|
|
c.Logger.Errorf("could not get systemd version: %s", err)
|
|
|
|
return err
|
|
|
|
}
|
2024-06-12 08:08:19 +00:00
|
|
|
// Extract the numeric portion of the version string using a regular expression
|
|
|
|
re := regexp.MustCompile(`\d+`)
|
|
|
|
matches := re.FindString(systemdVersion)
|
|
|
|
if matches == "" {
|
|
|
|
return fmt.Errorf("could not extract numeric part from systemd version: %s", systemdVersion)
|
|
|
|
}
|
2023-12-18 10:38:26 +00:00
|
|
|
// Change systemdVersion to int value
|
|
|
|
systemdVersionInt, err := strconv.Atoi(systemdVersion)
|
|
|
|
if err != nil {
|
|
|
|
c.Logger.Errorf("could not convert systemd version to int: %s", err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// If systemd version is less than 252 return
|
|
|
|
if systemdVersionInt < 252 {
|
|
|
|
c.Logger.Infof("systemd version is %s, we need 252 or higher for encrypting partitions", systemdVersion)
|
|
|
|
return nil
|
|
|
|
}
|
2023-12-12 20:51:55 +00:00
|
|
|
|
2024-01-10 09:38:31 +00:00
|
|
|
// Check for a TPM 2.0 device as its needed to encrypt
|
|
|
|
// Exposed by the kernel to userspace as /dev/tpmrm0 since kernel 4.12
|
|
|
|
// https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=fdc915f7f71939ad5a3dda3389b8d2d7a7c5ee66
|
|
|
|
_, err = os.Stat("/dev/tpmrm0")
|
|
|
|
if err != nil {
|
|
|
|
c.Logger.Warnf("Skipping partition encryption, could not find TPM 2.0 device at /dev/tpmrm0")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-03-07 14:11:51 +00:00
|
|
|
c.Logger.Logger.Debug().Msg("Running KcryptUKI hook")
|
|
|
|
|
2023-12-12 20:51:55 +00:00
|
|
|
// We always encrypt OEM and PERSISTENT under UKI
|
|
|
|
// If mounted, unmount it
|
|
|
|
_ = machine.Umount(constants.OEMDir) //nolint:errcheck
|
|
|
|
_ = machine.Umount(constants.PersistentDir) //nolint:errcheck
|
|
|
|
|
|
|
|
// Backup oem as we already copied files on there and on luksify it will be wiped
|
2024-03-01 11:27:26 +00:00
|
|
|
err = machine.Mount(constants.OEMLabel, constants.OEMDir)
|
2023-12-12 20:51:55 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
tmpDir, err := fsutils.TempDir(c.Fs, "", "oem-backup-xxxx")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove everything when we finish
|
|
|
|
defer c.Fs.RemoveAll(tmpDir) //nolint:errcheck
|
|
|
|
|
|
|
|
err = internalutils.SyncData(c.Logger, c.Runner, c.Fs, constants.OEMDir, tmpDir, []string{}...)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
err = machine.Umount(constants.OEMDir) //nolint:errcheck
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2024-03-01 11:27:26 +00:00
|
|
|
for _, p := range append([]string{constants.OEMLabel, constants.PersistentLabel}, c.Install.Encrypt...) {
|
2023-12-12 20:51:55 +00:00
|
|
|
c.Logger.Infof("Encrypting %s", p)
|
2024-06-20 09:19:29 +00:00
|
|
|
_ = os.Setenv("SYSTEMD_LOG_LEVEL", "debug")
|
2024-11-20 09:41:52 +00:00
|
|
|
err := kcrypt.LuksifyMeasurements(p, c.BindPublicPCRs, c.BindPCRs, c.Logger.Logger)
|
2024-06-20 09:19:29 +00:00
|
|
|
_ = os.Unsetenv("SYSTEMD_LOG_LEVEL")
|
2023-12-12 20:51:55 +00:00
|
|
|
if err != nil {
|
|
|
|
c.Logger.Errorf("could not encrypt partition: %s", err)
|
|
|
|
if c.FailOnBundleErrors {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// Give time to show the error
|
|
|
|
time.Sleep(10 * time.Second)
|
|
|
|
return nil // do not error out
|
|
|
|
}
|
|
|
|
c.Logger.Infof("Done encrypting %s", p)
|
|
|
|
}
|
|
|
|
|
2024-01-30 13:28:54 +00:00
|
|
|
_, _ = utils.SH("sync")
|
|
|
|
|
2024-06-20 09:19:29 +00:00
|
|
|
_ = os.Setenv("SYSTEMD_LOG_LEVEL", "debug")
|
|
|
|
err = kcrypt.UnlockAllWithLogger(true, c.Logger.Logger)
|
|
|
|
|
|
|
|
_ = os.Unsetenv("SYSTEMD_LOG_LEVEL")
|
2023-12-12 20:51:55 +00:00
|
|
|
if err != nil {
|
2024-03-19 08:27:13 +00:00
|
|
|
c.Logger.Errorf("could not unlock partitions: %s", err)
|
2023-12-12 20:51:55 +00:00
|
|
|
return err
|
|
|
|
}
|
2024-01-30 13:28:54 +00:00
|
|
|
// Close the unlocked partitions after dealing with them, otherwise we leave them open and they can be mounted by anyone
|
|
|
|
defer func() {
|
2024-03-01 11:27:26 +00:00
|
|
|
for _, p := range append([]string{constants.OEMLabel, constants.PersistentLabel}, c.Install.Encrypt...) {
|
2024-01-30 13:28:54 +00:00
|
|
|
c.Logger.Debugf("Closing unencrypted /dev/disk/by-label/%s", p)
|
|
|
|
out, err := utils.SH(fmt.Sprintf("cryptsetup close /dev/disk/by-label/%s", p))
|
|
|
|
// There is a known error with cryptsetup that it can't close the device because of a semaphore
|
|
|
|
// doesnt seem to affect anything as the device is closed as expected so we ignore it if it matches the
|
|
|
|
// output of the error
|
|
|
|
if err != nil && !strings.Contains(out, "incorrect semaphore state") {
|
|
|
|
c.Logger.Errorf("could not close /dev/disk/by-label/%s: %s", p, out)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
// Here it can take the oem partition a bit of time to appear after unlocking so we need to retry a couple of time with some waiting
|
|
|
|
// retry + backoff
|
2024-03-01 11:27:26 +00:00
|
|
|
// Check all encrypted partitions are unlocked
|
|
|
|
for _, p := range append([]string{constants.OEMLabel, constants.PersistentLabel}) {
|
|
|
|
for i := 0; i < 10; i++ {
|
|
|
|
c.Logger.Infof("Waiting for unlocked partition %s to appear", p)
|
|
|
|
_, _ = utils.SH("sync")
|
|
|
|
part, _ := utils.SH(fmt.Sprintf("blkid -L %s", p))
|
|
|
|
if part == "" {
|
|
|
|
c.Logger.Infof("Partition %s not found, waiting %d seconds before retrying", p, i)
|
|
|
|
time.Sleep(time.Duration(i) * time.Second)
|
2024-03-19 08:27:13 +00:00
|
|
|
// Retry the unlock as well, because maybe the partition was not refreshed on time for unlock to unlock it
|
|
|
|
// So no matter how many tries we do, it will still be locked and will never appear
|
2024-06-20 09:19:29 +00:00
|
|
|
err := kcrypt.UnlockAllWithLogger(true, c.Logger.Logger)
|
2024-03-19 08:27:13 +00:00
|
|
|
if err != nil {
|
|
|
|
c.Logger.Debugf("UnlockAll returned: %s", err)
|
|
|
|
}
|
|
|
|
if i == 9 {
|
|
|
|
c.Logger.Errorf("Partition %s not unlocked/found after 10 retries", p)
|
|
|
|
return fmt.Errorf("partition %s not unlocked/found after 10 retries", p)
|
|
|
|
}
|
2024-03-01 11:27:26 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
c.Logger.Infof("Partition found, continuing")
|
|
|
|
break
|
2024-01-30 13:28:54 +00:00
|
|
|
}
|
|
|
|
}
|
2024-03-01 11:27:26 +00:00
|
|
|
|
|
|
|
err = machine.Mount(constants.OEMLabel, constants.OEMDir)
|
2023-12-12 20:51:55 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
err = internalutils.SyncData(c.Logger, c.Runner, c.Fs, tmpDir, constants.OEMDir, []string{}...)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-01-30 13:28:54 +00:00
|
|
|
err = machine.Umount(constants.OEMDir)
|
2023-12-12 20:51:55 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-03-01 11:27:26 +00:00
|
|
|
|
2024-04-24 14:42:51 +00:00
|
|
|
c.Logger.Logger.Debug().Msg("Finish KcryptUKI hook")
|
|
|
|
// We now have the partitions unlocked and ready, lets call the other hooks here instead of closing and reopening them each time
|
|
|
|
err = BundlePostInstall{}.Run(c, spec)
|
2024-03-01 11:27:26 +00:00
|
|
|
if err != nil {
|
2024-04-24 14:42:51 +00:00
|
|
|
return err
|
2024-03-01 11:27:26 +00:00
|
|
|
}
|
2024-04-24 14:42:51 +00:00
|
|
|
_ = CustomMounts{}.Run(c, spec)
|
|
|
|
err = CopyLogs{}.Run(c, spec)
|
2024-03-01 11:27:26 +00:00
|
|
|
if err != nil {
|
2024-04-24 14:42:51 +00:00
|
|
|
return err
|
2024-03-01 11:27:26 +00:00
|
|
|
}
|
2023-12-12 20:51:55 +00:00
|
|
|
return nil
|
|
|
|
}
|