mirror of
https://github.com/kairos-io/kcrypt-challenger.git
synced 2025-08-23 08:51:26 +00:00
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>
This commit is contained in:
parent
bf59ecd475
commit
311b8adda0
@ -16,6 +16,9 @@ import (
|
|||||||
"github.com/mudler/yip/pkg/utils"
|
"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 errPartNotFound error = fmt.Errorf("pass for partition not found")
|
||||||
var errBadCertificate error = fmt.Errorf("unknown certificate")
|
var errBadCertificate error = fmt.Errorf("unknown certificate")
|
||||||
|
|
||||||
@ -30,6 +33,10 @@ func NewClient() (*Client, error) {
|
|||||||
|
|
||||||
// ❯ echo '{ "data": "{ \\"label\\": \\"LABEL\\" }"}' | sudo -E WSS_SERVER="http://localhost:8082/challenge" ./challenger "discovery.password"
|
// ❯ echo '{ "data": "{ \\"label\\": \\"LABEL\\" }"}' | sudo -E WSS_SERVER="http://localhost:8082/challenge" ./challenger "discovery.password"
|
||||||
func (c *Client) Start() error {
|
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()
|
factory := pluggable.NewPluginFactory()
|
||||||
|
|
||||||
// Input: bus.EventInstallPayload
|
// Input: bus.EventInstallPayload
|
||||||
@ -59,7 +66,7 @@ func (c *Client) Start() error {
|
|||||||
return factory.Run(pluggable.EventType(os.Args[1]), os.Stdin, os.Stdout)
|
return factory.Run(pluggable.EventType(os.Args[1]), os.Stdin, os.Stdout)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) generatePass(postEndpoint string, p *block.Partition) error {
|
func (c *Client) generatePass(postEndpoint string, headers map[string]string, p *block.Partition) error {
|
||||||
|
|
||||||
rand := utils.RandomString(32)
|
rand := utils.RandomString(32)
|
||||||
pass, err := tpm.EncryptBlob([]byte(rand))
|
pass, err := tpm.EncryptBlob([]byte(rand))
|
||||||
@ -75,6 +82,10 @@ func (c *Client) generatePass(postEndpoint string, p *block.Partition) error {
|
|||||||
tpm.WithAdditionalHeader("name", p.Name),
|
tpm.WithAdditionalHeader("name", p.Name),
|
||||||
tpm.WithAdditionalHeader("uuid", p.UUID),
|
tpm.WithAdditionalHeader("uuid", p.UUID),
|
||||||
}
|
}
|
||||||
|
for k, v := range headers {
|
||||||
|
opts = append(opts, tpm.WithAdditionalHeader(k, v))
|
||||||
|
}
|
||||||
|
|
||||||
conn, err := tpm.Connection(postEndpoint, opts...)
|
conn, err := tpm.Connection(postEndpoint, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -84,20 +95,27 @@ func (c *Client) generatePass(postEndpoint string, p *block.Partition) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) waitPass(p *block.Partition, attempts int) (pass string, err error) {
|
func (c *Client) waitPass(p *block.Partition, attempts int) (pass string, err error) {
|
||||||
// IF we don't have any server configured, just do local
|
additionalHeaders := map[string]string{}
|
||||||
if c.Config.Kcrypt.Challenger.Server == "" {
|
serverURL := c.Config.Kcrypt.Challenger.Server
|
||||||
|
|
||||||
|
// If we don't have any server configured, just do local
|
||||||
|
if serverURL == "" {
|
||||||
return localPass(c.Config)
|
return localPass(c.Config)
|
||||||
}
|
}
|
||||||
|
|
||||||
challengeEndpoint := fmt.Sprintf("%s/getPass", c.Config.Kcrypt.Challenger.Server)
|
if c.Config.Kcrypt.Challenger.MDNS {
|
||||||
postEndpoint := fmt.Sprintf("%s/postPass", c.Config.Kcrypt.Challenger.Server)
|
serverURL, additionalHeaders, err = queryMDNS(serverURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
getEndpoint := fmt.Sprintf("%s/getPass", serverURL)
|
||||||
|
postEndpoint := fmt.Sprintf("%s/postPass", serverURL)
|
||||||
|
|
||||||
for tries := 0; tries < attempts; tries++ {
|
for tries := 0; tries < attempts; tries++ {
|
||||||
var generated bool
|
var generated bool
|
||||||
pass, generated, err = getPass(challengeEndpoint, c.Config.Kcrypt.Challenger.Certificate, p)
|
pass, generated, err = getPass(getEndpoint, additionalHeaders, c.Config.Kcrypt.Challenger.Certificate, p)
|
||||||
if err == errPartNotFound {
|
if err == errPartNotFound {
|
||||||
// IF server doesn't have a pass for us, then we generate one and we set it
|
// IF server doesn't have a pass for us, then we generate one and we set it
|
||||||
err = c.generatePass(postEndpoint, p)
|
err = c.generatePass(postEndpoint, additionalHeaders, p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -118,7 +136,7 @@ func (c *Client) waitPass(p *block.Partition, attempts int) (pass string, err er
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("Failed with error: %s . Will retry.\n", err.Error())
|
logToFile("Failed with error: %s . Will retry.\n", err.Error())
|
||||||
time.Sleep(1 * time.Second) // network errors? retry
|
time.Sleep(1 * time.Second) // network errors? retry
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,3 +163,14 @@ func (c *Client) decryptPassphrase(pass string) (string, error) {
|
|||||||
|
|
||||||
return string(passBytes), err
|
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)
|
||||||
|
}
|
||||||
|
@ -13,11 +13,10 @@ type Client struct {
|
|||||||
type Config struct {
|
type Config struct {
|
||||||
Kcrypt struct {
|
Kcrypt struct {
|
||||||
Challenger struct {
|
Challenger struct {
|
||||||
|
MDNS bool `yaml:"mdns,omitempty"`
|
||||||
Server string `yaml:"challenger_server,omitempty"`
|
Server string `yaml:"challenger_server,omitempty"`
|
||||||
// Non-volatile index memory: where we store the encrypted passphrase (offline mode)
|
NVIndex string `yaml:"nv_index,omitempty"` // Non-volatile index memory: where we store the encrypted passphrase (offline mode)
|
||||||
NVIndex string `yaml:"nv_index,omitempty"`
|
CIndex string `yaml:"c_index,omitempty"` // Certificate index: this is where the rsa pair that decrypts the passphrase lives
|
||||||
// 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"`
|
TPMDevice string `yaml:"tpm_device,omitempty"`
|
||||||
Certificate string `yaml:"certificate,omitempty"`
|
Certificate string `yaml:"certificate,omitempty"`
|
||||||
}
|
}
|
||||||
|
@ -16,13 +16,19 @@ import (
|
|||||||
|
|
||||||
const DefaultNVIndex = "0x1500000"
|
const DefaultNVIndex = "0x1500000"
|
||||||
|
|
||||||
func getPass(server, certificate string, partition *block.Partition) (string, bool, error) {
|
func getPass(server string, headers map[string]string, certificate string, partition *block.Partition) (string, bool, error) {
|
||||||
msg, err := tpm.Get(server,
|
opts := []tpm.Option{
|
||||||
tpm.WithCAs([]byte(certificate)),
|
tpm.WithCAs([]byte(certificate)),
|
||||||
tpm.AppendCustomCAToSystemCA,
|
tpm.AppendCustomCAToSystemCA,
|
||||||
tpm.WithAdditionalHeader("label", partition.FilesystemLabel),
|
tpm.WithAdditionalHeader("label", partition.FilesystemLabel),
|
||||||
tpm.WithAdditionalHeader("name", partition.Name),
|
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 {
|
if err != nil {
|
||||||
return "", false, err
|
return "", false, err
|
||||||
}
|
}
|
||||||
|
85
cmd/discovery/client/mdns.go
Normal file
85
cmd/discovery/client/mdns.go
Normal 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 "", ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
4
go.mod
4
go.mod
@ -6,10 +6,11 @@ require (
|
|||||||
github.com/go-logr/logr v1.2.4
|
github.com/go-logr/logr v1.2.4
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
github.com/gorilla/websocket v1.5.0
|
github.com/gorilla/websocket v1.5.0
|
||||||
|
github.com/hashicorp/mdns v1.0.5
|
||||||
github.com/jaypipes/ghw v0.11.0
|
github.com/jaypipes/ghw v0.11.0
|
||||||
github.com/kairos-io/kairos-sdk v0.0.15
|
github.com/kairos-io/kairos-sdk v0.0.15
|
||||||
github.com/kairos-io/kcrypt v0.7.0
|
github.com/kairos-io/kcrypt v0.7.0
|
||||||
github.com/kairos-io/tpm-helpers v0.0.0-20231220125202-70a0b64e5111
|
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-pluggable v0.0.0-20230126220627-7710299a0ae5
|
||||||
github.com/mudler/go-processmanager v0.0.0-20220724164624-c45b5c61312d
|
github.com/mudler/go-processmanager v0.0.0-20220724164624-c45b5c61312d
|
||||||
github.com/mudler/yip v1.3.0
|
github.com/mudler/yip v1.3.0
|
||||||
@ -85,7 +86,6 @@ require (
|
|||||||
github.com/gookit/color v1.5.3 // indirect
|
github.com/gookit/color v1.5.3 // indirect
|
||||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
github.com/hashicorp/mdns v1.0.5 // indirect
|
|
||||||
github.com/huandu/xstrings v1.3.3 // indirect
|
github.com/huandu/xstrings v1.3.3 // indirect
|
||||||
github.com/imdario/mergo v0.3.15 // indirect
|
github.com/imdario/mergo v0.3.15 // indirect
|
||||||
github.com/ipfs/go-log v1.0.5 // indirect
|
github.com/ipfs/go-log v1.0.5 // indirect
|
||||||
|
4
go.sum
4
go.sum
@ -415,8 +415,8 @@ github.com/kairos-io/kairos-sdk v0.0.15 h1:1hcnRfKlBzDWcZ8z7UrUqJ2v6GafCHZknPqm9
|
|||||||
github.com/kairos-io/kairos-sdk v0.0.15/go.mod h1:Ew3NKFuXByu3Y3yGu8Q92M3oMqsXrg2VilouubdhYqA=
|
github.com/kairos-io/kairos-sdk v0.0.15/go.mod h1:Ew3NKFuXByu3Y3yGu8Q92M3oMqsXrg2VilouubdhYqA=
|
||||||
github.com/kairos-io/kcrypt v0.7.0 h1:ESmCBIFbBBv7mJf0/f6ugqwSvz63M5oP9sUIdHiDlLc=
|
github.com/kairos-io/kcrypt v0.7.0 h1:ESmCBIFbBBv7mJf0/f6ugqwSvz63M5oP9sUIdHiDlLc=
|
||||||
github.com/kairos-io/kcrypt v0.7.0/go.mod h1:a9eI+vPVIQHPRtqEV/O/yIfDOdMWd9epVrq1p94gccM=
|
github.com/kairos-io/kcrypt v0.7.0/go.mod h1:a9eI+vPVIQHPRtqEV/O/yIfDOdMWd9epVrq1p94gccM=
|
||||||
github.com/kairos-io/tpm-helpers v0.0.0-20231220125202-70a0b64e5111 h1:iEBUZdneuS3YfRunKmRpxfCe4XBxeSiBcIXRmNcUzrw=
|
github.com/kairos-io/tpm-helpers v0.0.0-20240123063624-f7a3fcc66199 h1:eXiZNiQfZDelYfTF733IxDOKGAKJqn0fF0kFY10QreU=
|
||||||
github.com/kairos-io/tpm-helpers v0.0.0-20231220125202-70a0b64e5111/go.mod h1:mxKPLc7DQu9bcL8hv8A5PqTSH/rWm0M7SGolwYRYJq4=
|
github.com/kairos-io/tpm-helpers v0.0.0-20240123063624-f7a3fcc66199/go.mod h1:6YGebKVrPoJGBd9QE+x4zyuo3vPw1y33iQkNChjlBo8=
|
||||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||||
|
@ -17,19 +17,23 @@ k3d cluster create kcrypt -p '30000:30000@server:0'
|
|||||||
```
|
```
|
||||||
helm repo add kairos https://kairos-io.github.io/helm-charts
|
helm repo add kairos https://kairos-io.github.io/helm-charts
|
||||||
helm install kairos-crd kairos/kairos-crds
|
helm install kairos-crd kairos/kairos-crds
|
||||||
helm install kairos-challenger kairos/kairos-challenger
|
|
||||||
```
|
```
|
||||||
|
|
||||||
- Edit the challenger service `kairos-challenger-escrow-service` and change it to NodePort (or do it through the helm chart when installing)
|
Create the following 'kcrypt-challenger-values.yaml` file:
|
||||||
|
|
||||||
```
|
|
||||||
ports:
|
```yaml
|
||||||
- name: wss
|
service:
|
||||||
|
challenger:
|
||||||
|
type: "NodePort"
|
||||||
port: 8082
|
port: 8082
|
||||||
protocol: TCP
|
|
||||||
targetPort: 8082
|
|
||||||
nodePort: 30000
|
nodePort: 30000
|
||||||
type: NodePort
|
```
|
||||||
|
|
||||||
|
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:
|
- Add the sealedvolume and secret for the tpm chip:
|
||||||
@ -52,7 +56,7 @@ metadata:
|
|||||||
spec:
|
spec:
|
||||||
TPMHash: "5640e37f4016da16b841a93880dcc44886904392fa3c86681087b77db5afedbe"
|
TPMHash: "5640e37f4016da16b841a93880dcc44886904392fa3c86681087b77db5afedbe"
|
||||||
partitions:
|
partitions:
|
||||||
- label: "persistent"
|
- label: COS_PERSISTENT
|
||||||
secret:
|
secret:
|
||||||
name: example-host-tpm-secret
|
name: example-host-tpm-secret
|
||||||
path: pass
|
path: pass
|
||||||
@ -62,7 +66,7 @@ spec:
|
|||||||
- Start the [simple-mdns-server](https://github.com/kairos-io/simple-mdns-server)
|
- Start the [simple-mdns-server](https://github.com/kairos-io/simple-mdns-server)
|
||||||
|
|
||||||
```
|
```
|
||||||
go run . --port 30000 --interfaceName enp121s0 --serviceType _kcrypt._tcp
|
go run . --port 30000 --interfaceName enp121s0 --serviceType _kcrypt._tcp --hostName mychallenger.local
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -86,7 +90,7 @@ install:
|
|||||||
# Kcrypt configuration block
|
# Kcrypt configuration block
|
||||||
kcrypt:
|
kcrypt:
|
||||||
challenger:
|
challenger:
|
||||||
challenger_server: "http://doesnt-matter-the-name.local"
|
challenger_server: "http://mychallenger.local"
|
||||||
```
|
```
|
||||||
|
|
||||||
- Install:
|
- Install:
|
||||||
|
Loading…
Reference in New Issue
Block a user