168 Commits

Author SHA1 Message Date
Dimitris Karakasilis
fac5dfb32d Remove stubbed version and fix tests
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2025-09-24 14:32:21 +03:00
Dimitris Karakasilis
5fb15c81f6 Allow the user to cleanup NV indexes
e.g. to reset the passphrase stored on the TPM for local encryption

Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2025-09-24 13:58:17 +03:00
Dimitris Karakasilis
caedb1ef7f Avoid global vars
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2025-09-24 13:04:13 +03:00
Dimitris Karakasilis
55a0d62231 Migrate to cobra cli for better code organization
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2025-09-24 12:57:32 +03:00
Dimitris Karakasilis
592426ae43 Remove meaningless test
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2025-09-24 11:41:56 +03:00
Dimitris Karakasilis
118189e672 Fix tests
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2025-09-24 10:44:32 +03:00
Dimitris Karakasilis
5f2d857097 [TMP] use a replace that points to a branch (instead of localy dir)
Point to this: https://github.com/kairos-io/tpm-helpers/pull/7

Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2025-09-24 10:14:38 +03:00
Dimitris Karakasilis
6ce6db1d84 Move path to a constant
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2025-09-23 16:38:03 +03:00
Dimitris Karakasilis
89b07027cb Remove unecessary wrapper
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2025-09-23 16:25:23 +03:00
Dimitris Karakasilis
bd19b91a1b Don't use the (now removed) redundant field
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2025-09-23 11:35:55 +03:00
Dimitris Karakasilis
9eeb285826 Handle PCR validation errors gracefully
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2025-09-22 16:10:25 +03:00
Dimitris Karakasilis
dc853ab2a4 Don't shot trace log when a security violation occurs
because it's not an application error but rather normal behaviour

Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2025-09-22 16:00:23 +03:00
Dimitris Karakasilis
8383f4b1b0 Use specific PCRs in tpm quote
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2025-09-22 15:56:32 +03:00
Dimitris Karakasilis
eba04e1479 Remove unecessary patches in deployment
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2025-09-22 15:54:28 +03:00
Dimitris Karakasilis
db5793d0d1 Treat and empty passphrase as an error
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2025-09-19 17:04:51 +03:00
Dimitris Karakasilis
8ce8651bca Implement TOFU flow on the server
and fix some issues with the data we send back and forth between the
client and the server

Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2025-09-19 16:48:52 +03:00
Dimitris Karakasilis
b674f911da Remove legacy methods from old flow
TODO: Implemnt TOFU on the server

Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2025-09-19 15:32:58 +03:00
Dimitris Karakasilis
2ef72d3c0a Use a KairosLogger consistently
in plugin mode: log only to a file and journal and in "debug" level by
default

