diff --git a/cli.go b/cli.go index cfae16c1..58714b40 100644 --- a/cli.go +++ b/cli.go @@ -209,7 +209,7 @@ Here is an example bash command using the CloudFlare DNS provider: fmt.Fprintln(w, "\tdnsmadeeasy:\tDNSMADEEASY_API_KEY, DNSMADEEASY_API_SECRET") fmt.Fprintln(w, "\texoscale:\tEXOSCALE_API_KEY, EXOSCALE_API_SECRET, EXOSCALE_ENDPOINT") fmt.Fprintln(w, "\tgandi:\tGANDI_API_KEY") - fmt.Fprintln(w, "\tgcloud:\tGCE_PROJECT") + fmt.Fprintln(w, "\tgcloud:\tGCE_PROJECT, GCE_SERVICE_ACCOUNT_FILE") fmt.Fprintln(w, "\tlinode:\tLINODE_API_KEY") fmt.Fprintln(w, "\tmanual:\tnone") fmt.Fprintln(w, "\tnamecheap:\tNAMECHEAP_API_USER, NAMECHEAP_API_KEY") diff --git a/cli_handlers.go b/cli_handlers.go index dad19a14..79bbb37e 100644 --- a/cli_handlers.go +++ b/cli_handlers.go @@ -114,7 +114,7 @@ func setup(c *cli.Context) (*Configuration, *Account, *acme.Client) { } if c.GlobalIsSet("dns") { - provider, err := dns.NewDNSChallengeProviderByName(c.GlobalString("dns")) + provider, err := dns.NewDNSChallengeProviderByName(c.GlobalString("dns")) if err != nil { logger().Fatal(err) } diff --git a/providers/dns/dns_providers.go b/providers/dns/dns_providers.go index 33fca0fa..af6c02cc 100644 --- a/providers/dns/dns_providers.go +++ b/providers/dns/dns_providers.go @@ -48,7 +48,7 @@ func NewDNSChallengeProviderByName(name string) (acme.ChallengeProvider, error) case "dyn": provider, err = dyn.NewDNSProvider() case "exoscale": - provider, err = exoscale.NewDNSProvider() + provider, err = exoscale.NewDNSProvider() case "gandi": provider, err = gandi.NewDNSProvider() case "gcloud": diff --git a/providers/dns/googlecloud/googlecloud.go b/providers/dns/googlecloud/googlecloud.go index ea6c0875..ba753f6d 100644 --- a/providers/dns/googlecloud/googlecloud.go +++ b/providers/dns/googlecloud/googlecloud.go @@ -4,12 +4,14 @@ package googlecloud import ( "fmt" + "io/ioutil" "os" "time" "github.com/xenolf/lego/acme" "golang.org/x/net/context" + "golang.org/x/oauth2" "golang.org/x/oauth2/google" "google.golang.org/api/dns/v1" @@ -22,9 +24,14 @@ type DNSProvider struct { } // NewDNSProvider returns a DNSProvider instance configured for Google Cloud -// DNS. Credentials must be passed in the environment variable: GCE_PROJECT. +// DNS. Project name must be passed in the environment variable: GCE_PROJECT. +// A Service Account file can be passed in the environment variable: +// GCE_SERVICE_ACCOUNT_FILE func NewDNSProvider() (*DNSProvider, error) { project := os.Getenv("GCE_PROJECT") + if saFile, ok := os.LookupEnv("GCE_SERVICE_ACCOUNT_FILE"); ok { + return NewDNSProviderServiceAccount(project, saFile) + } return NewDNSProviderCredentials(project) } @@ -49,6 +56,36 @@ func NewDNSProviderCredentials(project string) (*DNSProvider, error) { }, nil } +// NewDNSProviderServiceAccount uses the supplied service account JSON file to +// return a DNSProvider instance configured for Google Cloud DNS. +func NewDNSProviderServiceAccount(project string, saFile string) (*DNSProvider, error) { + if project == "" { + return nil, fmt.Errorf("Google Cloud project name missing") + } + if saFile == "" { + return nil, fmt.Errorf("Google Cloud Service Account file missing") + } + + dat, err := ioutil.ReadFile(saFile) + if err != nil { + return nil, fmt.Errorf("Unable to read Service Account file: %v", err) + } + conf, err := google.JWTConfigFromJSON(dat, dns.NdevClouddnsReadwriteScope) + if err != nil { + return nil, fmt.Errorf("Unable to acquire config: %v", err) + } + client := conf.Client(oauth2.NoContext) + + svc, err := dns.New(client) + if err != nil { + return nil, fmt.Errorf("Unable to create Google Cloud DNS service: %v", err) + } + return &DNSProvider{ + project: project, + client: svc, + }, nil +} + // Present creates a TXT record to fulfil the dns-01 challenge. func (c *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)