isle/entrypoint/src/bootstrap/hosts.go
Brian Picciano ffd276bd3e Refactor how nebula certs are signed and propagated
I had previously made the mistake of thinking that the Curve25519 key
which is generated for each host to use in nebula communication could
also be used for signing. This is not the case, Ed25519 is used for
signing and is different thant Curve25519.

Rather than figuring out how to convert the Curve25519 key into an
Ed25519 key, which there is no apparent support for in the standard
library, I opted to instead ship a separate key just for signing with
each host. Doing this required a bit of refactoring in order to keep all
the different keys straight and ensure all data which needs a signature
still has it.
2022-11-05 15:23:29 +01:00

93 lines
2.5 KiB
Go

package bootstrap
import (
"bytes"
"cryptic-net/nebula"
"fmt"
"net"
"strings"
"gopkg.in/yaml.v3"
)
// NebulaHost describes the nebula configuration of a Host which is relevant for
// other hosts to know.
type NebulaHost struct {
SignedPublicCredentials string `yaml:"signed_public_credentials"`
PublicAddr string `yaml:"public_addr,omitempty"`
}
// NewNebulaHostSignedPublicCredentials constructs the SignedPublicCredentials
// field of the NebulaHost struct, using the CACredentials to sign the
// HostPublicCredentials.
func NewNebulaHostSignedPublicCredentials(
caCreds nebula.CACredentials,
hostPublicCreds nebula.HostPublicCredentials,
) (
string, error,
) {
hostPublicCredsB, err := yaml.Marshal(hostPublicCreds)
if err != nil {
return "", fmt.Errorf("yaml marshaling host's public credentials: %w", err)
}
buf := new(bytes.Buffer)
err = nebula.SignAndWrap(buf, caCreds.SigningPrivateKeyPEM, hostPublicCredsB)
if err != nil {
return "", fmt.Errorf("signing host's public credentials: %w", err)
}
return buf.String(), nil
}
// GarageHost describes a single garage instance in the GarageHost.
type GarageHostInstance struct {
ID string `yaml:"id"`
RPCPort int `yaml:"rpc_port"`
S3APIPort int `yaml:"s3_api_port"`
}
// GarageHost describes the garage configuration of a Host which is relevant for
// other hosts to know.
type GarageHost struct {
Instances []GarageHostInstance `yaml:"instances"`
}
// Host consolidates all information about a single host from the bootstrap
// file.
type Host struct {
Name string `yaml:"name"`
Nebula NebulaHost `yaml:"nebula"`
Garage *GarageHost `yaml:"garage,omitempty"`
}
// IP returns the IP address encoded in the Host's nebula certificate, or panics
// if there is an error.
//
// This assumes that the Host and its data has already been verified against the
// CA signing key.
func (h Host) IP() net.IP {
hostPublicCredsB, _, err := nebula.Unwrap(
strings.NewReader(h.Nebula.SignedPublicCredentials),
)
if err != nil {
panic(fmt.Errorf("unwrapping host's signed public credentials: %w", err))
}
var hostPublicCreds nebula.HostPublicCredentials
if err := yaml.Unmarshal(hostPublicCredsB, &hostPublicCreds); err != nil {
panic(fmt.Errorf("yaml unmarshaling host's public credentials: %w", err))
}
ip, err := nebula.IPFromHostCertPEM(hostPublicCreds.CertPEM)
if err != nil {
panic(fmt.Errorf("could not parse IP out of cert for host %q: %w", h.Name, err))
}
return ip
}