From 7d89b19899b2ced290d6594a83f615a41e5a5fff Mon Sep 17 00:00:00 2001 From: Marko Kreen Date: Mon, 3 Aug 2015 21:56:43 +0300 Subject: [PATCH] tls: Test scripts Todo: merge with main test.sh --- test/ssl/lib.sh | 52 ++++++++++ test/ssl/newca.sh | 70 +++++++++++++ test/ssl/newsite.sh | 34 ++++++ test/ssl/test.ini | 93 +++++++++++++++++ test/ssl/test.sh | 248 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 497 insertions(+) create mode 100644 test/ssl/lib.sh create mode 100755 test/ssl/newca.sh create mode 100755 test/ssl/newsite.sh create mode 100644 test/ssl/test.ini create mode 100755 test/ssl/test.sh diff --git a/test/ssl/lib.sh b/test/ssl/lib.sh new file mode 100644 index 0000000..8e1716f --- /dev/null +++ b/test/ssl/lib.sh @@ -0,0 +1,52 @@ +#! /bin/sh + +# PEM format + +# req fields +# C = Country +# ST = State/Province +# L = Locality +# O = Organization +# OU = Org Unit +# CN = commonName +# ? = emailAddress + +umask 077 + +run() { + echo '$' "$@" + "$@" 2>&1 | sed 's/^/ > /' +} + +# key -> csr +run_req() { + tmp="csr.template" + args="" + while test "$1" != '--'; do + args="$args $1" + shift + done + shift + + ( + echo "[req]" + echo "prompt=no" + echo "distinguished_name=req_distinguished_name" + echo "[req_distinguished_name]" + for arg; do echo "$arg"; done + ) > "$tmp" + run openssl req $args -config "$tmp" + rm -f csr.template +} + +run_ca() { + ser=`cat ${CaName}/serial` + run openssl ca -batch -config "${CaName}/config.ini" "$@" + while test "$1" != '-out'; do + shift + done + if test "$1" = '-out'; then + cp "${CaName}/certs/$ser.pem" "$2" + fi +} + diff --git a/test/ssl/newca.sh b/test/ssl/newca.sh new file mode 100755 index 0000000..fecfc24 --- /dev/null +++ b/test/ssl/newca.sh @@ -0,0 +1,70 @@ +#! /bin/sh + +# create new CA + +set -e + +test -n "$1" || { + echo "usage: $0 CaName [K=V]*" + exit 1 +} + +test -d "$1" && { + echo "CA '$1' already exists" + exit 1 +} + +name="$1" +shift + +mkdir -p "$name"/certs +mkdir -p "$name"/sites +touch "$name"/index.txt +echo 01 > "$name"/serial + +. ./lib.sh + +days=10240 + +#run openssl genrsa -out "$name/ca.key" $ksize +run openssl ecparam -name prime256v1 -genkey -out "$name/ca.key" + +# self-signed cert +run_req -new -x509 -days $days -key "$name/ca.key" -out "$name/ca.crt" -- "$@" + + +cat > "$name"/config.ini < " + exit 1 +} + +test -f "$1/ca.key" || { + echo "CA $1 does not exist" + exit 1 +} + +days=10240 + +. ./lib.sh + +CaName="$1" +DstName="$2" +shift 2 + +ser=`cat $CaName/serial` + +pfx=$CaName/sites/${ser}-$DstName + +run openssl ecparam -genkey -name prime256v1 -out $pfx.key + +# cert reqs +run_req -new -key "$pfx.key" -out "$pfx.csr" -- CN="$DstName" "$@" + +# accept certs +run_ca -days $days -policy pol-server -in "$pfx.csr" -out "$pfx.crt" + diff --git a/test/ssl/test.ini b/test/ssl/test.ini new file mode 100644 index 0000000..9b75b40 --- /dev/null +++ b/test/ssl/test.ini @@ -0,0 +1,93 @@ +;; database name = connect string +[databases] + +p0 = port=6666 host=localhost dbname=p0 user=bouncer pool_size=2 +p1 = port=6666 host=localhost dbname=p1 user=bouncer +p2 = port=6668 host=localhost dbname=p2 user=bouncer + +;; Configuation section +[pgbouncer] + +;;; +;;; Administrative settings +;;; + +logfile = tmp/test.log +pidfile = tmp/test.pid + +;;; +;;; Where to wait for clients +;;; + +; ip address or * which means all ip-s +listen_addr = 127.0.0.1 +listen_port = 6667 +unix_socket_dir = /tmp + +;;; +;;; Authentication settings +;;; + +; any, trust, plain, crypt, md5 +;auth_type = trust +auth_file = tmp/userlist.txt + +;;; +;;; Pooler personality questions +;;; + +; When server connection is released back to pool: +; session - after client disconnects +; transaction - after transaction finishes +; statement - after statement finishes +pool_mode = statement + +; When taking idle server into use, this query is ran first. +; +; Query for session pooling: +; ABORT; RESET ALL; SET SESSION AUTHORIZATION DEFAULT +; Query for statement/transaction pooling: +; SELECT 1 +; Empty query disables the functionality +server_check_query = select 1 + +; If server was used more recently that this many seconds ago, +; skip the check query. If 0, the check query is always ran. +server_check_delay = 10 + +;;; +;;; Connection limits +;;; + +; total number of clients that can connect +max_client_conn = 10 +default_pool_size = 5 + +;;; +;;; Timeouts +;;; + +; Close server connection if its been connected longer. +server_lifetime = 120 + +; Close server connection if its not been used in this time. +; Allows to clean unneccessary connections from pool after peak. +server_idle_timeout = 60 + +; Cancel connection attepmt if server does not answer takes longer. +server_connect_timeout = 15 + +; If server login failed (server_connect_timeout or auth failure) +; then wait this many second. +server_login_retry = 15 + +; Dangerous. Server connection is closed if query does not return +; in this time. Should be used to survive network problems, +; _not_ as statement_timeout. (default: 0) +query_timeout = 0 + +; Dangerous. Client connection is closed if no activity in this time. +; Should be used to survive network problems. (default: 0) +client_idle_timeout = 0 + + diff --git a/test/ssl/test.sh b/test/ssl/test.sh new file mode 100755 index 0000000..5c50eba --- /dev/null +++ b/test/ssl/test.sh @@ -0,0 +1,248 @@ +#! /bin/sh + +rm -rf TestCA1 + +( +./newca.sh TestCA1 C=QQ O=Org1 CN="TestCA1" +./newsite.sh TestCA1 localhost C=QQ O=Org1 L=computer OU=db +./newsite.sh TestCA1 bouncer C=QQ O=Org1 L=computer OU=Dev +./newsite.sh TestCA1 random C=QQ O=Org1 L=computer OU=Dev +) > /dev/null + +export PATH=/usr/lib/postgresql/9.4/bin:$PATH +export PGDATA=$PWD/pgdata +export PGHOST=localhost +export PGPORT=6667 +export EF_ALLOW_MALLOC_0=1 + +mkdir -p tmp + +BOUNCER_LOG=tmp/test.log +BOUNCER_INI=test.ini +BOUNCER_PID=tmp/test.pid +BOUNCER_PORT=`sed -n '/^listen_port/s/listen_port.*=[^0-9]*//p' $BOUNCER_INI` +BOUNCER_EXE="../../pgbouncer" + +LOGDIR=tmp +NC_PORT=6668 +PG_PORT=6666 +PG_LOG=$LOGDIR/pg.log + +pgctl() { + pg_ctl -o "-p $PG_PORT" -D $PGDATA $@ >>$PG_LOG 2>&1 +} + +rm -f core +ulimit -c unlimited + +for f in pgdata/postmaster.pid tmp/test.pid; do + test -f $f && { kill `cat $f` || true; } +done + +mkdir -p $LOGDIR +rm -fr $BOUNCER_LOG $PG_LOG +rm -rr $PGDATA + +if [ ! -d $PGDATA ]; then + echo "initdb" + mkdir $PGDATA + initdb --nosync >> $PG_LOG 2>&1 + sed -r -i "/unix_socket_director/s:.*(unix_socket_director.*=).*:\\1 '/tmp':" pgdata/postgresql.conf + echo "port = $PG_PORT" >> pgdata/postgresql.conf + echo "log_connections = on" >> pgdata/postgresql.conf + echo "log_disconnections = on" >> pgdata/postgresql.conf + cp pgdata/postgresql.conf pgdata/postgresql.conf.orig + cp pgdata/pg_hba.conf pgdata/pg_hba.conf.orig + cp pgdata/pg_ident.conf pgdata/pg_ident.conf.orig + + cp -p TestCA1/sites/01-localhost.crt pgdata/server.crt + cp -p TestCA1/sites/01-localhost.key pgdata/server.key + cp -p TestCA1/ca.crt pgdata/root.crt + + echo '"bouncer" "zzz"' > tmp/userlist.txt + + chmod 600 pgdata/server.key + chmod 600 tmp/userlist.txt +fi + +pgctl start +sleep 5 + +echo "createdb" +psql -p $PG_PORT -l | grep p0 > /dev/null || { + psql -p $PG_PORT -c "create user bouncer" template1 + createdb -p $PG_PORT p0 + createdb -p $PG_PORT p1 +} + +$BOUNCER_EXE -d $BOUNCER_INI +sleep 1 + +reconf_bouncer() { + cp test.ini tmp/test.ini + for ln in "$@"; do + echo "$ln" >> tmp/test.ini + done + test -f tmp/test.pid && kill `cat tmp/test.pid` + sleep 1 + $BOUNCER_EXE -v -v -v -d tmp/test.ini +} + +reconf_pgsql() { + cp pgdata/postgresql.conf.orig pgdata/postgresql.conf + for ln in "$@"; do + echo "$ln" >> pgdata/postgresql.conf + done + pgctl stop + pgctl start + sleep 1 +} + + +# +# fw hacks +# + +# +# util functions +# + +complete() { + test -f $BOUNCER_PID && kill `cat $BOUNCER_PID` >/dev/null 2>&1 + pgctl -m fast stop + rm -f $BOUNCER_PID +} + +die() { + echo $@ + complete + exit 1 +} + +admin() { + psql -h /tmp -U pgbouncer pgbouncer -c "$@;" || die "Cannot contact bouncer!" +} + +runtest() { + echo -n "`date` running $1 ... " + eval $1 >$LOGDIR/$1.log 2>&1 + if [ $? -eq 0 ]; then + echo "ok" + else + echo "FAILED" + fi + date >> $LOGDIR/$1.log + + # allow background processing to complete + wait + # start with fresh config + kill -HUP `cat $BOUNCER_PID` +} + +psql_pg() { + psql -U bouncer -h 127.0.0.1 -p $PG_PORT "$@" +} + +psql_bouncer() { + PGUSER=bouncer psql "$@" +} + +# server_lifetime +test_server_ssl() { + reconf_bouncer "auth_type = trust" "server_tls_sslmode = require" + echo "hostssl all all 127.0.0.1/32 trust" > pgdata/pg_hba.conf + reconf_pgsql "ssl=on" "ssl_ca_file='root.crt'" + psql_bouncer -q -d p0 -c "select 'ssl-connect'" | tee tmp/test.tmp0 + grep -q "ssl-connect" tmp/test.tmp0 + rc=$? + return $rc +} + +test_server_ssl_verify() { + reconf_bouncer "auth_type = trust" \ + "server_tls_sslmode = verify-full" \ + "server_tls_ca_file = TestCA1/ca.crt" + + echo "hostssl all all 127.0.0.1/32 trust" > pgdata/pg_hba.conf + reconf_pgsql "ssl=on" "ssl_ca_file='root.crt'" + psql_bouncer -q -d p0 -c "select 'ssl-full-connect'" | tee tmp/test.tmp1 + grep -q "ssl-full-connect" tmp/test.tmp1 + rc=$? + return $rc +} + +test_server_ssl_pg_auth() { + reconf_bouncer "auth_type = trust" \ + "server_tls_sslmode = verify-full" \ + "server_tls_ca_file = TestCA1/ca.crt" \ + "server_tls_key_file = TestCA1/sites/02-bouncer.key" \ + "server_tls_cert_file = TestCA1/sites/02-bouncer.crt" + + echo "hostssl all all 127.0.0.1/32 cert" > pgdata/pg_hba.conf + reconf_pgsql "ssl=on" "ssl_ca_file='root.crt'" + psql_bouncer -q -d p0 -c "select 'ssl-cert-connect'" | tee tmp/test.tmp2 + grep "ssl-cert-connect" tmp/test.tmp2 + rc=$? + return $rc +} + +test_client_ssl() { + reconf_bouncer "auth_type = trust" "server_tls_sslmode = prefer" \ + "client_tls_sslmode = require" \ + "client_tls_key_file = TestCA1/sites/01-localhost.key" \ + "client_tls_cert_file = TestCA1/sites/01-localhost.crt" + echo "host all all 127.0.0.1/32 trust" > pgdata/pg_hba.conf + reconf_pgsql "ssl=on" "ssl_ca_file='root.crt'" + psql_bouncer -q -d "dbname=p0 sslmode=require" -c "select 'client-ssl-connect'" | tee tmp/test.tmp + grep -q "client-ssl-connect" tmp/test.tmp + rc=$? + return $rc +} + +test_client_ssl() { + reconf_bouncer "auth_type = trust" "server_tls_sslmode = prefer" \ + "client_tls_sslmode = require" \ + "client_tls_key_file = TestCA1/sites/01-localhost.key" \ + "client_tls_cert_file = TestCA1/sites/01-localhost.crt" + echo "host all all 127.0.0.1/32 trust" > pgdata/pg_hba.conf + reconf_pgsql "ssl=on" "ssl_ca_file='root.crt'" + psql_bouncer -q -d "dbname=p0 sslmode=verify-full sslrootcert=TestCA1/ca.crt" -c "select 'client-ssl-connect'" | tee tmp/test.tmp 2>&1 + grep -q "client-ssl-connect" tmp/test.tmp + rc=$? + return $rc +} + +test_client_ssl_auth() { + reconf_bouncer "auth_type = cert" "server_tls_sslmode = prefer" \ + "client_tls_sslmode = verify-full" \ + "client_tls_ca_file = TestCA1/ca.crt" \ + "client_tls_key_file = TestCA1/sites/01-localhost.key" \ + "client_tls_cert_file = TestCA1/sites/01-localhost.crt" + echo "host all all 127.0.0.1/32 trust" > pgdata/pg_hba.conf + reconf_pgsql "ssl=on" "ssl_ca_file='root.crt'" + psql_bouncer -q -d "dbname=p0 sslmode=require sslkey=TestCA1/sites/02-bouncer.key sslcert=TestCA1/sites/02-bouncer.crt" \ + -c "select 'client-ssl-connect'" | tee tmp/test.tmp 2>&1 + grep -q "client-ssl-connect" tmp/test.tmp + rc=$? + return $rc +} + +testlist=" +test_server_ssl +test_server_ssl_verify +test_server_ssl_pg_auth +test_client_ssl +test_client_ssl_auth +" +if [ $# -gt 0 ]; then + testlist="$*" +fi + +for test in $testlist +do + runtest $test +done + +complete + +# vim: sts=0 sw=8 noet nosmarttab: -- 2.40.0