d0e2badbae
The result will still pass gofmtcheck and won't trigger additional changes if someone isn't using goimports, but it will avoid the piecemeal imports changes we've been seeing.
130 lines
3.1 KiB
Go
130 lines
3.1 KiB
Go
package kubernetes
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/hashicorp/errwrap"
|
|
hclog "github.com/hashicorp/go-hclog"
|
|
"github.com/hashicorp/vault/api"
|
|
"github.com/hashicorp/vault/command/agent/auth"
|
|
)
|
|
|
|
const (
|
|
serviceAccountFile = "/var/run/secrets/kubernetes.io/serviceaccount/token"
|
|
)
|
|
|
|
type kubernetesMethod struct {
|
|
logger hclog.Logger
|
|
mountPath string
|
|
|
|
role string
|
|
|
|
// tokenPath is an optional path to a projected service account token inside
|
|
// the pod, for use instead of the default service account token.
|
|
tokenPath string
|
|
|
|
// jwtData is a ReadCloser used to inject a ReadCloser for mocking tests.
|
|
jwtData io.ReadCloser
|
|
}
|
|
|
|
// NewKubernetesAuthMethod reads the user configuration and returns a configured
|
|
// AuthMethod
|
|
func NewKubernetesAuthMethod(conf *auth.AuthConfig) (auth.AuthMethod, error) {
|
|
if conf == nil {
|
|
return nil, errors.New("empty config")
|
|
}
|
|
if conf.Config == nil {
|
|
return nil, errors.New("empty config data")
|
|
}
|
|
|
|
k := &kubernetesMethod{
|
|
logger: conf.Logger,
|
|
mountPath: conf.MountPath,
|
|
}
|
|
|
|
roleRaw, ok := conf.Config["role"]
|
|
if !ok {
|
|
return nil, errors.New("missing 'role' value")
|
|
}
|
|
k.role, ok = roleRaw.(string)
|
|
if !ok {
|
|
return nil, errors.New("could not convert 'role' config value to string")
|
|
}
|
|
|
|
tokenPathRaw, ok := conf.Config["token_path"]
|
|
if ok {
|
|
k.tokenPath, ok = tokenPathRaw.(string)
|
|
if !ok {
|
|
return nil, errors.New("could not convert 'token_path' config value to string")
|
|
}
|
|
}
|
|
|
|
if k.role == "" {
|
|
return nil, errors.New("'role' value is empty")
|
|
}
|
|
|
|
return k, nil
|
|
}
|
|
|
|
func (k *kubernetesMethod) Authenticate(ctx context.Context, client *api.Client) (string, map[string]interface{}, error) {
|
|
k.logger.Trace("beginning authentication")
|
|
|
|
jwtString, err := k.readJWT()
|
|
if err != nil {
|
|
return "", nil, errwrap.Wrapf("error reading JWT with Kubernetes Auth: {{err}}", err)
|
|
}
|
|
|
|
return fmt.Sprintf("%s/login", k.mountPath), map[string]interface{}{
|
|
"role": k.role,
|
|
"jwt": jwtString,
|
|
}, nil
|
|
}
|
|
|
|
func (k *kubernetesMethod) NewCreds() chan struct{} {
|
|
return nil
|
|
}
|
|
|
|
func (k *kubernetesMethod) CredSuccess() {
|
|
}
|
|
|
|
func (k *kubernetesMethod) Shutdown() {
|
|
}
|
|
|
|
// readJWT reads the JWT data for the Agent to submit to Vault. The default is
|
|
// to read the JWT from the default service account location, defined by the
|
|
// constant serviceAccountFile. In normal use k.jwtData is nil at invocation and
|
|
// the method falls back to reading the token path with os.Open, opening a file
|
|
// from either the default location or from the token_path path specified in
|
|
// configuration.
|
|
func (k *kubernetesMethod) readJWT() (string, error) {
|
|
// load configured token path if set, default to serviceAccountFile
|
|
tokenFilePath := serviceAccountFile
|
|
if k.tokenPath != "" {
|
|
tokenFilePath = k.tokenPath
|
|
}
|
|
|
|
data := k.jwtData
|
|
// k.jwtData should only be non-nil in tests
|
|
if data == nil {
|
|
f, err := os.Open(tokenFilePath)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
data = f
|
|
}
|
|
defer data.Close()
|
|
|
|
contentBytes, err := ioutil.ReadAll(data)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return strings.TrimSpace(string(contentBytes)), nil
|
|
}
|