2017-09-19 14:27:26 +00:00
|
|
|
package kubeauth
|
|
|
|
|
|
|
|
import (
|
2018-01-19 10:56:34 +00:00
|
|
|
"context"
|
2017-09-19 14:27:26 +00:00
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
"github.com/hashicorp/vault/logical"
|
|
|
|
"github.com/hashicorp/vault/logical/framework"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
configPath string = "config"
|
|
|
|
rolePrefix string = "role/"
|
|
|
|
)
|
|
|
|
|
|
|
|
// kubeAuthBackend implements logical.Backend
|
|
|
|
type kubeAuthBackend struct {
|
|
|
|
*framework.Backend
|
|
|
|
|
|
|
|
// reviewFactory is used to configure the strategy for doing a token review.
|
|
|
|
// Currently the only options are using the kubernetes API or mocking the
|
|
|
|
// review. Mocks should only be used in tests.
|
|
|
|
reviewFactory tokenReviewFactory
|
|
|
|
|
|
|
|
l sync.RWMutex
|
|
|
|
}
|
|
|
|
|
|
|
|
// Factory returns a new backend as logical.Backend.
|
2018-01-19 10:56:34 +00:00
|
|
|
func Factory(ctx context.Context, conf *logical.BackendConfig) (logical.Backend, error) {
|
2017-09-19 14:27:26 +00:00
|
|
|
b := Backend()
|
2018-01-19 10:56:34 +00:00
|
|
|
if err := b.Setup(ctx, conf); err != nil {
|
2017-09-19 14:27:26 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return b, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func Backend() *kubeAuthBackend {
|
|
|
|
b := &kubeAuthBackend{}
|
|
|
|
|
|
|
|
b.Backend = &framework.Backend{
|
2018-01-08 20:26:13 +00:00
|
|
|
AuthRenew: b.pathLoginRenew(),
|
2017-09-19 14:27:26 +00:00
|
|
|
BackendType: logical.TypeCredential,
|
|
|
|
Help: backendHelp,
|
|
|
|
PathsSpecial: &logical.Paths{
|
|
|
|
Unauthenticated: []string{
|
|
|
|
"login",
|
|
|
|
},
|
2017-12-21 13:31:20 +00:00
|
|
|
SealWrapStorage: []string{
|
|
|
|
configPath,
|
|
|
|
},
|
2017-09-19 14:27:26 +00:00
|
|
|
},
|
|
|
|
Paths: framework.PathAppend(
|
|
|
|
[]*framework.Path{
|
|
|
|
pathConfig(b),
|
|
|
|
pathLogin(b),
|
|
|
|
},
|
|
|
|
pathsRole(b),
|
|
|
|
),
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the review factory to default to calling into the kubernetes API.
|
|
|
|
b.reviewFactory = tokenReviewAPIFactory
|
|
|
|
|
|
|
|
return b
|
|
|
|
}
|
|
|
|
|
|
|
|
// config takes a storage object and returns a kubeConfig object
|
2018-01-19 10:56:34 +00:00
|
|
|
func (b *kubeAuthBackend) config(ctx context.Context, s logical.Storage) (*kubeConfig, error) {
|
|
|
|
raw, err := s.Get(ctx, configPath)
|
2017-09-19 14:27:26 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if raw == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
conf := &kubeConfig{}
|
|
|
|
if err := json.Unmarshal(raw.Value, conf); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse the public keys from the CertificatesBytes
|
|
|
|
conf.PublicKeys = make([]interface{}, len(conf.PEMKeys))
|
|
|
|
for i, cert := range conf.PEMKeys {
|
|
|
|
conf.PublicKeys[i], err = parsePublicKeyPEM([]byte(cert))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return conf, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// role takes a storage backend and the name and returns the role's storage
|
|
|
|
// entry
|
2018-01-19 10:56:34 +00:00
|
|
|
func (b *kubeAuthBackend) role(ctx context.Context, s logical.Storage, name string) (*roleStorageEntry, error) {
|
|
|
|
raw, err := s.Get(ctx, fmt.Sprintf("%s%s", rolePrefix, strings.ToLower(name)))
|
2017-09-19 14:27:26 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if raw == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
role := &roleStorageEntry{}
|
|
|
|
if err := json.Unmarshal(raw.Value, role); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return role, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var backendHelp string = `
|
|
|
|
The Kubernetes Auth Backend allows authentication for Kubernetes service accounts.
|
|
|
|
`
|