Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,712 changes: 1,712 additions & 0 deletions PARTITION_ENCRYPTION_REFACTORING_PLAN.md
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these are temporary, used to put the AI into context every time. I will remove them before merge.

Large diffs are not rendered by default.

148 changes: 148 additions & 0 deletions REFACTORING_PLAN_SHORT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
# Partition Encryption Refactoring - Summary

## ✅ REFACTORING STATUS (Updated: 2025-10-24)

### Implementation Complete - Interface-Based Approach ✅

We implemented a cleaner architecture using the **Strategy Pattern** with a `PartitionEncryptor` interface.

#### Completed Work:

**1. kairos-sdk/kcrypt/encryptor.go (NEW FILE)**
- `PartitionEncryptor` interface with `Encrypt()`, `Unlock()`, `Name()`, `Validate()` methods
- Three implementations: `RemoteKMSEncryptor`, `TPMWithPCREncryptor`, `LocalTPMNVEncryptor`
- `GetEncryptor(logger)` factory with automatic config scanning and validation

**2. kairos-sdk/kcrypt/tpm_passphrase.go (NEW FILE)**
- Local TPM NV passphrase management (moved from kcrypt-challenger)

**3. kairos-sdk/kcrypt/lock.go + unlock.go (MODIFIED)**
- New encryption functions for local TPM NV passphrase
- **SECURITY FIX** ✅: Removed passphrase logging (only log length)

**4. kairos-agent/internal/agent/hooks/encrypt.go (NEW - ~350 LINES)**
- Unified `Encrypt()` method for both UKI and non-UKI modes
- Helper methods: `determinePartitionsToEncrypt()`, `preparePartitionsForEncryption()`,
`backupOEMIfNeeded()`, `restoreOEMIfNeeded()`, `copyCloudConfigToOEM()`, `udevAdmSettle()`
- **BUG FIX** ✅: `GetEncryptor` called BEFORE unmounting OEM (so it can read kcrypt config)
- **BUG FIX** ✅: `restoreOEMIfNeeded` uses dmsetup to find mapper device + udev settle
- Legacy methods: `EncryptNonUKI()`, `EncryptUKI()` (backward compatibility)

**5. kairos-agent/internal/agent/hooks/finish.go (SIMPLIFIED - 51 LINES)**
- Clean orchestration, minimal imports

**6. kairos-agent/internal/agent/hooks/hook.go (MODIFIED)**
- **BUG FIX** ✅: `lockPartitions()` uses `dmsetup ls` to properly close mapper devices

#### Critical Bug Fixes (2025-10-24):

**Issue 1: Challenger Config Ignored** ✅
- Root cause: `GetEncryptor` called after OEM unmounted
- Fix: Call `GetEncryptor` at step 1.5 (before unmounting)
- Result: Challenger server now properly detected and used

**Issue 2: OEM Restore Failed** ✅
- Root cause: `blkid -L` returned LUKS container, not mapper device; device node not ready
- Fix: Use `dmsetup ls` to verify mapper exists + `udevAdmSettle()` to wait for device node
- Result: OEM successfully restored after encryption

**Issue 3: Passphrase Logging** ✅
- Root cause: Passphrases logged in plaintext
- Fix: Removed from all log messages, only log length
- Result: No sensitive data in logs

**Issue 4: Partition Locking Failed** ✅
- Root cause: Tried to close by label path instead of mapper name
- Fix: Use `dmsetup ls --target crypt` to find active mappers
- Result: All encrypted devices properly closed

#### Decision Logic:
1. If `challenger_server` or `mdns` → **Remote KMS** (both UKI & non-UKI)
2. Else if UKI mode → **TPM + PCR policy**
3. Else → **Local TPM NV passphrase**

#### Production Status:
- ✅ All code compiles successfully
- ✅ Challenger-based encryption tested and working
- ✅ OEM backup/restore working
- ✅ Encrypted devices properly locked
- ✅ No security issues
- ⚠️ Remove `replace` directive from go.mod before production merge

