Compare commits

..

169 Commits

Author SHA1 Message Date
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
Mauro Morales
e0ae7a12a4 Remove golint cuz it has some issues with cgo
Signed-off-by: Mauro Morales <mauro.morales@spectrocloud.com>
2023-03-30 09:21:09 +02:00
Mauro Morales
c2c50877da Remove patch number from defined go version
Signed-off-by: Mauro Morales <mauro.morales@spectrocloud.com>
2023-03-30 09:19:58 +02:00
Mauro Morales
7a2627fcc8 Add go version to workflow
Signed-off-by: Mauro Morales <mauro.morales@spectrocloud.com>
2023-03-29 15:47:00 +02:00
Mauro Morales
a0c4462f99 Update setup-go to v4
Signed-off-by: Mauro Morales <mauro.morales@spectrocloud.com>
2023-03-29 15:44:09 +02:00
Mauro Morales
145dd400b1 Add lint workflow
Signed-off-by: Mauro Morales <mauro.morales@spectrocloud.com>
2023-03-29 15:33:27 +02:00
Mauro Morales
95dbb0d0be Add yamllint
Signed-off-by: Mauro Morales <mauro.morales@spectrocloud.com>
2023-03-29 14:33:29 +02:00
Mauro Morales
0e56e52cbf Bump go to 1.20
Signed-off-by: Mauro Morales <mauro.morales@spectrocloud.com>
2023-03-29 14:28:45 +02:00
Dimitris Karakasilis
3a22134226 Run tests with sudo to be able to access /dev/kvm
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2023-02-20 12:33:44 +02:00
Dimitris Karakasilis
7b561efed8 Try KVM on CI
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2023-02-17 17:59:38 +02:00
Dimitris Karakasilis
6ff6262459 Configure earthly to use the docker mirror in CI
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2023-02-17 17:11:26 +02:00
Dimitris Karakasilis
816013d33d Don't use the earthly script
in order to avoid nested docker and use the deployed docker mirror

Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2023-02-17 16:06:51 +02:00
Dimitris Karakasilis
8d0fb0148d export KUBECONFIG so that it's set when running commands in go tests
Error:

```
  [FAILED] Error from server (NotFound): namespaces "actions-runner-system" not found
```
coming from: /runner/_work/kcrypt-challenger/kcrypt-challenger/tests/encryption_test.go:157

is suspicious. That namespace shouldn't exist in the test k3d cluster,
no idea why it was looked up. I suspect the env for the following
command somehow pointed to the "outer" cluster:

```
cmd := exec.Command("kubectl", "get", "secrets", ...)
```

Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2023-02-17 15:53:02 +02:00
Dimitris Karakasilis
ffd5f18bcf Fix error interface conversion: interface {} is []uint8, not string
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2023-02-17 15:20:18 +02:00
Dimitris Karakasilis
3b89def5b4 Make sure we run command in bash to avoid error in Ubuntu
```
set: Illegal option -o pipefail
```

Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2023-02-17 12:38:22 +02:00
Dimitris Karakasilis
887d67907b Avoid the host cluster CIDR to let DNS work in k3d
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2023-02-17 11:52:14 +02:00
Dimitris Karakasilis
b0a7aa5fdf Revert "Try to fix the MTU problem in a hackish way (tmp)"
This reverts commit 40875bbae1.
2023-02-16 11:14:30 +02:00
Dimitris Karakasilis
40875bbae1 Try to fix the MTU problem in a hackish way (tmp)
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2023-02-16 10:36:45 +02:00
Dimitris Karakasilis
7166b14c7e Revert "Bump earthly"
This reverts commit 9eb5d9b086.
2023-02-16 09:57:25 +02:00
Dimitris Karakasilis
9eb5d9b086 Bump earthly
hoping to get this fix:
https://github.com/earthly/earthly/issues/1934#issuecomment-1160819298

and see if it makes any difference

Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2023-02-16 09:53:30 +02:00
Dimitris Karakasilis
4da6a4f3b0 "Modernize" the +iso target
according to this example:
4e2dd37e70/Earthfile (L114)

Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2023-02-16 09:08:15 +02:00
Dimitris Karakasilis
74fc9c62b4 Switch to ubuntu because opensuse repos time out
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2023-02-15 18:41:15 +02:00
Dimitris Karakasilis
f3f10b4919 Don't prompt
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2023-02-15 18:10:01 +02:00
Dimitris Karakasilis
3d4829859b Run e2e tests on self-hosted runners
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2023-02-15 17:52:14 +02:00
Dimitris Karakasilis
8a17ff714c Merge pull request #12 from kairos-io/346-tls-support
346 tls support
2023-02-15 12:28:20 +02:00
Dimitris Karakasilis
27114b8db8 Run e2e tests without earthly
getting closer to running them with KVM enabled. This will require self
hosted runners with KVM enabled but we will get there eventually.

Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2023-02-15 11:00:24 +02:00
Dimitris Karakasilis
1e3efb57cc Split scenarios in different GA jobs
to parallelise better and allow re-running just the failed tests

Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2023-02-15 09:58:16 +02:00
Dimitris Karakasilis
0c236b6145 Let OnFailure handle abnormal VM termination
now that peg gracefully terminates the VM when `Destroy` is called.

Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2023-02-14 16:15:11 +02:00
Dimitris Karakasilis
d390f77688 Bump peg (after merging PR#9)
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2023-02-14 10:48:08 +02:00
Dimitris Karakasilis
266c4f20e9 Handle unexpected VM exit better and use a core image with working DNS
Also print serial output when something goes wrong

Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2023-02-14 10:09:46 +02:00
Dimitris Karakasilis
4c0b40d3a0 Add gettext-runtime in Earthly image
which provides the `envsubst` command needed in the e2e test script

Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2023-02-09 12:10:38 +02:00
Dimitris Karakasilis
08bb62f94e Remove TODO
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2023-02-09 12:03:24 +02:00
Dimitris Karakasilis
0d3406fa7b Fallback to system CAs
No automated test for this case because it's complicated to get a
properly signed certificate in tests:

- the domain we use is sslip.io (not sure if letsencrypt would sign it)
- we need to use the letsencrypt production and that has quotas not
  suitable for CI

Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2023-02-09 11:48:59 +02:00
Dimitris Karakasilis
1cd4d9a7af Implement test that checks invalid cert case
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2023-02-09 11:48:59 +02:00
Dimitris Karakasilis
d875e54171 Implement pinned certs
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2023-02-09 11:48:59 +02:00
Dimitris Karakasilis
2967fb0a6c [WIP] Implement failing test for pinned cert
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2023-02-09 11:48:59 +02:00
Dimitris Karakasilis
e9433d2ba7 Move challenger server inside the cluster and serve with TLS
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2023-02-09 11:48:59 +02:00
Ettore Di Giacinto
7abdc7b092 📖 Update README 2023-02-07 12:29:13 +01:00
Dimitris Karakasilis
9448ecdd54 Ignore README changes in e2e workflow
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2023-02-02 12:06:19 +02:00
Dimitris Karakasilis
d8cd48b411 Fix link in README to send users directly to "main" runs
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2023-02-02 12:03:55 +02:00
Dimitris Karakasilis
43d629c974 Show "main" status in badge
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2023-02-02 12:02:02 +02:00
Dimitris Karakasilis
b00d3af43b Rename "master" to "main" so that e2e tests run correctly
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2023-02-02 11:58:12 +02:00
Dimitris Karakasilis
7d83e07b05 Fix typo in badge url
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2023-02-02 11:56:38 +02:00
Dimitris Karakasilis
2fe3f3bc00 Add badges to the README
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2023-02-02 11:54:45 +02:00
Dimitris Karakasilis
791d9dbb8b Merge pull request #11 from kairos-io/e2e-tests
E2e tests
2023-02-02 11:49:35 +02:00
Dimitris Karakasilis
7dc1e39ac7 Implement an e2e test suite for kcrypt encryption
Scenarios based on docs: https://kairos.io/docs/advanced/partition_encryption/

Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2023-02-02 11:48:44 +02:00
mudler
076a50b2e9 Drop unnecessary condition
Signed-off-by: mudler <mudler@c3os.io>
2023-01-24 17:53:38 +01:00
mudler
f8e7a0df87 Revert "Change function return style"
This reverts commit 968ff53267.
2023-01-24 17:40:00 +01:00
mudler
968ff53267 Change function return style
Signed-off-by: mudler <mudler@c3os.io>
2023-01-24 16:19:33 +01:00
mudler
a95436bf16 Clean up default secret names 2023-01-24 12:16:09 +01:00
mudler
dfe29aa24f Return a payload
Signed-off-by: mudler <mudler@c3os.io>
2023-01-24 12:03:08 +01:00
mudler
db2b6758de 🌱 Handle case when secret doesn't exist
Signed-off-by: mudler <mudler@c3os.io>
2023-01-23 23:00:16 +01:00
39 changed files with 2625 additions and 971 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.3.0
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
skip-commit-verification: true
- name: Checkout repository
uses: actions/checkout@v4
- 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}}

