mirror of
https://github.com/vxcontrol/rancher-letsencrypt.git
synced 2026-07-20 01:25:07 -04:00
Merge branch 'check-dns-names-on-load'
This commit is contained in:
@@ -10,6 +10,4 @@ ADD https://github.com/janeczku/rancher-letsencrypt/releases/download/${LETSENCR
|
||||
RUN tar -zxvf /tmp/rancher-letsencrypt.tar.gz -C /usr/bin \
|
||||
&& chmod +x /usr/bin/rancher-letsencrypt
|
||||
|
||||
VOLUME /etc/letsencrypt
|
||||
|
||||
ENTRYPOINT ["/usr/bin/rancher-letsencrypt"]
|
||||
+1
-5
@@ -7,8 +7,4 @@ ADD build/rancher-letsencrypt-linux-amd64 /usr/bin/rancher-letsencrypt
|
||||
|
||||
RUN chmod +x /usr/bin/rancher-letsencrypt
|
||||
|
||||
VOLUME /etc/letsencrypt
|
||||
|
||||
ENV DEBUG=true
|
||||
|
||||
ENTRYPOINT ["/usr/bin/rancher-letsencrypt"]
|
||||
ENTRYPOINT ["/usr/bin/rancher-letsencrypt", "-debug"]
|
||||
Generated
+60
-60
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"ImportPath": "github.com/janeczku/rancher-letsencrypt",
|
||||
"GoVersion": "go1.5",
|
||||
"GodepVersion": "v62",
|
||||
"GodepVersion": "v70",
|
||||
"Deps": [
|
||||
{
|
||||
"ImportPath": "github.com/Sirupsen/logrus",
|
||||
@@ -10,113 +10,113 @@
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws",
|
||||
"Comment": "v1.1.15",
|
||||
"Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
|
||||
"Comment": "v1.1.25",
|
||||
"Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/awserr",
|
||||
"Comment": "v1.1.15",
|
||||
"Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
|
||||
"Comment": "v1.1.25",
|
||||
"Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/awsutil",
|
||||
"Comment": "v1.1.15",
|
||||
"Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
|
||||
"Comment": "v1.1.25",
|
||||
"Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/client",
|
||||
"Comment": "v1.1.15",
|
||||
"Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
|
||||
"Comment": "v1.1.25",
|
||||
"Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/client/metadata",
|
||||
"Comment": "v1.1.15",
|
||||
"Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
|
||||
"Comment": "v1.1.25",
|
||||
"Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/corehandlers",
|
||||
"Comment": "v1.1.15",
|
||||
"Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
|
||||
"Comment": "v1.1.25",
|
||||
"Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/credentials",
|
||||
"Comment": "v1.1.15",
|
||||
"Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
|
||||
"Comment": "v1.1.25",
|
||||
"Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds",
|
||||
"Comment": "v1.1.15",
|
||||
"Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
|
||||
"Comment": "v1.1.25",
|
||||
"Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/defaults",
|
||||
"Comment": "v1.1.15",
|
||||
"Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
|
||||
"Comment": "v1.1.25",
|
||||
"Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/ec2metadata",
|
||||
"Comment": "v1.1.15",
|
||||
"Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
|
||||
"Comment": "v1.1.25",
|
||||
"Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/request",
|
||||
"Comment": "v1.1.15",
|
||||
"Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
|
||||
"Comment": "v1.1.25",
|
||||
"Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws/session",
|
||||
"Comment": "v1.1.15",
|
||||
"Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
|
||||
"Comment": "v1.1.25",
|
||||
"Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/private/endpoints",
|
||||
"Comment": "v1.1.15",
|
||||
"Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
|
||||
"Comment": "v1.1.25",
|
||||
"Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol",
|
||||
"Comment": "v1.1.15",
|
||||
"Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
|
||||
"Comment": "v1.1.25",
|
||||
"Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/query",
|
||||
"Comment": "v1.1.15",
|
||||
"Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
|
||||
"Comment": "v1.1.25",
|
||||
"Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/query/queryutil",
|
||||
"Comment": "v1.1.15",
|
||||
"Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
|
||||
"Comment": "v1.1.25",
|
||||
"Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/rest",
|
||||
"Comment": "v1.1.15",
|
||||
"Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
|
||||
"Comment": "v1.1.25",
|
||||
"Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/restxml",
|
||||
"Comment": "v1.1.15",
|
||||
"Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
|
||||
"Comment": "v1.1.25",
|
||||
"Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil",
|
||||
"Comment": "v1.1.15",
|
||||
"Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
|
||||
"Comment": "v1.1.25",
|
||||
"Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/private/signer/v4",
|
||||
"Comment": "v1.1.15",
|
||||
"Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
|
||||
"Comment": "v1.1.25",
|
||||
"Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/private/waiter",
|
||||
"Comment": "v1.1.15",
|
||||
"Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
|
||||
"Comment": "v1.1.25",
|
||||
"Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/service/route53",
|
||||
"Comment": "v1.1.15",
|
||||
"Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a"
|
||||
"Comment": "v1.1.25",
|
||||
"Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/go-ini/ini",
|
||||
@@ -134,7 +134,7 @@
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/miekg/dns",
|
||||
"Rev": "b9171237b0642de1d8e8004f16869970e065f46b"
|
||||
"Rev": "48ab6605c66ac797e07f615101c3e9e10e932b66"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/rancher/go-rancher/client",
|
||||
@@ -147,38 +147,38 @@
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/xenolf/lego/acme",
|
||||
"Comment": "v0.3.1-5-g9484835",
|
||||
"Rev": "948483535f53c34d144419869ecbed86251a30f6"
|
||||
"Comment": "v0.3.1-15-g30a7a8e",
|
||||
"Rev": "30a7a8e8821de3532192d1240a45e53c6204f603"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/xenolf/lego/providers/dns/cloudflare",
|
||||
"Comment": "v0.3.1-5-g9484835",
|
||||
"Rev": "948483535f53c34d144419869ecbed86251a30f6"
|
||||
"Comment": "v0.3.1-15-g30a7a8e",
|
||||
"Rev": "30a7a8e8821de3532192d1240a45e53c6204f603"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/xenolf/lego/providers/dns/digitalocean",
|
||||
"Comment": "v0.3.1-5-g9484835",
|
||||
"Rev": "948483535f53c34d144419869ecbed86251a30f6"
|
||||
"Comment": "v0.3.1-15-g30a7a8e",
|
||||
"Rev": "30a7a8e8821de3532192d1240a45e53c6204f603"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/xenolf/lego/providers/dns/dnsimple",
|
||||
"Comment": "v0.3.1-5-g9484835",
|
||||
"Rev": "948483535f53c34d144419869ecbed86251a30f6"
|
||||
"Comment": "v0.3.1-15-g30a7a8e",
|
||||
"Rev": "30a7a8e8821de3532192d1240a45e53c6204f603"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/xenolf/lego/providers/dns/dyn",
|
||||
"Comment": "v0.3.1-5-g9484835",
|
||||
"Rev": "948483535f53c34d144419869ecbed86251a30f6"
|
||||
"Comment": "v0.3.1-15-g30a7a8e",
|
||||
"Rev": "30a7a8e8821de3532192d1240a45e53c6204f603"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/xenolf/lego/providers/dns/namecheap",
|
||||
"Comment": "v0.3.1-5-g9484835",
|
||||
"Rev": "948483535f53c34d144419869ecbed86251a30f6"
|
||||
"Comment": "v0.3.1-15-g30a7a8e",
|
||||
"Rev": "30a7a8e8821de3532192d1240a45e53c6204f603"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/xenolf/lego/providers/dns/route53",
|
||||
"Comment": "v0.3.1-5-g9484835",
|
||||
"Rev": "948483535f53c34d144419869ecbed86251a30f6"
|
||||
"Comment": "v0.3.1-15-g30a7a8e",
|
||||
"Rev": "30a7a8e8821de3532192d1240a45e53c6204f603"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/crypto/ocsp",
|
||||
|
||||
+24
-2
@@ -2,6 +2,7 @@ package client
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
@@ -29,13 +30,15 @@ func (d DefaultRetryer) MaxRetries() int {
|
||||
return d.NumMaxRetries
|
||||
}
|
||||
|
||||
var seededRand = rand.New(&lockedSource{src: rand.NewSource(time.Now().UnixNano())})
|
||||
|
||||
// RetryRules returns the delay duration before retrying this request again
|
||||
func (d DefaultRetryer) RetryRules(r *request.Request) time.Duration {
|
||||
// Set the upper limit of delay in retrying at ~five minutes
|
||||
minTime := 30
|
||||
throttle := d.shouldThrottle(r)
|
||||
if throttle {
|
||||
minTime = 1000
|
||||
minTime = 500
|
||||
}
|
||||
|
||||
retryCount := r.RetryCount
|
||||
@@ -45,7 +48,7 @@ func (d DefaultRetryer) RetryRules(r *request.Request) time.Duration {
|
||||
retryCount = 8
|
||||
}
|
||||
|
||||
delay := (1 << uint(retryCount)) * (rand.Intn(30) + minTime)
|
||||
delay := (1 << uint(retryCount)) * (seededRand.Intn(minTime) + minTime)
|
||||
return time.Duration(delay) * time.Millisecond
|
||||
}
|
||||
|
||||
@@ -66,3 +69,22 @@ func (d DefaultRetryer) shouldThrottle(r *request.Request) bool {
|
||||
}
|
||||
return r.IsErrorThrottle()
|
||||
}
|
||||
|
||||
// lockedSource is a thread-safe implementation of rand.Source
|
||||
type lockedSource struct {
|
||||
lk sync.Mutex
|
||||
src rand.Source
|
||||
}
|
||||
|
||||
func (r *lockedSource) Int63() (n int64) {
|
||||
r.lk.Lock()
|
||||
n = r.src.Int63()
|
||||
r.lk.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
func (r *lockedSource) Seed(seed int64) {
|
||||
r.lk.Lock()
|
||||
r.src.Seed(seed)
|
||||
r.lk.Unlock()
|
||||
}
|
||||
|
||||
+47
@@ -100,6 +100,31 @@ type Config struct {
|
||||
// Amazon S3: Virtual Hosting of Buckets
|
||||
S3ForcePathStyle *bool
|
||||
|
||||
// Set this to `true` to disable the SDK adding the `Expect: 100-Continue`
|
||||
// header to PUT requests over 2MB of content. 100-Continue instructs the
|
||||
// HTTP client not to send the body until the service responds with a
|
||||
// `continue` status. This is useful to prevent sending the request body
|
||||
// until after the request is authenticated, and validated.
|
||||
//
|
||||
// http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectPUT.html
|
||||
//
|
||||
// 100-Continue is only enabled for Go 1.6 and above. See `http.Transport`'s
|
||||
// `ExpectContinueTimeout` for information on adjusting the continue wait timeout.
|
||||
// https://golang.org/pkg/net/http/#Transport
|
||||
//
|
||||
// You should use this flag to disble 100-Continue if you experiance issues
|
||||
// with proxies or thrid party S3 compatible services.
|
||||
S3Disable100Continue *bool
|
||||
|
||||
// Set this to `true` to enable S3 Accelerate feature. For all operations compatible
|
||||
// with S3 Accelerate will use the accelerate endpoint for requests. Requests not compatible
|
||||
// will fall back to normal S3 requests.
|
||||
//
|
||||
// The bucket must be enable for accelerate to be used with S3 client with accelerate
|
||||
// enabled. If the bucket is not enabled for accelerate an error will be returned.
|
||||
// The bucket name must be DNS compatible to also work with accelerate.
|
||||
S3UseAccelerate *bool
|
||||
|
||||
// Set this to `true` to disable the EC2Metadata client from overriding the
|
||||
// default http.Client's Timeout. This is helpful if you do not want the EC2Metadata
|
||||
// client to create a new http.Client. This options is only meaningful if you're not
|
||||
@@ -210,6 +235,20 @@ func (c *Config) WithS3ForcePathStyle(force bool) *Config {
|
||||
return c
|
||||
}
|
||||
|
||||
// WithS3Disable100Continue sets a config S3Disable100Continue value returning
|
||||
// a Config pointer for chaining.
|
||||
func (c *Config) WithS3Disable100Continue(disable bool) *Config {
|
||||
c.S3Disable100Continue = &disable
|
||||
return c
|
||||
}
|
||||
|
||||
// WithS3UseAccelerate sets a config S3UseAccelerate value returning a Config
|
||||
// pointer for chaining.
|
||||
func (c *Config) WithS3UseAccelerate(enable bool) *Config {
|
||||
c.S3UseAccelerate = &enable
|
||||
return c
|
||||
}
|
||||
|
||||
// WithEC2MetadataDisableTimeoutOverride sets a config EC2MetadataDisableTimeoutOverride value
|
||||
// returning a Config pointer for chaining.
|
||||
func (c *Config) WithEC2MetadataDisableTimeoutOverride(enable bool) *Config {
|
||||
@@ -288,6 +327,14 @@ func mergeInConfig(dst *Config, other *Config) {
|
||||
dst.S3ForcePathStyle = other.S3ForcePathStyle
|
||||
}
|
||||
|
||||
if other.S3Disable100Continue != nil {
|
||||
dst.S3Disable100Continue = other.S3Disable100Continue
|
||||
}
|
||||
|
||||
if other.S3UseAccelerate != nil {
|
||||
dst.S3UseAccelerate = other.S3UseAccelerate
|
||||
}
|
||||
|
||||
if other.EC2MetadataDisableTimeoutOverride != nil {
|
||||
dst.EC2MetadataDisableTimeoutOverride = other.EC2MetadataDisableTimeoutOverride
|
||||
}
|
||||
|
||||
+28
-20
@@ -24,30 +24,38 @@ type lener interface {
|
||||
// BuildContentLengthHandler builds the content length of a request based on the body,
|
||||
// or will use the HTTPRequest.Header's "Content-Length" if defined. If unable
|
||||
// to determine request body length and no "Content-Length" was specified it will panic.
|
||||
//
|
||||
// The Content-Length will only be aded to the request if the length of the body
|
||||
// is greater than 0. If the body is empty or the current `Content-Length`
|
||||
// header is <= 0, the header will also be stripped.
|
||||
var BuildContentLengthHandler = request.NamedHandler{Name: "core.BuildContentLengthHandler", Fn: func(r *request.Request) {
|
||||
if slength := r.HTTPRequest.Header.Get("Content-Length"); slength != "" {
|
||||
length, _ := strconv.ParseInt(slength, 10, 64)
|
||||
r.HTTPRequest.ContentLength = length
|
||||
return
|
||||
}
|
||||
|
||||
var length int64
|
||||
switch body := r.Body.(type) {
|
||||
case nil:
|
||||
length = 0
|
||||
case lener:
|
||||
length = int64(body.Len())
|
||||
case io.Seeker:
|
||||
r.BodyStart, _ = body.Seek(0, 1)
|
||||
end, _ := body.Seek(0, 2)
|
||||
body.Seek(r.BodyStart, 0) // make sure to seek back to original location
|
||||
length = end - r.BodyStart
|
||||
default:
|
||||
panic("Cannot get length of body, must provide `ContentLength`")
|
||||
|
||||
if slength := r.HTTPRequest.Header.Get("Content-Length"); slength != "" {
|
||||
length, _ = strconv.ParseInt(slength, 10, 64)
|
||||
} else {
|
||||
switch body := r.Body.(type) {
|
||||
case nil:
|
||||
length = 0
|
||||
case lener:
|
||||
length = int64(body.Len())
|
||||
case io.Seeker:
|
||||
r.BodyStart, _ = body.Seek(0, 1)
|
||||
end, _ := body.Seek(0, 2)
|
||||
body.Seek(r.BodyStart, 0) // make sure to seek back to original location
|
||||
length = end - r.BodyStart
|
||||
default:
|
||||
panic("Cannot get length of body, must provide `ContentLength`")
|
||||
}
|
||||
}
|
||||
|
||||
r.HTTPRequest.ContentLength = length
|
||||
r.HTTPRequest.Header.Set("Content-Length", fmt.Sprintf("%d", length))
|
||||
if length > 0 {
|
||||
r.HTTPRequest.ContentLength = length
|
||||
r.HTTPRequest.Header.Set("Content-Length", fmt.Sprintf("%d", length))
|
||||
} else {
|
||||
r.HTTPRequest.ContentLength = 0
|
||||
r.HTTPRequest.Header.Del("Content-Length")
|
||||
}
|
||||
}}
|
||||
|
||||
// SDKVersionUserAgentHandler is a request handler for adding the SDK Version to the user agent.
|
||||
|
||||
Generated
Vendored
+6
-142
@@ -1,153 +1,17 @@
|
||||
package corehandlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
)
|
||||
import "github.com/aws/aws-sdk-go/aws/request"
|
||||
|
||||
// ValidateParametersHandler is a request handler to validate the input parameters.
|
||||
// Validating parameters only has meaning if done prior to the request being sent.
|
||||
var ValidateParametersHandler = request.NamedHandler{Name: "core.ValidateParametersHandler", Fn: func(r *request.Request) {
|
||||
if r.ParamsFilled() {
|
||||
v := validator{errors: []string{}}
|
||||
v.validateAny(reflect.ValueOf(r.Params), "")
|
||||
|
||||
if count := len(v.errors); count > 0 {
|
||||
format := "%d validation errors:\n- %s"
|
||||
msg := fmt.Sprintf(format, count, strings.Join(v.errors, "\n- "))
|
||||
r.Error = awserr.New("InvalidParameter", msg, nil)
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
||||
// A validator validates values. Collects validations errors which occurs.
|
||||
type validator struct {
|
||||
errors []string
|
||||
}
|
||||
|
||||
// There's no validation to be done on the contents of []byte values. Prepare
|
||||
// to check validateAny arguments against that type so we can quickly skip
|
||||
// them.
|
||||
var byteSliceType = reflect.TypeOf([]byte(nil))
|
||||
|
||||
// validateAny will validate any struct, slice or map type. All validations
|
||||
// are also performed recursively for nested types.
|
||||
func (v *validator) validateAny(value reflect.Value, path string) {
|
||||
value = reflect.Indirect(value)
|
||||
if !value.IsValid() {
|
||||
if !r.ParamsFilled() {
|
||||
return
|
||||
}
|
||||
|
||||
switch value.Kind() {
|
||||
case reflect.Struct:
|
||||
v.validateStruct(value, path)
|
||||
case reflect.Slice:
|
||||
if value.Type() == byteSliceType {
|
||||
// We don't need to validate the contents of []byte.
|
||||
return
|
||||
}
|
||||
for i := 0; i < value.Len(); i++ {
|
||||
v.validateAny(value.Index(i), path+fmt.Sprintf("[%d]", i))
|
||||
}
|
||||
case reflect.Map:
|
||||
for _, n := range value.MapKeys() {
|
||||
v.validateAny(value.MapIndex(n), path+fmt.Sprintf("[%q]", n.String()))
|
||||
if v, ok := r.Params.(request.Validator); ok {
|
||||
if err := v.Validate(); err != nil {
|
||||
r.Error = err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// validateStruct will validate the struct value's fields. If the structure has
|
||||
// nested types those types will be validated also.
|
||||
func (v *validator) validateStruct(value reflect.Value, path string) {
|
||||
prefix := "."
|
||||
if path == "" {
|
||||
prefix = ""
|
||||
}
|
||||
|
||||
for i := 0; i < value.Type().NumField(); i++ {
|
||||
f := value.Type().Field(i)
|
||||
if strings.ToLower(f.Name[0:1]) == f.Name[0:1] {
|
||||
continue
|
||||
}
|
||||
fvalue := value.FieldByName(f.Name)
|
||||
|
||||
err := validateField(f, fvalue, validateFieldRequired, validateFieldMin)
|
||||
if err != nil {
|
||||
v.errors = append(v.errors, fmt.Sprintf("%s: %s", err.Error(), path+prefix+f.Name))
|
||||
continue
|
||||
}
|
||||
|
||||
v.validateAny(fvalue, path+prefix+f.Name)
|
||||
}
|
||||
}
|
||||
|
||||
type validatorFunc func(f reflect.StructField, fvalue reflect.Value) error
|
||||
|
||||
func validateField(f reflect.StructField, fvalue reflect.Value, funcs ...validatorFunc) error {
|
||||
for _, fn := range funcs {
|
||||
if err := fn(f, fvalue); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validates that a field has a valid value provided for required fields.
|
||||
func validateFieldRequired(f reflect.StructField, fvalue reflect.Value) error {
|
||||
if f.Tag.Get("required") == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch fvalue.Kind() {
|
||||
case reflect.Ptr, reflect.Slice, reflect.Map:
|
||||
if fvalue.IsNil() {
|
||||
return fmt.Errorf("missing required parameter")
|
||||
}
|
||||
default:
|
||||
if !fvalue.IsValid() {
|
||||
return fmt.Errorf("missing required parameter")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validates that if a value is provided for a field, that value must be at
|
||||
// least a minimum length.
|
||||
func validateFieldMin(f reflect.StructField, fvalue reflect.Value) error {
|
||||
minStr := f.Tag.Get("min")
|
||||
if minStr == "" {
|
||||
return nil
|
||||
}
|
||||
min, _ := strconv.ParseInt(minStr, 10, 64)
|
||||
|
||||
kind := fvalue.Kind()
|
||||
if kind == reflect.Ptr {
|
||||
if fvalue.IsNil() {
|
||||
return nil
|
||||
}
|
||||
fvalue = fvalue.Elem()
|
||||
}
|
||||
|
||||
switch fvalue.Kind() {
|
||||
case reflect.String:
|
||||
if int64(fvalue.Len()) < min {
|
||||
return fmt.Errorf("field too short, minimum length %d", min)
|
||||
}
|
||||
case reflect.Slice, reflect.Map:
|
||||
if fvalue.IsNil() {
|
||||
return nil
|
||||
}
|
||||
if int64(fvalue.Len()) < min {
|
||||
return fmt.Errorf("field too short, minimum length %d", min)
|
||||
}
|
||||
|
||||
// TODO min can also apply to number minimum value.
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}}
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -14,7 +14,7 @@ var (
|
||||
ErrStaticCredentialsEmpty = awserr.New("EmptyStaticCreds", "static credentials are empty", nil)
|
||||
)
|
||||
|
||||
// A StaticProvider is a set of credentials which are set pragmatically,
|
||||
// A StaticProvider is a set of credentials which are set programmatically,
|
||||
// and will never expire.
|
||||
type StaticProvider struct {
|
||||
Value
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
// +build go1.5
|
||||
|
||||
package request
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func copyHTTPRequest(r *http.Request, body io.ReadCloser) *http.Request {
|
||||
return &http.Request{
|
||||
URL: r.URL,
|
||||
Header: r.Header,
|
||||
Close: r.Close,
|
||||
Form: r.Form,
|
||||
PostForm: r.PostForm,
|
||||
Body: body,
|
||||
MultipartForm: r.MultipartForm,
|
||||
Host: r.Host,
|
||||
Method: r.Method,
|
||||
Proto: r.Proto,
|
||||
ContentLength: r.ContentLength,
|
||||
// Cancel will be deprecated in 1.7 and will be replaced with Context
|
||||
Cancel: r.Cancel,
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
// +build !go1.5
|
||||
|
||||
package request
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func copyHTTPRequest(r *http.Request, body io.ReadCloser) *http.Request {
|
||||
return &http.Request{
|
||||
URL: r.URL,
|
||||
Header: r.Header,
|
||||
Close: r.Close,
|
||||
Form: r.Form,
|
||||
PostForm: r.PostForm,
|
||||
Body: body,
|
||||
MultipartForm: r.MultipartForm,
|
||||
Host: r.Host,
|
||||
Method: r.Method,
|
||||
Proto: r.Proto,
|
||||
ContentLength: r.ContentLength,
|
||||
}
|
||||
}
|
||||
+16
-15
@@ -221,6 +221,13 @@ func (r *Request) Sign() error {
|
||||
//
|
||||
// Send will sign the request prior to sending. All Send Handlers will
|
||||
// be executed in the order they were set.
|
||||
//
|
||||
// Canceling a request is non-deterministic. If a request has been canceled,
|
||||
// then the transport will choose, randomly, one of the state channels during
|
||||
// reads or getting the connection.
|
||||
//
|
||||
// readLoop() and getConn(req *Request, cm connectMethod)
|
||||
// https://github.com/golang/go/blob/master/src/net/http/transport.go
|
||||
func (r *Request) Send() error {
|
||||
for {
|
||||
if aws.BoolValue(r.Retryable) {
|
||||
@@ -240,22 +247,12 @@ func (r *Request) Send() error {
|
||||
body = ioutil.NopCloser(r.Body)
|
||||
}
|
||||
|
||||
r.HTTPRequest = &http.Request{
|
||||
URL: r.HTTPRequest.URL,
|
||||
Header: r.HTTPRequest.Header,
|
||||
Close: r.HTTPRequest.Close,
|
||||
Form: r.HTTPRequest.Form,
|
||||
PostForm: r.HTTPRequest.PostForm,
|
||||
Body: body,
|
||||
MultipartForm: r.HTTPRequest.MultipartForm,
|
||||
Host: r.HTTPRequest.Host,
|
||||
Method: r.HTTPRequest.Method,
|
||||
Proto: r.HTTPRequest.Proto,
|
||||
ContentLength: r.HTTPRequest.ContentLength,
|
||||
r.HTTPRequest = copyHTTPRequest(r.HTTPRequest, body)
|
||||
if r.HTTPResponse != nil && r.HTTPResponse.Body != nil {
|
||||
// Closing response body. Since we are setting a new request to send off, this
|
||||
// response will get squashed and leaked.
|
||||
r.HTTPResponse.Body.Close()
|
||||
}
|
||||
// Closing response body. Since we are setting a new request to send off, this
|
||||
// response will get squashed and leaked.
|
||||
r.HTTPResponse.Body.Close()
|
||||
}
|
||||
|
||||
r.Sign()
|
||||
@@ -267,6 +264,10 @@ func (r *Request) Send() error {
|
||||
|
||||
r.Handlers.Send.Run(r)
|
||||
if r.Error != nil {
|
||||
if strings.Contains(r.Error.Error(), "net/http: request canceled") {
|
||||
return r.Error
|
||||
}
|
||||
|
||||
err := r.Error
|
||||
r.Handlers.Retry.Run(r)
|
||||
r.Handlers.AfterRetry.Run(r)
|
||||
|
||||
+234
@@ -0,0 +1,234 @@
|
||||
package request
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
)
|
||||
|
||||
const (
|
||||
// InvalidParameterErrCode is the error code for invalid parameters errors
|
||||
InvalidParameterErrCode = "InvalidParameter"
|
||||
// ParamRequiredErrCode is the error code for required parameter errors
|
||||
ParamRequiredErrCode = "ParamRequiredError"
|
||||
// ParamMinValueErrCode is the error code for fields with too low of a
|
||||
// number value.
|
||||
ParamMinValueErrCode = "ParamMinValueError"
|
||||
// ParamMinLenErrCode is the error code for fields without enough elements.
|
||||
ParamMinLenErrCode = "ParamMinLenError"
|
||||
)
|
||||
|
||||
// Validator provides a way for types to perform validation logic on their
|
||||
// input values that external code can use to determine if a type's values
|
||||
// are valid.
|
||||
type Validator interface {
|
||||
Validate() error
|
||||
}
|
||||
|
||||
// An ErrInvalidParams provides wrapping of invalid parameter errors found when
|
||||
// validating API operation input parameters.
|
||||
type ErrInvalidParams struct {
|
||||
// Context is the base context of the invalid parameter group.
|
||||
Context string
|
||||
errs []ErrInvalidParam
|
||||
}
|
||||
|
||||
// Add adds a new invalid parameter error to the collection of invalid
|
||||
// parameters. The context of the invalid parameter will be updated to reflect
|
||||
// this collection.
|
||||
func (e *ErrInvalidParams) Add(err ErrInvalidParam) {
|
||||
err.SetContext(e.Context)
|
||||
e.errs = append(e.errs, err)
|
||||
}
|
||||
|
||||
// AddNested adds the invalid parameter errors from another ErrInvalidParams
|
||||
// value into this collection. The nested errors will have their nested context
|
||||
// updated and base context to reflect the merging.
|
||||
//
|
||||
// Use for nested validations errors.
|
||||
func (e *ErrInvalidParams) AddNested(nestedCtx string, nested ErrInvalidParams) {
|
||||
for _, err := range nested.errs {
|
||||
err.SetContext(e.Context)
|
||||
err.AddNestedContext(nestedCtx)
|
||||
e.errs = append(e.errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Len returns the number of invalid parameter errors
|
||||
func (e ErrInvalidParams) Len() int {
|
||||
return len(e.errs)
|
||||
}
|
||||
|
||||
// Code returns the code of the error
|
||||
func (e ErrInvalidParams) Code() string {
|
||||
return InvalidParameterErrCode
|
||||
}
|
||||
|
||||
// Message returns the message of the error
|
||||
func (e ErrInvalidParams) Message() string {
|
||||
return fmt.Sprintf("%d validation error(s) found.", len(e.errs))
|
||||
}
|
||||
|
||||
// Error returns the string formatted form of the invalid parameters.
|
||||
func (e ErrInvalidParams) Error() string {
|
||||
w := &bytes.Buffer{}
|
||||
fmt.Fprintf(w, "%s: %s\n", e.Code(), e.Message())
|
||||
|
||||
for _, err := range e.errs {
|
||||
fmt.Fprintf(w, "- %s\n", err.Message())
|
||||
}
|
||||
|
||||
return w.String()
|
||||
}
|
||||
|
||||
// OrigErr returns the invalid parameters as a awserr.BatchedErrors value
|
||||
func (e ErrInvalidParams) OrigErr() error {
|
||||
return awserr.NewBatchError(
|
||||
InvalidParameterErrCode, e.Message(), e.OrigErrs())
|
||||
}
|
||||
|
||||
// OrigErrs returns a slice of the invalid parameters
|
||||
func (e ErrInvalidParams) OrigErrs() []error {
|
||||
errs := make([]error, len(e.errs))
|
||||
for i := 0; i < len(errs); i++ {
|
||||
errs[i] = e.errs[i]
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
// An ErrInvalidParam represents an invalid parameter error type.
|
||||
type ErrInvalidParam interface {
|
||||
awserr.Error
|
||||
|
||||
// Field name the error occurred on.
|
||||
Field() string
|
||||
|
||||
// SetContext updates the context of the error.
|
||||
SetContext(string)
|
||||
|
||||
// AddNestedContext updates the error's context to include a nested level.
|
||||
AddNestedContext(string)
|
||||
}
|
||||
|
||||
type errInvalidParam struct {
|
||||
context string
|
||||
nestedContext string
|
||||
field string
|
||||
code string
|
||||
msg string
|
||||
}
|
||||
|
||||
// Code returns the error code for the type of invalid parameter.
|
||||
func (e *errInvalidParam) Code() string {
|
||||
return e.code
|
||||
}
|
||||
|
||||
// Message returns the reason the parameter was invalid, and its context.
|
||||
func (e *errInvalidParam) Message() string {
|
||||
return fmt.Sprintf("%s, %s.", e.msg, e.Field())
|
||||
}
|
||||
|
||||
// Error returns the string version of the invalid parameter error.
|
||||
func (e *errInvalidParam) Error() string {
|
||||
return fmt.Sprintf("%s: %s", e.code, e.Message())
|
||||
}
|
||||
|
||||
// OrigErr returns nil, Implemented for awserr.Error interface.
|
||||
func (e *errInvalidParam) OrigErr() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Field Returns the field and context the error occurred.
|
||||
func (e *errInvalidParam) Field() string {
|
||||
field := e.context
|
||||
if len(field) > 0 {
|
||||
field += "."
|
||||
}
|
||||
if len(e.nestedContext) > 0 {
|
||||
field += fmt.Sprintf("%s.", e.nestedContext)
|
||||
}
|
||||
field += e.field
|
||||
|
||||
return field
|
||||
}
|
||||
|
||||
// SetContext updates the base context of the error.
|
||||
func (e *errInvalidParam) SetContext(ctx string) {
|
||||
e.context = ctx
|
||||
}
|
||||
|
||||
// AddNestedContext prepends a context to the field's path.
|
||||
func (e *errInvalidParam) AddNestedContext(ctx string) {
|
||||
if len(e.nestedContext) == 0 {
|
||||
e.nestedContext = ctx
|
||||
} else {
|
||||
e.nestedContext = fmt.Sprintf("%s.%s", ctx, e.nestedContext)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// An ErrParamRequired represents an required parameter error.
|
||||
type ErrParamRequired struct {
|
||||
errInvalidParam
|
||||
}
|
||||
|
||||
// NewErrParamRequired creates a new required parameter error.
|
||||
func NewErrParamRequired(field string) *ErrParamRequired {
|
||||
return &ErrParamRequired{
|
||||
errInvalidParam{
|
||||
code: ParamRequiredErrCode,
|
||||
field: field,
|
||||
msg: fmt.Sprintf("missing required field"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// An ErrParamMinValue represents a minimum value parameter error.
|
||||
type ErrParamMinValue struct {
|
||||
errInvalidParam
|
||||
min float64
|
||||
}
|
||||
|
||||
// NewErrParamMinValue creates a new minimum value parameter error.
|
||||
func NewErrParamMinValue(field string, min float64) *ErrParamMinValue {
|
||||
return &ErrParamMinValue{
|
||||
errInvalidParam: errInvalidParam{
|
||||
code: ParamMinValueErrCode,
|
||||
field: field,
|
||||
msg: fmt.Sprintf("minimum field value of %v", min),
|
||||
},
|
||||
min: min,
|
||||
}
|
||||
}
|
||||
|
||||
// MinValue returns the field's require minimum value.
|
||||
//
|
||||
// float64 is returned for both int and float min values.
|
||||
func (e *ErrParamMinValue) MinValue() float64 {
|
||||
return e.min
|
||||
}
|
||||
|
||||
// An ErrParamMinLen represents a minimum length parameter error.
|
||||
type ErrParamMinLen struct {
|
||||
errInvalidParam
|
||||
min int
|
||||
}
|
||||
|
||||
// NewErrParamMinLen creates a new minimum length parameter error.
|
||||
func NewErrParamMinLen(field string, min int) *ErrParamMinLen {
|
||||
return &ErrParamMinLen{
|
||||
errInvalidParam: errInvalidParam{
|
||||
code: ParamMinValueErrCode,
|
||||
field: field,
|
||||
msg: fmt.Sprintf("minimum field size of %v", min),
|
||||
},
|
||||
min: min,
|
||||
}
|
||||
}
|
||||
|
||||
// MinLen returns the field's required minimum length.
|
||||
func (e *ErrParamMinLen) MinLen() int {
|
||||
return e.min
|
||||
}
|
||||
+1
-1
@@ -86,7 +86,7 @@ func initHandlers(s *Session) {
|
||||
//
|
||||
// Example:
|
||||
// // Create a copy of the current session, configured for the us-west-2 region.
|
||||
// sess.Copy(&aws.Config{Region: aws.String("us-west-2"})
|
||||
// sess.Copy(&aws.Config{Region: aws.String("us-west-2")})
|
||||
func (s *Session) Copy(cfgs ...*aws.Config) *Session {
|
||||
newSession := &Session{
|
||||
Config: s.Config.Copy(cfgs...),
|
||||
|
||||
+1
-1
@@ -5,4 +5,4 @@ package aws
|
||||
const SDKName = "aws-sdk-go"
|
||||
|
||||
// SDKVersion is the version of this SDK
|
||||
const SDKVersion = "1.1.15"
|
||||
const SDKVersion = "1.1.25"
|
||||
|
||||
+37
-10
@@ -29,8 +29,8 @@ const (
|
||||
var ignoredHeaders = rules{
|
||||
blacklist{
|
||||
mapRule{
|
||||
"Content-Length": struct{}{},
|
||||
"User-Agent": struct{}{},
|
||||
"Authorization": struct{}{},
|
||||
"User-Agent": struct{}{},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -153,6 +153,7 @@ func Sign(req *request.Request) {
|
||||
}
|
||||
|
||||
req.Error = s.sign()
|
||||
req.Time = s.Time
|
||||
req.SignedHeaderVals = s.signedHeaderVals
|
||||
}
|
||||
|
||||
@@ -162,11 +163,12 @@ func (v4 *signer) sign() error {
|
||||
}
|
||||
|
||||
if v4.isRequestSigned() {
|
||||
if !v4.Credentials.IsExpired() {
|
||||
if !v4.Credentials.IsExpired() && time.Now().Before(v4.Time.Add(10*time.Minute)) {
|
||||
// If the request is already signed, and the credentials have not
|
||||
// expired yet ignore the signing request.
|
||||
// expired, and the request is not too old ignore the signing request.
|
||||
return nil
|
||||
}
|
||||
v4.Time = time.Now()
|
||||
|
||||
// The credentials have expired for this request. The current signing
|
||||
// is invalid, and needs to be request because the request will fail.
|
||||
@@ -303,13 +305,18 @@ func (v4 *signer) buildCanonicalHeaders(r rule, header http.Header) {
|
||||
if !r.IsValid(canonicalKey) {
|
||||
continue // ignored header
|
||||
}
|
||||
|
||||
lowerCaseKey := strings.ToLower(k)
|
||||
headers = append(headers, lowerCaseKey)
|
||||
|
||||
if v4.signedHeaderVals == nil {
|
||||
v4.signedHeaderVals = make(http.Header)
|
||||
}
|
||||
|
||||
lowerCaseKey := strings.ToLower(k)
|
||||
if _, ok := v4.signedHeaderVals[lowerCaseKey]; ok {
|
||||
// include additional values
|
||||
v4.signedHeaderVals[lowerCaseKey] = append(v4.signedHeaderVals[lowerCaseKey], v...)
|
||||
continue
|
||||
}
|
||||
|
||||
headers = append(headers, lowerCaseKey)
|
||||
v4.signedHeaderVals[lowerCaseKey] = v
|
||||
}
|
||||
sort.Strings(headers)
|
||||
@@ -326,11 +333,11 @@ func (v4 *signer) buildCanonicalHeaders(r rule, header http.Header) {
|
||||
headerValues[i] = "host:" + v4.Request.URL.Host
|
||||
} else {
|
||||
headerValues[i] = k + ":" +
|
||||
strings.Join(v4.Request.Header[http.CanonicalHeaderKey(k)], ",")
|
||||
strings.Join(v4.signedHeaderVals[k], ",")
|
||||
}
|
||||
}
|
||||
|
||||
v4.canonicalHeaders = strings.Join(headerValues, "\n")
|
||||
v4.canonicalHeaders = strings.Join(stripExcessSpaces(headerValues), "\n")
|
||||
}
|
||||
|
||||
func (v4 *signer) buildCanonicalString() {
|
||||
@@ -436,3 +443,23 @@ func makeSha256Reader(reader io.ReadSeeker) []byte {
|
||||
io.Copy(hash, reader)
|
||||
return hash.Sum(nil)
|
||||
}
|
||||
|
||||
func stripExcessSpaces(headerVals []string) []string {
|
||||
vals := make([]string, len(headerVals))
|
||||
for i, str := range headerVals {
|
||||
stripped := ""
|
||||
found := false
|
||||
str = strings.TrimSpace(str)
|
||||
for _, c := range str {
|
||||
if !found && c == ' ' {
|
||||
stripped += string(c)
|
||||
found = true
|
||||
} else if c != ' ' {
|
||||
stripped += string(c)
|
||||
found = false
|
||||
}
|
||||
}
|
||||
vals[i] = stripped
|
||||
}
|
||||
return vals
|
||||
}
|
||||
|
||||
+1100
-3
File diff suppressed because it is too large
Load Diff
+5
-2
@@ -12,7 +12,7 @@ can build servers and resolvers with it.
|
||||
|
||||
We try to keep the "master" branch as sane as possible and at the bleeding edge
|
||||
of standards, avoiding breaking changes wherever reasonable. We support the last
|
||||
two versions of Go, currently: 1.4 and 1.5.
|
||||
two versions of Go, currently: 1.5 and 1.6.
|
||||
|
||||
# Goals
|
||||
|
||||
@@ -48,6 +48,8 @@ A not-so-up-to-date-list-that-may-be-actually-current:
|
||||
* https://github.com/miekg/unbound
|
||||
* https://github.com/miekg/exdns
|
||||
* https://dnslookup.org
|
||||
* https://github.com/looterz/grimd
|
||||
* https://github.com/phamhongviet/serf-dns
|
||||
|
||||
Send pull request if you want to be listed here.
|
||||
|
||||
@@ -61,7 +63,7 @@ Send pull request if you want to be listed here.
|
||||
* Server side programming (mimicking the net/http package);
|
||||
* Client side programming;
|
||||
* DNSSEC: signing, validating and key generation for DSA, RSA and ECDSA;
|
||||
* EDNS0, NSID;
|
||||
* EDNS0, NSID, Cookies;
|
||||
* AXFR/IXFR;
|
||||
* TSIG, SIG(0);
|
||||
* DNS over TLS: optional encrypted connection between client and server;
|
||||
@@ -140,6 +142,7 @@ Example programs can be found in the `github.com/miekg/exdns` repository.
|
||||
* 7553 - URI record
|
||||
* xxxx - EDNS0 DNS Update Lease (draft)
|
||||
* yyyy - DNS over TLS: Initiation and Performance Considerations (draft)
|
||||
* xxxx - Domain Name System (DNS) Cookies (draft-ietf-dnsop-cookies)
|
||||
|
||||
## Loosely based upon
|
||||
|
||||
|
||||
+21
-5
@@ -28,9 +28,10 @@ type Client struct {
|
||||
Net string // if "tcp" or "tcp-tls" (DNS over TLS) a TCP query will be initiated, otherwise an UDP one (default is "" for UDP)
|
||||
UDPSize uint16 // minimum receive buffer for UDP messages
|
||||
TLSConfig *tls.Config // TLS connection configuration
|
||||
DialTimeout time.Duration // net.DialTimeout, defaults to 2 seconds
|
||||
ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds
|
||||
WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds
|
||||
Timeout time.Duration // a cumulative timeout for dial, write and read, defaults to 0 (disabled) - overrides DialTimeout, ReadTimeout and WriteTimeout when non-zero
|
||||
DialTimeout time.Duration // net.DialTimeout, defaults to 2 seconds - overridden by Timeout when that value is non-zero
|
||||
ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero
|
||||
WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero
|
||||
TsigSecret map[string]string // secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be fully qualified
|
||||
SingleInflight bool // if true suppress multiple outstanding queries for the same Qname, Qtype and Qclass
|
||||
group singleflight
|
||||
@@ -129,6 +130,9 @@ func (c *Client) Exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err erro
|
||||
}
|
||||
|
||||
func (c *Client) dialTimeout() time.Duration {
|
||||
if c.Timeout != 0 {
|
||||
return c.Timeout
|
||||
}
|
||||
if c.DialTimeout != 0 {
|
||||
return c.DialTimeout
|
||||
}
|
||||
@@ -170,6 +174,11 @@ func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err erro
|
||||
}
|
||||
}
|
||||
|
||||
var deadline time.Time
|
||||
if c.Timeout != 0 {
|
||||
deadline = time.Now().Add(c.Timeout)
|
||||
}
|
||||
|
||||
if tls {
|
||||
co, err = DialTimeoutWithTLS(network, a, c.TLSConfig, c.dialTimeout())
|
||||
} else {
|
||||
@@ -192,12 +201,12 @@ func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err erro
|
||||
}
|
||||
|
||||
co.TsigSecret = c.TsigSecret
|
||||
co.SetWriteDeadline(time.Now().Add(c.writeTimeout()))
|
||||
co.SetWriteDeadline(deadlineOrTimeout(deadline, c.writeTimeout()))
|
||||
if err = co.WriteMsg(m); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
co.SetReadDeadline(time.Now().Add(c.readTimeout()))
|
||||
co.SetReadDeadline(deadlineOrTimeout(deadline, c.readTimeout()))
|
||||
r, err = co.ReadMsg()
|
||||
if err == nil && r.Id != m.Id {
|
||||
err = ErrId
|
||||
@@ -434,3 +443,10 @@ func DialTimeoutWithTLS(network, address string, tlsConfig *tls.Config, timeout
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func deadlineOrTimeout(deadline time.Time, timeout time.Duration) time.Time {
|
||||
if deadline.IsZero() {
|
||||
return time.Now().Add(timeout)
|
||||
}
|
||||
return deadline
|
||||
}
|
||||
|
||||
+9
-5
@@ -142,9 +142,13 @@ func (dns *Msg) IsTsig() *TSIG {
|
||||
// record in the additional section will do. It returns the OPT record
|
||||
// found or nil.
|
||||
func (dns *Msg) IsEdns0() *OPT {
|
||||
for _, r := range dns.Extra {
|
||||
if r.Header().Rrtype == TypeOPT {
|
||||
return r.(*OPT)
|
||||
// EDNS0 is at the end of the additional section, start there.
|
||||
// We might want to change this to *only* look at the last two
|
||||
// records. So we see TSIG and/or OPT - this a slightly bigger
|
||||
// change though.
|
||||
for i := len(dns.Extra) - 1; i >= 0; i-- {
|
||||
if dns.Extra[i].Header().Rrtype == TypeOPT {
|
||||
return dns.Extra[i].(*OPT)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -163,8 +167,8 @@ func IsDomainName(s string) (labels int, ok bool) {
|
||||
return labels, err == nil
|
||||
}
|
||||
|
||||
// IsSubDomain checks if child is indeed a child of the parent. Both child and
|
||||
// parent are *not* downcased before doing the comparison.
|
||||
// IsSubDomain checks if child is indeed a child of the parent. If child and parent
|
||||
// are the same domain true is returned as well.
|
||||
func IsSubDomain(parent, child string) bool {
|
||||
// Entire child is contained in parent
|
||||
return CompareDomainName(parent, child) == CountLabel(parent)
|
||||
|
||||
+36
-3
@@ -17,6 +17,7 @@ const (
|
||||
EDNS0N3U = 0x7 // NSEC3 Hash Understood
|
||||
EDNS0SUBNET = 0x8 // client-subnet (RFC6891)
|
||||
EDNS0EXPIRE = 0x9 // EDNS0 expire
|
||||
EDNS0COOKIE = 0xa // EDNS0 Cookie
|
||||
EDNS0SUBNETDRAFT = 0x50fa // Don't use! Use EDNS0SUBNET
|
||||
EDNS0LOCALSTART = 0xFDE9 // Beginning of range reserved for local/experimental use (RFC6891)
|
||||
EDNS0LOCALEND = 0xFFFE // End of range reserved for local/experimental use (RFC6891)
|
||||
@@ -56,6 +57,8 @@ func (rr *OPT) String() string {
|
||||
if o.(*EDNS0_SUBNET).DraftOption {
|
||||
s += " (draft)"
|
||||
}
|
||||
case *EDNS0_COOKIE:
|
||||
s += "\n; COOKIE: " + o.String()
|
||||
case *EDNS0_UL:
|
||||
s += "\n; UPDATE LEASE: " + o.String()
|
||||
case *EDNS0_LLQ:
|
||||
@@ -96,13 +99,16 @@ func (rr *OPT) SetVersion(v uint8) {
|
||||
}
|
||||
|
||||
// ExtendedRcode returns the EDNS extended RCODE field (the upper 8 bits of the TTL).
|
||||
func (rr *OPT) ExtendedRcode() uint8 {
|
||||
return uint8((rr.Hdr.Ttl & 0xFF000000) >> 24)
|
||||
func (rr *OPT) ExtendedRcode() int {
|
||||
return int((rr.Hdr.Ttl&0xFF000000)>>24) + 15
|
||||
}
|
||||
|
||||
// SetExtendedRcode sets the EDNS extended RCODE field.
|
||||
func (rr *OPT) SetExtendedRcode(v uint8) {
|
||||
rr.Hdr.Ttl = rr.Hdr.Ttl&0x00FFFFFF | (uint32(v) << 24)
|
||||
if v < RcodeBadVers { // Smaller than 16.. Use the 4 bits you have!
|
||||
return
|
||||
}
|
||||
rr.Hdr.Ttl = rr.Hdr.Ttl&0x00FFFFFF | (uint32(v-15) << 24)
|
||||
}
|
||||
|
||||
// UDPSize returns the UDP buffer size.
|
||||
@@ -283,6 +289,33 @@ func (e *EDNS0_SUBNET) String() (s string) {
|
||||
return
|
||||
}
|
||||
|
||||
// The Cookie EDNS0 option
|
||||
//
|
||||
// o := new(dns.OPT)
|
||||
// o.Hdr.Name = "."
|
||||
// o.Hdr.Rrtype = dns.TypeOPT
|
||||
// e := new(dns.EDNS0_COOKIE)
|
||||
// e.Code = dns.EDNS0COOKIE
|
||||
// e.Cookie = "24a5ac.."
|
||||
// o.Option = append(o.Option, e)
|
||||
//
|
||||
type EDNS0_COOKIE struct {
|
||||
Code uint16 // Always EDNS0COOKIE
|
||||
Cookie string // Hex-encoded cookie data
|
||||
}
|
||||
|
||||
func (e *EDNS0_COOKIE) pack() ([]byte, error) {
|
||||
h, err := hex.DecodeString(e.Cookie)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return h, nil
|
||||
}
|
||||
|
||||
func (e *EDNS0_COOKIE) Option() uint16 { return EDNS0COOKIE }
|
||||
func (e *EDNS0_COOKIE) unpack(b []byte) error { e.Cookie = hex.EncodeToString(b); return nil }
|
||||
func (e *EDNS0_COOKIE) String() string { return e.Cookie }
|
||||
|
||||
// The EDNS0_UL (Update Lease) (draft RFC) option is used to tell the server to set
|
||||
// an expiration on an update RR. This is helpful for clients that cannot clean
|
||||
// up after themselves. This is a draft RFC and more information can be found at
|
||||
|
||||
+1
-1
@@ -107,7 +107,7 @@ func CountLabel(s string) (labels int) {
|
||||
|
||||
// Split splits a name s into its label indexes.
|
||||
// www.miek.nl. returns []int{0, 4, 9}, www.miek.nl also returns []int{0, 4, 9}.
|
||||
// The root name (.) returns nil. Also see SplitDomainName.
|
||||
// The root name (.) returns nil. Also see SplitDomainName.
|
||||
// s must be a syntactically valid domain name.
|
||||
func Split(s string) []int {
|
||||
if s == "." {
|
||||
|
||||
+13
-1
@@ -596,7 +596,7 @@ func packStructValue(val reflect.Value, msg []byte, off int, compression map[str
|
||||
for j := 0; j < val.Field(i).Len(); j++ {
|
||||
element := val.Field(i).Index(j).Interface()
|
||||
b, e := element.(EDNS0).pack()
|
||||
if e != nil {
|
||||
if e != nil || off+3 > lenmsg {
|
||||
return lenmsg, &Error{err: "overflow packing opt"}
|
||||
}
|
||||
// Option code
|
||||
@@ -953,6 +953,13 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er
|
||||
if code == EDNS0SUBNETDRAFT {
|
||||
e.DraftOption = true
|
||||
}
|
||||
case EDNS0COOKIE:
|
||||
e := new(EDNS0_COOKIE)
|
||||
if err := e.unpack(msg[off1 : off1+int(optlen)]); err != nil {
|
||||
return lenmsg, err
|
||||
}
|
||||
edns = append(edns, e)
|
||||
off = off1 + int(optlen)
|
||||
case EDNS0UL:
|
||||
e := new(EDNS0_UL)
|
||||
if err := e.unpack(msg[off1 : off1+int(optlen)]); err != nil {
|
||||
@@ -1896,6 +1903,11 @@ func Copy(r RR) RR {
|
||||
return r1
|
||||
}
|
||||
|
||||
// Len returns the length (in octets) of the uncompressed RR in wire format.
|
||||
func Len(r RR) int {
|
||||
return r.len()
|
||||
}
|
||||
|
||||
// Copy returns a new *Msg which is a deep-copy of dns.
|
||||
func (dns *Msg) Copy() *Msg {
|
||||
return dns.CopyTo(new(Msg))
|
||||
|
||||
+4
-1
@@ -229,7 +229,10 @@ func main() {
|
||||
if sl, ok := st.Field(i).Type().(*types.Slice); ok {
|
||||
t := sl.Underlying().String()
|
||||
t = strings.TrimPrefix(t, "[]")
|
||||
t = strings.TrimPrefix(t, "github.com/miekg/dns.")
|
||||
if strings.Contains(t, ".") {
|
||||
splits := strings.Split(t, ".")
|
||||
t = splits[len(splits)-1]
|
||||
}
|
||||
fmt.Fprintf(b, "%s := make([]%s, len(rr.%s)); copy(%s, rr.%s)\n",
|
||||
f, t, f, f, f)
|
||||
fields = append(fields, f)
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// +build !windows
|
||||
// +build !windows,!plan9
|
||||
|
||||
package dns
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// +build !linux
|
||||
// +build !linux,!plan9
|
||||
|
||||
package dns
|
||||
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
func setUDPSocketOptions(conn *net.UDPConn) error { return nil }
|
||||
|
||||
// SessionUDP holds the remote address and the associated
|
||||
// out-of-band data.
|
||||
type SessionUDP struct {
|
||||
raddr *net.UDPAddr
|
||||
context []byte
|
||||
}
|
||||
|
||||
// RemoteAddr returns the remote network address.
|
||||
func (s *SessionUDP) RemoteAddr() net.Addr { return s.raddr }
|
||||
|
||||
// ReadFromSessionUDP acts just like net.UDPConn.ReadFrom(), but returns a session object instead of a
|
||||
// net.UDPAddr.
|
||||
func ReadFromSessionUDP(conn *net.UDPConn, b []byte) (int, *SessionUDP, error) {
|
||||
oob := make([]byte, 40)
|
||||
n, oobn, _, raddr, err := conn.ReadMsgUDP(b, oob)
|
||||
if err != nil {
|
||||
return n, nil, err
|
||||
}
|
||||
return n, &SessionUDP{raddr, oob[:oobn]}, err
|
||||
}
|
||||
|
||||
// WriteToSessionUDP acts just like net.UDPConn.WritetTo(), but uses a *SessionUDP instead of a net.Addr.
|
||||
func WriteToSessionUDP(conn *net.UDPConn, b []byte, session *SessionUDP) (int, error) {
|
||||
n, _, err := conn.WriteMsgUDP(b, session.context, session.raddr)
|
||||
return n, err
|
||||
}
|
||||
+2
-2
@@ -162,8 +162,8 @@ func (t *Transfer) inIxfr(id uint16, c chan *Envelope) {
|
||||
//
|
||||
// ch := make(chan *dns.Envelope)
|
||||
// tr := new(dns.Transfer)
|
||||
// tr.Out(w, r, ch)
|
||||
// c <- &dns.Envelope{RR: []dns.RR{soa, rr1, rr2, rr3, soa}}
|
||||
// go tr.Out(w, r, ch)
|
||||
// ch <- &dns.Envelope{RR: []dns.RR{soa, rr1, rr2, rr3, soa}}
|
||||
// close(ch)
|
||||
// w.Hijack()
|
||||
// // w.Close() // Client closes connection
|
||||
|
||||
+6
-2
@@ -26,6 +26,9 @@ var RecursiveNameservers = []string{
|
||||
"google-public-dns-b.google.com:53",
|
||||
}
|
||||
|
||||
// DNSTimeout is used to override the default DNS timeout of 10 seconds.
|
||||
var DNSTimeout = 10 * time.Second
|
||||
|
||||
// DNS01Record returns a DNS record which will fulfill the `dns-01` challenge
|
||||
func DNS01Record(domain, keyAuth string) (fqdn string, value string, ttl int) {
|
||||
keyAuthShaBytes := sha256.Sum256([]byte(keyAuth))
|
||||
@@ -161,10 +164,11 @@ func dnsQuery(fqdn string, rtype uint16, nameservers []string, recursive bool) (
|
||||
// Will retry the request based on the number of servers (n+1)
|
||||
for i := 1; i <= len(nameservers)+1; i++ {
|
||||
ns := nameservers[i%len(nameservers)]
|
||||
in, err = dns.Exchange(m, ns)
|
||||
udp := &dns.Client{Net: "udp", Timeout: DNSTimeout}
|
||||
in, _, err = udp.Exchange(m, ns)
|
||||
|
||||
if err == dns.ErrTruncated {
|
||||
tcp := &dns.Client{Net: "tcp"}
|
||||
tcp := &dns.Client{Net: "tcp", Timeout: DNSTimeout}
|
||||
// If the TCP request suceeds, the err will reset to nil
|
||||
in, _, err = tcp.Exchange(m, ns)
|
||||
}
|
||||
|
||||
+4
-1
@@ -14,8 +14,11 @@ import (
|
||||
// UserAgent (if non-empty) will be tacked onto the User-Agent string in requests.
|
||||
var UserAgent string
|
||||
|
||||
// HTTPTimeout is used to override the default HTTP timeout of 10 seconds.
|
||||
var HTTPTimeout = 10 * time.Second
|
||||
|
||||
// defaultClient is an HTTP client with a reasonable timeout value.
|
||||
var defaultClient = http.Client{Timeout: 10 * time.Second}
|
||||
var defaultClient = http.Client{Timeout: HTTPTimeout}
|
||||
|
||||
const (
|
||||
// defaultGoUserAgent is the Go HTTP package user agent string. Too
|
||||
|
||||
Generated
Vendored
+13
-5
@@ -156,8 +156,9 @@ func (c *DNSProvider) findTxtRecord(fqdn string) (*cloudFlareRecord, error) {
|
||||
func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawMessage, error) {
|
||||
// APIError contains error details for failed requests
|
||||
type APIError struct {
|
||||
Code int `json:"code,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Code int `json:"code,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
ErrorChain []APIError `json:"error_chain,omitempty"`
|
||||
}
|
||||
|
||||
// APIResponse represents a response from CloudFlare API
|
||||
@@ -179,7 +180,7 @@ func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawM
|
||||
client := http.Client{Timeout: 30 * time.Second}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error querying API -> %v", err)
|
||||
return nil, fmt.Errorf("Error querying Cloudflare API -> %v", err)
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
@@ -192,9 +193,16 @@ func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawM
|
||||
|
||||
if !r.Success {
|
||||
if len(r.Errors) > 0 {
|
||||
return nil, fmt.Errorf("API error -> %d: %s", r.Errors[0].Code, r.Errors[0].Message)
|
||||
errStr := ""
|
||||
for _, apiErr := range r.Errors {
|
||||
errStr += fmt.Sprintf("\t Error: %d: %s", apiErr.Code, apiErr.Message)
|
||||
for _, chainErr := range apiErr.ErrorChain {
|
||||
errStr += fmt.Sprintf("<- %d: %s", chainErr.Code, chainErr.Message)
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("Cloudflare API Error \n%s", errStr)
|
||||
}
|
||||
return nil, fmt.Errorf("API error")
|
||||
return nil, fmt.Errorf("Cloudflare API error")
|
||||
}
|
||||
|
||||
return r.Result, nil
|
||||
|
||||
@@ -22,7 +22,7 @@ help:
|
||||
@echo "make clean - Duh!"
|
||||
@echo "make release - tag with version and trigger CI release build"
|
||||
@echo "make image - build Docker image"
|
||||
@echo "make dockerhub - build and push image to Docker Hub"
|
||||
@echo "make dockerhub - build and push dev image to Docker Hub"
|
||||
@echo "make version - show app version"
|
||||
|
||||
build: build-dir
|
||||
@@ -48,8 +48,8 @@ clean:
|
||||
rm -fr ./dist
|
||||
|
||||
dockerhub: image
|
||||
@echo "Pushing $(DOCKER_IMAGE):$(VERSION)"
|
||||
docker push $(DOCKER_IMAGE):$(VERSION)
|
||||
@echo "Pushing $(DOCKER_IMAGE):dev-$(SHA)"
|
||||
docker push $(DOCKER_IMAGE):dev-$(SHA)
|
||||
|
||||
image:
|
||||
docker build -t $(DOCKER_IMAGE):dev-$(SHA) -f Dockerfile.dev .
|
||||
|
||||
+7
-8
@@ -9,13 +9,6 @@ general:
|
||||
- "dist/*.tar.gz"
|
||||
- "dist/*.sha512"
|
||||
|
||||
checkout:
|
||||
post:
|
||||
- git config user.name "Circle CI Build Bot"
|
||||
- git config user.email "no-reply@circleci.com"
|
||||
- git remote -v
|
||||
- git fetch --tags
|
||||
|
||||
dependencies:
|
||||
pre:
|
||||
- go version
|
||||
@@ -25,6 +18,12 @@ dependencies:
|
||||
- make deps
|
||||
override:
|
||||
- echo "deps are vendored don't run go build"
|
||||
post:
|
||||
- git config user.name "Circle CI Build Bot"
|
||||
- git config user.email "no-reply@circleci.com"
|
||||
- git remote -v
|
||||
- git fetch --tags
|
||||
- git describe --abbrev=0 --tags
|
||||
|
||||
test:
|
||||
pre:
|
||||
@@ -41,7 +40,7 @@ deployment:
|
||||
commands:
|
||||
- make ci-release
|
||||
- curl --data "docker_tag=latest" -X POST ${DOCKER_HUB_URI}/${DOCKER_HUB_TOKEN}/
|
||||
- sleep 30
|
||||
- sleep 60
|
||||
- curl --data "source_type=Tag&source_name=${CIRCLE_TAG}" -X POST ${DOCKER_HUB_URI}/${DOCKER_HUB_TOKEN}/
|
||||
- echo `git name-rev --tags --name-only $(git rev-parse HEAD)`
|
||||
- echo tag ${CIRCLE_TAG}
|
||||
|
||||
+25
-45
@@ -15,36 +15,35 @@ const (
|
||||
CERT_DESCRIPTION = "Created by Let's Encrypt Certificate Manager"
|
||||
ISSUER_PRODUCTION = "Let's Encrypt"
|
||||
ISSUER_STAGING = "fake CA"
|
||||
PREFIX_PROD = "[LE] "
|
||||
PREFIX_STAGING = "[LE-TESTING] "
|
||||
)
|
||||
|
||||
type Context struct {
|
||||
Acme *letsencrypt.Client
|
||||
Rancher *rancher.Client
|
||||
|
||||
Domains []string
|
||||
RenewalTime int
|
||||
CertificateName string
|
||||
Domains []string
|
||||
RenewalTime int
|
||||
|
||||
ExpiryDate time.Time
|
||||
RancherCertId string
|
||||
RancherCertName string
|
||||
ExpiryDate time.Time
|
||||
RancherCertId string
|
||||
|
||||
Debug bool
|
||||
}
|
||||
|
||||
// InitContext initializes the application context from environmental variables
|
||||
func (c *Context) InitContext() {
|
||||
var err error
|
||||
c.Debug = debug
|
||||
cattleUrl := getEnvOption("CATTLE_URL", true)
|
||||
cattleApiKey := getEnvOption("CATTLE_ACCESS_KEY", true)
|
||||
cattleSecretKey := getEnvOption("CATTLE_SECRET_KEY", true)
|
||||
debugParam := getEnvOption("DEBUG", false)
|
||||
eulaParam := getEnvOption("EULA", false)
|
||||
apiVerParam := getEnvOption("API_VERSION", true)
|
||||
emailParam := getEnvOption("EMAIL", true)
|
||||
domainParam := getEnvOption("DOMAINS", true)
|
||||
keyTypeParam := getEnvOption("PUBLIC_KEY_TYPE", true)
|
||||
certNameParam := getEnvOption("CERT_NAME", false)
|
||||
certNameParam := getEnvOption("CERT_NAME", true)
|
||||
timeParam := getEnvOption("RENEWAL_TIME", true)
|
||||
providerParam := getEnvOption("PROVIDER", true)
|
||||
|
||||
@@ -52,38 +51,20 @@ func (c *Context) InitContext() {
|
||||
logrus.Fatalf("Terms of service were not accepted")
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
c.Domains = listToSlice(domainParam)
|
||||
if len(c.Domains) == 0 {
|
||||
logrus.Fatalf("Invalid value for DOMAINS: %s", domainParam)
|
||||
}
|
||||
|
||||
c.CertificateName = certNameParam
|
||||
c.RenewalTime, err = strconv.Atoi(timeParam)
|
||||
if err != nil || c.RenewalTime < 0 || c.RenewalTime > 23 {
|
||||
logrus.Fatalf("Invalid value for RENEWAL_TIME: %s", timeParam)
|
||||
}
|
||||
|
||||
var certAutoName string
|
||||
|
||||
switch apiVerParam {
|
||||
case "Production":
|
||||
certAutoName = PREFIX_PROD + c.Domains[0]
|
||||
case "Sandbox":
|
||||
certAutoName = PREFIX_STAGING + c.Domains[0]
|
||||
default:
|
||||
logrus.Fatalf("Invalid value for API_VERSION: %s", apiVerParam)
|
||||
}
|
||||
|
||||
apiVersion := letsencrypt.ApiVersion(apiVerParam)
|
||||
keyType := letsencrypt.KeyType(keyTypeParam)
|
||||
|
||||
if len(certNameParam) != 0 {
|
||||
c.RancherCertName = certNameParam
|
||||
} else {
|
||||
c.RancherCertName = certAutoName
|
||||
}
|
||||
|
||||
c.Rancher, err = rancher.NewClient(cattleUrl, cattleApiKey, cattleSecretKey)
|
||||
if err != nil {
|
||||
logrus.Fatalf("Could not connect to Rancher API: %v", err)
|
||||
@@ -91,18 +72,18 @@ func (c *Context) InitContext() {
|
||||
|
||||
providerOpts := letsencrypt.ProviderOpts{
|
||||
Provider: letsencrypt.DnsProvider(providerParam),
|
||||
CloudflareEmail: os.Getenv("CLOUDFLARE_EMAIL"),
|
||||
CloudflareKey: os.Getenv("CLOUDFLARE_KEY"),
|
||||
DoAccessToken: os.Getenv("DO_ACCESS_TOKEN"),
|
||||
AwsAccessKey: os.Getenv("AWS_ACCESS_KEY"),
|
||||
AwsSecretKey: os.Getenv("AWS_SECRET_KEY"),
|
||||
DNSimpleEmail: os.Getenv("DNSIMPLE_EMAIL"),
|
||||
DNSimpleKey: os.Getenv("DNSIMPLE_KEY"),
|
||||
DynCustomerName: os.Getenv("DYN_CUSTOMER_NAME"),
|
||||
DynUserName: os.Getenv("DYN_USER_NAME"),
|
||||
DynPassword: os.Getenv("DYN_PASSWORD"),
|
||||
NamecheapApiUser: os.Getenv("NAMECHEAP_API_USER"),
|
||||
NamecheapApiKey: os.Getenv("NAMECHEAP_API_KEY"),
|
||||
CloudflareEmail: getEnvOption("CLOUDFLARE_EMAIL", false),
|
||||
CloudflareKey: getEnvOption("CLOUDFLARE_KEY", false),
|
||||
DoAccessToken: getEnvOption("DO_ACCESS_TOKEN", false),
|
||||
AwsAccessKey: getEnvOption("AWS_ACCESS_KEY", false),
|
||||
AwsSecretKey: getEnvOption("AWS_SECRET_KEY", false),
|
||||
DNSimpleEmail: getEnvOption("DNSIMPLE_EMAIL", false),
|
||||
DNSimpleKey: getEnvOption("DNSIMPLE_KEY", false),
|
||||
DynCustomerName: getEnvOption("DYN_CUSTOMER_NAME", false),
|
||||
DynUserName: getEnvOption("DYN_USER_NAME", false),
|
||||
DynPassword: getEnvOption("DYN_PASSWORD", false),
|
||||
NamecheapApiUser: getEnvOption("NAMECHEAP_API_USER", false),
|
||||
NamecheapApiKey: getEnvOption("NAMECHEAP_API_KEY", false),
|
||||
}
|
||||
|
||||
c.Acme, err = letsencrypt.NewClient(emailParam, keyType, apiVersion, providerOpts)
|
||||
@@ -110,10 +91,9 @@ func (c *Context) InitContext() {
|
||||
logrus.Fatalf("LetsEncrypt client: %v", err)
|
||||
}
|
||||
|
||||
// Enable debug/test mode
|
||||
if strings.EqualFold(debugParam, "true") {
|
||||
// Enable debug mode
|
||||
if c.Debug {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
c.Debug = true
|
||||
c.Acme.EnableDebug()
|
||||
}
|
||||
}
|
||||
@@ -123,10 +103,10 @@ func getEnvOption(name string, required bool) string {
|
||||
if required && len(val) == 0 {
|
||||
logrus.Fatalf("Required environment variable not set: %s", name)
|
||||
}
|
||||
return val
|
||||
return strings.TrimSpace(val)
|
||||
}
|
||||
|
||||
func listToSlice(str string) []string {
|
||||
str = strings.Join(strings.Fields(str), "")
|
||||
str = strings.ToLower(strings.Join(strings.Fields(str), ""))
|
||||
return strings.Split(str, ",")
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ func (a *Account) GetRegistration() *lego.RegistrationResource {
|
||||
}
|
||||
|
||||
func accountPath(email string, apiVer ApiVersion) string {
|
||||
path := path.Join(ConfigDir, strings.ToLower(string(apiVer)), "accounts", email)
|
||||
path := path.Join(StorageDir, strings.ToLower(string(apiVer)), "accounts", email)
|
||||
maybeCreatePath(path)
|
||||
return path
|
||||
}
|
||||
|
||||
+97
-60
@@ -7,6 +7,7 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -15,7 +16,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
ConfigDir = "/etc/letsencrypt"
|
||||
StorageDir = "/etc/letsencrypt"
|
||||
ProductionApiUri = "https://acme-v01.api.letsencrypt.org/directory"
|
||||
StagingApiUri = "https://acme-staging.api.letsencrypt.org/directory"
|
||||
)
|
||||
@@ -46,8 +47,9 @@ const (
|
||||
// Domain string
|
||||
type AcmeCertificate struct {
|
||||
lego.CertificateResource
|
||||
DnsNames string `json:"dnsNames"`
|
||||
ExpiryDate time.Time `json:"expiryDate"`
|
||||
SerialNumber string `json:"serialnumber"`
|
||||
SerialNumber string `json:"serialNumber"`
|
||||
}
|
||||
|
||||
// Client represents a Lets Encrypt client
|
||||
@@ -81,7 +83,7 @@ func NewClient(email string, kt KeyType, apiVer ApiVersion, provider ProviderOpt
|
||||
case Sandbox:
|
||||
serverUri = StagingApiUri
|
||||
default:
|
||||
return nil, fmt.Errorf("Invalid LE API version: %s", string(apiVer))
|
||||
return nil, fmt.Errorf("Invalid API version: %s", string(apiVer))
|
||||
}
|
||||
|
||||
acc, err := NewAccount(email, apiVer, keyType)
|
||||
@@ -143,82 +145,80 @@ func (c *Client) EnableDebug() {
|
||||
}
|
||||
|
||||
// Issue obtains a new SAN certificate from the Lets Encrypt CA
|
||||
func (c *Client) Issue(domains []string) (*AcmeCertificate, map[string]error) {
|
||||
cert, failures := c.client.ObtainCertificate(domains, true, nil)
|
||||
func (c *Client) Issue(certName string, domains []string) (*AcmeCertificate, map[string]error) {
|
||||
certRes, failures := c.client.ObtainCertificate(domains, true, nil)
|
||||
if len(failures) > 0 {
|
||||
return nil, failures
|
||||
}
|
||||
|
||||
expirydate, _ := lego.GetPEMCertExpiration(cert.Certificate)
|
||||
serialnum, _ := getPEMCertSerialNo(cert.Certificate)
|
||||
|
||||
acmeCert := AcmeCertificate{
|
||||
CertificateResource: cert,
|
||||
ExpiryDate: expirydate,
|
||||
SerialNumber: serialnum,
|
||||
dnsNames := dnsNamesIdentifier(domains)
|
||||
acmeCert, err := c.saveCertificate(certName, dnsNames, certRes)
|
||||
if err != nil {
|
||||
logrus.Fatalf("Error saving certificate '%s': %v", certName, err)
|
||||
}
|
||||
|
||||
c.saveCertificate(acmeCert)
|
||||
return &acmeCert, nil
|
||||
return acmeCert, nil
|
||||
}
|
||||
|
||||
// Renew obtains a renewed SAN certificate from the Lets Encrypt CA
|
||||
func (c *Client) Renew(domains []string) (*AcmeCertificate, error) {
|
||||
domain := domains[0]
|
||||
currentCert, err := c.loadCertificate(domain)
|
||||
// Renew renewes the given stored certificate
|
||||
func (c *Client) Renew(certName string) (*AcmeCertificate, error) {
|
||||
acmeCert, err := c.loadCertificateByName(certName)
|
||||
if err != nil {
|
||||
logrus.Fatalf(err.Error())
|
||||
return nil, fmt.Errorf("Error loading certificate '%s': %v", certName, err)
|
||||
}
|
||||
certRes := currentCert.CertificateResource
|
||||
|
||||
newCert, err := c.client.RenewCertificate(certRes, true)
|
||||
certRes := acmeCert.CertificateResource
|
||||
newCertRes, err := c.client.RenewCertificate(certRes, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
expirydate, _ := lego.GetPEMCertExpiration(newCert.Certificate)
|
||||
serialnum, _ := getPEMCertSerialNo(newCert.Certificate)
|
||||
|
||||
acmeCert := AcmeCertificate{
|
||||
CertificateResource: newCert,
|
||||
ExpiryDate: expirydate,
|
||||
SerialNumber: serialnum,
|
||||
newAcmeCert, err := c.saveCertificate(certName, acmeCert.DnsNames, newCertRes)
|
||||
if err != nil {
|
||||
logrus.Fatalf("Error saving certificate '%s': %v", certName, err)
|
||||
}
|
||||
|
||||
c.saveCertificate(acmeCert)
|
||||
return &acmeCert, nil
|
||||
return newAcmeCert, nil
|
||||
}
|
||||
|
||||
// GetStoredCertificate returns a locally stored certificate for the given domains
|
||||
func (c *Client) GetStoredCertificate(domains []string) (bool, *AcmeCertificate) {
|
||||
domain := domains[0]
|
||||
if !c.haveCertificate(domain) {
|
||||
// GetStoredCertificate returns the locally stored certificate for the given domains
|
||||
func (c *Client) GetStoredCertificate(certName string, domains []string) (bool, *AcmeCertificate) {
|
||||
logrus.Debugf("Looking up stored certificate by name '%s'", certName)
|
||||
if !c.haveCertificateByName(certName) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
acmeCert, err := c.loadCertificate(domain)
|
||||
acmeCert, err := c.loadCertificateByName(certName)
|
||||
if err != nil {
|
||||
// Don't fail here. Try to create a new certificate instead.
|
||||
logrus.Error(err.Error())
|
||||
// Don't quit. Try to issue a new certificate instead.
|
||||
logrus.Errorf("Error loading certificate '%s': %v", certName, err)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// check if the DNS names are a match
|
||||
if dnsNames := dnsNamesIdentifier(domains); acmeCert.DnsNames != dnsNames {
|
||||
logrus.Infof("Stored certificate does not have matching domain names: '%s' ", acmeCert.DnsNames)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, &acmeCert
|
||||
}
|
||||
|
||||
func (c *Client) haveCertificate(domain string) bool {
|
||||
certPath := c.CertPath(domain)
|
||||
func (c *Client) haveCertificateByName(certName string) bool {
|
||||
certPath := c.CertPath(certName)
|
||||
if _, err := os.Stat(path.Join(certPath, "metadata.json")); err != nil {
|
||||
logrus.Debugf("No existing acme certificate resource found for '%s'", domain)
|
||||
logrus.Debugf("No certificate in path '%s'", certPath)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *Client) loadCertificate(domain string) (AcmeCertificate, error) {
|
||||
func (c *Client) loadCertificateByName(certName string) (AcmeCertificate, error) {
|
||||
var acmeCert AcmeCertificate
|
||||
certPath := c.CertPath(domain)
|
||||
certPath := c.CertPath(certName)
|
||||
|
||||
logrus.Debugf("Loading acme certificate resource for '%s' from '%s'", domain, certPath)
|
||||
logrus.Debugf("Loading certificate '%s' from '%s'", certName, certPath)
|
||||
|
||||
certIn := path.Join(certPath, "fullchain.pem")
|
||||
privIn := path.Join(certPath, "privkey.pem")
|
||||
@@ -226,76 +226,113 @@ func (c *Client) loadCertificate(domain string) (AcmeCertificate, error) {
|
||||
|
||||
certBytes, err := ioutil.ReadFile(certIn)
|
||||
if err != nil {
|
||||
return acmeCert, fmt.Errorf("Failed to load certificate for domain '%s': %s", domain, err.Error())
|
||||
return acmeCert, fmt.Errorf("Failed to load certificate from '%s': %v", certIn, err)
|
||||
}
|
||||
|
||||
metaBytes, err := ioutil.ReadFile(metaIn)
|
||||
if err != nil {
|
||||
return acmeCert, fmt.Errorf("Failed to load meta data for domain '%s': %s", domain, err.Error())
|
||||
return acmeCert, fmt.Errorf("Failed to load meta data from '%s': %v", metaIn, err)
|
||||
}
|
||||
|
||||
keyBytes, err := ioutil.ReadFile(privIn)
|
||||
if err != nil {
|
||||
return acmeCert, fmt.Errorf("Failed to load private key for domain '%s': %s", domain, err.Error())
|
||||
return acmeCert, fmt.Errorf("Failed to load private key from '%s': %v", privIn, err)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(metaBytes, &acmeCert)
|
||||
if err != nil {
|
||||
return acmeCert, fmt.Errorf("Failed to unmarshal meta data for domain '%s': %s", domain, err.Error())
|
||||
return acmeCert, fmt.Errorf("Failed to unmarshal json meta data from '%s': %v", metaIn, err)
|
||||
}
|
||||
|
||||
acmeCert.PrivateKey = keyBytes
|
||||
acmeCert.Certificate = certBytes
|
||||
|
||||
return acmeCert, nil
|
||||
}
|
||||
|
||||
func (c *Client) saveCertificate(acmeCert AcmeCertificate) {
|
||||
certPath := c.CertPath(acmeCert.Domain)
|
||||
func (c *Client) saveCertificate(certName, dnsNames string, certRes lego.CertificateResource) (*AcmeCertificate, error) {
|
||||
expiryDate, err := lego.GetPEMCertExpiration(certRes.Certificate)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to read certificate expiry date: %v", err)
|
||||
}
|
||||
serialNumber, err := getPEMCertSerialNumber(certRes.Certificate)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to read certificate serial number: %v", err)
|
||||
}
|
||||
|
||||
acmeCert := AcmeCertificate{
|
||||
CertificateResource: certRes,
|
||||
ExpiryDate: expiryDate,
|
||||
SerialNumber: serialNumber,
|
||||
DnsNames: dnsNames,
|
||||
}
|
||||
|
||||
certPath := c.CertPath(certName)
|
||||
maybeCreatePath(certPath)
|
||||
|
||||
logrus.Debugf("Saving acme certificate resource for '%s' to '%s'", acmeCert.Domain, certPath)
|
||||
logrus.Debugf("Saving certificate '%s' to path '%s'", certName, certPath)
|
||||
|
||||
certOut := path.Join(certPath, "fullchain.pem")
|
||||
privOut := path.Join(certPath, "privkey.pem")
|
||||
metaOut := path.Join(certPath, "metadata.json")
|
||||
|
||||
err := ioutil.WriteFile(certOut, acmeCert.Certificate, 0600)
|
||||
err = ioutil.WriteFile(certOut, acmeCert.Certificate, 0600)
|
||||
if err != nil {
|
||||
logrus.Fatalf("Failed to save certificate for domain %s\n\t%s", acmeCert.Domain, err.Error())
|
||||
return nil, fmt.Errorf("Failed to save certificate to '%s': %v", certOut, err)
|
||||
}
|
||||
|
||||
logrus.Infof("Certificate saved to '%s'", certOut)
|
||||
|
||||
err = ioutil.WriteFile(privOut, acmeCert.PrivateKey, 0600)
|
||||
if err != nil {
|
||||
logrus.Fatalf("Failed to save private key for domain %s\n\t%s", acmeCert.Domain, err.Error())
|
||||
return nil, fmt.Errorf("Failed to save private key to '%s': %v", privOut, err)
|
||||
}
|
||||
|
||||
logrus.Infof("Private key saved to '%s'", privOut)
|
||||
|
||||
jsonBytes, err := json.MarshalIndent(acmeCert, "", "\t")
|
||||
if err != nil {
|
||||
logrus.Fatalf("Failed to marshal meta data for domain %s\n\t%s", acmeCert.Domain, err.Error())
|
||||
return nil, fmt.Errorf("Failed to marshal meta data for certificate '%s': %v", certName, err)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(metaOut, jsonBytes, 0600)
|
||||
if err != nil {
|
||||
logrus.Fatalf("Failed to save meta data for domain %s\n\t%s", acmeCert.Domain, err.Error())
|
||||
return nil, fmt.Errorf("Failed to save meta data to '%s': %v", metaOut, err)
|
||||
}
|
||||
|
||||
return &acmeCert, nil
|
||||
}
|
||||
|
||||
func (c *Client) ConfigPath() string {
|
||||
path := path.Join(ConfigDir, strings.ToLower(string(c.apiVersion)))
|
||||
path := path.Join(StorageDir, strings.ToLower(string(c.apiVersion)))
|
||||
maybeCreatePath(path)
|
||||
return path
|
||||
}
|
||||
|
||||
func (c *Client) CertPath(domain string) string {
|
||||
return path.Join(c.ConfigPath(), "certificates", domain)
|
||||
func (c *Client) CertPath(certName string) string {
|
||||
return path.Join(c.ConfigPath(), "certs", safeFileName(certName))
|
||||
}
|
||||
|
||||
func dnsNamesIdentifier(domains []string) string {
|
||||
return strings.Join(domains, "|")
|
||||
}
|
||||
|
||||
func maybeCreatePath(path string) {
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
err = os.MkdirAll(path, 0700)
|
||||
if err != nil {
|
||||
logrus.Fatalf("Error creating path: %v", err)
|
||||
logrus.Fatalf("Failed to create path: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// safeFileName replaces separators with dashes and removes all
|
||||
// characters other than alphanumerics, dashes, underscores and dots.
|
||||
func safeFileName(str string) string {
|
||||
separators := regexp.MustCompile(`[ /&=+:]`)
|
||||
illegals := regexp.MustCompile(`[^[:alnum:]-_.]`)
|
||||
dashes := regexp.MustCompile(`[\-]+`)
|
||||
str = separators.ReplaceAllString(str, "-")
|
||||
str = illegals.ReplaceAllString(str, "")
|
||||
str = dashes.ReplaceAllString(str, "-")
|
||||
return str
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ func loadPrivateKey(file string) (crypto.PrivateKey, error) {
|
||||
return nil, fmt.Errorf("Unknown private key type.")
|
||||
}
|
||||
|
||||
func getPEMCertSerialNo(cert []byte) (string, error) {
|
||||
func getPEMCertSerialNumber(cert []byte) (string, error) {
|
||||
pemBlock, _ := pem.Decode(cert)
|
||||
if pemBlock == nil {
|
||||
return "", fmt.Errorf("Pem decode did not yield a valid block")
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
@@ -10,14 +11,18 @@ var (
|
||||
// Must be set at build
|
||||
Version string
|
||||
Git string
|
||||
|
||||
debug bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.BoolVar(&debug, "debug", false, "Enable debugging")
|
||||
logrus.SetFormatter(&logrus.TextFormatter{DisableTimestamp: true})
|
||||
logrus.SetOutput(os.Stdout)
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
logrus.Infof("Starting Let's Encrypt Certificate Manager %s %s", Version, Git)
|
||||
context := &Context{}
|
||||
context.InitContext()
|
||||
|
||||
+30
-30
@@ -21,44 +21,44 @@ func (c *Context) Run() {
|
||||
}
|
||||
|
||||
func (c *Context) startup() {
|
||||
var haveLocal, haveRemote bool
|
||||
ok, acmeCert := c.Acme.GetStoredCertificate(c.Domains)
|
||||
var storedLocally, storedInRancher bool
|
||||
ok, acmeCert := c.Acme.GetStoredCertificate(c.CertificateName, c.Domains)
|
||||
if ok {
|
||||
haveLocal = true
|
||||
storedLocally = true
|
||||
c.ExpiryDate = acmeCert.ExpiryDate
|
||||
logrus.Infof("Found locally stored certificate for '%s'", strings.Join(c.Domains, " | "))
|
||||
logrus.Infof("Found locally stored certificate '%s'", c.CertificateName)
|
||||
}
|
||||
|
||||
rancherCert, err := c.Rancher.FindCertByName(c.RancherCertName)
|
||||
rancherCert, err := c.Rancher.FindCertByName(c.CertificateName)
|
||||
if err != nil {
|
||||
logrus.Fatalf("Error looking up Rancher certificates: %v", err)
|
||||
logrus.Fatalf("Could not lookup certificate in Rancher API: %v", err)
|
||||
}
|
||||
|
||||
if rancherCert != nil {
|
||||
haveRemote = true
|
||||
storedInRancher = true
|
||||
c.RancherCertId = rancherCert.Id
|
||||
logrus.Infof("Found existing Rancher certificate '%s'", rancherCert.Name)
|
||||
logrus.Infof("Found existing certificate '%s' in Rancher", c.CertificateName)
|
||||
}
|
||||
|
||||
if haveLocal && haveRemote {
|
||||
if storedLocally && storedInRancher {
|
||||
if rancherCert.SerialNumber == acmeCert.SerialNumber {
|
||||
logrus.Infof("Managing renewal of Rancher certificate '%s'", rancherCert.Name)
|
||||
logrus.Infof("Managing renewal of certificate '%s'", c.CertificateName)
|
||||
return
|
||||
}
|
||||
logrus.Infof("Certificate serial number mismatch. Overwriting Rancher certificate '%s'", rancherCert.Name)
|
||||
logrus.Infof("Serial number mismatch between Rancher and local certificate '%s'", c.CertificateName)
|
||||
c.updateRancherCert(acmeCert.PrivateKey, acmeCert.Certificate)
|
||||
return
|
||||
}
|
||||
|
||||
if haveLocal && !haveRemote {
|
||||
logrus.Infof("Adding Rancher certificate '%s'", rancherCert.Name)
|
||||
if storedLocally && !storedInRancher {
|
||||
logrus.Debugf("Adding certificate '%s' to Rancher", c.CertificateName)
|
||||
c.addRancherCert(acmeCert.PrivateKey, acmeCert.Certificate)
|
||||
return
|
||||
}
|
||||
|
||||
logrus.Infof("Trying to obtain SSL certificate for %s", strings.Join(c.Domains, " | "))
|
||||
logrus.Infof("Trying to obtain SSL certificate (%s) from Let's Encrypt CA", strings.Join(c.Domains, ","))
|
||||
|
||||
acmeCert, failures := c.Acme.Issue(c.Domains)
|
||||
acmeCert, failures := c.Acme.Issue(c.CertificateName, c.Domains)
|
||||
if len(failures) > 0 {
|
||||
for k, v := range failures {
|
||||
logrus.Errorf("[%s] Error obtaining certificate: %s", k, v.Error())
|
||||
@@ -66,12 +66,12 @@ func (c *Context) startup() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
logrus.Infof("Successfully obtained SSL certificate")
|
||||
logrus.Infof("Certificate obtained successfully")
|
||||
|
||||
c.ExpiryDate = acmeCert.ExpiryDate
|
||||
|
||||
if haveRemote {
|
||||
logrus.Infof("Overwriting Rancher certificate '%s'", rancherCert.Name)
|
||||
if storedInRancher {
|
||||
logrus.Debugf("Overwriting Rancher certificate '%s'", c.CertificateName)
|
||||
c.updateRancherCert(acmeCert.PrivateKey, acmeCert.Certificate)
|
||||
return
|
||||
}
|
||||
@@ -80,36 +80,36 @@ func (c *Context) startup() {
|
||||
}
|
||||
|
||||
func (c *Context) addRancherCert(privateKey, cert []byte) {
|
||||
rancherCert, err := c.Rancher.AddCertificate(c.RancherCertName, CERT_DESCRIPTION, privateKey, cert)
|
||||
rancherCert, err := c.Rancher.AddCertificate(c.CertificateName, CERT_DESCRIPTION, privateKey, cert)
|
||||
if err != nil {
|
||||
logrus.Fatalf("Failed to add Rancher certificate '%s': %v", c.RancherCertName, err)
|
||||
logrus.Fatalf("Failed to add Rancher certificate '%s': %v", c.CertificateName, err)
|
||||
}
|
||||
c.RancherCertId = rancherCert.Id
|
||||
logrus.Infof("Added Rancher certificate '%s'", c.RancherCertName)
|
||||
logrus.Infof("Certificate '%s' added to Rancher", c.CertificateName)
|
||||
}
|
||||
|
||||
func (c *Context) updateRancherCert(privateKey, cert []byte) {
|
||||
err := c.Rancher.UpdateCertificate(c.RancherCertId, CERT_DESCRIPTION, privateKey, cert)
|
||||
if err != nil {
|
||||
logrus.Fatalf("Failed to update Rancher certificate '%s': %v", c.RancherCertName, err)
|
||||
logrus.Fatalf("Failed to update Rancher certificate '%s': %v", c.CertificateName, err)
|
||||
}
|
||||
logrus.Infof("Updated Rancher certificate '%s'", c.RancherCertName)
|
||||
logrus.Infof("Updated Rancher certificate '%s'", c.CertificateName)
|
||||
|
||||
err = c.Rancher.UpgradeLoadBalancers(c.RancherCertId)
|
||||
if err != nil {
|
||||
logrus.Fatalf("Error upgrading load balancers: %v", err)
|
||||
logrus.Fatalf("Failed to upgrade load balancers: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Context) renew() {
|
||||
logrus.Infof("Trying to renew certificate for '%s'", strings.Join(c.Domains, " | "))
|
||||
logrus.Infof("Trying to obtain renewed SSL certificate (%s) from Let's Encrypt CA", strings.Join(c.Domains, ","))
|
||||
|
||||
acmeCert, err := c.Acme.Renew(c.Domains)
|
||||
acmeCert, err := c.Acme.Renew(c.CertificateName)
|
||||
if err != nil {
|
||||
logrus.Fatalf("Failed to renew certificate: %v", err)
|
||||
}
|
||||
|
||||
logrus.Infof("Successfully renewed certificate")
|
||||
logrus.Infof("Certificate renewed successfully")
|
||||
|
||||
c.ExpiryDate = acmeCert.ExpiryDate
|
||||
c.updateRancherCert(acmeCert.PrivateKey, acmeCert.Certificate)
|
||||
@@ -123,11 +123,11 @@ func (c *Context) timer() <-chan time.Time {
|
||||
left = 10 * time.Second
|
||||
}
|
||||
|
||||
logrus.Infof("Next certificate renewal scheduled for %s", next.Format("2006/01/02 15:04 MST"))
|
||||
logrus.Infof("Certificate renewal scheduled for %s", next.Format("2006/01/02 15:04 MST"))
|
||||
|
||||
// Debug option set to true enables test mode
|
||||
// Debug option forces renewal
|
||||
if c.Debug {
|
||||
logrus.Debug("Test mode enabled: certificate will be renewed in 120 seconds")
|
||||
logrus.Debug("Debug mode: Forced certificate renewal in 120 seconds")
|
||||
left = 120 * time.Second
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user