autocert.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. package acme
  2. // Similar to golang.org/x/crypto/acme/autocert
  3. import (
  4. "context"
  5. "crypto/ecdsa"
  6. "crypto/elliptic"
  7. "crypto/rand"
  8. "crypto/tls"
  9. "crypto/x509"
  10. "crypto/x509/pkix"
  11. "encoding/pem"
  12. "errors"
  13. "fmt"
  14. "io/ioutil"
  15. "net/http"
  16. "path"
  17. "strings"
  18. "sync"
  19. )
  20. // HostCheck function prototype to implement for checking hosts against before issuing certificates
  21. type HostCheck func(host string) error
  22. // WhitelistHosts implements a simple whitelist HostCheck
  23. func WhitelistHosts(hosts ...string) HostCheck {
  24. m := map[string]bool{}
  25. for _, v := range hosts {
  26. m[v] = true
  27. }
  28. return func(host string) error {
  29. if !m[host] {
  30. return errors.New("autocert: host not whitelisted")
  31. }
  32. return nil
  33. }
  34. }
  35. // AutoCert is a stateful certificate manager for issuing certificates on connecting hosts
  36. type AutoCert struct {
  37. // Acme directory Url
  38. // If nil, uses `LetsEncryptStaging`
  39. DirectoryURL string
  40. // Options contains the options used for creating the acme client
  41. Options []OptionFunc
  42. // A function to check whether a host is allowed or not
  43. // If nil, all hosts allowed
  44. // Use `WhitelistHosts(hosts ...string)` for a simple white list of hostnames
  45. HostCheck HostCheck
  46. // Cache dir to store account data and certificates
  47. // If nil, does not write cache data to file
  48. CacheDir string
  49. // When using a staging environment, include a root certificate for verification purposes
  50. RootCert string
  51. // Called before updating challenges
  52. PreUpdateChallengeHook func(Account, Challenge)
  53. // Mapping of token -> keyauth
  54. // Protected by a mutex, but not rwmutex because tokens are deleted once read
  55. tokensLock sync.RWMutex
  56. tokens map[string][]byte
  57. // Mapping of cache key -> value
  58. cacheLock sync.Mutex
  59. cache map[string][]byte
  60. // read lock around getting existing certs
  61. // write lock around issuing new certificate
  62. certLock sync.RWMutex
  63. client Client
  64. }
  65. // HTTPHandler Wraps a handler and provides serving of http-01 challenge tokens from /.well-known/acme-challenge/
  66. // If handler is nil, will redirect all traffic otherwise to https
  67. func (m *AutoCert) HTTPHandler(handler http.Handler) http.Handler {
  68. if handler == nil {
  69. handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  70. http.Redirect(w, r, "https://"+r.Host+r.URL.RequestURI(), http.StatusMovedPermanently)
  71. })
  72. }
  73. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  74. if !strings.HasPrefix(r.URL.Path, "/.well-known/acme-challenge/") {
  75. handler.ServeHTTP(w, r)
  76. return
  77. }
  78. if err := m.checkHost(r.Host); err != nil {
  79. http.Error(w, err.Error(), http.StatusForbidden)
  80. return
  81. }
  82. token := path.Base(r.URL.Path)
  83. m.tokensLock.RLock()
  84. defer m.tokensLock.RUnlock()
  85. keyAuth := m.tokens[token]
  86. if len(keyAuth) == 0 {
  87. http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
  88. return
  89. }
  90. _, _ = w.Write(keyAuth)
  91. })
  92. }
  93. // GetCertificate implements a tls.Config.GetCertificate hook
  94. func (m *AutoCert) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
  95. name := strings.TrimSuffix(hello.ServerName, ".")
  96. if name == "" {
  97. return nil, errors.New("autocert: missing server name")
  98. }
  99. if !strings.Contains(strings.Trim(name, "."), ".") {
  100. return nil, errors.New("autocert: server name component count invalid")
  101. }
  102. if strings.ContainsAny(name, `/\`) {
  103. return nil, errors.New("autocert: server name contains invalid character")
  104. }
  105. // check the hostname is allowed
  106. if err := m.checkHost(name); err != nil {
  107. return nil, err
  108. }
  109. // check if there's an existing cert
  110. m.certLock.RLock()
  111. existingCert, _ := m.getExistingCert(name)
  112. m.certLock.RUnlock()
  113. if existingCert != nil {
  114. return existingCert, nil
  115. }
  116. // if not, attempt to issue a new cert
  117. m.certLock.Lock()
  118. defer m.certLock.Unlock()
  119. return m.issueCert(name)
  120. }
  121. func (m *AutoCert) getDirectoryURL() string {
  122. if m.DirectoryURL != "" {
  123. return m.DirectoryURL
  124. }
  125. return LetsEncryptStaging
  126. }
  127. func (m *AutoCert) getCache(keys ...string) []byte {
  128. key := strings.Join(keys, "-")
  129. m.cacheLock.Lock()
  130. defer m.cacheLock.Unlock()
  131. b := m.cache[key]
  132. if len(b) > 0 {
  133. return b
  134. }
  135. if m.CacheDir == "" {
  136. return nil
  137. }
  138. b, _ = ioutil.ReadFile(path.Join(m.CacheDir, key))
  139. if len(b) == 0 {
  140. return nil
  141. }
  142. if m.cache == nil {
  143. m.cache = map[string][]byte{}
  144. }
  145. m.cache[key] = b
  146. return b
  147. }
  148. func (m *AutoCert) putCache(data []byte, keys ...string) context.Context {
  149. ctx, cancel := context.WithCancel(context.Background())
  150. key := strings.Join(keys, "-")
  151. m.cacheLock.Lock()
  152. defer m.cacheLock.Unlock()
  153. if m.cache == nil {
  154. m.cache = map[string][]byte{}
  155. }
  156. m.cache[key] = data
  157. if m.CacheDir == "" {
  158. cancel()
  159. return ctx
  160. }
  161. go func() {
  162. _ = ioutil.WriteFile(path.Join(m.CacheDir, key), data, 0700)
  163. cancel()
  164. }()
  165. return ctx
  166. }
  167. func (m *AutoCert) checkHost(name string) error {
  168. if m.HostCheck == nil {
  169. return nil
  170. }
  171. return m.HostCheck(name)
  172. }
  173. func (m *AutoCert) getExistingCert(name string) (*tls.Certificate, error) {
  174. // check for a stored cert
  175. certData := m.getCache("cert", name)
  176. if len(certData) == 0 {
  177. return nil, errors.New("autocert: no existing certificate")
  178. }
  179. privBlock, pubData := pem.Decode(certData)
  180. if len(pubData) == 0 {
  181. return nil, errors.New("autocert: no public key data (cert/issuer)")
  182. }
  183. // decode pub chain
  184. var pubDER [][]byte
  185. var pub []byte
  186. for len(pubData) > 0 {
  187. var b *pem.Block
  188. b, pubData = pem.Decode(pubData)
  189. if b == nil {
  190. break
  191. }
  192. pubDER = append(pubDER, b.Bytes)
  193. pub = append(pub, b.Bytes...)
  194. }
  195. if len(pubData) > 0 {
  196. return nil, errors.New("autocert: leftover data in file - possibly corrupt")
  197. }
  198. certs, err := x509.ParseCertificates(pub)
  199. if err != nil {
  200. return nil, fmt.Errorf("autocert: bad certificate: %v", err)
  201. }
  202. leaf := certs[0]
  203. // add any intermediate certs if present
  204. var intermediates *x509.CertPool
  205. if len(certs) > 1 {
  206. intermediates = x509.NewCertPool()
  207. for i := 1; i < len(certs); i++ {
  208. intermediates.AddCert(certs[i])
  209. }
  210. }
  211. // add a root certificate if present
  212. var roots *x509.CertPool
  213. if m.RootCert != "" {
  214. block, rest := pem.Decode([]byte(m.RootCert))
  215. for block != nil {
  216. rootCert, err := x509.ParseCertificate(block.Bytes)
  217. if err != nil {
  218. return nil, errors.New("autocert: error parsing root certificate")
  219. }
  220. if roots == nil {
  221. roots = x509.NewCertPool()
  222. }
  223. roots.AddCert(rootCert)
  224. block, rest = pem.Decode(rest)
  225. }
  226. }
  227. opts := x509.VerifyOptions{
  228. DNSName: name,
  229. Intermediates: intermediates,
  230. Roots: roots,
  231. }
  232. if _, err := leaf.Verify(opts); err != nil {
  233. return nil, fmt.Errorf("autocert: unable to verify: %v", err)
  234. }
  235. privKey, err := x509.ParseECPrivateKey(privBlock.Bytes)
  236. if err != nil {
  237. return nil, errors.New("autocert: invalid private key")
  238. }
  239. return &tls.Certificate{
  240. Certificate: pubDER,
  241. PrivateKey: privKey,
  242. Leaf: leaf,
  243. }, nil
  244. }
  245. func (m *AutoCert) issueCert(domainName string) (*tls.Certificate, error) {
  246. // attempt to load an existing account key
  247. var privKey *ecdsa.PrivateKey
  248. if keyData := m.getCache("account"); len(keyData) > 0 {
  249. block, _ := pem.Decode(keyData)
  250. x509Encoded := block.Bytes
  251. privKey, _ = x509.ParseECPrivateKey(x509Encoded)
  252. }
  253. // otherwise generate a new one
  254. if privKey == nil {
  255. var err error
  256. privKey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
  257. if err != nil {
  258. return nil, fmt.Errorf("autocert: error generating new account key: %v", err)
  259. }
  260. x509Encoded, _ := x509.MarshalECPrivateKey(privKey)
  261. pemEncoded := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: x509Encoded})
  262. m.putCache(pemEncoded, "account")
  263. }
  264. // create a new client if one doesn't exist
  265. if m.client.Directory().URL == "" {
  266. var err error
  267. m.client, err = NewClient(m.getDirectoryURL(), m.Options...)
  268. if err != nil {
  269. return nil, err
  270. }
  271. }
  272. // create/fetch acme account
  273. account, err := m.client.NewAccount(privKey, false, true)
  274. if err != nil {
  275. return nil, fmt.Errorf("autocert: error creating/fetching account: %v", err)
  276. }
  277. // start a new order process
  278. order, err := m.client.NewOrderDomains(account, domainName)
  279. if err != nil {
  280. return nil, fmt.Errorf("autocert: error creating new order for domain %s: %v", domainName, err)
  281. }
  282. // loop through each of the provided authorization Urls
  283. for _, authURL := range order.Authorizations {
  284. auth, err := m.client.FetchAuthorization(account, authURL)
  285. if err != nil {
  286. return nil, fmt.Errorf("autocert: error fetching authorization Url %q: %v", authURL, err)
  287. }
  288. if auth.Status == "valid" {
  289. continue
  290. }
  291. chal, ok := auth.ChallengeMap[ChallengeTypeHTTP01]
  292. if !ok {
  293. return nil, fmt.Errorf("autocert: unable to find http-01 challenge for auth %s, Url: %s", auth.Identifier.Value, authURL)
  294. }
  295. m.tokensLock.Lock()
  296. if m.tokens == nil {
  297. m.tokens = map[string][]byte{}
  298. }
  299. m.tokens[chal.Token] = []byte(chal.KeyAuthorization)
  300. m.tokensLock.Unlock()
  301. if m.PreUpdateChallengeHook != nil {
  302. m.PreUpdateChallengeHook(account, chal)
  303. }
  304. chal, err = m.client.UpdateChallenge(account, chal)
  305. if err != nil {
  306. return nil, fmt.Errorf("autocert: error updating authorization %s challenge (Url: %s) : %v", auth.Identifier.Value, authURL, err)
  307. }
  308. m.tokensLock.Lock()
  309. delete(m.tokens, chal.Token)
  310. m.tokensLock.Unlock()
  311. }
  312. // generate private key for cert
  313. certKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
  314. if err != nil {
  315. return nil, fmt.Errorf("autocert: error generating certificate key for %s: %v", domainName, err)
  316. }
  317. certKeyEnc, err := x509.MarshalECPrivateKey(certKey)
  318. if err != nil {
  319. return nil, fmt.Errorf("autocert: error encoding certificate key for %s: %v", domainName, err)
  320. }
  321. certKeyPem := pem.EncodeToMemory(&pem.Block{
  322. Type: "EC PRIVATE KEY",
  323. Bytes: certKeyEnc,
  324. })
  325. // create the new csr template
  326. tpl := &x509.CertificateRequest{
  327. SignatureAlgorithm: x509.ECDSAWithSHA256,
  328. PublicKeyAlgorithm: x509.ECDSA,
  329. PublicKey: certKey.Public(),
  330. Subject: pkix.Name{CommonName: domainName},
  331. DNSNames: []string{domainName},
  332. }
  333. csrDer, err := x509.CreateCertificateRequest(rand.Reader, tpl, certKey)
  334. if err != nil {
  335. return nil, fmt.Errorf("autocert: error creating certificate request for %s: %v", domainName, err)
  336. }
  337. csr, err := x509.ParseCertificateRequest(csrDer)
  338. if err != nil {
  339. return nil, fmt.Errorf("autocert: error parsing certificate request for %s: %v", domainName, err)
  340. }
  341. // finalize the order with the acme server given a csr
  342. order, err = m.client.FinalizeOrder(account, order, csr)
  343. if err != nil {
  344. return nil, fmt.Errorf("autocert: error finalizing order for %s: %v", domainName, err)
  345. }
  346. // fetch the certificate chain from the finalized order provided by the acme server
  347. certs, err := m.client.FetchCertificates(account, order.Certificate)
  348. if err != nil {
  349. return nil, fmt.Errorf("autocert: error fetching order certificates for %s: %v", domainName, err)
  350. }
  351. certPem := certKeyPem
  352. // var certDer [][]byte
  353. for _, c := range certs {
  354. b := pem.EncodeToMemory(&pem.Block{
  355. Type: "CERTIFICATE",
  356. Bytes: c.Raw,
  357. })
  358. certPem = append(certPem, b...)
  359. // certDer = append(certDer, c.Raw)
  360. }
  361. m.putCache(certPem, "cert", domainName)
  362. return m.getExistingCert(domainName)
  363. }