forked from hashicorp/packer
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
28 changed files
with
992 additions
and
526 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
package arm | ||
|
||
import ( | ||
"crypto/ecdsa" | ||
"crypto/rand" | ||
"crypto/rsa" | ||
"crypto/sha1" | ||
"crypto/x509" | ||
"encoding/base64" | ||
"encoding/pem" | ||
"fmt" | ||
"io/ioutil" | ||
"os" | ||
"time" | ||
|
||
"github.com/Azure/go-autorest/autorest/azure" | ||
jwt "github.com/dgrijalva/jwt-go" | ||
) | ||
|
||
func NewCertOAuthTokenProvider(env azure.Environment, clientID, clientCertPath, tenantID string) (oAuthTokenProvider, error) { | ||
cert, key, err := readCert(clientCertPath) | ||
if err != nil { | ||
return nil, fmt.Errorf("Error reading certificate: %v", err) | ||
} | ||
|
||
audience := fmt.Sprintf("%s%s/oauth2/token", env.ActiveDirectoryEndpoint, tenantID) | ||
jwt, err := makeJWT(clientID, audience, cert, key, time.Hour, true) | ||
if err != nil { | ||
return nil, fmt.Errorf("Error generating JWT: %v", err) | ||
} | ||
|
||
return NewJWTOAuthTokenProvider(env, clientID, jwt, tenantID), nil | ||
} | ||
|
||
// Creates a new JSON Web Token to be used as bearer JWT to authenticate | ||
// to the Azure AD token endpoint to retrieve an access token for `audience`. | ||
// If the full certificate is included in the token, then issuer/subject name | ||
// could be used to authenticate if configured by the identity provider (AAD). | ||
func makeJWT(clientID string, audience string, | ||
cert *x509.Certificate, privatekey interface{}, | ||
validFor time.Duration, includeFullCertificate bool) (string, error) { | ||
|
||
// The jti (JWT ID) claim provides a unique identifier for the JWT. | ||
// See https://tools.ietf.org/html/rfc7519#section-4.1.7 | ||
jti := make([]byte, 20) | ||
_, err := rand.Read(jti) | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
var token *jwt.Token | ||
if cert.PublicKeyAlgorithm == x509.RSA { | ||
token = jwt.New(jwt.SigningMethodRS256) | ||
} else if cert.PublicKeyAlgorithm == x509.ECDSA { | ||
token = jwt.New(jwt.SigningMethodES256) | ||
} else { | ||
return "", fmt.Errorf("Don't know how to handle this type of key algorithm: %v", cert.PublicKeyAlgorithm) | ||
} | ||
|
||
hasher := sha1.New() | ||
if _, err := hasher.Write(cert.Raw); err != nil { | ||
return "", err | ||
} | ||
thumbprint := base64.URLEncoding.EncodeToString(hasher.Sum(nil)) | ||
|
||
// X.509 thumbprint, see https://tools.ietf.org/html/rfc7515#section-4.1.7 | ||
token.Header["x5t"] = thumbprint | ||
if includeFullCertificate { | ||
// X.509 certificate (chain), see https://tools.ietf.org/html/rfc7515#section-4.1.6 | ||
token.Header["x5c"] = []string{base64.StdEncoding.EncodeToString(cert.Raw)} | ||
} | ||
|
||
token.Claims = jwt.MapClaims{ | ||
// See https://tools.ietf.org/html/rfc7519#section-4.1 | ||
"aud": audience, | ||
"iss": clientID, | ||
"sub": clientID, | ||
"jti": base64.URLEncoding.EncodeToString(jti), | ||
"nbf": time.Now().Unix(), | ||
"exp": time.Now().Add(validFor).Unix(), | ||
} | ||
|
||
return token.SignedString(privatekey) | ||
} | ||
|
||
func readCert(file string) (cert *x509.Certificate, key interface{}, err error) { | ||
f, err := os.Open(file) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
defer f.Close() | ||
d, err := ioutil.ReadAll(f) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
|
||
blocks := []*pem.Block{} | ||
for len(d) > 0 { | ||
var b *pem.Block | ||
b, d = pem.Decode(d) | ||
if b == nil { | ||
break | ||
} | ||
blocks = append(blocks, b) | ||
} | ||
|
||
certs := []*x509.Certificate{} | ||
for _, block := range blocks { | ||
if block.Type == "CERTIFICATE" { | ||
c, err := x509.ParseCertificate(block.Bytes) | ||
if err != nil { | ||
return nil, nil, fmt.Errorf( | ||
"Failed to read certificate block: %v", err) | ||
} | ||
certs = append(certs, c) | ||
} else if block.Type == "PRIVATE KEY" { | ||
key, err = x509.ParsePKCS8PrivateKey(block.Bytes) | ||
if err != nil { | ||
return nil, nil, fmt.Errorf( | ||
"Failed to read private key block: %v", err) | ||
} | ||
} | ||
// Don't care about other types of blocks, ignore | ||
} | ||
|
||
if key == nil { | ||
return nil, nil, fmt.Errorf("Did not find private key in pem file") | ||
} | ||
|
||
// find the certificate that belongs to the private key by comparing the public keys | ||
switch key := key.(type) { | ||
case *rsa.PrivateKey: | ||
for _, c := range certs { | ||
if cp, ok := c.PublicKey.(*rsa.PublicKey); ok && | ||
(cp.N.Cmp(key.PublicKey.N) == 0) { | ||
cert = c | ||
} | ||
} | ||
|
||
case *ecdsa.PrivateKey: | ||
for _, c := range certs { | ||
if cp, ok := c.PublicKey.(*ecdsa.PublicKey); ok && | ||
(cp.X.Cmp(key.PublicKey.X) == 0) && | ||
(cp.Y.Cmp(key.PublicKey.Y) == 0) { | ||
cert = c | ||
} | ||
} | ||
} | ||
|
||
if cert == nil { | ||
return nil, nil, fmt.Errorf("Did not find certificate belonging to private key in pem file") | ||
} | ||
|
||
return cert, key, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package arm | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/Azure/go-autorest/autorest/adal" | ||
"github.com/Azure/go-autorest/autorest/azure" | ||
packerAzureCommon "github.com/hashicorp/packer/builder/azure/common" | ||
) | ||
|
||
func NewDeviceFlowOAuthTokenProvider(env azure.Environment, say func(string), tenantID string) oAuthTokenProvider { | ||
return &deviceflowOauthTokenProvider{} | ||
} | ||
|
||
type deviceflowOauthTokenProvider struct { | ||
env azure.Environment | ||
say func(string) | ||
tenantID string | ||
} | ||
|
||
func (tp *deviceflowOauthTokenProvider) getServicePrincipalToken() (*adal.ServicePrincipalToken, error) { | ||
return tp.getServicePrincipalTokenWithResource(tp.env.ResourceManagerEndpoint) | ||
} | ||
|
||
func (tp *deviceflowOauthTokenProvider) getServicePrincipalTokenWithResource(resource string) (*adal.ServicePrincipalToken, error) { | ||
if resource == tp.env.ServiceManagementEndpoint { | ||
tp.say("Getting auth token for Service management endpoint") | ||
} else if resource == strings.TrimRight(tp.env.KeyVaultEndpoint, "/") { | ||
tp.say("Getting token for Vault resource") | ||
} else { | ||
tp.say(fmt.Sprintf("Getting token for %s", resource)) | ||
} | ||
|
||
return packerAzureCommon.Authenticate(tp.env, tp.tenantID, tp.say, resource) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package arm | ||
|
||
import ( | ||
"net/url" | ||
|
||
"github.com/Azure/go-autorest/autorest/adal" | ||
"github.com/Azure/go-autorest/autorest/azure" | ||
) | ||
|
||
// for clientID/bearer JWT auth | ||
type jwtOAuthTokenProvider struct { | ||
env azure.Environment | ||
clientID, clientJWT, tenantID string | ||
} | ||
|
||
func NewJWTOAuthTokenProvider(env azure.Environment, clientID, clientJWT, tenantID string) oAuthTokenProvider { | ||
return &jwtOAuthTokenProvider{env, clientID, clientJWT, tenantID} | ||
} | ||
|
||
func (tp *jwtOAuthTokenProvider) getServicePrincipalToken() (*adal.ServicePrincipalToken, error) { | ||
return tp.getServicePrincipalTokenWithResource(tp.env.ResourceManagerEndpoint) | ||
} | ||
|
||
func (tp *jwtOAuthTokenProvider) getServicePrincipalTokenWithResource(resource string) (*adal.ServicePrincipalToken, error) { | ||
oauthConfig, err := adal.NewOAuthConfig(tp.env.ActiveDirectoryEndpoint, tp.tenantID) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return adal.NewServicePrincipalTokenWithSecret( | ||
*oauthConfig, | ||
tp.clientID, | ||
resource, | ||
tp) | ||
} | ||
|
||
// implements github.com/Azure/go-autorest/autorest/adal.ServicePrincipalSecret | ||
func (tp *jwtOAuthTokenProvider) SetAuthenticationValues( | ||
t *adal.ServicePrincipalToken, v *url.Values) error { | ||
v.Set("client_assertion", tp.clientJWT) | ||
v.Set("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer") | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package arm | ||
|
||
import ( | ||
"github.com/Azure/go-autorest/autorest/adal" | ||
"github.com/Azure/go-autorest/autorest/azure" | ||
) | ||
|
||
// for managed identity auth | ||
type msiOAuthTokenProvider struct { | ||
env azure.Environment | ||
} | ||
|
||
func NewMSIOAuthTokenProvider(env azure.Environment) oAuthTokenProvider { | ||
return &msiOAuthTokenProvider{env} | ||
} | ||
|
||
func (tp *msiOAuthTokenProvider) getServicePrincipalToken() (*adal.ServicePrincipalToken, error) { | ||
return tp.getServicePrincipalTokenWithResource(tp.env.ResourceManagerEndpoint) | ||
} | ||
|
||
func (tp *msiOAuthTokenProvider) getServicePrincipalTokenWithResource(resource string) (*adal.ServicePrincipalToken, error) { | ||
return adal.NewServicePrincipalTokenFromMSI("http://169.254.169.254/metadata/identity/oauth2/token", resource) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package arm | ||
|
||
import ( | ||
"github.com/Azure/go-autorest/autorest/adal" | ||
"github.com/Azure/go-autorest/autorest/azure" | ||
) | ||
|
||
// for clientID/secret auth | ||
type secretOAuthTokenProvider struct { | ||
env azure.Environment | ||
clientID, clientSecret, tenantID string | ||
} | ||
|
||
func NewSecretOAuthTokenProvider(env azure.Environment, clientID, clientSecret, tenantID string) oAuthTokenProvider { | ||
return &secretOAuthTokenProvider{env, clientID, clientSecret, tenantID} | ||
} | ||
|
||
func (tp *secretOAuthTokenProvider) getServicePrincipalToken() (*adal.ServicePrincipalToken, error) { | ||
return tp.getServicePrincipalTokenWithResource(tp.env.ResourceManagerEndpoint) | ||
} | ||
|
||
func (tp *secretOAuthTokenProvider) getServicePrincipalTokenWithResource(resource string) (*adal.ServicePrincipalToken, error) { | ||
oauthConfig, err := adal.NewOAuthConfig(tp.env.ActiveDirectoryEndpoint, tp.tenantID) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
spt, err := adal.NewServicePrincipalToken( | ||
*oauthConfig, | ||
tp.clientID, | ||
tp.clientSecret, | ||
resource) | ||
|
||
return spt, err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.