[MDEV-32342] WSREP_SST_OPT_REMOTE_AUTH bad value, causes bad socat commonname, causes SST to fail Created: 2023-10-03  Updated: 2024-01-21

Status: Stalled
Project: MariaDB Server
Component/s: wsrep
Affects Version/s: 10.3.39, 10.6.15, 10.11.5
Fix Version/s: 10.4, 10.5, 10.6, 10.11, 11.0, 11.1

Type: Bug Priority: Major
Reporter: Walter Doekes Assignee: Julius Goryavsky
Resolution: Unresolved Votes: 0
Labels: None

Attachments: File issueCN_IN_REMOTE_AUTH.patch    
Issue Links:
Relates
relates to MDEV-32344 IST "Donor does not know my secret" w... Closed
relates to MDEV-26360 Using hostnames for MariaBackup SSTs ... Closed

 Description   

Recently I ran into this:

WSREP_SST: [INFO] Evaluating '/usr//bin/mbstream' -c 'xtrabackup_galera_info' | socat stdio openssl-connect:JOINER_IP:4444,linger=10,cert='/etc/mysql/ssl/galera.crt',key='/etc/mysql/ssl/galera.key',cafile='/etc/mysql/ssl/ca.crt',commonname='CN '; RC=( ${PIPESTATUS[@]} ) (20231002 18:20:06.014)
2023/10/02 14:16:55 socat[1316] E certificate is valid but its commonName does not match hostname

So, the donor refuses to connect to the joiner because the commonname does not match. And the common name is CN<space>, which is unexpected and wrong.

The commonname should be autodetected by the joiner and passed to the donor, here (10.3 branch, at mariadb-10.3.39):
./scripts/wsrep_sst_mariabackup.sh

wait_for_listen()
{
    for i in {1..150}; do
        if check_port "" "$SST_PORT" 'socat|nc'; then
            break
        fi
        sleep 0.2
    done
    echo "ready $ADDR:$SST_PORT/$MODULE/$lsn/$sst_ver"
}

The donor turns that response into:

2023-10-02 18:53:19 1 [Note] WSREP: Prepared SST request: mariabackup|CN :e4083cfc25e917fca34c140c56397c60@JOINER_IP:4444/xtrabackup_sst//1

The parts before the "@" get collected by sst_prepare_other into wsrep_sst_donate_cb where it is passed to ./scripts/wsrep_sst_mariabackup.sh (in ./scripts/wsrep_sst_common.sh) as WSREP_SST_OPT_REMOTE_AUTH:

WSREP_SST_OPT_REMOTE_AUTH="${WSREP_SST_OPT_REMOTE_AUTH:-}"
...
    readonly WSREP_SST_OPT_REMOTE_USER="${WSREP_SST_OPT_REMOTE_AUTH%%:*}"
    readonly WSREP_SST_OPT_REMOTE_PSWD="${WSREP_SST_OPT_REMOTE_AUTH#*:}"

Here it is finally used in ./scripts/wsrep_sst_mariabackup.sh:

           else
                # CA verification
                verify_ca_matches_cert "$tpem" "$tcert" "$tcap"
                if [ -n "$WSREP_SST_OPT_REMOTE_USER" ]; then
                    CN_option=",commonname='$WSREP_SST_OPT_REMOTE_USER'"
                elif [ "$WSREP_SST_OPT_ROLE" = 'joiner' -o $encrypt -eq 4 ]
                then
                    CN_option=",commonname=''"
                elif is_local_ip "$WSREP_SST_OPT_HOST_UNESCAPED"; then
                    CN_option=',commonname=localhost'
                else
                    CN_option=",commonname='$WSREP_SST_OPT_HOST_UNESCAPED'"
                fi

Where we get the strange "CN" value. Disabling commonname checking with encrypt=4 is not even possible.

