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.

storage.go 9.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  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. "context"
  17. "path"
  18. "regexp"
  19. "strings"
  20. "sync"
  21. "time"
  22. "go.uber.org/zap"
  23. )
  24. // Storage is a type that implements a key-value store.
  25. // Keys are prefix-based, with forward slash '/' as separators
  26. // and without a leading slash.
  27. //
  28. // Processes running in a cluster will wish to use the
  29. // same Storage value (its implementation and configuration)
  30. // in order to share certificates and other TLS resources
  31. // with the cluster.
  32. //
  33. // The Load, Delete, List, and Stat methods should return
  34. // ErrNotExist if the key does not exist.
  35. //
  36. // Implementations of Storage must be safe for concurrent use.
  37. type Storage interface {
  38. // Locker provides atomic synchronization
  39. // operations, making Storage safe to share.
  40. Locker
  41. // Store puts value at key.
  42. Store(key string, value []byte) error
  43. // Load retrieves the value at key.
  44. Load(key string) ([]byte, error)
  45. // Delete deletes key. An error should be
  46. // returned only if the key still exists
  47. // when the method returns.
  48. Delete(key string) error
  49. // Exists returns true if the key exists
  50. // and there was no error checking.
  51. Exists(key string) bool
  52. // List returns all keys that match prefix.
  53. // If recursive is true, non-terminal keys
  54. // will be enumerated (i.e. "directories"
  55. // should be walked); otherwise, only keys
  56. // prefixed exactly by prefix will be listed.
  57. List(prefix string, recursive bool) ([]string, error)
  58. // Stat returns information about key.
  59. Stat(key string) (KeyInfo, error)
  60. }
  61. // Locker facilitates synchronization of certificate tasks across
  62. // machines and networks.
  63. type Locker interface {
  64. // Lock acquires the lock for key, blocking until the lock
  65. // can be obtained or an error is returned. Note that, even
  66. // after acquiring a lock, an idempotent operation may have
  67. // already been performed by another process that acquired
  68. // the lock before - so always check to make sure idempotent
  69. // operations still need to be performed after acquiring the
  70. // lock.
  71. //
  72. // The actual implementation of obtaining of a lock must be
  73. // an atomic operation so that multiple Lock calls at the
  74. // same time always results in only one caller receiving the
  75. // lock at any given time.
  76. //
  77. // To prevent deadlocks, all implementations (where this concern
  78. // is relevant) should put a reasonable expiration on the lock in
  79. // case Unlock is unable to be called due to some sort of network
  80. // failure or system crash. Additionally, implementations should
  81. // honor context cancellation as much as possible (in case the
  82. // caller wishes to give up and free resources before the lock
  83. // can be obtained).
  84. Lock(ctx context.Context, key string) error
  85. // Unlock releases the lock for key. This method must ONLY be
  86. // called after a successful call to Lock, and only after the
  87. // critical section is finished, even if it errored or timed
  88. // out. Unlock cleans up any resources allocated during Lock.
  89. Unlock(key string) error
  90. }
  91. // KeyInfo holds information about a key in storage.
  92. // Key and IsTerminal are required; Modified and Size
  93. // are optional if the storage implementation is not
  94. // able to get that information. Setting them will
  95. // make certain operations more consistent or
  96. // predictable, but it is not crucial to basic
  97. // functionality.
  98. type KeyInfo struct {
  99. Key string
  100. Modified time.Time
  101. Size int64
  102. IsTerminal bool // false for keys that only contain other keys (like directories)
  103. }
  104. // storeTx stores all the values or none at all.
  105. func storeTx(s Storage, all []keyValue) error {
  106. for i, kv := range all {
  107. err := s.Store(kv.key, kv.value)
  108. if err != nil {
  109. for j := i - 1; j >= 0; j-- {
  110. s.Delete(all[j].key)
  111. }
  112. return err
  113. }
  114. }
  115. return nil
  116. }
  117. // keyValue pairs a key and a value.
  118. type keyValue struct {
  119. key string
  120. value []byte
  121. }
  122. // KeyBuilder provides a namespace for methods that
  123. // build keys and key prefixes, for addressing items
  124. // in a Storage implementation.
  125. type KeyBuilder struct{}
  126. // CertsPrefix returns the storage key prefix for
  127. // the given certificate issuer.
  128. func (keys KeyBuilder) CertsPrefix(issuerKey string) string {
  129. return path.Join(prefixCerts, keys.Safe(issuerKey))
  130. }
  131. // CertsSitePrefix returns a key prefix for items associated with
  132. // the site given by domain using the given issuer key.
  133. func (keys KeyBuilder) CertsSitePrefix(issuerKey, domain string) string {
  134. return path.Join(keys.CertsPrefix(issuerKey), keys.Safe(domain))
  135. }
  136. // SiteCert returns the path to the certificate file for domain
  137. // that is associated with the issuer with the given issuerKey.
  138. func (keys KeyBuilder) SiteCert(issuerKey, domain string) string {
  139. safeDomain := keys.Safe(domain)
  140. return path.Join(keys.CertsSitePrefix(issuerKey, domain), safeDomain+".crt")
  141. }
  142. // SitePrivateKey returns the path to the private key file for domain
  143. // that is associated with the certificate from the given issuer with
  144. // the given issuerKey.
  145. func (keys KeyBuilder) SitePrivateKey(issuerKey, domain string) string {
  146. safeDomain := keys.Safe(domain)
  147. return path.Join(keys.CertsSitePrefix(issuerKey, domain), safeDomain+".key")
  148. }
  149. // SiteMeta returns the path to the metadata file for domain that
  150. // is associated with the certificate from the given issuer with
  151. // the given issuerKey.
  152. func (keys KeyBuilder) SiteMeta(issuerKey, domain string) string {
  153. safeDomain := keys.Safe(domain)
  154. return path.Join(keys.CertsSitePrefix(issuerKey, domain), safeDomain+".json")
  155. }
  156. // OCSPStaple returns a key for the OCSP staple associated
  157. // with the given certificate. If you have the PEM bundle
  158. // handy, pass that in to save an extra encoding step.
  159. func (keys KeyBuilder) OCSPStaple(cert *Certificate, pemBundle []byte) string {
  160. var ocspFileName string
  161. if len(cert.Names) > 0 {
  162. firstName := keys.Safe(cert.Names[0])
  163. ocspFileName = firstName + "-"
  164. }
  165. ocspFileName += fastHash(pemBundle)
  166. return path.Join(prefixOCSP, ocspFileName)
  167. }
  168. // Safe standardizes and sanitizes str for use as
  169. // a single component of a storage key. This method
  170. // is idempotent.
  171. func (keys KeyBuilder) Safe(str string) string {
  172. str = strings.ToLower(str)
  173. str = strings.TrimSpace(str)
  174. // replace a few specific characters
  175. repl := strings.NewReplacer(
  176. " ", "_",
  177. "+", "_plus_",
  178. "*", "wildcard_",
  179. ":", "-",
  180. "..", "", // prevent directory traversal (regex allows single dots)
  181. )
  182. str = repl.Replace(str)
  183. // finally remove all non-word characters
  184. return safeKeyRE.ReplaceAllLiteralString(str, "")
  185. }
  186. // CleanUpOwnLocks immediately cleans up all
  187. // current locks obtained by this process. Since
  188. // this does not cancel the operations that
  189. // the locks are synchronizing, this should be
  190. // called only immediately before process exit.
  191. // Errors are only reported if a logger is given.
  192. func CleanUpOwnLocks(logger *zap.Logger) {
  193. locksMu.Lock()
  194. defer locksMu.Unlock()
  195. for lockKey, storage := range locks {
  196. err := storage.Unlock(lockKey)
  197. if err == nil {
  198. delete(locks, lockKey)
  199. } else if logger != nil {
  200. logger.Error("unable to clean up lock in storage backend",
  201. zap.Any("storage", storage),
  202. zap.String("lock_key", lockKey),
  203. zap.Error(err),
  204. )
  205. }
  206. }
  207. }
  208. func acquireLock(ctx context.Context, storage Storage, lockKey string) error {
  209. err := storage.Lock(ctx, lockKey)
  210. if err == nil {
  211. locksMu.Lock()
  212. locks[lockKey] = storage
  213. locksMu.Unlock()
  214. }
  215. return err
  216. }
  217. func releaseLock(storage Storage, lockKey string) error {
  218. err := storage.Unlock(lockKey)
  219. if err == nil {
  220. locksMu.Lock()
  221. delete(locks, lockKey)
  222. locksMu.Unlock()
  223. }
  224. return err
  225. }
  226. // locks stores a reference to all the current
  227. // locks obtained by this process.
  228. var locks = make(map[string]Storage)
  229. var locksMu sync.Mutex
  230. // StorageKeys provides methods for accessing
  231. // keys and key prefixes for items in a Storage.
  232. // Typically, you will not need to use this
  233. // because accessing storage is abstracted away
  234. // for most cases. Only use this if you need to
  235. // directly access TLS assets in your application.
  236. var StorageKeys KeyBuilder
  237. const (
  238. prefixCerts = "certificates"
  239. prefixOCSP = "ocsp"
  240. )
  241. // safeKeyRE matches any undesirable characters in storage keys.
  242. // Note that this allows dots, so you'll have to strip ".." manually.
  243. var safeKeyRE = regexp.MustCompile(`[^\w@.-]`)
  244. // ErrNotExist is returned by Storage implementations when
  245. // a resource is not found. It is similar to os.IsNotExist
  246. // except this is a type, not a variable.
  247. // TODO: use new Go error wrapping conventions
  248. type ErrNotExist interface {
  249. error
  250. }
  251. // defaultFileStorage is a convenient, default storage
  252. // implementation using the local file system.
  253. var defaultFileStorage = &FileStorage{Path: dataDir()}