Commit 3cdad5e8 authored by Francesc Guasch's avatar Francesc Guasch Committed by Francesc Guasch
Browse files

Fix ports expose (#1520)

fix(backend): remove conflicting prerouting

* test(ports); check duplicated internal ip
* feat(frontend): debug_ports setting

issue #1505
parent 4e71e0e4
......@@ -1688,6 +1688,11 @@ sub _sql_insert_defaults($self){
,name => 'display_password'
,value => 1
}
,{
id_parent => $id_backend
,name => "debug_ports"
,value => 0
}
]
);
my %field = ( settings => 'name' );
......@@ -4379,6 +4384,7 @@ sub _refresh_down_nodes($self, $request = undef ) {
}
sub _check_duplicated_prerouting($self, $request = undef ) {
my $debug_ports = $self->setting('/backend/debug_ports');
my $sth = $CONNECTOR->dbh->prepare(
"SELECT id FROM vms WHERE is_active=1 "
);
......@@ -4390,16 +4396,23 @@ sub _check_duplicated_prerouting($self, $request = undef ) {
if ($vm) {
my $iptables = $vm->iptables_list();
my %prerouting;
my %already_open;
for my $line (@{$iptables->{'nat'}}) {
my %args = @$line;
next if $args{A} ne 'PREROUTING' || !$args{dport};
my $port = $args{dport};
if ($prerouting{$port}) {
$self->_reopen_ports($port);
$self->_delete_iptables_rule($vm,'nat', \%args);
$self->_delete_iptables_rule($vm,'nat', $prerouting{$port});
for my $item ( 'dport' , 'to-destination') {
my $value = $args{$item} or next;
if ($prerouting{$value}) {
warn "clean duplicated prerouting "
.Dumper($prerouting{$value}, \%args) if $debug_ports;
$self->_reopen_ports($port) unless $already_open{$port}++;
$self->_delete_iptables_rule($vm,'nat', \%args);
$self->_delete_iptables_rule($vm,'nat', $prerouting{$port});
}
$prerouting{$value} = \%args;
}
$prerouting{$port} = \%args;
}
}
}
......
......@@ -3140,6 +3140,7 @@ sub _used_port_displays($self, $port, $skip_id_port) {
}
sub _open_exposed_port($self, $internal_port, $name, $restricted) {
my $debug_ports = Ravada::setting(undef,'/backend/debug_ports');
my $sth = $$CONNECTOR->dbh->prepare("SELECT id,public_port FROM domain_ports"
." WHERE id_domain=? AND internal_port=?"
);
......@@ -3150,9 +3151,14 @@ sub _open_exposed_port($self, $internal_port, $name, $restricted) {
confess "Error: I can't get the internal IP of ".$self->name
if !$internal_ip || $internal_ip !~ /^(\d+\.\d+)/;
$public_port = undef if $public_port
&&( $self->_used_ports_iptables($public_port, "$internal_ip:$internal_port")
|| $self->_used_port_displays($public_port,$id_port));
if ($public_port
&& ( $self->_used_ports_iptables($public_port, "$internal_ip:$internal_port")
|| $self->_used_port_displays($public_port,$id_port))
) {
warn $self->name." cleared duplicate $public_port\n"
if $debug_ports;
$public_port = undef;
}
$public_port = $self->_set_public_port($id_port, $internal_port, $name, $restricted)
if !$public_port;
......@@ -3164,6 +3170,23 @@ sub _open_exposed_port($self, $internal_port, $name, $restricted) {
$sth->execute($internal_ip, $self->id, $internal_port);
if ( !$> ) {
my ($out, $err) = $self->_vm->run_command("iptables-save","-t","nat");
my @open1 = (grep /--dport $public_port/, split/\n/,$out );
my @open2 = (grep /--to-destination $internal_ip:$internal_port/, split/\n/,$out );
my %removed;
for my $line ( @open1, @open2 ) {
next if $removed{$line}++;
warn $self->name." clean $line\n" if $debug_ports;
$line =~ s/^-A/-t nat -D/;
my ($out,$err) = $self->_vm->run_command("iptables",split / /,$line);
warn $out if$out;
warn $err if $err;
}
warn $self->name." open $public_port ->"
." $internal_ip:$internal_port\n"
if $debug_ports;
$self->_vm->iptables_unique(
t => 'nat'
,A => 'PREROUTING'
......
......@@ -700,6 +700,75 @@ sub _iptables_save($vm,$table=undef,$chain=undef) {
return @out;
}
sub test_redirect_ip_duplicated($vm) {
diag("Test redirect ip duplicated ".$vm->type);
my $internal_port = 22;
my $domain = $BASE->clone(name => new_domain_name, user => user_admin);
$domain->expose(port => $internal_port, name => "ssh");
$domain->start( remote_ip => '10.1.1.2', user => user_admin);
my $ip = _wait_ip2($vm, $domain);
wait_request(debug => 1);
my @ports0 = $domain->list_ports();
my ($public_port) = $ports0[0]->{public_port};
$vm->iptables(t => 'nat'
, A => 'PREROUTING'
, p => 'tcp'
, d => $vm->ip
, dport => $public_port+10
, j => 'DNAT'
, 'to-destination' => "$ip:$internal_port"
);
my @out = split /\n/, `iptables-save -t nat`;
my @open = (grep /--to-destination $ip/, @out);
is(scalar(@open),2) or die Dumper(\@open);
$domain->start( remote_ip => '10.1.1.2', user => user_admin);
wait_request(debug => 1);
@out = split /\n/, `iptables-save -t nat`;
@open = (grep /--to-destination $ip/, @out);
is(scalar(@open),1) or die Dumper(\@open);
$domain->remove(user_admin);
}
sub test_redirect_ip_duplicated_refresh($vm) {
diag("Test redirect ip duplicated refresh".$vm->type);
my $internal_port = 22;
my $domain = $BASE->clone(name => new_domain_name, user => user_admin);
$domain->expose(port => $internal_port, name => "ssh");
$domain->start( remote_ip => '10.1.1.2', user => user_admin);
my $ip = _wait_ip2($vm, $domain);
wait_request(debug => 1);
my @ports0 = $domain->list_ports();
my ($public_port) = $ports0[0]->{public_port};
$vm->iptables(t => 'nat'
, A => 'PREROUTING'
, p => 'tcp'
, d => $vm->ip
, dport => $public_port+10
, j => 'DNAT'
, 'to-destination' => "$ip:$internal_port"
);
my @out = split /\n/, `iptables-save -t nat`;
my @open = (grep /--to-destination $ip/, @out);
is(scalar(@open),2) or die Dumper(\@open);
my $req = Ravada::Request->refresh_vms();
wait_request();
is($req->status,'done');
is($req->error, '');
@out = split /\n/, `iptables-save -t nat`;
@open = (grep /--to-destination $ip/, @out);
is(scalar(@open),1) or die Dumper(\@open);
$domain->remove(user_admin);
}
sub test_open_port_duplicated($vm) {
diag("Test open port duplicated ".$vm->type);
my $base = $BASE->clone(name => new_domain_name, user => user_admin);
......@@ -1243,6 +1312,7 @@ for my $vm_name ( vm_names() ) {
flush_rules() if !$<;
import_base($vm);
test_redirect_ip_duplicated($vm);
test_open_port_duplicated($vm);
test_close_port($vm);
......
......@@ -117,6 +117,19 @@
<div class="row">
<div class="col-md-1"></div>
<div class="col-md-2">
<label for="debug">Debug Ports</label>
</div>
<div class="col-md-6">
<input name="debug" ng-model="settings.backend.debug_ports.value"
ng-true-value="1" ng-false-value="0"
type="checkbox">
<%= l 'Debug Ports Exposed' %>
</div>
</div>
<div class="row">
<div class="col-md-1"></div>
<div class="col-md-2">
<label for="debug">Display Password</label>
</div>
<div class="col-md-6">
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment