2022-10-15 14:28:03 +00:00
|
|
|
package bootstrap
|
2021-04-20 21:31:37 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2024-11-05 20:25:04 +00:00
|
|
|
"isle/garage"
|
2024-06-10 16:56:36 +00:00
|
|
|
"isle/nebula"
|
2024-07-13 14:08:13 +00:00
|
|
|
"net/netip"
|
2022-10-15 16:41:07 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// NebulaHost describes the nebula configuration of a Host which is relevant for
|
|
|
|
// other hosts to know.
|
2021-04-20 21:31:37 +00:00
|
|
|
type NebulaHost struct {
|
2024-06-10 20:31:29 +00:00
|
|
|
PublicAddr string
|
2021-04-20 21:31:37 +00:00
|
|
|
}
|
|
|
|
|
2022-10-15 16:41:07 +00:00
|
|
|
// GarageHost describes a single garage instance in the GarageHost.
|
2021-04-20 21:31:37 +00:00
|
|
|
type GarageHostInstance struct {
|
2024-06-10 16:56:36 +00:00
|
|
|
ID string
|
|
|
|
RPCPort int
|
|
|
|
S3APIPort int
|
2021-04-20 21:31:37 +00:00
|
|
|
}
|
|
|
|
|
2022-10-15 16:41:07 +00:00
|
|
|
// GarageHost describes the garage configuration of a Host which is relevant for
|
|
|
|
// other hosts to know.
|
2021-04-20 21:31:37 +00:00
|
|
|
type GarageHost struct {
|
2024-06-10 16:56:36 +00:00
|
|
|
Instances []GarageHostInstance
|
2021-04-20 21:31:37 +00:00
|
|
|
}
|
|
|
|
|
2024-06-10 20:31:29 +00:00
|
|
|
// HostAssigned are all fields related to a host which were assigned to it by an
|
|
|
|
// admin.
|
|
|
|
type HostAssigned struct {
|
2024-07-12 13:30:21 +00:00
|
|
|
Name nebula.HostName
|
2024-06-10 20:31:29 +00:00
|
|
|
PublicCredentials nebula.HostPublicCredentials
|
2021-04-20 21:31:37 +00:00
|
|
|
}
|
|
|
|
|
2024-06-10 20:31:29 +00:00
|
|
|
// HostConfigured are all the fields a host can configure for itself.
|
|
|
|
type HostConfigured struct {
|
|
|
|
Nebula NebulaHost `json:",omitempty"`
|
|
|
|
Garage GarageHost `json:",omitempty"`
|
|
|
|
}
|
2022-10-29 19:11:40 +00:00
|
|
|
|
2024-06-10 20:31:29 +00:00
|
|
|
// AuthenticatedHost wraps all the data about a host which other hosts may know
|
|
|
|
// about it, such that those hosts can authenticate that the data is valid and
|
|
|
|
// approved by an admin.
|
|
|
|
type AuthenticatedHost struct {
|
|
|
|
Assigned nebula.Signed[HostAssigned] // signed by CA
|
|
|
|
Configured nebula.Signed[HostConfigured] // signed by host
|
|
|
|
}
|
2022-11-05 14:23:29 +00:00
|
|
|
|
2024-06-10 20:31:29 +00:00
|
|
|
// Unwrap attempts to authenticate and unwrap the Host embedded in this
|
|
|
|
// instance. nebula.ErrInvalidSignature is returned if any signatures are
|
|
|
|
// invalid.
|
|
|
|
func (ah AuthenticatedHost) Unwrap(caCreds nebula.CAPublicCredentials) (Host, error) {
|
|
|
|
assigned, err := ah.Assigned.Unwrap(caCreds.SigningKey)
|
2022-11-05 14:23:29 +00:00
|
|
|
if err != nil {
|
2024-06-10 20:31:29 +00:00
|
|
|
return Host{}, fmt.Errorf("unwrapping assigned fields using CA public key: %w", err)
|
2022-11-05 14:23:29 +00:00
|
|
|
}
|
|
|
|
|
2024-06-10 20:31:29 +00:00
|
|
|
configured, err := ah.Configured.Unwrap(assigned.PublicCredentials.SigningKey)
|
|
|
|
if err != nil {
|
|
|
|
return Host{}, fmt.Errorf("unwrapping configured fields using host public key: %w", err)
|
2022-11-05 14:23:29 +00:00
|
|
|
}
|
|
|
|
|
2024-06-10 20:31:29 +00:00
|
|
|
return Host{assigned, configured}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Host contains all data bout a host which other hosts may know about it.
|
|
|
|
//
|
|
|
|
// A Host should only be obtained over the network as an AuthenticatedHost, and
|
|
|
|
// subsequently Unwrapped.
|
|
|
|
type Host struct {
|
|
|
|
HostAssigned
|
|
|
|
HostConfigured
|
|
|
|
}
|
|
|
|
|
2024-12-07 19:39:13 +00:00
|
|
|
// NewHost creates a Host instance using the given assigned fields, along with
|
|
|
|
// the HostPrivateCredentials which its PublicCredentials field.
|
|
|
|
func NewHost(
|
|
|
|
caCreds nebula.CACredentials, name nebula.HostName, ip netip.Addr,
|
|
|
|
) (
|
|
|
|
host Host, hostPrivCreds nebula.HostPrivateCredentials, err error,
|
|
|
|
) {
|
|
|
|
hostPubCreds, hostPrivCreds, err := nebula.NewHostCredentials(
|
|
|
|
caCreds, name, ip,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
err = fmt.Errorf("generating host credentials: %w", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
host = Host{
|
|
|
|
HostAssigned: HostAssigned{
|
|
|
|
Name: name,
|
|
|
|
PublicCredentials: hostPubCreds,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-06-10 20:31:29 +00:00
|
|
|
// 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.
|
2024-07-13 14:08:13 +00:00
|
|
|
func (h Host) IP() netip.Addr {
|
2024-07-07 10:44:49 +00:00
|
|
|
cert := h.PublicCredentials.Cert.Unwrap()
|
|
|
|
if len(cert.Details.Ips) == 0 {
|
|
|
|
panic(fmt.Sprintf("host %q not configured with any ips: %+v", h.Name, h))
|
|
|
|
}
|
2024-07-13 14:08:13 +00:00
|
|
|
|
|
|
|
ip := cert.Details.Ips[0].IP
|
|
|
|
addr, ok := netip.AddrFromSlice(ip)
|
|
|
|
if !ok {
|
|
|
|
panic(fmt.Sprintf("ip %q (%#v) is not valid, somehow", ip, ip))
|
|
|
|
}
|
|
|
|
|
|
|
|
return addr
|
2022-10-29 19:11:40 +00:00
|
|
|
}
|
2024-11-05 20:25:04 +00:00
|
|
|
|
2024-11-08 16:46:44 +00:00
|
|
|
// GarageNodes returns a RemoteNode for each garage instance advertised by this
|
2024-11-05 20:25:04 +00:00
|
|
|
// Host.
|
2024-11-08 16:46:44 +00:00
|
|
|
func (h Host) GarageNodes() []garage.RemoteNode {
|
|
|
|
var nodes []garage.RemoteNode
|
2024-11-05 20:25:04 +00:00
|
|
|
for _, instance := range h.Garage.Instances {
|
2024-11-08 16:46:44 +00:00
|
|
|
nodes = append(nodes, garage.RemoteNode{
|
2024-11-05 20:25:04 +00:00
|
|
|
ID: instance.ID,
|
|
|
|
IP: h.IP().String(),
|
|
|
|
RPCPort: instance.RPCPort,
|
|
|
|
S3APIPort: instance.S3APIPort,
|
|
|
|
})
|
|
|
|
}
|
2024-11-08 16:46:44 +00:00
|
|
|
return nodes
|
2024-11-05 20:25:04 +00:00
|
|
|
}
|