diff --git a/net/shadowsocks/Makefile b/net/shadowsocks/Makefile
index 34945a08ca..15f9cdfc7f 100644
--- a/net/shadowsocks/Makefile
+++ b/net/shadowsocks/Makefile
@@ -1,5 +1,5 @@
PLUGIN_NAME= shadowsocks
-PLUGIN_VERSION= 1.3
+PLUGIN_VERSION= 1.4
PLUGIN_COMMENT= Secure socks5 proxy
PLUGIN_DEPENDS= shadowsocks-rust
PLUGIN_MAINTAINER= m.muenz@gmail.com
diff --git a/net/shadowsocks/src/opnsense/mvc/app/controllers/OPNsense/Shadowsocks/forms/general.xml b/net/shadowsocks/src/opnsense/mvc/app/controllers/OPNsense/Shadowsocks/forms/general.xml
index 185decc5aa..f5edd92f0e 100644
--- a/net/shadowsocks/src/opnsense/mvc/app/controllers/OPNsense/Shadowsocks/forms/general.xml
+++ b/net/shadowsocks/src/opnsense/mvc/app/controllers/OPNsense/Shadowsocks/forms/general.xml
@@ -54,4 +54,23 @@
checkbox
Allow IP fragmentation on the outbound UDP socket.
+
+
+ general.outbound_blocklist_enabled
+
+ checkbox
+ Generate a Shadowsocks server ACL using [outbound_block_list]. This blocks destinations after sslocal has proxied traffic into this server. Add only the destinations you want to block below.
+
+
+ general.outbound_blocklist_ipv4
+
+ text
+ Comma separated IPv4 addresses or CIDR networks to block, for example 172.28.0.0/16,10.1.0.0/16,10.152.183.0/24.
+
+
+ general.outbound_blocklist_ipv6
+
+ text
+ Comma separated IPv6 addresses or CIDR networks to block, for example fd01::/108,fd98::/108,2a02:8010:6538::/48.
+
diff --git a/net/shadowsocks/src/opnsense/mvc/app/models/OPNsense/Shadowsocks/General.xml b/net/shadowsocks/src/opnsense/mvc/app/models/OPNsense/Shadowsocks/General.xml
index 6819ff0986..700e594579 100644
--- a/net/shadowsocks/src/opnsense/mvc/app/models/OPNsense/Shadowsocks/General.xml
+++ b/net/shadowsocks/src/opnsense/mvc/app/models/OPNsense/Shadowsocks/General.xml
@@ -1,7 +1,7 @@
//OPNsense/shadowsocks/general
Shadowsocks configuration
- 1.0.2
+ 1.0.3
0
@@ -71,5 +71,19 @@
0
Y
+
+ 0
+ Y
+
+
+ N
+ /^([0-9\.\/ ]*?,)*([0-9\.\/ ]*)$/
+ Please provide a comma separated list of IPv4 addresses or CIDR networks.
+
+
+ N
+ /^([a-fA-F0-9\.:\[\]\/ ]*?,)*([a-fA-F0-9\.:\[\]\/ ]*)$/
+ Please provide a comma separated list of IPv6 addresses or CIDR networks.
+
diff --git a/net/shadowsocks/src/opnsense/service/templates/OPNsense/Shadowsocks/+TARGETS b/net/shadowsocks/src/opnsense/service/templates/OPNsense/Shadowsocks/+TARGETS
index bb1f9c5e44..40f7119583 100644
--- a/net/shadowsocks/src/opnsense/service/templates/OPNsense/Shadowsocks/+TARGETS
+++ b/net/shadowsocks/src/opnsense/service/templates/OPNsense/Shadowsocks/+TARGETS
@@ -2,3 +2,4 @@ ssserver:/etc/rc.conf.d/ssserver_rust
config.json:/usr/local/etc/shadowsocks-rust/config.json
sslocal:/etc/rc.conf.d/sslocal_rust
local.json:/usr/local/etc/shadowsocks-rust/local.json
+server.acl:/usr/local/etc/shadowsocks-rust/server.acl
diff --git a/net/shadowsocks/src/opnsense/service/templates/OPNsense/Shadowsocks/config.json b/net/shadowsocks/src/opnsense/service/templates/OPNsense/Shadowsocks/config.json
index 09062d237f..a5fcc0438f 100644
--- a/net/shadowsocks/src/opnsense/service/templates/OPNsense/Shadowsocks/config.json
+++ b/net/shadowsocks/src/opnsense/service/templates/OPNsense/Shadowsocks/config.json
@@ -7,6 +7,7 @@
"timeout":{{ OPNsense.shadowsocks.general.timeout }},
"mode":"{{ OPNsense.shadowsocks.general.tcpudpmode }}",
"method":"{{ OPNsense.shadowsocks.general.cipher }}",
- "outbound_udp_allow_fragmentation":{{ "true" if OPNsense.shadowsocks.general.fragmentation == '1' else "false"}}
+ "outbound_udp_allow_fragmentation":{{ "true" if OPNsense.shadowsocks.general.fragmentation == '1' else "false"}}{% if helpers.exists('OPNsense.shadowsocks.general.outbound_blocklist_enabled') and OPNsense.shadowsocks.general.outbound_blocklist_enabled == '1' %},
+ "acl":"/usr/local/etc/shadowsocks-rust/server.acl"{% endif %}
}
{% endif %}
diff --git a/net/shadowsocks/src/opnsense/service/templates/OPNsense/Shadowsocks/server.acl b/net/shadowsocks/src/opnsense/service/templates/OPNsense/Shadowsocks/server.acl
new file mode 100644
index 0000000000..0388ccf2a8
--- /dev/null
+++ b/net/shadowsocks/src/opnsense/service/templates/OPNsense/Shadowsocks/server.acl
@@ -0,0 +1,31 @@
+{% if helpers.exists('OPNsense.shadowsocks.general.enabled') and OPNsense.shadowsocks.general.enabled == '1' and helpers.exists('OPNsense.shadowsocks.general.outbound_blocklist_enabled') and OPNsense.shadowsocks.general.outbound_blocklist_enabled == '1' %}
+# Generated by OPNsense os-shadowsocks.
+# Applies to ssserver only. sslocal ACLs route/proxy/bypass; they do not block.
+# Only user-specified destinations are emitted here; no default private/reserved ranges.
+
+[accept_all]
+
+[outbound_allow_all]
+
+[outbound_block_list]
+{% if helpers.exists('OPNsense.shadowsocks.general.outbound_blocklist_ipv4') and OPNsense.shadowsocks.general.outbound_blocklist_ipv4 != '' %}
+
+# User IPv4 blocklist
+{% for item in OPNsense.shadowsocks.general.outbound_blocklist_ipv4.split(',') %}
+{% set entry = item.strip() %}
+{% if entry != '' %}
+{{ entry }}
+{% endif %}
+{% endfor %}
+{% endif %}
+{% if helpers.exists('OPNsense.shadowsocks.general.outbound_blocklist_ipv6') and OPNsense.shadowsocks.general.outbound_blocklist_ipv6 != '' %}
+
+# User IPv6 blocklist
+{% for item in OPNsense.shadowsocks.general.outbound_blocklist_ipv6.split(',') %}
+{% set entry = item.strip() %}
+{% if entry != '' %}
+{{ entry }}
+{% endif %}
+{% endfor %}
+{% endif %}
+{% endif %}