Implement RPC socket and use it to list hosts
This commit is contained in:
parent
47e53dffb7
commit
c3609252a5
@ -69,7 +69,7 @@ in rec {
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
vendorHash = "sha256-heXFvEea3u3zvdUvVxlI+FTxqEeImoXd1sqdJJTASoc=";
|
vendorHash = "sha256-fBCwaZLusixNnD1QG0XtDe/NpNvonI7wBhqWbRrLFAg=";
|
||||||
|
|
||||||
subPackages = [
|
subPackages = [
|
||||||
"./cmd/entrypoint"
|
"./cmd/entrypoint"
|
||||||
|
@ -78,5 +78,5 @@ type Host struct {
|
|||||||
// This assumes that the Host and its data has already been verified against the
|
// This assumes that the Host and its data has already been verified against the
|
||||||
// CA signing key.
|
// CA signing key.
|
||||||
func (h Host) IP() net.IP {
|
func (h Host) IP() net.IP {
|
||||||
return h.PublicCredentials.Cert.Details.Ips[0].IP
|
return h.PublicCredentials.Cert.Unwrap().Details.Ips[0].IP
|
||||||
}
|
}
|
||||||
|
@ -372,7 +372,7 @@ var subCmdAdminCreateNebulaCert = subCmd{
|
|||||||
return fmt.Errorf("creating cert: %w", err)
|
return fmt.Errorf("creating cert: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
nebulaHostCertPEM, err := nebulaHostCert.MarshalToPEM()
|
nebulaHostCertPEM, err := nebulaHostCert.Unwrap().MarshalToPEM()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("marshaling cert to PEM: %w", err)
|
return fmt.Errorf("marshaling cert to PEM: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -115,6 +115,23 @@ func runDaemonPmuxOnce(
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
{
|
||||||
|
logger := logger.WithNamespace("http")
|
||||||
|
httpSrv, err := newHTTPServer(
|
||||||
|
ctx, logger, daemon.NewRPC(daemonInst),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return bootstrap.Bootstrap{}, fmt.Errorf("starting HTTP server: %w", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
// see comment in daemonInst shutdown logic regarding background
|
||||||
|
// context.
|
||||||
|
if err := httpSrv.Shutdown(context.Background()); err != nil {
|
||||||
|
logger.Error(ctx, "Failed to cleanly shutdown http server", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
ticker := time.NewTicker(3 * time.Minute)
|
ticker := time.NewTicker(3 * time.Minute)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
@ -1,10 +1,18 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"isle/bootstrap"
|
"isle/bootstrap"
|
||||||
"isle/daemon"
|
"isle/daemon"
|
||||||
|
"isle/daemon/jsonrpc2"
|
||||||
"isle/garage/garagesrv"
|
"isle/garage/garagesrv"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"dev.mediocregopher.com/mediocre-go-lib.git/mctx"
|
||||||
|
"dev.mediocregopher.com/mediocre-go-lib.git/mlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
func coalesceDaemonConfigAndBootstrap(
|
func coalesceDaemonConfigAndBootstrap(
|
||||||
@ -50,3 +58,41 @@ func coalesceDaemonConfigAndBootstrap(
|
|||||||
|
|
||||||
return hostBootstrap, nil
|
return hostBootstrap, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const daemonHTTPRPCPath = "/rpc/v0.json"
|
||||||
|
|
||||||
|
func newHTTPServer(
|
||||||
|
ctx context.Context, logger *mlog.Logger, rpc *daemon.RPC,
|
||||||
|
) (
|
||||||
|
*http.Server, error,
|
||||||
|
) {
|
||||||
|
l, err := net.Listen("unix", envSocketPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"failed to listen on socket %q: %w", envSocketPath, err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = mctx.Annotate(ctx, "httpAddr", l.Addr().String())
|
||||||
|
logger.Info(ctx, "HTTP server socket created")
|
||||||
|
|
||||||
|
rpcHandler := jsonrpc2.Chain(
|
||||||
|
jsonrpc2.NewMLogMiddleware(logger.WithNamespace("rpc")),
|
||||||
|
jsonrpc2.ExposeServerSideErrorsMiddleware,
|
||||||
|
)(
|
||||||
|
jsonrpc2.NewDispatchHandler(&rpc),
|
||||||
|
)
|
||||||
|
|
||||||
|
httpMux := http.NewServeMux()
|
||||||
|
httpMux.Handle(daemonHTTPRPCPath, jsonrpc2.NewHTTPHandler(rpcHandler))
|
||||||
|
|
||||||
|
srv := &http.Server{Handler: httpMux}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
if err := srv.Serve(l); !errors.Is(err, http.ErrServerClosed) {
|
||||||
|
logger.Fatal(ctx, "HTTP server unexpectedly shut down", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return srv, nil
|
||||||
|
}
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"isle/bootstrap"
|
"isle/bootstrap"
|
||||||
|
"isle/daemon"
|
||||||
"isle/jsonutil"
|
"isle/jsonutil"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"dev.mediocregopher.com/mediocre-go-lib.git/mlog"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var hostNameRegexp = regexp.MustCompile(`^[a-z][a-z0-9\-]*$`)
|
var hostNameRegexp = regexp.MustCompile(`^[a-z][a-z0-9\-]*$`)
|
||||||
@ -24,39 +24,21 @@ func validateHostName(name string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var subCmdHostsList = subCmd{
|
var subCmdHostsList = subCmd{
|
||||||
name: "list",
|
name: "list",
|
||||||
descr: "Lists all hosts in the network, and their IPs",
|
descr: "Lists all hosts in the network, and their IPs",
|
||||||
checkLock: true,
|
|
||||||
do: func(subCmdCtx subCmdCtx) error {
|
do: func(subCmdCtx subCmdCtx) error {
|
||||||
|
|
||||||
flags := subCmdCtx.flagSet(false)
|
|
||||||
|
|
||||||
logLevelStr := flags.StringP(
|
|
||||||
"log-level", "l", "info",
|
|
||||||
`Maximum log level which should be output. Values can be "debug", "info", "warn", "error", "fatal". Does not apply to sub-processes`,
|
|
||||||
)
|
|
||||||
|
|
||||||
if err := flags.Parse(subCmdCtx.args); err != nil {
|
|
||||||
return fmt.Errorf("parsing flags: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := subCmdCtx.ctx
|
ctx := subCmdCtx.ctx
|
||||||
|
|
||||||
logLevel := mlog.LevelFromString(*logLevelStr)
|
var resRaw json.RawMessage
|
||||||
if logLevel == nil {
|
err := subCmdCtx.daemonRCPClient.Call(ctx, &resRaw, "GetHosts", nil)
|
||||||
return fmt.Errorf("couldn't parse log level %q", *logLevelStr)
|
if err != nil {
|
||||||
|
return fmt.Errorf("calling GetHosts: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
logger := subCmdCtx.logger.WithMaxLevel(logLevel.Int())
|
var res daemon.GetHostsResult
|
||||||
|
if err := json.Unmarshal(resRaw, &res); err != nil {
|
||||||
hostBootstrap, err := loadHostBootstrap()
|
return fmt.Errorf("unmarshaling %s into %T: %w", string(resRaw), res, err)
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("loading host bootstrap: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
hostsMap, err := hostBootstrap.GetGarageBootstrapHosts(ctx, logger)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("retrieving hosts from garage: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type host struct {
|
type host struct {
|
||||||
@ -67,8 +49,8 @@ var subCmdHostsList = subCmd{
|
|||||||
Storage bootstrap.GarageHost `json:",omitempty"`
|
Storage bootstrap.GarageHost `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
hosts := make([]host, 0, len(hostsMap))
|
hosts := make([]host, 0, len(res.Hosts))
|
||||||
for _, h := range hostsMap {
|
for _, h := range res.Hosts {
|
||||||
|
|
||||||
host := host{
|
host := host{
|
||||||
Name: h.Name,
|
Name: h.Name,
|
||||||
|
@ -8,9 +8,9 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/adrg/xdg"
|
|
||||||
"dev.mediocregopher.com/mediocre-go-lib.git/mctx"
|
"dev.mediocregopher.com/mediocre-go-lib.git/mctx"
|
||||||
"dev.mediocregopher.com/mediocre-go-lib.git/mlog"
|
"dev.mediocregopher.com/mediocre-go-lib.git/mlog"
|
||||||
|
"github.com/adrg/xdg"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The purpose of this binary is to act as the entrypoint of the isle
|
// The purpose of this binary is to act as the entrypoint of the isle
|
||||||
|
@ -21,7 +21,7 @@ var subCmdNebulaShow = subCmd{
|
|||||||
return fmt.Errorf("loading host bootstrap: %w", err)
|
return fmt.Errorf("loading host bootstrap: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
caCert := hostBootstrap.CAPublicCredentials.Cert
|
caCert := hostBootstrap.CAPublicCredentials.Cert.Unwrap()
|
||||||
caCertPEM, err := caCert.MarshalToPEM()
|
caCertPEM, err := caCert.MarshalToPEM()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("marshaling CA cert to PEM: %w", err)
|
return fmt.Errorf("marshaling CA cert to PEM: %w", err)
|
||||||
|
@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"isle/daemon/jsonrpc2"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -16,8 +17,9 @@ type subCmdCtx struct {
|
|||||||
args []string // command-line arguments, excluding the subCmd itself.
|
args []string // command-line arguments, excluding the subCmd itself.
|
||||||
subCmdNames []string // names of subCmds so far, including this one
|
subCmdNames []string // names of subCmds so far, including this one
|
||||||
|
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
logger *mlog.Logger
|
logger *mlog.Logger
|
||||||
|
daemonRCPClient jsonrpc2.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
type subCmd struct {
|
type subCmd struct {
|
||||||
@ -107,12 +109,17 @@ func (ctx subCmdCtx) doSubCmd(subCmds ...subCmd) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
daemonRCPClient := jsonrpc2.NewUnixHTTPClient(
|
||||||
|
envSocketPath, daemonHTTPRPCPath,
|
||||||
|
)
|
||||||
|
|
||||||
err := subCmd.do(subCmdCtx{
|
err := subCmd.do(subCmdCtx{
|
||||||
subCmd: subCmd,
|
subCmd: subCmd,
|
||||||
args: args,
|
args: args,
|
||||||
subCmdNames: append(ctx.subCmdNames, subCmdName),
|
subCmdNames: append(ctx.subCmdNames, subCmdName),
|
||||||
ctx: ctx.ctx,
|
ctx: ctx.ctx,
|
||||||
logger: ctx.logger,
|
logger: ctx.logger,
|
||||||
|
daemonRCPClient: daemonRCPClient,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -59,14 +59,14 @@ func nebulaPmuxProcConfig(
|
|||||||
staticHostMap[ip] = []string{host.Nebula.PublicAddr}
|
staticHostMap[ip] = []string{host.Nebula.PublicAddr}
|
||||||
}
|
}
|
||||||
|
|
||||||
caCertPEM, err := hostBootstrap.CAPublicCredentials.Cert.MarshalToPEM()
|
caCertPEM, err := hostBootstrap.CAPublicCredentials.Cert.Unwrap().MarshalToPEM()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return pmuxlib.ProcessConfig{}, fmt.Errorf(
|
return pmuxlib.ProcessConfig{}, fmt.Errorf(
|
||||||
"marshaling CA cert to PEM: :%w", err,
|
"marshaling CA cert to PEM: :%w", err,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
hostCertPEM, err := hostBootstrap.PublicCredentials.Cert.MarshalToPEM()
|
hostCertPEM, err := hostBootstrap.PublicCredentials.Cert.Unwrap().MarshalToPEM()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return pmuxlib.ProcessConfig{}, fmt.Errorf(
|
return pmuxlib.ProcessConfig{}, fmt.Errorf(
|
||||||
"marshaling host cert to PEM: :%w", err,
|
"marshaling host cert to PEM: :%w", err,
|
||||||
|
@ -6,6 +6,9 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/tv42/httpunix"
|
||||||
)
|
)
|
||||||
|
|
||||||
type httpClient struct {
|
type httpClient struct {
|
||||||
@ -15,12 +18,33 @@ type httpClient struct {
|
|||||||
|
|
||||||
// NewHTTPClient returns a Client which will use HTTP POST requests against the
|
// NewHTTPClient returns a Client which will use HTTP POST requests against the
|
||||||
// given URL as a transport for JSONRPC2 method calls.
|
// given URL as a transport for JSONRPC2 method calls.
|
||||||
func NewHTTPClient(url string) Client {
|
func NewHTTPClient(urlStr string) Client {
|
||||||
return &httpClient{
|
return &httpClient{
|
||||||
c: &http.Client{
|
c: &http.Client{
|
||||||
Transport: http.DefaultTransport.(*http.Transport).Clone(),
|
Transport: http.DefaultTransport.(*http.Transport).Clone(),
|
||||||
},
|
},
|
||||||
url: url,
|
url: urlStr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUnixHTTPClient returns a Client which will use HTTP POST requests against
|
||||||
|
// the given unix socket as a transport for JSONRPC2 method calls. The given
|
||||||
|
// path will be used as the path portion of the HTTP request.
|
||||||
|
func NewUnixHTTPClient(unixSocketPath, reqPath string) Client {
|
||||||
|
const host = "uds"
|
||||||
|
|
||||||
|
u := &url.URL{
|
||||||
|
Scheme: httpunix.Scheme,
|
||||||
|
Host: host,
|
||||||
|
Path: reqPath,
|
||||||
|
}
|
||||||
|
|
||||||
|
transport := new(httpunix.Transport)
|
||||||
|
transport.RegisterLocation(host, unixSocketPath)
|
||||||
|
|
||||||
|
return &httpClient{
|
||||||
|
c: &http.Client{Transport: transport},
|
||||||
|
url: u.String(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,9 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
"net"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
@ -156,3 +158,20 @@ func TestHTTP(t *testing.T) {
|
|||||||
t.Cleanup(server.Close)
|
t.Cleanup(server.Close)
|
||||||
testClient(t, NewHTTPClient(server.URL))
|
testClient(t, NewHTTPClient(server.URL))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUnixHTTP(t *testing.T) {
|
||||||
|
var (
|
||||||
|
unixSocketPath = filepath.Join(t.TempDir(), "test.sock")
|
||||||
|
server = httptest.NewUnstartedServer(NewHTTPHandler(testHandler))
|
||||||
|
)
|
||||||
|
|
||||||
|
var err error
|
||||||
|
if server.Listener, err = net.Listen("unix", unixSocketPath); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
server.Start()
|
||||||
|
t.Cleanup(server.Close)
|
||||||
|
|
||||||
|
testClient(t, NewUnixHTTPClient(unixSocketPath, "/"))
|
||||||
|
}
|
||||||
|
@ -18,6 +18,11 @@ func NewHTTPHandler(h Handler) http.Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h httpHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
func (h httpHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method != http.MethodPost {
|
||||||
|
rw.WriteHeader(http.StatusMethodNotAllowed)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ctx = r.Context()
|
ctx = r.Context()
|
||||||
enc = json.NewEncoder(rw)
|
enc = json.NewEncoder(rw)
|
||||||
|
46
go/daemon/rpc.go
Normal file
46
go/daemon/rpc.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package daemon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"cmp"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"isle/bootstrap"
|
||||||
|
"slices"
|
||||||
|
|
||||||
|
"golang.org/x/exp/maps"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetHostsResult wraps the results from the GetHosts RPC method.
|
||||||
|
type GetHostsResult struct {
|
||||||
|
Hosts []bootstrap.Host
|
||||||
|
}
|
||||||
|
|
||||||
|
// RPC exposes all RPC methods which are available to be called over the RPC
|
||||||
|
// interface.
|
||||||
|
type RPC struct {
|
||||||
|
daemon Daemon
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRPC initializes and returns an RPC instance.
|
||||||
|
func NewRPC(daemon Daemon) *RPC {
|
||||||
|
return &RPC{daemon}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHosts returns all hosts known to the cluster, sorted by their name.
|
||||||
|
func (r *RPC) GetHosts(
|
||||||
|
ctx context.Context, req struct{},
|
||||||
|
) (
|
||||||
|
GetHostsResult, error,
|
||||||
|
) {
|
||||||
|
hostsMap, err := r.daemon.GetGarageBootstrapHosts(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return GetHostsResult{}, fmt.Errorf("retrieving hosts: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hosts := maps.Values(hostsMap)
|
||||||
|
slices.SortFunc(hosts, func(a, b bootstrap.Host) int {
|
||||||
|
return cmp.Compare(a.Name, b.Name)
|
||||||
|
})
|
||||||
|
|
||||||
|
return GetHostsResult{hosts}, nil
|
||||||
|
}
|
@ -12,13 +12,14 @@ require (
|
|||||||
github.com/shirou/gopsutil v3.21.11+incompatible
|
github.com/shirou/gopsutil v3.21.11+incompatible
|
||||||
github.com/slackhq/nebula v1.6.1
|
github.com/slackhq/nebula v1.6.1
|
||||||
github.com/spf13/pflag v1.0.5
|
github.com/spf13/pflag v1.0.5
|
||||||
|
github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c
|
||||||
|
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/dustin/go-humanize v1.0.0 // indirect
|
github.com/dustin/go-humanize v1.0.0 // indirect
|
||||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||||
github.com/google/go-cmp v0.5.6 // indirect
|
|
||||||
github.com/google/uuid v1.1.1 // indirect
|
github.com/google/uuid v1.1.1 // indirect
|
||||||
github.com/gopherjs/gopherjs v1.17.2 // indirect
|
github.com/gopherjs/gopherjs v1.17.2 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
|
10
go/go.sum
10
go/go.sum
@ -13,8 +13,8 @@ github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
|||||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
@ -77,10 +77,14 @@ github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03O
|
|||||||
github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
|
github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
|
||||||
github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o=
|
github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o=
|
||||||
github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
|
github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
|
||||||
|
github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c h1:u6SKchux2yDvFQnDHS3lPnIRmfVJ5Sxy3ao2SIdysLQ=
|
||||||
|
github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM=
|
||||||
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
|
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
|
||||||
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||||
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 h1:tkVvjkPTB7pnW3jnid7kNyAMPVWllTNOf/qKDze4p9o=
|
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 h1:tkVvjkPTB7pnW3jnid7kNyAMPVWllTNOf/qKDze4p9o=
|
||||||
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
|
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY=
|
||||||
|
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=
|
||||||
golang.org/x/net v0.0.0-20220403103023-749bd193bc2b h1:vI32FkLJNAWtGD4BwkThwEy6XS7ZLLMHkSkYfF8M0W0=
|
golang.org/x/net v0.0.0-20220403103023-749bd193bc2b h1:vI32FkLJNAWtGD4BwkThwEy6XS7ZLLMHkSkYfF8M0W0=
|
||||||
golang.org/x/net v0.0.0-20220403103023-749bd193bc2b/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
golang.org/x/net v0.0.0-20220403103023-749bd193bc2b/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@ -92,8 +96,6 @@ golang.org/x/sys v0.0.0-20220406155245-289d7a0edf71/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2 h1:GLw7MR8AfAG2GmGcmVgObFOHXYypgGjnGno25RDwn3Y=
|
golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2 h1:GLw7MR8AfAG2GmGcmVgObFOHXYypgGjnGno25RDwn3Y=
|
||||||
golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0=
|
golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
||||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
|
29
go/nebula/certificate.go
Normal file
29
go/nebula/certificate.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package nebula
|
||||||
|
|
||||||
|
import "github.com/slackhq/nebula/cert"
|
||||||
|
|
||||||
|
// Certificate wraps a NebulaCertificate to provide convenient (and consistent)
|
||||||
|
// text (un)marshaling methods.
|
||||||
|
type Certificate struct {
|
||||||
|
inner cert.NebulaCertificate
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unwrap returns the wrapped NebulaCertificate type.
|
||||||
|
func (c Certificate) Unwrap() *cert.NebulaCertificate {
|
||||||
|
return &c.inner
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalText implements the encoding.TextMarshaler interface.
|
||||||
|
func (c Certificate) MarshalText() ([]byte, error) {
|
||||||
|
return c.inner.MarshalToPEM()
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalText implements the encoding.TextUnmarshaler interface.
|
||||||
|
func (c *Certificate) UnmarshalText(b []byte) error {
|
||||||
|
nebCrt, _, err := cert.UnmarshalNebulaCertificateFromPEM(b)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.inner = *nebCrt
|
||||||
|
return nil
|
||||||
|
}
|
@ -13,7 +13,7 @@ import (
|
|||||||
// HostPublicCredentials contains certificate and signing public keys which are
|
// HostPublicCredentials contains certificate and signing public keys which are
|
||||||
// able to be broadcast publicly.
|
// able to be broadcast publicly.
|
||||||
type HostPublicCredentials struct {
|
type HostPublicCredentials struct {
|
||||||
Cert cert.NebulaCertificate
|
Cert Certificate
|
||||||
SigningKey SigningPublicKey
|
SigningKey SigningPublicKey
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,7 +28,7 @@ type HostPrivateCredentials struct {
|
|||||||
// able to be broadcast publicly. The signing public key is the same one which
|
// able to be broadcast publicly. The signing public key is the same one which
|
||||||
// is embedded into the certificate.
|
// is embedded into the certificate.
|
||||||
type CAPublicCredentials struct {
|
type CAPublicCredentials struct {
|
||||||
Cert cert.NebulaCertificate
|
Cert Certificate
|
||||||
SigningKey SigningPublicKey
|
SigningKey SigningPublicKey
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,20 +47,20 @@ func NewHostCert(
|
|||||||
hostName string,
|
hostName string,
|
||||||
ip net.IP,
|
ip net.IP,
|
||||||
) (
|
) (
|
||||||
cert.NebulaCertificate, error,
|
Certificate, error,
|
||||||
) {
|
) {
|
||||||
caCert := caCreds.Public.Cert
|
caCert := caCreds.Public.Cert
|
||||||
|
|
||||||
issuer, err := caCert.Sha256Sum()
|
issuer, err := caCert.inner.Sha256Sum()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cert.NebulaCertificate{}, fmt.Errorf("getting ca.crt issuer: %w", err)
|
return Certificate{}, fmt.Errorf("getting ca.crt issuer: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
expireAt := caCert.Details.NotAfter.Add(-1 * time.Second)
|
expireAt := caCert.inner.Details.NotAfter.Add(-1 * time.Second)
|
||||||
|
|
||||||
subnet := caCert.Details.Subnets[0]
|
subnet := caCert.inner.Details.Subnets[0]
|
||||||
if !subnet.Contains(ip) {
|
if !subnet.Contains(ip) {
|
||||||
return cert.NebulaCertificate{}, fmt.Errorf("invalid ip %q, not contained by network subnet %q", ip, subnet)
|
return Certificate{}, fmt.Errorf("invalid ip %q, not contained by network subnet %q", ip, subnet)
|
||||||
}
|
}
|
||||||
|
|
||||||
hostCert := cert.NebulaCertificate{
|
hostCert := cert.NebulaCertificate{
|
||||||
@ -78,15 +78,15 @@ func NewHostCert(
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := hostCert.CheckRootConstrains(&caCert); err != nil {
|
if err := hostCert.CheckRootConstrains(&caCert.inner); err != nil {
|
||||||
return cert.NebulaCertificate{}, fmt.Errorf("validating certificate constraints: %w", err)
|
return Certificate{}, fmt.Errorf("validating certificate constraints: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := signCert(&hostCert, caCreds.SigningPrivateKey); err != nil {
|
if err := signCert(&hostCert, caCreds.SigningPrivateKey); err != nil {
|
||||||
return cert.NebulaCertificate{}, fmt.Errorf("signing host cert with ca.key: %w", err)
|
return Certificate{}, fmt.Errorf("signing host cert with ca.key: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return hostCert, nil
|
return Certificate{hostCert}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHostCredentials generates a new key/cert for a nebula host using the CA
|
// NewHostCredentials generates a new key/cert for a nebula host using the CA
|
||||||
@ -149,7 +149,7 @@ func NewCACredentials(domain string, subnet *net.IPNet) (CACredentials, error) {
|
|||||||
|
|
||||||
return CACredentials{
|
return CACredentials{
|
||||||
Public: CAPublicCredentials{
|
Public: CAPublicCredentials{
|
||||||
Cert: caCert,
|
Cert: Certificate{caCert},
|
||||||
SigningKey: signingPubKey,
|
SigningKey: signingPubKey,
|
||||||
},
|
},
|
||||||
SigningPrivateKey: signingPrivKey,
|
SigningPrivateKey: signingPrivKey,
|
||||||
|
@ -16,8 +16,8 @@ func TestSigningKeysJSON(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !strings.HasPrefix(string(pubJSON), `"-----BEGIN `) {
|
if !strings.HasPrefix(string(pubJSON), `"S0`) {
|
||||||
t.Fatalf("pub key didn't marshal to PEM: %q", pubJSON)
|
t.Fatalf("pub key didn't marshal with prefix: %q", pubJSON)
|
||||||
}
|
}
|
||||||
|
|
||||||
var pub2 SigningPublicKey
|
var pub2 SigningPublicKey
|
||||||
@ -36,8 +36,8 @@ func TestSigningKeysJSON(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !strings.HasPrefix(string(privJSON), `"-----BEGIN `) {
|
if !strings.HasPrefix(string(privJSON), `"s0`) {
|
||||||
t.Fatalf("priv key didn't marshal to PEM: %q", privJSON)
|
t.Fatalf("priv key didn't marshal with prefix: %q", privJSON)
|
||||||
}
|
}
|
||||||
|
|
||||||
var priv2 SigningPrivateKey
|
var priv2 SigningPrivateKey
|
||||||
|
13
tests/cases/hosts/00-list.sh
Normal file
13
tests/cases/hosts/00-list.sh
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# shellcheck source=../../utils/with-1-data-1-empty-node-cluster.sh
|
||||||
|
source "$UTILS"/with-1-data-1-empty-node-cluster.sh
|
||||||
|
|
||||||
|
as_primus
|
||||||
|
hosts="$(isle hosts list)"
|
||||||
|
|
||||||
|
[ "$(echo "$hosts" | jq -r '.[0].Name')" = "primus" ]
|
||||||
|
[ "$(echo "$hosts" | jq -r '.[0].VPN.IP')" = "10.6.9.1" ]
|
||||||
|
[ "$(echo "$hosts" | jq -r '.[0].Storage.Instances|length')" = "3" ]
|
||||||
|
|
||||||
|
[ "$(echo "$hosts" | jq -r '.[1].Name')" = "secondus" ]
|
||||||
|
[ "$(echo "$hosts" | jq -r '.[1].VPN.IP')" = "10.6.9.2" ]
|
||||||
|
[ "$(echo "$hosts" | jq -r '.[1].Storage.Instances|length')" = "0" ]
|
@ -12,5 +12,6 @@ cat <<EOF
|
|||||||
export TMPDIR="$TMPDIR"
|
export TMPDIR="$TMPDIR"
|
||||||
export XDG_RUNTIME_DIR="$XDG_RUNTIME_DIR"
|
export XDG_RUNTIME_DIR="$XDG_RUNTIME_DIR"
|
||||||
export XDG_STATE_HOME="$XDG_STATE_HOME"
|
export XDG_STATE_HOME="$XDG_STATE_HOME"
|
||||||
|
export ISLE_SOCKET_PATH="$ROOT_TMPDIR/$base-daemon.sock"
|
||||||
cd "$TMPDIR"
|
cd "$TMPDIR"
|
||||||
EOF
|
EOF
|
||||||
|
@ -62,10 +62,10 @@ EOF
|
|||||||
pid="$!"
|
pid="$!"
|
||||||
echo "Waiting for primus daemon (process $pid) to initialize"
|
echo "Waiting for primus daemon (process $pid) to initialize"
|
||||||
|
|
||||||
while ! isle hosts list >/dev/null; do sleep 1; done
|
|
||||||
|
|
||||||
$SHELL "$UTILS/register-cleanup.sh" "$pid" "1-data-1-empty-node-cluster/primus"
|
$SHELL "$UTILS/register-cleanup.sh" "$pid" "1-data-1-empty-node-cluster/primus"
|
||||||
|
|
||||||
|
while ! isle hosts list >/dev/null; do sleep 1; done
|
||||||
|
|
||||||
echo "Creating secondus bootstrap"
|
echo "Creating secondus bootstrap"
|
||||||
isle admin create-bootstrap \
|
isle admin create-bootstrap \
|
||||||
--admin-path admin.json \
|
--admin-path admin.json \
|
||||||
@ -86,8 +86,7 @@ EOF
|
|||||||
pid="$!"
|
pid="$!"
|
||||||
echo "Waiting for secondus daemon (process $!) to initialize"
|
echo "Waiting for secondus daemon (process $!) to initialize"
|
||||||
|
|
||||||
while ! isle hosts list >/dev/null; do sleep 1; done
|
|
||||||
|
|
||||||
$SHELL "$UTILS/register-cleanup.sh" "$pid" "1-data-1-empty-node-cluster/secondus"
|
$SHELL "$UTILS/register-cleanup.sh" "$pid" "1-data-1-empty-node-cluster/secondus"
|
||||||
|
while ! isle hosts list >/dev/null; do sleep 1; done
|
||||||
)
|
)
|
||||||
fi
|
fi
|
||||||
|
Loading…
Reference in New Issue
Block a user