fix: netcup and DuckDNS.
This commit is contained in:
parent
3f044c48fa
commit
8e9b8a0953
36 changed files with 2716 additions and 650 deletions
30
Gopkg.lock
generated
30
Gopkg.lock
generated
|
@ -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"
|
||||
]
|
||||
|
|
|
@ -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 <kbd>Enter</kbd>. | 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 <kbd>Enter</kbd>. | 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`
|
||||
|
||||
|
|
408
vendor/github.com/Azure/go-autorest/autorest/azure/auth/auth.go
generated
vendored
Normal file
408
vendor/github.com/Azure/go-autorest/autorest/azure/auth/auth.go
generated
vendored
Normal file
|
@ -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
|
||||
}
|
201
vendor/github.com/dimchansky/utfbom/LICENSE
generated
vendored
Normal file
201
vendor/github.com/dimchansky/utfbom/LICENSE
generated
vendored
Normal file
|
@ -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.
|
174
vendor/github.com/dimchansky/utfbom/utfbom.go
generated
vendored
Normal file
174
vendor/github.com/dimchansky/utfbom/utfbom.go
generated
vendored
Normal file
|
@ -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
|
||||
}
|
22
vendor/github.com/edeckers/auroradnsclient/client.go
generated
vendored
22
vendor/github.com/edeckers/auroradnsclient/client.go
generated
vendored
|
@ -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
|
||||
}
|
11
vendor/github.com/edeckers/auroradnsclient/errors.go
generated
vendored
11
vendor/github.com/edeckers/auroradnsclient/errors.go
generated
vendored
|
@ -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
|
||||
}
|
75
vendor/github.com/edeckers/auroradnsclient/records.go
generated
vendored
75
vendor/github.com/edeckers/auroradnsclient/records.go
generated
vendored
|
@ -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
|
||||
}
|
31
vendor/github.com/edeckers/auroradnsclient/records/datatypes.go
generated
vendored
31
vendor/github.com/edeckers/auroradnsclient/records/datatypes.go
generated
vendored
|
@ -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 {
|
||||
}
|
19
vendor/github.com/edeckers/auroradnsclient/requests/errors/errors.go
generated
vendored
19
vendor/github.com/edeckers/auroradnsclient/requests/errors/errors.go
generated
vendored
|
@ -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
|
124
vendor/github.com/edeckers/auroradnsclient/requests/requestor.go
generated
vendored
124
vendor/github.com/edeckers/auroradnsclient/requests/requestor.go
generated
vendored
|
@ -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
|
||||
}
|
35
vendor/github.com/edeckers/auroradnsclient/tokens/generator.go
generated
vendored
35
vendor/github.com/edeckers/auroradnsclient/tokens/generator.go
generated
vendored
|
@ -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
|
||||
}
|
29
vendor/github.com/edeckers/auroradnsclient/zones.go
generated
vendored
29
vendor/github.com/edeckers/auroradnsclient/zones.go
generated
vendored
|
@ -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
|
||||
}
|
7
vendor/github.com/edeckers/auroradnsclient/zones/datatypes.go
generated
vendored
7
vendor/github.com/edeckers/auroradnsclient/zones/datatypes.go
generated
vendored
|
@ -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"`
|
||||
}
|
98
vendor/github.com/ldez/go-auroradns/auth.go
generated
vendored
Normal file
98
vendor/github.com/ldez/go-auroradns/auth.go
generated
vendored
Normal file
|
@ -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
|
||||
}
|
144
vendor/github.com/ldez/go-auroradns/client.go
generated
vendored
Normal file
144
vendor/github.com/ldez/go-auroradns/client.go
generated
vendored
Normal file
|
@ -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
|
||||
}
|
||||
}
|
91
vendor/github.com/ldez/go-auroradns/records.go
generated
vendored
Normal file
91
vendor/github.com/ldez/go-auroradns/records.go
generated
vendored
Normal file
|
@ -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
|
||||
}
|
69
vendor/github.com/ldez/go-auroradns/zones.go
generated
vendored
Normal file
69
vendor/github.com/ldez/go-auroradns/zones.go
generated
vendored
Normal file
|
@ -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
|
||||
}
|
9
vendor/github.com/xenolf/lego/acme/dns_challenge.go
generated
vendored
9
vendor/github.com/xenolf/lego/acme/dns_challenge.go
generated
vendored
|
@ -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
|
||||
|
|
45
vendor/github.com/xenolf/lego/providers/dns/auroradns/auroradns.go
generated
vendored
45
vendor/github.com/xenolf/lego/providers/dns/auroradns/auroradns.go
generated
vendored
|
@ -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")
|
||||
}
|
||||
|
|
165
vendor/github.com/xenolf/lego/providers/dns/azure/azure.go
generated
vendored
165
vendor/github.com/xenolf/lego/providers/dns/azure/azure.go
generated
vendored
|
@ -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
|
||||
}
|
||||
|
|
15
vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/client.go
generated
vendored
15
vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/client.go
generated
vendored
|
@ -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
|
||||
|
|
21
vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/dnsmadeeasy.go
generated
vendored
21
vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/dnsmadeeasy.go
generated
vendored
|
@ -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.
|
||||
|
|
2
vendor/github.com/xenolf/lego/providers/dns/dreamhost/dreamhost.go
generated
vendored
2
vendor/github.com/xenolf/lego/providers/dns/dreamhost/dreamhost.go
generated
vendored
|
@ -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
|
||||
|
|
37
vendor/github.com/xenolf/lego/providers/dns/duckdns/duckdns.go
generated
vendored
37
vendor/github.com/xenolf/lego/providers/dns/duckdns/duckdns.go
generated
vendored
|
@ -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]:]
|
||||
}
|
||||
|
|
229
vendor/github.com/xenolf/lego/providers/dns/netcup/client.go
generated
vendored
229
vendor/github.com/xenolf/lego/providers/dns/netcup/client.go
generated
vendored
|
@ -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
|
||||
}
|
||||
|
|
93
vendor/github.com/xenolf/lego/providers/dns/netcup/netcup.go
generated
vendored
93
vendor/github.com/xenolf/lego/providers/dns/netcup/netcup.go
generated
vendored
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
50
vendor/golang.org/x/crypto/pkcs12/bmp-string.go
generated
vendored
Normal file
50
vendor/golang.org/x/crypto/pkcs12/bmp-string.go
generated
vendored
Normal file
|
@ -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
|
||||
}
|
131
vendor/golang.org/x/crypto/pkcs12/crypto.go
generated
vendored
Normal file
131
vendor/golang.org/x/crypto/pkcs12/crypto.go
generated
vendored
Normal file
|
@ -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
|
||||
}
|
23
vendor/golang.org/x/crypto/pkcs12/errors.go
generated
vendored
Normal file
23
vendor/golang.org/x/crypto/pkcs12/errors.go
generated
vendored
Normal file
|
@ -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)
|
||||
}
|
271
vendor/golang.org/x/crypto/pkcs12/internal/rc2/rc2.go
generated
vendored
Normal file
271
vendor/golang.org/x/crypto/pkcs12/internal/rc2/rc2.go
generated
vendored
Normal file
|
@ -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)
|
||||
}
|
45
vendor/golang.org/x/crypto/pkcs12/mac.go
generated
vendored
Normal file
45
vendor/golang.org/x/crypto/pkcs12/mac.go
generated
vendored
Normal file
|
@ -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
|
||||
}
|
170
vendor/golang.org/x/crypto/pkcs12/pbkdf.go
generated
vendored
Normal file
170
vendor/golang.org/x/crypto/pkcs12/pbkdf.go
generated
vendored
Normal file
|
@ -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".
|
||||
}
|
346
vendor/golang.org/x/crypto/pkcs12/pkcs12.go
generated
vendored
Normal file
346
vendor/golang.org/x/crypto/pkcs12/pkcs12.go
generated
vendored
Normal file
|
@ -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
|
||||
}
|
57
vendor/golang.org/x/crypto/pkcs12/safebags.go
generated
vendored
Normal file
57
vendor/golang.org/x/crypto/pkcs12/safebags.go
generated
vendored
Normal file
|
@ -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
|
||||
}
|
Loading…
Reference in a new issue