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) downloaddiff --git a/src/backend/crypto/kmgr.c b/src/backend/crypto/kmgr.cnew file mode 100644index 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 */
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