mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 21:28:11 +09:00 
			
		
		
		
	[Vendor] update certmagic (#15590)
* update github.com/caddyserver/certmagic v0.12.0 -> v0.13.0 * migrate
This commit is contained in:
		
							
								
								
									
										6
									
								
								vendor/github.com/caddyserver/certmagic/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								vendor/github.com/caddyserver/certmagic/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -260,7 +260,7 @@ magic := certmagic.New(cache, certmagic.Config{ | ||||
| 	// any customizations you need go here | ||||
| }) | ||||
|  | ||||
| myACME := certmagic.NewACMEManager(magic, ACMEManager{ | ||||
| myACME := certmagic.NewACMEManager(magic, certmagic.ACMEManager{ | ||||
| 	CA:     certmagic.LetsEncryptStagingCA, | ||||
| 	Email:  "you@yours.com", | ||||
| 	Agreed: true, | ||||
| @@ -285,7 +285,7 @@ tlsConfig := magic.TLSConfig() | ||||
| // we can simply set its GetCertificate field and append the | ||||
| // TLS-ALPN challenge protocol to the NextProtos | ||||
| myTLSConfig.GetCertificate = magic.GetCertificate | ||||
| myTLSConfig.NextProtos = append(myTLSConfig.NextProtos, tlsalpn01.ACMETLS1Protocol} | ||||
| myTLSConfig.NextProtos = append(myTLSConfig.NextProtos, tlsalpn01.ACMETLS1Protocol) | ||||
|  | ||||
| // the HTTP challenge has to be handled by your HTTP server; | ||||
| // if you don't have one, you should have disabled it earlier | ||||
| @@ -394,7 +394,7 @@ To enable it, just set the `DNS01Solver` field on a `certmagic.ACMEManager` stru | ||||
| import "github.com/libdns/cloudflare" | ||||
|  | ||||
| certmagic.DefaultACME.DNS01Solver = &certmagic.DNS01Solver{ | ||||
| 	DNSProvider: cloudflare.Provider{ | ||||
| 	DNSProvider: &cloudflare.Provider{ | ||||
| 		APIToken: "topsecret", | ||||
| 	}, | ||||
| } | ||||
|   | ||||
							
								
								
									
										139
									
								
								vendor/github.com/caddyserver/certmagic/account.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										139
									
								
								vendor/github.com/caddyserver/certmagic/account.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -16,6 +16,8 @@ package certmagic | ||||
|  | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"bytes" | ||||
| 	"context" | ||||
| 	"crypto/ecdsa" | ||||
| 	"crypto/elliptic" | ||||
| 	"crypto/rand" | ||||
| @@ -33,18 +35,24 @@ import ( | ||||
| // getAccount either loads or creates a new account, depending on if | ||||
| // an account can be found in storage for the given CA + email combo. | ||||
| func (am *ACMEManager) getAccount(ca, email string) (acme.Account, error) { | ||||
| 	regBytes, err := am.config.Storage.Load(am.storageKeyUserReg(ca, email)) | ||||
| 	acct, err := am.loadAccount(ca, email) | ||||
| 	if err != nil { | ||||
| 		if _, ok := err.(ErrNotExist); ok { | ||||
| 			return am.newAccount(email) | ||||
| 		} | ||||
| 		return acct, err | ||||
| 	} | ||||
| 	return acct, err | ||||
| } | ||||
|  | ||||
| // loadAccount loads an account from storage, but does not create a new one. | ||||
| func (am *ACMEManager) loadAccount(ca, email string) (acme.Account, error) { | ||||
| 	regBytes, err := am.config.Storage.Load(am.storageKeyUserReg(ca, email)) | ||||
| 	if err != nil { | ||||
| 		return acme.Account{}, err | ||||
| 	} | ||||
| 	keyBytes, err := am.config.Storage.Load(am.storageKeyUserPrivateKey(ca, email)) | ||||
| 	if err != nil { | ||||
| 		if _, ok := err.(ErrNotExist); ok { | ||||
| 			return am.newAccount(email) | ||||
| 		} | ||||
| 		return acme.Account{}, err | ||||
| 	} | ||||
|  | ||||
| @@ -58,54 +66,6 @@ func (am *ACMEManager) getAccount(ca, email string) (acme.Account, error) { | ||||
| 		return acct, fmt.Errorf("could not decode account's private key: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	// TODO: July 2020 - transition to new ACME lib and account structure; | ||||
| 	// for a while, we will need to convert old accounts to new structure | ||||
| 	acct, err = am.transitionAccountToACMEzJuly2020Format(ca, acct, regBytes) | ||||
| 	if err != nil { | ||||
| 		return acct, fmt.Errorf("one-time account transition: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	return acct, err | ||||
| } | ||||
|  | ||||
| // TODO: this is a temporary transition helper starting July 2020. | ||||
| // It can go away when we think enough time has passed that most active assets have transitioned. | ||||
| func (am *ACMEManager) transitionAccountToACMEzJuly2020Format(ca string, acct acme.Account, regBytes []byte) (acme.Account, error) { | ||||
| 	if acct.Status != "" && acct.Location != "" { | ||||
| 		return acct, nil | ||||
| 	} | ||||
|  | ||||
| 	var oldAcct struct { | ||||
| 		Email        string `json:"Email"` | ||||
| 		Registration struct { | ||||
| 			Body struct { | ||||
| 				Status                 string          `json:"status"` | ||||
| 				TermsOfServiceAgreed   bool            `json:"termsOfServiceAgreed"` | ||||
| 				Orders                 string          `json:"orders"` | ||||
| 				ExternalAccountBinding json.RawMessage `json:"externalAccountBinding"` | ||||
| 			} `json:"body"` | ||||
| 			URI string `json:"uri"` | ||||
| 		} `json:"Registration"` | ||||
| 	} | ||||
| 	err := json.Unmarshal(regBytes, &oldAcct) | ||||
| 	if err != nil { | ||||
| 		return acct, fmt.Errorf("decoding into old account type: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	acct.Status = oldAcct.Registration.Body.Status | ||||
| 	acct.TermsOfServiceAgreed = oldAcct.Registration.Body.TermsOfServiceAgreed | ||||
| 	acct.Location = oldAcct.Registration.URI | ||||
| 	acct.ExternalAccountBinding = oldAcct.Registration.Body.ExternalAccountBinding | ||||
| 	acct.Orders = oldAcct.Registration.Body.Orders | ||||
| 	if oldAcct.Email != "" { | ||||
| 		acct.Contact = []string{"mailto:" + oldAcct.Email} | ||||
| 	} | ||||
|  | ||||
| 	err = am.saveAccount(ca, acct) | ||||
| 	if err != nil { | ||||
| 		return acct, fmt.Errorf("saving converted account: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	return acct, nil | ||||
| } | ||||
|  | ||||
| @@ -124,6 +84,71 @@ func (*ACMEManager) newAccount(email string) (acme.Account, error) { | ||||
| 	return acct, nil | ||||
| } | ||||
|  | ||||
| // GetAccount first tries loading the account with the associated private key from storage. | ||||
| // If it does not exist in storage, it will be retrieved from the ACME server and added to storage. | ||||
| // The account must already exist; it does not create a new account. | ||||
| func (am *ACMEManager) GetAccount(ctx context.Context, privateKeyPEM []byte) (acme.Account, error) { | ||||
| 	account, err := am.loadAccountByKey(ctx, privateKeyPEM) | ||||
| 	if err != nil { | ||||
| 		if _, ok := err.(ErrNotExist); ok { | ||||
| 			account, err = am.lookUpAccount(ctx, privateKeyPEM) | ||||
| 		} else { | ||||
| 			return account, err | ||||
| 		} | ||||
| 	} | ||||
| 	return account, err | ||||
| } | ||||
|  | ||||
| // loadAccountByKey loads the account with the given private key from storage, if it exists. | ||||
| // If it does not exist, an error of type ErrNotExist is returned. This is not very efficient | ||||
| // for lots of accounts. | ||||
| func (am *ACMEManager) loadAccountByKey(ctx context.Context, privateKeyPEM []byte) (acme.Account, error) { | ||||
| 	accountList, err := am.config.Storage.List(am.storageKeyUsersPrefix(am.CA), false) | ||||
| 	if err != nil { | ||||
| 		return acme.Account{}, err | ||||
| 	} | ||||
| 	for _, accountFolderKey := range accountList { | ||||
| 		email := path.Base(accountFolderKey) | ||||
| 		keyBytes, err := am.config.Storage.Load(am.storageKeyUserPrivateKey(am.CA, email)) | ||||
| 		if err != nil { | ||||
| 			return acme.Account{}, err | ||||
| 		} | ||||
| 		if bytes.Equal(bytes.TrimSpace(keyBytes), bytes.TrimSpace(privateKeyPEM)) { | ||||
| 			return am.loadAccount(am.CA, email) | ||||
| 		} | ||||
| 	} | ||||
| 	return acme.Account{}, ErrNotExist(fmt.Errorf("no account found with that key")) | ||||
| } | ||||
|  | ||||
| // lookUpAccount looks up the account associated with privateKeyPEM from the ACME server. | ||||
| // If the account is found by the server, it will be saved to storage and returned. | ||||
| func (am *ACMEManager) lookUpAccount(ctx context.Context, privateKeyPEM []byte) (acme.Account, error) { | ||||
| 	client, err := am.newACMEClient(false) | ||||
| 	if err != nil { | ||||
| 		return acme.Account{}, fmt.Errorf("creating ACME client: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	privateKey, err := decodePrivateKey([]byte(privateKeyPEM)) | ||||
| 	if err != nil { | ||||
| 		return acme.Account{}, fmt.Errorf("decoding private key: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	// look up the account | ||||
| 	account := acme.Account{PrivateKey: privateKey} | ||||
| 	account, err = client.GetAccount(ctx, account) | ||||
| 	if err != nil { | ||||
| 		return acme.Account{}, fmt.Errorf("looking up account with server: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	// save the account details to storage | ||||
| 	err = am.saveAccount(client.Directory, account) | ||||
| 	if err != nil { | ||||
| 		return account, fmt.Errorf("could not save account to storage: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	return account, nil | ||||
| } | ||||
|  | ||||
| // saveAccount persists an ACME account's info and private key to storage. | ||||
| // It does NOT register the account via ACME or prompt the user. | ||||
| func (am *ACMEManager) saveAccount(ca string, account acme.Account) error { | ||||
| @@ -242,8 +267,12 @@ func (am *ACMEManager) askUserAgreement(agreementURL string) bool { | ||||
| 	return answer == "y" || answer == "yes" | ||||
| } | ||||
|  | ||||
| func storageKeyACMECAPrefix(issuerKey string) string { | ||||
| 	return path.Join(prefixACME, StorageKeys.Safe(issuerKey)) | ||||
| } | ||||
|  | ||||
| func (am *ACMEManager) storageKeyCAPrefix(caURL string) string { | ||||
| 	return path.Join(prefixACME, StorageKeys.Safe(am.issuerKey(caURL))) | ||||
| 	return storageKeyACMECAPrefix(am.issuerKey(caURL)) | ||||
| } | ||||
|  | ||||
| func (am *ACMEManager) storageKeyUsersPrefix(caURL string) string { | ||||
| @@ -305,7 +334,8 @@ func (am *ACMEManager) mostRecentAccountEmail(caURL string) (string, bool) { | ||||
| 	// get all the key infos ahead of sorting, because | ||||
| 	// we might filter some out | ||||
| 	stats := make(map[string]KeyInfo) | ||||
| 	for i, u := range accountList { | ||||
| 	for i := 0; i < len(accountList); i++ { | ||||
| 		u := accountList[i] | ||||
| 		keyInfo, err := am.config.Storage.Stat(u) | ||||
| 		if err != nil { | ||||
| 			continue | ||||
| @@ -318,6 +348,7 @@ func (am *ACMEManager) mostRecentAccountEmail(caURL string) (string, bool) { | ||||
| 			// frankly one's OS shouldn't mess with the data folder | ||||
| 			// in the first place. | ||||
| 			accountList = append(accountList[:i], accountList[i+1:]...) | ||||
| 			i-- | ||||
| 			continue | ||||
| 		} | ||||
| 		stats[u] = keyInfo | ||||
|   | ||||
							
								
								
									
										193
									
								
								vendor/github.com/caddyserver/certmagic/acmeclient.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										193
									
								
								vendor/github.com/caddyserver/certmagic/acmeclient.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -37,19 +37,104 @@ func init() { | ||||
| 	weakrand.Seed(time.Now().UnixNano()) | ||||
| } | ||||
|  | ||||
| // acmeClient holds state necessary for us to perform | ||||
| // ACME operations for certificate management. Call | ||||
| // ACMEManager.newACMEClient() to get a valid one to . | ||||
| // acmeClient holds state necessary to perform ACME operations | ||||
| // for certificate management with an ACME account. Call | ||||
| // ACMEManager.newACMEClientWithAccount() to get a valid one. | ||||
| type acmeClient struct { | ||||
| 	mgr        *ACMEManager | ||||
| 	acmeClient *acmez.Client | ||||
| 	account    acme.Account | ||||
| } | ||||
|  | ||||
| // newACMEClient creates the underlying ACME library client type. | ||||
| // If useTestCA is true, am.TestCA will be used if it is set; | ||||
| // otherwise, the primary CA will still be used. | ||||
| func (am *ACMEManager) newACMEClient(ctx context.Context, useTestCA, interactive bool) (*acmeClient, error) { | ||||
| // newACMEClientWithAccount creates an ACME client ready to use with an account, including | ||||
| // loading one from storage or registering a new account with the CA if necessary. If | ||||
| // useTestCA is true, am.TestCA will be used if set; otherwise, the primary CA will be used. | ||||
| func (am *ACMEManager) newACMEClientWithAccount(ctx context.Context, useTestCA, interactive bool) (*acmeClient, error) { | ||||
| 	// first, get underlying ACME client | ||||
| 	client, err := am.newACMEClient(useTestCA) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	// look up or create the ACME account | ||||
| 	var account acme.Account | ||||
| 	if am.AccountKeyPEM != "" { | ||||
| 		account, err = am.GetAccount(ctx, []byte(am.AccountKeyPEM)) | ||||
| 	} else { | ||||
| 		account, err = am.getAccount(client.Directory, am.Email) | ||||
| 	} | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("getting ACME account: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	// register account if it is new | ||||
| 	if account.Status == "" { | ||||
| 		if am.NewAccountFunc != nil { | ||||
| 			account, err = am.NewAccountFunc(ctx, am, account) | ||||
| 			if err != nil { | ||||
| 				return nil, fmt.Errorf("account pre-registration callback: %v", err) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// agree to terms | ||||
| 		if interactive { | ||||
| 			if !am.Agreed { | ||||
| 				var termsURL string | ||||
| 				dir, err := client.GetDirectory(ctx) | ||||
| 				if err != nil { | ||||
| 					return nil, fmt.Errorf("getting directory: %w", err) | ||||
| 				} | ||||
| 				if dir.Meta != nil { | ||||
| 					termsURL = dir.Meta.TermsOfService | ||||
| 				} | ||||
| 				if termsURL != "" { | ||||
| 					am.Agreed = am.askUserAgreement(termsURL) | ||||
| 					if !am.Agreed { | ||||
| 						return nil, fmt.Errorf("user must agree to CA terms") | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} else { | ||||
| 			// can't prompt a user who isn't there; they should | ||||
| 			// have reviewed the terms beforehand | ||||
| 			am.Agreed = true | ||||
| 		} | ||||
| 		account.TermsOfServiceAgreed = am.Agreed | ||||
|  | ||||
| 		// associate account with external binding, if configured | ||||
| 		if am.ExternalAccount != nil { | ||||
| 			err := account.SetExternalAccountBinding(ctx, client.Client, *am.ExternalAccount) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// create account | ||||
| 		account, err = client.NewAccount(ctx, account) | ||||
| 		if err != nil { | ||||
| 			return nil, fmt.Errorf("registering account %v with server: %w", account.Contact, err) | ||||
| 		} | ||||
|  | ||||
| 		// persist the account to storage | ||||
| 		err = am.saveAccount(client.Directory, account) | ||||
| 		if err != nil { | ||||
| 			return nil, fmt.Errorf("could not save account %v: %v", account.Contact, err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	c := &acmeClient{ | ||||
| 		mgr:        am, | ||||
| 		acmeClient: client, | ||||
| 		account:    account, | ||||
| 	} | ||||
|  | ||||
| 	return c, nil | ||||
| } | ||||
|  | ||||
| // newACMEClient creates a new underlying ACME client using the settings in am, | ||||
| // independent of any particular ACME account. If useTestCA is true, am.TestCA | ||||
| // will be used if it is set; otherwise, the primary CA will be used. | ||||
| func (am *ACMEManager) newACMEClient(useTestCA bool) (*acmez.Client, error) { | ||||
| 	// ensure defaults are filled in | ||||
| 	var caURL string | ||||
| 	if useTestCA { | ||||
| @@ -78,12 +163,6 @@ func (am *ACMEManager) newACMEClient(ctx context.Context, useTestCA, interactive | ||||
| 		return nil, fmt.Errorf("%s: insecure CA URL (HTTPS required)", caURL) | ||||
| 	} | ||||
|  | ||||
| 	// look up or create the ACME account | ||||
| 	account, err := am.getAccount(caURL, am.Email) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("getting ACME account: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	// set up the dialers and resolver for the ACME client's HTTP client | ||||
| 	dialer := &net.Dialer{ | ||||
| 		Timeout:   30 * time.Second, | ||||
| @@ -153,12 +232,12 @@ func (am *ACMEManager) newACMEClient(ctx context.Context, useTestCA, interactive | ||||
| 				useHTTPPort = am.AltHTTPPort | ||||
| 			} | ||||
| 			client.ChallengeSolvers[acme.ChallengeTypeHTTP01] = distributedSolver{ | ||||
| 				acmeManager: am, | ||||
| 				storage:                am.config.Storage, | ||||
| 				storageKeyIssuerPrefix: am.storageKeyCAPrefix(client.Directory), | ||||
| 				solver: &httpSolver{ | ||||
| 					acmeManager: am, | ||||
| 					address:     net.JoinHostPort(am.ListenHost, strconv.Itoa(useHTTPPort)), | ||||
| 				}, | ||||
| 				caURL: client.Directory, | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| @@ -172,12 +251,12 @@ func (am *ACMEManager) newACMEClient(ctx context.Context, useTestCA, interactive | ||||
| 				useTLSALPNPort = am.AltTLSALPNPort | ||||
| 			} | ||||
| 			client.ChallengeSolvers[acme.ChallengeTypeTLSALPN01] = distributedSolver{ | ||||
| 				acmeManager: am, | ||||
| 				storage:                am.config.Storage, | ||||
| 				storageKeyIssuerPrefix: am.storageKeyCAPrefix(client.Directory), | ||||
| 				solver: &tlsALPNSolver{ | ||||
| 					config:  am.config, | ||||
| 					address: net.JoinHostPort(am.ListenHost, strconv.Itoa(useTLSALPNPort)), | ||||
| 				}, | ||||
| 				caURL: client.Directory, | ||||
| 			} | ||||
| 		} | ||||
| 	} else { | ||||
| @@ -185,68 +264,26 @@ func (am *ACMEManager) newACMEClient(ctx context.Context, useTestCA, interactive | ||||
| 		client.ChallengeSolvers[acme.ChallengeTypeDNS01] = am.DNS01Solver | ||||
| 	} | ||||
|  | ||||
| 	// register account if it is new | ||||
| 	if account.Status == "" { | ||||
| 		if am.NewAccountFunc != nil { | ||||
| 			err = am.NewAccountFunc(ctx, am, account) | ||||
| 			if err != nil { | ||||
| 				return nil, fmt.Errorf("account pre-registration callback: %v", err) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// agree to terms | ||||
| 		if interactive { | ||||
| 			if !am.Agreed { | ||||
| 				var termsURL string | ||||
| 				dir, err := client.GetDirectory(ctx) | ||||
| 				if err != nil { | ||||
| 					return nil, fmt.Errorf("getting directory: %w", err) | ||||
| 				} | ||||
| 				if dir.Meta != nil { | ||||
| 					termsURL = dir.Meta.TermsOfService | ||||
| 				} | ||||
| 				if termsURL != "" { | ||||
| 					am.Agreed = am.askUserAgreement(termsURL) | ||||
| 					if !am.Agreed { | ||||
| 						return nil, fmt.Errorf("user must agree to CA terms") | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} else { | ||||
| 			// can't prompt a user who isn't there; they should | ||||
| 			// have reviewed the terms beforehand | ||||
| 			am.Agreed = true | ||||
| 		} | ||||
| 		account.TermsOfServiceAgreed = am.Agreed | ||||
|  | ||||
| 		// associate account with external binding, if configured | ||||
| 		if am.ExternalAccount != nil { | ||||
| 			err := account.SetExternalAccountBinding(ctx, client.Client, *am.ExternalAccount) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// create account | ||||
| 		account, err = client.NewAccount(ctx, account) | ||||
| 		if err != nil { | ||||
| 			return nil, fmt.Errorf("registering account with server: %w", err) | ||||
| 		} | ||||
|  | ||||
| 		// persist the account to storage | ||||
| 		err = am.saveAccount(caURL, account) | ||||
| 		if err != nil { | ||||
| 			return nil, fmt.Errorf("could not save account: %v", err) | ||||
| 		} | ||||
| 	// wrap solvers in our wrapper so that we can keep track of challenge | ||||
| 	// info: this is useful for solving challenges globally as a process; | ||||
| 	// for example, usually there is only one process that can solve the | ||||
| 	// HTTP and TLS-ALPN challenges, and only one server in that process | ||||
| 	// that can bind the necessary port(s), so if a server listening on | ||||
| 	// a different port needed a certificate, it would have to know about | ||||
| 	// the other server listening on that port, and somehow convey its | ||||
| 	// challenge info or share its config, but this isn't always feasible; | ||||
| 	// what the wrapper does is it accesses a global challenge memory so | ||||
| 	// that unrelated servers in this process can all solve each others' | ||||
| 	// challenges without having to know about each other - Caddy's admin | ||||
| 	// endpoint uses this functionality since it and the HTTP/TLS modules | ||||
| 	// do not know about each other | ||||
| 	// (doing this here in a separate loop ensures that even if we expose | ||||
| 	// solver config to users later, we will even wrap their own solvers) | ||||
| 	for name, solver := range client.ChallengeSolvers { | ||||
| 		client.ChallengeSolvers[name] = solverWrapper{solver} | ||||
| 	} | ||||
|  | ||||
| 	c := &acmeClient{ | ||||
| 		mgr:        am, | ||||
| 		acmeClient: client, | ||||
| 		account:    account, | ||||
| 	} | ||||
|  | ||||
| 	return c, nil | ||||
| 	return client, nil | ||||
| } | ||||
|  | ||||
| func (c *acmeClient) throttle(ctx context.Context, names []string) error { | ||||
| @@ -325,7 +362,7 @@ var ( | ||||
|  | ||||
| 	// RateLimitEvents is how many new events can be allowed | ||||
| 	// in RateLimitEventsWindow. | ||||
| 	RateLimitEvents = 10 | ||||
| 	RateLimitEvents = 20 | ||||
|  | ||||
| 	// RateLimitEventsWindow is the size of the sliding | ||||
| 	// window that throttles events. | ||||
|   | ||||
							
								
								
									
										154
									
								
								vendor/github.com/caddyserver/certmagic/acmemanager.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										154
									
								
								vendor/github.com/caddyserver/certmagic/acmemanager.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -7,6 +7,7 @@ import ( | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"net/url" | ||||
| 	"sort" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| @@ -19,7 +20,7 @@ import ( | ||||
| // Issuer, and Revoker interfaces. | ||||
| // | ||||
| // It is NOT VALID to use an ACMEManager without calling NewACMEManager(). | ||||
| // It fills in default values from DefaultACME as well as setting up | ||||
| // It fills in any default values from DefaultACME as well as setting up | ||||
| // internal state that is necessary for valid use. Always call | ||||
| // NewACMEManager() to get a valid ACMEManager value. | ||||
| type ACMEManager struct { | ||||
| @@ -37,6 +38,12 @@ type ACMEManager struct { | ||||
| 	// selecting an existing ACME server account | ||||
| 	Email string | ||||
|  | ||||
| 	// The PEM-encoded private key of the ACME | ||||
| 	// account to use; only needed if the account | ||||
| 	// is already created on the server and | ||||
| 	// can be looked up with the ACME protocol | ||||
| 	AccountKeyPEM string | ||||
|  | ||||
| 	// Set to true if agreed to the CA's | ||||
| 	// subscriber agreement | ||||
| 	Agreed bool | ||||
| @@ -92,9 +99,13 @@ type ACMEManager struct { | ||||
| 	// Callback function that is called before a | ||||
| 	// new ACME account is registered with the CA; | ||||
| 	// it allows for last-second config changes | ||||
| 	// of the ACMEManager (TODO: this feature is | ||||
| 	// still EXPERIMENTAL and subject to change) | ||||
| 	NewAccountFunc func(context.Context, *ACMEManager, acme.Account) error | ||||
| 	// of the ACMEManager and the Account. | ||||
| 	// (TODO: this feature is still EXPERIMENTAL and subject to change) | ||||
| 	NewAccountFunc func(context.Context, *ACMEManager, acme.Account) (acme.Account, error) | ||||
|  | ||||
| 	// Preferences for selecting alternate | ||||
| 	// certificate chains | ||||
| 	PreferredChains ChainPreference | ||||
|  | ||||
| 	// Set a logger to enable logging | ||||
| 	Logger *zap.Logger | ||||
| @@ -105,10 +116,12 @@ type ACMEManager struct { | ||||
|  | ||||
| // NewACMEManager constructs a valid ACMEManager based on a template | ||||
| // configuration; any empty values will be filled in by defaults in | ||||
| // DefaultACME. The associated config is also required. | ||||
| // DefaultACME, and if any required values are still empty, sensible | ||||
| // defaults will be used. | ||||
| // | ||||
| // Typically, you'll create the Config first, then call NewACMEManager(), | ||||
| // then assign the return value to the Issuer/Revoker fields of the Config. | ||||
| // Typically, you'll create the Config first with New() or NewDefault(), | ||||
| // then call NewACMEManager(), then assign the return value to the Issuers | ||||
| // field of the Config. | ||||
| func NewACMEManager(cfg *Config, template ACMEManager) *ACMEManager { | ||||
| 	if cfg == nil { | ||||
| 		panic("cannot make valid ACMEManager without an associated CertMagic config") | ||||
| @@ -126,6 +139,9 @@ func NewACMEManager(cfg *Config, template ACMEManager) *ACMEManager { | ||||
| 	if template.Email == "" { | ||||
| 		template.Email = DefaultACME.Email | ||||
| 	} | ||||
| 	if template.AccountKeyPEM == "" { | ||||
| 		template.AccountKeyPEM = DefaultACME.AccountKeyPEM | ||||
| 	} | ||||
| 	if !template.Agreed { | ||||
| 		template.Agreed = DefaultACME.Agreed | ||||
| 	} | ||||
| @@ -175,7 +191,7 @@ func (am *ACMEManager) IssuerKey() string { | ||||
| 	return am.issuerKey(am.CA) | ||||
| } | ||||
|  | ||||
| func (am *ACMEManager) issuerKey(ca string) string { | ||||
| func (*ACMEManager) issuerKey(ca string) string { | ||||
| 	key := ca | ||||
| 	if caURL, err := url.Parse(key); err == nil { | ||||
| 		key = caURL.Host | ||||
| @@ -202,11 +218,11 @@ func (am *ACMEManager) issuerKey(ca string) string { | ||||
| // batch is eligible for certificates if using Let's Encrypt. | ||||
| // It also ensures that an email address is available. | ||||
| func (am *ACMEManager) PreCheck(_ context.Context, names []string, interactive bool) error { | ||||
| 	letsEncrypt := strings.Contains(am.CA, "api.letsencrypt.org") | ||||
| 	if letsEncrypt { | ||||
| 	publicCA := strings.Contains(am.CA, "api.letsencrypt.org") || strings.Contains(am.CA, "acme.zerossl.com") | ||||
| 	if publicCA { | ||||
| 		for _, name := range names { | ||||
| 			if !SubjectQualifiesForPublicCert(name) { | ||||
| 				return fmt.Errorf("subject does not qualify for a Let's Encrypt certificate: %s", name) | ||||
| 				return fmt.Errorf("subject does not qualify for a public certificate: %s", name) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| @@ -282,7 +298,7 @@ func (am *ACMEManager) Issue(ctx context.Context, csr *x509.CertificateRequest) | ||||
| } | ||||
|  | ||||
| func (am *ACMEManager) doIssue(ctx context.Context, csr *x509.CertificateRequest, useTestCA bool) (*IssuedCertificate, bool, error) { | ||||
| 	client, err := am.newACMEClient(ctx, useTestCA, false) | ||||
| 	client, err := am.newACMEClientWithAccount(ctx, useTestCA, false) | ||||
| 	if err != nil { | ||||
| 		return nil, false, err | ||||
| 	} | ||||
| @@ -300,20 +316,103 @@ func (am *ACMEManager) doIssue(ctx context.Context, csr *x509.CertificateRequest | ||||
| 	if err != nil { | ||||
| 		return nil, usingTestCA, fmt.Errorf("%v %w (ca=%s)", nameSet, err, client.acmeClient.Directory) | ||||
| 	} | ||||
| 	if len(certChains) == 0 { | ||||
| 		return nil, usingTestCA, fmt.Errorf("no certificate chains") | ||||
| 	} | ||||
|  | ||||
| 	preferredChain := am.selectPreferredChain(certChains) | ||||
|  | ||||
| 	// TODO: ACME server could in theory issue a cert with multiple chains, | ||||
| 	// but we don't (yet) have a way to choose one, so just use first one | ||||
| 	ic := &IssuedCertificate{ | ||||
| 		Certificate: certChains[0].ChainPEM, | ||||
| 		Metadata:    certChains[0], | ||||
| 		Certificate: preferredChain.ChainPEM, | ||||
| 		Metadata:    preferredChain, | ||||
| 	} | ||||
|  | ||||
| 	return ic, usingTestCA, nil | ||||
| } | ||||
|  | ||||
| // selectPreferredChain sorts and then filters the certificate chains to find the optimal | ||||
| // chain preferred by the client. If there's only one chain, that is returned without any | ||||
| // processing. If there are no matches, the first chain is returned. | ||||
| func (am *ACMEManager) selectPreferredChain(certChains []acme.Certificate) acme.Certificate { | ||||
| 	if len(certChains) == 1 { | ||||
| 		if am.Logger != nil && (len(am.PreferredChains.AnyCommonName) > 0 || len(am.PreferredChains.RootCommonName) > 0) { | ||||
| 			am.Logger.Debug("there is only one chain offered; selecting it regardless of preferences", | ||||
| 				zap.String("chain_url", certChains[0].URL)) | ||||
| 		} | ||||
| 		return certChains[0] | ||||
| 	} | ||||
|  | ||||
| 	if am.PreferredChains.Smallest != nil { | ||||
| 		if *am.PreferredChains.Smallest { | ||||
| 			sort.Slice(certChains, func(i, j int) bool { | ||||
| 				return len(certChains[i].ChainPEM) < len(certChains[j].ChainPEM) | ||||
| 			}) | ||||
| 		} else { | ||||
| 			sort.Slice(certChains, func(i, j int) bool { | ||||
| 				return len(certChains[i].ChainPEM) > len(certChains[j].ChainPEM) | ||||
| 			}) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if len(am.PreferredChains.AnyCommonName) > 0 || len(am.PreferredChains.RootCommonName) > 0 { | ||||
| 		// in order to inspect, we need to decode their PEM contents | ||||
| 		decodedChains := make([][]*x509.Certificate, len(certChains)) | ||||
| 		for i, chain := range certChains { | ||||
| 			certs, err := parseCertsFromPEMBundle(chain.ChainPEM) | ||||
| 			if err != nil { | ||||
| 				if am.Logger != nil { | ||||
| 					am.Logger.Error("unable to parse PEM certificate chain", | ||||
| 						zap.Int("chain", i), | ||||
| 						zap.Error(err)) | ||||
| 				} | ||||
| 				continue | ||||
| 			} | ||||
| 			decodedChains[i] = certs | ||||
| 		} | ||||
|  | ||||
| 		if len(am.PreferredChains.AnyCommonName) > 0 { | ||||
| 			for _, prefAnyCN := range am.PreferredChains.AnyCommonName { | ||||
| 				for i, chain := range decodedChains { | ||||
| 					for _, cert := range chain { | ||||
| 						if cert.Issuer.CommonName == prefAnyCN { | ||||
| 							if am.Logger != nil { | ||||
| 								am.Logger.Debug("found preferred certificate chain by issuer common name", | ||||
| 									zap.String("preference", prefAnyCN), | ||||
| 									zap.Int("chain", i)) | ||||
| 							} | ||||
| 							return certChains[i] | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if len(am.PreferredChains.RootCommonName) > 0 { | ||||
| 			for _, prefRootCN := range am.PreferredChains.RootCommonName { | ||||
| 				for i, chain := range decodedChains { | ||||
| 					if chain[len(chain)-1].Issuer.CommonName == prefRootCN { | ||||
| 						if am.Logger != nil { | ||||
| 							am.Logger.Debug("found preferred certificate chain by root common name", | ||||
| 								zap.String("preference", prefRootCN), | ||||
| 								zap.Int("chain", i)) | ||||
| 						} | ||||
| 						return certChains[i] | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if am.Logger != nil { | ||||
| 			am.Logger.Warn("did not find chain matching preferences; using first") | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return certChains[0] | ||||
| } | ||||
|  | ||||
| // Revoke implements the Revoker interface. It revokes the given certificate. | ||||
| func (am *ACMEManager) Revoke(ctx context.Context, cert CertificateResource, reason int) error { | ||||
| 	client, err := am.newACMEClient(ctx, false, false) | ||||
| 	client, err := am.newACMEClientWithAccount(ctx, false, false) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| @@ -326,8 +425,24 @@ func (am *ACMEManager) Revoke(ctx context.Context, cert CertificateResource, rea | ||||
| 	return client.revoke(ctx, certs[0], reason) | ||||
| } | ||||
|  | ||||
| // DefaultACME specifies the default settings | ||||
| // to use for ACMEManagers. | ||||
| // ChainPreference describes the client's preferred certificate chain, | ||||
| // useful if the CA offers alternate chains. The first matching chain | ||||
| // will be selected. | ||||
| type ChainPreference struct { | ||||
| 	// Prefer chains with the fewest number of bytes. | ||||
| 	Smallest *bool | ||||
|  | ||||
| 	// Select first chain having a root with one of | ||||
| 	// these common names. | ||||
| 	RootCommonName []string | ||||
|  | ||||
| 	// Select first chain that has any issuer with one | ||||
| 	// of these common names. | ||||
| 	AnyCommonName []string | ||||
| } | ||||
|  | ||||
| // DefaultACME specifies default settings to use for ACMEManagers. | ||||
| // Using this value is optional but can be convenient. | ||||
| var DefaultACME = ACMEManager{ | ||||
| 	CA:     LetsEncryptProductionCA, | ||||
| 	TestCA: LetsEncryptStagingCA, | ||||
| @@ -337,6 +452,7 @@ var DefaultACME = ACMEManager{ | ||||
| const ( | ||||
| 	LetsEncryptStagingCA    = "https://acme-staging-v02.api.letsencrypt.org/directory" | ||||
| 	LetsEncryptProductionCA = "https://acme-v02.api.letsencrypt.org/directory" | ||||
| 	ZeroSSLProductionCA     = "https://acme.zerossl.com/v2/DV90" | ||||
| ) | ||||
|  | ||||
| // prefixACME is the storage key prefix used for ACME-specific assets. | ||||
|   | ||||
							
								
								
									
										76
									
								
								vendor/github.com/caddyserver/certmagic/certificates.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										76
									
								
								vendor/github.com/caddyserver/certmagic/certificates.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -113,10 +113,11 @@ func (cfg *Config) CacheManagedCertificate(domain string) (Certificate, error) { | ||||
| 	return cert, nil | ||||
| } | ||||
|  | ||||
| // loadManagedCertificate loads the managed certificate for domain, | ||||
| // but it does not add it to the cache. It just loads from storage. | ||||
| // loadManagedCertificate loads the managed certificate for domain from any | ||||
| // of the configured issuers' storage locations, but it does not add it to | ||||
| // the cache. It just loads from storage and returns it. | ||||
| func (cfg *Config) loadManagedCertificate(domain string) (Certificate, error) { | ||||
| 	certRes, err := cfg.loadCertResource(domain) | ||||
| 	certRes, err := cfg.loadCertResourceAnyIssuer(domain) | ||||
| 	if err != nil { | ||||
| 		return Certificate{}, err | ||||
| 	} | ||||
| @@ -154,7 +155,7 @@ func (cfg *Config) CacheUnmanagedTLSCertificate(tlsCert tls.Certificate, tags [] | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	_, err = stapleOCSP(cfg.Storage, &cert, nil) | ||||
| 	_, err = stapleOCSP(cfg.OCSP, cfg.Storage, &cert, nil) | ||||
| 	if err != nil && cfg.Logger != nil { | ||||
| 		cfg.Logger.Warn("stapling OCSP", zap.Error(err)) | ||||
| 	} | ||||
| @@ -202,7 +203,7 @@ func (cfg Config) makeCertificateWithOCSP(certPEMBlock, keyPEMBlock []byte) (Cer | ||||
| 	if err != nil { | ||||
| 		return cert, err | ||||
| 	} | ||||
| 	_, err = stapleOCSP(cfg.Storage, &cert, certPEMBlock) | ||||
| 	_, err = stapleOCSP(cfg.OCSP, cfg.Storage, &cert, certPEMBlock) | ||||
| 	if err != nil && cfg.Logger != nil { | ||||
| 		cfg.Logger.Warn("stapling OCSP", zap.Error(err)) | ||||
| 	} | ||||
| @@ -295,19 +296,12 @@ func fillCertFromLeaf(cert *Certificate, tlsCert tls.Certificate) error { | ||||
| // meantime, and it would be a good idea to simply load the cert | ||||
| // into our cache rather than repeating the renewal process again. | ||||
| func (cfg *Config) managedCertInStorageExpiresSoon(cert Certificate) (bool, error) { | ||||
| 	certRes, err := cfg.loadCertResource(cert.Names[0]) | ||||
| 	certRes, err := cfg.loadCertResourceAnyIssuer(cert.Names[0]) | ||||
| 	if err != nil { | ||||
| 		return false, err | ||||
| 	} | ||||
| 	tlsCert, err := tls.X509KeyPair(certRes.CertificatePEM, certRes.PrivateKeyPEM) | ||||
| 	if err != nil { | ||||
| 		return false, err | ||||
| 	} | ||||
| 	leaf, err := x509.ParseCertificate(tlsCert.Certificate[0]) | ||||
| 	if err != nil { | ||||
| 		return false, err | ||||
| 	} | ||||
| 	return currentlyInRenewalWindow(leaf.NotBefore, leaf.NotAfter, cfg.RenewalWindowRatio), nil | ||||
| 	_, needsRenew := cfg.managedCertNeedsRenewal(certRes) | ||||
| 	return needsRenew, nil | ||||
| } | ||||
|  | ||||
| // reloadManagedCertificate reloads the certificate corresponding to the name(s) | ||||
| @@ -341,8 +335,9 @@ func SubjectQualifiesForCert(subj string) bool { | ||||
| 		!strings.HasPrefix(subj, ".") && | ||||
| 		!strings.HasSuffix(subj, ".") && | ||||
|  | ||||
| 		// if it has a wildcard, must be a left-most label | ||||
| 		(!strings.Contains(subj, "*") || strings.HasPrefix(subj, "*.")) && | ||||
| 		// if it has a wildcard, must be a left-most label (or exactly "*" | ||||
| 		// which won't be trusted by browsers but still technically works) | ||||
| 		(!strings.Contains(subj, "*") || strings.HasPrefix(subj, "*.") || subj == "*") && | ||||
|  | ||||
| 		// must not contain other common special characters | ||||
| 		!strings.ContainsAny(subj, "()[]{}<> \t\n\"\\!@#$%^&|;'+=") | ||||
| @@ -356,32 +351,45 @@ func SubjectQualifiesForCert(subj string) bool { | ||||
| // allowed, as long as they conform to CABF requirements (only | ||||
| // one wildcard label, and it must be the left-most label). | ||||
| func SubjectQualifiesForPublicCert(subj string) bool { | ||||
| 	// must at least qualify for certificate | ||||
| 	// must at least qualify for a certificate | ||||
| 	return SubjectQualifiesForCert(subj) && | ||||
|  | ||||
| 		// localhost is ineligible | ||||
| 		subj != "localhost" && | ||||
|  | ||||
| 		// .localhost TLD is ineligible | ||||
| 		!strings.HasSuffix(subj, ".localhost") && | ||||
|  | ||||
| 		// .local TLD is ineligible | ||||
| 		!strings.HasSuffix(subj, ".local") && | ||||
|  | ||||
| 		// only one wildcard label allowed, and it must be left-most | ||||
| 		(!strings.Contains(subj, "*") || | ||||
| 			(strings.Count(subj, "*") == 1 && | ||||
| 				len(subj) > 2 && | ||||
| 				strings.HasPrefix(subj, "*."))) && | ||||
| 		// localhost, .localhost TLD, and .local TLD are ineligible | ||||
| 		!SubjectIsInternal(subj) && | ||||
|  | ||||
| 		// cannot be an IP address (as of yet), see | ||||
| 		// https://community.letsencrypt.org/t/certificate-for-static-ip/84/2?u=mholt | ||||
| 		net.ParseIP(subj) == nil | ||||
| 		!SubjectIsIP(subj) && | ||||
|  | ||||
| 		// only one wildcard label allowed, and it must be left-most, with 3+ labels | ||||
| 		(!strings.Contains(subj, "*") || | ||||
| 			(strings.Count(subj, "*") == 1 && | ||||
| 				strings.Count(subj, ".") > 1 && | ||||
| 				len(subj) > 2 && | ||||
| 				strings.HasPrefix(subj, "*."))) | ||||
| } | ||||
|  | ||||
| // SubjectIsIP returns true if subj is an IP address. | ||||
| func SubjectIsIP(subj string) bool { | ||||
| 	return net.ParseIP(subj) != nil | ||||
| } | ||||
|  | ||||
| // SubjectIsInternal returns true if subj is an internal-facing | ||||
| // hostname or address. | ||||
| func SubjectIsInternal(subj string) bool { | ||||
| 	return subj == "localhost" || | ||||
| 		strings.HasSuffix(subj, ".localhost") || | ||||
| 		strings.HasSuffix(subj, ".local") | ||||
| } | ||||
|  | ||||
| // MatchWildcard returns true if subject (a candidate DNS name) | ||||
| // matches wildcard (a reference DNS name), mostly according to | ||||
| // RFC6125-compliant wildcard rules. | ||||
| // RFC 6125-compliant wildcard rules. See also RFC 2818 which | ||||
| // states that IP addresses must match exactly, but this function | ||||
| // does not attempt to distinguish IP addresses from internal or | ||||
| // external DNS names that happen to look like IP addresses. | ||||
| // It uses DNS wildcard matching logic. | ||||
| // https://tools.ietf.org/html/rfc2818#section-3.1 | ||||
| func MatchWildcard(subject, wildcard string) bool { | ||||
| 	if subject == wildcard { | ||||
| 		return true | ||||
|   | ||||
							
								
								
									
										24
									
								
								vendor/github.com/caddyserver/certmagic/certmagic.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										24
									
								
								vendor/github.com/caddyserver/certmagic/certmagic.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -125,8 +125,10 @@ func HTTPS(domainNames []string, mux http.Handler) error { | ||||
| 		WriteTimeout:      5 * time.Second, | ||||
| 		IdleTimeout:       5 * time.Second, | ||||
| 	} | ||||
| 	if am, ok := cfg.Issuer.(*ACMEManager); ok { | ||||
| 		httpServer.Handler = am.HTTPChallengeHandler(http.HandlerFunc(httpRedirectHandler)) | ||||
| 	if len(cfg.Issuers) > 0 { | ||||
| 		if am, ok := cfg.Issuers[0].(*ACMEManager); ok { | ||||
| 			httpServer.Handler = am.HTTPChallengeHandler(http.HandlerFunc(httpRedirectHandler)) | ||||
| 		} | ||||
| 	} | ||||
| 	httpsServer := &http.Server{ | ||||
| 		ReadHeaderTimeout: 10 * time.Second, | ||||
| @@ -425,9 +427,11 @@ func (cr *CertificateResource) NamesKey() string { | ||||
|  | ||||
| // Default contains the package defaults for the | ||||
| // various Config fields. This is used as a template | ||||
| // when creating your own Configs with New(), and it | ||||
| // is also used as the Config by all the high-level | ||||
| // functions in this package. | ||||
| // when creating your own Configs with New() or | ||||
| // NewDefault(), and it is also used as the Config | ||||
| // by all the high-level functions in this package | ||||
| // that abstract away most configuration (HTTPS(), | ||||
| // TLS(), Listen(), etc). | ||||
| // | ||||
| // The fields of this value will be used for Config | ||||
| // fields which are unset. Feel free to modify these | ||||
| @@ -436,8 +440,10 @@ func (cr *CertificateResource) NamesKey() string { | ||||
| // obtained by calling New() (if you have your own | ||||
| // certificate cache) or NewDefault() (if you only | ||||
| // need a single config and want to use the default | ||||
| // cache). This is the only Config which can access | ||||
| // the default certificate cache. | ||||
| // cache). | ||||
| // | ||||
| // Even if the Issuers or Storage fields are not set, | ||||
| // defaults will be applied in the call to New(). | ||||
| var Default = Config{ | ||||
| 	RenewalWindowRatio: DefaultRenewalWindowRatio, | ||||
| 	Storage:            defaultFileStorage, | ||||
| @@ -459,12 +465,12 @@ const ( | ||||
| // are set to; otherwise ACME challenges will fail. | ||||
| var ( | ||||
| 	// HTTPPort is the port on which to serve HTTP | ||||
| 	// and, by extension, the HTTP challenge (unless | ||||
| 	// and, as such, the HTTP challenge (unless | ||||
| 	// Default.AltHTTPPort is set). | ||||
| 	HTTPPort = 80 | ||||
|  | ||||
| 	// HTTPSPort is the port on which to serve HTTPS | ||||
| 	// and, by extension, the TLS-ALPN challenge | ||||
| 	// and, as such, the TLS-ALPN challenge | ||||
| 	// (unless Default.AltTLSALPNPort is set). | ||||
| 	HTTPSPort = 443 | ||||
| ) | ||||
|   | ||||
							
								
								
									
										389
									
								
								vendor/github.com/caddyserver/certmagic/config.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										389
									
								
								vendor/github.com/caddyserver/certmagic/config.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -23,6 +23,7 @@ import ( | ||||
| 	"crypto/x509" | ||||
| 	"crypto/x509/pkix" | ||||
| 	"encoding/asn1" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	weakrand "math/rand" | ||||
| 	"net" | ||||
| @@ -31,7 +32,9 @@ import ( | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/mholt/acmez" | ||||
| 	"github.com/mholt/acmez/acme" | ||||
| 	"go.uber.org/zap" | ||||
| 	"golang.org/x/net/idna" | ||||
| ) | ||||
|  | ||||
| // Config configures a certificate manager instance. | ||||
| @@ -54,45 +57,48 @@ type Config struct { | ||||
|  | ||||
| 	// DefaultServerName specifies a server name | ||||
| 	// to use when choosing a certificate if the | ||||
| 	// ClientHello's ServerName field is empty | ||||
| 	// ClientHello's ServerName field is empty. | ||||
| 	DefaultServerName string | ||||
|  | ||||
| 	// The state needed to operate on-demand TLS; | ||||
| 	// if non-nil, on-demand TLS is enabled and | ||||
| 	// certificate operations are deferred to | ||||
| 	// TLS handshakes (or as-needed) | ||||
| 	// TLS handshakes (or as-needed). | ||||
| 	// TODO: Can we call this feature "Reactive/Lazy/Passive TLS" instead? | ||||
| 	OnDemand *OnDemandConfig | ||||
|  | ||||
| 	// Add the must staple TLS extension to the CSR | ||||
| 	// Adds the must staple TLS extension to the CSR. | ||||
| 	MustStaple bool | ||||
|  | ||||
| 	// The type that issues certificates; the | ||||
| 	// default Issuer is ACMEManager | ||||
| 	Issuer Issuer | ||||
|  | ||||
| 	// The type that revokes certificates; must | ||||
| 	// be configured in conjunction with the Issuer | ||||
| 	// field such that both the Issuer and Revoker | ||||
| 	// are related (because issuance information is | ||||
| 	// required for revocation) | ||||
| 	Revoker Revoker | ||||
| 	// The source for getting new certificates; the | ||||
| 	// default Issuer is ACMEManager. If multiple | ||||
| 	// issuers are specified, they will be tried in | ||||
| 	// turn until one succeeds. | ||||
| 	Issuers []Issuer | ||||
|  | ||||
| 	// The source of new private keys for certificates; | ||||
| 	// the default KeySource is StandardKeyGenerator | ||||
| 	// the default KeySource is StandardKeyGenerator. | ||||
| 	KeySource KeyGenerator | ||||
|  | ||||
| 	// CertSelection chooses one of the certificates | ||||
| 	// with which the ClientHello will be completed; | ||||
| 	// if not set, DefaultCertificateSelector will | ||||
| 	// be used | ||||
| 	// be used. | ||||
| 	CertSelection CertificateSelector | ||||
|  | ||||
| 	// The storage to access when storing or | ||||
| 	// loading TLS assets | ||||
| 	// OCSP configures how OCSP is handled. By default, | ||||
| 	// OCSP responses are fetched for every certificate | ||||
| 	// with a responder URL, and cached on disk. Changing | ||||
| 	// these defaults is STRONGLY discouraged unless you | ||||
| 	// have a compelling reason to put clients at greater | ||||
| 	// risk and reduce their privacy. | ||||
| 	OCSP OCSPConfig | ||||
|  | ||||
| 	// The storage to access when storing or loading | ||||
| 	// TLS assets. Default is the local file system. | ||||
| 	Storage Storage | ||||
|  | ||||
| 	// Set a logger to enable logging | ||||
| 	// Set a logger to enable logging. | ||||
| 	Logger *zap.Logger | ||||
|  | ||||
| 	// required pointer to the in-memory cert cache | ||||
| @@ -116,6 +122,9 @@ type Config struct { | ||||
| // same, default certificate cache. All configs returned | ||||
| // by NewDefault() are based on the values of the fields of | ||||
| // Default at the time it is called. | ||||
| // | ||||
| // This is the only way to get a config that uses the | ||||
| // default certificate cache. | ||||
| func NewDefault() *Config { | ||||
| 	defaultCacheMu.Lock() | ||||
| 	if defaultCache == nil { | ||||
| @@ -153,7 +162,7 @@ func NewDefault() *Config { | ||||
| // the vast majority of cases, there will be only a | ||||
| // single Config, thus the default cache (which always | ||||
| // uses the default Config) and default config will | ||||
| // suffice, and you should use New() instead. | ||||
| // suffice, and you should use NewDefault() instead. | ||||
| func New(certCache *Cache, cfg Config) *Config { | ||||
| 	if certCache == nil { | ||||
| 		panic("a certificate cache is required") | ||||
| @@ -196,23 +205,11 @@ func newWithCache(certCache *Cache, cfg Config) *Config { | ||||
| 	if cfg.Storage == nil { | ||||
| 		cfg.Storage = Default.Storage | ||||
| 	} | ||||
| 	if cfg.Issuer == nil { | ||||
| 		cfg.Issuer = Default.Issuer | ||||
| 		if cfg.Issuer == nil { | ||||
| 			// okay really, we need an issuer, | ||||
| 			// that's kind of the point; most | ||||
| 			// people would probably want ACME | ||||
| 			cfg.Issuer = NewACMEManager(&cfg, DefaultACME) | ||||
| 		} | ||||
| 		// issuer and revoker go together; if user | ||||
| 		// specifies their own issuer, we don't want | ||||
| 		// to override their revoker, hence we only | ||||
| 		// do this if Issuer was also nil | ||||
| 		if cfg.Revoker == nil { | ||||
| 			cfg.Revoker = Default.Revoker | ||||
| 			if cfg.Revoker == nil { | ||||
| 				cfg.Revoker = NewACMEManager(&cfg, DefaultACME) | ||||
| 			} | ||||
| 	if len(cfg.Issuers) == 0 { | ||||
| 		cfg.Issuers = Default.Issuers | ||||
| 		if len(cfg.Issuers) == 0 { | ||||
| 			// at least one issuer is absolutely required | ||||
| 			cfg.Issuers = []Issuer{NewACMEManager(&cfg, DefaultACME)} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @@ -223,7 +220,6 @@ func newWithCache(certCache *Cache, cfg Config) *Config { | ||||
| 		cfg.Storage = defaultFileStorage | ||||
| 	} | ||||
|  | ||||
| 	// ensure the unexported fields are valid | ||||
| 	cfg.certCache = certCache | ||||
|  | ||||
| 	return &cfg | ||||
| @@ -254,6 +250,29 @@ func (cfg *Config) ManageSync(domainNames []string) error { | ||||
| 	return cfg.manageAll(nil, domainNames, false) | ||||
| } | ||||
|  | ||||
| // ClientCredentials returns a list of TLS client certificate chains for the given identifiers. | ||||
| // The return value can be used in a tls.Config to enable client authentication using managed certificates. | ||||
| // Any certificates that need to be obtained or renewed for these identifiers will be managed accordingly. | ||||
| func (cfg *Config) ClientCredentials(ctx context.Context, identifiers []string) ([]tls.Certificate, error) { | ||||
| 	err := cfg.manageAll(ctx, identifiers, false) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	var chains []tls.Certificate | ||||
| 	for _, id := range identifiers { | ||||
| 		certRes, err := cfg.loadCertResourceAnyIssuer(id) | ||||
| 		if err != nil { | ||||
| 			return chains, err | ||||
| 		} | ||||
| 		chain, err := tls.X509KeyPair(certRes.CertificatePEM, certRes.PrivateKeyPEM) | ||||
| 		if err != nil { | ||||
| 			return chains, err | ||||
| 		} | ||||
| 		chains = append(chains, chain) | ||||
| 	} | ||||
| 	return chains, nil | ||||
| } | ||||
|  | ||||
| // ManageAsync is the same as ManageSync, except that ACME | ||||
| // operations are performed asynchronously (in the background). | ||||
| // This method returns before certificates are ready. It is | ||||
| @@ -360,6 +379,28 @@ func (cfg *Config) manageOne(ctx context.Context, domainName string, async bool) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Unmanage causes the certificates for domainNames to stop being managed. | ||||
| // If there are certificates for the supplied domain names in the cache, they | ||||
| // are evicted from the cache. | ||||
| func (cfg *Config) Unmanage(domainNames []string) { | ||||
| 	var deleteQueue []Certificate | ||||
| 	for _, domainName := range domainNames { | ||||
| 		certs := cfg.certCache.AllMatchingCertificates(domainName) | ||||
| 		for _, cert := range certs { | ||||
| 			if !cert.managed { | ||||
| 				continue | ||||
| 			} | ||||
| 			deleteQueue = append(deleteQueue, cert) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	cfg.certCache.mu.Lock() | ||||
| 	for _, cert := range deleteQueue { | ||||
| 		cfg.certCache.removeCertificate(cert) | ||||
| 	} | ||||
| 	cfg.certCache.mu.Unlock() | ||||
| } | ||||
|  | ||||
| // ObtainCert obtains a certificate for name using cfg, as long | ||||
| // as a certificate does not already exist in storage for that | ||||
| // name. The name must qualify and cfg must be flagged as Managed. | ||||
| @@ -372,27 +413,22 @@ func (cfg *Config) manageOne(ctx context.Context, domainName string, async bool) | ||||
| // TODO: consider moving interactive param into the Config struct, | ||||
| // and maybe retry settings into the Config struct as well? (same for RenewCert) | ||||
| func (cfg *Config) ObtainCert(ctx context.Context, name string, interactive bool) error { | ||||
| 	if cfg.storageHasCertResources(name) { | ||||
| 	if len(cfg.Issuers) == 0 { | ||||
| 		return fmt.Errorf("no issuers configured; impossible to obtain or check for existing certificate in storage") | ||||
| 	} | ||||
| 	if cfg.storageHasCertResourcesAnyIssuer(name) { | ||||
| 		return nil | ||||
| 	} | ||||
| 	issuer, err := cfg.getPrecheckedIssuer(ctx, []string{name}, interactive) | ||||
| 	// ensure storage is writeable and readable | ||||
| 	// TODO: this is not necessary every time; should only perform check once every so often for each storage, which may require some global state... | ||||
| 	err := cfg.checkStorage() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 		return fmt.Errorf("failed storage check: %v - storage is probably misconfigured", err) | ||||
| 	} | ||||
| 	if issuer == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return cfg.obtainWithIssuer(ctx, issuer, name, interactive) | ||||
| 	return cfg.obtainCert(ctx, name, interactive) | ||||
| } | ||||
|  | ||||
| func loggerNamed(l *zap.Logger, name string) *zap.Logger { | ||||
| 	if l == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return l.Named(name) | ||||
| } | ||||
|  | ||||
| func (cfg *Config) obtainWithIssuer(ctx context.Context, issuer Issuer, name string, interactive bool) error { | ||||
| func (cfg *Config) obtainCert(ctx context.Context, name string, interactive bool) error { | ||||
| 	log := loggerNamed(cfg.Logger, "obtain") | ||||
|  | ||||
| 	if log != nil { | ||||
| @@ -400,10 +436,10 @@ func (cfg *Config) obtainWithIssuer(ctx context.Context, issuer Issuer, name str | ||||
| 	} | ||||
|  | ||||
| 	// ensure idempotency of the obtain operation for this name | ||||
| 	lockKey := cfg.lockKey("cert_acme", name) | ||||
| 	lockKey := cfg.lockKey(certIssueLockOp, name) | ||||
| 	err := acquireLock(ctx, cfg.Storage, lockKey) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 		return fmt.Errorf("unable to acquire lock '%s': %v", lockKey, err) | ||||
| 	} | ||||
| 	defer func() { | ||||
| 		if log != nil { | ||||
| @@ -424,7 +460,7 @@ func (cfg *Config) obtainWithIssuer(ctx context.Context, issuer Issuer, name str | ||||
|  | ||||
| 	f := func(ctx context.Context) error { | ||||
| 		// check if obtain is still needed -- might have been obtained during lock | ||||
| 		if cfg.storageHasCertResources(name) { | ||||
| 		if cfg.storageHasCertResourcesAnyIssuer(name) { | ||||
| 			if log != nil { | ||||
| 				log.Info("certificate already exists in storage", zap.String("identifier", name)) | ||||
| 			} | ||||
| @@ -445,8 +481,24 @@ func (cfg *Config) obtainWithIssuer(ctx context.Context, issuer Issuer, name str | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		issuedCert, err := issuer.Issue(ctx, csr) | ||||
| 		// try to obtain from each issuer until we succeed | ||||
| 		var issuedCert *IssuedCertificate | ||||
| 		var issuerUsed Issuer | ||||
| 		for _, issuer := range cfg.Issuers { | ||||
| 			if prechecker, ok := issuer.(PreChecker); ok { | ||||
| 				err = prechecker.PreCheck(ctx, []string{name}, interactive) | ||||
| 				if err != nil { | ||||
| 					continue | ||||
| 				} | ||||
| 			} | ||||
| 			issuedCert, err = issuer.Issue(ctx, csr) | ||||
| 			if err == nil { | ||||
| 				issuerUsed = issuer | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 		if err != nil { | ||||
| 			// TODO: only the error from the last issuer will be returned, oh well? | ||||
| 			return fmt.Errorf("[%s] Obtain: %w", name, err) | ||||
| 		} | ||||
|  | ||||
| @@ -457,7 +509,7 @@ func (cfg *Config) obtainWithIssuer(ctx context.Context, issuer Issuer, name str | ||||
| 			PrivateKeyPEM:  privKeyPEM, | ||||
| 			IssuerData:     issuedCert.Metadata, | ||||
| 		} | ||||
| 		err = cfg.saveCertResource(certRes) | ||||
| 		err = cfg.saveCertResource(issuerUsed, certRes) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("[%s] Obtain: saving assets: %v", name, err) | ||||
| 		} | ||||
| @@ -480,21 +532,32 @@ func (cfg *Config) obtainWithIssuer(ctx context.Context, issuer Issuer, name str | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| func (cfg *Config) storageHasCertResourcesAnyIssuer(name string) bool { | ||||
| 	for _, iss := range cfg.Issuers { | ||||
| 		if cfg.storageHasCertResources(iss, name) { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| // RenewCert renews the certificate for name using cfg. It stows the | ||||
| // renewed certificate and its assets in storage if successful. It | ||||
| // DOES NOT update the in-memory cache with the new certificate. | ||||
| func (cfg *Config) RenewCert(ctx context.Context, name string, interactive bool) error { | ||||
| 	issuer, err := cfg.getPrecheckedIssuer(ctx, []string{name}, interactive) | ||||
| 	if len(cfg.Issuers) == 0 { | ||||
| 		return fmt.Errorf("no issuers configured; impossible to renew or check existing certificate in storage") | ||||
| 	} | ||||
| 	// ensure storage is writeable and readable | ||||
| 	// TODO: this is not necessary every time; should only perform check once every so often for each storage, which may require some global state... | ||||
| 	err := cfg.checkStorage() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 		return fmt.Errorf("failed storage check: %v - storage is probably misconfigured", err) | ||||
| 	} | ||||
| 	if issuer == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return cfg.renewWithIssuer(ctx, issuer, name, interactive) | ||||
| 	return cfg.renewCert(ctx, name, interactive) | ||||
| } | ||||
|  | ||||
| func (cfg *Config) renewWithIssuer(ctx context.Context, issuer Issuer, name string, interactive bool) error { | ||||
| func (cfg *Config) renewCert(ctx context.Context, name string, interactive bool) error { | ||||
| 	log := loggerNamed(cfg.Logger, "renew") | ||||
|  | ||||
| 	if log != nil { | ||||
| @@ -502,10 +565,10 @@ func (cfg *Config) renewWithIssuer(ctx context.Context, issuer Issuer, name stri | ||||
| 	} | ||||
|  | ||||
| 	// ensure idempotency of the renew operation for this name | ||||
| 	lockKey := cfg.lockKey("cert_acme", name) | ||||
| 	lockKey := cfg.lockKey(certIssueLockOp, name) | ||||
| 	err := acquireLock(ctx, cfg.Storage, lockKey) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 		return fmt.Errorf("unable to acquire lock '%s': %v", lockKey, err) | ||||
| 	} | ||||
| 	defer func() { | ||||
| 		if log != nil { | ||||
| @@ -526,7 +589,7 @@ func (cfg *Config) renewWithIssuer(ctx context.Context, issuer Issuer, name stri | ||||
|  | ||||
| 	f := func(ctx context.Context) error { | ||||
| 		// prepare for renewal (load PEM cert, key, and meta) | ||||
| 		certRes, err := cfg.loadCertResource(name) | ||||
| 		certRes, err := cfg.loadCertResourceAnyIssuer(name) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| @@ -556,8 +619,24 @@ func (cfg *Config) renewWithIssuer(ctx context.Context, issuer Issuer, name stri | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		issuedCert, err := issuer.Issue(ctx, csr) | ||||
| 		// try to obtain from each issuer until we succeed | ||||
| 		var issuedCert *IssuedCertificate | ||||
| 		var issuerUsed Issuer | ||||
| 		for _, issuer := range cfg.Issuers { | ||||
| 			if prechecker, ok := issuer.(PreChecker); ok { | ||||
| 				err = prechecker.PreCheck(ctx, []string{name}, interactive) | ||||
| 				if err != nil { | ||||
| 					continue | ||||
| 				} | ||||
| 			} | ||||
| 			issuedCert, err = issuer.Issue(ctx, csr) | ||||
| 			if err == nil { | ||||
| 				issuerUsed = issuer | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 		if err != nil { | ||||
| 			// TODO: only the error from the last issuer will be returned, oh well? | ||||
| 			return fmt.Errorf("[%s] Renew: %w", name, err) | ||||
| 		} | ||||
|  | ||||
| @@ -568,7 +647,7 @@ func (cfg *Config) renewWithIssuer(ctx context.Context, issuer Issuer, name stri | ||||
| 			PrivateKeyPEM:  certRes.PrivateKeyPEM, | ||||
| 			IssuerData:     issuedCert.Metadata, | ||||
| 		} | ||||
| 		err = cfg.saveCertResource(newCertRes) | ||||
| 		err = cfg.saveCertResource(issuerUsed, newCertRes) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("[%s] Renew: saving assets: %v", name, err) | ||||
| 		} | ||||
| @@ -602,7 +681,12 @@ func (cfg *Config) generateCSR(privateKey crypto.PrivateKey, sans []string) (*x5 | ||||
| 		} else if u, err := url.Parse(name); err == nil && strings.Contains(name, "/") { | ||||
| 			csrTemplate.URIs = append(csrTemplate.URIs, u) | ||||
| 		} else { | ||||
| 			csrTemplate.DNSNames = append(csrTemplate.DNSNames, name) | ||||
| 			// convert IDNs to ASCII according to RFC 5280 section 7 | ||||
| 			normalizedName, err := idna.ToASCII(name) | ||||
| 			if err != nil { | ||||
| 				return nil, fmt.Errorf("converting identifier '%s' to ASCII: %v", name, err) | ||||
| 			} | ||||
| 			csrTemplate.DNSNames = append(csrTemplate.DNSNames, normalizedName) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @@ -619,43 +703,45 @@ func (cfg *Config) generateCSR(privateKey crypto.PrivateKey, sans []string) (*x5 | ||||
| } | ||||
|  | ||||
| // RevokeCert revokes the certificate for domain via ACME protocol. It requires | ||||
| // that cfg.Issuer is properly configured with the same issuer that issued the | ||||
| // that cfg.Issuers is properly configured with the same issuer that issued the | ||||
| // certificate being revoked. See RFC 5280 §5.3.1 for reason codes. | ||||
| func (cfg *Config) RevokeCert(ctx context.Context, domain string, reason int, interactive bool) error { | ||||
| 	rev := cfg.Revoker | ||||
| 	if rev == nil { | ||||
| 		rev = Default.Revoker | ||||
| 	} | ||||
| 	for i, issuer := range cfg.Issuers { | ||||
| 		issuerKey := issuer.IssuerKey() | ||||
|  | ||||
| 	certRes, err := cfg.loadCertResource(domain) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 		rev, ok := issuer.(Revoker) | ||||
| 		if !ok { | ||||
| 			return fmt.Errorf("issuer %d (%s) is not a Revoker", i, issuerKey) | ||||
| 		} | ||||
|  | ||||
| 	issuerKey := cfg.Issuer.IssuerKey() | ||||
| 		certRes, err := cfg.loadCertResource(issuer, domain) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 	if !cfg.Storage.Exists(StorageKeys.SitePrivateKey(issuerKey, domain)) { | ||||
| 		return fmt.Errorf("private key not found for %s", certRes.SANs) | ||||
| 	} | ||||
| 		if !cfg.Storage.Exists(StorageKeys.SitePrivateKey(issuerKey, domain)) { | ||||
| 			return fmt.Errorf("private key not found for %s", certRes.SANs) | ||||
| 		} | ||||
|  | ||||
| 	err = rev.Revoke(ctx, certRes, reason) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 		err = rev.Revoke(ctx, certRes, reason) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("issuer %d (%s): %v", i, issuerKey, err) | ||||
| 		} | ||||
|  | ||||
| 	cfg.emit("cert_revoked", domain) | ||||
| 		cfg.emit("cert_revoked", domain) | ||||
|  | ||||
| 	err = cfg.Storage.Delete(StorageKeys.SiteCert(issuerKey, domain)) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("certificate revoked, but unable to delete certificate file: %v", err) | ||||
| 	} | ||||
| 	err = cfg.Storage.Delete(StorageKeys.SitePrivateKey(issuerKey, domain)) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("certificate revoked, but unable to delete private key: %v", err) | ||||
| 	} | ||||
| 	err = cfg.Storage.Delete(StorageKeys.SiteMeta(issuerKey, domain)) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("certificate revoked, but unable to delete certificate metadata: %v", err) | ||||
| 		err = cfg.Storage.Delete(StorageKeys.SiteCert(issuerKey, domain)) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("certificate revoked, but unable to delete certificate file: %v", err) | ||||
| 		} | ||||
| 		err = cfg.Storage.Delete(StorageKeys.SitePrivateKey(issuerKey, domain)) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("certificate revoked, but unable to delete private key: %v", err) | ||||
| 		} | ||||
| 		err = cfg.Storage.Delete(StorageKeys.SiteMeta(issuerKey, domain)) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("certificate revoked, but unable to delete certificate metadata: %v", err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| @@ -692,27 +778,50 @@ func (cfg *Config) TLSConfig() *tls.Config { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // getPrecheckedIssuer returns an Issuer with pre-checks | ||||
| // completed, if it is also a PreChecker. It also checks | ||||
| // that storage is functioning. If a nil Issuer is returned | ||||
| // with a nil error, that means to skip this operation | ||||
| // (not an error, just a no-op). | ||||
| func (cfg *Config) getPrecheckedIssuer(ctx context.Context, names []string, interactive bool) (Issuer, error) { | ||||
| 	// ensure storage is writeable and readable | ||||
| 	// TODO: this is not necessary every time; should only | ||||
| 	// perform check once every so often for each storage, | ||||
| 	// which may require some global state... | ||||
| 	err := cfg.checkStorage() | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed storage check: %v - storage is probably misconfigured", err) | ||||
| // getChallengeInfo loads the challenge info from either the internal challenge memory | ||||
| // or the external storage (implying distributed solving). The second return value | ||||
| // indicates whether challenge info was loaded from external storage. If true, the | ||||
| // challenge is being solved in a distributed fashion; if false, from internal memory. | ||||
| // If no matching challenge information can be found, an error is returned. | ||||
| func (cfg *Config) getChallengeInfo(identifier string) (Challenge, bool, error) { | ||||
| 	// first, check if our process initiated this challenge; if so, just return it | ||||
| 	chalData, ok := GetACMEChallenge(identifier) | ||||
| 	if ok { | ||||
| 		return chalData, false, nil | ||||
| 	} | ||||
| 	if prechecker, ok := cfg.Issuer.(PreChecker); ok { | ||||
| 		err := prechecker.PreCheck(ctx, names, interactive) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
|  | ||||
| 	// otherwise, perhaps another instance in the cluster initiated it; check | ||||
| 	// the configured storage to retrieve challenge data | ||||
|  | ||||
| 	var chalInfo acme.Challenge | ||||
| 	var chalInfoBytes []byte | ||||
| 	var tokenKey string | ||||
| 	for _, issuer := range cfg.Issuers { | ||||
| 		ds := distributedSolver{ | ||||
| 			storage:                cfg.Storage, | ||||
| 			storageKeyIssuerPrefix: storageKeyACMECAPrefix(issuer.IssuerKey()), | ||||
| 		} | ||||
| 		tokenKey = ds.challengeTokensKey(identifier) | ||||
| 		var err error | ||||
| 		chalInfoBytes, err = cfg.Storage.Load(tokenKey) | ||||
| 		if err == nil { | ||||
| 			break | ||||
| 		} | ||||
| 		if _, ok := err.(ErrNotExist); ok { | ||||
| 			continue | ||||
| 		} | ||||
| 		return Challenge{}, false, fmt.Errorf("opening distributed challenge token file %s: %v", tokenKey, err) | ||||
| 	} | ||||
| 	return cfg.Issuer, nil | ||||
| 	if len(chalInfoBytes) == 0 { | ||||
| 		return Challenge{}, false, fmt.Errorf("no information found to solve challenge for identifier: %s", identifier) | ||||
| 	} | ||||
|  | ||||
| 	err := json.Unmarshal(chalInfoBytes, &chalInfo) | ||||
| 	if err != nil { | ||||
| 		return Challenge{}, false, fmt.Errorf("decoding challenge token file %s (corrupted?): %v", tokenKey, err) | ||||
| 	} | ||||
|  | ||||
| 	return Challenge{Challenge: chalInfo}, true, nil | ||||
| } | ||||
|  | ||||
| // checkStorage tests the storage by writing random bytes | ||||
| @@ -758,8 +867,8 @@ func (cfg *Config) checkStorage() error { | ||||
| // associated with cfg's certificate cache has all the | ||||
| // resources related to the certificate for domain: the | ||||
| // certificate, the private key, and the metadata. | ||||
| func (cfg *Config) storageHasCertResources(domain string) bool { | ||||
| 	issuerKey := cfg.Issuer.IssuerKey() | ||||
| func (cfg *Config) storageHasCertResources(issuer Issuer, domain string) bool { | ||||
| 	issuerKey := issuer.IssuerKey() | ||||
| 	certKey := StorageKeys.SiteCert(issuerKey, domain) | ||||
| 	keyKey := StorageKeys.SitePrivateKey(issuerKey, domain) | ||||
| 	metaKey := StorageKeys.SiteMeta(issuerKey, domain) | ||||
| @@ -771,18 +880,19 @@ func (cfg *Config) storageHasCertResources(domain string) bool { | ||||
| // lockKey returns a key for a lock that is specific to the operation | ||||
| // named op being performed related to domainName and this config's CA. | ||||
| func (cfg *Config) lockKey(op, domainName string) string { | ||||
| 	return fmt.Sprintf("%s_%s_%s", op, domainName, cfg.Issuer.IssuerKey()) | ||||
| 	return fmt.Sprintf("%s_%s", op, domainName) | ||||
| } | ||||
|  | ||||
| // managedCertNeedsRenewal returns true if certRes is | ||||
| // expiring soon or already expired, or if the process | ||||
| // of checking the expiration returned an error. | ||||
| // managedCertNeedsRenewal returns true if certRes is expiring soon or already expired, | ||||
| // or if the process of decoding the cert and checking its expiration returned an error. | ||||
| func (cfg *Config) managedCertNeedsRenewal(certRes CertificateResource) (time.Duration, bool) { | ||||
| 	cert, err := makeCertificate(certRes.CertificatePEM, certRes.PrivateKeyPEM) | ||||
| 	certChain, err := parseCertsFromPEMBundle(certRes.CertificatePEM) | ||||
| 	if err != nil { | ||||
| 		return 0, true | ||||
| 	} | ||||
| 	return time.Until(cert.Leaf.NotAfter), cert.NeedsRenewal(cfg) | ||||
| 	remaining := time.Until(certChain[0].NotAfter) | ||||
| 	needsRenew := currentlyInRenewalWindow(certChain[0].NotBefore, certChain[0].NotAfter, cfg.RenewalWindowRatio) | ||||
| 	return remaining, needsRenew | ||||
| } | ||||
|  | ||||
| func (cfg *Config) emit(eventName string, data interface{}) { | ||||
| @@ -792,11 +902,40 @@ func (cfg *Config) emit(eventName string, data interface{}) { | ||||
| 	cfg.OnEvent(eventName, data) | ||||
| } | ||||
|  | ||||
| func loggerNamed(l *zap.Logger, name string) *zap.Logger { | ||||
| 	if l == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return l.Named(name) | ||||
| } | ||||
|  | ||||
| // CertificateSelector is a type which can select a certificate to use given multiple choices. | ||||
| type CertificateSelector interface { | ||||
| 	SelectCertificate(*tls.ClientHelloInfo, []Certificate) (Certificate, error) | ||||
| } | ||||
|  | ||||
| // OCSPConfig configures how OCSP is handled. | ||||
| type OCSPConfig struct { | ||||
| 	// Disable automatic OCSP stapling; strongly | ||||
| 	// discouraged unless you have a good reason. | ||||
| 	// Disabling this puts clients at greater risk | ||||
| 	// and reduces their privacy. | ||||
| 	DisableStapling bool | ||||
|  | ||||
| 	// A map of OCSP responder domains to replacement | ||||
| 	// domains for querying OCSP servers. Used for | ||||
| 	// overriding the OCSP responder URL that is | ||||
| 	// embedded in certificates. Mapping to an empty | ||||
| 	// URL will disable OCSP from that responder. | ||||
| 	ResponderOverrides map[string]string | ||||
| } | ||||
|  | ||||
| // certIssueLockOp is the name of the operation used | ||||
| // when naming a lock to make it mutually exclusive | ||||
| // with other certificate issuance operations for a | ||||
| // certain name. | ||||
| const certIssueLockOp = "issue_cert" | ||||
|  | ||||
| // Constants for PKIX MustStaple extension. | ||||
| var ( | ||||
| 	tlsFeatureExtensionOID = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 24} | ||||
|   | ||||
							
								
								
									
										136
									
								
								vendor/github.com/caddyserver/certmagic/crypto.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										136
									
								
								vendor/github.com/caddyserver/certmagic/crypto.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -28,9 +28,12 @@ import ( | ||||
| 	"encoding/pem" | ||||
| 	"fmt" | ||||
| 	"hash/fnv" | ||||
| 	"sort" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/klauspost/cpuid" | ||||
| 	"go.uber.org/zap" | ||||
| 	"golang.org/x/net/idna" | ||||
| ) | ||||
|  | ||||
| // encodePrivateKey marshals a EC or RSA private key into a PEM-encoded array of bytes. | ||||
| @@ -129,13 +132,13 @@ func fastHash(input []byte) string { | ||||
| // saveCertResource saves the certificate resource to disk. This | ||||
| // includes the certificate file itself, the private key, and the | ||||
| // metadata file. | ||||
| func (cfg *Config) saveCertResource(cert CertificateResource) error { | ||||
| func (cfg *Config) saveCertResource(issuer Issuer, cert CertificateResource) error { | ||||
| 	metaBytes, err := json.MarshalIndent(cert, "", "\t") | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("encoding certificate metadata: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	issuerKey := cfg.Issuer.IssuerKey() | ||||
| 	issuerKey := issuer.IssuerKey() | ||||
| 	certKey := cert.NamesKey() | ||||
|  | ||||
| 	all := []keyValue{ | ||||
| @@ -156,20 +159,95 @@ func (cfg *Config) saveCertResource(cert CertificateResource) error { | ||||
| 	return storeTx(cfg.Storage, all) | ||||
| } | ||||
|  | ||||
| func (cfg *Config) loadCertResource(certNamesKey string) (CertificateResource, error) { | ||||
| // loadCertResourceAnyIssuer loads and returns the certificate resource from any | ||||
| // of the configured issuers. If multiple are found (e.g. if there are 3 issuers | ||||
| // configured, and all 3 have a resource matching certNamesKey), then the newest | ||||
| // (latest NotBefore date) resource will be chosen. | ||||
| func (cfg *Config) loadCertResourceAnyIssuer(certNamesKey string) (CertificateResource, error) { | ||||
| 	// we can save some extra decoding steps if there's only one issuer, since | ||||
| 	// we don't need to compare potentially multiple available resources to | ||||
| 	// select the best one, when there's only one choice anyway | ||||
| 	if len(cfg.Issuers) == 1 { | ||||
| 		return cfg.loadCertResource(cfg.Issuers[0], certNamesKey) | ||||
| 	} | ||||
|  | ||||
| 	type decodedCertResource struct { | ||||
| 		CertificateResource | ||||
| 		issuer  Issuer | ||||
| 		decoded *x509.Certificate | ||||
| 	} | ||||
| 	var certResources []decodedCertResource | ||||
| 	var lastErr error | ||||
|  | ||||
| 	// load and decode all certificate resources found with the | ||||
| 	// configured issuers so we can sort by newest | ||||
| 	for _, issuer := range cfg.Issuers { | ||||
| 		certRes, err := cfg.loadCertResource(issuer, certNamesKey) | ||||
| 		if err != nil { | ||||
| 			if _, ok := err.(ErrNotExist); ok { | ||||
| 				// not a problem, but we need to remember the error | ||||
| 				// in case we end up not finding any cert resources | ||||
| 				// since we'll need an error to return in that case | ||||
| 				lastErr = err | ||||
| 				continue | ||||
| 			} | ||||
| 			return CertificateResource{}, err | ||||
| 		} | ||||
| 		certs, err := parseCertsFromPEMBundle(certRes.CertificatePEM) | ||||
| 		if err != nil { | ||||
| 			return CertificateResource{}, err | ||||
| 		} | ||||
| 		certResources = append(certResources, decodedCertResource{ | ||||
| 			CertificateResource: certRes, | ||||
| 			issuer:              issuer, | ||||
| 			decoded:             certs[0], | ||||
| 		}) | ||||
| 	} | ||||
| 	if len(certResources) == 0 { | ||||
| 		if lastErr == nil { | ||||
| 			lastErr = fmt.Errorf("no certificate resources found") // just in case; e.g. no Issuers configured | ||||
| 		} | ||||
| 		return CertificateResource{}, lastErr | ||||
| 	} | ||||
|  | ||||
| 	// sort by date so the most recently issued comes first | ||||
| 	sort.Slice(certResources, func(i, j int) bool { | ||||
| 		return certResources[j].decoded.NotBefore.Before(certResources[i].decoded.NotBefore) | ||||
| 	}) | ||||
|  | ||||
| 	if cfg.Logger != nil { | ||||
| 		cfg.Logger.Debug("loading managed certificate", | ||||
| 			zap.String("domain", certNamesKey), | ||||
| 			zap.Time("expiration", certResources[0].decoded.NotAfter), | ||||
| 			zap.String("issuer_key", certResources[0].issuer.IssuerKey()), | ||||
| 			zap.Any("storage", cfg.Storage), | ||||
| 		) | ||||
| 	} | ||||
|  | ||||
| 	return certResources[0].CertificateResource, nil | ||||
| } | ||||
|  | ||||
| // loadCertResource loads a certificate resource from the given issuer's storage location. | ||||
| func (cfg *Config) loadCertResource(issuer Issuer, certNamesKey string) (CertificateResource, error) { | ||||
| 	var certRes CertificateResource | ||||
| 	issuerKey := cfg.Issuer.IssuerKey() | ||||
| 	certBytes, err := cfg.Storage.Load(StorageKeys.SiteCert(issuerKey, certNamesKey)) | ||||
| 	issuerKey := issuer.IssuerKey() | ||||
|  | ||||
| 	normalizedName, err := idna.ToASCII(certNamesKey) | ||||
| 	if err != nil { | ||||
| 		return certRes, fmt.Errorf("converting '%s' to ASCII: %v", certNamesKey, err) | ||||
| 	} | ||||
|  | ||||
| 	certBytes, err := cfg.Storage.Load(StorageKeys.SiteCert(issuerKey, normalizedName)) | ||||
| 	if err != nil { | ||||
| 		return CertificateResource{}, err | ||||
| 	} | ||||
| 	certRes.CertificatePEM = certBytes | ||||
| 	keyBytes, err := cfg.Storage.Load(StorageKeys.SitePrivateKey(issuerKey, certNamesKey)) | ||||
| 	keyBytes, err := cfg.Storage.Load(StorageKeys.SitePrivateKey(issuerKey, normalizedName)) | ||||
| 	if err != nil { | ||||
| 		return CertificateResource{}, err | ||||
| 	} | ||||
| 	certRes.PrivateKeyPEM = keyBytes | ||||
| 	metaBytes, err := cfg.Storage.Load(StorageKeys.SiteMeta(issuerKey, certNamesKey)) | ||||
| 	metaBytes, err := cfg.Storage.Load(StorageKeys.SiteMeta(issuerKey, normalizedName)) | ||||
| 	if err != nil { | ||||
| 		return CertificateResource{}, err | ||||
| 	} | ||||
| @@ -178,50 +256,6 @@ func (cfg *Config) loadCertResource(certNamesKey string) (CertificateResource, e | ||||
| 		return CertificateResource{}, fmt.Errorf("decoding certificate metadata: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	// TODO: July 2020 - transition to new ACME lib and cert resource structure; | ||||
| 	// for a while, we will need to convert old cert resources to new structure | ||||
| 	certRes, err = cfg.transitionCertMetaToACMEzJuly2020Format(certRes, metaBytes) | ||||
| 	if err != nil { | ||||
| 		return certRes, fmt.Errorf("one-time certificate resource transition: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	return certRes, nil | ||||
| } | ||||
|  | ||||
| // TODO: this is a temporary transition helper starting July 2020. | ||||
| // It can go away when we think enough time has passed that most active assets have transitioned. | ||||
| func (cfg *Config) transitionCertMetaToACMEzJuly2020Format(certRes CertificateResource, metaBytes []byte) (CertificateResource, error) { | ||||
| 	data, ok := certRes.IssuerData.(map[string]interface{}) | ||||
| 	if !ok { | ||||
| 		return certRes, nil | ||||
| 	} | ||||
| 	if certURL, ok := data["url"].(string); ok && certURL != "" { | ||||
| 		return certRes, nil | ||||
| 	} | ||||
|  | ||||
| 	var oldCertRes struct { | ||||
| 		SANs       []string `json:"sans"` | ||||
| 		IssuerData struct { | ||||
| 			Domain        string `json:"domain"` | ||||
| 			CertURL       string `json:"certUrl"` | ||||
| 			CertStableURL string `json:"certStableUrl"` | ||||
| 		} `json:"issuer_data"` | ||||
| 	} | ||||
| 	err := json.Unmarshal(metaBytes, &oldCertRes) | ||||
| 	if err != nil { | ||||
| 		return certRes, fmt.Errorf("decoding into old certificate resource type: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	data = map[string]interface{}{ | ||||
| 		"url": oldCertRes.IssuerData.CertURL, | ||||
| 	} | ||||
| 	certRes.IssuerData = data | ||||
|  | ||||
| 	err = cfg.saveCertResource(certRes) | ||||
| 	if err != nil { | ||||
| 		return certRes, fmt.Errorf("saving converted certificate resource: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	return certRes, nil | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										19
									
								
								vendor/github.com/caddyserver/certmagic/filestorage.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										19
									
								
								vendor/github.com/caddyserver/certmagic/filestorage.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -147,7 +147,7 @@ func (fs *FileStorage) Lock(ctx context.Context, key string) error { | ||||
| 			err2 := json.NewDecoder(f).Decode(&meta) | ||||
| 			f.Close() | ||||
| 			if err2 != nil { | ||||
| 				return err2 | ||||
| 				return fmt.Errorf("decoding lockfile contents: %w", err2) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| @@ -306,7 +306,15 @@ func updateLockfileFreshness(filename string) (bool, error) { | ||||
|  | ||||
| 	// write updated timestamp | ||||
| 	meta.Updated = time.Now() | ||||
| 	return false, json.NewEncoder(f).Encode(meta) | ||||
| 	if err = json.NewEncoder(f).Encode(meta); err != nil { | ||||
| 		return false, err | ||||
| 	} | ||||
|  | ||||
| 	// sync to device; we suspect that sometimes file systems | ||||
| 	// (particularly AWS EFS) don't do this on their own, | ||||
| 	// leaving the file empty when we close it; see | ||||
| 	// https://github.com/caddyserver/caddy/issues/3954 | ||||
| 	return false, f.Sync() | ||||
| } | ||||
|  | ||||
| // atomicallyCreateFile atomically creates the file | ||||
| @@ -325,8 +333,11 @@ func atomicallyCreateFile(filename string, writeLockInfo bool) error { | ||||
| 			Created: now, | ||||
| 			Updated: now, | ||||
| 		} | ||||
| 		err := json.NewEncoder(f).Encode(meta) | ||||
| 		if err != nil { | ||||
| 		if err := json.NewEncoder(f).Encode(meta); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		// see https://github.com/caddyserver/caddy/issues/3954 | ||||
| 		if err := f.Sync(); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|   | ||||
							
								
								
									
										5
									
								
								vendor/github.com/caddyserver/certmagic/go.mod
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								vendor/github.com/caddyserver/certmagic/go.mod
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -4,9 +4,10 @@ go 1.14 | ||||
|  | ||||
| require ( | ||||
| 	github.com/klauspost/cpuid v1.2.5 | ||||
| 	github.com/libdns/libdns v0.1.0 | ||||
| 	github.com/mholt/acmez v0.1.1 | ||||
| 	github.com/libdns/libdns v0.2.0 | ||||
| 	github.com/mholt/acmez v0.1.3 | ||||
| 	github.com/miekg/dns v1.1.30 | ||||
| 	go.uber.org/zap v1.15.0 | ||||
| 	golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de | ||||
| 	golang.org/x/net v0.0.0-20200707034311-ab3426394381 | ||||
| ) | ||||
|   | ||||
							
								
								
									
										12
									
								
								vendor/github.com/caddyserver/certmagic/go.sum
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								vendor/github.com/caddyserver/certmagic/go.sum
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -12,10 +12,10 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN | ||||
| github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= | ||||
| github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= | ||||
| github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= | ||||
| github.com/libdns/libdns v0.1.0 h1:0ctCOrVJsVzj53mop1angHp/pE3hmAhP7KiHvR0HD04= | ||||
| github.com/libdns/libdns v0.1.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40= | ||||
| github.com/mholt/acmez v0.1.1 h1:KQODCqk+hBn3O7qfCRPj6L96uG65T5BSS95FKNEqtdA= | ||||
| github.com/mholt/acmez v0.1.1/go.mod h1:8qnn8QA/Ewx8E3ZSsmscqsIjhhpxuy9vqdgbX2ceceM= | ||||
| github.com/libdns/libdns v0.2.0 h1:ewg3ByWrdUrxrje8ChPVMBNcotg7H9LQYg+u5De2RzI= | ||||
| github.com/libdns/libdns v0.2.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40= | ||||
| github.com/mholt/acmez v0.1.3 h1:J7MmNIk4Qf9b8mAGqAh4XkNeowv3f1zW816yf4zt7Qk= | ||||
| github.com/mholt/acmez v0.1.3/go.mod h1:8qnn8QA/Ewx8E3ZSsmscqsIjhhpxuy9vqdgbX2ceceM= | ||||
| github.com/miekg/dns v1.1.30 h1:Qww6FseFn8PRfw07jueqIXqodm0JKiiKuK0DeXSqfyo= | ||||
| github.com/miekg/dns v1.1.30/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= | ||||
| github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= | ||||
| @@ -47,9 +47,7 @@ golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKG | ||||
| golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= | ||||
| golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||||
| golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||||
| golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= | ||||
| golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g= | ||||
| golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU= | ||||
| golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= | ||||
| @@ -57,7 +55,6 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEha | ||||
| golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe h1:6fAMxZRR6sl1Uq8U61gxU+kPTs2tR8uOySCbBP7BN/M= | ||||
| golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= | ||||
| golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| @@ -66,7 +63,6 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||
| golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= | ||||
| golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= | ||||
| golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||
| golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs= | ||||
| golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||
| golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnfG5kSmgy9KZR9sW3W5QeA= | ||||
| golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | ||||
|   | ||||
							
								
								
									
										326
									
								
								vendor/github.com/caddyserver/certmagic/handshake.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										326
									
								
								vendor/github.com/caddyserver/certmagic/handshake.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -17,7 +17,6 @@ package certmagic | ||||
| import ( | ||||
| 	"context" | ||||
| 	"crypto/tls" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"net" | ||||
| 	"strings" | ||||
| @@ -25,7 +24,6 @@ import ( | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/mholt/acmez" | ||||
| 	"github.com/mholt/acmez/acme" | ||||
| 	"go.uber.org/zap" | ||||
| ) | ||||
|  | ||||
| @@ -44,41 +42,23 @@ func (cfg *Config) GetCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certif | ||||
| 	// (https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-05) | ||||
| 	for _, proto := range clientHello.SupportedProtos { | ||||
| 		if proto == acmez.ACMETLS1Protocol { | ||||
| 			cfg.certCache.mu.RLock() | ||||
| 			challengeCert, ok := cfg.certCache.cache[tlsALPNCertKeyName(clientHello.ServerName)] | ||||
| 			cfg.certCache.mu.RUnlock() | ||||
| 			if !ok { | ||||
| 				// see if this challenge was started in a cluster; try distributed challenge solver | ||||
| 				// (note that the tls.Config's ALPN settings must include the ACME TLS-ALPN challenge | ||||
| 				// protocol string, otherwise a valid certificate will not solve the challenge; we | ||||
| 				// should already have taken care of that when we made the tls.Config) | ||||
| 				challengeCert, ok, err := cfg.tryDistributedChallengeSolver(clientHello) | ||||
| 				if err != nil { | ||||
| 					if cfg.Logger != nil { | ||||
| 						cfg.Logger.Error("tls-alpn challenge", | ||||
| 							zap.String("server_name", clientHello.ServerName), | ||||
| 							zap.Error(err)) | ||||
| 					} | ||||
| 			challengeCert, distributed, err := cfg.getTLSALPNChallengeCert(clientHello) | ||||
| 			if err != nil { | ||||
| 				if cfg.Logger != nil { | ||||
| 					cfg.Logger.Error("tls-alpn challenge", | ||||
| 						zap.String("server_name", clientHello.ServerName), | ||||
| 						zap.Error(err)) | ||||
| 				} | ||||
| 				if ok { | ||||
| 					if cfg.Logger != nil { | ||||
| 						cfg.Logger.Info("served key authentication certificate", | ||||
| 							zap.String("server_name", clientHello.ServerName), | ||||
| 							zap.String("challenge", "tls-alpn-01"), | ||||
| 							zap.String("remote", clientHello.Conn.RemoteAddr().String()), | ||||
| 							zap.Bool("distributed", true)) | ||||
| 					} | ||||
| 					return &challengeCert.Certificate, nil | ||||
| 				} | ||||
| 				return nil, fmt.Errorf("no certificate to complete TLS-ALPN challenge for SNI name: %s", clientHello.ServerName) | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			if cfg.Logger != nil { | ||||
| 				cfg.Logger.Info("served key authentication certificate", | ||||
| 					zap.String("server_name", clientHello.ServerName), | ||||
| 					zap.String("challenge", "tls-alpn-01"), | ||||
| 					zap.String("remote", clientHello.Conn.RemoteAddr().String())) | ||||
| 					zap.String("remote", clientHello.Conn.RemoteAddr().String()), | ||||
| 					zap.Bool("distributed", distributed)) | ||||
| 			} | ||||
| 			return &challengeCert.Certificate, nil | ||||
| 			return challengeCert, nil | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @@ -107,16 +87,12 @@ func (cfg *Config) GetCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certif | ||||
| // | ||||
| // This function is safe for concurrent use. | ||||
| func (cfg *Config) getCertificate(hello *tls.ClientHelloInfo) (cert Certificate, matched, defaulted bool) { | ||||
| 	name := NormalizedName(hello.ServerName) | ||||
| 	name := normalizedName(hello.ServerName) | ||||
|  | ||||
| 	if name == "" { | ||||
| 		// if SNI is empty, prefer matching IP address | ||||
| 		if hello.Conn != nil { | ||||
| 			addr := hello.Conn.LocalAddr().String() | ||||
| 			ip, _, err := net.SplitHostPort(addr) | ||||
| 			if err == nil { | ||||
| 				addr = ip | ||||
| 			} | ||||
| 			addr := localIPFromConn(hello.Conn) | ||||
| 			cert, matched = cfg.selectCert(hello, addr) | ||||
| 			if matched { | ||||
| 				return | ||||
| @@ -125,7 +101,7 @@ func (cfg *Config) getCertificate(hello *tls.ClientHelloInfo) (cert Certificate, | ||||
|  | ||||
| 		// fall back to a "default" certificate, if specified | ||||
| 		if cfg.DefaultServerName != "" { | ||||
| 			normDefault := NormalizedName(cfg.DefaultServerName) | ||||
| 			normDefault := normalizedName(cfg.DefaultServerName) | ||||
| 			cert, defaulted = cfg.selectCert(hello, normDefault) | ||||
| 			if defaulted { | ||||
| 				return | ||||
| @@ -260,6 +236,12 @@ func (cfg *Config) getCertDuringHandshake(hello *tls.ClientHelloInfo, loadIfNece | ||||
| 	if cfg.OnDemand != nil && loadIfNecessary { | ||||
| 		// Then check to see if we have one on disk | ||||
| 		loadedCert, err := cfg.CacheManagedCertificate(name) | ||||
| 		if _, ok := err.(ErrNotExist); ok { | ||||
| 			// If no exact match, try a wildcard variant, which is something we can still use | ||||
| 			labels := strings.Split(name, ".") | ||||
| 			labels[0] = "*" | ||||
| 			loadedCert, err = cfg.CacheManagedCertificate(strings.Join(labels, ".")) | ||||
| 		} | ||||
| 		if err == nil { | ||||
| 			loadedCert, err = cfg.handshakeMaintenance(hello, loadedCert) | ||||
| 			if err != nil { | ||||
| @@ -273,14 +255,6 @@ func (cfg *Config) getCertDuringHandshake(hello *tls.ClientHelloInfo, loadIfNece | ||||
| 		} | ||||
| 		if obtainIfNecessary { | ||||
| 			// By this point, we need to ask the CA for a certificate | ||||
|  | ||||
| 			// Make sure the certificate should be obtained based on config | ||||
| 			err := cfg.checkIfCertShouldBeObtained(name) | ||||
| 			if err != nil { | ||||
| 				return Certificate{}, err | ||||
| 			} | ||||
|  | ||||
| 			// Obtain certificate from the CA | ||||
| 			return cfg.obtainOnDemandCertificate(hello) | ||||
| 		} | ||||
| 	} | ||||
| @@ -347,6 +321,11 @@ func (cfg *Config) obtainOnDemandCertificate(hello *tls.ClientHelloInfo) (Certif | ||||
|  | ||||
| 	name := cfg.getNameFromClientHello(hello) | ||||
|  | ||||
| 	getCertWithoutReobtaining := func() (Certificate, error) { | ||||
| 		// very important to set the obtainIfNecessary argument to false, so we don't repeat this infinitely | ||||
| 		return cfg.getCertDuringHandshake(hello, true, false) | ||||
| 	} | ||||
|  | ||||
| 	// We must protect this process from happening concurrently, so synchronize. | ||||
| 	obtainCertWaitChansMu.Lock() | ||||
| 	wait, ok := obtainCertWaitChans[name] | ||||
| @@ -354,8 +333,17 @@ func (cfg *Config) obtainOnDemandCertificate(hello *tls.ClientHelloInfo) (Certif | ||||
| 		// lucky us -- another goroutine is already obtaining the certificate. | ||||
| 		// wait for it to finish obtaining the cert and then we'll use it. | ||||
| 		obtainCertWaitChansMu.Unlock() | ||||
| 		<-wait | ||||
| 		return cfg.getCertDuringHandshake(hello, true, false) | ||||
|  | ||||
| 		// TODO: see if we can get a proper context in here, for true cancellation | ||||
| 		timeout := time.NewTimer(2 * time.Minute) | ||||
| 		select { | ||||
| 		case <-timeout.C: | ||||
| 			return Certificate{}, fmt.Errorf("timed out waiting to obtain certificate for %s", name) | ||||
| 		case <-wait: | ||||
| 			timeout.Stop() | ||||
| 		} | ||||
|  | ||||
| 		return getCertWithoutReobtaining() | ||||
| 	} | ||||
|  | ||||
| 	// looks like it's up to us to do all the work and obtain the cert. | ||||
| @@ -364,22 +352,35 @@ func (cfg *Config) obtainOnDemandCertificate(hello *tls.ClientHelloInfo) (Certif | ||||
| 	obtainCertWaitChans[name] = wait | ||||
| 	obtainCertWaitChansMu.Unlock() | ||||
|  | ||||
| 	// obtain the certificate | ||||
| 	unblockWaiters := func() { | ||||
| 		obtainCertWaitChansMu.Lock() | ||||
| 		close(wait) | ||||
| 		delete(obtainCertWaitChans, name) | ||||
| 		obtainCertWaitChansMu.Unlock() | ||||
| 	} | ||||
|  | ||||
| 	// Make sure the certificate should be obtained based on config | ||||
| 	err := cfg.checkIfCertShouldBeObtained(name) | ||||
| 	if err != nil { | ||||
| 		unblockWaiters() | ||||
| 		return Certificate{}, err | ||||
| 	} | ||||
|  | ||||
| 	if log != nil { | ||||
| 		log.Info("obtaining new certificate", zap.String("server_name", name)) | ||||
| 	} | ||||
|  | ||||
| 	// TODO: use a proper context; we use one with timeout because retries are enabled because interactive is false | ||||
| 	ctx, cancel := context.WithTimeout(context.TODO(), 90*time.Second) | ||||
| 	defer cancel() | ||||
| 	err := cfg.ObtainCert(ctx, name, false) | ||||
|  | ||||
| 	// Obtain the certificate | ||||
| 	err = cfg.ObtainCert(ctx, name, false) | ||||
|  | ||||
| 	// immediately unblock anyone waiting for it; doing this in | ||||
| 	// a defer would risk deadlock because of the recursive call | ||||
| 	// to getCertDuringHandshake below when we return! | ||||
| 	obtainCertWaitChansMu.Lock() | ||||
| 	close(wait) | ||||
| 	delete(obtainCertWaitChans, name) | ||||
| 	obtainCertWaitChansMu.Unlock() | ||||
| 	unblockWaiters() | ||||
|  | ||||
| 	if err != nil { | ||||
| 		// shucks; failed to solve challenge on-demand | ||||
| @@ -388,7 +389,7 @@ func (cfg *Config) obtainOnDemandCertificate(hello *tls.ClientHelloInfo) (Certif | ||||
|  | ||||
| 	// success; certificate was just placed on disk, so | ||||
| 	// we need only restart serving the certificate | ||||
| 	return cfg.getCertDuringHandshake(hello, true, false) | ||||
| 	return getCertWithoutReobtaining() | ||||
| } | ||||
|  | ||||
| // handshakeMaintenance performs a check on cert for expiration and OCSP validity. | ||||
| @@ -400,13 +401,7 @@ func (cfg *Config) handshakeMaintenance(hello *tls.ClientHelloInfo, cert Certifi | ||||
| 	log := loggerNamed(cfg.Logger, "on_demand") | ||||
|  | ||||
| 	// Check cert expiration | ||||
| 	timeLeft := cert.Leaf.NotAfter.Sub(time.Now().UTC()) | ||||
| 	if currentlyInRenewalWindow(cert.Leaf.NotBefore, cert.Leaf.NotAfter, cfg.RenewalWindowRatio) { | ||||
| 		if log != nil { | ||||
| 			log.Info("certificate expires soon; attempting renewal", | ||||
| 				zap.Strings("identifiers", cert.Names), | ||||
| 				zap.Duration("remaining", timeLeft)) | ||||
| 		} | ||||
| 		return cfg.renewDynamicCertificate(hello, cert) | ||||
| 	} | ||||
|  | ||||
| @@ -414,7 +409,7 @@ func (cfg *Config) handshakeMaintenance(hello *tls.ClientHelloInfo, cert Certifi | ||||
| 	if cert.ocsp != nil { | ||||
| 		refreshTime := cert.ocsp.ThisUpdate.Add(cert.ocsp.NextUpdate.Sub(cert.ocsp.ThisUpdate) / 2) | ||||
| 		if time.Now().After(refreshTime) { | ||||
| 			_, err := stapleOCSP(cfg.Storage, &cert, nil) | ||||
| 			_, err := stapleOCSP(cfg.OCSP, cfg.Storage, &cert, nil) | ||||
| 			if err != nil { | ||||
| 				// An error with OCSP stapling is not the end of the world, and in fact, is | ||||
| 				// quite common considering not all certs have issuer URLs that support it. | ||||
| @@ -436,22 +431,59 @@ func (cfg *Config) handshakeMaintenance(hello *tls.ClientHelloInfo, cert Certifi | ||||
| // renewDynamicCertificate renews the certificate for name using cfg. It returns the | ||||
| // certificate to use and an error, if any. name should already be lower-cased before | ||||
| // calling this function. name is the name obtained directly from the handshake's | ||||
| // ClientHello. | ||||
| // ClientHello. If the certificate hasn't yet expired, currentCert will be returned | ||||
| // and the renewal will happen in the background; otherwise this blocks until the | ||||
| // certificate has been renewed, and returns the renewed certificate. | ||||
| // | ||||
| // This function is safe for use by multiple concurrent goroutines. | ||||
| func (cfg *Config) renewDynamicCertificate(hello *tls.ClientHelloInfo, currentCert Certificate) (Certificate, error) { | ||||
| 	log := loggerNamed(cfg.Logger, "on_demand") | ||||
|  | ||||
| 	name := cfg.getNameFromClientHello(hello) | ||||
| 	timeLeft := time.Until(currentCert.Leaf.NotAfter) | ||||
|  | ||||
| 	getCertWithoutReobtaining := func() (Certificate, error) { | ||||
| 		// very important to set the obtainIfNecessary argument to false, so we don't repeat this infinitely | ||||
| 		return cfg.getCertDuringHandshake(hello, true, false) | ||||
| 	} | ||||
|  | ||||
| 	// see if another goroutine is already working on this certificate | ||||
| 	obtainCertWaitChansMu.Lock() | ||||
| 	wait, ok := obtainCertWaitChans[name] | ||||
| 	if ok { | ||||
| 		// lucky us -- another goroutine is already renewing the certificate. | ||||
| 		// wait for it to finish, then we'll use the new one. | ||||
| 		// lucky us -- another goroutine is already renewing the certificate | ||||
| 		obtainCertWaitChansMu.Unlock() | ||||
| 		<-wait | ||||
| 		return cfg.getCertDuringHandshake(hello, true, false) | ||||
|  | ||||
| 		if timeLeft > 0 { | ||||
| 			// the current certificate hasn't expired, and another goroutine is already | ||||
| 			// renewing it, so we might as well serve what we have without blocking | ||||
| 			if log != nil { | ||||
| 				log.Debug("certificate expires soon but is already being renewed; serving current certificate", | ||||
| 					zap.Strings("identifiers", currentCert.Names), | ||||
| 					zap.Duration("remaining", timeLeft)) | ||||
| 			} | ||||
| 			return currentCert, nil | ||||
| 		} | ||||
|  | ||||
| 		// otherwise, we'll have to wait for the renewal to finish so we don't serve | ||||
| 		// an expired certificate | ||||
|  | ||||
| 		if log != nil { | ||||
| 			log.Debug("certificate has expired, but is already being renewed; waiting for renewal to complete", | ||||
| 				zap.Strings("identifiers", currentCert.Names), | ||||
| 				zap.Time("expired", currentCert.Leaf.NotAfter)) | ||||
| 		} | ||||
|  | ||||
| 		// TODO: see if we can get a proper context in here, for true cancellation | ||||
| 		timeout := time.NewTimer(2 * time.Minute) | ||||
| 		select { | ||||
| 		case <-timeout.C: | ||||
| 			return Certificate{}, fmt.Errorf("timed out waiting for certificate renewal of %s", name) | ||||
| 		case <-wait: | ||||
| 			timeout.Stop() | ||||
| 		} | ||||
|  | ||||
| 		return getCertWithoutReobtaining() | ||||
| 	} | ||||
|  | ||||
| 	// looks like it's up to us to do all the work and renew the cert | ||||
| @@ -459,6 +491,21 @@ func (cfg *Config) renewDynamicCertificate(hello *tls.ClientHelloInfo, currentCe | ||||
| 	obtainCertWaitChans[name] = wait | ||||
| 	obtainCertWaitChansMu.Unlock() | ||||
|  | ||||
| 	unblockWaiters := func() { | ||||
| 		obtainCertWaitChansMu.Lock() | ||||
| 		close(wait) | ||||
| 		delete(obtainCertWaitChans, name) | ||||
| 		obtainCertWaitChansMu.Unlock() | ||||
| 	} | ||||
|  | ||||
| 	if log != nil { | ||||
| 		log.Info("attempting certificate renewal", | ||||
| 			zap.String("server_name", name), | ||||
| 			zap.Strings("identifiers", currentCert.Names), | ||||
| 			zap.Time("expiration", currentCert.Leaf.NotAfter), | ||||
| 			zap.Duration("remaining", timeLeft)) | ||||
| 	} | ||||
|  | ||||
| 	// Make sure a certificate for this name should be obtained on-demand | ||||
| 	err := cfg.checkIfCertShouldBeObtained(name) | ||||
| 	if err != nil { | ||||
| @@ -466,105 +513,118 @@ func (cfg *Config) renewDynamicCertificate(hello *tls.ClientHelloInfo, currentCe | ||||
| 		cfg.certCache.mu.Lock() | ||||
| 		cfg.certCache.removeCertificate(currentCert) | ||||
| 		cfg.certCache.mu.Unlock() | ||||
| 		unblockWaiters() | ||||
| 		return Certificate{}, err | ||||
| 	} | ||||
|  | ||||
| 	// renew and reload the certificate | ||||
| 	if log != nil { | ||||
| 		log.Info("renewing certificate", zap.String("server_name", name)) | ||||
| 	} | ||||
| 	// TODO: use a proper context; we use one with timeout because retries are enabled because interactive is false | ||||
| 	ctx, cancel := context.WithTimeout(context.TODO(), 90*time.Second) | ||||
| 	defer cancel() | ||||
| 	err = cfg.RenewCert(ctx, name, false) | ||||
| 	if err == nil { | ||||
| 		// even though the recursive nature of the dynamic cert loading | ||||
| 		// would just call this function anyway, we do it here to | ||||
| 		// make the replacement as atomic as possible. | ||||
| 		newCert, err := cfg.CacheManagedCertificate(name) | ||||
| 		if err != nil { | ||||
| 			if log != nil { | ||||
| 				log.Error("loading renewed certificate", zap.String("server_name", name), zap.Error(err)) | ||||
| 	// Renew and reload the certificate | ||||
| 	renewAndReload := func(ctx context.Context, cancel context.CancelFunc) (Certificate, error) { | ||||
| 		defer cancel() | ||||
| 		err = cfg.RenewCert(ctx, name, false) | ||||
| 		if err == nil { | ||||
| 			// even though the recursive nature of the dynamic cert loading | ||||
| 			// would just call this function anyway, we do it here to | ||||
| 			// make the replacement as atomic as possible. | ||||
| 			newCert, err := cfg.CacheManagedCertificate(name) | ||||
| 			if err != nil { | ||||
| 				if log != nil { | ||||
| 					log.Error("loading renewed certificate", zap.String("server_name", name), zap.Error(err)) | ||||
| 				} | ||||
| 			} else { | ||||
| 				// replace the old certificate with the new one | ||||
| 				cfg.certCache.replaceCertificate(currentCert, newCert) | ||||
| 			} | ||||
| 		} else { | ||||
| 			// replace the old certificate with the new one | ||||
| 			cfg.certCache.replaceCertificate(currentCert, newCert) | ||||
| 		} | ||||
|  | ||||
| 		// immediately unblock anyone waiting for it; doing this in | ||||
| 		// a defer would risk deadlock because of the recursive call | ||||
| 		// to getCertDuringHandshake below when we return! | ||||
| 		unblockWaiters() | ||||
|  | ||||
| 		if err != nil { | ||||
| 			return Certificate{}, err | ||||
| 		} | ||||
|  | ||||
| 		return getCertWithoutReobtaining() | ||||
| 	} | ||||
|  | ||||
| 	// immediately unblock anyone waiting for it; doing this in | ||||
| 	// a defer would risk deadlock because of the recursive call | ||||
| 	// to getCertDuringHandshake below when we return! | ||||
| 	obtainCertWaitChansMu.Lock() | ||||
| 	close(wait) | ||||
| 	delete(obtainCertWaitChans, name) | ||||
| 	obtainCertWaitChansMu.Unlock() | ||||
|  | ||||
| 	if err != nil { | ||||
| 		return Certificate{}, err | ||||
| 	// if the certificate hasn't expired, we can serve what we have and renew in the background | ||||
| 	if timeLeft > 0 { | ||||
| 		// TODO: get a proper context; we use one with timeout because retries are enabled because interactive is false | ||||
| 		ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Minute) | ||||
| 		go renewAndReload(ctx, cancel) | ||||
| 		return currentCert, nil | ||||
| 	} | ||||
|  | ||||
| 	return cfg.getCertDuringHandshake(hello, true, false) | ||||
| 	// otherwise, we have to block while we renew an expired certificate | ||||
| 	ctx, cancel := context.WithTimeout(context.TODO(), 90*time.Second) | ||||
| 	return renewAndReload(ctx, cancel) | ||||
| } | ||||
|  | ||||
| // tryDistributedChallengeSolver is to be called when the clientHello pertains to | ||||
| // a TLS-ALPN challenge and a certificate is required to solve it. This method | ||||
| // checks the distributed store of challenge info files and, if a matching ServerName | ||||
| // is present, it makes a certificate to solve this challenge and returns it. For | ||||
| // this to succeed, it requires that cfg.Issuer is of type *ACMEManager. | ||||
| // A boolean true is returned if a valid certificate is returned. | ||||
| func (cfg *Config) tryDistributedChallengeSolver(clientHello *tls.ClientHelloInfo) (Certificate, bool, error) { | ||||
| 	am, ok := cfg.Issuer.(*ACMEManager) | ||||
| 	if !ok { | ||||
| 		return Certificate{}, false, nil | ||||
| 	} | ||||
| 	tokenKey := distributedSolver{acmeManager: am, caURL: am.CA}.challengeTokensKey(clientHello.ServerName) | ||||
| 	chalInfoBytes, err := cfg.Storage.Load(tokenKey) | ||||
| // getTLSALPNChallengeCert is to be called when the clientHello pertains to | ||||
| // a TLS-ALPN challenge and a certificate is required to solve it. This method gets | ||||
| // the relevant challenge info and then returns the associated certificate (if any) | ||||
| // or generates it anew if it's not available (as is the case when distributed | ||||
| // solving). True is returned if the challenge is being solved distributed (there | ||||
| // is no semantic difference with distributed solving; it is mainly for logging). | ||||
| func (cfg *Config) getTLSALPNChallengeCert(clientHello *tls.ClientHelloInfo) (*tls.Certificate, bool, error) { | ||||
| 	chalData, distributed, err := cfg.getChallengeInfo(clientHello.ServerName) | ||||
| 	if err != nil { | ||||
| 		if _, ok := err.(ErrNotExist); ok { | ||||
| 			return Certificate{}, false, nil | ||||
| 		} | ||||
| 		return Certificate{}, false, fmt.Errorf("opening distributed challenge token file %s: %v", tokenKey, err) | ||||
| 		return nil, distributed, err | ||||
| 	} | ||||
|  | ||||
| 	var chalInfo acme.Challenge | ||||
| 	err = json.Unmarshal(chalInfoBytes, &chalInfo) | ||||
| 	if err != nil { | ||||
| 		return Certificate{}, false, fmt.Errorf("decoding challenge token file %s (corrupted?): %v", tokenKey, err) | ||||
| 	// fast path: we already created the certificate (this avoids having to re-create | ||||
| 	// it at every handshake that tries to verify, e.g. multi-perspective validation) | ||||
| 	if chalData.data != nil { | ||||
| 		return chalData.data.(*tls.Certificate), distributed, nil | ||||
| 	} | ||||
|  | ||||
| 	cert, err := acmez.TLSALPN01ChallengeCert(chalInfo) | ||||
| 	// otherwise, we can re-create the solution certificate, but it takes a few cycles | ||||
| 	cert, err := acmez.TLSALPN01ChallengeCert(chalData.Challenge) | ||||
| 	if err != nil { | ||||
| 		return Certificate{}, false, fmt.Errorf("making TLS-ALPN challenge certificate: %v", err) | ||||
| 		return nil, distributed, fmt.Errorf("making TLS-ALPN challenge certificate: %v", err) | ||||
| 	} | ||||
| 	if cert == nil { | ||||
| 		return Certificate{}, false, fmt.Errorf("got nil TLS-ALPN challenge certificate but no error") | ||||
| 		return nil, distributed, fmt.Errorf("got nil TLS-ALPN challenge certificate but no error") | ||||
| 	} | ||||
|  | ||||
| 	return Certificate{Certificate: *cert}, true, nil | ||||
| 	return cert, distributed, nil | ||||
| } | ||||
|  | ||||
| // getNameFromClientHello returns a normalized form of hello.ServerName. | ||||
| // If hello.ServerName is empty (i.e. client did not use SNI), then the | ||||
| // associated connection's local address is used to extract an IP address. | ||||
| func (*Config) getNameFromClientHello(hello *tls.ClientHelloInfo) string { | ||||
| 	name := NormalizedName(hello.ServerName) | ||||
| 	if name != "" || hello.Conn == nil { | ||||
| 	if name := normalizedName(hello.ServerName); name != "" { | ||||
| 		return name | ||||
| 	} | ||||
|  | ||||
| 	// if no SNI, try using IP address on the connection | ||||
| 	localAddr := hello.Conn.LocalAddr().String() | ||||
| 	localAddrHost, _, err := net.SplitHostPort(localAddr) | ||||
| 	if err == nil { | ||||
| 		return localAddrHost | ||||
| 	} | ||||
| 	return localAddr | ||||
| 	return localIPFromConn(hello.Conn) | ||||
| } | ||||
|  | ||||
| // NormalizedName returns a cleaned form of serverName that is | ||||
| // localIPFromConn returns the host portion of c's local address | ||||
| // and strips the scope ID if one exists (see RFC 4007). | ||||
| func localIPFromConn(c net.Conn) string { | ||||
| 	if c == nil { | ||||
| 		return "" | ||||
| 	} | ||||
| 	localAddr := c.LocalAddr().String() | ||||
| 	ip, _, err := net.SplitHostPort(localAddr) | ||||
| 	if err != nil { | ||||
| 		// OK; assume there was no port | ||||
| 		ip = localAddr | ||||
| 	} | ||||
| 	// IPv6 addresses can have scope IDs, e.g. "fe80::4c3:3cff:fe4f:7e0b%eth0", | ||||
| 	// but for our purposes, these are useless (unless a valid use case proves | ||||
| 	// otherwise; see issue #3911) | ||||
| 	if scopeIDStart := strings.Index(ip, "%"); scopeIDStart > -1 { | ||||
| 		ip = ip[:scopeIDStart] | ||||
| 	} | ||||
| 	return ip | ||||
| } | ||||
|  | ||||
| // normalizedName returns a cleaned form of serverName that is | ||||
| // used for consistency when referring to a SNI value. | ||||
| func NormalizedName(serverName string) string { | ||||
| func normalizedName(serverName string) string { | ||||
| 	return strings.ToLower(strings.TrimSpace(serverName)) | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										49
									
								
								vendor/github.com/caddyserver/certmagic/httphandler.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										49
									
								
								vendor/github.com/caddyserver/certmagic/httphandler.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -15,7 +15,6 @@ | ||||
| package certmagic | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
|  | ||||
| @@ -71,41 +70,24 @@ func (am *ACMEManager) distributedHTTPChallengeSolver(w http.ResponseWriter, r * | ||||
| 	if am == nil { | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	host := hostOnly(r.Host) | ||||
|  | ||||
| 	tokenKey := distributedSolver{acmeManager: am, caURL: am.CA}.challengeTokensKey(host) | ||||
| 	chalInfoBytes, err := am.config.Storage.Load(tokenKey) | ||||
| 	if err != nil { | ||||
| 		if _, ok := err.(ErrNotExist); !ok { | ||||
| 			if am.Logger != nil { | ||||
| 				am.Logger.Error("opening distributed HTTP challenge token file", | ||||
| 					zap.String("host", host), | ||||
| 					zap.Error(err)) | ||||
| 			} | ||||
| 		} | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	var challenge acme.Challenge | ||||
| 	err = json.Unmarshal(chalInfoBytes, &challenge) | ||||
| 	chalInfo, distributed, err := am.config.getChallengeInfo(host) | ||||
| 	if err != nil { | ||||
| 		if am.Logger != nil { | ||||
| 			am.Logger.Error("decoding HTTP challenge token file (corrupted?)", | ||||
| 			am.Logger.Error("looking up info for HTTP challenge", | ||||
| 				zap.String("host", host), | ||||
| 				zap.String("token_key", tokenKey), | ||||
| 				zap.Error(err)) | ||||
| 		} | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	return am.answerHTTPChallenge(w, r, challenge) | ||||
| 	return solveHTTPChallenge(am.Logger, w, r, chalInfo.Challenge, distributed) | ||||
| } | ||||
|  | ||||
| // answerHTTPChallenge solves the challenge with chalInfo. | ||||
| // Most of this code borrowed from xenolf's built-in HTTP-01 | ||||
| // challenge solver in March 2018. | ||||
| func (am *ACMEManager) answerHTTPChallenge(w http.ResponseWriter, r *http.Request, challenge acme.Challenge) bool { | ||||
| // solveHTTPChallenge solves the HTTP challenge using the given challenge information. | ||||
| // If the challenge is being solved in a distributed fahsion, set distributed to true for logging purposes. | ||||
| // It returns true the properties of the request check out in relation to the HTTP challenge. | ||||
| // Most of this code borrowed from xenolf's built-in HTTP-01 challenge solver in March 2018. | ||||
| func solveHTTPChallenge(logger *zap.Logger, w http.ResponseWriter, r *http.Request, challenge acme.Challenge, distributed bool) bool { | ||||
| 	challengeReqPath := challenge.HTTP01ResourcePath() | ||||
| 	if r.URL.Path == challengeReqPath && | ||||
| 		strings.EqualFold(hostOnly(r.Host), challenge.Identifier.Value) && // mitigate DNS rebinding attacks | ||||
| @@ -113,17 +95,26 @@ func (am *ACMEManager) answerHTTPChallenge(w http.ResponseWriter, r *http.Reques | ||||
| 		w.Header().Add("Content-Type", "text/plain") | ||||
| 		w.Write([]byte(challenge.KeyAuthorization)) | ||||
| 		r.Close = true | ||||
| 		if am.Logger != nil { | ||||
| 			am.Logger.Info("served key authentication", | ||||
| 		if logger != nil { | ||||
| 			logger.Info("served key authentication", | ||||
| 				zap.String("identifier", challenge.Identifier.Value), | ||||
| 				zap.String("challenge", "http-01"), | ||||
| 				zap.String("remote", r.RemoteAddr)) | ||||
| 				zap.String("remote", r.RemoteAddr), | ||||
| 				zap.Bool("distributed", distributed)) | ||||
| 		} | ||||
| 		return true | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| // SolveHTTPChallenge solves the HTTP challenge. It should be used only on HTTP requests that are | ||||
| // from ACME servers trying to validate an identifier (i.e. LooksLikeHTTPChallenge() == true). It | ||||
| // returns true if the request criteria check out and it answered with key authentication, in which | ||||
| // case no further handling of the request is necessary. | ||||
| func SolveHTTPChallenge(logger *zap.Logger, w http.ResponseWriter, r *http.Request, challenge acme.Challenge) bool { | ||||
| 	return solveHTTPChallenge(logger, w, r, challenge, false) | ||||
| } | ||||
|  | ||||
| // LooksLikeHTTPChallenge returns true if r looks like an ACME | ||||
| // HTTP challenge request from an ACME server. | ||||
| func LooksLikeHTTPChallenge(r *http.Request) bool { | ||||
|   | ||||
							
								
								
									
										7
									
								
								vendor/github.com/caddyserver/certmagic/maintain.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								vendor/github.com/caddyserver/certmagic/maintain.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -141,6 +141,9 @@ func (certCache *Cache) RenewManagedCertificates(ctx context.Context) error { | ||||
| 			} | ||||
| 			continue | ||||
| 		} | ||||
| 		if cfg.OnDemand != nil { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		// if time is up or expires soon, we need to try to renew it | ||||
| 		if cert.NeedsRenewal(cfg) { | ||||
| @@ -337,8 +340,8 @@ func (certCache *Cache) updateOCSPStaples(ctx context.Context) { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		ocspResp, err := stapleOCSP(cfg.Storage, &cert, nil) | ||||
| 		if err != nil { | ||||
| 		ocspResp, err := stapleOCSP(cfg.OCSP, cfg.Storage, &cert, nil) | ||||
| 		if err != nil || ocspResp == nil { | ||||
| 			if cert.ocsp != nil { | ||||
| 				// if there was no staple before, that's fine; otherwise we should log the error | ||||
| 				if log != nil { | ||||
|   | ||||
							
								
								
									
										27
									
								
								vendor/github.com/caddyserver/certmagic/ocsp.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										27
									
								
								vendor/github.com/caddyserver/certmagic/ocsp.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -34,11 +34,16 @@ import ( | ||||
| // If you don't have the PEM blocks already, just pass in nil. | ||||
| // | ||||
| // Errors here are not necessarily fatal, it could just be that the | ||||
| // certificate doesn't have an issuer URL. | ||||
| // certificate doesn't have an issuer URL. This function may return | ||||
| // both nil values if OCSP stapling is disabled according to ocspConfig. | ||||
| // | ||||
| // If a status was received, it returns that status. Note that the | ||||
| // returned status is not always stapled to the certificate. | ||||
| func stapleOCSP(storage Storage, cert *Certificate, pemBundle []byte) (*ocsp.Response, error) { | ||||
| func stapleOCSP(ocspConfig OCSPConfig, storage Storage, cert *Certificate, pemBundle []byte) (*ocsp.Response, error) { | ||||
| 	if ocspConfig.DisableStapling { | ||||
| 		return nil, nil | ||||
| 	} | ||||
|  | ||||
| 	if pemBundle == nil { | ||||
| 		// we need a PEM encoding only for some function calls below | ||||
| 		bundle := new(bytes.Buffer) | ||||
| @@ -82,7 +87,7 @@ func stapleOCSP(storage Storage, cert *Certificate, pemBundle []byte) (*ocsp.Res | ||||
| 	// If we couldn't get a fresh staple by reading the cache, | ||||
| 	// then we need to request it from the OCSP responder | ||||
| 	if ocspResp == nil || len(ocspBytes) == 0 { | ||||
| 		ocspBytes, ocspResp, ocspErr = getOCSPForCert(pemBundle) | ||||
| 		ocspBytes, ocspResp, ocspErr = getOCSPForCert(ocspConfig, pemBundle) | ||||
| 		if ocspErr != nil { | ||||
| 			// An error here is not a problem because a certificate may simply | ||||
| 			// not contain a link to an OCSP server. But we should log it anyway. | ||||
| @@ -125,7 +130,7 @@ func stapleOCSP(storage Storage, cert *Certificate, pemBundle []byte) (*ocsp.Res | ||||
| // values are nil, the OCSP status may be assumed OCSPUnknown. | ||||
| // | ||||
| // Borrowed from xenolf. | ||||
| func getOCSPForCert(bundle []byte) ([]byte, *ocsp.Response, error) { | ||||
| func getOCSPForCert(ocspConfig OCSPConfig, bundle []byte) ([]byte, *ocsp.Response, error) { | ||||
| 	// TODO: Perhaps this should be synchronized too, with a Locker? | ||||
|  | ||||
| 	certificates, err := parseCertsFromPEMBundle(bundle) | ||||
| @@ -142,6 +147,18 @@ func getOCSPForCert(bundle []byte) ([]byte, *ocsp.Response, error) { | ||||
| 	if len(issuedCert.OCSPServer) == 0 { | ||||
| 		return nil, nil, fmt.Errorf("no OCSP server specified in certificate") | ||||
| 	} | ||||
|  | ||||
| 	// apply override for responder URL | ||||
| 	respURL := issuedCert.OCSPServer[0] | ||||
| 	if len(ocspConfig.ResponderOverrides) > 0 { | ||||
| 		if override, ok := ocspConfig.ResponderOverrides[respURL]; ok { | ||||
| 			respURL = override | ||||
| 		} | ||||
| 	} | ||||
| 	if respURL == "" { | ||||
| 		return nil, nil, fmt.Errorf("override disables querying OCSP responder: %v", issuedCert.OCSPServer[0]) | ||||
| 	} | ||||
|  | ||||
| 	if len(certificates) == 1 { | ||||
| 		if len(issuedCert.IssuingCertificateURL) == 0 { | ||||
| 			return nil, nil, fmt.Errorf("no URL to issuing certificate") | ||||
| @@ -176,7 +193,7 @@ func getOCSPForCert(bundle []byte) ([]byte, *ocsp.Response, error) { | ||||
| 	} | ||||
|  | ||||
| 	reader := bytes.NewReader(ocspReq) | ||||
| 	req, err := http.Post(issuedCert.OCSPServer[0], "application/ocsp-request", reader) | ||||
| 	req, err := http.Post(respURL, "application/ocsp-request", reader) | ||||
| 	if err != nil { | ||||
| 		return nil, nil, fmt.Errorf("making OCSP request: %v", err) | ||||
| 	} | ||||
|   | ||||
							
								
								
									
										127
									
								
								vendor/github.com/caddyserver/certmagic/solvers.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										127
									
								
								vendor/github.com/caddyserver/certmagic/solvers.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -123,22 +123,19 @@ type tlsALPNSolver struct { | ||||
| // Present adds the certificate to the certificate cache and, if | ||||
| // needed, starts a TLS server for answering TLS-ALPN challenges. | ||||
| func (s *tlsALPNSolver) Present(ctx context.Context, chal acme.Challenge) error { | ||||
| 	// load the certificate into the cache; this isn't strictly necessary | ||||
| 	// if we're using the distributed solver since our GetCertificate | ||||
| 	// function will check storage for the keyAuth anyway, but it seems | ||||
| 	// like loading it into the cache is the right thing to do | ||||
| 	// we pre-generate the certificate for efficiency with multi-perspective | ||||
| 	// validation, so it only has to be done once (at least, by this instance; | ||||
| 	// distributed solving does not have that luxury, oh well) - update the | ||||
| 	// challenge data in memory to be the generated certificate | ||||
| 	cert, err := acmez.TLSALPN01ChallengeCert(chal) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	certHash := hashCertificateChain(cert.Certificate) | ||||
| 	s.config.certCache.mu.Lock() | ||||
| 	s.config.certCache.cache[tlsALPNCertKeyName(chal.Identifier.Value)] = Certificate{ | ||||
| 		Certificate: *cert, | ||||
| 		Names:       []string{chal.Identifier.Value}, | ||||
| 		hash:        certHash, // perhaps not necesssary | ||||
| 	} | ||||
| 	s.config.certCache.mu.Unlock() | ||||
| 	activeChallengesMu.Lock() | ||||
| 	chalData := activeChallenges[chal.Identifier.Value] | ||||
| 	chalData.data = cert | ||||
| 	activeChallenges[chal.Identifier.Value] = chalData | ||||
| 	activeChallengesMu.Unlock() | ||||
|  | ||||
| 	// the rest of this function increments the | ||||
| 	// challenge count for the solver at this | ||||
| @@ -273,13 +270,6 @@ func (s *DNS01Solver) Present(ctx context.Context, challenge acme.Challenge) err | ||||
| 	dnsName := challenge.DNS01TXTRecordName() | ||||
| 	keyAuth := challenge.DNS01KeyAuthorization() | ||||
|  | ||||
| 	rec := libdns.Record{ | ||||
| 		Type:  "TXT", | ||||
| 		Name:  dnsName, | ||||
| 		Value: keyAuth, | ||||
| 		TTL:   s.TTL, | ||||
| 	} | ||||
|  | ||||
| 	// multiple identifiers can have the same ACME challenge | ||||
| 	// domain (e.g. example.com and *.example.com) so we need | ||||
| 	// to ensure that we don't solve those concurrently and | ||||
| @@ -292,6 +282,13 @@ func (s *DNS01Solver) Present(ctx context.Context, challenge acme.Challenge) err | ||||
| 		return fmt.Errorf("could not determine zone for domain %q: %v", dnsName, err) | ||||
| 	} | ||||
|  | ||||
| 	rec := libdns.Record{ | ||||
| 		Type:  "TXT", | ||||
| 		Name:  libdns.RelativeName(dnsName+".", zone), | ||||
| 		Value: keyAuth, | ||||
| 		TTL:   s.TTL, | ||||
| 	} | ||||
|  | ||||
| 	results, err := s.DNSProvider.AppendRecords(ctx, zone, []libdns.Record{rec}) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("adding temporary record for zone %s: %w", zone, err) | ||||
| @@ -458,20 +455,19 @@ func (mmu *mapMutex) locked(key interface{}) (ok bool) { | ||||
| // sharing sync and storage, and using the facilities provided by | ||||
| // this package for solving the challenges. | ||||
| type distributedSolver struct { | ||||
| 	// The config with a certificate cache | ||||
| 	// with a reference to the storage to | ||||
| 	// use which is shared among all the | ||||
| 	// instances in the cluster - REQUIRED. | ||||
| 	acmeManager *ACMEManager | ||||
| 	// The storage backing the distributed solver. It must be | ||||
| 	// the same storage configuration as what is solving the | ||||
| 	// challenge in order to be effective. | ||||
| 	storage Storage | ||||
|  | ||||
| 	// The storage key prefix, associated with the issuer | ||||
| 	// that is solving the challenge. | ||||
| 	storageKeyIssuerPrefix string | ||||
|  | ||||
| 	// Since the distributedSolver is only a | ||||
| 	// wrapper over an actual solver, place | ||||
| 	// the actual solver here. | ||||
| 	solver acmez.Solver | ||||
|  | ||||
| 	// The CA endpoint URL associated with | ||||
| 	// this solver. | ||||
| 	caURL string | ||||
| } | ||||
|  | ||||
| // Present invokes the underlying solver's Present method | ||||
| @@ -483,7 +479,7 @@ func (dhs distributedSolver) Present(ctx context.Context, chal acme.Challenge) e | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	err = dhs.acmeManager.config.Storage.Store(dhs.challengeTokensKey(chal.Identifier.Value), infoBytes) | ||||
| 	err = dhs.storage.Store(dhs.challengeTokensKey(chal.Identifier.Value), infoBytes) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| @@ -495,10 +491,18 @@ func (dhs distributedSolver) Present(ctx context.Context, chal acme.Challenge) e | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Wait wraps the underlying solver's Wait() method, if any. Implements acmez.Waiter. | ||||
| func (dhs distributedSolver) Wait(ctx context.Context, challenge acme.Challenge) error { | ||||
| 	if waiter, ok := dhs.solver.(acmez.Waiter); ok { | ||||
| 		return waiter.Wait(ctx, challenge) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // CleanUp invokes the underlying solver's CleanUp method | ||||
| // and also cleans up any assets saved to storage. | ||||
| func (dhs distributedSolver) CleanUp(ctx context.Context, chal acme.Challenge) error { | ||||
| 	err := dhs.acmeManager.config.Storage.Delete(dhs.challengeTokensKey(chal.Identifier.Value)) | ||||
| 	err := dhs.storage.Delete(dhs.challengeTokensKey(chal.Identifier.Value)) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| @@ -511,7 +515,7 @@ func (dhs distributedSolver) CleanUp(ctx context.Context, chal acme.Challenge) e | ||||
|  | ||||
| // challengeTokensPrefix returns the key prefix for challenge info. | ||||
| func (dhs distributedSolver) challengeTokensPrefix() string { | ||||
| 	return path.Join(dhs.acmeManager.storageKeyCAPrefix(dhs.caURL), "challenge_tokens") | ||||
| 	return path.Join(dhs.storageKeyIssuerPrefix, "challenge_tokens") | ||||
| } | ||||
|  | ||||
| // challengeTokensKey returns the key to use to store and access | ||||
| @@ -607,6 +611,15 @@ func dialTCPSocket(addr string) error { | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // GetACMEChallenge returns an active ACME challenge for the given identifier, | ||||
| // or false if no active challenge for that identifier is known. | ||||
| func GetACMEChallenge(identifier string) (Challenge, bool) { | ||||
| 	activeChallengesMu.Lock() | ||||
| 	chalData, ok := activeChallenges[identifier] | ||||
| 	activeChallengesMu.Unlock() | ||||
| 	return chalData, ok | ||||
| } | ||||
|  | ||||
| // The active challenge solvers, keyed by listener address, | ||||
| // and protected by a mutex. Note that the creation of | ||||
| // solver listeners and the incrementing of their counts | ||||
| @@ -616,8 +629,56 @@ var ( | ||||
| 	solversMu sync.Mutex | ||||
| ) | ||||
|  | ||||
| // activeChallenges holds information about all known, currently-active | ||||
| // ACME challenges, keyed by identifier. CertMagic guarantees that | ||||
| // challenges for the same identifier do not overlap, by its locking | ||||
| // mechanisms; thus if a challenge comes in for a certain identifier, | ||||
| // we can be confident that if this process initiated the challenge, | ||||
| // the correct information to solve it is in this map. (It may have | ||||
| // alternatively been initiated by another instance in a cluster, in | ||||
| // which case the distributed solver will take care of that.) | ||||
| var ( | ||||
| 	activeChallenges   = make(map[string]Challenge) | ||||
| 	activeChallengesMu sync.Mutex | ||||
| ) | ||||
|  | ||||
| // Challenge is an ACME challenge, but optionally paired with | ||||
| // data that can make it easier or more efficient to solve. | ||||
| type Challenge struct { | ||||
| 	acme.Challenge | ||||
| 	data interface{} | ||||
| } | ||||
|  | ||||
| // solverWrapper should be used to wrap all challenge solvers so that | ||||
| // we can add the challenge info to memory; this makes challenges globally | ||||
| // solvable by a single HTTP or TLS server even if multiple servers with | ||||
| // different configurations/scopes need to get certificates. | ||||
| type solverWrapper struct{ acmez.Solver } | ||||
|  | ||||
| func (sw solverWrapper) Present(ctx context.Context, chal acme.Challenge) error { | ||||
| 	activeChallengesMu.Lock() | ||||
| 	activeChallenges[chal.Identifier.Value] = Challenge{Challenge: chal} | ||||
| 	activeChallengesMu.Unlock() | ||||
| 	return sw.Solver.Present(ctx, chal) | ||||
| } | ||||
|  | ||||
| func (sw solverWrapper) Wait(ctx context.Context, chal acme.Challenge) error { | ||||
| 	if waiter, ok := sw.Solver.(acmez.Waiter); ok { | ||||
| 		return waiter.Wait(ctx, chal) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (sw solverWrapper) CleanUp(ctx context.Context, chal acme.Challenge) error { | ||||
| 	activeChallengesMu.Lock() | ||||
| 	delete(activeChallenges, chal.Identifier.Value) | ||||
| 	activeChallengesMu.Unlock() | ||||
| 	return sw.Solver.CleanUp(ctx, chal) | ||||
| } | ||||
|  | ||||
| // Interface guards | ||||
| var ( | ||||
| 	_ acmez.Solver = (*DNS01Solver)(nil) | ||||
| 	_ acmez.Waiter = (*DNS01Solver)(nil) | ||||
| 	_ acmez.Solver = (*solverWrapper)(nil) | ||||
| 	_ acmez.Waiter = (*solverWrapper)(nil) | ||||
| 	_ acmez.Waiter = (*distributedSolver)(nil) | ||||
| ) | ||||
|   | ||||
							
								
								
									
										16
									
								
								vendor/github.com/caddyserver/certmagic/storage.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										16
									
								
								vendor/github.com/caddyserver/certmagic/storage.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -16,12 +16,13 @@ package certmagic | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"log" | ||||
| 	"path" | ||||
| 	"regexp" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	"go.uber.org/zap" | ||||
| ) | ||||
|  | ||||
| // Storage is a type that implements a key-value store. | ||||
| @@ -213,16 +214,20 @@ func (keys KeyBuilder) Safe(str string) string { | ||||
| // this does not cancel the operations that | ||||
| // the locks are synchronizing, this should be | ||||
| // called only immediately before process exit. | ||||
| func CleanUpOwnLocks() { | ||||
| // Errors are only reported if a logger is given. | ||||
| func CleanUpOwnLocks(logger *zap.Logger) { | ||||
| 	locksMu.Lock() | ||||
| 	defer locksMu.Unlock() | ||||
| 	for lockKey, storage := range locks { | ||||
| 		err := storage.Unlock(lockKey) | ||||
| 		if err == nil { | ||||
| 			delete(locks, lockKey) | ||||
| 		} else { | ||||
| 			log.Printf("[ERROR] Unable to clean up lock: %v (lock=%s storage=%s)", | ||||
| 				err, lockKey, storage) | ||||
| 		} else if logger != nil { | ||||
| 			logger.Error("unable to clean up lock in storage backend", | ||||
| 				zap.Any("storage", storage), | ||||
| 				zap.String("lock_key", lockKey), | ||||
| 				zap.Error(err), | ||||
| 			) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -272,6 +277,7 @@ var safeKeyRE = regexp.MustCompile(`[^\w@.-]`) | ||||
| // ErrNotExist is returned by Storage implementations when | ||||
| // a resource is not found. It is similar to os.IsNotExist | ||||
| // except this is a type, not a variable. | ||||
| // TODO: use new Go error wrapping conventions | ||||
| type ErrNotExist interface { | ||||
| 	error | ||||
| } | ||||
|   | ||||
							
								
								
									
										2
									
								
								vendor/github.com/miekg/dns/Makefile.release
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/miekg/dns/Makefile.release
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,7 +1,7 @@ | ||||
| # Makefile for releasing. | ||||
| # | ||||
| # The release is controlled from version.go. The version found there is | ||||
| # used to tag the git repo, we're not building any artifects so there is nothing | ||||
| # used to tag the git repo, we're not building any artifacts so there is nothing | ||||
| # to upload to github. | ||||
| # | ||||
| # * Up the version in version.go | ||||
|   | ||||
							
								
								
									
										2
									
								
								vendor/github.com/miekg/dns/client.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/miekg/dns/client.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -379,7 +379,7 @@ func Dial(network, address string) (conn *Conn, err error) { | ||||
| func ExchangeContext(ctx context.Context, m *Msg, a string) (r *Msg, err error) { | ||||
| 	client := Client{Net: "udp"} | ||||
| 	r, _, err = client.ExchangeContext(ctx, m, a) | ||||
| 	// ignorint rtt to leave the original ExchangeContext API unchanged, but | ||||
| 	// ignoring rtt to leave the original ExchangeContext API unchanged, but | ||||
| 	// this function will go away | ||||
| 	return r, err | ||||
| } | ||||
|   | ||||
							
								
								
									
										5
									
								
								vendor/github.com/miekg/dns/defaults.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								vendor/github.com/miekg/dns/defaults.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -349,10 +349,7 @@ func ReverseAddr(addr string) (arpa string, err error) { | ||||
| 	// Add it, in reverse, to the buffer | ||||
| 	for i := len(ip) - 1; i >= 0; i-- { | ||||
| 		v := ip[i] | ||||
| 		buf = append(buf, hexDigit[v&0xF]) | ||||
| 		buf = append(buf, '.') | ||||
| 		buf = append(buf, hexDigit[v>>4]) | ||||
| 		buf = append(buf, '.') | ||||
| 		buf = append(buf, hexDigit[v&0xF], '.', hexDigit[v>>4], '.') | ||||
| 	} | ||||
| 	// Append "ip6.arpa." and return (buf already has the final .) | ||||
| 	buf = append(buf, "ip6.arpa."...) | ||||
|   | ||||
							
								
								
									
										5
									
								
								vendor/github.com/miekg/dns/dnssec.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								vendor/github.com/miekg/dns/dnssec.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -4,6 +4,7 @@ import ( | ||||
| 	"bytes" | ||||
| 	"crypto" | ||||
| 	"crypto/ecdsa" | ||||
| 	"crypto/ed25519" | ||||
| 	"crypto/elliptic" | ||||
| 	"crypto/rand" | ||||
| 	"crypto/rsa" | ||||
| @@ -17,8 +18,6 @@ import ( | ||||
| 	"sort" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"golang.org/x/crypto/ed25519" | ||||
| ) | ||||
|  | ||||
| // DNSSEC encryption algorithm codes. | ||||
| @@ -500,7 +499,7 @@ func (rr *RRSIG) ValidityPeriod(t time.Time) bool { | ||||
| 	return ti <= utc && utc <= te | ||||
| } | ||||
|  | ||||
| // Return the signatures base64 encodedig sigdata as a byte slice. | ||||
| // Return the signatures base64 encoding sigdata as a byte slice. | ||||
| func (rr *RRSIG) sigBuf() []byte { | ||||
| 	sigbuf, err := fromBase64([]byte(rr.Signature)) | ||||
| 	if err != nil { | ||||
|   | ||||
							
								
								
									
										3
									
								
								vendor/github.com/miekg/dns/dnssec_keygen.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								vendor/github.com/miekg/dns/dnssec_keygen.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -3,12 +3,11 @@ package dns | ||||
| import ( | ||||
| 	"crypto" | ||||
| 	"crypto/ecdsa" | ||||
| 	"crypto/ed25519" | ||||
| 	"crypto/elliptic" | ||||
| 	"crypto/rand" | ||||
| 	"crypto/rsa" | ||||
| 	"math/big" | ||||
|  | ||||
| 	"golang.org/x/crypto/ed25519" | ||||
| ) | ||||
|  | ||||
| // Generate generates a DNSKEY of the given bit size. | ||||
|   | ||||
							
								
								
									
										3
									
								
								vendor/github.com/miekg/dns/dnssec_keyscan.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								vendor/github.com/miekg/dns/dnssec_keyscan.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -4,13 +4,12 @@ import ( | ||||
| 	"bufio" | ||||
| 	"crypto" | ||||
| 	"crypto/ecdsa" | ||||
| 	"crypto/ed25519" | ||||
| 	"crypto/rsa" | ||||
| 	"io" | ||||
| 	"math/big" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
|  | ||||
| 	"golang.org/x/crypto/ed25519" | ||||
| ) | ||||
|  | ||||
| // NewPrivateKey returns a PrivateKey by parsing the string s. | ||||
|   | ||||
							
								
								
									
										3
									
								
								vendor/github.com/miekg/dns/dnssec_privkey.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								vendor/github.com/miekg/dns/dnssec_privkey.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -3,11 +3,10 @@ package dns | ||||
| import ( | ||||
| 	"crypto" | ||||
| 	"crypto/ecdsa" | ||||
| 	"crypto/ed25519" | ||||
| 	"crypto/rsa" | ||||
| 	"math/big" | ||||
| 	"strconv" | ||||
|  | ||||
| 	"golang.org/x/crypto/ed25519" | ||||
| ) | ||||
|  | ||||
| const format = "Private-key-format: v1.3\n" | ||||
|   | ||||
							
								
								
									
										2
									
								
								vendor/github.com/miekg/dns/edns.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/miekg/dns/edns.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -525,7 +525,7 @@ func (e *EDNS0_N3U) String() string { | ||||
| } | ||||
| func (e *EDNS0_N3U) copy() EDNS0 { return &EDNS0_N3U{e.Code, e.AlgCode} } | ||||
|  | ||||
| // EDNS0_EXPIRE implementes the EDNS0 option as described in RFC 7314. | ||||
| // EDNS0_EXPIRE implements the EDNS0 option as described in RFC 7314. | ||||
| type EDNS0_EXPIRE struct { | ||||
| 	Code   uint16 // Always EDNS0EXPIRE | ||||
| 	Expire uint32 | ||||
|   | ||||
							
								
								
									
										10
									
								
								vendor/github.com/miekg/dns/go.mod
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								vendor/github.com/miekg/dns/go.mod
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,11 +1,9 @@ | ||||
| module github.com/miekg/dns | ||||
|  | ||||
| go 1.12 | ||||
| go 1.13 | ||||
|  | ||||
| require ( | ||||
| 	golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 | ||||
| 	golang.org/x/net v0.0.0-20190923162816-aa69164e4478 | ||||
| 	golang.org/x/sync v0.0.0-20190423024810-112230192c58 | ||||
| 	golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe | ||||
| 	golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 // indirect | ||||
| 	golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 | ||||
| 	golang.org/x/sync v0.0.0-20210220032951-036812b2e83c | ||||
| 	golang.org/x/sys v0.0.0-20210303074136-134d130e1a04 | ||||
| ) | ||||
|   | ||||
							
								
								
									
										47
									
								
								vendor/github.com/miekg/dns/go.sum
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										47
									
								
								vendor/github.com/miekg/dns/go.sum
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,39 +1,10 @@ | ||||
| golang.org/x/crypto v0.0.0-20181001203147-e3636079e1a4 h1:Vk3wNqEZwyGyei9yq5ekj7frek2u7HUfffJ1/opblzc= | ||||
| golang.org/x/crypto v0.0.0-20181001203147-e3636079e1a4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= | ||||
| golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||||
| golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472 h1:Gv7RPwsi3eZ2Fgewe3CBsuOebPwO27PoXzRpJPsvSSM= | ||||
| golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||||
| golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392 h1:ACG4HJsFiNMf47Y4PeRoebLNy/2lXT9EtprMuTFWt1M= | ||||
| golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= | ||||
| golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= | ||||
| golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||||
| golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= | ||||
| golang.org/x/net v0.0.0-20180926154720-4dfa2610cdf3 h1:dgd4x4kJt7G4k4m93AYLzM8Ni6h2qLTfh9n9vXJT3/0= | ||||
| golang.org/x/net v0.0.0-20180926154720-4dfa2610cdf3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||
| golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||||
| golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM= | ||||
| golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g= | ||||
| golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= | ||||
| golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= | ||||
| golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sys v0.0.0-20180928133829-e4b3c5e90611 h1:O33LKL7WyJgjN9CvxfTIomjIClbd/Kq86/iipowHQU0= | ||||
| golang.org/x/sys v0.0.0-20180928133829-e4b3c5e90611/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20190904154756-749cb33beabd h1:DBH9mDw0zluJT/R+nGuV3jWFWLFaHyYZWD4tOT+cjn0= | ||||
| golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe h1:6fAMxZRR6sl1Uq8U61gxU+kPTs2tR8uOySCbBP7BN/M= | ||||
| golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||
| golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= | ||||
| golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= | ||||
| golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= | ||||
| golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= | ||||
| golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210303074136-134d130e1a04 h1:cEhElsAv9LUt9ZUUocxzWe05oFLVd+AA2nstydTeI8g= | ||||
| golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= | ||||
| golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||||
| golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | ||||
| golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||
| golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnfG5kSmgy9KZR9sW3W5QeA= | ||||
| golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | ||||
| golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
|   | ||||
							
								
								
									
										2
									
								
								vendor/github.com/miekg/dns/labels.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/miekg/dns/labels.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -10,7 +10,7 @@ package dns | ||||
| // escaped dots (\.) for instance. | ||||
| // s must be a syntactically valid domain name, see IsDomainName. | ||||
| func SplitDomainName(s string) (labels []string) { | ||||
| 	if len(s) == 0 { | ||||
| 	if s == "" { | ||||
| 		return nil | ||||
| 	} | ||||
| 	fqdnEnd := 0 // offset of the final '.' or the length of the name | ||||
|   | ||||
							
								
								
									
										2
									
								
								vendor/github.com/miekg/dns/msg.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/miekg/dns/msg.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -742,7 +742,7 @@ func (dns *Msg) packBufferWithCompressionMap(buf []byte, compression compression | ||||
| 	} | ||||
|  | ||||
| 	// Set extended rcode unconditionally if we have an opt, this will allow | ||||
| 	// reseting the extended rcode bits if they need to. | ||||
| 	// resetting the extended rcode bits if they need to. | ||||
| 	if opt := dns.IsEdns0(); opt != nil { | ||||
| 		opt.SetExtendedRcode(uint16(dns.Rcode)) | ||||
| 	} else if dns.Rcode > 0xF { | ||||
|   | ||||
							
								
								
									
										2
									
								
								vendor/github.com/miekg/dns/privaterr.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/miekg/dns/privaterr.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -6,7 +6,7 @@ import "strings" | ||||
| // RFC 6895. This allows one to experiment with new RR types, without requesting an | ||||
| // official type code. Also see dns.PrivateHandle and dns.PrivateHandleRemove. | ||||
| type PrivateRdata interface { | ||||
| 	// String returns the text presentaton of the Rdata of the Private RR. | ||||
| 	// String returns the text presentation of the Rdata of the Private RR. | ||||
| 	String() string | ||||
| 	// Parse parses the Rdata of the private RR. | ||||
| 	Parse([]string) error | ||||
|   | ||||
							
								
								
									
										4
									
								
								vendor/github.com/miekg/dns/scan.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/miekg/dns/scan.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1233,7 +1233,7 @@ func stringToCm(token string) (e, m uint8, ok bool) { | ||||
| 			// 'nn.1' must be treated as 'nn-meters and 10cm, not 1cm. | ||||
| 			cmeters *= 10 | ||||
| 		} | ||||
| 		if len(s[0]) == 0 { | ||||
| 		if s[0] == "" { | ||||
| 			// This will allow omitting the 'meter' part, like .01 (meaning 0.01m = 1cm). | ||||
| 			break | ||||
| 		} | ||||
| @@ -1352,7 +1352,7 @@ func stringToNodeID(l lex) (uint64, *ParseError) { | ||||
| 	if len(l.token) < 19 { | ||||
| 		return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l} | ||||
| 	} | ||||
| 	// There must be three colons at fixes postitions, if not its a parse error | ||||
| 	// There must be three colons at fixes positions, if not its a parse error | ||||
| 	if l.token[4] != ':' && l.token[9] != ':' && l.token[14] != ':' { | ||||
| 		return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l} | ||||
| 	} | ||||
|   | ||||
							
								
								
									
										46
									
								
								vendor/github.com/miekg/dns/scan_rr.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										46
									
								
								vendor/github.com/miekg/dns/scan_rr.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -609,7 +609,7 @@ func (rr *LOC) parse(c *zlexer, o string) *ParseError { | ||||
|  | ||||
| 	c.Next() // zBlank | ||||
| 	l, _ = c.Next() | ||||
| 	if i, err := strconv.ParseFloat(l.token, 32); err != nil || l.err || i < 0 || i >= 60 { | ||||
| 	if i, err := strconv.ParseFloat(l.token, 64); err != nil || l.err || i < 0 || i >= 60 { | ||||
| 		return &ParseError{"", "bad LOC Latitude seconds", l} | ||||
| 	} else { | ||||
| 		rr.Latitude += uint32(1000 * i) | ||||
| @@ -645,7 +645,7 @@ East: | ||||
| 	} | ||||
| 	c.Next() // zBlank | ||||
| 	l, _ = c.Next() | ||||
| 	if i, err := strconv.ParseFloat(l.token, 32); err != nil || l.err || i < 0 || i >= 60 { | ||||
| 	if i, err := strconv.ParseFloat(l.token, 64); err != nil || l.err || i < 0 || i >= 60 { | ||||
| 		return &ParseError{"", "bad LOC Longitude seconds", l} | ||||
| 	} else { | ||||
| 		rr.Longitude += uint32(1000 * i) | ||||
| @@ -662,7 +662,7 @@ East: | ||||
| Altitude: | ||||
| 	c.Next() // zBlank | ||||
| 	l, _ = c.Next() | ||||
| 	if len(l.token) == 0 || l.err { | ||||
| 	if l.token == "" || l.err { | ||||
| 		return &ParseError{"", "bad LOC Altitude", l} | ||||
| 	} | ||||
| 	if l.token[len(l.token)-1] == 'M' || l.token[len(l.token)-1] == 'm' { | ||||
| @@ -722,7 +722,7 @@ func (rr *HIP) parse(c *zlexer, o string) *ParseError { | ||||
|  | ||||
| 	c.Next()        // zBlank | ||||
| 	l, _ = c.Next() // zString | ||||
| 	if len(l.token) == 0 || l.err { | ||||
| 	if l.token == "" || l.err { | ||||
| 		return &ParseError{"", "bad HIP Hit", l} | ||||
| 	} | ||||
| 	rr.Hit = l.token // This can not contain spaces, see RFC 5205 Section 6. | ||||
| @@ -730,7 +730,7 @@ func (rr *HIP) parse(c *zlexer, o string) *ParseError { | ||||
|  | ||||
| 	c.Next()        // zBlank | ||||
| 	l, _ = c.Next() // zString | ||||
| 	if len(l.token) == 0 || l.err { | ||||
| 	if l.token == "" || l.err { | ||||
| 		return &ParseError{"", "bad HIP PublicKey", l} | ||||
| 	} | ||||
| 	rr.PublicKey = l.token // This cannot contain spaces | ||||
| @@ -846,6 +846,38 @@ func (rr *CSYNC) parse(c *zlexer, o string) *ParseError { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (rr *ZONEMD) parse(c *zlexer, o string) *ParseError { | ||||
| 	l, _ := c.Next() | ||||
| 	i, e := strconv.ParseUint(l.token, 10, 32) | ||||
| 	if e != nil || l.err { | ||||
| 		return &ParseError{"", "bad ZONEMD Serial", l} | ||||
| 	} | ||||
| 	rr.Serial = uint32(i) | ||||
|  | ||||
| 	c.Next() // zBlank | ||||
| 	l, _ = c.Next() | ||||
| 	i, e1 := strconv.ParseUint(l.token, 10, 8) | ||||
| 	if e1 != nil || l.err { | ||||
| 		return &ParseError{"", "bad ZONEMD Scheme", l} | ||||
| 	} | ||||
| 	rr.Scheme = uint8(i) | ||||
|  | ||||
| 	c.Next() // zBlank | ||||
| 	l, _ = c.Next() | ||||
| 	i, err := strconv.ParseUint(l.token, 10, 8) | ||||
| 	if err != nil || l.err { | ||||
| 		return &ParseError{"", "bad ZONEMD Hash Algorithm", l} | ||||
| 	} | ||||
| 	rr.Hash = uint8(i) | ||||
|  | ||||
| 	s, e2 := endingToString(c, "bad ZONEMD Digest") | ||||
| 	if e2 != nil { | ||||
| 		return e2 | ||||
| 	} | ||||
| 	rr.Digest = s | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (rr *SIG) parse(c *zlexer, o string) *ParseError { return rr.RRSIG.parse(c, o) } | ||||
|  | ||||
| func (rr *RRSIG) parse(c *zlexer, o string) *ParseError { | ||||
| @@ -997,7 +1029,7 @@ func (rr *NSEC3) parse(c *zlexer, o string) *ParseError { | ||||
| 	rr.Iterations = uint16(i) | ||||
| 	c.Next() | ||||
| 	l, _ = c.Next() | ||||
| 	if len(l.token) == 0 || l.err { | ||||
| 	if l.token == "" || l.err { | ||||
| 		return &ParseError{"", "bad NSEC3 Salt", l} | ||||
| 	} | ||||
| 	if l.token != "-" { | ||||
| @@ -1007,7 +1039,7 @@ func (rr *NSEC3) parse(c *zlexer, o string) *ParseError { | ||||
|  | ||||
| 	c.Next() | ||||
| 	l, _ = c.Next() | ||||
| 	if len(l.token) == 0 || l.err { | ||||
| 	if l.token == "" || l.err { | ||||
| 		return &ParseError{"", "bad NSEC3 NextDomain", l} | ||||
| 	} | ||||
| 	rr.HashLength = 20 // Fix for NSEC3 (sha1 160 bits) | ||||
|   | ||||
							
								
								
									
										4
									
								
								vendor/github.com/miekg/dns/sig0.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/miekg/dns/sig0.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -17,7 +17,7 @@ func (rr *SIG) Sign(k crypto.Signer, m *Msg) ([]byte, error) { | ||||
| 	if k == nil { | ||||
| 		return nil, ErrPrivKey | ||||
| 	} | ||||
| 	if rr.KeyTag == 0 || len(rr.SignerName) == 0 || rr.Algorithm == 0 { | ||||
| 	if rr.KeyTag == 0 || rr.SignerName == "" || rr.Algorithm == 0 { | ||||
| 		return nil, ErrKey | ||||
| 	} | ||||
|  | ||||
| @@ -78,7 +78,7 @@ func (rr *SIG) Verify(k *KEY, buf []byte) error { | ||||
| 	if k == nil { | ||||
| 		return ErrKey | ||||
| 	} | ||||
| 	if rr.KeyTag == 0 || len(rr.SignerName) == 0 || rr.Algorithm == 0 { | ||||
| 	if rr.KeyTag == 0 || rr.SignerName == "" || rr.Algorithm == 0 { | ||||
| 		return ErrKey | ||||
| 	} | ||||
|  | ||||
|   | ||||
							
								
								
									
										4
									
								
								vendor/github.com/miekg/dns/svcb.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/miekg/dns/svcb.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -321,7 +321,7 @@ func (s *SVCBAlpn) pack() ([]byte, error) { | ||||
| 	// Liberally estimate the size of an alpn as 10 octets | ||||
| 	b := make([]byte, 0, 10*len(s.Alpn)) | ||||
| 	for _, e := range s.Alpn { | ||||
| 		if len(e) == 0 { | ||||
| 		if e == "" { | ||||
| 			return nil, errors.New("dns: svcbalpn: empty alpn-id") | ||||
| 		} | ||||
| 		if len(e) > 255 { | ||||
| @@ -390,7 +390,7 @@ func (*SVCBNoDefaultAlpn) unpack(b []byte) error { | ||||
| } | ||||
|  | ||||
| func (*SVCBNoDefaultAlpn) parse(b string) error { | ||||
| 	if len(b) != 0 { | ||||
| 	if b != "" { | ||||
| 		return errors.New("dns: svcbnodefaultalpn: no_default_alpn must have no value") | ||||
| 	} | ||||
| 	return nil | ||||
|   | ||||
							
								
								
									
										31
									
								
								vendor/github.com/miekg/dns/types.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										31
									
								
								vendor/github.com/miekg/dns/types.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -81,6 +81,7 @@ const ( | ||||
| 	TypeCDNSKEY    uint16 = 60 | ||||
| 	TypeOPENPGPKEY uint16 = 61 | ||||
| 	TypeCSYNC      uint16 = 62 | ||||
| 	TypeZONEMD     uint16 = 63 | ||||
| 	TypeSVCB       uint16 = 64 | ||||
| 	TypeHTTPS      uint16 = 65 | ||||
| 	TypeSPF        uint16 = 99 | ||||
| @@ -150,6 +151,17 @@ const ( | ||||
| 	OpcodeUpdate = 5 | ||||
| ) | ||||
|  | ||||
| // Used in ZONEMD https://tools.ietf.org/html/rfc8976 | ||||
|  | ||||
| const ( | ||||
| 	// ZoneMD Accepted Schemes | ||||
| 	ZoneMDSchemeSimple = 1 | ||||
|  | ||||
| 	// ZoneMD Hash Algorithms | ||||
| 	ZoneMDHashAlgSHA384 = 1 | ||||
| 	ZoneMDHashAlgSHA512 = 2 | ||||
| ) | ||||
|  | ||||
| // Header is the wire format for the DNS packet header. | ||||
| type Header struct { | ||||
| 	Id                                 uint16 | ||||
| @@ -1361,6 +1373,23 @@ func (rr *CSYNC) len(off int, compression map[string]struct{}) int { | ||||
| 	return l | ||||
| } | ||||
|  | ||||
| // ZONEMD RR, from draft-ietf-dnsop-dns-zone-digest | ||||
| type ZONEMD struct { | ||||
| 	Hdr    RR_Header | ||||
| 	Serial uint32 | ||||
| 	Scheme uint8 | ||||
| 	Hash   uint8 | ||||
| 	Digest string `dns:"hex"` | ||||
| } | ||||
|  | ||||
| func (rr *ZONEMD) String() string { | ||||
| 	return rr.Hdr.String() + | ||||
| 		strconv.Itoa(int(rr.Serial)) + | ||||
| 		" " + strconv.Itoa(int(rr.Scheme)) + | ||||
| 		" " + strconv.Itoa(int(rr.Hash)) + | ||||
| 		" " + rr.Digest | ||||
| } | ||||
|  | ||||
| // APL RR. See RFC 3123. | ||||
| type APL struct { | ||||
| 	Hdr      RR_Header | ||||
| @@ -1472,7 +1501,7 @@ func StringToTime(s string) (uint32, error) { | ||||
|  | ||||
| // saltToString converts a NSECX salt to uppercase and returns "-" when it is empty. | ||||
| func saltToString(s string) string { | ||||
| 	if len(s) == 0 { | ||||
| 	if s == "" { | ||||
| 		return "-" | ||||
| 	} | ||||
| 	return strings.ToUpper(s) | ||||
|   | ||||
							
								
								
									
										2
									
								
								vendor/github.com/miekg/dns/version.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/miekg/dns/version.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -3,7 +3,7 @@ package dns | ||||
| import "fmt" | ||||
|  | ||||
| // Version is current version of this library. | ||||
| var Version = v{1, 1, 40} | ||||
| var Version = v{1, 1, 41} | ||||
|  | ||||
| // v holds the version of this library. | ||||
| type v struct { | ||||
|   | ||||
							
								
								
									
										21
									
								
								vendor/github.com/miekg/dns/zduplicate.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/miekg/dns/zduplicate.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1317,3 +1317,24 @@ func (r1 *X25) isDuplicate(_r2 RR) bool { | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (r1 *ZONEMD) isDuplicate(_r2 RR) bool { | ||||
| 	r2, ok := _r2.(*ZONEMD) | ||||
| 	if !ok { | ||||
| 		return false | ||||
| 	} | ||||
| 	_ = r2 | ||||
| 	if r1.Serial != r2.Serial { | ||||
| 		return false | ||||
| 	} | ||||
| 	if r1.Scheme != r2.Scheme { | ||||
| 		return false | ||||
| 	} | ||||
| 	if r1.Hash != r2.Hash { | ||||
| 		return false | ||||
| 	} | ||||
| 	if r1.Digest != r2.Digest { | ||||
| 		return false | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
|   | ||||
							
								
								
									
										52
									
								
								vendor/github.com/miekg/dns/zmsg.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										52
									
								
								vendor/github.com/miekg/dns/zmsg.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1118,6 +1118,26 @@ func (rr *X25) pack(msg []byte, off int, compression compressionMap, compress bo | ||||
| 	return off, nil | ||||
| } | ||||
|  | ||||
| func (rr *ZONEMD) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { | ||||
| 	off, err = packUint32(rr.Serial, msg, off) | ||||
| 	if err != nil { | ||||
| 		return off, err | ||||
| 	} | ||||
| 	off, err = packUint8(rr.Scheme, msg, off) | ||||
| 	if err != nil { | ||||
| 		return off, err | ||||
| 	} | ||||
| 	off, err = packUint8(rr.Hash, msg, off) | ||||
| 	if err != nil { | ||||
| 		return off, err | ||||
| 	} | ||||
| 	off, err = packStringHex(rr.Digest, msg, off) | ||||
| 	if err != nil { | ||||
| 		return off, err | ||||
| 	} | ||||
| 	return off, nil | ||||
| } | ||||
|  | ||||
| // unpack*() functions | ||||
|  | ||||
| func (rr *A) unpack(msg []byte, off int) (off1 int, err error) { | ||||
| @@ -2821,3 +2841,35 @@ func (rr *X25) unpack(msg []byte, off int) (off1 int, err error) { | ||||
| 	} | ||||
| 	return off, nil | ||||
| } | ||||
|  | ||||
| func (rr *ZONEMD) unpack(msg []byte, off int) (off1 int, err error) { | ||||
| 	rdStart := off | ||||
| 	_ = rdStart | ||||
|  | ||||
| 	rr.Serial, off, err = unpackUint32(msg, off) | ||||
| 	if err != nil { | ||||
| 		return off, err | ||||
| 	} | ||||
| 	if off == len(msg) { | ||||
| 		return off, nil | ||||
| 	} | ||||
| 	rr.Scheme, off, err = unpackUint8(msg, off) | ||||
| 	if err != nil { | ||||
| 		return off, err | ||||
| 	} | ||||
| 	if off == len(msg) { | ||||
| 		return off, nil | ||||
| 	} | ||||
| 	rr.Hash, off, err = unpackUint8(msg, off) | ||||
| 	if err != nil { | ||||
| 		return off, err | ||||
| 	} | ||||
| 	if off == len(msg) { | ||||
| 		return off, nil | ||||
| 	} | ||||
| 	rr.Digest, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength)) | ||||
| 	if err != nil { | ||||
| 		return off, err | ||||
| 	} | ||||
| 	return off, nil | ||||
| } | ||||
|   | ||||
							
								
								
									
										14
									
								
								vendor/github.com/miekg/dns/ztypes.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								vendor/github.com/miekg/dns/ztypes.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -82,6 +82,7 @@ var TypeToRR = map[uint16]func() RR{ | ||||
| 	TypeUINFO:      func() RR { return new(UINFO) }, | ||||
| 	TypeURI:        func() RR { return new(URI) }, | ||||
| 	TypeX25:        func() RR { return new(X25) }, | ||||
| 	TypeZONEMD:     func() RR { return new(ZONEMD) }, | ||||
| } | ||||
|  | ||||
| // TypeToString is a map of strings for each RR type. | ||||
| @@ -168,6 +169,7 @@ var TypeToString = map[uint16]string{ | ||||
| 	TypeUNSPEC:     "UNSPEC", | ||||
| 	TypeURI:        "URI", | ||||
| 	TypeX25:        "X25", | ||||
| 	TypeZONEMD:     "ZONEMD", | ||||
| 	TypeNSAPPTR:    "NSAP-PTR", | ||||
| } | ||||
|  | ||||
| @@ -245,6 +247,7 @@ func (rr *UID) Header() *RR_Header        { return &rr.Hdr } | ||||
| func (rr *UINFO) Header() *RR_Header      { return &rr.Hdr } | ||||
| func (rr *URI) Header() *RR_Header        { return &rr.Hdr } | ||||
| func (rr *X25) Header() *RR_Header        { return &rr.Hdr } | ||||
| func (rr *ZONEMD) Header() *RR_Header     { return &rr.Hdr } | ||||
|  | ||||
| // len() functions | ||||
| func (rr *A) len(off int, compression map[string]struct{}) int { | ||||
| @@ -684,6 +687,14 @@ func (rr *X25) len(off int, compression map[string]struct{}) int { | ||||
| 	l += len(rr.PSDNAddress) + 1 | ||||
| 	return l | ||||
| } | ||||
| func (rr *ZONEMD) len(off int, compression map[string]struct{}) int { | ||||
| 	l := rr.Hdr.len(off, compression) | ||||
| 	l += 4 // Serial | ||||
| 	l++    // Scheme | ||||
| 	l++    // Hash | ||||
| 	l += len(rr.Digest) / 2 | ||||
| 	return l | ||||
| } | ||||
|  | ||||
| // copy() functions | ||||
| func (rr *A) copy() RR { | ||||
| @@ -936,3 +947,6 @@ func (rr *URI) copy() RR { | ||||
| func (rr *X25) copy() RR { | ||||
| 	return &X25{rr.Hdr, rr.PSDNAddress} | ||||
| } | ||||
| func (rr *ZONEMD) copy() RR { | ||||
| 	return &ZONEMD{rr.Hdr, rr.Serial, rr.Scheme, rr.Hash, rr.Digest} | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user