aboutsummaryrefslogtreecommitdiff
path: root/core/https/client.go
diff options
context:
space:
mode:
authorGravatar Miek Gieben <miek@miek.nl> 2016-08-19 17:14:17 -0700
committerGravatar GitHub <noreply@github.com> 2016-08-19 17:14:17 -0700
commit9ac3cab1b7b1b1e78f86ce3c6a80fbee312162e6 (patch)
tree437e9755927c33af16276ad2602a6da115f948cb /core/https/client.go
parenta1989c35231b0e5ea271b2f68d82c1a63e697cd0 (diff)
downloadcoredns-9ac3cab1b7b1b1e78f86ce3c6a80fbee312162e6.tar.gz
coredns-9ac3cab1b7b1b1e78f86ce3c6a80fbee312162e6.tar.zst
coredns-9ac3cab1b7b1b1e78f86ce3c6a80fbee312162e6.zip
Make CoreDNS a server type plugin for Caddy (#220)
* Make CoreDNS a server type plugin for Caddy Remove code we don't need and port all middleware over. Fix all tests and rework the documentation. Also make `go generate` build a caddy binary which we then copy into our directory. This means `go build`-builds remain working as-is. And new etc instances in each etcd test for better isolation. Fix more tests and rework test.Server with the newer support Caddy offers. Fix Makefile to support new mode of operation.
Diffstat (limited to 'core/https/client.go')
-rw-r--r--core/https/client.go215
1 files changed, 0 insertions, 215 deletions
diff --git a/core/https/client.go b/core/https/client.go
deleted file mode 100644
index e9e8cd82c..000000000
--- a/core/https/client.go
+++ /dev/null
@@ -1,215 +0,0 @@
-package https
-
-import (
- "encoding/json"
- "errors"
- "fmt"
- "io/ioutil"
- "net"
- "sync"
- "time"
-
- "github.com/miekg/coredns/server"
- "github.com/xenolf/lego/acme"
-)
-
-// acmeMu ensures that only one ACME challenge occurs at a time.
-var acmeMu sync.Mutex
-
-// ACMEClient is an acme.Client with custom state attached.
-type ACMEClient struct {
- *acme.Client
- AllowPrompts bool // if false, we assume AlternatePort must be used
-}
-
-// NewACMEClient creates a new ACMEClient given an email and whether
-// prompting the user is allowed. Clients should not be kept and
-// re-used over long periods of time, but immediate re-use is more
-// efficient than re-creating on every iteration.
-var NewACMEClient = func(email string, allowPrompts bool) (*ACMEClient, error) {
- // Look up or create the LE user account
- leUser, err := getUser(email)
- if err != nil {
- return nil, err
- }
-
- // The client facilitates our communication with the CA server.
- client, err := acme.NewClient(CAUrl, &leUser, KeyType)
- if err != nil {
- return nil, err
- }
-
- // If not registered, the user must register an account with the CA
- // and agree to terms
- if leUser.Registration == nil {
- reg, err := client.Register()
- if err != nil {
- return nil, errors.New("registration error: " + err.Error())
- }
- leUser.Registration = reg
-
- if allowPrompts { // can't prompt a user who isn't there
- if !Agreed && reg.TosURL == "" {
- Agreed = promptUserAgreement(saURL, false) // TODO - latest URL
- }
- if !Agreed && reg.TosURL == "" {
- return nil, errors.New("user must agree to terms")
- }
- }
-
- err = client.AgreeToTOS()
- if err != nil {
- saveUser(leUser) // Might as well try, right?
- return nil, errors.New("error agreeing to terms: " + err.Error())
- }
-
- // save user to the file system
- err = saveUser(leUser)
- if err != nil {
- return nil, errors.New("could not save user: " + err.Error())
- }
- }
-
- return &ACMEClient{
- Client: client,
- AllowPrompts: allowPrompts,
- }, nil
-}
-
-// NewACMEClientGetEmail creates a new ACMEClient and gets an email
-// address at the same time (a server config is required, since it
-// may contain an email address in it).
-func NewACMEClientGetEmail(config server.Config, allowPrompts bool) (*ACMEClient, error) {
- return NewACMEClient(getEmail(config, allowPrompts), allowPrompts)
-}
-
-// Configure configures c according to bindHost, which is the host (not
-// whole address) to bind the listener to in solving the http and tls-sni
-// challenges.
-func (c *ACMEClient) Configure(bindHost string) {
- // If we allow prompts, operator must be present. In our case,
- // that is synonymous with saying the server is not already
- // started. So if the user is still there, we don't use
- // AlternatePort because we don't need to proxy the challenges.
- // Conversely, if the operator is not there, the server has
- // already started and we need to proxy the challenge.
- if c.AllowPrompts {
- // Operator is present; server is not already listening
- c.SetHTTPAddress(net.JoinHostPort(bindHost, ""))
- c.SetTLSAddress(net.JoinHostPort(bindHost, ""))
- //c.ExcludeChallenges([]acme.Challenge{acme.DNS01})
- } else {
- // Operator is not present; server is started, so proxy challenges
- c.SetHTTPAddress(net.JoinHostPort(bindHost, AlternatePort))
- c.SetTLSAddress(net.JoinHostPort(bindHost, AlternatePort))
- //c.ExcludeChallenges([]acme.Challenge{acme.TLSSNI01, acme.DNS01})
- }
- c.ExcludeChallenges([]acme.Challenge{acme.TLSSNI01, acme.DNS01}) // TODO: can we proxy TLS challenges? and we should support DNS...
-}
-
-// Obtain obtains a single certificate for names. It stores the certificate
-// on the disk if successful.
-func (c *ACMEClient) Obtain(names []string) error {
-Attempts:
- for attempts := 0; attempts < 2; attempts++ {
- acmeMu.Lock()
- certificate, failures := c.ObtainCertificate(names, true, nil)
- acmeMu.Unlock()
- if len(failures) > 0 {
- // Error - try to fix it or report it to the user and abort
- var errMsg string // we'll combine all the failures into a single error message
- var promptedForAgreement bool // only prompt user for agreement at most once
-
- for errDomain, obtainErr := range failures {
- // TODO: Double-check, will obtainErr ever be nil?
- if tosErr, ok := obtainErr.(acme.TOSError); ok {
- // Terms of Service agreement error; we can probably deal with this
- if !Agreed && !promptedForAgreement && c.AllowPrompts {
- Agreed = promptUserAgreement(tosErr.Detail, true) // TODO: Use latest URL
- promptedForAgreement = true
- }
- if Agreed || !c.AllowPrompts {
- err := c.AgreeToTOS()
- if err != nil {
- return errors.New("error agreeing to updated terms: " + err.Error())
- }
- continue Attempts
- }
- }
-
- // If user did not agree or it was any other kind of error, just append to the list of errors
- errMsg += "[" + errDomain + "] failed to get certificate: " + obtainErr.Error() + "\n"
- }
- return errors.New(errMsg)
- }
-
- // Success - immediately save the certificate resource
- err := saveCertResource(certificate)
- if err != nil {
- return fmt.Errorf("error saving assets for %v: %v", names, err)
- }
-
- break
- }
-
- return nil
-}
-
-// Renew renews the managed certificate for name. Right now our storage
-// mechanism only supports one name per certificate, so this function only
-// accepts one domain as input. It can be easily modified to support SAN
-// certificates if, one day, they become desperately needed enough that our
-// storage mechanism is upgraded to be more complex to support SAN certs.
-//
-// Anyway, this function is safe for concurrent use.
-func (c *ACMEClient) Renew(name string) error {
- // Prepare for renewal (load PEM cert, key, and meta)
- certBytes, err := ioutil.ReadFile(storage.SiteCertFile(name))
- if err != nil {
- return err
- }
- keyBytes, err := ioutil.ReadFile(storage.SiteKeyFile(name))
- if err != nil {
- return err
- }
- metaBytes, err := ioutil.ReadFile(storage.SiteMetaFile(name))
- if err != nil {
- return err
- }
- var certMeta acme.CertificateResource
- err = json.Unmarshal(metaBytes, &certMeta)
- certMeta.Certificate = certBytes
- certMeta.PrivateKey = keyBytes
-
- // Perform renewal and retry if necessary, but not too many times.
- var newCertMeta acme.CertificateResource
- var success bool
- for attempts := 0; attempts < 2; attempts++ {
- acmeMu.Lock()
- newCertMeta, err = c.RenewCertificate(certMeta, true)
- acmeMu.Unlock()
- if err == nil {
- success = true
- break
- }
-
- // If the legal terms changed and need to be agreed to again,
- // we can handle that.
- if _, ok := err.(acme.TOSError); ok {
- err := c.AgreeToTOS()
- if err != nil {
- return err
- }
- continue
- }
-
- // For any other kind of error, wait 10s and try again.
- time.Sleep(10 * time.Second)
- }
-
- if !success {
- return errors.New("too many renewal attempts; last error: " + err.Error())
- }
-
- return saveCertResource(newCertMeta)
-}