4
0
mirror of https://github.com/cwinfo/matterbridge.git synced 2025-07-03 04:57:44 +00:00

Update dependencies (#975)

This commit is contained in:
Wim
2020-01-09 21:02:56 +01:00
committed by GitHub
parent b9354de8fd
commit 0f708daf2d
360 changed files with 72921 additions and 42691 deletions

View File

@ -35,6 +35,9 @@ import (
"golang.org/x/net/idna"
)
// DefaultACMEDirectory is the default ACME Directory URL used when the Manager's Client is nil.
const DefaultACMEDirectory = "https://acme-v02.api.letsencrypt.org/directory"
// createCertRetryAfter is how much time to wait before removing a failed state
// entry due to an unsuccessful createCert call.
// This is a variable instead of a const for testing.
@ -88,9 +91,9 @@ func defaultHostPolicy(context.Context, string) error {
}
// Manager is a stateful certificate manager built on top of acme.Client.
// It obtains and refreshes certificates automatically using "tls-alpn-01",
// "tls-sni-01", "tls-sni-02" and "http-01" challenge types,
// as well as providing them to a TLS server via tls.Config.
// It obtains and refreshes certificates automatically using "tls-alpn-01"
// or "http-01" challenge types, as well as providing them to a TLS server
// via tls.Config.
//
// You must specify a cache implementation, such as DirCache,
// to reuse obtained certificates across program restarts.
@ -135,9 +138,10 @@ type Manager struct {
// Client is used to perform low-level operations, such as account registration
// and requesting new certificates.
//
// If Client is nil, a zero-value acme.Client is used with acme.LetsEncryptURL
// as directory endpoint. If the Client.Key is nil, a new ECDSA P-256 key is
// generated and, if Cache is not nil, stored in cache.
// If Client is nil, a zero-value acme.Client is used with DefaultACMEDirectory
// as the directory endpoint.
// If the Client.Key is nil, a new ECDSA P-256 key is generated and,
// if Cache is not nil, stored in cache.
//
// Mutating the field after the first call of GetCertificate method will have no effect.
Client *acme.Client
@ -174,8 +178,8 @@ type Manager struct {
renewalMu sync.Mutex
renewal map[certKey]*domainRenewal
// tokensMu guards the rest of the fields: tryHTTP01, certTokens and httpTokens.
tokensMu sync.RWMutex
// challengeMu guards tryHTTP01, certTokens and httpTokens.
challengeMu sync.RWMutex
// tryHTTP01 indicates whether the Manager should try "http-01" challenge type
// during the authorization flow.
tryHTTP01 bool
@ -184,12 +188,11 @@ type Manager struct {
// to be provisioned.
// The entries are stored for the duration of the authorization flow.
httpTokens map[string][]byte
// certTokens contains temporary certificates for tls-sni and tls-alpn challenges
// and is keyed by token domain name, which matches server name of ClientHello.
// Keys always have ".acme.invalid" suffix for tls-sni. Otherwise, they are domain names
// for tls-alpn.
// certTokens contains temporary certificates for tls-alpn-01 challenges
// and is keyed by the domain name which matches the ClientHello server name.
// The entries are stored for the duration of the authorization flow.
certTokens map[string]*tls.Certificate
// nowFunc, if not nil, returns the current time. This may be set for
// testing purposes.
nowFunc func() time.Time
@ -226,7 +229,7 @@ func (m *Manager) TLSConfig() *tls.Config {
// GetCertificate implements the tls.Config.GetCertificate hook.
// It provides a TLS certificate for hello.ServerName host, including answering
// tls-alpn-01 and *.acme.invalid (tls-sni-01 and tls-sni-02) challenges.
// tls-alpn-01 challenges.
// All other fields of hello are ignored.
//
// If m.HostPolicy is non-nil, GetCertificate calls the policy before requesting
@ -235,9 +238,7 @@ func (m *Manager) TLSConfig() *tls.Config {
// This does not affect cached certs. See HostPolicy field description for more details.
//
// If GetCertificate is used directly, instead of via Manager.TLSConfig, package users will
// also have to add acme.ALPNProto to NextProtos for tls-alpn-01, or use HTTPHandler
// for http-01. (The tls-sni-* challenges have been deprecated by popular ACME providers
// due to security issues in the ecosystem.)
// also have to add acme.ALPNProto to NextProtos for tls-alpn-01, or use HTTPHandler for http-01.
func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
if m.Prompt == nil {
return nil, errors.New("acme/autocert: Manager.Prompt not set")
@ -269,13 +270,10 @@ func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate,
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
// Check whether this is a token cert requested for TLS-SNI or TLS-ALPN challenge.
// Check whether this is a token cert requested for TLS-ALPN challenge.
if wantsTokenCert(hello) {
m.tokensMu.RLock()
defer m.tokensMu.RUnlock()
// It's ok to use the same token cert key for both tls-sni and tls-alpn
// because there's always at most 1 token cert per on-going domain authorization.
// See m.verify for details.
m.challengeMu.RLock()
defer m.challengeMu.RUnlock()
if cert := m.certTokens[name]; cert != nil {
return cert, nil
}
@ -318,8 +316,7 @@ func wantsTokenCert(hello *tls.ClientHelloInfo) bool {
if len(hello.SupportedProtos) == 1 && hello.SupportedProtos[0] == acme.ALPNProto {
return true
}
// tls-sni-xx
return strings.HasSuffix(hello.ServerName, ".acme.invalid")
return false
}
func supportsECDSA(hello *tls.ClientHelloInfo) bool {
@ -384,8 +381,8 @@ func supportsECDSA(hello *tls.ClientHelloInfo) bool {
// If HTTPHandler is never called, the Manager will only use the "tls-alpn-01"
// challenge for domain verification.
func (m *Manager) HTTPHandler(fallback http.Handler) http.Handler {
m.tokensMu.Lock()
defer m.tokensMu.Unlock()
m.challengeMu.Lock()
defer m.challengeMu.Unlock()
m.tryHTTP01 = true
if fallback == nil {
@ -648,71 +645,64 @@ func (m *Manager) certState(ck certKey) (*certState, error) {
// authorizedCert starts the domain ownership verification process and requests a new cert upon success.
// The key argument is the certificate private key.
func (m *Manager) authorizedCert(ctx context.Context, key crypto.Signer, ck certKey) (der [][]byte, leaf *x509.Certificate, err error) {
client, err := m.acmeClient(ctx)
if err != nil {
return nil, nil, err
}
if err := m.verify(ctx, client, ck.domain); err != nil {
return nil, nil, err
}
csr, err := certRequest(key, ck.domain, m.ExtraExtensions)
if err != nil {
return nil, nil, err
}
der, _, err = client.CreateCert(ctx, csr, 0, true)
if err != nil {
return nil, nil, err
}
leaf, err = validCert(ck, der, key, m.now())
if err != nil {
return nil, nil, err
}
return der, leaf, nil
}
// revokePendingAuthz revokes all authorizations idenfied by the elements of uri slice.
// It ignores revocation errors.
func (m *Manager) revokePendingAuthz(ctx context.Context, uri []string) {
client, err := m.acmeClient(ctx)
if err != nil {
return
return nil, nil, err
}
for _, u := range uri {
client.RevokeAuthorization(ctx, u)
dir, err := client.Discover(ctx)
if err != nil {
return nil, nil, err
}
var chain [][]byte
switch {
// Pre-RFC legacy CA.
case dir.OrderURL == "":
if err := m.verify(ctx, client, ck.domain); err != nil {
return nil, nil, err
}
der, _, err := client.CreateCert(ctx, csr, 0, true)
if err != nil {
return nil, nil, err
}
chain = der
// RFC 8555 compliant CA.
default:
o, err := m.verifyRFC(ctx, client, ck.domain)
if err != nil {
return nil, nil, err
}
der, _, err := client.CreateOrderCert(ctx, o.FinalizeURL, csr, true)
if err != nil {
return nil, nil, err
}
chain = der
}
leaf, err = validCert(ck, chain, key, m.now())
if err != nil {
return nil, nil, err
}
return chain, leaf, nil
}
// verify runs the identifier (domain) authorization flow
// verify runs the identifier (domain) pre-authorization flow for legacy CAs
// using each applicable ACME challenge type.
func (m *Manager) verify(ctx context.Context, client *acme.Client, domain string) error {
// The list of challenge types we'll try to fulfill
// in this specific order.
challengeTypes := []string{"tls-alpn-01", "tls-sni-02", "tls-sni-01"}
m.tokensMu.RLock()
if m.tryHTTP01 {
challengeTypes = append(challengeTypes, "http-01")
}
m.tokensMu.RUnlock()
// Keep track of pending authzs and revoke the ones that did not validate.
pendingAuthzs := make(map[string]bool)
// Remove all hanging authorizations to reduce rate limit quotas
// after we're done.
var authzURLs []string
defer func() {
var uri []string
for k, pending := range pendingAuthzs {
if pending {
uri = append(uri, k)
}
}
if len(uri) > 0 {
// Use "detached" background context.
// The revocations need not happen in the current verification flow.
go m.revokePendingAuthz(context.Background(), uri)
}
go m.deactivatePendingAuthz(authzURLs)
}()
// errs accumulates challenge failure errors, printed if all fail
errs := make(map[*acme.Challenge]error)
challengeTypes := m.supportedChallengeTypes()
var nextTyp int // challengeType index of the next challenge type to try
for {
// Start domain authorization and get the challenge.
@ -720,6 +710,7 @@ func (m *Manager) verify(ctx context.Context, client *acme.Client, domain string
if err != nil {
return err
}
authzURLs = append(authzURLs, authz.URI)
// No point in accepting challenges if the authorization status
// is in a final state.
switch authz.Status {
@ -729,8 +720,6 @@ func (m *Manager) verify(ctx context.Context, client *acme.Client, domain string
return fmt.Errorf("acme/autocert: invalid authorization %q", authz.URI)
}
pendingAuthzs[authz.URI] = true
// Pick the next preferred challenge.
var chal *acme.Challenge
for chal == nil && nextTyp < len(challengeTypes) {
@ -760,11 +749,126 @@ func (m *Manager) verify(ctx context.Context, client *acme.Client, domain string
errs[chal] = err
continue
}
delete(pendingAuthzs, authz.URI)
return nil
}
}
// verifyRFC runs the identifier (domain) order-based authorization flow for RFC compliant CAs
// using each applicable ACME challenge type.
func (m *Manager) verifyRFC(ctx context.Context, client *acme.Client, domain string) (*acme.Order, error) {
// Try each supported challenge type starting with a new order each time.
// The nextTyp index of the next challenge type to try is shared across
// all order authorizations: if we've tried a challenge type once and it didn't work,
// it will most likely not work on another order's authorization either.
challengeTypes := m.supportedChallengeTypes()
nextTyp := 0 // challengeTypes index
AuthorizeOrderLoop:
for {
o, err := client.AuthorizeOrder(ctx, acme.DomainIDs(domain))
if err != nil {
return nil, err
}
// Remove all hanging authorizations to reduce rate limit quotas
// after we're done.
defer func(urls []string) {
go m.deactivatePendingAuthz(urls)
}(o.AuthzURLs)
// Check if there's actually anything we need to do.
switch o.Status {
case acme.StatusReady:
// Already authorized.
return o, nil
case acme.StatusPending:
// Continue normal Order-based flow.
default:
return nil, fmt.Errorf("acme/autocert: invalid new order status %q; order URL: %q", o.Status, o.URI)
}
// Satisfy all pending authorizations.
for _, zurl := range o.AuthzURLs {
z, err := client.GetAuthorization(ctx, zurl)
if err != nil {
return nil, err
}
if z.Status != acme.StatusPending {
// We are interested only in pending authorizations.
continue
}
// Pick the next preferred challenge.
var chal *acme.Challenge
for chal == nil && nextTyp < len(challengeTypes) {
chal = pickChallenge(challengeTypes[nextTyp], z.Challenges)
nextTyp++
}
if chal == nil {
return nil, fmt.Errorf("acme/autocert: unable to satisfy %q for domain %q: no viable challenge type found", z.URI, domain)
}
// Respond to the challenge and wait for validation result.
cleanup, err := m.fulfill(ctx, client, chal, domain)
if err != nil {
continue AuthorizeOrderLoop
}
defer cleanup()
if _, err := client.Accept(ctx, chal); err != nil {
continue AuthorizeOrderLoop
}
if _, err := client.WaitAuthorization(ctx, z.URI); err != nil {
continue AuthorizeOrderLoop
}
}
// All authorizations are satisfied.
// Wait for the CA to update the order status.
o, err = client.WaitOrder(ctx, o.URI)
if err != nil {
continue AuthorizeOrderLoop
}
return o, nil
}
}
func pickChallenge(typ string, chal []*acme.Challenge) *acme.Challenge {
for _, c := range chal {
if c.Type == typ {
return c
}
}
return nil
}
func (m *Manager) supportedChallengeTypes() []string {
m.challengeMu.RLock()
defer m.challengeMu.RUnlock()
typ := []string{"tls-alpn-01"}
if m.tryHTTP01 {
typ = append(typ, "http-01")
}
return typ
}
// deactivatePendingAuthz relinquishes all authorizations identified by the elements
// of the provided uri slice which are in "pending" state.
// It ignores revocation errors.
//
// deactivatePendingAuthz takes no context argument and instead runs with its own
// "detached" context because deactivations are done in a goroutine separate from
// that of the main issuance or renewal flow.
func (m *Manager) deactivatePendingAuthz(uri []string) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
client, err := m.acmeClient(ctx)
if err != nil {
return
}
for _, u := range uri {
z, err := client.GetAuthorization(ctx, u)
if err == nil && z.Status == acme.StatusPending {
client.RevokeAuthorization(ctx, u)
}
}
}
// fulfill provisions a response to the challenge chal.
// The cleanup is non-nil only if provisioning succeeded.
func (m *Manager) fulfill(ctx context.Context, client *acme.Client, chal *acme.Challenge, domain string) (cleanup func(), err error) {
@ -776,20 +880,6 @@ func (m *Manager) fulfill(ctx context.Context, client *acme.Client, chal *acme.C
}
m.putCertToken(ctx, domain, &cert)
return func() { go m.deleteCertToken(domain) }, nil
case "tls-sni-01":
cert, name, err := client.TLSSNI01ChallengeCert(chal.Token)
if err != nil {
return nil, err
}
m.putCertToken(ctx, name, &cert)
return func() { go m.deleteCertToken(name) }, nil
case "tls-sni-02":
cert, name, err := client.TLSSNI02ChallengeCert(chal.Token)
if err != nil {
return nil, err
}
m.putCertToken(ctx, name, &cert)
return func() { go m.deleteCertToken(name) }, nil
case "http-01":
resp, err := client.HTTP01ChallengeResponse(chal.Token)
if err != nil {
@ -802,20 +892,11 @@ func (m *Manager) fulfill(ctx context.Context, client *acme.Client, chal *acme.C
return nil, fmt.Errorf("acme/autocert: unknown challenge type %q", chal.Type)
}
func pickChallenge(typ string, chal []*acme.Challenge) *acme.Challenge {
for _, c := range chal {
if c.Type == typ {
return c
}
}
return nil
}
// putCertToken stores the token certificate with the specified name
// in both m.certTokens map and m.Cache.
func (m *Manager) putCertToken(ctx context.Context, name string, cert *tls.Certificate) {
m.tokensMu.Lock()
defer m.tokensMu.Unlock()
m.challengeMu.Lock()
defer m.challengeMu.Unlock()
if m.certTokens == nil {
m.certTokens = make(map[string]*tls.Certificate)
}
@ -826,8 +907,8 @@ func (m *Manager) putCertToken(ctx context.Context, name string, cert *tls.Certi
// deleteCertToken removes the token certificate with the specified name
// from both m.certTokens map and m.Cache.
func (m *Manager) deleteCertToken(name string) {
m.tokensMu.Lock()
defer m.tokensMu.Unlock()
m.challengeMu.Lock()
defer m.challengeMu.Unlock()
delete(m.certTokens, name)
if m.Cache != nil {
ck := certKey{domain: name, isToken: true}
@ -838,8 +919,8 @@ func (m *Manager) deleteCertToken(name string) {
// httpToken retrieves an existing http-01 token value from an in-memory map
// or the optional cache.
func (m *Manager) httpToken(ctx context.Context, tokenPath string) ([]byte, error) {
m.tokensMu.RLock()
defer m.tokensMu.RUnlock()
m.challengeMu.RLock()
defer m.challengeMu.RUnlock()
if v, ok := m.httpTokens[tokenPath]; ok {
return v, nil
}
@ -854,8 +935,8 @@ func (m *Manager) httpToken(ctx context.Context, tokenPath string) ([]byte, erro
//
// It ignores any error returned from Cache.Put.
func (m *Manager) putHTTPToken(ctx context.Context, tokenPath, val string) {
m.tokensMu.Lock()
defer m.tokensMu.Unlock()
m.challengeMu.Lock()
defer m.challengeMu.Unlock()
if m.httpTokens == nil {
m.httpTokens = make(map[string][]byte)
}
@ -871,8 +952,8 @@ func (m *Manager) putHTTPToken(ctx context.Context, tokenPath, val string) {
//
// If m.Cache is non-nil, it blocks until Cache.Delete returns without a timeout.
func (m *Manager) deleteHTTPToken(tokenPath string) {
m.tokensMu.Lock()
defer m.tokensMu.Unlock()
m.challengeMu.Lock()
defer m.challengeMu.Unlock()
delete(m.httpTokens, tokenPath)
if m.Cache != nil {
m.Cache.Delete(context.Background(), httpTokenCacheKey(tokenPath))
@ -971,7 +1052,7 @@ func (m *Manager) acmeClient(ctx context.Context) (*acme.Client, error) {
client := m.Client
if client == nil {
client = &acme.Client{DirectoryURL: acme.LetsEncryptURL}
client = &acme.Client{DirectoryURL: DefaultACMEDirectory}
}
if client.Key == nil {
var err error
@ -989,14 +1070,23 @@ func (m *Manager) acmeClient(ctx context.Context) (*acme.Client, error) {
}
a := &acme.Account{Contact: contact}
_, err := client.Register(ctx, a, m.Prompt)
if ae, ok := err.(*acme.Error); err == nil || ok && ae.StatusCode == http.StatusConflict {
// conflict indicates the key is already registered
if err == nil || isAccountAlreadyExist(err) {
m.client = client
err = nil
}
return m.client, err
}
// isAccountAlreadyExist reports whether the err, as returned from acme.Client.Register,
// indicates the account has already been registered.
func isAccountAlreadyExist(err error) bool {
if err == acme.ErrAccountAlreadyExists {
return true
}
ae, ok := err.(*acme.Error)
return ok && ae.StatusCode == http.StatusConflict
}
func (m *Manager) hostPolicy() HostPolicy {
if m.HostPolicy != nil {
return m.HostPolicy

View File

@ -77,6 +77,7 @@ func (d DirCache) Put(ctx context.Context, name string, data []byte) error {
if tmp, err = d.writeTempFile(name, data); err != nil {
return
}
defer os.Remove(tmp)
select {
case <-ctx.Done():
// Don't overwrite the file if the context was canceled.
@ -116,12 +117,17 @@ func (d DirCache) Delete(ctx context.Context, name string) error {
}
// writeTempFile writes b to a temporary file, closes the file and returns its path.
func (d DirCache) writeTempFile(prefix string, b []byte) (string, error) {
func (d DirCache) writeTempFile(prefix string, b []byte) (name string, reterr error) {
// TempFile uses 0600 permissions
f, err := ioutil.TempFile(string(d), prefix)
if err != nil {
return "", err
}
defer func() {
if reterr != nil {
os.Remove(f.Name())
}
}()
if _, err := f.Write(b); err != nil {
f.Close()
return "", err