]> git.proxmox.com Git - pmg-api.git/blob - PMG/Config.pm
new max_policy option
[pmg-api.git] / PMG / Config.pm
1 package PMG::Config::Base;
2
3 use strict;
4 use warnings;
5 use Data::Dumper;
6
7 use PVE::Tools;
8 use PVE::JSONSchema qw(get_standard_option);
9 use PVE::SectionConfig;
10
11 use base qw(PVE::SectionConfig);
12
13 my $defaultData = {
14 propertyList => {
15 type => { description => "Section type." },
16 section => {
17 description => "Secion ID.",
18 type => 'string', format => 'pve-configid',
19 },
20 },
21 };
22
23 sub private {
24 return $defaultData;
25 }
26
27 sub format_section_header {
28 my ($class, $type, $sectionId) = @_;
29
30 die "internal error ($type ne $sectionId)" if $type ne $sectionId;
31
32 return "section: $type\n";
33 }
34
35
36 sub parse_section_header {
37 my ($class, $line) = @_;
38
39 if ($line =~ m/^section:\s*(\S+)\s*$/) {
40 my $section = $1;
41 my $errmsg = undef; # set if you want to skip whole section
42 eval { PVE::JSONSchema::pve_verify_configid($section); };
43 $errmsg = $@ if $@;
44 my $config = {}; # to return additional attributes
45 return ($section, $section, $errmsg, $config);
46 }
47 return undef;
48 }
49
50 package PMG::Config::Admin;
51
52 use strict;
53 use warnings;
54
55 use base qw(PMG::Config::Base);
56
57 sub type {
58 return 'admin';
59 }
60
61 sub properties {
62 return {
63 dailyreport => {
64 description => "Send daily reports.",
65 type => 'boolean',
66 default => 1,
67 },
68 demo => {
69 description => "Demo mode - do not start SMTP filter.",
70 type => 'boolean',
71 default => 0,
72 },
73 email => {
74 description => "Administrator E-Mail address.",
75 type => 'string', format => 'email',
76 default => 'admin@domain.tld',
77 },
78 proxyport => {
79 description => "HTTP proxy port.",
80 type => 'integer',
81 minimum => 1,
82 default => 8080,
83 },
84 proxyserver => {
85 description => "HTTP proxy server address.",
86 type => 'string',
87 },
88 proxyuser => {
89 description => "HTTP proxy user name.",
90 type => 'string',
91 },
92 proxypassword => {
93 description => "HTTP proxy password.",
94 type => 'string',
95 },
96 };
97 }
98
99 sub options {
100 return {
101 dailyreport => { optional => 1 },
102 demo => { optional => 1 },
103 proxyport => { optional => 1 },
104 proxyserver => { optional => 1 },
105 proxyuser => { optional => 1 },
106 proxypassword => { optional => 1 },
107 };
108 }
109
110 package PMG::Config::Spam;
111
112 use strict;
113 use warnings;
114
115 use base qw(PMG::Config::Base);
116
117 sub type {
118 return 'spam';
119 }
120
121 sub properties {
122 return {
123 languages => {
124 description => "This option is used to specify which languages are considered OK for incoming mail.",
125 type => 'string',
126 pattern => '(all|([a-z][a-z])+( ([a-z][a-z])+)*)',
127 default => 'all',
128 },
129 use_bayes => {
130 description => "Whether to use the naive-Bayesian-style classifier.",
131 type => 'boolean',
132 default => 1,
133 },
134 use_awl => {
135 description => "Use the Auto-Whitelist plugin.",
136 type => 'boolean',
137 default => 1,
138 },
139 use_razor => {
140 description => "Whether to use Razor2, if it is available.",
141 type => 'boolean',
142 default => 1,
143 },
144 use_ocr => {
145 description => "Enable OCR to scan pictures.",
146 type => 'boolean',
147 default => 0,
148 },
149 wl_bounce_relays => {
150 description => "Whitelist legitimate bounce relays.",
151 type => 'string',
152 },
153 bounce_score => {
154 description => "Additional score for bounce mails.",
155 type => 'integer',
156 minimum => 0,
157 maximum => 1000,
158 default => 0,
159 },
160 rbl_checks => {
161 description => "Enable real time blacklists (RBL) checks.",
162 type => 'boolean',
163 default => 1,
164 },
165 maxspamsize => {
166 description => "Maximum size of spam messages in bytes.",
167 type => 'integer',
168 minimum => 64,
169 default => 200*1024,
170 },
171 };
172 }
173
174 sub options {
175 return {
176 use_awl => { optional => 1 },
177 use_razor => { optional => 1 },
178 use_ocr => { optional => 1 },
179 wl_bounce_relays => { optional => 1 },
180 languages => { optional => 1 },
181 use_bayes => { optional => 1 },
182 bounce_score => { optional => 1 },
183 rbl_checks => { optional => 1 },
184 maxspamsize => { optional => 1 },
185 };
186 }
187
188 package PMG::Config::ClamAV;
189
190 use strict;
191 use warnings;
192
193 use base qw(PMG::Config::Base);
194
195 sub type {
196 return 'clamav';
197 }
198
199 sub properties {
200 return {
201 dbmirror => {
202 description => "ClamAV database mirror server.",
203 type => 'string',
204 default => 'database.clamav.net',
205 },
206 archiveblockencrypted => {
207 description => "Wether to block encrypted archives. Mark encrypted archives as viruses.",
208 type => 'boolean',
209 default => 0,
210 },
211 archivemaxrec => {
212 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.",
213 type => 'integer',
214 minimum => 1,
215 default => 5,
216 },
217 archivemaxfiles => {
218 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.",
219 type => 'integer',
220 minimum => 0,
221 default => 1000,
222 },
223 archivemaxsize => {
224 description => "Files larger than this limit won't be scanned.",
225 type => 'integer',
226 minimum => 1000000,
227 default => 25000000,
228 },
229 maxscansize => {
230 description => "Sets the maximum amount of data to be scanned for each input file.",
231 type => 'integer',
232 minimum => 1000000,
233 default => 100000000,
234 },
235 maxcccount => {
236 description => "This option sets the lowest number of Credit Card or Social Security numbers found in a file to generate a detect.",
237 type => 'integer',
238 minimum => 0,
239 default => 0,
240 },
241 };
242 }
243
244 sub options {
245 return {
246 archiveblockencrypted => { optional => 1 },
247 archivemaxrec => { optional => 1 },
248 archivemaxfiles => { optional => 1 },
249 archivemaxsize => { optional => 1 },
250 maxscansize => { optional => 1 },
251 dbmirror => { optional => 1 },
252 maxcccount => { optional => 1 },
253 };
254 }
255
256 package PMG::Config::Mail;
257
258 use strict;
259 use warnings;
260
261 use PVE::ProcFSTools;
262
263 use base qw(PMG::Config::Base);
264
265 sub type {
266 return 'mail';
267 }
268
269 my $physicalmem = 0;
270 sub physical_memory {
271
272 return $physicalmem if $physicalmem;
273
274 my $info = PVE::ProcFSTools::read_meminfo();
275 my $total = int($info->{memtotal} / (1024*1024));
276
277 return $total;
278 }
279
280 sub get_max_filters {
281 # estimate optimal number of filter servers
282
283 my $max_servers = 5;
284 my $servermem = 120;
285 my $memory = physical_memory();
286 my $add_servers = int(($memory - 512)/$servermem);
287 $max_servers += $add_servers if $add_servers > 0;
288 $max_servers = 40 if $max_servers > 40;
289
290 return $max_servers - 2;
291 }
292
293 sub get_max_smtpd {
294 # estimate optimal number of smtpd daemons
295
296 my $max_servers = 25;
297 my $servermem = 20;
298 my $memory = physical_memory();
299 my $add_servers = int(($memory - 512)/$servermem);
300 $max_servers += $add_servers if $add_servers > 0;
301 $max_servers = 100 if $max_servers > 100;
302 return $max_servers;
303 }
304
305 sub get_max_policy {
306 # estimate optimal number of proxpolicy servers
307 my $max_servers = 2;
308 my $memory = physical_memory();
309 $max_servers = 5 if $memory >= 500;
310 return $max_servers;
311 }
312
313 sub properties {
314 return {
315 relay => {
316 description => "The default mail delivery transport (incoming mails).",
317 type => 'string',
318 },
319 relayport => {
320 description => "SMTP port number for relay host.",
321 type => 'integer',
322 minimum => 1,
323 maximum => 65535,
324 default => 25,
325 },
326 relaynomx => {
327 description => "Disable MX lookups for default relay.",
328 type => 'boolean',
329 default => 0,
330 },
331 smarthost => {
332 description => "When set, all outgoing mails are deliverd to the specified smarthost.",
333 type => 'string',
334 },
335 banner => {
336 description => "ESMTP banner.",
337 type => 'string',
338 maxLength => 1024,
339 default => 'ESMTP Proxmox',
340 },
341 max_filters => {
342 description => "Maximum number of pmg-smtp-filter processes.",
343 type => 'integer',
344 minimum => 3,
345 maximum => 40,
346 default => get_max_filters(),
347 },
348 max_policy => {
349 description => "Maximum number of pmgpolicy processes.",
350 type => 'integer',
351 minimum => 2,
352 maximum => 10,
353 default => get_max_policy(),
354 },
355 max_smtpd_in => {
356 description => "Maximum number of SMTP daemon processes (in).",
357 type => 'integer',
358 minimum => 3,
359 maximum => 100,
360 default => get_max_smtpd(),
361 },
362 max_smtpd_out => {
363 description => "Maximum number of SMTP daemon processes (out).",
364 type => 'integer',
365 minimum => 3,
366 maximum => 100,
367 default => get_max_smtpd(),
368 },
369 conn_count_limit => {
370 description => "How many simultaneous connections any client is allowed to make to this service. To disable this feature, specify a limit of 0.",
371 type => 'integer',
372 minimum => 0,
373 default => 50,
374 },
375 conn_rate_limit => {
376 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.",
377 type => 'integer',
378 minimum => 0,
379 default => 0,
380 },
381 message_rate_limit => {
382 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.",
383 type => 'integer',
384 minimum => 0,
385 default => 0,
386 },
387 hide_received => {
388 description => "Hide received header in outgoing mails.",
389 type => 'boolean',
390 default => 0,
391 },
392 maxsize => {
393 description => "Maximum email size. Larger mails are rejected.",
394 type => 'integer',
395 minimum => 1024,
396 default => 1024*1024*10,
397 },
398 dwarning => {
399 description => "SMTP delay warning time (in hours).",
400 type => 'integer',
401 minimum => 0,
402 default => 4,
403 },
404 use_rbl => {
405 description => "Use Realtime Blacklists.",
406 type => 'boolean',
407 default => 1,
408 },
409 tls => {
410 description => "Use TLS.",
411 type => 'boolean',
412 default => 0,
413 },
414 spf => {
415 description => "Use Sender Policy Framework.",
416 type => 'boolean',
417 default => 1,
418 },
419 greylist => {
420 description => "Use Greylisting.",
421 type => 'boolean',
422 default => 1,
423 },
424 helotests => {
425 description => "Use SMTP HELO tests.",
426 type => 'boolean',
427 default => 0,
428 },
429 rejectunknown => {
430 description => "Reject unknown clients.",
431 type => 'boolean',
432 default => 0,
433 },
434 rejectunknownsender => {
435 description => "Reject unknown senders.",
436 type => 'boolean',
437 default => 0,
438 },
439 verifyreceivers => {
440 description => "Enable receiver verification. The value (if greater than 0) spefifies the numerical reply code when the Postfix SMTP server rejects a recipient address (450 or 550).",
441 type => 'integer',
442 minimum => 0,
443 maximum => 599,
444 default => 0,
445 },
446 dnsbl_sites => {
447 description => "Optional list of DNS white/blacklist domains (see postscreen_dnsbl_sites parameter).",
448 type => 'string',
449 },
450 };
451 }
452
453 sub options {
454 return {
455 relay => { optional => 1 },
456 relayport => { optional => 1 },
457 relaynomx => { optional => 1 },
458 dwarning => { optional => 1 },
459 max_smtpd_in => { optional => 1 },
460 max_smtpd_out => { optional => 1 },
461 greylist => { optional => 1 },
462 helotests => { optional => 1 },
463 use_rbl => { optional => 1 },
464 tls => { optional => 1 },
465 spf => { optional => 1 },
466 maxsize => { optional => 1 },
467 banner => { optional => 1 },
468 max_filters => { optional => 1 },
469 max_policy => { optional => 1 },
470 hide_received => { optional => 1 },
471 rejectunknown => { optional => 1 },
472 rejectunknownsender => { optional => 1 },
473 conn_count_limit => { optional => 1 },
474 conn_rate_limit => { optional => 1 },
475 message_rate_limit => { optional => 1 },
476 verifyreceivers => { optional => 1 },
477 dnsbl_sites => { optional => 1 },
478 };
479 }
480 package PMG::Config;
481
482 use strict;
483 use warnings;
484 use IO::File;
485 use Data::Dumper;
486 use Template;
487
488 use PVE::SafeSyslog;
489 use PVE::Tools;
490 use PVE::INotify;
491
492 PMG::Config::Admin->register();
493 PMG::Config::Mail->register();
494 PMG::Config::Spam->register();
495 PMG::Config::ClamAV->register();
496
497 # initialize all plugins
498 PMG::Config::Base->init();
499
500
501 sub new {
502 my ($type) = @_;
503
504 my $class = ref($type) || $type;
505
506 my $cfg = PVE::INotify::read_file("pmg.conf");
507
508 return bless $cfg, $class;
509 }
510
511 sub write {
512 my ($self) = @_;
513
514 PVE::INotify::write_file("pmg.conf", $self);
515 }
516
517 my $lockfile = "/var/lock/pmgconfig.lck";
518
519 sub lock_config {
520 my ($code, $errmsg) = @_;
521
522 my $p = PVE::Tools::lock_file($lockfile, undef, $code);
523 if (my $err = $@) {
524 $errmsg ? die "$errmsg: $err" : die $err;
525 }
526 }
527
528 # set section values
529 sub set {
530 my ($self, $section, $key, $value) = @_;
531
532 my $pdata = PMG::Config::Base->private();
533
534 my $plugin = $pdata->{plugins}->{$section};
535 die "no such section '$section'" if !$plugin;
536
537 if (defined($value)) {
538 my $tmp = PMG::Config::Base->check_value($section, $key, $value, $section, 0);
539 $self->{ids}->{$section} = { type => $section } if !defined($self->{ids}->{$section});
540 $self->{ids}->{$section}->{$key} = PMG::Config::Base->decode_value($section, $key, $tmp);
541 } else {
542 if (defined($self->{ids}->{$section})) {
543 delete $self->{ids}->{$section}->{$key};
544 }
545 }
546
547 return undef;
548 }
549
550 # get section value or default
551 sub get {
552 my ($self, $section, $key) = @_;
553
554 my $pdata = PMG::Config::Base->private();
555 return undef if !defined($pdata->{options}->{$section});
556 return undef if !defined($pdata->{options}->{$section}->{$key});
557 my $pdesc = $pdata->{propertyList}->{$key};
558 return undef if !defined($pdesc);
559
560 if (defined($self->{ids}->{$section}) &&
561 defined(my $value = $self->{ids}->{$section}->{$key})) {
562 return $value;
563 }
564
565 return $pdesc->{default};
566 }
567
568 # get a whole section with default value
569 sub get_section {
570 my ($self, $section) = @_;
571
572 my $pdata = PMG::Config::Base->private();
573 return undef if !defined($pdata->{options}->{$section});
574
575 my $res = {};
576
577 foreach my $key (keys %{$pdata->{options}->{$section}}) {
578
579 my $pdesc = $pdata->{propertyList}->{$key};
580
581 if (defined($self->{ids}->{$section}) &&
582 defined(my $value = $self->{ids}->{$section}->{$key})) {
583 $res->{$key} = $value;
584 next;
585 }
586 $res->{$key} = $pdesc->{default};
587 }
588
589 return $res;
590 }
591
592 # get a whole config with default values
593 sub get_config {
594 my ($self) = @_;
595
596 my $pdata = PMG::Config::Base->private();
597
598 my $res = {};
599
600 foreach my $type (keys %{$pdata->{plugins}}) {
601 my $plugin = $pdata->{plugins}->{$type};
602 $res->{$type} = $self->get_section($type);
603 }
604
605 return $res;
606 }
607
608 sub read_pmg_conf {
609 my ($filename, $fh) = @_;
610
611 local $/ = undef; # slurp mode
612
613 my $raw = <$fh>;
614
615 return PMG::Config::Base->parse_config($filename, $raw);
616 }
617
618 sub write_pmg_conf {
619 my ($filename, $fh, $cfg) = @_;
620
621 my $raw = PMG::Config::Base->write_config($filename, $cfg);
622
623 PVE::Tools::safe_print($filename, $fh, $raw);
624 }
625
626 PVE::INotify::register_file('pmg.conf', "/etc/proxmox/pmg.conf",
627 \&read_pmg_conf,
628 \&write_pmg_conf);
629
630 # parsers/writers for other files
631
632 my $domainsfilename = "/etc/proxmox/domains";
633
634 sub read_pmg_domains {
635 my ($filename, $fh) = @_;
636
637 my $domains = [];
638
639 if (defined($fh)) {
640 while (defined(my $line = <$fh>)) {
641 if ($line =~ m/^\s*(\S+)\s*$/) {
642 my $domain = $1;
643 push @$domains, $domain;
644 }
645 }
646 }
647
648 return $domains;
649 }
650
651 sub write_pmg_domains {
652 my ($filename, $fh, $domain) = @_;
653
654 foreach my $domain (sort @$domain) {
655 PVE::Tools::safe_print($filename, $fh, "$domain\n");
656 }
657 }
658
659 PVE::INotify::register_file('domains', $domainsfilename,
660 \&read_pmg_domains,
661 \&write_pmg_domains,
662 undef, always_call_parser => 1);
663
664 my $transport_map_filename = "/etc/postfix/transport";
665
666 sub read_transport_map {
667 my ($filename, $fh) = @_;
668
669 return [] if !defined($fh);
670
671 my $res = {};
672
673 while (defined(my $line = <$fh>)) {
674 chomp $line;
675 next if $line =~ m/^\s*$/;
676 next if $line =~ m/^\s*\#/;
677
678 if ($line =~ m/^(\S+)\s+smtp:([^\s:]+):(\d+)\s*$/) {
679 my $domain = $1;
680 my $host = $2;
681 my $port =$3;
682 my $nomx;
683
684 if ($host =~ m/^\[(.*)\]$/) {
685 $host = $1;
686 $nomx = 1;
687 }
688
689 my $key = "$host:$port";
690
691 $res->{$key}->{nomx} = $nomx;
692 $res->{$key}->{host} = $host;
693 $res->{$key}->{port} = $port;
694 $res->{$key}->{transport} = $key;
695
696 push @{$res->{$key}->{domains}}, $domain;
697 }
698 }
699
700 my $ta = [];
701
702 foreach my $t (sort keys %$res) {
703 push @$ta, $res->{$t};
704 }
705
706 return $ta;
707 }
708
709 sub write_ransport_map {
710 my ($filename, $fh, $tmap) = @_;
711
712 return if !$tmap;
713
714 foreach my $t (sort { $a->{transport} cmp $b->{transport} } @$tmap) {
715 my $domains = $t->{domains};
716
717 foreach my $d (sort @$domains) {
718 if ($t->{nomx}) {
719 PVE::Tools::safe_print($filename, $fh, "$d smtp:[$t->{host}]:$t->{port}\n");
720 } else {
721 PVE::Tools::safe_print($filename, $fh, "$d smtp:$t->{host}:$t->{port}\n");
722 }
723 }
724 }
725 }
726
727 PVE::INotify::register_file('transport', $transport_map_filename,
728 \&read_transport_map,
729 \&write_ransport_map,
730 undef, always_call_parser => 1);
731
732 # config file generation using templates
733
734 sub get_template_vars {
735 my ($self) = @_;
736
737 my $vars = { pmg => $self->get_config() };
738
739 my $nodename = PVE::INotify::nodename();
740 my $int_ip = PMG::Cluster::remote_node_ip($nodename);
741 my $int_net_cidr = PMG::Utils::find_local_network_for_ip($int_ip);
742
743 $vars->{ipconfig}->{int_ip} = $int_ip;
744 # $vars->{ipconfig}->{int_net_cidr} = $int_net_cidr;
745 $vars->{ipconfig}->{int_port} = 26;
746 $vars->{ipconfig}->{ext_port} = 25;
747
748 my $transportnets = []; # fixme
749 $vars->{postfix}->{transportnets} = join(' ', @$transportnets);
750
751 my $mynetworks = [ '127.0.0.0/8', '[::1]/128' ];
752 push @$mynetworks, @$transportnets;
753 push @$mynetworks, $int_net_cidr;
754
755 # add default relay to mynetworks
756 if (my $relay = $self->get('mail', 'relay')) {
757 if (Net::IP::ip_is_ipv4($relay)) {
758 push @$mynetworks, "$relay/32";
759 } elsif (Net::IP::ip_is_ipv6($relay)) {
760 push @$mynetworks, "[$relay]/128";
761 } else {
762 warn "unable to detect IP version of relay '$relay'";
763 }
764 }
765
766 $vars->{postfix}->{mynetworks} = join(' ', @$mynetworks);
767
768 my $usepolicy = 0;
769 $usepolicy = 1 if $self->get('mail', 'greylist') ||
770 $self->get('mail', 'spf') || $self->get('mail', 'use_rbl');
771 $vars->{postfix}->{usepolicy} = $usepolicy;
772
773 my $resolv = PVE::INotify::read_file('resolvconf');
774 $vars->{dns}->{hostname} = $nodename;
775 $vars->{dns}->{domain} = $resolv->{search};
776
777 return $vars;
778 }
779
780 # rewrite file from template
781 # return true if file has changed
782 sub rewrite_config_file {
783 my ($self, $tmplname, $dstfn) = @_;
784
785 my $demo = $self->get('admin', 'demo');
786
787 my $srcfn = ($tmplname =~ m|^.?/|) ?
788 $tmplname : "/var/lib/pmg/templates/$tmplname";
789
790 if ($demo) {
791 my $demosrc = "$srcfn.demo";
792 $srcfn = $demosrc if -f $demosrc;
793 }
794
795 my ($perm, $uid, $gid);
796
797 my $srcfd = IO::File->new ($srcfn, "r")
798 || die "cant read template '$srcfn' - $!: ERROR";
799
800 if ($dstfn eq '/etc/fetchmailrc') {
801 (undef, undef, $uid, $gid) = getpwnam('fetchmail');
802 $perm = 0600;
803 } elsif ($dstfn eq '/etc/clamav/freshclam.conf') {
804 # needed if file contains a HTTPProxyPasswort
805
806 $uid = getpwnam('clamav');
807 $gid = getgrnam('adm');
808 $perm = 0600;
809 }
810
811 my $template = Template->new({});
812
813 my $vars = $self->get_template_vars();
814
815 my $output = '';
816
817 $template->process($srcfd, $vars, \$output) ||
818 die $template->error();
819
820 $srcfd->close();
821
822 my $old = PVE::Tools::file_get_contents($dstfn, 128*1024) if -f $dstfn;
823
824 return 0 if defined($old) && ($old eq $output); # no change
825
826 PVE::Tools::file_set_contents($dstfn, $output, $perm);
827
828 if (defined($uid) && defined($gid)) {
829 chown($uid, $gid, $dstfn);
830 }
831
832 return 1;
833 }
834
835 # rewrite spam configuration
836 sub rewrite_config_spam {
837 my ($self) = @_;
838
839 my $use_awl = $self->get('spam', 'use_awl');
840 my $use_bayes = $self->get('spam', 'use_bayes');
841 my $use_razor = $self->get('spam', 'use_razor');
842
843 my $changes = 0;
844
845 # delete AW and bayes databases if those features are disabled
846 if (!$use_awl) {
847 $changes = 1 if unlink '/root/.spamassassin/auto-whitelist';
848 }
849
850 if (!$use_bayes) {
851 $changes = 1 if unlink '/root/.spamassassin/bayes_journal';
852 $changes = 1 if unlink '/root/.spamassassin/bayes_seen';
853 $changes = 1 if unlink '/root/.spamassassin/bayes_toks';
854 }
855
856 # make sure we have a custom.cf file (else cluster sync fails)
857 IO::File->new('/etc/mail/spamassassin/custom.cf', 'a', 0644);
858
859 $changes = 1 if $self->rewrite_config_file(
860 'local.cf.in', '/etc/mail/spamassassin/local.cf');
861
862 $changes = 1 if $self->rewrite_config_file(
863 'init.pre.in', '/etc/mail/spamassassin/init.pre');
864
865 $changes = 1 if $self->rewrite_config_file(
866 'v310.pre.in', '/etc/mail/spamassassin/v310.pre');
867
868 $changes = 1 if $self->rewrite_config_file(
869 'v320.pre.in', '/etc/mail/spamassassin/v320.pre');
870
871 if ($use_razor) {
872 mkdir "/root/.razor";
873
874 $changes = 1 if $self->rewrite_config_file(
875 'razor-agent.conf.in', '/root/.razor/razor-agent.conf');
876
877 if (! -e '/root/.razor/identity') {
878 eval {
879 my $timeout = 30;
880 PVE::Tools::run_command(['razor-admin', '-discover'], timeout => $timeout);
881 PVE::Tools::run_command(['razor-admin', '-register'], timeout => $timeout);
882 };
883 my $err = $@;
884 syslog('info', msgquote ("registering razor failed: $err")) if $err;
885 }
886 }
887
888 return $changes;
889 }
890
891 # rewrite ClamAV configuration
892 sub rewrite_config_clam {
893 my ($self) = @_;
894
895 return $self->rewrite_config_file(
896 'clamd.conf.in', '/etc/clamav/clamd.conf');
897 }
898
899 sub rewrite_config_freshclam {
900 my ($self) = @_;
901
902 return $self->rewrite_config_file(
903 'freshclam.conf.in', '/etc/clamav/freshclam.conf');
904 }
905
906 sub rewrite_config_postgres {
907 my ($self) = @_;
908
909 my $pgconfdir = "/etc/postgresql/9.6/main";
910
911 my $changes = 0;
912
913 $changes = 1 if $self->rewrite_config_file(
914 'pg_hba.conf.in', "$pgconfdir/pg_hba.conf");
915
916 $changes = 1 if $self->rewrite_config_file(
917 'postgresql.conf.in', "$pgconfdir/postgresql.conf");
918
919 return $changes;
920 }
921
922 # rewrite /root/.forward
923 sub rewrite_dot_forward {
924 my ($self) = @_;
925
926 my $dstfn = '/root/.forward';
927
928 my $email = $self->get('administration', 'email');
929
930 my $output = '';
931 if ($email && $email =~ m/\s*(\S+)\s*/) {
932 $output = "$1\n";
933 } else {
934 # empty .forward does not forward mails (see man local)
935 }
936
937 my $old = PVE::Tools::file_get_contents($dstfn, 128*1024) if -f $dstfn;
938
939 return 0 if defined($old) && ($old eq $output); # no change
940
941 PVE::Tools::file_set_contents($dstfn, $output);
942
943 return 1;
944 }
945
946 # rewrite /etc/postfix/*
947 sub rewrite_config_postfix {
948 my ($self) = @_;
949
950 # make sure we have required files (else postfix start fails)
951 IO::File->new($domainsfilename, 'a', 0644);
952 IO::File->new($transport_map_filename, 'a', 0644);
953
954 my $changes = 0;
955
956 if ($self->get('mail', 'tls')) {
957 eval {
958 my $resolv = PVE::INotify::read_file('resolvconf');
959 my $domain = $resolv->{search};
960
961 my $company = $domain; # what else ?
962 my $cn = "*.$domain";
963 PMG::Utils::gen_proxmox_tls_cert(0, $company, $cn);
964 };
965 syslog ('info', msgquote ("generating certificate failed: $@")) if $@;
966 }
967
968 $changes = 1 if $self->rewrite_config_file(
969 'main.cf.in', '/etc/postfix/main.cf');
970
971 $changes = 1 if $self->rewrite_config_file(
972 'master.cf.in', '/etc/postfix/master.cf');
973
974 #rewrite_config_transports ($class);
975 #rewrite_config_whitelist ($class);
976 #rewrite_config_tls_policy ($class);
977
978 # make sure aliases.db is up to date
979 system('/usr/bin/newaliases');
980
981 return $changes;
982 }
983
984 sub rewrite_config {
985 my ($self, $restart_services) = @_;
986
987 if ($self->rewrite_config_postfix() && $restart_services) {
988 PMG::Utils::service_cmd('postfix', 'restart');
989 }
990
991 if ($self->rewrite_dot_forward() && $restart_services) {
992 # no need to restart anything
993 }
994
995 if ($self->rewrite_config_postgres() && $restart_services) {
996 # do nothing (too many side effects)?
997 # does not happen anyways, because config does not change.
998 }
999
1000 if ($self->rewrite_config_spam() && $restart_services) {
1001 PMG::Utils::service_cmd('pmg-smtp-filter', 'restart');
1002 }
1003
1004 if ($self->rewrite_config_clam() && $restart_services) {
1005 PMG::Utils::service_cmd('clamd', 'restart');
1006 }
1007
1008 if ($self->rewrite_config_freshclam() && $restart_services) {
1009 PMG::Utils::service_cmd('freshclam', 'restart');
1010 }
1011
1012 }
1013
1014 1;