backend/azurerm: Support for authenticating using the Azure CLI (#19465)
* Upgrading to 2.0.0 of github.com/hashicorp/go-azure-helpers * Support for authenticating using Azure CLI * backend/azurerm: support for authenticating using the Azure CLI
This commit is contained in:
parent
a475b766de
commit
383bc98f5c
|
@ -65,9 +65,10 @@ func buildArmClient(config BackendConfig) (*ArmClient, error) {
|
|||
MsiEndpoint: config.MsiEndpoint,
|
||||
|
||||
// Feature Toggles
|
||||
SupportsAzureCliToken: true,
|
||||
SupportsClientSecretAuth: true,
|
||||
SupportsManagedServiceIdentity: config.UseMsi,
|
||||
// TODO: support for Azure CLI / Client Certificate auth
|
||||
// TODO: support for Client Certificate auth
|
||||
}
|
||||
armConfig, err := builder.Build()
|
||||
if err != nil {
|
||||
|
|
6
go.mod
6
go.mod
|
@ -32,8 +32,6 @@ require (
|
|||
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d // indirect
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/dgrijalva/jwt-go v0.0.0-20160617170158-f0777076321a // indirect
|
||||
github.com/dimchansky/utfbom v1.0.0 // indirect
|
||||
github.com/dnaeon/go-vcr v0.0.0-20180920040454-5637cf3d8a31 // indirect
|
||||
github.com/dylanmei/iso8601 v0.1.0 // indirect
|
||||
github.com/dylanmei/winrmtest v0.0.0-20170819153634-c2fbb09e6c08
|
||||
|
@ -55,7 +53,7 @@ require (
|
|||
github.com/grpc-ecosystem/grpc-gateway v1.5.1 // indirect
|
||||
github.com/hashicorp/consul v0.0.0-20171026175957-610f3c86a089
|
||||
github.com/hashicorp/errwrap v1.0.0
|
||||
github.com/hashicorp/go-azure-helpers v0.0.0-20181122151743-c51a3103be3b
|
||||
github.com/hashicorp/go-azure-helpers v0.0.0-20181126135526-ec113df69f49
|
||||
github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0
|
||||
github.com/hashicorp/go-getter v0.0.0-20180327010114-90bb99a48d86
|
||||
|
@ -98,7 +96,7 @@ require (
|
|||
github.com/mitchellh/cli v0.0.0-20171129193617-33edc47170b5
|
||||
github.com/mitchellh/colorstring v0.0.0-20150917214807-8631ce90f286
|
||||
github.com/mitchellh/copystructure v0.0.0-20170525013902-d23ffcb85de3
|
||||
github.com/mitchellh/go-homedir v0.0.0-20161203194507-b8bc1bf76747
|
||||
github.com/mitchellh/go-homedir v1.0.0
|
||||
github.com/mitchellh/go-linereader v0.0.0-20141013185533-07bab5fdd958
|
||||
github.com/mitchellh/go-wordwrap v1.0.0
|
||||
github.com/mitchellh/hashstructure v0.0.0-20160209213820-6b17d669fac5
|
||||
|
|
6
go.sum
6
go.sum
|
@ -68,6 +68,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
|||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go v0.0.0-20160617170158-f0777076321a h1:pzKxqfSfp4kqrm6jfyVYYkWhf+e1hPRt3rX+Yj/3UBU=
|
||||
github.com/dgrijalva/jwt-go v0.0.0-20160617170158-f0777076321a/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dimchansky/utfbom v1.0.0 h1:fGC2kkf4qOoKqZ4q7iIh+Vef4ubC1c38UDsEyZynZPc=
|
||||
github.com/dimchansky/utfbom v1.0.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
|
||||
github.com/dnaeon/go-vcr v0.0.0-20180920040454-5637cf3d8a31 h1:Dzuw9GtbmllUqEcoHfScT9YpKFUssSiZ5PgZkIGf/YQ=
|
||||
|
@ -121,6 +123,8 @@ github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/U
|
|||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-azure-helpers v0.0.0-20181122151743-c51a3103be3b h1:AJjRaIeZiWlhYVf2skNYOjooaaIOcQzS94a3iSKnXKE=
|
||||
github.com/hashicorp/go-azure-helpers v0.0.0-20181122151743-c51a3103be3b/go.mod h1:e+GPy2nvD+spqsdjUyw5tbo73rBbu955QBaV9GZoBEA=
|
||||
github.com/hashicorp/go-azure-helpers v0.0.0-20181126135526-ec113df69f49 h1:jkGYE3AQEm95w4n8o/iR59duXnuSjMCf1MCNSwOJxAw=
|
||||
github.com/hashicorp/go-azure-helpers v0.0.0-20181126135526-ec113df69f49/go.mod h1:Y5ejHZY3jQby82dOASJzyQ2xZw37zs+D5x6AaOC6O5E=
|
||||
github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de h1:XDCSythtg8aWSRSO29uwhgh7b127fWr+m5SemqjSUL8=
|
||||
github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de/go.mod h1:xIwEieBHERyEvaeKF/TcHh1Hu+lxPM+n2vT1+g9I4m4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig=
|
||||
|
@ -229,6 +233,8 @@ github.com/mitchellh/copystructure v0.0.0-20170525013902-d23ffcb85de3 h1:dECZqiJ
|
|||
github.com/mitchellh/copystructure v0.0.0-20170525013902-d23ffcb85de3/go.mod h1:eOsF2yLPlBBJPvD+nhl5QMTBSOBbOph6N7j/IDUw7PY=
|
||||
github.com/mitchellh/go-homedir v0.0.0-20161203194507-b8bc1bf76747 h1:eQox4Rh4ewJF+mqYPxCkmBAirRnPaHEB26UkNuPyjlk=
|
||||
github.com/mitchellh/go-homedir v0.0.0-20161203194507-b8bc1bf76747/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-linereader v0.0.0-20141013185533-07bab5fdd958 h1:wN+5lV34eSnVSZgBLWRGHr6L4giR3/wI2B9DLmVnlfI=
|
||||
github.com/mitchellh/go-linereader v0.0.0-20141013185533-07bab5fdd958/go.mod h1:OaY7UOoTkkrX3wRwjpYRKafIkkyeD0UtweSHAWWiqQM=
|
||||
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 h1:7GoSOOW2jpsfkntVKaS2rAr1TJqfcxotyaUcuxoZSzg=
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
language: go
|
||||
|
||||
script:
|
||||
- go vet ./...
|
||||
- go test -v ./...
|
||||
|
||||
go:
|
||||
- 1.3
|
||||
- 1.4
|
||||
- 1.5
|
||||
- 1.6
|
||||
- 1.7
|
||||
- tip
|
||||
|
|
|
@ -56,8 +56,9 @@ This simple parsing example:
|
|||
is directly mapped to:
|
||||
|
||||
```go
|
||||
if token, err := request.ParseFromRequest(tokenString, request.OAuth2Extractor, req, keyLookupFunc); err == nil {
|
||||
fmt.Printf("Token for user %v expires %v", token.Claims["user"], token.Claims["exp"])
|
||||
if token, err := request.ParseFromRequest(req, request.OAuth2Extractor, keyLookupFunc); err == nil {
|
||||
claims := token.Claims.(jwt.MapClaims)
|
||||
fmt.Printf("Token for user %v expires %v", claims["user"], claims["exp"])
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
A [go](http://www.golang.org) (or 'golang' for search engine friendliness) implementation of [JSON Web Tokens](http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html)
|
||||
# jwt-go
|
||||
|
||||
[![Build Status](https://travis-ci.org/dgrijalva/jwt-go.svg?branch=master)](https://travis-ci.org/dgrijalva/jwt-go)
|
||||
[![GoDoc](https://godoc.org/github.com/dgrijalva/jwt-go?status.svg)](https://godoc.org/github.com/dgrijalva/jwt-go)
|
||||
|
||||
**BREAKING CHANGES:*** Version 3.0.0 is here. It includes _a lot_ of changes including a few that break the API. We've tried to break as few things as possible, so there should just be a few type signature changes. A full list of breaking changes is available in `VERSION_HISTORY.md`. See `MIGRATION_GUIDE.md` for more information on updating your code.
|
||||
A [go](http://www.golang.org) (or 'golang' for search engine friendliness) implementation of [JSON Web Tokens](http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html)
|
||||
|
||||
**NOTICE:** A vulnerability in JWT was [recently published](https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/). As this library doesn't force users to validate the `alg` is what they expected, it's possible your usage is effected. There will be an update soon to remedy this, and it will likey require backwards-incompatible changes to the API. In the short term, please make sure your implementation verifies the `alg` is what you expect.
|
||||
**NEW VERSION COMING:** There have been a lot of improvements suggested since the version 3.0.0 released in 2016. I'm working now on cutting two different releases: 3.2.0 will contain any non-breaking changes or enhancements. 4.0.0 will follow shortly which will include breaking changes. See the 4.0.0 milestone to get an idea of what's coming. If you have other ideas, or would like to participate in 4.0.0, now's the time. If you depend on this library and don't want to be interrupted, I recommend you use your dependency mangement tool to pin to version 3.
|
||||
|
||||
**SECURITY NOTICE:** Some older versions of Go have a security issue in the cryotp/elliptic. Recommendation is to upgrade to at least 1.8.3. See issue #216 for more detail.
|
||||
|
||||
**SECURITY NOTICE:** It's important that you [validate the `alg` presented is what you expect](https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/). This library attempts to make it easy to do the right thing by requiring key types match the expected alg, but you should take the extra step to verify it in your usage. See the examples provided.
|
||||
|
||||
## What the heck is a JWT?
|
||||
|
||||
|
@ -47,7 +51,10 @@ This library is considered production ready. Feedback and feature requests are
|
|||
|
||||
This project uses [Semantic Versioning 2.0.0](http://semver.org). Accepted pull requests will land on `master`. Periodically, versions will be tagged from `master`. You can find all the releases on [the project releases page](https://github.com/dgrijalva/jwt-go/releases).
|
||||
|
||||
While we try to make it obvious when we make breaking changes, there isn't a great mechanism for pushing announcements out to users. You may want to use this alternative package include: `gopkg.in/dgrijalva/jwt-go.v2`. It will do the right thing WRT semantic versioning.
|
||||
While we try to make it obvious when we make breaking changes, there isn't a great mechanism for pushing announcements out to users. You may want to use this alternative package include: `gopkg.in/dgrijalva/jwt-go.v3`. It will do the right thing WRT semantic versioning.
|
||||
|
||||
**BREAKING CHANGES:***
|
||||
* Version 3.0.0 includes _a lot_ of changes from the 2.x line, including a few that break the API. We've tried to break as few things as possible, so there should just be a few type signature changes. A full list of breaking changes is available in `VERSION_HISTORY.md`. See `MIGRATION_GUIDE.md` for more information on updating your code.
|
||||
|
||||
## Usage Tips
|
||||
|
||||
|
@ -68,6 +75,14 @@ Symmetric signing methods, such as HSA, use only a single secret. This is probab
|
|||
|
||||
Asymmetric signing methods, such as RSA, use different keys for signing and verifying tokens. This makes it possible to produce tokens with a private key, and allow any consumer to access the public key for verification.
|
||||
|
||||
### Signing Methods and Key Types
|
||||
|
||||
Each signing method expects a different object type for its signing keys. See the package documentation for details. Here are the most common ones:
|
||||
|
||||
* The [HMAC signing method](https://godoc.org/github.com/dgrijalva/jwt-go#SigningMethodHMAC) (`HS256`,`HS384`,`HS512`) expect `[]byte` values for signing and validation
|
||||
* The [RSA signing method](https://godoc.org/github.com/dgrijalva/jwt-go#SigningMethodRSA) (`RS256`,`RS384`,`RS512`) expect `*rsa.PrivateKey` for signing and `*rsa.PublicKey` for validation
|
||||
* The [ECDSA signing method](https://godoc.org/github.com/dgrijalva/jwt-go#SigningMethodECDSA) (`ES256`,`ES384`,`ES512`) expect `*ecdsa.PrivateKey` for signing and `*ecdsa.PublicKey` for validation
|
||||
|
||||
### JWT and OAuth
|
||||
|
||||
It's worth mentioning that OAuth and JWT are not the same thing. A JWT token is simply a signed JSON object. It can be used anywhere such a thing is useful. There is some confusion, though, as JWT is the most common type of bearer token used in OAuth2 authentication.
|
||||
|
@ -82,4 +97,4 @@ Without going too far down the rabbit hole, here's a description of the interact
|
|||
|
||||
Documentation can be found [on godoc.org](http://godoc.org/github.com/dgrijalva/jwt-go).
|
||||
|
||||
The command line utility included in this project (cmd/jwt) provides a straightforward example of token creation and parsing as well as a useful tool for debugging your own integration. You'll also find several implementation examples in to documentation.
|
||||
The command line utility included in this project (cmd/jwt) provides a straightforward example of token creation and parsing as well as a useful tool for debugging your own integration. You'll also find several implementation examples in the documentation.
|
||||
|
|
|
@ -1,5 +1,18 @@
|
|||
## `jwt-go` Version History
|
||||
|
||||
#### 3.2.0
|
||||
|
||||
* Added method `ParseUnverified` to allow users to split up the tasks of parsing and validation
|
||||
* HMAC signing method returns `ErrInvalidKeyType` instead of `ErrInvalidKey` where appropriate
|
||||
* Added options to `request.ParseFromRequest`, which allows for an arbitrary list of modifiers to parsing behavior. Initial set include `WithClaims` and `WithParser`. Existing usage of this function will continue to work as before.
|
||||
* Deprecated `ParseFromRequestWithClaims` to simplify API in the future.
|
||||
|
||||
#### 3.1.0
|
||||
|
||||
* Improvements to `jwt` command line tool
|
||||
* Added `SkipClaimsValidation` option to `Parser`
|
||||
* Documentation updates
|
||||
|
||||
#### 3.0.0
|
||||
|
||||
* **Compatibility Breaking Changes**: See MIGRATION_GUIDE.md for tips on updating your code
|
||||
|
|
|
@ -14,6 +14,7 @@ var (
|
|||
)
|
||||
|
||||
// Implements the ECDSA family of signing methods signing methods
|
||||
// Expects *ecdsa.PrivateKey for signing and *ecdsa.PublicKey for verification
|
||||
type SigningMethodECDSA struct {
|
||||
Name string
|
||||
Hash crypto.Hash
|
||||
|
|
|
@ -51,13 +51,9 @@ func (e ValidationError) Error() string {
|
|||
} else {
|
||||
return "token is invalid"
|
||||
}
|
||||
return e.Inner.Error()
|
||||
}
|
||||
|
||||
// No errors
|
||||
func (e *ValidationError) valid() bool {
|
||||
if e.Errors > 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return e.Errors == 0
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
)
|
||||
|
||||
// Implements the HMAC-SHA family of signing methods signing methods
|
||||
// Expects key type of []byte for both signing and validation
|
||||
type SigningMethodHMAC struct {
|
||||
Name string
|
||||
Hash crypto.Hash
|
||||
|
@ -90,5 +91,5 @@ func (m *SigningMethodHMAC) Sign(signingString string, key interface{}) (string,
|
|||
return EncodeSegment(hasher.Sum(nil)), nil
|
||||
}
|
||||
|
||||
return "", ErrInvalidKey
|
||||
return "", ErrInvalidKeyType
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
type Parser struct {
|
||||
ValidMethods []string // If populated, only these methods will be considered valid
|
||||
UseJSONNumber bool // Use JSON Number format in JSON decoder
|
||||
SkipClaimsValidation bool // Skip claims validation during token parsing
|
||||
}
|
||||
|
||||
// Parse, validate, and return a token.
|
||||
|
@ -20,55 +21,9 @@ func (p *Parser) Parse(tokenString string, keyFunc Keyfunc) (*Token, error) {
|
|||
}
|
||||
|
||||
func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) (*Token, error) {
|
||||
parts := strings.Split(tokenString, ".")
|
||||
if len(parts) != 3 {
|
||||
return nil, NewValidationError("token contains an invalid number of segments", ValidationErrorMalformed)
|
||||
}
|
||||
|
||||
var err error
|
||||
token := &Token{Raw: tokenString}
|
||||
|
||||
// parse Header
|
||||
var headerBytes []byte
|
||||
if headerBytes, err = DecodeSegment(parts[0]); err != nil {
|
||||
if strings.HasPrefix(strings.ToLower(tokenString), "bearer ") {
|
||||
return token, NewValidationError("tokenstring should not contain 'bearer '", ValidationErrorMalformed)
|
||||
}
|
||||
return token, &ValidationError{Inner: err, Errors: ValidationErrorMalformed}
|
||||
}
|
||||
if err = json.Unmarshal(headerBytes, &token.Header); err != nil {
|
||||
return token, &ValidationError{Inner: err, Errors: ValidationErrorMalformed}
|
||||
}
|
||||
|
||||
// parse Claims
|
||||
var claimBytes []byte
|
||||
token.Claims = claims
|
||||
|
||||
if claimBytes, err = DecodeSegment(parts[1]); err != nil {
|
||||
return token, &ValidationError{Inner: err, Errors: ValidationErrorMalformed}
|
||||
}
|
||||
dec := json.NewDecoder(bytes.NewBuffer(claimBytes))
|
||||
if p.UseJSONNumber {
|
||||
dec.UseNumber()
|
||||
}
|
||||
// JSON Decode. Special case for map type to avoid weird pointer behavior
|
||||
if c, ok := token.Claims.(MapClaims); ok {
|
||||
err = dec.Decode(&c)
|
||||
} else {
|
||||
err = dec.Decode(&claims)
|
||||
}
|
||||
// Handle decode error
|
||||
token, parts, err := p.ParseUnverified(tokenString, claims)
|
||||
if err != nil {
|
||||
return token, &ValidationError{Inner: err, Errors: ValidationErrorMalformed}
|
||||
}
|
||||
|
||||
// Lookup signature method
|
||||
if method, ok := token.Header["alg"].(string); ok {
|
||||
if token.Method = GetSigningMethod(method); token.Method == nil {
|
||||
return token, NewValidationError("signing method (alg) is unavailable.", ValidationErrorUnverifiable)
|
||||
}
|
||||
} else {
|
||||
return token, NewValidationError("signing method (alg) is unspecified.", ValidationErrorUnverifiable)
|
||||
return token, err
|
||||
}
|
||||
|
||||
// Verify signing method is in the required set
|
||||
|
@ -95,12 +50,16 @@ func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyf
|
|||
}
|
||||
if key, err = keyFunc(token); err != nil {
|
||||
// keyFunc returned an error
|
||||
if ve, ok := err.(*ValidationError); ok {
|
||||
return token, ve
|
||||
}
|
||||
return token, &ValidationError{Inner: err, Errors: ValidationErrorUnverifiable}
|
||||
}
|
||||
|
||||
vErr := &ValidationError{}
|
||||
|
||||
// Validate Claims
|
||||
if !p.SkipClaimsValidation {
|
||||
if err := token.Claims.Valid(); err != nil {
|
||||
|
||||
// If the Claims Valid returned an error, check if it is a validation error,
|
||||
|
@ -111,6 +70,7 @@ func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyf
|
|||
vErr = e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Perform validation
|
||||
token.Signature = parts[2]
|
||||
|
@ -126,3 +86,63 @@ func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyf
|
|||
|
||||
return token, vErr
|
||||
}
|
||||
|
||||
// WARNING: Don't use this method unless you know what you're doing
|
||||
//
|
||||
// This method parses the token but doesn't validate the signature. It's only
|
||||
// ever useful in cases where you know the signature is valid (because it has
|
||||
// been checked previously in the stack) and you want to extract values from
|
||||
// it.
|
||||
func (p *Parser) ParseUnverified(tokenString string, claims Claims) (token *Token, parts []string, err error) {
|
||||
parts = strings.Split(tokenString, ".")
|
||||
if len(parts) != 3 {
|
||||
return nil, parts, NewValidationError("token contains an invalid number of segments", ValidationErrorMalformed)
|
||||
}
|
||||
|
||||
token = &Token{Raw: tokenString}
|
||||
|
||||
// parse Header
|
||||
var headerBytes []byte
|
||||
if headerBytes, err = DecodeSegment(parts[0]); err != nil {
|
||||
if strings.HasPrefix(strings.ToLower(tokenString), "bearer ") {
|
||||
return token, parts, NewValidationError("tokenstring should not contain 'bearer '", ValidationErrorMalformed)
|
||||
}
|
||||
return token, parts, &ValidationError{Inner: err, Errors: ValidationErrorMalformed}
|
||||
}
|
||||
if err = json.Unmarshal(headerBytes, &token.Header); err != nil {
|
||||
return token, parts, &ValidationError{Inner: err, Errors: ValidationErrorMalformed}
|
||||
}
|
||||
|
||||
// parse Claims
|
||||
var claimBytes []byte
|
||||
token.Claims = claims
|
||||
|
||||
if claimBytes, err = DecodeSegment(parts[1]); err != nil {
|
||||
return token, parts, &ValidationError{Inner: err, Errors: ValidationErrorMalformed}
|
||||
}
|
||||
dec := json.NewDecoder(bytes.NewBuffer(claimBytes))
|
||||
if p.UseJSONNumber {
|
||||
dec.UseNumber()
|
||||
}
|
||||
// JSON Decode. Special case for map type to avoid weird pointer behavior
|
||||
if c, ok := token.Claims.(MapClaims); ok {
|
||||
err = dec.Decode(&c)
|
||||
} else {
|
||||
err = dec.Decode(&claims)
|
||||
}
|
||||
// Handle decode error
|
||||
if err != nil {
|
||||
return token, parts, &ValidationError{Inner: err, Errors: ValidationErrorMalformed}
|
||||
}
|
||||
|
||||
// Lookup signature method
|
||||
if method, ok := token.Header["alg"].(string); ok {
|
||||
if token.Method = GetSigningMethod(method); token.Method == nil {
|
||||
return token, parts, NewValidationError("signing method (alg) is unavailable.", ValidationErrorUnverifiable)
|
||||
}
|
||||
} else {
|
||||
return token, parts, NewValidationError("signing method (alg) is unspecified.", ValidationErrorUnverifiable)
|
||||
}
|
||||
|
||||
return token, parts, nil
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
)
|
||||
|
||||
// Implements the RSA family of signing methods signing methods
|
||||
// Expects *rsa.PrivateKey for signing and *rsa.PublicKey for validation
|
||||
type SigningMethodRSA struct {
|
||||
Name string
|
||||
Hash crypto.Hash
|
||||
|
@ -44,7 +45,7 @@ func (m *SigningMethodRSA) Alg() string {
|
|||
}
|
||||
|
||||
// Implements the Verify method from SigningMethod
|
||||
// For this signing method, must be an rsa.PublicKey structure.
|
||||
// For this signing method, must be an *rsa.PublicKey structure.
|
||||
func (m *SigningMethodRSA) Verify(signingString, signature string, key interface{}) error {
|
||||
var err error
|
||||
|
||||
|
@ -73,7 +74,7 @@ func (m *SigningMethodRSA) Verify(signingString, signature string, key interface
|
|||
}
|
||||
|
||||
// Implements the Sign method from SigningMethod
|
||||
// For this signing method, must be an rsa.PrivateKey structure.
|
||||
// For this signing method, must be an *rsa.PrivateKey structure.
|
||||
func (m *SigningMethodRSA) Sign(signingString string, key interface{}) (string, error) {
|
||||
var rsaKey *rsa.PrivateKey
|
||||
var ok bool
|
||||
|
|
|
@ -39,6 +39,38 @@ func ParseRSAPrivateKeyFromPEM(key []byte) (*rsa.PrivateKey, error) {
|
|||
return pkey, nil
|
||||
}
|
||||
|
||||
// Parse PEM encoded PKCS1 or PKCS8 private key protected with password
|
||||
func ParseRSAPrivateKeyFromPEMWithPassword(key []byte, password string) (*rsa.PrivateKey, error) {
|
||||
var err error
|
||||
|
||||
// Parse PEM block
|
||||
var block *pem.Block
|
||||
if block, _ = pem.Decode(key); block == nil {
|
||||
return nil, ErrKeyMustBePEMEncoded
|
||||
}
|
||||
|
||||
var parsedKey interface{}
|
||||
|
||||
var blockDecrypted []byte
|
||||
if blockDecrypted, err = x509.DecryptPEMBlock(block, []byte(password)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if parsedKey, err = x509.ParsePKCS1PrivateKey(blockDecrypted); err != nil {
|
||||
if parsedKey, err = x509.ParsePKCS8PrivateKey(blockDecrypted); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var pkey *rsa.PrivateKey
|
||||
var ok bool
|
||||
if pkey, ok = parsedKey.(*rsa.PrivateKey); !ok {
|
||||
return nil, ErrNotRSAPrivateKey
|
||||
}
|
||||
|
||||
return pkey, nil
|
||||
}
|
||||
|
||||
// Parse PEM encoded PKCS1 or PKCS8 public key
|
||||
func ParseRSAPublicKeyFromPEM(key []byte) (*rsa.PublicKey, error) {
|
||||
var err error
|
||||
|
|
|
@ -48,25 +48,16 @@ func (a azureCliParsingAuth) build(b Builder) (authMethod, error) {
|
|||
}
|
||||
|
||||
func (a azureCliParsingAuth) isApplicable(b Builder) bool {
|
||||
return b.SupportsAzureCliCloudShellParsing
|
||||
return b.SupportsAzureCliParsing
|
||||
}
|
||||
|
||||
func (a azureCliParsingAuth) getAuthorizationToken(oauthConfig *adal.OAuthConfig, endpoint string) (*autorest.BearerAuthorizer, error) {
|
||||
if a.profile.usingCloudShell {
|
||||
// load the refreshed tokens from the CloudShell Azure CLI credentials
|
||||
err := a.profile.populateClientIdAndAccessToken()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error loading the refreshed CloudShell tokens: %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
spt, err := adal.NewServicePrincipalTokenFromManualToken(*oauthConfig, a.profile.clientId, endpoint, *a.profile.accessToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = spt.Refresh()
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error refreshing Service Principal Token: %+v", err)
|
||||
}
|
||||
|
|
146
vendor/github.com/hashicorp/go-azure-helpers/authentication/auth_method_azure_cli_token.go
generated
vendored
Normal file
146
vendor/github.com/hashicorp/go-azure-helpers/authentication/auth_method_azure_cli_token.go
generated
vendored
Normal file
|
@ -0,0 +1,146 @@
|
|||
package authentication
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/Azure/go-autorest/autorest"
|
||||
"github.com/Azure/go-autorest/autorest/adal"
|
||||
"github.com/Azure/go-autorest/autorest/azure/cli"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
)
|
||||
|
||||
type azureCliTokenAuth struct {
|
||||
profile *azureCLIProfile
|
||||
}
|
||||
|
||||
func (a azureCliTokenAuth) build(b Builder) (authMethod, error) {
|
||||
auth := azureCliTokenAuth{
|
||||
profile: &azureCLIProfile{
|
||||
clientId: b.ClientID,
|
||||
environment: b.Environment,
|
||||
subscriptionId: b.SubscriptionID,
|
||||
tenantId: b.TenantID,
|
||||
},
|
||||
}
|
||||
profilePath, err := cli.ProfilePath()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error loading the Profile Path from the Azure CLI: %+v", err)
|
||||
}
|
||||
|
||||
profile, err := cli.LoadProfile(profilePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Azure CLI Authorization Profile was not found. Please ensure the Azure CLI is installed and then log-in with `az login`.")
|
||||
}
|
||||
|
||||
auth.profile.profile = profile
|
||||
|
||||
err = auth.profile.populateFields()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = auth.profile.populateClientId()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error populating Client ID from the Azure CLI: %+v", err)
|
||||
}
|
||||
|
||||
return auth, nil
|
||||
}
|
||||
|
||||
func (a azureCliTokenAuth) isApplicable(b Builder) bool {
|
||||
return b.SupportsAzureCliToken
|
||||
}
|
||||
|
||||
func (a azureCliTokenAuth) getAuthorizationToken(oauthConfig *adal.OAuthConfig, endpoint string) (*autorest.BearerAuthorizer, error) {
|
||||
// the Azure CLI appears to cache these, so to maintain compatibility with the interface this method is intentionally not on the pointer
|
||||
token, err := obtainAuthorizationToken(endpoint, a.profile.subscriptionId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error obtaining Authorization Token from the Azure CLI: %s", err)
|
||||
}
|
||||
|
||||
adalToken, err := token.ToADALToken()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error converting Authorization Token to an ADAL Token: %s", err)
|
||||
}
|
||||
|
||||
spt, err := adal.NewServicePrincipalTokenFromManualToken(*oauthConfig, a.profile.clientId, endpoint, adalToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
auth := autorest.NewBearerAuthorizer(spt)
|
||||
return auth, nil
|
||||
}
|
||||
|
||||
func (a azureCliTokenAuth) name() string {
|
||||
return "Obtaining a token from the Azure CLI"
|
||||
}
|
||||
|
||||
func (a azureCliTokenAuth) populateConfig(c *Config) error {
|
||||
c.ClientID = a.profile.clientId
|
||||
c.Environment = a.profile.environment
|
||||
c.SubscriptionID = a.profile.subscriptionId
|
||||
c.TenantID = a.profile.tenantId
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a azureCliTokenAuth) validate() error {
|
||||
var err *multierror.Error
|
||||
|
||||
errorMessageFmt := "A %s was not found in your Azure CLI Credentials.\n\nPlease login to the Azure CLI again via `az login`"
|
||||
|
||||
if a.profile == nil {
|
||||
return fmt.Errorf("Azure CLI Profile is nil - this is an internal error and should be reported.")
|
||||
}
|
||||
|
||||
if a.profile.clientId == "" {
|
||||
err = multierror.Append(err, fmt.Errorf(errorMessageFmt, "Client ID"))
|
||||
}
|
||||
|
||||
if a.profile.subscriptionId == "" {
|
||||
err = multierror.Append(err, fmt.Errorf(errorMessageFmt, "Subscription ID"))
|
||||
}
|
||||
|
||||
if a.profile.tenantId == "" {
|
||||
err = multierror.Append(err, fmt.Errorf(errorMessageFmt, "Tenant ID"))
|
||||
}
|
||||
|
||||
return err.ErrorOrNil()
|
||||
}
|
||||
|
||||
func obtainAuthorizationToken(endpoint string, subscriptionId string) (*cli.Token, error) {
|
||||
var stderr bytes.Buffer
|
||||
var stdout bytes.Buffer
|
||||
|
||||
cmd := exec.Command("az", "account", "get-access-token", "--resource", endpoint, "--subscription", subscriptionId, "-o=json")
|
||||
|
||||
cmd.Stderr = &stderr
|
||||
cmd.Stdout = &stdout
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
return nil, fmt.Errorf("Error launching Azure CLI: %+v", err)
|
||||
}
|
||||
|
||||
if err := cmd.Wait(); err != nil {
|
||||
return nil, fmt.Errorf("Error waiting for the Azure CLI: %+v", err)
|
||||
}
|
||||
|
||||
stdOutStr := stdout.String()
|
||||
stdErrStr := stderr.String()
|
||||
|
||||
if stdErrStr != "" {
|
||||
return nil, fmt.Errorf("Error retrieving access token from Azure CLI: %s", strings.TrimSpace(stdErrStr))
|
||||
}
|
||||
|
||||
var token *cli.Token
|
||||
err := json.Unmarshal([]byte(stdOutStr), &token)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error unmarshaling Access Token from the Azure CLI: %s", err)
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
|
@ -13,10 +13,9 @@ import (
|
|||
type azureCliAccessToken struct {
|
||||
ClientID string
|
||||
AccessToken *adal.Token
|
||||
IsCloudShell bool
|
||||
}
|
||||
|
||||
func findValidAccessTokenForTenant(tokens []cli.Token, tenantId string) (*azureCliAccessToken, error) {
|
||||
func findValidAccessTokenForTenant(tokens []cli.Token, tenantId string, allowExpired bool) (*azureCliAccessToken, error) {
|
||||
for _, accessToken := range tokens {
|
||||
token, err := accessToken.ToADALToken()
|
||||
if err != nil {
|
||||
|
@ -28,7 +27,7 @@ func findValidAccessTokenForTenant(tokens []cli.Token, tenantId string) (*azureC
|
|||
return nil, fmt.Errorf("Error parsing expiration date: %q", accessToken.ExpiresOn)
|
||||
}
|
||||
|
||||
if expirationDate.UTC().Before(time.Now().UTC()) {
|
||||
if expirationDate.UTC().Before(time.Now().UTC()) && !allowExpired {
|
||||
log.Printf("[DEBUG] Token %q has expired", token.AccessToken)
|
||||
continue
|
||||
}
|
||||
|
@ -46,7 +45,6 @@ func findValidAccessTokenForTenant(tokens []cli.Token, tenantId string) (*azureC
|
|||
validAccessToken := azureCliAccessToken{
|
||||
ClientID: accessToken.ClientID,
|
||||
AccessToken: &token,
|
||||
IsCloudShell: accessToken.RefreshToken == "",
|
||||
}
|
||||
return &validAccessToken, nil
|
||||
}
|
||||
|
|
7
vendor/github.com/hashicorp/go-azure-helpers/authentication/azure_cli_profile.go
generated
vendored
7
vendor/github.com/hashicorp/go-azure-helpers/authentication/azure_cli_profile.go
generated
vendored
|
@ -13,7 +13,6 @@ type azureCLIProfile struct {
|
|||
subscriptionId string
|
||||
tenantId string
|
||||
accessToken *adal.Token
|
||||
usingCloudShell bool
|
||||
}
|
||||
|
||||
func (a *azureCLIProfile) populateFields() error {
|
||||
|
@ -33,12 +32,6 @@ func (a *azureCLIProfile) populateFields() error {
|
|||
}
|
||||
}
|
||||
|
||||
// now we know the Subscription ID & Tenant ID we can find the associated Client ID/Access Token
|
||||
err := a.populateClientIdAndAccessToken()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// always pull the environment from the Azure CLI, since the Access Token's associated with it
|
||||
return a.populateEnvironment()
|
||||
}
|
||||
|
|
|
@ -27,6 +27,29 @@ func (a *azureCLIProfile) populateTenantID() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (a *azureCLIProfile) populateClientId() error {
|
||||
// we can now pull out the ClientID and the Access Token to use from the Access Token
|
||||
tokensPath, err := cli.AccessTokensPath()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error loading the Tokens Path from the Azure CLI: %+v", err)
|
||||
}
|
||||
|
||||
tokens, err := cli.LoadTokens(tokensPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("No Authorization Tokens were found - please ensure the Azure CLI is installed and then log-in with `az login`.")
|
||||
}
|
||||
|
||||
validToken, err := findValidAccessTokenForTenant(tokens, a.tenantId, true)
|
||||
if err != nil {
|
||||
return fmt.Errorf("No Authorization Tokens were found - please re-authenticate using `az login`.")
|
||||
}
|
||||
|
||||
token := *validToken
|
||||
a.clientId = token.ClientID
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *azureCLIProfile) populateClientIdAndAccessToken() error {
|
||||
// we can now pull out the ClientID and the Access Token to use from the Access Token
|
||||
tokensPath, err := cli.AccessTokensPath()
|
||||
|
@ -39,7 +62,7 @@ func (a *azureCLIProfile) populateClientIdAndAccessToken() error {
|
|||
return fmt.Errorf("No Authorization Tokens were found - please ensure the Azure CLI is installed and then log-in with `az login`.")
|
||||
}
|
||||
|
||||
validToken, err := findValidAccessTokenForTenant(tokens, a.tenantId)
|
||||
validToken, err := findValidAccessTokenForTenant(tokens, a.tenantId, false)
|
||||
if err != nil {
|
||||
return fmt.Errorf("No (unexpired) Authorization Tokens were found - please re-authenticate using `az login`.")
|
||||
}
|
||||
|
@ -47,7 +70,6 @@ func (a *azureCLIProfile) populateClientIdAndAccessToken() error {
|
|||
token := *validToken
|
||||
a.accessToken = token.AccessToken
|
||||
a.clientId = token.ClientID
|
||||
a.usingCloudShell = token.IsCloudShell
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -18,8 +18,11 @@ type Builder struct {
|
|||
// only applicable for Azure Stack at this time.
|
||||
CustomResourceManagerEndpoint string
|
||||
|
||||
// Azure CLI Parsing / CloudShell Auth
|
||||
SupportsAzureCliCloudShellParsing bool
|
||||
// Azure CLI Parsing
|
||||
SupportsAzureCliParsing bool
|
||||
|
||||
// Azure CLI Tokens Auth
|
||||
SupportsAzureCliToken bool
|
||||
|
||||
// Managed Service Identity Auth
|
||||
SupportsManagedServiceIdentity bool
|
||||
|
@ -52,6 +55,7 @@ func (b Builder) Build() (*Config, error) {
|
|||
servicePrincipalClientCertificateAuth{},
|
||||
servicePrincipalClientSecretAuth{},
|
||||
managedServiceIdentityAuth{},
|
||||
azureCliTokenAuth{},
|
||||
azureCliParsingAuth{},
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
module github.com/mitchellh/go-homedir
|
|
@ -77,13 +77,30 @@ func Expand(path string) (string, error) {
|
|||
}
|
||||
|
||||
func dirUnix() (string, error) {
|
||||
homeEnv := "HOME"
|
||||
if runtime.GOOS == "plan9" {
|
||||
// On plan9, env vars are lowercase.
|
||||
homeEnv = "home"
|
||||
}
|
||||
|
||||
// First prefer the HOME environmental variable
|
||||
if home := os.Getenv("HOME"); home != "" {
|
||||
if home := os.Getenv(homeEnv); home != "" {
|
||||
return home, nil
|
||||
}
|
||||
|
||||
// If that fails, try getent
|
||||
var stdout bytes.Buffer
|
||||
|
||||
// If that fails, try OS specific commands
|
||||
if runtime.GOOS == "darwin" {
|
||||
cmd := exec.Command("sh", "-c", `dscl -q . -read /Users/"$(whoami)" NFSHomeDirectory | sed 's/^[^ ]*: //'`)
|
||||
cmd.Stdout = &stdout
|
||||
if err := cmd.Run(); err == nil {
|
||||
result := strings.TrimSpace(stdout.String())
|
||||
if result != "" {
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cmd := exec.Command("getent", "passwd", strconv.Itoa(os.Getuid()))
|
||||
cmd.Stdout = &stdout
|
||||
if err := cmd.Run(); err != nil {
|
||||
|
@ -100,10 +117,11 @@ func dirUnix() (string, error) {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If all else fails, try the shell
|
||||
stdout.Reset()
|
||||
cmd = exec.Command("sh", "-c", "cd && pwd")
|
||||
cmd := exec.Command("sh", "-c", "cd && pwd")
|
||||
cmd.Stdout = &stdout
|
||||
if err := cmd.Run(); err != nil {
|
||||
return "", err
|
||||
|
@ -123,14 +141,16 @@ func dirWindows() (string, error) {
|
|||
return home, nil
|
||||
}
|
||||
|
||||
// Prefer standard environment variable USERPROFILE
|
||||
if home := os.Getenv("USERPROFILE"); home != "" {
|
||||
return home, nil
|
||||
}
|
||||
|
||||
drive := os.Getenv("HOMEDRIVE")
|
||||
path := os.Getenv("HOMEPATH")
|
||||
home := drive + path
|
||||
if drive == "" || path == "" {
|
||||
home = os.Getenv("USERPROFILE")
|
||||
}
|
||||
if home == "" {
|
||||
return "", errors.New("HOMEDRIVE, HOMEPATH, and USERPROFILE are blank")
|
||||
return "", errors.New("HOMEDRIVE, HOMEPATH, or USERPROFILE are blank")
|
||||
}
|
||||
|
||||
return home, nil
|
||||
|
|
|
@ -208,7 +208,7 @@ github.com/coreos/etcd/pkg/tlsutil
|
|||
github.com/coreos/go-semver/semver
|
||||
# github.com/davecgh/go-spew v1.1.1
|
||||
github.com/davecgh/go-spew/spew
|
||||
# github.com/dgrijalva/jwt-go v0.0.0-20160617170158-f0777076321a
|
||||
# github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/dgrijalva/jwt-go
|
||||
# github.com/dimchansky/utfbom v1.0.0
|
||||
github.com/dimchansky/utfbom
|
||||
|
@ -302,7 +302,7 @@ github.com/hashicorp/consul/lib/freeport
|
|||
github.com/hashicorp/consul/testutil/retry
|
||||
# github.com/hashicorp/errwrap v1.0.0
|
||||
github.com/hashicorp/errwrap
|
||||
# github.com/hashicorp/go-azure-helpers v0.0.0-20181122151743-c51a3103be3b
|
||||
# github.com/hashicorp/go-azure-helpers v0.0.0-20181126135526-ec113df69f49
|
||||
github.com/hashicorp/go-azure-helpers/authentication
|
||||
github.com/hashicorp/go-azure-helpers/storage
|
||||
# github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de
|
||||
|
@ -417,7 +417,7 @@ github.com/mitchellh/cli
|
|||
github.com/mitchellh/colorstring
|
||||
# github.com/mitchellh/copystructure v0.0.0-20170525013902-d23ffcb85de3
|
||||
github.com/mitchellh/copystructure
|
||||
# github.com/mitchellh/go-homedir v0.0.0-20161203194507-b8bc1bf76747
|
||||
# github.com/mitchellh/go-homedir v1.0.0
|
||||
github.com/mitchellh/go-homedir
|
||||
# github.com/mitchellh/go-linereader v0.0.0-20141013185533-07bab5fdd958
|
||||
github.com/mitchellh/go-linereader
|
||||
|
|
|
@ -15,7 +15,7 @@ Stores the state as a Blob with the given Key within the Blob Container within [
|
|||
|
||||
## Example Configuration
|
||||
|
||||
When authenticating using a Service Principal:
|
||||
When authenticating using the Azure CLI or a Service Principal:
|
||||
|
||||
```hcl
|
||||
terraform {
|
||||
|
|
Loading…
Reference in New Issue