diff --git a/Gopkg.lock b/Gopkg.lock
index c8b9f4239..e8b4586ec 100644
--- a/Gopkg.lock
+++ b/Gopkg.lock
@@ -49,6 +49,7 @@
"autorest",
"autorest/adal",
"autorest/azure",
+ "autorest/azure/auth",
"autorest/date",
"autorest/to"
]
@@ -363,6 +364,12 @@
revision = "06ea1031745cb8b3dab3f6a236daf2b0aa468b7e"
version = "v3.2.0"
+[[projects]]
+ name = "github.com/dimchansky/utfbom"
+ packages = ["."]
+ revision = "5448fe645cb1964ba70ac8f9f2ffe975e61a536c"
+ version = "v1.0.0"
+
[[projects]]
branch = "master"
name = "github.com/dnsimple/dnsimple-go"
@@ -533,19 +540,6 @@
revision = "44cc805cf13205b55f69e14bcb69867d1ae92f98"
version = "v1.1.0"
-[[projects]]
- name = "github.com/edeckers/auroradnsclient"
- packages = [
- ".",
- "records",
- "requests",
- "requests/errors",
- "tokens",
- "zones"
- ]
- revision = "1563e622aaca0a8bb895a448f31d4a430ab97586"
- version = "v1.0.3"
-
[[projects]]
branch = "master"
name = "github.com/elazarl/go-bindata-assetfs"
@@ -863,6 +857,12 @@
packages = ["."]
revision = "b84e30acd515aadc4b783ad4ff83aff3299bdfe0"
+[[projects]]
+ name = "github.com/ldez/go-auroradns"
+ packages = ["."]
+ revision = "b40dfcae7c417f8129579362695dc1f3cfe5928d"
+ version = "v2.0.0"
+
[[projects]]
branch = "master"
name = "github.com/libkermit/compose"
@@ -1398,7 +1398,7 @@
"providers/dns/vegadns",
"providers/dns/vultr"
]
- revision = "160d6fe60303699067faad57dc0b1e147ac499ef"
+ revision = "1151b4e3befc51b7b215179c87791753721dc6d5"
[[projects]]
branch = "master"
@@ -1410,6 +1410,8 @@
"ed25519/internal/edwards25519",
"ocsp",
"pbkdf2",
+ "pkcs12",
+ "pkcs12/internal/rc2",
"scrypt",
"ssh/terminal"
]
diff --git a/docs/configuration/acme.md b/docs/configuration/acme.md
index cfa527b8a..6cb617744 100644
--- a/docs/configuration/acme.md
+++ b/docs/configuration/acme.md
@@ -252,50 +252,51 @@ Useful if internal networks block external DNS queries.
Here is a list of supported `provider`s, that can automate the DNS verification, along with the required environment variables and their [wildcard & root domain support](/configuration/acme/#wildcard-domains) for each. Do not hesitate to complete it.
-| Provider Name | Provider Code | Environment Variables | Wildcard & Root Domain Support |
-|--------------------------------------------------------|----------------|---------------------------------------------------------------------------------------------------------------------------------|--------------------------------|
-| [Alibaba Cloud](https://www.vultr.com) | `alidns` | `ALICLOUD_ACCESS_KEY`, `ALICLOUD_SECRET_KEY`, `ALICLOUD_REGION_ID` | Not tested yet |
-| [Auroradns](https://www.pcextreme.com/aurora/dns) | `auroradns` | `AURORA_USER_ID`, `AURORA_KEY`, `AURORA_ENDPOINT` | Not tested yet |
-| [Azure](https://azure.microsoft.com/services/dns/) | `azure` | `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_SUBSCRIPTION_ID`, `AZURE_TENANT_ID`, `AZURE_RESOURCE_GROUP` | Not tested yet |
-| [Blue Cat](https://www.bluecatnetworks.com/) | `bluecat` | `BLUECAT_SERVER_URL`, `BLUECAT_USER_NAME`, `BLUECAT_PASSWORD`, `BLUECAT_CONFIG_NAME`, `BLUECAT_DNS_VIEW` | Not tested yet |
-| [Cloudflare](https://www.cloudflare.com) | `cloudflare` | `CF_API_EMAIL`, `CF_API_KEY` - The `Global API Key` needs to be used, not the `Origin CA Key` | YES |
-| [CloudXNS](https://www.cloudxns.net) | `cloudxns` | `CLOUDXNS_API_KEY`, `CLOUDXNS_SECRET_KEY` | Not tested yet |
-| [DigitalOcean](https://www.digitalocean.com) | `digitalocean` | `DO_AUTH_TOKEN` | YES |
-| [DNSimple](https://dnsimple.com) | `dnsimple` | `DNSIMPLE_OAUTH_TOKEN`, `DNSIMPLE_BASE_URL` | Not tested yet |
-| [DNS Made Easy](https://dnsmadeeasy.com) | `dnsmadeeasy` | `DNSMADEEASY_API_KEY`, `DNSMADEEASY_API_SECRET`, `DNSMADEEASY_SANDBOX` | Not tested yet |
-| [DNSPod](http://www.dnspod.net/) | `dnspod` | `DNSPOD_API_KEY` | Not tested yet |
-| [DreamHost](https://www.dreamhost.com/) | `dreamhost` | `DREAMHOST_API_KEY` | YES |
-| [Duck DNS](https://www.duckdns.org/) | `duckdns` | `DUCKDNS_TOKEN` | No |
-| [Dyn](https://dyn.com) | `dyn` | `DYN_CUSTOMER_NAME`, `DYN_USER_NAME`, `DYN_PASSWORD` | Not tested yet |
-| External Program | `exec` | `EXEC_PATH` | Not tested yet |
-| [Exoscale](https://www.exoscale.ch) | `exoscale` | `EXOSCALE_API_KEY`, `EXOSCALE_API_SECRET`, `EXOSCALE_ENDPOINT` | YES |
-| [Fast DNS](https://www.akamai.com/) | `fastdns` | `AKAMAI_CLIENT_TOKEN`, `AKAMAI_CLIENT_SECRET`, `AKAMAI_ACCESS_TOKEN` | Not tested yet |
-| [Gandi](https://www.gandi.net) | `gandi` | `GANDI_API_KEY` | Not tested yet |
-| [Gandi v5](http://doc.livedns.gandi.net) | `gandiv5` | `GANDIV5_API_KEY` | YES |
-| [Glesys](https://glesys.com/) | `glesys` | `GLESYS_API_USER`, `GLESYS_API_KEY`, `GLESYS_DOMAIN` | Not tested yet |
-| [GoDaddy](https://godaddy.com/domains) | `godaddy` | `GODADDY_API_KEY`, `GODADDY_API_SECRET` | Not tested yet |
-| [Google Cloud DNS](https://cloud.google.com/dns/docs/) | `gcloud` | `GCE_PROJECT`, `GCE_SERVICE_ACCOUNT_FILE` | YES |
-| [hosting.de](https://www.hosting.de) | `hostingde` | `HOSTINGDE_API_KEY`, `HOSTINGDE_ZONE_NAME` | Not tested yet |
-| [IIJ](https://www.iij.ad.jp/) | `iij` | `IIJ_API_ACCESS_KEY`, `IIJ_API_SECRET_KEY`, `IIJ_DO_SERVICE_CODE` | Not tested yet |
-| [Lightsail](https://aws.amazon.com/lightsail/) | `lightsail` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `DNS_ZONE` | Not tested yet |
-| [Linode](https://www.linode.com) | `linode` | `LINODE_API_KEY` | Not tested yet |
-| [Linode v4](https://www.linode.com) | `linodev4` | `LINODE_TOKEN` | Not tested yet |
-| manual | - | none, but you need to run Traefik interactively, turn on `acmeLogging` to see instructions and press Enter. | YES |
-| [Namecheap](https://www.namecheap.com) | `namecheap` | `NAMECHEAP_API_USER`, `NAMECHEAP_API_KEY` | YES |
-| [name.com](https://www.name.com/) | `namedotcom` | `NAMECOM_USERNAME`, `NAMECOM_API_TOKEN`, `NAMECOM_SERVER` | Not tested yet |
-| [Netcup](https://www.netcup.eu/) | `netcup` | `NETCUP_CUSTOMER_NUMBER`, `NETCUP_API_KEY`, `NETCUP_API_PASSWORD` | Not tested yet |
-| [NIFCloud](https://cloud.nifty.com/service/dns.htm) | `nifcloud` | `NIFCLOUD_ACCESS_KEY_ID`, `NIFCLOUD_SECRET_ACCESS_KEY` | Not tested yet |
-| [Ns1](https://ns1.com/) | `ns1` | `NS1_API_KEY` | Not tested yet |
-| [Open Telekom Cloud](https://cloud.telekom.de) | `otc` | `OTC_DOMAIN_NAME`, `OTC_USER_NAME`, `OTC_PASSWORD`, `OTC_PROJECT_NAME`, `OTC_IDENTITY_ENDPOINT` | Not tested yet |
-| [OVH](https://www.ovh.com) | `ovh` | `OVH_ENDPOINT`, `OVH_APPLICATION_KEY`, `OVH_APPLICATION_SECRET`, `OVH_CONSUMER_KEY` | YES |
-| [PowerDNS](https://www.powerdns.com) | `pdns` | `PDNS_API_KEY`, `PDNS_API_URL` | Not tested yet |
-| [Rackspace](https://www.rackspace.com/cloud/dns) | `rackspace` | `RACKSPACE_USER`, `RACKSPACE_API_KEY` | Not tested yet |
-| [RFC2136](https://tools.ietf.org/html/rfc2136) | `rfc2136` | `RFC2136_TSIG_KEY`, `RFC2136_TSIG_SECRET`, `RFC2136_TSIG_ALGORITHM`, `RFC2136_NAMESERVER` | Not tested yet |
-| [Route 53](https://aws.amazon.com/route53/) | `route53` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `[AWS_REGION]`, `[AWS_HOSTED_ZONE_ID]` or a configured user/instance IAM profile. | YES |
-| [Sakura Cloud](https://cloud.sakura.ad.jp/) | `sakuracloud` | `SAKURACLOUD_ACCESS_TOKEN`, `SAKURACLOUD_ACCESS_TOKEN_SECRET` | Not tested yet |
-| [Stackpath](https://www.stackpath.com/) | `stackpath` | `STACKPATH_CLIENT_ID`, `STACKPATH_CLIENT_SECRET`, `STACKPATH_STACK_ID` | Not tested yet |
-| [VegaDNS](https://github.com/shupp/VegaDNS-API) | `vegadns` | `SECRET_VEGADNS_KEY`, `SECRET_VEGADNS_SECRET`, `VEGADNS_URL` | Not tested yet |
-| [VULTR](https://www.vultr.com) | `vultr` | `VULTR_API_KEY` | Not tested yet |
+| Provider Name | Provider Code | Environment Variables | Wildcard & Root Domain Support |
+|--------------------------------------------------------|----------------|-------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------|
+| [ACME DNS](https://github.com/joohoi/acme-dns) | `acmedns` | `ACME_DNS_API_BASE`, `ACME_DNS_STORAGE_PATH` | Not tested yet |
+| [Alibaba Cloud](https://www.vultr.com) | `alidns` | `ALICLOUD_ACCESS_KEY`, `ALICLOUD_SECRET_KEY`, `ALICLOUD_REGION_ID` | Not tested yet |
+| [Auroradns](https://www.pcextreme.com/aurora/dns) | `auroradns` | `AURORA_USER_ID`, `AURORA_KEY`, `AURORA_ENDPOINT` | Not tested yet |
+| [Azure](https://azure.microsoft.com/services/dns/) | `azure` | `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_SUBSCRIPTION_ID`, `AZURE_TENANT_ID`, `AZURE_RESOURCE_GROUP`, `[AZURE_METADATA_ENDPOINT]` | Not tested yet |
+| [Blue Cat](https://www.bluecatnetworks.com/) | `bluecat` | `BLUECAT_SERVER_URL`, `BLUECAT_USER_NAME`, `BLUECAT_PASSWORD`, `BLUECAT_CONFIG_NAME`, `BLUECAT_DNS_VIEW` | Not tested yet |
+| [Cloudflare](https://www.cloudflare.com) | `cloudflare` | `CF_API_EMAIL`, `CF_API_KEY` - The `Global API Key` needs to be used, not the `Origin CA Key` | YES |
+| [CloudXNS](https://www.cloudxns.net) | `cloudxns` | `CLOUDXNS_API_KEY`, `CLOUDXNS_SECRET_KEY` | Not tested yet |
+| [DigitalOcean](https://www.digitalocean.com) | `digitalocean` | `DO_AUTH_TOKEN` | YES |
+| [DNSimple](https://dnsimple.com) | `dnsimple` | `DNSIMPLE_OAUTH_TOKEN`, `DNSIMPLE_BASE_URL` | Not tested yet |
+| [DNS Made Easy](https://dnsmadeeasy.com) | `dnsmadeeasy` | `DNSMADEEASY_API_KEY`, `DNSMADEEASY_API_SECRET`, `DNSMADEEASY_SANDBOX` | Not tested yet |
+| [DNSPod](http://www.dnspod.net/) | `dnspod` | `DNSPOD_API_KEY` | Not tested yet |
+| [DreamHost](https://www.dreamhost.com/) | `dreamhost` | `DREAMHOST_API_KEY` | YES |
+| [Duck DNS](https://www.duckdns.org/) | `duckdns` | `DUCKDNS_TOKEN` | No |
+| [Dyn](https://dyn.com) | `dyn` | `DYN_CUSTOMER_NAME`, `DYN_USER_NAME`, `DYN_PASSWORD` | Not tested yet |
+| External Program | `exec` | `EXEC_PATH` | Not tested yet |
+| [Exoscale](https://www.exoscale.ch) | `exoscale` | `EXOSCALE_API_KEY`, `EXOSCALE_API_SECRET`, `EXOSCALE_ENDPOINT` | YES |
+| [Fast DNS](https://www.akamai.com/) | `fastdns` | `AKAMAI_CLIENT_TOKEN`, `AKAMAI_CLIENT_SECRET`, `AKAMAI_ACCESS_TOKEN` | Not tested yet |
+| [Gandi](https://www.gandi.net) | `gandi` | `GANDI_API_KEY` | Not tested yet |
+| [Gandi v5](http://doc.livedns.gandi.net) | `gandiv5` | `GANDIV5_API_KEY` | YES |
+| [Glesys](https://glesys.com/) | `glesys` | `GLESYS_API_USER`, `GLESYS_API_KEY`, `GLESYS_DOMAIN` | Not tested yet |
+| [GoDaddy](https://godaddy.com/domains) | `godaddy` | `GODADDY_API_KEY`, `GODADDY_API_SECRET` | Not tested yet |
+| [Google Cloud DNS](https://cloud.google.com/dns/docs/) | `gcloud` | `GCE_PROJECT`, `GCE_SERVICE_ACCOUNT_FILE` | YES |
+| [hosting.de](https://www.hosting.de) | `hostingde` | `HOSTINGDE_API_KEY`, `HOSTINGDE_ZONE_NAME` | Not tested yet |
+| [IIJ](https://www.iij.ad.jp/) | `iij` | `IIJ_API_ACCESS_KEY`, `IIJ_API_SECRET_KEY`, `IIJ_DO_SERVICE_CODE` | Not tested yet |
+| [Lightsail](https://aws.amazon.com/lightsail/) | `lightsail` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `DNS_ZONE` | Not tested yet |
+| [Linode](https://www.linode.com) | `linode` | `LINODE_API_KEY` | Not tested yet |
+| [Linode v4](https://www.linode.com) | `linodev4` | `LINODE_TOKEN` | Not tested yet |
+| manual | - | none, but you need to run Traefik interactively, turn on `acmeLogging` to see instructions and press Enter. | YES |
+| [Namecheap](https://www.namecheap.com) | `namecheap` | `NAMECHEAP_API_USER`, `NAMECHEAP_API_KEY` | YES |
+| [name.com](https://www.name.com/) | `namedotcom` | `NAMECOM_USERNAME`, `NAMECOM_API_TOKEN`, `NAMECOM_SERVER` | Not tested yet |
+| [Netcup](https://www.netcup.eu/) | `netcup` | `NETCUP_CUSTOMER_NUMBER`, `NETCUP_API_KEY`, `NETCUP_API_PASSWORD` | Not tested yet |
+| [NIFCloud](https://cloud.nifty.com/service/dns.htm) | `nifcloud` | `NIFCLOUD_ACCESS_KEY_ID`, `NIFCLOUD_SECRET_ACCESS_KEY` | Not tested yet |
+| [Ns1](https://ns1.com/) | `ns1` | `NS1_API_KEY` | Not tested yet |
+| [Open Telekom Cloud](https://cloud.telekom.de) | `otc` | `OTC_DOMAIN_NAME`, `OTC_USER_NAME`, `OTC_PASSWORD`, `OTC_PROJECT_NAME`, `OTC_IDENTITY_ENDPOINT` | Not tested yet |
+| [OVH](https://www.ovh.com) | `ovh` | `OVH_ENDPOINT`, `OVH_APPLICATION_KEY`, `OVH_APPLICATION_SECRET`, `OVH_CONSUMER_KEY` | YES |
+| [PowerDNS](https://www.powerdns.com) | `pdns` | `PDNS_API_KEY`, `PDNS_API_URL` | Not tested yet |
+| [Rackspace](https://www.rackspace.com/cloud/dns) | `rackspace` | `RACKSPACE_USER`, `RACKSPACE_API_KEY` | Not tested yet |
+| [RFC2136](https://tools.ietf.org/html/rfc2136) | `rfc2136` | `RFC2136_TSIG_KEY`, `RFC2136_TSIG_SECRET`, `RFC2136_TSIG_ALGORITHM`, `RFC2136_NAMESERVER` | Not tested yet |
+| [Route 53](https://aws.amazon.com/route53/) | `route53` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `[AWS_REGION]`, `[AWS_HOSTED_ZONE_ID]` or a configured user/instance IAM profile. | YES |
+| [Sakura Cloud](https://cloud.sakura.ad.jp/) | `sakuracloud` | `SAKURACLOUD_ACCESS_TOKEN`, `SAKURACLOUD_ACCESS_TOKEN_SECRET` | Not tested yet |
+| [Stackpath](https://www.stackpath.com/) | `stackpath` | `STACKPATH_CLIENT_ID`, `STACKPATH_CLIENT_SECRET`, `STACKPATH_STACK_ID` | Not tested yet |
+| [VegaDNS](https://github.com/shupp/VegaDNS-API) | `vegadns` | `SECRET_VEGADNS_KEY`, `SECRET_VEGADNS_SECRET`, `VEGADNS_URL` | Not tested yet |
+| [VULTR](https://www.vultr.com) | `vultr` | `VULTR_API_KEY` | Not tested yet |
### `domains`
diff --git a/vendor/github.com/Azure/go-autorest/autorest/azure/auth/auth.go b/vendor/github.com/Azure/go-autorest/autorest/azure/auth/auth.go
new file mode 100644
index 000000000..dd89d9c9d
--- /dev/null
+++ b/vendor/github.com/Azure/go-autorest/autorest/azure/auth/auth.go
@@ -0,0 +1,408 @@
+package auth
+
+// Copyright 2017 Microsoft Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import (
+ "bytes"
+ "crypto/rsa"
+ "crypto/x509"
+ "encoding/binary"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "log"
+ "os"
+ "strings"
+ "unicode/utf16"
+
+ "github.com/Azure/go-autorest/autorest"
+ "github.com/Azure/go-autorest/autorest/adal"
+ "github.com/Azure/go-autorest/autorest/azure"
+ "github.com/dimchansky/utfbom"
+ "golang.org/x/crypto/pkcs12"
+)
+
+// NewAuthorizerFromEnvironment creates an Authorizer configured from environment variables in the order:
+// 1. Client credentials
+// 2. Client certificate
+// 3. Username password
+// 4. MSI
+func NewAuthorizerFromEnvironment() (autorest.Authorizer, error) {
+ tenantID := os.Getenv("AZURE_TENANT_ID")
+ clientID := os.Getenv("AZURE_CLIENT_ID")
+ clientSecret := os.Getenv("AZURE_CLIENT_SECRET")
+ certificatePath := os.Getenv("AZURE_CERTIFICATE_PATH")
+ certificatePassword := os.Getenv("AZURE_CERTIFICATE_PASSWORD")
+ username := os.Getenv("AZURE_USERNAME")
+ password := os.Getenv("AZURE_PASSWORD")
+ envName := os.Getenv("AZURE_ENVIRONMENT")
+ resource := os.Getenv("AZURE_AD_RESOURCE")
+
+ var env azure.Environment
+ if envName == "" {
+ env = azure.PublicCloud
+ } else {
+ var err error
+ env, err = azure.EnvironmentFromName(envName)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ if resource == "" {
+ resource = env.ResourceManagerEndpoint
+ }
+
+ //1.Client Credentials
+ if clientSecret != "" {
+ config := NewClientCredentialsConfig(clientID, clientSecret, tenantID)
+ config.AADEndpoint = env.ActiveDirectoryEndpoint
+ config.Resource = resource
+ return config.Authorizer()
+ }
+
+ //2. Client Certificate
+ if certificatePath != "" {
+ config := NewClientCertificateConfig(certificatePath, certificatePassword, clientID, tenantID)
+ config.AADEndpoint = env.ActiveDirectoryEndpoint
+ config.Resource = resource
+ return config.Authorizer()
+ }
+
+ //3. Username Password
+ if username != "" && password != "" {
+ config := NewUsernamePasswordConfig(username, password, clientID, tenantID)
+ config.AADEndpoint = env.ActiveDirectoryEndpoint
+ config.Resource = resource
+ return config.Authorizer()
+ }
+
+ // 4. MSI
+ config := NewMSIConfig()
+ config.Resource = resource
+ config.ClientID = clientID
+ return config.Authorizer()
+}
+
+// NewAuthorizerFromFile creates an Authorizer configured from a configuration file.
+func NewAuthorizerFromFile(baseURI string) (autorest.Authorizer, error) {
+ fileLocation := os.Getenv("AZURE_AUTH_LOCATION")
+ if fileLocation == "" {
+ return nil, errors.New("auth file not found. Environment variable AZURE_AUTH_LOCATION is not set")
+ }
+
+ contents, err := ioutil.ReadFile(fileLocation)
+ if err != nil {
+ return nil, err
+ }
+
+ // Auth file might be encoded
+ decoded, err := decode(contents)
+ if err != nil {
+ return nil, err
+ }
+
+ file := file{}
+ err = json.Unmarshal(decoded, &file)
+ if err != nil {
+ return nil, err
+ }
+
+ resource, err := getResourceForToken(file, baseURI)
+ if err != nil {
+ return nil, err
+ }
+
+ config, err := adal.NewOAuthConfig(file.ActiveDirectoryEndpoint, file.TenantID)
+ if err != nil {
+ return nil, err
+ }
+
+ spToken, err := adal.NewServicePrincipalToken(*config, file.ClientID, file.ClientSecret, resource)
+ if err != nil {
+ return nil, err
+ }
+
+ return autorest.NewBearerAuthorizer(spToken), nil
+}
+
+// File represents the authentication file
+type file struct {
+ ClientID string `json:"clientId,omitempty"`
+ ClientSecret string `json:"clientSecret,omitempty"`
+ SubscriptionID string `json:"subscriptionId,omitempty"`
+ TenantID string `json:"tenantId,omitempty"`
+ ActiveDirectoryEndpoint string `json:"activeDirectoryEndpointUrl,omitempty"`
+ ResourceManagerEndpoint string `json:"resourceManagerEndpointUrl,omitempty"`
+ GraphResourceID string `json:"activeDirectoryGraphResourceId,omitempty"`
+ SQLManagementEndpoint string `json:"sqlManagementEndpointUrl,omitempty"`
+ GalleryEndpoint string `json:"galleryEndpointUrl,omitempty"`
+ ManagementEndpoint string `json:"managementEndpointUrl,omitempty"`
+}
+
+func decode(b []byte) ([]byte, error) {
+ reader, enc := utfbom.Skip(bytes.NewReader(b))
+
+ switch enc {
+ case utfbom.UTF16LittleEndian:
+ u16 := make([]uint16, (len(b)/2)-1)
+ err := binary.Read(reader, binary.LittleEndian, &u16)
+ if err != nil {
+ return nil, err
+ }
+ return []byte(string(utf16.Decode(u16))), nil
+ case utfbom.UTF16BigEndian:
+ u16 := make([]uint16, (len(b)/2)-1)
+ err := binary.Read(reader, binary.BigEndian, &u16)
+ if err != nil {
+ return nil, err
+ }
+ return []byte(string(utf16.Decode(u16))), nil
+ }
+ return ioutil.ReadAll(reader)
+}
+
+func getResourceForToken(f file, baseURI string) (string, error) {
+ // Compare dafault base URI from the SDK to the endpoints from the public cloud
+ // Base URI and token resource are the same string. This func finds the authentication
+ // file field that matches the SDK base URI. The SDK defines the public cloud
+ // endpoint as its default base URI
+ if !strings.HasSuffix(baseURI, "/") {
+ baseURI += "/"
+ }
+ switch baseURI {
+ case azure.PublicCloud.ServiceManagementEndpoint:
+ return f.ManagementEndpoint, nil
+ case azure.PublicCloud.ResourceManagerEndpoint:
+ return f.ResourceManagerEndpoint, nil
+ case azure.PublicCloud.ActiveDirectoryEndpoint:
+ return f.ActiveDirectoryEndpoint, nil
+ case azure.PublicCloud.GalleryEndpoint:
+ return f.GalleryEndpoint, nil
+ case azure.PublicCloud.GraphEndpoint:
+ return f.GraphResourceID, nil
+ }
+ return "", fmt.Errorf("auth: base URI not found in endpoints")
+}
+
+// NewClientCredentialsConfig creates an AuthorizerConfig object configured to obtain an Authorizer through Client Credentials.
+// Defaults to Public Cloud and Resource Manager Endpoint.
+func NewClientCredentialsConfig(clientID string, clientSecret string, tenantID string) ClientCredentialsConfig {
+ return ClientCredentialsConfig{
+ ClientID: clientID,
+ ClientSecret: clientSecret,
+ TenantID: tenantID,
+ Resource: azure.PublicCloud.ResourceManagerEndpoint,
+ AADEndpoint: azure.PublicCloud.ActiveDirectoryEndpoint,
+ }
+}
+
+// NewClientCertificateConfig creates a ClientCertificateConfig object configured to obtain an Authorizer through client certificate.
+// Defaults to Public Cloud and Resource Manager Endpoint.
+func NewClientCertificateConfig(certificatePath string, certificatePassword string, clientID string, tenantID string) ClientCertificateConfig {
+ return ClientCertificateConfig{
+ CertificatePath: certificatePath,
+ CertificatePassword: certificatePassword,
+ ClientID: clientID,
+ TenantID: tenantID,
+ Resource: azure.PublicCloud.ResourceManagerEndpoint,
+ AADEndpoint: azure.PublicCloud.ActiveDirectoryEndpoint,
+ }
+}
+
+// NewUsernamePasswordConfig creates an UsernamePasswordConfig object configured to obtain an Authorizer through username and password.
+// Defaults to Public Cloud and Resource Manager Endpoint.
+func NewUsernamePasswordConfig(username string, password string, clientID string, tenantID string) UsernamePasswordConfig {
+ return UsernamePasswordConfig{
+ Username: username,
+ Password: password,
+ ClientID: clientID,
+ TenantID: tenantID,
+ Resource: azure.PublicCloud.ResourceManagerEndpoint,
+ AADEndpoint: azure.PublicCloud.ActiveDirectoryEndpoint,
+ }
+}
+
+// NewMSIConfig creates an MSIConfig object configured to obtain an Authorizer through MSI.
+func NewMSIConfig() MSIConfig {
+ return MSIConfig{
+ Resource: azure.PublicCloud.ResourceManagerEndpoint,
+ }
+}
+
+// NewDeviceFlowConfig creates a DeviceFlowConfig object configured to obtain an Authorizer through device flow.
+// Defaults to Public Cloud and Resource Manager Endpoint.
+func NewDeviceFlowConfig(clientID string, tenantID string) DeviceFlowConfig {
+ return DeviceFlowConfig{
+ ClientID: clientID,
+ TenantID: tenantID,
+ Resource: azure.PublicCloud.ResourceManagerEndpoint,
+ AADEndpoint: azure.PublicCloud.ActiveDirectoryEndpoint,
+ }
+}
+
+//AuthorizerConfig provides an authorizer from the configuration provided.
+type AuthorizerConfig interface {
+ Authorizer() (autorest.Authorizer, error)
+}
+
+// ClientCredentialsConfig provides the options to get a bearer authorizer from client credentials.
+type ClientCredentialsConfig struct {
+ ClientID string
+ ClientSecret string
+ TenantID string
+ AADEndpoint string
+ Resource string
+}
+
+// Authorizer gets the authorizer from client credentials.
+func (ccc ClientCredentialsConfig) Authorizer() (autorest.Authorizer, error) {
+ oauthConfig, err := adal.NewOAuthConfig(ccc.AADEndpoint, ccc.TenantID)
+ if err != nil {
+ return nil, err
+ }
+
+ spToken, err := adal.NewServicePrincipalToken(*oauthConfig, ccc.ClientID, ccc.ClientSecret, ccc.Resource)
+ if err != nil {
+ return nil, fmt.Errorf("failed to get oauth token from client credentials: %v", err)
+ }
+
+ return autorest.NewBearerAuthorizer(spToken), nil
+}
+
+// ClientCertificateConfig provides the options to get a bearer authorizer from a client certificate.
+type ClientCertificateConfig struct {
+ ClientID string
+ CertificatePath string
+ CertificatePassword string
+ TenantID string
+ AADEndpoint string
+ Resource string
+}
+
+// Authorizer gets an authorizer object from client certificate.
+func (ccc ClientCertificateConfig) Authorizer() (autorest.Authorizer, error) {
+ oauthConfig, err := adal.NewOAuthConfig(ccc.AADEndpoint, ccc.TenantID)
+
+ certData, err := ioutil.ReadFile(ccc.CertificatePath)
+ if err != nil {
+ return nil, fmt.Errorf("failed to read the certificate file (%s): %v", ccc.CertificatePath, err)
+ }
+
+ certificate, rsaPrivateKey, err := decodePkcs12(certData, ccc.CertificatePassword)
+ if err != nil {
+ return nil, fmt.Errorf("failed to decode pkcs12 certificate while creating spt: %v", err)
+ }
+
+ spToken, err := adal.NewServicePrincipalTokenFromCertificate(*oauthConfig, ccc.ClientID, certificate, rsaPrivateKey, ccc.Resource)
+
+ if err != nil {
+ return nil, fmt.Errorf("failed to get oauth token from certificate auth: %v", err)
+ }
+
+ return autorest.NewBearerAuthorizer(spToken), nil
+}
+
+// DeviceFlowConfig provides the options to get a bearer authorizer using device flow authentication.
+type DeviceFlowConfig struct {
+ ClientID string
+ TenantID string
+ AADEndpoint string
+ Resource string
+}
+
+// Authorizer gets the authorizer from device flow.
+func (dfc DeviceFlowConfig) Authorizer() (autorest.Authorizer, error) {
+ oauthClient := &autorest.Client{}
+ oauthConfig, err := adal.NewOAuthConfig(dfc.AADEndpoint, dfc.TenantID)
+ deviceCode, err := adal.InitiateDeviceAuth(oauthClient, *oauthConfig, dfc.ClientID, dfc.AADEndpoint)
+ if err != nil {
+ return nil, fmt.Errorf("failed to start device auth flow: %s", err)
+ }
+
+ log.Println(*deviceCode.Message)
+
+ token, err := adal.WaitForUserCompletion(oauthClient, deviceCode)
+ if err != nil {
+ return nil, fmt.Errorf("failed to finish device auth flow: %s", err)
+ }
+
+ spToken, err := adal.NewServicePrincipalTokenFromManualToken(*oauthConfig, dfc.ClientID, dfc.Resource, *token)
+ if err != nil {
+ return nil, fmt.Errorf("failed to get oauth token from device flow: %v", err)
+ }
+
+ return autorest.NewBearerAuthorizer(spToken), nil
+}
+
+func decodePkcs12(pkcs []byte, password string) (*x509.Certificate, *rsa.PrivateKey, error) {
+ privateKey, certificate, err := pkcs12.Decode(pkcs, password)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ rsaPrivateKey, isRsaKey := privateKey.(*rsa.PrivateKey)
+ if !isRsaKey {
+ return nil, nil, fmt.Errorf("PKCS#12 certificate must contain an RSA private key")
+ }
+
+ return certificate, rsaPrivateKey, nil
+}
+
+// UsernamePasswordConfig provides the options to get a bearer authorizer from a username and a password.
+type UsernamePasswordConfig struct {
+ ClientID string
+ Username string
+ Password string
+ TenantID string
+ AADEndpoint string
+ Resource string
+}
+
+// Authorizer gets the authorizer from a username and a password.
+func (ups UsernamePasswordConfig) Authorizer() (autorest.Authorizer, error) {
+
+ oauthConfig, err := adal.NewOAuthConfig(ups.AADEndpoint, ups.TenantID)
+
+ spToken, err := adal.NewServicePrincipalTokenFromUsernamePassword(*oauthConfig, ups.ClientID, ups.Username, ups.Password, ups.Resource)
+
+ if err != nil {
+ return nil, fmt.Errorf("failed to get oauth token from username and password auth: %v", err)
+ }
+
+ return autorest.NewBearerAuthorizer(spToken), nil
+}
+
+// MSIConfig provides the options to get a bearer authorizer through MSI.
+type MSIConfig struct {
+ Resource string
+ ClientID string
+}
+
+// Authorizer gets the authorizer from MSI.
+func (mc MSIConfig) Authorizer() (autorest.Authorizer, error) {
+ msiEndpoint, err := adal.GetMSIVMEndpoint()
+ if err != nil {
+ return nil, err
+ }
+
+ spToken, err := adal.NewServicePrincipalTokenFromMSI(msiEndpoint, mc.Resource)
+ if err != nil {
+ return nil, fmt.Errorf("failed to get oauth token from MSI: %v", err)
+ }
+
+ return autorest.NewBearerAuthorizer(spToken), nil
+}
diff --git a/vendor/github.com/dimchansky/utfbom/LICENSE b/vendor/github.com/dimchansky/utfbom/LICENSE
new file mode 100644
index 000000000..8dada3eda
--- /dev/null
+++ b/vendor/github.com/dimchansky/utfbom/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright {yyyy} {name of copyright owner}
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/vendor/github.com/dimchansky/utfbom/utfbom.go b/vendor/github.com/dimchansky/utfbom/utfbom.go
new file mode 100644
index 000000000..648184a12
--- /dev/null
+++ b/vendor/github.com/dimchansky/utfbom/utfbom.go
@@ -0,0 +1,174 @@
+// Package utfbom implements the detection of the BOM (Unicode Byte Order Mark) and removing as necessary.
+// It wraps an io.Reader object, creating another object (Reader) that also implements the io.Reader
+// interface but provides automatic BOM checking and removing as necessary.
+package utfbom
+
+import (
+ "errors"
+ "io"
+)
+
+// Encoding is type alias for detected UTF encoding.
+type Encoding int
+
+// Constants to identify detected UTF encodings.
+const (
+ // Unknown encoding, returned when no BOM was detected
+ Unknown Encoding = iota
+
+ // UTF8, BOM bytes: EF BB BF
+ UTF8
+
+ // UTF-16, big-endian, BOM bytes: FE FF
+ UTF16BigEndian
+
+ // UTF-16, little-endian, BOM bytes: FF FE
+ UTF16LittleEndian
+
+ // UTF-32, big-endian, BOM bytes: 00 00 FE FF
+ UTF32BigEndian
+
+ // UTF-32, little-endian, BOM bytes: FF FE 00 00
+ UTF32LittleEndian
+)
+
+const maxConsecutiveEmptyReads = 100
+
+// Skip creates Reader which automatically detects BOM (Unicode Byte Order Mark) and removes it as necessary.
+// It also returns the encoding detected by the BOM.
+// If the detected encoding is not needed, you can call the SkipOnly function.
+func Skip(rd io.Reader) (*Reader, Encoding) {
+ // Is it already a Reader?
+ b, ok := rd.(*Reader)
+ if ok {
+ return b, Unknown
+ }
+
+ enc, left, err := detectUtf(rd)
+ return &Reader{
+ rd: rd,
+ buf: left,
+ err: err,
+ }, enc
+}
+
+// SkipOnly creates Reader which automatically detects BOM (Unicode Byte Order Mark) and removes it as necessary.
+func SkipOnly(rd io.Reader) *Reader {
+ r, _ := Skip(rd)
+ return r
+}
+
+// Reader implements automatic BOM (Unicode Byte Order Mark) checking and
+// removing as necessary for an io.Reader object.
+type Reader struct {
+ rd io.Reader // reader provided by the client
+ buf []byte // buffered data
+ err error // last error
+}
+
+// Read is an implementation of io.Reader interface.
+// The bytes are taken from the underlying Reader, but it checks for BOMs, removing them as necessary.
+func (r *Reader) Read(p []byte) (n int, err error) {
+ if len(p) == 0 {
+ return 0, nil
+ }
+
+ if r.buf == nil {
+ if r.err != nil {
+ return 0, r.readErr()
+ }
+
+ return r.rd.Read(p)
+ }
+
+ // copy as much as we can
+ n = copy(p, r.buf)
+ r.buf = nilIfEmpty(r.buf[n:])
+ return n, nil
+}
+
+func (r *Reader) readErr() error {
+ err := r.err
+ r.err = nil
+ return err
+}
+
+var errNegativeRead = errors.New("utfbom: reader returned negative count from Read")
+
+func detectUtf(rd io.Reader) (enc Encoding, buf []byte, err error) {
+ buf, err = readBOM(rd)
+
+ if len(buf) >= 4 {
+ if isUTF32BigEndianBOM4(buf) {
+ return UTF32BigEndian, nilIfEmpty(buf[4:]), err
+ }
+ if isUTF32LittleEndianBOM4(buf) {
+ return UTF32LittleEndian, nilIfEmpty(buf[4:]), err
+ }
+ }
+
+ if len(buf) > 2 && isUTF8BOM3(buf) {
+ return UTF8, nilIfEmpty(buf[3:]), err
+ }
+
+ if (err != nil && err != io.EOF) || (len(buf) < 2) {
+ return Unknown, nilIfEmpty(buf), err
+ }
+
+ if isUTF16BigEndianBOM2(buf) {
+ return UTF16BigEndian, nilIfEmpty(buf[2:]), err
+ }
+ if isUTF16LittleEndianBOM2(buf) {
+ return UTF16LittleEndian, nilIfEmpty(buf[2:]), err
+ }
+
+ return Unknown, nilIfEmpty(buf), err
+}
+
+func readBOM(rd io.Reader) (buf []byte, err error) {
+ const maxBOMSize = 4
+ var bom [maxBOMSize]byte // used to read BOM
+
+ // read as many bytes as possible
+ for nEmpty, n := 0, 0; err == nil && len(buf) < maxBOMSize; buf = bom[:len(buf)+n] {
+ if n, err = rd.Read(bom[len(buf):]); n < 0 {
+ panic(errNegativeRead)
+ }
+ if n > 0 {
+ nEmpty = 0
+ } else {
+ nEmpty++
+ if nEmpty >= maxConsecutiveEmptyReads {
+ err = io.ErrNoProgress
+ }
+ }
+ }
+ return
+}
+
+func isUTF32BigEndianBOM4(buf []byte) bool {
+ return buf[0] == 0x00 && buf[1] == 0x00 && buf[2] == 0xFE && buf[3] == 0xFF
+}
+
+func isUTF32LittleEndianBOM4(buf []byte) bool {
+ return buf[0] == 0xFF && buf[1] == 0xFE && buf[2] == 0x00 && buf[3] == 0x00
+}
+
+func isUTF8BOM3(buf []byte) bool {
+ return buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF
+}
+
+func isUTF16BigEndianBOM2(buf []byte) bool {
+ return buf[0] == 0xFE && buf[1] == 0xFF
+}
+
+func isUTF16LittleEndianBOM2(buf []byte) bool {
+ return buf[0] == 0xFF && buf[1] == 0xFE
+}
+
+func nilIfEmpty(buf []byte) (res []byte) {
+ if len(buf) > 0 {
+ res = buf
+ }
+ return
+}
diff --git a/vendor/github.com/edeckers/auroradnsclient/client.go b/vendor/github.com/edeckers/auroradnsclient/client.go
deleted file mode 100644
index d366afef3..000000000
--- a/vendor/github.com/edeckers/auroradnsclient/client.go
+++ /dev/null
@@ -1,22 +0,0 @@
-package auroradnsclient
-
-import (
- "github.com/edeckers/auroradnsclient/requests"
-)
-
-// AuroraDNSClient is a client for accessing the Aurora DNS API
-type AuroraDNSClient struct {
- requestor *requests.AuroraRequestor
-}
-
-// NewAuroraDNSClient instantiates a new client
-func NewAuroraDNSClient(endpoint string, userID string, key string) (*AuroraDNSClient, error) {
- requestor, err := requests.NewAuroraRequestor(endpoint, userID, key)
- if err != nil {
- return nil, err
- }
-
- return &AuroraDNSClient{
- requestor: requestor,
- }, nil
-}
diff --git a/vendor/github.com/edeckers/auroradnsclient/errors.go b/vendor/github.com/edeckers/auroradnsclient/errors.go
deleted file mode 100644
index 452718aa4..000000000
--- a/vendor/github.com/edeckers/auroradnsclient/errors.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package auroradnsclient
-
-// AuroraDNSError describes the format of a generic AuroraDNS API error
-type AuroraDNSError struct {
- ErrorCode string `json:"error"`
- Message string `json:"errormsg"`
-}
-
-func (e AuroraDNSError) Error() string {
- return e.Message
-}
diff --git a/vendor/github.com/edeckers/auroradnsclient/records.go b/vendor/github.com/edeckers/auroradnsclient/records.go
deleted file mode 100644
index e40786e80..000000000
--- a/vendor/github.com/edeckers/auroradnsclient/records.go
+++ /dev/null
@@ -1,75 +0,0 @@
-package auroradnsclient
-
-import (
- "encoding/json"
- "fmt"
-
- "github.com/edeckers/auroradnsclient/records"
- "github.com/sirupsen/logrus"
-)
-
-// GetRecords returns a list of all records in given zone
-func (client *AuroraDNSClient) GetRecords(zoneID string) ([]records.GetRecordsResponse, error) {
- logrus.Debugf("GetRecords(%s)", zoneID)
- relativeURL := fmt.Sprintf("zones/%s/records", zoneID)
-
- response, err := client.requestor.Request(relativeURL, "GET", []byte(""))
- if err != nil {
- logrus.Errorf("Failed to receive records: %s", err)
- return nil, err
- }
-
- var respData []records.GetRecordsResponse
- err = json.Unmarshal(response, &respData)
- if err != nil {
- logrus.Errorf("Failed to unmarshall response: %s", err)
- return nil, err
- }
-
- return respData, nil
-}
-
-// CreateRecord creates a new record in given zone
-func (client *AuroraDNSClient) CreateRecord(zoneID string, data records.CreateRecordRequest) (*records.CreateRecordResponse, error) {
- logrus.Debugf("CreateRecord(%s, %+v)", zoneID, data)
- body, err := json.Marshal(data)
- if err != nil {
- logrus.Errorf("Failed to marshall request body: %s", err)
-
- return nil, err
- }
-
- relativeURL := fmt.Sprintf("zones/%s/records", zoneID)
-
- response, err := client.requestor.Request(relativeURL, "POST", body)
- if err != nil {
- logrus.Errorf("Failed to create record: %s", err)
-
- return nil, err
- }
-
- var respData *records.CreateRecordResponse
- err = json.Unmarshal(response, &respData)
- if err != nil {
- logrus.Errorf("Failed to unmarshall response: %s", err)
-
- return nil, err
- }
-
- return respData, nil
-}
-
-// RemoveRecord removes a record corresponding to a particular id in a given zone
-func (client *AuroraDNSClient) RemoveRecord(zoneID string, recordID string) (*records.RemoveRecordResponse, error) {
- logrus.Debugf("RemoveRecord(%s, %s)", zoneID, recordID)
- relativeURL := fmt.Sprintf("zones/%s/records/%s", zoneID, recordID)
-
- _, err := client.requestor.Request(relativeURL, "DELETE", nil)
- if err != nil {
- logrus.Errorf("Failed to remove record: %s", err)
-
- return nil, err
- }
-
- return &records.RemoveRecordResponse{}, nil
-}
diff --git a/vendor/github.com/edeckers/auroradnsclient/records/datatypes.go b/vendor/github.com/edeckers/auroradnsclient/records/datatypes.go
deleted file mode 100644
index ce7efa7ec..000000000
--- a/vendor/github.com/edeckers/auroradnsclient/records/datatypes.go
+++ /dev/null
@@ -1,31 +0,0 @@
-package records
-
-// CreateRecordRequest describes the json payload for creating a record
-type CreateRecordRequest struct {
- RecordType string `json:"type"`
- Name string `json:"name"`
- Content string `json:"content"`
- TTL int `json:"ttl"`
-}
-
-// CreateRecordResponse describes the json response for creating a record
-type CreateRecordResponse struct {
- ID string `json:"id"`
- RecordType string `json:"type"`
- Name string `json:"name"`
- Content string `json:"content"`
- TTL int `json:"ttl"`
-}
-
-// GetRecordsResponse describes the json response of a single record
-type GetRecordsResponse struct {
- ID string `json:"id"`
- RecordType string `json:"type"`
- Name string `json:"name"`
- Content string `json:"content"`
- TTL int `json:"ttl"`
-}
-
-// RemoveRecordResponse describes the json response for removing a record
-type RemoveRecordResponse struct {
-}
diff --git a/vendor/github.com/edeckers/auroradnsclient/requests/errors/errors.go b/vendor/github.com/edeckers/auroradnsclient/requests/errors/errors.go
deleted file mode 100644
index e6d7a5649..000000000
--- a/vendor/github.com/edeckers/auroradnsclient/requests/errors/errors.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package errors
-
-// BadRequest HTTP error wrapper
-type BadRequest error
-
-// Unauthorized HTTP error wrapper
-type Unauthorized error
-
-// Forbidden HTTP error wrapper
-type Forbidden error
-
-// NotFound HTTP error wrapper
-type NotFound error
-
-// ServerError HTTP error wrapper
-type ServerError error
-
-// InvalidStatusCodeError is used when none of the other types applies
-type InvalidStatusCodeError error
diff --git a/vendor/github.com/edeckers/auroradnsclient/requests/requestor.go b/vendor/github.com/edeckers/auroradnsclient/requests/requestor.go
deleted file mode 100644
index b01829c9f..000000000
--- a/vendor/github.com/edeckers/auroradnsclient/requests/requestor.go
+++ /dev/null
@@ -1,124 +0,0 @@
-package requests
-
-import (
- "bytes"
- "errors"
- "fmt"
- "io/ioutil"
- "net/http"
- "net/http/httputil"
- "time"
-
- request_errors "github.com/edeckers/auroradnsclient/requests/errors"
- "github.com/edeckers/auroradnsclient/tokens"
- "github.com/sirupsen/logrus"
-)
-
-// AuroraRequestor performs actual requests to API
-type AuroraRequestor struct {
- endpoint string
- userID string
- key string
-}
-
-// NewAuroraRequestor instantiates a new requestor
-func NewAuroraRequestor(endpoint string, userID string, key string) (*AuroraRequestor, error) {
- if endpoint == "" {
- return nil, fmt.Errorf("Aurora endpoint missing")
- }
-
- if userID == "" || key == "" {
- return nil, fmt.Errorf("Aurora credentials missing")
- }
-
- return &AuroraRequestor{endpoint: endpoint, userID: userID, key: key}, nil
-}
-
-func (requestor *AuroraRequestor) buildRequest(relativeURL string, method string, body []byte) (*http.Request, error) {
- url := fmt.Sprintf("%s/%s", requestor.endpoint, relativeURL)
-
- request, err := http.NewRequest(method, url, bytes.NewReader(body))
- if err != nil {
- logrus.Errorf("Failed to build request: %s", err)
-
- return request, err
- }
-
- timestamp := time.Now().UTC()
- fmtTime := timestamp.Format("20060102T150405Z")
-
- token := tokens.NewToken(requestor.userID, requestor.key, method, fmt.Sprintf("/%s", relativeURL), timestamp)
-
- request.Header.Set("X-AuroraDNS-Date", fmtTime)
- request.Header.Set("Authorization", fmt.Sprintf("AuroraDNSv1 %s", token))
-
- request.Header.Set("Content-Type", "application/json")
-
- rawRequest, err := httputil.DumpRequestOut(request, true)
- if err != nil {
- logrus.Errorf("Failed to dump request: %s", err)
- }
-
- logrus.Debugf("Built request:\n%s", rawRequest)
-
- return request, err
-}
-
-func (requestor *AuroraRequestor) testInvalidResponse(resp *http.Response, response []byte) ([]byte, error) {
- if resp.StatusCode < 400 {
- return response, nil
- }
-
- logrus.Errorf("Received invalid status code %d:\n%s", resp.StatusCode, response)
-
- content := errors.New(string(response))
-
- statusCodeErrorMap := map[int]error{
- 400: request_errors.BadRequest(content),
- 401: request_errors.Unauthorized(content),
- 403: request_errors.Forbidden(content),
- 404: request_errors.NotFound(content),
- 500: request_errors.ServerError(content),
- }
-
- mappedError := statusCodeErrorMap[resp.StatusCode]
-
- if mappedError == nil {
- return nil, request_errors.InvalidStatusCodeError(content)
- }
-
- return nil, mappedError
-}
-
-// Request builds and executues a request to the API
-func (requestor *AuroraRequestor) Request(relativeURL string, method string, body []byte) ([]byte, error) {
- req, err := requestor.buildRequest(relativeURL, method, body)
-
- client := http.Client{Timeout: 30 * time.Second}
- resp, err := client.Do(req)
- if err != nil {
- logrus.Errorf("Failed request: %s", err)
- return nil, err
- }
-
- defer resp.Body.Close()
-
- rawResponse, err := httputil.DumpResponse(resp, true)
- logrus.Debugf("Received raw response:\n%s", rawResponse)
- if err != nil {
- logrus.Errorf("Failed to dump response: %s", err)
- }
-
- response, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- logrus.Errorf("Failed to read response: %s", response)
- return nil, err
- }
-
- response, err = requestor.testInvalidResponse(resp, response)
- if err != nil {
- return nil, err
- }
-
- return response, nil
-}
diff --git a/vendor/github.com/edeckers/auroradnsclient/tokens/generator.go b/vendor/github.com/edeckers/auroradnsclient/tokens/generator.go
deleted file mode 100644
index bb47c25b3..000000000
--- a/vendor/github.com/edeckers/auroradnsclient/tokens/generator.go
+++ /dev/null
@@ -1,35 +0,0 @@
-package tokens
-
-import (
- "crypto/hmac"
- "crypto/sha256"
- "encoding/base64"
- "fmt"
- "strings"
- "time"
-
- "github.com/sirupsen/logrus"
-)
-
-// NewToken generates a token for accessing a specific method of the API
-func NewToken(userID string, key string, method string, action string, timestamp time.Time) string {
- fmtTime := timestamp.Format("20060102T150405Z")
- logrus.Debugf("Built timestamp: %s", fmtTime)
-
- message := strings.Join([]string{method, action, fmtTime}, "")
- logrus.Debugf("Built message: %s", message)
-
- signatureHmac := hmac.New(sha256.New, []byte(key))
-
- signatureHmac.Write([]byte(message))
-
- signature := base64.StdEncoding.EncodeToString([]byte(signatureHmac.Sum(nil)))
- logrus.Debugf("Built signature: %s", signature)
-
- userIDAndSignature := fmt.Sprintf("%s:%s", userID, signature)
-
- token := base64.StdEncoding.EncodeToString([]byte(userIDAndSignature))
- logrus.Debugf("Built token: %s", token)
-
- return token
-}
diff --git a/vendor/github.com/edeckers/auroradnsclient/zones.go b/vendor/github.com/edeckers/auroradnsclient/zones.go
deleted file mode 100644
index 49f3ce5ae..000000000
--- a/vendor/github.com/edeckers/auroradnsclient/zones.go
+++ /dev/null
@@ -1,29 +0,0 @@
-package auroradnsclient
-
-import (
- "encoding/json"
-
- "github.com/edeckers/auroradnsclient/zones"
- "github.com/sirupsen/logrus"
-)
-
-// GetZones returns a list of all zones
-func (client *AuroraDNSClient) GetZones() ([]zones.ZoneRecord, error) {
- logrus.Debugf("GetZones")
- response, err := client.requestor.Request("zones", "GET", []byte(""))
-
- if err != nil {
- logrus.Errorf("Failed to get zones: %s", err)
- return nil, err
- }
-
- var respData []zones.ZoneRecord
- err = json.Unmarshal(response, &respData)
- if err != nil {
- logrus.Errorf("Failed to unmarshall response: %s", err)
- return nil, err
- }
-
- logrus.Debugf("Unmarshalled response: %+v", respData)
- return respData, nil
-}
diff --git a/vendor/github.com/edeckers/auroradnsclient/zones/datatypes.go b/vendor/github.com/edeckers/auroradnsclient/zones/datatypes.go
deleted file mode 100644
index 16841ec57..000000000
--- a/vendor/github.com/edeckers/auroradnsclient/zones/datatypes.go
+++ /dev/null
@@ -1,7 +0,0 @@
-package zones
-
-// ZoneRecord describes the json format for a zone
-type ZoneRecord struct {
- ID string `json:"id"`
- Name string `json:"name"`
-}
diff --git a/vendor/github.com/edeckers/auroradnsclient/LICENSE b/vendor/github.com/ldez/go-auroradns/LICENSE
similarity index 100%
rename from vendor/github.com/edeckers/auroradnsclient/LICENSE
rename to vendor/github.com/ldez/go-auroradns/LICENSE
diff --git a/vendor/github.com/ldez/go-auroradns/auth.go b/vendor/github.com/ldez/go-auroradns/auth.go
new file mode 100644
index 000000000..295c34c9b
--- /dev/null
+++ b/vendor/github.com/ldez/go-auroradns/auth.go
@@ -0,0 +1,98 @@
+package auroradns
+
+import (
+ "crypto/hmac"
+ "crypto/sha256"
+ "encoding/base64"
+ "fmt"
+ "net/http"
+ "strings"
+ "time"
+)
+
+// TokenTransport HTTP transport for API authentication
+type TokenTransport struct {
+ userID string
+ key string
+
+ // Transport is the underlying HTTP transport to use when making requests.
+ // It will default to http.DefaultTransport if nil.
+ Transport http.RoundTripper
+}
+
+// NewTokenTransport Creates a new TokenTransport
+func NewTokenTransport(userID, key string) (*TokenTransport, error) {
+ if userID == "" || key == "" {
+ return nil, fmt.Errorf("credentials missing")
+ }
+
+ return &TokenTransport{userID: userID, key: key}, nil
+}
+
+// RoundTrip executes a single HTTP transaction
+func (t *TokenTransport) RoundTrip(req *http.Request) (*http.Response, error) {
+ enrichedReq := &http.Request{}
+ *enrichedReq = *req
+
+ enrichedReq.Header = make(http.Header, len(req.Header))
+ for k, s := range req.Header {
+ enrichedReq.Header[k] = append([]string(nil), s...)
+ }
+
+ if t.userID != "" && t.key != "" {
+ timestamp := time.Now().UTC()
+
+ fmtTime := timestamp.Format("20060102T150405Z")
+ req.Header.Set("X-AuroraDNS-Date", fmtTime)
+
+ token, err := newToken(t.userID, t.key, req.Method, req.URL.Path, timestamp)
+ if err == nil {
+ req.Header.Set("Authorization", fmt.Sprintf("AuroraDNSv1 %s", token))
+ }
+ }
+
+ return t.transport().RoundTrip(enrichedReq)
+}
+
+// Wrap Wrap a HTTP client Transport with the TokenTransport
+func (t *TokenTransport) Wrap(client *http.Client) *http.Client {
+ backup := client.Transport
+ t.Transport = backup
+ client.Transport = t
+ return client
+}
+
+// Client Creates a new HTTP client
+func (t *TokenTransport) Client() *http.Client {
+ return &http.Client{
+ Transport: t,
+ Timeout: 30 * time.Second,
+ }
+}
+
+func (t *TokenTransport) transport() http.RoundTripper {
+ if t.Transport != nil {
+ return t.Transport
+ }
+ return http.DefaultTransport
+}
+
+// newToken generates a token for accessing a specific method of the API
+func newToken(userID string, key string, method string, action string, timestamp time.Time) (string, error) {
+ fmtTime := timestamp.Format("20060102T150405Z")
+ message := strings.Join([]string{method, action, fmtTime}, "")
+
+ signatureHmac := hmac.New(sha256.New, []byte(key))
+ _, err := signatureHmac.Write([]byte(message))
+ if err != nil {
+ return "", err
+ }
+
+ signature := base64.StdEncoding.EncodeToString(signatureHmac.Sum(nil))
+
+ userIDAndSignature := fmt.Sprintf("%s:%s", userID, signature)
+
+ token := base64.StdEncoding.EncodeToString([]byte(userIDAndSignature))
+
+ return token, nil
+}
diff --git a/vendor/github.com/ldez/go-auroradns/client.go b/vendor/github.com/ldez/go-auroradns/client.go
new file mode 100644
index 000000000..4387b1a73
--- /dev/null
+++ b/vendor/github.com/ldez/go-auroradns/client.go
@@ -0,0 +1,144 @@
+package auroradns
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+)
+
+const defaultBaseURL = "https://api.auroradns.eu"
+
+const (
+ contentTypeHeader = "Content-Type"
+ contentTypeJSON = "application/json"
+)
+
+// ErrorResponse A representation of an API error message.
+type ErrorResponse struct {
+ ErrorCode string `json:"error"`
+ Message string `json:"errormsg"`
+}
+
+func (e *ErrorResponse) Error() string {
+ return fmt.Sprintf("%s - %s", e.ErrorCode, e.Message)
+}
+
+// Option Type of a client option
+type Option func(*Client) error
+
+// Client The API client
+type Client struct {
+ baseURL *url.URL
+ UserAgent string
+ httpClient *http.Client
+}
+
+// NewClient Creates a new client
+func NewClient(httpClient *http.Client, opts ...Option) (*Client, error) {
+ if httpClient == nil {
+ httpClient = http.DefaultClient
+ }
+
+ baseURL, _ := url.Parse(defaultBaseURL)
+
+ client := &Client{
+ baseURL: baseURL,
+ httpClient: httpClient,
+ }
+
+ for _, opt := range opts {
+ err := opt(client)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ return client, nil
+}
+
+func (c *Client) newRequest(method, resource string, body io.Reader) (*http.Request, error) {
+ u, err := c.baseURL.Parse(resource)
+ if err != nil {
+ return nil, err
+ }
+
+ req, err := http.NewRequest(method, u.String(), body)
+ if err != nil {
+ return nil, err
+ }
+
+ req.Header.Set(contentTypeHeader, contentTypeJSON)
+
+ if c.UserAgent != "" {
+ req.Header.Set("User-Agent", c.UserAgent)
+ }
+
+ return req, nil
+}
+
+func (c *Client) do(req *http.Request, v interface{}) (*http.Response, error) {
+ resp, err := c.httpClient.Do(req)
+ if err != nil {
+ return nil, err
+ }
+ defer func() { _ = resp.Body.Close() }()
+
+ if err = checkResponse(resp); err != nil {
+ return resp, err
+ }
+
+ if v == nil {
+ return resp, nil
+ }
+
+ raw, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return resp, fmt.Errorf("failed to read body: %v", err)
+ }
+
+ if err = json.Unmarshal(raw, v); err != nil {
+ return resp, fmt.Errorf("unmarshaling %T error: %v: %s", err, v, string(raw))
+ }
+
+ return resp, nil
+}
+
+func checkResponse(resp *http.Response) error {
+ if c := resp.StatusCode; 200 <= c && c <= 299 {
+ return nil
+ }
+
+ data, err := ioutil.ReadAll(resp.Body)
+ if err == nil && data != nil {
+ errorResponse := new(ErrorResponse)
+ err = json.Unmarshal(data, errorResponse)
+ if err != nil {
+ return fmt.Errorf("unmarshaling ErrorResponse error: %v: %s", err.Error(), string(data))
+ }
+
+ return errorResponse
+ }
+ defer func() { _ = resp.Body.Close() }()
+
+ return nil
+}
+
+// WithBaseURL Allows to define a custom base URL
+func WithBaseURL(rawBaseURL string) func(*Client) error {
+ return func(client *Client) error {
+ if len(rawBaseURL) == 0 {
+ return nil
+ }
+
+ baseURL, err := url.Parse(rawBaseURL)
+ if err != nil {
+ return err
+ }
+
+ client.baseURL = baseURL
+ return nil
+ }
+}
diff --git a/vendor/github.com/ldez/go-auroradns/records.go b/vendor/github.com/ldez/go-auroradns/records.go
new file mode 100644
index 000000000..a1cf08717
--- /dev/null
+++ b/vendor/github.com/ldez/go-auroradns/records.go
@@ -0,0 +1,91 @@
+package auroradns
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "net/http"
+)
+
+// Record types
+const (
+ RecordTypeA = "A"
+ RecordTypeAAAA = "AAAA"
+ RecordTypeCNAME = "CNAME"
+ RecordTypeMX = "MX"
+ RecordTypeNS = "NS"
+ RecordTypeSOA = "SOA"
+ RecordTypeSRV = "SRV"
+ RecordTypeTXT = "TXT"
+ RecordTypeDS = "DS"
+ RecordTypePTR = "PTR"
+ RecordTypeSSHFP = "SSHFP"
+ RecordTypeTLSA = "TLS"
+)
+
+// Record a DNS record
+type Record struct {
+ ID string `json:"id,omitempty"`
+ RecordType string `json:"type"`
+ Name string `json:"name"`
+ Content string `json:"content"`
+ TTL int `json:"ttl,omitempty"`
+}
+
+// CreateRecord Creates a new record.
+func (c *Client) CreateRecord(zoneID string, record Record) (*Record, *http.Response, error) {
+ body, err := json.Marshal(record)
+ if err != nil {
+ return nil, nil, fmt.Errorf("failed to marshall request body: %v", err)
+ }
+
+ resource := fmt.Sprintf("/zones/%s/records", zoneID)
+
+ req, err := c.newRequest(http.MethodPost, resource, bytes.NewReader(body))
+ if err != nil {
+ return nil, nil, err
+ }
+
+ newRecord := new(Record)
+ resp, err := c.do(req, newRecord)
+ if err != nil {
+ return nil, resp, err
+ }
+
+ return newRecord, resp, nil
+}
+
+// DeleteRecord Delete a record.
+func (c *Client) DeleteRecord(zoneID string, recordID string) (bool, *http.Response, error) {
+ resource := fmt.Sprintf("/zones/%s/records/%s", zoneID, recordID)
+
+ req, err := c.newRequest(http.MethodDelete, resource, nil)
+ if err != nil {
+ return false, nil, err
+ }
+
+ resp, err := c.do(req, nil)
+ if err != nil {
+ return false, resp, err
+ }
+
+ return true, resp, nil
+}
+
+// ListRecords returns a list of all records in given zone
+func (c *Client) ListRecords(zoneID string) ([]Record, *http.Response, error) {
+ resource := fmt.Sprintf("/zones/%s/records", zoneID)
+
+ req, err := c.newRequest(http.MethodGet, resource, nil)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ var records []Record
+ resp, err := c.do(req, &records)
+ if err != nil {
+ return nil, resp, err
+ }
+
+ return records, resp, nil
+}
diff --git a/vendor/github.com/ldez/go-auroradns/zones.go b/vendor/github.com/ldez/go-auroradns/zones.go
new file mode 100644
index 000000000..8e372188f
--- /dev/null
+++ b/vendor/github.com/ldez/go-auroradns/zones.go
@@ -0,0 +1,69 @@
+package auroradns
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "net/http"
+)
+
+// Zone a DNS zone
+type Zone struct {
+ ID string `json:"id,omitempty"`
+ Name string `json:"name"`
+}
+
+// CreateZone Creates a zone.
+func (c *Client) CreateZone(domain string) (*Zone, *http.Response, error) {
+ body, err := json.Marshal(Zone{Name: domain})
+ if err != nil {
+ return nil, nil, fmt.Errorf("failed to marshall request body: %v", err)
+ }
+
+ req, err := c.newRequest(http.MethodPost, "/zones", bytes.NewReader(body))
+ if err != nil {
+ return nil, nil, err
+ }
+
+ zone := new(Zone)
+ resp, err := c.do(req, zone)
+ if err != nil {
+ return nil, resp, err
+ }
+
+ return zone, resp, nil
+}
+
+// DeleteZone Delete a zone.
+func (c *Client) DeleteZone(zoneID string) (bool, *http.Response, error) {
+ resource := fmt.Sprintf("/zones/%s", zoneID)
+
+ req, err := c.newRequest(http.MethodDelete, resource, nil)
+ if err != nil {
+ return false, nil, err
+ }
+
+ resp, err := c.do(req, nil)
+ if err != nil {
+ return false, resp, err
+ }
+
+ return true, resp, nil
+
+}
+
+// ListZones returns a list of all zones.
+func (c *Client) ListZones() ([]Zone, *http.Response, error) {
+ req, err := c.newRequest(http.MethodGet, "/zones", nil)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ var zones []Zone
+ resp, err := c.do(req, &zones)
+ if err != nil {
+ return nil, resp, err
+ }
+
+ return zones, resp, nil
+}
diff --git a/vendor/github.com/xenolf/lego/acme/dns_challenge.go b/vendor/github.com/xenolf/lego/acme/dns_challenge.go
index f803d0a8c..d9c252e7e 100644
--- a/vendor/github.com/xenolf/lego/acme/dns_challenge.go
+++ b/vendor/github.com/xenolf/lego/acme/dns_challenge.go
@@ -7,6 +7,7 @@ import (
"fmt"
"net"
"strings"
+ "sync"
"time"
"github.com/miekg/dns"
@@ -18,8 +19,9 @@ type preCheckDNSFunc func(fqdn, value string) (bool, error)
var (
// PreCheckDNS checks DNS propagation before notifying ACME that
// the DNS challenge is ready.
- PreCheckDNS preCheckDNSFunc = checkDNSPropagation
- fqdnToZone = map[string]string{}
+ PreCheckDNS preCheckDNSFunc = checkDNSPropagation
+ fqdnToZone = map[string]string{}
+ muFqdnToZone sync.Mutex
)
const defaultResolvConf = "/etc/resolv.conf"
@@ -262,6 +264,9 @@ func lookupNameservers(fqdn string) ([]string, error) {
// FindZoneByFqdn determines the zone apex for the given fqdn by recursing up the
// domain labels until the nameserver returns a SOA record in the answer section.
func FindZoneByFqdn(fqdn string, nameservers []string) (string, error) {
+ muFqdnToZone.Lock()
+ defer muFqdnToZone.Unlock()
+
// Do we have it cached?
if zone, ok := fqdnToZone[fqdn]; ok {
return zone, nil
diff --git a/vendor/github.com/xenolf/lego/providers/dns/auroradns/auroradns.go b/vendor/github.com/xenolf/lego/providers/dns/auroradns/auroradns.go
index d3cb76da9..21e455c90 100644
--- a/vendor/github.com/xenolf/lego/providers/dns/auroradns/auroradns.go
+++ b/vendor/github.com/xenolf/lego/providers/dns/auroradns/auroradns.go
@@ -1,3 +1,4 @@
+// Package auroradns implements a DNS provider for solving the DNS-01 challenge using Aurora DNS.
package auroradns
import (
@@ -6,9 +7,7 @@ import (
"sync"
"time"
- "github.com/edeckers/auroradnsclient"
- "github.com/edeckers/auroradnsclient/records"
- "github.com/edeckers/auroradnsclient/zones"
+ "github.com/ldez/go-auroradns"
"github.com/xenolf/lego/acme"
"github.com/xenolf/lego/platform/config/env"
)
@@ -39,7 +38,7 @@ type DNSProvider struct {
recordIDs map[string]string
recordIDsMu sync.Mutex
config *Config
- client *auroradnsclient.AuroraDNSClient
+ client *auroradns.Client
}
// NewDNSProvider returns a DNSProvider instance configured for AuroraDNS.
@@ -85,7 +84,12 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
config.BaseURL = defaultBaseURL
}
- client, err := auroradnsclient.NewAuroraDNSClient(config.BaseURL, config.UserID, config.Key)
+ tr, err := auroradns.NewTokenTransport(config.UserID, config.Key)
+ if err != nil {
+ return nil, fmt.Errorf("aurora: %v", err)
+ }
+
+ client, err := auroradns.NewClient(tr.Client(), auroradns.WithBaseURL(config.BaseURL))
if err != nil {
return nil, fmt.Errorf("aurora: %v", err)
}
@@ -117,26 +121,25 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
authZone = acme.UnFqdn(authZone)
- zoneRecord, err := d.getZoneInformationByName(authZone)
+ zone, err := d.getZoneInformationByName(authZone)
if err != nil {
return fmt.Errorf("aurora: could not create record: %v", err)
}
- reqData :=
- records.CreateRecordRequest{
- RecordType: "TXT",
- Name: subdomain,
- Content: value,
- TTL: d.config.TTL,
- }
+ record := auroradns.Record{
+ RecordType: "TXT",
+ Name: subdomain,
+ Content: value,
+ TTL: d.config.TTL,
+ }
- respData, err := d.client.CreateRecord(zoneRecord.ID, reqData)
+ newRecord, _, err := d.client.CreateRecord(zone.ID, record)
if err != nil {
return fmt.Errorf("aurora: could not create record: %v", err)
}
d.recordIDsMu.Lock()
- d.recordIDs[fqdn] = respData.ID
+ d.recordIDs[fqdn] = newRecord.ID
d.recordIDsMu.Unlock()
return nil
@@ -161,12 +164,12 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
authZone = acme.UnFqdn(authZone)
- zoneRecord, err := d.getZoneInformationByName(authZone)
+ zone, err := d.getZoneInformationByName(authZone)
if err != nil {
return err
}
- _, err = d.client.RemoveRecord(zoneRecord.ID, recordID)
+ _, _, err = d.client.DeleteRecord(zone.ID, recordID)
if err != nil {
return err
}
@@ -184,10 +187,10 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
return d.config.PropagationTimeout, d.config.PollingInterval
}
-func (d *DNSProvider) getZoneInformationByName(name string) (zones.ZoneRecord, error) {
- zs, err := d.client.GetZones()
+func (d *DNSProvider) getZoneInformationByName(name string) (auroradns.Zone, error) {
+ zs, _, err := d.client.ListZones()
if err != nil {
- return zones.ZoneRecord{}, err
+ return auroradns.Zone{}, err
}
for _, element := range zs {
@@ -196,5 +199,5 @@ func (d *DNSProvider) getZoneInformationByName(name string) (zones.ZoneRecord, e
}
}
- return zones.ZoneRecord{}, fmt.Errorf("could not find Zone record")
+ return auroradns.Zone{}, fmt.Errorf("could not find Zone record")
}
diff --git a/vendor/github.com/xenolf/lego/providers/dns/azure/azure.go b/vendor/github.com/xenolf/lego/providers/dns/azure/azure.go
index d04c3d668..74a4e531f 100644
--- a/vendor/github.com/xenolf/lego/providers/dns/azure/azure.go
+++ b/vendor/github.com/xenolf/lego/providers/dns/azure/azure.go
@@ -7,6 +7,7 @@ import (
"context"
"errors"
"fmt"
+ "io/ioutil"
"net/http"
"strings"
"time"
@@ -15,18 +16,26 @@ import (
"github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/adal"
"github.com/Azure/go-autorest/autorest/azure"
+ "github.com/Azure/go-autorest/autorest/azure/auth"
"github.com/Azure/go-autorest/autorest/to"
"github.com/xenolf/lego/acme"
"github.com/xenolf/lego/platform/config/env"
)
+const defaultMetadataEndpoint = "http://169.254.169.254"
+
// Config is used to configure the creation of the DNSProvider
type Config struct {
- ClientID string
- ClientSecret string
- SubscriptionID string
- TenantID string
- ResourceGroup string
+ // optional if using instance metadata service
+ ClientID string
+ ClientSecret string
+ TenantID string
+
+ SubscriptionID string
+ ResourceGroup string
+
+ MetadataEndpoint string
+
PropagationTimeout time.Duration
PollingInterval time.Duration
TTL int
@@ -39,29 +48,26 @@ func NewDefaultConfig() *Config {
TTL: env.GetOrDefaultInt("AZURE_TTL", 60),
PropagationTimeout: env.GetOrDefaultSecond("AZURE_PROPAGATION_TIMEOUT", 2*time.Minute),
PollingInterval: env.GetOrDefaultSecond("AZURE_POLLING_INTERVAL", 2*time.Second),
+ MetadataEndpoint: env.GetOrFile("AZURE_METADATA_ENDPOINT"),
}
}
// DNSProvider is an implementation of the acme.ChallengeProvider interface
type DNSProvider struct {
- config *Config
+ config *Config
+ authorizer autorest.Authorizer
}
// NewDNSProvider returns a DNSProvider instance configured for azure.
-// Credentials must be passed in the environment variables: AZURE_CLIENT_ID,
-// AZURE_CLIENT_SECRET, AZURE_SUBSCRIPTION_ID, AZURE_TENANT_ID, AZURE_RESOURCE_GROUP
+// Credentials can be passed in the environment variables:
+// AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_SUBSCRIPTION_ID, AZURE_TENANT_ID, AZURE_RESOURCE_GROUP
+// If the credentials are _not_ set via the environment,
+// then it will attempt to get a bearer token via the instance metadata service.
+// see: https://github.com/Azure/go-autorest/blob/v10.14.0/autorest/azure/auth/auth.go#L38-L42
func NewDNSProvider() (*DNSProvider, error) {
- values, err := env.Get("AZURE_CLIENT_ID", "AZURE_CLIENT_SECRET", "AZURE_SUBSCRIPTION_ID", "AZURE_TENANT_ID", "AZURE_RESOURCE_GROUP")
- if err != nil {
- return nil, fmt.Errorf("azure: %v", err)
- }
-
config := NewDefaultConfig()
- config.ClientID = values["AZURE_CLIENT_ID"]
- config.ClientSecret = values["AZURE_CLIENT_SECRET"]
- config.SubscriptionID = values["AZURE_SUBSCRIPTION_ID"]
- config.TenantID = values["AZURE_TENANT_ID"]
- config.ResourceGroup = values["AZURE_RESOURCE_GROUP"]
+ config.SubscriptionID = env.GetOrFile("AZURE_SUBSCRIPTION_ID")
+ config.ResourceGroup = env.GetOrFile("AZURE_RESOURCE_GROUP")
return NewDNSProviderConfig(config)
}
@@ -73,8 +79,8 @@ func NewDNSProviderCredentials(clientID, clientSecret, subscriptionID, tenantID,
config := NewDefaultConfig()
config.ClientID = clientID
config.ClientSecret = clientSecret
- config.SubscriptionID = subscriptionID
config.TenantID = tenantID
+ config.SubscriptionID = subscriptionID
config.ResourceGroup = resourceGroup
return NewDNSProviderConfig(config)
@@ -86,11 +92,40 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
return nil, errors.New("azure: the configuration of the DNS provider is nil")
}
- if config.ClientID == "" || config.ClientSecret == "" || config.SubscriptionID == "" || config.TenantID == "" || config.ResourceGroup == "" {
- return nil, errors.New("azure: some credentials information are missing")
+ if config.HTTPClient == nil {
+ config.HTTPClient = http.DefaultClient
}
- return &DNSProvider{config: config}, nil
+ authorizer, err := getAuthorizer(config)
+ if err != nil {
+ return nil, err
+ }
+
+ if config.SubscriptionID == "" {
+ subsID, err := getMetadata(config, "subscriptionId")
+ if err != nil {
+ return nil, fmt.Errorf("azure: %v", err)
+ }
+
+ if subsID == "" {
+ return nil, errors.New("azure: SubscriptionID is missing")
+ }
+ config.SubscriptionID = subsID
+ }
+
+ if config.ResourceGroup == "" {
+ resGroup, err := getMetadata(config, "resourceGroupName")
+ if err != nil {
+ return nil, fmt.Errorf("azure: %v", err)
+ }
+
+ if resGroup == "" {
+ return nil, errors.New("azure: ResourceGroup is missing")
+ }
+ config.ResourceGroup = resGroup
+ }
+
+ return &DNSProvider{config: config, authorizer: authorizer}, nil
}
// Timeout returns the timeout and interval to use when checking for DNS
@@ -110,12 +145,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
}
rsc := dns.NewRecordSetsClient(d.config.SubscriptionID)
- spt, err := d.newServicePrincipalToken(azure.PublicCloud.ResourceManagerEndpoint)
- if err != nil {
- return fmt.Errorf("azure: %v", err)
- }
-
- rsc.Authorizer = autorest.NewBearerAuthorizer(spt)
+ rsc.Authorizer = d.authorizer
relative := toRelativeRecord(fqdn, acme.ToFqdn(zone))
rec := dns.RecordSet{
@@ -145,12 +175,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
relative := toRelativeRecord(fqdn, acme.ToFqdn(zone))
rsc := dns.NewRecordSetsClient(d.config.SubscriptionID)
- spt, err := d.newServicePrincipalToken(azure.PublicCloud.ResourceManagerEndpoint)
- if err != nil {
- return fmt.Errorf("azure: %v", err)
- }
-
- rsc.Authorizer = autorest.NewBearerAuthorizer(spt)
+ rsc.Authorizer = d.authorizer
_, err = rsc.Delete(ctx, d.config.ResourceGroup, zone, relative, dns.TXT, "")
if err != nil {
@@ -166,14 +191,8 @@ func (d *DNSProvider) getHostedZoneID(ctx context.Context, fqdn string) (string,
return "", err
}
- // Now we want to to Azure and get the zone.
- spt, err := d.newServicePrincipalToken(azure.PublicCloud.ResourceManagerEndpoint)
- if err != nil {
- return "", err
- }
-
dc := dns.NewZonesClient(d.config.SubscriptionID)
- dc.Authorizer = autorest.NewBearerAuthorizer(spt)
+ dc.Authorizer = d.authorizer
zone, err := dc.Get(ctx, d.config.ResourceGroup, acme.UnFqdn(authZone))
if err != nil {
@@ -184,17 +203,61 @@ func (d *DNSProvider) getHostedZoneID(ctx context.Context, fqdn string) (string,
return to.String(zone.Name), nil
}
-// NewServicePrincipalTokenFromCredentials creates a new ServicePrincipalToken using values of the
-// passed credentials map.
-func (d *DNSProvider) newServicePrincipalToken(scope string) (*adal.ServicePrincipalToken, error) {
- oauthConfig, err := adal.NewOAuthConfig(azure.PublicCloud.ActiveDirectoryEndpoint, d.config.TenantID)
- if err != nil {
- return nil, err
- }
- return adal.NewServicePrincipalToken(*oauthConfig, d.config.ClientID, d.config.ClientSecret, scope)
-}
-
// Returns the relative record to the domain
func toRelativeRecord(domain, zone string) string {
return acme.UnFqdn(strings.TrimSuffix(domain, zone))
}
+
+func getAuthorizer(config *Config) (autorest.Authorizer, error) {
+ if config.ClientID != "" && config.ClientSecret != "" && config.TenantID != "" {
+ oauthConfig, err := adal.NewOAuthConfig(azure.PublicCloud.ActiveDirectoryEndpoint, config.TenantID)
+ if err != nil {
+ return nil, err
+ }
+
+ spt, err := adal.NewServicePrincipalToken(*oauthConfig, config.ClientID, config.ClientSecret, azure.PublicCloud.ResourceManagerEndpoint)
+ if err != nil {
+ return nil, err
+ }
+
+ spt.SetSender(config.HTTPClient)
+ return autorest.NewBearerAuthorizer(spt), nil
+ }
+
+ return auth.NewAuthorizerFromEnvironment()
+}
+
+// Fetches metadata from environment or he instance metadata service
+// borrowed from https://github.com/Microsoft/azureimds/blob/master/imdssample.go
+func getMetadata(config *Config, field string) (string, error) {
+ metadataEndpoint := config.MetadataEndpoint
+ if len(metadataEndpoint) == 0 {
+ metadataEndpoint = defaultMetadataEndpoint
+ }
+
+ resource := fmt.Sprintf("%s/metadata/instance/compute/%s", metadataEndpoint, field)
+ req, err := http.NewRequest(http.MethodGet, resource, nil)
+ if err != nil {
+ return "", err
+ }
+
+ req.Header.Add("Metadata", "True")
+
+ q := req.URL.Query()
+ q.Add("format", "text")
+ q.Add("api-version", "2017-12-01")
+ req.URL.RawQuery = q.Encode()
+
+ resp, err := config.HTTPClient.Do(req)
+ if err != nil {
+ return "", err
+ }
+ defer resp.Body.Close()
+
+ respBody, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return "", err
+ }
+
+ return string(respBody[:]), nil
+}
diff --git a/vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/client.go b/vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/client.go
index ba0727b9e..95f2dda05 100644
--- a/vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/client.go
+++ b/vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/client.go
@@ -7,6 +7,7 @@ import (
"encoding/hex"
"encoding/json"
"fmt"
+ "io/ioutil"
"net/http"
"time"
)
@@ -27,6 +28,10 @@ type Record struct {
SourceID int `json:"sourceId"`
}
+type recordsResponse struct {
+ Records *[]Record `json:"data"`
+}
+
// Client DNSMadeEasy client
type Client struct {
apiKey string
@@ -82,10 +87,6 @@ func (c *Client) GetRecords(domain *Domain, recordName, recordType string) (*[]R
}
defer resp.Body.Close()
- type recordsResponse struct {
- Records *[]Record `json:"data"`
- }
-
records := &recordsResponse{}
err = json.NewDecoder(resp.Body).Decode(&records)
if err != nil {
@@ -151,7 +152,11 @@ func (c *Client) sendRequest(method, resource string, payload interface{}) (*htt
}
if resp.StatusCode > 299 {
- return nil, fmt.Errorf("DNSMadeEasy API request failed with HTTP status code %d", resp.StatusCode)
+ body, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return nil, fmt.Errorf("request failed with HTTP status code %d", resp.StatusCode)
+ }
+ return nil, fmt.Errorf("request failed with HTTP status code %d: %s", resp.StatusCode, string(body))
}
return resp, nil
diff --git a/vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/dnsmadeeasy.go b/vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/dnsmadeeasy.go
index 519a44519..a55ea3978 100644
--- a/vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/dnsmadeeasy.go
+++ b/vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/dnsmadeeasy.go
@@ -1,3 +1,4 @@
+// Package dnsmadeeasy implements a DNS provider for solving the DNS-01 challenge using DNS Made Easy.
package dnsmadeeasy
import (
@@ -112,13 +113,13 @@ func (d *DNSProvider) Present(domainName, token, keyAuth string) error {
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
if err != nil {
- return err
+ return fmt.Errorf("dnsmadeeasy: unable to find zone for %s: %v", fqdn, err)
}
// fetch the domain details
domain, err := d.client.GetDomain(authZone)
if err != nil {
- return err
+ return fmt.Errorf("dnsmadeeasy: unable to get domain for zone %s: %v", authZone, err)
}
// create the TXT record
@@ -126,7 +127,10 @@ func (d *DNSProvider) Present(domainName, token, keyAuth string) error {
record := &Record{Type: "TXT", Name: name, Value: value, TTL: d.config.TTL}
err = d.client.CreateRecord(domain, record)
- return err
+ if err != nil {
+ return fmt.Errorf("dnsmadeeasy: unable to create record for %s: %v", name, err)
+ }
+ return nil
}
// CleanUp removes the TXT records matching the specified parameters
@@ -135,31 +139,32 @@ func (d *DNSProvider) CleanUp(domainName, token, keyAuth string) error {
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
if err != nil {
- return err
+ return fmt.Errorf("dnsmadeeasy: unable to find zone for %s: %v", fqdn, err)
}
// fetch the domain details
domain, err := d.client.GetDomain(authZone)
if err != nil {
- return err
+ return fmt.Errorf("dnsmadeeasy: unable to get domain for zone %s: %v", authZone, err)
}
// find matching records
name := strings.Replace(fqdn, "."+authZone, "", 1)
records, err := d.client.GetRecords(domain, name, "TXT")
if err != nil {
- return err
+ return fmt.Errorf("dnsmadeeasy: unable to get records for domain %s: %v", domain.Name, err)
}
// delete records
+ var lastError error
for _, record := range *records {
err = d.client.DeleteRecord(record)
if err != nil {
- return err
+ lastError = fmt.Errorf("dnsmadeeasy: unable to delete record [id=%d, name=%s]: %v", record.ID, record.Name, err)
}
}
- return nil
+ return lastError
}
// Timeout returns the timeout and interval to use when checking for DNS propagation.
diff --git a/vendor/github.com/xenolf/lego/providers/dns/dreamhost/dreamhost.go b/vendor/github.com/xenolf/lego/providers/dns/dreamhost/dreamhost.go
index cc0e36338..9edd27b72 100644
--- a/vendor/github.com/xenolf/lego/providers/dns/dreamhost/dreamhost.go
+++ b/vendor/github.com/xenolf/lego/providers/dns/dreamhost/dreamhost.go
@@ -1,4 +1,4 @@
-// Package dreamhost Adds lego support for http://dreamhost.com DNS updates
+// Package dreamhost implements a DNS provider for solving the DNS-01 challenge using DreamHost.
// See https://help.dreamhost.com/hc/en-us/articles/217560167-API_overview
// and https://help.dreamhost.com/hc/en-us/articles/217555707-DNS-API-commands for the API spec.
package dreamhost
diff --git a/vendor/github.com/xenolf/lego/providers/dns/duckdns/duckdns.go b/vendor/github.com/xenolf/lego/providers/dns/duckdns/duckdns.go
index 6bbf76352..7581af173 100644
--- a/vendor/github.com/xenolf/lego/providers/dns/duckdns/duckdns.go
+++ b/vendor/github.com/xenolf/lego/providers/dns/duckdns/duckdns.go
@@ -1,4 +1,4 @@
-// Package duckdns Adds lego support for http://duckdns.org.
+// Package duckdns implements a DNS provider for solving the DNS-01 challenge using DuckDNS.
// See http://www.duckdns.org/spec.jsp for more info on updating TXT records.
package duckdns
@@ -7,8 +7,12 @@ import (
"fmt"
"io/ioutil"
"net/http"
+ "net/url"
+ "strconv"
+ "strings"
"time"
+ "github.com/miekg/dns"
"github.com/xenolf/lego/acme"
"github.com/xenolf/lego/platform/config/env"
)
@@ -96,9 +100,16 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
// To update the TXT record we just need to make one simple get request.
// In DuckDNS you only have one TXT record shared with the domain and all sub domains.
func updateTxtRecord(domain, token, txt string, clear bool) error {
- u := fmt.Sprintf("https://www.duckdns.org/update?domains=%s&token=%s&clear=%t&txt=%s", domain, token, clear, txt)
+ u, _ := url.Parse("https://www.duckdns.org/update")
- response, err := acme.HTTPClient.Get(u)
+ query := u.Query()
+ query.Set("domains", getMainDomain(domain))
+ query.Set("token", token)
+ query.Set("clear", strconv.FormatBool(clear))
+ query.Set("txt", txt)
+ u.RawQuery = query.Encode()
+
+ response, err := acme.HTTPClient.Get(u.String())
if err != nil {
return err
}
@@ -115,3 +126,23 @@ func updateTxtRecord(domain, token, txt string, clear bool) error {
}
return nil
}
+
+// DuckDNS only lets you write to your subdomain
+// so it must be in format subdomain.duckdns.org
+// not in format subsubdomain.subdomain.duckdns.org
+// so strip off everything that is not top 3 levels
+func getMainDomain(domain string) string {
+ domain = acme.UnFqdn(domain)
+
+ split := dns.Split(domain)
+ if strings.HasSuffix(strings.ToLower(domain), "duckdns.org") {
+ if len(split) < 3 {
+ return ""
+ }
+
+ firstSubDomainIndex := split[len(split)-3]
+ return domain[firstSubDomainIndex:]
+ }
+
+ return domain[split[len(split)-1]:]
+}
diff --git a/vendor/github.com/xenolf/lego/providers/dns/netcup/client.go b/vendor/github.com/xenolf/lego/providers/dns/netcup/client.go
index f30bd7f12..17a2ae968 100644
--- a/vendor/github.com/xenolf/lego/providers/dns/netcup/client.go
+++ b/vendor/github.com/xenolf/lego/providers/dns/netcup/client.go
@@ -25,27 +25,27 @@ type Request struct {
Param interface{} `json:"param"`
}
-// LoginMsg as specified in netcup WSDL
+// LoginRequest as specified in netcup WSDL
// https://ccp.netcup.net/run/webservice/servers/endpoint.php#login
-type LoginMsg struct {
+type LoginRequest struct {
CustomerNumber string `json:"customernumber"`
APIKey string `json:"apikey"`
APIPassword string `json:"apipassword"`
ClientRequestID string `json:"clientrequestid,omitempty"`
}
-// LogoutMsg as specified in netcup WSDL
+// LogoutRequest as specified in netcup WSDL
// https://ccp.netcup.net/run/webservice/servers/endpoint.php#logout
-type LogoutMsg struct {
+type LogoutRequest struct {
CustomerNumber string `json:"customernumber"`
APIKey string `json:"apikey"`
APISessionID string `json:"apisessionid"`
ClientRequestID string `json:"clientrequestid,omitempty"`
}
-// UpdateDNSRecordsMsg as specified in netcup WSDL
+// UpdateDNSRecordsRequest as specified in netcup WSDL
// https://ccp.netcup.net/run/webservice/servers/endpoint.php#updateDnsRecords
-type UpdateDNSRecordsMsg struct {
+type UpdateDNSRecordsRequest struct {
DomainName string `json:"domainname"`
CustomerNumber string `json:"customernumber"`
APIKey string `json:"apikey"`
@@ -55,15 +55,15 @@ type UpdateDNSRecordsMsg struct {
}
// DNSRecordSet as specified in netcup WSDL
-// needed in UpdateDNSRecordsMsg
+// needed in UpdateDNSRecordsRequest
// https://ccp.netcup.net/run/webservice/servers/endpoint.php#Dnsrecordset
type DNSRecordSet struct {
DNSRecords []DNSRecord `json:"dnsrecords"`
}
-// InfoDNSRecordsMsg as specified in netcup WSDL
+// InfoDNSRecordsRequest as specified in netcup WSDL
// https://ccp.netcup.net/run/webservice/servers/endpoint.php#infoDnsRecords
-type InfoDNSRecordsMsg struct {
+type InfoDNSRecordsRequest struct {
DomainName string `json:"domainname"`
CustomerNumber string `json:"customernumber"`
APIKey string `json:"apikey"`
@@ -87,33 +87,30 @@ type DNSRecord struct {
// ResponseMsg as specified in netcup WSDL
// https://ccp.netcup.net/run/webservice/servers/endpoint.php#Responsemessage
type ResponseMsg struct {
- ServerRequestID string `json:"serverrequestid"`
- ClientRequestID string `json:"clientrequestid,omitempty"`
- Action string `json:"action"`
- Status string `json:"status"`
- StatusCode int `json:"statuscode"`
- ShortMessage string `json:"shortmessage"`
- LongMessage string `json:"longmessage"`
- ResponseData ResponseData `json:"responsedata,omitempty"`
+ ServerRequestID string `json:"serverrequestid"`
+ ClientRequestID string `json:"clientrequestid,omitempty"`
+ Action string `json:"action"`
+ Status string `json:"status"`
+ StatusCode int `json:"statuscode"`
+ ShortMessage string `json:"shortmessage"`
+ LongMessage string `json:"longmessage"`
+ ResponseData json.RawMessage `json:"responsedata,omitempty"`
}
-// LogoutResponseMsg similar to ResponseMsg
-// allows empty ResponseData field whilst unmarshaling
-type LogoutResponseMsg struct {
- ServerRequestID string `json:"serverrequestid"`
- ClientRequestID string `json:"clientrequestid,omitempty"`
- Action string `json:"action"`
- Status string `json:"status"`
- StatusCode int `json:"statuscode"`
- ShortMessage string `json:"shortmessage"`
- LongMessage string `json:"longmessage"`
- ResponseData string `json:"responsedata,omitempty"`
+func (r *ResponseMsg) Error() string {
+ return fmt.Sprintf("an error occurred during the action %s: [Status=%s, StatusCode=%d, ShortMessage=%s, LongMessage=%s]",
+ r.Action, r.Status, r.StatusCode, r.ShortMessage, r.LongMessage)
}
-// ResponseData to enable correct unmarshaling of ResponseMsg
-type ResponseData struct {
+// LoginResponse response to login action.
+type LoginResponse struct {
+ APISessionID string `json:"apisessionid"`
+}
+
+// InfoDNSRecordsResponse response to infoDnsRecords action.
+type InfoDNSRecordsResponse struct {
APISessionID string `json:"apisessionid"`
- DNSRecords []DNSRecord `json:"dnsrecords"`
+ DNSRecords []DNSRecord `json:"dnsrecords,omitempty"`
}
// Client netcup DNS client
@@ -126,7 +123,11 @@ type Client struct {
}
// NewClient creates a netcup DNS client
-func NewClient(customerNumber string, apiKey string, apiPassword string) *Client {
+func NewClient(customerNumber string, apiKey string, apiPassword string) (*Client, error) {
+ if customerNumber == "" || apiKey == "" || apiPassword == "" {
+ return nil, fmt.Errorf("credentials missing")
+ }
+
return &Client{
customerNumber: customerNumber,
apiKey: apiKey,
@@ -135,7 +136,7 @@ func NewClient(customerNumber string, apiKey string, apiPassword string) *Client
HTTPClient: &http.Client{
Timeout: 10 * time.Second,
},
- }
+ }, nil
}
// Login performs the login as specified by the netcup WSDL
@@ -144,7 +145,7 @@ func NewClient(customerNumber string, apiKey string, apiPassword string) *Client
func (c *Client) Login() (string, error) {
payload := &Request{
Action: "login",
- Param: &LoginMsg{
+ Param: &LoginRequest{
CustomerNumber: c.customerNumber,
APIKey: c.apiKey,
APIPassword: c.apiPassword,
@@ -152,21 +153,13 @@ func (c *Client) Login() (string, error) {
},
}
- response, err := c.sendRequest(payload)
+ var responseData LoginResponse
+ err := c.doRequest(payload, &responseData)
if err != nil {
- return "", fmt.Errorf("error sending request to DNS-API, %v", err)
+ return "", fmt.Errorf("loging error: %v", err)
}
- var r ResponseMsg
-
- err = json.Unmarshal(response, &r)
- if err != nil {
- return "", fmt.Errorf("error decoding response of DNS-API, %v", err)
- }
- if r.Status != success {
- return "", fmt.Errorf("error logging into DNS-API, %v", r.LongMessage)
- }
- return r.ResponseData.APISessionID, nil
+ return responseData.APISessionID, nil
}
// Logout performs the logout with the supplied sessionID as specified by the netcup WSDL
@@ -174,7 +167,7 @@ func (c *Client) Login() (string, error) {
func (c *Client) Logout(sessionID string) error {
payload := &Request{
Action: "logout",
- Param: &LogoutMsg{
+ Param: &LogoutRequest{
CustomerNumber: c.customerNumber,
APIKey: c.apiKey,
APISessionID: sessionID,
@@ -182,54 +175,34 @@ func (c *Client) Logout(sessionID string) error {
},
}
- response, err := c.sendRequest(payload)
+ err := c.doRequest(payload, nil)
if err != nil {
- return fmt.Errorf("error logging out of DNS-API: %v", err)
+ return fmt.Errorf("logout error: %v", err)
}
- var r LogoutResponseMsg
-
- err = json.Unmarshal(response, &r)
- if err != nil {
- return fmt.Errorf("error logging out of DNS-API: %v", err)
- }
-
- if r.Status != success {
- return fmt.Errorf("error logging out of DNS-API: %v", r.ShortMessage)
- }
return nil
}
// UpdateDNSRecord performs an update of the DNSRecords as specified by the netcup WSDL
// https://ccp.netcup.net/run/webservice/servers/endpoint.php
-func (c *Client) UpdateDNSRecord(sessionID, domainName string, record DNSRecord) error {
+func (c *Client) UpdateDNSRecord(sessionID, domainName string, records []DNSRecord) error {
payload := &Request{
Action: "updateDnsRecords",
- Param: UpdateDNSRecordsMsg{
+ Param: UpdateDNSRecordsRequest{
DomainName: domainName,
CustomerNumber: c.customerNumber,
APIKey: c.apiKey,
APISessionID: sessionID,
ClientRequestID: "",
- DNSRecordSet: DNSRecordSet{DNSRecords: []DNSRecord{record}},
+ DNSRecordSet: DNSRecordSet{DNSRecords: records},
},
}
- response, err := c.sendRequest(payload)
+ err := c.doRequest(payload, nil)
if err != nil {
- return err
+ return fmt.Errorf("error when sending the request: %v", err)
}
- var r ResponseMsg
-
- err = json.Unmarshal(response, &r)
- if err != nil {
- return err
- }
-
- if r.Status != success {
- return fmt.Errorf("%s: %+v", r.ShortMessage, r)
- }
return nil
}
@@ -239,7 +212,7 @@ func (c *Client) UpdateDNSRecord(sessionID, domainName string, record DNSRecord)
func (c *Client) GetDNSRecords(hostname, apiSessionID string) ([]DNSRecord, error) {
payload := &Request{
Action: "infoDnsRecords",
- Param: InfoDNSRecordsMsg{
+ Param: InfoDNSRecordsRequest{
DomainName: hostname,
CustomerNumber: c.customerNumber,
APIKey: c.apiKey,
@@ -248,82 +221,98 @@ func (c *Client) GetDNSRecords(hostname, apiSessionID string) ([]DNSRecord, erro
},
}
- response, err := c.sendRequest(payload)
+ var responseData InfoDNSRecordsResponse
+ err := c.doRequest(payload, &responseData)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("error when sending the request: %v", err)
}
- var r ResponseMsg
-
- err = json.Unmarshal(response, &r)
- if err != nil {
- return nil, err
- }
-
- if r.Status != success {
- return nil, fmt.Errorf("%s", r.ShortMessage)
- }
- return r.ResponseData.DNSRecords, nil
+ return responseData.DNSRecords, nil
}
-// sendRequest marshals given body to JSON, send the request to netcup API
+// doRequest marshals given body to JSON, send the request to netcup API
// and returns body of response
-func (c *Client) sendRequest(payload interface{}) ([]byte, error) {
+func (c *Client) doRequest(payload interface{}, responseData interface{}) error {
body, err := json.Marshal(payload)
if err != nil {
- return nil, err
+ return err
}
req, err := http.NewRequest(http.MethodPost, c.BaseURL, bytes.NewReader(body))
if err != nil {
- return nil, err
+ return err
}
- req.Close = true
+ req.Close = true
req.Header.Set("content-type", "application/json")
req.Header.Set("User-Agent", acme.UserAgent)
resp, err := c.HTTPClient.Do(req)
if err != nil {
- return nil, err
+ return err
}
- if resp.StatusCode > 299 {
- return nil, fmt.Errorf("API request failed with HTTP Status code %d", resp.StatusCode)
+ if err = checkResponse(resp); err != nil {
+ return err
}
- body, err = ioutil.ReadAll(resp.Body)
+ respMsg, err := decodeResponseMsg(resp)
if err != nil {
- return nil, fmt.Errorf("read of response body failed, %v", err)
+ return err
}
- defer resp.Body.Close()
- return body, nil
-}
+ if respMsg.Status != success {
+ return respMsg
+ }
-// GetDNSRecordIdx searches a given array of DNSRecords for a given DNSRecord
-// equivalence is determined by Destination and RecortType attributes
-// returns index of given DNSRecord in given array of DNSRecords
-func GetDNSRecordIdx(records []DNSRecord, record DNSRecord) (int, error) {
- for index, element := range records {
- if record.Destination == element.Destination && record.RecordType == element.RecordType {
- return index, nil
+ if responseData != nil {
+ err = json.Unmarshal(respMsg.ResponseData, responseData)
+ if err != nil {
+ return fmt.Errorf("%v: unmarshaling %T error: %v: %s",
+ respMsg, responseData, err, string(respMsg.ResponseData))
}
}
- return -1, fmt.Errorf("no DNS Record found")
+
+ return nil
}
-// CreateTxtRecord uses the supplied values to return a DNSRecord of type TXT for the dns-01 challenge
-func CreateTxtRecord(hostname, value string, ttl int) DNSRecord {
- return DNSRecord{
- ID: 0,
- Hostname: hostname,
- RecordType: "TXT",
- Priority: "",
- Destination: value,
- DeleteRecord: false,
- State: "",
- TTL: ttl,
+func checkResponse(resp *http.Response) error {
+ if resp.StatusCode > 299 {
+ if resp.Body == nil {
+ return fmt.Errorf("response body is nil, status code=%d", resp.StatusCode)
+ }
+
+ defer resp.Body.Close()
+
+ raw, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return fmt.Errorf("unable to read body: status code=%d, error=%v", resp.StatusCode, err)
+ }
+
+ return fmt.Errorf("status code=%d: %s", resp.StatusCode, string(raw))
}
+
+ return nil
+}
+
+func decodeResponseMsg(resp *http.Response) (*ResponseMsg, error) {
+ if resp.Body == nil {
+ return nil, fmt.Errorf("response body is nil, status code=%d", resp.StatusCode)
+ }
+
+ defer resp.Body.Close()
+
+ raw, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return nil, fmt.Errorf("unable to read body: status code=%d, error=%v", resp.StatusCode, err)
+ }
+
+ var respMsg ResponseMsg
+ err = json.Unmarshal(raw, &respMsg)
+ if err != nil {
+ return nil, fmt.Errorf("unmarshaling %T error [status code=%d]: %v: %s", respMsg, resp.StatusCode, err, string(raw))
+ }
+
+ return &respMsg, nil
}
diff --git a/vendor/github.com/xenolf/lego/providers/dns/netcup/netcup.go b/vendor/github.com/xenolf/lego/providers/dns/netcup/netcup.go
index 983b71e57..0dc59f960 100644
--- a/vendor/github.com/xenolf/lego/providers/dns/netcup/netcup.go
+++ b/vendor/github.com/xenolf/lego/providers/dns/netcup/netcup.go
@@ -9,6 +9,7 @@ import (
"time"
"github.com/xenolf/lego/acme"
+ "github.com/xenolf/lego/log"
"github.com/xenolf/lego/platform/config/env"
)
@@ -27,8 +28,8 @@ type Config struct {
func NewDefaultConfig() *Config {
return &Config{
TTL: env.GetOrDefaultInt("NETCUP_TTL", 120),
- PropagationTimeout: env.GetOrDefaultSecond("NETCUP_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout),
- PollingInterval: env.GetOrDefaultSecond("NETCUP_POLLING_INTERVAL", acme.DefaultPollingInterval),
+ PropagationTimeout: env.GetOrDefaultSecond("NETCUP_PROPAGATION_TIMEOUT", 120*time.Second),
+ PollingInterval: env.GetOrDefaultSecond("NETCUP_POLLING_INTERVAL", 5*time.Second),
HTTPClient: &http.Client{
Timeout: env.GetOrDefaultSecond("NETCUP_HTTP_TIMEOUT", 10*time.Second),
},
@@ -76,11 +77,11 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
return nil, errors.New("netcup: the configuration of the DNS provider is nil")
}
- if config.Customer == "" || config.Key == "" || config.Password == "" {
- return nil, fmt.Errorf("netcup: netcup credentials missing")
+ client, err := NewClient(config.Customer, config.Key, config.Password)
+ if err != nil {
+ return nil, fmt.Errorf("netcup: %v", err)
}
- client := NewClient(config.Customer, config.Key, config.Password)
client.HTTPClient = config.HTTPClient
return &DNSProvider{client: client, config: config}, nil
@@ -100,27 +101,37 @@ func (d *DNSProvider) Present(domainName, token, keyAuth string) error {
return fmt.Errorf("netcup: %v", err)
}
- hostname := strings.Replace(fqdn, "."+zone, "", 1)
- record := CreateTxtRecord(hostname, value, d.config.TTL)
-
- err = d.client.UpdateDNSRecord(sessionID, acme.UnFqdn(zone), record)
- if err != nil {
- if errLogout := d.client.Logout(sessionID); errLogout != nil {
- return fmt.Errorf("netcup: failed to add TXT-Record: %v; %v", err, errLogout)
+ defer func() {
+ err = d.client.Logout(sessionID)
+ if err != nil {
+ log.Print("netcup: %v", err)
}
+ }()
+
+ hostname := strings.Replace(fqdn, "."+zone, "", 1)
+ record := createTxtRecord(hostname, value, d.config.TTL)
+
+ zone = acme.UnFqdn(zone)
+
+ records, err := d.client.GetDNSRecords(zone, sessionID)
+ if err != nil {
+ // skip no existing records
+ log.Infof("no existing records, error ignored: %v", err)
+ }
+
+ records = append(records, record)
+
+ err = d.client.UpdateDNSRecord(sessionID, zone, records)
+ if err != nil {
return fmt.Errorf("netcup: failed to add TXT-Record: %v", err)
}
- err = d.client.Logout(sessionID)
- if err != nil {
- return fmt.Errorf("netcup: %v", err)
- }
return nil
}
// CleanUp removes the TXT record matching the specified parameters
-func (d *DNSProvider) CleanUp(domainname, token, keyAuth string) error {
- fqdn, value, _ := acme.DNS01Record(domainname, keyAuth)
+func (d *DNSProvider) CleanUp(domainName, token, keyAuth string) error {
+ fqdn, value, _ := acme.DNS01Record(domainName, keyAuth)
zone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
if err != nil {
@@ -132,6 +143,13 @@ func (d *DNSProvider) CleanUp(domainname, token, keyAuth string) error {
return fmt.Errorf("netcup: %v", err)
}
+ defer func() {
+ err = d.client.Logout(sessionID)
+ if err != nil {
+ log.Print("netcup: %v", err)
+ }
+ }()
+
hostname := strings.Replace(fqdn, "."+zone, "", 1)
zone = acme.UnFqdn(zone)
@@ -141,27 +159,20 @@ func (d *DNSProvider) CleanUp(domainname, token, keyAuth string) error {
return fmt.Errorf("netcup: %v", err)
}
- record := CreateTxtRecord(hostname, value, 0)
+ record := createTxtRecord(hostname, value, 0)
- idx, err := GetDNSRecordIdx(records, record)
+ idx, err := getDNSRecordIdx(records, record)
if err != nil {
return fmt.Errorf("netcup: %v", err)
}
records[idx].DeleteRecord = true
- err = d.client.UpdateDNSRecord(sessionID, zone, records[idx])
+ err = d.client.UpdateDNSRecord(sessionID, zone, []DNSRecord{records[idx]})
if err != nil {
- if errLogout := d.client.Logout(sessionID); errLogout != nil {
- return fmt.Errorf("netcup: %v; %v", err, errLogout)
- }
return fmt.Errorf("netcup: %v", err)
}
- err = d.client.Logout(sessionID)
- if err != nil {
- return fmt.Errorf("netcup: %v", err)
- }
return nil
}
@@ -170,3 +181,29 @@ func (d *DNSProvider) CleanUp(domainname, token, keyAuth string) error {
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
return d.config.PropagationTimeout, d.config.PollingInterval
}
+
+// getDNSRecordIdx searches a given array of DNSRecords for a given DNSRecord
+// equivalence is determined by Destination and RecortType attributes
+// returns index of given DNSRecord in given array of DNSRecords
+func getDNSRecordIdx(records []DNSRecord, record DNSRecord) (int, error) {
+ for index, element := range records {
+ if record.Destination == element.Destination && record.RecordType == element.RecordType {
+ return index, nil
+ }
+ }
+ return -1, fmt.Errorf("no DNS Record found")
+}
+
+// createTxtRecord uses the supplied values to return a DNSRecord of type TXT for the dns-01 challenge
+func createTxtRecord(hostname, value string, ttl int) DNSRecord {
+ return DNSRecord{
+ ID: 0,
+ Hostname: hostname,
+ RecordType: "TXT",
+ Priority: "",
+ Destination: value,
+ DeleteRecord: false,
+ State: "",
+ TTL: ttl,
+ }
+}
diff --git a/vendor/golang.org/x/crypto/pkcs12/bmp-string.go b/vendor/golang.org/x/crypto/pkcs12/bmp-string.go
new file mode 100644
index 000000000..233b8b62c
--- /dev/null
+++ b/vendor/golang.org/x/crypto/pkcs12/bmp-string.go
@@ -0,0 +1,50 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package pkcs12
+
+import (
+ "errors"
+ "unicode/utf16"
+)
+
+// bmpString returns s encoded in UCS-2 with a zero terminator.
+func bmpString(s string) ([]byte, error) {
+ // References:
+ // https://tools.ietf.org/html/rfc7292#appendix-B.1
+ // https://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane
+ // - non-BMP characters are encoded in UTF 16 by using a surrogate pair of 16-bit codes
+ // EncodeRune returns 0xfffd if the rune does not need special encoding
+ // - the above RFC provides the info that BMPStrings are NULL terminated.
+
+ ret := make([]byte, 0, 2*len(s)+2)
+
+ for _, r := range s {
+ if t, _ := utf16.EncodeRune(r); t != 0xfffd {
+ return nil, errors.New("pkcs12: string contains characters that cannot be encoded in UCS-2")
+ }
+ ret = append(ret, byte(r/256), byte(r%256))
+ }
+
+ return append(ret, 0, 0), nil
+}
+
+func decodeBMPString(bmpString []byte) (string, error) {
+ if len(bmpString)%2 != 0 {
+ return "", errors.New("pkcs12: odd-length BMP string")
+ }
+
+ // strip terminator if present
+ if l := len(bmpString); l >= 2 && bmpString[l-1] == 0 && bmpString[l-2] == 0 {
+ bmpString = bmpString[:l-2]
+ }
+
+ s := make([]uint16, 0, len(bmpString)/2)
+ for len(bmpString) > 0 {
+ s = append(s, uint16(bmpString[0])<<8+uint16(bmpString[1]))
+ bmpString = bmpString[2:]
+ }
+
+ return string(utf16.Decode(s)), nil
+}
diff --git a/vendor/golang.org/x/crypto/pkcs12/crypto.go b/vendor/golang.org/x/crypto/pkcs12/crypto.go
new file mode 100644
index 000000000..484ca51b7
--- /dev/null
+++ b/vendor/golang.org/x/crypto/pkcs12/crypto.go
@@ -0,0 +1,131 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package pkcs12
+
+import (
+ "bytes"
+ "crypto/cipher"
+ "crypto/des"
+ "crypto/x509/pkix"
+ "encoding/asn1"
+ "errors"
+
+ "golang.org/x/crypto/pkcs12/internal/rc2"
+)
+
+var (
+ oidPBEWithSHAAnd3KeyTripleDESCBC = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 3})
+ oidPBEWithSHAAnd40BitRC2CBC = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 6})
+)
+
+// pbeCipher is an abstraction of a PKCS#12 cipher.
+type pbeCipher interface {
+ // create returns a cipher.Block given a key.
+ create(key []byte) (cipher.Block, error)
+ // deriveKey returns a key derived from the given password and salt.
+ deriveKey(salt, password []byte, iterations int) []byte
+ // deriveKey returns an IV derived from the given password and salt.
+ deriveIV(salt, password []byte, iterations int) []byte
+}
+
+type shaWithTripleDESCBC struct{}
+
+func (shaWithTripleDESCBC) create(key []byte) (cipher.Block, error) {
+ return des.NewTripleDESCipher(key)
+}
+
+func (shaWithTripleDESCBC) deriveKey(salt, password []byte, iterations int) []byte {
+ return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 1, 24)
+}
+
+func (shaWithTripleDESCBC) deriveIV(salt, password []byte, iterations int) []byte {
+ return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 2, 8)
+}
+
+type shaWith40BitRC2CBC struct{}
+
+func (shaWith40BitRC2CBC) create(key []byte) (cipher.Block, error) {
+ return rc2.New(key, len(key)*8)
+}
+
+func (shaWith40BitRC2CBC) deriveKey(salt, password []byte, iterations int) []byte {
+ return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 1, 5)
+}
+
+func (shaWith40BitRC2CBC) deriveIV(salt, password []byte, iterations int) []byte {
+ return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 2, 8)
+}
+
+type pbeParams struct {
+ Salt []byte
+ Iterations int
+}
+
+func pbDecrypterFor(algorithm pkix.AlgorithmIdentifier, password []byte) (cipher.BlockMode, int, error) {
+ var cipherType pbeCipher
+
+ switch {
+ case algorithm.Algorithm.Equal(oidPBEWithSHAAnd3KeyTripleDESCBC):
+ cipherType = shaWithTripleDESCBC{}
+ case algorithm.Algorithm.Equal(oidPBEWithSHAAnd40BitRC2CBC):
+ cipherType = shaWith40BitRC2CBC{}
+ default:
+ return nil, 0, NotImplementedError("algorithm " + algorithm.Algorithm.String() + " is not supported")
+ }
+
+ var params pbeParams
+ if err := unmarshal(algorithm.Parameters.FullBytes, ¶ms); err != nil {
+ return nil, 0, err
+ }
+
+ key := cipherType.deriveKey(params.Salt, password, params.Iterations)
+ iv := cipherType.deriveIV(params.Salt, password, params.Iterations)
+
+ block, err := cipherType.create(key)
+ if err != nil {
+ return nil, 0, err
+ }
+
+ return cipher.NewCBCDecrypter(block, iv), block.BlockSize(), nil
+}
+
+func pbDecrypt(info decryptable, password []byte) (decrypted []byte, err error) {
+ cbc, blockSize, err := pbDecrypterFor(info.Algorithm(), password)
+ if err != nil {
+ return nil, err
+ }
+
+ encrypted := info.Data()
+ if len(encrypted) == 0 {
+ return nil, errors.New("pkcs12: empty encrypted data")
+ }
+ if len(encrypted)%blockSize != 0 {
+ return nil, errors.New("pkcs12: input is not a multiple of the block size")
+ }
+ decrypted = make([]byte, len(encrypted))
+ cbc.CryptBlocks(decrypted, encrypted)
+
+ psLen := int(decrypted[len(decrypted)-1])
+ if psLen == 0 || psLen > blockSize {
+ return nil, ErrDecryption
+ }
+
+ if len(decrypted) < psLen {
+ return nil, ErrDecryption
+ }
+ ps := decrypted[len(decrypted)-psLen:]
+ decrypted = decrypted[:len(decrypted)-psLen]
+ if bytes.Compare(ps, bytes.Repeat([]byte{byte(psLen)}, psLen)) != 0 {
+ return nil, ErrDecryption
+ }
+
+ return
+}
+
+// decryptable abstracts an object that contains ciphertext.
+type decryptable interface {
+ Algorithm() pkix.AlgorithmIdentifier
+ Data() []byte
+}
diff --git a/vendor/golang.org/x/crypto/pkcs12/errors.go b/vendor/golang.org/x/crypto/pkcs12/errors.go
new file mode 100644
index 000000000..7377ce6fb
--- /dev/null
+++ b/vendor/golang.org/x/crypto/pkcs12/errors.go
@@ -0,0 +1,23 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package pkcs12
+
+import "errors"
+
+var (
+ // ErrDecryption represents a failure to decrypt the input.
+ ErrDecryption = errors.New("pkcs12: decryption error, incorrect padding")
+
+ // ErrIncorrectPassword is returned when an incorrect password is detected.
+ // Usually, P12/PFX data is signed to be able to verify the password.
+ ErrIncorrectPassword = errors.New("pkcs12: decryption password incorrect")
+)
+
+// NotImplementedError indicates that the input is not currently supported.
+type NotImplementedError string
+
+func (e NotImplementedError) Error() string {
+ return "pkcs12: " + string(e)
+}
diff --git a/vendor/golang.org/x/crypto/pkcs12/internal/rc2/rc2.go b/vendor/golang.org/x/crypto/pkcs12/internal/rc2/rc2.go
new file mode 100644
index 000000000..7499e3fb6
--- /dev/null
+++ b/vendor/golang.org/x/crypto/pkcs12/internal/rc2/rc2.go
@@ -0,0 +1,271 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package rc2 implements the RC2 cipher
+/*
+https://www.ietf.org/rfc/rfc2268.txt
+http://people.csail.mit.edu/rivest/pubs/KRRR98.pdf
+
+This code is licensed under the MIT license.
+*/
+package rc2
+
+import (
+ "crypto/cipher"
+ "encoding/binary"
+)
+
+// The rc2 block size in bytes
+const BlockSize = 8
+
+type rc2Cipher struct {
+ k [64]uint16
+}
+
+// New returns a new rc2 cipher with the given key and effective key length t1
+func New(key []byte, t1 int) (cipher.Block, error) {
+ // TODO(dgryski): error checking for key length
+ return &rc2Cipher{
+ k: expandKey(key, t1),
+ }, nil
+}
+
+func (*rc2Cipher) BlockSize() int { return BlockSize }
+
+var piTable = [256]byte{
+ 0xd9, 0x78, 0xf9, 0xc4, 0x19, 0xdd, 0xb5, 0xed, 0x28, 0xe9, 0xfd, 0x79, 0x4a, 0xa0, 0xd8, 0x9d,
+ 0xc6, 0x7e, 0x37, 0x83, 0x2b, 0x76, 0x53, 0x8e, 0x62, 0x4c, 0x64, 0x88, 0x44, 0x8b, 0xfb, 0xa2,
+ 0x17, 0x9a, 0x59, 0xf5, 0x87, 0xb3, 0x4f, 0x13, 0x61, 0x45, 0x6d, 0x8d, 0x09, 0x81, 0x7d, 0x32,
+ 0xbd, 0x8f, 0x40, 0xeb, 0x86, 0xb7, 0x7b, 0x0b, 0xf0, 0x95, 0x21, 0x22, 0x5c, 0x6b, 0x4e, 0x82,
+ 0x54, 0xd6, 0x65, 0x93, 0xce, 0x60, 0xb2, 0x1c, 0x73, 0x56, 0xc0, 0x14, 0xa7, 0x8c, 0xf1, 0xdc,
+ 0x12, 0x75, 0xca, 0x1f, 0x3b, 0xbe, 0xe4, 0xd1, 0x42, 0x3d, 0xd4, 0x30, 0xa3, 0x3c, 0xb6, 0x26,
+ 0x6f, 0xbf, 0x0e, 0xda, 0x46, 0x69, 0x07, 0x57, 0x27, 0xf2, 0x1d, 0x9b, 0xbc, 0x94, 0x43, 0x03,
+ 0xf8, 0x11, 0xc7, 0xf6, 0x90, 0xef, 0x3e, 0xe7, 0x06, 0xc3, 0xd5, 0x2f, 0xc8, 0x66, 0x1e, 0xd7,
+ 0x08, 0xe8, 0xea, 0xde, 0x80, 0x52, 0xee, 0xf7, 0x84, 0xaa, 0x72, 0xac, 0x35, 0x4d, 0x6a, 0x2a,
+ 0x96, 0x1a, 0xd2, 0x71, 0x5a, 0x15, 0x49, 0x74, 0x4b, 0x9f, 0xd0, 0x5e, 0x04, 0x18, 0xa4, 0xec,
+ 0xc2, 0xe0, 0x41, 0x6e, 0x0f, 0x51, 0xcb, 0xcc, 0x24, 0x91, 0xaf, 0x50, 0xa1, 0xf4, 0x70, 0x39,
+ 0x99, 0x7c, 0x3a, 0x85, 0x23, 0xb8, 0xb4, 0x7a, 0xfc, 0x02, 0x36, 0x5b, 0x25, 0x55, 0x97, 0x31,
+ 0x2d, 0x5d, 0xfa, 0x98, 0xe3, 0x8a, 0x92, 0xae, 0x05, 0xdf, 0x29, 0x10, 0x67, 0x6c, 0xba, 0xc9,
+ 0xd3, 0x00, 0xe6, 0xcf, 0xe1, 0x9e, 0xa8, 0x2c, 0x63, 0x16, 0x01, 0x3f, 0x58, 0xe2, 0x89, 0xa9,
+ 0x0d, 0x38, 0x34, 0x1b, 0xab, 0x33, 0xff, 0xb0, 0xbb, 0x48, 0x0c, 0x5f, 0xb9, 0xb1, 0xcd, 0x2e,
+ 0xc5, 0xf3, 0xdb, 0x47, 0xe5, 0xa5, 0x9c, 0x77, 0x0a, 0xa6, 0x20, 0x68, 0xfe, 0x7f, 0xc1, 0xad,
+}
+
+func expandKey(key []byte, t1 int) [64]uint16 {
+
+ l := make([]byte, 128)
+ copy(l, key)
+
+ var t = len(key)
+ var t8 = (t1 + 7) / 8
+ var tm = byte(255 % uint(1<<(8+uint(t1)-8*uint(t8))))
+
+ for i := len(key); i < 128; i++ {
+ l[i] = piTable[l[i-1]+l[uint8(i-t)]]
+ }
+
+ l[128-t8] = piTable[l[128-t8]&tm]
+
+ for i := 127 - t8; i >= 0; i-- {
+ l[i] = piTable[l[i+1]^l[i+t8]]
+ }
+
+ var k [64]uint16
+
+ for i := range k {
+ k[i] = uint16(l[2*i]) + uint16(l[2*i+1])*256
+ }
+
+ return k
+}
+
+func rotl16(x uint16, b uint) uint16 {
+ return (x >> (16 - b)) | (x << b)
+}
+
+func (c *rc2Cipher) Encrypt(dst, src []byte) {
+
+ r0 := binary.LittleEndian.Uint16(src[0:])
+ r1 := binary.LittleEndian.Uint16(src[2:])
+ r2 := binary.LittleEndian.Uint16(src[4:])
+ r3 := binary.LittleEndian.Uint16(src[6:])
+
+ var j int
+
+ for j <= 16 {
+ // mix r0
+ r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1)
+ r0 = rotl16(r0, 1)
+ j++
+
+ // mix r1
+ r1 = r1 + c.k[j] + (r0 & r3) + ((^r0) & r2)
+ r1 = rotl16(r1, 2)
+ j++
+
+ // mix r2
+ r2 = r2 + c.k[j] + (r1 & r0) + ((^r1) & r3)
+ r2 = rotl16(r2, 3)
+ j++
+
+ // mix r3
+ r3 = r3 + c.k[j] + (r2 & r1) + ((^r2) & r0)
+ r3 = rotl16(r3, 5)
+ j++
+
+ }
+
+ r0 = r0 + c.k[r3&63]
+ r1 = r1 + c.k[r0&63]
+ r2 = r2 + c.k[r1&63]
+ r3 = r3 + c.k[r2&63]
+
+ for j <= 40 {
+ // mix r0
+ r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1)
+ r0 = rotl16(r0, 1)
+ j++
+
+ // mix r1
+ r1 = r1 + c.k[j] + (r0 & r3) + ((^r0) & r2)
+ r1 = rotl16(r1, 2)
+ j++
+
+ // mix r2
+ r2 = r2 + c.k[j] + (r1 & r0) + ((^r1) & r3)
+ r2 = rotl16(r2, 3)
+ j++
+
+ // mix r3
+ r3 = r3 + c.k[j] + (r2 & r1) + ((^r2) & r0)
+ r3 = rotl16(r3, 5)
+ j++
+
+ }
+
+ r0 = r0 + c.k[r3&63]
+ r1 = r1 + c.k[r0&63]
+ r2 = r2 + c.k[r1&63]
+ r3 = r3 + c.k[r2&63]
+
+ for j <= 60 {
+ // mix r0
+ r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1)
+ r0 = rotl16(r0, 1)
+ j++
+
+ // mix r1
+ r1 = r1 + c.k[j] + (r0 & r3) + ((^r0) & r2)
+ r1 = rotl16(r1, 2)
+ j++
+
+ // mix r2
+ r2 = r2 + c.k[j] + (r1 & r0) + ((^r1) & r3)
+ r2 = rotl16(r2, 3)
+ j++
+
+ // mix r3
+ r3 = r3 + c.k[j] + (r2 & r1) + ((^r2) & r0)
+ r3 = rotl16(r3, 5)
+ j++
+ }
+
+ binary.LittleEndian.PutUint16(dst[0:], r0)
+ binary.LittleEndian.PutUint16(dst[2:], r1)
+ binary.LittleEndian.PutUint16(dst[4:], r2)
+ binary.LittleEndian.PutUint16(dst[6:], r3)
+}
+
+func (c *rc2Cipher) Decrypt(dst, src []byte) {
+
+ r0 := binary.LittleEndian.Uint16(src[0:])
+ r1 := binary.LittleEndian.Uint16(src[2:])
+ r2 := binary.LittleEndian.Uint16(src[4:])
+ r3 := binary.LittleEndian.Uint16(src[6:])
+
+ j := 63
+
+ for j >= 44 {
+ // unmix r3
+ r3 = rotl16(r3, 16-5)
+ r3 = r3 - c.k[j] - (r2 & r1) - ((^r2) & r0)
+ j--
+
+ // unmix r2
+ r2 = rotl16(r2, 16-3)
+ r2 = r2 - c.k[j] - (r1 & r0) - ((^r1) & r3)
+ j--
+
+ // unmix r1
+ r1 = rotl16(r1, 16-2)
+ r1 = r1 - c.k[j] - (r0 & r3) - ((^r0) & r2)
+ j--
+
+ // unmix r0
+ r0 = rotl16(r0, 16-1)
+ r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1)
+ j--
+ }
+
+ r3 = r3 - c.k[r2&63]
+ r2 = r2 - c.k[r1&63]
+ r1 = r1 - c.k[r0&63]
+ r0 = r0 - c.k[r3&63]
+
+ for j >= 20 {
+ // unmix r3
+ r3 = rotl16(r3, 16-5)
+ r3 = r3 - c.k[j] - (r2 & r1) - ((^r2) & r0)
+ j--
+
+ // unmix r2
+ r2 = rotl16(r2, 16-3)
+ r2 = r2 - c.k[j] - (r1 & r0) - ((^r1) & r3)
+ j--
+
+ // unmix r1
+ r1 = rotl16(r1, 16-2)
+ r1 = r1 - c.k[j] - (r0 & r3) - ((^r0) & r2)
+ j--
+
+ // unmix r0
+ r0 = rotl16(r0, 16-1)
+ r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1)
+ j--
+
+ }
+
+ r3 = r3 - c.k[r2&63]
+ r2 = r2 - c.k[r1&63]
+ r1 = r1 - c.k[r0&63]
+ r0 = r0 - c.k[r3&63]
+
+ for j >= 0 {
+ // unmix r3
+ r3 = rotl16(r3, 16-5)
+ r3 = r3 - c.k[j] - (r2 & r1) - ((^r2) & r0)
+ j--
+
+ // unmix r2
+ r2 = rotl16(r2, 16-3)
+ r2 = r2 - c.k[j] - (r1 & r0) - ((^r1) & r3)
+ j--
+
+ // unmix r1
+ r1 = rotl16(r1, 16-2)
+ r1 = r1 - c.k[j] - (r0 & r3) - ((^r0) & r2)
+ j--
+
+ // unmix r0
+ r0 = rotl16(r0, 16-1)
+ r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1)
+ j--
+
+ }
+
+ binary.LittleEndian.PutUint16(dst[0:], r0)
+ binary.LittleEndian.PutUint16(dst[2:], r1)
+ binary.LittleEndian.PutUint16(dst[4:], r2)
+ binary.LittleEndian.PutUint16(dst[6:], r3)
+}
diff --git a/vendor/golang.org/x/crypto/pkcs12/mac.go b/vendor/golang.org/x/crypto/pkcs12/mac.go
new file mode 100644
index 000000000..5f38aa7de
--- /dev/null
+++ b/vendor/golang.org/x/crypto/pkcs12/mac.go
@@ -0,0 +1,45 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package pkcs12
+
+import (
+ "crypto/hmac"
+ "crypto/sha1"
+ "crypto/x509/pkix"
+ "encoding/asn1"
+)
+
+type macData struct {
+ Mac digestInfo
+ MacSalt []byte
+ Iterations int `asn1:"optional,default:1"`
+}
+
+// from PKCS#7:
+type digestInfo struct {
+ Algorithm pkix.AlgorithmIdentifier
+ Digest []byte
+}
+
+var (
+ oidSHA1 = asn1.ObjectIdentifier([]int{1, 3, 14, 3, 2, 26})
+)
+
+func verifyMac(macData *macData, message, password []byte) error {
+ if !macData.Mac.Algorithm.Algorithm.Equal(oidSHA1) {
+ return NotImplementedError("unknown digest algorithm: " + macData.Mac.Algorithm.Algorithm.String())
+ }
+
+ key := pbkdf(sha1Sum, 20, 64, macData.MacSalt, password, macData.Iterations, 3, 20)
+
+ mac := hmac.New(sha1.New, key)
+ mac.Write(message)
+ expectedMAC := mac.Sum(nil)
+
+ if !hmac.Equal(macData.Mac.Digest, expectedMAC) {
+ return ErrIncorrectPassword
+ }
+ return nil
+}
diff --git a/vendor/golang.org/x/crypto/pkcs12/pbkdf.go b/vendor/golang.org/x/crypto/pkcs12/pbkdf.go
new file mode 100644
index 000000000..5c419d41e
--- /dev/null
+++ b/vendor/golang.org/x/crypto/pkcs12/pbkdf.go
@@ -0,0 +1,170 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package pkcs12
+
+import (
+ "bytes"
+ "crypto/sha1"
+ "math/big"
+)
+
+var (
+ one = big.NewInt(1)
+)
+
+// sha1Sum returns the SHA-1 hash of in.
+func sha1Sum(in []byte) []byte {
+ sum := sha1.Sum(in)
+ return sum[:]
+}
+
+// fillWithRepeats returns v*ceiling(len(pattern) / v) bytes consisting of
+// repeats of pattern.
+func fillWithRepeats(pattern []byte, v int) []byte {
+ if len(pattern) == 0 {
+ return nil
+ }
+ outputLen := v * ((len(pattern) + v - 1) / v)
+ return bytes.Repeat(pattern, (outputLen+len(pattern)-1)/len(pattern))[:outputLen]
+}
+
+func pbkdf(hash func([]byte) []byte, u, v int, salt, password []byte, r int, ID byte, size int) (key []byte) {
+ // implementation of https://tools.ietf.org/html/rfc7292#appendix-B.2 , RFC text verbatim in comments
+
+ // Let H be a hash function built around a compression function f:
+
+ // Z_2^u x Z_2^v -> Z_2^u
+
+ // (that is, H has a chaining variable and output of length u bits, and
+ // the message input to the compression function of H is v bits). The
+ // values for u and v are as follows:
+
+ // HASH FUNCTION VALUE u VALUE v
+ // MD2, MD5 128 512
+ // SHA-1 160 512
+ // SHA-224 224 512
+ // SHA-256 256 512
+ // SHA-384 384 1024
+ // SHA-512 512 1024
+ // SHA-512/224 224 1024
+ // SHA-512/256 256 1024
+
+ // Furthermore, let r be the iteration count.
+
+ // We assume here that u and v are both multiples of 8, as are the
+ // lengths of the password and salt strings (which we denote by p and s,
+ // respectively) and the number n of pseudorandom bits required. In
+ // addition, u and v are of course non-zero.
+
+ // For information on security considerations for MD5 [19], see [25] and
+ // [1], and on those for MD2, see [18].
+
+ // The following procedure can be used to produce pseudorandom bits for
+ // a particular "purpose" that is identified by a byte called "ID".
+ // This standard specifies 3 different values for the ID byte:
+
+ // 1. If ID=1, then the pseudorandom bits being produced are to be used
+ // as key material for performing encryption or decryption.
+
+ // 2. If ID=2, then the pseudorandom bits being produced are to be used
+ // as an IV (Initial Value) for encryption or decryption.
+
+ // 3. If ID=3, then the pseudorandom bits being produced are to be used
+ // as an integrity key for MACing.
+
+ // 1. Construct a string, D (the "diversifier"), by concatenating v/8
+ // copies of ID.
+ var D []byte
+ for i := 0; i < v; i++ {
+ D = append(D, ID)
+ }
+
+ // 2. Concatenate copies of the salt together to create a string S of
+ // length v(ceiling(s/v)) bits (the final copy of the salt may be
+ // truncated to create S). Note that if the salt is the empty
+ // string, then so is S.
+
+ S := fillWithRepeats(salt, v)
+
+ // 3. Concatenate copies of the password together to create a string P
+ // of length v(ceiling(p/v)) bits (the final copy of the password
+ // may be truncated to create P). Note that if the password is the
+ // empty string, then so is P.
+
+ P := fillWithRepeats(password, v)
+
+ // 4. Set I=S||P to be the concatenation of S and P.
+ I := append(S, P...)
+
+ // 5. Set c=ceiling(n/u).
+ c := (size + u - 1) / u
+
+ // 6. For i=1, 2, ..., c, do the following:
+ A := make([]byte, c*20)
+ var IjBuf []byte
+ for i := 0; i < c; i++ {
+ // A. Set A2=H^r(D||I). (i.e., the r-th hash of D||1,
+ // H(H(H(... H(D||I))))
+ Ai := hash(append(D, I...))
+ for j := 1; j < r; j++ {
+ Ai = hash(Ai)
+ }
+ copy(A[i*20:], Ai[:])
+
+ if i < c-1 { // skip on last iteration
+ // B. Concatenate copies of Ai to create a string B of length v
+ // bits (the final copy of Ai may be truncated to create B).
+ var B []byte
+ for len(B) < v {
+ B = append(B, Ai[:]...)
+ }
+ B = B[:v]
+
+ // C. Treating I as a concatenation I_0, I_1, ..., I_(k-1) of v-bit
+ // blocks, where k=ceiling(s/v)+ceiling(p/v), modify I by
+ // setting I_j=(I_j+B+1) mod 2^v for each j.
+ {
+ Bbi := new(big.Int).SetBytes(B)
+ Ij := new(big.Int)
+
+ for j := 0; j < len(I)/v; j++ {
+ Ij.SetBytes(I[j*v : (j+1)*v])
+ Ij.Add(Ij, Bbi)
+ Ij.Add(Ij, one)
+ Ijb := Ij.Bytes()
+ // We expect Ijb to be exactly v bytes,
+ // if it is longer or shorter we must
+ // adjust it accordingly.
+ if len(Ijb) > v {
+ Ijb = Ijb[len(Ijb)-v:]
+ }
+ if len(Ijb) < v {
+ if IjBuf == nil {
+ IjBuf = make([]byte, v)
+ }
+ bytesShort := v - len(Ijb)
+ for i := 0; i < bytesShort; i++ {
+ IjBuf[i] = 0
+ }
+ copy(IjBuf[bytesShort:], Ijb)
+ Ijb = IjBuf
+ }
+ copy(I[j*v:(j+1)*v], Ijb)
+ }
+ }
+ }
+ }
+ // 7. Concatenate A_1, A_2, ..., A_c together to form a pseudorandom
+ // bit string, A.
+
+ // 8. Use the first n bits of A as the output of this entire process.
+ return A[:size]
+
+ // If the above process is being used to generate a DES key, the process
+ // should be used to create 64 random bits, and the key's parity bits
+ // should be set after the 64 bits have been produced. Similar concerns
+ // hold for 2-key and 3-key triple-DES keys, for CDMF keys, and for any
+ // similar keys with parity bits "built into them".
+}
diff --git a/vendor/golang.org/x/crypto/pkcs12/pkcs12.go b/vendor/golang.org/x/crypto/pkcs12/pkcs12.go
new file mode 100644
index 000000000..eff9ad3a9
--- /dev/null
+++ b/vendor/golang.org/x/crypto/pkcs12/pkcs12.go
@@ -0,0 +1,346 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package pkcs12 implements some of PKCS#12.
+//
+// This implementation is distilled from https://tools.ietf.org/html/rfc7292
+// and referenced documents. It is intended for decoding P12/PFX-stored
+// certificates and keys for use with the crypto/tls package.
+package pkcs12
+
+import (
+ "crypto/ecdsa"
+ "crypto/rsa"
+ "crypto/x509"
+ "crypto/x509/pkix"
+ "encoding/asn1"
+ "encoding/hex"
+ "encoding/pem"
+ "errors"
+)
+
+var (
+ oidDataContentType = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 7, 1})
+ oidEncryptedDataContentType = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 7, 6})
+
+ oidFriendlyName = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 20})
+ oidLocalKeyID = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 21})
+ oidMicrosoftCSPName = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 4, 1, 311, 17, 1})
+)
+
+type pfxPdu struct {
+ Version int
+ AuthSafe contentInfo
+ MacData macData `asn1:"optional"`
+}
+
+type contentInfo struct {
+ ContentType asn1.ObjectIdentifier
+ Content asn1.RawValue `asn1:"tag:0,explicit,optional"`
+}
+
+type encryptedData struct {
+ Version int
+ EncryptedContentInfo encryptedContentInfo
+}
+
+type encryptedContentInfo struct {
+ ContentType asn1.ObjectIdentifier
+ ContentEncryptionAlgorithm pkix.AlgorithmIdentifier
+ EncryptedContent []byte `asn1:"tag:0,optional"`
+}
+
+func (i encryptedContentInfo) Algorithm() pkix.AlgorithmIdentifier {
+ return i.ContentEncryptionAlgorithm
+}
+
+func (i encryptedContentInfo) Data() []byte { return i.EncryptedContent }
+
+type safeBag struct {
+ Id asn1.ObjectIdentifier
+ Value asn1.RawValue `asn1:"tag:0,explicit"`
+ Attributes []pkcs12Attribute `asn1:"set,optional"`
+}
+
+type pkcs12Attribute struct {
+ Id asn1.ObjectIdentifier
+ Value asn1.RawValue `asn1:"set"`
+}
+
+type encryptedPrivateKeyInfo struct {
+ AlgorithmIdentifier pkix.AlgorithmIdentifier
+ EncryptedData []byte
+}
+
+func (i encryptedPrivateKeyInfo) Algorithm() pkix.AlgorithmIdentifier {
+ return i.AlgorithmIdentifier
+}
+
+func (i encryptedPrivateKeyInfo) Data() []byte {
+ return i.EncryptedData
+}
+
+// PEM block types
+const (
+ certificateType = "CERTIFICATE"
+ privateKeyType = "PRIVATE KEY"
+)
+
+// unmarshal calls asn1.Unmarshal, but also returns an error if there is any
+// trailing data after unmarshaling.
+func unmarshal(in []byte, out interface{}) error {
+ trailing, err := asn1.Unmarshal(in, out)
+ if err != nil {
+ return err
+ }
+ if len(trailing) != 0 {
+ return errors.New("pkcs12: trailing data found")
+ }
+ return nil
+}
+
+// ConvertToPEM converts all "safe bags" contained in pfxData to PEM blocks.
+func ToPEM(pfxData []byte, password string) ([]*pem.Block, error) {
+ encodedPassword, err := bmpString(password)
+ if err != nil {
+ return nil, ErrIncorrectPassword
+ }
+
+ bags, encodedPassword, err := getSafeContents(pfxData, encodedPassword)
+
+ if err != nil {
+ return nil, err
+ }
+
+ blocks := make([]*pem.Block, 0, len(bags))
+ for _, bag := range bags {
+ block, err := convertBag(&bag, encodedPassword)
+ if err != nil {
+ return nil, err
+ }
+ blocks = append(blocks, block)
+ }
+
+ return blocks, nil
+}
+
+func convertBag(bag *safeBag, password []byte) (*pem.Block, error) {
+ block := &pem.Block{
+ Headers: make(map[string]string),
+ }
+
+ for _, attribute := range bag.Attributes {
+ k, v, err := convertAttribute(&attribute)
+ if err != nil {
+ return nil, err
+ }
+ block.Headers[k] = v
+ }
+
+ switch {
+ case bag.Id.Equal(oidCertBag):
+ block.Type = certificateType
+ certsData, err := decodeCertBag(bag.Value.Bytes)
+ if err != nil {
+ return nil, err
+ }
+ block.Bytes = certsData
+ case bag.Id.Equal(oidPKCS8ShroundedKeyBag):
+ block.Type = privateKeyType
+
+ key, err := decodePkcs8ShroudedKeyBag(bag.Value.Bytes, password)
+ if err != nil {
+ return nil, err
+ }
+
+ switch key := key.(type) {
+ case *rsa.PrivateKey:
+ block.Bytes = x509.MarshalPKCS1PrivateKey(key)
+ case *ecdsa.PrivateKey:
+ block.Bytes, err = x509.MarshalECPrivateKey(key)
+ if err != nil {
+ return nil, err
+ }
+ default:
+ return nil, errors.New("found unknown private key type in PKCS#8 wrapping")
+ }
+ default:
+ return nil, errors.New("don't know how to convert a safe bag of type " + bag.Id.String())
+ }
+ return block, nil
+}
+
+func convertAttribute(attribute *pkcs12Attribute) (key, value string, err error) {
+ isString := false
+
+ switch {
+ case attribute.Id.Equal(oidFriendlyName):
+ key = "friendlyName"
+ isString = true
+ case attribute.Id.Equal(oidLocalKeyID):
+ key = "localKeyId"
+ case attribute.Id.Equal(oidMicrosoftCSPName):
+ // This key is chosen to match OpenSSL.
+ key = "Microsoft CSP Name"
+ isString = true
+ default:
+ return "", "", errors.New("pkcs12: unknown attribute with OID " + attribute.Id.String())
+ }
+
+ if isString {
+ if err := unmarshal(attribute.Value.Bytes, &attribute.Value); err != nil {
+ return "", "", err
+ }
+ if value, err = decodeBMPString(attribute.Value.Bytes); err != nil {
+ return "", "", err
+ }
+ } else {
+ var id []byte
+ if err := unmarshal(attribute.Value.Bytes, &id); err != nil {
+ return "", "", err
+ }
+ value = hex.EncodeToString(id)
+ }
+
+ return key, value, nil
+}
+
+// Decode extracts a certificate and private key from pfxData. This function
+// assumes that there is only one certificate and only one private key in the
+// pfxData.
+func Decode(pfxData []byte, password string) (privateKey interface{}, certificate *x509.Certificate, err error) {
+ encodedPassword, err := bmpString(password)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ bags, encodedPassword, err := getSafeContents(pfxData, encodedPassword)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ if len(bags) != 2 {
+ err = errors.New("pkcs12: expected exactly two safe bags in the PFX PDU")
+ return
+ }
+
+ for _, bag := range bags {
+ switch {
+ case bag.Id.Equal(oidCertBag):
+ if certificate != nil {
+ err = errors.New("pkcs12: expected exactly one certificate bag")
+ }
+
+ certsData, err := decodeCertBag(bag.Value.Bytes)
+ if err != nil {
+ return nil, nil, err
+ }
+ certs, err := x509.ParseCertificates(certsData)
+ if err != nil {
+ return nil, nil, err
+ }
+ if len(certs) != 1 {
+ err = errors.New("pkcs12: expected exactly one certificate in the certBag")
+ return nil, nil, err
+ }
+ certificate = certs[0]
+
+ case bag.Id.Equal(oidPKCS8ShroundedKeyBag):
+ if privateKey != nil {
+ err = errors.New("pkcs12: expected exactly one key bag")
+ }
+
+ if privateKey, err = decodePkcs8ShroudedKeyBag(bag.Value.Bytes, encodedPassword); err != nil {
+ return nil, nil, err
+ }
+ }
+ }
+
+ if certificate == nil {
+ return nil, nil, errors.New("pkcs12: certificate missing")
+ }
+ if privateKey == nil {
+ return nil, nil, errors.New("pkcs12: private key missing")
+ }
+
+ return
+}
+
+func getSafeContents(p12Data, password []byte) (bags []safeBag, updatedPassword []byte, err error) {
+ pfx := new(pfxPdu)
+ if err := unmarshal(p12Data, pfx); err != nil {
+ return nil, nil, errors.New("pkcs12: error reading P12 data: " + err.Error())
+ }
+
+ if pfx.Version != 3 {
+ return nil, nil, NotImplementedError("can only decode v3 PFX PDU's")
+ }
+
+ if !pfx.AuthSafe.ContentType.Equal(oidDataContentType) {
+ return nil, nil, NotImplementedError("only password-protected PFX is implemented")
+ }
+
+ // unmarshal the explicit bytes in the content for type 'data'
+ if err := unmarshal(pfx.AuthSafe.Content.Bytes, &pfx.AuthSafe.Content); err != nil {
+ return nil, nil, err
+ }
+
+ if len(pfx.MacData.Mac.Algorithm.Algorithm) == 0 {
+ return nil, nil, errors.New("pkcs12: no MAC in data")
+ }
+
+ if err := verifyMac(&pfx.MacData, pfx.AuthSafe.Content.Bytes, password); err != nil {
+ if err == ErrIncorrectPassword && len(password) == 2 && password[0] == 0 && password[1] == 0 {
+ // some implementations use an empty byte array
+ // for the empty string password try one more
+ // time with empty-empty password
+ password = nil
+ err = verifyMac(&pfx.MacData, pfx.AuthSafe.Content.Bytes, password)
+ }
+ if err != nil {
+ return nil, nil, err
+ }
+ }
+
+ var authenticatedSafe []contentInfo
+ if err := unmarshal(pfx.AuthSafe.Content.Bytes, &authenticatedSafe); err != nil {
+ return nil, nil, err
+ }
+
+ if len(authenticatedSafe) != 2 {
+ return nil, nil, NotImplementedError("expected exactly two items in the authenticated safe")
+ }
+
+ for _, ci := range authenticatedSafe {
+ var data []byte
+
+ switch {
+ case ci.ContentType.Equal(oidDataContentType):
+ if err := unmarshal(ci.Content.Bytes, &data); err != nil {
+ return nil, nil, err
+ }
+ case ci.ContentType.Equal(oidEncryptedDataContentType):
+ var encryptedData encryptedData
+ if err := unmarshal(ci.Content.Bytes, &encryptedData); err != nil {
+ return nil, nil, err
+ }
+ if encryptedData.Version != 0 {
+ return nil, nil, NotImplementedError("only version 0 of EncryptedData is supported")
+ }
+ if data, err = pbDecrypt(encryptedData.EncryptedContentInfo, password); err != nil {
+ return nil, nil, err
+ }
+ default:
+ return nil, nil, NotImplementedError("only data and encryptedData content types are supported in authenticated safe")
+ }
+
+ var safeContents []safeBag
+ if err := unmarshal(data, &safeContents); err != nil {
+ return nil, nil, err
+ }
+ bags = append(bags, safeContents...)
+ }
+
+ return bags, password, nil
+}
diff --git a/vendor/golang.org/x/crypto/pkcs12/safebags.go b/vendor/golang.org/x/crypto/pkcs12/safebags.go
new file mode 100644
index 000000000..def1f7b98
--- /dev/null
+++ b/vendor/golang.org/x/crypto/pkcs12/safebags.go
@@ -0,0 +1,57 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package pkcs12
+
+import (
+ "crypto/x509"
+ "encoding/asn1"
+ "errors"
+)
+
+var (
+ // see https://tools.ietf.org/html/rfc7292#appendix-D
+ oidCertTypeX509Certificate = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 22, 1})
+ oidPKCS8ShroundedKeyBag = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 10, 1, 2})
+ oidCertBag = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 10, 1, 3})
+)
+
+type certBag struct {
+ Id asn1.ObjectIdentifier
+ Data []byte `asn1:"tag:0,explicit"`
+}
+
+func decodePkcs8ShroudedKeyBag(asn1Data, password []byte) (privateKey interface{}, err error) {
+ pkinfo := new(encryptedPrivateKeyInfo)
+ if err = unmarshal(asn1Data, pkinfo); err != nil {
+ return nil, errors.New("pkcs12: error decoding PKCS#8 shrouded key bag: " + err.Error())
+ }
+
+ pkData, err := pbDecrypt(pkinfo, password)
+ if err != nil {
+ return nil, errors.New("pkcs12: error decrypting PKCS#8 shrouded key bag: " + err.Error())
+ }
+
+ ret := new(asn1.RawValue)
+ if err = unmarshal(pkData, ret); err != nil {
+ return nil, errors.New("pkcs12: error unmarshaling decrypted private key: " + err.Error())
+ }
+
+ if privateKey, err = x509.ParsePKCS8PrivateKey(pkData); err != nil {
+ return nil, errors.New("pkcs12: error parsing PKCS#8 private key: " + err.Error())
+ }
+
+ return privateKey, nil
+}
+
+func decodeCertBag(asn1Data []byte) (x509Certificates []byte, err error) {
+ bag := new(certBag)
+ if err := unmarshal(asn1Data, bag); err != nil {
+ return nil, errors.New("pkcs12: error decoding cert bag: " + err.Error())
+ }
+ if !bag.Id.Equal(oidCertTypeX509Certificate) {
+ return nil, NotImplementedError("only X509 certificates are supported")
+ }
+ return bag.Data, nil
+}