Add client_cert_token_timeout to address GH-9465 (#10528)

This commit is contained in:
Brian Farrell 2021-03-02 04:51:18 -06:00 committed by GitHub
parent 96b753f3b0
commit 273a720440
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 61 additions and 27 deletions

View File

@ -23,6 +23,7 @@ type FlatConfig struct {
ClientID *string `mapstructure:"client_id" cty:"client_id" hcl:"client_id"`
ClientSecret *string `mapstructure:"client_secret" cty:"client_secret" hcl:"client_secret"`
ClientCertPath *string `mapstructure:"client_cert_path" cty:"client_cert_path" hcl:"client_cert_path"`
ClientCertExpireTimeout *string `mapstructure:"client_cert_token_timeout" required:"false" cty:"client_cert_token_timeout" hcl:"client_cert_token_timeout"`
ClientJWT *string `mapstructure:"client_jwt" cty:"client_jwt" hcl:"client_jwt"`
ObjectID *string `mapstructure:"object_id" cty:"object_id" hcl:"object_id"`
TenantID *string `mapstructure:"tenant_id" required:"false" cty:"tenant_id" hcl:"tenant_id"`
@ -151,6 +152,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"client_id": &hcldec.AttrSpec{Name: "client_id", Type: cty.String, Required: false},
"client_secret": &hcldec.AttrSpec{Name: "client_secret", Type: cty.String, Required: false},
"client_cert_path": &hcldec.AttrSpec{Name: "client_cert_path", Type: cty.String, Required: false},
"client_cert_token_timeout": &hcldec.AttrSpec{Name: "client_cert_token_timeout", Type: cty.String, Required: false},
"client_jwt": &hcldec.AttrSpec{Name: "client_jwt", Type: cty.String, Required: false},
"object_id": &hcldec.AttrSpec{Name: "object_id", Type: cty.String, Required: false},
"tenant_id": &hcldec.AttrSpec{Name: "tenant_id", Type: cty.String, Required: false},

View File

@ -22,6 +22,7 @@ type FlatConfig struct {
ClientID *string `mapstructure:"client_id" cty:"client_id" hcl:"client_id"`
ClientSecret *string `mapstructure:"client_secret" cty:"client_secret" hcl:"client_secret"`
ClientCertPath *string `mapstructure:"client_cert_path" cty:"client_cert_path" hcl:"client_cert_path"`
ClientCertExpireTimeout *string `mapstructure:"client_cert_token_timeout" required:"false" cty:"client_cert_token_timeout" hcl:"client_cert_token_timeout"`
ClientJWT *string `mapstructure:"client_jwt" cty:"client_jwt" hcl:"client_jwt"`
ObjectID *string `mapstructure:"object_id" cty:"object_id" hcl:"object_id"`
TenantID *string `mapstructure:"tenant_id" required:"false" cty:"tenant_id" hcl:"tenant_id"`
@ -76,6 +77,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"client_id": &hcldec.AttrSpec{Name: "client_id", Type: cty.String, Required: false},
"client_secret": &hcldec.AttrSpec{Name: "client_secret", Type: cty.String, Required: false},
"client_cert_path": &hcldec.AttrSpec{Name: "client_cert_path", Type: cty.String, Required: false},
"client_cert_token_timeout": &hcldec.AttrSpec{Name: "client_cert_token_timeout", Type: cty.String, Required: false},
"client_jwt": &hcldec.AttrSpec{Name: "client_jwt", Type: cty.String, Required: false},
"object_id": &hcldec.AttrSpec{Name: "object_id", Type: cty.String, Required: false},
"tenant_id": &hcldec.AttrSpec{Name: "tenant_id", Type: cty.String, Required: false},

View File

@ -39,6 +39,8 @@ type Config struct {
// The path to a pem-encoded certificate that will be used to authenticate
// as the specified AAD SP.
ClientCertPath string `mapstructure:"client_cert_path"`
// The timeout for the JWT Token when using a [client certificate](#client_cert_path). Defaults to 1 hour.
ClientCertExpireTimeout time.Duration `mapstructure:"client_cert_token_timeout" required:"false"`
// A JWT bearer token for client auth (RFC 7523, Sec. 2.2) that will be used
// to authenticate the AAD SP. Provides more control over token the expiration
// when using certificate authentication than when using `client_cert_path`.
@ -163,6 +165,9 @@ func (c Config) Validate(errs *packersdk.MultiError) {
if _, err := os.Stat(c.ClientCertPath); err != nil {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("client_cert_path is not an accessible file: %v", err))
}
if c.ClientCertExpireTimeout < 5*time.Minute {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("client_cert_token_timeout will expire within 5 minutes, please set a value greater than 5 minutes"))
}
return
}
@ -259,7 +264,7 @@ func (c Config) GetServicePrincipalToken(
auth = NewSecretOAuthTokenProvider(*c.cloudEnvironment, c.ClientID, c.ClientSecret, c.TenantID)
case authTypeClientCert:
say("Getting tokens using client certificate")
auth, err = NewCertOAuthTokenProvider(*c.cloudEnvironment, c.ClientID, c.ClientCertPath, c.TenantID)
auth, err = NewCertOAuthTokenProvider(*c.cloudEnvironment, c.ClientID, c.ClientCertPath, c.TenantID, c.ClientCertExpireTimeout)
if err != nil {
return nil, err
}
@ -336,6 +341,10 @@ func (c *Config) FillParameters() error {
c.TenantID = tenantID
}
if c.ClientCertExpireTimeout == 0 {
c.ClientCertExpireTimeout = time.Hour
}
return nil
}

View File

@ -95,6 +95,16 @@ func Test_ClientConfig_RequiredParametersSet(t *testing.T) {
},
wantErr: true,
},
{
name: "client_cert_token_timeout should be 5 minutes or more",
config: Config{
SubscriptionID: "ok",
ClientID: "ok",
ClientCertPath: "/dev/null",
ClientCertExpireTimeout: 1 * time.Minute,
},
wantErr: true,
},
{
name: "too many client_* values",
config: Config{

View File

@ -18,14 +18,14 @@ import (
"github.com/hashicorp/packer/builder/azure/pkcs12"
)
func NewCertOAuthTokenProvider(env azure.Environment, clientID, clientCertPath, tenantID string) (oAuthTokenProvider, error) {
func NewCertOAuthTokenProvider(env azure.Environment, clientID, clientCertPath, tenantID string, certExpireTimeout time.Duration) (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)
jwt, err := makeJWT(clientID, audience, cert, key, certExpireTimeout, true)
if err != nil {
return nil, fmt.Errorf("Error generating JWT: %v", err)
}

View File

@ -49,6 +49,7 @@ type FlatConfig struct {
ClientID *string `mapstructure:"client_id" cty:"client_id" hcl:"client_id"`
ClientSecret *string `mapstructure:"client_secret" cty:"client_secret" hcl:"client_secret"`
ClientCertPath *string `mapstructure:"client_cert_path" cty:"client_cert_path" hcl:"client_cert_path"`
ClientCertExpireTimeout *string `mapstructure:"client_cert_token_timeout" required:"false" cty:"client_cert_token_timeout" hcl:"client_cert_token_timeout"`
ClientJWT *string `mapstructure:"client_jwt" cty:"client_jwt" hcl:"client_jwt"`
ObjectID *string `mapstructure:"object_id" cty:"object_id" hcl:"object_id"`
TenantID *string `mapstructure:"tenant_id" required:"false" cty:"tenant_id" hcl:"tenant_id"`
@ -163,6 +164,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"client_id": &hcldec.AttrSpec{Name: "client_id", Type: cty.String, Required: false},
"client_secret": &hcldec.AttrSpec{Name: "client_secret", Type: cty.String, Required: false},
"client_cert_path": &hcldec.AttrSpec{Name: "client_cert_path", Type: cty.String, Required: false},
"client_cert_token_timeout": &hcldec.AttrSpec{Name: "client_cert_token_timeout", Type: cty.String, Required: false},
"client_jwt": &hcldec.AttrSpec{Name: "client_jwt", Type: cty.String, Required: false},
"object_id": &hcldec.AttrSpec{Name: "object_id", Type: cty.String, Required: false},
"tenant_id": &hcldec.AttrSpec{Name: "tenant_id", Type: cty.String, Required: false},

View File

@ -37,30 +37,31 @@ func (*FlatArtifactParameter) HCL2Spec() map[string]hcldec.Spec {
// FlatConfig is an auto-generated flat version of Config.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatConfig struct {
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"`
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"`
PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"`
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"`
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"`
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
CloudEnvironmentName *string `mapstructure:"cloud_environment_name" required:"false" cty:"cloud_environment_name" hcl:"cloud_environment_name"`
ClientID *string `mapstructure:"client_id" cty:"client_id" hcl:"client_id"`
ClientSecret *string `mapstructure:"client_secret" cty:"client_secret" hcl:"client_secret"`
ClientCertPath *string `mapstructure:"client_cert_path" cty:"client_cert_path" hcl:"client_cert_path"`
ClientJWT *string `mapstructure:"client_jwt" cty:"client_jwt" hcl:"client_jwt"`
ObjectID *string `mapstructure:"object_id" cty:"object_id" hcl:"object_id"`
TenantID *string `mapstructure:"tenant_id" required:"false" cty:"tenant_id" hcl:"tenant_id"`
SubscriptionID *string `mapstructure:"subscription_id" cty:"subscription_id" hcl:"subscription_id"`
UseAzureCLIAuth *bool `mapstructure:"use_azure_cli_auth" required:"false" cty:"use_azure_cli_auth" hcl:"use_azure_cli_auth"`
DtlArtifacts []FlatDtlArtifact `mapstructure:"dtl_artifacts" cty:"dtl_artifacts" hcl:"dtl_artifacts"`
LabName *string `mapstructure:"lab_name" cty:"lab_name" hcl:"lab_name"`
ResourceGroupName *string `mapstructure:"lab_resource_group_name" cty:"lab_resource_group_name" hcl:"lab_resource_group_name"`
VMName *string `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"`
PollingDurationTimeout *string `mapstructure:"polling_duration_timeout" required:"false" cty:"polling_duration_timeout" hcl:"polling_duration_timeout"`
AzureTags map[string]*string `mapstructure:"azure_tags" cty:"azure_tags" hcl:"azure_tags"`
Json map[string]interface{} `cty:"json" hcl:"json"`
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"`
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"`
PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"`
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"`
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"`
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
CloudEnvironmentName *string `mapstructure:"cloud_environment_name" required:"false" cty:"cloud_environment_name" hcl:"cloud_environment_name"`
ClientID *string `mapstructure:"client_id" cty:"client_id" hcl:"client_id"`
ClientSecret *string `mapstructure:"client_secret" cty:"client_secret" hcl:"client_secret"`
ClientCertPath *string `mapstructure:"client_cert_path" cty:"client_cert_path" hcl:"client_cert_path"`
ClientCertExpireTimeout *string `mapstructure:"client_cert_token_timeout" required:"false" cty:"client_cert_token_timeout" hcl:"client_cert_token_timeout"`
ClientJWT *string `mapstructure:"client_jwt" cty:"client_jwt" hcl:"client_jwt"`
ObjectID *string `mapstructure:"object_id" cty:"object_id" hcl:"object_id"`
TenantID *string `mapstructure:"tenant_id" required:"false" cty:"tenant_id" hcl:"tenant_id"`
SubscriptionID *string `mapstructure:"subscription_id" cty:"subscription_id" hcl:"subscription_id"`
UseAzureCLIAuth *bool `mapstructure:"use_azure_cli_auth" required:"false" cty:"use_azure_cli_auth" hcl:"use_azure_cli_auth"`
DtlArtifacts []FlatDtlArtifact `mapstructure:"dtl_artifacts" cty:"dtl_artifacts" hcl:"dtl_artifacts"`
LabName *string `mapstructure:"lab_name" cty:"lab_name" hcl:"lab_name"`
ResourceGroupName *string `mapstructure:"lab_resource_group_name" cty:"lab_resource_group_name" hcl:"lab_resource_group_name"`
VMName *string `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"`
PollingDurationTimeout *string `mapstructure:"polling_duration_timeout" required:"false" cty:"polling_duration_timeout" hcl:"polling_duration_timeout"`
AzureTags map[string]*string `mapstructure:"azure_tags" cty:"azure_tags" hcl:"azure_tags"`
Json map[string]interface{} `cty:"json" hcl:"json"`
}
// FlatMapstructure returns a new FlatConfig.
@ -87,6 +88,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"client_id": &hcldec.AttrSpec{Name: "client_id", Type: cty.String, Required: false},
"client_secret": &hcldec.AttrSpec{Name: "client_secret", Type: cty.String, Required: false},
"client_cert_path": &hcldec.AttrSpec{Name: "client_cert_path", Type: cty.String, Required: false},
"client_cert_token_timeout": &hcldec.AttrSpec{Name: "client_cert_token_timeout", Type: cty.String, Required: false},
"client_jwt": &hcldec.AttrSpec{Name: "client_jwt", Type: cty.String, Required: false},
"object_id": &hcldec.AttrSpec{Name: "object_id", Type: cty.String, Required: false},
"tenant_id": &hcldec.AttrSpec{Name: "tenant_id", Type: cty.String, Required: false},

View File

@ -61,6 +61,9 @@ you should specify `subscription_id`, `client_id` and one of `client_secret`,
- `client_cert_path` (string) - The location of a PEM file containing a
certificate and private key for service principal.
- `client_cert_token_timeout` (duration string | ex: "1h30m12s") - How long to set the expire time on the token created when using
`client_cert_path`.
- `client_jwt` (string) - The bearer JWT assertion signed using a certificate
associated with your service principal principal. See [Azure Active
Directory docs](https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-certificate-credentials)

View File

@ -94,6 +94,8 @@ way to authenticate the SP to AAD:
for the AAD SP.
- `client_cert_path` - allows usage of a certificate to be used to
authenticate as the specified AAD SP.
- `client_cert_token_timeout` - How long to set the expire time on the token created when using
`client_cert_path`.
- `client_jwt` - For advanced scenario's where the used cannot provide Packer
the full certificate, they can provide a JWT bearer token for client auth
(RFC 7523, Sec. 2.2). These bearer tokens are created and signed using a

View File

@ -12,6 +12,8 @@
- `client_cert_path` (string) - The path to a pem-encoded certificate that will be used to authenticate
as the specified AAD SP.
- `client_cert_token_timeout` (duration string | ex: "1h5m2s") - The timeout for the JWT Token when using a [client certificate](#client_cert_path). Defaults to 1 hour.
- `client_jwt` (string) - A JWT bearer token for client auth (RFC 7523, Sec. 2.2) that will be used
to authenticate the AAD SP. Provides more control over token the expiration
when using certificate authentication than when using `client_cert_path`.