110
.github/workflows/e2e-tests.yml vendored Normal file
View File

@@ -0,0 +1,110 @@
name: End to end tests
on:
push:
paths-ignore:
- 'README.md'
branches:
- main
pull_request:
paths-ignore:
- 'README.md'
concurrency:
group: ci-e2e-${{ github.head_ref || github.ref }}-${{ github.repository }}
cancel-in-progress: true
jobs:
build-iso:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install Go
uses: actions/setup-go@v5
- name: Install earthly
uses: earthly/actions-setup@v1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: build iso
run: |
# 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: |
[registry."docker.io"]
mirrors = ["registry.docker-mirror.svc.cluster.local:5000"]
[registry."registry.docker-mirror.svc.cluster.local:5000"]
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@v4
with:
fetch-depth: 0
- name: Install Go
uses: actions/setup-go@v5
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@v4
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@v4
- name: Prepare
id: prep
@@ -46,18 +50,18 @@ 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: .
file: ./Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.prep.outputs.tags }}
tags: ${{ steps.prep.outputs.tags }}

33
.github/workflows/lint.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
name: Lint
on:
push:
branches:
- main
pull_request:
paths:
- '**'
concurrency:
group: ci-lint-${{ github.head_ref || github.ref }}-${{ github.repository }}
cancel-in-progress: true
env:
FORCE_COLOR: 1
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install Go
uses: actions/setup-go@v5
- name: Install earthly
uses: earthly/actions-setup@v1
with:
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@v1.9.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@v4
- 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@v5
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@v4
- 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@v4
- 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,18 +1,35 @@
name: Unit tests
on:
push:
branches:
- master
pull_request:
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.24-bookworm"]
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
uses: actions/checkout@v4
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

2
.gitignore vendored
View File

@@ -24,3 +24,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

21
.yamllint Normal file
View File

@@ -0,0 +1,21 @@
extends: default
rules:
# 80 chars should be enough, but don't fail if a line is longer
line-length:
max: 150
level: warning
# accept both key:
# - item
#
# and key:
# - item
indentation:
indent-sequences: whatever
truthy:
check-keys: false
document-start:
present: false

View File

@@ -1,5 +1,5 @@
# Build the manager binary
FROM golang:1.18 as builder
FROM golang:1.24 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

111
Earthfile
View File

@@ -1,14 +1,20 @@
VERSION 0.6
ARG BASE_IMAGE=quay.io/kairos/core-opensuse: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
ARG GO_VERSION=1.18
# renovate: datasource=docker depName=golang
ARG GO_VERSION=1.24-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
@@ -16,37 +22,94 @@ image:
COPY +build-challenger/kcrypt-discovery-challenger /system/discovery/kcrypt-discovery-challenger
SAVE IMAGE $IMAGE
image-rootfs:
FROM +image
SAVE ARTIFACT --keep-own /. rootfs
iso:
ARG OSBUILDER_IMAGE
ARG ISO_NAME=challenger
FROM $OSBUILDER_IMAGE
RUN zypper in -y jq docker
WORKDIR /build
WITH DOCKER --allow-privileged --load $IMAGE=(+image --IMAGE=test)
RUN /entrypoint.sh --name $ISO_NAME --debug build-iso --date=false --local test --output /build/
END
# See: https://github.com/rancher/elemental-cli/issues/228
RUN sha256sum $ISO_NAME.iso > $ISO_NAME.iso.sha256
COPY --keep-own +image-rootfs/rootfs /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
# usage e.g. ./earthly.sh +datasource-iso --CLOUD_CONFIG=tests/assets/qrcode.yaml
datasource-iso:
ARG OSBUILDER_IMAGE
ARG CLOUD_CONFIG
FROM $OSBUILDER_IMAGE
RUN zypper in -y mkisofs
WORKDIR /build
RUN touch meta-data
COPY ${CLOUD_CONFIG} user-data
RUN cat user-data
RUN mkisofs -output ci.iso -volid cidata -joliet -rock user-data meta-data
SAVE ARTIFACT /build/ci.iso iso.iso AS LOCAL build/datasource.iso
luet:
FROM quay.io/luet/base:$LUET_VERSION
SAVE ARTIFACT /usr/bin/luet /luet
e2e-tests-image:
FROM opensuse/tumbleweed
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
IF [ -e /test/build/kairos.iso ]
ENV ISO=/test/build/kairos.iso
ELSE
COPY +iso/kairos.iso kairos.iso
ENV ISO=/test/kairos.iso
END
COPY +luet/luet /usr/bin/luet
RUN mkdir -p /etc/luet/repos.conf.d/
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
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
lint:
BUILD +yamllint
yamllint:
FROM cytopia/yamllint
COPY . .
RUN yamllint .github/workflows/

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 ./... -coverprofile cover.out
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test ./pkg/... -coverprofile cover.out
##@ 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.14.0
KUSTOMIZE_INSTALL_SCRIPT ?= "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh"
.PHONY: kustomize
@@ -257,4 +257,4 @@ undeploy-dev: ## Undeploy controller from the K8s cluster specified in ~/.kube/c
kubesplit: manifests kustomize
rm -rf helm-chart
mkdir helm-chart
$(KUSTOMIZE) build config/default | kubesplit -helm helm-chart
$(KUSTOMIZE) build config/default | kubesplit -helm helm-chart

View File

