consul: include admin partition in JWT login requests (#22226)

When logging into a JWT auth method, we need to explicitly supply the Consul
admin partition if the local Consul agent is in a partition. We can't derive
this from agent configuration because the Consul agent's configuration is
canonical, so instead we get the partition from the fingerprint (if
available). This changeset updates the Consul client constructor so that we
close over the partition from the fingerprint.

Ref: https://hashicorp.atlassian.net/browse/NET-9451
This commit is contained in:
Tim Gross
2024-05-29 16:31:09 -04:00
committed by GitHub
parent de38ff4189
commit 140747240f
4 changed files with 53 additions and 34 deletions

3
.changelog/22226.txt Normal file
View File

@@ -0,0 +1,3 @@
```release-note:bug
consul: Fixed a bug where Consul admin partition was not used to login via Consul JWT auth method
```

View File

@@ -130,7 +130,7 @@ func (ar *allocRunner) initRunnerHooks(config *clientconfig.Config) error {
allocdir: ar.allocDir,
widmgr: ar.widmgr,
consulConfigs: ar.clientConfig.GetConsulConfigs(hookLogger),
consulClientConstructor: consul.NewConsulClient,
consulClientConstructor: consul.NewConsulClientFactory(config.Node),
hookResources: ar.hookResources,
envBuilder: newEnvBuilder,
logger: hookLogger,

View File

@@ -23,7 +23,7 @@ type consulHook struct {
allocdir allocdir.Interface
widmgr widmgr.IdentityManager
consulConfigs map[string]*structsc.ConsulConfig
consulClientConstructor func(*structsc.ConsulConfig, log.Logger) (consul.Client, error)
consulClientConstructor consul.ConsulClientFunc
hookResources *cstructs.AllocHookResources
envBuilder *taskenv.Builder
@@ -39,7 +39,7 @@ type consulHookConfig struct {
consulConfigs map[string]*structsc.ConsulConfig
// consulClientConstructor injects the function that will return a consul
// client (eases testing)
consulClientConstructor func(*structsc.ConsulConfig, log.Logger) (consul.Client, error)
consulClientConstructor consul.ConsulClientFunc
// hookResources is used for storing and retrieving Consul tokens
hookResources *cstructs.AllocHookResources

View File

@@ -63,43 +63,56 @@ type consulClient struct {
// client is the API client to interact with consul
client *consulapi.Client
// partition is the Consul partition for the local agent
partition string
// config is the configuration to connect to consul
config *config.ConsulConfig
logger hclog.Logger
}
// NewConsulClient creates a new Consul client
func NewConsulClient(config *config.ConsulConfig, logger hclog.Logger) (Client, error) {
if config == nil {
return nil, fmt.Errorf("nil consul config")
// ConsulClientFunc creates a new Consul client for the specific Consul config
type ConsulClientFunc func(config *config.ConsulConfig, logger hclog.Logger) (Client, error)
// NewConsulClientFactory returns a ConsulClientFunc that closes over the
// partition
func NewConsulClientFactory(node *structs.Node) ConsulClientFunc {
partition := node.Attributes["consul.partition"]
return func(config *config.ConsulConfig, logger hclog.Logger) (Client, error) {
if config == nil {
return nil, fmt.Errorf("nil consul config")
}
logger = logger.Named("consul").With("name", config.Name)
c := &consulClient{
config: config,
logger: logger,
partition: partition,
}
// Get the Consul API configuration
apiConf, err := config.ApiConfig()
if err != nil {
logger.Error("error creating default Consul API config", "error", err)
return nil, err
}
// Create the API client
client, err := consulapi.NewClient(apiConf)
if err != nil {
logger.Error("error creating Consul client", "error", err)
return nil, err
}
useragent.SetHeaders(client)
c.client = client
return c, nil
}
logger = logger.Named("consul").With("name", config.Name)
c := &consulClient{
config: config,
logger: logger,
}
// Get the Consul API configuration
apiConf, err := config.ApiConfig()
if err != nil {
logger.Error("error creating default Consul API config", "error", err)
return nil, err
}
// Create the API client
client, err := consulapi.NewClient(apiConf)
if err != nil {
logger.Error("error creating Consul client", "error", err)
return nil, err
}
useragent.SetHeaders(client)
c.client = client
return c, nil
}
// DeriveTokenWithJWT takes a JWT from request and returns a consul token.
@@ -108,7 +121,10 @@ func (c *consulClient) DeriveTokenWithJWT(req JWTLoginRequest) (*consulapi.ACLTo
AuthMethod: req.AuthMethodName,
BearerToken: req.JWT,
Meta: req.Meta,
}, nil)
}, &consulapi.WriteOptions{
Partition: c.partition,
})
return t, err
}