mirror of
https://github.com/kemko/nomad.git
synced 2026-01-01 16:05:42 +03:00
When a task restarts, the Nomad client may need to rewrite the Consul token, but it's created with permissions that prevent a non-root agent from writing to it. While Nomad clients should be run as root (currently), it's harmless to allow whatever user the Nomad agent is running as to be able to write to it, and that's one less barrier to rootless Nomad. Ref: https://github.com/hashicorp/nomad/issues/23859#issuecomment-2465757392
92 lines
2.3 KiB
Go
92 lines
2.3 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package taskrunner
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"slices"
|
|
"strings"
|
|
|
|
log "github.com/hashicorp/go-hclog"
|
|
"github.com/hashicorp/go-multierror"
|
|
"github.com/hashicorp/nomad/client/allocrunner/interfaces"
|
|
cstructs "github.com/hashicorp/nomad/client/structs"
|
|
"github.com/hashicorp/nomad/nomad/structs"
|
|
)
|
|
|
|
const (
|
|
// consulTokenFilename is the name of the file holding the Consul SI token
|
|
// inside the task's secret directory.
|
|
consulTokenFilename = "consul_token"
|
|
|
|
// consulTokenFilePerms is the level of file permissions granted on the file in
|
|
// the secrets directory for the task
|
|
consulTokenFilePerms = 0640
|
|
)
|
|
|
|
type consulHook struct {
|
|
task *structs.Task
|
|
tokenDir string
|
|
hookResources *cstructs.AllocHookResources
|
|
|
|
logger log.Logger
|
|
}
|
|
|
|
func newConsulHook(logger log.Logger, tr *TaskRunner) *consulHook {
|
|
h := &consulHook{
|
|
task: tr.Task(),
|
|
tokenDir: tr.taskDir.SecretsDir,
|
|
hookResources: tr.allocHookResources,
|
|
}
|
|
h.logger = logger.Named(h.Name())
|
|
return h
|
|
}
|
|
|
|
func (*consulHook) Name() string {
|
|
return "consul_task"
|
|
}
|
|
|
|
func (h *consulHook) Prestart(ctx context.Context, req *interfaces.TaskPrestartRequest, resp *interfaces.TaskPrestartResponse) error {
|
|
mErr := multierror.Error{}
|
|
|
|
tokens := h.hookResources.GetConsulTokens()
|
|
|
|
// Write tokens to tasks' secret dirs
|
|
for _, t := range tokens {
|
|
for tokenName, token := range t {
|
|
s := strings.SplitN(tokenName, "/", 2)
|
|
if len(s) < 2 {
|
|
continue
|
|
}
|
|
identity := s[0]
|
|
taskName := s[1]
|
|
// do not write tokens that do not belong to any of this task's
|
|
// identities
|
|
if taskName != h.task.Name || !slices.ContainsFunc(
|
|
h.task.Identities,
|
|
func(id *structs.WorkloadIdentity) bool { return id.Name == identity }) &&
|
|
identity != h.task.Identity.Name {
|
|
continue
|
|
}
|
|
|
|
tokenPath := filepath.Join(h.tokenDir, consulTokenFilename)
|
|
if err := os.WriteFile(tokenPath, []byte(token.SecretID), consulTokenFilePerms); err != nil {
|
|
mErr.Errors = append(mErr.Errors, fmt.Errorf("failed to write Consul SI token: %w", err))
|
|
}
|
|
|
|
env := map[string]string{
|
|
"CONSUL_TOKEN": token.SecretID,
|
|
"CONSUL_HTTP_TOKEN": token.SecretID,
|
|
}
|
|
|
|
resp.Env = env
|
|
}
|
|
}
|
|
|
|
return mErr.ErrorOrNil()
|
|
}
|