1 package PMG
::Config
::Base
;
9 use PVE
::JSONSchema
qw(get_standard_option);
10 use PVE
::SectionConfig
;
12 use base
qw(PVE::SectionConfig);
16 type
=> { description
=> "Section type." },
18 description
=> "Secion ID.",
19 type
=> 'string', format
=> 'pve-configid',
28 sub format_section_header
{
29 my ($class, $type, $sectionId) = @_;
31 die "internal error ($type ne $sectionId)" if $type ne $sectionId;
33 return "section: $type\n";
37 sub parse_section_header
{
38 my ($class, $line) = @_;
40 if ($line =~ m/^section:\s*(\S+)\s*$/) {
42 my $errmsg = undef; # set if you want to skip whole section
43 eval { PVE
::JSONSchema
::pve_verify_configid
($section); };
45 my $config = {}; # to return additional attributes
46 return ($section, $section, $errmsg, $config);
51 package PMG
::Config
::Admin
;
56 use base
qw(PMG::Config::Base);
65 description
=> "Use advanced filters for statistic.",
70 description
=> "Send daily reports.",
75 description
=> "User Statistics Lifetime (days)",
81 description
=> "Demo mode - do not start SMTP filter.",
86 description
=> "Administrator E-Mail address.",
87 type
=> 'string', format
=> 'email',
88 default => 'admin@domain.tld',
91 description
=> "Specify external http proxy which is used for downloads (example: 'http://username:password\@host:port/')",
93 pattern
=> "http://.*",
100 advfilter
=> { optional
=> 1 },
101 statlifetime
=> { optional
=> 1 },
102 dailyreport
=> { optional
=> 1 },
103 demo
=> { optional
=> 1 },
104 email
=> { optional
=> 1 },
105 http_proxy
=> { optional
=> 1 },
109 package PMG
::Config
::Spam
;
114 use base
qw(PMG::Config::Base);
123 description
=> "This option is used to specify which languages are considered OK for incoming mail.",
125 pattern
=> '(all|([a-z][a-z])+( ([a-z][a-z])+)*)',
129 description
=> "Whether to use the naive-Bayesian-style classifier.",
134 description
=> "Use the Auto-Whitelist plugin.",
139 description
=> "Whether to use Razor2, if it is available.",
143 wl_bounce_relays
=> {
144 description
=> "Whitelist legitimate bounce relays.",
148 description
=> "Additional score for bounce mails.",
155 description
=> "Enable real time blacklists (RBL) checks.",
160 description
=> "Maximum size of spam messages in bytes.",
170 use_awl
=> { optional
=> 1 },
171 use_razor
=> { optional
=> 1 },
172 wl_bounce_relays
=> { optional
=> 1 },
173 languages
=> { optional
=> 1 },
174 use_bayes
=> { optional
=> 1 },
175 bounce_score
=> { optional
=> 1 },
176 rbl_checks
=> { optional
=> 1 },
177 maxspamsize
=> { optional
=> 1 },
181 package PMG
::Config
::SpamQuarantine
;
186 use base
qw(PMG::Config::Base);
195 description
=> "Quarantine life time (days)",
201 description
=> "Authentication mode to access the quarantine interface. Mode 'ticket' allows login using tickets sent with the daily spam report. Mode 'ldap' requires to login using an LDAP account. Finally, mode 'ldapticket' allows both ways.",
203 enum
=> [qw(ticket ldap ldapticket)],
207 description
=> "Spam report style.",
209 enum
=> [qw(none short verbose custom)],
210 default => 'verbose',
213 description
=> "Allow to view images.",
218 description
=> "Allow to view hyperlinks.",
223 description
=> "Quarantine Host. Usefule if you run a Cluster and want users to connect to a specific host.",
224 type
=> 'string', format
=> 'address',
227 description
=> "Text for 'From' header in daily spam report mails.",
235 mailfrom
=> { optional
=> 1 },
236 hostname
=> { optional
=> 1 },
237 lifetime
=> { optional
=> 1 },
238 authmode
=> { optional
=> 1 },
239 reportstyle
=> { optional
=> 1 },
240 viewimages
=> { optional
=> 1 },
241 allowhrefs
=> { optional
=> 1 },
245 package PMG
::Config
::VirusQuarantine
;
250 use base
qw(PMG::Config::Base);
262 lifetime
=> { optional
=> 1 },
263 viewimages
=> { optional
=> 1 },
264 allowhrefs
=> { optional
=> 1 },
268 package PMG
::Config
::ClamAV
;
273 use base
qw(PMG::Config::Base);
282 description
=> "ClamAV database mirror server.",
284 default => 'database.clamav.net',
286 archiveblockencrypted
=> {
287 description
=> "Wether to block encrypted archives. Mark encrypted archives as viruses.",
292 description
=> "Nested archives are scanned recursively, e.g. if a ZIP archive contains a TAR file, all files within it will also be scanned. This options specifies how deeply the process should be continued. Warning: setting this limit too high may result in severe damage to the system.",
298 description
=> "Number of files to be scanned within an archive, a document, or any other kind of container. Warning: disabling this limit or setting it too high may result in severe damage to the system.",
304 description
=> "Files larger than this limit won't be scanned.",
310 description
=> "Sets the maximum amount of data to be scanned for each input file.",
313 default => 100000000,
316 description
=> "This option sets the lowest number of Credit Card or Social Security numbers found in a file to generate a detect.",
322 description
=> "Enables support for Google Safe Browsing.",
331 archiveblockencrypted
=> { optional
=> 1 },
332 archivemaxrec
=> { optional
=> 1 },
333 archivemaxfiles
=> { optional
=> 1 },
334 archivemaxsize
=> { optional
=> 1 },
335 maxscansize
=> { optional
=> 1 },
336 dbmirror
=> { optional
=> 1 },
337 maxcccount
=> { optional
=> 1 },
338 safebrowsing
=> { optional
=> 1 },
342 package PMG
::Config
::Mail
;
347 use PVE
::ProcFSTools
;
349 use base
qw(PMG::Config::Base);
356 sub physical_memory
{
358 return $physicalmem if $physicalmem;
360 my $info = PVE
::ProcFSTools
::read_meminfo
();
361 my $total = int($info->{memtotal
} / (1024*1024));
366 sub get_max_filters
{
367 # estimate optimal number of filter servers
371 my $memory = physical_memory
();
372 my $add_servers = int(($memory - 512)/$servermem);
373 $max_servers += $add_servers if $add_servers > 0;
374 $max_servers = 40 if $max_servers > 40;
376 return $max_servers - 2;
380 # estimate optimal number of smtpd daemons
382 my $max_servers = 25;
384 my $memory = physical_memory
();
385 my $add_servers = int(($memory - 512)/$servermem);
386 $max_servers += $add_servers if $add_servers > 0;
387 $max_servers = 100 if $max_servers > 100;
392 # estimate optimal number of proxpolicy servers
394 my $memory = physical_memory
();
395 $max_servers = 5 if $memory >= 500;
402 description
=> "SMTP port number for outgoing mail (trusted).",
409 description
=> "SMTP port number for incoming mail (untrusted). This must be a different number than 'int_port'.",
416 description
=> "The default mail delivery transport (incoming mails).",
417 type
=> 'string', format
=> 'address',
420 description
=> "SMTP port number for relay host.",
427 description
=> "Disable MX lookups for default relay.",
432 description
=> "When set, all outgoing mails are deliverd to the specified smarthost.",
433 type
=> 'string', format
=> 'address',
436 description
=> "ESMTP banner.",
439 default => 'ESMTP Proxmox',
442 description
=> "Maximum number of pmg-smtp-filter processes.",
446 default => get_max_filters
(),
449 description
=> "Maximum number of pmgpolicy processes.",
453 default => get_max_policy
(),
456 description
=> "Maximum number of SMTP daemon processes (in).",
460 default => get_max_smtpd
(),
463 description
=> "Maximum number of SMTP daemon processes (out).",
467 default => get_max_smtpd
(),
469 conn_count_limit
=> {
470 description
=> "How many simultaneous connections any client is allowed to make to this service. To disable this feature, specify a limit of 0.",
476 description
=> "The maximal number of connection attempts any client is allowed to make to this service per minute. To disable this feature, specify a limit of 0.",
481 message_rate_limit
=> {
482 description
=> "The maximal number of message delivery requests that any client is allowed to make to this service per minute.To disable this feature, specify a limit of 0.",
488 description
=> "Hide received header in outgoing mails.",
493 description
=> "Maximum email size. Larger mails are rejected.",
496 default => 1024*1024*10,
499 description
=> "SMTP delay warning time (in hours).",
505 description
=> "Use Realtime Blacklists.",
510 description
=> "Enable TLS.",
515 description
=> "Enable TLS Logging.",
520 description
=> "Add TLS received header.",
525 description
=> "Use Sender Policy Framework.",
530 description
=> "Use Greylisting.",
535 description
=> "Use SMTP HELO tests.",
540 description
=> "Reject unknown clients.",
544 rejectunknownsender
=> {
545 description
=> "Reject unknown senders.",
550 description
=> "Enable receiver verification. The value spefifies the numerical reply code when the Postfix SMTP server rejects a recipient address.",
552 enum
=> ['450', '550'],
555 description
=> "Optional list of DNS white/blacklist domains (see postscreen_dnsbl_sites parameter).",
563 int_port
=> { optional
=> 1 },
564 ext_port
=> { optional
=> 1 },
565 smarthost
=> { optional
=> 1 },
566 relay
=> { optional
=> 1 },
567 relayport
=> { optional
=> 1 },
568 relaynomx
=> { optional
=> 1 },
569 dwarning
=> { optional
=> 1 },
570 max_smtpd_in
=> { optional
=> 1 },
571 max_smtpd_out
=> { optional
=> 1 },
572 greylist
=> { optional
=> 1 },
573 helotests
=> { optional
=> 1 },
574 use_rbl
=> { optional
=> 1 },
575 tls
=> { optional
=> 1 },
576 tlslog
=> { optional
=> 1 },
577 tlsheader
=> { optional
=> 1 },
578 spf
=> { optional
=> 1 },
579 maxsize
=> { optional
=> 1 },
580 banner
=> { optional
=> 1 },
581 max_filters
=> { optional
=> 1 },
582 max_policy
=> { optional
=> 1 },
583 hide_received
=> { optional
=> 1 },
584 rejectunknown
=> { optional
=> 1 },
585 rejectunknownsender
=> { optional
=> 1 },
586 conn_count_limit
=> { optional
=> 1 },
587 conn_rate_limit
=> { optional
=> 1 },
588 message_rate_limit
=> { optional
=> 1 },
589 verifyreceivers
=> { optional
=> 1 },
590 dnsbl_sites
=> { optional
=> 1 },
603 use PVE
::Tools
qw($IPV4RE $IPV6RE);
609 PMG
::Config
::Admin-
>register();
610 PMG
::Config
::Mail-
>register();
611 PMG
::Config
::SpamQuarantine-
>register();
612 PMG
::Config
::VirusQuarantine-
>register();
613 PMG
::Config
::Spam-
>register();
614 PMG
::Config
::ClamAV-
>register();
616 # initialize all plugins
617 PMG
::Config
::Base-
>init();
619 PVE
::JSONSchema
::register_format
(
620 'transport-domain', \
&pmg_verify_transport_domain
);
621 sub pmg_verify_transport_domain
{
622 my ($name, $noerr) = @_;
624 # like dns-name, but can contain leading dot
625 my $namere = "([a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?)";
627 if ($name !~ /^\.?(${namere}\.)*${namere}$/) {
628 return undef if $noerr;
629 die "value does not look like a valid transport domain\n";
637 my $class = ref($type) || $type;
639 my $cfg = PVE
::INotify
::read_file
("pmg.conf");
641 return bless $cfg, $class;
647 PVE
::INotify
::write_file
("pmg.conf", $self);
650 my $lockfile = "/var/lock/pmgconfig.lck";
653 my ($code, $errmsg) = @_;
655 my $p = PVE
::Tools
::lock_file
($lockfile, undef, $code);
657 $errmsg ?
die "$errmsg: $err" : die $err;
663 my ($self, $section, $key, $value) = @_;
665 my $pdata = PMG
::Config
::Base-
>private();
667 my $plugin = $pdata->{plugins
}->{$section};
668 die "no such section '$section'" if !$plugin;
670 if (defined($value)) {
671 my $tmp = PMG
::Config
::Base-
>check_value($section, $key, $value, $section, 0);
672 $self->{ids
}->{$section} = { type
=> $section } if !defined($self->{ids
}->{$section});
673 $self->{ids
}->{$section}->{$key} = PMG
::Config
::Base-
>decode_value($section, $key, $tmp);
675 if (defined($self->{ids
}->{$section})) {
676 delete $self->{ids
}->{$section}->{$key};
683 # get section value or default
685 my ($self, $section, $key, $nodefault) = @_;
687 my $pdata = PMG
::Config
::Base-
>private();
688 my $pdesc = $pdata->{propertyList
}->{$key};
689 die "no such property '$section/$key'\n"
690 if !(defined($pdesc) && defined($pdata->{options
}->{$section}) &&
691 defined($pdata->{options
}->{$section}->{$key}));
693 if (defined($self->{ids
}->{$section}) &&
694 defined(my $value = $self->{ids
}->{$section}->{$key})) {
698 return undef if $nodefault;
700 return $pdesc->{default};
703 # get a whole section with default value
705 my ($self, $section) = @_;
707 my $pdata = PMG
::Config
::Base-
>private();
708 return undef if !defined($pdata->{options
}->{$section});
712 foreach my $key (keys %{$pdata->{options
}->{$section}}) {
714 my $pdesc = $pdata->{propertyList
}->{$key};
716 if (defined($self->{ids
}->{$section}) &&
717 defined(my $value = $self->{ids
}->{$section}->{$key})) {
718 $res->{$key} = $value;
721 $res->{$key} = $pdesc->{default};
727 # get a whole config with default values
731 my $pdata = PMG
::Config
::Base-
>private();
735 foreach my $type (keys %{$pdata->{plugins
}}) {
736 my $plugin = $pdata->{plugins
}->{$type};
737 $res->{$type} = $self->get_section($type);
744 my ($filename, $fh) = @_;
746 local $/ = undef; # slurp mode
748 my $raw = <$fh> if defined($fh);
750 return PMG
::Config
::Base-
>parse_config($filename, $raw);
754 my ($filename, $fh, $cfg) = @_;
756 my $raw = PMG
::Config
::Base-
>write_config($filename, $cfg);
758 PVE
::Tools
::safe_print
($filename, $fh, $raw);
761 PVE
::INotify
::register_file
('pmg.conf', "/etc/pmg/pmg.conf",
764 undef, always_call_parser
=> 1);
766 # parsers/writers for other files
768 my $domainsfilename = "/etc/pmg/domains";
770 sub postmap_pmg_domains
{
771 PMG
::Utils
::run_postmap
($domainsfilename);
774 sub read_pmg_domains
{
775 my ($filename, $fh) = @_;
781 while (defined(my $line = <$fh>)) {
783 next if $line =~ m/^\s*$/;
784 if ($line =~ m/^#(.*)\s*$/) {
788 if ($line =~ m/^(\S+)\s.*$/) {
790 $domains->{$domain} = {
791 domain
=> $domain, comment
=> $comment };
794 warn "parse error in '$filename': $line\n";
803 sub write_pmg_domains
{
804 my ($filename, $fh, $domains) = @_;
806 foreach my $domain (sort keys %$domains) {
807 my $comment = $domains->{$domain}->{comment
};
808 PVE
::Tools
::safe_print
($filename, $fh, "#$comment\n")
809 if defined($comment) && $comment !~ m/^\s*$/;
811 PVE
::Tools
::safe_print
($filename, $fh, "$domain 1\n");
815 PVE
::INotify
::register_file
('domains', $domainsfilename,
818 undef, always_call_parser
=> 1);
820 my $mynetworks_filename = "/etc/pmg/mynetworks";
822 sub postmap_pmg_mynetworks
{
823 PMG
::Utils
::run_postmap
($mynetworks_filename);
826 sub read_pmg_mynetworks
{
827 my ($filename, $fh) = @_;
833 while (defined(my $line = <$fh>)) {
835 next if $line =~ m/^\s*$/;
836 if ($line =~ m!^((?:$IPV4RE|$IPV6RE))/(\d+)\s*(?:#(.*)\s*)?$!) {
837 my ($network, $prefix_size, $comment) = ($1, $2, $3);
838 my $cidr = "$network/${prefix_size}";
839 $mynetworks->{$cidr} = {
841 network_address
=> $network,
842 prefix_size
=> $prefix_size,
843 comment
=> $comment // '',
846 warn "parse error in '$filename': $line\n";
854 sub write_pmg_mynetworks
{
855 my ($filename, $fh, $mynetworks) = @_;
857 foreach my $cidr (sort keys %$mynetworks) {
858 my $data = $mynetworks->{$cidr};
859 my $comment = $data->{comment
} // '*';
860 PVE
::Tools
::safe_print
($filename, $fh, "$cidr #$comment\n");
864 PVE
::INotify
::register_file
('mynetworks', $mynetworks_filename,
865 \
&read_pmg_mynetworks
,
866 \
&write_pmg_mynetworks
,
867 undef, always_call_parser
=> 1);
869 my $transport_map_filename = "/etc/pmg/transport";
871 sub postmap_pmg_transport
{
872 PMG
::Utils
::run_postmap
($transport_map_filename);
875 sub read_transport_map
{
876 my ($filename, $fh) = @_;
878 return [] if !defined($fh);
884 while (defined(my $line = <$fh>)) {
886 next if $line =~ m/^\s*$/;
887 if ($line =~ m/^#(.*)\s*$/) {
892 my $parse_error = sub {
894 warn "parse error in '$filename': $line - $err";
898 if ($line =~ m/^(\S+)\s+smtp:(\S+):(\d+)\s*$/) {
899 my ($domain, $host, $port) = ($1, $2, $3);
901 eval { pmg_verify_transport_domain
($domain); };
903 $parse_error->($err);
907 if ($host =~ m/^\[(.*)\]$/) {
912 eval { PVE
::JSONSchema
::pve_verify_address
($host); };
914 $parse_error->($err);
925 $res->{$domain} = $data;
928 $parse_error->('wrong format');
935 sub write_transport_map
{
936 my ($filename, $fh, $tmap) = @_;
940 foreach my $domain (sort keys %$tmap) {
941 my $data = $tmap->{$domain};
943 my $comment = $data->{comment
};
944 PVE
::Tools
::safe_print
($filename, $fh, "#$comment\n")
945 if defined($comment) && $comment !~ m/^\s*$/;
947 my $use_mx = $data->{use_mx
};
948 $use_mx = 0 if $data->{host
} =~ m/^(?:$IPV4RE|$IPV6RE)$/;
951 PVE
::Tools
::safe_print
(
952 $filename, $fh, "$data->{domain} smtp:$data->{host}:$data->{port}\n");
954 PVE
::Tools
::safe_print
(
955 $filename, $fh, "$data->{domain} smtp:[$data->{host}]:$data->{port}\n");
960 PVE
::INotify
::register_file
('transport', $transport_map_filename,
961 \
&read_transport_map
,
962 \
&write_transport_map
,
963 undef, always_call_parser
=> 1);
965 # config file generation using templates
967 sub get_template_vars
{
970 my $vars = { pmg
=> $self->get_config() };
972 my $nodename = PVE
::INotify
::nodename
();
973 my $int_ip = PMG
::Cluster
::remote_node_ip
($nodename);
974 my $int_net_cidr = PMG
::Utils
::find_local_network_for_ip
($int_ip);
975 $vars->{ipconfig
}->{int_ip
} = $int_ip;
976 # $vars->{ipconfig}->{int_net_cidr} = $int_net_cidr;
978 my $transportnets = [];
980 my $tmap = PVE
::INotify
::read_file
('transport');
981 foreach my $domain (sort keys %$tmap) {
982 my $data = $tmap->{$domain};
983 my $host = $data->{host
};
984 if ($host =~ m/^$IPV4RE$/) {
985 push @$transportnets, "$host/32";
986 } elsif ($host =~ m/^$IPV6RE$/) {
987 push @$transportnets, "[$host]/128";
991 $vars->{postfix
}->{transportnets
} = join(' ', @$transportnets);
993 my $mynetworks = [ '127.0.0.0/8', '[::1]/128' ];
994 push @$mynetworks, @$transportnets;
995 push @$mynetworks, $int_net_cidr;
996 push @$mynetworks, 'hash:/etc/pmg/mynetworks';
998 my $netlist = PVE
::INotify
::read_file
('mynetworks');
999 # add default relay to mynetworks
1000 if (my $relay = $self->get('mail', 'relay')) {
1001 if ($relay =~ m/^$IPV4RE$/) {
1002 push @$mynetworks, "$relay/32";
1003 } elsif ($relay =~ m/^$IPV6RE$/) {
1004 push @$mynetworks, "[$relay]/128";
1006 # DNS name - do nothing ?
1010 $vars->{postfix
}->{mynetworks
} = join(' ', @$mynetworks);
1013 $usepolicy = 1 if $self->get('mail', 'greylist') ||
1014 $self->get('mail', 'spf') || $self->get('mail', 'use_rbl');
1015 $vars->{postfix
}->{usepolicy
} = $usepolicy;
1017 my $resolv = PVE
::INotify
::read_file
('resolvconf');
1018 $vars->{dns
}->{hostname
} = $nodename;
1019 $vars->{dns
}->{domain
} = $resolv->{search
};
1021 my $wlbr = "$nodename.$resolv->{search}";
1022 foreach my $r (PVE
::Tools
::split_list
($vars->{pmg
}->{spam
}->{wl_bounce_relays
})) {
1025 $vars->{composed
}->{wl_bounce_relays
} = $wlbr;
1027 if (my $proxy = $vars->{pmg
}->{admin
}->{http_proxy
}) {
1029 my $uri = URI-
>new($proxy);
1030 my $host = $uri->host;
1031 my $port = $uri->port // 8080;
1033 my $data = { host
=> $host, port
=> $port };
1034 if (my $ui = $uri->userinfo) {
1035 my ($username, $pw) = split(/:/, $ui, 2);
1036 $data->{username
} = $username;
1037 $data->{password
} = $pw if defined($pw);
1039 $vars->{proxy
} = $data;
1042 warn "parse http_proxy failed - $@" if $@;
1048 # use one global TT cache
1049 our $tt_include_path = ['/etc/pmg/templates' ,'/var/lib/pmg/templates' ];
1051 my $template_toolkit;
1053 sub get_template_toolkit
{
1055 return $template_toolkit if $template_toolkit;
1057 $template_toolkit = Template-
>new({ INCLUDE_PATH
=> $tt_include_path });
1059 return $template_toolkit;
1062 # rewrite file from template
1063 # return true if file has changed
1064 sub rewrite_config_file
{
1065 my ($self, $tmplname, $dstfn) = @_;
1067 my $demo = $self->get('admin', 'demo');
1070 my $demosrc = "$tmplname.demo";
1071 $tmplname = $demosrc if -f
"/var/lib/pmg/templates/$demosrc";
1074 my ($perm, $uid, $gid);
1076 if ($dstfn eq '/etc/clamav/freshclam.conf') {
1077 # needed if file contains a HTTPProxyPasswort
1079 $uid = getpwnam('clamav');
1080 $gid = getgrnam('adm');
1084 my $tt = get_template_toolkit
();
1086 my $vars = $self->get_template_vars();
1090 $tt->process($tmplname, $vars, \
$output) ||
1091 die $tt->error() . "\n";
1093 my $old = PVE
::Tools
::file_get_contents
($dstfn, 128*1024) if -f
$dstfn;
1095 return 0 if defined($old) && ($old eq $output); # no change
1097 PVE
::Tools
::file_set_contents
($dstfn, $output, $perm);
1099 if (defined($uid) && defined($gid)) {
1100 chown($uid, $gid, $dstfn);
1106 # rewrite spam configuration
1107 sub rewrite_config_spam
{
1110 my $use_awl = $self->get('spam', 'use_awl');
1111 my $use_bayes = $self->get('spam', 'use_bayes');
1112 my $use_razor = $self->get('spam', 'use_razor');
1116 # delete AW and bayes databases if those features are disabled
1118 $changes = 1 if unlink '/root/.spamassassin/auto-whitelist';
1122 $changes = 1 if unlink '/root/.spamassassin/bayes_journal';
1123 $changes = 1 if unlink '/root/.spamassassin/bayes_seen';
1124 $changes = 1 if unlink '/root/.spamassassin/bayes_toks';
1127 # make sure we have a custom.cf file (else cluster sync fails)
1128 IO
::File-
>new('/etc/mail/spamassassin/custom.cf', 'a', 0644);
1130 $changes = 1 if $self->rewrite_config_file(
1131 'local.cf.in', '/etc/mail/spamassassin/local.cf');
1133 $changes = 1 if $self->rewrite_config_file(
1134 'init.pre.in', '/etc/mail/spamassassin/init.pre');
1136 $changes = 1 if $self->rewrite_config_file(
1137 'v310.pre.in', '/etc/mail/spamassassin/v310.pre');
1139 $changes = 1 if $self->rewrite_config_file(
1140 'v320.pre.in', '/etc/mail/spamassassin/v320.pre');
1143 mkdir "/root/.razor";
1145 $changes = 1 if $self->rewrite_config_file(
1146 'razor-agent.conf.in', '/root/.razor/razor-agent.conf');
1148 if (! -e
'/root/.razor/identity') {
1151 PVE
::Tools
::run_command
(['razor-admin', '-discover'], timeout
=> $timeout);
1152 PVE
::Tools
::run_command
(['razor-admin', '-register'], timeout
=> $timeout);
1155 syslog
('info', "registering razor failed: $err") if $err;
1162 # rewrite ClamAV configuration
1163 sub rewrite_config_clam
{
1166 return $self->rewrite_config_file(
1167 'clamd.conf.in', '/etc/clamav/clamd.conf');
1170 sub rewrite_config_freshclam
{
1173 return $self->rewrite_config_file(
1174 'freshclam.conf.in', '/etc/clamav/freshclam.conf');
1177 sub rewrite_config_postgres
{
1180 my $pgconfdir = "/etc/postgresql/9.6/main";
1184 $changes = 1 if $self->rewrite_config_file(
1185 'pg_hba.conf.in', "$pgconfdir/pg_hba.conf");
1187 $changes = 1 if $self->rewrite_config_file(
1188 'postgresql.conf.in', "$pgconfdir/postgresql.conf");
1193 # rewrite /root/.forward
1194 sub rewrite_dot_forward
{
1197 my $dstfn = '/root/.forward';
1199 my $email = $self->get('admin', 'email');
1202 if ($email && $email =~ m/\s*(\S+)\s*/) {
1205 # empty .forward does not forward mails (see man local)
1208 my $old = PVE
::Tools
::file_get_contents
($dstfn, 128*1024) if -f
$dstfn;
1210 return 0 if defined($old) && ($old eq $output); # no change
1212 PVE
::Tools
::file_set_contents
($dstfn, $output);
1217 my $write_smtp_whitelist = sub {
1218 my ($filename, $data, $action) = @_;
1220 $action = 'OK' if !$action;
1222 my $old = PVE
::Tools
::file_get_contents
($filename, 1024*1024)
1226 foreach my $k (sort keys %$data) {
1227 $new .= "$k $action\n";
1230 return 0 if defined($old) && ($old eq $new); # no change
1232 PVE
::Tools
::file_set_contents
($filename, $new);
1234 PMG
::Utils
::run_postmap
($filename);
1239 my $rewrite_config_whitelist = sub {
1240 my ($rulecache) = @_;
1242 # see man page for regexp_table for postfix regex table format
1244 # we use a hash to avoid duplicate entries in regex tables
1247 my $clientlist = {};
1249 foreach my $obj (@{$rulecache->{"greylist:receiver"}}) {
1250 my $oclass = ref($obj);
1251 if ($oclass eq 'PMG::RuleDB::Receiver') {
1252 my $addr = PMG
::Utils
::quote_regex
($obj->{address
});
1253 $tolist->{"/^$addr\$/"} = 1;
1254 } elsif ($oclass eq 'PMG::RuleDB::ReceiverDomain') {
1255 my $addr = PMG
::Utils
::quote_regex
($obj->{address
});
1256 $tolist->{"/^.+\@$addr\$/"} = 1;
1257 } elsif ($oclass eq 'PMG::RuleDB::ReceiverRegex') {
1258 my $addr = $obj->{address
};
1260 $tolist->{"/^$addr\$/"} = 1;
1264 foreach my $obj (@{$rulecache->{"greylist:sender"}}) {
1265 my $oclass = ref($obj);
1266 my $addr = PMG
::Utils
::quote_regex
($obj->{address
});
1267 if ($oclass eq 'PMG::RuleDB::EMail') {
1268 my $addr = PMG
::Utils
::quote_regex
($obj->{address
});
1269 $fromlist->{"/^$addr\$/"} = 1;
1270 } elsif ($oclass eq 'PMG::RuleDB::Domain') {
1271 my $addr = PMG
::Utils
::quote_regex
($obj->{address
});
1272 $fromlist->{"/^.+\@$addr\$/"} = 1;
1273 } elsif ($oclass eq 'PMG::RuleDB::WhoRegex') {
1274 my $addr = $obj->{address
};
1276 $fromlist->{"/^$addr\$/"} = 1;
1277 } elsif ($oclass eq 'PMG::RuleDB::IPAddress') {
1278 $clientlist->{$obj->{address
}} = 1;
1279 } elsif ($oclass eq 'PMG::RuleDB::IPNet') {
1280 $clientlist->{$obj->{address
}} = 1;
1284 $write_smtp_whitelist->("/etc/postfix/senderaccess", $fromlist);
1285 $write_smtp_whitelist->("/etc/postfix/rcptaccess", $tolist);
1286 $write_smtp_whitelist->("/etc/postfix/clientaccess", $clientlist);
1287 $write_smtp_whitelist->("/etc/postfix/postscreen_access", $clientlist, 'permit');
1290 # rewrite /etc/postfix/*
1291 sub rewrite_config_postfix
{
1292 my ($self, $rulecache) = @_;
1294 # make sure we have required files (else postfix start fails)
1295 postmap_pmg_domains
();
1296 postmap_pmg_transport
();
1297 postmap_pmg_mynetworks
();
1299 IO
::File-
>new($transport_map_filename, 'a', 0644);
1303 if ($self->get('mail', 'tls')) {
1305 PMG
::Utils
::gen_proxmox_tls_cert
();
1307 syslog
('info', "generating certificate failed: $@") if $@;
1310 $changes = 1 if $self->rewrite_config_file(
1311 'main.cf.in', '/etc/postfix/main.cf');
1313 $changes = 1 if $self->rewrite_config_file(
1314 'master.cf.in', '/etc/postfix/master.cf');
1316 $rewrite_config_whitelist->($rulecache) if $rulecache;
1318 # fixme: rewrite_config_tls_policy ($class);
1320 # make sure aliases.db is up to date
1321 system('/usr/bin/newaliases');
1326 sub rewrite_config
{
1327 my ($self, $rulecache, $restart_services, $force_restart) = @_;
1329 $force_restart = {} if ! $force_restart;
1331 if (($self->rewrite_config_postfix($rulecache) && $restart_services) ||
1332 $force_restart->{postfix
}) {
1333 PMG
::Utils
::service_cmd
('postfix', 'restart');
1336 if ($self->rewrite_dot_forward() && $restart_services) {
1337 # no need to restart anything
1340 if ($self->rewrite_config_postgres() && $restart_services) {
1341 # do nothing (too many side effects)?
1342 # does not happen anyways, because config does not change.
1345 if (($self->rewrite_config_spam() && $restart_services) ||
1346 $force_restart->{spam
}) {
1347 PMG
::Utils
::service_cmd
('pmg-smtp-filter', 'restart');
1350 if (($self->rewrite_config_clam() && $restart_services) ||
1351 $force_restart->{clam
}) {
1352 PMG
::Utils
::service_cmd
('clamav-daemon', 'restart');
1355 if (($self->rewrite_config_freshclam() && $restart_services) ||
1356 $force_restart->{freshclam
}) {
1357 PMG
::Utils
::service_cmd
('clamav-freshclam', 'restart');