X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=PMG%2FConfig.pm;h=2392db8eaefeff84eba5351280cfeea4c4d472ff;hb=670ca9b933688f89162bdf7e12778733a4197de6;hp=18b076aefd0e9c258db86315863a66f56bae4462;hpb=2664d3cbad4746edf83e4fde05c2f760134b1f50;p=pmg-api.git diff --git a/PMG/Config.pm b/PMG/Config.pm index 18b076a..2392db8 100755 --- a/PMG/Config.pm +++ b/PMG/Config.pm @@ -15,7 +15,7 @@ my $defaultData = { propertyList => { type => { description => "Section type." }, section => { - description => "Secion ID.", + description => "Section ID.", type => 'string', format => 'pve-configid', }, }, @@ -92,17 +92,41 @@ sub properties { type => 'string', pattern => "http://.*", }, + avast => { + description => "Use Avast Virus Scanner (/usr/bin/scan). You need to buy and install 'Avast Core Security' before you can enable this feature.", + type => 'boolean', + default => 0, + }, + clamav => { + description => "Use ClamAV Virus Scanner. This is the default virus scanner and is enabled by default.", + type => 'boolean', + default => 1, + }, + custom_check => { + description => "Use Custom Check Script. The script has to take the defined arguments and can return Virus findings or a Spamscore.", + type => 'boolean', + default => 0, + }, + custom_check_path => { + description => "Absolute Path to the Custom Check Script", + type => 'string', pattern => '^/([^/\0]+\/)+[^/\0]+$', + default => '/usr/local/bin/pmg-custom-check', + }, }; } sub options { return { advfilter => { optional => 1 }, + avast => { optional => 1 }, + clamav => { optional => 1 }, statlifetime => { optional => 1 }, dailyreport => { optional => 1 }, demo => { optional => 1 }, email => { optional => 1 }, http_proxy => { optional => 1 }, + custom_check => { optional => 1 }, + custom_check_path => { optional => 1 }, }; } @@ -145,7 +169,7 @@ sub properties { type => 'string', }, clamav_heuristic_score => { - description => "Score for ClamaAV heuristics (Google Safe Browsing database, PhishingScanURLs, ...).", + description => "Score for ClamAV heuristics (Google Safe Browsing database, PhishingScanURLs, ...).", type => 'integer', minimum => 0, maximum => 1000, @@ -307,7 +331,7 @@ sub properties { default => 'database.clamav.net', }, archiveblockencrypted => { - description => "Wether to block encrypted archives. Mark encrypted archives as viruses.", + description => "Whether to block encrypted archives. Mark encrypted archives as viruses.", type => 'boolean', default => 0, }, @@ -455,6 +479,13 @@ sub properties { description => "When set, all outgoing mails are deliverd to the specified smarthost.", type => 'string', format => 'address', }, + smarthostport => { + description => "SMTP port number for smarthost.", + type => 'integer', + minimum => 1, + maximum => 65535, + default => 25, + }, banner => { description => "ESMTP banner.", type => 'string', @@ -573,6 +604,12 @@ sub properties { description => "Optional list of DNS white/blacklist domains (see postscreen_dnsbl_sites parameter).", type => 'string', format => 'dnsbl-entry-list', }, + dnsbl_threshold => { + description => "The inclusive lower bound for blocking a remote SMTP client, based on its combined DNSBL score (see postscreen_dnsbl_threshold parameter).", + type => 'integer', + minimum => 0, + default => 1 + }, }; } @@ -581,6 +618,7 @@ sub options { int_port => { optional => 1 }, ext_port => { optional => 1 }, smarthost => { optional => 1 }, + smarthostport => { optional => 1 }, relay => { optional => 1 }, relayport => { optional => 1 }, relaynomx => { optional => 1 }, @@ -605,6 +643,7 @@ sub options { message_rate_limit => { optional => 1 }, verifyreceivers => { optional => 1 }, dnsbl_sites => { optional => 1 }, + dnsbl_threshold => { optional => 1 }, }; } @@ -649,16 +688,42 @@ sub pmg_verify_transport_domain { return $name; } +PVE::JSONSchema::register_format( + 'transport-domain-or-email', \&pmg_verify_transport_domain_or_email); + +sub pmg_verify_transport_domain_or_email { + my ($name, $noerr) = @_; + + my $namere = "([a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?)"; + + # email address + if ($name =~ m/^(?:[^\s\/\@]+\@)(${namere}\.)*${namere}$/) { + return $name; + } + + # like dns-name, but can contain leading dot + if ($name !~ /^\.?(${namere}\.)*${namere}$/) { + return undef if $noerr; + die "value does not look like a valid transport domain or email address\n"; + } + return $name; +} + PVE::JSONSchema::register_format( 'dnsbl-entry', \&pmg_verify_dnsbl_entry); sub pmg_verify_dnsbl_entry { my ($name, $noerr) = @_; - # like dns-name, but can contain trailing weight: 'domain*' + # like dns-name, but can contain trailing filter and weight: 'domain=*' + # see http://www.postfix.org/postconf.5.html#postscreen_dnsbl_sites + # we don't implement the ';' separated numbers in pattern, because this + # breaks at PVE::JSONSchema::split_list my $namere = "([a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?)"; - if ($name !~ /^(${namere}\.)*${namere}(\*\-?\d+)?$/) { + my $dnsbloctet = qr/[0-9]+|\[(?:[0-9]+\.\.[0-9]+)\]/; + my $filterre = qr/=$dnsbloctet(:?\.$dnsbloctet){3}/; + if ($name !~ /^(${namere}\.)*${namere}(:?${filterre})?(?:\*\-?\d+)?$/) { return undef if $noerr; die "value '$name' does not look like a valid dnsbl entry\n"; } @@ -896,7 +961,92 @@ PVE::INotify::register_file('mynetworks', $mynetworks_filename, \&write_pmg_mynetworks, undef, always_call_parser => 1); +PVE::JSONSchema::register_format( + 'tls-policy', \&pmg_verify_tls_policy); + +# TODO: extend to parse attributes of the policy +my $VALID_TLS_POLICY_RE = qr/none|may|encrypt|dane|dane-only|fingerprint|verify|secure/; +sub pmg_verify_tls_policy { + my ($policy, $noerr) = @_; + + if ($policy !~ /^$VALID_TLS_POLICY_RE\b/) { + return undef if $noerr; + die "value '$policy' does not look like a valid tls policy\n"; + } + return $policy; +} + +PVE::JSONSchema::register_format( + 'tls-policy-strict', \&pmg_verify_tls_policy_strict); + +sub pmg_verify_tls_policy_strict { + my ($policy, $noerr) = @_; + + if ($policy !~ /^$VALID_TLS_POLICY_RE$/) { + return undef if $noerr; + die "value '$policy' does not look like a valid tls policy\n"; + } + return $policy; +} + +sub read_tls_policy { + my ($filename, $fh) = @_; + + return {} if !defined($fh); + + my $tls_policy = {}; + + while (defined(my $line = <$fh>)) { + chomp $line; + next if $line =~ m/^\s*$/; + next if $line =~ m/^#(.*)\s*$/; + + my $parse_error = sub { + my ($err) = @_; + die "parse error in '$filename': $line - $err"; + }; + + if ($line =~ m/^(\S+)\s+(.+)\s*$/) { + my ($domain, $policy) = ($1, $2); + + eval { + pmg_verify_transport_domain($domain); + pmg_verify_tls_policy($policy); + }; + if (my $err = $@) { + $parse_error->($err); + next; + } + + $tls_policy->{$domain} = { + domain => $domain, + policy => $policy, + }; + } else { + $parse_error->('wrong format'); + } + } + + return $tls_policy; +} + +sub write_tls_policy { + my ($filename, $fh, $tls_policy) = @_; + + return if !$tls_policy; + + foreach my $domain (sort keys %$tls_policy) { + my $entry = $tls_policy->{$domain}; + PVE::Tools::safe_print( + $filename, $fh, "$entry->{domain} $entry->{policy}\n"); + } +} + my $tls_policy_map_filename = "/etc/pmg/tls_policy"; +PVE::INotify::register_file('tls_policy', $tls_policy_map_filename, + \&read_tls_policy, + \&write_tls_policy, + undef, always_call_parser => 1); sub postmap_tls_policy { PMG::Utils::run_postmap($tls_policy_map_filename); @@ -934,7 +1084,7 @@ sub read_transport_map { if ($line =~ m/^(\S+)\s+smtp:(\S+):(\d+)\s*$/) { my ($domain, $host, $port) = ($1, $2, $3); - eval { pmg_verify_transport_domain($domain); }; + eval { pmg_verify_transport_domain_or_email($domain); }; if (my $err = $@) { $parse_error->($err); next; @@ -1042,7 +1192,7 @@ sub get_template_vars { } my $netlist = PVE::INotify::read_file('mynetworks'); - foreach my $cidr (keys %$netlist) { + foreach my $cidr (sort keys %$netlist) { if ($cidr =~ m/^($IPV6RE)\/(\d+)$/) { push @$mynetworks, "[$1]/$2"; } else { @@ -1071,6 +1221,8 @@ sub get_template_vars { $vars->{postfix}->{dnsbl_sites} = join(',', @dnsbl_sites); } + $vars->{postfix}->{dnsbl_threshold} = $self->get('mail', 'dnsbl_threshold'); + my $usepolicy = 0; $usepolicy = 1 if $self->get('mail', 'greylist') || $self->get('mail', 'spf'); @@ -1398,9 +1550,14 @@ sub rewrite_config { $force_restart = {} if ! $force_restart; + my $log_restart = sub { + syslog ('info', "configuration change detected for '$_[0]', restarting"); + }; + if (($self->rewrite_config_postfix($rulecache) && $restart_services) || $force_restart->{postfix}) { - PMG::Utils::service_cmd('postfix', 'restart'); + $log_restart->('postfix'); + PMG::Utils::service_cmd('postfix', 'reload'); } if ($self->rewrite_dot_forward() && $restart_services) { @@ -1414,16 +1571,19 @@ sub rewrite_config { if (($self->rewrite_config_spam() && $restart_services) || $force_restart->{spam}) { + $log_restart->('pmg-smtp-filter'); PMG::Utils::service_cmd('pmg-smtp-filter', 'restart'); } if (($self->rewrite_config_clam() && $restart_services) || $force_restart->{clam}) { + $log_restart->('clamav-daemon'); PMG::Utils::service_cmd('clamav-daemon', 'restart'); } if (($self->rewrite_config_freshclam() && $restart_services) || $force_restart->{freshclam}) { + $log_restart->('clamav-freshclam'); PMG::Utils::service_cmd('clamav-freshclam', 'restart'); } }