1 package PMG
::Config
::Base
;
8 use PVE
::JSONSchema
qw(get_standard_option);
9 use PVE
::SectionConfig
;
11 use base
qw(PVE::SectionConfig);
15 type
=> { description
=> "Section type." },
17 description
=> "Secion ID.",
18 type
=> 'string', format
=> 'pve-configid',
27 sub format_section_header
{
28 my ($class, $type, $sectionId) = @_;
30 die "internal error ($type ne $sectionId)" if $type ne $sectionId;
32 return "section: $type\n";
36 sub parse_section_header
{
37 my ($class, $line) = @_;
39 if ($line =~ m/^section:\s*(\S+)\s*$/) {
41 my $errmsg = undef; # set if you want to skip whole section
42 eval { PVE
::JSONSchema
::pve_verify_configid
($section); };
44 my $config = {}; # to return additional attributes
45 return ($section, $section, $errmsg, $config);
50 package PMG
::Config
::Admin
;
55 use base
qw(PMG::Config::Base);
64 description
=> "Send daily reports.",
69 description
=> "User Statistics Lifetime (days)",
75 description
=> "Demo mode - do not start SMTP filter.",
80 description
=> "Administrator E-Mail address.",
81 type
=> 'string', format
=> 'email',
82 default => 'admin@domain.tld',
85 description
=> "HTTP proxy port.",
91 description
=> "HTTP proxy server address.",
95 description
=> "HTTP proxy user name.",
99 description
=> "HTTP proxy password.",
107 statlifetime
=> { optional
=> 1 },
108 dailyreport
=> { optional
=> 1 },
109 demo
=> { optional
=> 1 },
110 email
=> { optional
=> 1 },
111 proxyport
=> { optional
=> 1 },
112 proxyserver
=> { optional
=> 1 },
113 proxyuser
=> { optional
=> 1 },
114 proxypassword
=> { optional
=> 1 },
118 package PMG
::Config
::Spam
;
123 use base
qw(PMG::Config::Base);
132 description
=> "This option is used to specify which languages are considered OK for incoming mail.",
134 pattern
=> '(all|([a-z][a-z])+( ([a-z][a-z])+)*)',
138 description
=> "Whether to use the naive-Bayesian-style classifier.",
143 description
=> "Use the Auto-Whitelist plugin.",
148 description
=> "Whether to use Razor2, if it is available.",
152 wl_bounce_relays
=> {
153 description
=> "Whitelist legitimate bounce relays.",
157 description
=> "Additional score for bounce mails.",
164 description
=> "Enable real time blacklists (RBL) checks.",
169 description
=> "Maximum size of spam messages in bytes.",
179 use_awl
=> { optional
=> 1 },
180 use_razor
=> { optional
=> 1 },
181 wl_bounce_relays
=> { optional
=> 1 },
182 languages
=> { optional
=> 1 },
183 use_bayes
=> { optional
=> 1 },
184 bounce_score
=> { optional
=> 1 },
185 rbl_checks
=> { optional
=> 1 },
186 maxspamsize
=> { optional
=> 1 },
190 package PMG
::Config
::SpamQuarantine
;
195 use base
qw(PMG::Config::Base);
204 description
=> "Quarantine life time (days)",
210 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.",
212 enum
=> [qw(ticket ldap ldapticket)],
216 description
=> "Spam report style.",
218 enum
=> [qw(none short verbose outlook custom)],
219 default => 'verbose',
222 description
=> "Allow to view images.",
227 description
=> "Allow to view hyperlinks.",
232 description
=> "Quarantine Host. Usefule if you run a Cluster and want users to connect to a specific host.",
233 type
=> 'string', format
=> 'address',
236 description
=> "Text for 'From' header in daily spam report mails.",
244 mailfrom
=> { optional
=> 1 },
245 hostname
=> { optional
=> 1 },
246 lifetime
=> { optional
=> 1 },
247 authmode
=> { optional
=> 1 },
248 reportstyle
=> { optional
=> 1 },
249 viewimages
=> { optional
=> 1 },
250 allowhrefs
=> { optional
=> 1 },
254 package PMG
::Config
::VirusQuarantine
;
259 use base
qw(PMG::Config::Base);
271 lifetime
=> { optional
=> 1 },
272 viewimages
=> { optional
=> 1 },
273 allowhrefs
=> { optional
=> 1 },
277 package PMG
::Config
::ClamAV
;
282 use base
qw(PMG::Config::Base);
291 description
=> "ClamAV database mirror server.",
293 default => 'database.clamav.net',
295 archiveblockencrypted
=> {
296 description
=> "Wether to block encrypted archives. Mark encrypted archives as viruses.",
301 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.",
307 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.",
313 description
=> "Files larger than this limit won't be scanned.",
319 description
=> "Sets the maximum amount of data to be scanned for each input file.",
322 default => 100000000,
325 description
=> "This option sets the lowest number of Credit Card or Social Security numbers found in a file to generate a detect.",
331 description
=> "Enables support for Google Safe Browsing.",
340 archiveblockencrypted
=> { optional
=> 1 },
341 archivemaxrec
=> { optional
=> 1 },
342 archivemaxfiles
=> { optional
=> 1 },
343 archivemaxsize
=> { optional
=> 1 },
344 maxscansize
=> { optional
=> 1 },
345 dbmirror
=> { optional
=> 1 },
346 maxcccount
=> { optional
=> 1 },
347 safebrowsing
=> { optional
=> 1 },
351 package PMG
::Config
::Mail
;
356 use PVE
::ProcFSTools
;
358 use base
qw(PMG::Config::Base);
365 sub physical_memory
{
367 return $physicalmem if $physicalmem;
369 my $info = PVE
::ProcFSTools
::read_meminfo
();
370 my $total = int($info->{memtotal
} / (1024*1024));
375 sub get_max_filters
{
376 # estimate optimal number of filter servers
380 my $memory = physical_memory
();
381 my $add_servers = int(($memory - 512)/$servermem);
382 $max_servers += $add_servers if $add_servers > 0;
383 $max_servers = 40 if $max_servers > 40;
385 return $max_servers - 2;
389 # estimate optimal number of smtpd daemons
391 my $max_servers = 25;
393 my $memory = physical_memory
();
394 my $add_servers = int(($memory - 512)/$servermem);
395 $max_servers += $add_servers if $add_servers > 0;
396 $max_servers = 100 if $max_servers > 100;
401 # estimate optimal number of proxpolicy servers
403 my $memory = physical_memory
();
404 $max_servers = 5 if $memory >= 500;
411 description
=> "SMTP port number for outgoing mail (trusted).",
418 description
=> "SMTP port number for incoming mail (untrusted). This must be a different number than 'int_port'.",
425 description
=> "The default mail delivery transport (incoming mails).",
426 type
=> 'string', format
=> 'address',
429 description
=> "SMTP port number for relay host.",
436 description
=> "Disable MX lookups for default relay.",
441 description
=> "When set, all outgoing mails are deliverd to the specified smarthost.",
442 type
=> 'string', format
=> 'address',
445 description
=> "ESMTP banner.",
448 default => 'ESMTP Proxmox',
451 description
=> "Maximum number of pmg-smtp-filter processes.",
455 default => get_max_filters
(),
458 description
=> "Maximum number of pmgpolicy processes.",
462 default => get_max_policy
(),
465 description
=> "Maximum number of SMTP daemon processes (in).",
469 default => get_max_smtpd
(),
472 description
=> "Maximum number of SMTP daemon processes (out).",
476 default => get_max_smtpd
(),
478 conn_count_limit
=> {
479 description
=> "How many simultaneous connections any client is allowed to make to this service. To disable this feature, specify a limit of 0.",
485 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.",
490 message_rate_limit
=> {
491 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.",
497 description
=> "Hide received header in outgoing mails.",
502 description
=> "Maximum email size. Larger mails are rejected.",
505 default => 1024*1024*10,
508 description
=> "SMTP delay warning time (in hours).",
514 description
=> "Use Realtime Blacklists.",
519 description
=> "Enable TLS.",
524 description
=> "Enable TLS Logging.",
529 description
=> "Add TLS received header.",
534 description
=> "Use Sender Policy Framework.",
539 description
=> "Use Greylisting.",
544 description
=> "Use SMTP HELO tests.",
549 description
=> "Reject unknown clients.",
553 rejectunknownsender
=> {
554 description
=> "Reject unknown senders.",
559 description
=> "Enable receiver verification. The value spefifies the numerical reply code when the Postfix SMTP server rejects a recipient address.",
561 enum
=> ['450', '550'],
564 description
=> "Optional list of DNS white/blacklist domains (see postscreen_dnsbl_sites parameter).",
572 int_port
=> { optional
=> 1 },
573 ext_port
=> { optional
=> 1 },
574 smarthost
=> { optional
=> 1 },
575 relay
=> { optional
=> 1 },
576 relayport
=> { optional
=> 1 },
577 relaynomx
=> { optional
=> 1 },
578 dwarning
=> { optional
=> 1 },
579 max_smtpd_in
=> { optional
=> 1 },
580 max_smtpd_out
=> { optional
=> 1 },
581 greylist
=> { optional
=> 1 },
582 helotests
=> { optional
=> 1 },
583 use_rbl
=> { optional
=> 1 },
584 tls
=> { optional
=> 1 },
585 tlslog
=> { optional
=> 1 },
586 tlsheader
=> { optional
=> 1 },
587 spf
=> { optional
=> 1 },
588 maxsize
=> { optional
=> 1 },
589 banner
=> { optional
=> 1 },
590 max_filters
=> { optional
=> 1 },
591 max_policy
=> { optional
=> 1 },
592 hide_received
=> { optional
=> 1 },
593 rejectunknown
=> { optional
=> 1 },
594 rejectunknownsender
=> { optional
=> 1 },
595 conn_count_limit
=> { optional
=> 1 },
596 conn_rate_limit
=> { optional
=> 1 },
597 message_rate_limit
=> { optional
=> 1 },
598 verifyreceivers
=> { optional
=> 1 },
599 dnsbl_sites
=> { optional
=> 1 },
611 use PVE
::Tools
qw($IPV4RE $IPV6RE);
615 PMG
::Config
::Admin-
>register();
616 PMG
::Config
::Mail-
>register();
617 PMG
::Config
::SpamQuarantine-
>register();
618 PMG
::Config
::VirusQuarantine-
>register();
619 PMG
::Config
::Spam-
>register();
620 PMG
::Config
::ClamAV-
>register();
622 # initialize all plugins
623 PMG
::Config
::Base-
>init();
625 PVE
::JSONSchema
::register_format
(
626 'transport-domain', \
&pmg_verify_transport_domain
);
627 sub pmg_verify_transport_domain
{
628 my ($name, $noerr) = @_;
630 # like dns-name, but can contain leading dot
631 my $namere = "([a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?)";
633 if ($name !~ /^\.?(${namere}\.)*${namere}$/) {
634 return undef if $noerr;
635 die "value does not look like a valid transport domain\n";
643 my $class = ref($type) || $type;
645 my $cfg = PVE
::INotify
::read_file
("pmg.conf");
647 return bless $cfg, $class;
653 PVE
::INotify
::write_file
("pmg.conf", $self);
656 my $lockfile = "/var/lock/pmgconfig.lck";
659 my ($code, $errmsg) = @_;
661 my $p = PVE
::Tools
::lock_file
($lockfile, undef, $code);
663 $errmsg ?
die "$errmsg: $err" : die $err;
669 my ($self, $section, $key, $value) = @_;
671 my $pdata = PMG
::Config
::Base-
>private();
673 my $plugin = $pdata->{plugins
}->{$section};
674 die "no such section '$section'" if !$plugin;
676 if (defined($value)) {
677 my $tmp = PMG
::Config
::Base-
>check_value($section, $key, $value, $section, 0);
678 $self->{ids
}->{$section} = { type
=> $section } if !defined($self->{ids
}->{$section});
679 $self->{ids
}->{$section}->{$key} = PMG
::Config
::Base-
>decode_value($section, $key, $tmp);
681 if (defined($self->{ids
}->{$section})) {
682 delete $self->{ids
}->{$section}->{$key};
689 # get section value or default
691 my ($self, $section, $key) = @_;
693 my $pdata = PMG
::Config
::Base-
>private();
694 my $pdesc = $pdata->{propertyList
}->{$key};
695 die "no such property '$section/$key'\n"
696 if !(defined($pdesc) && defined($pdata->{options
}->{$section}) &&
697 defined($pdata->{options
}->{$section}->{$key}));
699 if (defined($self->{ids
}->{$section}) &&
700 defined(my $value = $self->{ids
}->{$section}->{$key})) {
704 return $pdesc->{default};
707 # get a whole section with default value
709 my ($self, $section) = @_;
711 my $pdata = PMG
::Config
::Base-
>private();
712 return undef if !defined($pdata->{options
}->{$section});
716 foreach my $key (keys %{$pdata->{options
}->{$section}}) {
718 my $pdesc = $pdata->{propertyList
}->{$key};
720 if (defined($self->{ids
}->{$section}) &&
721 defined(my $value = $self->{ids
}->{$section}->{$key})) {
722 $res->{$key} = $value;
725 $res->{$key} = $pdesc->{default};
731 # get a whole config with default values
735 my $pdata = PMG
::Config
::Base-
>private();
739 foreach my $type (keys %{$pdata->{plugins
}}) {
740 my $plugin = $pdata->{plugins
}->{$type};
741 $res->{$type} = $self->get_section($type);
748 my ($filename, $fh) = @_;
750 local $/ = undef; # slurp mode
752 my $raw = <$fh> if defined($fh);
754 return PMG
::Config
::Base-
>parse_config($filename, $raw);
758 my ($filename, $fh, $cfg) = @_;
760 my $raw = PMG
::Config
::Base-
>write_config($filename, $cfg);
762 PVE
::Tools
::safe_print
($filename, $fh, $raw);
765 PVE
::INotify
::register_file
('pmg.conf', "/etc/pmg/pmg.conf",
768 undef, always_call_parser
=> 1);
770 # parsers/writers for other files
772 my $domainsfilename = "/etc/pmg/domains";
774 sub postmap_pmg_domains
{
775 PMG
::Utils
::run_postmap
($domainsfilename);
778 sub read_pmg_domains
{
779 my ($filename, $fh) = @_;
785 while (defined(my $line = <$fh>)) {
787 next if $line =~ m/^\s*$/;
788 if ($line =~ m/^#(.*)\s*$/) {
792 if ($line =~ m/^(\S+)\s.*$/) {
794 $domains->{$domain} = {
795 domain
=> $domain, comment
=> $comment };
798 warn "parse error in '$filename': $line\n";
807 sub write_pmg_domains
{
808 my ($filename, $fh, $domains) = @_;
810 foreach my $domain (sort keys %$domains) {
811 my $comment = $domains->{$domain}->{comment
};
812 PVE
::Tools
::safe_print
($filename, $fh, "#$comment\n")
813 if defined($comment) && $comment !~ m/^\s*$/;
815 PVE
::Tools
::safe_print
($filename, $fh, "$domain 1\n");
819 PVE
::INotify
::register_file
('domains', $domainsfilename,
822 undef, always_call_parser
=> 1);
824 my $mynetworks_filename = "/etc/pmg/mynetworks";
826 sub postmap_pmg_mynetworks
{
827 PMG
::Utils
::run_postmap
($mynetworks_filename);
830 sub read_pmg_mynetworks
{
831 my ($filename, $fh) = @_;
837 while (defined(my $line = <$fh>)) {
839 next if $line =~ m/^\s*$/;
840 if ($line =~ m!^((?:$IPV4RE|$IPV6RE))/(\d+)\s*(?:#(.*)\s*)?$!) {
841 my ($network, $prefix_size, $comment) = ($1, $2, $3);
842 my $cidr = "$network/${prefix_size}";
843 $mynetworks->{$cidr} = {
845 network_address
=> $network,
846 prefix_size
=> $prefix_size,
847 comment
=> $comment // '',
850 warn "parse error in '$filename': $line\n";
858 sub write_pmg_mynetworks
{
859 my ($filename, $fh, $mynetworks) = @_;
861 foreach my $cidr (sort keys %$mynetworks) {
862 my $data = $mynetworks->{$cidr};
863 my $comment = $data->{comment
} // '*';
864 PVE
::Tools
::safe_print
($filename, $fh, "$cidr #$comment\n");
868 PVE
::INotify
::register_file
('mynetworks', $mynetworks_filename,
869 \
&read_pmg_mynetworks
,
870 \
&write_pmg_mynetworks
,
871 undef, always_call_parser
=> 1);
873 my $transport_map_filename = "/etc/pmg/transport";
875 sub postmap_pmg_transport
{
876 PMG
::Utils
::run_postmap
($transport_map_filename);
879 sub read_transport_map
{
880 my ($filename, $fh) = @_;
882 return [] if !defined($fh);
888 while (defined(my $line = <$fh>)) {
890 next if $line =~ m/^\s*$/;
891 if ($line =~ m/^#(.*)\s*$/) {
896 my $parse_error = sub {
898 warn "parse error in '$filename': $line - $err";
902 if ($line =~ m/^(\S+)\s+smtp:(\S+):(\d+)\s*$/) {
903 my ($domain, $host, $port) = ($1, $2, $3);
905 eval { pmg_verify_transport_domain
($domain); };
907 $parse_error->($err);
911 if ($host =~ m/^\[(.*)\]$/) {
916 eval { PVE
::JSONSchema
::pve_verify_address
($host); };
918 $parse_error->($err);
929 $res->{$domain} = $data;
932 $parse_error->('wrong format');
939 sub write_transport_map
{
940 my ($filename, $fh, $tmap) = @_;
944 foreach my $domain (sort keys %$tmap) {
945 my $data = $tmap->{$domain};
947 my $comment = $data->{comment
};
948 PVE
::Tools
::safe_print
($filename, $fh, "#$comment\n")
949 if defined($comment) && $comment !~ m/^\s*$/;
951 my $use_mx = $data->{use_mx
};
952 $use_mx = 0 if $data->{host
} =~ m/^(?:$IPV4RE|$IPV6RE)$/;
955 PVE
::Tools
::safe_print
(
956 $filename, $fh, "$data->{domain} smtp:$data->{host}:$data->{port}\n");
958 PVE
::Tools
::safe_print
(
959 $filename, $fh, "$data->{domain} smtp:[$data->{host}]:$data->{port}\n");
964 PVE
::INotify
::register_file
('transport', $transport_map_filename,
965 \
&read_transport_map
,
966 \
&write_transport_map
,
967 undef, always_call_parser
=> 1);
969 # config file generation using templates
971 sub get_template_vars
{
974 my $vars = { pmg
=> $self->get_config() };
976 my $nodename = PVE
::INotify
::nodename
();
977 my $int_ip = PMG
::Cluster
::remote_node_ip
($nodename);
978 my $int_net_cidr = PMG
::Utils
::find_local_network_for_ip
($int_ip);
979 $vars->{ipconfig
}->{int_ip
} = $int_ip;
980 # $vars->{ipconfig}->{int_net_cidr} = $int_net_cidr;
982 my $transportnets = [];
984 my $tmap = PVE
::INotify
::read_file
('transport');
985 foreach my $domain (sort keys %$tmap) {
986 my $data = $tmap->{$domain};
987 my $host = $data->{host
};
988 if ($host =~ m/^$IPV4RE$/) {
989 push @$transportnets, "$host/32";
990 } elsif ($host =~ m/^$IPV6RE$/) {
991 push @$transportnets, "[$host]/128";
995 $vars->{postfix
}->{transportnets
} = join(' ', @$transportnets);
997 my $mynetworks = [ '127.0.0.0/8', '[::1]/128' ];
998 push @$mynetworks, @$transportnets;
999 push @$mynetworks, $int_net_cidr;
1000 push @$mynetworks, 'hash:/etc/pmg/mynetworks';
1002 my $netlist = PVE
::INotify
::read_file
('mynetworks');
1003 # add default relay to mynetworks
1004 if (my $relay = $self->get('mail', 'relay')) {
1005 if ($relay =~ m/^$IPV4RE$/) {
1006 push @$mynetworks, "$relay/32";
1007 } elsif ($relay =~ m/^$IPV6RE$/) {
1008 push @$mynetworks, "[$relay]/128";
1010 # DNS name - do nothing ?
1014 $vars->{postfix
}->{mynetworks
} = join(' ', @$mynetworks);
1017 $usepolicy = 1 if $self->get('mail', 'greylist') ||
1018 $self->get('mail', 'spf') || $self->get('mail', 'use_rbl');
1019 $vars->{postfix
}->{usepolicy
} = $usepolicy;
1021 my $resolv = PVE
::INotify
::read_file
('resolvconf');
1022 $vars->{dns
}->{hostname
} = $nodename;
1023 $vars->{dns
}->{domain
} = $resolv->{search
};
1028 # rewrite file from template
1029 # return true if file has changed
1030 sub rewrite_config_file
{
1031 my ($self, $tmplname, $dstfn) = @_;
1033 my $demo = $self->get('admin', 'demo');
1035 my $srcfn = ($tmplname =~ m
|^.?
/|) ?
1036 $tmplname : "/var/lib/pmg/templates/$tmplname";
1039 my $demosrc = "$srcfn.demo";
1040 $srcfn = $demosrc if -f
$demosrc;
1043 my ($perm, $uid, $gid);
1045 my $srcfd = IO
::File-
>new ($srcfn, "r")
1046 || die "cant read template '$srcfn' - $!: ERROR";
1048 if ($dstfn eq '/etc/fetchmailrc') {
1049 (undef, undef, $uid, $gid) = getpwnam('fetchmail');
1051 } elsif ($dstfn eq '/etc/clamav/freshclam.conf') {
1052 # needed if file contains a HTTPProxyPasswort
1054 $uid = getpwnam('clamav');
1055 $gid = getgrnam('adm');
1059 my $template = Template-
>new({});
1061 my $vars = $self->get_template_vars();
1065 $template->process($srcfd, $vars, \
$output) ||
1066 die $template->error();
1070 my $old = PVE
::Tools
::file_get_contents
($dstfn, 128*1024) if -f
$dstfn;
1072 return 0 if defined($old) && ($old eq $output); # no change
1074 PVE
::Tools
::file_set_contents
($dstfn, $output, $perm);
1076 if (defined($uid) && defined($gid)) {
1077 chown($uid, $gid, $dstfn);
1083 # rewrite spam configuration
1084 sub rewrite_config_spam
{
1087 my $use_awl = $self->get('spam', 'use_awl');
1088 my $use_bayes = $self->get('spam', 'use_bayes');
1089 my $use_razor = $self->get('spam', 'use_razor');
1093 # delete AW and bayes databases if those features are disabled
1095 $changes = 1 if unlink '/root/.spamassassin/auto-whitelist';
1099 $changes = 1 if unlink '/root/.spamassassin/bayes_journal';
1100 $changes = 1 if unlink '/root/.spamassassin/bayes_seen';
1101 $changes = 1 if unlink '/root/.spamassassin/bayes_toks';
1104 # make sure we have a custom.cf file (else cluster sync fails)
1105 IO
::File-
>new('/etc/mail/spamassassin/custom.cf', 'a', 0644);
1107 $changes = 1 if $self->rewrite_config_file(
1108 'local.cf.in', '/etc/mail/spamassassin/local.cf');
1110 $changes = 1 if $self->rewrite_config_file(
1111 'init.pre.in', '/etc/mail/spamassassin/init.pre');
1113 $changes = 1 if $self->rewrite_config_file(
1114 'v310.pre.in', '/etc/mail/spamassassin/v310.pre');
1116 $changes = 1 if $self->rewrite_config_file(
1117 'v320.pre.in', '/etc/mail/spamassassin/v320.pre');
1120 mkdir "/root/.razor";
1122 $changes = 1 if $self->rewrite_config_file(
1123 'razor-agent.conf.in', '/root/.razor/razor-agent.conf');
1125 if (! -e
'/root/.razor/identity') {
1128 PVE
::Tools
::run_command
(['razor-admin', '-discover'], timeout
=> $timeout);
1129 PVE
::Tools
::run_command
(['razor-admin', '-register'], timeout
=> $timeout);
1132 syslog
('info', "registering razor failed: $err") if $err;
1139 # rewrite ClamAV configuration
1140 sub rewrite_config_clam
{
1143 return $self->rewrite_config_file(
1144 'clamd.conf.in', '/etc/clamav/clamd.conf');
1147 sub rewrite_config_freshclam
{
1150 return $self->rewrite_config_file(
1151 'freshclam.conf.in', '/etc/clamav/freshclam.conf');
1154 sub rewrite_config_postgres
{
1157 my $pgconfdir = "/etc/postgresql/9.6/main";
1161 $changes = 1 if $self->rewrite_config_file(
1162 'pg_hba.conf.in', "$pgconfdir/pg_hba.conf");
1164 $changes = 1 if $self->rewrite_config_file(
1165 'postgresql.conf.in', "$pgconfdir/postgresql.conf");
1170 # rewrite /root/.forward
1171 sub rewrite_dot_forward
{
1174 my $dstfn = '/root/.forward';
1176 my $email = $self->get('admin', 'email');
1179 if ($email && $email =~ m/\s*(\S+)\s*/) {
1182 # empty .forward does not forward mails (see man local)
1185 my $old = PVE
::Tools
::file_get_contents
($dstfn, 128*1024) if -f
$dstfn;
1187 return 0 if defined($old) && ($old eq $output); # no change
1189 PVE
::Tools
::file_set_contents
($dstfn, $output);
1194 my $write_smtp_whitelist = sub {
1195 my ($filename, $data, $action) = @_;
1197 $action = 'OK' if !$action;
1199 my $old = PVE
::Tools
::file_get_contents
($filename, 1024*1024)
1203 foreach my $k (sort keys %$data) {
1204 $new .= "$k $action\n";
1207 return 0 if defined($old) && ($old eq $new); # no change
1209 PVE
::Tools
::file_set_contents
($filename, $new);
1211 PMG
::Utils
::run_postmap
($filename);
1216 my $rewrite_config_whitelist = sub {
1217 my ($rulecache) = @_;
1219 # see man page for regexp_table for postfix regex table format
1221 # we use a hash to avoid duplicate entries in regex tables
1224 my $clientlist = {};
1226 foreach my $obj (@{$rulecache->{"greylist:receiver"}}) {
1227 my $oclass = ref($obj);
1228 if ($oclass eq 'PMG::RuleDB::Receiver') {
1229 my $addr = PMG
::Utils
::quote_regex
($obj->{address
});
1230 $tolist->{"/^$addr\$/"} = 1;
1231 } elsif ($oclass eq 'PMG::RuleDB::ReceiverDomain') {
1232 my $addr = PMG
::Utils
::quote_regex
($obj->{address
});
1233 $tolist->{"/^.+\@$addr\$/"} = 1;
1234 } elsif ($oclass eq 'PMG::RuleDB::ReceiverRegex') {
1235 my $addr = $obj->{address
};
1237 $tolist->{"/^$addr\$/"} = 1;
1241 foreach my $obj (@{$rulecache->{"greylist:sender"}}) {
1242 my $oclass = ref($obj);
1243 my $addr = PMG
::Utils
::quote_regex
($obj->{address
});
1244 if ($oclass eq 'PMG::RuleDB::EMail') {
1245 my $addr = PMG
::Utils
::quote_regex
($obj->{address
});
1246 $fromlist->{"/^$addr\$/"} = 1;
1247 } elsif ($oclass eq 'PMG::RuleDB::Domain') {
1248 my $addr = PMG
::Utils
::quote_regex
($obj->{address
});
1249 $fromlist->{"/^.+\@$addr\$/"} = 1;
1250 } elsif ($oclass eq 'PMG::RuleDB::WhoRegex') {
1251 my $addr = $obj->{address
};
1253 $fromlist->{"/^$addr\$/"} = 1;
1254 } elsif ($oclass eq 'PMG::RuleDB::IPAddress') {
1255 $clientlist->{$obj->{address
}} = 1;
1256 } elsif ($oclass eq 'PMG::RuleDB::IPNet') {
1257 $clientlist->{$obj->{address
}} = 1;
1261 $write_smtp_whitelist->("/etc/postfix/senderaccess", $fromlist);
1262 $write_smtp_whitelist->("/etc/postfix/rcptaccess", $tolist);
1263 $write_smtp_whitelist->("/etc/postfix/clientaccess", $clientlist);
1264 $write_smtp_whitelist->("/etc/postfix/postscreen_access", $clientlist, 'permit');
1267 # rewrite /etc/postfix/*
1268 sub rewrite_config_postfix
{
1269 my ($self, $rulecache) = @_;
1271 # make sure we have required files (else postfix start fails)
1272 postmap_pmg_domains
();
1273 postmap_pmg_transport
();
1274 postmap_pmg_mynetworks
();
1276 IO
::File-
>new($transport_map_filename, 'a', 0644);
1280 if ($self->get('mail', 'tls')) {
1282 PMG
::Utils
::gen_proxmox_tls_cert
();
1284 syslog
('info', "generating certificate failed: $@") if $@;
1287 $changes = 1 if $self->rewrite_config_file(
1288 'main.cf.in', '/etc/postfix/main.cf');
1290 $changes = 1 if $self->rewrite_config_file(
1291 'master.cf.in', '/etc/postfix/master.cf');
1293 $rewrite_config_whitelist->($rulecache);
1295 # fixme: rewrite_config_tls_policy ($class);
1297 # make sure aliases.db is up to date
1298 system('/usr/bin/newaliases');
1303 sub rewrite_config
{
1304 my ($self, $rulecache, $restart_services, $force_restart) = @_;
1306 $force_restart = {} if ! $force_restart;
1308 if (($self->rewrite_config_postfix($rulecache) && $restart_services) ||
1309 $force_restart->{postfix
}) {
1310 PMG
::Utils
::service_cmd
('postfix', 'restart');
1313 if ($self->rewrite_dot_forward() && $restart_services) {
1314 # no need to restart anything
1317 if ($self->rewrite_config_postgres() && $restart_services) {
1318 # do nothing (too many side effects)?
1319 # does not happen anyways, because config does not change.
1322 if (($self->rewrite_config_spam() && $restart_services) ||
1323 $force_restart->{spam
}) {
1324 PMG
::Utils
::service_cmd
('pmg-smtp-filter', 'restart');
1327 if (($self->rewrite_config_clam() && $restart_services) ||
1328 $force_restart->{clam
}) {
1329 PMG
::Utils
::service_cmd
('clamav-daemon', 'restart');
1332 if (($self->rewrite_config_freshclam() && $restart_services) ||
1333 $force_restart->{freshclam
}) {
1334 PMG
::Utils
::service_cmd
('clamav-freshclam', 'restart');