1package lego
2
3import (
4	"crypto/tls"
5	"crypto/x509"
6	"fmt"
7	"net"
8	"net/http"
9	"os"
10	"time"
11
12	"github.com/go-acme/lego/v4/certcrypto"
13	"github.com/go-acme/lego/v4/registration"
14)
15
16const (
17	// caCertificatesEnvVar is the environment variable name that can be used to
18	// specify the path to PEM encoded CA Certificates that can be used to
19	// authenticate an ACME server with a HTTPS certificate not issued by a CA in
20	// the system-wide trusted root list.
21	caCertificatesEnvVar = "LEGO_CA_CERTIFICATES"
22
23	// caServerNameEnvVar is the environment variable name that can be used to
24	// specify the CA server name that can be used to
25	// authenticate an ACME server with a HTTPS certificate not issued by a CA in
26	// the system-wide trusted root list.
27	caServerNameEnvVar = "LEGO_CA_SERVER_NAME"
28
29	// LEDirectoryProduction URL to the Let's Encrypt production.
30	LEDirectoryProduction = "https://acme-v02.api.letsencrypt.org/directory"
31
32	// LEDirectoryStaging URL to the Let's Encrypt staging.
33	LEDirectoryStaging = "https://acme-staging-v02.api.letsencrypt.org/directory"
34)
35
36type Config struct {
37	CADirURL    string
38	User        registration.User
39	UserAgent   string
40	HTTPClient  *http.Client
41	Certificate CertificateConfig
42}
43
44func NewConfig(user registration.User) *Config {
45	return &Config{
46		CADirURL:   LEDirectoryProduction,
47		User:       user,
48		HTTPClient: createDefaultHTTPClient(),
49		Certificate: CertificateConfig{
50			KeyType: certcrypto.RSA2048,
51			Timeout: 30 * time.Second,
52		},
53	}
54}
55
56type CertificateConfig struct {
57	KeyType certcrypto.KeyType
58	Timeout time.Duration
59}
60
61// createDefaultHTTPClient Creates an HTTP client with a reasonable timeout value
62// and potentially a custom *x509.CertPool
63// based on the caCertificatesEnvVar environment variable (see the `initCertPool` function).
64func createDefaultHTTPClient() *http.Client {
65	return &http.Client{
66		Timeout: 2 * time.Minute,
67		Transport: &http.Transport{
68			Proxy: http.ProxyFromEnvironment,
69			DialContext: (&net.Dialer{
70				Timeout:   30 * time.Second,
71				KeepAlive: 30 * time.Second,
72			}).DialContext,
73			TLSHandshakeTimeout:   30 * time.Second,
74			ResponseHeaderTimeout: 30 * time.Second,
75			TLSClientConfig: &tls.Config{
76				ServerName: os.Getenv(caServerNameEnvVar),
77				RootCAs:    initCertPool(),
78			},
79		},
80	}
81}
82
83// initCertPool creates a *x509.CertPool populated with the PEM certificates
84// found in the filepath specified in the caCertificatesEnvVar OS environment
85// variable. If the caCertificatesEnvVar is not set then initCertPool will
86// return nil. If there is an error creating a *x509.CertPool from the provided
87// caCertificatesEnvVar value then initCertPool will panic.
88func initCertPool() *x509.CertPool {
89	if customCACertsPath := os.Getenv(caCertificatesEnvVar); customCACertsPath != "" {
90		customCAs, err := os.ReadFile(customCACertsPath)
91		if err != nil {
92			panic(fmt.Sprintf("error reading %s=%q: %v",
93				caCertificatesEnvVar, customCACertsPath, err))
94		}
95		certPool := x509.NewCertPool()
96		if ok := certPool.AppendCertsFromPEM(customCAs); !ok {
97			panic(fmt.Sprintf("error creating x509 cert pool from %s=%q: %v",
98				caCertificatesEnvVar, customCACertsPath, err))
99		}
100		return certPool
101	}
102	return nil
103}
104