mirror of
https://github.com/postgres/postgres.git
synced 2025-05-03 22:24:49 +03:00
Add scripts for retrieving the cluster file encryption key
Scripts are passphrase, direct, AWS, and two Yubikey ones. Backpatch-through: master
This commit is contained in:
parent
3d4843babc
commit
d7602afa2e
@ -54,6 +54,15 @@ ifeq ($(with_systemd),yes)
|
|||||||
LIBS += -lsystemd
|
LIBS += -lsystemd
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
CRYPTO_SCRIPTDIR=auth_commands
|
||||||
|
CRYPTO_SCRIPTS = \
|
||||||
|
ckey_aws.sh.sample \
|
||||||
|
ckey_direct.sh.sample \
|
||||||
|
ckey_passphrase.sh.sample \
|
||||||
|
ckey_piv_nopin.sh.sample \
|
||||||
|
ckey_piv_pin.sh.sample \
|
||||||
|
ssl_passphrase.sh.sample
|
||||||
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
all: submake-libpgport submake-catalog-headers submake-utils-headers postgres $(POSTGRES_IMP)
|
all: submake-libpgport submake-catalog-headers submake-utils-headers postgres $(POSTGRES_IMP)
|
||||||
@ -212,6 +221,7 @@ endif
|
|||||||
$(INSTALL_DATA) $(srcdir)/libpq/pg_hba.conf.sample '$(DESTDIR)$(datadir)/pg_hba.conf.sample'
|
$(INSTALL_DATA) $(srcdir)/libpq/pg_hba.conf.sample '$(DESTDIR)$(datadir)/pg_hba.conf.sample'
|
||||||
$(INSTALL_DATA) $(srcdir)/libpq/pg_ident.conf.sample '$(DESTDIR)$(datadir)/pg_ident.conf.sample'
|
$(INSTALL_DATA) $(srcdir)/libpq/pg_ident.conf.sample '$(DESTDIR)$(datadir)/pg_ident.conf.sample'
|
||||||
$(INSTALL_DATA) $(srcdir)/utils/misc/postgresql.conf.sample '$(DESTDIR)$(datadir)/postgresql.conf.sample'
|
$(INSTALL_DATA) $(srcdir)/utils/misc/postgresql.conf.sample '$(DESTDIR)$(datadir)/postgresql.conf.sample'
|
||||||
|
$(INSTALL_DATA) $(addprefix 'crypto/', $(CRYPTO_SCRIPTS)) '$(DESTDIR)$(datadir)/$(CRYPTO_SCRIPTDIR)'
|
||||||
|
|
||||||
ifeq ($(with_llvm), yes)
|
ifeq ($(with_llvm), yes)
|
||||||
install-bin: install-postgres-bitcode
|
install-bin: install-postgres-bitcode
|
||||||
@ -237,6 +247,7 @@ endif
|
|||||||
|
|
||||||
installdirs:
|
installdirs:
|
||||||
$(MKDIR_P) '$(DESTDIR)$(bindir)' '$(DESTDIR)$(datadir)'
|
$(MKDIR_P) '$(DESTDIR)$(bindir)' '$(DESTDIR)$(datadir)'
|
||||||
|
$(MKDIR_P) '$(DESTDIR)$(datadir)' '$(DESTDIR)$(datadir)/$(CRYPTO_SCRIPTDIR)'
|
||||||
ifeq ($(PORTNAME), cygwin)
|
ifeq ($(PORTNAME), cygwin)
|
||||||
ifeq ($(MAKE_DLL), true)
|
ifeq ($(MAKE_DLL), true)
|
||||||
$(MKDIR_P) '$(DESTDIR)$(libdir)'
|
$(MKDIR_P) '$(DESTDIR)$(libdir)'
|
||||||
@ -257,6 +268,7 @@ endif
|
|||||||
|
|
||||||
uninstall:
|
uninstall:
|
||||||
rm -f '$(DESTDIR)$(bindir)/postgres$(X)' '$(DESTDIR)$(bindir)/postmaster'
|
rm -f '$(DESTDIR)$(bindir)/postgres$(X)' '$(DESTDIR)$(bindir)/postmaster'
|
||||||
|
rm -f $(addprefix '$(DESTDIR)$(datadir)/$(CRYPTO_SCRIPTDIR)'/, $(CRYPTO_SCRIPTS))
|
||||||
ifeq ($(MAKE_EXPORTS), true)
|
ifeq ($(MAKE_EXPORTS), true)
|
||||||
rm -f '$(DESTDIR)$(pkglibdir)/$(POSTGRES_IMP)'
|
rm -f '$(DESTDIR)$(pkglibdir)/$(POSTGRES_IMP)'
|
||||||
rm -f '$(DESTDIR)$(pgxsdir)/$(MKLDEXPORT_DIR)/mkldexport.sh'
|
rm -f '$(DESTDIR)$(pgxsdir)/$(MKLDEXPORT_DIR)/mkldexport.sh'
|
||||||
|
50
src/backend/crypto/ckey_aws.sh.sample
Executable file
50
src/backend/crypto/ckey_aws.sh.sample
Executable file
@ -0,0 +1,50 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# This uses the AWS Secrets Manager using the AWS CLI and OpenSSL.
|
||||||
|
|
||||||
|
[ "$#" -ne 1 ] && echo "cluster_key_command usage: $0 \"%d\"" 1>&2 && exit 1
|
||||||
|
# No need for %R or -R since we are not prompting
|
||||||
|
|
||||||
|
DIR="$1"
|
||||||
|
[ ! -e "$DIR" ] && echo "$DIR does not exist" 1>&2 && exit 1
|
||||||
|
[ ! -d "$DIR" ] && echo "$DIR is not a directory" 1>&2 && exit 1
|
||||||
|
|
||||||
|
# File containing the id of the AWS secret
|
||||||
|
AWS_ID_FILE="$DIR/aws-secret.id"
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# Create an AWS Secrets Manager secret?
|
||||||
|
if [ ! -e "$AWS_ID_FILE" ]
|
||||||
|
then # The 'postgres' operating system user must have permission to
|
||||||
|
# access the AWS CLI
|
||||||
|
|
||||||
|
# The epoch-time/directory/hostname combination is unique
|
||||||
|
HASH=$(echo -n "$(date '+%s')$DIR$(hostname)" | sha1sum | cut -d' ' -f1)
|
||||||
|
AWS_SECRET_ID="Postgres-cluster-key-$HASH"
|
||||||
|
|
||||||
|
# Use stdin to avoid passing the secret on the command line
|
||||||
|
openssl rand -hex 32 |
|
||||||
|
aws secretsmanager create-secret \
|
||||||
|
--name "$AWS_SECRET_ID" \
|
||||||
|
--description 'Used for Postgres cluster file encryption' \
|
||||||
|
--secret-string 'file:///dev/stdin' \
|
||||||
|
--output text > /dev/null
|
||||||
|
if [ "$?" -ne 0 ]
|
||||||
|
then echo 'cluster key generation failed' 1>&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$AWS_SECRET_ID" > "$AWS_ID_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! aws secretsmanager get-secret-value \
|
||||||
|
--secret-id "$(cat "$AWS_ID_FILE")" \
|
||||||
|
--output text
|
||||||
|
then echo 'cluster key retrieval failed' 1>&2
|
||||||
|
exit 1
|
||||||
|
fi | awk -F'\t' 'NR == 1 {print $4}'
|
||||||
|
|
||||||
|
exit 0
|
37
src/backend/crypto/ckey_direct.sh.sample
Executable file
37
src/backend/crypto/ckey_direct.sh.sample
Executable file
@ -0,0 +1,37 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# This uses a key supplied by the user
|
||||||
|
# If OpenSSL is installed, you can generate a pseudo-random key by running:
|
||||||
|
# openssl rand -hex 32
|
||||||
|
# To get a true random key, run:
|
||||||
|
# wget -q -O - 'https://www.random.org/cgi-bin/randbyte?nbytes=32&format=h' | tr -d ' \n'; echo
|
||||||
|
|
||||||
|
[ "$#" -lt 1 ] && echo "cluster_key_command usage: $0 %R [%p]" 1>&2 && exit 1
|
||||||
|
# Supports environment variable PROMPT
|
||||||
|
|
||||||
|
FD="$1"
|
||||||
|
[ ! -t "$FD" ] && echo "file descriptor $FD does not refer to a terminal" 1>&2 && exit 1
|
||||||
|
|
||||||
|
[ "$2" ] && PROMPT="$2"
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
[ ! "$PROMPT" ] && PROMPT='Enter cluster key as 64 hexadecimal characters: '
|
||||||
|
|
||||||
|
stty -echo <&"$FD"
|
||||||
|
|
||||||
|
echo 1>&"$FD"
|
||||||
|
echo -n "$PROMPT" 1>&"$FD"
|
||||||
|
read KEY <&"$FD"
|
||||||
|
|
||||||
|
stty echo <&"$FD"
|
||||||
|
|
||||||
|
if [ "$(expr "$KEY" : '[0-9a-fA-F]*$')" -ne 64 ]
|
||||||
|
then echo 'invalid; must be 64 hexadecimal characters' 1>&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$KEY"
|
||||||
|
|
||||||
|
exit 0
|
33
src/backend/crypto/ckey_passphrase.sh.sample
Executable file
33
src/backend/crypto/ckey_passphrase.sh.sample
Executable file
@ -0,0 +1,33 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# This uses a passphrase supplied by the user.
|
||||||
|
|
||||||
|
[ "$#" -lt 1 ] && echo "cluster_key_command usage: $0 %R [\"%p\"]" 1>&2 && exit 1
|
||||||
|
|
||||||
|
FD="$1"
|
||||||
|
[ ! -t "$FD" ] && echo "file descriptor $FD does not refer to a terminal" 1>&2 && exit 1
|
||||||
|
# Supports environment variable PROMPT
|
||||||
|
|
||||||
|
[ "$2" ] && PROMPT="$2"
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
[ ! "$PROMPT" ] && PROMPT='Enter cluster passphrase: '
|
||||||
|
|
||||||
|
stty -echo <&"$FD"
|
||||||
|
|
||||||
|
echo 1>&"$FD"
|
||||||
|
echo -n "$PROMPT" 1>&"$FD"
|
||||||
|
read PASS <&"$FD"
|
||||||
|
|
||||||
|
stty echo <&"$FD"
|
||||||
|
|
||||||
|
if [ ! "$PASS" ]
|
||||||
|
then echo 'invalid: empty passphrase' 1>&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$PASS" | sha256sum | cut -d' ' -f1
|
||||||
|
|
||||||
|
exit 0
|
63
src/backend/crypto/ckey_piv_nopin.sh.sample
Executable file
63
src/backend/crypto/ckey_piv_nopin.sh.sample
Executable file
@ -0,0 +1,63 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# This uses the public/private keys on a PIV device, like a CAC or Yubikey.
|
||||||
|
# It uses a PIN stored in a file.
|
||||||
|
# It uses OpenSSL with PKCS11 enabled via OpenSC.
|
||||||
|
|
||||||
|
[ "$#" -ne 1 ] && echo "cluster_key_command usage: $0 \"%d\"" 1>&2 && exit 1
|
||||||
|
# Supports environment variable PIV_PIN_FILE
|
||||||
|
# No need for %R or -R since we are not prompting for a PIN
|
||||||
|
|
||||||
|
DIR="$1"
|
||||||
|
[ ! -e "$DIR" ] && echo "$DIR does not exist" 1>&2 && exit 1
|
||||||
|
[ ! -d "$DIR" ] && echo "$DIR is not a directory" 1>&2 && exit 1
|
||||||
|
|
||||||
|
# Set these here or pass in as environment variables.
|
||||||
|
# File that stores the PIN to unlock the PIV
|
||||||
|
#PIV_PIN_FILE=''
|
||||||
|
# PIV slot 3 is the "Key Management" slot, so we use '0:3'
|
||||||
|
PIV_SLOT='0:3'
|
||||||
|
|
||||||
|
# File containing the cluster key encrypted with the PIV_SLOT's public key
|
||||||
|
KEY_FILE="$DIR/pivpass.key"
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
[ ! "$PIV_PIN_FILE" ] && echo 'PIV_PIN_FILE undefined' 1>&2 && exit 1
|
||||||
|
[ ! -e "$PIV_PIN_FILE" ] && echo "$PIV_PIN_FILE does not exist" 1>&2 && exit 1
|
||||||
|
[ -d "$PIV_PIN_FILE" ] && echo "$PIV_PIN_FILE is a directory" 1>&2 && exit 1
|
||||||
|
|
||||||
|
[ ! "$KEY_FILE" ] && echo 'KEY_FILE undefined' 1>&2 && exit 1
|
||||||
|
[ -d "$KEY_FILE" ] && echo "$KEY_FILE is a directory" 1>&2 && exit 1
|
||||||
|
|
||||||
|
# Create a cluster key encrypted with the PIV_SLOT's public key?
|
||||||
|
if [ ! -e "$KEY_FILE" ]
|
||||||
|
then # The 'postgres' operating system user must have permission to
|
||||||
|
# access the PIV device.
|
||||||
|
|
||||||
|
openssl rand -hex 32 |
|
||||||
|
if ! openssl rsautl -engine pkcs11 -keyform engine -encrypt \
|
||||||
|
-inkey "$PIV_SLOT" -passin file:"$PIV_PIN_FILE" -out "$KEY_FILE"
|
||||||
|
then echo 'cluster key generation failed' 1>&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Warn the user to save the cluster key in a safe place
|
||||||
|
cat 1>&2 <<END
|
||||||
|
|
||||||
|
WARNING: The PIV device can be locked and require a reset if too many PIN
|
||||||
|
attempts fail. It is recommended to run this command manually and save
|
||||||
|
the cluster key in a secure location for possible recovery.
|
||||||
|
END
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Decrypt the cluster key encrypted with the PIV_SLOT's public key
|
||||||
|
if ! openssl rsautl -engine pkcs11 -keyform engine -decrypt \
|
||||||
|
-inkey "$PIV_SLOT" -passin file:"$PIV_PIN_FILE" -in "$KEY_FILE"
|
||||||
|
then echo 'cluster key decryption failed' 1>&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
76
src/backend/crypto/ckey_piv_pin.sh.sample
Executable file
76
src/backend/crypto/ckey_piv_pin.sh.sample
Executable file
@ -0,0 +1,76 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# This uses the public/private keys on a PIV device, like a CAC or Yubikey.
|
||||||
|
# It requires a user-entered PIN.
|
||||||
|
# It uses OpenSSL with PKCS11 enabled via OpenSC.
|
||||||
|
|
||||||
|
[ "$#" -lt 2 ] && echo "cluster_key_command usage: $0 \"%d\" %R [\"%p\"]" 1>&2 && exit 1
|
||||||
|
# Supports environment variable PROMPT
|
||||||
|
|
||||||
|
DIR="$1"
|
||||||
|
[ ! -e "$DIR" ] && echo "$DIR does not exist" 1>&2 && exit 1
|
||||||
|
[ ! -d "$DIR" ] && echo "$DIR is not a directory" 1>&2 && exit 1
|
||||||
|
|
||||||
|
FD="$2"
|
||||||
|
[ ! -t "$FD" ] && echo "file descriptor $FD does not refer to a terminal" 1>&2 && exit 1
|
||||||
|
|
||||||
|
[ "$3" ] && PROMPT="$3"
|
||||||
|
|
||||||
|
# PIV slot 3 is the "Key Management" slot, so we use '0:3'
|
||||||
|
PIV_SLOT='0:3'
|
||||||
|
|
||||||
|
# File containing the cluster key encrypted with the PIV_SLOT's public key
|
||||||
|
KEY_FILE="$DIR/pivpass.key"
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
[ ! "$PROMPT" ] && PROMPT='Enter PIV PIN: '
|
||||||
|
|
||||||
|
stty -echo <&"$FD"
|
||||||
|
|
||||||
|
# Create a cluster key encrypted with the PIV_SLOT's public key?
|
||||||
|
if [ ! -e "$KEY_FILE" ]
|
||||||
|
then echo 1>&"$FD"
|
||||||
|
echo -n "$PROMPT" 1>&"$FD"
|
||||||
|
|
||||||
|
# The 'postgres' operating system user must have permission to
|
||||||
|
# access the PIV device.
|
||||||
|
|
||||||
|
openssl rand -hex 32 |
|
||||||
|
# 'engine "pkcs11" set.' message confuses prompting
|
||||||
|
if ! openssl rsautl -engine pkcs11 -keyform engine -encrypt \
|
||||||
|
-inkey "$PIV_SLOT" -passin fd:"$FD" -out "$KEY_FILE" 2>&1
|
||||||
|
then stty echo <&"$FD"
|
||||||
|
echo 'cluster key generation failed' 1>&2
|
||||||
|
exit 1
|
||||||
|
fi | grep -v 'engine "pkcs11" set\.'
|
||||||
|
|
||||||
|
echo 1>&"$FD"
|
||||||
|
|
||||||
|
# Warn the user to save the cluster key in a safe place
|
||||||
|
cat 1>&"$FD" <<END
|
||||||
|
|
||||||
|
WARNING: The PIV can be locked and require a reset if too many PIN
|
||||||
|
attempts fail. It is recommended to run this command manually and save
|
||||||
|
the cluster key in a secure location for possible recovery.
|
||||||
|
END
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo 1>&"$FD"
|
||||||
|
echo -n "$PROMPT" 1>&"$FD"
|
||||||
|
|
||||||
|
# Decrypt the cluster key encrypted with the PIV_SLOT's public key
|
||||||
|
if ! openssl rsautl -engine pkcs11 -keyform engine -decrypt \
|
||||||
|
-inkey "$PIV_SLOT" -passin fd:"$FD" -in "$KEY_FILE" 2>&1
|
||||||
|
then stty echo <&"$FD"
|
||||||
|
echo 'cluster key retrieval failed' 1>&2
|
||||||
|
exit 1
|
||||||
|
fi | grep -v 'engine "pkcs11" set\.'
|
||||||
|
|
||||||
|
echo 1>&"$FD"
|
||||||
|
|
||||||
|
stty echo <&"$FD"
|
||||||
|
|
||||||
|
exit 0
|
33
src/backend/crypto/ssl_passphrase.sh.sample
Executable file
33
src/backend/crypto/ssl_passphrase.sh.sample
Executable file
@ -0,0 +1,33 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# This uses a passphrase supplied by the user.
|
||||||
|
|
||||||
|
[ "$#" -lt 1 ] && echo "ssl_passphrase_command usage: $0 %R [\"%p\"]" 1>&2 && exit 1
|
||||||
|
|
||||||
|
FD="$1"
|
||||||
|
[ ! -t "$FD" ] && echo "file descriptor $FD does not refer to a terminal" 1>&2 && exit 1
|
||||||
|
# Supports environment variable PROMPT
|
||||||
|
|
||||||
|
[ "$2" ] && PROMPT="$2"
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
[ ! "$PROMPT" ] && PROMPT='Enter cluster passphrase: '
|
||||||
|
|
||||||
|
stty -echo <&"$FD"
|
||||||
|
|
||||||
|
echo 1>&"$FD"
|
||||||
|
echo -n "$PROMPT" 1>&"$FD"
|
||||||
|
read PASS <&"$FD"
|
||||||
|
|
||||||
|
stty echo <&"$FD"
|
||||||
|
|
||||||
|
if [ ! "$PASS" ]
|
||||||
|
then echo 'invalid: empty passphrase' 1>&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$PASS"
|
||||||
|
|
||||||
|
exit 0
|
Loading…
x
Reference in New Issue
Block a user