Skip to content

Commit

Permalink
ACMEv2 POST-as-GET support
Browse files Browse the repository at this point in the history
©! I, Hugo Landau <[email protected]>, hereby licence these changes under the
©! licence with SHA256 hash
©! fd80a26fbb3f644af1fa994134446702932968519797227e07a1368dea80f0bc.
  • Loading branch information
hlandau committed Oct 16, 2019
1 parent 4dfbe62 commit ca90c4c
Show file tree
Hide file tree
Showing 13 changed files with 63 additions and 27 deletions.
4 changes: 2 additions & 2 deletions cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import (
"strings"
"syscall"

"github.com/hlandau/acmeapi"
"github.com/hlandau/acmeapi/acmeutils"
"github.com/hlandau/acmetool/hooks"
"github.com/hlandau/acmetool/interaction"
"github.com/hlandau/acmetool/redirector"
Expand All @@ -21,6 +19,8 @@ import (
"github.com/hlandau/dexlogconfig"
"github.com/hlandau/xlog"
"gopkg.in/alecthomas/kingpin.v2"
"gopkg.in/hlandau/acmeapi.v2"
"gopkg.in/hlandau/acmeapi.v2/acmeutils"
"gopkg.in/hlandau/easyconfig.v1/adaptflag"
"gopkg.in/hlandau/service.v2"
"gopkg.in/square/go-jose.v1"
Expand Down
4 changes: 2 additions & 2 deletions cli/main_ig_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ package cli

import (
"fmt"
//"github.com/hlandau/acmeapi"
"github.com/hlandau/acmeapi/pebbletest"
//"gopkg.in/hlandau/acmeapi.v2"
"github.com/hlandau/acmetool/interaction"
"github.com/hlandau/acmetool/responder"
"github.com/hlandau/acmetool/storageops"
"gopkg.in/hlandau/acmeapi.v2/pebbletest"
"io/ioutil"
"path/filepath"
"strings"
Expand Down
4 changes: 2 additions & 2 deletions cli/quickstart.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import (
"bytes"
"crypto/rand"
"fmt"
"github.com/hlandau/acmeapi"
"github.com/hlandau/acmeapi/acmeendpoints"
"github.com/hlandau/acmetool/hooks"
"github.com/hlandau/acmetool/interaction"
"github.com/hlandau/acmetool/storage"
"github.com/hlandau/acmetool/storageops"
"gopkg.in/hlandau/acmeapi.v2"
"gopkg.in/hlandau/acmeapi.v2/acmeendpoints"
"gopkg.in/hlandau/svcutils.v1/exepath"
"gopkg.in/hlandau/svcutils.v1/passwd"
"io/ioutil"
Expand Down
2 changes: 1 addition & 1 deletion responder/dns.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"crypto"
"encoding/json"
"fmt"
"github.com/hlandau/acmeapi/acmeutils"
"gopkg.in/hlandau/acmeapi.v2/acmeutils"
)

type DNSChallengeInfo struct {
Expand Down
2 changes: 1 addition & 1 deletion responder/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import (
"crypto/tls"
"encoding/json"
"fmt"
"github.com/hlandau/acmeapi/acmeutils"
"github.com/hlandau/acmetool/responder/reshttp"
denet "github.com/hlandau/goutils/net"
deos "github.com/hlandau/goutils/os"
"gopkg.in/hlandau/acmeapi.v2/acmeutils"
"io/ioutil"
"net"
"net/http"
Expand Down
4 changes: 2 additions & 2 deletions solver/order.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ package solver
import (
"context"
"fmt"
"github.com/hlandau/acmeapi"
"github.com/hlandau/acmetool/responder"
"github.com/hlandau/acmetool/util"
denet "github.com/hlandau/goutils/net"
"github.com/hlandau/xlog"
"gopkg.in/hlandau/acmeapi.v2"
"sync"
"time"
)
Expand Down Expand Up @@ -100,7 +100,7 @@ func orderProcess(ctx context.Context, rc *acmeapi.RealmClient, acct *acmeapi.Ac
}

// Get a fresh picture of the order status. orderAuthorizeAll doesn't refresh it.
err = rc.LoadOrder(ctx, order)
err = rc.LoadOrder(ctx, acct, order)
if err != nil {
return true, err
}
Expand Down
2 changes: 1 addition & 1 deletion solver/preference.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package solver

import (
"github.com/hlandau/acmeapi"
"gopkg.in/hlandau/acmeapi.v2"
"sort"
)

Expand Down
2 changes: 1 addition & 1 deletion solver/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ package solver

import (
"fmt"
"github.com/hlandau/acmeapi"
"github.com/hlandau/acmetool/interaction"
"golang.org/x/net/context"
"gopkg.in/hlandau/acmeapi.v2"
"net/mail"
)

Expand Down
2 changes: 1 addition & 1 deletion storage/abs.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ type Store interface {

ImportKey(privateKey crypto.PrivateKey) (*Key, error) // Imports the key if it isn't already imported.
ImportAccount(directoryURL string, privateKey crypto.PrivateKey) (*Account, error) // Imports an account key if it isn't already imported.
ImportCertificate(url string) (*Certificate, error) // Imports a certificate if it isn't already imported.
ImportCertificate(acct *Account, url string) (*Certificate, error) // Imports a certificate if it isn't already imported.

SetPreferredCertificateForHostname(hostname string, c *Certificate) error

Expand Down
29 changes: 24 additions & 5 deletions storage/storage-fdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import (
"crypto"
"crypto/x509"
"fmt"
"github.com/hlandau/acmeapi"
"github.com/hlandau/acmeapi/acmeutils"
"github.com/hlandau/acmetool/fdb"
"github.com/hlandau/acmetool/util"
"github.com/hlandau/xlog"
"gopkg.in/hlandau/acmeapi.v2"
"gopkg.in/hlandau/acmeapi.v2/acmeutils"
"gopkg.in/yaml.v2"
"io"
"io/ioutil"
Expand Down Expand Up @@ -491,6 +491,18 @@ func (s *fdbStore) validateCert(certID string, c *fdb.Collection) error {
crt.Cached = true
}

acctLink, err := c.ReadLink("account")
if err == nil {
if !strings.HasPrefix(acctLink.Target, "accounts/") {
return fmt.Errorf("malformed certificate account symlink: %q %q", certID, acctLink.Target)
}

crt.Account = s.AccountByID(acctLink.Target[9:])
if crt.Account == nil {
log.Warnf("certificate directory %#v contains account reference %#v but no such account was found", certID, acctLink.Target)
}
}

s.certs[certID] = crt

return nil
Expand Down Expand Up @@ -775,20 +787,27 @@ func (s *fdbStore) ImportKey(privateKey crypto.PrivateKey) (*Key, error) {
// Given a certificate URL, imports the certificate into the store. The
// certificate will be retrieved on the next reconcile. If a certificate with
// that URL already exists, this is a no-op and returns nil.
func (s *fdbStore) ImportCertificate(url string) (*Certificate, error) {
func (s *fdbStore) ImportCertificate(acct *Account, url string) (*Certificate, error) {
certID := determineCertificateID(url)
c, ok := s.certs[certID]
if ok {
return c, nil
}

err := fdb.WriteBytes(s.db.Collection("certs/"+certID), "url", []byte(url))
coll := s.db.Collection("certs/" + certID)
err := coll.WriteLink("account", fdb.Link{"accounts/" + acct.ID()})
if err != nil {
return nil, err
}

err = fdb.WriteBytes(coll, "url", []byte(url))
if err != nil {
return nil, err
}

c = &Certificate{
URL: url,
URL: url,
Account: acct,
}

s.certs[certID] = c
Expand Down
7 changes: 6 additions & 1 deletion storage/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import (
"crypto/rsa"
"encoding/base32"
"fmt"
"github.com/hlandau/acmeapi"
"github.com/satori/go.uuid"
"gopkg.in/hlandau/acmeapi.v2"
"strings"
)

Expand Down Expand Up @@ -250,6 +250,11 @@ type Certificate struct {
// N (for now). Whether this certificate has been revoked.
Revoked bool

// N. Now required due to need to support POST-as-GET. The account under
// which the certificate was requested. nil if this is unknown due to being a
// legacy certificate directory.
Account *Account

// D. Certificate data retrieved from URL, plus chained certificates.
// The end certificate comes first, the root last, etc.
Certificates [][]byte
Expand Down
2 changes: 1 addition & 1 deletion storage/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"crypto/x509"
"encoding/base32"
"fmt"
"github.com/hlandau/acmeapi/acmeutils"
"gopkg.in/hlandau/acmeapi.v2/acmeutils"
"io"
"math/big"
"net/url"
Expand Down
26 changes: 19 additions & 7 deletions storageops/reconcile.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ import (
"crypto/x509/pkix"
"encoding/asn1"
"fmt"
"github.com/hlandau/acmeapi"
"github.com/hlandau/acmeapi/acmeendpoints"
"github.com/hlandau/acmetool/hooks"
"github.com/hlandau/acmetool/responder"
"github.com/hlandau/acmetool/solver"
"github.com/hlandau/acmetool/storage"
"github.com/hlandau/acmetool/util"
"github.com/hlandau/xlog"
"github.com/jmhodges/clock"
"gopkg.in/hlandau/acmeapi.v2"
"gopkg.in/hlandau/acmeapi.v2/acmeendpoints"
"net/http"
"path/filepath"
"sort"
Expand Down Expand Up @@ -458,7 +458,7 @@ func (r *reconcile) requestCertificateForTarget(t *storage.Target) error {
return err
}

c, err := r.store.ImportCertificate(order.URL)
c, err := r.store.ImportCertificate(acct, order.URL)
if err != nil {
log.Errore(err, "could not import certificate")
return err
Expand Down Expand Up @@ -590,14 +590,26 @@ func (r *reconcile) generateOrGetKey(trk *storage.TargetRequestKey) (crypto.Priv
func (r *reconcile) downloadCertificateAdaptive(c *storage.Certificate) error {
log.Debugf("downloading certificate %v", c)

cl, err := r.getGenericClient()
if c.Account == nil {
return fmt.Errorf("cannot download certificate because it is unknown which account requested it: %v", c)
}

cl, err := r.getClientForDirectoryURL(c.Account.DirectoryURL)
if err != nil {
return err
}

acctAPI := c.Account.ToAPI()
if acctAPI.URL == "" {
err = cl.LocateAccount(context.TODO(), acctAPI)
if err != nil {
return err
}
}

order := &acmeapi.Order{}
cert := &acmeapi.Certificate{}
isCert, err := cl.LoadOrderOrCertificate(context.TODO(), c.URL, order, cert)
isCert, err := cl.LoadOrderOrCertificate(context.TODO(), c.URL, acctAPI, order, cert)
if err != nil {
return err
}
Expand All @@ -621,7 +633,7 @@ func (r *reconcile) downloadCertificateAdaptive(c *storage.Certificate) error {
return err
}

err = cl.WaitLoadOrder(context.TODO(), order)
err = cl.WaitLoadOrder(context.TODO(), acctAPI, order)
if err != nil {
return err
}
Expand All @@ -640,7 +652,7 @@ func (r *reconcile) downloadCertificateAdaptive(c *storage.Certificate) error {
URL: order.CertificateURL,
}

err = cl.LoadCertificate(context.TODO(), cert)
err = cl.LoadCertificate(context.TODO(), acctAPI, cert)
if err != nil {
return err
}
Expand Down

3 comments on commit ca90c4c

@glasser
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh huh, are you porting this to v2? I am 90% done porting our app to https://github.com/go-acme/lego because I wasn't sure if https://github.com/hlandau/acmeapi was finished (and I couldn't quite figure out how to use it), but if I just have to upgrade the imports that would be nice...

@hlandau
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@glasser This is now available, see tag v0.2.1 on hlandau/acmetool.

#322

Sorry for the time it's taken to get this done.

@glasser
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heh, thanks!

That said, I found it hard to figure out exactly how to use the new acmeapi without an example (even given my project's old usage of acmeapi) and finishing the port to lego seemed easier. I am giving up acmeapi's ability to get me fine grained reporting on exactly what stage of getting a certificate failed, though...

Please sign in to comment.