Skip to content

Commit 411d5cb

Browse files
committed
feat: add firewall reach rules
This can be usefull to: - redirect a public to an internal port (NethVoice proxy) - forward a public port to a remote host - create complex rules for the default zone
1 parent b50a01b commit 411d5cb

File tree

7 files changed

+132
-0
lines changed

7 files changed

+132
-0
lines changed

core/imageroot/usr/local/agent/pypkg/agent/__init__.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,37 @@ def remove_custom_zone(name):
485485
)
486486
return response['exit_code'] == 0
487487

488+
def add_rich_rules(rich_rules):
489+
"""
490+
Apply an array of firewall rich rules on the node using firewall-cmd.
491+
Each element of `rich_rules` should be a complete rich-rule string as
492+
accepted by `--add-rich-rule`. Example:
493+
'rule family=ipv4 forward-port port=5060 protocol=udp to-port=5060 to-addr=192.168.1.100'
494+
"""
495+
node_id = os.environ['NODE_ID']
496+
data = {'rich_rules': rich_rules}
497+
response = agent.tasks.run(
498+
agent_id=f'node/{node_id}',
499+
action='add-rich-rules',
500+
data=data
501+
)
502+
return response['exit_code'] == 0
503+
504+
def remove_rich_rules(rich_rules):
505+
"""
506+
Remove an array of firewall rich rules on the node using firewall-cmd.
507+
Each element of `rich_rules` should be a complete rich-rule string as
508+
accepted by `--remove-rich-rule` (the same format used for add).
509+
"""
510+
node_id = os.environ['NODE_ID']
511+
data = {'rich_rules': rich_rules}
512+
response = agent.tasks.run(
513+
agent_id=f'node/{node_id}',
514+
action='remove-rich-rules',
515+
data=data
516+
)
517+
return response['exit_code'] == 0
518+
488519

