You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

cache.go 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. // Copyright 2015 Matthew Holt
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package certmagic
  15. import (
  16. "fmt"
  17. weakrand "math/rand" // seeded elsewhere
  18. "strings"
  19. "sync"
  20. "time"
  21. "go.uber.org/zap"
  22. )
  23. // Cache is a structure that stores certificates in memory.
  24. // A Cache indexes certificates by name for quick access
  25. // during TLS handshakes, and avoids duplicating certificates
  26. // in memory. Generally, there should only be one per process.
  27. // However, that is not a strict requirement; but using more
  28. // than one is a code smell, and may indicate an
  29. // over-engineered design.
  30. //
  31. // An empty cache is INVALID and must not be used. Be sure
  32. // to call NewCache to get a valid value.
  33. //
  34. // These should be very long-lived values and must not be
  35. // copied. Before all references leave scope to be garbage
  36. // collected, ensure you call Stop() to stop maintenance on
  37. // the certificates stored in this cache and release locks.
  38. //
  39. // Caches are not usually manipulated directly; create a
  40. // Config value with a pointer to a Cache, and then use
  41. // the Config to interact with the cache. Caches are
  42. // agnostic of any particular storage or ACME config,
  43. // since each certificate may be managed and stored
  44. // differently.
  45. type Cache struct {
  46. // User configuration of the cache
  47. options CacheOptions
  48. // The cache is keyed by certificate hash
  49. cache map[string]Certificate
  50. // cacheIndex is a map of SAN to cache key (cert hash)
  51. cacheIndex map[string][]string
  52. // Protects the cache and index maps
  53. mu sync.RWMutex
  54. // Close this channel to cancel asset maintenance
  55. stopChan chan struct{}
  56. // Used to signal when stopping is completed
  57. doneChan chan struct{}
  58. logger *zap.Logger
  59. }
  60. // NewCache returns a new, valid Cache for efficiently
  61. // accessing certificates in memory. It also begins a
  62. // maintenance goroutine to tend to the certificates
  63. // in the cache. Call Stop() when you are done with the
  64. // cache so it can clean up locks and stuff.
  65. //
  66. // Most users of this package will not need to call this
  67. // because a default certificate cache is created for you.
  68. // Only advanced use cases require creating a new cache.
  69. //
  70. // This function panics if opts.GetConfigForCert is not
  71. // set. The reason is that a cache absolutely needs to
  72. // be able to get a Config with which to manage TLS
  73. // assets, and it is not safe to assume that the Default
  74. // config is always the correct one, since you have
  75. // created the cache yourself.
  76. //
  77. // See the godoc for Cache to use it properly. When
  78. // no longer needed, caches should be stopped with
  79. // Stop() to clean up resources even if the process
  80. // is being terminated, so that it can clean up
  81. // any locks for other processes to unblock!
  82. func NewCache(opts CacheOptions) *Cache {
  83. // assume default options if necessary
  84. if opts.OCSPCheckInterval <= 0 {
  85. opts.OCSPCheckInterval = DefaultOCSPCheckInterval
  86. }
  87. if opts.RenewCheckInterval <= 0 {
  88. opts.RenewCheckInterval = DefaultRenewCheckInterval
  89. }
  90. if opts.Capacity < 0 {
  91. opts.Capacity = 0
  92. }
  93. // this must be set, because we cannot not
  94. // safely assume that the Default Config
  95. // is always the correct one to use
  96. if opts.GetConfigForCert == nil {
  97. panic("cache must be initialized with a GetConfigForCert callback")
  98. }
  99. c := &Cache{
  100. options: opts,
  101. cache: make(map[string]Certificate),
  102. cacheIndex: make(map[string][]string),
  103. stopChan: make(chan struct{}),
  104. doneChan: make(chan struct{}),
  105. logger: opts.Logger,
  106. }
  107. go c.maintainAssets(0)
  108. return c
  109. }
  110. // Stop stops the maintenance goroutine for
  111. // certificates in certCache. It blocks until
  112. // stopping is complete. Once a cache is
  113. // stopped, it cannot be reused.
  114. func (certCache *Cache) Stop() {
  115. close(certCache.stopChan) // signal to stop
  116. <-certCache.doneChan // wait for stop to complete
  117. }
  118. // CacheOptions is used to configure certificate caches.
  119. // Once a cache has been created with certain options,
  120. // those settings cannot be changed.
  121. type CacheOptions struct {
  122. // REQUIRED. A function that returns a configuration
  123. // used for managing a certificate, or for accessing
  124. // that certificate's asset storage (e.g. for
  125. // OCSP staples, etc). The returned Config MUST
  126. // be associated with the same Cache as the caller.
  127. //
  128. // The reason this is a callback function, dynamically
  129. // returning a Config (instead of attaching a static
  130. // pointer to a Config on each certificate) is because
  131. // the config for how to manage a domain's certificate
  132. // might change from maintenance to maintenance. The
  133. // cache is so long-lived, we cannot assume that the
  134. // host's situation will always be the same; e.g. the
  135. // certificate might switch DNS providers, so the DNS
  136. // challenge (if used) would need to be adjusted from
  137. // the last time it was run ~8 weeks ago.
  138. GetConfigForCert ConfigGetter
  139. // How often to check certificates for renewal;
  140. // if unset, DefaultOCSPCheckInterval will be used.
  141. OCSPCheckInterval time.Duration
  142. // How often to check certificates for renewal;
  143. // if unset, DefaultRenewCheckInterval will be used.
  144. RenewCheckInterval time.Duration
  145. // Maximum number of certificates to allow in the cache.
  146. // If reached, certificates will be randomly evicted to
  147. // make room for new ones. 0 means unlimited.
  148. Capacity int
  149. // Set a logger to enable logging
  150. Logger *zap.Logger
  151. }
  152. // ConfigGetter is a function that returns a prepared,
  153. // valid config that should be used when managing the
  154. // given certificate or its assets.
  155. type ConfigGetter func(Certificate) (*Config, error)
  156. // cacheCertificate calls unsyncedCacheCertificate with a write lock.
  157. //
  158. // This function is safe for concurrent use.
  159. func (certCache *Cache) cacheCertificate(cert Certificate) {
  160. certCache.mu.Lock()
  161. certCache.unsyncedCacheCertificate(cert)
  162. certCache.mu.Unlock()
  163. }
  164. // unsyncedCacheCertificate adds cert to the in-memory cache unless
  165. // it already exists in the cache (according to cert.Hash). It
  166. // updates the name index.
  167. //
  168. // This function is NOT safe for concurrent use. Callers MUST acquire
  169. // a write lock on certCache.mu first.
  170. func (certCache *Cache) unsyncedCacheCertificate(cert Certificate) {
  171. // no-op if this certificate already exists in the cache
  172. if _, ok := certCache.cache[cert.hash]; ok {
  173. return
  174. }
  175. // if the cache is at capacity, make room for new cert
  176. cacheSize := len(certCache.cache)
  177. if certCache.options.Capacity > 0 && cacheSize >= certCache.options.Capacity {
  178. // Go maps are "nondeterministic" but not actually random,
  179. // so although we could just chop off the "front" of the
  180. // map with less code, that is a heavily skewed eviction
  181. // strategy; generating random numbers is cheap and
  182. // ensures a much better distribution.
  183. rnd := weakrand.Intn(cacheSize)
  184. i := 0
  185. for _, randomCert := range certCache.cache {
  186. if i == rnd {
  187. certCache.removeCertificate(randomCert)
  188. break
  189. }
  190. i++
  191. }
  192. }
  193. // store the certificate
  194. certCache.cache[cert.hash] = cert
  195. // update the index so we can access it by name
  196. for _, name := range cert.Names {
  197. certCache.cacheIndex[name] = append(certCache.cacheIndex[name], cert.hash)
  198. }
  199. }
  200. // removeCertificate removes cert from the cache.
  201. //
  202. // This function is NOT safe for concurrent use; callers
  203. // MUST first acquire a write lock on certCache.mu.
  204. func (certCache *Cache) removeCertificate(cert Certificate) {
  205. // delete all mentions of this cert from the name index
  206. for _, name := range cert.Names {
  207. keyList := certCache.cacheIndex[name]
  208. for i, cacheKey := range keyList {
  209. if cacheKey == cert.hash {
  210. keyList = append(keyList[:i], keyList[i+1:]...)
  211. }
  212. }
  213. if len(keyList) == 0 {
  214. delete(certCache.cacheIndex, name)
  215. } else {
  216. certCache.cacheIndex[name] = keyList
  217. }
  218. }
  219. // delete the actual cert from the cache
  220. delete(certCache.cache, cert.hash)
  221. }
  222. // replaceCertificate atomically replaces oldCert with newCert in
  223. // the cache.
  224. //
  225. // This method is safe for concurrent use.
  226. func (certCache *Cache) replaceCertificate(oldCert, newCert Certificate) {
  227. certCache.mu.Lock()
  228. certCache.removeCertificate(oldCert)
  229. certCache.unsyncedCacheCertificate(newCert)
  230. certCache.mu.Unlock()
  231. if certCache.logger != nil {
  232. certCache.logger.Info("replaced certificate in cache",
  233. zap.Strings("identifiers", newCert.Names),
  234. zap.Time("new_expiration", newCert.Leaf.NotAfter))
  235. }
  236. }
  237. func (certCache *Cache) getAllMatchingCerts(name string) []Certificate {
  238. certCache.mu.RLock()
  239. defer certCache.mu.RUnlock()
  240. allCertKeys := certCache.cacheIndex[name]
  241. certs := make([]Certificate, len(allCertKeys))
  242. for i := range allCertKeys {
  243. certs[i] = certCache.cache[allCertKeys[i]]
  244. }
  245. return certs
  246. }
  247. func (certCache *Cache) getAllCerts() []Certificate {
  248. certCache.mu.RLock()
  249. defer certCache.mu.RUnlock()
  250. certs := make([]Certificate, 0, len(certCache.cache))
  251. for _, cert := range certCache.cache {
  252. certs = append(certs, cert)
  253. }
  254. return certs
  255. }
  256. func (certCache *Cache) getConfig(cert Certificate) (*Config, error) {
  257. cfg, err := certCache.options.GetConfigForCert(cert)
  258. if err != nil {
  259. return nil, err
  260. }
  261. if cfg.certCache != nil && cfg.certCache != certCache {
  262. return nil, fmt.Errorf("config returned for certificate %v is not nil and points to different cache; got %p, expected %p (this one)",
  263. cert.Names, cfg.certCache, certCache)
  264. }
  265. return cfg, nil
  266. }
  267. // AllMatchingCertificates returns a list of all certificates that could
  268. // be used to serve the given SNI name, including exact SAN matches and
  269. // wildcard matches.
  270. func (certCache *Cache) AllMatchingCertificates(name string) []Certificate {
  271. // get exact matches first
  272. certs := certCache.getAllMatchingCerts(name)
  273. // then look for wildcard matches by replacing each
  274. // label of the domain name with wildcards
  275. labels := strings.Split(name, ".")
  276. for i := range labels {
  277. labels[i] = "*"
  278. candidate := strings.Join(labels, ".")
  279. certs = append(certs, certCache.getAllMatchingCerts(candidate)...)
  280. }
  281. return certs
  282. }
  283. var (
  284. defaultCache *Cache
  285. defaultCacheMu sync.Mutex
  286. )