### Remaining Work:
- ⏳ **kcrypt-challenger cleanup**: Remove local TPM NV logic (now in kairos-sdk)
- ⏳ **Testing**: Full end-to-end testing of all three encryption methods
- ⏳ **Cloud-init path bug**: Fix mkdir on file paths in `pkg/utils/runstage.go:83`
- ⏳ **immucore integration** (optional)

---

## Proposed Architecture (ORIGINAL PLAN)

### Component Responsibilities

- **kairos-agent**: Orchestrates encryption with unified code path for UKI and non-UKI modes. Decides passphrase source based on config (remote KMS configured → use plugin, otherwise local). Handles common operations: unmount partitions, backup/restore OEM, unlock/wait for encrypted devices.

- **kairos-sdk**: Provides single `Encrypt()` function that accepts passphrase source parameter (remote/local-TPM/ephemeral). Handles all LUKS creation, PCR policy enrollment, and partition formatting. Contains local TPM NV passphrase storage/retrieval logic.

- **kcrypt-challenger**: Focused solely on remote KMS operations - implements TPM attestation protocol to retrieve passphrases from remote server. No local encryption logic.

## Unified Encryption Flow (kairos-agent)

```go
func encryptPartitions(config Config, isUKI bool) error {
// 1. Common preparation (extracted methods)
partitions := determinePartitionsToEncrypt(config, isUKI)
preparePartitionsForEncryption(partitions) // unmount all
oemBackup := backupOEMIfNeeded(partitions) // backup OEM data
defer restoreOEMIfNeeded(oemBackup)

// 2. For each partition
for _, partition := range partitions {
// 2a. Determine passphrase source
source := determinePassphraseSource(config, isUKI)
// Options: "remote" (KMS), "local_tpm" (NV memory), "ephemeral" (random)

// 2b. Get passphrase when needed
passphrase := getPassphrase(partition, config, source)

// 2c. Encrypt with unified logic (kairos-sdk)
kcrypt.Encrypt(
partition,
passphrase,
pcrBinding: isUKI ? config.BindPCRs : nil,
keepPasswordSlot: source != "ephemeral",
)
// This decides:
// - Creates LUKS with passphrase
// - If pcrBinding: adds TPM PCR policy keyslot
// - If !keepPasswordSlot: wipes password keyslot (TPM-only unlock)
}

// 3. Common cleanup (extracted methods)
unlockEncryptedPartitions(partitions)
waitForUnlockedPartitions(partitions)
lockPartitions(partitions)

return nil
}
```

**Key Points:**
- Passphrase retrieval is a separate step, only called when needed
- Encryption logic (kairos-sdk) decides whether to keep password keyslot based on source
- Common operations extracted into reusable methods
- Same flow for UKI and non-UKI, only parameters differ

## Key Changes by Repository

- **kairos-agent**: Merge `Encrypt()` and `EncryptUKI()` into unified flow, extract common operations (unmount, backup OEM, unlock, restore), simple decision logic for passphrase source
- **kairos-sdk**: Add unified `Encrypt()` function with passphrase source parameter, move local TPM NV logic from kcrypt-challenger, consolidate all LUKS/PCR operations
- **kcrypt-challenger**: Remove local TPM NV logic, remain as remote KMS client only (no changes to remote attestation protocol)

## Benefits

