]> git.proxmox.com Git - pmg-api.git/blame - PMG/Config.pm
PMG/Config.pm: add more config properties, new helper rewrite_config_clam()
[pmg-api.git] / PMG / Config.pm
CommitLineData
7e0e6dbe
DM
1package PMG::Config::Base;
2
3use strict;
4use warnings;
5use Data::Dumper;
6
7use PVE::Tools;
8use PVE::JSONSchema qw(get_standard_option);
9use PVE::SectionConfig;
10
11use base qw(PVE::SectionConfig);
12
13my $defaultData = {
14 propertyList => {
15 type => { description => "Section type." },
16 section_id => {
17 description => "Secion ID.",
18 type => 'string', format => 'pve-configid',
19 },
20 },
21};
22
23sub private {
24 return $defaultData;
25}
26
27sub 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
39sub 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 55package PMG::Config::Admin;
7e0e6dbe
DM
56
57use strict;
58use warnings;
59
60use base qw(PMG::Config::Base);
61
62sub type {
ac5d1312 63 return 'admin';
7e0e6dbe
DM
64}
65
66sub 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
104sub 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
115package PMG::Config::Spam;
116
117use strict;
118use warnings;
119
120use base qw(PMG::Config::Base);
121
122sub type {
123 return 'spam';
124}
125
126sub 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',
173 minimim => 64,
174 default => 200*1024,
175 },
7e0e6dbe
DM
176 };
177}
178
179sub 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
193package PMG::Config::ClamAV;
194
195use strict;
196use warnings;
197
198use base qw(PMG::Config::Base);
199
200sub type {
201 return 'clamav';
202}
203
204sub 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.",
218 minimum => 1,
219 default => 5,
220 },
f62194b2 221 archivemaxfiles => {
ac5d1312 222 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
223 type => 'integer',
224 minimum => 0,
225 default => 1000,
226 },
ac5d1312
DM
227 archivemaxsize => {
228 description => "Files larger than this limit won't be scanned.",
229 type => 'integer',
230 minimum => 1000000,
231 default => 25000000,
232 },
233 maxscansize => {
234 description => "Sets the maximum amount of data to be scanned for each input file.",
235 type => 'integer',
236 minimum => 1000000,
237 default => 100000000,
238 },
239 maxcccount => {
240 description => "This option sets the lowest number of Credit Card or Social Security numbers found in a file to generate a detect.",
241 type => 'integer',
242 minimum => 0,
243 default => 0,
244 },
f62194b2
DM
245 };
246}
247
248sub options {
249 return {
ac5d1312
DM
250 archiveblockencrypted => { optional => 1 },
251 archivemaxrec => { optional => 1 },
f62194b2 252 archivemaxfiles => { optional => 1 },
ac5d1312
DM
253 archivemaxsize => { optional => 1 },
254 maxscansize => { optional => 1 },
255 dbmirror => { optional => 1 },
256 maxcccount => { optional => 1 },
7e0e6dbe
DM
257 };
258}
259
260package PMG::Config::LDAP;
261
262use strict;
263use warnings;
264
265use base qw(PMG::Config::Base);
266
267sub type {
268 return 'ldap';
269}
270
271sub properties {
272 return {
273 mode => {
274 description => "LDAP protocol mode ('ldap' or 'ldaps').",
275 type => 'string',
276 enum => ['ldap', 'ldaps'],
277 default => 'ldap',
278 },
279 };
280}
281
282sub options {
283 return {
284 mode => { optional => 1 },
285 };
286}
f62194b2 287
d9dc3c08
DM
288package PMG::Config::Mail;
289
290use strict;
291use warnings;
292
f62194b2
DM
293use PVE::ProcFSTools;
294
d9dc3c08
DM
295use base qw(PMG::Config::Base);
296
297sub type {
298 return 'mail';
299}
300
f62194b2
DM
301my $physicalmem = 0;
302sub physical_memory {
303
304 return $physicalmem if $physicalmem;
305
306 my $info = PVE::ProcFSTools::read_meminfo();
307 my $total = int($info->{memtotal} / (1024*1024));
308
309 return $total;
310}
311
312sub get_max_filters {
313 # estimate optimal number of filter servers
314
315 my $max_servers = 5;
316 my $servermem = 120;
317 my $memory = physical_memory();
318 my $add_servers = int(($memory - 512)/$servermem);
319 $max_servers += $add_servers if $add_servers > 0;
320 $max_servers = 40 if $max_servers > 40;
321
322 return $max_servers - 2;
323}
324
d9dc3c08
DM
325sub properties {
326 return {
327 banner => {
328 description => "ESMTP banner.",
329 type => 'string',
330 maxLength => 1024,
331 default => 'ESMTP Proxmox',
332 },
f62194b2
DM
333 max_filters => {
334 description => "Maximum number of filter processes.",
335 type => 'integer',
336 minimum => 3,
337 maximum => 40,
338 default => get_max_filters(),
339 },
340 hide_received => {
341 description => "Hide received header in outgoing mails.",
342 type => 'boolean',
ac5d1312
DM
343 default => 0,
344 },
345 max_size => {
346 description => "Maximum email size. Larger mails are rejected.",
347 type => 'integer',
348 minimum => 1024,
349 default => 1024*1024*10,
f62194b2 350 },
d9dc3c08
DM
351 };
352}
353
354sub options {
355 return {
ac5d1312 356 max_size => { optional => 1 },
d9dc3c08 357 banner => { optional => 1 },
f62194b2
DM
358 max_filters => { optional => 1 },
359 hide_received => { optional => 1 },
d9dc3c08
DM
360 };
361}
7e0e6dbe
DM
362package PMG::Config;
363
364use strict;
365use warnings;
9123cab5 366use IO::File;
7e0e6dbe
DM
367use Data::Dumper;
368
9123cab5 369use PVE::SafeSyslog;
7e0e6dbe
DM
370use PVE::Tools;
371use PVE::INotify;
372
ac5d1312 373PMG::Config::Admin->register();
d9dc3c08 374PMG::Config::Mail->register();
7e0e6dbe
DM
375PMG::Config::Spam->register();
376PMG::Config::LDAP->register();
f62194b2 377PMG::Config::ClamAV->register();
7e0e6dbe
DM
378
379# initialize all plugins
380PMG::Config::Base->init();
381
f62194b2
DM
382
383sub new {
384 my ($type) = @_;
385
386 my $class = ref($type) || $type;
387
388 my $cfg = PVE::INotify::read_file("pmg.conf");
389
390 return bless $cfg, $class;
391}
392
393# get section value or default
394# this does not work for ldap entries
395sub get {
396 my ($self, $section, $key) = @_;
397
398 my $pdata = PMG::Config::Base->private();
399 return undef if !defined($pdata->{options}->{$section});
400 return undef if !defined($pdata->{options}->{$section}->{$key});
401 my $pdesc = $pdata->{propertyList}->{$key};
402 return undef if !defined($pdesc);
403
404 my $configid = "section_$section";
405 if (defined($self->{ids}->{$configid}) &&
406 defined(my $value = $self->{ids}->{$configid}->{$key})) {
407 return $value;
1ccc8e95 408 }
f62194b2
DM
409
410 return $pdesc->{default};
411}
412
1ccc8e95
DM
413# get a whole section with default value
414# this does not work for ldap entries
415sub get_section {
416 my ($self, $section) = @_;
417
418 my $pdata = PMG::Config::Base->private();
419 return undef if !defined($pdata->{options}->{$section});
420
421 my $res = {};
422
423 foreach my $key (keys %{$pdata->{options}->{$section}}) {
424
425 my $pdesc = $pdata->{propertyList}->{$key};
426
427 my $configid = "section_$section";
428 if (defined($self->{ids}->{$configid}) &&
429 defined(my $value = $self->{ids}->{$configid}->{$key})) {
430 $res->{$key} = $value;
431 next;
432 }
433 $res->{$key} = $pdesc->{default};
434 }
435
436 return $res;
437}
438
be16be07
DM
439# get a whole config with default values
440# this does not work for ldap entries
441sub get_config {
442 my ($self) = @_;
443
444 my $res = {};
445
446 foreach my $key (keys %{$self->{ids}}) {
447 if ($key =~ m/^section_(\S+)$/) {
448 my $section = $1;
449 $res->{$section} = $self->get_section($section);
450 }
451 }
452
453 return $res;
454}
455
7e0e6dbe
DM
456sub read_pmg_conf {
457 my ($filename, $fh) = @_;
f62194b2 458
7e0e6dbe 459 local $/ = undef; # slurp mode
f62194b2 460
7e0e6dbe
DM
461 my $raw = <$fh>;
462
463 return PMG::Config::Base->parse_config($filename, $raw);
464}
465
466sub write_pmg_conf {
467 my ($filename, $fh, $cfg) = @_;
468
469 my $raw = PMG::Config::Base->write_config($filename, $cfg);
470
471 PVE::Tools::safe_print($filename, $fh, $raw);
472}
473
f62194b2
DM
474PVE::INotify::register_file('pmg.conf', "/etc/proxmox/pmg.conf",
475 \&read_pmg_conf,
7e0e6dbe
DM
476 \&write_pmg_conf);
477
478
9123cab5
DM
479# rewrite spam configuration
480sub rewrite_config_spam {
481 my ($self) = @_;
482
483 my $use_awl = $self->get('spam', 'use_awl');
484 my $use_bayes = $self->get('spam', 'use_bayes');
485 my $use_razor = $self->get('spam', 'use_razor');
486
487 # delete AW and bayes databases if those features are disabled
488 unlink '/root/.spamassassin/auto-whitelist' if !$use_awl;
489 if (!$use_bayes) {
490 unlink '/root/.spamassassin/bayes_journal';
491 unlink '/root/.spamassassin/bayes_seen';
492 unlink '/root/.spamassassin/bayes_toks';
493 }
494
495 # make sure we have a custom.cf file (else cluster sync fails)
496 IO::File->new('/etc/mail/spamassassin/custom.cf', 'a', 0644);
497
498 PMG::Utils::rewrite_config_file($self, 'local.cf.in', '/etc/mail/spamassassin/local.cf');
499 PMG::Utils::rewrite_config_file($self, 'init.pre.in', '/etc/mail/spamassassin/init.pre');
500 PMG::Utils::rewrite_config_file($self, 'v310.pre.in', '/etc/mail/spamassassin/v310.pre');
501 PMG::Utils::rewrite_config_file($self, 'v320.pre.in', '/etc/mail/spamassassin/v320.pre');
502
503 if ($use_razor) {
504 mkdir "/root/.razor";
505 PMG::Utils::rewrite_config_file(
506 $self, 'razor-agent.conf.in', '/root/.razor/razor-agent.conf');
507 if (! -e '/root/.razor/identity') {
508 eval {
509 my $timeout = 30;
510 PVE::Tools::run_command (['razor-admin', '-discover'], timeout => $timeout);
511 PVE::Tools::run_command (['razor-admin', '-register'], timeout => $timeout);
512 };
513 my $err = $@;
514 syslog('info', msgquote ("registering razor failed: $err")) if $err;
515 }
516 }
517}
518
ac5d1312
DM
519# rewrite ClamAV configuration
520sub rewrite_config_clam {
521 my ($self) = @_;
522
523 PMG::Utils::rewrite_config_file($self, 'clamd.conf.in', '/etc/clamav/clamd.conf');
524 PMG::Utils::rewrite_config_file($self, 'freshclam.conf.in', '/etc/clamav/freshclam.conf');
525}
526
7e0e6dbe 5271;