Working back again, we see where the "ready $ADDR" comes from. If tmode starts with VERIFY (on the joiner), it runs (./scripts/wsrep_sst_mariabackup.sh):

    if [ "${tmode#VERIFY}" != "$tmode" ]; then
        # backward-incompatible behavior:
        CN=""
        if [ -n "$tpem" ]; then
            # find out my Common Name
            get_openssl
            if [ -z "$OPENSSL_BINARY" ]; then
                wsrep_log_error \
                    'openssl not found but it is required for authentication'
                exit 42
            fi
            CN=$("$OPENSSL_BINARY" x509 -noout -subject -in "$tpem" | \
                 tr ',' '\n' | grep -F 'CN =' | cut -d '=' -f2 | sed s/^\ // | \
                 sed s/\ %//)
        fi
        MY_SECRET="$(wsrep_gen_secret)"
        # Add authentication data to address
        ADDR="$CN:$MY_SECRET@$ADDR"

And that CN generation is broken.

The trouble arises with the following example strings:

(openssl 1.1 and openssl 3.0)

subject=CN = galera-node1, O = OSSO B.V., C = NL, ST = Groningen, L = Groningen

(openssl 1.0)

subject= /CN=galera-node1/O=OSSO B.V./C=NL/ST=Groningen/L=Groningen

I don't expect the openssl-1.0 string to work. But the openssl-1.1/3.0 strings don't work either when CN is the first keyword of the subject.

See these:

$ echo 'subject= /CN=galera-node1/O=OSSO B.V./C=NL/ST=Groningen/L=Groningen' | tr ',' '\n' | grep -F 'CN =' | cut -d '=' -f2 | sed s/^\ // | sed s/\ %// | cat -A

$ echo 'subject=CN = galera-node1, O = OSSO B.V., C = NL, ST = Groningen, L = Groningen' | tr ',' '\n' | grep -F 'CN =' | cut -d '=' -f2 | sed s/^\ // | sed s/\ %// | cat -A
CN $

The only one that would work, would be something like:

$ echo 'subject=O = OSSO B.V., CN = galera-node1, C = NL, ST = Groningen, L = Groningen' | tr ',' '\n' | grep -F 'CN =' | cut -d '=' -f2 | sed s/^\ // | sed s/\ %// | cat -A
galera-node1$

I suggest replacing the tr+grep+cut+sed madness, with a single sed oneliner:

sed -e 's/^subject=//;s/^/, /;s@.*[/,][[:blank:]]*CN[[:blank:]]*=[[:blank:]]*@@;s@[/,].*@@'

That works on all mentioned -subject output lines.

Diff:

diff --git a/scripts/wsrep_sst_mariabackup.sh b/scripts/wsrep_sst_mariabackup.sh
index 7e26af83701..45dd1f3c4ff 100644
--- a/scripts/wsrep_sst_mariabackup.sh
+++ b/scripts/wsrep_sst_mariabackup.sh
@@ -1334,9 +1334,11 @@ else # joiner
                     'openssl not found but it is required for authentication'
                 exit 42
             fi
-            CN=$("$OPENSSL_BINARY" x509 -noout -subject -in "$tpem" | \
-                 tr ',' '\n' | grep -F 'CN =' | cut -d '=' -f2 | sed s/^\ // | \
-                 sed s/\ %//)
+            CN=$("$OPENSSL_BINARY" x509 -noout -subject -in "$tpem" | sed -e '
+               s/^subject=//
+               s/^/, /
+               s@.*[/,][[:blank:]]*CN[[:blank:]]*=[[:blank:]]*@@
+               s@[/,].*@@')
         fi
         MY_SECRET="$(wsrep_gen_secret)"
         # Add authentication data to address

This was tested with the following config:

[mysqld]
wsrep_sst_method=mariabackup
ssl-cert=/etc/mysql/ssl/galera.crt
ssl-key=/etc/mysql/ssl/galera.key
ssl-ca=/etc/mysql/ssl/ca.crt
 
[sst]
ssl-mode=VERIFY_CA
encrypt=3

This yields on donor (10.3.39+maria~ubu2004):

WSREP_SST: [INFO] SSL configuration: CA='/etc/mysql/ssl/ca.crt', CAPATH='', CERT='/etc/mysql/ssl/galera.crt', KEY='/etc/mysql/ssl/galera.key', MODE='VERIFY_CA', encrypt='3' (20231002 20:20:44.172)
...
WSREP_SST: [INFO] Evaluating '/usr//bin/mbstream' -c 'xtrabackup_galera_info' | socat stdio openssl-connect:JOINER_IP:4444,linger=10,cert='/etc/mysql/ssl/galera.crt',key='/etc/mysql/ssl/galera.key',cafile='/etc/mysql/ssl/ca.crt',commonname='galera-node3'; RC=( ${PIPESTATUS[@]} ) (20231002 20:20:44.323)
...

And everyone lives happily after.

Cheers,
Walter Doekes
OSSO B.V.


Generated at Thu Feb 08 10:30:37 UTC 2024 using Jira 8.20.16#820016-sha1:9d11dbea5f4be3d4cc21f03a88dd11d8c8687422.