✅ UKI gains remote KMS support
✅ Single code path eliminates duplication
✅ Clear separation: remote=plugin, local=sdk
✅ Backwards compatible
10 changes: 8 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ require (
github.com/jaypipes/ghw v0.20.0
github.com/joho/godotenv v1.5.1
github.com/kairos-io/go-nodepair v0.3.0
github.com/kairos-io/kairos-sdk v0.11.0
github.com/kairos-io/kairos-sdk v0.12.1-0.20251103152832-82bc7cc6481b
github.com/labstack/echo/v4 v4.13.4
github.com/mitchellh/mapstructure v1.5.0
github.com/mudler/go-pluggable v0.0.0-20230126220627-7710299a0ae5
Expand Down Expand Up @@ -42,7 +42,6 @@ require (
github.com/distribution/reference v0.6.0
github.com/gofrs/uuid v4.4.0+incompatible
github.com/google/go-github/v74 v74.0.0
github.com/google/go-github/v76 v76.0.0
github.com/twpayne/go-vfs/v5 v5.0.5
github.com/urfave/cli/v2 v2.27.7
)
Expand Down Expand Up @@ -110,12 +109,18 @@ require (
github.com/gofrs/flock v0.12.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/google/certificate-transparency-go v1.1.4 // indirect
github.com/google/go-attestation v0.5.1 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/go-tpm v0.9.6 // indirect
github.com/google/go-tpm-tools v0.4.4 // indirect
github.com/google/go-tspi v0.3.0 // indirect
github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gookit/color v1.5.4 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/huandu/xstrings v1.5.0 // indirect
Expand All @@ -125,6 +130,7 @@ require (
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/jezek/xgb v1.1.0 // indirect
github.com/jzelinskie/whirlpool v0.0.0-20201016144138-0675e54bb004 // indirect
github.com/kairos-io/tpm-helpers v0.0.0-20251103104631-9d4298b86881 // indirect
github.com/kbinani/screenshot v0.0.0-20230812210009-b87d31814237 // indirect
github.com/kendru/darwin/go/depgraph v0.0.0-20230809052043-4d1c7e9d1767 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
Expand Down
31 changes: 28 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,11 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
github.com/google/certificate-transparency-go v1.1.4 h1:hCyXHDbtqlr/lMXU0D4WgbalXL0Zk4dSWWMbPV8VrqY=
github.com/google/certificate-transparency-go v1.1.4/go.mod h1:D6lvbfwckhNrbM9WVl1EVeMOyzC19mpIjMOI4nxBHtQ=
github.com/google/go-attestation v0.5.1 h1:jqtOrLk5MNdliTKjPbIPrAaRKJaKW+0LIU2n/brJYms=
github.com/google/go-attestation v0.5.1/go.mod h1:KqGatdUhg5kPFkokyzSBDxwSCFyRgIgtRkMp6c3lOBQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
Expand All @@ -243,13 +248,26 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/go-configfs-tsm v0.2.2 h1:YnJ9rXIOj5BYD7/0DNnzs8AOp7UcvjfTvt215EWcs98=
github.com/google/go-configfs-tsm v0.2.2/go.mod h1:EL1GTDFMb5PZQWDviGfZV9n87WeGTR/JUg13RfwkgRo=
github.com/google/go-containerregistry v0.20.6 h1:cvWX87UxxLgaH76b4hIvya6Dzz9qHB31qAwjAohdSTU=
github.com/google/go-containerregistry v0.20.6/go.mod h1:T0x8MuoAoKX/873bkeSfLD2FAkwCDf9/HZgsFJ02E2Y=
github.com/google/go-github/v74 v74.0.0 h1:yZcddTUn8DPbj11GxnMrNiAnXH14gNs559AsUpNpPgM=
github.com/google/go-github/v74 v74.0.0/go.mod h1:ubn/YdyftV80VPSI26nSJvaEsTOnsjrxG3o9kJhcyak=
github.com/google/go-github/v76 v76.0.0/go.mod h1:38+d/8pYDO4fBLYfBhXF5EKO0wA3UkXBjfmQapFsNCQ=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/go-sev-guest v0.9.3 h1:GOJ+EipURdeWFl/YYdgcCxyPeMgQUWlI056iFkBD8UU=
github.com/google/go-sev-guest v0.9.3/go.mod h1:hc1R4R6f8+NcJwITs0L90fYWTsBpd1Ix+Gur15sqHDs=
github.com/google/go-tdx-guest v0.3.1 h1:gl0KvjdsD4RrJzyLefDOvFOUH3NAJri/3qvaL5m83Iw=
github.com/google/go-tdx-guest v0.3.1/go.mod h1:/rc3d7rnPykOPuY8U9saMyEps0PZDThLk/RygXm04nE=
github.com/google/go-tpm v0.9.6 h1:Ku42PT4LmjDu1H5C5ISWLlpI1mj+Zq7sPGKoRw2XROA=
github.com/google/go-tpm v0.9.6/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
github.com/google/go-tpm-tools v0.4.4 h1:oiQfAIkc6xTy9Fl5NKTeTJkBTlXdHsxAofmQyxBKY98=
github.com/google/go-tpm-tools v0.4.4/go.mod h1:T8jXkp2s+eltnCDIsXR84/MTcVU9Ja7bh3Mit0pa4AY=
github.com/google/go-tspi v0.3.0 h1:ADtq8RKfP+jrTyIWIZDIYcKOMecRqNJFOew2IT0Inus=
github.com/google/go-tspi v0.3.0/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5by8CiHI=
github.com/google/logger v1.1.1 h1:+6Z2geNxc9G+4D4oDO9njjjn2d0wN5d7uOo0vOIW1NQ=
github.com/google/logger v1.1.1/go.mod h1:BkeJZ+1FhQ+/d087r4dzojEg1u2ZX+ZqG1jTUrLM+zQ=
github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 h1:EEHtgt9IwisQ2AZ4pIsMjahcegHh6rmhqxzIRQIyepY=
github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
Expand All @@ -261,6 +279,9 @@ github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQ
github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo=
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
Expand Down Expand Up @@ -298,8 +319,10 @@ github.com/jzelinskie/whirlpool v0.0.0-20201016144138-0675e54bb004 h1:G+9t9cEtnC
github.com/jzelinskie/whirlpool v0.0.0-20201016144138-0675e54bb004/go.mod h1:KmHnJWQrgEvbuy0vcvj00gtMqbvNn1L+3YUZLK/B92c=
github.com/kairos-io/go-nodepair v0.3.0 h1:JIMBAtbNhIAsx89aP61mQDGMuGFoIQH/woK2tMDYD6k=
github.com/kairos-io/go-nodepair v0.3.0/go.mod h1:7i905W/KmR9DAcMSVJr/Wdb84E5Yyu9YLgj7chwX1xs=
github.com/kairos-io/kairos-sdk v0.11.0 h1:FFpkjjK5Sf/SeWuxZORXuEEuoLldQ6fhMsUOcjQk7Q0=
github.com/kairos-io/kairos-sdk v0.11.0/go.mod h1:SFHfHIYug+lRgNkXhcxVGfUOQ06QJCYKcULlbsN+/Ys=
github.com/kairos-io/kairos-sdk v0.12.1-0.20251103152832-82bc7cc6481b h1:daoHz1g/ZRWl4nbODGkco+RAXkoh7wltFnOx5SBWPok=
github.com/kairos-io/kairos-sdk v0.12.1-0.20251103152832-82bc7cc6481b/go.mod h1:39AhCp9dUQBfdkOQMTCegTo2Q5GUJR1//gPXo3r+9AA=
github.com/kairos-io/tpm-helpers v0.0.0-20251103104631-9d4298b86881 h1:teAOOtMYhlPz7Q0DXTih8ZOVgPctyUz86PplTi1K29g=
github.com/kairos-io/tpm-helpers v0.0.0-20251103104631-9d4298b86881/go.mod h1:zOtMRZ4zGpy+qL6W1N0LrczM/9T0sdoaJ1K5PgfchAs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kbinani/screenshot v0.0.0-20230812210009-b87d31814237 h1:YOp8St+CM/AQ9Vp4XYm4272E77MptJDHkwypQHIRl9Q=
Expand Down Expand Up @@ -419,6 +442,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw=
github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/phayes/permbits v0.0.0-20190612203442-39d7c581d2ee h1:P6U24L02WMfj9ymZTxl7CxS73JC99x3ukk+DBkgQGQs=
Expand Down
Loading
Loading