summaryrefslogtreecommitdiff
path: root/data/templates/firewall/nftables-nat.tmpl
blob: 9ea8806973f53306a23c5063213e56236cbf8605 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
#!/usr/sbin/nft -f

{% macro nat_rule(rule, config, chain) %}
{%   set comment  = '' %}
{%   set base_log = '' %}
{%   set src_addr  = 'ip saddr ' + config.source.address.replace('!','!= ') if config.source is defined and config.source.address is defined and config.source.address is not none %}
{%   set dst_addr  = 'ip daddr ' + config.destination.address.replace('!','!= ') if config.destination is defined and config.destination.address is defined and config.destination.address is not none %}
{#   negated port groups need special treatment, move != in front of { } group #}
{%   if config.source is defined and config.source.port is defined and config.source.port is not none and config.source.port.startswith('!=') %}
{%     set src_port  = 'sport != { ' + config.source.port.replace('!=','') + ' }' %}
{%   else %}
{%     set src_port  = 'sport { ' + config.source.port + ' }' if config.source is defined and config.source.port is defined and config.source.port is not none %}
{%   endif %}
{#   negated port groups need special treatment, move != in front of { } group #}
{%   if config.destination is defined and config.destination.port is defined and config.destination.port is not none and config.destination.port.startswith('!=') %}
{%     set dst_port  = 'dport != { ' + config.destination.port.replace('!=','') + ' }' %}
{%   else %}
{%     set dst_port  = 'dport { ' + config.destination.port + ' }' if config.destination is defined and config.destination.port is defined and config.destination.port is not none %}
{%   endif %}
{%   if chain == 'PREROUTING' %}
{%     set comment   = 'DST-NAT-' + rule %}
{%     set base_log  = '[NAT-DST-' + rule %}
{%     set interface = ' iifname "' + config.inbound_interface + '"' if config.inbound_interface is defined and config.inbound_interface != 'any' else '' %}
{%     if config.translation is defined and config.translation.address is defined and config.translation.address is not none %}
{#       support 1:1 network translation #}
{%       if config.translation.address | is_ip_network %}
{%         set trns_addr = 'dnat ip prefix to ip daddr map { ' + config.destination.address + ' : ' + config.translation.address + ' }' %}
{#         we can now clear out the dst_addr part as it's already covered in aboves map #}
{%         set dst_addr  = '' %}
{%       else %}
{%         set trns_addr = 'dnat to ' + config.translation.address %}
{%       endif %}
{%     endif %}
{%   elif chain == 'POSTROUTING' %}
{%     set comment   = 'SRC-NAT-' + rule %}
{%     set base_log  = '[NAT-SRC-' + rule %}
{%     set interface = ' oifname "' + config.outbound_interface + '"' if config.outbound_interface is defined and config.outbound_interface != 'any' else '' %}
{%     if config.translation is defined and config.translation.address is defined and config.translation.address is not none %}
{%       if config.translation.address == 'masquerade' %}
{%         set trns_addr = config.translation.address %}
{%         if config.translation.port is defined and config.translation.port is not none %}
{%           set trns_addr = trns_addr + ' to ' %}
{%         endif %}
{#       support 1:1 network translation #}
{%       elif config.translation.address | is_ip_network %}
{%         set trns_addr = 'snat ip prefix to ip saddr map { ' + config.source.address + ' : ' + config.translation.address + ' }' %}
{#         we can now clear out the src_addr part as it's already covered in aboves map #}
{%         set src_addr  = '' %}
{%       else %}
{%         set trns_addr = 'snat to ' + config.translation.address %}
{%       endif %}
{%     endif %}
{%   endif %}
{%   set trns_port = ':' + config.translation.port if config.translation is defined and config.translation.port is defined and config.translation.port is not none %}
{#   protocol has a default value thus it is always present #}
{%   if config.protocol == 'tcp_udp' %}
{%     set protocol  = 'tcp' %}
{%     set comment   = comment + ' tcp_udp' %}
{%   else %}
{%     set protocol  = config.protocol %}
{%   endif %}
{%   if config.log is defined %}
{%     if config.exclude is defined %}
{%       set log = base_log + '-EXCL]' %}
{%     elif config.translation is defined and config.translation.address is defined and config.translation.address == 'masquerade' %}
{%       set log = base_log +'-MASQ]' %}
{%     else %}
{%       set log = base_log + ']' %}
{%     endif %}
{%   endif %}
{%   if config.exclude is defined %}
{#     rule has been marked as 'exclude' thus we simply return here #}
{%     set trns_addr = 'return' %}
{%     set trns_port = '' %}
{%   endif %}
{#   T1083: NAT address and port translation options #}
{%   if config.translation is defined and config.translation.options is defined and config.translation.options is not none %}
{%     if config.translation.options.address_mapping is defined and config.translation.options.address_mapping == "persistent" %}
{%         set trns_opts_addr  = 'persistent' %}
{%     endif %}
{%     if config.translation.options.port_mapping is defined %}
{%       if config.translation.options.port_mapping == "random" %}
{%         set trns_opts_port  = 'random' %}
{%       elif config.translation.options.port_mapping == "fully-random" %}
{%         set trns_opts_port  = 'fully-random' %}
{%       endif %}
{%     endif %}
{%   endif %}
{%   if trns_opts_addr and trns_opts_port %}
{%     set trns_opts  = trns_opts_addr + ',' + trns_opts_port %}
{%   elif trns_opts_addr %}
{%     set trns_opts  = trns_opts_addr %}
{%   elif trns_opts_port %}
{%     set trns_opts  = trns_opts_port %}
{%   endif %}
{%   set output = 'add rule ip nat ' + chain + interface %}
{%   if protocol != 'all' %}
{%     set output = output + ' ip protocol ' + protocol %}
{%   endif %}
{%   if src_addr %}
{%     set output = output + ' ' + src_addr %}
{%   endif %}
{%   if src_port %}
{%     set output = output + ' ' + protocol + ' ' + src_port %}
{%   endif %}
{%   if dst_addr %}
{%     set output = output + ' ' + dst_addr %}
{%   endif %}
{%   if dst_port %}
{%     set output = output + ' ' + protocol + ' ' + dst_port %}
{%   endif %}
{#   Count packets #}
{%     set output = output + ' counter' %}
{#   Special handling of log option, we must repeat the entire rule before the #}
{#   NAT translation options are added, this is essential                      #}
{%   if log %}
{%     set log_output = output + ' log prefix "' + log + '" comment "' + comment + '"' %}
{%   endif %}
{%   if trns_addr %}
{%     set output = output + ' ' + trns_addr %}
{%   endif %}
{%   if trns_port %}
{#     Do not add a whitespace here, translation port must be directly added after IP address #}
{#     e.g. 192.0.2.10:3389                                                                   #}
{%     set output = output + trns_port %}
{%   endif %}
{%   if trns_opts %}
{%     set output = output + ' ' + trns_opts %}
{%   endif %}
{%   if comment %}
{%     set output = output + ' comment "' + comment + '"' %}
{%   endif %}
{{ log_output if log_output }}
{{ output }}
{#   Special handling if protocol is tcp_udp, we must repeat the entire rule with udp as protocol #}
{%   if config.protocol == 'tcp_udp' %}
{#     Beware of trailing whitespace, without it the comment tcp_udp will be changed to udp_udp   #}
{{ log_output | replace('tcp ', 'udp ') if log_output }}
{{ output | replace('tcp ', 'udp ') }}
{%   endif %}
{% endmacro %}

# Start with clean NAT table
flush table ip nat
{% if helper_functions == 'remove' %}
{# NAT if going to be disabled - remove rules and targets from nftables #}
{%   set base_command = 'delete rule ip raw' %}
{{ base_command }} PREROUTING handle {{ pre_ct_ignore }}
{{ base_command }} OUTPUT     handle {{ out_ct_ignore }}
{{ base_command }} PREROUTING handle {{ pre_ct_conntrack }}
{{ base_command }} OUTPUT     handle {{ out_ct_conntrack }}

delete chain ip raw NAT_CONNTRACK

{% elif helper_functions == 'add' %}
{# NAT if enabled - add targets to nftables #}
add chain ip raw NAT_CONNTRACK
add rule ip raw NAT_CONNTRACK counter accept
{%   set base_command = 'add rule ip raw' %}
{{ base_command }} PREROUTING position {{ pre_ct_ignore }}    counter jump VYOS_CT_HELPER
{{ base_command }} OUTPUT     position {{ out_ct_ignore }}    counter jump VYOS_CT_HELPER
{{ base_command }} PREROUTING position {{ pre_ct_conntrack }} counter jump NAT_CONNTRACK
{{ base_command }} OUTPUT     position {{ out_ct_conntrack }} counter jump NAT_CONNTRACK
{% endif %}

#
# Destination NAT rules build up here
#
{% if destination is defined and destination.rule is defined and destination.rule is not none %}
{%   for rule, config in destination.rule.items() if config.disable is not defined %}
{{ nat_rule(rule, config, 'PREROUTING') }}
{%   endfor %}
{% endif %}
#
# Source NAT rules build up here
#
{% if source is defined and source.rule is defined and source.rule is not none %}
{%   for rule, config in source.rule.items() if config.disable is not defined %}
{{ nat_rule(rule, config, 'POSTROUTING') }}
{%   endfor %}
{% endif %}