@@ -1,10 +1,72 @@
# kcrypt-challenger
<h1 align="center">
<br>
<img width="184" alt="kairos-white-column 5bc2fe34" src="https://user-images.githubusercontent.com/2420543/193010398-72d4ba6e-7efe-4c2e-b7ba-d3a826a55b7d.png"><br>
Kcrypt challenger
<br>
</h1>
<h3 align="center">Kcrypt TPM challenger</h3>
<p align="center">
<a href="https://opensource.org/licenses/">
<img src="https://img.shields.io/badge/licence-APL2-brightgreen"
alt="license">
</a>
<a href="https://github.com/kairos-io/kcrypt-challenger/issues"><img src="https://img.shields.io/github/issues/kairos-io/kcrypt-challenger"></a>
<a href="https://kairos.io/docs/" target=_blank> <img src="https://img.shields.io/badge/Documentation-blue"
alt="docs"></a>
<img src="https://img.shields.io/badge/made%20with-Go-blue">
<img src="https://goreportcard.com/badge/github.com/kairos-io/kcrypt-challenger" alt="go report card" />
<a href="https://github.com/kairos-io/kcrypt-challenger/actions/workflows/e2e-tests.yml?query=branch%3Amain"> <img src="https://github.com/kairos-io/kcrypt-challenger/actions/workflows/e2e-tests.yml/badge.svg?branch=main"></a>
</p>
With Kairos you can build immutable, bootable Kubernetes and OS images for your edge devices as easily as writing a Dockerfile. Optional P2P mesh with distributed ledger automates node bootstrapping and coordination. Updating nodes is as easy as CI/CD: push a new image to your container registry and let secure, risk-free A/B atomic upgrades do the rest.
<table>
<tr>
<th align="center">
<img width="640" height="1px">
<p>
<small>
Documentation
</small>
</p>
</th>
<th align="center">
<img width="640" height="1">
<p>
<small>
Contribute
</small>
</p>
</th>
</tr>
<tr>
<td>
📚 [Getting started with Kairos](https://kairos.io/docs/getting-started) <br> :bulb: [Examples](https://kairos.io/docs/examples) <br> :movie_camera: [Video](https://kairos.io/docs/media/) <br> :open_hands:[Engage with the Community](https://kairos.io/community/)
</td>
<td>
🙌[ CONTRIBUTING.md ]( https://github.com/kairos-io/kairos/blob/master/CONTRIBUTING.md ) <br> :raising_hand: [ GOVERNANCE ]( https://github.com/kairos-io/kairos/blob/master/GOVERNANCE.md ) <br>:construction_worker:[Code of conduct](https://github.com/kairos-io/kairos/blob/master/CODE_OF_CONDUCT.md)
</td>
</tr>
</table>
| :exclamation: | This is experimental! |
|-|:-|
This is the Kairos kcrypt-challenger Kubernetes Native Extension.
## Usage
See the documentation in our website: https://kairos.io/docs/advanced/partition_encryption/.
## Installation
To install, use helm:
```

View File

@@ -9,13 +9,18 @@ import (
"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/tpm-helpers"
"github.com/mudler/go-pluggable"
"github.com/mudler/yip/pkg/utils"
)
// Because of how go-pluggable works, we can't just print to stdout
const LOGFILE = "/tmp/kcrypt-challenger-client.log"
var errPartNotFound error = fmt.Errorf("pass for partition not found")
var errBadCertificate error = fmt.Errorf("unknown certificate")
func NewClient() (*Client, error) {
conf, err := unmarshalConfig()
@@ -28,6 +33,10 @@ func NewClient() (*Client, error) {
// 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
@@ -57,50 +66,77 @@ func (c *Client) Start() error {
return factory.Run(pluggable.EventType(os.Args[1]), os.Stdin, os.Stdout)
}
func (c *Client) generatePass(postEndpoint string, headers map[string]string, p *block.Partition) error {
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.FilesystemLabel),
tpm.WithAdditionalHeader("name", p.Name),
tpm.WithAdditionalHeader("uuid", p.UUID),
}
for k, v := range headers {
opts = append(opts, tpm.WithAdditionalHeader(k, v))
}
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 == "" {
additionalHeaders := map[string]string{}
serverURL := 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)
// IF server doesn't have a pass for us, then we generate one and we set it
if _, _, err := getPass(challengeEndpoint, p); err == errPartNotFound {
rand := utils.RandomString(32)
pass, err := tpm.EncryptBlob([]byte(rand))
if err != nil {
return "", err
}
bpass := base64.RawURLEncoding.EncodeToString(pass)
opts := []tpm.Option{
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
}
err = conn.WriteJSON(map[string]string{"passphrase": bpass, constants.GeneratedByKey: constants.TPMSecret})
if err != nil {
return rand, err
}
if c.Config.Kcrypt.Challenger.MDNS {
serverURL, additionalHeaders, err = queryMDNS(serverURL)
}
getEndpoint := fmt.Sprintf("%s/getPass", serverURL)
postEndpoint := fmt.Sprintf("%s/postPass", serverURL)
for tries := 0; tries < attempts; tries++ {
var generated bool
pass, generated, err = getPass(challengeEndpoint, p)
pass, generated, err = getPass(getEndpoint, additionalHeaders, 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, additionalHeaders, p)
if err != nil {
return
}
// Attempt to fetch again - validate that the server has it now
tries = 0
continue
}
if generated { // passphrase is encrypted
return c.decryptPassphrase(pass)
}
if err == nil || err == errPartNotFound { // passphrase not encrypted or not available
if err == errBadCertificate { // No need to retry, won't succeed.
return
}
if err == nil { // passphrase available, no errors
return
}
logToFile("Failed with error: %s . Will retry.\n", err.Error())
time.Sleep(1 * time.Second) // network errors? retry
}
@@ -127,3 +163,14 @@ func (c *Client) decryptPassphrase(pass string) (string, error) {
return string(passBytes), err
}
func logToFile(format string, a ...any) {
s := fmt.Sprintf(format, a...)
file, err := os.OpenFile(LOGFILE, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
panic(err)
}
defer file.Close()
file.WriteString(s)
}

View File

@@ -1,8 +1,9 @@
package client
import (
"github.com/kairos-io/kairos/pkg/config"
"github.com/kairos-io/kairos-sdk/collector"
kconfig "github.com/kairos-io/kcrypt/pkg/config"
"gopkg.in/yaml.v3"
)
type Client struct {
@@ -12,12 +13,12 @@ type Client struct {
type Config struct {
Kcrypt struct {
Challenger struct {
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"`
TPMDevice string `yaml:"tpm_device,omitempty"`
MDNS bool `yaml:"mdns,omitempty"`
Server string `yaml:"challenger_server,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"`
}
}
}
@@ -25,12 +26,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(append(kconfig.ConfigScanDirs, "/tmp/oem")...)); 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

@@ -3,8 +3,10 @@ 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"
@@ -14,24 +16,40 @@ import (
const DefaultNVIndex = "0x1500000"
func getPass(server string, partition *block.Partition) (string, bool, error) {
msg, err := tpm.Get(server,
tpm.WithAdditionalHeader("label", partition.Label),
func getPass(server string, headers map[string]string, certificate string, partition *block.Partition) (string, bool, error) {
opts := []tpm.Option{
tpm.WithCAs([]byte(certificate)),
tpm.AppendCustomCAToSystemCA,
tpm.WithAdditionalHeader("label", partition.FilesystemLabel),
tpm.WithAdditionalHeader("name", partition.Name),
tpm.WithAdditionalHeader("uuid", partition.UUID))
tpm.WithAdditionalHeader("uuid", partition.UUID),
}
for k, v := range headers {
opts = append(opts, tpm.WithAdditionalHeader(k, v))
}
msg, err := tpm.Get(server, opts...)
if err != nil {
return "", false, err
}
result := map[string]interface{}{}
result := payload.Data{}
err = json.Unmarshal(msg, &result)
if err != nil {
return "", false, errors.Wrap(err, string(msg))
}
generatedBy, generated := result[constants.GeneratedByKey]
p, ok := result["passphrase"]
if ok {
return fmt.Sprint(p), generated && generatedBy == constants.TPMSecret, nil
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, errors.New(result.Error)
}
return "", false, errPartNotFound
}

View File

@@ -0,0 +1,85 @@
package client
import (
"fmt"
"net/url"
"strconv"
"strings"
"time"
"github.com/hashicorp/mdns"
)
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) (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)
if mdnsIP == "" { // no reply
logToFile("no reply from mdns\n")
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) (string, string) {
// Make a channel for results and start listening
entriesCh := make(chan *mdns.ServiceEntry, 4)
defer close(entriesCh)
logToFile("Will now wait for some mdns server to respond\n")
// 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:
logToFile("mdns response received\n")
if entry.Host == expectedHost {
logToFile("%s matches %s\n", entry.Host, expectedHost)
return entry.AddrV4.String(), strconv.Itoa(entry.Port) // TODO: v6?
} else {
logToFile("%s didn't match %s\n", entry.Host, expectedHost)
}
case <-time.After(MDNSTimeout):
logToFile("timed out waiting for mdns\n")
return "", ""
}
}
}

