Skip to content

Commit d32e449

Browse files
committed
Avoid using hostssl without certs
The default value is a dangerous one as a drop-in replacement for the official postgres image. It produces these errors: ``` LOG: hostssl requires SSL to be turned on HINT: Set ssl = on in postgresql.conf. CONTEXT: line 10 of configuration file "/etc/postgres/pg_hba.conf" FATAL: could not load pg_hba.conf LOG: database system is shut down ``` To avoid that problem, no `hostssl` entries are created if there are no certs.
1 parent c398fcf commit d32e449

File tree

3 files changed

+101
-28
lines changed

3 files changed

+101
-28
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ Method required to authenticate clients that connect from WAN.
8585

8686
#### `WAN_CONNECTION`
8787

88-
Connection type allowed for WAN connections.
88+
Connection type allowed for WAN connections. If it is `hostssl`, it will only have effect when the required certs are received.
8989

9090
#### `WAN_DATABASES`
9191

autoconf-entrypoint

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -101,14 +101,15 @@ for interface in netifaces.interfaces():
101101
))
102102

103103
# Generate WAN auth configuration
104-
for user, db, cidr in product(WAN_USERS, WAN_DATABASES, WAN_CIDRS):
105-
hba_conf.append(WAN_HBA_TPL.format(
106-
connection=WAN_CONNECTION,
107-
db=db,
108-
user=user,
109-
cidr=cidr,
110-
meth=WAN_AUTH_METHOD,
111-
))
104+
if WAN_CONNECTION != "hostssl" or ssl_conf:
105+
for user, db, cidr in product(WAN_USERS, WAN_DATABASES, WAN_CIDRS):
106+
hba_conf.append(WAN_HBA_TPL.format(
107+
connection=WAN_CONNECTION,
108+
db=db,
109+
user=user,
110+
cidr=cidr,
111+
meth=WAN_AUTH_METHOD,
112+
))
112113

113114
# Write postgres configuration files
114115
with open(CONF_FILE, "w") as conf_file:

tests/test.py

Lines changed: 91 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from plumbum import FG, local
88
from plumbum.cmd import cat, docker
9+
from plumbum.commands.processes import ProcessExecutionError
910

1011
# Make sure all paths are relative to tests dir
1112
local.cwd.chdir(os.path.dirname(__file__))
@@ -16,7 +17,8 @@ class PostgresAutoconfCase(unittest.TestCase):
1617
"""Test behavior for this docker image"""
1718
def setUp(self):
1819
with local.cwd(local.cwd / ".."):
19-
local["./hooks/build"]()
20+
print("Building image")
21+
local["./hooks/build"] & FG
2022
docker("network", "create", "lan")
2123
docker("network", "create", "wan")
2224
self.version = os.environ["DOCKER_TAG"]
@@ -32,19 +34,19 @@ def tearDown(self):
3234
try:
3335
print("Postgres container logs:")
3436
docker["container", "logs", self.postgres_container] & FG
35-
docker("container", "stop", self.postgres_container)
36-
docker("container", "rm", self.postgres_container)
37+
docker["container", "stop", self.postgres_container] & FG
38+
docker["container", "rm", self.postgres_container] & FG
3739
except AttributeError:
3840
pass # No postgres daemon
3941
docker("network", "rm", "lan", "wan")
4042
return super().tearDown()
4143

42-
def generate_certs(self):
44+
def _generate_certs(self):
4345
"""Generate certificates for testing the image."""
4446
certgen("example.com", "test_user")
4547

