Move common daemon types and values into daecommon
This commit is contained in:
parent
ef86c1bbd1
commit
a840d0e701
@ -6,6 +6,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
|
|
||||||
"isle/daemon"
|
"isle/daemon"
|
||||||
|
"isle/daemon/daecommon"
|
||||||
|
|
||||||
"dev.mediocregopher.com/mediocre-go-lib.git/mlog"
|
"dev.mediocregopher.com/mediocre-go-lib.git/mlog"
|
||||||
)
|
)
|
||||||
@ -41,7 +42,7 @@ var subCmdDaemon = subCmd{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if *dumpConfig {
|
if *dumpConfig {
|
||||||
return daemon.CopyDefaultConfig(os.Stdout, envAppDirPath)
|
return daecommon.CopyDefaultConfig(os.Stdout, envAppDirPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
logLevel := mlog.LevelFromString(*logLevelStr)
|
logLevel := mlog.LevelFromString(*logLevelStr)
|
||||||
@ -55,7 +56,7 @@ var subCmdDaemon = subCmd{
|
|||||||
// required linux capabilities are set.
|
// required linux capabilities are set.
|
||||||
// TODO check that the tun module is loaded (for nebula).
|
// TODO check that the tun module is loaded (for nebula).
|
||||||
|
|
||||||
daemonConfig, err := daemon.LoadConfig(envAppDirPath, *daemonConfigPath)
|
daemonConfig, err := daecommon.LoadConfig(envAppDirPath, *daemonConfigPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("loading daemon config: %w", err)
|
return fmt.Errorf("loading daemon config: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,13 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"isle/daemon"
|
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"isle/daemon/daecommon"
|
||||||
|
|
||||||
"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"
|
||||||
)
|
)
|
||||||
@ -21,7 +22,7 @@ func getAppDirPath() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
daemonEnvVars = daemon.GetEnvVars()
|
daemonEnvVars = daecommon.GetEnvVars()
|
||||||
envAppDirPath = getAppDirPath()
|
envAppDirPath = getAppDirPath()
|
||||||
envBinDirPath = filepath.Join(envAppDirPath, "bin")
|
envBinDirPath = filepath.Join(envAppDirPath, "bin")
|
||||||
)
|
)
|
||||||
|
@ -130,7 +130,9 @@ func (ctx subCmdCtx) doSubCmd(subCmds ...subCmd) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
daemonRPC := daemon.RPCFromClient(
|
daemonRPC := daemon.RPCFromClient(
|
||||||
jsonrpc2.NewUnixHTTPClient(daemon.HTTPSocketPath(), daemonHTTPRPCPath),
|
jsonrpc2.NewUnixHTTPClient(
|
||||||
|
daemon.HTTPSocketPath(), daemonHTTPRPCPath,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
err := subCmd.do(subCmdCtx{
|
err := subCmd.do(subCmdCtx{
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"isle/bootstrap"
|
"isle/bootstrap"
|
||||||
|
"isle/daemon/daecommon"
|
||||||
"isle/garage/garagesrv"
|
"isle/garage/garagesrv"
|
||||||
"isle/jsonutil"
|
"isle/jsonutil"
|
||||||
"isle/secrets"
|
"isle/secrets"
|
||||||
@ -42,7 +43,7 @@ func writeBootstrapToStateDir(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func coalesceDaemonConfigAndBootstrap(
|
func coalesceDaemonConfigAndBootstrap(
|
||||||
daemonConfig Config, hostBootstrap bootstrap.Bootstrap,
|
daemonConfig daecommon.Config, hostBootstrap bootstrap.Bootstrap,
|
||||||
) (
|
) (
|
||||||
bootstrap.Bootstrap, error,
|
bootstrap.Bootstrap, error,
|
||||||
) {
|
) {
|
||||||
@ -88,7 +89,7 @@ type bootstrapDiff struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func calcBootstrapDiff(
|
func calcBootstrapDiff(
|
||||||
daemonConfig Config,
|
daemonConfig daecommon.Config,
|
||||||
prevBootstrap, nextBootstrap bootstrap.Bootstrap,
|
prevBootstrap, nextBootstrap bootstrap.Bootstrap,
|
||||||
) (
|
) (
|
||||||
diff bootstrapDiff, err error,
|
diff bootstrapDiff, err error,
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"isle/bootstrap"
|
"isle/bootstrap"
|
||||||
|
"isle/daemon/daecommon"
|
||||||
"isle/dnsmasq"
|
"isle/dnsmasq"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
@ -13,7 +14,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func dnsmasqConfig(
|
func dnsmasqConfig(
|
||||||
daemonConfig Config, hostBootstrap bootstrap.Bootstrap,
|
daemonConfig daecommon.Config, hostBootstrap bootstrap.Bootstrap,
|
||||||
) dnsmasq.ConfData {
|
) dnsmasq.ConfData {
|
||||||
hostsSlice := make([]dnsmasq.ConfDataHost, 0, len(hostBootstrap.Hosts))
|
hostsSlice := make([]dnsmasq.ConfDataHost, 0, len(hostBootstrap.Hosts))
|
||||||
for _, host := range hostBootstrap.Hosts {
|
for _, host := range hostBootstrap.Hosts {
|
||||||
@ -37,7 +38,7 @@ func dnsmasqConfig(
|
|||||||
|
|
||||||
func dnsmasqWriteConfig(
|
func dnsmasqWriteConfig(
|
||||||
runtimeDirPath string,
|
runtimeDirPath string,
|
||||||
daemonConfig Config,
|
daemonConfig daecommon.Config,
|
||||||
hostBootstrap bootstrap.Bootstrap,
|
hostBootstrap bootstrap.Bootstrap,
|
||||||
) (
|
) (
|
||||||
string, error,
|
string, error,
|
||||||
@ -57,7 +58,7 @@ func dnsmasqWriteConfig(
|
|||||||
func dnsmasqPmuxProcConfig(
|
func dnsmasqPmuxProcConfig(
|
||||||
logger *mlog.Logger,
|
logger *mlog.Logger,
|
||||||
runtimeDirPath, binDirPath string,
|
runtimeDirPath, binDirPath string,
|
||||||
daemonConfig Config,
|
daemonConfig daecommon.Config,
|
||||||
hostBootstrap bootstrap.Bootstrap,
|
hostBootstrap bootstrap.Bootstrap,
|
||||||
) (
|
) (
|
||||||
pmuxlib.ProcessConfig, error,
|
pmuxlib.ProcessConfig, error,
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"isle/bootstrap"
|
"isle/bootstrap"
|
||||||
|
"isle/daemon/daecommon"
|
||||||
"isle/garage"
|
"isle/garage"
|
||||||
"isle/garage/garagesrv"
|
"isle/garage/garagesrv"
|
||||||
"net"
|
"net"
|
||||||
@ -23,7 +24,7 @@ func garageAdminClientLogger(logger *mlog.Logger) *mlog.Logger {
|
|||||||
// or it will _panic_ if there is no local instance configured.
|
// or it will _panic_ if there is no local instance configured.
|
||||||
func newGarageAdminClient(
|
func newGarageAdminClient(
|
||||||
logger *mlog.Logger,
|
logger *mlog.Logger,
|
||||||
daemonConfig Config,
|
daemonConfig daecommon.Config,
|
||||||
adminToken string,
|
adminToken string,
|
||||||
hostBootstrap bootstrap.Bootstrap,
|
hostBootstrap bootstrap.Bootstrap,
|
||||||
) *garage.AdminClient {
|
) *garage.AdminClient {
|
||||||
@ -43,7 +44,7 @@ func newGarageAdminClient(
|
|||||||
func waitForGarage(
|
func waitForGarage(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
logger *mlog.Logger,
|
logger *mlog.Logger,
|
||||||
daemonConfig Config,
|
daemonConfig daecommon.Config,
|
||||||
adminToken string,
|
adminToken string,
|
||||||
hostBootstrap bootstrap.Bootstrap,
|
hostBootstrap bootstrap.Bootstrap,
|
||||||
) error {
|
) error {
|
||||||
@ -88,7 +89,7 @@ func waitForGarage(
|
|||||||
// This assumes that coalesceDaemonConfigAndBootstrap has already been called.
|
// This assumes that coalesceDaemonConfigAndBootstrap has already been called.
|
||||||
func bootstrapGarageHostForAlloc(
|
func bootstrapGarageHostForAlloc(
|
||||||
host bootstrap.Host,
|
host bootstrap.Host,
|
||||||
alloc ConfigStorageAllocation,
|
alloc daecommon.ConfigStorageAllocation,
|
||||||
) bootstrap.GarageHostInstance {
|
) bootstrap.GarageHostInstance {
|
||||||
|
|
||||||
for _, inst := range host.Garage.Instances {
|
for _, inst := range host.Garage.Instances {
|
||||||
@ -103,7 +104,7 @@ func bootstrapGarageHostForAlloc(
|
|||||||
func garageWriteChildConfig(
|
func garageWriteChildConfig(
|
||||||
rpcSecret, runtimeDirPath, adminToken string,
|
rpcSecret, runtimeDirPath, adminToken string,
|
||||||
hostBootstrap bootstrap.Bootstrap,
|
hostBootstrap bootstrap.Bootstrap,
|
||||||
alloc ConfigStorageAllocation,
|
alloc daecommon.ConfigStorageAllocation,
|
||||||
) (
|
) (
|
||||||
string, error,
|
string, error,
|
||||||
) {
|
) {
|
||||||
@ -147,7 +148,7 @@ func garagePmuxProcConfigs(
|
|||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
logger *mlog.Logger,
|
logger *mlog.Logger,
|
||||||
rpcSecret, runtimeDirPath, binDirPath string,
|
rpcSecret, runtimeDirPath, binDirPath string,
|
||||||
daemonConfig Config,
|
daemonConfig daecommon.Config,
|
||||||
adminToken string,
|
adminToken string,
|
||||||
hostBootstrap bootstrap.Bootstrap,
|
hostBootstrap bootstrap.Bootstrap,
|
||||||
) (
|
) (
|
||||||
@ -188,7 +189,7 @@ func garagePmuxProcConfigs(
|
|||||||
func garageApplyLayout(
|
func garageApplyLayout(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
logger *mlog.Logger,
|
logger *mlog.Logger,
|
||||||
daemonConfig Config,
|
daemonConfig daecommon.Config,
|
||||||
adminToken string,
|
adminToken string,
|
||||||
hostBootstrap bootstrap.Bootstrap,
|
hostBootstrap bootstrap.Bootstrap,
|
||||||
) error {
|
) error {
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"isle/bootstrap"
|
"isle/bootstrap"
|
||||||
|
"isle/daemon/daecommon"
|
||||||
"isle/yamlutil"
|
"isle/yamlutil"
|
||||||
"net"
|
"net"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -47,7 +48,7 @@ func waitForNebula(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func nebulaConfig(
|
func nebulaConfig(
|
||||||
daemonConfig Config,
|
daemonConfig daecommon.Config,
|
||||||
hostBootstrap bootstrap.Bootstrap,
|
hostBootstrap bootstrap.Bootstrap,
|
||||||
) (
|
) (
|
||||||
map[string]any, error,
|
map[string]any, error,
|
||||||
@ -136,7 +137,7 @@ func nebulaConfig(
|
|||||||
|
|
||||||
func nebulaWriteConfig(
|
func nebulaWriteConfig(
|
||||||
runtimeDirPath string,
|
runtimeDirPath string,
|
||||||
daemonConfig Config,
|
daemonConfig daecommon.Config,
|
||||||
hostBootstrap bootstrap.Bootstrap,
|
hostBootstrap bootstrap.Bootstrap,
|
||||||
) (
|
) (
|
||||||
string, error,
|
string, error,
|
||||||
@ -157,7 +158,7 @@ func nebulaWriteConfig(
|
|||||||
|
|
||||||
func nebulaPmuxProcConfig(
|
func nebulaPmuxProcConfig(
|
||||||
runtimeDirPath, binDirPath string,
|
runtimeDirPath, binDirPath string,
|
||||||
daemonConfig Config,
|
daemonConfig daecommon.Config,
|
||||||
hostBootstrap bootstrap.Bootstrap,
|
hostBootstrap bootstrap.Bootstrap,
|
||||||
) (
|
) (
|
||||||
pmuxlib.ProcessConfig, error,
|
pmuxlib.ProcessConfig, error,
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"isle/bootstrap"
|
"isle/bootstrap"
|
||||||
|
"isle/daemon/daecommon"
|
||||||
|
|
||||||
"code.betamike.com/micropelago/pmux/pmuxlib"
|
"code.betamike.com/micropelago/pmux/pmuxlib"
|
||||||
)
|
)
|
||||||
@ -11,7 +12,7 @@ import (
|
|||||||
func (c *Children) newPmuxConfig(
|
func (c *Children) newPmuxConfig(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
garageRPCSecret, binDirPath string,
|
garageRPCSecret, binDirPath string,
|
||||||
daemonConfig Config,
|
daemonConfig daecommon.Config,
|
||||||
garageAdminToken string,
|
garageAdminToken string,
|
||||||
hostBootstrap bootstrap.Bootstrap,
|
hostBootstrap bootstrap.Bootstrap,
|
||||||
) (
|
) (
|
||||||
@ -67,7 +68,7 @@ func (c *Children) newPmuxConfig(
|
|||||||
|
|
||||||
func (c *Children) postPmuxInit(
|
func (c *Children) postPmuxInit(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
daemonConfig Config,
|
daemonConfig daecommon.Config,
|
||||||
garageAdminToken string,
|
garageAdminToken string,
|
||||||
hostBootstrap bootstrap.Bootstrap,
|
hostBootstrap bootstrap.Bootstrap,
|
||||||
) error {
|
) error {
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"isle/bootstrap"
|
"isle/bootstrap"
|
||||||
|
"isle/daemon/daecommon"
|
||||||
"isle/secrets"
|
"isle/secrets"
|
||||||
|
|
||||||
"code.betamike.com/micropelago/pmux/pmuxlib"
|
"code.betamike.com/micropelago/pmux/pmuxlib"
|
||||||
@ -18,7 +19,7 @@ import (
|
|||||||
// - garage (0 or more, depending on configured storage allocations)
|
// - garage (0 or more, depending on configured storage allocations)
|
||||||
type Children struct {
|
type Children struct {
|
||||||
logger *mlog.Logger
|
logger *mlog.Logger
|
||||||
daemonConfig Config
|
daemonConfig daecommon.Config
|
||||||
opts Opts
|
opts Opts
|
||||||
|
|
||||||
pmux *pmuxlib.Pmux
|
pmux *pmuxlib.Pmux
|
||||||
@ -31,7 +32,7 @@ func NewChildren(
|
|||||||
logger *mlog.Logger,
|
logger *mlog.Logger,
|
||||||
binDirPath string,
|
binDirPath string,
|
||||||
secretsStore secrets.Store,
|
secretsStore secrets.Store,
|
||||||
daemonConfig Config,
|
daemonConfig daecommon.Config,
|
||||||
garageAdminToken string,
|
garageAdminToken string,
|
||||||
hostBootstrap bootstrap.Bootstrap,
|
hostBootstrap bootstrap.Bootstrap,
|
||||||
opts *Opts,
|
opts *Opts,
|
||||||
@ -41,7 +42,7 @@ func NewChildren(
|
|||||||
opts = opts.withDefaults()
|
opts = opts.withDefaults()
|
||||||
|
|
||||||
logger.Info(ctx, "Loading secrets")
|
logger.Info(ctx, "Loading secrets")
|
||||||
garageRPCSecret, err := getGarageRPCSecret(ctx, secretsStore)
|
garageRPCSecret, err := daecommon.GetGarageRPCSecret(ctx, secretsStore)
|
||||||
if err != nil && !errors.Is(err, secrets.ErrNotFound) {
|
if err != nil && !errors.Is(err, secrets.ErrNotFound) {
|
||||||
return nil, fmt.Errorf("loading garage RPC secret: %w", err)
|
return nil, fmt.Errorf("loading garage RPC secret: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -1,183 +1,81 @@
|
|||||||
package daemon
|
package daemon
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io/fs"
|
||||||
"isle/yamlutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"slices"
|
||||||
|
"strings"
|
||||||
"github.com/imdario/mergo"
|
"sync"
|
||||||
"gopkg.in/yaml.v3"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func defaultConfigPath(appDirPath string) string {
|
func getDefaultHTTPSocketDirPath() string {
|
||||||
return filepath.Join(appDirPath, "etc", "daemon.yml")
|
path, err := firstExistingDir(
|
||||||
|
"/tmp",
|
||||||
|
|
||||||
|
// TODO it's possible the daemon process can't actually write to these
|
||||||
|
"/run",
|
||||||
|
"/var/run",
|
||||||
|
"/dev/shm",
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("Failed to find directory for HTTP socket: %v", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
type ConfigTun struct {
|
return path
|
||||||
Device string `yaml:"device"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ConfigFirewall struct {
|
// HTTPSocketPath returns the path to the daemon's HTTP socket which is used for
|
||||||
Conntrack ConfigConntrack `yaml:"conntrack"`
|
// RPC and other functionality.
|
||||||
Outbound []ConfigFirewallRule `yaml:"outbound"`
|
var HTTPSocketPath = sync.OnceValue(func() string {
|
||||||
Inbound []ConfigFirewallRule `yaml:"inbound"`
|
return envOr(
|
||||||
}
|
"ISLE_DAEMON_HTTP_SOCKET_PATH",
|
||||||
|
func() string {
|
||||||
type ConfigConntrack struct {
|
return filepath.Join(
|
||||||
TCPTimeout string `yaml:"tcp_timeout"`
|
getDefaultHTTPSocketDirPath(), "isle-daemon.sock",
|
||||||
UDPTimeout string `yaml:"udp_timeout"`
|
)
|
||||||
DefaultTimeout string `yaml:"default_timeout"`
|
|
||||||
MaxConnections int `yaml:"max_connections"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ConfigFirewallRule struct {
|
|
||||||
Port string `yaml:"port,omitempty"`
|
|
||||||
Code string `yaml:"code,omitempty"`
|
|
||||||
Proto string `yaml:"proto,omitempty"`
|
|
||||||
Host string `yaml:"host,omitempty"`
|
|
||||||
Group string `yaml:"group,omitempty"`
|
|
||||||
Groups []string `yaml:"groups,omitempty"`
|
|
||||||
CIDR string `yaml:"cidr,omitempty"`
|
|
||||||
CASha string `yaml:"ca_sha,omitempty"`
|
|
||||||
CAName string `yaml:"ca_name,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConfigStorageAllocation describes the structure of each storage allocation
|
|
||||||
// within the daemon config file.
|
|
||||||
type ConfigStorageAllocation struct {
|
|
||||||
DataPath string `yaml:"data_path"`
|
|
||||||
MetaPath string `yaml:"meta_path"`
|
|
||||||
Capacity int `yaml:"capacity"`
|
|
||||||
S3APIPort int `yaml:"s3_api_port"`
|
|
||||||
RPCPort int `yaml:"rpc_port"`
|
|
||||||
AdminPort int `yaml:"admin_port"`
|
|
||||||
|
|
||||||
// Zone is a secret option which makes it easier to test garage bugs, but
|
|
||||||
// which we don't want users to otherwise know about.
|
|
||||||
Zone string `yaml:"zone"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Config describes the structure of the daemon config file.
|
|
||||||
type Config struct {
|
|
||||||
DNS struct {
|
|
||||||
Resolvers []string `yaml:"resolvers"`
|
|
||||||
} `yaml:"dns"`
|
|
||||||
VPN struct {
|
|
||||||
PublicAddr string `yaml:"public_addr"`
|
|
||||||
Firewall ConfigFirewall `yaml:"firewall"`
|
|
||||||
Tun ConfigTun `yaml:"tun"`
|
|
||||||
} `yaml:"vpn"`
|
|
||||||
Storage struct {
|
|
||||||
Allocations []ConfigStorageAllocation
|
|
||||||
} `yaml:"storage"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Config) fillDefaults() {
|
|
||||||
|
|
||||||
var firewallGarageInbound []ConfigFirewallRule
|
|
||||||
|
|
||||||
for i := range c.Storage.Allocations {
|
|
||||||
if c.Storage.Allocations[i].RPCPort == 0 {
|
|
||||||
c.Storage.Allocations[i].RPCPort = 3900 + (i * 10)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.Storage.Allocations[i].S3APIPort == 0 {
|
|
||||||
c.Storage.Allocations[i].S3APIPort = 3901 + (i * 10)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.Storage.Allocations[i].AdminPort == 0 {
|
|
||||||
c.Storage.Allocations[i].AdminPort = 3902 + (i * 10)
|
|
||||||
}
|
|
||||||
|
|
||||||
alloc := c.Storage.Allocations[i]
|
|
||||||
|
|
||||||
firewallGarageInbound = append(
|
|
||||||
firewallGarageInbound,
|
|
||||||
ConfigFirewallRule{
|
|
||||||
Port: strconv.Itoa(alloc.S3APIPort),
|
|
||||||
Proto: "tcp",
|
|
||||||
Host: "any",
|
|
||||||
},
|
|
||||||
ConfigFirewallRule{
|
|
||||||
Port: strconv.Itoa(alloc.RPCPort),
|
|
||||||
Proto: "tcp",
|
|
||||||
Host: "any",
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Jigs
|
||||||
|
|
||||||
|
func envOr(name string, fallback func() string) string {
|
||||||
|
if v := os.Getenv(name); v != "" {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return fallback()
|
||||||
}
|
}
|
||||||
|
|
||||||
c.VPN.Firewall.Inbound = append(
|
func firstExistingDir(paths ...string) (string, error) {
|
||||||
c.VPN.Firewall.Inbound,
|
var errs []error
|
||||||
firewallGarageInbound...,
|
for _, path := range paths {
|
||||||
|
stat, err := os.Stat(path)
|
||||||
|
switch {
|
||||||
|
case errors.Is(err, fs.ErrExist):
|
||||||
|
continue
|
||||||
|
case err != nil:
|
||||||
|
errs = append(
|
||||||
|
errs, fmt.Errorf("checking if path %q exists: %w", path, err),
|
||||||
)
|
)
|
||||||
}
|
case !stat.IsDir():
|
||||||
|
errs = append(
|
||||||
// CopyDefaultConfig copies the daemon config file embedded in the AppDir into
|
errs, fmt.Errorf("path %q exists but is not a directory", path),
|
||||||
// the given io.Writer.
|
)
|
||||||
func CopyDefaultConfig(into io.Writer, appDirPath string) error {
|
default:
|
||||||
|
return path, nil
|
||||||
defaultConfigPath := defaultConfigPath(appDirPath)
|
|
||||||
|
|
||||||
f, err := os.Open(defaultConfigPath)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("opening daemon config at %q: %w", defaultConfigPath, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
if _, err := io.Copy(into, f); err != nil {
|
|
||||||
return fmt.Errorf("copying daemon config from %q: %w", defaultConfigPath, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadConfig loads the daemon config from userConfigPath, merges it with
|
|
||||||
// the default found in the appDirPath, and returns the result.
|
|
||||||
//
|
|
||||||
// If userConfigPath is not given then the default is loaded and returned.
|
|
||||||
func LoadConfig(
|
|
||||||
appDirPath, userConfigPath string,
|
|
||||||
) (
|
|
||||||
Config, error,
|
|
||||||
) {
|
|
||||||
|
|
||||||
defaultConfigPath := defaultConfigPath(appDirPath)
|
|
||||||
|
|
||||||
var fullDaemon map[string]interface{}
|
|
||||||
|
|
||||||
if err := yamlutil.LoadYamlFile(&fullDaemon, defaultConfigPath); err != nil {
|
|
||||||
return Config{}, fmt.Errorf("parsing default daemon config file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if userConfigPath != "" {
|
|
||||||
|
|
||||||
var daemonConfig map[string]interface{}
|
|
||||||
if err := yamlutil.LoadYamlFile(&daemonConfig, userConfigPath); err != nil {
|
|
||||||
return Config{}, fmt.Errorf("parsing %q: %w", userConfigPath, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err := mergo.Merge(&fullDaemon, daemonConfig, mergo.WithOverride)
|
|
||||||
if err != nil {
|
|
||||||
return Config{}, fmt.Errorf("merging contents of file %q: %w", userConfigPath, err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fullDaemonB, err := yaml.Marshal(fullDaemon)
|
err := fmt.Errorf(
|
||||||
|
"no directory found at any of the following paths: %s",
|
||||||
if err != nil {
|
strings.Join(paths, ", "),
|
||||||
return Config{}, fmt.Errorf("yaml marshaling: %w", err)
|
)
|
||||||
|
if len(errs) > 0 {
|
||||||
|
err = errors.Join(slices.Insert(errs, 0, err)...)
|
||||||
}
|
}
|
||||||
|
return "", err
|
||||||
var config Config
|
|
||||||
if err := yaml.Unmarshal(fullDaemonB, &config); err != nil {
|
|
||||||
return Config{}, fmt.Errorf("yaml unmarshaling back into Config struct: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
config.fillDefaults()
|
|
||||||
|
|
||||||
return config, nil
|
|
||||||
}
|
}
|
||||||
|
183
go/daemon/daecommon/config.go
Normal file
183
go/daemon/daecommon/config.go
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
package daecommon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"isle/yamlutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/imdario/mergo"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
func defaultConfigPath(appDirPath string) string {
|
||||||
|
return filepath.Join(appDirPath, "etc", "daemon.yml")
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConfigTun struct {
|
||||||
|
Device string `yaml:"device"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConfigFirewall struct {
|
||||||
|
Conntrack ConfigConntrack `yaml:"conntrack"`
|
||||||
|
Outbound []ConfigFirewallRule `yaml:"outbound"`
|
||||||
|
Inbound []ConfigFirewallRule `yaml:"inbound"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConfigConntrack struct {
|
||||||
|
TCPTimeout string `yaml:"tcp_timeout"`
|
||||||
|
UDPTimeout string `yaml:"udp_timeout"`
|
||||||
|
DefaultTimeout string `yaml:"default_timeout"`
|
||||||
|
MaxConnections int `yaml:"max_connections"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConfigFirewallRule struct {
|
||||||
|
Port string `yaml:"port,omitempty"`
|
||||||
|
Code string `yaml:"code,omitempty"`
|
||||||
|
Proto string `yaml:"proto,omitempty"`
|
||||||
|
Host string `yaml:"host,omitempty"`
|
||||||
|
Group string `yaml:"group,omitempty"`
|
||||||
|
Groups []string `yaml:"groups,omitempty"`
|
||||||
|
CIDR string `yaml:"cidr,omitempty"`
|
||||||
|
CASha string `yaml:"ca_sha,omitempty"`
|
||||||
|
CAName string `yaml:"ca_name,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfigStorageAllocation describes the structure of each storage allocation
|
||||||
|
// within the daemon config file.
|
||||||
|
type ConfigStorageAllocation struct {
|
||||||
|
DataPath string `yaml:"data_path"`
|
||||||
|
MetaPath string `yaml:"meta_path"`
|
||||||
|
Capacity int `yaml:"capacity"`
|
||||||
|
S3APIPort int `yaml:"s3_api_port"`
|
||||||
|
RPCPort int `yaml:"rpc_port"`
|
||||||
|
AdminPort int `yaml:"admin_port"`
|
||||||
|
|
||||||
|
// Zone is a secret option which makes it easier to test garage bugs, but
|
||||||
|
// which we don't want users to otherwise know about.
|
||||||
|
Zone string `yaml:"zone"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Config describes the structure of the daemon config file.
|
||||||
|
type Config struct {
|
||||||
|
DNS struct {
|
||||||
|
Resolvers []string `yaml:"resolvers"`
|
||||||
|
} `yaml:"dns"`
|
||||||
|
VPN struct {
|
||||||
|
PublicAddr string `yaml:"public_addr"`
|
||||||
|
Firewall ConfigFirewall `yaml:"firewall"`
|
||||||
|
Tun ConfigTun `yaml:"tun"`
|
||||||
|
} `yaml:"vpn"`
|
||||||
|
Storage struct {
|
||||||
|
Allocations []ConfigStorageAllocation
|
||||||
|
} `yaml:"storage"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) fillDefaults() {
|
||||||
|
|
||||||
|
var firewallGarageInbound []ConfigFirewallRule
|
||||||
|
|
||||||
|
for i := range c.Storage.Allocations {
|
||||||
|
if c.Storage.Allocations[i].RPCPort == 0 {
|
||||||
|
c.Storage.Allocations[i].RPCPort = 3900 + (i * 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Storage.Allocations[i].S3APIPort == 0 {
|
||||||
|
c.Storage.Allocations[i].S3APIPort = 3901 + (i * 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Storage.Allocations[i].AdminPort == 0 {
|
||||||
|
c.Storage.Allocations[i].AdminPort = 3902 + (i * 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
alloc := c.Storage.Allocations[i]
|
||||||
|
|
||||||
|
firewallGarageInbound = append(
|
||||||
|
firewallGarageInbound,
|
||||||
|
ConfigFirewallRule{
|
||||||
|
Port: strconv.Itoa(alloc.S3APIPort),
|
||||||
|
Proto: "tcp",
|
||||||
|
Host: "any",
|
||||||
|
},
|
||||||
|
ConfigFirewallRule{
|
||||||
|
Port: strconv.Itoa(alloc.RPCPort),
|
||||||
|
Proto: "tcp",
|
||||||
|
Host: "any",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.VPN.Firewall.Inbound = append(
|
||||||
|
c.VPN.Firewall.Inbound,
|
||||||
|
firewallGarageInbound...,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CopyDefaultConfig copies the daemon config file embedded in the AppDir into
|
||||||
|
// the given io.Writer.
|
||||||
|
func CopyDefaultConfig(into io.Writer, appDirPath string) error {
|
||||||
|
|
||||||
|
defaultConfigPath := defaultConfigPath(appDirPath)
|
||||||
|
|
||||||
|
f, err := os.Open(defaultConfigPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("opening daemon config at %q: %w", defaultConfigPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
if _, err := io.Copy(into, f); err != nil {
|
||||||
|
return fmt.Errorf("copying daemon config from %q: %w", defaultConfigPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadConfig loads the daemon config from userConfigPath, merges it with
|
||||||
|
// the default found in the appDirPath, and returns the result.
|
||||||
|
//
|
||||||
|
// If userConfigPath is not given then the default is loaded and returned.
|
||||||
|
func LoadConfig(
|
||||||
|
appDirPath, userConfigPath string,
|
||||||
|
) (
|
||||||
|
Config, error,
|
||||||
|
) {
|
||||||
|
|
||||||
|
defaultConfigPath := defaultConfigPath(appDirPath)
|
||||||
|
|
||||||
|
var fullDaemon map[string]interface{}
|
||||||
|
|
||||||
|
if err := yamlutil.LoadYamlFile(&fullDaemon, defaultConfigPath); err != nil {
|
||||||
|
return Config{}, fmt.Errorf("parsing default daemon config file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if userConfigPath != "" {
|
||||||
|
|
||||||
|
var daemonConfig map[string]interface{}
|
||||||
|
if err := yamlutil.LoadYamlFile(&daemonConfig, userConfigPath); err != nil {
|
||||||
|
return Config{}, fmt.Errorf("parsing %q: %w", userConfigPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := mergo.Merge(&fullDaemon, daemonConfig, mergo.WithOverride)
|
||||||
|
if err != nil {
|
||||||
|
return Config{}, fmt.Errorf("merging contents of file %q: %w", userConfigPath, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fullDaemonB, err := yaml.Marshal(fullDaemon)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return Config{}, fmt.Errorf("yaml marshaling: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var config Config
|
||||||
|
if err := yaml.Unmarshal(fullDaemonB, &config); err != nil {
|
||||||
|
return Config{}, fmt.Errorf("yaml unmarshaling back into Config struct: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config.fillDefaults()
|
||||||
|
|
||||||
|
return config, nil
|
||||||
|
}
|
3
go/daemon/daecommon/daecommon.go
Normal file
3
go/daemon/daecommon/daecommon.go
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
// Package daecommon holds types and functionality which are required the daemon
|
||||||
|
// package and other of its subpackages.
|
||||||
|
package daecommon
|
45
go/daemon/daecommon/env.go
Normal file
45
go/daemon/daecommon/env.go
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package daecommon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/adrg/xdg"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EnvVars are variables which are derived based on the environment which the
|
||||||
|
// process is running in.
|
||||||
|
type EnvVars struct {
|
||||||
|
RuntimeDirPath string
|
||||||
|
StateDirPath string
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEnvVars will return the EnvVars of the current processes, as determined by
|
||||||
|
// the process's environment.
|
||||||
|
var GetEnvVars = sync.OnceValue(func() (v EnvVars) {
|
||||||
|
// RUNTIME_DIRECTORY/STATE_DIRECTORY are used by the systemd service in
|
||||||
|
// conjunction with the RuntimeDirectory/StateDirectory directives.
|
||||||
|
|
||||||
|
v.RuntimeDirPath = envOr(
|
||||||
|
"RUNTIME_DIRECTORY",
|
||||||
|
func() string { return filepath.Join(xdg.RuntimeDir, "isle") },
|
||||||
|
)
|
||||||
|
|
||||||
|
v.StateDirPath = envOr(
|
||||||
|
"STATE_DIRECTORY",
|
||||||
|
func() string { return filepath.Join(xdg.StateHome, "isle") },
|
||||||
|
)
|
||||||
|
|
||||||
|
return
|
||||||
|
})
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Jigs
|
||||||
|
|
||||||
|
func envOr(name string, fallback func() string) string {
|
||||||
|
if v := os.Getenv(name); v != "" {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return fallback()
|
||||||
|
}
|
51
go/daemon/daecommon/secrets.go
Normal file
51
go/daemon/daecommon/secrets.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package daecommon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"isle/garage"
|
||||||
|
"isle/nebula"
|
||||||
|
"isle/secrets"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
secretsNSNebula = "nebula"
|
||||||
|
secretsNSGarage = "garage"
|
||||||
|
)
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Nebula-related secrets
|
||||||
|
|
||||||
|
// IDs and Get/Set functions for nebula-related secrets.
|
||||||
|
var (
|
||||||
|
NebulaCASigningPrivateKeySecretID = secrets.NewID(secretsNSNebula, "ca-signing-private-key")
|
||||||
|
|
||||||
|
GetNebulaCASigningPrivateKey, SetNebulaCASigningPrivateKey = secrets.GetSetFunctions[nebula.SigningPrivateKey](
|
||||||
|
NebulaCASigningPrivateKeySecretID,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Garage-related secrets
|
||||||
|
|
||||||
|
func garageS3APIBucketCredentialsSecretID(credsName string) secrets.ID {
|
||||||
|
return secrets.NewID(
|
||||||
|
secretsNSGarage, fmt.Sprintf("s3-api-bucket-credentials-%s", credsName),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDs and Get/Set functions for garage-related secrets.
|
||||||
|
var (
|
||||||
|
GarageRPCSecretSecretID = secrets.NewID(secretsNSGarage, "rpc-secret")
|
||||||
|
GarageS3APIGlobalBucketCredentialsSecretID = garageS3APIBucketCredentialsSecretID(
|
||||||
|
garage.GlobalBucketS3APICredentialsName,
|
||||||
|
)
|
||||||
|
|
||||||
|
GetGarageRPCSecret, SetGarageRPCSecret = secrets.GetSetFunctions[string](
|
||||||
|
GarageRPCSecretSecretID,
|
||||||
|
)
|
||||||
|
|
||||||
|
GetGarageS3APIGlobalBucketCredentials,
|
||||||
|
SetGarageS3APIGlobalBucketCredentials = secrets.GetSetFunctions[garage.S3APICredentials](
|
||||||
|
GarageS3APIGlobalBucketCredentialsSecretID,
|
||||||
|
)
|
||||||
|
)
|
@ -12,6 +12,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"isle/bootstrap"
|
"isle/bootstrap"
|
||||||
|
"isle/daemon/daecommon"
|
||||||
"isle/jsonutil"
|
"isle/jsonutil"
|
||||||
"isle/nebula"
|
"isle/nebula"
|
||||||
"isle/secrets"
|
"isle/secrets"
|
||||||
@ -33,7 +34,8 @@ type Opts struct {
|
|||||||
// will be directed to.
|
// will be directed to.
|
||||||
Stdout, Stderr io.Writer
|
Stdout, Stderr io.Writer
|
||||||
|
|
||||||
EnvVars EnvVars // Defaults to that returned by GetEnvVars.
|
// Defaults to that returned by daecommon.GetEnvVars.
|
||||||
|
EnvVars daecommon.EnvVars
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Opts) withDefaults() *Opts {
|
func (o *Opts) withDefaults() *Opts {
|
||||||
@ -49,8 +51,8 @@ func (o *Opts) withDefaults() *Opts {
|
|||||||
o.Stderr = os.Stderr
|
o.Stderr = os.Stderr
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.EnvVars == (EnvVars{}) {
|
if o.EnvVars == (daecommon.EnvVars{}) {
|
||||||
o.EnvVars = GetEnvVars()
|
o.EnvVars = daecommon.GetEnvVars()
|
||||||
}
|
}
|
||||||
|
|
||||||
return o
|
return o
|
||||||
@ -83,7 +85,7 @@ var _ RPC = (*Daemon)(nil)
|
|||||||
// canceled.
|
// canceled.
|
||||||
type Daemon struct {
|
type Daemon struct {
|
||||||
logger *mlog.Logger
|
logger *mlog.Logger
|
||||||
daemonConfig Config
|
daemonConfig daecommon.Config
|
||||||
envBinDirPath string
|
envBinDirPath string
|
||||||
opts *Opts
|
opts *Opts
|
||||||
|
|
||||||
@ -103,7 +105,7 @@ type Daemon struct {
|
|||||||
func NewDaemon(
|
func NewDaemon(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
logger *mlog.Logger,
|
logger *mlog.Logger,
|
||||||
daemonConfig Config,
|
daemonConfig daecommon.Config,
|
||||||
envBinDirPath string,
|
envBinDirPath string,
|
||||||
opts *Opts,
|
opts *Opts,
|
||||||
) (
|
) (
|
||||||
@ -121,7 +123,7 @@ func NewDaemon(
|
|||||||
bootstrapFilePath = bootstrap.StateDirPath(d.opts.EnvVars.StateDirPath)
|
bootstrapFilePath = bootstrap.StateDirPath(d.opts.EnvVars.StateDirPath)
|
||||||
)
|
)
|
||||||
|
|
||||||
if err := d.opts.EnvVars.init(); err != nil {
|
if err := createDirs(d.opts.EnvVars); err != nil {
|
||||||
return nil, fmt.Errorf("initializing daemon directories: %w", err)
|
return nil, fmt.Errorf("initializing daemon directories: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,6 +153,27 @@ func NewDaemon(
|
|||||||
return d, nil
|
return d, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createDirs(e daecommon.EnvVars) error {
|
||||||
|
var errs []error
|
||||||
|
if err := mkDir(e.RuntimeDirPath); err != nil {
|
||||||
|
errs = append(errs, fmt.Errorf(
|
||||||
|
"creating runtime directory %q: %w",
|
||||||
|
e.RuntimeDirPath,
|
||||||
|
err,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mkDir(e.StateDirPath); err != nil {
|
||||||
|
errs = append(errs, fmt.Errorf(
|
||||||
|
"creating state directory %q: %w",
|
||||||
|
e.StateDirPath,
|
||||||
|
err,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.Join(errs...)
|
||||||
|
}
|
||||||
|
|
||||||
// initialize must be called with d.l write lock held.
|
// initialize must be called with d.l write lock held.
|
||||||
func (d *Daemon) initialize(
|
func (d *Daemon) initialize(
|
||||||
ctx context.Context, currBootstrap bootstrap.Bootstrap,
|
ctx context.Context, currBootstrap bootstrap.Bootstrap,
|
||||||
@ -312,7 +335,9 @@ func (d *Daemon) postInit(ctx context.Context) error {
|
|||||||
//
|
//
|
||||||
// TODO this is pretty hacky, but there doesn't seem to be a better way to
|
// TODO this is pretty hacky, but there doesn't seem to be a better way to
|
||||||
// manage it at the moment.
|
// manage it at the moment.
|
||||||
_, err := getGarageS3APIGlobalBucketCredentials(ctx, d.secretsStore)
|
_, err := daecommon.GetGarageS3APIGlobalBucketCredentials(
|
||||||
|
ctx, d.secretsStore,
|
||||||
|
)
|
||||||
if errors.Is(err, secrets.ErrNotFound) {
|
if errors.Is(err, secrets.ErrNotFound) {
|
||||||
d.logger.Info(ctx, "Initializing garage shared global bucket")
|
d.logger.Info(ctx, "Initializing garage shared global bucket")
|
||||||
garageGlobalBucketCreds, err := garageInitializeGlobalBucket(
|
garageGlobalBucketCreds, err := garageInitializeGlobalBucket(
|
||||||
@ -326,7 +351,7 @@ func (d *Daemon) postInit(ctx context.Context) error {
|
|||||||
return fmt.Errorf("initializing global bucket: %w", err)
|
return fmt.Errorf("initializing global bucket: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = setGarageS3APIGlobalBucketCredentials(
|
err = daecommon.SetGarageS3APIGlobalBucketCredentials(
|
||||||
ctx, d.secretsStore, garageGlobalBucketCreds,
|
ctx, d.secretsStore, garageGlobalBucketCreds,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -406,12 +431,14 @@ func (d *Daemon) CreateNetwork(
|
|||||||
garageRPCSecret = randStr(32)
|
garageRPCSecret = randStr(32)
|
||||||
)
|
)
|
||||||
|
|
||||||
err = setGarageRPCSecret(ctx, d.secretsStore, garageRPCSecret)
|
err = daecommon.SetGarageRPCSecret(ctx, d.secretsStore, garageRPCSecret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("setting garage RPC secret: %w", err)
|
return fmt.Errorf("setting garage RPC secret: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = setNebulaCASigningPrivateKey(ctx, d.secretsStore, nebulaCACreds.SigningPrivateKey)
|
err = daecommon.SetNebulaCASigningPrivateKey(
|
||||||
|
ctx, d.secretsStore, nebulaCACreds.SigningPrivateKey,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("setting nebula CA signing key secret: %w", err)
|
return fmt.Errorf("setting nebula CA signing key secret: %w", err)
|
||||||
}
|
}
|
||||||
@ -679,7 +706,7 @@ func (d *Daemon) CreateHost(
|
|||||||
}
|
}
|
||||||
// TODO if the ip is given, check that it's not already in use.
|
// TODO if the ip is given, check that it's not already in use.
|
||||||
|
|
||||||
caSigningPrivateKey, err := getNebulaCASigningPrivateKey(
|
caSigningPrivateKey, err := daecommon.GetNebulaCASigningPrivateKey(
|
||||||
ctx, d.secretsStore,
|
ctx, d.secretsStore,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -701,12 +728,14 @@ func (d *Daemon) CreateHost(
|
|||||||
}
|
}
|
||||||
|
|
||||||
secretsIDs := []secrets.ID{
|
secretsIDs := []secrets.ID{
|
||||||
garageRPCSecretSecretID,
|
daecommon.GarageRPCSecretSecretID,
|
||||||
garageS3APIGlobalBucketCredentialsSecretID,
|
daecommon.GarageS3APIGlobalBucketCredentialsSecretID,
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.CanCreateHosts {
|
if opts.CanCreateHosts {
|
||||||
secretsIDs = append(secretsIDs, nebulaCASigningPrivateKeySecretID)
|
secretsIDs = append(
|
||||||
|
secretsIDs, daecommon.NebulaCASigningPrivateKeySecretID,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if joiningBootstrap.Secrets, err = secrets.Export(
|
if joiningBootstrap.Secrets, err = secrets.Export(
|
||||||
@ -760,7 +789,7 @@ func (d *Daemon) CreateNebulaCertificate(
|
|||||||
}
|
}
|
||||||
ip := host.IP()
|
ip := host.IP()
|
||||||
|
|
||||||
caSigningPrivateKey, err := getNebulaCASigningPrivateKey(
|
caSigningPrivateKey, err := daecommon.GetNebulaCASigningPrivateKey(
|
||||||
ctx, d.secretsStore,
|
ctx, d.secretsStore,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
130
go/daemon/env.go
130
go/daemon/env.go
@ -1,130 +0,0 @@
|
|||||||
package daemon
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io/fs"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"slices"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/adrg/xdg"
|
|
||||||
)
|
|
||||||
|
|
||||||
// EnvVars are variables which are derived based on the environment which the
|
|
||||||
// process is running in.
|
|
||||||
type EnvVars struct {
|
|
||||||
RuntimeDirPath string
|
|
||||||
StateDirPath string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e EnvVars) init() error {
|
|
||||||
var errs []error
|
|
||||||
if err := mkDir(e.RuntimeDirPath); err != nil {
|
|
||||||
errs = append(errs, fmt.Errorf(
|
|
||||||
"creating runtime directory %q: %w",
|
|
||||||
e.RuntimeDirPath,
|
|
||||||
err,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := mkDir(e.StateDirPath); err != nil {
|
|
||||||
errs = append(errs, fmt.Errorf(
|
|
||||||
"creating state directory %q: %w",
|
|
||||||
e.StateDirPath,
|
|
||||||
err,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors.Join(errs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getDefaultHTTPSocketDirPath() string {
|
|
||||||
path, err := firstExistingDir(
|
|
||||||
"/tmp",
|
|
||||||
|
|
||||||
// TODO it's possible the daemon process can't actually write to these
|
|
||||||
"/run",
|
|
||||||
"/var/run",
|
|
||||||
"/dev/shm",
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("Failed to find directory for HTTP socket: %v", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
return path
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTPSocketPath returns the path to the daemon's HTTP socket which is used for
|
|
||||||
// RPC and other functionality.
|
|
||||||
var HTTPSocketPath = sync.OnceValue(func() string {
|
|
||||||
return envOr(
|
|
||||||
"ISLE_DAEMON_HTTP_SOCKET_PATH",
|
|
||||||
func() string {
|
|
||||||
return filepath.Join(
|
|
||||||
getDefaultHTTPSocketDirPath(), "isle-daemon.sock",
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
// GetEnvVars will return the EnvVars of the current processes, as determined by
|
|
||||||
// the process's environment.
|
|
||||||
var GetEnvVars = sync.OnceValue(func() (v EnvVars) {
|
|
||||||
// RUNTIME_DIRECTORY/STATE_DIRECTORY are used by the systemd service in
|
|
||||||
// conjunction with the RuntimeDirectory/StateDirectory directives.
|
|
||||||
|
|
||||||
v.RuntimeDirPath = envOr(
|
|
||||||
"RUNTIME_DIRECTORY",
|
|
||||||
func() string { return filepath.Join(xdg.RuntimeDir, "isle") },
|
|
||||||
)
|
|
||||||
|
|
||||||
v.StateDirPath = envOr(
|
|
||||||
"STATE_DIRECTORY",
|
|
||||||
func() string { return filepath.Join(xdg.StateHome, "isle") },
|
|
||||||
)
|
|
||||||
|
|
||||||
return
|
|
||||||
})
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Jigs
|
|
||||||
|
|
||||||
func envOr(name string, fallback func() string) string {
|
|
||||||
if v := os.Getenv(name); v != "" {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
return fallback()
|
|
||||||
}
|
|
||||||
|
|
||||||
func firstExistingDir(paths ...string) (string, error) {
|
|
||||||
var errs []error
|
|
||||||
for _, path := range paths {
|
|
||||||
stat, err := os.Stat(path)
|
|
||||||
switch {
|
|
||||||
case errors.Is(err, fs.ErrExist):
|
|
||||||
continue
|
|
||||||
case err != nil:
|
|
||||||
errs = append(
|
|
||||||
errs, fmt.Errorf("checking if path %q exists: %w", path, err),
|
|
||||||
)
|
|
||||||
case !stat.IsDir():
|
|
||||||
errs = append(
|
|
||||||
errs, fmt.Errorf("path %q exists but is not a directory", path),
|
|
||||||
)
|
|
||||||
default:
|
|
||||||
return path, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err := fmt.Errorf(
|
|
||||||
"no directory found at any of the following paths: %s",
|
|
||||||
strings.Join(paths, ", "),
|
|
||||||
)
|
|
||||||
if len(errs) > 0 {
|
|
||||||
err = errors.Join(slices.Insert(errs, 0, err)...)
|
|
||||||
}
|
|
||||||
return "", err
|
|
||||||
}
|
|
@ -5,6 +5,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"isle/bootstrap"
|
"isle/bootstrap"
|
||||||
|
"isle/daemon/daecommon"
|
||||||
"isle/garage"
|
"isle/garage"
|
||||||
"isle/secrets"
|
"isle/secrets"
|
||||||
)
|
)
|
||||||
@ -24,12 +25,14 @@ func (d *Daemon) getGarageClientParams(
|
|||||||
) (
|
) (
|
||||||
GarageClientParams, error,
|
GarageClientParams, error,
|
||||||
) {
|
) {
|
||||||
creds, err := getGarageS3APIGlobalBucketCredentials(ctx, d.secretsStore)
|
creds, err := daecommon.GetGarageS3APIGlobalBucketCredentials(
|
||||||
|
ctx, d.secretsStore,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return GarageClientParams{}, fmt.Errorf("getting garage global bucket creds: %w", err)
|
return GarageClientParams{}, fmt.Errorf("getting garage global bucket creds: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
rpcSecret, err := getGarageRPCSecret(ctx, d.secretsStore)
|
rpcSecret, err := daecommon.GetGarageRPCSecret(ctx, d.secretsStore)
|
||||||
if err != nil && !errors.Is(err, secrets.ErrNotFound) {
|
if err != nil && !errors.Is(err, secrets.ErrNotFound) {
|
||||||
return GarageClientParams{}, fmt.Errorf("getting garage rpc secret: %w", err)
|
return GarageClientParams{}, fmt.Errorf("getting garage rpc secret: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"isle/bootstrap"
|
"isle/bootstrap"
|
||||||
|
"isle/daemon/daecommon"
|
||||||
"isle/garage"
|
"isle/garage"
|
||||||
"isle/nebula"
|
"isle/nebula"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -23,7 +24,7 @@ const (
|
|||||||
func garageInitializeGlobalBucket(
|
func garageInitializeGlobalBucket(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
logger *mlog.Logger,
|
logger *mlog.Logger,
|
||||||
daemonConfig Config,
|
daemonConfig daecommon.Config,
|
||||||
adminToken string,
|
adminToken string,
|
||||||
hostBootstrap bootstrap.Bootstrap,
|
hostBootstrap bootstrap.Bootstrap,
|
||||||
) (
|
) (
|
||||||
|
@ -1,52 +0,0 @@
|
|||||||
package daemon
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"isle/garage"
|
|
||||||
"isle/nebula"
|
|
||||||
"isle/secrets"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
secretsNSNebula = "nebula"
|
|
||||||
secretsNSGarage = "garage"
|
|
||||||
)
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Nebula-related secrets
|
|
||||||
|
|
||||||
var (
|
|
||||||
nebulaCASigningPrivateKeySecretID = secrets.NewID(secretsNSNebula, "ca-signing-private-key")
|
|
||||||
)
|
|
||||||
|
|
||||||
var getNebulaCASigningPrivateKey, setNebulaCASigningPrivateKey = secrets.GetSetFunctions[nebula.SigningPrivateKey](
|
|
||||||
nebulaCASigningPrivateKeySecretID,
|
|
||||||
)
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Garage-related secrets
|
|
||||||
|
|
||||||
func garageS3APIBucketCredentialsSecretID(credsName string) secrets.ID {
|
|
||||||
return secrets.NewID(
|
|
||||||
secretsNSGarage, fmt.Sprintf("s3-api-bucket-credentials-%s", credsName),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
garageRPCSecretSecretID = secrets.NewID(secretsNSGarage, "rpc-secret")
|
|
||||||
garageS3APIGlobalBucketCredentialsSecretID = garageS3APIBucketCredentialsSecretID(
|
|
||||||
garage.GlobalBucketS3APICredentialsName,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Get/Set functions for garage-related secrets.
|
|
||||||
var (
|
|
||||||
getGarageRPCSecret, setGarageRPCSecret = secrets.GetSetFunctions[string](
|
|
||||||
garageRPCSecretSecretID,
|
|
||||||
)
|
|
||||||
|
|
||||||
getGarageS3APIGlobalBucketCredentials,
|
|
||||||
setGarageS3APIGlobalBucketCredentials = secrets.GetSetFunctions[garage.S3APICredentials](
|
|
||||||
garageS3APIGlobalBucketCredentialsSecretID,
|
|
||||||
)
|
|
||||||
)
|
|
Loading…
Reference in New Issue
Block a user