in cli mode: respect the `--debug` flag and write to the stdout

Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2025-09-18 14:29:48 +03:00
Dimitris Karakasilis
f943b01c90 Introduce a cli interface to interace with the challenger client
This will make debugging easier both while developing and in production.
No need to use it through the kcrypt binary anymore, because we might
not actually care about decrypting the disks but rather about getting
the passphrase from the KMS.

Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2025-09-18 13:47:10 +03:00
Dimitris Karakasilis
80cd276ff3 [WIP] Split with-TPM and without-TPM flows
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2025-09-17 17:29:34 +03:00
renovate[bot]
db720d392a fix(deps): update module github.com/onsi/ginkgo/v2 to v2.25.3 (#140)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-13 17:56:01 +00:00
renovate[bot]
af5f9b34e6 chore(deps): update actions/download-artifact action to v5 (#135)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-13 08:58:54 +00:00
renovate[bot]
69bd83e5ba fix(deps): update module github.com/mudler/yip to v1.18.0 (#138)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-13 07:58:46 +00:00
renovate[bot]
932a59b960 chore(deps): update google/osv-scanner-action action to v2.2.2 (#142)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-13 07:01:15 +00:00
renovate[bot]
eea31d697d chore(deps): update actions/setup-go action to v6 (#144)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-13 06:48:45 +00:00
renovate[bot]
ccd79623ad fix(deps): update module github.com/jaypipes/ghw to v0.19.1 (#141)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-13 06:37:31 +00:00
renovate[bot]
a4613048c6 fix(deps): update module github.com/onsi/gomega to v1.38.2 (#134)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-13 06:27:30 +00:00
renovate[bot]
ea1f84ed49 chore(deps): update earthly/earthly docker tag to v0.8.16 (#132)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-13 06:11:18 +00:00
Itxaka
afb9d5e70d chore: update Go version and dependencies (#143)
* chore: update Go version and dependencies

Upgrade Go to version 1.25 and update various dependencies to their latest versions for improved performance and security. This includes updates to the kairos-sdk, docker, and opentelemetry packages.

Signed-off-by: Itxaka <itxaka@kairos.io>

* chore: update Go version to 1.25

This change updates the Go version in the Earthfile and unit-tests.yml to 1.25-bookworm to ensure compatibility with the latest features and improvements.

Signed-off-by: Itxaka <itxaka@kairos.io>

* go mod tidy

Signed-off-by: Itxaka <itxaka@kairos.io>

* go mod tidy

Signed-off-by: Itxaka <itxaka@kairos.io>

* Fix?

Signed-off-by: Itxaka <itxaka@kairos.io>

* Fix

Signed-off-by: Itxaka <itxaka@kairos.io>

* maybe fix

Signed-off-by: Itxaka <itxaka@kairos.io>

* Fix

Signed-off-by: Itxaka <itxaka@kairos.io>

---------

Signed-off-by: Itxaka <itxaka@kairos.io>
2025-09-12 14:03:36 +02:00
renovate[bot]
432c71e4c2 Update google/osv-scanner-action action to v2.2.1 (#136)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-12 04:29:27 +00:00
renovate[bot]
79dae2a87d Update actions/checkout action to v5 (#137)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-12 04:05:50 +00:00
renovate[bot]
1f596e0abf Update module github.com/mudler/yip to v1.16.3 (#130)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-14 14:25:03 +00:00
renovate[bot]
53b26c2635 Update module github.com/kairos-io/kairos-sdk to v0.9.4 (#129)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-14 14:21:05 +00:00
renovate[bot]
2683ad797b Update google/osv-scanner-action action to v2.1.0 (#131)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-14 07:03:46 +00:00
renovate[bot]
74e82836a5 Update module github.com/jaypipes/ghw to v0.17.0 (#127)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-04 11:29:24 +00:00
renovate[bot]
24d88295d8 Update module github.com/go-logr/logr to v1.4.3 (#128)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-04 10:55:22 +00:00
renovate[bot]
17efbabdfc Update module github.com/mudler/yip to v1.16.2 (#126)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-04 06:49:57 +00:00
renovate[bot]
05157abbd4 Update module github.com/kairos-io/kairos-sdk to v0.9.3 (#125)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-14 00:01:20 +00:00
renovate[bot]
f1360e172b Update dependabot/fetch-metadata action to v2.4.0 (#124)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-10 02:03:40 +00:00
renovate[bot]
ac41a4fdcb Update module github.com/kairos-io/kairos-sdk to v0.9.2 (#123)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-08 23:43:38 +00:00
renovate[bot]
17cc494985 Update module github.com/kairos-io/kairos-sdk to v0.9.1 (#122)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-07 00:09:15 +00:00
Itxaka
ddd65746f0 Drop kcrypt, use sdk (#120) 2025-05-06 09:18:50 +00:00
renovate[bot]
5787d7fa47 Update module github.com/mudler/yip to v1.16.0 (#115)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-05 08:53:59 +00:00
renovate[bot]
2ee88ce704 Update module github.com/kairos-io/kcrypt to v0.15.0 (#112)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-05 08:52:46 +00:00
renovate[bot]
7d2e6cbed7 Update module github.com/kairos-io/kairos-sdk to v0.9.0 (#118)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-05 08:27:07 +00:00
renovate[bot]
fd47042069 Update google/osv-scanner-action action to v2.0.2 (#121)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-05 08:03:06 +00:00
renovate[bot]
c77d4d94f2 Update module github.com/jaypipes/ghw to v0.16.0 (#111)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-05 09:39:16 +02:00
renovate[bot]
d4cbf44973 Update module github.com/onsi/gomega to v1.37.0 (#114)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-05 09:39:01 +02:00
renovate[bot]
99f69e38e5 Update module github.com/onsi/ginkgo/v2 to v2.23.4 (#113)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-23 21:54:34 +02:00
renovate[bot]
e032586eb4 Update google/osv-scanner-action action to v2 (#116)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-23 20:09:49 +02:00
Dimitris Karakasilis
2aba011ada Fix vet error
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2025-04-22 12:21:48 +03:00
Itxaka
c1a92786b2 Update unit-tests.yml with earthly 2025-04-22 10:54:44 +02:00
Itxaka
a8e35a94f5 Update e2e-tests.yml with earthly 2025-04-22 10:53:50 +02:00
Itxaka
5089f4cc71 Update Earthfile with new go version 2025-04-22 10:42:58 +02:00
Itxaka
a925d877bc Fix earthly in lint 2025-04-22 10:41:38 +02:00
Itxaka
a21fb32bc0 install earthyl manually 2025-04-22 10:37:44 +02:00
Dimitris Karakasilis
f8ef34077d Bump golang
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2025-04-22 11:17:36 +03:00
Itxaka
4c2891e33b Use tag instead of version for archive release
Signed-off-by: Itxaka <itxaka@kairos.io>
2025-04-11 18:33:33 +02:00
Itxaka
3bf6a1e612 terst (#119) 2025-04-11 18:16:43 +02:00
renovate[bot]
3e519be076 Update module github.com/onsi/ginkgo/v2 to v2.23.0 (#110)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-06 03:57:44 +00:00
renovate[bot]
f8045707ff Update module github.com/jaypipes/ghw to v0.14.0 (#108)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-05 07:24:12 +00:00
renovate[bot]
8194344115 Update dependency go to v1.24.1 (#109)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-05 03:34:07 +00:00
renovate[bot]
c92402b6c9 Update module github.com/mudler/yip to v1.15.0 (#107)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-19 03:35:55 +00:00
renovate[bot]
8f9d463bc0 Update module github.com/hashicorp/mdns to v1.0.6 (#103)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-13 07:37:36 +00:00
renovate[bot]
33ba761d42 Update dependabot/fetch-metadata action to v2.3.0 (#104)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-13 07:13:39 +00:00
renovate[bot]
65108068e8 Update module github.com/kairos-io/kairos-sdk to v0.7.3 (#105)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-13 06:46:40 +00:00
renovate[bot]
8314c64169 Update golang Docker tag to v1.24 (#106)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-13 06:21:41 +00:00
renovate[bot]
52dfdf3420 Update module github.com/mudler/yip to v1.14.1 (#102)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-21 14:55:02 +00:00
Dimitris Karakasilis
09a6ec31ec go mod tidy
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2025-01-21 15:53:34 +02:00
renovate[bot]
a33d7872c5 Update module github.com/mudler/yip to v1.13.1 (#100)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-17 00:41:51 +00:00
renovate[bot]
2f0d6d778a Update module github.com/kairos-io/kairos-sdk to v0.7.2 (#101)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-14 23:55:55 +00:00
renovate[bot]
2d15026331 Update module github.com/onsi/ginkgo/v2 to v2.22.2 (#98)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-30 22:38:27 +00:00
renovate[bot]
0fa24f7679 Update google/osv-scanner-action action to v1.9.2 (#97)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-20 02:24:02 +00:00
renovate[bot]
865c2fc795 Update module github.com/kairos-io/kcrypt to v0.13.0 (#96)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-19 00:20:41 +00:00
renovate[bot]
42fca7593a Update module github.com/mudler/yip to v1.13.0 (#92)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-18 16:47:01 +02:00
renovate[bot]
4e87807d1f Update module github.com/kairos-io/kairos-sdk to v0.6.1 (#88)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-18 14:42:15 +00:00
Dimitris Karakasilis
e984eed1c1 Bump golang.org/x/crypto to v0.31.0 (#95)
To get this security fix:

https://go.googlesource.com/crypto/+/refs/tags/v0.31.0

Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2024-12-18 16:12:46 +02:00
renovate[bot]
4e33127982 Update module github.com/onsi/gomega to v1.36.1 (#89)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-18 14:12:28 +00:00
renovate[bot]
6a180b7cde Update codecov/codecov-action action to v5 (#94)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-18 15:43:06 +02:00
renovate[bot]
6e2211e4d6 Update module github.com/onsi/ginkgo/v2 to v2.22.0 (#93)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-18 15:42:52 +02:00
renovate[bot]
97dcf030cb Update module github.com/jaypipes/ghw to v0.13.0 (#90)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-18 15:42:39 +02:00
renovate[bot]
93596bd189 Update github.com/mudler/go-processmanager digest to 8b802d3 (#87)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-18 15:42:26 +02:00
renovate[bot]
012329e54b Update google/osv-scanner-action action to v1.9.1 2024-12-18 15:42:13 +02:00
Dimitris Karakasilis
57e911e62a Don't run jobs on arm workers
because we get this error:

  stderr: qemu-system-x86_64: invalid accelerator kvm

Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2024-11-11 09:51:26 +02:00
Dimitris Karakasilis
401e3f9735 More go bumps
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2024-11-08 17:13:00 +02:00
Dimitris Karakasilis
91edb4eb57 Bump go verion in Earthly target too
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2024-11-08 17:04:57 +02:00
Dimitris Karakasilis
9bdc42fbba Bump go in go.mod and run go mod tidy
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2024-11-08 17:01:27 +02:00
renovate[bot]
d6b79752a3 Update module github.com/onsi/ginkgo/v2 to v2.20.2 (#82)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-28 16:42:16 +00:00
renovate[bot]
63795470b1 Update google/osv-scanner-action action to v1.8.4 2024-08-22 16:46:07 +00:00
Itxaka
09e155828c Bump deps for CVE-2024-41110 (#84) 2024-08-01 12:10:10 +02:00
renovate[bot]
f3ade81dd3 Update module github.com/mudler/yip to v1.9.3 (#73)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-15 13:57:48 +00:00
renovate[bot]
07ce451b60 Update earthly/earthly Docker tag to v0.8.15 2024-07-12 21:38:57 +00:00
Itxaka
978d0aa3be Run secscans in proper events (#77) 2024-07-12 09:34:23 +02:00
renovate[bot]
615d2013b7 Update module github.com/kairos-io/kcrypt to v0.12.0 (#72)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-12 06:59:26 +00:00
renovate[bot]
6b8245dc61 Update module github.com/onsi/ginkgo/v2 to v2.19.0 (#74)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-11 18:06:04 +00:00
renovate[bot]
df29a61b8b Update module github.com/kairos-io/kairos-sdk to v0.3.1 (#68)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-11 17:26:08 +00:00
renovate[bot]
23e4a1dd55 Update module github.com/onsi/gomega to v1.33.1 (#75)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-11 18:37:39 +02:00
Itxaka
42709484ac Pin to specific go version so it can bump it
Otherwise renovate wont bump it

Signed-off-by: Itxaka <itxaka@kairos.io>
2024-07-11 18:36:58 +02:00
renovate[bot]
97f92cc809 Update dependabot/fetch-metadata action to v2 (#57)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-11 18:06:29 +02:00
renovate[bot]
09a93ff001 Update actions/setup-go action to v5 (#56)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-11 18:05:11 +02:00
renovate[bot]
02b5389fc6 Update actions/checkout action to v4 (#55)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-11 18:04:15 +02:00
renovate[bot]
f970ef1899 Update GitHub Artifact Actions to v4 (#54)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-11 18:04:03 +02:00
renovate[bot]
429b86ea09 Update docker/build-push-action action to v6 (#76)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-11 18:03:47 +02:00
renovate[bot]
5bfbac6892 Update docker/login-action action to v3 (#59)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-11 18:03:34 +02:00
renovate[bot]
d9e658b202 Update dependabot/fetch-metadata action to v1.7.0 (#53)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-11 18:02:53 +02:00
renovate[bot]
204ce64465 Update google/osv-scanner-action action to v1.8.2 (#71)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-11 18:02:32 +02:00
Itxaka
17d1414b14 Update renovate.json 2024-07-11 18:01:41 +02:00
dependabot[bot]
b6c5d331fb Bump golang.org/x/net in the go_modules group across 1 directory (#63)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Itxaka <itxaka.garcia@spectrocloud.com>
2024-07-11 17:58:30 +02:00
Itxaka
a2b28af7b2 Bump to go1.22 (#70) 2024-07-11 17:51:49 +02:00
Mauro Morales
dd187adf3a Merge pull request #69 from kairos-io/add-osv
Add osv scanner on PRs
2024-07-03 12:35:27 +02:00
Mauro Morales
196bcf8500 Add osv scanner on PRs
Signed-off-by: Mauro Morales <contact@mauromorales.com>
2024-07-02 16:51:02 +02:00
renovate[bot]
50441f8e4c Update module github.com/google/uuid to v1.6.0 (#67)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-18 23:43:14 +00:00
renovate[bot]
a5e73df6e6 Update module github.com/gorilla/websocket to v1.5.3 2024-06-18 05:14:11 +00:00
renovate[bot]
904ce9a1b8 Update module github.com/go-logr/logr to v1.4.2 2024-06-18 02:11:46 +00:00
renovate[bot]
2039b57421 Update earthly/earthly Docker tag to v0.8.14 2024-06-17 23:02:49 +00:00
renovate[bot]
c4dcabcabb Update earthly/earthly Docker tag to v0.8.11 (#48)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-17 01:07:28 +00:00
renovate[bot]
f757f852dd Update github.com/spectrocloud/peg digest to c5da712 (#22)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-21 07:35:47 +00:00
dependabot[bot]
ef14cef5c4 Bump github.com/opencontainers/runc (#62)
Bumps the go_modules group with 1 update in the / directory: [github.com/opencontainers/runc](https://github.com/opencontainers/runc).


Updates `github.com/opencontainers/runc` from 1.1.5 to 1.1.12
- [Release notes](https://github.com/opencontainers/runc/releases)
- [Changelog](https://github.com/opencontainers/runc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/opencontainers/runc/compare/v1.1.5...v1.1.12)

---
updated-dependencies:
- dependency-name: github.com/opencontainers/runc
  dependency-type: indirect
  dependency-group: go_modules
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Itxaka <itxaka.garcia@spectrocloud.com>
2024-04-16 09:34:18 +00:00
renovate[bot]
7205723259 Update module github.com/kairos-io/kairos-sdk to v0.1.0 (#61)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-16 10:47:08 +02:00
Itxaka
202668005f Bump deps (#60) 2024-04-16 09:33:39 +02:00
Dimitris Karakasilis
b572776381 Merge pull request #49 from jkolo/bugfix/hardcoded_architecture
Fixed hardoced architecture and OS in Dockerfile
2024-04-16 09:12:55 +03:00
Ettore Di Giacinto
f189719055 Merge pull request #38 from kairos-io/renovate/github.com-mudler-yip-1.x
Update module github.com/mudler/yip to v1.6.0
2024-04-15 14:36:57 +02:00
renovate[bot]
5bbc4fd0fb Update module github.com/mudler/yip to v1.6.0 2024-04-15 12:13:06 +00:00
Itxaka
2f2f577db7 Run unit tests on a go matrix version (#52) 2024-04-15 14:12:00 +02:00
Ettore Di Giacinto
d64cab6a7d Create renovate_auto.yml 2024-04-15 12:43:55 +02:00
Ettore Di Giacinto
050d1832dd Update dependabot_auto.yml 2024-04-15 12:29:50 +02:00
Ettore Di Giacinto
06552b7777 Create dependabot_auto.yml 2024-04-15 12:29:22 +02:00
Ettore Di Giacinto
b8ff5f31dc Merge pull request #42 from kairos-io/renovate/github.com-mudler-go-processmanager-digest
Update github.com/mudler/go-processmanager digest to f204007
2024-04-15 12:26:39 +02:00
Ettore Di Giacinto
2f582b3a83 Merge pull request #39 from kairos-io/renovate/github.com-jaypipes-ghw-0.x
Update module github.com/jaypipes/ghw to v0.12.0
2024-04-15 12:26:28 +02:00
Ettore Di Giacinto
521363de93 Merge pull request #40 from kairos-io/renovate/github.com-onsi-gomega-1.x
Update module github.com/onsi/gomega to v1.32.0
2024-04-15 12:25:59 +02:00
renovate[bot]
7a805f374b Update module github.com/onsi/gomega to v1.32.0 2024-03-18 17:26:59 +00:00
Jerzy Kołosowski
06d3d6b1c1 Fixed hardoced architecture and OS in Dockerfile 2024-03-08 19:35:15 +01:00
renovate[bot]
6a337e5812 Update module github.com/jaypipes/ghw to v0.12.0 2024-01-26 03:14:49 +00:00
renovate[bot]
a4c5c84719 Update module github.com/kairos-io/kairos-sdk to v0.0.23 2024-01-26 01:13:54 +00:00
renovate[bot]
a410398adb Update module github.com/gorilla/websocket to v1.5.1 2024-01-26 00:04:59 +00:00
renovate[bot]
a59b3019ed Update github.com/mudler/go-processmanager digest to f204007 2024-01-25 13:59:14 +00:00
Dimitris Karakasilis
e0138fe609 Merge pull request #45 from kairos-io/2069-mdns-kms
Do an mdns lookup when KMS url ends in .local
2024-01-25 15:58:08 +02:00
Dimitris Karakasilis
fe5d338ed5 Use renovate to bump the base image for the iso
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2024-01-25 15:15:40 +02:00
Dimitris Karakasilis
d708fcfa26 Skip test that is not ready yet
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2024-01-25 12:40:14 +02:00
Dimitris Karakasilis
2e63d50125 Change test expectation
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2024-01-25 12:38:41 +02:00
Dimitris Karakasilis
d4e8b2adc2 Add neednet grub setting to mdns notes (it's needed)
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2024-01-25 12:36:58 +02:00
Dimitris Karakasilis
10dcecdc85 Allow test to expect failed installation
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2024-01-25 10:27:02 +02:00
Dimitris Karakasilis
3c4663afa5 Fix problem when MACHINE_SPICY is not set
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2024-01-25 09:57:44 +02:00
Dimitris Karakasilis
95a352f4b4 Implement a test for discoverable KMS
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2024-01-25 09:39:17 +02:00
Dimitris Karakasilis
fbfd7c9f07 Add TODO for test
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2024-01-23 18:55:10 +02:00
Dimitris Karakasilis
7d84c01663 Fix tests
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2024-01-23 17:25:46 +02:00
Dimitris Karakasilis
311b8adda0 Migrate mdns functions from tpm helpers to this repo
because tpm has nothing to do with mdns.

TODO: Remove the functions from tpm helpers and bump the module here

Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2024-01-23 12:53:44 +02:00
Dimitris Karakasilis
bf59ecd475 Bump tpm-helpers
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2023-12-20 14:56:32 +02:00
Dimitris Karakasilis
71e90b94aa Remove instructions that don't work after rebase
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2023-12-20 14:47:19 +02:00
Dimitris Karakasilis
3d2d2de9dc Do an mdns lookup when KMS url ends in .local
Part of: https://github.com/kairos-io/kairos/issues/2069

Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2023-12-20 14:40:23 +02:00
renovate[bot]
c42e66a9de Update module github.com/kairos-io/kairos-sdk to v0.0.15 2023-10-27 12:39:28 +00:00
Dimitris Karakasilis
da93e626c5 Merge pull request #43 from kairos-io/1836-more-logs
1836 more logs
2023-10-27 09:20:27 +03:00
Dimitris Karakasilis
ecbbe1499e Add more logs and refactor the server handers
- Flatten if/else logic by handling errors and returning early
- Use different logger for server logs. Also handle skipped errors.
- Remove unecessary for loop
- --zap-log-level can already be used (and it works)
- Remove non-existent enki flag
- Run tests with KVM enabled on self-hosted runners
  and also don't add grub.cfg since it's already there in the base image
- Remove non-used earthly target

Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2023-10-27 09:17:48 +03:00
Mauro Morales
09981d750e Configure automerge for patch updates 2023-10-04 16:56:52 +02:00
Itxaka
b5b4d0d042 🤖 Add concurrency to CI (#33) 2023-06-15 11:20:19 +02:00
renovate[bot]
8420155746 Update docker/build-push-action action to v4 (#32)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-15 11:15:24 +02:00
renovate[bot]
a9359bf713 Update actions/checkout action to v3 (#31)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-15 11:15:01 +02:00
renovate[bot]
b31467e925 Update module github.com/mudler/yip to v1.2.0 (#28)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-15 11:14:48 +02:00
renovate[bot]
c5dc8db56b Update module github.com/jaypipes/ghw to v0.11.0 (#26)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-15 11:10:42 +02:00
renovate[bot]
a80703a556 Update module github.com/onsi/gomega to v1.27.8 (#24)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-15 11:10:22 +02:00
renovate[bot]
0b6f771d32 Update module github.com/kairos-io/kcrypt to v0.7.0 (#27)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-15 11:08:46 +02:00
renovate[bot]
72dd7d3e50 Update module github.com/onsi/ginkgo/v2 to v2.10.0 (#29)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-15 11:08:11 +02:00
Itxaka
0619047a20 Drop kairos and use sdk for collector (#20) 2023-06-15 09:35:01 +02:00
renovate[bot]
715664969a Add renovate.json (#6)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Itxaka <itxaka.garcia@spectrocloud.com>
2023-06-14 14:38:01 +02:00
Mauro Morales
bcda5b5b38 Update issue templates
relates to https://github.com/kairos-io/kairos/issues/1483
2023-06-13 12:17:08 +02:00
Itxaka
b2a0330dd8 Fix lint
Signed-off-by: Itxaka <itxaka.garcia@spectrocloud.com>
2023-05-10 00:31:15 +02:00
Itxaka
0b68d90081 Bump ghw and fix label (#17)
* Bump ghw and fix label

old label was the new FilesystemLabel. Now the label refers to the
partition label which is different

Signed-off-by: Itxaka <itxaka.garcia@spectrocloud.com>

* bump deps

Signed-off-by: Itxaka <itxaka.garcia@spectrocloud.com>

* Rework ginkgo

Signed-off-by: Itxaka <itxaka.garcia@spectrocloud.com>

* docker login

Signed-off-by: Itxaka <itxaka.garcia@spectrocloud.com>

* [Will drop]Allow building kcrypt from branches

Otherwise any changes that need both wont pass tests.

Signed-off-by: Itxaka <itxaka.garcia@spectrocloud.com>

* Dont build the iso 5 times

Signed-off-by: Itxaka <itxaka.garcia@spectrocloud.com>

* This confirms Im dumb and dont know how to program

Signed-off-by: Itxaka <itxaka.garcia@spectrocloud.com>

* debug logs

Signed-off-by: Itxaka <itxaka.garcia@spectrocloud.com>

* debug

Signed-off-by: Itxaka <itxaka.garcia@spectrocloud.com>

* 🤖 run in github CI

Signed-off-by: Itxaka <itxaka.garcia@spectrocloud.com>

* Debug

Signed-off-by: Itxaka <itxaka.garcia@spectrocloud.com>

* debug

Signed-off-by: Itxaka <itxaka.garcia@spectrocloud.com>

* Add /tmp/oem to scan dirs for config

Signed-off-by: Itxaka <itxaka.garcia@spectrocloud.com>

---------

Signed-off-by: Itxaka <itxaka.garcia@spectrocloud.com>
2023-05-10 00:24:58 +02:00
Itxaka
40267d4c24 Merge pull request #13 from kairos-io/bump-go-version-to-1.20.2
⬆️ Bump go to 1.20
2023-03-30 21:37:09 +02:00
48 changed files with 3635 additions and 1769 deletions

1
.earthlyignore Normal file
View File

@@ -0,0 +1 @@
bin/

View File

@@ -0,0 +1,12 @@
---
name: File issues on main Kairos repo
about: Tell users to file their issues on the main Kairos repo
title: ''
labels: ''
assignees: ''
---
:warning: All Kairos issues are tracked in our main repo, please file your issue there, thanks! :warning:
https://github.com/kairos-io/kairos/issues

42
.github/workflows/dependabot_auto.yml vendored Normal file
View File

@@ -0,0 +1,42 @@
name: Dependabot auto-merge
on:
- pull_request_target
permissions:
contents: write
pull-requests: write
packages: read
jobs:
dependabot:
runs-on: ubuntu-latest
if: ${{ github.actor == 'dependabot[bot]' }}
steps:
- name: Dependabot metadata
id: metadata
uses: dependabot/fetch-metadata@v2.4.0
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
skip-commit-verification: true
- name: Checkout repository
uses: actions/checkout@v5
- name: Approve a PR if not already approved
run: |
gh pr checkout "$PR_URL"
if [ "$(gh pr status --json reviewDecision -q .currentBranch.reviewDecision)" != "APPROVED" ];
then
gh pr review --approve "$PR_URL"
else
echo "PR already approved.";
fi
env:
PR_URL: ${{github.event.pull_request.html_url}}
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
- name: Enable auto-merge for Dependabot PRs
run: gh pr merge --auto --squash "$PR_URL"
env:
PR_URL: ${{github.event.pull_request.html_url}}
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}

View File

@@ -9,54 +9,29 @@ on:
paths-ignore:
- 'README.md'
concurrency:
group: ci-e2e-${{ github.head_ref || github.ref }}-${{ github.repository }}
cancel-in-progress: true
jobs:
e2e-tests:
runs-on: self-hosted
strategy:
fail-fast: false
matrix:
include:
- label: "local-encryption"
- label: "remote-auto"
- label: "remote-static"
- label: "remote-https-pinned"
- label: "remote-https-bad-cert"
build-iso:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Install Go
uses: actions/setup-go@v4
uses: actions/setup-go@v6
- name: Install earthly
uses: earthly/actions-setup@v1
with:
go-version: ^1.20
- name: Run tests
env:
LABEL: ${{ matrix.label }}
KVM: true
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: build iso
run: |
sudo apt update && \
sudo apt install -y git qemu-system-x86 qemu-utils swtpm jq make glibc-tools \
openssl curl gettext ca-certificates curl gnupg lsb-release
curl -L https://github.com/mudler/luet/releases/download/0.33.0/luet-0.33.0-linux-amd64 -o luet
chmod +x luet
sudo mv luet /usr/bin/luet
sudo mkdir -p /etc/luet/repos.conf.d/
sudo luet repo add -y kairos --url quay.io/kairos/packages --type docker
LUET_NOLOCK=true sudo -E luet install -y container/kubectl utils/k3d utils/earthly
earthly -P +iso
export ISO=$PWD/build/challenger.iso
go install -mod=mod github.com/onsi/ginkgo/v2/ginkgo
go get github.com/onsi/gomega/...
go get github.com/onsi/ginkgo/v2/ginkgo/internal@v2.7.1
go get github.com/onsi/ginkgo/v2/ginkgo/generators@v2.7.1
go get github.com/onsi/ginkgo/v2/ginkgo/labels@v2.7.1
# Configure earthly to use the docker mirror in CI
# https://docs.earthly.dev/ci-integration/pull-through-cache#configuring-earthly-to-use-the-cache
mkdir -p ~/.earthly/
cat << EOF > ~/.earthly/config.yml
global:
buildkit_additional_config: |
@@ -66,5 +41,70 @@ jobs:
insecure = true
EOF
earthly -P +iso
- uses: actions/upload-artifact@v4
with:
name: challenger.iso.zip
path: |
build/*.iso
e2e-tests:
needs:
- build-iso
runs-on: kvm
strategy:
fail-fast: false
matrix:
include:
- label: "local-encryption"
- label: "remote-auto"
- label: "remote-static"
- label: "remote-https-pinned"
- label: "remote-https-bad-cert"
- label: "discoverable-kms"
steps:
- name: Checkout code
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Install Go
uses: actions/setup-go@v6
with:
go-version-file: go.mod
- name: Install earthly
uses: earthly/actions-setup@v1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Install deps
run: |
curl -L https://github.com/mudler/luet/releases/download/0.33.0/luet-0.33.0-linux-amd64 -o luet
chmod +x luet
sudo mv luet /usr/bin/luet
sudo mkdir -p /etc/luet/repos.conf.d/
sudo luet repo add -y kairos --url quay.io/kairos/packages --type docker
LUET_NOLOCK=true sudo -E luet install -y container/kubectl utils/k3d
- name: Download artifacts
uses: actions/download-artifact@v5
with:
name: challenger.iso.zip
- name: Run tests
env:
LABEL: ${{ matrix.label }}
KVM: true
run: |
sudo apt update && \
sudo apt install -y git qemu-system-x86 qemu-utils swtpm jq make glibc-tools \
openssl curl gettext ca-certificates curl gnupg lsb-release
export ISO=$PWD/$(ls *.iso)
# update controllers
make test
# Generate controller image
make docker-build
# We run with sudo to be able to access /dev/kvm
sudo -E ./scripts/e2e-tests.sh
- uses: actions/upload-artifact@v4
if: failure()
with:
name: ${{ matrix.label }}-test.logs.zip
path: tests/**/logs/*
if-no-files-found: warn

View File

@@ -1,4 +1,3 @@
---
name: 'build container images'
on:
@@ -8,12 +7,17 @@ on:
tags:
- '*'
concurrency:
group: ci-image-${{ github.head_ref || github.ref }}-${{ github.repository }}
cancel-in-progress: true
jobs:
docker:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v5
- name: Prepare
id: prep
@@ -46,14 +50,14 @@ jobs:
- name: Login to DockerHub
if: github.event_name != 'pull_request'
uses: docker/login-action@v1
uses: docker/login-action@v3
with:
registry: quay.io
username: ${{ secrets.QUAY_USERNAME }}
password: ${{ secrets.QUAY_PASSWORD }}
- name: Build
uses: docker/build-push-action@v2
uses: docker/build-push-action@v6
with:
builder: ${{ steps.buildx.outputs.name }}
context: .

View File

@@ -6,6 +6,12 @@ on:
pull_request:
paths:
- '**'
concurrency:
group: ci-lint-${{ github.head_ref || github.ref }}-${{ github.repository }}
cancel-in-progress: true
env:
FORCE_COLOR: 1
jobs:
@@ -13,18 +19,15 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Install Go
uses: actions/setup-go@v4
with:
go-version: ^1.20
uses: actions/setup-go@v6
- name: Install earthly
uses: Luet-lab/luet-install-action@v1
uses: earthly/actions-setup@v1
with:
repository: quay.io/kairos/packages
packages: utils/earthly
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Run Lint checks
run: |
earthly +lint

21
.github/workflows/osv-scanner-pr.yaml vendored Normal file
View File

@@ -0,0 +1,21 @@
name: OSV-Scanner PR Scan
# Change "main" to your default branch if you use a different name, i.e. "master"
on:
pull_request:
push:
branches:
- main
merge_group:
branches: [main]
permissions:
# Require writing security events to upload SARIF file to security tab
security-events: write
# Only need to read contents adn actions
contents: read
actions: read
jobs:
scan-pr:
uses: "google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@v2.2.2"

27
.github/workflows/release.yaml vendored Normal file
View File

@@ -0,0 +1,27 @@
name: goreleaser
on:
push:
tags:
- 'v*'
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- run: |
git fetch --prune --unshallow
- name: Install gcc for arm64
run: sudo apt-get update && sudo apt-get install -y build-essential crossbuild-essential-arm64
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version-file: 'go.mod'
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v6
with:
version: latest
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

35
.github/workflows/renovate_auto.yml vendored Normal file
View File

@@ -0,0 +1,35 @@
name: Renovate auto-merge
on:
- pull_request_target
permissions:
contents: write
pull-requests: write
packages: read
jobs:
dependabot:
runs-on: ubuntu-latest
if: ${{ github.actor == 'renovate[bot]' }}
steps:
- name: Checkout repository
uses: actions/checkout@v5
- name: Approve a PR if not already approved
run: |
gh pr checkout "$PR_URL"
if [ "$(gh pr status --json reviewDecision -q .currentBranch.reviewDecision)" != "APPROVED" ];
then
gh pr review --approve "$PR_URL"
else
echo "PR already approved.";
fi
env:
PR_URL: ${{github.event.pull_request.html_url}}
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
- name: Enable auto-merge for Renovate PRs
run: gh pr merge --auto --squash "$PR_URL"
env:
PR_URL: ${{github.event.pull_request.html_url}}
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}

32
.github/workflows/secscan.yaml vendored Normal file
View File

@@ -0,0 +1,32 @@
name: "Security Scan"
# Run workflow each time code is pushed to your repository and on a schedule.
# The scheduled workflow runs every at 00:00 on Sunday UTC time.
on:
push:
branches:
- main
pull_request:
paths:
- '**'
schedule:
- cron: '0 0 * * 0'
jobs:
tests:
runs-on: ubuntu-latest
env:
GO111MODULE: on
steps:
- name: Checkout Source
uses: actions/checkout@v5
- name: Run Gosec Security Scanner
uses: securego/gosec@master
with:
# we let the report trigger content trigger a failure using the GitHub Security features.
args: '-no-fail -fmt sarif -out results.sarif ./...'
- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@v3
with:
# Path to SARIF file relative to the root of the repository
sarif_file: results.sarif

View File

@@ -1,19 +1,35 @@
---
name: Unit tests
on:
push:
branches:
- master
pull_request:
env:
FORCE_COLOR: 1
concurrency:
group: ci-unit-${{ github.head_ref || github.ref }}-${{ github.repository }}
cancel-in-progress: true
jobs:
unit-tests:
strategy:
matrix:
go-version: ["1.25-bookworm"]
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Install earthly
uses: earthly/actions-setup@v1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Run tests
run: |
./earthly.sh +test
earthly +test --GO_VERSION=${{ matrix.go-version }}
- name: Codecov
uses: codecov/codecov-action@v5
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
with:
file: ./coverage.out

3
.gitignore vendored
View File

@@ -6,6 +6,7 @@
*.dylib
bin
testbin/*
manager
# Test binary, build with `go test -c`
*.test
@@ -24,3 +25,5 @@ testbin/*
*~
/helm-chart
build/
dist/

73
.goreleaser.yaml Normal file
View File

@@ -0,0 +1,73 @@
# Make sure to check the documentation at http://goreleaser.com
version: 2
project_name: kcrypt-discovery-challenger
builds:
- env:
- CGO_ENABLED=0
- CGO_LDFLAGS="-ldl"
goos:
- linux
goarch:
- amd64
- arm64
binary: '{{ .ProjectName }}'
id: default
main: ./cmd/discovery/main.go
- env:
- CGO_ENABLED=0
- GOEXPERIMENT=boringcrypto
- CGO_LDFLAGS="-ldl"
goos:
- linux
goarch:
- amd64
binary: '{{ .ProjectName }}'
id: fips-amd64
main: ./cmd/discovery/main.go
hooks:
post:
- bash -c 'set -e; go version {{.Path}} | grep boringcrypto || (echo "boringcrypto not found" && exit 1)'
- env:
- CGO_ENABLED=0
- GOEXPERIMENT=boringcrypto
- CC=aarch64-linux-gnu-gcc
- CGO_LDFLAGS="-ldl"
goos:
- linux
goarch:
- arm64
binary: '{{ .ProjectName }}'
id: fips-arm64
main: ./cmd/discovery/main.go
hooks:
post:
- bash -c 'set -e; go version {{.Path}} | grep boringcrypto || (echo "boringcrypto not found" && exit 1)'
source:
enabled: true
name_template: '{{ .ProjectName }}-{{ .Tag }}-source'
archives:
- id: default-archive
ids:
- default
name_template: '{{ .ProjectName }}-{{ .Tag }}-{{ .Os }}-{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}-{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}'
- id: fips-archive
ids:
- fips-arm64
- fips-amd64
name_template: '{{ .ProjectName }}-{{ .Tag }}-{{ .Os }}-{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}-{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}-fips'
checksum:
name_template: '{{ .ProjectName }}-{{ .Tag }}-checksums.txt'
snapshot:
version_template: "{{ .Tag }}-next"
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'
- '^Merge pull request'
env:
- GOSUMDB=sum.golang.org
before:
hooks:
- go mod tidy

View File

@@ -1,5 +1,5 @@
# Build the manager binary
FROM golang:1.20 as builder
FROM golang:1.25 as builder
WORKDIR /workspace
# Copy the Go Modules manifests
@@ -16,7 +16,7 @@ COPY pkg/ pkg/
COPY controllers/ controllers/
# Build
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o manager main.go
RUN CGO_ENABLED=0 go build -a -o manager main.go
# Use distroless as minimal base image to package the manager binary
# Refer to https://github.com/GoogleContainerTools/distroless for more details

View File

@@ -1,16 +1,20 @@
VERSION 0.6
ARG BASE_IMAGE=quay.io/kairos/core-ubuntu:latest
# renovate: datasource=github-releases depName=kairos-io/kairos
ARG KAIROS_VERSION="v2.5.0"
ARG BASE_IMAGE=quay.io/kairos/ubuntu:23.10-core-amd64-generic-$KAIROS_VERSION
ARG OSBUILDER_IMAGE=quay.io/kairos/osbuilder-tools
# renovate: datasource=docker depName=golang
ARG GO_VERSION=1.20
ARG GO_VERSION=1.25-bookworm
ARG LUET_VERSION=0.33.0
build-challenger:
FROM golang:alpine
FROM +go-deps
COPY . /work
WORKDIR /work
RUN CGO_ENABLED=0 go build -o kcrypt-discovery-challenger ./cmd/discovery
SAVE ARTIFACT /work/kcrypt-discovery-challenger AS LOCAL kcrypt-discovery-challenger
SAVE ARTIFACT /work/kcrypt-discovery-challenger kcrypt-discovery-challenger AS LOCAL kcrypt-discovery-challenger
image:
FROM $BASE_IMAGE
@@ -22,42 +26,33 @@ image-rootfs:
FROM +image
SAVE ARTIFACT --keep-own /. rootfs
grub-files:
FROM alpine
RUN apk add wget
RUN wget https://raw.githubusercontent.com/c3os-io/c3os/master/overlay/files-iso/boot/grub2/grub.cfg -O grub.cfg
SAVE ARTIFACT --keep-own grub.cfg grub.cfg
iso:
ARG OSBUILDER_IMAGE
ARG ISO_NAME=challenger
FROM $OSBUILDER_IMAGE
WORKDIR /build
COPY --keep-own +grub-files/grub.cfg /build/files-iso/boot/grub2/grub.cfg
COPY --keep-own +image-rootfs/rootfs /build/rootfs
RUN /entrypoint.sh --name $ISO_NAME --debug build-iso --squash-no-compression --date=false --local --overlay-iso /build/files-iso --output /build/ dir:/build/rootfs
RUN /entrypoint.sh --name $ISO_NAME --debug build-iso --squash-no-compression --date=false --output /build/ dir:/build/rootfs
SAVE ARTIFACT /build/$ISO_NAME.iso kairos.iso AS LOCAL build/$ISO_NAME.iso
SAVE ARTIFACT /build/$ISO_NAME.iso.sha256 kairos.iso.sha256 AS LOCAL build/$ISO_NAME.iso.sha256
test:
go-deps:
ARG GO_VERSION
FROM golang:$GO_VERSION
ENV CGO_ENABLED=0
WORKDIR /build
COPY go.mod go.sum ./
RUN go mod download
RUN go mod verify
SAVE ARTIFACT go.mod AS LOCAL go.mod
SAVE ARTIFACT go.sum AS LOCAL go.sum
test:
FROM +go-deps
ENV CGO_ENABLED=0
WORKDIR /work
# Cache layer for modules
COPY go.mod go.sum ./
RUN go mod download && go mod verify
RUN go get github.com/onsi/gomega/...
RUN go get github.com/onsi/ginkgo/v2/ginkgo/internal@v2.1.4
RUN go get github.com/onsi/ginkgo/v2/ginkgo/generators@v2.1.4
RUN go get github.com/onsi/ginkgo/v2/ginkgo/labels@v2.1.4
RUN go install -mod=mod github.com/onsi/ginkgo/v2/ginkgo
COPY . /work
RUN PATH=$PATH:$GOPATH/bin ginkgo run --covermode=atomic --coverprofile=coverage.out -p -r pkg/challenger cmd/discovery/client
COPY . .
RUN go run github.com/onsi/ginkgo/v2/ginkgo run --covermode=atomic --coverprofile=coverage.out -p -r pkg/challenger cmd/discovery/client
SAVE ARTIFACT coverage.out AS LOCAL coverage.out
# Generic targets
@@ -81,18 +76,12 @@ luet:
e2e-tests-image:
FROM opensuse/tumbleweed
RUN zypper in -y go git qemu-x86 qemu-arm qemu-tools swtpm docker jq docker-compose make glibc libopenssl-devel curl gettext-runtime
RUN zypper in -y go1.23 git qemu-x86 qemu-arm qemu-tools swtpm docker jq docker-compose make glibc libopenssl-devel curl gettext-runtime awk envsubst
ENV GOPATH="/go"
COPY . /test
WORKDIR /test
RUN go install -mod=mod github.com/onsi/ginkgo/v2/ginkgo
RUN go get github.com/onsi/gomega/...
RUN go get github.com/onsi/ginkgo/v2/ginkgo/internal@v2.7.1
RUN go get github.com/onsi/ginkgo/v2/ginkgo/generators@v2.7.1
RUN go get github.com/onsi/ginkgo/v2/ginkgo/labels@v2.7.1
IF [ -e /test/build/kairos.iso ]
ENV ISO=/test/build/kairos.iso
ELSE
@@ -105,11 +94,15 @@ e2e-tests-image:
RUN luet repo add -y kairos --url quay.io/kairos/packages --type docker
RUN LUET_NOLOCK=true luet install -y container/kubectl utils/k3d
controller-latest:
FROM DOCKERFILE .
SAVE IMAGE controller:latest
e2e-tests:
FROM +e2e-tests-image
ARG LABEL
WITH DOCKER --allow-privileged
RUN make test # This also generates the latest controllers automatically, we do that before building the docker image with them
WITH DOCKER --allow-privileged --load controller:latest=+controller-latest
RUN ./scripts/e2e-tests.sh
END

View File

@@ -103,7 +103,7 @@ vet: ## Run go vet against code.
.PHONY: test
test: manifests generate fmt vet envtest ## Run tests.
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test ./pkg/... -coverprofile cover.out
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test ./pkg/...
##@ Build
@@ -160,7 +160,7 @@ ENVTEST ?= $(LOCALBIN)/setup-envtest
## Tool Versions
KUSTOMIZE_VERSION ?= v3.8.7
CONTROLLER_TOOLS_VERSION ?= v0.9.2
CONTROLLER_TOOLS_VERSION ?= v0.16.0
KUSTOMIZE_INSTALL_SCRIPT ?= "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh"
.PHONY: kustomize
@@ -171,7 +171,8 @@ $(KUSTOMIZE): $(LOCALBIN)
.PHONY: controller-gen
controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary.
$(CONTROLLER_GEN): $(LOCALBIN)
test -s $(LOCALBIN)/controller-gen || GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_TOOLS_VERSION)
test -s $(LOCALBIN)/controller-gen || curl -L -v -Sso $(LOCALBIN)/controller-gen https://github.com/kubernetes-sigs/controller-tools/releases/download/$(CONTROLLER_TOOLS_VERSION)/controller-gen-linux-amd64
chmod +x $(LOCALBIN)/controller-gen
.PHONY: envtest
envtest: $(ENVTEST) ## Download envtest-setup locally if necessary.

View File

@@ -65,6 +65,33 @@ This is the Kairos kcrypt-challenger Kubernetes Native Extension.
See the documentation in our website: https://kairos.io/docs/advanced/partition_encryption/.
### TPM NV Memory Cleanup
⚠️ **DANGER**: This command removes encryption passphrases from TPM memory!
⚠️ **If you delete the wrong index, your encrypted disk may become UNBOOTABLE!**
During development and testing, the kcrypt-challenger may store passphrases in TPM non-volatile (NV) memory. These passphrases persist across reboots and can accumulate over time, taking up space in the TPM.
To clean up TPM NV memory used by the challenger:
```bash
# Clean up the default NV index (respects config or defaults to 0x1500000)
kcrypt-discovery-challenger cleanup
# Clean up a specific NV index
kcrypt-discovery-challenger cleanup --nv-index=0x1500001
# Clean up with specific TPM device
kcrypt-discovery-challenger cleanup --tpm-device=/dev/tpmrm0
```
**Safety Features:**
- By default, the command shows warnings and prompts for confirmation
- You must type "yes" to proceed with deletion
- Use `--i-know-what-i-am-doing` flag to skip the prompt (not recommended)
**Note**: This command uses native Go TPM libraries and requires appropriate permissions to access the TPM device.
## Installation
To install, use helm:

View File

@@ -23,11 +23,39 @@ import (
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
// PCRValues represents Platform Configuration Register values for boot state verification
// Uses a flexible map where keys are PCR indices (as strings) and values are hex-encoded PCR values
type PCRValues struct {
// PCRs is a flexible map of PCR index (as string) to PCR value (hex-encoded)
// Example: {"0": "a1b2c3...", "7": "d4e5f6...", "11": "g7h8i9..."}
// This allows for any combination of PCRs without hardcoding specific indices
PCRs map[string]string `json:"pcrs,omitempty"`
}
// AttestationSpec defines TPM attestation data for TOFU enrollment and verification
type AttestationSpec struct {
// EKPublicKey stores the Endorsement Key public key in PEM format
EKPublicKey string `json:"ekPublicKey,omitempty"`
// AKPublicKey stores the Attestation Key public key in PEM format
AKPublicKey string `json:"akPublicKey,omitempty"`
// PCRValues stores the expected PCR values for boot state verification
PCRValues *PCRValues `json:"pcrValues,omitempty"`
// EnrolledAt timestamp when this TPM was first enrolled
EnrolledAt *metav1.Time `json:"enrolledAt,omitempty"`
// LastVerifiedAt timestamp of the last successful attestation
LastVerifiedAt *metav1.Time `json:"lastVerifiedAt,omitempty"`
}
// SealedVolumeSpec defines the desired state of SealedVolume
type SealedVolumeSpec struct {
TPMHash string `json:"TPMHash,omitempty"`
Partitions []PartitionSpec `json:"partitions,omitempty"`
Quarantined bool `json:"quarantined,omitempty"`
Attestation *AttestationSpec `json:"attestation,omitempty"`
}
// PartitionSpec defines a Partition. A partition can be identified using

View File

@@ -25,6 +25,56 @@ import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AttestationSpec) DeepCopyInto(out *AttestationSpec) {
*out = *in
if in.PCRValues != nil {
in, out := &in.PCRValues, &out.PCRValues
*out = new(PCRValues)
(*in).DeepCopyInto(*out)
}
if in.EnrolledAt != nil {
in, out := &in.EnrolledAt, &out.EnrolledAt
*out = (*in).DeepCopy()
}
if in.LastVerifiedAt != nil {
in, out := &in.LastVerifiedAt, &out.LastVerifiedAt
*out = (*in).DeepCopy()
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AttestationSpec.
func (in *AttestationSpec) DeepCopy() *AttestationSpec {
if in == nil {
return nil
}
out := new(AttestationSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PCRValues) DeepCopyInto(out *PCRValues) {
*out = *in
if in.PCRs != nil {
in, out := &in.PCRs, &out.PCRs
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PCRValues.
func (in *PCRValues) DeepCopy() *PCRValues {
if in == nil {
return nil
}
out := new(PCRValues)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PartitionSpec) DeepCopyInto(out *PartitionSpec) {
*out = *in
@@ -114,6 +164,11 @@ func (in *SealedVolumeSpec) DeepCopyInto(out *SealedVolumeSpec) {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.Attestation != nil {
in, out := &in.Attestation, &out.Attestation
*out = new(AttestationSpec)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SealedVolumeSpec.

374
cmd/discovery/cli_test.go Normal file
View File

@@ -0,0 +1,374 @@
package main
import (
"os"
"path/filepath"
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func TestCLI(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Discovery CLI Suite")
}
var _ = Describe("CLI Interface", func() {
BeforeEach(func() {
// Clean up any previous log files
_ = os.Remove("/tmp/kcrypt-challenger-client.log")
})
AfterEach(func() {
// Clean up log files
_ = os.Remove("/tmp/kcrypt-challenger-client.log")
})
Context("CLI help", func() {
It("should show help when --help is used", func() {
err := ExecuteWithArgs([]string{"--help"})
Expect(err).To(BeNil())
// We can't easily test the output content without complex output capture,
// but we can verify the function executes without error
})
})
Context("Input validation", func() {
It("should require all partition parameters for get command", func() {
err := ExecuteWithArgs([]string{"get"})
Expect(err).To(HaveOccurred())
// Should return an error when required parameters are missing
})
It("should validate that all required fields are provided for get command", func() {
// Test with valid partition parameters
err := ExecuteWithArgs([]string{"get", "--partition-name=/dev/sda2"})
Expect(err).To(HaveOccurred()) // Should fail at client connection but parsing should work
// Test with valid UUID
err = ExecuteWithArgs([]string{"get", "--partition-uuid=12345"})
Expect(err).To(HaveOccurred()) // Should fail at client connection but parsing should work
})
It("should handle invalid flags gracefully", func() {
err := ExecuteWithArgs([]string{"--invalid-flag"})
Expect(err).To(HaveOccurred())
// Should return an error for invalid flags
})
})
Context("Flow detection and backend integration", func() {
It("should attempt to get passphrase with valid parameters", func() {
err := ExecuteWithArgs([]string{
"get",
"--partition-name=/dev/test",
"--partition-uuid=test-uuid-12345",
"--partition-label=test-label",
"--attempts=1",
})
// We expect this to fail since there's no server, but it should reach the backend logic
Expect(err).To(HaveOccurred())
// Should show flow detection in the log (if created)
logContent, readErr := os.ReadFile("/tmp/kcrypt-challenger-client.log")
if readErr == nil {
logStr := string(logContent)
// Should contain flow detection message
Expect(logStr).To(ContainSubstring("flow"))
}
})
It("should use the correct backend client logic", func() {
// Test that the CLI mode uses the same GetPassphrase method
err := ExecuteWithArgs([]string{
"get",
"--partition-name=/dev/test",
"--partition-uuid=test-uuid",
"--partition-label=test-label",
"--attempts=1",
})
// Should fail but attempt to use the client
Expect(err).To(HaveOccurred())
// The important thing is that it reaches the backend and doesn't crash
})
})
Context("Configuration overrides with debug logging", func() {
var tempDir string
var originalLogFile string
var testLogFile string
var configDir string
BeforeEach(func() {
// Create a temporary directory for this test
var err error
tempDir, err = os.MkdirTemp("", "kcrypt-test-*")
Expect(err).NotTo(HaveOccurred())
// Use /tmp/oem since it's already in confScanDirs
configDir = "/tmp/oem"
err = os.MkdirAll(configDir, 0755)
Expect(err).NotTo(HaveOccurred())
// Create a test configuration file with known values
configContent := `kcrypt:
challenger:
challenger_server: "https://default-server.com:8080"
mdns: false
certificate: "/default/path/to/cert.pem"
nv_index: "0x1500000"
c_index: "0x1400000"
tpm_device: "/dev/tpm0"
`
configFile := filepath.Join(configDir, "kairos.yaml")
err = os.WriteFile(configFile, []byte(configContent), 0644)
Expect(err).NotTo(HaveOccurred())
// Override the log file location for testing
originalLogFile = os.Getenv("KAIROS_LOG_FILE")
testLogFile = filepath.Join(tempDir, "kcrypt-discovery-challenger.log")
os.Setenv("KAIROS_LOG_FILE", testLogFile)
})
AfterEach(func() {
// Restore original log file setting
if originalLogFile != "" {
os.Setenv("KAIROS_LOG_FILE", originalLogFile)
} else {
os.Unsetenv("KAIROS_LOG_FILE")
}
// Clean up config file
_ = os.RemoveAll(configDir)
// Clean up temporary directory
_ = os.RemoveAll(tempDir)
})
It("should read and use original configuration values without overrides", func() {
err := ExecuteWithArgs([]string{
"get",
"--partition-name=/dev/test",
"--partition-uuid=test-uuid",
"--partition-label=test-label",
"--debug",
"--attempts=1",
})
// Should fail at passphrase retrieval but config parsing should work
Expect(err).To(HaveOccurred())
// Check that original configuration values are logged
logContent, readErr := os.ReadFile(testLogFile)
if readErr == nil {
logStr := string(logContent)
// Should show original configuration values from the file
Expect(logStr).To(ContainSubstring("Original configuration"))
Expect(logStr).To(ContainSubstring("https://default-server.com:8080"))
Expect(logStr).To(ContainSubstring("false")) // mdns value
Expect(logStr).To(ContainSubstring("/default/path/to/cert.pem"))
// Should also show final configuration (which should be the same as original)
Expect(logStr).To(ContainSubstring("Final configuration"))
// Should NOT contain any override messages since no flags were provided
Expect(logStr).NotTo(ContainSubstring("Overriding server URL"))
Expect(logStr).NotTo(ContainSubstring("Overriding MDNS setting"))
Expect(logStr).NotTo(ContainSubstring("Overriding certificate"))
}
})
It("should show configuration file values being overridden by CLI flags", func() {
err := ExecuteWithArgs([]string{
"get",
"--partition-name=/dev/test",
"--partition-uuid=test-uuid",
"--partition-label=test-label",
"--challenger-server=https://overridden-server.com:9999",
"--mdns=true",
"--certificate=/overridden/cert.pem",
"--debug",
"--attempts=1",
})
// Should fail at passphrase retrieval but config parsing and overrides should work
Expect(err).To(HaveOccurred())
// Check that both original and overridden values are logged
logContent, readErr := os.ReadFile(testLogFile)
if readErr == nil {
logStr := string(logContent)
// Should show original configuration values from the file
Expect(logStr).To(ContainSubstring("Original configuration"))
Expect(logStr).To(ContainSubstring("https://default-server.com:8080"))
Expect(logStr).To(ContainSubstring("/default/path/to/cert.pem"))
// Should show override messages
Expect(logStr).To(ContainSubstring("Overriding server URL"))
Expect(logStr).To(ContainSubstring("https://default-server.com:8080 -> https://overridden-server.com:9999"))
Expect(logStr).To(ContainSubstring("Overriding MDNS setting"))
Expect(logStr).To(ContainSubstring("false -> true"))
Expect(logStr).To(ContainSubstring("Overriding certificate"))
// Should show final configuration with overridden values
Expect(logStr).To(ContainSubstring("Final configuration"))
Expect(logStr).To(ContainSubstring("https://overridden-server.com:9999"))
Expect(logStr).To(ContainSubstring("/overridden/cert.pem"))
}
})
It("should apply CLI flag overrides and log configuration changes", func() {
err := ExecuteWithArgs([]string{
"get",
"--partition-name=/dev/test",
"--partition-uuid=test-uuid",
"--partition-label=test-label",
"--challenger-server=https://custom-server.com:8082",
"--mdns=true",
"--certificate=/path/to/cert.pem",
"--debug",
"--attempts=1",
})
// Should fail at passphrase retrieval but flag parsing should work
Expect(err).To(HaveOccurred())
// Check if debug log exists and contains configuration information
logContent, readErr := os.ReadFile(testLogFile)
if readErr == nil {
logStr := string(logContent)
// Should contain debug information about configuration overrides
Expect(logStr).To(ContainSubstring("Overriding server URL"))
Expect(logStr).To(ContainSubstring("https://custom-server.com:8082"))
Expect(logStr).To(ContainSubstring("Overriding MDNS setting"))
Expect(logStr).To(ContainSubstring("Overriding certificate"))
}
})
It("should show original vs final configuration in debug mode", func() {
err := ExecuteWithArgs([]string{
"get",
"--partition-name=/dev/test",
"--partition-uuid=test-uuid",
"--partition-label=test-label",
"--challenger-server=https://override-server.com:9999",
"--debug",
"--attempts=1",
})
// Should fail but debug information should be logged
Expect(err).To(HaveOccurred())
// Check for original and final configuration logging
logContent, readErr := os.ReadFile(testLogFile)
if readErr == nil {
logStr := string(logContent)
Expect(logStr).To(ContainSubstring("Original configuration"))
Expect(logStr).To(ContainSubstring("Final configuration"))
Expect(logStr).To(ContainSubstring("https://override-server.com:9999"))
}
})
It("should log partition details in debug mode", func() {
err := ExecuteWithArgs([]string{
"get",
"--partition-name=/dev/custom-partition",
"--partition-uuid=custom-uuid-123",
"--partition-label=custom-label-456",
"--debug",
"--attempts=2",
})
Expect(err).To(HaveOccurred())
// Check for partition details in debug log
logContent, readErr := os.ReadFile(testLogFile)
if readErr == nil {
logStr := string(logContent)
Expect(logStr).To(ContainSubstring("Partition details"))
Expect(logStr).To(ContainSubstring("/dev/custom-partition"))
Expect(logStr).To(ContainSubstring("custom-uuid-123"))
Expect(logStr).To(ContainSubstring("custom-label-456"))
Expect(logStr).To(ContainSubstring("Attempts: 2"))
}
})
It("should not log debug information without debug flag", func() {
err := ExecuteWithArgs([]string{
"get",
"--partition-name=/dev/test",
"--partition-uuid=test-uuid",
"--partition-label=test-label",
"--attempts=1",
})
Expect(err).To(HaveOccurred())
// Debug log should not exist or should not contain detailed debug info
logContent, readErr := os.ReadFile(testLogFile)
if readErr == nil {
logStr := string(logContent)
// Should not contain debug-level details
Expect(logStr).NotTo(ContainSubstring("Original configuration"))
Expect(logStr).NotTo(ContainSubstring("Partition details"))
}
})
It("should handle missing configuration file gracefully and show defaults", func() {
// Remove the config file to test default behavior
_ = os.RemoveAll(configDir)
err := ExecuteWithArgs([]string{
"get",
"--partition-name=/dev/test",
"--partition-uuid=test-uuid",
"--partition-label=test-label",
"--debug",
"--attempts=1",
})
// Should fail at passphrase retrieval but not due to config parsing
Expect(err).To(HaveOccurred())
// Check that default/empty configuration values are logged
logContent, readErr := os.ReadFile(testLogFile)
if readErr == nil {
logStr := string(logContent)
// Should show original configuration (which should be empty/defaults)
Expect(logStr).To(ContainSubstring("Original configuration"))
Expect(logStr).To(ContainSubstring("Final configuration"))
// Should NOT contain override messages since no flags were provided
Expect(logStr).NotTo(ContainSubstring("Overriding server URL"))
Expect(logStr).NotTo(ContainSubstring("Overriding MDNS setting"))
Expect(logStr).NotTo(ContainSubstring("Overriding certificate"))
}
})
})
Context("CLI argument parsing", func() {
It("should parse all arguments correctly", func() {
// This will fail at the client creation/server connection,
// but should successfully parse all arguments
err := ExecuteWithArgs([]string{
"get",
"--partition-name=/dev/custom",
"--partition-uuid=custom-uuid-999",
"--partition-label=custom-label",
"--attempts=5",
})
Expect(err).To(HaveOccurred()) // Fails due to no server
// The important thing is that flag parsing worked and it reached the backend
})
It("should handle boolean flags correctly", func() {
// Test help flag
err := ExecuteWithArgs([]string{"--help"})
Expect(err).To(BeNil())
})
})
})

View File

@@ -1,35 +1,55 @@
package client
import (
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"fmt"
"os"
"time"
"github.com/google/go-attestation/attest"
"github.com/gorilla/websocket"
"github.com/jaypipes/ghw/pkg/block"
"github.com/kairos-io/kairos-challenger/pkg/constants"
"github.com/kairos-io/kairos-challenger/pkg/payload"
"github.com/kairos-io/kcrypt/pkg/bus"
"github.com/kairos-io/kairos-sdk/kcrypt/bus"
"github.com/kairos-io/kairos-sdk/types"
"github.com/kairos-io/tpm-helpers"
"github.com/mudler/go-pluggable"
"github.com/mudler/yip/pkg/utils"
"github.com/kairos-io/kairos-challenger/pkg/constants"
)
// Because of how go-pluggable works, we can't just print to stdout
const LOGFILE = "/tmp/kcrypt-challenger-client.log"
// Retry delays for different failure types
const (
TPMRetryDelay = 100 * time.Millisecond // Brief delay for TPM hardware busy/unavailable
NetworkRetryDelay = 1 * time.Second // Longer delay for network/server issues
)
var errPartNotFound error = fmt.Errorf("pass for partition not found")
var errBadCertificate error = fmt.Errorf("unknown certificate")
func NewClient() (*Client, error) {
return NewClientWithLogger(types.NewKairosLogger("kcrypt-challenger-client", "error", false))
}
func NewClientWithLogger(logger types.KairosLogger) (*Client, error) {
conf, err := unmarshalConfig()
if err != nil {
return nil, err
}
return &Client{Config: conf}, nil
return &Client{Config: conf, Logger: logger}, nil
}
// echo '{ "data": "{ \\"label\\": \\"LABEL\\" }"}' | sudo -E WSS_SERVER="http://localhost:8082/challenge" ./challenger "discovery.password"
func (c *Client) Start() error {
if err := os.RemoveAll(LOGFILE); err != nil { // Start fresh
return fmt.Errorf("removing the logfile: %w", err)
}
factory := pluggable.NewPluginFactory()
// Input: bus.EventInstallPayload
@@ -44,7 +64,8 @@ func (c *Client) Start() error {
}
}
pass, err := c.waitPass(b, 30)
// Use the extracted core logic
pass, err := c.GetPassphrase(b, 30)
if err != nil {
return pluggable.EventResponse{
Error: fmt.Sprintf("failed getting pass: %s", err.Error()),
@@ -59,70 +80,207 @@ func (c *Client) Start() error {
return factory.Run(pluggable.EventType(os.Args[1]), os.Stdin, os.Stdout)
}
func (c *Client) generatePass(postEndpoint string, p *block.Partition) error {
// echo '{ "data": "{ \\"label\\": \\"LABEL\\" }"}' | sudo -E WSS_SERVER="http://localhost:8082/challenge" ./challenger "discovery.password"
// GetPassphrase retrieves a passphrase for the given partition - core business logic
func (c *Client) GetPassphrase(partition *block.Partition, attempts int) (string, error) {
serverURL := c.Config.Kcrypt.Challenger.Server
rand := utils.RandomString(32)
pass, err := tpm.EncryptBlob([]byte(rand))
if err != nil {
return err
}
bpass := base64.RawURLEncoding.EncodeToString(pass)
opts := []tpm.Option{
tpm.WithCAs([]byte(c.Config.Kcrypt.Challenger.Certificate)),
tpm.AppendCustomCAToSystemCA,
tpm.WithAdditionalHeader("label", p.Label),
tpm.WithAdditionalHeader("name", p.Name),
tpm.WithAdditionalHeader("uuid", p.UUID),
}
conn, err := tpm.Connection(postEndpoint, opts...)
if err != nil {
return err
}
return conn.WriteJSON(payload.Data{Passphrase: bpass, GeneratedBy: constants.TPMSecret})
}
func (c *Client) waitPass(p *block.Partition, attempts int) (pass string, err error) {
// IF we don't have any server configured, just do local
if c.Config.Kcrypt.Challenger.Server == "" {
// If we don't have any server configured, just do local
if serverURL == "" {
return localPass(c.Config)
}
challengeEndpoint := fmt.Sprintf("%s/getPass", c.Config.Kcrypt.Challenger.Server)
postEndpoint := fmt.Sprintf("%s/postPass", c.Config.Kcrypt.Challenger.Server)
additionalHeaders := map[string]string{}
var err error
if c.Config.Kcrypt.Challenger.MDNS {
serverURL, additionalHeaders, err = queryMDNS(serverURL, c.Logger)
if err != nil {
return "", err
}
}
c.Logger.Debugf("Starting TPM attestation flow with server: %s", serverURL)
return c.waitPassWithTPMAttestation(serverURL, additionalHeaders, partition, attempts)
}
// waitPassWithTPMAttestation implements the new TPM remote attestation flow over WebSocket
func (c *Client) waitPassWithTPMAttestation(serverURL string, additionalHeaders map[string]string, p *block.Partition, attempts int) (string, error) {
attestationEndpoint := fmt.Sprintf("%s/tpm-attestation", serverURL)
c.Logger.Debugf("Debug: TPM attestation endpoint: %s", attestationEndpoint)
for tries := 0; tries < attempts; tries++ {
var generated bool
pass, generated, err = getPass(challengeEndpoint, c.Config.Kcrypt.Challenger.Certificate, p)
if err == errPartNotFound {
// IF server doesn't have a pass for us, then we generate one and we set it
err = c.generatePass(postEndpoint, p)
c.Logger.Debugf("Debug: TPM attestation attempt %d/%d", tries+1, attempts)
// Step 1: Initialize AK Manager
c.Logger.Debugf("Debug: Initializing AK Manager with handle file: %s", constants.AKBlobFile)
akManager, err := tpm.NewAKManager(tpm.WithAKHandleFile(constants.AKBlobFile))
if err != nil {
return
c.Logger.Debugf("Failed to create AK manager: %v", err)
time.Sleep(TPMRetryDelay)
continue
}
// Attempt to fetch again - validate that the server has it now
tries = 0
c.Logger.Debugf("Debug: AK Manager initialized successfully")
// Step 2: Ensure AK exists
c.Logger.Debugf("Debug: Getting or creating AK")
_, err = akManager.GetOrCreateAK()
if err != nil {
c.Logger.Debugf("Failed to get/create AK: %v", err)
time.Sleep(TPMRetryDelay)
continue
}
c.Logger.Debugf("Debug: AK obtained/created successfully")
// Step 3: Start WebSocket-based attestation flow
c.Logger.Debugf("Debug: Starting WebSocket-based attestation flow")
passphrase, err := c.performTPMAttestation(attestationEndpoint, additionalHeaders, akManager, p)
if err != nil {
c.Logger.Debugf("Failed TPM attestation: %v", err)
time.Sleep(NetworkRetryDelay)
continue
}
if generated { // passphrase is encrypted
return c.decryptPassphrase(pass)
return passphrase, nil
}
if err == errBadCertificate { // No need to retry, won't succeed.
return
return "", fmt.Errorf("exhausted all attempts (%d) for TPM attestation", attempts)
}
// performTPMAttestation handles the complete attestation flow over a single WebSocket connection
func (c *Client) performTPMAttestation(endpoint string, additionalHeaders map[string]string, akManager *tpm.AKManager, p *block.Partition) (string, error) {
c.Logger.Debugf("Debug: Creating WebSocket connection to endpoint: %s", endpoint)
c.Logger.Debugf("Debug: Partition details - Label: %s, Name: %s, UUID: %s", p.FilesystemLabel, p.Name, p.UUID)
c.Logger.Debugf("Debug: Certificate length: %d", len(c.Config.Kcrypt.Challenger.Certificate))
// Create WebSocket connection
opts := []tpm.Option{
tpm.WithAdditionalHeader("label", p.FilesystemLabel),
tpm.WithAdditionalHeader("name", p.Name),
tpm.WithAdditionalHeader("uuid", p.UUID),
}
if err == nil { // passphrase available, no errors
return
// Only add certificate options if a certificate is provided
if len(c.Config.Kcrypt.Challenger.Certificate) > 0 {
c.Logger.Debugf("Debug: Adding certificate validation options")
opts = append(opts,
tpm.WithCAs([]byte(c.Config.Kcrypt.Challenger.Certificate)),
tpm.AppendCustomCAToSystemCA,
)
} else {
c.Logger.Debugf("Debug: No certificate provided, using insecure connection")
}
for k, v := range additionalHeaders {
opts = append(opts, tpm.WithAdditionalHeader(k, v))
}
c.Logger.Debugf("Debug: WebSocket options configured, attempting connection...")
// Add connection timeout to prevent hanging indefinitely
type connectionResult struct {
conn interface{}
err error
}
fmt.Printf("Failed with error: %s . Will retry.\n", err.Error())
time.Sleep(1 * time.Second) // network errors? retry
done := make(chan connectionResult, 1)
go func() {
c.Logger.Debugf("Debug: Using tpm.AttestationConnection for new TPM flow")
conn, err := tpm.AttestationConnection(endpoint, opts...)
c.Logger.Debugf("Debug: tpm.AttestationConnection returned with err: %v", err)
done <- connectionResult{conn: conn, err: err}
}()
var conn *websocket.Conn
select {
case result := <-done:
if result.err != nil {
c.Logger.Debugf("Debug: WebSocket connection failed: %v", result.err)
return "", fmt.Errorf("creating WebSocket connection: %w", result.err)
}
var ok bool
conn, ok = result.conn.(*websocket.Conn)
if !ok {
return "", fmt.Errorf("unexpected connection type")
}
c.Logger.Debugf("Debug: WebSocket connection established successfully")
case <-time.After(10 * time.Second):
c.Logger.Debugf("Debug: WebSocket connection timed out after 10 seconds")
return "", fmt.Errorf("WebSocket connection timed out")
}
return
defer conn.Close() //nolint:errcheck
// Protocol Step 1: Send attestation data (EK + AK) to server so it can generate proper challenge
c.Logger.Debugf("Debug: Getting attestation data for challenge generation")
ek, akParams, err := akManager.GetAttestationData()
if err != nil {
return "", fmt.Errorf("getting attestation data: %w", err)
}
c.Logger.Debugf("Debug: Got EK and AK attestation data")
// Serialize EK to bytes using the existing encoding from tmp-helpers
ekPEM, err := encodeEKToBytes(ek)
if err != nil {
return "", fmt.Errorf("encoding EK to bytes: %w", err)
}
// Serialize AK parameters to JSON bytes
akBytes, err := json.Marshal(akParams)
if err != nil {
return "", fmt.Errorf("marshaling AK parameters: %w", err)
}
// Send attestation data to server as bytes
attestationData := struct {
EKBytes []byte `json:"ek_bytes"`
AKBytes []byte `json:"ak_bytes"`
}{
EKBytes: ekPEM,
AKBytes: akBytes,
}
c.Logger.Debugf("Debug: Sending attestation data to server")
if err := conn.WriteJSON(attestationData); err != nil {
return "", fmt.Errorf("sending attestation data: %w", err)
}
c.Logger.Debugf("Debug: Attestation data sent successfully")
// Protocol Step 2: Wait for challenge response from server
c.Logger.Debugf("Debug: Waiting for challenge from server")
var challengeResp tpm.AttestationChallengeResponse
if err := conn.ReadJSON(&challengeResp); err != nil {
return "", fmt.Errorf("reading challenge from server: %w", err)
}
c.Logger.Debugf("Challenge received - Enrolled: %t", challengeResp.Enrolled)
// Protocol Step 3: Create proof request using AK Manager
c.Logger.Debugf("Debug: Creating proof request from challenge response")
proofReq, err := akManager.CreateProofRequest(&challengeResp)
if err != nil {
return "", fmt.Errorf("creating proof request: %w", err)
}
c.Logger.Debugf("Debug: Proof request created successfully")
// Protocol Step 4: Send proof to server
c.Logger.Debugf("Debug: Sending proof request to server")
if err := conn.WriteJSON(proofReq); err != nil {
return "", fmt.Errorf("sending proof request: %w", err)
}
c.Logger.Debugf("Proof request sent")
// Protocol Step 5: Receive passphrase from server
c.Logger.Debugf("Debug: Waiting for passphrase response")
var proofResp tpm.ProofResponse
if err := conn.ReadJSON(&proofResp); err != nil {
return "", fmt.Errorf("reading passphrase response: %w", err)
}
c.Logger.Debugf("Passphrase received - Length: %d bytes", len(proofResp.Passphrase))
// Check if we received an empty passphrase (indicates server error)
if len(proofResp.Passphrase) == 0 {
return "", fmt.Errorf("server returned empty passphrase, indicating an error occurred during attestation")
}
return string(proofResp.Passphrase), nil
}
// decryptPassphrase decodes (base64) and decrypts the passphrase returned
@@ -145,3 +303,26 @@ func (c *Client) decryptPassphrase(pass string) (string, error) {
return string(passBytes), err
}
// encodeEKToBytes encodes an EK to PEM bytes for transmission
func encodeEKToBytes(ek *attest.EK) ([]byte, error) {
if ek.Certificate != nil {
pemBlock := &pem.Block{
Type: "CERTIFICATE",
Bytes: ek.Certificate.Raw,
}
return pem.EncodeToMemory(pemBlock), nil
}
// For EKs without certificates, marshal the public key
pubBytes, err := x509.MarshalPKIXPublicKey(ek.Public)
if err != nil {
return nil, fmt.Errorf("marshaling EK public key: %w", err)
}
pemBlock := &pem.Block{
Type: "PUBLIC KEY",
Bytes: pubBytes,
}
return pem.EncodeToMemory(pemBlock), nil
}

View File

@@ -1,22 +1,30 @@
package client
import (
"github.com/kairos-io/kairos/pkg/config"
kconfig "github.com/kairos-io/kcrypt/pkg/config"
"github.com/kairos-io/kairos-sdk/collector"
"github.com/kairos-io/kairos-sdk/types"
"gopkg.in/yaml.v3"
)
// There are the directories under which we expect to find kairos configuration.
// When we are booted from an iso (during installation), configuration is expected
// under `/oem`. When we are booting an installed system (in initramfs phase),
// the path is `/sysroot/oem`.
// When we run the challenger in hooks, we may have the config under /tmp/oem
var confScanDirs = []string{"/oem", "/sysroot/oem", "/tmp/oem"}
type Client struct {
Config Config
Logger types.KairosLogger
}
type Config struct {
Kcrypt struct {
Challenger struct {
MDNS bool `yaml:"mdns,omitempty"`
Server string `yaml:"challenger_server,omitempty"`
// Non-volatile index memory: where we store the encrypted passphrase (offline mode)
NVIndex string `yaml:"nv_index,omitempty"`
// Certificate index: this is where the rsa pair that decrypts the passphrase lives
CIndex string `yaml:"c_index,omitempty"`
NVIndex string `yaml:"nv_index,omitempty"` // Non-volatile index memory: where we store the encrypted passphrase (offline mode)
CIndex string `yaml:"c_index,omitempty"` // Certificate index: this is where the rsa pair that decrypts the passphrase lives
TPMDevice string `yaml:"tpm_device,omitempty"`
Certificate string `yaml:"certificate,omitempty"`
}
@@ -26,12 +34,21 @@ type Config struct {
func unmarshalConfig() (Config, error) {
var result Config
c, err := config.Scan(config.Directories(kconfig.ConfigScanDirs...), config.NoLogs)
o := &collector.Options{NoLogs: true, MergeBootCMDLine: false}
if err := o.Apply(collector.Directories(confScanDirs...)); err != nil {
return result, err
}
c, err := collector.Scan(o, func(d []byte) ([]byte, error) {
return d, nil
})
if err != nil {
return result, err
}
if err = c.Unmarshal(&result); err != nil {
a, _ := c.String()
err = yaml.Unmarshal([]byte(a), &result)
if err != nil {
return result, err
}

View File

@@ -1,52 +1,12 @@
package client
import (
"encoding/json"
"fmt"
"strings"
"github.com/kairos-io/kairos-challenger/pkg/constants"
"github.com/kairos-io/kairos-challenger/pkg/payload"
"github.com/jaypipes/ghw/pkg/block"
"github.com/kairos-io/tpm-helpers"
"github.com/mudler/yip/pkg/utils"
"github.com/pkg/errors"
)
const DefaultNVIndex = "0x1500000"
func getPass(server, certificate string, partition *block.Partition) (string, bool, error) {
msg, err := tpm.Get(server,
tpm.WithCAs([]byte(certificate)),
tpm.AppendCustomCAToSystemCA,
tpm.WithAdditionalHeader("label", partition.Label),
tpm.WithAdditionalHeader("name", partition.Name),
tpm.WithAdditionalHeader("uuid", partition.UUID))
if err != nil {
return "", false, err
}
result := payload.Data{}
err = json.Unmarshal(msg, &result)
if err != nil {
return "", false, errors.Wrap(err, string(msg))
}
if result.HasPassphrase() {
return fmt.Sprint(result.Passphrase), result.HasBeenGenerated() && result.GeneratedBy == constants.TPMSecret, nil
} else if result.HasError() {
if strings.Contains(result.Error, "No secret found for") {
return "", false, errPartNotFound
}
if strings.Contains(result.Error, "x509: certificate signed by unknown authority") {
return "", false, errBadCertificate
}
return "", false, fmt.Errorf(result.Error)
}
return "", false, errPartNotFound
}
func genAndStore(k Config) (string, error) {
opts := []tpm.TPMOption{}
if k.Kcrypt.Challenger.TPMDevice != "" {

View File

@@ -0,0 +1,47 @@
package client
import (
"testing"
"github.com/kairos-io/kairos-sdk/types"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func TestClient(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Discovery Client Suite")
}
var _ = Describe("Flow Detection", func() {
var client *Client
BeforeEach(func() {
// Create a test client with basic config and logger
client = &Client{}
client.Config.Kcrypt.Challenger.Server = "http://test-server.local"
client.Logger = types.NewKairosLogger("test-client", "debug", false)
})
Context("TPM attestation capabilities", func() {
It("should handle TPM operations", func() {
// Test that client can be created without errors
// TPM availability testing requires actual hardware
Expect(client).ToNot(BeNil())
})
})
Context("Logging functionality", func() {
It("should have a valid logger", func() {
// Test that client has a valid logger
Expect(client.Logger).NotTo(BeNil())
// Test debug logging works without error
client.Logger.Debugf("Test log entry for flow detection")
// If we get here without panic, logging is working
Expect(true).To(BeTrue())
})
})
})

View File

@@ -0,0 +1,86 @@
package client
import (
"fmt"
"net/url"
"strconv"
"strings"
"time"
"github.com/hashicorp/mdns"
"github.com/kairos-io/kairos-sdk/types"
)
const (
MDNSServiceType = "_kcrypt._tcp"
MDNSTimeout = 15 * time.Second
)
// queryMDNS will make an mdns query on local network to find a kcrypt challenger server
// instance. If none is found, the original URL is returned and no additional headers.
// If a response is received, the IP address and port from the response will be returned// and an additional "Host" header pointing to the original host.
func queryMDNS(originalURL string, logger types.KairosLogger) (string, map[string]string, error) {
additionalHeaders := map[string]string{}
var err error
parsedURL, err := url.Parse(originalURL)
if err != nil {
return originalURL, additionalHeaders, fmt.Errorf("parsing the original host: %w", err)
}
host := parsedURL.Host
if !strings.HasSuffix(host, ".local") { // sanity check
return "", additionalHeaders, fmt.Errorf("domain should end in \".local\" when using mdns")
}
mdnsIP, mdnsPort := discoverMDNSServer(host, logger)
if mdnsIP == "" { // no reply
logger.Debugf("no reply from mdns")
return originalURL, additionalHeaders, nil
}
additionalHeaders["Host"] = parsedURL.Host
newURL := strings.ReplaceAll(originalURL, host, mdnsIP)
// Remove any port in the original url
if port := parsedURL.Port(); port != "" {
newURL = strings.ReplaceAll(newURL, port, "")
}
// Add any possible port from the mdns response
if mdnsPort != "" {
newURL = strings.ReplaceAll(newURL, mdnsIP, fmt.Sprintf("%s:%s", mdnsIP, mdnsPort))
}
return newURL, additionalHeaders, nil
}
// discoverMDNSServer performs an mDNS query to discover any running kcrypt challenger
// servers on the same network that matches the given hostname.
// If a response if received, the IP address and the Port from the response are returned.
func discoverMDNSServer(hostname string, logger types.KairosLogger) (string, string) {
// Make a channel for results and start listening
entriesCh := make(chan *mdns.ServiceEntry, 4)
defer close(entriesCh)
logger.Debugf("Will now wait for some mdns server to respond")
// Start the lookup. It will block until we read from the chan.
mdns.Lookup(MDNSServiceType, entriesCh)
expectedHost := hostname + "." // FQDN
// Wait until a matching server is found or we reach a timeout
for {
select {
case entry := <-entriesCh:
logger.Debugf("mdns response received")
if entry.Host == expectedHost {
logger.Debugf("%s matches %s", entry.Host, expectedHost)
return entry.AddrV4.String(), strconv.Itoa(entry.Port) // TODO: v6?
} else {
logger.Debugf("%s didn't match %s", entry.Host, expectedHost)
}
case <-time.After(MDNSTimeout):
logger.Debugf("timed out waiting for mdns")
return "", ""
}
}
}

View File

@@ -1,30 +1,478 @@
package main
import (
"bufio"
"fmt"
"os"
"strings"
"github.com/jaypipes/ghw/pkg/block"
"github.com/kairos-io/kairos-challenger/cmd/discovery/client"
"github.com/kairos-io/kcrypt/pkg/bus"
"github.com/kairos-io/kairos-challenger/pkg/constants"
"github.com/kairos-io/kairos-sdk/kcrypt/bus"
"github.com/kairos-io/kairos-sdk/types"
"github.com/kairos-io/tpm-helpers"
"github.com/spf13/cobra"
)
func main() {
if len(os.Args) >= 2 && bus.IsEventDefined(os.Args[1]) {
c, err := client.NewClient()
checkErr(err)
checkErr(c.Start())
return
}
pubhash, err := tpm.GetPubHash()
checkErr(err)
fmt.Print(pubhash)
// GetFlags holds all flags specific to the get command
type GetFlags struct {
PartitionName string
PartitionUUID string
PartitionLabel string
Attempts int
ChallengerServer string
EnableMDNS bool
ServerCertificate string
}
func checkErr(err error) {
if err != nil {
fmt.Println(err)
var (
// Global/persistent flags
debug bool
)
// rootCmd represents the base command (TPM hash generation)
var rootCmd = &cobra.Command{
Use: "kcrypt-discovery-challenger",
Short: "kcrypt-challenger discovery client",
Long: `kcrypt-challenger discovery client
This tool provides TPM-based operations for encrypted partition management.
By default, it outputs the TPM hash for this device.
Configuration:
The client reads configuration from Kairos configuration files in the following directories:
- /oem (during installation from ISO)
- /sysroot/oem (on installed systems during initramfs)
- /tmp/oem (when running in hooks)
Configuration format (YAML):
kcrypt:
challenger:
challenger_server: "https://my-server.com:8082" # Server URL
mdns: true # Enable mDNS discovery
certificate: "/path/to/server-cert.pem" # Server certificate
nv_index: "0x1500000" # TPM NV index (offline mode)
c_index: "0x1500001" # TPM certificate index
tpm_device: "/dev/tpmrm0" # TPM device path`,
Example: ` # Get TPM hash for this device (default)
kcrypt-discovery-challenger
# Get passphrase for encrypted partition
kcrypt-discovery-challenger get --partition-name=/dev/sda2
# Clean up TPM NV memory (useful for development)
kcrypt-discovery-challenger cleanup
# Run plugin event
kcrypt-discovery-challenger discovery.password`,
RunE: func(cmd *cobra.Command, args []string) error {
return runTPMHash()
},
}
// newCleanupCmd creates the cleanup command
func newCleanupCmd() *cobra.Command {
var nvIndex string
var tpmDevice string
var skipConfirmation bool
cmd := &cobra.Command{
Use: "cleanup",
Short: "Clean up TPM NV memory",
Long: `Clean up TPM NV memory by undefining specific NV indices.
⚠️ DANGER: This command removes encryption passphrases from TPM memory!
⚠️ If you delete the wrong index, your encrypted disk may become UNBOOTABLE!
This command helps clean up TPM NV memory used by the local pass flow,
which stores encrypted passphrases in TPM non-volatile memory. Without
cleanup, these passphrases persist indefinitely and take up space.
The command will prompt for confirmation before deletion unless you use
the --i-know-what-i-am-doing flag to skip the safety prompt.
Default behavior:
- Uses the same NV index as the local pass flow (from config or 0x1500000)
- Uses the same TPM device as configured (or system default if none specified)
- Prompts for confirmation with safety warnings`,
Example: ` # Clean up default NV index (with confirmation prompt)
kcrypt-discovery-challenger cleanup
# Clean up specific NV index
kcrypt-discovery-challenger cleanup --nv-index=0x1500001
# Clean up with specific TPM device
kcrypt-discovery-challenger cleanup --tpm-device=/dev/tpmrm0
# Skip confirmation prompt (DANGEROUS!)
kcrypt-discovery-challenger cleanup --i-know-what-i-am-doing`,
RunE: func(cmd *cobra.Command, args []string) error {
return runCleanup(nvIndex, tpmDevice, skipConfirmation)
},
}
cmd.Flags().StringVar(&nvIndex, "nv-index", "", fmt.Sprintf("NV index to clean up (defaults to configured index or %s)", client.DefaultNVIndex))
cmd.Flags().StringVar(&tpmDevice, "tpm-device", "", "TPM device path (defaults to configured device or system default)")
cmd.Flags().BoolVar(&skipConfirmation, "i-know-what-i-am-doing", false, "Skip confirmation prompt (DANGEROUS: may make encrypted disks unbootable)")
return cmd
}
// newGetCmd creates the get command with its flags
func newGetCmd() *cobra.Command {
flags := &GetFlags{}
cmd := &cobra.Command{
Use: "get",
Short: "Get passphrase for encrypted partition",
Long: `Get passphrase for encrypted partition using TPM attestation.
This command retrieves passphrases for encrypted partitions by communicating
with a challenger server using TPM-based attestation. At least one partition
identifier (name, UUID, or label) must be provided.
The command uses configuration from the root command's config files, but flags
can override specific settings:
--challenger-server Override kcrypt.challenger.challenger_server
--mdns Override kcrypt.challenger.mdns
--certificate Override kcrypt.challenger.certificate`,
Example: ` # Get passphrase using partition name
kcrypt-discovery-challenger get --partition-name=/dev/sda2
# Get passphrase using UUID
kcrypt-discovery-challenger get --partition-uuid=12345-abcde
# Get passphrase using filesystem label
kcrypt-discovery-challenger get --partition-label=encrypted-data
# Get passphrase with multiple identifiers
kcrypt-discovery-challenger get --partition-name=/dev/sda2 --partition-uuid=12345-abcde --partition-label=encrypted-data
# Get passphrase with custom server
kcrypt-discovery-challenger get --partition-label=encrypted-data --challenger-server=https://my-server.com:8082`,
PreRunE: func(cmd *cobra.Command, args []string) error {
// Validate that at least one partition identifier is provided
if flags.PartitionName == "" && flags.PartitionUUID == "" && flags.PartitionLabel == "" {
return fmt.Errorf("at least one of --partition-name, --partition-uuid, or --partition-label must be provided")
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
return runGetPassphrase(flags)
},
}
// Register flags
cmd.Flags().StringVar(&flags.PartitionName, "partition-name", "", "Name of the partition (at least one identifier required)")
cmd.Flags().StringVar(&flags.PartitionUUID, "partition-uuid", "", "UUID of the partition (at least one identifier required)")
cmd.Flags().StringVar(&flags.PartitionLabel, "partition-label", "", "Filesystem label of the partition (at least one identifier required)")
cmd.Flags().IntVar(&flags.Attempts, "attempts", 30, "Number of attempts to get the passphrase")
cmd.Flags().StringVar(&flags.ChallengerServer, "challenger-server", "", "URL of the challenger server (overrides config)")
cmd.Flags().BoolVar(&flags.EnableMDNS, "mdns", false, "Enable mDNS discovery (overrides config)")
cmd.Flags().StringVar(&flags.ServerCertificate, "certificate", "", "Server certificate for verification (overrides config)")
return cmd
}
// pluginCmd represents the plugin event commands
var pluginCmd = &cobra.Command{
Use: string(bus.EventDiscoveryPassword),
Short: fmt.Sprintf("Run %s plugin event", bus.EventDiscoveryPassword),
Long: fmt.Sprintf(`Run the %s plugin event.
This command runs in plugin mode, reading JSON partition data from stdin
and outputting the passphrase to stdout. This is used for integration
with kcrypt and other tools.`, bus.EventDiscoveryPassword),
Example: fmt.Sprintf(` # Plugin mode (for integration with kcrypt)
echo '{"data": "{\"name\": \"/dev/sda2\", \"uuid\": \"12345-abcde\", \"label\": \"encrypted-data\"}"}' | kcrypt-discovery-challenger %s`, bus.EventDiscoveryPassword),
RunE: func(cmd *cobra.Command, args []string) error {
return runPluginMode()
},
}
func init() {
// Global/persistent flags (available to all commands)
rootCmd.PersistentFlags().BoolVar(&debug, "debug", false, "Enable debug logging")
// Add subcommands
rootCmd.AddCommand(newGetCmd())
rootCmd.AddCommand(newCleanupCmd())
rootCmd.AddCommand(pluginCmd)
}
func main() {
if err := rootCmd.Execute(); err != nil {
os.Exit(1)
}
}
// ExecuteWithArgs executes the root command with the given arguments.
// This function is used by tests to simulate CLI execution.
func ExecuteWithArgs(args []string) error {
// Set command arguments (this overrides os.Args)
rootCmd.SetArgs(args)
return rootCmd.Execute()
}
// runTPMHash handles the root command - TPM hash generation
func runTPMHash() error {
// Create logger based on debug flag
var logger types.KairosLogger
if debug {
logger = types.NewKairosLogger("kcrypt-discovery-challenger", "debug", false)
logger.Debugf("Debug mode enabled for TPM hash generation")
} else {
logger = types.NewKairosLogger("kcrypt-discovery-challenger", "error", false)
}
// Initialize AK Manager with the standard handle file
logger.Debugf("Initializing AK Manager with handle file: %s", constants.AKBlobFile)
akManager, err := tpm.NewAKManager(tpm.WithAKHandleFile(constants.AKBlobFile))
if err != nil {
return fmt.Errorf("creating AK manager: %w", err)
}
logger.Debugf("AK Manager initialized successfully")
// Ensure AK exists (create if necessary)
logger.Debugf("Getting or creating AK")
_, err = akManager.GetOrCreateAK()
if err != nil {
return fmt.Errorf("getting/creating AK: %w", err)
}
logger.Debugf("AK obtained/created successfully")
// Get attestation data (includes EK)
logger.Debugf("Getting attestation data")
ek, _, err := akManager.GetAttestationData()
if err != nil {
return fmt.Errorf("getting attestation data: %w", err)
}
logger.Debugf("Attestation data retrieved successfully")
// Compute TPM hash from EK
logger.Debugf("Computing TPM hash from EK")
tpmHash, err := tpm.DecodePubHash(ek)
if err != nil {
return fmt.Errorf("computing TPM hash: %w", err)
}
logger.Debugf("TPM hash computed successfully: %s", tpmHash)
// Output the TPM hash to stdout
fmt.Print(tpmHash)
return nil
}
// runGetPassphrase handles the get subcommand - passphrase retrieval
func runGetPassphrase(flags *GetFlags) error {
// Create logger based on debug flag
var logger types.KairosLogger
if debug {
logger = types.NewKairosLogger("kcrypt-discovery-challenger", "debug", false)
} else {
logger = types.NewKairosLogger("kcrypt-discovery-challenger", "error", false)
}
// Create client with potential CLI overrides
c, err := createClientWithOverrides(flags.ChallengerServer, flags.EnableMDNS, flags.ServerCertificate, logger)
if err != nil {
return fmt.Errorf("creating client: %w", err)
}
// Create partition object
partition := &block.Partition{
Name: flags.PartitionName,
UUID: flags.PartitionUUID,
FilesystemLabel: flags.PartitionLabel,
}
// Log partition information
logger.Debugf("Partition details:")
logger.Debugf(" Name: %s", partition.Name)
logger.Debugf(" UUID: %s", partition.UUID)
logger.Debugf(" Label: %s", partition.FilesystemLabel)
logger.Debugf(" Attempts: %d", flags.Attempts)
// Get the passphrase using the same backend logic as the plugin
fmt.Fprintf(os.Stderr, "Requesting passphrase for partition %s (UUID: %s, Label: %s)...\n",
flags.PartitionName, flags.PartitionUUID, flags.PartitionLabel)
passphrase, err := c.GetPassphrase(partition, flags.Attempts)
if err != nil {
return fmt.Errorf("getting passphrase: %w", err)
}
// Output the passphrase to stdout (this is what tools expect)
fmt.Print(passphrase)
fmt.Fprintf(os.Stderr, "\nPassphrase retrieved successfully\n")
return nil
}
// runPluginMode handles plugin event commands
func runPluginMode() error {
// In plugin mode, use quiet=true to log to file instead of console
// Log level depends on debug flag, write logs to /var/log/kairos/kcrypt-discovery-challenger.log
var logLevel string
if debug {
logLevel = "debug"
} else {
logLevel = "error"
}
logger := types.NewKairosLogger("kcrypt-discovery-challenger", logLevel, true)
c, err := client.NewClientWithLogger(logger)
if err != nil {
return fmt.Errorf("creating client: %w", err)
}
err = c.Start()
if err != nil {
return fmt.Errorf("starting plugin: %w", err)
}
return nil
}
// createClientWithOverrides creates a client and applies CLI flag overrides to the config
func createClientWithOverrides(serverURL string, enableMDNS bool, certificate string, logger types.KairosLogger) (*client.Client, error) {
// Start with the default config from files and pass the logger
c, err := client.NewClientWithLogger(logger)
if err != nil {
return nil, err
}
// Log the original configuration values
logger.Debugf("Original configuration:")
logger.Debugf(" Server: %s", c.Config.Kcrypt.Challenger.Server)
logger.Debugf(" MDNS: %t", c.Config.Kcrypt.Challenger.MDNS)
logger.Debugf(" Certificate: %s", maskSensitiveString(c.Config.Kcrypt.Challenger.Certificate))
// Apply CLI overrides if provided
if serverURL != "" {
logger.Debugf("Overriding server URL: %s -> %s", c.Config.Kcrypt.Challenger.Server, serverURL)
c.Config.Kcrypt.Challenger.Server = serverURL
}
// For boolean flags, we can directly use the value since Cobra handles it properly
if enableMDNS {
logger.Debugf("Overriding MDNS setting: %t -> %t", c.Config.Kcrypt.Challenger.MDNS, enableMDNS)
c.Config.Kcrypt.Challenger.MDNS = enableMDNS
}
if certificate != "" {
logger.Debugf("Overriding certificate: %s -> %s",
maskSensitiveString(c.Config.Kcrypt.Challenger.Certificate),
maskSensitiveString(certificate))
c.Config.Kcrypt.Challenger.Certificate = certificate
}
// Log the final configuration values
logger.Debugf("Final configuration:")
logger.Debugf(" Server: %s", c.Config.Kcrypt.Challenger.Server)
logger.Debugf(" MDNS: %t", c.Config.Kcrypt.Challenger.MDNS)
logger.Debugf(" Certificate: %s", maskSensitiveString(c.Config.Kcrypt.Challenger.Certificate))
return c, nil
}
// runCleanup handles the cleanup subcommand - TPM NV memory cleanup
func runCleanup(nvIndex, tpmDevice string, skipConfirmation bool) error {
// Create logger based on debug flag
var logger types.KairosLogger
if debug {
logger = types.NewKairosLogger("kcrypt-discovery-challenger", "debug", false)
logger.Debugf("Debug mode enabled for TPM NV cleanup")
} else {
logger = types.NewKairosLogger("kcrypt-discovery-challenger", "error", false)
}
// Load configuration to get defaults if flags not provided
var config client.Config
c, err := client.NewClientWithLogger(logger)
if err != nil {
logger.Debugf("Warning: Could not load configuration: %v", err)
// Continue with defaults - not a fatal error
} else {
config = c.Config
}
// Determine NV index to clean up (follow same pattern as localPass/genAndStore)
targetIndex := nvIndex
if targetIndex == "" {
// First check config, then fall back to the same default used by the local pass flow
if config.Kcrypt.Challenger.NVIndex != "" {
targetIndex = config.Kcrypt.Challenger.NVIndex
} else {
targetIndex = client.DefaultNVIndex
}
}
// Determine TPM device
targetDevice := tpmDevice
if targetDevice == "" && config.Kcrypt.Challenger.TPMDevice != "" {
targetDevice = config.Kcrypt.Challenger.TPMDevice
}
logger.Debugf("Cleaning up TPM NV index: %s", targetIndex)
if targetDevice != "" {
logger.Debugf("Using TPM device: %s", targetDevice)
}
// Check if the NV index exists first
opts := []tpm.TPMOption{tpm.WithIndex(targetIndex)}
if targetDevice != "" {
opts = append(opts, tpm.WithDevice(targetDevice))
}
// Try to read from the index to see if it exists
logger.Debugf("Checking if NV index %s exists", targetIndex)
_, err = tpm.ReadBlob(opts...)
if err != nil {
// If we can't read it, it might not exist or be empty
logger.Debugf("NV index %s appears to be empty or non-existent: %v", targetIndex, err)
fmt.Printf("NV index %s appears to be empty or does not exist\n", targetIndex)
return nil
}
// Confirmation prompt with warning
if !skipConfirmation {
fmt.Printf("\n⚠ WARNING: You are about to delete TPM NV index %s\n", targetIndex)
fmt.Printf("⚠️ If this index contains your disk encryption passphrase, your encrypted disk will become UNBOOTABLE!\n")
fmt.Printf("⚠️ This action CANNOT be undone.\n\n")
fmt.Printf("Are you sure you want to continue? (type 'yes' to confirm): ")
scanner := bufio.NewScanner(os.Stdin)
scanner.Scan()
response := strings.TrimSpace(strings.ToLower(scanner.Text()))
if response != "yes" {
fmt.Printf("Cleanup cancelled.\n")
return nil
}
}
// Use native Go TPM library to undefine the NV space
logger.Debugf("Using native TPM library to undefine NV index")
fmt.Printf("Cleaning up TPM NV index %s...\n", targetIndex)
err = tpm.UndefineBlob(opts...)
if err != nil {
return fmt.Errorf("failed to undefine NV index %s: %w", targetIndex, err)
}
fmt.Printf("Successfully cleaned up NV index %s\n", targetIndex)
logger.Debugf("Successfully undefined NV index %s", targetIndex)
return nil
}
// maskSensitiveString masks certificate paths/content for logging
func maskSensitiveString(s string) string {
if s == "" {
return "<empty>"
}
if len(s) <= 10 {
return strings.Repeat("*", len(s))
}
// Show first 3 and last 3 characters with * in between
return s[:3] + strings.Repeat("*", len(s)-6) + s[len(s)-3:]
}

View File

@@ -37,6 +37,40 @@ spec:
properties:
TPMHash:
type: string
attestation:
description: AttestationSpec defines TPM attestation data for TOFU
enrollment and verification
properties:
akPublicKey:
description: AKPublicKey stores the Attestation Key public key
in PEM format
type: string
ekPublicKey:
description: EKPublicKey stores the Endorsement Key public key
in PEM format
type: string
enrolledAt:
description: EnrolledAt timestamp when this TPM was first enrolled
format: date-time
type: string
lastVerifiedAt:
description: LastVerifiedAt timestamp of the last successful attestation
format: date-time
type: string
pcrValues:
description: PCRValues stores the expected PCR values for boot
state verification
properties:
pcrs:
additionalProperties:
type: string
description: 'PCRs is a flexible map of PCR index (as string)
to PCR value (hex-encoded) Example: {"0": "a1b2c3...", "7":
"d4e5f6...", "11": "g7h8i9..."} This allows for any combination
of PCRs without hardcoding specific indices'
type: object
type: object
type: object
partitions:
items:
description: 'PartitionSpec defines a Partition. A partition can

View File

@@ -25,11 +25,6 @@ bases:
#- ../prometheus
patchesStrategicMerge:
# Protect the /metrics endpoint by putting it behind auth.
# If you want your controller-manager to expose the /metrics
# endpoint w/o any authn/z, please comment the following line.
- manager_auth_proxy_patch.yaml
# Mount the controller config file for loading manager configurations
# through a ComponentConfig type
#- manager_config_patch.yaml

View File

@@ -1,39 +0,0 @@
# This patch inject a sidecar container which is a HTTP proxy for the
# controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews.
apiVersion: apps/v1
kind: Deployment
metadata:
name: controller-manager
namespace: system
spec:
template:
spec:
containers:
- name: kube-rbac-proxy
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- "ALL"
image: gcr.io/kubebuilder/kube-rbac-proxy:v0.13.0
args:
- "--secure-listen-address=0.0.0.0:8443"
- "--upstream=http://127.0.0.1:8080/"
- "--logtostderr=true"
- "--v=0"
ports:
- containerPort: 8443
protocol: TCP
name: https
resources:
limits:
cpu: 500m
memory: 128Mi
requests:
cpu: 5m
memory: 64Mi
- name: manager
args:
- "--health-probe-bind-address=:8081"
- "--metrics-bind-address=127.0.0.1:8080"
- "--leader-elect"

View File

@@ -25,10 +25,6 @@ bases:
#- ../prometheus
patchesStrategicMerge:
# Protect the /metrics endpoint by putting it behind auth.
# If you want your controller-manager to expose the /metrics
# endpoint w/o any authn/z, please comment the following line.
- manager_auth_proxy_patch.yaml
- pull.yaml
# Mount the controller config file for loading manager configurations
# through a ComponentConfig type

View File

@@ -1,39 +0,0 @@
# This patch inject a sidecar container which is a HTTP proxy for the
# controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews.
apiVersion: apps/v1
kind: Deployment
metadata:
name: controller-manager
namespace: system
spec:
template:
spec:
containers:
- name: kube-rbac-proxy
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- "ALL"
image: gcr.io/kubebuilder/kube-rbac-proxy:v0.13.0
args:
- "--secure-listen-address=0.0.0.0:8443"
- "--upstream=http://127.0.0.1:8080/"
- "--logtostderr=true"
- "--v=0"
ports:
- containerPort: 8443
protocol: TCP
name: https
resources:
limits:
cpu: 500m
memory: 128Mi
requests:
cpu: 5m
memory: 64Mi
- name: manager
args:
- "--health-probe-bind-address=:8081"
- "--metrics-bind-address=127.0.0.1:8080"
- "--leader-elect"

View File

@@ -9,4 +9,6 @@ spec:
containers:
- name: manager
imagePullPolicy: IfNotPresent
- name: kube-rbac-proxy
imagePullPolicy: IfNotPresent

View File

@@ -34,10 +34,41 @@ spec:
# seccompProfile:
# type: RuntimeDefault
containers:
- name: kube-rbac-proxy
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- "ALL"
image: gcr.io/kubebuilder/kube-rbac-proxy:v0.13.0
args:
- "--secure-listen-address=0.0.0.0:8443"
- "--upstream=http://127.0.0.1:8080/"
- "--logtostderr=true"
- "--v=0"
ports:
- containerPort: 8443
protocol: TCP
name: https
resources:
limits:
cpu: 500m
memory: 128Mi
requests:
cpu: 5m
memory: 64Mi
- command:
- /manager
args:
- --leader-elect
- "--health-probe-bind-address=:8081"
- "--metrics-bind-address=127.0.0.1:8080"
- "--leader-elect"
- "--namespace=$(POD_NAMESPACE)"
env:
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
image: controller:latest
name: manager
securityContext:

View File

@@ -20,14 +20,13 @@ import (
"path/filepath"
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/envtest"
"sigs.k8s.io/controller-runtime/pkg/envtest/printer"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
@@ -44,10 +43,7 @@ var testEnv *envtest.Environment
func TestAPIs(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecsWithDefaultAndCustomReporters(t,
"Controller Suite",
[]Reporter{printer.NewlineReporter{}})
RunSpecs(t, "Control")
}
var _ = BeforeSuite(func() {
@@ -73,8 +69,7 @@ var _ = BeforeSuite(func() {
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
Expect(err).NotTo(HaveOccurred())
Expect(k8sClient).NotTo(BeNil())
}, 60)
})
var _ = AfterSuite(func() {
By("tearing down the test environment")

View File

@@ -1,3 +1,3 @@
#!/bin/bash
docker run --privileged -v /var/run/docker.sock:/var/run/docker.sock --rm -t -v $(pwd):/workspace -v earthly-tmp:/tmp/earthly:rw earthly/earthly:v0.6.21 --allow-privileged $@
docker run --privileged -v /var/run/docker.sock:/var/run/docker.sock --rm -t -v $(pwd):/workspace -v earthly-tmp:/tmp/earthly:rw earthly/earthly:v0.8.16 --allow-privileged $@

64
examples/cli-usage.sh Executable file
View File

@@ -0,0 +1,64 @@
#!/bin/bash
# Example script demonstrating the new CLI interface for kcrypt-challenger
# This makes testing and debugging much easier than using the plugin interface
echo "=== kcrypt-challenger CLI Examples ==="
echo
# Build the binary if it doesn't exist
if [ ! -f "./kcrypt-discovery-challenger" ]; then
echo "Building kcrypt-discovery-challenger..."
go build -o kcrypt-discovery-challenger ./cmd/discovery/
echo
fi
echo "1. Show help:"
./kcrypt-discovery-challenger --help
echo
echo "2. Show version:"
./kcrypt-discovery-challenger --version
echo
echo "3. Test CLI mode with example parameters (will fail without server, but shows the flow):"
echo " Command: ./kcrypt-discovery-challenger --partition-name=/dev/sda2 --partition-uuid=12345-abcde --partition-label=encrypted-data --attempts=1"
echo " Expected: Error connecting to server, but flow detection should work"
echo
./kcrypt-discovery-challenger --partition-name=/dev/sda2 --partition-uuid=12345-abcde --partition-label=encrypted-data --attempts=1 2>&1 || true
echo
echo "4. Test CLI mode with configuration overrides:"
echo " Command: ./kcrypt-discovery-challenger --partition-name=/dev/sda2 --partition-uuid=12345-abcde --partition-label=encrypted-data --challenger-server=https://custom-server.com:8082 --mdns=true --attempts=1"
echo " Expected: Same error but with custom server configuration"
echo
./kcrypt-discovery-challenger --partition-name=/dev/sda2 --partition-uuid=12345-abcde --partition-label=encrypted-data --challenger-server=https://custom-server.com:8082 --mdns=true --attempts=1 2>&1 || true
echo
echo "4. Check the log file for flow detection:"
if [ -f "/tmp/kcrypt-challenger-client.log" ]; then
echo " Log contents:"
cat /tmp/kcrypt-challenger-client.log
echo
else
echo " No log file found"
fi
echo "5. Test plugin mode (for comparison):"
echo " Command: echo '{\"data\": \"{\\\"name\\\": \\\"/dev/sda2\\\", \\\"uuid\\\": \\\"12345-abcde\\\", \\\"filesystemLabel\\\": \\\"encrypted-data\\\"}\"}' | ./kcrypt-discovery-challenger discovery.password"
echo " Expected: Same behavior as CLI mode"
echo
echo '{"data": "{\"name\": \"/dev/sda2\", \"uuid\": \"12345-abcde\", \"filesystemLabel\": \"encrypted-data\"}"}' | ./kcrypt-discovery-challenger discovery.password 2>&1 || true
echo
echo "=== Summary ==="
echo "✅ CLI interface successfully created"
echo "✅ Full compatibility with plugin mode maintained"
echo "✅ Same backend logic used for both interfaces"
echo "✅ Flow detection works in both modes"
echo ""
echo "Benefits:"
echo "- Much easier testing during development"
echo "- Can be used for debugging in production"
echo "- Clear command-line interface with help and examples"
echo "- Maintains full compatibility with kcrypt integration"

236
go.mod
View File

@@ -1,144 +1,188 @@
module github.com/kairos-io/kairos-challenger
go 1.20
go 1.25
replace github.com/kairos-io/tpm-helpers => github.com/kairos-io/tpm-helpers v0.0.0-20250924104130-49f51e390ef3
//replace github.com/kairos-io/tpm-helpers => /home/dimitris/workspace/kairos/tpm-helpers
require (
github.com/google/uuid v1.3.0
github.com/gorilla/websocket v1.5.0
github.com/jaypipes/ghw v0.9.0
github.com/kairos-io/kairos v1.24.3-56.0.20230208235509-4d28f3b87f60
github.com/kairos-io/kcrypt v0.5.0
github.com/kairos-io/tpm-helpers v0.0.0-20230119140150-3fa97128ef6b
github.com/go-logr/logr v1.4.3
github.com/google/go-attestation v0.5.1
github.com/google/uuid v1.6.0
github.com/gorilla/websocket v1.5.3
github.com/hashicorp/mdns v1.0.6
github.com/jaypipes/ghw v0.19.1
github.com/kairos-io/kairos-sdk v0.10.1
github.com/kairos-io/tpm-helpers v0.0.0-20240123063624-f7a3fcc66199
github.com/mudler/go-pluggable v0.0.0-20230126220627-7710299a0ae5
github.com/mudler/go-processmanager v0.0.0-20220724164624-c45b5c61312d
github.com/mudler/yip v1.0.0
github.com/onsi/ginkgo v1.16.5
github.com/onsi/ginkgo/v2 v2.8.1
github.com/onsi/gomega v1.26.0
github.com/pkg/errors v0.9.1
github.com/spectrocloud/peg v0.0.0-20230214140930-4d6672f825b2
github.com/mudler/go-processmanager v0.0.0-20240820160718-8b802d3ecf82
github.com/mudler/yip v1.18.0
github.com/onsi/ginkgo/v2 v2.25.3
github.com/onsi/gomega v1.38.2
github.com/spectrocloud/peg v0.0.0-20240405075800-c5da7125e30f
github.com/spf13/cobra v1.10.1
gopkg.in/yaml.v3 v3.0.1
k8s.io/api v0.24.2
k8s.io/apimachinery v0.24.2
k8s.io/client-go v0.24.2
sigs.k8s.io/controller-runtime v0.12.2
k8s.io/api v0.27.2
k8s.io/apimachinery v0.27.4
k8s.io/client-go v0.27.2
sigs.k8s.io/controller-runtime v0.15.0
)
require (
atomicgo.dev/cursor v0.1.1 // indirect
atomicgo.dev/cursor v0.2.0 // indirect
atomicgo.dev/keyboard v0.2.9 // indirect
cloud.google.com/go v0.93.3 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest v0.11.18 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.13 // indirect
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
atomicgo.dev/schedule v0.1.0 // indirect
dario.cat/mergo v1.0.1 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.1.1 // indirect
github.com/Masterminds/sprig/v3 v3.2.2 // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/StackExchange/wmi v1.2.1 // indirect
github.com/Masterminds/semver/v3 v3.4.0 // indirect
github.com/Masterminds/sprig/v3 v3.3.0 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/Microsoft/hcsshim v0.12.9 // indirect
github.com/avast/retry-go v3.0.0+incompatible // indirect
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bramvdbogaerde/go-scp v1.2.1 // indirect
github.com/cavaliergopher/grab/v3 v3.0.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/chuckpreslar/emission v0.0.0-20170206194824-a7ddd980baf9 // indirect
github.com/codingsince1985/checksum v1.2.6 // indirect
github.com/containerd/console v1.0.3 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/containerd/cgroups/v3 v3.0.5 // indirect
github.com/containerd/console v1.0.4 // indirect
github.com/containerd/containerd v1.7.27 // indirect
github.com/containerd/continuity v0.4.5 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.16.3 // indirect
github.com/containerd/typeurl/v2 v2.2.3 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/denisbrodbeck/machineid v1.0.1 // indirect
github.com/emicklei/go-restful v2.9.5+incompatible // indirect
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
github.com/folbricht/tpmk v0.1.2-0.20230104073416-f20b20c289d7 // indirect
github.com/form3tech-oss/jwt-go v3.2.5+incompatible // indirect
github.com/fsnotify/fsnotify v1.5.4 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/zapr v1.2.0 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/cli v28.2.2+incompatible // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker v28.3.3+incompatible // indirect
github.com/docker/docker-credential-helpers v0.9.3 // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/emicklei/go-restful/v3 v3.10.1 // indirect
github.com/evanphx/json-patch/v5 v5.6.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-logr/zapr v1.2.4 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.19.5 // indirect
github.com/go-openapi/swag v0.19.14 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonreference v0.20.1 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/certificate-transparency-go v1.1.4 // indirect
github.com/google/gnostic v0.5.7-v3refs // indirect
github.com/google/go-attestation v0.4.4-0.20220404204839-8820d49b18d9 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/go-tpm v0.3.3 // indirect
github.com/google/go-tpm-tools v0.3.10 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/go-configfs-tsm v0.3.3 // indirect
github.com/google/go-containerregistry v0.20.6 // indirect
github.com/google/go-tpm v0.9.1 // indirect
github.com/google/go-tpm-tools v0.4.4 // indirect
github.com/google/go-tspi v0.3.0 // indirect
github.com/google/gofuzz v1.1.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/gookit/color v1.5.2 // indirect
github.com/gookit/color v1.5.4 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/huandu/xstrings v1.3.2 // indirect
github.com/imdario/mergo v0.3.13 // indirect
github.com/huandu/xstrings v1.5.0 // indirect
github.com/imdario/mergo v0.3.15 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/ipfs/go-log v1.0.5 // indirect
github.com/ipfs/go-log/v2 v2.5.1 // indirect
github.com/itchyny/gojq v0.12.11 // indirect
github.com/itchyny/timefmt-go v0.1.5 // indirect
github.com/itchyny/gojq v0.12.17 // indirect
github.com/itchyny/timefmt-go v0.1.6 // indirect
github.com/jaypipes/pcidb v1.1.1 // indirect
github.com/joho/godotenv v1.5.1 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/lithammer/fuzzysearch v1.1.5 // indirect
github.com/mailru/easyjson v0.7.6 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/lithammer/fuzzysearch v1.1.8 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/miekg/dns v1.1.55 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/sys/sequential v0.6.0 // indirect
github.com/moby/sys/userns v0.1.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
github.com/nxadm/tail v1.4.8 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 // indirect
github.com/prometheus/client_golang v1.13.0 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/pterm/pterm v0.12.54 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/prometheus/client_golang v1.20.2 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/pterm/pterm v0.12.80 // indirect
github.com/qeesung/image2ascii v1.0.1 // indirect
github.com/rivo/uniseg v0.4.3 // indirect
github.com/shopspring/decimal v1.3.1 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/testify v1.8.1 // indirect
github.com/twpayne/go-vfs v1.7.2 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rs/zerolog v1.33.0 // indirect
github.com/shirou/gopsutil/v4 v4.24.7 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af // indirect
github.com/spf13/cast v1.7.1 // indirect
github.com/spf13/pflag v1.0.9 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/twpayne/go-vfs/v4 v4.3.0 // indirect
github.com/vbatts/tar-split v0.12.1 // indirect
github.com/wayneashleyberry/terminal-dimensions v1.1.0 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
go.opentelemetry.io/otel v1.36.0 // indirect
go.opentelemetry.io/otel/metric v1.36.0 // indirect
go.opentelemetry.io/otel/trace v1.36.0 // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
go.uber.org/automaxprocs v1.6.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.24.0 // indirect
golang.org/x/crypto v0.6.0 // indirect
golang.org/x/net v0.6.0 // indirect
golang.org/x/oauth2 v0.4.0 // indirect
golang.org/x/sys v0.5.0 // indirect
golang.org/x/term v0.5.0 // indirect
golang.org/x/text v0.7.0 // indirect
golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect
gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.28.1 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/crypto v0.42.0 // indirect
golang.org/x/mod v0.28.0 // indirect
golang.org/x/net v0.44.0 // indirect
golang.org/x/oauth2 v0.30.0 // indirect
golang.org/x/sync v0.17.0 // indirect
golang.org/x/sys v0.36.0 // indirect
golang.org/x/term v0.35.0 // indirect
golang.org/x/text v0.29.0 // indirect
golang.org/x/time v0.11.0 // indirect
golang.org/x/tools v0.37.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.3.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250212204824-5a70512c5d8b // indirect
google.golang.org/grpc v1.70.0 // indirect
google.golang.org/protobuf v1.36.7 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
howett.net/plist v1.0.0 // indirect
k8s.io/apiextensions-apiserver v0.24.2 // indirect
k8s.io/component-base v0.24.2 // indirect
k8s.io/klog/v2 v2.80.1 // indirect
k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42 // indirect
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect
sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
howett.net/plist v1.0.2-0.20250314012144-ee69052608d9 // indirect
k8s.io/apiextensions-apiserver v0.27.2 // indirect
k8s.io/component-base v0.27.2 // indirect
k8s.io/klog/v2 v2.90.1 // indirect
k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect
k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
)

1449
go.sum

File diff suppressed because it is too large Load Diff

View File

@@ -23,6 +23,7 @@ import (
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
// to ensure that exec-entrypoint and run can make use of them.
"k8s.io/client-go/kubernetes"
_ "k8s.io/client-go/plugin/pkg/client/auth"
@@ -120,7 +121,9 @@ func main() {
os.Exit(1)
}
go challenger.Start(context.Background(), clientset, reconciler, namespace, challengerAddr)
serverLog := ctrl.Log.WithName("server")
go challenger.Start(context.Background(), serverLog, clientset, reconciler, namespace, challengerAddr)
setupLog.Info("starting manager")
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {

103
mdns-notes.md Normal file
View File

@@ -0,0 +1,103 @@
# Prerequisites
Nodes and KMS should be on the same local network (mdns requirement)
# Steps
- Create a cluster with a port bound to the host:
```
k3d cluster create kcrypt -p '30000:30000@server:0'
```
(we are going to assign this port to the kcrypt challenger server and advertise it over mdns)
- Follow [the instructions to setup the kcrypt challenger server](https://github.com/kairos-io/kcrypt-challenger#installation):
```
helm repo add kairos https://kairos-io.github.io/helm-charts
helm install kairos-crd kairos/kairos-crds
```
Create the following 'kcrypt-challenger-values.yaml` file:
```yaml
service:
challenger:
type: "NodePort"
port: 8082
nodePort: 30000
```
and deploy the challenger server with it:
```bash
helm install -f kcrypt-challenger-values.yaml kairos-challenger kairos/kairos-challenger
```
- Add the sealedvolume and secret for the tpm chip:
```
apiVersion: v1
kind: Secret
metadata:
name: example-host-tpm-secret
namespace: default
type: Opaque
stringData:
pass: "awesome-passphrase"
---
apiVersion: keyserver.kairos.io/v1alpha1
kind: SealedVolume
metadata:
name: example-host
namespace: default
spec:
TPMHash: "5640e37f4016da16b841a93880dcc44886904392fa3c86681087b77db5afedbe"
partitions:
- label: COS_PERSISTENT
secret:
name: example-host-tpm-secret
path: pass
quarantined: false
```
- Start the [simple-mdns-server](https://github.com/kairos-io/simple-mdns-server)
```
go run . --port 30000 --interfaceName enp121s0 --serviceType _kcrypt._tcp --hostName mychallenger.local
```
- Start a node in manual install mode
- Replace `/system/discovery/kcrypt-discovery-challenger` with a custom build (until we merge)
- Create the following config:
```
#cloud-config
users:
- name: kairos
passwd: kairos
install:
grub_options:
extra_cmdline: "rd.neednet=1"
encrypted_partitions:
- COS_PERSISTENT
# Kcrypt configuration block
kcrypt:
challenger:
mdns: true
challenger_server: "http://mychallenger.local"
```
- Install:
```
kairos-agent manual-install --device auto config.yaml
```

File diff suppressed because it is too large Load Diff

View File

@@ -38,7 +38,7 @@ var _ = Describe("challenger", func() {
})
It("returns the sealed volume data", func() {
volumeData := findVolumeFor(requestData, volumeList)
volumeData, _ := findVolumeFor(requestData, volumeList)
Expect(volumeData).ToNot(BeNil())
Expect(volumeData.Quarantined).To(BeFalse())
Expect(volumeData.SecretName).To(Equal("the_secret"))
@@ -67,7 +67,7 @@ var _ = Describe("challenger", func() {
})
It("doesn't match a request with an empty field", func() {
volumeData := findVolumeFor(requestData, volumeList)
volumeData, _ := findVolumeFor(requestData, volumeList)
Expect(volumeData).To(BeNil())
})
})
@@ -86,7 +86,7 @@ var _ = Describe("challenger", func() {
})
It("returns the sealed volume data", func() {
volumeData := findVolumeFor(requestData, volumeList)
volumeData, _ := findVolumeFor(requestData, volumeList)
Expect(volumeData).ToNot(BeNil())
Expect(volumeData.Quarantined).To(BeFalse())
Expect(volumeData.SecretName).To(Equal("the_secret"))
@@ -108,7 +108,7 @@ var _ = Describe("challenger", func() {
})
It("returns the sealed volume data", func() {
volumeData := findVolumeFor(requestData, volumeList)
volumeData, _ := findVolumeFor(requestData, volumeList)
Expect(volumeData).ToNot(BeNil())
Expect(volumeData.Quarantined).To(BeFalse())
Expect(volumeData.SecretName).To(Equal("the_secret"))
@@ -130,7 +130,7 @@ var _ = Describe("challenger", func() {
})
It("returns nil sealedVolumeData", func() {
volumeData := findVolumeFor(requestData, volumeList)
volumeData, _ := findVolumeFor(requestData, volumeList)
Expect(volumeData).To(BeNil())
})
})

View File

@@ -2,3 +2,4 @@ package constants
const TPMSecret = "tpm"
const GeneratedByKey = "generated_by"
const AKBlobFile = "/etc/kairos/ak.blob"

44
renovate.json Normal file
View File

@@ -0,0 +1,44 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:base"
],
"schedule": [
"after 11pm every weekday",
"before 7am every weekday",
"every weekend"
],
"timezone": "Europe/Brussels",
"rebaseWhen": "behind-base-branch",
"reviewers": [ "team:maintainers" ],
"packageRules": [
{
"matchUpdateTypes": [
"patch"
],
"automerge": true
}
],
"regexManagers": [
{
"fileMatch": [
"^Earthfile$"
],
"matchStrings": [
"#\\s*renovate:\\s*datasource=(?<datasource>.*?) depName=(?<depName>.*?)( versioning=(?<versioning>.*?))?\\sARG\\s+.+_VERSION=(?<currentValue>.*?)\\s"
],
"versioningTemplate": "{{#if versioning}}{{versioning}}{{else}}semver{{/if}}"
},
{
"fileMatch": [
"^earthly\\.(sh|ps1)$"
],
"datasourceTemplate": "docker",
"depNameTemplate": "earthly/earthly",
"matchStrings": [
"earthly\\/earthly:(?<currentValue>.*?)\\s"
],
"versioningTemplate": "semver-coerced"
}
]
}

View File

@@ -34,10 +34,8 @@ trap cleanup EXIT
k3d cluster create "$CLUSTER_NAME" --k3s-arg "--cluster-cidr=10.49.0.1/16@server:0" --k3s-arg "--service-cidr=10.48.0.1/16@server:0" -p '80:80@server:0' -p '443:443@server:0' --image "$K3S_IMAGE"
k3d kubeconfig get "$CLUSTER_NAME" > "$KUBECONFIG"
# Build the docker image
IMG=controller:latest make docker-build
# Import the image to the cluster
# Import the controller image that we built at the start into to the cluster
# this image has to exists and be available in the local docker
k3d image import -c "$CLUSTER_NAME" controller:latest
# Install cert manager
@@ -59,4 +57,4 @@ kubectl apply -k "$SCRIPT_DIR/../tests/assets/"
# https://stackoverflow.com/a/6752280
export KMS_ADDRESS="10.0.2.2.challenger.sslip.io"
PATH=$PATH:$GOPATH/bin ginkgo -v --nodes $GINKGO_NODES --label-filter $LABEL --fail-fast -r ./tests/
go run github.com/onsi/ginkgo/v2/ginkgo -v --nodes $GINKGO_NODES --label-filter $LABEL --fail-fast -r ./tests/

View File

@@ -11,6 +11,7 @@ spec:
- hosts:
- 10.0.2.2.challenger.sslip.io
- ${CLUSTER_IP}.challenger.sslip.io
- discoverable-kms.local
secretName: kms-tls
rules:
- host: 10.0.2.2.challenger.sslip.io
@@ -33,3 +34,13 @@ spec:
name: kcrypt-controller-kcrypt-escrow-server
port:
number: 8082
- host: discoverable-kms.local
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: kcrypt-controller-kcrypt-escrow-server
port:
number: 8082

View File

@@ -19,13 +19,19 @@ import (
var installationOutput string
var vm VM
var mdnsVM VM
var _ = Describe("kcrypt encryption", func() {
var config string
var vmOpts VMOptions
var expectedInstallationSuccess bool
BeforeEach(func() {
expectedInstallationSuccess = true
vmOpts = DefaultVMOptions()
RegisterFailHandler(printInstallationOutput)
_, vm = startVM()
_, vm = startVM(vmOpts)
fmt.Printf("\nvm.StateDir = %+v\n", vm.StateDir)
vm.EventuallyConnects(1200)
@@ -43,10 +49,13 @@ var _ = Describe("kcrypt encryption", func() {
Expect(err).ToNot(HaveOccurred())
installationOutput, err = vm.Sudo("/bin/bash -c 'set -o pipefail && kairos-agent manual-install --device auto config.yaml 2>&1 | tee manual-install.txt'")
if expectedInstallationSuccess {
Expect(err).ToNot(HaveOccurred(), installationOutput)
}
})
AfterEach(func() {
vm.GatherLog("/run/immucore/immucore.log")
err := vm.Destroy(func(vm VM) {
// Stop TPM emulator
tpmPID, err := os.ReadFile(path.Join(vm.StateDir, "tpm", "pid"))
@@ -62,6 +71,63 @@ var _ = Describe("kcrypt encryption", func() {
Expect(err).ToNot(HaveOccurred())
})
When("discovering KMS with mdns", Label("discoverable-kms"), func() {
var tpmHash string
var mdnsHostname string
BeforeEach(func() {
By("creating the secret in kubernetes")
tpmHash = createTPMPassphraseSecret(vm)
mdnsHostname = "discoverable-kms.local"
By("deploying simple-mdns-server vm")
mdnsVM = deploySimpleMDNSServer(mdnsHostname)
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:
mdns: true
challenger_server: "http://%[1]s"
`, mdnsHostname)
})
AfterEach(func() {
cmd := exec.Command("kubectl", "delete", "sealedvolume", tpmHash)
out, err := cmd.CombinedOutput()
Expect(err).ToNot(HaveOccurred(), out)
err = mdnsVM.Destroy(func(vm VM) {})
Expect(err).ToNot(HaveOccurred())
})
It("discovers the KMS using mdns", func() {
Skip("TODO: make this test work")
By("rebooting")
vm.Reboot()
By("checking that we can connect after installation")
vm.EventuallyConnects(1200)
By("checking if we got an encrypted partition")
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/#offline-mode
When("doing local encryption", Label("local-encryption"), func() {
BeforeEach(func() {
@@ -91,25 +157,9 @@ users:
//https://kairos.io/docs/advanced/partition_encryption/#online-mode
When("using a remote key management server (automated passphrase generation)", Label("remote-auto"), 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
`, strings.TrimSpace(tpmHash)))
tpmHash = createTPMPassphraseSecret(vm)
config = fmt.Sprintf(`#cloud-config
hostname: metal-{{ trunc 4 .MachineID }}
@@ -212,10 +262,6 @@ install:
kcrypt:
challenger:
challenger_server: "http://%s"
nv_index: ""
c_index: ""
tpm_device: ""
`, os.Getenv("KMS_ADDRESS"))
})
@@ -242,24 +288,15 @@ kcrypt:
When("the key management server is listening on https", func() {
var tpmHash string
var err error
BeforeEach(func() {
tpmHash, err = vm.Sudo("/system/discovery/kcrypt-discovery-challenger")
Expect(err).ToNot(HaveOccurred(), tpmHash)
tpmHash = createTPMPassphraseSecret(vm)
})
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
`, strings.TrimSpace(tpmHash)))
AfterEach(func() {
cmd := exec.Command("kubectl", "delete", "sealedvolume", tpmHash)
out, err := cmd.CombinedOutput()
Expect(err).ToNot(HaveOccurred(), out)
})
When("the certificate is pinned on the configuration", Label("remote-https-pinned"), func() {
@@ -299,6 +336,8 @@ install:
When("the no certificate is set in the configuration", Label("remote-https-bad-cert"), func() {
BeforeEach(func() {
expectedInstallationSuccess = false
config = fmt.Sprintf(`#cloud-config
hostname: metal-{{ trunc 4 .MachineID }}
@@ -316,16 +355,13 @@ install:
kcrypt:
challenger:
challenger_server: "https://%s"
nv_index: ""
c_index: ""
tpm_device: ""
`, os.Getenv("KMS_ADDRESS"))
})
It("fails to talk to the server", func() {
out, err := vm.Sudo("cat manual-install.txt")
Expect(err).ToNot(HaveOccurred(), out)
Expect(out).To(MatchRegexp("could not encrypt partition.*x509: certificate signed by unknown authority"))
Expect(out).To(MatchRegexp("failed to verify certificate: x509: certificate signed by unknown authority"))
})
})
})
@@ -362,29 +398,57 @@ func getChallengerServerCert() string {
}
func createConfigWithCert(server, cert string) client.Config {
return client.Config{
Kcrypt: struct {
Challenger struct {
Server string "yaml:\"challenger_server,omitempty\""
NVIndex string "yaml:\"nv_index,omitempty\""
CIndex string "yaml:\"c_index,omitempty\""
TPMDevice string "yaml:\"tpm_device,omitempty\""
Certificate string "yaml:\"certificate,omitempty\""
}
}{
Challenger: struct {
Server string "yaml:\"challenger_server,omitempty\""
NVIndex string "yaml:\"nv_index,omitempty\""
CIndex string "yaml:\"c_index,omitempty\""
TPMDevice string "yaml:\"tpm_device,omitempty\""
Certificate string "yaml:\"certificate,omitempty\""
}{
Server: server,
NVIndex: "",
CIndex: "",
TPMDevice: "",
Certificate: cert,
},
},
}
c := client.Config{}
c.Kcrypt.Challenger.Server = server
c.Kcrypt.Challenger.Certificate = cert
return c
}
func createTPMPassphraseSecret(vm VM) string {
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
`, strings.TrimSpace(tpmHash)))
return tpmHash
}
// We run the simple-mdns-server (https://github.com/kairos-io/simple-mdns-server/)
// inside a VM next to the one we test. The server advertises the KMS as running on 10.0.2.2
// (the host machine). This is a "hack" and is needed because of how the default
// networking in qemu works. We need to be within the same network and that
// network is only available withing another VM.
// https://wiki.qemu.org/Documentation/Networking
func deploySimpleMDNSServer(hostname string) VM {
opts := DefaultVMOptions()
opts.Memory = "2000"
opts.CPUS = "1"
opts.EmulateTPM = false
_, vm := startVM(opts)
vm.EventuallyConnects(1200)
out, err := vm.Sudo(`curl -s https://api.github.com/repos/kairos-io/simple-mdns-server/releases/latest | jq -r .assets[].browser_download_url | grep $(uname -m) | xargs curl -L -o sms.tar.gz`)
Expect(err).ToNot(HaveOccurred(), string(out))
out, err = vm.Sudo("tar xvf sms.tar.gz")
Expect(err).ToNot(HaveOccurred(), string(out))
// Start the simple-mdns-server in the background
out, err = vm.Sudo(fmt.Sprintf(
"/bin/bash -c './simple-mdns-server --port 80 --address 10.0.2.2 --serviceType _kcrypt._tcp --hostName %s &'", hostname))
Expect(err).ToNot(HaveOccurred(), string(out))
return vm
}

View File

@@ -25,6 +25,53 @@ func TestE2e(t *testing.T) {
RunSpecs(t, "kcrypt-challenger e2e test Suite")
}
type VMOptions struct {
ISO string
User string
Password string
Memory string
CPUS string
RunSpicy bool
UseKVM bool
EmulateTPM bool
}
func DefaultVMOptions() VMOptions {
var err error
memory := os.Getenv("MEMORY")
if memory == "" {
memory = "2096"
}
cpus := os.Getenv("CPUS")
if cpus == "" {
cpus = "2"
}
runSpicy := false
if s := os.Getenv("MACHINE_SPICY"); s != "" {
runSpicy, err = strconv.ParseBool(os.Getenv("MACHINE_SPICY"))
Expect(err).ToNot(HaveOccurred())
}
useKVM := false
if envKVM := os.Getenv("KVM"); envKVM != "" {
useKVM, err = strconv.ParseBool(os.Getenv("KVM"))
Expect(err).ToNot(HaveOccurred())
}
return VMOptions{
ISO: os.Getenv("ISO"),
User: user(),
Password: pass(),
Memory: memory,
CPUS: cpus,
RunSpicy: runSpicy,
UseKVM: useKVM,
EmulateTPM: true,
}
}
func user() string {
user := os.Getenv("SSH_USER")
if user == "" {
@@ -42,8 +89,8 @@ func pass() string {
return pass
}
func startVM() (context.Context, VM) {
if os.Getenv("ISO") == "" {
func startVM(vmOpts VMOptions) (context.Context, VM) {
if vmOpts.ISO == "" {
fmt.Println("ISO missing")
os.Exit(1)
}
@@ -53,29 +100,22 @@ func startVM() (context.Context, VM) {
stateDir, err := os.MkdirTemp("", "")
Expect(err).ToNot(HaveOccurred())
if vmOpts.EmulateTPM {
emulateTPM(stateDir)
}
sshPort, err := getFreePort()
Expect(err).ToNot(HaveOccurred())
memory := os.Getenv("MEMORY")
if memory == "" {
memory = "2096"
}
cpus := os.Getenv("CPUS")
if cpus == "" {
cpus = "2"
}
opts := []types.MachineOption{
types.QEMUEngine,
types.WithISO(os.Getenv("ISO")),
types.WithMemory(memory),
types.WithCPU(cpus),
types.WithISO(vmOpts.ISO),
types.WithMemory(vmOpts.Memory),
types.WithCPU(vmOpts.CPUS),
types.WithSSHPort(strconv.Itoa(sshPort)),
types.WithID(vmName),
types.WithSSHUser(user()),
types.WithSSHPass(pass()),
types.WithSSHUser(vmOpts.User),
types.WithSSHPass(vmOpts.Password),
types.OnFailure(func(p *process.Process) {
defer GinkgoRecover()
@@ -109,9 +149,12 @@ func startVM() (context.Context, VM) {
types.WithStateDir(stateDir),
// Serial output to file: https://superuser.com/a/1412150
func(m *types.MachineConfig) error {
if vmOpts.EmulateTPM {
m.Args = append(m.Args,
"-chardev", fmt.Sprintf("socket,id=chrtpm,path=%s/swtpm-sock", path.Join(stateDir, "tpm")),
"-tpmdev", "emulator,id=tpm0,chardev=chrtpm", "-device", "tpm-tis,tpmdev=tpm0",
"-tpmdev", "emulator,id=tpm0,chardev=chrtpm", "-device", "tpm-tis,tpmdev=tpm0")
}
m.Args = append(m.Args,
"-chardev", fmt.Sprintf("stdio,mux=on,id=char0,logfile=%s,signal=off", path.Join(stateDir, "serial.log")),
"-serial", "chardev:char0",
"-mon", "chardev=char0",
@@ -123,14 +166,14 @@ func startVM() (context.Context, VM) {
// Set this to true to debug.
// You can connect to it with "spicy" or other tool.
var spicePort int
if os.Getenv("MACHINE_SPICY") != "" {
if vmOpts.RunSpicy {
spicePort, err = getFreePort()
Expect(err).ToNot(HaveOccurred())
fmt.Printf("Spice port = %d\n", spicePort)
opts = append(opts, types.WithDisplay(fmt.Sprintf("-spice port=%d,addr=127.0.0.1,disable-ticketing", spicePort)))
}
if os.Getenv("KVM") != "" {
if vmOpts.UseKVM {
opts = append(opts, func(m *types.MachineConfig) error {
m.Args = append(m.Args,
"-enable-kvm",
@@ -147,7 +190,7 @@ func startVM() (context.Context, VM) {
ctx, err := vm.Start(context.Background())
Expect(err).ToNot(HaveOccurred())
if os.Getenv("MACHINE_SPICY") != "" {
if vmOpts.RunSpicy {
cmd := exec.Command("spicy",
"-h", "127.0.0.1",
"-p", strconv.Itoa(spicePort))