489520
def list_service_providers(rdb, service, transport='*', filters={}):
490521
"""Look up the endpoint information about a given service. Filter

core/imageroot/var/lib/nethserver/cluster/actions/add-node/50update

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,8 @@ cluster.grants.grant(rdb, "add-public-service", f'node/{node_id}', "fwadm")
184184
cluster.grants.grant(rdb, "remove-public-service", f'node/{node_id}', "fwadm")
185185
cluster.grants.grant(rdb, "add-custom-zone", f'node/{node_id}', "fwadm")
186186
cluster.grants.grant(rdb, "remove-custom-zone", f'node/{node_id}', "fwadm")
187+
cluster.grants.grant(rdb, "add-rich-rules", f'node/{node_id}', "fwadm")
188+
cluster.grants.grant(rdb, "remove-rich-rules", f'node/{node_id}', "fwadm")
187189

188190
cluster.grants.grant(rdb, "add-public-service", f'node/{node_id}', "tunadm")
189191
cluster.grants.grant(rdb, "remove-public-service", f'node/{node_id}', "tunadm")
@@ -200,6 +202,9 @@ cluster.grants.grant(rdb, "add-public-service", f'node/{node_id}', "fwadm,portsa
200202
cluster.grants.grant(rdb, "remove-public-service", f'node/{node_id}', "fwadm,portsadm")
201203
cluster.grants.grant(rdb, "add-custom-zone", f'node/{node_id}', "fwadm,portsadm")
202204
cluster.grants.grant(rdb, "remove-custom-zone", f'node/{node_id}', "fwadm,portsadm")
205+
cluster.grants.grant(rdb, "add-rich-rules", f'node/{node_id}', "fwadm,portsadm")
206+
cluster.grants.grant(rdb, "remove-rich-rules", f'node/{node_id}', "fwadm,portsadm")
207+
203208
cluster.grants.grant(rdb, "allocate-ports", f'node/{node_id}', "tunadm,portsadm")
204209
cluster.grants.grant(rdb, "deallocate-ports", f'node/{node_id}', "tunadm,portsadm")
205210
cluster.grants.grant(rdb, "add-tun", f'node/{node_id}', "tunadm,portsadm")
@@ -209,6 +214,7 @@ cluster.grants.grant(rdb, "remove-public-service", f'node/{node_id}', "tunadm,po
209214
cluster.grants.grant(rdb, "add-custom-zone", f'node/{node_id}', "tunadm,portsadm")
210215
cluster.grants.grant(rdb, "remove-custom-zone", f'node/{node_id}', "tunadm,portsadm")
211216

217+
212218
# Grant on cascade the owner role on the new node, to users with the owner
213219
# role on cluster
214220
for userk in rdb.scan_iter('roles/*'):
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#!/usr/bin/env python3
2+
3+
#
4+
# Copyright (C) 2025 Nethesis S.r.l.
5+
# SPDX-License-Identifier: GPL-3.0-or-later
6+
#
7+
8+
import sys
9+
import json
10+
import agent
11+
12+
request = json.load(sys.stdin)
13+
14+
rich_rules = request.get('rich_rules', [])
15+
if not isinstance(rich_rules, list) or len(rich_rules) == 0:
16+
agent.assert_exp(False, 'rich_rules must be a non-empty array')
17+
18+
for rule in rich_rules:
19+
# Apply rule for both families if not already family-qualified
20+
print(agent.SD_INFO + f'Adding rich-rule: {rule}', file=sys.stderr)
21+
agent.run_helper('firewall-cmd', '--permanent', f'--add-rich-rule={rule}')
22+
23+
# Apply the configuration
24+
agent.run_helper('firewall-cmd', '--reload').check_returncode()
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"title": "add-rich-rules input",
4+
"$id": "http://schema.nethserver.org/node/add-rich-rules-input.json",
5+
"description": "Add firewall rich rules",
6+
"examples": [
7+
{
8+
"rich_rules": [
9+
"rule family=ipv4 forward-port port=5060 protocol=udp to-port=5060",
10+
"rule family=ipv6 forward-port port=5060 protocol=udp to-port=5060 to-addr=2001:db8::1"
11+
]
12+
}
13+
],
14+
"type": "object",
15+
"required": ["rich_rules"],
16+
"properties": {
17+
"rich_rules": {
18+
"type": "array",
19+
"minItems": 1,
20+
"items": { "type": "string" }
21+
}
22+
}
23+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#!/usr/bin/env python3
2+
3+
#
4+
# Copyright (C) 2025 Nethesis S.r.l.
5+
# SPDX-License-Identifier: GPL-3.0-or-later
6+
#
7+
8+
import sys
9+
import json
10+
import agent
11+
12+
request = json.load(sys.stdin)
13+
14+
rich_rules = request.get('rich_rules', [])
15+
if not isinstance(rich_rules, list) or len(rich_rules) == 0:
16+
agent.assert_exp(False, 'rich_rules must be a non-empty array')
17+
18+
for rule in rich_rules:
19+
print(agent.SD_INFO + f'Removing rich-rule: {rule}', file=sys.stderr)
20+
agent.run_helper('firewall-cmd', '--permanent', f'--remove-rich-rule={rule}')
21+
22+
# Apply the configuration
23+
agent.run_helper('firewall-cmd', '--reload').check_returncode()
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"title": "remove-rich-rules input",
4+
"$id": "http://schema.nethserver.org/node/remove-rich-rules-input.json",
5+
"description": "Remove firewall rich rules",
6+
"examples": [
7+
{
8+
"rich_rules": [
9+
"rule family=ipv4 forward-port port=5060 protocol=udp to-port=5060",
10+
"rule family=ipv6 forward-port port=5060 protocol=udp to-port=5060 to-addr=2001:db8::1"
11+
]
12+
}
13+
],
14+
"type": "object",
15+
"required": ["rich_rules"],
16+
"properties": {
17+
"rich_rules": {
18+
"type": "array",
19+
"minItems": 1,
20+
"items": { "type": "string" }
21+
}
22+
}
23+
}

core/imageroot/var/lib/nethserver/node/install-finalize.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,8 @@ cluster.grants.grant(rdb, action_clause="add-public-service", to_clause="tunadm
145145
cluster.grants.grant(rdb, action_clause="remove-public-service", to_clause="tunadm,portsadm", on_clause='node/1')
146146
cluster.grants.grant(rdb, action_clause="add-custom-zone", to_clause="tunadm,portsadm", on_clause='node/1')
147147
cluster.grants.grant(rdb, action_clause="remove-custom-zone", to_clause="tunadm,portsadm", on_clause='node/1')
148+
cluster.grants.grant(rdb, action_clause="add-rich-rules", to_clause="fwadm,portsadm", on_clause='node/1')
149+
cluster.grants.grant(rdb, action_clause="remove-rich-rules", to_clause="fwadm,portsadm", on_clause='node/1')
148150
149151
cluster.grants.grant(rdb, action_clause="update-routes", to_clause="accountprovider", on_clause='cluster')
150152
cluster.grants.grant(rdb, action_clause="bind-user-domains", to_clause="accountconsumer", on_clause='cluster')

0 commit comments

Comments
 (0)