in managementnode/lib/VCL/Module/OS/Linux/firewall/iptables.pm [1918:2173]
sub nat_configure_host {
my $self = shift;
if (ref($self) !~ /VCL::Module/i) {
notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
return 0;
}
my $computer_name = $self->data->get_computer_hostname();
my $public_ip_address = $self->data->get_nathost_public_ip_address();
my $internal_ip_address = $self->data->get_nathost_internal_ip_address();
my $nat_host_chain_name = $self->get_nat_host_chain_name();
# Enable IP port forwarding
if (!$self->os->enable_ip_forwarding()) {
notify($ERRORS{'WARNING'}, 0, "unable to configure NAT host $computer_name, failed to enable IP forwarding");
return;
}
my $nat_table_info = $self->get_table_info('nat');
if (!$nat_table_info) {
notify($ERRORS{'WARNING'}, 0, "failed to configure NAT on $computer_name, nat table info could not be retrieved");
return;
}
elsif (!defined($nat_table_info->{PREROUTING})) {
notify($ERRORS{'WARNING'}, 0, "unable to configure NAT on $computer_name, nat table does not contain a PREROUTING chain:\n" . format_data($nat_table_info));
return;
}
elsif (!defined($nat_table_info->{POSTROUTING})) {
notify($ERRORS{'WARNING'}, 0, "unable to configure NAT on $computer_name, nat table does not contain a POSTROUTING chain:\n" . format_data($nat_table_info));
return;
}
# Check if NAT has previously been configured
if (defined($nat_table_info->{$nat_host_chain_name})) {
notify($ERRORS{'DEBUG'}, 0, "NAT has already been configured on $computer_name, '$nat_host_chain_name' chain exists in nat table");
return 1;
}
else {
# Before VCL 2.5, dedicated NAT host chain wasn't created, check if MASQUERADE rule exists
for my $rule (@{$nat_table_info->{POSTROUTING}{rules}}) {
my $rule_specification_string = $rule->{rule_specification};
if ($rule_specification_string =~ /MASQUERADE/) {
notify($ERRORS{'DEBUG'}, 0, "POSTROUTING chain in nat table contains a MASQUERADE rule, assuming NAT has already been configured: $rule_specification_string");
return 1;
}
}
}
# Figure out the public and internal interface names
my $public_interface_name;
my $internal_interface_name;
my $public_subnet_mask;
my $internal_subnet_mask;
my $network_configuration = $self->os->get_network_configuration();
for my $interface_name (keys %$network_configuration) {
my @ip_addresses = keys %{$network_configuration->{$interface_name}{ip_address}};
# Check if the interface is assigned the nathost.publicIPaddress
if (grep { $_ eq $public_ip_address } @ip_addresses) {
$public_interface_name = $interface_name;
$public_subnet_mask = $network_configuration->{$interface_name}{ip_address}{$public_ip_address};
}
# If nathost.internalIPaddress is set, check if interface is assigned matching IP address
if (grep { $_ eq $internal_ip_address } @ip_addresses) {
$internal_interface_name = $interface_name;
$internal_subnet_mask = $network_configuration->{$interface_name}{ip_address}{$internal_ip_address};
}
}
if (!$public_interface_name) {
notify($ERRORS{'WARNING'}, 0, "failed to configure NAT host $computer_name, no interface is assigned the public IP address configured in the nathost table: $public_ip_address\n" . format_data($network_configuration));
return;
}
if (!$internal_interface_name) {
notify($ERRORS{'WARNING'}, 0, "failed to configure NAT host $computer_name, no interface is assigned the internal IP address configured in the nathost table: $internal_ip_address\n" . format_data($network_configuration));
return;
}
my ($public_network_address, $public_network_bits) = ip_address_to_network_address($public_ip_address, $public_subnet_mask);
my ($internal_network_address, $internal_network_bits) = ip_address_to_network_address($internal_ip_address, $internal_subnet_mask);
notify($ERRORS{'DEBUG'}, 0, "determined NAT host interfaces:\n" .
"public - interface: $public_interface_name, IP address: $public_ip_address/$public_subnet_mask, network: $public_network_address/$public_network_bits\n" .
"internal - interface: $internal_interface_name, IP address: $internal_ip_address/$internal_subnet_mask, network: $internal_network_address/$internal_network_bits"
);
my @natport_ranges = get_natport_ranges();
my $destination_ports = '';
for my $natport_range (@natport_ranges) {
my ($start_port, $end_port) = @$natport_range;
if (!defined($start_port)) {
notify($ERRORS{'WARNING'}, 0, "unable to parse NAT port range: '$natport_range'");
next;
}
$destination_ports .= "," if ($destination_ports);
$destination_ports .= "$start_port:$end_port";
}
$self->create_chain('filter', $nat_host_chain_name);
$self->create_chain('nat', $nat_host_chain_name);
if (!$self->insert_rule('filter', 'INPUT',
{
'parameters' => {
'jump' => $nat_host_chain_name,
},
'match_extensions' => {
'comment' => {
'comment' => "VCL: jump from filter table INPUT chain to NAT host $nat_host_chain_name chain",
},
},
}
)) {
return;
}
if (!$self->insert_rule('filter', 'FORWARD',
{
'parameters' => {
'jump' => $nat_host_chain_name,
},
'match_extensions' => {
'comment' => {
'comment' => "VCL: jump from filter table FORWARD chain to NAT host $nat_host_chain_name chain",
},
},
}
)) {
return;
}
if (!$self->insert_rule('nat', 'POSTROUTING',
{
'parameters' => {
'jump' => $nat_host_chain_name,
},
'match_extensions' => {
'comment' => {
'comment' => "VCL: jump from nat table POSTROUTING chain to NAT host $nat_host_chain_name chain",
},
},
}
)) {
return;
}
if (!$self->insert_rule('nat', $nat_host_chain_name,
{
'parameters' => {
'out-interface' => $public_interface_name,
'!destination' => "$internal_network_address/$internal_network_bits",
'jump' => 'MASQUERADE',
},
'match_extensions' => {
'comment' => {
'comment' => "VCL: change IP of outbound $public_interface_name packets to NAT host IP address $public_ip_address",
},
},
}
)) {
return;
}
if (!$self->insert_rule('filter', $nat_host_chain_name,
{
'parameters' => {
'in-interface' => $public_interface_name,
'destination' => $public_ip_address,
'jump' => 'ACCEPT',
'protocol' => 'tcp',
},
'match_extensions' => {
'state' => {
'state' => 'NEW,RELATED,ESTABLISHED',
},
'multiport' => {
'destination-ports' => $destination_ports,
},
'comment' => {
'comment' => "VCL: allow inbound TCP traffic on the NAT port ranges to public $public_interface_name",
},
},
}
)) {
return;
}
if (!$self->insert_rule('filter', $nat_host_chain_name,
{
'parameters' => {
'in-interface' => $public_interface_name,
'destination' => $public_ip_address,
'jump' => 'ACCEPT',
'protocol' => 'udp',
},
'match_extensions' => {
'state' => {
'state' => 'NEW,RELATED,ESTABLISHED',
},
'multiport' => {
'destination-ports' => $destination_ports,
},
'comment' => {
'comment' => "VCL: allow inbound UDP traffic on the NAT port ranges to public $public_interface_name",
},
},
}
)) {
return;
}
if (!$self->insert_rule('filter', $nat_host_chain_name,
{
'parameters' => {
'in-interface' => $public_interface_name,
'out-interface' => $internal_interface_name,
'jump' => 'ACCEPT',
},
'match_extensions' => {
'state' => {
'state' => 'NEW,RELATED,ESTABLISHED',
},
'comment' => {
'comment' => "VCL: forward inbound packets from public $public_interface_name to internal $internal_interface_name",
},
},
}
)) {
return;
}
if (!$self->insert_rule('filter', $nat_host_chain_name,
{
'parameters' => {
'in-interface' => $internal_interface_name,
'out-interface' => $public_interface_name,
'jump' => 'ACCEPT',
},
'match_extensions' => {
'state' => {
'state' => 'NEW,RELATED,ESTABLISHED',
},
'comment' => {
'comment' => "VCL: forward outbound packets from internal $internal_interface_name to public $public_interface_name",
},
},
}
)) {
return;
}
$self->save_configuration();
notify($ERRORS{'DEBUG'}, 0, "successfully configured NAT on $computer_name");
return 1;
}