Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit d7602af

Browse files
committed
Add scripts for retrieving the cluster file encryption key
Scripts are passphrase, direct, AWS, and two Yubikey ones. Backpatch-through: master
1 parent 3d4843b commit d7602af

7 files changed

+304
-0
lines changed

src/backend/Makefile

+12
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,15 @@ ifeq ($(with_systemd),yes)
5454
LIBS += -lsystemd
5555
endif
5656

57+
CRYPTO_SCRIPTDIR=auth_commands
58+
CRYPTO_SCRIPTS = \
59+
ckey_aws.sh.sample \
60+
ckey_direct.sh.sample \
61+
ckey_passphrase.sh.sample \
62+
ckey_piv_nopin.sh.sample \
63+
ckey_piv_pin.sh.sample \
64+
ssl_passphrase.sh.sample
65+
5766
##########################################################################
5867

5968
all: submake-libpgport submake-catalog-headers submake-utils-headers postgres $(POSTGRES_IMP)
@@ -212,6 +221,7 @@ endif
212221
$(INSTALL_DATA) $(srcdir)/libpq/pg_hba.conf.sample '$(DESTDIR)$(datadir)/pg_hba.conf.sample'
213222
$(INSTALL_DATA) $(srcdir)/libpq/pg_ident.conf.sample '$(DESTDIR)$(datadir)/pg_ident.conf.sample'
214223
$(INSTALL_DATA) $(srcdir)/utils/misc/postgresql.conf.sample '$(DESTDIR)$(datadir)/postgresql.conf.sample'
224+
$(INSTALL_DATA) $(addprefix 'crypto/', $(CRYPTO_SCRIPTS)) '$(DESTDIR)$(datadir)/$(CRYPTO_SCRIPTDIR)'
215225

216226
ifeq ($(with_llvm), yes)
217227
install-bin: install-postgres-bitcode
@@ -237,6 +247,7 @@ endif
237247

238248
installdirs:
239249
$(MKDIR_P) '$(DESTDIR)$(bindir)' '$(DESTDIR)$(datadir)'
250+
$(MKDIR_P) '$(DESTDIR)$(datadir)' '$(DESTDIR)$(datadir)/$(CRYPTO_SCRIPTDIR)'
240251
ifeq ($(PORTNAME), cygwin)
241252
ifeq ($(MAKE_DLL), true)
242253
$(MKDIR_P) '$(DESTDIR)$(libdir)'
@@ -257,6 +268,7 @@ endif
257268

258269
uninstall:
259270
rm -f '$(DESTDIR)$(bindir)/postgres$(X)' '$(DESTDIR)$(bindir)/postmaster'
271+
rm -f $(addprefix '$(DESTDIR)$(datadir)/$(CRYPTO_SCRIPTDIR)'/, $(CRYPTO_SCRIPTS))
260272
ifeq ($(MAKE_EXPORTS), true)
261273
rm -f '$(DESTDIR)$(pkglibdir)/$(POSTGRES_IMP)'
262274
rm -f '$(DESTDIR)$(pgxsdir)/$(MKLDEXPORT_DIR)/mkldexport.sh'

