update deps (#3425)

Update deps for Kubernetes and GCP auth plugins
This commit is contained in:
Vishal Nayak 2017-10-04 22:59:12 -04:00 committed by GitHub
parent 3f7454939e
commit b46c42179e
19 changed files with 619 additions and 126 deletions

View File

@ -0,0 +1,207 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
name = "cloud.google.com/go"
packages = ["compute/metadata"]
revision = "5a9e19d4e1e41a734154e44a2132b358afb49a03"
version = "v0.13.0"
[[projects]]
name = "github.com/SermoDigital/jose"
packages = [".","crypto","jws","jwt"]
revision = "f6df55f235c24f236d11dbcf665249a59ac2021f"
version = "1.1"
[[projects]]
branch = "master"
name = "github.com/armon/go-radix"
packages = ["."]
revision = "1fca145dffbcaa8fe914309b1ec0cfc67500fe61"
[[projects]]
name = "github.com/fatih/structs"
packages = ["."]
revision = "a720dfa8df582c51dee1b36feabb906bde1588bd"
version = "v1.0"
[[projects]]
branch = "master"
name = "github.com/golang/protobuf"
packages = ["proto","ptypes","ptypes/any","ptypes/duration","ptypes/timestamp"]
revision = "17ce1425424ab154092bbb43af630bd647f3bb0d"
[[projects]]
branch = "master"
name = "github.com/golang/snappy"
packages = ["."]
revision = "553a641470496b2327abcac10b36396bd98e45c9"
[[projects]]
branch = "master"
name = "github.com/hashicorp/errwrap"
packages = ["."]
revision = "7554cd9344cec97297fa6649b055a8c98c2a1e55"
[[projects]]
branch = "master"
name = "github.com/hashicorp/go-cleanhttp"
packages = ["."]
revision = "3573b8b52aa7b37b9358d966a898feb387f62437"
[[projects]]
branch = "master"
name = "github.com/hashicorp/go-hclog"
packages = ["."]
revision = "8105cc0a3736cc153a2025f5d0d91b80045fc9ff"
[[projects]]
branch = "master"
name = "github.com/hashicorp/go-multierror"
packages = ["."]
revision = "83588e72410abfbe4df460eeb6f30841ae47d4c4"
[[projects]]
branch = "master"
name = "github.com/hashicorp/go-plugin"
packages = ["."]
revision = "3e6d191694b5a3a2b99755f31b47fa209e4bcd09"
[[projects]]
branch = "master"
name = "github.com/hashicorp/go-rootcerts"
packages = ["."]
revision = "6bb64b370b90e7ef1fa532be9e591a81c3493e00"
[[projects]]
branch = "master"
name = "github.com/hashicorp/go-uuid"
packages = ["."]
revision = "64130c7a86d732268a38cb04cfbaf0cc987fda98"
[[projects]]
branch = "master"
name = "github.com/hashicorp/hcl"
packages = [".","hcl/ast","hcl/parser","hcl/scanner","hcl/strconv","hcl/token","json/parser","json/scanner","json/token"]
revision = "68e816d1c783414e79bc65b3994d9ab6b0a722ab"
[[projects]]
branch = "master"
name = "github.com/hashicorp/vault"
packages = ["api","helper/certutil","helper/compressutil","helper/consts","helper/errutil","helper/jsonutil","helper/logformat","helper/mlock","helper/parseutil","helper/pluginutil","helper/policyutil","helper/salt","helper/strutil","helper/wrapping","logical","logical/framework","logical/plugin","version"]
revision = "27197f728e7fc8bffc9eaa59e8af4e0766b81320"
[[projects]]
branch = "master"
name = "github.com/hashicorp/yamux"
packages = ["."]
revision = "d1caa6c97c9fc1cc9e83bbe34d0603f9ff0ce8bd"
[[projects]]
name = "github.com/mattn/go-colorable"
packages = ["."]
revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072"
version = "v0.0.9"
[[projects]]
name = "github.com/mattn/go-isatty"
packages = ["."]
revision = "fc9e8d8ef48496124e79ae0df75490096eccf6fe"
version = "v0.0.2"
[[projects]]
branch = "master"
name = "github.com/mgutz/ansi"
packages = ["."]
revision = "9520e82c474b0a04dd04f8a40959027271bab992"
[[projects]]
name = "github.com/mgutz/logxi"
packages = ["v1"]
revision = "aebf8a7d67ab4625e0fd4a665766fef9a709161b"
version = "v1"
[[projects]]
branch = "master"
name = "github.com/mitchellh/go-homedir"
packages = ["."]
revision = "b8bc1bf767474819792c23f32d8286a45736f1c6"
[[projects]]
branch = "master"
name = "github.com/mitchellh/go-testing-interface"
packages = ["."]
revision = "7bf6f6eaf1bed2fd3c6c63114b18cb64facb9de2"
[[projects]]
branch = "master"
name = "github.com/mitchellh/mapstructure"
packages = ["."]
revision = "d0303fe809921458f417bcf828397a65db30a7e4"
[[projects]]
branch = "master"
name = "github.com/sethgrid/pester"
packages = ["."]
revision = "a86a2d88f4dc3c7dbf3a6a6bbbfb095690b834b6"
[[projects]]
branch = "master"
name = "golang.org/x/net"
packages = ["context","context/ctxhttp","http2","http2/hpack","idna","internal/timeseries","lex/httplex","trace"]
revision = "859d1a86bb617c0c20d154590c3c5d3fcb670b07"
[[projects]]
branch = "master"
name = "golang.org/x/oauth2"
packages = [".","google","internal","jws","jwt"]
revision = "13449ad91cb26cb47661c1b080790392170385fd"
[[projects]]
branch = "master"
name = "golang.org/x/sys"
packages = ["unix"]
revision = "062cd7e4e68206d8bab9b18396626e855c992658"
[[projects]]
branch = "master"
name = "golang.org/x/text"
packages = ["collate","collate/build","internal/colltab","internal/gen","internal/tag","internal/triegen","internal/ucd","language","secure/bidirule","transform","unicode/bidi","unicode/cldr","unicode/norm","unicode/rangetable"]
revision = "ab5ac5f9a8deb4855a60fab02bc61a4ec770bd49"
[[projects]]
branch = "master"
name = "google.golang.org/api"
packages = ["compute/v1","gensupport","googleapi","googleapi/internal/uritemplates","iam/v1","oauth2/v2"]
revision = "519500316f39a9934d1d88615a1a047035a4bae5"
[[projects]]
name = "google.golang.org/appengine"
packages = [".","internal","internal/app_identity","internal/base","internal/datastore","internal/log","internal/modules","internal/remote_api","internal/urlfetch","urlfetch"]
revision = "150dc57a1b433e64154302bdc40b6bb8aefa313a"
version = "v1.0.0"
[[projects]]
branch = "master"
name = "google.golang.org/genproto"
packages = ["googleapis/rpc/status"]
revision = "595979c8a7bf586b2d293fb42246bf91a0b893d9"
[[projects]]
name = "google.golang.org/grpc"
packages = [".","codes","connectivity","credentials","grpclb/grpc_lb_v1/messages","grpclog","health","health/grpc_health_v1","internal","keepalive","metadata","naming","peer","stats","status","tap","transport"]
revision = "f92cdcd7dcdc69e81b2d7b338479a19a8723cfa3"
version = "v1.6.0"
[[projects]]
name = "gopkg.in/square/go-jose.v2"
packages = [".","cipher","json","jwt"]
revision = "b25e6cab129e4a54675b42ea49d38e9c33ade9e6"
version = "v2.1.2"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "f9ed330c7039eae92bba02edc9d8463bb8f3dd93229e287004acc600825f9e92"
solver-name = "gps-cdcl"
solver-version = 1

View File

@ -0,0 +1,46 @@
# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
[[constraint]]
name = "github.com/SermoDigital/jose"
version = "1.1.0"
[[constraint]]
branch = "master"
name = "github.com/hashicorp/go-cleanhttp"
[[constraint]]
name = "github.com/hashicorp/vault"
branch = "master"
[[constraint]]
name = "github.com/mgutz/logxi"
version = "1.0.0"
[[constraint]]
branch = "master"
name = "golang.org/x/oauth2"
[[constraint]]
branch = "master"
name = "google.golang.org/api"

View File

@ -0,0 +1,58 @@
TOOL?=vault-gcp-auth-plugin
TEST?=$$(go list ./... | grep -v /vendor/)
VETARGS?=-asmdecl -atomic -bool -buildtags -copylocks -methods -nilfunc -printf -rangeloops -shift -structtags -unsafeptr
EXTERNAL_TOOLS=\
github.com/mitchellh/gox \
github.com/kardianos/govendor
BUILD_TAGS?=${TOOL}
GOFMT_FILES?=$$(find . -name '*.go' | grep -v vendor)
# bin generates the releaseable binaries for this plugin
bin: fmtcheck generate
@CGO_ENABLED=0 BUILD_TAGS='$(BUILD_TAGS)' sh -c "'$(CURDIR)/scripts/build.sh'"
default: dev
# dev creates binaries for testing Vault locally. These are put
# into ./bin/ as well as $GOPATH/bin, except for quickdev which
# is only put into /bin/
quickdev: generate
@CGO_ENABLED=0 go build -i -tags='$(BUILD_TAGS)' -o bin/vault-gcp-auth-plugin
dev: fmtcheck generate
@CGO_ENABLED=0 BUILD_TAGS='$(BUILD_TAGS)' VAULT_DEV_BUILD=1 sh -c "'$(CURDIR)/scripts/build.sh'"
dev-dynamic: generate
@CGO_ENABLED=1 BUILD_TAGS='$(BUILD_TAGS)' VAULT_DEV_BUILD=1 sh -c "'$(CURDIR)/scripts/build.sh'"
testcompile: fmtcheck generate
@for pkg in $(TEST) ; do \
go test -v -c -tags='$(BUILD_TAGS)' $$pkg -parallel=4 ; \
done
# test runs all tests
test: fmtcheck generate
@if [ "$(TEST)" = "./..." ]; then \
echo "ERROR: Set TEST to a specific package"; \
exit 1; \
fi
VAULT_ACC=1 go test -tags='$(BUILD_TAGS)' $(TEST) -v $(TESTARGS) -timeout 45m
# generate runs `go generate` to build the dynamically generated
# source files.
generate:
go generate $(go list ./... | grep -v /vendor/)
# bootstrap the build by downloading additional tools
bootstrap:
@for tool in $(EXTERNAL_TOOLS) ; do \
echo "Installing/Updating $$tool" ; \
go get -u $$tool; \
done
fmtcheck:
@sh -c "'$(CURDIR)/scripts/gofmtcheck.sh'"
fmt:
gofmt -w $(GOFMT_FILES)
.PHONY: bin default generate test vet bootstrap fmt fmtcheck

View File

@ -0,0 +1,149 @@
# Vault Plugin: Google Cloud Platform Auth Backend
This is a standalone backend plugin for use with [Hashicorp Vault](https://www.github.com/hashicorp/vault).
This plugin allows for various GCP entities to authenticate with Vault.
This is currently included in Vault distributions.
Currently, this plugin supports login for:
- IAM service accounts
- GCE Instances
**Please note**: We take Vault's security and our users' trust very seriously. If you believe you have found a security issue in Vault, _please responsibly disclose_ by contacting us at [security@hashicorp.com](mailto:security@hashicorp.com).
## Quick Links
- [Vault Website](https://www.vaultproject.io)
- [GCP Auth BE Docs](https://www.vaultproject.io/docs/auth/gcp.html)
- [Vault Github](https://www.github.com/hashicorp/vault)
- [General Announcement List](https://groups.google.com/forum/#!forum/hashicorp-announce)
- [Discussion List](https://groups.google.com/forum/#!forum/vault-tool)
## Getting Started
This is a [Vault plugin](https://www.vaultproject.io/docs/internals/plugins.html)
and is meant to work with Vault. This guide assumes you have already installed Vault
and have a basic understanding of how Vault works.
Otherwise, first read this guide on how to [get started with Vault](https://www.vaultproject.io/intro/getting-started/install.html).
To learn specifically about how plugins work, see documentation on [Vault plugins](https://www.vaultproject.io/docs/internals/plugins.html).
### Usage
Please see [documentation for the plugin](https://www.vaultproject.io/docs/auth/gcp.html)
on the Vault website.
This plugin is currently built into Vault and by default is accessed
at `auth/gcp`. To enable this in a running Vault server:
```sh
$ vault auth-enable 'gcp'
Successfully enabled 'gcp' at 'gcp'!
```
To see all the supported paths, see the [GCP auth backend docs](https://www.vaultproject.io/docs/auth/gcp.html).
## Developing
If you wish to work on this plugin, you'll first need
[Go](https://www.golang.org) installed on your machine
(version 1.8+ is *required*).
For local dev first make sure Go is properly installed, including
setting up a [GOPATH](https://golang.org/doc/code.html#GOPATH).
Next, clone this repository into
`$GOPATH/src/github.com/hashicorp/vault-gcp-auth-plugin`.
You can then download any required build tools by bootstrapping your
environment:
```sh
$ make bootstrap
```
To compile a development version of this plugin, run `make` or `make dev`.
This will put the plugin binary in the `bin` and `$GOPATH/bin` folders. `dev`
mode will only generate the binary for your platform and is faster:
```sh
$ make
$ make dev
```
Put the plugin binary into a location of your choice. This directory
will be specified as the [`plugin_directory`](https://www.vaultproject.io/docs/configuration/index.html#plugin_directory)
in the Vault config used to start the server.
```json
...
plugin_directory = "path/to/plugin/directory"
...
```
Start a Vault server with this config file:
```sh
$ vault server -config=path/to/config.json ...
...
```
Once the server is started, register the plugin in the Vault server's [plugin catalog](https://www.vaultproject.io/docs/internals/plugins.html#plugin-catalog):
```sh
$ vault write sys/plugins/catalog/mygcpplugin \
sha_256=<expected SHA256 Hex value of the plugin binary> \
command="vault-plugin-auth-gcp"
...
Success! Data written to: sys/plugins/catalog/mygcpplugin
```
Note you should generate a new sha256 checksum if you have made changes
to the plugin. Example using openssl:
```sh
openssl dgst -sha256 $GOPATH/vault-plugin-gcp-auth
...
SHA256(.../go/bin/vault-plugin-auth-gcp)= 896c13c0f5305daed381952a128322e02bc28a57d0c862a78cbc2ea66e8c6fa1
```
Any name can be substituted for the plugin name "mygcpplugin". This
name will be referenced in the next step, where we enable the auth
plugin backend using the GCP auth plugin:
```sh
$ vault auth-enable -plugin-name='mygcpplugin' -path='gcp' plugin
...
Successfully enabled 'plugin' at 'gcp'!
```
#### Tests
This plugin has comprehensive [acceptance tests](https://en.wikipedia.org/wiki/Acceptance_testing)
covering most of the features of this auth backend.
If you are developing this plugin and want to verify it is still
functioning (and you haven't broken anything else), we recommend
running the acceptance tests.
Acceptance tests typically require other environment variables to be set for
things such as access keys. The test itself should error early and tell
you what to set, so it is not documented here.
**Warning:** The acceptance tests create/destroy/modify *real resources*,
which may incur real costs in some cases. In the presence of a bug,
it is technically possible that broken backends could leave dangling
data behind. Therefore, please run the acceptance tests at your own risk.
At the very least, we recommend running them in their own private
account for whatever backend you're testing.
To run the acceptance tests, invoke `make test`:
```sh
$ make test
```
You can also specify a `TESTARGS` variable to filter tests like so:
```sh
$ make test TESTARGS='--run=TestConfig'
```

View File

@ -0,0 +1,28 @@
package main
import (
"log"
"os"
gcpbackend "github.com/hashicorp/vault-plugin-auth-gcp/plugin"
"github.com/hashicorp/vault/helper/pluginutil"
"github.com/hashicorp/vault/logical/plugin"
)
func main() {
apiClientMeta := &pluginutil.APIClientMeta{}
flags := apiClientMeta.FlagSet()
flags.Parse(os.Args[1:])
tlsConfig := apiClientMeta.GetTLSConfig()
tlsProviderFunc := pluginutil.VaultPluginTLSProvider(tlsConfig)
err := plugin.Serve(&plugin.ServeOpts{
BackendFactoryFunc: gcpbackend.Factory,
TLSProviderFunc: tlsProviderFunc,
})
if err != nil {
log.Println(err)
os.Exit(1)
}
}

View File

@ -3,6 +3,10 @@ package gcpauth
import (
"errors"
"fmt"
"strconv"
"strings"
"time"
"github.com/hashicorp/vault-plugin-auth-gcp/plugin/util"
"github.com/hashicorp/vault/helper/policyutil"
"github.com/hashicorp/vault/helper/strutil"
@ -11,9 +15,6 @@ import (
"google.golang.org/api/compute/v1"
"google.golang.org/api/iam/v1"
"gopkg.in/square/go-jose.v2/jwt"
"strconv"
"strings"
"time"
)
const (
@ -39,8 +40,8 @@ GCE identity metadata token ('iam', 'gce' roles).`,
},
Callbacks: map[logical.Operation]framework.OperationFunc{
logical.UpdateOperation: b.pathLogin,
logical.PersonaLookaheadOperation: b.pathLogin,
logical.UpdateOperation: b.pathLogin,
logical.AliasLookaheadOperation: b.pathLogin,
},
HelpSynopsis: pathLoginHelpSyn,
@ -82,11 +83,11 @@ func (b *GcpAuthBackend) pathLoginRenew(req *logical.Request, data *framework.Fi
switch role.RoleType {
case iamRoleType:
if err := b.pathIamRenew(req, role); err != nil {
if err := b.pathIamRenew(req, roleName, role); err != nil {
return logical.ErrorResponse(err.Error()), nil
}
case gceRoleType:
if err := b.pathGceRenew(req, role); err != nil {
if err := b.pathGceRenew(req, roleName, role); err != nil {
return logical.ErrorResponse(err.Error()), nil
}
default:
@ -263,7 +264,7 @@ func (b *GcpAuthBackend) pathIamLogin(req *logical.Request, loginInfo *gcpLoginI
role := loginInfo.Role
if !role.AllowGCEInference && loginInfo.GceMetadata != nil {
return logical.ErrorResponse(fmt.Sprintf(
"IAM role '%s' does not allow gce inference but GCE instance metadata token given", loginInfo.RoleName)), nil
"Got GCE token but IAM role '%s' does not allow GCE inference", loginInfo.RoleName)), nil
}
// TODO(emilymye): move to general JWT validation once custom expiry is supported for other JWT types.
@ -280,10 +281,10 @@ func (b *GcpAuthBackend) pathIamLogin(req *logical.Request, loginInfo *gcpLoginI
return nil, errors.New("service account is empty")
}
if req.Operation == logical.PersonaLookaheadOperation {
if req.Operation == logical.AliasLookaheadOperation {
return &logical.Response{
Auth: &logical.Auth{
Persona: &logical.Persona{
Alias: &logical.Alias{
Name: serviceAccount.UniqueId,
},
},
@ -298,15 +299,11 @@ func (b *GcpAuthBackend) pathIamLogin(req *logical.Request, loginInfo *gcpLoginI
resp := &logical.Response{
Auth: &logical.Auth{
Period: role.Period,
Persona: &logical.Persona{
Alias: &logical.Alias{
Name: serviceAccount.UniqueId,
},
Policies: role.Policies,
Metadata: map[string]string{
"service_account_id": serviceAccount.UniqueId,
"service_account_email": serviceAccount.Email,
"role": loginInfo.RoleName,
},
Policies: role.Policies,
Metadata: authMetadata(loginInfo, serviceAccount),
DisplayName: serviceAccount.Email,
LeaseOptions: logical.LeaseOptions{
Renewable: true,
@ -320,7 +317,7 @@ func (b *GcpAuthBackend) pathIamLogin(req *logical.Request, loginInfo *gcpLoginI
// pathIamRenew returns an error if the service account referenced in the auth token metadata cannot renew the
// auth token for the given role.
func (b *GcpAuthBackend) pathIamRenew(req *logical.Request, role *gcpRole) error {
func (b *GcpAuthBackend) pathIamRenew(req *logical.Request, roleName string, role *gcpRole) error {
iamClient, err := b.IAM(req.Storage)
if err != nil {
return fmt.Errorf(clientErrorTemplate, "IAM", err)
@ -336,8 +333,13 @@ func (b *GcpAuthBackend) pathIamRenew(req *logical.Request, role *gcpRole) error
return fmt.Errorf("cannot find service account %s", serviceAccountId)
}
_, isGceInferred := req.Auth.Metadata["instance_id"]
if isGceInferred && !role.AllowGCEInference {
return fmt.Errorf("GCE inferrence is no longer allowed for role %s", roleName)
}
if err := b.authorizeIAMServiceAccount(serviceAccount, role); err != nil {
return errors.New("service account is no longer authorized for role")
return fmt.Errorf("service account is no longer authorized for role %s", roleName)
}
return nil
@ -370,8 +372,6 @@ func (b *GcpAuthBackend) authorizeIAMServiceAccount(serviceAccount *iam.ServiceA
func (b *GcpAuthBackend) pathGceLogin(req *logical.Request, loginInfo *gcpLoginInfo) (*logical.Response, error) {
role := loginInfo.Role
metadata := loginInfo.GceMetadata
fmt.Printf("here\n")
fmt.Printf("metadata.CreatedAt \n")
if metadata == nil {
return logical.ErrorResponse("could not get GCE metadata from given JWT"), nil
}
@ -399,24 +399,27 @@ func (b *GcpAuthBackend) pathGceLogin(req *logical.Request, loginInfo *gcpLoginI
return logical.ErrorResponse(err.Error()), nil
}
iamClient, err := b.IAM(req.Storage)
if err != nil {
return logical.ErrorResponse(fmt.Sprintf(clientErrorTemplate, "IAM", err)), nil
}
serviceAccount, err := util.ServiceAccount(iamClient, loginInfo.ServiceAccountId, loginInfo.Role.ProjectId)
if err != nil {
return logical.ErrorResponse(fmt.Sprintf(
"Could not find service account '%s' used for GCE metadata token: %s",
loginInfo.ServiceAccountId, err)), nil
}
resp := &logical.Response{
Auth: &logical.Auth{
InternalData: map[string]interface{}{},
Period: role.Period,
Persona: &logical.Persona{
Alias: &logical.Alias{
Name: fmt.Sprintf("gce-%s", strconv.FormatUint(instance.Id, 10)),
},
Policies: role.Policies,
Metadata: map[string]string{
"project_id": metadata.ProjectId,
"project_number": strconv.FormatInt(metadata.ProjectNumber, 10),
"zone": metadata.Zone,
"instance_id": metadata.InstanceId,
"instance_name": metadata.InstanceName,
"instance_creation_timestamp": strconv.FormatInt(metadata.CreatedAt, 10),
"service_account_id": loginInfo.ServiceAccountId,
"role": loginInfo.RoleName,
},
Policies: role.Policies,
Metadata: authMetadata(loginInfo, serviceAccount),
DisplayName: instance.Name,
LeaseOptions: logical.LeaseOptions{
Renewable: true,
@ -428,9 +431,28 @@ func (b *GcpAuthBackend) pathGceLogin(req *logical.Request, loginInfo *gcpLoginI
return resp, nil
}
func authMetadata(loginInfo *gcpLoginInfo, serviceAccount *iam.ServiceAccount) map[string]string {
metadata := map[string]string{
"role": loginInfo.RoleName,
"service_account_id": serviceAccount.UniqueId,
"service_account_email": serviceAccount.Email,
}
if loginInfo.GceMetadata != nil {
gceMetadata := loginInfo.GceMetadata
metadata["project_id"] = gceMetadata.ProjectId
metadata["project_number"] = strconv.FormatInt(gceMetadata.ProjectNumber, 10)
metadata["zone"] = gceMetadata.Zone
metadata["instance_id"] = gceMetadata.InstanceId
metadata["instance_name"] = gceMetadata.InstanceName
metadata["instance_creation_timestamp"] = strconv.FormatInt(gceMetadata.CreatedAt, 10)
}
return metadata
}
// pathGceRenew returns an error if the instance referenced in the auth token metadata cannot renew the
// auth token for the given role.
func (b *GcpAuthBackend) pathGceRenew(req *logical.Request, role *gcpRole) error {
func (b *GcpAuthBackend) pathGceRenew(req *logical.Request, roleName string, role *gcpRole) error {
gceClient, err := b.GCE(req.Storage)
if err != nil {
return fmt.Errorf(clientErrorTemplate, "GCE", err)
@ -451,7 +473,7 @@ func (b *GcpAuthBackend) pathGceRenew(req *logical.Request, role *gcpRole) error
return errors.New("invalid auth metadata: service_account_id not found")
}
if err := b.authorizeGCEInstance(instance, req.Storage, role, meta.Zone, serviceAccountId); err != nil {
return err
return fmt.Errorf("could not renew token for role %s: %v", roleName, err)
}
return nil

View File

@ -143,7 +143,7 @@
branch = "master"
name = "github.com/hashicorp/vault"
packages = ["api","helper/certutil","helper/compressutil","helper/consts","helper/errutil","helper/jsonutil","helper/logformat","helper/mlock","helper/parseutil","helper/pluginutil","helper/policyutil","helper/salt","helper/strutil","helper/wrapping","logical","logical/framework","logical/plugin"]
revision = "87581df7dd8f9b2a7faf16dc48b692aaab329ade"
revision = "27197f728e7fc8bffc9eaa59e8af4e0766b81320"
[[projects]]
branch = "master"

View File

@ -7,7 +7,7 @@ This plugin allows for Kubernets Service Accounts to authenticate with Vault.
## Quick Links
- Vault Website: https://www.vaultproject.io
- Kubernetes Auth Docs: https://www.vaultproject.io/docs/auth/kubernetes.html
- Kunernetes Auth Docs: https://www.vaultproject.io/docs/auth/kubernetes.html
- Main Project Github: https://www.github.com/hashicorp/vault
@ -21,7 +21,13 @@ Otherwise, first read this guide on how to [get started with Vault](https://www.
To learn specifically about how plugins work, see documentation on [Vault plugins](https://www.vaultproject.io/docs/internals/plugins.html).
### Usage
## Security Model
The current authentication model requires providing Vault with a Service Account token, which can be used to make authenticated calls to Kubernetes. This token should not typically be shared, but in order for Kubernetes to be treated as a trusted third party, Vault must validate something that Kubernetes has cryptographically signed and that conveys the identity of the token holder.
We expect Kubernetes to support less sensitive mechanisms in the future, and the Vault integration will be updated to use those mechanisms when available.
## Usage
Please see [documentation for the plugin](https://www.vaultproject.io/docs/auth/kubernetes.html)
on the Vault website.

View File

@ -1,15 +0,0 @@
-----BEGIN CERTIFICATE-----
MIICZDCCAeugAwIBAgIJALM9NbK8WRuBMAkGByqGSM49BAEwRTELMAkGA1UEBhMC
dXMxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdp
dHMgUHR5IEx0ZDAeFw0xNzA5MTExNzQ2NDNaFw0yNzA5MDkxNzQ2NDNaMEUxCzAJ
BgNVBAYTAnVzMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5l
dCBXaWRnaXRzIFB0eSBMdGQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATcqsBLxKP+
UHk7Y6ktGGFvfrIfIXHxeZe3Xwt691CWfdmJFvrGzyzW5/AbJIuO1utdOsqUStAm
W/Scfxop/FGadKqR4nAWLNBI4intgnf0r1rzBCSOmanolHqxQPqQ0UOjgacwgaQw
HQYDVR0OBBYEFHxh1pTd8ApEzg0gKMwwt01aA10TMHUGA1UdIwRuMGyAFHxh1pTd
8ApEzg0gKMwwt01aA10ToUmkRzBFMQswCQYDVQQGEwJ1czETMBEGA1UECBMKU29t
ZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkggkAsz01
srxZG4EwDAYDVR0TBAUwAwEB/zAJBgcqhkjOPQQBA2gAMGUCMCR+CvAoNBhqSe2M
4qWWD/9XX/0qmf0O442Qowcg5MWH1+mwl1s7ozinvbTPDPaYDwIxAM54qKhuL6xt
GxqJpa7Onn15Hu8zTsdzeYBqUUXA6wtn+Pa7197CgUkfty9yc2eeQw==
-----END CERTIFICATE-----

View File

@ -1,6 +0,0 @@
-----BEGIN EC PRIVATE KEY-----
MIGkAgEBBDC8aqQEyHUFnPPhnwk6wzO4L2bxB+eCvgNKuvaV+A0Ut8wgWR9Et/X4
2fNGXFvXBnOgBwYFK4EEACKhZANiAATcqsBLxKP+UHk7Y6ktGGFvfrIfIXHxeZe3
Xwt691CWfdmJFvrGzyzW5/AbJIuO1utdOsqUStAmW/Scfxop/FGadKqR4nAWLNBI
4intgnf0r1rzBCSOmanolHqxQPqQ0UM=
-----END EC PRIVATE KEY-----

View File

@ -1,5 +0,0 @@
-----BEGIN EC PRIVATE KEY-----
MHQCAQEEICRfLPO5eiMXmBDKiCcpzAJ6d8/grut5jviH/CZc2svSoAcGBSuBBAAK
oUQDQgAEkBfDOBa/VAm0zem0wmu2QlAFprcYU3vTcQgTYIZ622sPTLvp8q4QxP7P
AkTjKGEOg4tfap+4hKd94psmlOex0A==
-----END EC PRIVATE KEY-----

View File

@ -1,4 +0,0 @@
-----BEGIN PUBLIC KEY-----
MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEkBfDOBa/VAm0zem0wmu2QlAFprcYU3vT
cQgTYIZ622sPTLvp8q4QxP7PAkTjKGEOg4tfap+4hKd94psmlOex0A==
-----END PUBLIC KEY-----

View File

@ -7,6 +7,7 @@ import (
"encoding/pem"
"errors"
"github.com/SermoDigital/jose/jws"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework"
)
@ -19,13 +20,6 @@ func pathConfig(b *kubeAuthBackend) *framework.Path {
return &framework.Path{
Pattern: "config$",
Fields: map[string]*framework.FieldSchema{
"pem_keys": {
Type: framework.TypeCommaStringSlice,
Description: `List of PEM-formated public keys or certificates
used to verify the signatures of kubernetes service account
JWTs. If a certificate is given, its public key will be
extracted.`,
},
"kubernetes_host": {
Type: framework.TypeString,
Description: "Host must be a host string, a host:port pair, or a URL to the base of the Kubernetes API server.",
@ -34,6 +28,19 @@ extracted.`,
Type: framework.TypeString,
Description: "PEM encoded CA cert for use by the TLS client used to talk with the API.",
},
"token_reviewer_jwt": {
Type: framework.TypeString,
Description: `A service account JWT used to access the
TokenReview API to validate other JWTs during login. If not set
the JWT used for login will be used to access the API.`,
},
"pem_keys": {
Type: framework.TypeCommaStringSlice,
Description: `Optional list of PEM-formated public keys or certificates
used to verify the signatures of kubernetes service account
JWTs. If a certificate is given, its public key will be
extracted. Not every installation of Kuberentes exposes these keys.`,
},
},
Callbacks: map[logical.Operation]framework.OperationFunc{
logical.UpdateOperation: b.pathConfigWrite(),
@ -57,9 +64,10 @@ func (b *kubeAuthBackend) pathConfigRead() framework.OperationFunc {
// Create a map of data to be returned
resp := &logical.Response{
Data: map[string]interface{}{
"pem_keys": config.PEMKeys,
"kubernetes_host": config.Host,
"kubernetes_ca_cert": config.CACert,
"token_reviewer_jwt": config.TokenReviewerJWT,
"pem_keys": config.PEMKeys,
},
}
@ -82,11 +90,21 @@ func (b *kubeAuthBackend) pathConfigWrite() framework.OperationFunc {
return logical.ErrorResponse("one of pem_keys or kubernetes_ca_cert must be set"), nil
}
tokenReviewer := data.Get("token_reviewer_jwt").(string)
if len(tokenReviewer) > 0 {
// Validate it's a JWT
_, err := jws.ParseJWT([]byte(tokenReviewer))
if err != nil {
return nil, err
}
}
config := &kubeConfig{
PublicKeys: make([]interface{}, len(pemList)),
PEMKeys: pemList,
Host: host,
CACert: caCert,
PublicKeys: make([]interface{}, len(pemList)),
PEMKeys: pemList,
Host: host,
CACert: caCert,
TokenReviewerJWT: tokenReviewer,
}
var err error
@ -121,6 +139,8 @@ type kubeConfig struct {
Host string `json:"host"`
// CACert is the CA Cert to use to call into the kubernetes API
CACert string `json:"ca_cert"`
// TokenReviewJWT is the bearer to use during the TokenReview API call
TokenReviewerJWT string `json:"token_reviewer_jwt"`
}
// PasrsePublicKeyPEM is used to parse RSA and ECDSA public keys from PEMs

View File

@ -44,8 +44,8 @@ func pathLogin(b *kubeAuthBackend) *framework.Path {
},
Callbacks: map[logical.Operation]framework.OperationFunc{
logical.UpdateOperation: b.pathLogin(),
logical.PersonaLookaheadOperation: b.personaLookahead(),
logical.UpdateOperation: b.pathLogin(),
logical.AliasLookaheadOperation: b.aliasLookahead(),
},
HelpSynopsis: pathLoginHelpSyn,
@ -100,7 +100,7 @@ func (b *kubeAuthBackend) pathLogin() framework.OperationFunc {
Auth: &logical.Auth{
NumUses: role.NumUses,
Period: role.Period,
Persona: &logical.Persona{
Alias: &logical.Alias{
Name: serviceAccount.UID,
},
InternalData: map[string]interface{}{
@ -134,9 +134,9 @@ func (b *kubeAuthBackend) pathLogin() framework.OperationFunc {
}
}
// personaLookahead returns the persona object with the SA UID from the JWT
// aliasLookahead returns the alias object with the SA UID from the JWT
// Claims.
func (b *kubeAuthBackend) personaLookahead() framework.OperationFunc {
func (b *kubeAuthBackend) aliasLookahead() framework.OperationFunc {
return func(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
jwtStr := data.Get("jwt").(string)
if len(jwtStr) == 0 {
@ -156,7 +156,7 @@ func (b *kubeAuthBackend) personaLookahead() framework.OperationFunc {
return &logical.Response{
Auth: &logical.Auth{
Persona: &logical.Persona{
Alias: &logical.Alias{
Name: saUID,
},
},

View File

@ -1,17 +0,0 @@
-----BEGIN CERTIFICATE-----
MIICsDCCAhmgAwIBAgIJAOfUpFI2c2Y9MA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQwHhcNMTcwOTEyMDAzODU1WhcNMTcxMDEyMDAzODU1WjBF
MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
gQDIuRPNO+X/6BwCriiV097TuN3wngX3k37LbmPe/i5gWRRxxOKN+9NBkqPkrZFK
aNxAFRaHazMUNxKljICYKjs9zs/H6Oe6pOpkZ3HOGuxwNPbPRPDz8/nJgLLXPQUd
zJket+YpPXE/5yoi7S5qceb5qz/mD8jOG0YCepusj9oheQIDAQABo4GnMIGkMB0G
A1UdDgQWBBQBlWx0kNoBmGW4T9dUqv8c0KS/WjB1BgNVHSMEbjBsgBQBlWx0kNoB
mGW4T9dUqv8c0KS/WqFJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUt
U3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJAOfUpFI2
c2Y9MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAZngST25nmzvmNvWu
zHaOL7cmYLPzuvfGiJYPj/vCzG1fc8Tf9AUznFq7ssXWunURkJ4MM9pZSAfP9nXA
pMWSxi9QJyYPrNAI3EMPXSiuQkDYussFJBfQ20/VjJ5KhwMbb9t3EP6bopYBcxb4
zs9O4svzeBlPT40wXAhvom3zBd0=
-----END CERTIFICATE-----

View File

@ -1,15 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIICXgIBAAKBgQDIuRPNO+X/6BwCriiV097TuN3wngX3k37LbmPe/i5gWRRxxOKN
+9NBkqPkrZFKaNxAFRaHazMUNxKljICYKjs9zs/H6Oe6pOpkZ3HOGuxwNPbPRPDz
8/nJgLLXPQUdzJket+YpPXE/5yoi7S5qceb5qz/mD8jOG0YCepusj9oheQIDAQAB
AoGBAMEXMdJcDczZN6Docwh28cOQ8ogXLDxXOkFup0qMEIcFCqLLOPpHR5mr/Ffv
FHaVW1OWNb79w/xtYlW5TLeU4LjE0Ej2KDHKWrCM2FN8U+7B621OX3sD2/3+bDgs
Y0amFoBpGDuxIQ+6MVTAOi1sqrsocAaJi4HvglIgj9Bgc5n1AkEA/XilqA2e8VGq
lAfypVi16iY82/3vEpSkZo26biAHAvFBhYPDU4pf5rJ3T61Kgyl2tgUWjm2cY+Ow
V3dgk/SA+wJBAMq5trTudcmgnbIpbS/sjYRGgb8AkVOqB4bzo9IFC7RzYP46Pmge
RCB6FBqsI9bpfyDLicyCbPn/PWVwdioQ5RsCQQCTQ1Ebfi5mDgiI0MVNA2lNjMG3
HqWTqgCKBLXX3Yu1Te2/YHpPQwnMwstG42tzINfzkKk2PsCp2FNPve/Chj+ZAkAe
5uxI7EicMZWYQORZ988iqLTCbs24WSTIl38TVp2QJj5UwoAc0vBDmxhRcIgODI3K
a/xXZlJCUXwEaH46r1SdAkEA5ntS8X4tfKB6N1b3089FcbLLvKFLNzr5MSVJqwaW
geHK0vrK9BAQmPEfsgSperZCbNgzbNSSFHO5lcEif1whaw==
-----END RSA PRIVATE KEY-----

View File

@ -78,8 +78,16 @@ func (t *tokenReviewAPI) Review(jwt string) (*tokenReviewResult, error) {
if err != nil {
return nil, err
}
// If we have a configured TokenReviewer JWT use it as the bearer, otherwise
// try to use the passed in JWT.
bearer := fmt.Sprintf("Bearer %s", jwt)
if len(t.config.TokenReviewerJWT) > 0 {
bearer = fmt.Sprintf("Bearer %s", t.config.TokenReviewerJWT)
}
// Set the JWT as the Bearer token
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", jwt))
req.Header.Set("Authorization", bearer)
resp, err := client.Do(req)
if err != nil {
@ -104,12 +112,17 @@ func (t *tokenReviewAPI) Review(jwt string) (*tokenReviewResult, error) {
return nil, errors.New("lookup failed: service account jwt not valid")
}
// the username is of format: system:serviceaccount:(NAMESPACE):(SERVICEACCOUNT)
// The username is of format: system:serviceaccount:(NAMESPACE):(SERVICEACCOUNT)
parts := strings.Split(r.Status.User.Username, ":")
if len(parts) != 4 {
return nil, errors.New("lookup failed: unexpected username format")
}
// Validate the user that comes back from token review is a service account
if parts[0] != "system" || parts[1] != "serviceaccount" {
return nil, errors.New("lookup failed: username returned is not a service account")
}
return &tokenReviewResult{
Name: parts[3],
Namespace: parts[2],

18
vendor/vendor.json vendored
View File

@ -1147,10 +1147,16 @@
"revisionTime": "2017-09-02T04:25:43Z"
},
{
"checksumSHA1": "J/+RffHqbSdOeI82+ysAJruyO9w=",
"checksumSHA1": "/11y5HSVYFHHjBUHUIt35qi6D/g=",
"path": "github.com/hashicorp/vault-plugin-auth-gcp",
"revision": "440e5e20278c115840fab1f27b7a9f99d405ebbd",
"revisionTime": "2017-10-05T00:02:52Z"
},
{
"checksumSHA1": "a/6XqbRHzvZ1ngOoGASIIIcmjwM=",
"path": "github.com/hashicorp/vault-plugin-auth-gcp/plugin",
"revision": "a807a8507e636e40403455258ed25954ec254cad",
"revisionTime": "2017-09-15T19:03:59Z"
"revision": "440e5e20278c115840fab1f27b7a9f99d405ebbd",
"revisionTime": "2017-10-05T00:02:52Z"
},
{
"checksumSHA1": "nrNcGdv/8Ut8ScFy3tQoY3dpQvs=",
@ -1159,10 +1165,10 @@
"revisionTime": "2017-09-15T19:03:59Z"
},
{
"checksumSHA1": "U8kIY2Z/WO1nU+3g7wykzo20C+o=",
"checksumSHA1": "/xje1EITZoa0tj7KqPof+3FG+og=",
"path": "github.com/hashicorp/vault-plugin-auth-kubernetes",
"revision": "e6ff3b4fefe641225a7a81013337b3b62027b3a0",
"revisionTime": "2017-09-19T14:00:28Z"
"revision": "7b79e81da4f5d56811fb7ddda078a96f7f950814",
"revisionTime": "2017-10-05T00:02:34Z"
},
{
"checksumSHA1": "ZhK6IO2XN81Y+3RAjTcVm1Ic7oU=",