Skip to content

Commit 8be39ea

Browse files
committed
Merge branch 'develop' into fix/ext-custom-scripts-salt
2 parents 157047c + 9d042fc commit 8be39ea

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+3038
-582
lines changed

.github/workflows/check-shellscripts.yml

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
name: Check shell scripts
22

33
on:
4-
push:
5-
branches:
6-
- develop
74
pull_request:
5+
merge_group:
86
workflow_dispatch:
97

108
concurrency:
11-
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
12-
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
9+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.event.merge_group.head_ref || github.ref }}
10+
cancel-in-progress: ${{ github.event_name == 'pull_request' || github.event_name == 'merge_group' }}
1311

1412
permissions:
1513
contents: read

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ name: Check merge requirements
22

33
on:
44
pull_request:
5+
merge_group:
56

67
permissions:
78
contents: read

.github/workflows/nix-build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ name: Nix CI
33
on:
44
push:
55
branches:
6-
- develop
76
- release/*
87
pull_request:
8+
merge_group:
99
workflow_dispatch:
1010

1111
permissions:

.github/workflows/publish-migrations-staging.yml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
name: Release Migrations - Staging
22

33
on:
4-
push:
5-
branches:
6-
- develop
4+
merge_group:
75
workflow_dispatch:
86

97
jobs:
10-
build:
8+
release-migrations-staging:
119
runs-on: blacksmith-2vcpu-ubuntu-2404-arm
1210
timeout-minutes: 15
1311
permissions:

Dockerfile-15

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ ARG pg_repack_release=1.4.8
3030
ARG vault_release=0.2.8
3131
ARG groonga_release=12.0.8
3232
ARG pgroonga_release=2.4.0
33-
ARG wrappers_release=0.5.4
33+
ARG wrappers_release=0.5.6
3434
ARG hypopg_release=1.3.1
3535
ARG pgvector_release=0.4.0
3636
ARG pg_tle_release=1.3.2

Dockerfile-17

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ ARG pg_repack_release=1.4.8
3131
ARG vault_release=0.2.8
3232
ARG groonga_release=12.0.8
3333
ARG pgroonga_release=2.4.0
34-
ARG wrappers_release=0.5.4
34+
ARG wrappers_release=0.5.6
3535
ARG hypopg_release=1.3.1
3636
ARG pgvector_release=0.4.0
3737
ARG pg_tle_release=1.3.2

Dockerfile-orioledb-17

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ ARG pg_repack_release=1.4.8
3131
ARG vault_release=0.2.8
3232
ARG groonga_release=12.0.8
3333
ARG pgroonga_release=2.4.0
34-
ARG wrappers_release=0.5.4
34+
ARG wrappers_release=0.5.6
3535
ARG hypopg_release=1.3.1
3636
ARG pgvector_release=0.4.0
3737
ARG pg_tle_release=1.3.2

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,4 +339,4 @@ TODO: find way to automate this
339339

340340
We are building the features of Firebase using enterprise-grade, open source products. We support existing communities wherever possible, and if the products don’t exist we build them and open source them ourselves.
341341

342-
[![New Sponsor](https://user-images.githubusercontent.com/10214025/90518111-e74bbb00-e198-11ea-8f88-c9e3c1aa4b5b.png)](https://github.com/sponsors/supabase)
342+
[![New Sponsor](https://user-images.githubusercontent.com/10214025/90518111-e74bbb00-e198-11ea-8f88-c9e3c1aa4b5b.png)](https://github.com/sponsors/supabase)

ansible/files/fail2ban_check.py

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
import subprocess
2+
import sys
3+
import os
4+
import re
5+
6+
7+
# Expected fail2ban configuration
8+
expected_fail2ban_config = {
9+
"jail": {
10+
"name": "postgresql",
11+
"enabled": True,
12+
"logpath": "/var/log/postgresql/auth-failures.csv",
13+
"filter": "postgresql",
14+
"port": "5432",
15+
"protocol": "tcp",
16+
"maxretry": 3,
17+
"ignoreip": ["192.168.0.0/16", "172.17.1.0/20"],
18+
"backend": "auto",
19+
},
20+
"filter": {
21+
"failregex": r'^.*,.*,.*,.*,"<HOST>:.*password authentication failed for user.*$',
22+
"ignoreregex": r'^.*,.*,.*,.*,"127\.0\.0\.1.*password authentication failed for user.*$',
23+
# Additional ignoreregex patterns added by Ansible (setup-fail2ban.yml lines 55-62)
24+
"custom_ignoreregex": [
25+
r'^.*,.*,.*,.*,"<HOST>:.*password authentication failed for user ""supabase_admin".*$',
26+
r'^.*,.*,.*,.*,"<HOST>:.*password authentication failed for user ""supabase_auth_admin".*$',
27+
r'^.*,.*,.*,.*,"<HOST>:.*password authentication failed for user ""supabase_storage_admin".*$',
28+
r'^.*,.*,.*,.*,"<HOST>:.*password authentication failed for user ""authenticator".*$',
29+
r'^.*,.*,.*,.*,"<HOST>:.*password authentication failed for user ""pgbouncer".*$',
30+
],
31+
},
32+
}
33+
34+
35+
def run_command(command):
36+
"""Run a shell command and return the output."""
37+
try:
38+
process = subprocess.Popen(
39+
command,
40+
shell=True,
41+
stdout=subprocess.PIPE,
42+
stderr=subprocess.PIPE,
43+
text=True,
44+
)
45+
stdout, stderr = process.communicate()
46+
return {
47+
"returncode": process.returncode,
48+
"stdout": stdout,
49+
"stderr": stderr,
50+
"succeeded": process.returncode == 0,
51+
}
52+
except Exception as e:
53+
print(f"Error running command '{command}': {e}")
54+
sys.exit(1)
55+
56+
57+
def check_fail2ban_config_syntax():
58+
"""Validate fail2ban configuration syntax using fail2ban-client -d."""
59+
print("Checking fail2ban configuration syntax...")
60+
61+
result = run_command("fail2ban-client -d")
62+
63+
if not result["succeeded"]:
64+
print("fail2ban configuration syntax check failed:")
65+
print(result["stderr"])
66+
sys.exit(1)
67+
68+
# Check that postgresql jail appears in the dump
69+
if "postgresql" not in result["stdout"]:
70+
print("postgresql jail not found in fail2ban configuration dump")
71+
sys.exit(1)
72+
73+
print("✓ fail2ban configuration syntax is valid")
74+
75+
76+
def check_fail2ban_filter_regex():
77+
"""Test fail2ban filter regex against the log file."""
78+
print("Testing fail2ban filter regex...")
79+
80+
logpath = expected_fail2ban_config["jail"]["logpath"]
81+
filter_path = "/etc/fail2ban/filter.d/postgresql.conf"
82+
83+
# Check if log file exists
84+
if not os.path.exists(logpath):
85+
print(f"Log file {logpath} does not exist")
86+
print(
87+
"Note: This is expected if PostgreSQL hasn't run yet. Skipping regex test."
88+
)
89+
return
90+
91+
# Check if filter file exists
92+
if not os.path.exists(filter_path):
93+
print(f"Filter file {filter_path} does not exist")
94+
sys.exit(1)
95+
96+
# Run fail2ban-regex to test the filter
97+
result = run_command(f"fail2ban-regex {logpath} {filter_path}")
98+
99+
if not result["succeeded"]:
100+
print("fail2ban-regex test failed:")
101+
print(result["stderr"])
102+
sys.exit(1)
103+
104+
print("✓ fail2ban filter regex test passed")
105+
106+
107+
def check_fail2ban_jail_config():
108+
"""Validate jail configuration file contents."""
109+
print("Checking fail2ban jail configuration...")
110+
111+
jail_config_path = "/etc/fail2ban/jail.d/postgresql.conf"
112+
113+
if not os.path.exists(jail_config_path):
114+
print(f"Jail configuration file {jail_config_path} does not exist")
115+
sys.exit(1)
116+
117+
with open(jail_config_path, "r") as f:
118+
jail_content = f.read()
119+
120+
expected_jail = expected_fail2ban_config["jail"]
121+
122+
# Check each expected configuration value
123+
checks = [
124+
(f"enabled = {str(expected_jail['enabled']).lower()}", "enabled setting"),
125+
(f"port = {expected_jail['port']}", "port setting"),
126+
(f"protocol = {expected_jail['protocol']}", "protocol setting"),
127+
(f"filter = {expected_jail['filter']}", "filter setting"),
128+
(f"logpath = {expected_jail['logpath']}", "logpath setting"),
129+
(f"maxretry = {expected_jail['maxretry']}", "maxretry setting"),
130+
(f"backend = {expected_jail['backend']}", "backend setting"),
131+
]
132+
133+
for expected_line, description in checks:
134+
if expected_line not in jail_content:
135+
print(f"Missing or incorrect {description} in {jail_config_path}")
136+
print(f"Expected: {expected_line}")
137+
sys.exit(1)
138+
139+
# Check ignoreip
140+
for ip_range in expected_jail["ignoreip"]:
141+
if ip_range not in jail_content:
142+
print(f"Missing ignoreip range {ip_range} in {jail_config_path}")
143+
sys.exit(1)
144+
145+
print("✓ fail2ban jail configuration is correct")
146+
147+
148+
def check_fail2ban_filter_config():
149+
"""Validate filter configuration file contents."""
150+
print("Checking fail2ban filter configuration...")
151+
152+
filter_config_path = "/etc/fail2ban/filter.d/postgresql.conf"
153+
154+
if not os.path.exists(filter_config_path):
155+
print(f"Filter configuration file {filter_config_path} does not exist")
156+
sys.exit(1)
157+
158+
with open(filter_config_path, "r") as f:
159+
filter_content = f.read()
160+
161+
expected_filter = expected_fail2ban_config["filter"]
162+
163+
# Check failregex
164+
if expected_filter["failregex"] not in filter_content:
165+
print(f"Missing or incorrect failregex in {filter_config_path}")
166+
print(f"Expected: {expected_filter['failregex']}")
167+
sys.exit(1)
168+
169+
# Check ignoreregex
170+
if expected_filter["ignoreregex"] not in filter_content:
171+
print(f"Missing or incorrect ignoreregex in {filter_config_path}")
172+
print(f"Expected: {expected_filter['ignoreregex']}")
173+
sys.exit(1)
174+
175+
# Check custom ignoreregex patterns for Supabase users
176+
for custom_pattern in expected_filter["custom_ignoreregex"]:
177+
if custom_pattern not in filter_content:
178+
print(f"Missing custom ignoreregex pattern in {filter_config_path}")
179+
print(f"Expected: {custom_pattern}")
180+
sys.exit(1)
181+
182+
print("✓ fail2ban filter configuration is correct")
183+
184+
185+
def check_fail2ban_jail_runtime():
186+
"""Validate fail2ban jail is running and monitoring the correct file."""
187+
print("Checking fail2ban jail runtime status...")
188+
189+
# Run fail2ban-client status postgresql
190+
result = run_command("fail2ban-client status postgresql")
191+
192+
if not result["succeeded"]:
193+
print("Failed to get fail2ban postgresql jail status:")
194+
print(result["stderr"])
195+
sys.exit(1)
196+
197+
output = result["stdout"]
198+
199+
# Parse the output
200+
# Expected format:
201+
# Status for the jail: postgresql
202+
# |- Filter
203+
# | |- Currently failed: 0
204+
# | |- Total failed: X
205+
# | `- File list: /var/log/postgresql/auth-failures.csv
206+
207+
# Check jail name
208+
if "Status for the jail: postgresql" not in output:
209+
print("postgresql jail is not active")
210+
print(output)
211+
sys.exit(1)
212+
213+
# Check file list
214+
expected_logpath = expected_fail2ban_config["jail"]["logpath"]
215+
if expected_logpath not in output:
216+
print(
217+
f"postgresql jail is not monitoring the expected log file: {expected_logpath}"
218+
)
219+
print(output)
220+
sys.exit(1)
221+
222+
# Extract and display some stats
223+
match = re.search(r"Currently failed:\s+(\d+)", output)
224+
if match:
225+
currently_failed = match.group(1)
226+
print(f" Currently failed IPs: {currently_failed}")
227+
228+
match = re.search(r"Total failed:\s+(\d+)", output)
229+
if match:
230+
total_failed = match.group(1)
231+
print(f" Total failed attempts: {total_failed}")
232+
233+
print("✓ fail2ban postgresql jail is active and monitoring correctly")
234+
235+
236+
def main():
237+
print("=" * 60)
238+
print("Supabase Postgres fail2ban Configuration Checker")
239+
print("=" * 60)
240+
241+
# Static validation (doesn't require fail2ban to be running)
242+
check_fail2ban_jail_config()
243+
check_fail2ban_filter_config()
244+
check_fail2ban_config_syntax()
245+
check_fail2ban_filter_regex()
246+
247+
# Runtime validation (requires fail2ban to be running)
248+
# This should be called when fail2ban service is started
249+
check_fail2ban_jail_runtime()
250+
251+
print("=" * 60)
252+
print("All fail2ban configuration checks passed!")
253+
print("=" * 60)
254+
255+
256+
if __name__ == "__main__":
257+
main()

ansible/files/fail2ban_config/jail-postgresql.conf.j2

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ filter = postgresql
66
logpath = /var/log/postgresql/auth-failures.csv
77
maxretry = 3
88
ignoreip = 192.168.0.0/16 172.17.1.0/20
9+
backend = auto

0 commit comments

Comments
 (0)