2022-10-20 19:59:46 +00:00
|
|
|
package main
|
2022-10-16 15:18:50 +00:00
|
|
|
|
|
|
|
import (
|
2022-10-16 19:22:58 +00:00
|
|
|
"crypto/rand"
|
|
|
|
"encoding/hex"
|
2022-10-16 15:18:50 +00:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
2023-08-05 21:53:17 +00:00
|
|
|
"isle/admin"
|
|
|
|
"isle/bootstrap"
|
|
|
|
"isle/nebula"
|
2024-07-12 13:30:21 +00:00
|
|
|
"net/netip"
|
2022-10-16 15:18:50 +00:00
|
|
|
"os"
|
|
|
|
)
|
|
|
|
|
2022-10-16 19:22:58 +00:00
|
|
|
func randStr(l int) string {
|
|
|
|
b := make([]byte, l)
|
|
|
|
if _, err := rand.Read(b); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return hex.EncodeToString(b)
|
|
|
|
}
|
|
|
|
|
2022-10-16 15:18:50 +00:00
|
|
|
func readAdmin(path string) (admin.Admin, error) {
|
|
|
|
|
|
|
|
if path == "-" {
|
|
|
|
|
|
|
|
adm, err := admin.FromReader(os.Stdin)
|
|
|
|
if err != nil {
|
2024-06-10 16:56:36 +00:00
|
|
|
return admin.Admin{}, fmt.Errorf("parsing admin.json from stdin: %w", err)
|
2022-10-16 15:18:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return adm, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
f, err := os.Open(path)
|
|
|
|
if err != nil {
|
|
|
|
return admin.Admin{}, fmt.Errorf("opening file: %w", err)
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
return admin.FromReader(f)
|
|
|
|
}
|
|
|
|
|
2022-11-05 15:41:14 +00:00
|
|
|
var subCmdAdminCreateBootstrap = subCmd{
|
2024-07-12 14:13:44 +00:00
|
|
|
name: "create-bootstrap",
|
|
|
|
descr: "Creates a new bootstrap.json file for a particular host and writes it to stdout",
|
2022-10-16 15:18:50 +00:00
|
|
|
do: func(subCmdCtx subCmdCtx) error {
|
2024-07-12 13:30:21 +00:00
|
|
|
var (
|
|
|
|
flags = subCmdCtx.flagSet(false)
|
|
|
|
hostName nebula.HostName
|
|
|
|
ip netip.Addr
|
|
|
|
)
|
2022-10-16 15:18:50 +00:00
|
|
|
|
2024-07-12 13:30:21 +00:00
|
|
|
hostNameF := flags.VarPF(
|
|
|
|
textUnmarshalerFlag{&hostName},
|
|
|
|
"hostname", "h",
|
2024-06-10 16:56:36 +00:00
|
|
|
"Name of the host to generate bootstrap.json for",
|
2022-10-16 15:18:50 +00:00
|
|
|
)
|
|
|
|
|
2024-07-12 13:30:21 +00:00
|
|
|
ipF := flags.VarPF(
|
|
|
|
textUnmarshalerFlag{&ip}, "ip", "i", "IP of the new host",
|
2022-10-29 19:11:40 +00:00
|
|
|
)
|
|
|
|
|
2022-10-16 15:18:50 +00:00
|
|
|
adminPath := flags.StringP(
|
|
|
|
"admin-path", "a", "",
|
2024-06-10 16:56:36 +00:00
|
|
|
`Path to admin.json file. If the given path is "-" then stdin is used.`,
|
2022-10-16 15:18:50 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
if err := flags.Parse(subCmdCtx.args); err != nil {
|
|
|
|
return fmt.Errorf("parsing flags: %w", err)
|
|
|
|
}
|
|
|
|
|
2024-07-12 13:30:21 +00:00
|
|
|
if !hostNameF.Changed ||
|
|
|
|
!ipF.Changed ||
|
|
|
|
*adminPath == "" {
|
2022-11-05 11:34:49 +00:00
|
|
|
return errors.New("--hostname, --ip, and --admin-path are required")
|
2022-10-16 15:18:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
adm, err := readAdmin(*adminPath)
|
|
|
|
if err != nil {
|
2024-06-10 16:56:36 +00:00
|
|
|
return fmt.Errorf("reading admin.json with --admin-path of %q: %w", *adminPath, err)
|
2022-10-16 15:18:50 +00:00
|
|
|
}
|
|
|
|
|
2024-06-10 20:31:29 +00:00
|
|
|
garageBootstrap := bootstrap.Garage{
|
|
|
|
RPCSecret: adm.Garage.RPCSecret,
|
|
|
|
AdminToken: randStr(32),
|
|
|
|
GlobalBucketS3APICredentials: adm.Garage.GlobalBucketS3APICredentials,
|
2022-10-16 15:18:50 +00:00
|
|
|
}
|
|
|
|
|
2024-06-10 20:31:29 +00:00
|
|
|
newHostBootstrap, err := bootstrap.New(
|
2022-11-05 14:23:29 +00:00
|
|
|
adm.Nebula.CACredentials,
|
2024-06-10 20:31:29 +00:00
|
|
|
adm.CreationParams,
|
|
|
|
garageBootstrap,
|
2024-07-12 13:30:21 +00:00
|
|
|
hostName,
|
2024-06-10 20:31:29 +00:00
|
|
|
ip,
|
2022-11-05 14:23:29 +00:00
|
|
|
)
|
|
|
|
if err != nil {
|
2024-06-10 20:31:29 +00:00
|
|
|
return fmt.Errorf("initializing bootstrap data: %w", err)
|
2022-11-05 14:23:29 +00:00
|
|
|
}
|
|
|
|
|
2024-07-09 13:14:29 +00:00
|
|
|
hostsRes, err := subCmdCtx.getHosts()
|
2024-06-10 20:31:29 +00:00
|
|
|
if err != nil {
|
2024-07-09 13:14:29 +00:00
|
|
|
return fmt.Errorf("getting hosts: %w", err)
|
2022-10-16 15:18:50 +00:00
|
|
|
}
|
|
|
|
|
2024-07-09 13:14:29 +00:00
|
|
|
for _, host := range hostsRes.Hosts {
|
|
|
|
newHostBootstrap.Hosts[host.Name] = host
|
|
|
|
}
|
2022-11-02 13:34:40 +00:00
|
|
|
|
2022-10-26 22:23:39 +00:00
|
|
|
return newHostBootstrap.WriteTo(os.Stdout)
|
2022-10-16 15:18:50 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2023-08-27 14:09:03 +00:00
|
|
|
var subCmdAdminCreateNebulaCert = subCmd{
|
2024-07-12 14:13:44 +00:00
|
|
|
name: "create-nebula-cert",
|
|
|
|
descr: "Creates a signed nebula certificate file and writes it to stdout",
|
2023-08-27 14:09:03 +00:00
|
|
|
do: func(subCmdCtx subCmdCtx) error {
|
2024-07-12 13:30:21 +00:00
|
|
|
var (
|
|
|
|
flags = subCmdCtx.flagSet(false)
|
|
|
|
hostName nebula.HostName
|
|
|
|
ip netip.Addr
|
|
|
|
)
|
2023-08-27 14:09:03 +00:00
|
|
|
|
2024-07-12 13:30:21 +00:00
|
|
|
hostNameF := flags.VarPF(
|
|
|
|
textUnmarshalerFlag{&hostName},
|
|
|
|
"hostname", "h",
|
|
|
|
"Name of the host to generate a certificate for",
|
2023-08-27 14:09:03 +00:00
|
|
|
)
|
|
|
|
|
2024-07-12 13:30:21 +00:00
|
|
|
ipF := flags.VarPF(
|
|
|
|
textUnmarshalerFlag{&ip}, "ip", "i", "IP of the new host",
|
2023-08-27 14:09:03 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
adminPath := flags.StringP(
|
|
|
|
"admin-path", "a", "",
|
2024-06-10 16:56:36 +00:00
|
|
|
`Path to admin.json file. If the given path is "-" then stdin is used.`,
|
2023-08-27 14:09:03 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
pubKeyPath := flags.StringP(
|
|
|
|
"public-key-path", "p", "",
|
|
|
|
`Path to PEM file containing public key which will be embedded in the cert.`,
|
|
|
|
)
|
|
|
|
|
|
|
|
if err := flags.Parse(subCmdCtx.args); err != nil {
|
|
|
|
return fmt.Errorf("parsing flags: %w", err)
|
|
|
|
}
|
|
|
|
|
2024-07-12 13:30:21 +00:00
|
|
|
if !hostNameF.Changed ||
|
|
|
|
!ipF.Changed ||
|
|
|
|
*adminPath == "" ||
|
|
|
|
*pubKeyPath == "" {
|
2023-08-27 14:09:03 +00:00
|
|
|
return errors.New("--hostname, --ip, --admin-path, and --pub-key-path are required")
|
|
|
|
}
|
|
|
|
|
|
|
|
adm, err := readAdmin(*adminPath)
|
|
|
|
if err != nil {
|
2024-06-10 16:56:36 +00:00
|
|
|
return fmt.Errorf("reading admin.json with --admin-path of %q: %w", *adminPath, err)
|
2023-08-27 14:09:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
hostPubPEM, err := os.ReadFile(*pubKeyPath)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("reading public key from %q: %w", *pubKeyPath, err)
|
|
|
|
}
|
|
|
|
|
2024-06-15 21:02:24 +00:00
|
|
|
var hostPub nebula.EncryptingPublicKey
|
|
|
|
if err := hostPub.UnmarshalNebulaPEM(hostPubPEM); err != nil {
|
|
|
|
return fmt.Errorf("unmarshaling public key as PEM: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
nebulaHostCert, err := nebula.NewHostCert(
|
2024-07-12 13:30:21 +00:00
|
|
|
adm.Nebula.CACredentials, hostPub, hostName, ip,
|
2023-08-27 14:09:03 +00:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("creating cert: %w", err)
|
|
|
|
}
|
|
|
|
|
2024-06-23 12:37:10 +00:00
|
|
|
nebulaHostCertPEM, err := nebulaHostCert.Unwrap().MarshalToPEM()
|
2024-06-15 21:02:24 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("marshaling cert to PEM: %w", err)
|
|
|
|
}
|
|
|
|
|
2023-08-27 14:09:03 +00:00
|
|
|
if _, err := os.Stdout.Write([]byte(nebulaHostCertPEM)); err != nil {
|
|
|
|
return fmt.Errorf("writing to stdout: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2022-10-16 15:18:50 +00:00
|
|
|
var subCmdAdmin = subCmd{
|
|
|
|
name: "admin",
|
|
|
|
descr: "Sub-commands which only admins can run",
|
|
|
|
do: func(subCmdCtx subCmdCtx) error {
|
|
|
|
return subCmdCtx.doSubCmd(
|
2022-11-05 15:41:14 +00:00
|
|
|
subCmdAdminCreateBootstrap,
|
2023-08-27 14:09:03 +00:00
|
|
|
subCmdAdminCreateNebulaCert,
|
2022-10-16 15:18:50 +00:00
|
|
|
)
|
|
|
|
},
|
|
|
|
}
|