Objective
“Is the database connection encrypted?” is a question that gets a yes far more often than it deserves. By the end of this lab you will be able to answer a sharper set of questions, with evidence:
- Is TLS on, and what certificate is the server presenting?
- Is the client checking that certificate, or merely encrypting?
- Could a client turn encryption off if it wanted to?
- Which sessions on this server right now are unencrypted?
The lab starts with a finding you may not expect. The Debian package turns TLS on by default and points it at a certificate no client can ever verify. That configuration passes a naive audit and provides no protection against an attacker positioned to intercept the connection. You will see exactly how far it gets you and exactly where it stops.
Architecture
One cluster, one small CA created for the lab, a server certificate carrying both a DNS name and an IP address in its subjectAltName, and a client certificate. Connections arrive over TCP by three different names so that host name verification can be exercised.
flowchart TD
CA["Lab CA\nca.crt / ca.key"] --> SC["server.crt\nCN=pg.lab.internal\nSAN: DNS pg.lab.internal, IP 172.26.0.4"]
CA --> CC["client.crt\nCN=tlsuser"]
SC --> S["PostgreSQL 18\nssl=on"]
C1["client via pg.lab.internal\nname matches the SAN"] --> S
C2["client via db.wrong.name\nname NOT in the SAN"] --> S
CC --> C1
S --> V["pg_stat_ssl\nssl, version, cipher, bits, client_dn"]
Requirements
- A PostgreSQL 18 cluster you can reload, whose
pg_hba.confyou can edit. The lab reusesrbpg-lab01from Labs 1, 3 and 4. - openssl on the same host. The lab installs it into the container.
- Two host names resolving to the server, one that appears in the
certificate and one that does not. The lab adds both to
/etc/hosts. - The addresses
172.26.0.4and the namespg.lab.internalanddb.wrong.nameare placeholders. Substitute your own consistently.
Scenario
You have inherited a PostgreSQL server. The handover notes say “connections are encrypted”. You need to find out what that sentence is worth before you sign off on it.
Tasks
Task 1 — Find out what the cluster is already doing
LAB="$HOME/rbpg-lab-05"
mkdir -p "$LAB"
docker exec -u postgres rbpg-lab01 psql -X -c "
SELECT name, setting FROM pg_settings WHERE name LIKE 'ssl%' ORDER BY name;" \
| tee "$LAB/tls-baseline.txt"
$ psql -X -c "SELECT name, setting FROM pg_settings WHERE name LIKE 'ssl%' ORDER BY name;" name | setting
----------------------------------------+----------------------------------------
ssl | on
ssl_ca_file |
ssl_cert_file | /etc/ssl/certs/ssl-cert-snakeoil.pem
ssl_ciphers | HIGH:MEDIUM:+3DES:!aNULL
ssl_crl_dir |
ssl_crl_file |
ssl_dh_params_file |
ssl_groups | X25519:prime256v1
ssl_key_file | /etc/ssl/private/ssl-cert-snakeoil.key
ssl_library | OpenSSL
ssl_max_protocol_version |
ssl_min_protocol_version | TLSv1.2
ssl_passphrase_command |
ssl_passphrase_command_supports_reload | off
ssl_prefer_server_ciphers | on
ssl_tls13_ciphers |
(16 rows)ssl = on, out of the box, with nobody having configured anything. The
certificate is the Debian snakeoil certificate, generated by the
ssl-cert package at install time. Look at what it is:
docker exec rbpg-lab01 openssl x509 -in /etc/ssl/certs/ssl-cert-snakeoil.pem \
-noout -subject -issuer -dates | tee -a "$LAB/tls-baseline.txt"
$ openssl x509 -in /etc/ssl/certs/ssl-cert-snakeoil.pem -noout -subject -issuer -datessubject=CN=5dbb8625e16a
issuer=CN=5dbb8625e16a
notBefore=Aug 28 00:09:00 2026 GMT
notAfter=Aug 25 00:09:00 2036 GMTSubject equals issuer, so it signed itself. The common name is the
machine’s host name at install time, which on this container is a
generated hex string and on a real host is whatever hostname returned
during the package install — not necessarily the name clients use.
Task 2 — Watch the gap for yourself
Open the server to the network, create a test role, and connect:
docker exec -i -u postgres rbpg-lab01 psql -X <<'SQL'
CREATE ROLE tlsuser LOGIN PASSWORD 'lab05-not-a-real-secret';
ALTER SYSTEM SET listen_addresses = '*';
SQL
docker exec rbpg-lab01 bash -c \
"echo 'host all all 172.26.0.0/16 scram-sha-256 # lab05' \
>> /etc/postgresql/18/main/pg_hba.conf"
docker exec rbpg-lab01 pg_ctlcluster 18 main restart
docker exec -u postgres -e PGPASSWORD=lab05-not-a-real-secret rbpg-lab01 \
psql -X "host=172.26.0.4 user=tlsuser dbname=postgres sslmode=require" \
-c "SELECT ssl, version, cipher, bits FROM pg_stat_ssl WHERE pid = pg_backend_pid();"
docker exec -u postgres -e PGPASSWORD=lab05-not-a-real-secret rbpg-lab01 \
psql -X "host=172.26.0.4 user=tlsuser dbname=postgres sslmode=verify-full" \
-c "SELECT 1;"
$ one connection with sslmode=require, one with sslmode=verify-full ssl | version | cipher | bits
-----+---------+------------------------+------
t | TLSv1.3 | TLS_AES_256_GCM_SHA384 | 256
(1 row)
psql: error: connection to server at "172.26.0.4", port 5432 failed: root certificate file "/var/lib/postgresql/.postgresql/root.crt" does not exist
Either provide the file, use the system's trusted roots with sslrootcert=system, or change sslmode to disable server certificate verification.TLS 1.3 with a 256-bit AES-GCM cipher, and a client that cannot verify who it is talking to. That is the whole gap in two commands.
The error message is unusually good: it names the exact file libpq
looked for (~/.postgresql/root.crt, relative to the connecting user’s
home directory) and lists the three ways forward.
Task 3 — Issue a certificate a client can actually verify
docker exec rbpg-lab01 bash -c '
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq openssl
mkdir -p /etc/postgresql/18/main/tls && cd /etc/postgresql/18/main/tls
# A CA for the lab.
openssl req -new -x509 -days 3650 -nodes -newkey rsa:2048 \
-keyout ca.key -out ca.crt -subj "/CN=RunBook Academy Lab CA"
# A server key and request.
openssl req -new -nodes -newkey rsa:2048 -keyout server.key -out server.csr \
-subj "/CN=pg.lab.internal"
# The extensions that matter: the names clients will use, and the purpose.
printf "subjectAltName=DNS:pg.lab.internal,IP:172.26.0.4\nextendedKeyUsage=serverAuth\n" \
> server.ext
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
-out server.crt -days 825 -extfile server.ext
chown -R postgres:postgres /etc/postgresql/18/main/tls
'
docker exec rbpg-lab01 openssl x509 -in /etc/postgresql/18/main/tls/server.crt \
-noout -subject -issuer -ext subjectAltName
$ openssl x509 -in server.crt -noout -subject -issuer -ext subjectAltNamesubject=CN=pg.lab.internal
issuer=CN=RunBook Academy Lab CA
X509v3 Subject Alternative Name:
DNS:pg.lab.internal, IP Address:172.26.0.4The subjectAltName is the part that does the work. Host name
verification checks the SAN; the common name is legacy and modern
clients may ignore it entirely. A certificate without a SAN covering the
name clients actually use will fail verify-full no matter how correct
everything else is.
Task 4 — Install it, with the key permissions wrong on purpose
docker exec rbpg-lab01 chmod 0644 /etc/postgresql/18/main/tls/server.key
docker exec -i -u postgres rbpg-lab01 psql -X <<'SQL'
ALTER SYSTEM SET ssl_cert_file = '/etc/postgresql/18/main/tls/server.crt';
ALTER SYSTEM SET ssl_key_file = '/etc/postgresql/18/main/tls/server.key';
ALTER SYSTEM SET ssl_ca_file = '/etc/postgresql/18/main/tls/ca.crt';
SELECT pg_reload_conf();
SQL
docker exec rbpg-lab01 tail -3 /var/log/postgresql/postgresql-18-main.log
$ reload after pointing ssl_key_file at a 0644 key, then tail the log2026-08-28 00:29:04.250 UTC [8708] LOG: private key file "/etc/postgresql/18/main/tls/server.key" has group or world access
2026-08-28 00:29:04.250 UTC [8708] DETAIL: File must have permissions u=rw (0600) or less if owned by the database user, or permissions u=rw,g=r (0640) or less if owned by root.
2026-08-28 00:29:04.250 UTC [8708] LOG: SSL configuration was not reloadedNote the last line: “SSL configuration was not reloaded”. This is the
same all-or-nothing behaviour you saw with pg_hba.conf in Lab 4. The
server kept its previous TLS configuration — the snakeoil certificate —
and carried on serving connections with it.
That is the dangerous shape again. A deployment that rotates a certificate and gets the permissions wrong does not fail loudly; it quietly keeps serving the old certificate until somebody restarts the server, at which point it does not come up.
The DETAIL line states both acceptable cases precisely: 0600 or
tighter if the key is owned by the database user, or 0640 or tighter
if it is owned by root. The second case exists so a key managed by a
certificate-renewal process running as root can be read by a
postgres-group process without being copied.
docker exec rbpg-lab01 chmod 0600 /etc/postgresql/18/main/tls/server.key
docker exec -u postgres rbpg-lab01 psql -X -c "SELECT pg_reload_conf();"
docker exec -u postgres rbpg-lab01 psql -X -c "
SELECT name, setting FROM pg_settings
WHERE name IN ('ssl','ssl_cert_file','ssl_key_file','ssl_ca_file') ORDER BY name;"
$ psql -X -c "SELECT name, setting FROM pg_settings WHERE name IN ('ssl','ssl_cert_file','ssl_key_file','ssl_ca_file') ORDER BY name;" name | setting
---------------+----------------------------------------
ssl | on
ssl_ca_file | /etc/postgresql/18/main/tls/ca.crt
ssl_cert_file | /etc/postgresql/18/main/tls/server.crt
ssl_key_file | /etc/postgresql/18/main/tls/server.key
(4 rows)Task 5 — The sslmode ladder, one server, six answers
Make the certificate’s name resolve, then walk every value:
docker exec rbpg-lab01 bash -c 'cp /etc/postgresql/18/main/tls/ca.crt /root/ca.crt
echo "172.26.0.4 pg.lab.internal" >> /etc/hosts'
for M in disable allow prefer require verify-ca verify-full; do
echo "sslmode=$M"
docker exec -e PGPASSWORD=lab05-not-a-real-secret rbpg-lab01 \
psql -X "host=pg.lab.internal user=tlsuser dbname=postgres sslmode=$M sslrootcert=/root/ca.crt" \
-c "SELECT ssl, version, cipher FROM pg_stat_ssl WHERE pid = pg_backend_pid();"
done | tee "$LAB/sslmode-ladder.txt"
$ six connections differing only in sslmodesslmode=disable
ssl | version | cipher
-----+---------+--------
f | |
sslmode=allow
ssl | version | cipher
-----+---------+--------
f | |
sslmode=prefer
ssl | version | cipher
-----+---------+------------------------
t | TLSv1.3 | TLS_AES_256_GCM_SHA384
sslmode=require
t | TLSv1.3 | TLS_AES_256_GCM_SHA384
sslmode=verify-ca
t | TLSv1.3 | TLS_AES_256_GCM_SHA384
sslmode=verify-full
t | TLSv1.3 | TLS_AES_256_GCM_SHA384Two of the six connected in clear text. disable never offers TLS;
allow prefers not to use it and only tries if a plain connection is
refused. Both are easy to read as “SSL is configured” and neither
encrypts anything on a server that accepts plain connections.
The remaining four all produced identical TLS. What separates them is not the encryption — it is what the client checked before trusting it, and Task 6 makes that visible.
Task 6 — Where verify-ca stops and verify-full continues
Add a second name for the same address, one that is not in the certificate:
docker exec rbpg-lab01 bash -c 'echo "172.26.0.4 db.wrong.name" >> /etc/hosts'
for M in require verify-ca verify-full; do
echo "sslmode=$M via db.wrong.name"
docker exec -e PGPASSWORD=lab05-not-a-real-secret rbpg-lab01 \
psql -X "host=db.wrong.name user=tlsuser dbname=postgres sslmode=$M sslrootcert=/root/ca.crt" \
-c "SELECT 'connected' AS status;"
done | tee "$LAB/verification-gap.txt"
$ three connections to a host name absent from the certificatesslmode=require via db.wrong.name
status
-----------
connected
(1 row)
sslmode=verify-ca via db.wrong.name
status
-----------
connected
(1 row)
sslmode=verify-full via db.wrong.name
psql: error: connection to server at "db.wrong.name" (172.26.0.4), port 5432 failed: server certificate for "pg.lab.internal" (and 1 other name) does not match host name "db.wrong.name"This is the whole point of the lab.
| sslmode | Encrypts | Checks the chain | Checks the host name |
|---|---|---|---|
disable | no | no | no |
allow | only if forced | no | no |
prefer | if offered | no | no |
require | yes | no | no |
verify-ca | yes | yes | no |
verify-full | yes | yes | yes |
require is the value most connection strings settle on, and it checks
nothing about who answered. verify-ca proves the certificate was
issued by a CA you trust, which stops a self-signed impostor but not a
different host holding a legitimate certificate from the same CA — a
meaningful gap in any environment where one internal CA signs for many
hosts.
Only verify-full closes it, and only if the certificate’s
subjectAltName actually covers the name the client connects by.
Task 7 — Take the choice away from the client
Everything so far has been the client’s decision. sslmode lives in the
client’s connection string, so a server that accepts plain connections
will get them from any client configured to send them — including one
misconfigured by accident.
Change one word in pg_hba.conf:
docker exec rbpg-lab01 sed -i \
's|^host all all 172.26.0.0/16 scram-sha-256 # lab05|hostssl all all 172.26.0.0/16 scram-sha-256 # lab05|' \
/etc/postgresql/18/main/pg_hba.conf
docker exec -u postgres rbpg-lab01 psql -X -c "SELECT pg_reload_conf();"
docker exec -e PGPASSWORD=lab05-not-a-real-secret rbpg-lab01 \
psql -X "host=pg.lab.internal user=tlsuser dbname=postgres sslmode=disable" \
-c "SELECT 1;" | tee "$LAB/enforcement.txt"
$ a connection with sslmode=disable after switching the rule to hostsslpsql: error: connection to server at "pg.lab.internal" (172.26.0.4), port 5432 failed: FATAL: no pg_hba.conf entry for host "172.26.0.4", user "tlsuser", database "postgres", no encryptionThe message is the Lab 4 “no entry” case, and the reason is worth
following: a hostssl rule simply does not match an unencrypted
connection, so the search ran off the end of the file. The encryption
requirement is expressed as a matching condition, not as a separate
check.
The counterpart is hostnossl, which matches only unencrypted
connections — occasionally useful for giving plain local traffic a
different authentication method, and more often useful as a rule to
grep for and delete.
Task 8 — The audit query
docker exec -u postgres rbpg-lab01 psql -X -c "
SELECT a.pid, a.usename, a.client_addr, s.ssl, s.version
FROM pg_stat_activity a
JOIN pg_stat_ssl s USING (pid)
WHERE a.backend_type = 'client backend';" | tee -a "$LAB/enforcement.txt"
$ psql -X -c "SELECT a.pid, a.usename, a.client_addr, s.ssl, s.version FROM pg_stat_activity a JOIN pg_stat_ssl s USING (pid) WHERE a.backend_type = 'client backend';" pid | usename | client_addr | ssl | version
------+----------+-------------+-----+---------
8962 | postgres | | f |
(1 row)This is the query to run on a server you have inherited. Any row with
ssl = f and a non-null client_addr is a network connection
carrying credentials and data in clear text. A null client_addr means
a Unix socket connection, where TLS is neither used nor needed — the
traffic never leaves the machine.
Run it repeatedly over a working day rather than once. Connection pools recycle, batch jobs connect on a schedule, and the badly configured client is rarely the one connected when you happen to look.
Task 9 — Authenticate by certificate, with no password
A client certificate can replace the password entirely.
docker exec rbpg-lab01 bash -c '
cd /etc/postgresql/18/main/tls
openssl req -new -nodes -newkey rsa:2048 -keyout client.key -out client.csr \
-subj "/CN=tlsuser"
printf "extendedKeyUsage=clientAuth\n" > client.ext
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
-out client.crt -days 825 -extfile client.ext
mkdir -p /root/tlsclient && cp client.crt client.key /root/tlsclient/
chmod 0600 /root/tlsclient/client.key
'
docker exec rbpg-lab01 sed -i \
's|^hostssl all all 172.26.0.0/16 scram-sha-256 # lab05|hostssl all all 172.26.0.0/16 cert clientcert=verify-full # lab05|' \
/etc/postgresql/18/main/pg_hba.conf
docker exec -u postgres rbpg-lab01 psql -X -c "SELECT pg_reload_conf();"
The certificate’s common name is tlsuser, matching the role name. That
is what cert authentication compares, unless a map= sends it through
pg_ident.conf as in Lab 4.
# Password alone, with a valid TLS session:
docker exec -e PGPASSWORD=lab05-not-a-real-secret rbpg-lab01 \
psql -X "host=pg.lab.internal user=tlsuser dbname=postgres sslmode=verify-full sslrootcert=/root/ca.crt" \
-c "SELECT 1;"
# Client certificate, and no password supplied at all:
docker exec rbpg-lab01 \
psql -X "host=pg.lab.internal user=tlsuser dbname=postgres sslmode=verify-full sslrootcert=/root/ca.crt sslcert=/root/tlsclient/client.crt sslkey=/root/tlsclient/client.key" \
-c "SELECT current_user, ssl, version, client_dn
FROM pg_stat_ssl JOIN pg_stat_activity USING (pid) WHERE pid = pg_backend_pid();"
$ one connection with a password only, one with a client certificate onlypsql: error: connection to server at "pg.lab.internal" (172.26.0.4), port 5432 failed: FATAL: connection requires a valid client certificate
current_user | ssl | version | client_dn
--------------+-----+---------+-------------
tlsuser | t | TLSv1.3 | /CN=tlsuser
(1 row)client_dn is now populated. That column is empty for every
password-authenticated session, which makes it a clean way to audit
which connections used a certificate.
Validation
test -s "$LAB/tls-baseline.txt" && echo "OK tls-baseline"
test -s "$LAB/sslmode-ladder.txt" && echo "OK sslmode-ladder"
test -s "$LAB/verification-gap.txt" && echo "OK verification-gap"
test -s "$LAB/enforcement.txt" && echo "OK enforcement"
grep -q "snakeoil" "$LAB/tls-baseline.txt" && echo "OK default certificate identified"
grep -c "TLSv1.3" "$LAB/sslmode-ladder.txt" # expect 4 of the 6 modes
grep -q "does not match host name" "$LAB/verification-gap.txt" && echo "OK verify-full gap shown"
grep -q "no pg_hba.conf entry" "$LAB/enforcement.txt" && echo "OK hostssl enforcement shown"
Questions to answer without looking anything up:
- A cluster reports
SHOW sslason. What have you learned, and what have you not? - What is the difference between
requireandverify-ca? Betweenverify-caandverify-full? - A client connects by IP address with
verify-fulland fails, but succeeds by DNS name. What is wrong with the certificate? - Your renewal job replaced the certificate and reloaded, and the log says “SSL configuration was not reloaded”. Which certificate are clients being served, and when will you find out?
- Which single change makes it impossible for a client to connect unencrypted, and which file is it in?
Expected Outcome
You have established that TLS was already on and worth very little,
replaced the certificate with one a client can verify, seen all six
sslmode values against one server, and moved the encryption decision
from the client to the server.
The two checks to carry away:
-- Which sessions are unencrypted over the network right now?
SELECT a.pid, a.usename, a.client_addr, s.ssl, s.version, s.client_dn
FROM pg_stat_activity a JOIN pg_stat_ssl s USING (pid)
WHERE a.backend_type = 'client backend' AND a.client_addr IS NOT NULL AND NOT s.ssl;
-- Can a client connect unencrypted at all?
SELECT rule_number, type, database, user_name, address, auth_method
FROM pg_hba_file_rules WHERE type IN ('host','hostnossl') ORDER BY rule_number;
The second is the one that matters. Any host rule reachable from the
network is a rule that will accept a plain connection.
Troubleshooting
The server refuses to start after installing the certificate.
Almost always the key permissions. PostgreSQL requires the private key
to be 0600 (or 0640 with group ownership) and owned by the user the
server runs as; it refuses to start otherwise and says so:
private key file "..." has group or world access. Task 4 sets the
permissions wrong deliberately so you meet this message here rather than
during a certificate renewal.
SSL configuration was not reloaded. A reload found the new TLS
material unusable and kept the context it already had. The server is
still serving with the old certificate. This is a warning that reads
like an error, and the important part is that nothing changed — check
the path, the permissions and the file’s validity, then reload again.
sslmode=verify-full fails with server certificate for "X" does not match host name "Y". That is the check working. verify-full
compares the name you connected to against the certificate’s subject
alternative names. Connect by the name in the certificate, or reissue
the certificate for the name you use.
sslmode=verify-ca fails with root certificate file ... does not exist. The client needs the CA certificate, and by default looks in
~/.postgresql/root.crt. Pass sslrootcert= explicitly rather than
relying on that path.
A client still connects unencrypted after Task 7. A host rule
earlier in the file matched it. host accepts encrypted and
unencrypted clients; only hostssl requires TLS. Check evaluation order
with pg_hba_file_rules.
pg_stat_ssl shows ssl = f for a session you expected to be
encrypted. Unix-socket connections are local and are never TLS. Filter
on client_addr IS NOT NULL, as the audit query in Task 8 does.
Cleanup
docker exec rbpg-lab01 sed -i '/# lab05/d' /etc/postgresql/18/main/pg_hba.conf
docker exec rbpg-lab01 rm -rf /etc/postgresql/18/main/tls /root/tlsclient /root/ca.crt
# /etc/hosts inside a container is a bind mount, so `sed -i` fails with
# "Device or resource busy" — it renames a temporary file over the target.
# Rewrite the contents in place instead.
docker exec rbpg-lab01 bash -c \
'grep -v -e pg.lab.internal -e db.wrong.name /etc/hosts > /tmp/hosts.new \
&& cat /tmp/hosts.new > /etc/hosts && rm -f /tmp/hosts.new'
docker exec -i -u postgres rbpg-lab01 psql -X <<'SQL'
ALTER SYSTEM RESET ssl_cert_file;
ALTER SYSTEM RESET ssl_key_file;
ALTER SYSTEM RESET ssl_ca_file;
ALTER SYSTEM RESET listen_addresses;
DROP ROLE IF EXISTS tlsuser;
SQL
docker exec rbpg-lab01 pg_ctlcluster 18 main restart
docker exec -u postgres rbpg-lab01 psql -X -c \
"SELECT name, setting FROM pg_settings WHERE name IN ('ssl','ssl_cert_file','listen_addresses');"
The cluster should be back to ssl = on with the snakeoil certificate
and listen_addresses = localhost — which, having done this lab, you
now know is a server with encryption available and identity unproven.
Production notes
sslmode=requireis the setting that reads as safe and is not. It encrypts and authenticates nothing — any certificate, from anyone, including one presented by whatever is between you and the server.verify-fullis the only value that checks both the chain and the name.sslmode=prefer— the libpq default — falls back to cleartext with no error, no warning and no log entry when the server declines TLS. A client using the default has not asked for encryption; it has expressed a preference.- Move the decision to the server. A
hostsslrule with no reachablehostrule is enforcement; a well-configured client is a convention that survives until somebody writes a new connection string. - Run the
pg_stat_sslaudit query on a schedule, not once. It is the only thing that answers “is anything talking to this cluster in cleartext right now” and the answer changes whenever an application is deployed. - Certificate expiry is an availability event for a cluster with
hostsslrules. Monitor the expiry date, and rehearse the renewal — the failed-reload behaviour above is what a botched renewal looks like.
What You Learned
- TLS being on says nothing about TLS being useful. The cluster started with a self-signed certificate no client could verify.
- The private key permissions are a startup precondition, not a recommendation. Wrong mode, no server.
- A failed TLS reload keeps the old context and warns rather than failing — so the server carries on with the certificate you thought you had replaced.
- The six
sslmodevalues are four different security postures.disableandallowandpreferdo not require encryption;requireencrypts without authenticating;verify-cachecks the chain; onlyverify-fullalso checks the name. verify-fullis what turns encryption into authentication, and the hostname mismatch it produces is the check working.- Only the server can enforce.
hostsslrefuses a plaintext client withno encryptionin the message; ahostrule accepts both.