src/backend/crypto/ckey_aws.sh.sample

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#!/bin/sh
2+
3+
# This uses the AWS Secrets Manager using the AWS CLI and OpenSSL.
4+
5+
[ "$#" -ne 1 ] && echo "cluster_key_command usage: $0 \"%d\"" 1>&2 && exit 1
6+
# No need for %R or -R since we are not prompting
7+
8+
DIR="$1"
9+
[ ! -e "$DIR" ] && echo "$DIR does not exist" 1>&2 && exit 1
10+
[ ! -d "$DIR" ] && echo "$DIR is not a directory" 1>&2 && exit 1
11+
12+
# File containing the id of the AWS secret
13+
AWS_ID_FILE="$DIR/aws-secret.id"
14+
15+
16+
# ----------------------------------------------------------------------
17+
18+
19+
# Create an AWS Secrets Manager secret?
20+
if [ ! -e "$AWS_ID_FILE" ]
21+
then # The 'postgres' operating system user must have permission to
22+
# access the AWS CLI
23+
24+
# The epoch-time/directory/hostname combination is unique
25+
HASH=$(echo -n "$(date '+%s')$DIR$(hostname)" | sha1sum | cut -d' ' -f1)
26+
AWS_SECRET_ID="Postgres-cluster-key-$HASH"
27+
28+
# Use stdin to avoid passing the secret on the command line
29+
openssl rand -hex 32 |
30+
aws secretsmanager create-secret \
31+
--name "$AWS_SECRET_ID" \
32+
--description 'Used for Postgres cluster file encryption' \
33+
--secret-string 'file:///dev/stdin' \
34+
--output text > /dev/null
35+
if [ "$?" -ne 0 ]
36+
then echo 'cluster key generation failed' 1>&2
37+
exit 1
38+
fi
39+
40+
echo "$AWS_SECRET_ID" > "$AWS_ID_FILE"
41+
fi
42+
43+
if ! aws secretsmanager get-secret-value \
44+
--secret-id "$(cat "$AWS_ID_FILE")" \
45+
--output text
46+
then echo 'cluster key retrieval failed' 1>&2
47+
exit 1
48+
fi | awk -F'\t' 'NR == 1 {print $4}'
49+
50+
exit 0
+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#!/bin/sh
2+
3+
# This uses a key supplied by the user
4+
# If OpenSSL is installed, you can generate a pseudo-random key by running:
5+
# openssl rand -hex 32
6+
# To get a true random key, run:
7+
# wget -q -O - 'https://www.random.org/cgi-bin/randbyte?nbytes=32&format=h' | tr -d ' \n'; echo
8+
9+
[ "$#" -lt 1 ] && echo "cluster_key_command usage: $0 %R [%p]" 1>&2 && exit 1
10+
# Supports environment variable PROMPT
11+
12+
FD="$1"
13+
[ ! -t "$FD" ] && echo "file descriptor $FD does not refer to a terminal" 1>&2 && exit 1
14+
15+
[ "$2" ] && PROMPT="$2"
16+
17+
18+
# ----------------------------------------------------------------------
19+
20+
[ ! "$PROMPT" ] && PROMPT='Enter cluster key as 64 hexadecimal characters: '
21+
22+
stty -echo <&"$FD"
23+
24+
echo 1>&"$FD"
25+
echo -n "$PROMPT" 1>&"$FD"
26+
read KEY <&"$FD"
27+
28+
stty echo <&"$FD"
29+
30+
if [ "$(expr "$KEY" : '[0-9a-fA-F]*$')" -ne 64 ]
31+
then echo 'invalid; must be 64 hexadecimal characters' 1>&2
32+
exit 1
33+
fi
34+
35+
echo "$KEY"
36+
37+
exit 0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/bin/sh
2+
3+
# This uses a passphrase supplied by the user.
4+
5+
[ "$#" -lt 1 ] && echo "cluster_key_command usage: $0 %R [\"%p\"]" 1>&2 && exit 1
6+
7+
FD="$1"
8+
[ ! -t "$FD" ] && echo "file descriptor $FD does not refer to a terminal" 1>&2 && exit 1
9+
# Supports environment variable PROMPT
10+
11+
[ "$2" ] && PROMPT="$2"
12+
13+
14+
# ----------------------------------------------------------------------
15+
16+
[ ! "$PROMPT" ] && PROMPT='Enter cluster passphrase: '
17+
18+
stty -echo <&"$FD"
19+
20+
echo 1>&"$FD"
21+
echo -n "$PROMPT" 1>&"$FD"
22+
read PASS <&"$FD"
23+
24+
stty echo <&"$FD"
25+
26+
if [ ! "$PASS" ]
27+
then echo 'invalid: empty passphrase' 1>&2
28+
exit 1
29+
fi
30+
31+
echo "$PASS" | sha256sum | cut -d' ' -f1
32+
33+
exit 0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#!/bin/sh
2+
3+
# This uses the public/private keys on a PIV device, like a CAC or Yubikey.
4+
# It uses a PIN stored in a file.
5+
# It uses OpenSSL with PKCS11 enabled via OpenSC.
6+
7+
[ "$#" -ne 1 ] && echo "cluster_key_command usage: $0 \"%d\"" 1>&2 && exit 1
8+
# Supports environment variable PIV_PIN_FILE
9+
# No need for %R or -R since we are not prompting for a PIN
10+
11+
DIR="$1"
12+
[ ! -e "$DIR" ] && echo "$DIR does not exist" 1>&2 && exit 1
13+
[ ! -d "$DIR" ] && echo "$DIR is not a directory" 1>&2 && exit 1
14+
15+
# Set these here or pass in as environment variables.
16+
# File that stores the PIN to unlock the PIV
17+
#PIV_PIN_FILE=''
18+
# PIV slot 3 is the "Key Management" slot, so we use '0:3'
19+
PIV_SLOT='0:3'
20+
21+
# File containing the cluster key encrypted with the PIV_SLOT's public key
22+
KEY_FILE="$DIR/pivpass.key"
23+
24+
25+
# ----------------------------------------------------------------------
26+
27+
[ ! "$PIV_PIN_FILE" ] && echo 'PIV_PIN_FILE undefined' 1>&2 && exit 1
28+
[ ! -e "$PIV_PIN_FILE" ] && echo "$PIV_PIN_FILE does not exist" 1>&2 && exit 1
29+
[ -d "$PIV_PIN_FILE" ] && echo "$PIV_PIN_FILE is a directory" 1>&2 && exit 1
30+
31+
[ ! "$KEY_FILE" ] && echo 'KEY_FILE undefined' 1>&2 && exit 1
32+
[ -d "$KEY_FILE" ] && echo "$KEY_FILE is a directory" 1>&2 && exit 1
33+
34+
# Create a cluster key encrypted with the PIV_SLOT's public key?
35+
if [ ! -e "$KEY_FILE" ]
36+
then # The 'postgres' operating system user must have permission to
37+
# access the PIV device.
38+
39+
openssl rand -hex 32 |
40+
if ! openssl rsautl -engine pkcs11 -keyform engine -encrypt \
41+
-inkey "$PIV_SLOT" -passin file:"$PIV_PIN_FILE" -out "$KEY_FILE"
42+
then echo 'cluster key generation failed' 1>&2
43+
exit 1
44+
fi
45+
46+
# Warn the user to save the cluster key in a safe place
47+
cat 1>&2 <<END
48+
49+
WARNING: The PIV device can be locked and require a reset if too many PIN
50+
attempts fail. It is recommended to run this command manually and save
51+
the cluster key in a secure location for possible recovery.
52+
END
53+
54+
fi
55+
56+
# Decrypt the cluster key encrypted with the PIV_SLOT's public key
57+
if ! openssl rsautl -engine pkcs11 -keyform engine -decrypt \
58+
-inkey "$PIV_SLOT" -passin file:"$PIV_PIN_FILE" -in "$KEY_FILE"
59+
then echo 'cluster key decryption failed' 1>&2
60+
exit 1
61+
fi
62+
63+
exit 0
+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#!/bin/sh
2+
3+
# This uses the public/private keys on a PIV device, like a CAC or Yubikey.
4+
# It requires a user-entered PIN.
5+
# It uses OpenSSL with PKCS11 enabled via OpenSC.
6+
7+
[ "$#" -lt 2 ] && echo "cluster_key_command usage: $0 \"%d\" %R [\"%p\"]" 1>&2 && exit 1
8+
# Supports environment variable PROMPT
9+
10+
DIR="$1"
11+
[ ! -e "$DIR" ] && echo "$DIR does not exist" 1>&2 && exit 1
12+
[ ! -d "$DIR" ] && echo "$DIR is not a directory" 1>&2 && exit 1
13+
14+
FD="$2"
15+
[ ! -t "$FD" ] && echo "file descriptor $FD does not refer to a terminal" 1>&2 && exit 1
16+
17+
[ "$3" ] && PROMPT="$3"
18+
19+
# PIV slot 3 is the "Key Management" slot, so we use '0:3'
20+
PIV_SLOT='0:3'
21+
22+
# File containing the cluster key encrypted with the PIV_SLOT's public key
23+
KEY_FILE="$DIR/pivpass.key"
24+
25+
26+
# ----------------------------------------------------------------------
27+
28+
[ ! "$PROMPT" ] && PROMPT='Enter PIV PIN: '
29+
30+
stty -echo <&"$FD"
31+
32+
# Create a cluster key encrypted with the PIV_SLOT's public key?
33+
if [ ! -e "$KEY_FILE" ]
34+
then echo 1>&"$FD"
35+
echo -n "$PROMPT" 1>&"$FD"
36+
37+
# The 'postgres' operating system user must have permission to
38+
# access the PIV device.
39+
40+
openssl rand -hex 32 |
41+
# 'engine "pkcs11" set.' message confuses prompting
42+
if ! openssl rsautl -engine pkcs11 -keyform engine -encrypt \
43+
-inkey "$PIV_SLOT" -passin fd:"$FD" -out "$KEY_FILE" 2>&1
44+
then stty echo <&"$FD"
45+
echo 'cluster key generation failed' 1>&2
46+
exit 1
47+
fi | grep -v 'engine "pkcs11" set\.'
48+
49+
echo 1>&"$FD"
50+
51+
# Warn the user to save the cluster key in a safe place
52+
cat 1>&"$FD" <<END
53+
54+
WARNING: The PIV can be locked and require a reset if too many PIN
55+
attempts fail. It is recommended to run this command manually and save
56+
the cluster key in a secure location for possible recovery.
57+
END
58+
59+
fi
60+
61+
echo 1>&"$FD"
62+
echo -n "$PROMPT" 1>&"$FD"
63+
64+
# Decrypt the cluster key encrypted with the PIV_SLOT's public key
65+
if ! openssl rsautl -engine pkcs11 -keyform engine -decrypt \
66+
-inkey "$PIV_SLOT" -passin fd:"$FD" -in "$KEY_FILE" 2>&1
67+
then stty echo <&"$FD"
68+
echo 'cluster key retrieval failed' 1>&2
69+
exit 1
70+
fi | grep -v 'engine "pkcs11" set\.'
71+
72+
echo 1>&"$FD"
73+
74+
stty echo <&"$FD"
75+
76+
exit 0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/bin/sh
2+
3+
# This uses a passphrase supplied by the user.
4+
5+
[ "$#" -lt 1 ] && echo "ssl_passphrase_command usage: $0 %R [\"%p\"]" 1>&2 && exit 1
6+
7+
FD="$1"
8+
[ ! -t "$FD" ] && echo "file descriptor $FD does not refer to a terminal" 1>&2 && exit 1
9+
# Supports environment variable PROMPT
10+
11+
[ "$2" ] && PROMPT="$2"
12+
13+
14+
# ----------------------------------------------------------------------
15+
16+
[ ! "$PROMPT" ] && PROMPT='Enter cluster passphrase: '
17+
18+
stty -echo <&"$FD"
19+
20+
echo 1>&"$FD"
21+
echo -n "$PROMPT" 1>&"$FD"
22+
read PASS <&"$FD"
23+
24+
stty echo <&"$FD"
25+
26+
if [ ! "$PASS" ]
27+
then echo 'invalid: empty passphrase' 1>&2
28+
exit 1
29+
fi
30+
31+
echo "$PASS"
32+
33+
exit 0

0 commit comments

Comments
 (0)