Merge branch 'check-dns-names-on-load'

This commit is contained in:
janeczku
2016-05-31 19:36:47 +02:00
38 changed files with 1925 additions and 440 deletions
-2
View File
@@ -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
View File
@@ -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"]
+60 -60
View File
@@ -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",
@@ -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
View File
@@ -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
View File
@@ -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.
@@ -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
}
}}
@@ -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
@@ -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,
}
}
@@ -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
View File
@@ -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)
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
}
File diff suppressed because it is too large Load Diff
+5 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -1,4 +1,4 @@
// +build !windows
// +build !windows,!plan9
package dns
+1 -1
View File
@@ -1,4 +1,4 @@
// +build !linux
// +build !linux,!plan9
package dns
+34
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
@@ -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
+3 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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, ",")
}
+1 -1
View File
@@ -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
View File
@@ -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
}
+1 -1
View File
@@ -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")
+5
View File
@@ -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
View File
@@ -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
}