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