View File

@@ -82,4 +82,4 @@ spec:
- name: wss
port: 8082
protocol: TCP
targetPort: wss
targetPort: wss

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() {

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.15 --allow-privileged $@

228
go.mod
View File

@@ -1,134 +1,182 @@
module github.com/kairos-io/kairos-challenger
go 1.18
go 1.24.2
require (
github.com/gorilla/websocket v1.5.0
github.com/jaypipes/ghw v0.9.0
github.com/kairos-io/kairos v1.24.3-56.0.20230118103822-e3dbd41dddd1
github.com/kairos-io/kcrypt v0.4.5-0.20230118125949-27183fbce7ea
github.com/kairos-io/tpm-helpers v0.0.0-20230119140150-3fa97128ef6b
github.com/mudler/go-pluggable v0.0.0-20220716112424-189d463e3ff3
github.com/mudler/yip v0.11.4
github.com/onsi/ginkgo v1.16.5
github.com/onsi/ginkgo/v2 v2.7.0
github.com/onsi/gomega v1.25.0
github.com/go-logr/logr v1.4.2
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.14.0
github.com/kairos-io/kairos-sdk v0.7.3
github.com/kairos-io/kcrypt v0.13.0
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-20240820160718-8b802d3ecf82
github.com/mudler/yip v1.15.0
github.com/onsi/ginkgo/v2 v2.23.0
github.com/onsi/gomega v1.36.2
github.com/pkg/errors v0.9.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
github.com/spectrocloud/peg v0.0.0-20240405075800-c5da7125e30f
gopkg.in/yaml.v3 v3.0.1
k8s.io/api v0.27.2
k8s.io/apimachinery v0.27.2
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/Masterminds/semver/v3 v3.3.1 // 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/StackExchange/wmi v1.2.1 // 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/cespare/xxhash/v2 v2.1.2 // 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.3.0 // indirect
github.com/chuckpreslar/emission v0.0.0-20170206194824-a7ddd980baf9 // indirect
github.com/containerd/console v1.0.3 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/codingsince1985/checksum v1.2.6 // indirect
github.com/containerd/cgroups/v3 v3.0.5 // indirect
github.com/containerd/console v1.0.4 // indirect
github.com/containerd/containerd v1.7.25 // 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/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/distribution/reference v0.6.0 // indirect
github.com/docker/cli v27.5.0+incompatible // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker v27.5.1+incompatible // indirect
github.com/docker/docker-credential-helpers v0.8.2 // 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/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/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/gofrs/uuid v4.4.0+incompatible // 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-cmp v0.6.0 // indirect
github.com/google/go-containerregistry v0.20.3 // indirect
github.com/google/go-tpm v0.3.3 // indirect
github.com/google/go-tpm-tools v0.3.10 // 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-20250208200701-d0013a598941 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/google/uuid v1.3.0 // 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/itchyny/gojq v0.12.11 // indirect
github.com/itchyny/timefmt-go v0.1.5 // indirect
github.com/joho/godotenv v1.4.0 // indirect
github.com/huandu/xstrings v1.5.0 // indirect
github.com/imdario/mergo v0.3.15 // 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.17 // indirect
github.com/itchyny/timefmt-go v0.1.6 // 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.17.11 // 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/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.53 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 // 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/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.6 // 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.11.6 // 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.59.0 // indirect
go.opentelemetry.io/otel v1.34.0 // indirect
go.opentelemetry.io/otel/metric v1.34.0 // indirect
go.opentelemetry.io/otel/trace v1.34.0 // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
go.uber.org/zap v1.23.0 // indirect
golang.org/x/crypto v0.5.0 // indirect
golang.org/x/net v0.5.0 // indirect
golang.org/x/oauth2 v0.4.0 // indirect
golang.org/x/sys v0.4.0 // indirect
golang.org/x/term v0.4.0 // indirect
golang.org/x/text v0.6.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.uber.org/multierr v1.9.0 // indirect
go.uber.org/zap v1.24.0 // indirect
golang.org/x/crypto v0.33.0 // indirect
golang.org/x/mod v0.23.0 // indirect
golang.org/x/net v0.35.0 // indirect
golang.org/x/oauth2 v0.25.0 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/term v0.29.0 // indirect
golang.org/x/text v0.22.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.30.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.5 // 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
gopkg.in/yaml.v3 v3.0.1 // 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
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
)

1015
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
```

View File

@@ -6,10 +6,14 @@ import (
"fmt"
"io/ioutil"
"net/http"
"strings"
"time"
"github.com/go-logr/logr"
keyserverv1alpha1 "github.com/kairos-io/kairos-challenger/api/v1alpha1"
"github.com/kairos-io/kairos-challenger/pkg/constants"
"github.com/kairos-io/kairos-challenger/pkg/payload"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/kairos-io/kairos-challenger/controllers"
@@ -45,6 +49,24 @@ var upgrader = websocket.Upgrader{
WriteBufferSize: 1024,
}
func cleanKubeName(s string) (d string) {
d = strings.ReplaceAll(s, "_", "-")
d = strings.ToLower(d)
return
}
func (s SealedVolumeData) DefaultSecret() (string, string) {
secretName := fmt.Sprintf("%s-%s", s.VolumeName, s.PartitionLabel)
secretPath := "passphrase"
if s.SecretName != "" {
secretName = s.SecretName
}
if s.SecretPath != "" {
secretPath = s.SecretPath
}
return cleanKubeName(secretName), cleanKubeName(secretPath)
}
func writeRead(conn *websocket.Conn, input []byte) ([]byte, error) {
writer, err := conn.NextWriter(websocket.BinaryMessage)
if err != nil {
@@ -76,8 +98,8 @@ func getPubHash(token string) (string, error) {
return tpm.DecodePubHash(ek)
}
func Start(ctx context.Context, kclient *kubernetes.Clientset, reconciler *controllers.SealedVolumeReconciler, namespace, address string) {
fmt.Println("Challenger started at", address)
func Start(ctx context.Context, logger logr.Logger, kclient *kubernetes.Clientset, reconciler *controllers.SealedVolumeReconciler, namespace, address string) {
logger.Info("Challenger started", "address", address)
s := http.Server{
Addr: address,
ReadTimeout: 10 * time.Second,
@@ -87,188 +109,213 @@ func Start(ctx context.Context, kclient *kubernetes.Clientset, reconciler *contr
m := http.NewServeMux()
m.HandleFunc("/postPass", func(w http.ResponseWriter, r *http.Request) {
conn, _ := upgrader.Upgrade(w, r, nil) // error ignored for sake of simplicity
for {
if err := tpm.AuthRequest(r, conn); err != nil {
fmt.Println("error", err.Error())
return
}
defer conn.Close()
token := r.Header.Get("Authorization")
hashEncoded, err := getPubHash(token)
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
logger.Error(err, "upgrading connection")
return
}
defer func() {
err := conn.Close()
if err != nil {
fmt.Println("error decoding pubhash", err.Error())
return
logger.Error(err, "closing the connection")
}
}()
label := r.Header.Get("label")
name := r.Header.Get("name")
uuid := r.Header.Get("uuid")
v := map[string]string{}
logger.Info("Receiving passphrase")
if err := tpm.AuthRequest(r, conn); err != nil {
errorMessage(conn, logger, err, "auth request")
return
}
logger.Info("[Receiving passphrase] auth succeeded")
volumeList := &keyserverv1alpha1.SealedVolumeList{}
token := r.Header.Get("Authorization")
hashEncoded, err := getPubHash(token)
if err != nil {
errorMessage(conn, logger, err, "decoding pubhash")
return
}
logger.Info("[Receiving passphrase] pubhash", "encodedhash", hashEncoded)
label := r.Header.Get("label")
name := r.Header.Get("name")
uuid := r.Header.Get("uuid")
v := &payload.Data{}
logger.Info("Reading request data", "label", label, "name", name, "uuid", uuid)
volumeList := &keyserverv1alpha1.SealedVolumeList{}
for {
if err := reconciler.List(ctx, volumeList, &client.ListOptions{Namespace: namespace}); err != nil {
fmt.Println("Failed listing volumes")
fmt.Println(err)
logger.Error(err, "listing volumes")
continue
}
sealedVolumeData := findVolumeFor(PassphraseRequestData{
TPMHash: hashEncoded,
Label: label,
DeviceName: name,
UUID: uuid,
}, volumeList)
if sealedVolumeData == nil {
fmt.Println("No TPM Hash found for", hashEncoded)
conn.Close()
return
}
if err := conn.ReadJSON(&v); err != nil {
fmt.Println("error", err.Error())
return
}
pass, ok := v["passphrase"]
if ok {
secretName := fmt.Sprintf("%s-%s", sealedVolumeData.VolumeName, sealedVolumeData.PartitionLabel)
secretPath := "passphrase"
if sealedVolumeData.SecretName != "" {
secretName = sealedVolumeData.SecretName
}
if sealedVolumeData.SecretPath != "" {
secretPath = sealedVolumeData.SecretPath
}
_, err := kclient.CoreV1().Secrets(namespace).Get(ctx, secretName, v1.GetOptions{})
if err != nil {
if !apierrors.IsNotFound(err) {
fmt.Printf("Failed getting secret: %s\n", err.Error())
continue
}
secret := corev1.Secret{
TypeMeta: v1.TypeMeta{
Kind: "Secret",
APIVersion: "apps/v1",
},
ObjectMeta: v1.ObjectMeta{
Name: secretName,
Namespace: namespace,
},
Data: map[string][]byte{
secretPath: []byte(pass),
constants.GeneratedByKey: []byte(v[constants.GeneratedByKey]),
},
Type: "Opaque",
}
_, err := kclient.CoreV1().Secrets(namespace).Create(ctx, &secret, v1.CreateOptions{})
if err != nil {
fmt.Println("failed during secret creation")
}
} else {
fmt.Println("Posted for already existing secret - ignoring")
}
} else {
fmt.Println("Invalid answer from client: doesn't contain any passphrase")
}
break
}
logger.Info("Looking up volume with request data")
sealedVolumeData := findVolumeFor(PassphraseRequestData{
TPMHash: hashEncoded,
Label: label,
DeviceName: name,
UUID: uuid,
}, volumeList)
if sealedVolumeData == nil {
errorMessage(conn, logger, fmt.Errorf("no TPM Hash found for %s", hashEncoded), "")
return
}
logger.Info("[Looking up volume with request data] succeeded")
if err := conn.ReadJSON(v); err != nil {
logger.Error(err, "reading json from connection")
return
}
if !v.HasPassphrase() {
errorMessage(conn, logger, fmt.Errorf("invalid answer from client: doesn't contain any passphrase"), "")
}
if v.HasError() {
errorMessage(conn, logger, fmt.Errorf("error: %s", v.Error), v.Error)
}
secretName, secretPath := sealedVolumeData.DefaultSecret()
logger.Info("Looking up secret in with name", "name", secretName, "namespace", namespace)
_, err = kclient.CoreV1().Secrets(namespace).Get(ctx, secretName, v1.GetOptions{})
if err == nil {
logger.Info("Posted for already existing secret - ignoring")
return
}
if !apierrors.IsNotFound(err) {
errorMessage(conn, logger, err, "failed getting secret")
return
}
logger.Info("secret not found, creating one")
secret := corev1.Secret{
TypeMeta: v1.TypeMeta{
Kind: "Secret",
APIVersion: "apps/v1",
},
ObjectMeta: v1.ObjectMeta{
Name: secretName,
Namespace: namespace,
},
StringData: map[string]string{
secretPath: v.Passphrase,
constants.GeneratedByKey: v.GeneratedBy,
},
Type: "Opaque",
}
_, err = kclient.CoreV1().Secrets(namespace).Create(ctx, &secret, v1.CreateOptions{})
if err != nil {
errorMessage(conn, logger, err, "failed during secret creation")
}
logger.Info("created new secret")
})
m.HandleFunc("/getPass", func(w http.ResponseWriter, r *http.Request) {
conn, _ := upgrader.Upgrade(w, r, nil) // error ignored for sake of simplicity
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
logger.Error(err, "upgrading connection")
return
}
defer func() {
err := conn.Close()
if err != nil {
logger.Error(err, "closing the connection")
}
}()
logger.Info("Received connection")
volumeList := &keyserverv1alpha1.SealedVolumeList{}
for {
fmt.Println("Received connection")
volumeList := &keyserverv1alpha1.SealedVolumeList{}
if err := reconciler.List(ctx, volumeList, &client.ListOptions{Namespace: namespace}); err != nil {
fmt.Println("Failed listing volumes")
fmt.Println(err)
logger.Error(err, "listing volumes")
continue
}
token := r.Header.Get("Authorization")
label := r.Header.Get("label")
name := r.Header.Get("name")
uuid := r.Header.Get("uuid")
if err := tpm.AuthRequest(r, conn); err != nil {
fmt.Println("error validating challenge", err.Error())
return
}
hashEncoded, err := getPubHash(token)
if err != nil {
fmt.Println("error decoding pubhash", err.Error())
return
}
sealedVolumeData := findVolumeFor(PassphraseRequestData{
TPMHash: hashEncoded,
Label: label,
DeviceName: name,
UUID: uuid,
}, volumeList)
if sealedVolumeData == nil {
fmt.Println("No TPM Hash found for", hashEncoded)
conn.Close()
return
}
writer, _ := conn.NextWriter(websocket.BinaryMessage)
if !sealedVolumeData.Quarantined {
secretName := fmt.Sprintf("%s-%s", sealedVolumeData.VolumeName, sealedVolumeData.PartitionLabel)
secretPath := "passphrase"
if sealedVolumeData.SecretName != "" {
secretName = sealedVolumeData.SecretName
}
if sealedVolumeData.SecretPath != "" {
secretPath = sealedVolumeData.SecretPath
}
// 1. The admin sets a specific cleartext password from Kube manager
// SealedVolume -> with a secret .
// 2. The admin just adds a SealedVolume associated with a TPM Hash ( you don't provide any passphrase )
// 3. There is no challenger server at all (offline mode)
//
secret, err := kclient.CoreV1().Secrets(namespace).Get(ctx, secretName, v1.GetOptions{})
if err == nil {
passphrase := secret.Data[secretPath]
generatedBy, generated := secret.Data[constants.GeneratedByKey]
result := map[string]string{"passphrase": string(passphrase)}
if generated {
result[constants.GeneratedByKey] = string(generatedBy)
}
err = json.NewEncoder(writer).Encode(result)
if err != nil {
fmt.Println("error encoding the passphrase to json", err.Error(), string(passphrase))
}
if err = writer.Close(); err != nil {
fmt.Println("error closing the writer", err.Error())
return
}
if err = conn.Close(); err != nil {
fmt.Println("error closing the connection", err.Error())
return
}
return
}
} else {
fmt.Println("error getting the secret", err.Error())
if err = conn.Close(); err != nil {
fmt.Println("error closing the connection", err.Error())
return
}
return
}
break
}
},
)
s.Handler = m
logger.Info("reading data from request")
token := r.Header.Get("Authorization")
label := r.Header.Get("label")
name := r.Header.Get("name")
uuid := r.Header.Get("uuid")
tokenStr := "empty"
if token != "" {
tokenStr = "not empty"
}
logger.Info("request data", "token", tokenStr, "label", label, "name", name, "uuid", uuid)
if err := tpm.AuthRequest(r, conn); err != nil {
logger.Error(err, "error validating challenge")
return
}
hashEncoded, err := getPubHash(token)
if err != nil {
logger.Error(err, "error decoding pubhash")
return
}
logger.Info("Looking up volume with request data")
sealedVolumeData := findVolumeFor(PassphraseRequestData{
TPMHash: hashEncoded,
Label: label,
DeviceName: name,
UUID: uuid,
}, volumeList)
if sealedVolumeData == nil {
errorMessage(conn, logger, fmt.Errorf("no volume found with data from request and hash: %s", hashEncoded), "")
return
}
logger.Info("[Looking up volume with request data] succeeded")
if sealedVolumeData.Quarantined {
errorMessage(conn, logger, fmt.Errorf("quarantined: %s", sealedVolumeData.PartitionLabel), "")
return
}
secretName, secretPath := sealedVolumeData.DefaultSecret()
// 1. The admin sets a specific cleartext password from Kube manager
// SealedVolume -> with a secret .
// 2. The admin just adds a SealedVolume associated with a TPM Hash ( you don't provide any passphrase )
// 3. There is no challenger server at all (offline mode)
//
logger.Info(fmt.Sprintf("looking up secret %s in namespace %s", secretName, namespace))
secret, err := kclient.CoreV1().Secrets(namespace).Get(ctx, secretName, v1.GetOptions{})
if err != nil {
if apierrors.IsNotFound(err) {
errorMessage(conn, logger, fmt.Errorf("No secret found for %s and %s", hashEncoded, sealedVolumeData.PartitionLabel), "")
} else {
errorMessage(conn, logger, err, "getting the secret from Kubernetes")
}
return
}
logger.Info(fmt.Sprintf("secret %s found in namespace %s", secretName, namespace))
passphrase := secret.Data[secretPath]
generatedBy := secret.Data[constants.GeneratedByKey]
writer, err := conn.NextWriter(websocket.BinaryMessage)
if err != nil {
logger.Error(err, "getting a writer from the connection")
}
p := payload.Data{Passphrase: string(passphrase), GeneratedBy: string(generatedBy)}
err = json.NewEncoder(writer).Encode(p)
if err != nil {
logger.Error(err, "writing passphrase to the websocket channel")
}
if err = writer.Close(); err != nil {
logger.Error(err, "closing the writer")
return
}
})
s.Handler = logRequestHandler(logger, m)
go func() {
err := s.ListenAndServe()
@@ -290,12 +337,19 @@ func findVolumeFor(requestData PassphraseRequestData, volumeList *keyserverv1alp
deviceNameMatches := requestData.DeviceName != "" && p.DeviceName == requestData.DeviceName
uuidMatches := requestData.UUID != "" && p.UUID == requestData.UUID
labelMatches := requestData.Label != "" && p.Label == requestData.Label
secretName := ""
if p.Secret != nil && p.Secret.Name != "" {
secretName = p.Secret.Name
}
secretPath := ""
if p.Secret != nil && p.Secret.Path != "" {
secretPath = p.Secret.Path
}
if labelMatches || uuidMatches || deviceNameMatches {
return &SealedVolumeData{
Quarantined: v.Spec.Quarantined,
SecretName: p.Secret.Name,
SecretPath: p.Secret.Path,
SecretName: secretName,
SecretPath: secretPath,
VolumeName: v.Name,
PartitionLabel: p.Label,
}
@@ -306,3 +360,36 @@ func findVolumeFor(requestData PassphraseRequestData, volumeList *keyserverv1alp
return nil
}
// errorMessage should be used when an error should be both, printed to the stdout
// and sent over the wire to the websocket client.
func errorMessage(conn *websocket.Conn, logger logr.Logger, theErr error, description string) {
if theErr == nil {
return
}
logger.Error(theErr, description)
writer, err := conn.NextWriter(websocket.BinaryMessage)
if err != nil {
logger.Error(err, "getting a writer from the connection")
}
errMsg := theErr.Error()
err = json.NewEncoder(writer).Encode(payload.Data{Error: errMsg})
if err != nil {
logger.Error(err, "error encoding the response to json")
}
err = writer.Close()
if err != nil {
logger.Error(err, "closing the writer")
}
}
func logRequestHandler(logger logr.Logger, h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
logger.Info("Incoming request", "method", r.Method, "uri", r.URL.String(),
"referer", r.Header.Get("Referer"), "userAgent", r.Header.Get("User-Agent"))
h.ServeHTTP(w, r)
})
}

19
pkg/payload/payload.go Normal file
View File

@@ -0,0 +1,19 @@
package payload
type Data struct {
Passphrase string `json:"passphrase"`
Error string `json:"error"`
GeneratedBy string `json:"generated_by"`
}
func (d Data) HasError() bool {
return d.Error != ""
}
func (d Data) HasPassphrase() bool {
return d.Passphrase != ""
}
func (d Data) HasBeenGenerated() bool {
return d.GeneratedBy != ""
}

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"
}
]
}

60
scripts/e2e-tests.sh Executable file
View File

@@ -0,0 +1,60 @@
#!/bin/bash
set -e
# This scripts prepares a cluster where we install the kcrypt CRDs.
# This is where sealed volumes are created.
GINKGO_NODES="${GINKGO_NODES:-1}"
K3S_IMAGE="rancher/k3s:v1.26.1-k3s1"
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
CLUSTER_NAME=$(echo $RANDOM | md5sum | head -c 10; echo;)
export KUBECONFIG=$(mktemp)
# https://unix.stackexchange.com/a/423052
getFreePort() {
echo $(comm -23 <(seq "30000" "30200" | sort) <(ss -Htan | awk '{print $4}' | cut -d':' -f2 | sort -u) | shuf | head -n "1")
}
cleanup() {
echo "Cleaning up $CLUSTER_NAME"
k3d cluster delete "$CLUSTER_NAME" || true
rm -rf "$KUBECONFIG"
}
trap cleanup EXIT
# Create a cluster and bind ports 80 and 443 on the host
# This will allow us to access challenger server on 10.0.2.2 which is the IP
# on which qemu "sees" the host.
# We change the CIDR because k3s creates iptables rules that block DNS traffic to this CIDR
# (something like that). If you run k3d inside a k3s cluster (inside a Pod), DNS won't work
# inside the k3d server container unless you use a different CIDR.
# Here we are avoiding CIDR "10.43.x.x"
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"
# 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
kubectl apply -f https://github.com/jetstack/cert-manager/releases/latest/download/cert-manager.yaml
kubectl wait --for=condition=Available deployment --timeout=2m -n cert-manager --all
# Replace the CLUSTER_IP in the kustomize resource
# Only needed for debugging so that we can access the server from the host
# (the 10.0.2.2 IP address is only useful from within qemu)
export CLUSTER_IP=$(docker inspect "k3d-${CLUSTER_NAME}-server-0" | jq -r '.[0].NetworkSettings.Networks[].IPAddress')
envsubst \
< "$SCRIPT_DIR/../tests/assets/challenger-server-ingress.template.yaml" \
> "$SCRIPT_DIR/../tests/assets/challenger-server-ingress.yaml"
# Install the challenger server kustomization
kubectl apply -k "$SCRIPT_DIR/../tests/assets/"
# 10.0.2.2 is where the vm sees the host
# https://stackoverflow.com/a/6752280
export KMS_ADDRESS="10.0.2.2.challenger.sslip.io"
go run github.com/onsi/ginkgo/v2/ginkgo -v --nodes $GINKGO_NODES --label-filter $LABEL --fail-fast -r ./tests/

View File

@@ -0,0 +1,37 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: controller-manager
namespace: system
spec:
template:
spec:
containers:
- name: manager
# Don't try to pull the image we built locally
imagePullPolicy: IfNotPresent
args:
- "--health-probe-bind-address"
- ":8081"
- "--metrics-bind-address"
- "127.0.0.1:8080"
- "--namespace"
- "default"
- "--leader-elect"
---
apiVersion: v1
kind: Service
metadata:
name: kcrypt-escrow-server
namespace: system
spec:
type: ClusterIP
selector:
control-plane: controller-manager
ports:
- name: wss
port: 8082
protocol: TCP
targetPort: 8082

View File

@@ -0,0 +1,46 @@
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: challenger-server
annotations:
cert-manager.io/cluster-issuer: "selfsigned"
kubernetes.io/ingress.class: "traefik"
spec:
tls:
- 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
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: kcrypt-controller-kcrypt-escrow-server
port:
number: 8082
- host: ${CLUSTER_IP}.challenger.sslip.io
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
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

@@ -0,0 +1,8 @@
---
# Self-signed issuer
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: selfsigned
spec:
selfSigned: {}

View File

@@ -0,0 +1,13 @@
# Adds namespace to all resources.
namespace: default
bases:
- ../../config/default
resources:
- challenger-server-ingress.yaml
- cluster-issuer.yaml
patchesStrategicMerge:
# Fix labels and selectors to make challenger server accessible
- challenger-patch.yaml

454
tests/encryption_test.go Normal file
View File

@@ -0,0 +1,454 @@
package e2e_test
import (
"fmt"
"os"
"os/exec"
"path"
"strconv"
"strings"
"syscall"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
. "github.com/spectrocloud/peg/matcher"
"gopkg.in/yaml.v3"
client "github.com/kairos-io/kairos-challenger/cmd/discovery/client"
)
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(vmOpts)
fmt.Printf("\nvm.StateDir = %+v\n", vm.StateDir)
vm.EventuallyConnects(1200)
})
JustBeforeEach(func() {
configFile, err := os.CreateTemp("", "")
Expect(err).ToNot(HaveOccurred())
defer os.Remove(configFile.Name())
err = os.WriteFile(configFile.Name(), []byte(config), 0744)
Expect(err).ToNot(HaveOccurred())
err = vm.Scp(configFile.Name(), "config.yaml", "0744")
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"))
Expect(err).ToNot(HaveOccurred())
if len(tpmPID) != 0 {
pid, err := strconv.Atoi(string(tpmPID))
Expect(err).ToNot(HaveOccurred())
syscall.Kill(pid, syscall.SIGKILL)
}
})
Expect(err).ToNot(HaveOccurred())
})
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() {
config = `#cloud-config
install:
encrypted_partitions:
- COS_PERSISTENT
reboot: false # we will reboot manually
hostname: metal-{{ trunc 4 .MachineID }}
users:
- name: kairos
passwd: kairos
`
})
It("boots and has an encrypted partition", func() {
vm.Reboot()
vm.EventuallyConnects(1200)
out, err := vm.Sudo("blkid")
Expect(err).ToNot(HaveOccurred(), out)
Expect(out).To(MatchRegexp("TYPE=\"crypto_LUKS\" PARTLABEL=\"persistent\""), out)
})
})
//https://kairos.io/docs/advanced/partition_encryption/#online-mode
When("using a remote key management server (automated passphrase generation)", Label("remote-auto"), func() {
var tpmHash string
BeforeEach(func() {
tpmHash = createTPMPassphraseSecret(vm)
config = fmt.Sprintf(`#cloud-config
hostname: metal-{{ trunc 4 .MachineID }}
users:
- name: kairos
passwd: kairos
install:
encrypted_partitions:
- COS_PERSISTENT
grub_options:
extra_cmdline: "rd.neednet=1"
reboot: false # we will reboot manually
kcrypt:
challenger:
challenger_server: "http://%s"
nv_index: ""
c_index: ""
tpm_device: ""
`, os.Getenv("KMS_ADDRESS"))
})
AfterEach(func() {
cmd := exec.Command("kubectl", "delete", "sealedvolume", tpmHash)
out, err := cmd.CombinedOutput()
Expect(err).ToNot(HaveOccurred(), out)
})
It("creates a passphrase and a key/pair to decrypt it", func() {
// Expect a LUKS partition
vm.Reboot(750)
vm.EventuallyConnects(1200)
out, err := vm.Sudo("blkid")
Expect(err).ToNot(HaveOccurred(), out)
Expect(out).To(MatchRegexp("TYPE=\"crypto_LUKS\" PARTLABEL=\"persistent\""), out)
// Expect a secret to be created
cmd := exec.Command("kubectl", "get", "secrets",
fmt.Sprintf("%s-cos-persistent", tpmHash),
"-o=go-template='{{.data.generated_by|base64decode}}'",
)
secretOut, err := cmd.CombinedOutput()
Expect(err).ToNot(HaveOccurred(), string(secretOut))
Expect(string(secretOut)).To(MatchRegexp("tpm"))
})
})
// https://kairos.io/docs/advanced/partition_encryption/#scenario-static-keys
When("using a remote key management server (static keys)", Label("remote-static"), func() {
var tpmHash string
var err error
BeforeEach(func() {
tpmHash, err = vm.Sudo("/system/discovery/kcrypt-discovery-challenger")
Expect(err).ToNot(HaveOccurred(), tpmHash)
kubectlApplyYaml(fmt.Sprintf(`---
apiVersion: v1
kind: Secret
metadata:
name: %[1]s
namespace: default
type: Opaque
stringData:
pass: "awesome-plaintext-passphrase"
`, tpmHash))
kubectlApplyYaml(fmt.Sprintf(`---
apiVersion: keyserver.kairos.io/v1alpha1
kind: SealedVolume
metadata:
name: %[1]s
namespace: default
spec:
TPMHash: "%[1]s"
partitions:
- label: COS_PERSISTENT
secret:
name: %[1]s
path: pass
quarantined: false
`, tpmHash))
config = fmt.Sprintf(`#cloud-config
hostname: metal-{{ trunc 4 .MachineID }}
users:
- name: kairos
passwd: kairos
install:
encrypted_partitions:
- COS_PERSISTENT
grub_options:
extra_cmdline: "rd.neednet=1"
reboot: false # we will reboot manually
kcrypt:
challenger:
challenger_server: "http://%s"
`, os.Getenv("KMS_ADDRESS"))
})
AfterEach(func() {
cmd := exec.Command("kubectl", "delete", "sealedvolume", tpmHash)
out, err := cmd.CombinedOutput()
Expect(err).ToNot(HaveOccurred(), out)
cmd = exec.Command("kubectl", "delete", "secret", tpmHash)
out, err = cmd.CombinedOutput()
Expect(err).ToNot(HaveOccurred(), out)
})
It("creates uses the existing passphrase to decrypt it", func() {
// Expect a LUKS partition
vm.Reboot()
vm.EventuallyConnects(1200)
out, err := vm.Sudo("blkid")
Expect(err).ToNot(HaveOccurred(), out)
Expect(out).To(MatchRegexp("TYPE=\"crypto_LUKS\" PARTLABEL=\"persistent\""), out)
Expect(out).To(MatchRegexp("/dev/mapper.*LABEL=\"COS_PERSISTENT\""), out)
})
})
When("the key management server is listening on https", func() {
var tpmHash string
BeforeEach(func() {
tpmHash = createTPMPassphraseSecret(vm)
})
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() {
BeforeEach(func() {
cert := getChallengerServerCert()
kcryptConfig := createConfigWithCert(fmt.Sprintf("https://%s", os.Getenv("KMS_ADDRESS")), cert)
kcryptConfigBytes, err := yaml.Marshal(kcryptConfig)
Expect(err).ToNot(HaveOccurred())
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
%s
`, string(kcryptConfigBytes))
})
It("successfully talks to the server", func() {
vm.Reboot()
vm.EventuallyConnects(1200)
out, err := vm.Sudo("blkid")
Expect(err).ToNot(HaveOccurred(), out)
Expect(out).To(MatchRegexp("TYPE=\"crypto_LUKS\" PARTLABEL=\"persistent\""), out)
Expect(out).To(MatchRegexp("/dev/mapper.*LABEL=\"COS_PERSISTENT\""), out)
})
})
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 }}
users:
- name: kairos
passwd: kairos
install:
encrypted_partitions:
- COS_PERSISTENT
grub_options:
extra_cmdline: "rd.neednet=1"
reboot: false # we will reboot manually
kcrypt:
challenger:
challenger_server: "https://%s"
`, 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("failed to verify certificate: x509: certificate signed by unknown authority"))
})
})
})
})
func printInstallationOutput(message string, callerSkip ...int) {
fmt.Printf("This is the installation output in case it's useful:\n%s\n", installationOutput)
// Ensures the correct line numbers are reported
Fail(message, callerSkip[0]+1)
}
func kubectlApplyYaml(yamlData string) {
yamlFile, err := os.CreateTemp("", "")
Expect(err).ToNot(HaveOccurred())
defer os.Remove(yamlFile.Name())
err = os.WriteFile(yamlFile.Name(), []byte(yamlData), 0744)
Expect(err).ToNot(HaveOccurred())
cmd := exec.Command("kubectl", "apply", "-f", yamlFile.Name())
out, err := cmd.CombinedOutput()
Expect(err).ToNot(HaveOccurred(), out)
}
func getChallengerServerCert() string {
cmd := exec.Command(
"kubectl", "get", "secret", "-n", "default", "kms-tls",
"-o", `go-template={{ index .data "ca.crt" | base64decode }}`)
out, err := cmd.CombinedOutput()
Expect(err).ToNot(HaveOccurred(), string(out))
return string(out)
}
func createConfigWithCert(server, cert string) client.Config {
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
}

234
tests/suite_test.go Normal file
View File

@@ -0,0 +1,234 @@
package e2e_test
import (
"context"
"fmt"
"net"
"os"
"os/exec"
"path"
"strconv"
"testing"
"github.com/google/uuid"
process "github.com/mudler/go-processmanager"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
. "github.com/spectrocloud/peg/matcher"
machine "github.com/spectrocloud/peg/pkg/machine"
"github.com/spectrocloud/peg/pkg/machine/types"
)
func TestE2e(t *testing.T) {
RegisterFailHandler(Fail)
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 == "" {
user = "kairos"
}
return user
}
func pass() string {
pass := os.Getenv("SSH_PASS")
if pass == "" {
pass = "kairos"
}
return pass
}
func startVM(vmOpts VMOptions) (context.Context, VM) {
if vmOpts.ISO == "" {
fmt.Println("ISO missing")
os.Exit(1)
}
vmName := uuid.New().String()
stateDir, err := os.MkdirTemp("", "")
Expect(err).ToNot(HaveOccurred())
if vmOpts.EmulateTPM {
emulateTPM(stateDir)
}
sshPort, err := getFreePort()
Expect(err).ToNot(HaveOccurred())
opts := []types.MachineOption{
types.QEMUEngine,
types.WithISO(vmOpts.ISO),
types.WithMemory(vmOpts.Memory),
types.WithCPU(vmOpts.CPUS),
types.WithSSHPort(strconv.Itoa(sshPort)),
types.WithID(vmName),
types.WithSSHUser(vmOpts.User),
types.WithSSHPass(vmOpts.Password),
types.OnFailure(func(p *process.Process) {
defer GinkgoRecover()
var stdout, stderr, serial, status string
if stdoutBytes, err := os.ReadFile(p.StdoutPath()); err != nil {
stdout = fmt.Sprintf("Error reading stdout file: %s\n", err)
} else {
stdout = string(stdoutBytes)
}
if stderrBytes, err := os.ReadFile(p.StderrPath()); err != nil {
stderr = fmt.Sprintf("Error reading stderr file: %s\n", err)
} else {
stderr = string(stderrBytes)
}
if status, err = p.ExitCode(); err != nil {
status = fmt.Sprintf("Error reading exit code file: %s\n", err)
}
if serialBytes, err := os.ReadFile(path.Join(p.StateDir(), "serial.log")); err != nil {
serial = fmt.Sprintf("Error reading serial log file: %s\n", err)
} else {
serial = string(serialBytes)
}
Fail(fmt.Sprintf("\nVM Aborted.\nstdout: %s\nstderr: %s\nserial: %s\nExit status: %s\n",
stdout, stderr, serial, status))
}),
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")
}
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",
)
return nil
},
}
// Set this to true to debug.
// You can connect to it with "spicy" or other tool.
var spicePort int
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 vmOpts.UseKVM {
opts = append(opts, func(m *types.MachineConfig) error {
m.Args = append(m.Args,
"-enable-kvm",
)
return nil
})
}
m, err := machine.New(opts...)
Expect(err).ToNot(HaveOccurred())
vm := NewVM(m, stateDir)
ctx, err := vm.Start(context.Background())
Expect(err).ToNot(HaveOccurred())
if vmOpts.RunSpicy {
cmd := exec.Command("spicy",
"-h", "127.0.0.1",
"-p", strconv.Itoa(spicePort))
err = cmd.Start()
Expect(err).ToNot(HaveOccurred())
}
return ctx, vm
}
// return the PID of the swtpm (to be killed later) and the state directory
func emulateTPM(stateDir string) {
t := path.Join(stateDir, "tpm")
err := os.MkdirAll(t, os.ModePerm)
Expect(err).ToNot(HaveOccurred())
cmd := exec.Command("swtpm",
"socket",
"--tpmstate", fmt.Sprintf("dir=%s", t),
"--ctrl", fmt.Sprintf("type=unixio,path=%s/swtpm-sock", t),
"--tpm2", "--log", "level=20")
err = cmd.Start()
Expect(err).ToNot(HaveOccurred())
err = os.WriteFile(path.Join(t, "pid"), []byte(strconv.Itoa(cmd.Process.Pid)), 0744)
Expect(err).ToNot(HaveOccurred())
}
// https://gist.github.com/sevkin/96bdae9274465b2d09191384f86ef39d
// GetFreePort asks the kernel for a free open port that is ready to use.
func getFreePort() (port int, err error) {
var a *net.TCPAddr
if a, err = net.ResolveTCPAddr("tcp", "localhost:0"); err == nil {
var l *net.TCPListener
if l, err = net.ListenTCP("tcp", a); err == nil {
defer l.Close()
return l.Addr().(*net.TCPAddr).Port, nil
}
}
return
}