]>
git.proxmox.com Git - pve-firewall.git/blob - src/pve-firewall
8 use Time
:: HiRes qw
( gettimeofday
);
9 use PVE
:: Tools
qw(dir_glob_foreach file_read_firstline) ;
12 use PVE
:: Cluster
qw(cfs_read_file) ;
13 use PVE
:: RPCEnvironment
;
16 use PVE
:: FirewallSimulator
;
19 use base
qw(PVE::Daemon) ;
21 $SIG { '__WARN__' } = sub {
26 syslog
( 'warning' , " %s " , $t );
30 my $cmdline = [ $0, @ARGV ];
32 my %daemon_options = ( restart_on_error
=> 5 , stop_wait_time
=> 5 );
36 $daemon = __PACKAGE__-
> new ( 'pve-firewall' , $cmdline, %daemon_options );
39 syslog
( "err" , "daemon init failed - $err " );
43 my $rpcenv = PVE
:: RPCEnvironment-
> init ( 'cli' );
45 $rpcenv -> init_request ();
46 $rpcenv -> set_language ( $ENV { LANG
});
47 $rpcenv -> set_user ( 'root @pam ' );
49 my $nodename = PVE
:: INotify
:: nodename
();
53 PVE
:: Cluster
:: cfs_update
();
55 PVE
:: Firewall
:: init
();
58 my $restart_request = 0 ;
64 my $initial_memory_usage ;
69 syslog
( 'info' , "server closing" );
72 1 while ( waitpid (- 1 , POSIX
:: WNOHANG
()) > 0 );
74 syslog
( 'info' , "clear firewall rules" );
76 eval { PVE
:: Firewall
:: remove_pvefw_chains
(); };
79 $self -> exit_daemon ( 0 );
91 local $SIG { '__WARN__' } = 'IGNORE' ; # do not fill up logs
95 $next_update = time () + $updatetime ;
97 my ( $ccsec, $cusec ) = gettimeofday
();
99 PVE
:: Cluster
:: cfs_update
();
100 PVE
:: Firewall
:: update
();
105 syslog
( 'err' , "status update error: $err " );
108 my ( $ccsec_end, $cusec_end ) = gettimeofday
();
109 my $cptime = ( $ccsec_end - $ccsec ) + ( $cusec_end - $cusec )/ 1000000 ;
111 syslog
( 'info' , sprintf ( "firewall update time (%.3f seconds)" , $cptime ))
116 my $mem = PVE
:: ProcFSTools
:: read_memory_usage
();
118 if (! defined ( $initial_memory_usage ) || ( $cycle < 10 )) {
119 $initial_memory_usage = $mem ->{ resident
};
121 my $diff = $mem ->{ resident
} - $initial_memory_usage ;
122 if ( $diff > 5 * 1024 * 1024 ) {
123 syslog
( 'info' , "restarting server after $cycle cycles to " .
124 "reduce memory usage (free $mem ->{resident} ( $diff ) bytes)" );
125 $self -> restart_daemon ();
130 while (( time () < $next_update ) &&
131 ( $wcount < $updatetime ) && # protect against time wrap
132 ! $restart_request ) { $wcount++ ; sleep ( 1 ); };
134 $self -> restart_daemon () if $restart_request ;
138 $daemon -> register_start_command ( __PACKAGE__
,
139 "Start the Proxmox VE firewall service." );
140 $daemon -> register_restart_command ( __PACKAGE__
, 1 ,
141 "Restart the Proxmox VE firewall service." );
142 $daemon -> register_stop_command ( __PACKAGE__
,
143 "Stop firewall. This removes all Proxmox VE " .
144 "related iptable rules. " .
145 "The host is unprotected afterwards." );
147 __PACKAGE__-
> register_method ({
151 description
=> "Get firewall status." ,
153 additionalProperties
=> 0 ,
158 additionalProperties
=> 0 ,
162 enum
=> [ 'unknown' , 'stopped' , 'running' ],
165 description
=> "Firewall is enabled (in 'cluster.fw')" ,
169 description
=> "Set when there are pending changes." ,
178 local $SIG { '__WARN__' } = 'DEFAULT' ; # do not fill up syslog
182 my $status = $daemon -> running () ?
'running' : 'stopped' ;
184 my $res = { status
=> $status };
186 my $verbose = 1 ; # show syntax errors
187 my $cluster_conf = PVE
:: Firewall
:: load_clusterfw_conf
( undef , $verbose );
188 $res ->{ enable
} = $cluster_conf ->{ options
}->{ enable
} ?
1 : 0 ;
190 if ( $status eq 'running' ) {
192 my ( $ruleset, $ipset_ruleset, $rulesetv6 ) = PVE
:: Firewall
:: compile
( $cluster_conf, undef , undef , $verbose );
194 $verbose = 0 ; # do not show iptables details
195 my ( undef , undef , $ipset_changes ) = PVE
:: Firewall
:: get_ipset_cmdlist
( $ipset_ruleset, $verbose );
196 my ( $test, $ruleset_changes ) = PVE
:: Firewall
:: get_ruleset_cmdlist
( $ruleset, $verbose );
197 my ( undef , $ruleset_changesv6 ) = PVE
:: Firewall
:: get_ruleset_cmdlist
( $rulesetv6, $verbose, "ip6tables" );
199 $res ->{ changes
} = ( $ipset_changes || $ruleset_changes || $ruleset_changesv6 ) ?
1 : 0 ;
205 return PVE
:: Firewall
:: run_locked
( $code );
208 __PACKAGE__-
> register_method ({
212 description
=> "Compile and print firewall rules. This is useful for testing." ,
214 additionalProperties
=> 0 ,
217 returns
=> { type
=> 'null' },
222 local $SIG { '__WARN__' } = 'DEFAULT' ; # do not fill up syslog
228 my $cluster_conf = PVE
:: Firewall
:: load_clusterfw_conf
( undef , $verbose );
229 my ( $ruleset, $ipset_ruleset, $rulesetv6 ) = PVE
:: Firewall
:: compile
( $cluster_conf, undef , undef , $verbose );
231 print "ipset cmdlist: \n " ;
232 my ( undef , undef , $ipset_changes ) = PVE
:: Firewall
:: get_ipset_cmdlist
( $ipset_ruleset, $verbose );
234 print " \n iptables cmdlist: \n " ;
235 my ( undef , $ruleset_changes ) = PVE
:: Firewall
:: get_ruleset_cmdlist
( $ruleset, $verbose );
237 print " \n ip6tables cmdlist: \n " ;
238 my ( undef , $ruleset_changesv6 ) = PVE
:: Firewall
:: get_ruleset_cmdlist
( $rulesetv6, $verbose, "ip6tables" );
240 if ( $ipset_changes || $ruleset_changes || $ruleset_changesv6 ) {
241 print "detected changes \n " ;
243 print "no changes \n " ;
245 if (! $cluster_conf ->{ options
}->{ enable
}) {
246 print "firewall disabled \n " ;
251 PVE
:: Firewall
:: run_locked
( $code );
256 __PACKAGE__-
> register_method ({
260 description
=> "Print information about local network." ,
262 additionalProperties
=> 0 ,
265 returns
=> { type
=> 'null' },
269 local $SIG { '__WARN__' } = 'DEFAULT' ; # do not fill up syslog
271 my $nodename = PVE
:: INotify
:: nodename
();
272 print "local hostname: $nodename\n " ;
274 my $ip = PVE
:: Cluster
:: remote_node_ip
( $nodename );
275 print "local IP address: $ip\n " ;
277 my $cluster_conf = PVE
:: Firewall
:: load_clusterfw_conf
();
279 my $localnet = PVE
:: Firewall
:: local_network
() || '127.0.0.0/8' ;
280 print "network auto detect: $localnet\n " ;
281 if ( $cluster_conf ->{ aliases
}->{ local_network
}) {
282 print "using user defined local_network: $cluster_conf ->{aliases}->{local_network}->{cidr} \n " ;
284 print "using detected local_network: $localnet\n " ;
290 __PACKAGE__-
> register_method ({
294 description
=> "Simulate firewall rules. This does not simulate kernel 'routing' table. Instead, this simply assumes that routing from source zone to destination zone is possible." ,
296 additionalProperties
=> 0 ,
299 description
=> "Verbose output." ,
305 description
=> "Source zone." ,
307 pattern
=> '(host|outside|vm\d+|ct\d+|vmbr\d+/\S+)' ,
309 default => 'outside' ,
312 description
=> "Destination zone." ,
314 pattern
=> '(host|outside|vm\d+|ct\d+|vmbr\d+/\S+)' ,
319 description
=> "Protocol." ,
321 pattern
=> '(tcp|udp)' ,
326 description
=> "Destination port." ,
333 description
=> "Source port." ,
340 description
=> "Source IP address." ,
341 type
=> 'string' , format
=> 'ipv4' ,
345 description
=> "Destination IP address." ,
346 type
=> 'string' , format
=> 'ipv4' ,
351 returns
=> { type
=> 'null' },
355 local $SIG { '__WARN__' } = 'DEFAULT' ; # do not fill up syslog
357 my ( $ruleset, $ipset_ruleset, $rulesetv6 ) = PVE
:: Firewall
:: compile
( undef , undef , undef , $param ->{ verbose
});
359 PVE
:: FirewallSimulator
:: debug
( $param ->{ verbose
} || 0 );
361 my $host_ip = PVE
:: Cluster
:: remote_node_ip
( $nodename );
363 PVE
:: FirewallSimulator
:: reset_trace
();
364 print Dumper
( $ruleset ) if $param ->{ verbose
};
367 from
=> $param ->{ from
},
369 proto
=> $param ->{ protocol
} || 'tcp' ,
370 source
=> $param ->{ source
},
371 dest
=> $param ->{ dest
},
372 dport
=> $param ->{ dport
},
373 sport
=> $param ->{ sport
},
376 if (! defined ( $test ->{ to
})) {
377 $test ->{ to
} = 'host' ;
378 PVE
:: FirewallSimulator
:: add_trace
( "Set Zone: to => ' $test ->{to}' \n " );
380 if (! defined ( $test ->{ from
})) {
381 $test ->{ from
} = 'outside' ,
382 PVE
:: FirewallSimulator
:: add_trace
( "Set Zone: from => ' $test ->{from}' \n " );
385 my $vmdata = PVE
:: Firewall
:: read_local_vm_config
();
387 print "Test packet: \n " ;
389 foreach my $k ( qw(from to proto source dest dport sport) ) {
390 printf ( " %-8s: %s\n " , $k, $test ->{ $k }) if defined ( $test ->{ $k });
393 $test ->{ action
} = 'QUERY' ;
395 my $res = PVE
:: FirewallSimulator
:: simulate_firewall
( $ruleset, $ipset_ruleset,
396 $host_ip, $vmdata, $test );
398 print "ACTION: $res\n " ;
404 start
=> [ __PACKAGE__
, 'start' , []],
405 restart
=> [ __PACKAGE__
, 'restart' , []],
406 stop
=> [ __PACKAGE__
, 'stop' , []],
407 compile
=> [ __PACKAGE__
, 'compile' , []],
408 simulate
=> [ __PACKAGE__
, 'simulate' , []],
409 localnet
=> [ __PACKAGE__
, 'localnet' , []],
410 status
=> [ __PACKAGE__
, 'status' , [], undef , sub {
412 my $status = ( $res ->{ enable
} ?
"enabled" : "disabled" ) . '/' . $res ->{ status
};
414 if ( $res ->{ changes
}) {
415 print "Status: $status (pending changes) \n " ;
417 print "Status: $status\n " ;
424 PVE
:: CLIHandler
:: handle_cmd
( $cmddef, $0, $cmd, \
@ARGV, undef , $0 );
432 pve-firewall - PVE Firewall Daemon
440 This service updates iptables rules periodically.
442 =include pve_copyright