public inbox for pmg-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Christoph Heiss <c.heiss@proxmox.com>
To: pmg-devel@lists.proxmox.com
Subject: [pmg-devel] [PATCH pmg-api 1/3] fix #2437: config: Add inbound TLS policy option
Date: Thu,  9 Mar 2023 11:18:44 +0100	[thread overview]
Message-ID: <20230309101846.192177-2-c.heiss@proxmox.com> (raw)
In-Reply-To: <20230309101846.192177-1-c.heiss@proxmox.com>

Add a new configuration file /etc/pmg/tls_inbound_policy, which is a
postfix map containing all domains having `reject_plaintext_session`
action set, which is then used in smtpd_sender_restriction in the
main.cf template.

Also add the accompanying API endpoint for modifying it.

Signed-off-by: Christoph Heiss <c.heiss@proxmox.com>
---
 src/Makefile                     |   1 +
 src/PMG/API2/Config.pm           |   7 ++
 src/PMG/API2/InboundTLSPolicy.pm | 127 +++++++++++++++++++++++++++++++
 src/PMG/Config.pm                |  56 ++++++++++++++
 src/templates/main.cf.in         |   1 +
 5 files changed, 192 insertions(+)
 create mode 100644 src/PMG/API2/InboundTLSPolicy.pm

diff --git a/src/Makefile b/src/Makefile
index 49c7974..139a4b5 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -130,6 +130,7 @@ LIBSOURCES =				\
 	PMG/API2/DKIMSignDomains.pm	\
 	PMG/API2/DKIMSign.pm		\
 	PMG/API2/Fetchmail.pm		\
+	PMG/API2/InboundTLSPolicy.pm	\
 	PMG/API2/Users.pm		\
 	PMG/API2/Transport.pm		\
 	PMG/API2/MyNetworks.pm		\
diff --git a/src/PMG/API2/Config.pm b/src/PMG/API2/Config.pm
index 37da096..8a8a469 100644
--- a/src/PMG/API2/Config.pm
+++ b/src/PMG/API2/Config.pm
@@ -23,6 +23,7 @@ use PMG::API2::SMTPWhitelist;
 use PMG::API2::MimeTypes;
 use PMG::API2::Fetchmail;
 use PMG::API2::DestinationTLSPolicy;
+use PMG::API2::InboundTLSPolicy;
 use PMG::API2::DKIMSign;
 use PMG::API2::SACustom;
 use PMG::API2::PBS::Remote;
@@ -86,6 +87,11 @@ __PACKAGE__->register_method ({
     path => 'tlspolicy',
 });

