pg.ddx.io  pgsql-hackers@postgresql.org mailing list archive  
help / color / mirror / Atom feed
From: Bruce Momjian <bruce@momjian.us>
To: Neil Chen <carpenter.nail.cz@gmail.com>
Cc: PostgreSQL-development <pgsql-hackers@postgresql.org>
Cc: Masahiko Sawada <masahiko.sawada@2ndquadrant.com>
Subject: Re: Proposed patch for key managment
Date: Tue, 15 Dec 2020 11:34:41 -0500
Message-ID: <20201215163441.GE14596@momjian.us> (raw)
In-Reply-To: <20201215041618.GC14596@momjian.us>
References: <20201202213814.GG20285@momjian.us>
	<20201214230615.GA14596@momjian.us>
	<CAA3qoJ=qtO5JcSBjqFDBT9iKUX9XKmC5bXCrd7rysE+XSMEuTg@mail.gmail.com>
	<20201215041618.GC14596@momjian.us>

On Mon, Dec 14, 2020 at 11:16:18PM -0500, Bruce Momjian wrote:
> > 1. Previously, we added a variable bootstrap_keys_wrap that is used for
> > encryption during initdb. However, since we save the "wrapped" key, we need to
> > use a global KEK that can be accessed in boot mode to unwrap it before use... I
> > don't know if that's good. To make it simple, I modified the
> > bootstrap_keys_wrap to store the "unwrapped" key so that the encryption
> > function can get it correctly. (The variable name should be changed
> > accordingly).
> 
> I see what you are saying.  We store the wrapped in bootstrap mode, but
> the unwrapped in normal mode.  There is also the case of when we copy
> the keys from an old cluster.  I will work on a patch tomorrow and
> report back here.

I had not considered that we need the date keys available in bootstrap
mode, even if we copied them from another cluster during pg_upgrade.  I
have updated the diff URLs and attaching a patch showing the changes I
made. Basically, I had to separate BootStrapKmgr() into sections:

1.  copy or create an empty live key directory
2.  get the pass phrase
3.  populate the live key directory if we didn't copy it
4.  decrypt they keys into a file-scoped variable

Thanks for showing me this missing feature.

-- 
  Bruce Momjian  <bruce@momjian.us>        https://momjian.us
  EnterpriseDB                             https://enterprisedb.com

  The usefulness of a cup is in its emptiness, Bruce Lee

Attachments:

bootstrap_key.diff (text/x-diff, 8.0K) download diff --git a/src/backend/crypto/kmgr.c b/src/backend/crypto/kmgr.c new file mode 100644 index 9143e72..24c5d7f *** a/src/backend/crypto/kmgr.c --- b/src/backend/crypto/kmgr.c *************** static KmgrShmemData *KmgrShmem; *** 50,56 **** char *cluster_passphrase_command = NULL; int file_encryption_keylen = 0; ! CryptoKey bootstrap_keys_wrap[KMGR_MAX_INTERNAL_KEYS]; extern char *bootstrap_old_key_datadir; extern int bootstrap_file_encryption_keylen; --- 50,56 ---- char *cluster_passphrase_command = NULL; int file_encryption_keylen = 0; ! CryptoKey bootstrap_keys[KMGR_MAX_INTERNAL_KEYS]; extern char *bootstrap_old_key_datadir; extern int bootstrap_file_encryption_keylen; *************** static CryptoKey *generate_crypto_key(in *** 65,74 **** void BootStrapKmgr(void) { ! PgKeyWrapCtx *ctx; char passphrase[KMGR_MAX_PASSPHRASE_LEN]; - uint8 KEK_enc[KMGR_ENC_KEY_LEN]; - uint8 KEK_hmac[KMGR_MAC_KEY_LEN]; int passlen; #ifndef USE_OPENSSL --- 65,74 ---- void BootStrapKmgr(void) { ! char live_path[MAXPGPATH]; ! CryptoKey *keys_wrap; ! int nkeys; char passphrase[KMGR_MAX_PASSPHRASE_LEN]; int passlen; #ifndef USE_OPENSSL *************** BootStrapKmgr(void) *** 78,83 **** --- 78,85 ---- errhint("Compile with --with-openssl to use cluster encryption.")))); #endif + snprintf(live_path, sizeof(live_path), "%s/%s", DataDir, LIVE_KMGR_DIR); + /* copy cluster file encryption keys from an old cluster? */ if (bootstrap_old_key_datadir != NULL) { *************** BootStrapKmgr(void) *** 87,122 **** bootstrap_old_key_datadir, LIVE_KMGR_DIR); copydir(old_key_dir, LIVE_KMGR_DIR, true); } ! /* generate new cluster file encryption keys */ else { ! char live_path[MAXPGPATH]; ! ! if (mkdir(LIVE_KMGR_DIR, pg_dir_create_mode) < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not create cluster file encryption directory \"%s\": %m", LIVE_KMGR_DIR))); - memset(bootstrap_keys_wrap, 0, sizeof(bootstrap_keys_wrap)); - /* bzero keys on exit */ - on_proc_exit(bzeroKmgrKeys, 0); - - /* Get key encryption key from the passphrase command */ - snprintf(live_path, sizeof(live_path), "%s/%s", DataDir, LIVE_KMGR_DIR); - passlen = kmgr_run_cluster_passphrase_command(cluster_passphrase_command, - passphrase, KMGR_MAX_PASSPHRASE_LEN, - live_path); - if (passlen < KMGR_MIN_PASSPHRASE_LEN) - ereport(ERROR, - (errmsg("passphrase must be at least %d bytes", - KMGR_MIN_PASSPHRASE_LEN))); - /* Get key encryption key and HMAC key from passphrase */ kmgr_derive_keys(passphrase, passlen, KEK_enc, KEK_hmac); - explicit_bzero(passphrase, passlen); - /* Create temporary key wrap context */ ctx = pg_create_keywrap_ctx(KEK_enc, KEK_hmac); if (!ctx) --- 89,128 ---- bootstrap_old_key_datadir, LIVE_KMGR_DIR); copydir(old_key_dir, LIVE_KMGR_DIR, true); } ! /* create empty directory */ else { ! if (mkdir(LIVE_KMGR_DIR, pg_dir_create_mode) < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not create cluster file encryption directory \"%s\": %m", LIVE_KMGR_DIR))); + } + + /* + * Get key encryption key from the passphrase command. The passphrase + * command might want to check for the existance of files in the + * live directory, so run this _after_ copying the directory in place. + */ + passlen = kmgr_run_cluster_passphrase_command(cluster_passphrase_command, + passphrase, KMGR_MAX_PASSPHRASE_LEN, + live_path); + if (passlen < KMGR_MIN_PASSPHRASE_LEN) + ereport(ERROR, + (errmsg("passphrase must be at least %d bytes", + KMGR_MIN_PASSPHRASE_LEN))); + + /* generate new cluster file encryption keys */ + if (bootstrap_old_key_datadir == NULL) + { + CryptoKey bootstrap_keys_wrap[KMGR_MAX_INTERNAL_KEYS]; + PgKeyWrapCtx *ctx; + uint8 KEK_enc[KMGR_ENC_KEY_LEN]; + uint8 KEK_hmac[KMGR_MAC_KEY_LEN]; /* Get key encryption key and HMAC key from passphrase */ kmgr_derive_keys(passphrase, passlen, KEK_enc, KEK_hmac); /* Create temporary key wrap context */ ctx = pg_create_keywrap_ctx(KEK_enc, KEK_hmac); if (!ctx) *************** BootStrapKmgr(void) *** 142,149 **** --- 148,177 ---- /* Save data encryption keys to the disk */ KmgrSaveCryptoKeys(LIVE_KMGR_DIR, bootstrap_keys_wrap); + explicit_bzero(bootstrap_keys_wrap, sizeof(bootstrap_keys_wrap)); pg_free_keywrap_ctx(ctx); } + + /* + * We are either decrypting keys we copied from an old cluster, or + * decrypting keys we just wrote above --- either way, we decrypt + * them here and store them in a file-scoped variable for use in + * later encrypting during bootstrap mode. + */ + /* Get the crypto keys from the file */ + keys_wrap = kmgr_get_cryptokeys(LIVE_KMGR_DIR, &nkeys); + Assert(nkeys == KMGR_MAX_INTERNAL_KEYS); + + if (!kmgr_verify_passphrase(passphrase, passlen, keys_wrap, bootstrap_keys, + KMGR_MAX_INTERNAL_KEYS)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("cluster passphrase does not match expected passphrase"))); + + /* bzero keys on exit */ + on_proc_exit(bzeroKmgrKeys, 0); + + explicit_bzero(passphrase, passlen); } /* Report shared-memory space needed by KmgrShmem */ *************** void *** 179,187 **** InitializeKmgr(void) { CryptoKey *keys_wrap; char passphrase[KMGR_MAX_PASSPHRASE_LEN]; int passlen; - int nkeys; struct stat buffer; char live_path[MAXPGPATH]; --- 207,215 ---- InitializeKmgr(void) { CryptoKey *keys_wrap; + int nkeys; char passphrase[KMGR_MAX_PASSPHRASE_LEN]; int passlen; struct stat buffer; char live_path[MAXPGPATH]; *************** InitializeKmgr(void) *** 219,234 **** (errcode(ERRCODE_INTERNAL_ERROR), (errmsg("cluster has no data encryption keys")))); - /* Get the crypto keys from the file */ - keys_wrap = kmgr_get_cryptokeys(LIVE_KMGR_DIR, &nkeys); - Assert(nkeys == KMGR_MAX_INTERNAL_KEYS); - /* Get cluster passphrase */ snprintf(live_path, sizeof(live_path), "%s/%s", DataDir, LIVE_KMGR_DIR); passlen = kmgr_run_cluster_passphrase_command(cluster_passphrase_command, passphrase, KMGR_MAX_PASSPHRASE_LEN, live_path); /* * Verify passphrase and prepare a data encryption key in plaintext in shared memory. */ --- 247,262 ---- (errcode(ERRCODE_INTERNAL_ERROR), (errmsg("cluster has no data encryption keys")))); /* Get cluster passphrase */ snprintf(live_path, sizeof(live_path), "%s/%s", DataDir, LIVE_KMGR_DIR); passlen = kmgr_run_cluster_passphrase_command(cluster_passphrase_command, passphrase, KMGR_MAX_PASSPHRASE_LEN, live_path); + /* Get the crypto keys from the file */ + keys_wrap = kmgr_get_cryptokeys(LIVE_KMGR_DIR, &nkeys); + Assert(nkeys == KMGR_MAX_INTERNAL_KEYS); + /* * Verify passphrase and prepare a data encryption key in plaintext in shared memory. */ *************** static void *** 245,251 **** bzeroKmgrKeys(int status, Datum arg) { if (IsBootstrapProcessingMode()) ! explicit_bzero(bootstrap_keys_wrap, sizeof(bootstrap_keys_wrap)); else explicit_bzero(KmgrShmem->intlKeys, sizeof(KmgrShmem->intlKeys)); } --- 273,279 ---- bzeroKmgrKeys(int status, Datum arg) { if (IsBootstrapProcessingMode()) ! explicit_bzero(bootstrap_keys, sizeof(bootstrap_keys)); else explicit_bzero(KmgrShmem->intlKeys, sizeof(KmgrShmem->intlKeys)); } *************** KmgrGetKey(int id) *** 256,262 **** Assert(id < KMGR_MAX_INTERNAL_KEYS); return (const CryptoKey *) (IsBootstrapProcessingMode() ? ! &(bootstrap_keys_wrap[id]) : &(KmgrShmem->intlKeys[id])); } /* Generate an empty CryptoKey */ --- 284,290 ---- Assert(id < KMGR_MAX_INTERNAL_KEYS); return (const CryptoKey *) (IsBootstrapProcessingMode() ? ! &(bootstrap_keys[id]) : &(KmgrShmem->intlKeys[id])); } /* Generate an empty CryptoKey */

view thread (93+ messages)  latest in thread

Message-ID: <20201215163441.GE14596@momjian.us>
Permalink:  ../20201215163441.GE14596@momjian.us/
Also on:    postgresql.org/message-id/20201215163441.GE14596@momjian.us

 ·  · 

reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Reply to all the recipients using the --to and --cc options:
  reply via email

  To: pgsql-hackers@postgresql.org
  Cc: bruce@momjian.us, carpenter.nail.cz@gmail.com, masahiko.sawada@2ndquadrant.com
  Subject: Re: Proposed patch for key managment
  In-Reply-To: <20201215163441.GE14596@momjian.us>

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox