From 736b23429c02d5fd660994883d13b68658d9915a Mon Sep 17 00:00:00 2001 From: Brian Picciano Date: Fri, 12 Jul 2024 15:30:21 +0200 Subject: [PATCH] Do proper type-based validation or hostnames and ipnets --- docs/admin/creating-a-new-network.md | 7 +- go/bootstrap/bootstrap.go | 12 +-- go/bootstrap/garage_global_bucket.go | 5 +- go/bootstrap/hosts.go | 2 +- go/cmd/entrypoint/admin.go | 73 ++++++++----------- go/cmd/entrypoint/flags.go | 22 ++++++ go/cmd/entrypoint/hosts.go | 2 +- go/cmd/entrypoint/network.go | 20 ++--- go/daemon/child_dnsmasq.go | 2 +- go/daemon/child_garage.go | 2 +- go/daemon/daemon.go | 54 ++++---------- go/daemon/global_bucket.go | 6 +- go/daemon/rpc.go | 10 ++- go/nebula/hostname.go | 22 ++++++ go/nebula/ip_net.go | 43 +++++++++++ go/nebula/nebula.go | 22 +++--- go/nebula/nebula_test.go | 14 ++-- .../utils/with-1-data-1-empty-node-network.sh | 4 +- 18 files changed, 187 insertions(+), 135 deletions(-) create mode 100644 go/cmd/entrypoint/flags.go create mode 100644 go/nebula/hostname.go create mode 100644 go/nebula/ip_net.go diff --git a/docs/admin/creating-a-new-network.md b/docs/admin/creating-a-new-network.md index ae69023..d0f7db0 100644 --- a/docs/admin/creating-a-new-network.md +++ b/docs/admin/creating-a-new-network.md @@ -103,7 +103,7 @@ To create the network, and the `admin.json` file in the process, run: ``` sudo isle network create \ --name \ - --ip-net \ + --ip-net \ --domain \ --hostname \ | gpg -e -r \ @@ -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 diff --git a/go/bootstrap/bootstrap.go b/go/bootstrap/bootstrap.go index c808ce6..9b87595 100644 --- a/go/bootstrap/bootstrap.go +++ b/go/bootstrap/bootstrap.go @@ -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 { diff --git a/go/bootstrap/garage_global_bucket.go b/go/bootstrap/garage_global_bucket.go index ec1b344..4cd5b30 100644 --- a/go/bootstrap/garage_global_bucket.go +++ b/go/bootstrap/garage_global_bucket.go @@ -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, diff --git a/go/bootstrap/hosts.go b/go/bootstrap/hosts.go index 97e024f..f50435f 100644 --- a/go/bootstrap/hosts.go +++ b/go/bootstrap/hosts.go @@ -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 } diff --git a/go/cmd/entrypoint/admin.go b/go/cmd/entrypoint/admin.go index 7484e1f..4342b3a 100644 --- a/go/cmd/entrypoint/admin.go +++ b/go/cmd/entrypoint/admin.go @@ -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,7 +92,7 @@ var subCmdAdminCreateBootstrap = subCmd{ adm.Nebula.CACredentials, adm.CreationParams, garageBootstrap, - *hostName, + hostName, ip, ) if err != nil { @@ -122,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( @@ -149,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) @@ -179,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) diff --git a/go/cmd/entrypoint/flags.go b/go/cmd/entrypoint/flags.go new file mode 100644 index 0000000..51d337a --- /dev/null +++ b/go/cmd/entrypoint/flags.go @@ -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" } diff --git a/go/cmd/entrypoint/hosts.go b/go/cmd/entrypoint/hosts.go index 367bdee..d3183b7 100644 --- a/go/cmd/entrypoint/hosts.go +++ b/go/cmd/entrypoint/hosts.go @@ -42,7 +42,7 @@ var subCmdHostsList = subCmd{ for _, h := range hostsRes.Hosts { host := host{ - Name: h.Name, + Name: string(h.Name), Storage: h.Garage, } diff --git a/go/cmd/entrypoint/network.go b/go/cmd/entrypoint/network.go index ba4461e..d31d87f 100644 --- a/go/cmd/entrypoint/network.go +++ b/go/cmd/entrypoint/network.go @@ -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") } diff --git a/go/daemon/child_dnsmasq.go b/go/daemon/child_dnsmasq.go index 766c2bf..c039dbb 100644 --- a/go/daemon/child_dnsmasq.go +++ b/go/daemon/child_dnsmasq.go @@ -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(), }) } diff --git a/go/daemon/child_garage.go b/go/daemon/child_garage.go index 5ce4f70..ba28c84 100644 --- a/go/daemon/child_garage.go +++ b/go/daemon/child_garage.go @@ -197,7 +197,7 @@ func GarageApplyLayout( id := bootstrapGarageHostForAlloc(thisHost, alloc).ID - zone := hostName + zone := string(hostName) if alloc.Zone != "" { zone = alloc.Zone } diff --git a/go/daemon/daemon.go b/go/daemon/daemon.go index d6cc377..8c10de8 100644 --- a/go/daemon/daemon.go +++ b/go/daemon/daemon.go @@ -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, ) @@ -65,7 +53,7 @@ type Daemon interface { 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, @@ -148,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, ) { @@ -478,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) } @@ -518,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) @@ -596,12 +568,12 @@ func (d *daemon) JoinNetwork( 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 currBootstrap.Hosts, nil }) diff --git a/go/daemon/global_bucket.go b/go/daemon/global_bucket.go index 6928d30..da11636 100644 --- a/go/daemon/global_bucket.go +++ b/go/daemon/global_bucket.go @@ -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, diff --git a/go/daemon/rpc.go b/go/daemon/rpc.go index a2aa016..a1bf1cf 100644 --- a/go/daemon/rpc.go +++ b/go/daemon/rpc.go @@ -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. diff --git a/go/nebula/hostname.go b/go/nebula/hostname.go new file mode 100644 index 0000000..8b87dc9 --- /dev/null +++ b/go/nebula/hostname.go @@ -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 +} diff --git a/go/nebula/ip_net.go b/go/nebula/ip_net.go new file mode 100644 index 0000000..4bf35cb --- /dev/null +++ b/go/nebula/ip_net.go @@ -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() +} diff --git a/go/nebula/nebula.go b/go/nebula/nebula.go index 33a0027..9654e53 100644 --- a/go/nebula/nebula.go +++ b/go/nebula/nebula.go @@ -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, diff --git a/go/nebula/nebula_test.go b/go/nebula/nebula_test.go index c99373c..c492bcd 100644 --- a/go/nebula/nebula_test.go +++ b/go/nebula/nebula_test.go @@ -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) } diff --git a/tests/utils/with-1-data-1-empty-node-network.sh b/tests/utils/with-1-data-1-empty-node-network.sh index 7ddf95b..00796dd 100644 --- a/tests/utils/with-1-data-1-empty-node-network.sh +++ b/tests/utils/with-1-data-1-empty-node-network.sh @@ -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