+__PACKAGE__->register_method ({
+    subclass => "PMG::API2::InboundTLSPolicy",
+    path => 'tlsinboundpolicy',
+});
+
 __PACKAGE__->register_method({
     subclass => "PMG::API2::DKIMSign",
     path => 'dkim',
@@ -146,6 +152,7 @@ __PACKAGE__->register_method ({
 	push @$res, { section => 'ruledb' };
 	push @$res, { section => 'tfa' };
 	push @$res, { section => 'tlspolicy' };
+	push @$res, { section => 'tlsinboundpolicy' };
 	push @$res, { section => 'transport' };
 	push @$res, { section => 'users' };
 	push @$res, { section => 'whitelist' };
diff --git a/src/PMG/API2/InboundTLSPolicy.pm b/src/PMG/API2/InboundTLSPolicy.pm
new file mode 100644
index 0000000..74fb16a
--- /dev/null
+++ b/src/PMG/API2/InboundTLSPolicy.pm
@@ -0,0 +1,127 @@
+package PMG::API2::InboundTLSPolicy;
+
+use strict;
+use warnings;
+
+use PVE::RESTHandler;
+use PVE::INotify;
+use PVE::Exception qw(raise_param_exc);
+
+use PMG::Config;
+
+use base qw(PVE::RESTHandler);
+
+__PACKAGE__->register_method ({
+    name => 'index',
+    path => '',
+    method => 'GET',
+    description => 'List tls_inbound_policy entries.',
+    proxyto => 'master',
+    permissions => { check => [ 'admin', 'audit' ] },
+    parameters => {
+	additionalProperties => 0,
+	properties => {},
+    },
+    returns => {
+	type => 'array',
+	items => {
+	    type => 'string',
+	    format => 'transport-domain',
+	},
+	description => 'List of domains for which TLS will be enforced on incoming connections',
+	links => [ { rel => 'child', href => '{domain}' } ],
+    },
+    code => sub {
+	my ($param) = @_;
+
+	my $res = [];
+
+	my $policies = PVE::INotify::read_file('tls_inbound_policy');
+
+	foreach my $domain (sort keys %$policies) {
+	    push @$res, { domain => $domain };
+	}
+
+	return $res;
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'create',
+    path => '',
+    method => 'POST',
+    proxyto => 'master',
+    protected => 1,
+    permissions => { check => [ 'admin' ] },
+    description => 'Add new tls_inbound_policy entry.',
+    parameters => {
+	additionalProperties => 0,
+	properties => {
+	    domain => {
+		type => 'string',
+		format => 'transport-domain',
+		description => 'Domain for which TLS should be enforced on incoming connections',
+	    },
+	},
+    },
+    returns => { type => 'null' },
+    code => sub {
+	my ($param) = @_;
+	my $domain = $param->{domain};
+
+	my $code = sub {
+	    my $policies = PVE::INotify::read_file('tls_inbound_policy');
+	    raise_param_exc({ domain => "InboundTLSPolicy entry for '$domain' already exists" })
+		if $policies->{$domain};
+
+	    $policies->{$domain} = 1;
+
+	    PVE::INotify::write_file('tls_inbound_policy', $policies);
+	    PMG::Config::postmap_tls_inbound_policy();
+	};
+
+	PMG::Config::lock_config($code, 'adding tls_inbound_policy entry failed');
+
+	return undef;
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'delete',
+    path => '{domain}',
+    method => 'DELETE',
+    description => 'Delete a tls_inbound_policy entry',
+    protected => 1,
+    permissions => { check => [ 'admin' ] },
+    proxyto => 'master',
+    parameters => {
+	additionalProperties => 0,
+	properties => {
+	    domain => {
+		type => 'string',
+		format => 'transport-domain',
+		description => 'Domain which should be removed from tls_inbound_policy',
+	    },
+	}
+    },
+    returns => { type => 'null' },
+    code => sub {
+	my ($param) = @_;
+	my $domain = $param->{domain};
+
+	my $code = sub {
+	    my $policies = PVE::INotify::read_file('tls_inbound_policy');
+
+	    raise_param_exc({ domain => "tls_inbound_policy entry for '$domain' does not exist" })
+		if !$policies->{$domain};
+
+	    delete $policies->{$domain};
+
+	    PVE::INotify::write_file('tls_inbound_policy', $policies);
+	    PMG::Config::postmap_tls_inbound_policy();
+	};
+
+	PMG::Config::lock_config($code, 'deleting tls_inbound_policy entry failed');
+
+	return undef;
+    }});
+
+1;
diff --git a/src/PMG/Config.pm b/src/PMG/Config.pm
index a0b1866..45a4b3a 100755
--- a/src/PMG/Config.pm
+++ b/src/PMG/Config.pm
@@ -1154,6 +1154,61 @@ sub postmap_tls_policy {
     PMG::Utils::run_postmap($tls_policy_map_filename);
 }

+sub read_tls_inbound_policy {
+    my ($filename, $fh) = @_;
+
+    return {} if !defined($fh);
+
+    my $tls_policy = {};
+
+    while (defined(my $line = <$fh>)) {
+	chomp $line;
+	next if $line =~ m/^\s*$/;
+	next if $line =~ m/^#(.*)\s*$/;
+
+	my $parse_error = sub {
+	    my ($err) = @_;
+	    die "parse error in '$filename': $line - $err";
+	};
+
+	if ($line =~ m/^(\S+)\s+.+\s*$/) {
+	    my $domain = $1;
+
+	    eval { pmg_verify_transport_domain($domain) };
+	    if (my $err = $@) {
+		$parse_error->($err);
+		next;
+	    }
+
+	    $tls_policy->{$domain} = 1;
+	} else {
+	    $parse_error->('wrong format');
+	}
+    }
+
+    return $tls_policy;
+}
+
+sub write_tls_inbound_policy {
+    my ($filename, $fh, $tls_policy) = @_;
+
+    return if !$tls_policy;
+
+    foreach my $domain (sort keys %$tls_policy) {
+	PVE::Tools::safe_print($filename, $fh, "$domain reject_plaintext_session\n");
+    }
+}
+
+my $tls_inbound_policy_map_filename = "/etc/pmg/tls_inbound_policy";
+PVE::INotify::register_file('tls_inbound_policy', $tls_inbound_policy_map_filename,
+			    \&read_tls_inbound_policy,
+			    \&write_tls_inbound_policy,
+			    undef, always_call_parser => 1);
+
+sub postmap_tls_inbound_policy {
+    PMG::Utils::run_postmap($tls_inbound_policy_map_filename);
+}
+
 my $transport_map_filename = "/etc/pmg/transport";

 sub postmap_pmg_transport {
@@ -1684,6 +1739,7 @@ sub rewrite_config_postfix {
     postmap_pmg_domains();
     postmap_pmg_transport();
     postmap_tls_policy();
+    postmap_tls_inbound_policy();

     rewrite_postfix_whitelist($rulecache) if $rulecache;

diff --git a/src/templates/main.cf.in b/src/templates/main.cf.in
index 190c913..4905eeb 100644
--- a/src/templates/main.cf.in
+++ b/src/templates/main.cf.in
@@ -79,6 +79,7 @@ smtpd_sender_restrictions =
         reject_non_fqdn_sender
         check_client_access     cidr:/etc/postfix/clientaccess
         check_sender_access     regexp:/etc/postfix/senderaccess
+        check_sender_access     hash:/etc/pmg/tls_inbound_policy
         check_recipient_access  regexp:/etc/postfix/rcptaccess
 [%- IF pmg.mail.rejectunknown %] reject_unknown_client_hostname[% END %]
 [%- IF pmg.mail.rejectunknownsender %] reject_unknown_sender_domain[% END %]
--
2.39.2





  reply	other threads:[~2023-03-09 10:19 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-03-09 10:18 [pmg-devel] [PATCH pmg-{api, gui, docs} 0/3] fix #2437: Add TLS inbound policy for sender domains Christoph Heiss
2023-03-09 10:18 ` Christoph Heiss [this message]
2023-03-16 12:50   ` [pmg-devel] [PATCH pmg-api 1/3] fix #2437: config: Add inbound TLS policy option Stoiko Ivanov
2023-03-20  8:21     ` Christoph Heiss
2023-03-09 10:18 ` [pmg-devel] [PATCH pmg-gui 2/3] fix #2437: proxy: Add 'TLS Inbound Policy' panel Christoph Heiss
2023-03-16 12:32   ` Stoiko Ivanov
2023-03-20  8:36     ` Christoph Heiss
2023-03-20  8:42       ` Stoiko Ivanov
2023-03-09 10:18 ` [pmg-devel] [PATCH pmg-docs 3/3] pmgconfig: Explain new TLS inbound policy configuration Christoph Heiss
2023-03-16 12:28 ` [pmg-devel] [PATCH pmg-{api, gui, docs} 0/3] fix #2437: Add TLS inbound policy for sender domains Stoiko Ivanov
2023-03-20  8:14   ` Christoph Heiss
2023-03-20  8:36     ` Stoiko Ivanov

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230309101846.192177-2-c.heiss@proxmox.com \
    --to=c.heiss@proxmox.com \
    --cc=pmg-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal