mirror of
				https://github.com/go-acme/lego.git
				synced 2025-10-31 08:27:38 +02:00 
			
		
		
		
	Add configurable timeout when obtaining certificates. (#747)
This commit is contained in:
		
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							b1fd570987
						
					
				
				
					commit
					7e1f4948ec
				
			| @@ -98,6 +98,7 @@ GLOBAL OPTIONS: | ||||
|    --http-timeout value         Set the HTTP timeout value to a specific value in seconds. (default: 0) | ||||
|    --dns-timeout value          Set the DNS timeout value to a specific value in seconds. Used only when performing authoritative name servers queries. (default: 10) | ||||
|    --pem                        Generate a .pem file by concatenating the .key and .crt files together. | ||||
|    --cert.timeout value         Set the certificate timeout value to a specific value in seconds. Only used when obtaining certificates. (default: 30) | ||||
|    --help, -h                   show help | ||||
|    --version, -v                print the version | ||||
| ``` | ||||
| @@ -234,7 +235,7 @@ func main() { | ||||
|  | ||||
| 	// This CA URL is configured for a local dev instance of Boulder running in Docker in a VM. | ||||
| 	config.CADirURL = "http://192.168.99.100:4000/directory" | ||||
| 	config.KeyType = certcrypto.RSA2048 | ||||
| 	config.Certificate.KeyType = certcrypto.RSA2048 | ||||
|  | ||||
| 	// A client facilitates communication with the CA server. | ||||
| 	client, err := lego.NewClient(config) | ||||
|   | ||||
| @@ -17,6 +17,7 @@ import ( | ||||
| 	"github.com/xenolf/lego/certcrypto" | ||||
| 	"github.com/xenolf/lego/challenge" | ||||
| 	"github.com/xenolf/lego/log" | ||||
| 	"github.com/xenolf/lego/platform/wait" | ||||
| 	"golang.org/x/crypto/ocsp" | ||||
| 	"golang.org/x/net/idna" | ||||
| ) | ||||
| @@ -60,17 +61,24 @@ type resolver interface { | ||||
| 	Solve(authorizations []acme.Authorization) error | ||||
| } | ||||
|  | ||||
| type Certifier struct { | ||||
| 	core     *api.Core | ||||
| 	keyType  certcrypto.KeyType | ||||
| 	resolver resolver | ||||
| type CertifierOptions struct { | ||||
| 	KeyType certcrypto.KeyType | ||||
| 	Timeout time.Duration | ||||
| } | ||||
|  | ||||
| func NewCertifier(core *api.Core, keyType certcrypto.KeyType, resolver resolver) *Certifier { | ||||
| // Certifier A service to obtain/renew/revoke certificates. | ||||
| type Certifier struct { | ||||
| 	core     *api.Core | ||||
| 	resolver resolver | ||||
| 	options  CertifierOptions | ||||
| } | ||||
|  | ||||
| // NewCertifier creates a Certifier. | ||||
| func NewCertifier(core *api.Core, resolver resolver, options CertifierOptions) *Certifier { | ||||
| 	return &Certifier{ | ||||
| 		core:     core, | ||||
| 		keyType:  keyType, | ||||
| 		resolver: resolver, | ||||
| 		options:  options, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -191,7 +199,7 @@ func (c *Certifier) ObtainForCSR(csr x509.CertificateRequest, bundle bool) (*Res | ||||
| func (c *Certifier) getForOrder(domains []string, order acme.ExtendedOrder, bundle bool, privateKey crypto.PrivateKey, mustStaple bool) (*Resource, error) { | ||||
| 	if privateKey == nil { | ||||
| 		var err error | ||||
| 		privateKey, err = certcrypto.GeneratePrivateKey(c.keyType) | ||||
| 		privateKey, err = certcrypto.GeneratePrivateKey(c.options.KeyType) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| @@ -237,9 +245,9 @@ func (c *Certifier) getForCSR(domains []string, order acme.ExtendedOrder, bundle | ||||
|  | ||||
| 	if respOrder.Status == acme.StatusValid { | ||||
| 		// if the certificate is available right away, short cut! | ||||
| 		ok, err := c.checkResponse(respOrder, certRes, bundle) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		ok, errR := c.checkResponse(respOrder, certRes, bundle) | ||||
| 		if errR != nil { | ||||
| 			return nil, errR | ||||
| 		} | ||||
|  | ||||
| 		if ok { | ||||
| @@ -247,34 +255,26 @@ func (c *Certifier) getForCSR(domains []string, order acme.ExtendedOrder, bundle | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return c.waitForCertificate(certRes, order.Location, bundle) | ||||
| } | ||||
|  | ||||
| func (c *Certifier) waitForCertificate(certRes *Resource, orderURL string, bundle bool) (*Resource, error) { | ||||
| 	stopTimer := time.NewTimer(30 * time.Second) | ||||
| 	defer stopTimer.Stop() | ||||
| 	retryTick := time.NewTicker(500 * time.Millisecond) | ||||
| 	defer retryTick.Stop() | ||||
|  | ||||
| 	for { | ||||
| 		select { | ||||
| 		case <-stopTimer.C: | ||||
| 			return nil, errors.New("certificate polling timed out") | ||||
| 		case <-retryTick.C: | ||||
| 			order, err := c.core.Orders.Get(orderURL) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
|  | ||||
| 			done, err := c.checkResponse(order, certRes, bundle) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			if done { | ||||
| 				return certRes, nil | ||||
| 			} | ||||
| 		} | ||||
| 	timeout := c.options.Timeout | ||||
| 	if c.options.Timeout <= 0 { | ||||
| 		timeout = 30 * time.Second | ||||
| 	} | ||||
|  | ||||
| 	err = wait.For("certificate", timeout, timeout/60, func() (bool, error) { | ||||
| 		ord, errW := c.core.Orders.Get(order.Location) | ||||
| 		if errW != nil { | ||||
| 			return false, errW | ||||
| 		} | ||||
|  | ||||
| 		done, errW := c.checkResponse(ord, certRes, bundle) | ||||
| 		if errW != nil { | ||||
| 			return false, errW | ||||
| 		} | ||||
|  | ||||
| 		return done, nil | ||||
| 	}) | ||||
|  | ||||
| 	return certRes, err | ||||
| } | ||||
|  | ||||
| // checkResponse checks to see if the certificate is ready and a link is contained in the response. | ||||
|   | ||||
| @@ -93,7 +93,7 @@ func Test_checkResponse(t *testing.T) { | ||||
| 	core, err := api.New(http.DefaultClient, "lego-test", apiURL+"/dir", "", key) | ||||
| 	require.NoError(t, err) | ||||
|  | ||||
| 	certifier := NewCertifier(core, certcrypto.RSA2048, &resolverMock{}) | ||||
| 	certifier := NewCertifier(core, &resolverMock{}, CertifierOptions{KeyType: certcrypto.RSA2048}) | ||||
|  | ||||
| 	order := acme.Order{ | ||||
| 		Status:      acme.StatusValid, | ||||
| @@ -141,7 +141,7 @@ func Test_checkResponse_issuerRelUp(t *testing.T) { | ||||
| 	core, err := api.New(http.DefaultClient, "lego-test", apiURL+"/dir", "", key) | ||||
| 	require.NoError(t, err) | ||||
|  | ||||
| 	certifier := NewCertifier(core, certcrypto.RSA2048, &resolverMock{}) | ||||
| 	certifier := NewCertifier(core, &resolverMock{}, CertifierOptions{KeyType: certcrypto.RSA2048}) | ||||
|  | ||||
| 	order := acme.Order{ | ||||
| 		Status:      acme.StatusValid, | ||||
| @@ -180,7 +180,7 @@ func Test_checkResponse_embeddedIssuer(t *testing.T) { | ||||
| 	core, err := api.New(http.DefaultClient, "lego-test", apiURL+"/dir", "", key) | ||||
| 	require.NoError(t, err) | ||||
|  | ||||
| 	certifier := NewCertifier(core, certcrypto.RSA2048, &resolverMock{}) | ||||
| 	certifier := NewCertifier(core, &resolverMock{}, CertifierOptions{KeyType: certcrypto.RSA2048}) | ||||
|  | ||||
| 	order := acme.Order{ | ||||
| 		Status:      acme.StatusValid, | ||||
|   | ||||
| @@ -105,5 +105,10 @@ func CreateFlags(defaultPath string) []cli.Flag { | ||||
| 			Name:  "pem", | ||||
| 			Usage: "Generate a .pem file by concatenating the .key and .crt files together.", | ||||
| 		}, | ||||
| 		cli.IntFlag{ | ||||
| 			Name:  "cert.timeout", | ||||
| 			Usage: "Set the certificate timeout value to a specific value in seconds. Only used when obtaining certificates.", | ||||
| 			Value: 30, | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -34,11 +34,13 @@ func setup(ctx *cli.Context, accountsStorage *AccountsStorage) (*Account, *lego. | ||||
| } | ||||
|  | ||||
| func newClient(ctx *cli.Context, acc registration.User) *lego.Client { | ||||
| 	keyType := getKeyType(ctx) | ||||
|  | ||||
| 	config := lego.NewConfig(acc) | ||||
| 	config.CADirURL = ctx.GlobalString("server") | ||||
| 	config.KeyType = keyType | ||||
|  | ||||
| 	config.Certificate = lego.CertificateConfig{ | ||||
| 		KeyType: getKeyType(ctx), | ||||
| 		Timeout: time.Duration(ctx.GlobalInt("cert.timeout")) * time.Second, | ||||
| 	} | ||||
| 	config.UserAgent = fmt.Sprintf("lego-cli/%s", ctx.App.Version) | ||||
|  | ||||
| 	if ctx.GlobalIsSet("http-timeout") { | ||||
|   | ||||
| @@ -53,9 +53,10 @@ func NewClient(config *Config) (*Client, error) { | ||||
| 	solversManager := resolver.NewSolversManager(core) | ||||
|  | ||||
| 	prober := resolver.NewProber(solversManager) | ||||
| 	certifier := certificate.NewCertifier(core, prober, certificate.CertifierOptions{KeyType: config.Certificate.KeyType, Timeout: config.Certificate.Timeout}) | ||||
|  | ||||
| 	return &Client{ | ||||
| 		Certificate:  certificate.NewCertifier(core, config.KeyType, prober), | ||||
| 		Certificate:  certifier, | ||||
| 		Challenge:    solversManager, | ||||
| 		Registration: registration.NewRegistrar(core, config.User), | ||||
| 		core:         core, | ||||
|   | ||||
| @@ -35,22 +35,30 @@ const ( | ||||
| ) | ||||
|  | ||||
| type Config struct { | ||||
| 	CADirURL   string | ||||
| 	User       registration.User | ||||
| 	KeyType    certcrypto.KeyType | ||||
| 	UserAgent  string | ||||
| 	HTTPClient *http.Client | ||||
| 	CADirURL    string | ||||
| 	User        registration.User | ||||
| 	UserAgent   string | ||||
| 	HTTPClient  *http.Client | ||||
| 	Certificate CertificateConfig | ||||
| } | ||||
|  | ||||
| func NewConfig(user registration.User) *Config { | ||||
| 	return &Config{ | ||||
| 		CADirURL:   LEDirectoryProduction, | ||||
| 		User:       user, | ||||
| 		KeyType:    certcrypto.RSA2048, | ||||
| 		HTTPClient: createDefaultHTTPClient(), | ||||
| 		Certificate: CertificateConfig{ | ||||
| 			KeyType: certcrypto.RSA2048, | ||||
| 			Timeout: 30 * time.Second, | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| type CertificateConfig struct { | ||||
| 	KeyType certcrypto.KeyType | ||||
| 	Timeout time.Duration | ||||
| } | ||||
|  | ||||
| // createDefaultHTTPClient Creates an HTTP client with a reasonable timeout value | ||||
| // and potentially a custom *x509.CertPool | ||||
| // based on the caCertificatesEnvVar environment variable (see the `initCertPool` function) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user