diff --git a/cmd/spire-agent/cli/api/api_posix_test.go b/cmd/spire-agent/cli/api/api_posix_test.go index 0e9856168e..34074cd9e5 100644 --- a/cmd/spire-agent/cli/api/api_posix_test.go +++ b/cmd/spire-agent/cli/api/api_posix_test.go @@ -8,6 +8,8 @@ const ( comma separated list of audience values -format value deprecated; use -output + -i string + Instance name to substitute into socket templates (env SPIRE_AGENT_PUBLIC_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -output value Desired output format (pretty, json); default: pretty. -socketPath string @@ -18,6 +20,8 @@ const ( Time to wait for a response (default 5s) ` fetchX509Usage = `Usage of fetch x509: + -i string + Instance name to substitute into socket templates (env SPIRE_AGENT_PUBLIC_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -output value Desired output format (pretty, json); default: pretty. -silent @@ -32,6 +36,8 @@ const ( validateJWTUsage = `Usage of validate jwt: -audience string expected audience value + -i string + Instance name to substitute into socket templates (env SPIRE_AGENT_PUBLIC_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -output value Desired output format (pretty, json); default: pretty. -socketPath string diff --git a/cmd/spire-agent/cli/common/config_posix.go b/cmd/spire-agent/cli/common/config_posix.go index acea475137..7448c8e1a9 100644 --- a/cmd/spire-agent/cli/common/config_posix.go +++ b/cmd/spire-agent/cli/common/config_posix.go @@ -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 SPIRE_AGENT_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, "SPIRE_AGENT_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, "SPIRE_AGENT_PUBLIC_SOCKET_TEMPLATE", c.instance) + addr, err := util.GetUnixAddrWithAbsPath(resolved) if err != nil { return "", err } diff --git a/cmd/spire-agent/cli/common/template.go b/cmd/spire-agent/cli/common/template.go new file mode 100644 index 0000000000..05c44e7939 --- /dev/null +++ b/cmd/spire-agent/cli/common/template.go @@ -0,0 +1,22 @@ +package common + +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 +} diff --git a/cmd/spire-agent/cli/healthcheck/healthcheck_posix.go b/cmd/spire-agent/cli/healthcheck/healthcheck_posix.go index f668904c92..65f29bc683 100644 --- a/cmd/spire-agent/cli/healthcheck/healthcheck_posix.go +++ b/cmd/spire-agent/cli/healthcheck/healthcheck_posix.go @@ -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 SPIRE_AGENT_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, "SPIRE_AGENT_PUBLIC_SOCKET_TEMPLATE", c.instance) + return util.GetUnixAddrWithAbsPath(resolved) } diff --git a/cmd/spire-agent/cli/healthcheck/healthcheck_posix_test.go b/cmd/spire-agent/cli/healthcheck/healthcheck_posix_test.go index 7781e48489..ed11379232 100644 --- a/cmd/spire-agent/cli/healthcheck/healthcheck_posix_test.go +++ b/cmd/spire-agent/cli/healthcheck/healthcheck_posix_test.go @@ -11,6 +11,8 @@ import ( var ( usage = `Usage of health: + -i string + Instance name to substitute into socket templates (env SPIRE_AGENT_PUBLIC_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -shallow Perform a less stringent health check -socketPath string diff --git a/cmd/spire-agent/cli/run/run.go b/cmd/spire-agent/cli/run/run.go index c5a334d7a9..be82df7b8d 100644 --- a/cmd/spire-agent/cli/run/run.go +++ b/cmd/spire-agent/cli/run/run.go @@ -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"` diff --git a/cmd/spire-agent/cli/run/run_posix.go b/cmd/spire-agent/cli/run/run_posix.go index 4c8ddd9d03..f6b1c0fc11 100644 --- a/cmd/spire-agent/cli/run/run_posix.go +++ b/cmd/spire-agent/cli/run/run_posix.go @@ -19,6 +19,7 @@ import ( 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 SPIRE_AGENT_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() { @@ -26,11 +27,22 @@ func (c *agentConfig) setPlatformDefaults() { } func (c *agentConfig) getAddr() (net.Addr, error) { - return util.GetUnixAddrWithAbsPath(c.SocketPath) + resolved := common.ResolveSocketPath(c.SocketPath, common.DefaultSocketPath, "SPIRE_AGENT_PUBLIC_SOCKET_TEMPLATE", c.Instance) + 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) } diff --git a/cmd/spire-server/cli/agent/agent_posix_test.go b/cmd/spire-server/cli/agent/agent_posix_test.go index 43ac27f7ad..9ae7494455 100644 --- a/cmd/spire-server/cli/agent/agent_posix_test.go +++ b/cmd/spire-server/cli/agent/agent_posix_test.go @@ -8,6 +8,8 @@ var ( Indicates that the command will not perform any action, but will print the agents that would be purged. -expiredFor duration Amount of time that has passed since the agent's SVID has expired. It is used to determine which agents to purge. (default 720h0m0s) + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -output value Desired output format (pretty, json); default: pretty. -socketPath string @@ -22,6 +24,8 @@ var ( Filter based on string received, 'true': agents that can reattest, 'false': agents that can't reattest, other value will return all. -expiresBefore string Filter by expiration time (format: "2006-01-02 15:04:05 -0700 -07") + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -matchSelectorsOn string The match mode used when filtering by selectors. Options: exact, any, superset and subset (default "superset") -output value @@ -32,6 +36,8 @@ var ( Path to the SPIRE Server API socket (default "/tmp/spire-server/private/api.sock") ` banUsage = `Usage of agent ban: + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -output value Desired output format (pretty, json); default: pretty. -socketPath string @@ -40,6 +46,8 @@ var ( The SPIFFE ID of the agent to ban (agent identity) ` evictUsage = `Usage of agent evict: + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -output value Desired output format (pretty, json); default: pretty. -socketPath string @@ -56,6 +64,8 @@ var ( Filter based on string received, 'true': agents that can reattest, 'false': agents that can't reattest, other value will return all. -expiresBefore string Filter by expiration time (format: "2006-01-02 15:04:05 -0700 -07") + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -matchSelectorsOn string The match mode used when filtering by selectors. Options: exact, any, superset and subset (default "superset") -output value @@ -66,6 +76,8 @@ var ( Path to the SPIRE Server API socket (default "/tmp/spire-server/private/api.sock") ` showUsage = `Usage of agent show: + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -output value Desired output format (pretty, json); default: pretty. -socketPath string diff --git a/cmd/spire-server/cli/bundle/bundle_posix_test.go b/cmd/spire-server/cli/bundle/bundle_posix_test.go index 040087a0f9..f19c1c6c14 100644 --- a/cmd/spire-server/cli/bundle/bundle_posix_test.go +++ b/cmd/spire-server/cli/bundle/bundle_posix_test.go @@ -6,6 +6,8 @@ var ( setUsage = `Usage of bundle set: -format string The format of the bundle data. Either "pem" or "spiffe". (default "pem") + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -id string SPIFFE ID of the trust domain -output value @@ -16,12 +18,16 @@ var ( Path to the SPIRE Server API socket (default "/tmp/spire-server/private/api.sock") ` countUsage = `Usage of bundle count: + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -output value Desired output format (pretty, json); default: pretty. -socketPath string Path to the SPIRE Server API socket (default "/tmp/spire-server/private/api.sock") ` deleteUsage = `Usage of bundle delete: + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -id string SPIFFE ID of the trust domain -mode string @@ -34,6 +40,8 @@ var ( listUsage = `Usage of bundle list: -format string The format to list federated bundles (only pretty output format supports this flag). Either "pem" or "spiffe". (default "pem") + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -id string SPIFFE ID of the trust domain -output value @@ -44,6 +52,8 @@ var ( showUsage = `Usage of bundle show: -format string The format to show the bundle (only pretty output format supports this flag). Either "pem" or "spiffe". (default "pem") + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -output value Desired output format (pretty, json); default: pretty. -socketPath string diff --git a/cmd/spire-server/cli/entry/util_posix_test.go b/cmd/spire-server/cli/entry/util_posix_test.go index ce8db79653..f9ae870d63 100644 --- a/cmd/spire-server/cli/entry/util_posix_test.go +++ b/cmd/spire-server/cli/entry/util_posix_test.go @@ -22,6 +22,8 @@ const ( SPIFFE ID of a trust domain to federate with. Can be used more than once -hint string The entry hint, used to disambiguate entries with the same SPIFFE ID + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -jwtSVIDTTL int The lifetime, in seconds, for JWT-SVIDs issued based on this registration entry. -node @@ -50,6 +52,8 @@ const ( SPIFFE ID of a trust domain an entry is federate with. Can be used more than once -hint string The Hint of the records to show (optional) + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -matchFederatesWithOn string The match mode used when filtering by federates with. Options: exact, any, superset and subset (default "superset") -matchSelectorsOn string @@ -84,6 +88,8 @@ const ( SPIFFE ID of a trust domain to federate with. Can be used more than once -hint string The entry hint, used to disambiguate entries with the same SPIFFE ID + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -jwtSVIDTTL int The lifetime, in seconds, for JWT-SVIDs issued based on this registration entry. -output value @@ -106,6 +112,8 @@ const ( The Registration Entry ID of the record to delete. -file string Path to a file containing a JSON structure for batch deletion (optional). If set to '-', read from stdin. + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -output value Desired output format (pretty, json); default: pretty. -socketPath string @@ -118,6 +126,8 @@ const ( SPIFFE ID of a trust domain an entry is federate with. Can be used more than once -hint string The Hint of the records to count (optional) + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -matchFederatesWithOn string The match mode used when filtering by federates with. Options: exact, any, superset and subset (default "superset") -matchSelectorsOn string diff --git a/cmd/spire-server/cli/federation/util_posix_test.go b/cmd/spire-server/cli/federation/util_posix_test.go index 4e4227fbc6..12c5ad1c30 100644 --- a/cmd/spire-server/cli/federation/util_posix_test.go +++ b/cmd/spire-server/cli/federation/util_posix_test.go @@ -12,6 +12,8 @@ const ( Path to a file containing federation relationships in JSON format (optional). If set to '-', read the JSON from stdin. -endpointSpiffeID string SPIFFE ID of the SPIFFE bundle endpoint server. Only used for 'spiffe' profile. + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -output value Desired output format (pretty, json); default: pretty. -socketPath string @@ -24,6 +26,8 @@ const ( Path to the trust domain bundle data (optional). ` deleteUsage = `Usage of federation delete: + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -id string SPIFFE ID of the trust domain -output value @@ -32,12 +36,16 @@ const ( Path to the SPIRE Server API socket (default "/tmp/spire-server/private/api.sock") ` listUsage = `Usage of federation list: + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -output value Desired output format (pretty, json); default: pretty. -socketPath string Path to the SPIRE Server API socket (default "/tmp/spire-server/private/api.sock") ` refreshUsage = `Usage of federation refresh: + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -id string SPIFFE ID of the trust domain -output value @@ -46,6 +54,8 @@ const ( Path to the SPIRE Server API socket (default "/tmp/spire-server/private/api.sock") ` showUsage = `Usage of federation show: + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -output value Desired output format (pretty, json); default: pretty. -socketPath string @@ -62,6 +72,8 @@ const ( Path to a file containing federation relationships in JSON format (optional). If set to '-', read the JSON from stdin. -endpointSpiffeID string SPIFFE ID of the SPIFFE bundle endpoint server. Only used for 'spiffe' profile. + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -output value Desired output format (pretty, json); default: pretty. -socketPath string diff --git a/cmd/spire-server/cli/healthcheck/healthcheck_posix_test.go b/cmd/spire-server/cli/healthcheck/healthcheck_posix_test.go index d56a982cf1..42ac7d5b20 100644 --- a/cmd/spire-server/cli/healthcheck/healthcheck_posix_test.go +++ b/cmd/spire-server/cli/healthcheck/healthcheck_posix_test.go @@ -4,6 +4,8 @@ package healthcheck var ( healthcheckUsage = `Usage of healthcheck: + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -shallow Perform a less stringent health check -socketPath string diff --git a/cmd/spire-server/cli/localauthority/jwt/jwt_activate_posix_test.go b/cmd/spire-server/cli/localauthority/jwt/jwt_activate_posix_test.go index 0c31fc4d2b..639fe4c923 100644 --- a/cmd/spire-server/cli/localauthority/jwt/jwt_activate_posix_test.go +++ b/cmd/spire-server/cli/localauthority/jwt/jwt_activate_posix_test.go @@ -6,6 +6,8 @@ var ( jwtActivateUsage = `Usage of localauthority jwt activate: -authorityID string The authority ID of the JWT authority to activate + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -output value Desired output format (pretty, json); default: pretty. -socketPath string diff --git a/cmd/spire-server/cli/localauthority/jwt/jwt_prepare_posix_test.go b/cmd/spire-server/cli/localauthority/jwt/jwt_prepare_posix_test.go index c8bafb0e22..1dc2f09027 100644 --- a/cmd/spire-server/cli/localauthority/jwt/jwt_prepare_posix_test.go +++ b/cmd/spire-server/cli/localauthority/jwt/jwt_prepare_posix_test.go @@ -4,6 +4,8 @@ package jwt_test var ( jwtPrepareUsage = `Usage of localauthority jwt prepare: + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -output value Desired output format (pretty, json); default: pretty. -socketPath string diff --git a/cmd/spire-server/cli/localauthority/jwt/jwt_revoke_posix_test.go b/cmd/spire-server/cli/localauthority/jwt/jwt_revoke_posix_test.go index 748e195020..ab5db6971c 100644 --- a/cmd/spire-server/cli/localauthority/jwt/jwt_revoke_posix_test.go +++ b/cmd/spire-server/cli/localauthority/jwt/jwt_revoke_posix_test.go @@ -6,6 +6,8 @@ var ( jwtRevokeUsage = `Usage of localauthority jwt revoke: -authorityID string The authority ID of the JWT authority to revoke + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -output value Desired output format (pretty, json); default: pretty. -socketPath string diff --git a/cmd/spire-server/cli/localauthority/jwt/jwt_show_posix_test.go b/cmd/spire-server/cli/localauthority/jwt/jwt_show_posix_test.go index 685692db23..25bfd34135 100644 --- a/cmd/spire-server/cli/localauthority/jwt/jwt_show_posix_test.go +++ b/cmd/spire-server/cli/localauthority/jwt/jwt_show_posix_test.go @@ -4,6 +4,8 @@ package jwt_test var ( jwtShowUsage = `Usage of localauthority jwt show: + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -output value Desired output format (pretty, json); default: pretty. -socketPath string diff --git a/cmd/spire-server/cli/localauthority/jwt/jwt_taint_posix_test.go b/cmd/spire-server/cli/localauthority/jwt/jwt_taint_posix_test.go index e761cc8503..9ed77fc45c 100644 --- a/cmd/spire-server/cli/localauthority/jwt/jwt_taint_posix_test.go +++ b/cmd/spire-server/cli/localauthority/jwt/jwt_taint_posix_test.go @@ -6,6 +6,8 @@ var ( jwtTaintUsage = `Usage of localauthority jwt taint: -authorityID string The authority ID of the JWT authority to taint + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -output value Desired output format (pretty, json); default: pretty. -socketPath string diff --git a/cmd/spire-server/cli/localauthority/x509/x509_activate_posix_test.go b/cmd/spire-server/cli/localauthority/x509/x509_activate_posix_test.go index 1ef5193439..f1b31c325c 100644 --- a/cmd/spire-server/cli/localauthority/x509/x509_activate_posix_test.go +++ b/cmd/spire-server/cli/localauthority/x509/x509_activate_posix_test.go @@ -6,6 +6,8 @@ var ( x509ActivateUsage = `Usage of localauthority x509 activate: -authorityID string The authority ID of the X.509 authority to activate + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -output value Desired output format (pretty, json); default: pretty. -socketPath string diff --git a/cmd/spire-server/cli/localauthority/x509/x509_prepare_posix_test.go b/cmd/spire-server/cli/localauthority/x509/x509_prepare_posix_test.go index 4d06a12d84..a4283a55df 100644 --- a/cmd/spire-server/cli/localauthority/x509/x509_prepare_posix_test.go +++ b/cmd/spire-server/cli/localauthority/x509/x509_prepare_posix_test.go @@ -4,6 +4,8 @@ package x509_test var ( x509PrepareUsage = `Usage of localauthority x509 prepare: + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -output value Desired output format (pretty, json); default: pretty. -socketPath string diff --git a/cmd/spire-server/cli/localauthority/x509/x509_revoke_posix_test.go b/cmd/spire-server/cli/localauthority/x509/x509_revoke_posix_test.go index 782282ed78..dfce752f37 100644 --- a/cmd/spire-server/cli/localauthority/x509/x509_revoke_posix_test.go +++ b/cmd/spire-server/cli/localauthority/x509/x509_revoke_posix_test.go @@ -6,6 +6,8 @@ var ( x509RevokeUsage = `Usage of localauthority x509 revoke: -authorityID string The authority ID of the X.509 authority to revoke + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -output value Desired output format (pretty, json); default: pretty. -socketPath string diff --git a/cmd/spire-server/cli/localauthority/x509/x509_show_posix_test.go b/cmd/spire-server/cli/localauthority/x509/x509_show_posix_test.go index ef30850643..2ec7b8bb39 100644 --- a/cmd/spire-server/cli/localauthority/x509/x509_show_posix_test.go +++ b/cmd/spire-server/cli/localauthority/x509/x509_show_posix_test.go @@ -4,6 +4,8 @@ package x509_test var ( x509ShowUsage = `Usage of localauthority x509 show: + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -output value Desired output format (pretty, json); default: pretty. -socketPath string diff --git a/cmd/spire-server/cli/localauthority/x509/x509_taint_posix_test.go b/cmd/spire-server/cli/localauthority/x509/x509_taint_posix_test.go index 5ff7043ab9..3a01516943 100644 --- a/cmd/spire-server/cli/localauthority/x509/x509_taint_posix_test.go +++ b/cmd/spire-server/cli/localauthority/x509/x509_taint_posix_test.go @@ -6,6 +6,8 @@ var ( x509TaintUsage = `Usage of localauthority x509 taint: -authorityID string The authority ID of the X.509 authority to taint + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -output value Desired output format (pretty, json); default: pretty. -socketPath string diff --git a/cmd/spire-server/cli/logger/get_posix_test.go b/cmd/spire-server/cli/logger/get_posix_test.go index 9e5cf4b3db..baab28cdbb 100644 --- a/cmd/spire-server/cli/logger/get_posix_test.go +++ b/cmd/spire-server/cli/logger/get_posix_test.go @@ -4,6 +4,8 @@ package logger_test var ( getUsage = `Usage of logger get: + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -output value Desired output format (pretty, json); default: pretty. -socketPath string diff --git a/cmd/spire-server/cli/logger/reset_posix_test.go b/cmd/spire-server/cli/logger/reset_posix_test.go index a52d116d7b..0f4b91cf8a 100644 --- a/cmd/spire-server/cli/logger/reset_posix_test.go +++ b/cmd/spire-server/cli/logger/reset_posix_test.go @@ -4,6 +4,8 @@ package logger_test var ( resetUsage = `Usage of logger reset: + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -output value Desired output format (pretty, json); default: pretty. -socketPath string diff --git a/cmd/spire-server/cli/logger/set_posix_test.go b/cmd/spire-server/cli/logger/set_posix_test.go index 4776830e8a..1438ef3f28 100644 --- a/cmd/spire-server/cli/logger/set_posix_test.go +++ b/cmd/spire-server/cli/logger/set_posix_test.go @@ -4,6 +4,8 @@ package logger_test var ( setUsage = `Usage of logger set: + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -level string The new log level, one of (panic, fatal, error, warn, info, debug, trace) -output value diff --git a/cmd/spire-server/cli/upstreamauthority/revoke_posix_test.go b/cmd/spire-server/cli/upstreamauthority/revoke_posix_test.go index e98f4dc476..5086bc618c 100644 --- a/cmd/spire-server/cli/upstreamauthority/revoke_posix_test.go +++ b/cmd/spire-server/cli/upstreamauthority/revoke_posix_test.go @@ -4,6 +4,8 @@ package upstreamauthority_test var ( revokeUsage = `Usage of upstreamauthority revoke: + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -output value Desired output format (pretty, json); default: pretty. -socketPath string diff --git a/cmd/spire-server/cli/upstreamauthority/taint_posix_test.go b/cmd/spire-server/cli/upstreamauthority/taint_posix_test.go index 73aaa858fc..6ecd063476 100644 --- a/cmd/spire-server/cli/upstreamauthority/taint_posix_test.go +++ b/cmd/spire-server/cli/upstreamauthority/taint_posix_test.go @@ -4,6 +4,8 @@ package upstreamauthority_test var ( taintUsage = `Usage of upstreamauthority taint: + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -output value Desired output format (pretty, json); default: pretty. -socketPath string diff --git a/cmd/spire-server/cli/wit/mint_posix_test.go b/cmd/spire-server/cli/wit/mint_posix_test.go new file mode 100644 index 0000000000..426a9eae74 --- /dev/null +++ b/cmd/spire-server/cli/wit/mint_posix_test.go @@ -0,0 +1,24 @@ +//go:build !windows + +package wit + +import ( + "github.com/spiffe/spire/test/clitest" +) + +var ( + expectedUsage = `Usage of wit mint: + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. + -keyType string + Key type of the WIT-SVID (default "ec-p256")` + clitest.AddrOutputForCasesWhereOptionsStartWithS + + ` -signingAlgorithm string + Signing algorithm for the workload signing key (default "ES256")` + clitest.AddrSocketPathUsageForCasesWhereOptionsStartWithS + + ` -spiffeID string + SPIFFE ID of the WIT-SVID + -ttl duration + TTL of the WIT-SVID + -write string + Directory to write output to instead of stdout +` +) diff --git a/cmd/spire-server/cli/wit/mint_test.go b/cmd/spire-server/cli/wit/mint_test.go index 32570ec68f..a5b8fea445 100644 --- a/cmd/spire-server/cli/wit/mint_test.go +++ b/cmd/spire-server/cli/wit/mint_test.go @@ -41,18 +41,6 @@ dlmdCTY3trEN+pXoR+kecSyZFcvjYBaND9mOPsSHCAc5AAtFPQF/j0H/ -----END PRIVATE KEY----- `)) availableFormats = []string{"pretty", "json"} - expectedUsage = `Usage of wit mint: - -keyType string - Key type of the WIT-SVID (default "ec-p256")` + clitest.AddrOutputForCasesWhereOptionsStartWithS + - ` -signingAlgorithm string - Signing algorithm for the workload signing key (default "ES256")` + clitest.AddrSocketPathUsageForCasesWhereOptionsStartWithS + - ` -spiffeID string - SPIFFE ID of the WIT-SVID - -ttl duration - TTL of the WIT-SVID - -write string - Directory to write output to instead of stdout -` ) func TestMintSynopsis(t *testing.T) { diff --git a/cmd/spire-server/cli/wit/mint_windows_test.go b/cmd/spire-server/cli/wit/mint_windows_test.go new file mode 100644 index 0000000000..c71f2e6e9c --- /dev/null +++ b/cmd/spire-server/cli/wit/mint_windows_test.go @@ -0,0 +1,22 @@ +//go:build windows + +package wit + +import ( + "github.com/spiffe/spire/test/clitest" +) + +var ( + expectedUsage = `Usage of wit mint: + -keyType string + Key type of the WIT-SVID (default "ec-p256")` + clitest.AddrOutputForCasesWhereOptionsStartWithS + + ` -signingAlgorithm string + Signing algorithm for the workload signing key (default "ES256")` + clitest.AddrSocketPathUsageForCasesWhereOptionsStartWithS + + ` -spiffeID string + SPIFFE ID of the WIT-SVID + -ttl duration + TTL of the WIT-SVID + -write string + Directory to write output to instead of stdout +` +) diff --git a/cmd/spire-server/util/util_posix.go b/cmd/spire-server/util/util_posix.go index c2de9d6641..f1cc1bd262 100644 --- a/cmd/spire-server/util/util_posix.go +++ b/cmd/spire-server/util/util_posix.go @@ -6,15 +6,18 @@ 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 { @@ -22,6 +25,16 @@ func (a *Adapter) getGRPCAddr() string { 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. diff --git a/doc/multiple_instances.md b/doc/multiple_instances.md new file mode 100644 index 0000000000..fe5a8d4fd2 --- /dev/null +++ b/doc/multiple_instances.md @@ -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 + +- `SPIRE_AGENT_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 SPIRE_AGENT_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`. diff --git a/test/clitest/common_posix.go b/test/clitest/common_posix.go index d0d470ebdb..6f1486729d 100644 --- a/test/clitest/common_posix.go +++ b/test/clitest/common_posix.go @@ -6,6 +6,8 @@ var ( AddrArg = "-socketPath" AddrError = "rpc error: code = Unavailable desc = connection error: desc = \"transport: Error while dialing: dial unix ///does-not-exist.sock: connect: no such file or directory\"\n" AddrOutputUsage = ` + -i string + Instance name to substitute into socket templates (env SPIRE_SERVER_PRIVATE_SOCKET_TEMPLATE). If omitted and the env var is set, defaults to 'main'. -output value Desired output format (pretty, json); default: pretty. -socketPath string