Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions cmd/spire-agent/cli/common/config_posix.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,22 @@ import (

type ConfigOS struct {
socketPath string
instance string
}

func (c *ConfigOS) AddOSFlags(flags *flag.FlagSet) {
flags.StringVar(&c.socketPath, "socketPath", DefaultSocketPath, "Path to the SPIRE Agent API Unix domain socket")
flags.StringVar(&c.instance, "i", "", "Instance name to substitute into socket templates (env SPIFFE_PUBLIC_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'.")
}

func (c *ConfigOS) GetAddr() (net.Addr, error) {
return util.GetUnixAddrWithAbsPath(c.socketPath)
resolved := ResolveSocketPath(c.socketPath, DefaultSocketPath, "SPIFFE_PUBLIC_SOCKET_TEMPLATE", c.instance)
return util.GetUnixAddrWithAbsPath(resolved)
}

func (c *ConfigOS) GetTargetName() (string, error) {
addr, err := util.GetUnixAddrWithAbsPath(c.socketPath)
resolved := ResolveSocketPath(c.socketPath, DefaultSocketPath, "SPIFFE_PUBLIC_SOCKET_TEMPLATE", c.instance)
addr, err := util.GetUnixAddrWithAbsPath(resolved)
if err != nil {
return "", err
}
Expand Down
22 changes: 22 additions & 0 deletions cmd/spire-agent/cli/common/template.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package common

Check failure on line 1 in cmd/spire-agent/cli/common/template.go

View workflow job for this annotation

GitHub Actions / lint (linux)

File is not properly formatted (gofmt)

Check failure on line 1 in cmd/spire-agent/cli/common/template.go

View workflow job for this annotation

GitHub Actions / lint (windows)

File is not properly formatted (gofmt)

import (
"os"
"strings"
)

func ResolveSocketPath(socketPath, defaultPath, templateEnv, instance string) string {
tpl := os.Getenv(templateEnv)
if tpl != "" && strings.Contains(tpl, "%i") {
if instance == "" {
instance = "main"
}
if socketPath == "" || socketPath == defaultPath {
return strings.ReplaceAll(tpl, "%i", instance)
}
}
if socketPath == "" {
return defaultPath
}
return socketPath
}
5 changes: 4 additions & 1 deletion cmd/spire-agent/cli/healthcheck/healthcheck_posix.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,15 @@ import (
// that complements healthCheckCommand
type healthCheckCommandOS struct {
socketPath string
instance string
}

func (c *healthCheckCommandOS) addOSFlags(flags *flag.FlagSet) {
flags.StringVar(&c.socketPath, "socketPath", common.DefaultSocketPath, "Path to the SPIRE Agent API socket")
flags.StringVar(&c.instance, "i", "", "Instance name to substitute into socket templates (env SPIFFE_PUBLIC_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'.")
}

func (c *healthCheckCommandOS) getAddr() (net.Addr, error) {
return util.GetUnixAddrWithAbsPath(c.socketPath)
resolved := common.ResolveSocketPath(c.socketPath, common.DefaultSocketPath, "SPIFFE_PUBLIC_SOCKET_TEMPLATE", c.instance)
return util.GetUnixAddrWithAbsPath(resolved)
}
1 change: 1 addition & 0 deletions cmd/spire-agent/cli/run/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ type agentConfig struct {
ServerAddress string `hcl:"server_address"`
ServerPort int `hcl:"server_port"`
SocketPath string `hcl:"socket_path"`
Instance string `hcl:"instance"`
WorkloadX509SVIDKeyType string `hcl:"workload_x509_svid_key_type"`
TrustBundleFormat string `hcl:"trust_bundle_format"`
TrustBundlePath string `hcl:"trust_bundle_path"`
Expand Down
15 changes: 13 additions & 2 deletions cmd/spire-agent/cli/run/run_posix.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,33 @@
"github.com/spiffe/spire/pkg/agent"
common_cli "github.com/spiffe/spire/pkg/common/cli"
"github.com/spiffe/spire/pkg/common/util"
)

Check failure on line 18 in cmd/spire-agent/cli/run/run_posix.go

View workflow job for this annotation

GitHub Actions / lint (linux)

File is not properly formatted (gofmt)

func (c *agentConfig) addOSFlags(flags *flag.FlagSet) {
flags.StringVar(&c.SocketPath, "socketPath", "", "Path to bind the SPIRE Agent API socket to")
flags.StringVar(&c.Instance, "i", "", "Instance name to substitute into socket templates (env SPIFFE_PUBLIC_SOCKET_TEMPLATE and SPIRE_AGENT_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var(s) are set, defaults to 'main'.")
}

func (c *agentConfig) setPlatformDefaults() {
c.SocketPath = common.DefaultSocketPath
}

func (c *agentConfig) getAddr() (net.Addr, error) {
return util.GetUnixAddrWithAbsPath(c.SocketPath)
resolved := common.ResolveSocketPath(c.SocketPath, common.DefaultSocketPath, "SPIFFE_PUBLIC_SOCKET_TEMPLATE", c.Instance)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this variable should be SPIRE_PUBLIC_SOCKET_TEMPLATE. We can't use anything from the SPIFFE_ namespace since the spec might want to use that later on.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

return util.GetUnixAddrWithAbsPath(resolved)
}

func (c *agentConfig) getAdminAddr() (net.Addr, error) {
socketPathAbs, err := filepath.Abs(c.SocketPath)
tpl := os.Getenv("SPIRE_AGENT_PRIVATE_SOCKET_TEMPLATE")
if tpl != "" && strings.Contains(tpl, "%i") {
if c.Instance == "" {
c.Instance = "main"
}
if c.AdminSocketPath == "" {
c.AdminSocketPath = strings.ReplaceAll(tpl, "%i", c.Instance)
}
}

if err != nil {
return nil, fmt.Errorf("failed to get absolute path for socket_path: %w", err)
}
Expand Down
13 changes: 13 additions & 0 deletions cmd/spire-server/util/util_posix.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,35 @@ import (
"context"
"flag"
"net"
"os"
"strings"
)

type adapterOS struct {
socketPath string
instance string
}

func (a *Adapter) addOSFlags(flags *flag.FlagSet) {
flags.StringVar(&a.socketPath, "socketPath", DefaultSocketPath, "Path to the SPIRE Server API socket")
flags.StringVar(&a.instance, "i", "", "Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'.")
}

func (a *Adapter) getGRPCAddr() string {
if a.socketPath == "" {
a.socketPath = DefaultSocketPath
}

tpl := os.Getenv("SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE")
if tpl != "" && strings.Contains(tpl, "%i") {
if a.instance == "" {
a.instance = "main"
}
if a.socketPath == DefaultSocketPath {
a.socketPath = strings.ReplaceAll(tpl, "%i", a.instance)
}
}

// When grpc-go deprecated grpc.DialContext() in favor of grpc.NewClient(),
// they made a breaking change to always use the DNS resolver, even when overriding the context dialer.
// This is problematic for clients that do not use DNS for address resolution and don't set a resolver in the address.
Expand Down
29 changes: 29 additions & 0 deletions doc/multiple_instances.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Running Multiple SPIRE Instances

Systemd (and other init systems) can run multiple instances of the same service using a template unit. To make it easier to target a particular instance of `spire-agent` or `spire-server`, the CLI supports environment-variable socket templates and a short `-i` flag.

Environment variables

- `SPIFFE_PUBLIC_SOCKET_TEMPLATE` — template for agent public/workload socket, use `%i` for the instance name.
- `SPIRE_AGENT_PRIVATE_SOCKET_TEMPLATE` — template for agent private/admin socket, use `%i` for the instance name.
- `SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE` — template for server private API socket, use `%i` for the instance name.

Defaulting and precedence

When one of the above env vars is set and the `-i` flag is omitted, the CLI will default the instance name to `main` and substitute it into the template. If you explicitly pass a `-socketPath`/`-socket_path` value, that value takes precedence over the environment template substitution.

Examples

```bash
export SPIFFE_PUBLIC_SOCKET_TEMPLATE=/var/run/spire/agent/sockets/%i/public/spiffe.sock
spire-agent -i a api watch # uses instance "a"
spire-agent api watch # uses instance "main" (because env var is set)

export SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE=/var/run/spire/server/sockets/%i/private/api.sock
spire-server agent list # uses instance "main" if -i omitted
```

Notes

- The `-i` flag is a short alias for specifying the instance name on the CLI. When omitted and a relevant template env var is present, the CLI assumes instance mode and uses `main` unless `-i` is provided.
- This feature aims to simplify using systemd template units such as `spire-agent@.service` and `spire-server@.service`.
Loading