github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/ca/keyreadwriter.go (about) 1 package ca 2 3 import ( 4 "crypto/x509" 5 "encoding/pem" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 "strconv" 10 "strings" 11 "sync" 12 13 "crypto/tls" 14 15 "github.com/docker/swarmkit/ca/keyutils" 16 "github.com/docker/swarmkit/ca/pkcs8" 17 "github.com/docker/swarmkit/ioutils" 18 "github.com/pkg/errors" 19 ) 20 21 const ( 22 // keyPerms are the permissions used to write the TLS keys 23 keyPerms = 0600 24 // certPerms are the permissions used to write TLS certificates 25 certPerms = 0644 26 // versionHeader is the TLS PEM key header that contains the KEK version 27 versionHeader = "kek-version" 28 ) 29 30 // PEMKeyHeaders is an interface for something that needs to know about PEM headers 31 // when reading or writing TLS keys in order to keep them updated with the latest 32 // KEK. 33 type PEMKeyHeaders interface { 34 35 // UnmarshalHeaders loads the headers map given the current KEK 36 UnmarshalHeaders(map[string]string, KEKData) (PEMKeyHeaders, error) 37 38 // MarshalHeaders returns a header map given the current KEK 39 MarshalHeaders(KEKData) (map[string]string, error) 40 41 // UpdateKEK gets called whenever KeyReadWriter gets a KEK update. This allows the 42 // PEMKeyHeaders to optionally update any internal state. It should return an 43 // updated (if needed) versino of itself. 44 UpdateKEK(KEKData, KEKData) PEMKeyHeaders 45 } 46 47 // KeyReader reads a TLS cert and key from disk 48 type KeyReader interface { 49 50 // Read reads and returns the certificate and the key PEM bytes that are on disk 51 Read() ([]byte, []byte, error) 52 53 // Target returns a string representation of where the cert data is being read from 54 Target() string 55 } 56 57 // KeyWriter writes a TLS key and cert to disk 58 type KeyWriter interface { 59 60 // Write accepts a certificate and key in PEM format, as well as an optional KEKData object. 61 // If there is a current KEK, the key is encrypted using the current KEK. If the KEKData object 62 // is provided (not nil), the key will be encrypted using the new KEK instead, and the current 63 // KEK in memory will be replaced by the provided KEKData. The reason to allow changing key 64 // material and KEK in a single step, as opposed to two steps, is to prevent the key material 65 // from being written unencrypted or with an old KEK in the first place (when a node gets a 66 // certificate from the CA, it will also request the current KEK so it won't have to immediately 67 // do a KEK rotation after getting the key). 68 Write([]byte, []byte, *KEKData) error 69 70 // ViewAndUpdateHeaders is a function that reads and updates the headers of the key in a single 71 // transaction (e.g. within a lock). It accepts a callback function which will be passed the 72 // current header management object, and which must return a new, updated, or same header 73 // management object. KeyReadWriter then performs the following actions: 74 // - uses the old header management object and the current KEK to deserialize/decrypt 75 // the existing PEM headers 76 // - uses the new header management object and the current KEK to to reserialize/encrypt 77 // the PEM headers 78 // - writes the new PEM headers, as well as the key material, unchanged, to disk 79 ViewAndUpdateHeaders(func(PEMKeyHeaders) (PEMKeyHeaders, error)) error 80 81 // ViewAndRotateKEK is a function that just re-encrypts the TLS key and headers in a single 82 // transaction (e.g. within a lock). It accepts a callback unction which will be passed the 83 // current KEK and the current headers management object, and which should return a new 84 // KEK and header management object. KeyReadWriter then performs the following actions: 85 // - uses the old KEK and header management object to deserialize/decrypt the 86 // TLS key and PEM headers 87 // - uses the new KEK and header management object to serialize/encrypt the TLS key 88 // and PEM headers 89 // - writes the new PEM headers and newly encrypted TLS key to disk 90 ViewAndRotateKEK(func(KEKData, PEMKeyHeaders) (KEKData, PEMKeyHeaders, error)) error 91 92 // GetCurrentState returns the current header management object and the current KEK. 93 GetCurrentState() (PEMKeyHeaders, KEKData) 94 95 // Target returns a string representation of where the cert data is being read from 96 Target() string 97 } 98 99 // KEKData provides an optional update to the kek when writing. The structure 100 // is needed so that we can tell the difference between "do not encrypt anymore" 101 // and there is "no update". 102 type KEKData struct { 103 KEK []byte 104 Version uint64 105 } 106 107 // ErrInvalidKEK means that we cannot decrypt the TLS key for some reason 108 type ErrInvalidKEK struct { 109 Wrapped error 110 } 111 112 func (e ErrInvalidKEK) Error() string { 113 return e.Wrapped.Error() 114 } 115 116 // KeyReadWriter is an object that knows how to read and write TLS keys and certs to disk, 117 // optionally encrypted and optionally updating PEM headers. It should be the only object which 118 // can write the TLS key, to ensure that writes are serialized and that the TLS key, the 119 // KEK (key encrypting key), and any headers which need to be written are never out of sync. 120 // It accepts a PEMKeyHeaders object, which is used to serialize/encrypt and deserialize/decrypt 121 // the PEM headers when given the current headers and the current KEK. 122 type KeyReadWriter struct { 123 124 // This lock is held whenever a key is read from or written to disk, or whenever the internal 125 // state of the KeyReadWriter (such as the KEK, the key formatter, or the PEM header management 126 // object changes.) 127 mu sync.Mutex 128 129 kekData KEKData 130 paths CertPaths 131 headersObj PEMKeyHeaders 132 keyFormatter keyutils.Formatter 133 } 134 135 // NewKeyReadWriter creates a new KeyReadWriter 136 func NewKeyReadWriter(paths CertPaths, kek []byte, headersObj PEMKeyHeaders) *KeyReadWriter { 137 return &KeyReadWriter{ 138 kekData: KEKData{KEK: kek}, 139 paths: paths, 140 headersObj: headersObj, 141 keyFormatter: keyutils.Default, 142 } 143 } 144 145 // SetKeyFormatter sets the keyformatter with which to encrypt and decrypt keys 146 func (k *KeyReadWriter) SetKeyFormatter(kf keyutils.Formatter) { 147 k.mu.Lock() 148 defer k.mu.Unlock() 149 k.keyFormatter = kf 150 } 151 152 // Migrate checks to see if a temporary key file exists. Older versions of 153 // swarmkit wrote temporary keys instead of temporary certificates, so 154 // migrate that temporary key if it exists. We want to write temporary certificates, 155 // instead of temporary keys, because we may need to periodically re-encrypt the 156 // keys and modify the headers, and it's easier to have a single canonical key 157 // location than two possible key locations. 158 func (k *KeyReadWriter) Migrate() error { 159 tmpPaths := k.genTempPaths() 160 keyBytes, err := ioutil.ReadFile(tmpPaths.Key) 161 if err != nil { 162 return nil // no key? no migration 163 } 164 165 // it does exist - no need to decrypt, because previous versions of swarmkit 166 // which supported this temporary key did not support encrypting TLS keys 167 cert, err := ioutil.ReadFile(k.paths.Cert) 168 if err != nil { 169 return os.RemoveAll(tmpPaths.Key) // no cert? no migration 170 } 171 172 // nope, this does not match the cert 173 if _, err = tls.X509KeyPair(cert, keyBytes); err != nil { 174 return os.RemoveAll(tmpPaths.Key) 175 } 176 177 return os.Rename(tmpPaths.Key, k.paths.Key) 178 } 179 180 // Read will read a TLS cert and key from the given paths 181 func (k *KeyReadWriter) Read() ([]byte, []byte, error) { 182 k.mu.Lock() 183 defer k.mu.Unlock() 184 keyBlock, err := k.readKey() 185 if err != nil { 186 return nil, nil, err 187 } 188 189 if version, ok := keyBlock.Headers[versionHeader]; ok { 190 if versionInt, err := strconv.ParseUint(version, 10, 64); err == nil { 191 k.kekData.Version = versionInt 192 } 193 } 194 delete(keyBlock.Headers, versionHeader) 195 196 if k.headersObj != nil { 197 newHeaders, err := k.headersObj.UnmarshalHeaders(keyBlock.Headers, k.kekData) 198 if err != nil { 199 return nil, nil, errors.Wrap(err, "unable to read TLS key headers") 200 } 201 k.headersObj = newHeaders 202 } 203 204 keyBytes := pem.EncodeToMemory(keyBlock) 205 cert, err := ioutil.ReadFile(k.paths.Cert) 206 // The cert is written to a temporary file first, then the key, and then 207 // the cert gets renamed - so, if interrupted, it's possible to end up with 208 // a cert that only exists in the temporary location. 209 switch { 210 case err == nil: 211 _, err = tls.X509KeyPair(cert, keyBytes) 212 case os.IsNotExist(err): //continue to try temp location 213 break 214 default: 215 return nil, nil, err 216 } 217 218 // either the cert doesn't exist, or it doesn't match the key - try the temp file, if it exists 219 if err != nil { 220 var tempErr error 221 tmpPaths := k.genTempPaths() 222 cert, tempErr = ioutil.ReadFile(tmpPaths.Cert) 223 if tempErr != nil { 224 return nil, nil, err // return the original error 225 } 226 if _, tempErr := tls.X509KeyPair(cert, keyBytes); tempErr != nil { 227 os.RemoveAll(tmpPaths.Cert) // nope, it doesn't match either - remove and return the original error 228 return nil, nil, err 229 } 230 os.Rename(tmpPaths.Cert, k.paths.Cert) // try to move the temp cert back to the regular location 231 232 } 233 234 return cert, keyBytes, nil 235 } 236 237 // ViewAndRotateKEK re-encrypts the key with a new KEK 238 func (k *KeyReadWriter) ViewAndRotateKEK(cb func(KEKData, PEMKeyHeaders) (KEKData, PEMKeyHeaders, error)) error { 239 k.mu.Lock() 240 defer k.mu.Unlock() 241 242 updatedKEK, updatedHeaderObj, err := cb(k.kekData, k.headersObj) 243 if err != nil { 244 return err 245 } 246 247 keyBlock, err := k.readKey() 248 if err != nil { 249 return err 250 } 251 252 return k.writeKey(keyBlock, updatedKEK, updatedHeaderObj) 253 } 254 255 // ViewAndUpdateHeaders updates the header manager, and updates any headers on the existing key 256 func (k *KeyReadWriter) ViewAndUpdateHeaders(cb func(PEMKeyHeaders) (PEMKeyHeaders, error)) error { 257 k.mu.Lock() 258 defer k.mu.Unlock() 259 260 pkh, err := cb(k.headersObj) 261 if err != nil { 262 return err 263 } 264 265 keyBlock, err := k.readKeyblock() 266 if err != nil { 267 return err 268 } 269 270 headers := make(map[string]string) 271 if pkh != nil { 272 var err error 273 headers, err = pkh.MarshalHeaders(k.kekData) 274 if err != nil { 275 return err 276 } 277 } 278 // we WANT any original encryption headers 279 for key, value := range keyBlock.Headers { 280 normalizedKey := strings.TrimSpace(strings.ToLower(key)) 281 if normalizedKey == "proc-type" || normalizedKey == "dek-info" { 282 headers[key] = value 283 } 284 } 285 headers[versionHeader] = strconv.FormatUint(k.kekData.Version, 10) 286 keyBlock.Headers = headers 287 288 if err = ioutils.AtomicWriteFile(k.paths.Key, pem.EncodeToMemory(keyBlock), keyPerms); err != nil { 289 return err 290 } 291 k.headersObj = pkh 292 return nil 293 } 294 295 // GetCurrentState returns the current KEK data, including version 296 func (k *KeyReadWriter) GetCurrentState() (PEMKeyHeaders, KEKData) { 297 k.mu.Lock() 298 defer k.mu.Unlock() 299 return k.headersObj, k.kekData 300 } 301 302 // Write attempts write a cert and key to text. This can also optionally update 303 // the KEK while writing, if an updated KEK is provided. If the pointer to the 304 // update KEK is nil, then we don't update. If the updated KEK itself is nil, 305 // then we update the KEK to be nil (data should be unencrypted). 306 func (k *KeyReadWriter) Write(certBytes, plaintextKeyBytes []byte, kekData *KEKData) error { 307 k.mu.Lock() 308 defer k.mu.Unlock() 309 310 // current assumption is that the cert and key will be in the same directory 311 if err := os.MkdirAll(filepath.Dir(k.paths.Key), 0755); err != nil { 312 return err 313 } 314 315 // Ensure that we will have a keypair on disk at all times by writing the cert to a 316 // temp path first. This is because we want to have only a single copy of the key 317 // for rotation and header modification. 318 tmpPaths := k.genTempPaths() 319 if err := ioutils.AtomicWriteFile(tmpPaths.Cert, certBytes, certPerms); err != nil { 320 return err 321 } 322 323 keyBlock, _ := pem.Decode(plaintextKeyBytes) 324 if keyBlock == nil { 325 return errors.New("invalid PEM-encoded private key") 326 } 327 328 if kekData == nil { 329 kekData = &k.kekData 330 } 331 pkh := k.headersObj 332 if k.headersObj != nil { 333 pkh = k.headersObj.UpdateKEK(k.kekData, *kekData) 334 } 335 336 if err := k.writeKey(keyBlock, *kekData, pkh); err != nil { 337 return err 338 } 339 return os.Rename(tmpPaths.Cert, k.paths.Cert) 340 } 341 342 func (k *KeyReadWriter) genTempPaths() CertPaths { 343 return CertPaths{ 344 Key: filepath.Join(filepath.Dir(k.paths.Key), "."+filepath.Base(k.paths.Key)), 345 Cert: filepath.Join(filepath.Dir(k.paths.Cert), "."+filepath.Base(k.paths.Cert)), 346 } 347 } 348 349 // Target returns a string representation of this KeyReadWriter, namely where 350 // it is writing to 351 func (k *KeyReadWriter) Target() string { 352 return k.paths.Cert 353 } 354 355 func (k *KeyReadWriter) readKeyblock() (*pem.Block, error) { 356 key, err := ioutil.ReadFile(k.paths.Key) 357 if err != nil { 358 return nil, err 359 } 360 361 // Decode the PEM private key 362 keyBlock, _ := pem.Decode(key) 363 if keyBlock == nil { 364 return nil, errors.New("invalid PEM-encoded private key") 365 } 366 367 return keyBlock, nil 368 } 369 370 // readKey returns the decrypted key pem bytes, and enforces the KEK if applicable 371 // (writes it back with the correct encryption if it is not correctly encrypted) 372 func (k *KeyReadWriter) readKey() (*pem.Block, error) { 373 keyBlock, err := k.readKeyblock() 374 if err != nil { 375 return nil, err 376 } 377 378 if !keyutils.IsEncryptedPEMBlock(keyBlock) { 379 return keyBlock, nil 380 } 381 382 // If it's encrypted, we can't read without a passphrase (we're assuming 383 // empty passphrases are invalid) 384 if k.kekData.KEK == nil { 385 return nil, ErrInvalidKEK{Wrapped: x509.IncorrectPasswordError} 386 } 387 388 derBytes, err := k.keyFormatter.DecryptPEMBlock(keyBlock, k.kekData.KEK) 389 if err == keyutils.ErrFIPSUnsupportedKeyFormat { 390 return nil, err 391 } else if err != nil { 392 return nil, ErrInvalidKEK{Wrapped: err} 393 } 394 395 // change header only if its pkcs8 396 if keyBlock.Type == "ENCRYPTED PRIVATE KEY" { 397 keyBlock.Type = "PRIVATE KEY" 398 } 399 400 // remove encryption PEM headers 401 headers := make(map[string]string) 402 mergePEMHeaders(headers, keyBlock.Headers) 403 404 return &pem.Block{ 405 Type: keyBlock.Type, // the key type doesn't change 406 Bytes: derBytes, 407 Headers: headers, 408 }, nil 409 } 410 411 // writeKey takes an unencrypted keyblock and, if the kek is not nil, encrypts it before 412 // writing it to disk. If the kek is nil, writes it to disk unencrypted. 413 func (k *KeyReadWriter) writeKey(keyBlock *pem.Block, kekData KEKData, pkh PEMKeyHeaders) error { 414 if kekData.KEK != nil { 415 encryptedPEMBlock, err := k.keyFormatter.EncryptPEMBlock(keyBlock.Bytes, kekData.KEK) 416 if err != nil { 417 return err 418 } 419 if !keyutils.IsEncryptedPEMBlock(encryptedPEMBlock) { 420 return errors.New("unable to encrypt key - invalid PEM file produced") 421 } 422 keyBlock = encryptedPEMBlock 423 } 424 425 if pkh != nil { 426 headers, err := pkh.MarshalHeaders(kekData) 427 if err != nil { 428 return err 429 } 430 mergePEMHeaders(keyBlock.Headers, headers) 431 } 432 keyBlock.Headers[versionHeader] = strconv.FormatUint(kekData.Version, 10) 433 434 if err := ioutils.AtomicWriteFile(k.paths.Key, pem.EncodeToMemory(keyBlock), keyPerms); err != nil { 435 return err 436 } 437 k.kekData = kekData 438 k.headersObj = pkh 439 return nil 440 } 441 442 // DowngradeKey converts the PKCS#8 key to PKCS#1 format and save it 443 func (k *KeyReadWriter) DowngradeKey() error { 444 _, key, err := k.Read() 445 if err != nil { 446 return err 447 } 448 449 oldBlock, _ := pem.Decode(key) 450 if oldBlock == nil { 451 return errors.New("invalid PEM-encoded private key") 452 } 453 454 // stop if the key is already downgraded to pkcs1 455 if !keyutils.IsPKCS8(oldBlock.Bytes) { 456 return errors.New("key is already downgraded to PKCS#1") 457 } 458 459 eckey, err := pkcs8.ConvertToECPrivateKeyPEM(key) 460 if err != nil { 461 return err 462 } 463 464 newBlock, _ := pem.Decode(eckey) 465 if newBlock == nil { 466 return errors.New("invalid PEM-encoded private key") 467 } 468 469 if k.kekData.KEK != nil { 470 newBlock, err = k.keyFormatter.EncryptPEMBlock(newBlock.Bytes, k.kekData.KEK) 471 if err != nil { 472 return err 473 } 474 } 475 476 // add kek-version header back to the new key 477 newBlock.Headers[versionHeader] = strconv.FormatUint(k.kekData.Version, 10) 478 mergePEMHeaders(newBlock.Headers, oldBlock.Headers) 479 480 // do not use krw.Write as it will convert the key to pkcs8 481 return ioutils.AtomicWriteFile(k.paths.Key, pem.EncodeToMemory(newBlock), keyPerms) 482 } 483 484 // merges one set of PEM headers onto another, excepting for key encryption value 485 // "proc-type" and "dek-info" 486 func mergePEMHeaders(original, newSet map[string]string) { 487 for key, value := range newSet { 488 normalizedKey := strings.TrimSpace(strings.ToLower(key)) 489 if normalizedKey != "proc-type" && normalizedKey != "dek-info" { 490 original[key] = value 491 } 492 } 493 }