ffd276bd3e
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.
93 lines
2.5 KiB
Go
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
|
|
}
|