46-
def check_cert_config(self):
47-
"""Check that the cert config is OK."""
48+
def _check_local_connection(self):
49+
"""Check that local connection works fine."""
4850
# The 1st test could fail while postgres boots
4951
for attempt in range(10):
5052
try:
@@ -65,31 +67,40 @@ def check_cert_config(self):
6567
raise
6668
else:
6769
continue
68-
run = docker[
70+
71+
def _check_password_auth(self, host=None):
72+
"""Test connection with password auth work fine."""
73+
if not host:
74+
# Connect via LAN by default
75+
host = self.postgres_container[:12]
76+
self.assertEqual("1\n", docker(
6977
"container", "run",
70-
]
71-
# Test LAN connection with password auth works fine
72-
self.assertEqual("1\n", run(
7378
"--network", "lan",
7479
"-e", "PGDATABASE=test_db",
7580
"-e", "PGPASSWORD=test_password",
7681
"-e", "PGSSLMODE=disable",
7782
"-e", "PGUSER=test_user",
7883
self.image, "psql",
79-
"--host", self.postgres_container[:12],
84+
"--host", host,
8085
"--command", "SELECT 1",
8186
"--no-align",
8287
"--tuples-only",
8388
))
84-
# Attach a new network to mock a WAN connection
89+
90+
def _connect_wan_network(self, alias="example.com"):
91+
"""Bind a new network, to imitate WAN connections."""
8592
docker(
8693
"network", "connect",
87-
"--alias", "example.com",
94+
"--alias", alias,
8895
"wan",
8996
self.postgres_container,
9097
)
91-
# Test WAN connection with cert auth works fine
92-
self.assertEqual("1\n", run(
98+
99+
def _check_cert_auth(self):
100+
"""Test connection with cert auth work fine."""
101+
# Test connection with cert auth works fine
102+
self.assertEqual("1\n", docker(
103+
"container", "run",
93104
"--network", "wan",
94105
"-e", "PGDATABASE=test_db",
95106
"-e", "PGSSLCERT=/certs/client.cert.pem",
@@ -109,7 +120,7 @@ def test_server_certs_var(self):
109120
"""Test server enables cert authentication through env vars."""
110121
with local.tempdir() as tdir:
111122
with local.cwd(tdir):
112-
self.generate_certs()
123+
self._generate_certs()
113124
certs_var = {name: cat(name) for name in self.cert_files}
114125
self.postgres_container = docker(
115126
"container", "run",
@@ -120,13 +131,16 @@ def test_server_certs_var(self):
120131
"-e", "POSTGRES_USER=test_user",
121132
self.image,
122133
).strip()
123-
self.check_cert_config()
134+
self._check_local_connection()
135+
self._check_password_auth()
136+
self._connect_wan_network()
137+
self._check_cert_auth()
124138

125139
def test_server_certs_mount(self):
126140
"""Test server enables cert authentication through file mounts."""
127141
with local.tempdir() as tdir:
128142
with local.cwd(tdir):
129-
self.generate_certs()
143+
self._generate_certs()
130144
cert_vols = [
131145
"-v{0}/{1}:/etc/postgres/{1}".format(local.cwd, cert)
132146
for cert in [
@@ -144,7 +158,65 @@ def test_server_certs_mount(self):
144158
*cert_vols,
145159
self.image,
146160
).strip()
147-
self.check_cert_config()
161+
self._check_local_connection()
162+
self._check_password_auth()
163+
self._connect_wan_network()
164+
self._check_cert_auth()
165+
166+
def test_no_certs_lan(self):
167+
"""Normal configuration without certs works fine."""
168+
self.postgres_container = docker(
169+
"container", "run", "-d",
170+
"--network", "lan",
171+
"-e", "POSTGRES_DB=test_db",
172+
"-e", "POSTGRES_PASSWORD=test_password",
173+
"-e", "POSTGRES_USER=test_user",
174+
self.image,
175+
).strip()
176+
self._check_local_connection()
177+
self._check_password_auth()
178+
self._connect_wan_network()
179+
with self.assertRaises(ProcessExecutionError):
180+
self._check_password_auth("example.com")
181+
182+
def test_no_certs_wan(self):
183+
"""Unencrypted WAN access works (although this is dangerous)."""
184+
self.postgres_container = docker(
185+
"container", "run", "-d",
186+
"--network", "lan",
187+
"-e", "POSTGRES_DB=test_db",
188+
"-e", "POSTGRES_PASSWORD=test_password",
189+
"-e", "POSTGRES_USER=test_user",
190+
"-e", "WAN_AUTH_METHOD=md5",
191+
"-e", "WAN_CONNECTION=host",
192+
self.image,
193+
).strip()
194+
self._check_local_connection()
195+
self._check_password_auth()
196+
self._connect_wan_network()
197+
with self.assertRaises(ProcessExecutionError):
198+
self._check_password_auth("example.com")
199+
200+
def test_certs_falsy_lan(self):
201+
"""Configuration with falsy values for certs works fine."""
202+
self.postgres_container = docker(
203+
"container", "run", "-d",
204+
"--network", "lan",
205+
"-e", "POSTGRES_DB=test_db",
206+
"-e", "POSTGRES_PASSWORD=test_password",
207+
"-e", "POSTGRES_USER=test_user",
208+
"-e", "CERTS={}".format(json.dumps({
209+
"client.ca.cert.pem": False,
210+
"server.cert.pem": False,
211+
"server.key.pem": False,
212+
})),
213+
self.image,
214+
).strip()
215+
self._check_local_connection()
216+
self._check_password_auth()
217+
self._connect_wan_network()
218+
with self.assertRaises(ProcessExecutionError):
219+
self._check_password_auth("example.com")
148220

149221

150222
if __name__ == "__main__":

0 commit comments

Comments
 (0)