Allow user to real oauth token from properly configured Vault instance
This commit is contained in:
parent
2a662b451c
commit
3c14c50aba
|
@ -36,7 +36,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
|||
// representing a GCE machine image.
|
||||
func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
|
||||
driver, err := NewDriverGCE(
|
||||
ui, b.config.ProjectId, b.config.Account)
|
||||
ui, b.config.ProjectId, b.config.Account, b.config.VaultGCPOauthEngine)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -165,6 +165,18 @@ type Config struct {
|
|||
// If true, use the instance's internal IP instead of its external IP
|
||||
// during building.
|
||||
UseInternalIP bool `mapstructure:"use_internal_ip" required:"false"`
|
||||
// Can be set instead of account_file. If set, this builder will use
|
||||
// HashiCorp Vault to generate an Oauth token for authenticating against
|
||||
// Google's cloud. The value should be the path of the token generator
|
||||
// within vault.
|
||||
// For information on how to configure your Vault + GCP engine to produce
|
||||
// Oauth tokens, see https://www.vaultproject.io/docs/auth/gcp.html
|
||||
// You must have the environment variables VAULT_ADDR and VAULT_TOKEN set,
|
||||
// along with any other relevant variables for accessing your vault
|
||||
// instance. For more information, see the Vault docs:
|
||||
// https://www.vaultproject.io/docs/commands/#environment-variables
|
||||
// Example:`"vault_gcp_oauth_engine": "gcp/token/my-project-editor",`
|
||||
VaultGCPOauthEngine string `mapstructure:"vault_gcp_oauth_engine"`
|
||||
// The zone in which to launch the instance used to create the image.
|
||||
// Example: "us-central1-a"
|
||||
Zone string `mapstructure:"zone" required:"true"`
|
||||
|
@ -322,7 +334,12 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
|
|||
errs = packer.MultiErrorAppend(errs, err)
|
||||
}
|
||||
|
||||
// Authenticating via an account file
|
||||
if c.AccountFile != "" {
|
||||
if c.VaultGCPOauthEngine != "" {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("You cannot "+
|
||||
"specify both account_file and vault_gcp_oauth_engine."))
|
||||
}
|
||||
cfg, err := ProcessAccountFile(c.AccountFile)
|
||||
if err != nil {
|
||||
errs = packer.MultiErrorAppend(errs, err)
|
||||
|
|
|
@ -19,6 +19,7 @@ import (
|
|||
"github.com/hashicorp/packer/common/retry"
|
||||
"github.com/hashicorp/packer/helper/useragent"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
vaultapi "github.com/hashicorp/vault/api"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/google"
|
||||
|
@ -35,13 +36,52 @@ type driverGCE struct {
|
|||
|
||||
var DriverScopes = []string{"https://www.googleapis.com/auth/compute", "https://www.googleapis.com/auth/devstorage.full_control"}
|
||||
|
||||
func NewClientGCE(conf *jwt.Config) (*http.Client, error) {
|
||||
// Define a TokenSource that gets tokens from Vault
|
||||
type OauthTokenSource struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
func (ots OauthTokenSource) Token() (*oauth2.Token, error) {
|
||||
log.Printf("Retrieving Oauth token from Vault...")
|
||||
vaultConfig := vaultapi.DefaultConfig()
|
||||
cli, err := vaultapi.NewClient(vaultConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s\n", err)
|
||||
}
|
||||
resp, err := cli.Logical().Read(ots.Path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error reading vault resp: %s", err)
|
||||
}
|
||||
if resp == nil {
|
||||
return nil, fmt.Errorf("Vault Oauth Engine does not exist at the given path.")
|
||||
}
|
||||
token, ok := resp.Data["token"]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("ERROR, token was not present in response body")
|
||||
}
|
||||
at := token.(string)
|
||||
|
||||
log.Printf("Retrieved Oauth token from Vault")
|
||||
return &oauth2.Token{
|
||||
AccessToken: at,
|
||||
Expiry: time.Now().Add(time.Minute * time.Duration(60)),
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
func NewClientGCE(conf *jwt.Config, vaultOauth string) (*http.Client, error) {
|
||||
var err error
|
||||
|
||||
var client *http.Client
|
||||
|
||||
// Auth with AccountFile first if provided
|
||||
if conf != nil && len(conf.PrivateKey) > 0 {
|
||||
if vaultOauth != "" {
|
||||
// Auth with Vault Oauth
|
||||
log.Printf("Using Vault to generate Oauth token.")
|
||||
ts := OauthTokenSource{vaultOauth}
|
||||
return oauth2.NewClient(oauth2.NoContext, ts), nil
|
||||
|
||||
} else if conf != nil && len(conf.PrivateKey) > 0 {
|
||||
// Auth with AccountFile if provided
|
||||
log.Printf("[INFO] Requesting Google token via account_file...")
|
||||
log.Printf("[INFO] -- Email: %s", conf.Email)
|
||||
log.Printf("[INFO] -- Scopes: %s", DriverScopes)
|
||||
|
@ -75,8 +115,8 @@ func NewClientGCE(conf *jwt.Config) (*http.Client, error) {
|
|||
return client, nil
|
||||
}
|
||||
|
||||
func NewDriverGCE(ui packer.Ui, p string, conf *jwt.Config) (Driver, error) {
|
||||
client, err := NewClientGCE(conf)
|
||||
func NewDriverGCE(ui packer.Ui, p string, conf *jwt.Config, vaultOauth string) (Driver, error) {
|
||||
client, err := NewClientGCE(conf, vaultOauth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ type Config struct {
|
|||
Network string `mapstructure:"network"`
|
||||
Paths []string `mapstructure:"paths"`
|
||||
Subnetwork string `mapstructure:"subnetwork"`
|
||||
VaultGCPOauthEngine string `mapstructure:"vault_gcp_oauth_engine"`
|
||||
Zone string `mapstructure:"zone"`
|
||||
|
||||
Account *jwt.Config
|
||||
|
@ -69,6 +70,12 @@ func (p *PostProcessor) Configure(raws ...interface{}) error {
|
|||
p.config.Network = "default"
|
||||
}
|
||||
|
||||
if p.config.AccountFile != "" && p.config.VaultGCPOauthEngine != "" {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, fmt.Errorf("May set either account_file or "+
|
||||
"vault_gcp_oauth_engine, but not both."))
|
||||
}
|
||||
|
||||
if len(errs.Errors) > 0 {
|
||||
return errs
|
||||
}
|
||||
|
@ -142,7 +149,8 @@ func (p *PostProcessor) PostProcess(ctx context.Context, ui packer.Ui, artifact
|
|||
}
|
||||
exporterConfig.CalcTimeout()
|
||||
|
||||
driver, err := googlecompute.NewDriverGCE(ui, builderProjectId, p.config.Account)
|
||||
driver, err := googlecompute.NewDriverGCE(ui, builderProjectId,
|
||||
p.config.Account, p.config.VaultGCPOauthEngine)
|
||||
if err != nil {
|
||||
return nil, false, false, err
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ type Config struct {
|
|||
ImageLabels map[string]string `mapstructure:"image_labels"`
|
||||
ImageName string `mapstructure:"image_name"`
|
||||
SkipClean bool `mapstructure:"skip_clean"`
|
||||
VaultGCPOauthEngine string `mapstructure:"vault_gcp_oauth_engine"`
|
||||
|
||||
Account *jwt.Config
|
||||
ctx interpolate.Context
|
||||
|
@ -78,6 +79,12 @@ func (p *PostProcessor) Configure(raws ...interface{}) error {
|
|||
p.config.Account = cfg
|
||||
}
|
||||
|
||||
if p.config.AccountFile != "" && p.config.VaultGCPOauthEngine != "" {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, fmt.Errorf("May set either account_file or "+
|
||||
"vault_gcp_oauth_engine, but not both."))
|
||||
}
|
||||
|
||||
templates := map[string]*string{
|
||||
"bucket": &p.config.Bucket,
|
||||
"image_name": &p.config.ImageName,
|
||||
|
@ -98,7 +105,7 @@ func (p *PostProcessor) Configure(raws ...interface{}) error {
|
|||
}
|
||||
|
||||
func (p *PostProcessor) PostProcess(ctx context.Context, ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, bool, error) {
|
||||
client, err := googlecompute.NewClientGCE(p.config.Account)
|
||||
client, err := googlecompute.NewClientGCE(p.config.Account, p.config.VaultGCPOauthEngine)
|
||||
if err != nil {
|
||||
return nil, false, false, err
|
||||
}
|
||||
|
|
|
@ -125,3 +125,15 @@
|
|||
- `use_internal_ip` (bool) - If true, use the instance's internal IP instead of its external IP
|
||||
during building.
|
||||
|
||||
- `vault_gcp_oauth_engine` (string) - Can be set instead of account_file. If set, this builder will use
|
||||
HashiCorp Vault to generate an Oauth token for authenticating against
|
||||
Google's cloud. The value should be the path of the token generator
|
||||
within vault.
|
||||
For information on how to configure your Vault + GCP engine to produce
|
||||
Oauth tokens, see https://www.vaultproject.io/docs/auth/gcp.html
|
||||
You must have the environment variables VAULT_ADDR and VAULT_TOKEN set,
|
||||
along with any other relevant variables for accessing your vault
|
||||
instance. For more information, see the Vault docs:
|
||||
https://www.vaultproject.io/docs/commands/#environment-variables
|
||||
Example:`"vault_gcp_oauth_engine": "gcp/token/my-project-editor",`
|
||||
|
Loading…
Reference in New Issue