Skip to content

Commit d2ebbff

Browse files
[fix] Handle missing VPN_DOMAIN safely in OpenVPN startup #572
Avoid container failure when VPN_DOMAIN is unset by logging a warning and exiting gracefully. Fixes #572
1 parent 76cd872 commit d2ebbff

5 files changed

Lines changed: 62 additions & 18 deletions

File tree

images/common/init_command.sh

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@ elif [ "$MODULE_NAME" = 'freeradius' ]; then
2929
source docker-entrypoint.sh -X
3030
fi
3131
elif [ "$MODULE_NAME" = 'openvpn' ]; then
32-
if [[ -z "$VPN_DOMAIN" ]]; then exit; fi
32+
if [ -z "$VPN_DOMAIN" ]; then
33+
echo "WARNING: VPN_DOMAIN not set, skipping OpenVPN setup" >&2
34+
exit 0
35+
fi
3336
wait_nginx_services
3437
openvpn_preconfig
3538
openvpn_config

images/common/utils.sh

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#!/bin/sh
1+
#!/bin/bash
22

33
function init_conf {
44
default_psql_vars
@@ -86,7 +86,7 @@ function wait_nginx_services {
8686
set +e
8787
while :; do
8888
wget -qS ${DASHBOARD_INTERNAL}/admin/login/ 2>&1 | grep -q "200 OK"
89-
if [[ $? = "0" ]]; then
89+
if [ $? = "0" ]; then
9090
FAILURE=0
9191
echo "Connection with dashboard established."
9292
break
@@ -97,7 +97,7 @@ function wait_nginx_services {
9797
}
9898

9999
function ssl_http_behaviour {
100-
if [ "$NGINX_HTTP_ALLOW" == "True" ]; then
100+
if [ "$NGINX_HTTP_ALLOW" = "True" ]; then
101101
envsubst_create_config /etc/nginx/openwisp.template.conf http DOMAIN
102102
else
103103
envsubst </etc/nginx/openwisp.ssl.80.template.conf >/etc/nginx/conf.d/openwisp.http.conf
@@ -236,18 +236,25 @@ function openvpn_config_checksum {
236236
}
237237

238238
function openvpn_config_download {
239-
curl --silent --retry 10 --retry-delay 5 --retry-max-time 300\
240-
--insecure --output vpn.tar.gz \
241-
${API_INTERNAL}/controller/vpn/download-config/$UUID/?key=$KEY
239+
curl --silent --retry 10 --retry-delay 5 --retry-max-time 300 --insecure --output vpn.tar.gz \
240+
"${API_INTERNAL}/controller/vpn/download-config/$UUID/?key=$KEY"
242241
curl --silent --insecure --output checksum \
243-
${API_INTERNAL}/controller/vpn/checksum/$UUID/?key=$KEY
242+
"${API_INTERNAL}/controller/vpn/checksum/$UUID/?key=$KEY"
244243
tar xzf vpn.tar.gz
245244
chmod 600 *.pem
245+
# Prefer a newly extracted non-standard filename and normalize it.
246+
CONF_FILE=$(find . -maxdepth 1 -type f -name '*.conf' ! -name 'openvpn.conf' -print -quit)
247+
if [ -n "$CONF_FILE" ]; then
248+
mv -f -- "$CONF_FILE" openvpn.conf
249+
elif [ ! -f openvpn.conf ]; then
250+
echo "ERROR: no OpenVPN config file found after extraction" >&2
251+
return 1
252+
fi
246253
}
247254

248255
function crl_download {
249256
curl --silent --insecure --output revoked.crl \
250-
${DASHBOARD_INTERNAL}/admin/pki/ca/x509/ca/${CA_UUID}.crl
257+
"${DASHBOARD_INTERNAL}/admin/pki/ca/x509/ca/${CA_UUID}.crl"
251258
}
252259

253260
function init_send_network_topology {

images/openwisp_dashboard/openvpn.json

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@
1212
"comp_lzo": "no",
1313
"auth": "SHA1",
1414
"data_ciphers": [
15-
{
16-
"cipher": "AES-128-GCM",
17-
"optional": false
18-
},
19-
{
20-
"cipher": "none",
21-
"optional": false
22-
}
15+
{
16+
"cipher": "AES-128-GCM",
17+
"optional": false
18+
},
19+
{
20+
"cipher": "none",
21+
"optional": false
22+
}
2323
],
2424
"data_ciphers_fallback": "AES-128-GCM",
2525
"cipher": "AES-128-GCM",

images/openwisp_openvpn/supervisord.conf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pidfile=/supervisord.pid
1818
[program:openvpn]
1919
user=root
2020
directory=/
21-
command=/usr/sbin/openvpn --config %(ENV_VPN_NAME)s.conf
21+
command=/usr/sbin/openvpn --config openvpn.conf
2222
autostart=true
2323
autorestart=true
2424
stopsignal=INT

tests/runtests.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,39 @@ def test_create_prefix_users(self):
237237
except (urlerror.HTTPError, OSError, ConnectionResetError, ValueError) as error:
238238
self.fail(f"Cannot download PDF file: {error}")
239239

240+
def test_openvpn_config_whitespace_handling(self):
241+
"""Verify openvpn_config_download handles whitespace."""
242+
import shlex
243+
import tempfile
244+
245+
with tempfile.TemporaryDirectory() as tmpdir:
246+
r_root = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
247+
u_path = os.path.join(r_root, "images", "common", "utils.sh")
248+
script = (
249+
f"source {shlex.quote(u_path)}\n"
250+
"curl() { true; }\n"
251+
"tar() { touch 'my vpn with spaces.conf'; }\n"
252+
"chmod() { true; }\n"
253+
"UUID='x'; KEY='x'; API='http://x'\n"
254+
"openvpn_config_download\n"
255+
"if [ -f 'openvpn.conf' ]; then echo 'PASS'; fi\n"
256+
)
257+
script_path = os.path.join(tmpdir, "test_mock.sh")
258+
with open(script_path, "w") as sf:
259+
sf.write(script)
260+
res = subprocess.run(
261+
["bash", script_path],
262+
cwd=tmpdir,
263+
capture_output=True,
264+
text=True,
265+
)
266+
self.assertEqual(
267+
res.returncode,
268+
0,
269+
f"Mock script failed:\n{res.stdout}\n{res.stderr}",
270+
)
271+
self.assertIn("PASS", res.stdout)
272+
240273
def test_console_errors(self):
241274
url_list = [
242275
"/admin/",
@@ -492,5 +525,6 @@ def test_containers_down(self):
492525
if __name__ == "__main__":
493526
suite = unittest.TestSuite()
494527
suite.addTest(TestServices("test_topology_graph"))
528+
suite.addTest(TestServices("test_openvpn_config_whitespace_handling"))
495529
runner = unittest.TextTestRunner(verbosity=2)
496530
runner.run(suite)

0 commit comments

Comments
 (0)