Compare commits
2 Commits
279c79a9f1
...
736b23429c
Author | SHA1 | Date | |
---|---|---|---|
736b23429c | |||
1ee396c976 |
@ -103,7 +103,7 @@ To create the network, and the `admin.json` file in the process, run:
|
||||
```
|
||||
sudo isle network create \
|
||||
--name <name> \
|
||||
--ip-net <ip/subnet-prefix> \
|
||||
--ip-net <subnet> \
|
||||
--domain <domain> \
|
||||
--hostname <hostname> \
|
||||
| gpg -e -r <my gpg email> \
|
||||
@ -112,11 +112,6 @@ sudo isle network create \
|
||||
|
||||
A couple of notes here:
|
||||
|
||||
* The `--ip-net` parameter is formed from both the subnet and the IP you chose
|
||||
within it. So if your subnet is `10.10.0.0/16`, and your chosen IP in that
|
||||
subnet is `10.10.4.20`, then your `--ip-net` parameter will be
|
||||
`10.10.4.20/16`.
|
||||
|
||||
* Only one gpg recipient is specified. If you intend on including other users as
|
||||
network administrators you can add them to the recipients list at this step,
|
||||
so they will be able to use the `admin.json` file as well. You can also
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
"isle/admin"
|
||||
"isle/garage"
|
||||
"isle/nebula"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
@ -51,7 +51,7 @@ type Bootstrap struct {
|
||||
HostAssigned `json:"-"`
|
||||
SignedHostAssigned nebula.Signed[HostAssigned] // signed by CA
|
||||
|
||||
Hosts map[string]Host
|
||||
Hosts map[nebula.HostName]Host
|
||||
}
|
||||
|
||||
// New initializes and returns a new Bootstrap file for a new host. This
|
||||
@ -60,8 +60,8 @@ func New(
|
||||
caCreds nebula.CACredentials,
|
||||
adminCreationParams admin.CreationParams,
|
||||
garage Garage,
|
||||
name string,
|
||||
ip net.IP,
|
||||
name nebula.HostName,
|
||||
ip netip.Addr,
|
||||
) (
|
||||
Bootstrap, error,
|
||||
) {
|
||||
@ -89,7 +89,7 @@ func New(
|
||||
PrivateCredentials: hostPrivCreds,
|
||||
HostAssigned: assigned,
|
||||
SignedHostAssigned: signedAssigned,
|
||||
Hosts: map[string]Host{},
|
||||
Hosts: map[nebula.HostName]Host{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -145,7 +145,7 @@ func (b Bootstrap) ThisHost() Host {
|
||||
}
|
||||
|
||||
// Hash returns a deterministic hash of the given hosts map.
|
||||
func HostsHash(hostsMap map[string]Host) ([]byte, error) {
|
||||
func HostsHash(hostsMap map[nebula.HostName]Host) ([]byte, error) {
|
||||
|
||||
hosts := make([]Host, 0, len(hostsMap))
|
||||
for _, host := range hostsMap {
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"isle/garage"
|
||||
"isle/nebula"
|
||||
"path/filepath"
|
||||
|
||||
"dev.mediocregopher.com/mediocre-go-lib.git/mctx"
|
||||
@ -46,12 +47,12 @@ func (b Bootstrap) GetGarageBootstrapHosts(
|
||||
ctx context.Context,
|
||||
logger *mlog.Logger,
|
||||
) (
|
||||
map[string]Host, error,
|
||||
map[nebula.HostName]Host, error,
|
||||
) {
|
||||
|
||||
client := b.GlobalBucketS3APIClient()
|
||||
|
||||
hosts := map[string]Host{}
|
||||
hosts := map[nebula.HostName]Host{}
|
||||
|
||||
objInfoCh := client.ListObjects(
|
||||
ctx, garage.GlobalBucket,
|
||||
|
@ -28,7 +28,7 @@ type GarageHost struct {
|
||||
// HostAssigned are all fields related to a host which were assigned to it by an
|
||||
// admin.
|
||||
type HostAssigned struct {
|
||||
Name string
|
||||
Name nebula.HostName
|
||||
PublicCredentials nebula.HostPublicCredentials
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@ import (
|
||||
"isle/admin"
|
||||
"isle/bootstrap"
|
||||
"isle/nebula"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
)
|
||||
|
||||
@ -46,17 +46,20 @@ var subCmdAdminCreateBootstrap = subCmd{
|
||||
descr: "Creates a new bootstrap.json file for a particular host and writes it to stdout",
|
||||
checkLock: false,
|
||||
do: func(subCmdCtx subCmdCtx) error {
|
||||
var (
|
||||
flags = subCmdCtx.flagSet(false)
|
||||
hostName nebula.HostName
|
||||
ip netip.Addr
|
||||
)
|
||||
|
||||
flags := subCmdCtx.flagSet(false)
|
||||
|
||||
hostName := flags.StringP(
|
||||
"hostname", "h", "",
|
||||
hostNameF := flags.VarPF(
|
||||
textUnmarshalerFlag{&hostName},
|
||||
"hostname", "h",
|
||||
"Name of the host to generate bootstrap.json for",
|
||||
)
|
||||
|
||||
ipStr := flags.StringP(
|
||||
"ip", "i", "",
|
||||
"IP of the new host",
|
||||
ipF := flags.VarPF(
|
||||
textUnmarshalerFlag{&ip}, "ip", "i", "IP of the new host",
|
||||
)
|
||||
|
||||
adminPath := flags.StringP(
|
||||
@ -68,20 +71,12 @@ var subCmdAdminCreateBootstrap = subCmd{
|
||||
return fmt.Errorf("parsing flags: %w", err)
|
||||
}
|
||||
|
||||
if *hostName == "" || *ipStr == "" || *adminPath == "" {
|
||||
if !hostNameF.Changed ||
|
||||
!ipF.Changed ||
|
||||
*adminPath == "" {
|
||||
return errors.New("--hostname, --ip, and --admin-path are required")
|
||||
}
|
||||
|
||||
if err := validateHostName(*hostName); err != nil {
|
||||
return fmt.Errorf("invalid hostname %q: %w", *hostName, err)
|
||||
}
|
||||
|
||||
ip := net.ParseIP(*ipStr)
|
||||
|
||||
if ip == nil {
|
||||
return fmt.Errorf("invalid ip %q", *ipStr)
|
||||
}
|
||||
|
||||
adm, err := readAdmin(*adminPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("reading admin.json with --admin-path of %q: %w", *adminPath, err)
|
||||
@ -97,19 +92,21 @@ var subCmdAdminCreateBootstrap = subCmd{
|
||||
adm.Nebula.CACredentials,
|
||||
adm.CreationParams,
|
||||
garageBootstrap,
|
||||
*hostName,
|
||||
hostName,
|
||||
ip,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("initializing bootstrap data: %w", err)
|
||||
}
|
||||
|
||||
hostBootstrap, err := loadHostBootstrap()
|
||||
hostsRes, err := subCmdCtx.getHosts()
|
||||
if err != nil {
|
||||
return fmt.Errorf("loading host bootstrap: %w", err)
|
||||
return fmt.Errorf("getting hosts: %w", err)
|
||||
}
|
||||
|
||||
newHostBootstrap.Hosts = hostBootstrap.Hosts
|
||||
for _, host := range hostsRes.Hosts {
|
||||
newHostBootstrap.Hosts[host.Name] = host
|
||||
}
|
||||
|
||||
return newHostBootstrap.WriteTo(os.Stdout)
|
||||
},
|
||||
@ -120,17 +117,20 @@ var subCmdAdminCreateNebulaCert = subCmd{
|
||||
descr: "Creates a signed nebula certificate file and writes it to stdout",
|
||||
checkLock: false,
|
||||
do: func(subCmdCtx subCmdCtx) error {
|
||||
|
||||
flags := subCmdCtx.flagSet(false)
|
||||
|
||||
hostName := flags.StringP(
|
||||
"hostname", "h", "",
|
||||
"Name of the host to generate bootstrap.json for",
|
||||
var (
|
||||
flags = subCmdCtx.flagSet(false)
|
||||
hostName nebula.HostName
|
||||
ip netip.Addr
|
||||
)
|
||||
|
||||
ipStr := flags.StringP(
|
||||
"ip", "i", "",
|
||||
"IP of the new host",
|
||||
hostNameF := flags.VarPF(
|
||||
textUnmarshalerFlag{&hostName},
|
||||
"hostname", "h",
|
||||
"Name of the host to generate a certificate for",
|
||||
)
|
||||
|
||||
ipF := flags.VarPF(
|
||||
textUnmarshalerFlag{&ip}, "ip", "i", "IP of the new host",
|
||||
)
|
||||
|
||||
adminPath := flags.StringP(
|
||||
@ -147,20 +147,13 @@ var subCmdAdminCreateNebulaCert = subCmd{
|
||||
return fmt.Errorf("parsing flags: %w", err)
|
||||
}
|
||||
|
||||
if *hostName == "" || *ipStr == "" || *adminPath == "" || *pubKeyPath == "" {
|
||||
if !hostNameF.Changed ||
|
||||
!ipF.Changed ||
|
||||
*adminPath == "" ||
|
||||
*pubKeyPath == "" {
|
||||
return errors.New("--hostname, --ip, --admin-path, and --pub-key-path are required")
|
||||
}
|
||||
|
||||
if err := validateHostName(*hostName); err != nil {
|
||||
return fmt.Errorf("invalid hostname %q: %w", *hostName, err)
|
||||
}
|
||||
|
||||
ip := net.ParseIP(*ipStr)
|
||||
|
||||
if ip == nil {
|
||||
return fmt.Errorf("invalid ip %q", *ipStr)
|
||||
}
|
||||
|
||||
adm, err := readAdmin(*adminPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("reading admin.json with --admin-path of %q: %w", *adminPath, err)
|
||||
@ -177,7 +170,7 @@ var subCmdAdminCreateNebulaCert = subCmd{
|
||||
}
|
||||
|
||||
nebulaHostCert, err := nebula.NewHostCert(
|
||||
adm.Nebula.CACredentials, hostPub, *hostName, ip,
|
||||
adm.Nebula.CACredentials, hostPub, hostName, ip,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating cert: %w", err)
|
||||
|
15
go/cmd/entrypoint/client.go
Normal file
15
go/cmd/entrypoint/client.go
Normal file
@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"isle/daemon"
|
||||
)
|
||||
|
||||
func (ctx subCmdCtx) getHosts() (daemon.GetHostsResult, error) {
|
||||
var res daemon.GetHostsResult
|
||||
err := ctx.daemonRCPClient.Call(ctx.ctx, &res, "GetHosts", nil)
|
||||
if err != nil {
|
||||
return daemon.GetHostsResult{}, fmt.Errorf("calling GetHosts: %w", err)
|
||||
}
|
||||
return res, nil
|
||||
}
|
22
go/cmd/entrypoint/flags.go
Normal file
22
go/cmd/entrypoint/flags.go
Normal file
@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type textUnmarshalerFlag struct {
|
||||
inner interface {
|
||||
encoding.TextUnmarshaler
|
||||
}
|
||||
}
|
||||
|
||||
func (f textUnmarshalerFlag) Set(v string) error {
|
||||
return f.inner.UnmarshalText([]byte(v))
|
||||
}
|
||||
|
||||
func (f textUnmarshalerFlag) String() string {
|
||||
return fmt.Sprint(f.inner)
|
||||
}
|
||||
|
||||
func (f textUnmarshalerFlag) Type() string { return "string" }
|
@ -4,7 +4,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"isle/bootstrap"
|
||||
"isle/daemon"
|
||||
"isle/jsonutil"
|
||||
"os"
|
||||
"regexp"
|
||||
@ -26,11 +25,7 @@ var subCmdHostsList = subCmd{
|
||||
name: "list",
|
||||
descr: "Lists all hosts in the network, and their IPs",
|
||||
do: func(subCmdCtx subCmdCtx) error {
|
||||
|
||||
ctx := subCmdCtx.ctx
|
||||
|
||||
var res daemon.GetHostsResult
|
||||
err := subCmdCtx.daemonRCPClient.Call(ctx, &res, "GetHosts", nil)
|
||||
hostsRes, err := subCmdCtx.getHosts()
|
||||
if err != nil {
|
||||
return fmt.Errorf("calling GetHosts: %w", err)
|
||||
}
|
||||
@ -43,11 +38,11 @@ var subCmdHostsList = subCmd{
|
||||
Storage bootstrap.GarageHost `json:",omitempty"`
|
||||
}
|
||||
|
||||
hosts := make([]host, 0, len(res.Hosts))
|
||||
for _, h := range res.Hosts {
|
||||
hosts := make([]host, 0, len(hostsRes.Hosts))
|
||||
for _, h := range hostsRes.Hosts {
|
||||
|
||||
host := host{
|
||||
Name: h.Name,
|
||||
Name: string(h.Name),
|
||||
Storage: h.Garage,
|
||||
}
|
||||
|
||||
|
@ -29,16 +29,16 @@ var subCmdNetworkCreate = subCmd{
|
||||
"Domain name that should be used as the root domain in the network.",
|
||||
)
|
||||
|
||||
flags.StringVarP(
|
||||
&req.IPNet, "ip-net", "i", "",
|
||||
`IP+prefix (e.g. "10.10.0.1/16") which denotes the IP of this`+
|
||||
` host, which will be the first host in the network, and the`+
|
||||
` range of IPs which other hosts in the network can be`+
|
||||
` assigned`,
|
||||
ipNetF := flags.VarPF(
|
||||
textUnmarshalerFlag{&req.IPNet}, "ip-net", "i",
|
||||
`An IP subnet, in CIDR form, which will be the overall range of`+
|
||||
` possible IPs in the network. The first IP in this network`+
|
||||
` range will become this first host's IP.`,
|
||||
)
|
||||
|
||||
flags.StringVarP(
|
||||
&req.HostName, "hostname", "h", "",
|
||||
hostNameF := flags.VarPF(
|
||||
textUnmarshalerFlag{&req.HostName},
|
||||
"hostname", "h",
|
||||
"Name of this host, which will be the first host in the network",
|
||||
)
|
||||
|
||||
@ -48,8 +48,8 @@ var subCmdNetworkCreate = subCmd{
|
||||
|
||||
if req.Name == "" ||
|
||||
req.Domain == "" ||
|
||||
req.IPNet == "" ||
|
||||
req.HostName == "" {
|
||||
!ipNetF.Changed ||
|
||||
!hostNameF.Changed {
|
||||
return errors.New("--name, --domain, --ip-net, and --hostname are required")
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@ func dnsmasqPmuxProcConfig(
|
||||
hostsSlice := make([]dnsmasq.ConfDataHost, 0, len(hostBootstrap.Hosts))
|
||||
for _, host := range hostBootstrap.Hosts {
|
||||
hostsSlice = append(hostsSlice, dnsmasq.ConfDataHost{
|
||||
Name: host.Name,
|
||||
Name: string(host.Name),
|
||||
IP: host.IP().String(),
|
||||
})
|
||||
}
|
||||
|
@ -197,7 +197,7 @@ func GarageApplyLayout(
|
||||
|
||||
id := bootstrapGarageHostForAlloc(thisHost, alloc).ID
|
||||
|
||||
zone := hostName
|
||||
zone := string(hostName)
|
||||
if alloc.Zone != "" {
|
||||
zone = alloc.Zone
|
||||
}
|
||||
|
@ -11,29 +11,15 @@ import (
|
||||
"io/fs"
|
||||
"isle/admin"
|
||||
"isle/bootstrap"
|
||||
"isle/daemon/jsonrpc2"
|
||||
"isle/garage"
|
||||
"isle/nebula"
|
||||
"net"
|
||||
"os"
|
||||
"regexp"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"dev.mediocregopher.com/mediocre-go-lib.git/mlog"
|
||||
)
|
||||
|
||||
var hostNameRegexp = regexp.MustCompile(`^[a-z][a-z0-9\-]*$`)
|
||||
|
||||
func validateHostName(name string) error {
|
||||
|
||||
if !hostNameRegexp.MatchString(name) {
|
||||
return errors.New("a host's name must start with a letter and only contain letters, numbers, and dashes")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Daemon presents all functionality required for client frontends to interact
|
||||
// with isle, typically via the unix socket.
|
||||
type Daemon interface {
|
||||
@ -41,15 +27,17 @@ type Daemon interface {
|
||||
// CreateNetwork will initialize a new network using the given parameters.
|
||||
// - name: Human-readable name of the network.
|
||||
// - domain: Primary domain name that network services are served under.
|
||||
// - ipNetStr: An IP + subnet mask which represents both the IP of this
|
||||
// first host in the network, as well as the overall range of possible IPs
|
||||
// in the network.
|
||||
// - ipNet: An IP subnet, in CIDR form, which will be the overall range of
|
||||
// possible IPs in the network. The first IP in this network range will
|
||||
// become this first host's IP.
|
||||
// - hostName: The name of this first host in the network.
|
||||
//
|
||||
// An Admin instance is returned, which is necessary to perform admin
|
||||
// actions in the future.
|
||||
CreateNetwork(
|
||||
ctx context.Context, name, domain, ipNetStr, hostName string,
|
||||
ctx context.Context, name, domain string,
|
||||
ipNet nebula.IPNet,
|
||||
hostName nebula.HostName,
|
||||
) (
|
||||
admin.Admin, error,
|
||||
)
|
||||
@ -61,12 +49,11 @@ type Daemon interface {
|
||||
// - ErrAlreadyJoined
|
||||
JoinNetwork(context.Context, bootstrap.Bootstrap) error
|
||||
|
||||
// GetGarageBootstrapHosts loads (and verifies) the <hostname>.json.signed
|
||||
// file for all hosts stored in garage.
|
||||
GetGarageBootstrapHosts(
|
||||
// GetBootstrapHosts returns the hosts stored in the bootstrap.
|
||||
GetBootstrapHosts(
|
||||
ctx context.Context,
|
||||
) (
|
||||
map[string]bootstrap.Host, error,
|
||||
map[nebula.HostName]bootstrap.Host, error,
|
||||
)
|
||||
|
||||
// Shutdown blocks until all resources held or created by the daemon,
|
||||
@ -149,10 +136,7 @@ type daemon struct {
|
||||
// it should restart itself only when there's something actually requiring a
|
||||
// restart.
|
||||
func NewDaemon(
|
||||
logger *mlog.Logger,
|
||||
daemonConfig Config,
|
||||
envBinDirPath string,
|
||||
opts *Opts,
|
||||
logger *mlog.Logger, daemonConfig Config, envBinDirPath string, opts *Opts,
|
||||
) (
|
||||
Daemon, error,
|
||||
) {
|
||||
@ -479,24 +463,11 @@ func (d *daemon) restartLoop(ctx context.Context, readyCh chan<- struct{}) {
|
||||
|
||||
func (d *daemon) CreateNetwork(
|
||||
ctx context.Context,
|
||||
name, domain, ipNetStr, hostName string,
|
||||
name, domain string, ipNet nebula.IPNet, hostName nebula.HostName,
|
||||
) (
|
||||
admin.Admin, error,
|
||||
) {
|
||||
ip, subnet, err := net.ParseCIDR(ipNetStr)
|
||||
if err != nil {
|
||||
return admin.Admin{}, jsonrpc2.NewInvalidParamsError(
|
||||
"parsing %q as a CIDR: %v", ipNetStr, err,
|
||||
)
|
||||
}
|
||||
|
||||
if err := validateHostName(hostName); err != nil {
|
||||
return admin.Admin{}, jsonrpc2.NewInvalidParamsError(
|
||||
"invalid hostname: %v", err,
|
||||
)
|
||||
}
|
||||
|
||||
nebulaCACreds, err := nebula.NewCACredentials(domain, subnet)
|
||||
nebulaCACreds, err := nebula.NewCACredentials(domain, ipNet)
|
||||
if err != nil {
|
||||
return admin.Admin{}, fmt.Errorf("creating nebula CA cert: %w", err)
|
||||
}
|
||||
@ -519,7 +490,7 @@ func (d *daemon) CreateNetwork(
|
||||
adm.CreationParams,
|
||||
garageBootstrap,
|
||||
hostName,
|
||||
ip,
|
||||
ipNet.FirstAddr(),
|
||||
)
|
||||
if err != nil {
|
||||
return adm, fmt.Errorf("initializing bootstrap data: %w", err)
|
||||
@ -594,17 +565,17 @@ func (d *daemon) JoinNetwork(
|
||||
}
|
||||
}
|
||||
|
||||
func (d *daemon) GetGarageBootstrapHosts(
|
||||
func (d *daemon) GetBootstrapHosts(
|
||||
ctx context.Context,
|
||||
) (
|
||||
map[string]bootstrap.Host, error,
|
||||
map[nebula.HostName]bootstrap.Host, error,
|
||||
) {
|
||||
return withCurrBootstrap(d, func(
|
||||
currBootstrap bootstrap.Bootstrap,
|
||||
) (
|
||||
map[string]bootstrap.Host, error,
|
||||
map[nebula.HostName]bootstrap.Host, error,
|
||||
) {
|
||||
return getGarageBootstrapHosts(ctx, d.logger, currBootstrap)
|
||||
return currBootstrap.Hosts, nil
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -86,7 +86,7 @@ func (d *daemon) putGarageBoostrapHost(ctx context.Context) error {
|
||||
|
||||
filePath := filepath.Join(
|
||||
garageGlobalBucketBootstrapHostsDirPath,
|
||||
host.Name+".json.signed",
|
||||
string(host.Name)+".json.signed",
|
||||
)
|
||||
|
||||
_, err = client.PutObject(
|
||||
@ -108,12 +108,12 @@ func (d *daemon) putGarageBoostrapHost(ctx context.Context) error {
|
||||
func getGarageBootstrapHosts(
|
||||
ctx context.Context, logger *mlog.Logger, currBootstrap bootstrap.Bootstrap,
|
||||
) (
|
||||
map[string]bootstrap.Host, error,
|
||||
map[nebula.HostName]bootstrap.Host, error,
|
||||
) {
|
||||
var (
|
||||
b = currBootstrap
|
||||
client = b.GlobalBucketS3APIClient()
|
||||
hosts = map[string]bootstrap.Host{}
|
||||
hosts = map[nebula.HostName]bootstrap.Host{}
|
||||
|
||||
objInfoCh = client.ListObjects(
|
||||
ctx, garage.GlobalBucket,
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"isle/admin"
|
||||
"isle/bootstrap"
|
||||
"isle/nebula"
|
||||
"slices"
|
||||
|
||||
"golang.org/x/exp/maps"
|
||||
@ -32,12 +33,13 @@ type CreateNetworkRequest struct {
|
||||
// Primary domain name that network services are served under.
|
||||
Domain string
|
||||
|
||||
// An IP + subnet mask which represents both the IP of this first host in
|
||||
// the network, as well as the overall range of possible IPs in the network.
|
||||
IPNet string
|
||||
// An IP subnet, in CIDR form, which will be the overall range of possible
|
||||
// IPs in the network. The first IP in this network range will become this
|
||||
// first host's IP.
|
||||
IPNet nebula.IPNet
|
||||
|
||||
// The name of this first host in the network.
|
||||
HostName string
|
||||
HostName nebula.HostName
|
||||
}
|
||||
|
||||
// CreateNetwork passes through to the Daemon method of the same name.
|
||||
@ -71,7 +73,7 @@ func (r *RPC) GetHosts(
|
||||
) (
|
||||
GetHostsResult, error,
|
||||
) {
|
||||
hostsMap, err := r.daemon.GetGarageBootstrapHosts(ctx)
|
||||
hostsMap, err := r.daemon.GetBootstrapHosts(ctx)
|
||||
if err != nil {
|
||||
return GetHostsResult{}, fmt.Errorf("retrieving hosts: %w", err)
|
||||
}
|
||||
|
22
go/nebula/hostname.go
Normal file
22
go/nebula/hostname.go
Normal file
@ -0,0 +1,22 @@
|
||||
package nebula
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
var hostNameRegexp = regexp.MustCompile(`^[a-z][a-z0-9\-]*$`)
|
||||
|
||||
// HostName is the name of a host in a network. HostNames may only have
|
||||
// lowercase letters, numbers, and hyphens, and must start with a letter.
|
||||
type HostName string
|
||||
|
||||
// UnmarshalText parses and validates a HostName from a text string.
|
||||
func (h *HostName) UnmarshalText(b []byte) error {
|
||||
if !hostNameRegexp.Match(b) {
|
||||
return errors.New("a host's name must start with a letter and only contain letters, numbers, and dashes")
|
||||
}
|
||||
|
||||
*h = HostName(b)
|
||||
return nil
|
||||
}
|
43
go/nebula/ip_net.go
Normal file
43
go/nebula/ip_net.go
Normal file
@ -0,0 +1,43 @@
|
||||
package nebula
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
)
|
||||
|
||||
// IPNet is the CIDR of a nebula network.
|
||||
type IPNet net.IPNet
|
||||
|
||||
// UnmarshalText parses and validates an IPNet from a text string.
|
||||
func (n *IPNet) UnmarshalText(b []byte) error {
|
||||
str := string(b)
|
||||
_, subnet, err := net.ParseCIDR(str)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if cstr := subnet.String(); cstr != str {
|
||||
return fmt.Errorf("IPNet is not given in its canonical form of %q", cstr)
|
||||
}
|
||||
|
||||
if !subnet.IP.IsPrivate() {
|
||||
return fmt.Errorf("IPNet is not in a private IP range")
|
||||
}
|
||||
|
||||
*n = IPNet(*subnet)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n IPNet) String() string {
|
||||
return (*net.IPNet)(&n).String()
|
||||
}
|
||||
|
||||
func (n IPNet) MarshalText() ([]byte, error) {
|
||||
return []byte(n.String()), nil
|
||||
}
|
||||
|
||||
// FirstAddr returns the first IP address in the subnet.
|
||||
func (n IPNet) FirstAddr() netip.Addr {
|
||||
return netip.MustParseAddr(n.IP.String()).Next()
|
||||
}
|
@ -5,6 +5,7 @@ package nebula
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"time"
|
||||
|
||||
"github.com/slackhq/nebula/cert"
|
||||
@ -44,12 +45,15 @@ type CACredentials struct {
|
||||
func NewHostCert(
|
||||
caCreds CACredentials,
|
||||
hostPub EncryptingPublicKey,
|
||||
hostName string,
|
||||
ip net.IP,
|
||||
hostName HostName,
|
||||
ip netip.Addr,
|
||||
) (
|
||||
Certificate, error,
|
||||
) {
|
||||
caCert := caCreds.Public.Cert
|
||||
var (
|
||||
caCert = caCreds.Public.Cert
|
||||
ipSlice = net.IP(ip.AsSlice())
|
||||
)
|
||||
|
||||
issuer, err := caCert.inner.Sha256Sum()
|
||||
if err != nil {
|
||||
@ -59,15 +63,15 @@ func NewHostCert(
|
||||
expireAt := caCert.inner.Details.NotAfter.Add(-1 * time.Second)
|
||||
|
||||
subnet := caCert.inner.Details.Subnets[0]
|
||||
if !subnet.Contains(ip) {
|
||||
if !subnet.Contains(ipSlice) {
|
||||
return Certificate{}, fmt.Errorf("invalid ip %q, not contained by network subnet %q", ip, subnet)
|
||||
}
|
||||
|
||||
hostCert := cert.NebulaCertificate{
|
||||
Details: cert.NebulaCertificateDetails{
|
||||
Name: hostName,
|
||||
Name: string(hostName),
|
||||
Ips: []*net.IPNet{{
|
||||
IP: ip,
|
||||
IP: ipSlice,
|
||||
Mask: subnet.Mask,
|
||||
}},
|
||||
NotBefore: time.Now(),
|
||||
@ -92,7 +96,7 @@ func NewHostCert(
|
||||
// NewHostCredentials generates a new key/cert for a nebula host using the CA
|
||||
// key which will be found in the adminFS.
|
||||
func NewHostCredentials(
|
||||
caCreds CACredentials, hostName string, ip net.IP,
|
||||
caCreds CACredentials, hostName HostName, ip netip.Addr,
|
||||
) (
|
||||
pub HostPublicCredentials, priv HostPrivateCredentials, err error,
|
||||
) {
|
||||
@ -125,7 +129,7 @@ func NewHostCredentials(
|
||||
|
||||
// NewCACredentials generates a CACredentials. The domain should be the network's root domain,
|
||||
// and is included in the signing certificate's Name field.
|
||||
func NewCACredentials(domain string, subnet *net.IPNet) (CACredentials, error) {
|
||||
func NewCACredentials(domain string, subnet IPNet) (CACredentials, error) {
|
||||
var (
|
||||
signingPubKey, signingPrivKey = GenerateSigningPair()
|
||||
now = time.Now()
|
||||
@ -135,7 +139,7 @@ func NewCACredentials(domain string, subnet *net.IPNet) (CACredentials, error) {
|
||||
caCert := cert.NebulaCertificate{
|
||||
Details: cert.NebulaCertificateDetails{
|
||||
Name: fmt.Sprintf("%s isle root cert", domain),
|
||||
Subnets: []*net.IPNet{subnet},
|
||||
Subnets: []*net.IPNet{(*net.IPNet)(&subnet)},
|
||||
NotBefore: now,
|
||||
NotAfter: expireAt,
|
||||
PublicKey: signingPubKey,
|
||||
|
@ -1,22 +1,20 @@
|
||||
package nebula
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
import "net/netip"
|
||||
|
||||
var (
|
||||
ip net.IP
|
||||
ipNet *net.IPNet
|
||||
ip netip.Addr
|
||||
ipNet IPNet
|
||||
caCredsA, caCredsB CACredentials
|
||||
hostPubCredsA, hostPubCredsB HostPublicCredentials
|
||||
hostPrivCredsA, hostPrivCredsB HostPrivateCredentials
|
||||
)
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
ip = netip.MustParseAddr("192.168.0.1")
|
||||
|
||||
ip, ipNet, err = net.ParseCIDR("192.168.0.1/24")
|
||||
if err != nil {
|
||||
var err error
|
||||
if err := ipNet.UnmarshalText([]byte("192.168.0.0/24")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,9 @@
|
||||
# shellcheck source=../../utils/with-1-data-1-empty-node-network.sh
|
||||
source "$UTILS"/with-1-data-1-empty-node-network.sh
|
||||
|
||||
as_primus
|
||||
# TODO when primus creates secondus' bootstrap it should write the new host to
|
||||
# its own bootstrap, as well as to garage.
|
||||
as_secondus
|
||||
hosts="$(isle hosts list)"
|
||||
|
||||
[ "$(echo "$hosts" | jq -r '.[0].Name')" = "primus" ]
|
||||
|
@ -2,6 +2,8 @@ set -e
|
||||
|
||||
base="shared/1-data-1-empty"
|
||||
|
||||
ipNet="10.6.9.0/24"
|
||||
|
||||
primus_base="$base/primus"
|
||||
primus_ip="10.6.9.1"
|
||||
|
||||
@ -60,7 +62,7 @@ EOF
|
||||
isle network create \
|
||||
--domain shared.test \
|
||||
--hostname primus \
|
||||
--ip-net "$current_ip/24" \
|
||||
--ip-net "$ipNet" \
|
||||
--name "testing" \
|
||||
> admin.json
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user