Commit 01cbada8 authored by Francesc Guasch's avatar Francesc Guasch Committed by Francesc Guasch
Browse files

fix(backend): ports exposing conflict fish (#1509)

issue #1505
parent 78e9f19f
......@@ -2609,6 +2609,7 @@ sub process_requests {
$sth->execute(time);
my @reqs;
my %duplicated;
while (my ($id_request,$id_domain)= $sth->fetchrow) {
my $req;
eval { $req = Ravada::Request->open($id_request) };
......@@ -2624,6 +2625,10 @@ sub process_requests {
next if $req->command !~ /shutdown/i
&& $self->_domain_working($id_domain, $id_request);
my $domain = '';
$domain = $id_domain if $id_domain;
$domain .= ($req->defined_arg('name') or '');
next if $duplicated{$req->command.":$domain"}++;
push @reqs,($req);
}
......@@ -3968,6 +3973,8 @@ sub _cmd_refresh_vms($self, $request=undef) {
$self->_clean_requests('refresh_vms', $request);
$self->_refresh_volatile_domains();
$self->_check_duplicated_prerouting();
$request->error('') if $request;
}
......@@ -4255,6 +4262,61 @@ sub _refresh_down_nodes($self, $request = undef ) {
}
}
sub _check_duplicated_prerouting($self, $request = undef ) {
my $sth = $CONNECTOR->dbh->prepare(
"SELECT id FROM vms WHERE is_active=1 "
);
$sth->execute();
while ( my ($id) = $sth->fetchrow()) {
my $vm;
eval { $vm = Ravada::VM->open($id) };
warn $@ if $@;
if ($vm) {
my $iptables = $vm->iptables_list();
my %prerouting;
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});
}
$prerouting{$port} = \%args;
}
}
}
}
sub _reopen_ports($self, $port) {
my $sth = $CONNECTOR->dbh->prepare("SELECT id_domain FROM domain_ports "
." WHERE public_port=?");
$sth->execute($port);
my ($id_domain) = $sth->fetchrow;
return if !$id_domain;
Ravada::Request->open_exposed_ports(
uid => Ravada::Utils::user_daemon->id
,id_domain => $id_domain
);
}
sub _delete_iptables_rule($self, $vm, $table, $rule) {
my %delete = %$rule;
my $chain = delete $delete{A};
my $to_destination = delete $delete{'to-destination'};
my $dport = delete $delete{dport};
my $m = delete $delete{m};
my $p = delete $delete{p};
my @delete = ( t => $table, 'D' => $chain
, m => $m, p => $p, dport => $dport
, %delete
, 'to-destination' => $to_destination);
$vm->iptables(@delete);
}
sub _refresh_disabled_nodes($self, $request = undef ) {
my @timeout = ();
@timeout = ( timeout => $request->args('timeout_shutdown') )
......
......@@ -373,7 +373,7 @@ sub _around_start($orig, $self, @arg) {
$self->_request_set_base();
next;
}
die $@;
die $error;
}
$self->_post_start(%arg);
......@@ -3297,10 +3297,9 @@ sub _close_exposed_port_nat($self, $iptables, %port) {
if (exists $args{j} && $args{j} eq 'DNAT'
&& exists $args{d} && $args{d} eq $ip
&& exists $args{dport}
&& exists $port{$args{dport}}
&& exists $args{'to-destination'}
) {
my $internal_port = $port{$args{dport}}->{internal_port} or next;
if ( $args{'to-destination'}=~/\:$internal_port$/ ) {
my %delete = %args;
delete $delete{A};
delete $delete{dport};
......@@ -3319,7 +3318,6 @@ sub _close_exposed_port_nat($self, $iptables, %port) {
'to-destination',$to_destination
);
$self->_vm->iptables(@delete);
}
}
}
}
......
......@@ -248,6 +248,7 @@ sub test_requests_by_domain {
is($domain->list_requests,3,Dumper([map { $_->{command} } $domain->list_requests]));
rvd_back->_process_all_requests_dont_fork();
wait_request();
is($req1->status , 'done');
is($req2->status , 'done');
......
......@@ -28,6 +28,7 @@ sub test_list_nats($vm) {
,vm_type => $vm->type
);
rvd_back->_process_requests_dont_fork();
wait_request();
is($req->status,'done');
is($req->error,'');
like($req->output,qr{\"$exp_nat[0]\"});
......@@ -65,6 +66,7 @@ sub test_list_bridges($vm) {
,vm_type => $vm->type
);
rvd_back->_process_requests_dont_fork();
wait_request();
is($req->status,'done');
is($req->error,'');
......
......@@ -700,6 +700,93 @@ sub _iptables_save($vm,$table=undef,$chain=undef) {
return @out;
}
sub test_open_port_duplicated($vm) {
diag("Test open port duplicated ".$vm->type);
my $base = $BASE->clone(name => new_domain_name, user => user_admin);
$base->expose(port => 22, name => "ssh");
my @base_ports0 = $base->list_ports();
$base->prepare_base(user => user_admin);
my $clone = $base->clone(name => new_domain_name, user => user_admin);
$clone->start(remote_ip => '10.1.1.1', user => user_admin);
_wait_ip2($vm, $clone);
wait_request();
my @out = split /\n/, `iptables-save -t nat`;
my @open = (grep /--to-destination [\d\.]+:22/, @out);
is(scalar(@open),1);
my ($public_port) = $open[0] =~ /--dport (\d+)/;
die "Error: no public port in $open[0]" if !$public_port;
$vm->iptables( t => 'nat'
, A => 'PREROUTING'
, p => 'tcp'
, d => '192.0.2.3'
, dport => $public_port
, j => 'DNAT'
, 'to-destination' => '127.0.0.1:23'
);
my @out2 = split /\n/, `iptables-save -t nat`;
my @open2 = (grep /--dport $public_port/, @out2);
is(scalar(@open2),2) or die Dumper(\@open2);
my $req = Ravada::Request->refresh_vms();
wait_request();
is($req->status,'done');
is($req->error, '');
my @out3 = split /\n/, `iptables-save -t nat`;
my @open3 = (grep /--dport $public_port/, @out3);
is(scalar(@open3),1) or die Dumper(\@open3);
$clone->remove(user_admin);
$base->remove(user_admin);
}
sub test_close_port($vm) {
diag("Test close port ".$vm->type);
my $base = $BASE->clone(name => new_domain_name, user => user_admin);
$base->expose(port => 22, name => "ssh");
my @base_ports0 = $base->list_ports();
$base->prepare_base(user => user_admin);
my $clone = $base->clone(name => new_domain_name, user => user_admin);
$clone->start(remote_ip => '10.1.1.1', user => user_admin);
_wait_ip2($vm, $clone);
wait_request();
my @out = split /\n/, `iptables-save -t nat`;
my @open = (grep /--to-destination [\d\.]+:22/, @out);
is(scalar(@open),1);
my ($public_port) = $open[0] =~ /--dport (\d+)/;
die "Error: no public port in $open[0]" if !$public_port;
$vm->iptables( t => 'nat'
, A => 'PREROUTING'
, p => 'tcp'
, d => '192.0.2.3'
, dport => $public_port
, j => 'DNAT'
, 'to-destination' => '127.0.0.1:23'
);
my @out2 = split /\n/, `iptables-save -t nat`;
my @open2 = (grep /--dport $public_port/, @out2);
is(scalar(@open2),2) or die Dumper(\@open);
$clone->shutdown_now(user_admin);
wait_request();
my @out3 = split /\n/, `iptables-save -t nat`;
my @open3 = (grep /--to-destination [\d\.]+:22/, @out3);
is(scalar(@open3),0, Dumper(\@open3));
$clone->remove(user_admin);
$base->remove(user_admin);
}
sub test_clone_exports_add_ports($vm) {
my $base = $BASE->clone(name => new_domain_name, user => user_admin);
......@@ -755,30 +842,6 @@ sub _wait_ip2($vm_name, $domain) {
sub _wait_ip {
return _wait_ip2(@_);
my $vm_name = shift;
my $domain = shift or confess "Missing domain arg";
return $domain->ip if $domain->ip;
sleep 1;
eval ' $domain->domain->send_key(Sys::Virt::Domain::KEYCODE_SET_LINUX,200, [28]) ';
die $@ if $@;
return if $@;
sleep 2;
for ( 1 .. 12 ) {
rvd_back->_process_requests_dont_fork();
eval ' $domain->domain->send_key(Sys::Virt::Domain::KEYCODE_SET_LINUX,200, [28]) ';
die $@ if $@;
sleep 2;
}
for (1 .. 30) {
last if $domain->ip;
sleep 1;
diag("waiting for ".$domain->name." ip") if $_ ==10;
}
return $domain->ip;
}
sub add_network_10 {
......@@ -1180,6 +1243,9 @@ for my $vm_name ( vm_names() ) {
flush_rules() if !$<;
import_base($vm);
test_open_port_duplicated($vm);
test_close_port($vm);
test_routing_hibernated($vm);
test_routing_already_used($vm,undef,'restricted');
test_routing_already_used($vm);
......
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