keyreadwriter.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. package ca
  2. import (
  3. cryptorand "crypto/rand"
  4. "crypto/x509"
  5. "encoding/pem"
  6. "io/ioutil"
  7. "os"
  8. "path/filepath"
  9. "strconv"
  10. "strings"
  11. "sync"
  12. "crypto/tls"
  13. "github.com/docker/swarmkit/ioutils"
  14. "github.com/pkg/errors"
  15. )
  16. const (
  17. // keyPerms are the permissions used to write the TLS keys
  18. keyPerms = 0600
  19. // certPerms are the permissions used to write TLS certificates
  20. certPerms = 0644
  21. // versionHeader is the TLS PEM key header that contains the KEK version
  22. versionHeader = "kek-version"
  23. )
  24. // PEMKeyHeaders is something that needs to know about PEM headers when reading
  25. // or writing TLS keys.
  26. type PEMKeyHeaders interface {
  27. // UnmarshalHeaders loads the headers map given the current KEK
  28. UnmarshalHeaders(map[string]string, KEKData) (PEMKeyHeaders, error)
  29. // MarshalHeaders returns a header map given the current KEK
  30. MarshalHeaders(KEKData) (map[string]string, error)
  31. // UpdateKEK may get a new PEMKeyHeaders if the KEK changes
  32. UpdateKEK(KEKData, KEKData) PEMKeyHeaders
  33. }
  34. // KeyReader reads a TLS cert and key from disk
  35. type KeyReader interface {
  36. Read() ([]byte, []byte, error)
  37. Target() string
  38. }
  39. // KeyWriter writes a TLS key and cert to disk
  40. type KeyWriter interface {
  41. Write([]byte, []byte, *KEKData) error
  42. ViewAndUpdateHeaders(func(PEMKeyHeaders) (PEMKeyHeaders, error)) error
  43. ViewAndRotateKEK(func(KEKData, PEMKeyHeaders) (KEKData, PEMKeyHeaders, error)) error
  44. GetCurrentState() (PEMKeyHeaders, KEKData)
  45. Target() string
  46. }
  47. // KEKData provides an optional update to the kek when writing. The structure
  48. // is needed so that we can tell the difference between "do not encrypt anymore"
  49. // and there is "no update".
  50. type KEKData struct {
  51. KEK []byte
  52. Version uint64
  53. }
  54. // ErrInvalidKEK means that we cannot decrypt the TLS key for some reason
  55. type ErrInvalidKEK struct {
  56. Wrapped error
  57. }
  58. func (e ErrInvalidKEK) Error() string {
  59. return e.Wrapped.Error()
  60. }
  61. // KeyReadWriter is an object that knows how to read and write TLS keys and certs to disk,
  62. // optionally encrypted and optionally updating PEM headers.
  63. type KeyReadWriter struct {
  64. mu sync.Mutex
  65. kekData KEKData
  66. paths CertPaths
  67. headersObj PEMKeyHeaders
  68. }
  69. // NewKeyReadWriter creates a new KeyReadWriter
  70. func NewKeyReadWriter(paths CertPaths, kek []byte, headersObj PEMKeyHeaders) *KeyReadWriter {
  71. return &KeyReadWriter{
  72. kekData: KEKData{KEK: kek},
  73. paths: paths,
  74. headersObj: headersObj,
  75. }
  76. }
  77. // Migrate checks to see if a temporary key file exists. Older versions of
  78. // swarmkit wrote temporary keys instead of temporary certificates, so
  79. // migrate that temporary key if it exists. We want to write temporary certificates,
  80. // instead of temporary keys, because we may need to periodically re-encrypt the
  81. // keys and modify the headers, and it's easier to have a single canonical key
  82. // location than two possible key locations.
  83. func (k *KeyReadWriter) Migrate() error {
  84. tmpPaths := k.genTempPaths()
  85. keyBytes, err := ioutil.ReadFile(tmpPaths.Key)
  86. if err != nil {
  87. return nil // no key? no migration
  88. }
  89. // it does exist - no need to decrypt, because previous versions of swarmkit
  90. // which supported this temporary key did not support encrypting TLS keys
  91. cert, err := ioutil.ReadFile(k.paths.Cert)
  92. if err != nil {
  93. return os.RemoveAll(tmpPaths.Key) // no cert? no migration
  94. }
  95. // nope, this does not match the cert
  96. if _, err = tls.X509KeyPair(cert, keyBytes); err != nil {
  97. return os.RemoveAll(tmpPaths.Key)
  98. }
  99. return os.Rename(tmpPaths.Key, k.paths.Key)
  100. }
  101. // Read will read a TLS cert and key from the given paths
  102. func (k *KeyReadWriter) Read() ([]byte, []byte, error) {
  103. k.mu.Lock()
  104. defer k.mu.Unlock()
  105. keyBlock, err := k.readKey()
  106. if err != nil {
  107. return nil, nil, err
  108. }
  109. if version, ok := keyBlock.Headers[versionHeader]; ok {
  110. if versionInt, err := strconv.ParseUint(version, 10, 64); err == nil {
  111. k.kekData.Version = versionInt
  112. }
  113. }
  114. delete(keyBlock.Headers, versionHeader)
  115. if k.headersObj != nil {
  116. newHeaders, err := k.headersObj.UnmarshalHeaders(keyBlock.Headers, k.kekData)
  117. if err != nil {
  118. return nil, nil, errors.Wrap(err, "unable to read TLS key headers")
  119. }
  120. k.headersObj = newHeaders
  121. }
  122. keyBytes := pem.EncodeToMemory(keyBlock)
  123. cert, err := ioutil.ReadFile(k.paths.Cert)
  124. // The cert is written to a temporary file first, then the key, and then
  125. // the cert gets renamed - so, if interrupted, it's possible to end up with
  126. // a cert that only exists in the temporary location.
  127. switch {
  128. case err == nil:
  129. _, err = tls.X509KeyPair(cert, keyBytes)
  130. case os.IsNotExist(err): //continue to try temp location
  131. break
  132. default:
  133. return nil, nil, err
  134. }
  135. // either the cert doesn't exist, or it doesn't match the key - try the temp file, if it exists
  136. if err != nil {
  137. var tempErr error
  138. tmpPaths := k.genTempPaths()
  139. cert, tempErr = ioutil.ReadFile(tmpPaths.Cert)
  140. if tempErr != nil {
  141. return nil, nil, err // return the original error
  142. }
  143. if _, tempErr := tls.X509KeyPair(cert, keyBytes); tempErr != nil {
  144. os.RemoveAll(tmpPaths.Cert) // nope, it doesn't match either - remove and return the original error
  145. return nil, nil, err
  146. }
  147. os.Rename(tmpPaths.Cert, k.paths.Cert) // try to move the temp cert back to the regular location
  148. }
  149. return cert, keyBytes, nil
  150. }
  151. // ViewAndRotateKEK re-encrypts the key with a new KEK
  152. func (k *KeyReadWriter) ViewAndRotateKEK(cb func(KEKData, PEMKeyHeaders) (KEKData, PEMKeyHeaders, error)) error {
  153. k.mu.Lock()
  154. defer k.mu.Unlock()
  155. updatedKEK, updatedHeaderObj, err := cb(k.kekData, k.headersObj)
  156. if err != nil {
  157. return err
  158. }
  159. keyBlock, err := k.readKey()
  160. if err != nil {
  161. return err
  162. }
  163. if err := k.writeKey(keyBlock, updatedKEK, updatedHeaderObj); err != nil {
  164. return err
  165. }
  166. return nil
  167. }
  168. // ViewAndUpdateHeaders updates the header manager, and updates any headers on the existing key
  169. func (k *KeyReadWriter) ViewAndUpdateHeaders(cb func(PEMKeyHeaders) (PEMKeyHeaders, error)) error {
  170. k.mu.Lock()
  171. defer k.mu.Unlock()
  172. pkh, err := cb(k.headersObj)
  173. if err != nil {
  174. return err
  175. }
  176. keyBlock, err := k.readKeyblock()
  177. if err != nil {
  178. return err
  179. }
  180. headers := make(map[string]string)
  181. if pkh != nil {
  182. var err error
  183. headers, err = pkh.MarshalHeaders(k.kekData)
  184. if err != nil {
  185. return err
  186. }
  187. }
  188. // we WANT any original encryption headers
  189. for key, value := range keyBlock.Headers {
  190. normalizedKey := strings.TrimSpace(strings.ToLower(key))
  191. if normalizedKey == "proc-type" || normalizedKey == "dek-info" {
  192. headers[key] = value
  193. }
  194. }
  195. headers[versionHeader] = strconv.FormatUint(k.kekData.Version, 10)
  196. keyBlock.Headers = headers
  197. if err = ioutils.AtomicWriteFile(k.paths.Key, pem.EncodeToMemory(keyBlock), keyPerms); err != nil {
  198. return err
  199. }
  200. k.headersObj = pkh
  201. return nil
  202. }
  203. // GetCurrentState returns the current KEK data, including version
  204. func (k *KeyReadWriter) GetCurrentState() (PEMKeyHeaders, KEKData) {
  205. k.mu.Lock()
  206. defer k.mu.Unlock()
  207. return k.headersObj, k.kekData
  208. }
  209. // Write attempts write a cert and key to text. This can also optionally update
  210. // the KEK while writing, if an updated KEK is provided. If the pointer to the
  211. // update KEK is nil, then we don't update. If the updated KEK itself is nil,
  212. // then we update the KEK to be nil (data should be unencrypted).
  213. func (k *KeyReadWriter) Write(certBytes, plaintextKeyBytes []byte, kekData *KEKData) error {
  214. k.mu.Lock()
  215. defer k.mu.Unlock()
  216. // current assumption is that the cert and key will be in the same directory
  217. if err := os.MkdirAll(filepath.Dir(k.paths.Key), 0755); err != nil {
  218. return err
  219. }
  220. // Ensure that we will have a keypair on disk at all times by writing the cert to a
  221. // temp path first. This is because we want to have only a single copy of the key
  222. // for rotation and header modification.
  223. tmpPaths := k.genTempPaths()
  224. if err := ioutils.AtomicWriteFile(tmpPaths.Cert, certBytes, certPerms); err != nil {
  225. return err
  226. }
  227. keyBlock, _ := pem.Decode(plaintextKeyBytes)
  228. if keyBlock == nil {
  229. return errors.New("invalid PEM-encoded private key")
  230. }
  231. if kekData == nil {
  232. kekData = &k.kekData
  233. }
  234. pkh := k.headersObj
  235. if k.headersObj != nil {
  236. pkh = k.headersObj.UpdateKEK(k.kekData, *kekData)
  237. }
  238. if err := k.writeKey(keyBlock, *kekData, pkh); err != nil {
  239. return err
  240. }
  241. return os.Rename(tmpPaths.Cert, k.paths.Cert)
  242. }
  243. func (k *KeyReadWriter) genTempPaths() CertPaths {
  244. return CertPaths{
  245. Key: filepath.Join(filepath.Dir(k.paths.Key), "."+filepath.Base(k.paths.Key)),
  246. Cert: filepath.Join(filepath.Dir(k.paths.Cert), "."+filepath.Base(k.paths.Cert)),
  247. }
  248. }
  249. // Target returns a string representation of this KeyReadWriter, namely where
  250. // it is writing to
  251. func (k *KeyReadWriter) Target() string {
  252. return k.paths.Cert
  253. }
  254. func (k *KeyReadWriter) readKeyblock() (*pem.Block, error) {
  255. key, err := ioutil.ReadFile(k.paths.Key)
  256. if err != nil {
  257. return nil, err
  258. }
  259. // Decode the PEM private key
  260. keyBlock, _ := pem.Decode(key)
  261. if keyBlock == nil {
  262. return nil, errors.New("invalid PEM-encoded private key")
  263. }
  264. return keyBlock, nil
  265. }
  266. // readKey returns the decrypted key pem bytes, and enforces the KEK if applicable
  267. // (writes it back with the correct encryption if it is not correctly encrypted)
  268. func (k *KeyReadWriter) readKey() (*pem.Block, error) {
  269. keyBlock, err := k.readKeyblock()
  270. if err != nil {
  271. return nil, err
  272. }
  273. if !x509.IsEncryptedPEMBlock(keyBlock) {
  274. return keyBlock, nil
  275. }
  276. // If it's encrypted, we can't read without a passphrase (we're assuming
  277. // empty passphrases are invalid)
  278. if k.kekData.KEK == nil {
  279. return nil, ErrInvalidKEK{Wrapped: x509.IncorrectPasswordError}
  280. }
  281. derBytes, err := x509.DecryptPEMBlock(keyBlock, k.kekData.KEK)
  282. if err != nil {
  283. return nil, ErrInvalidKEK{Wrapped: err}
  284. }
  285. // remove encryption PEM headers
  286. headers := make(map[string]string)
  287. mergePEMHeaders(headers, keyBlock.Headers)
  288. return &pem.Block{
  289. Type: keyBlock.Type, // the key type doesn't change
  290. Bytes: derBytes,
  291. Headers: headers,
  292. }, nil
  293. }
  294. // writeKey takes an unencrypted keyblock and, if the kek is not nil, encrypts it before
  295. // writing it to disk. If the kek is nil, writes it to disk unencrypted.
  296. func (k *KeyReadWriter) writeKey(keyBlock *pem.Block, kekData KEKData, pkh PEMKeyHeaders) error {
  297. if kekData.KEK != nil {
  298. encryptedPEMBlock, err := x509.EncryptPEMBlock(cryptorand.Reader,
  299. keyBlock.Type,
  300. keyBlock.Bytes,
  301. kekData.KEK,
  302. x509.PEMCipherAES256)
  303. if err != nil {
  304. return err
  305. }
  306. if encryptedPEMBlock.Headers == nil {
  307. return errors.New("unable to encrypt key - invalid PEM file produced")
  308. }
  309. keyBlock = encryptedPEMBlock
  310. }
  311. if pkh != nil {
  312. headers, err := pkh.MarshalHeaders(kekData)
  313. if err != nil {
  314. return err
  315. }
  316. mergePEMHeaders(keyBlock.Headers, headers)
  317. }
  318. keyBlock.Headers[versionHeader] = strconv.FormatUint(kekData.Version, 10)
  319. if err := ioutils.AtomicWriteFile(k.paths.Key, pem.EncodeToMemory(keyBlock), keyPerms); err != nil {
  320. return err
  321. }
  322. k.kekData = kekData
  323. k.headersObj = pkh
  324. return nil
  325. }
  326. // merges one set of PEM headers onto another, excepting for key encryption value
  327. // "proc-type" and "dek-info"
  328. func mergePEMHeaders(original, newSet map[string]string) {
  329. for key, value := range newSet {
  330. normalizedKey := strings.TrimSpace(strings.ToLower(key))
  331. if normalizedKey != "proc-type" && normalizedKey != "dek-info" {
  332. original[key] = value
  333. }
  334. }
  335. }