public inbox for pmg-devel@lists.proxmox.com
 help / color / mirror / Atom feed
* [pmg-devel] [PATCH pmg-api/gui v3] add quarantine self service button
@ 2020-11-18 10:59 Dominik Csapak
  2020-11-18 10:59 ` [pmg-devel] [PATCH pmg-api v3 1/3] refactor domain_regex to Utils Dominik Csapak
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Dominik Csapak @ 2020-11-18 10:59 UTC (permalink / raw)
  To: pmg-devel

adds an option/api call to request an quarantine link for an
email whose domain is in the relay domains

for now, we do not expose that option to the ui, but this can easily be
added if wanted

NOTES on security:

this adds a world reachable api call, that can potentially send e-mails
to users that belong to a relay domain

we ratelimit 1 request/5sec and 1 request/user/hour so that a dos is infeasible

for now all text is hardcoded, templates could be used later on
(if users want that)

changes from v2:
* introduce ratelimit
* factor out the sending sub (for readability)
* change the gui window to only have an 'OK' button (without reset)

changes from v1:
* move config to 'spamquar' section
* show button also on admin interface

pmg-api:

Dominik Csapak (3):
  refactor domain_regex to Utils
  add 'quarantinelink' to spamquar config
  api2/quarantine: add global sendlink api call

 src/PMG/API2/Quarantine.pm  | 126 ++++++++++++++++++++++++++++++++++++
 src/PMG/CLI/pmgqm.pm        |  29 +--------
 src/PMG/Config.pm           |   6 ++
 src/PMG/HTTPServer.pm       |   1 +
 src/PMG/Service/pmgproxy.pm |   4 ++
 src/PMG/Utils.pm            |  26 ++++++++
 6 files changed, 165 insertions(+), 27 deletions(-)

pmg-gui:

Dominik Csapak (1):
  add 'Request Quarantine Link' Button to LoginView

 js/LoginView.js   | 33 +++++++++++++++++++++++++++++++++
 pmg-index.html.tt |  3 ++-
 2 files changed, 35 insertions(+), 1 deletion(-)

-- 
2.20.1





^ permalink raw reply	[flat|nested] 6+ messages in thread

* [pmg-devel] [PATCH pmg-api v3 1/3] refactor domain_regex to Utils
  2020-11-18 10:59 [pmg-devel] [PATCH pmg-api/gui v3] add quarantine self service button Dominik Csapak
@ 2020-11-18 10:59 ` Dominik Csapak
  2020-11-18 10:59 ` [pmg-devel] [PATCH pmg-api v3 2/3] add 'quarantinelink' to spamquar config Dominik Csapak
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Dominik Csapak @ 2020-11-18 10:59 UTC (permalink / raw)
  To: pmg-devel

we will need this somewhere else later

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 src/PMG/CLI/pmgqm.pm | 29 ++---------------------------
 src/PMG/Utils.pm     | 26 ++++++++++++++++++++++++++
 2 files changed, 28 insertions(+), 27 deletions(-)

diff --git a/src/PMG/CLI/pmgqm.pm b/src/PMG/CLI/pmgqm.pm
index 937269f..39253db 100755
--- a/src/PMG/CLI/pmgqm.pm
+++ b/src/PMG/CLI/pmgqm.pm
@@ -33,31 +33,6 @@ sub setup_environment {
     PMG::RESTEnvironment->setup_default_cli_env();
 }
 
-sub domain_regex {
-    my ($domains) = @_;
-
-    my @ra;
-    foreach my $d (@$domains) {
-	# skip domains with non-DNS name characters
-	next if $d =~ m/[^A-Za-z0-9\-\.]/;
-	if ($d =~ m/^\.(.*)$/) {
-	    my $dom = $1;
-	    $dom =~ s/\./\\\./g;
-	    push @ra, $dom;
-	    push @ra, "\.\*\\.$dom";
-	} else {
-	    $d =~ s/\./\\\./g;
-	    push @ra, $d;
-	}
-    }
-
-    my $re = join ('|', @ra);
-
-    my $regex = qr/\@($re)$/i;
-
-    return $regex;
-}
-
 sub get_item_data {
     my ($data, $ref) = @_;
 
@@ -145,7 +120,7 @@ __PACKAGE__->register_method ({
 	my $dbh = PMG::DBTools::open_ruledb();
 
 	my $domains = PVE::INotify::read_file('domains');
-	my $domainregex = domain_regex([keys %$domains]);
+	my $domainregex = PMG::Utils::domain_regex([keys %$domains]);
 
 	my $sth = $dbh->prepare(
 	    "SELECT pmail, AVG(spamlevel) as spamlevel, count(*)  FROM CMailStore, CMSReceivers " .
@@ -278,7 +253,7 @@ __PACKAGE__->register_method ({
 	}
 
 	my $domains = PVE::INotify::read_file('domains');
-	my $domainregex = domain_regex([keys %$domains]);
+	my $domainregex = PMG::Utils::domain_regex([keys %$domains]);
 
 	my $template = "spamreport-${reportstyle}.tt";
 	my $found = 0;
diff --git a/src/PMG/Utils.pm b/src/PMG/Utils.pm
index d0654e1..a04d41b 100644
--- a/src/PMG/Utils.pm
+++ b/src/PMG/Utils.pm
@@ -1417,4 +1417,30 @@ sub reload_smtp_filter {
     return kill (10, $pid); # send SIGUSR1
 }
 
+sub domain_regex {
+    my ($domains) = @_;
+
+    my @ra;
+    foreach my $d (@$domains) {
+	# skip domains with non-DNS name characters
+	next if $d =~ m/[^A-Za-z0-9\-\.]/;
+	if ($d =~ m/^\.(.*)$/) {
+	    my $dom = $1;
+	    $dom =~ s/\./\\\./g;
+	    push @ra, $dom;
+	    push @ra, "\.\*\\.$dom";
+	} else {
+	    $d =~ s/\./\\\./g;
+	    push @ra, $d;
+	}
+    }
+
+    my $re = join ('|', @ra);
+
+    my $regex = qr/\@($re)$/i;
+
+    return $regex;
+}
+
+
 1;
-- 
2.20.1





^ permalink raw reply	[flat|nested] 6+ messages in thread

* [pmg-devel] [PATCH pmg-api v3 2/3] add 'quarantinelink' to spamquar config
  2020-11-18 10:59 [pmg-devel] [PATCH pmg-api/gui v3] add quarantine self service button Dominik Csapak
  2020-11-18 10:59 ` [pmg-devel] [PATCH pmg-api v3 1/3] refactor domain_regex to Utils Dominik Csapak
@ 2020-11-18 10:59 ` Dominik Csapak
  2020-11-18 10:59 ` [pmg-devel] [PATCH pmg-api v3 3/3] api2/quarantine: add global sendlink api call Dominik Csapak
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Dominik Csapak @ 2020-11-18 10:59 UTC (permalink / raw)
  To: pmg-devel

to enable the 'Request Quarantine Link' button and api call

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 src/PMG/Config.pm           | 6 ++++++
 src/PMG/Service/pmgproxy.pm | 4 ++++
 2 files changed, 10 insertions(+)

diff --git a/src/PMG/Config.pm b/src/PMG/Config.pm
index cd69c9c..155990b 100755
--- a/src/PMG/Config.pm
+++ b/src/PMG/Config.pm
@@ -289,6 +289,11 @@ sub properties {
 	    description => "Text for 'From' header in daily spam report mails.",
 	    type => 'string',
 	},
+	quarantinelink => {
+	    description => "Enables user self-service for Quarantine Links. Caution: this is accessible without authentication",
+	    type => 'boolean',
+	    default => 0,
+	},
     };
 }
 
@@ -303,6 +308,7 @@ sub options {
 	allowhrefs => { optional => 1 },
 	port => { optional => 1 },
 	protocol => { optional => 1 },
+	quarantinelink => { optional => 1 },
     };
 }
 
diff --git a/src/PMG/Service/pmgproxy.pm b/src/PMG/Service/pmgproxy.pm
index ea58b50..cec2754 100755
--- a/src/PMG/Service/pmgproxy.pm
+++ b/src/PMG/Service/pmgproxy.pm
@@ -21,6 +21,7 @@ use PVE::APIServer::Utils;
 
 use PMG::HTTPServer;
 use PMG::API2;
+use PMG::Config;
 
 use Template;
 
@@ -227,6 +228,8 @@ sub get_index {
 	$version = $1;
     };
 
+    my $cfg = PMG::Config->new();
+    my $quarantinelink = $cfg->get('spamquar', 'quarantinelink');
 
     $username = '' if !$username;
 
@@ -242,6 +245,7 @@ sub get_index {
 	debug => $args->{debug} || $server->{debug},
 	version => $version,
 	wtversion => $wtversion,
+	quarantinelink => $quarantinelink,
     };
 
     my $template_name;
-- 
2.20.1





^ permalink raw reply	[flat|nested] 6+ messages in thread

* [pmg-devel] [PATCH pmg-api v3 3/3] api2/quarantine: add global sendlink api call
  2020-11-18 10:59 [pmg-devel] [PATCH pmg-api/gui v3] add quarantine self service button Dominik Csapak
  2020-11-18 10:59 ` [pmg-devel] [PATCH pmg-api v3 1/3] refactor domain_regex to Utils Dominik Csapak
  2020-11-18 10:59 ` [pmg-devel] [PATCH pmg-api v3 2/3] add 'quarantinelink' to spamquar config Dominik Csapak
@ 2020-11-18 10:59 ` Dominik Csapak
  2020-11-18 10:59 ` [pmg-devel] [PATCH pmg-gui v3 1/1] add 'Request Quarantine Link' Button to LoginView Dominik Csapak
  2020-11-18 16:56 ` [pmg-devel] applied: [PATCH pmg-api/gui v3] add quarantine self service button Thomas Lamprecht
  4 siblings, 0 replies; 6+ messages in thread
From: Dominik Csapak @ 2020-11-18 10:59 UTC (permalink / raw)
  To: pmg-devel

this api call takes an email, checks it against the relay domains,
and prepares a custom quarantinelink for that email  and sends it there

this has to happen unauthenticated, since the idea is that the user
want to access the quarantine but has no current ticket (and no
old spam report with a ticket)

we rate limit the requests by allowing only a request per 5 seconds
(to prevent dos'ing the internal mail server) and only
one request per user/hour

this api call is disabled by default

if admins want even more ratelimiting, they can setup something
like fail2ban to block hosts hitting this api call often

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 src/PMG/API2/Quarantine.pm | 126 +++++++++++++++++++++++++++++++++++++
 src/PMG/HTTPServer.pm      |   1 +
 2 files changed, 127 insertions(+)

diff --git a/src/PMG/API2/Quarantine.pm b/src/PMG/API2/Quarantine.pm
index 73fb0ec..2085eec 100644
--- a/src/PMG/API2/Quarantine.pm
+++ b/src/PMG/API2/Quarantine.pm
@@ -8,6 +8,10 @@ use Data::Dumper;
 use Encode;
 use File::Path;
 use IO::File;
+use MIME::Entity;
+use URI::Escape;
+use Time::HiRes qw(usleep gettimeofday tv_interval);
+use File::stat ();
 
 use Mail::Header;
 use Mail::SpamAssassin;
@@ -195,6 +199,7 @@ __PACKAGE__->register_method ({
 	    { name => 'attachment' },
 	    { name => 'listattachments' },
 	    { name => 'download' },
+	    { name => 'sendlink' },
 	];
 
 	return $result;
@@ -1239,4 +1244,125 @@ __PACKAGE__->register_method ({
 	return undef;
     }});
 
+my $link_map_fn = "/run/pmgproxy/quarantinelink.map";
+my $per_user_limit = 60*60; # 1 hour
+
+my sub send_link_mail {
+    my ($cfg, $receiver) = @_;
+
+    my $hostname = PVE::INotify::nodename();
+    my $fqdn = $cfg->get('spamquar', 'hostname') //
+    PVE::Tools::get_fqdn($hostname);
+
+    my $port = $cfg->get('spamquar', 'port') // 8006;
+
+    my $protocol = $cfg->get('spamquar', 'protocol') // 'https';
+
+    my $protocol_fqdn_port = "$protocol://$fqdn";
+    if (($protocol eq 'https' && $port != 443) ||
+	($protocol eq 'http' && $port != 80)) {
+	$protocol_fqdn_port .= ":$port";
+    }
+
+    my $mailfrom = $cfg->get ('spamquar', 'mailfrom') //
+    "Proxmox Mail Gateway <postmaster>";
+
+    my $ticket = PMG::Ticket::assemble_quarantine_ticket($receiver);
+    my $esc_ticket = uri_escape($ticket);
+    my $link = "$protocol_fqdn_port/quarantine?ticket=${esc_ticket}";
+
+    my $text = "Here is your Link for the Spam Quarantine on $fqdn:\n\n$link\n";
+
+    my $mail = MIME::Entity->build(
+	Type    => "text/plain",
+	To      => $receiver,
+	From    => $mailfrom,
+	Subject => "Proxmox Mail Gateway - Quarantine Link",
+	Data    => $text,
+    );
+
+    # we use an empty envelope sender (we dont want to receive NDRs)
+    PMG::Utils::reinject_mail ($mail, '', [$receiver], undef, $fqdn);
+}
+
+__PACKAGE__->register_method ({
+    name =>'sendlink',
+    path => 'sendlink',
+    method => 'POST',
+    description => "Send Quarantine link to given e-mail.",
+    permissions => { user => 'world' },
+    protected => 1,
+    parameters => {
+	additionalProperties => 0,
+	properties => {
+	    mail => get_standard_option('pmg-email-address'),
+	},
+    },
+    returns => { type => "null" },
+    code => sub {
+	my ($param) = @_;
+
+	my $starttime = [gettimeofday];
+
+	my $cfg = PMG::Config->new();
+	my $is_enabled = $cfg->get('spamquar', 'quarantinelink');
+	if (!$is_enabled) {
+	    die "This feature is not enabled\n";
+	}
+
+	my $stat = File::stat::stat($link_map_fn);
+
+	if (defined($stat) && ($stat->mtime) + 5 > $starttime->[0]) {
+	    die "Too many requests. Please try again later\n";
+	}
+
+	my $domains = PVE::INotify::read_file('domains');
+	my $domainregex = PMG::Utils::domain_regex([keys %$domains]);
+
+	my $receiver = $param->{mail};
+
+	if ($receiver !~ $domainregex) {
+	    return undef; # silently ignore invalid mails
+	}
+
+	PVE::Tools::lock_file_full("${link_map_fn}.lck", 10, 1, sub {
+	    if (-f $link_map_fn) {
+		# check if user is allowed to request mail
+		my $lines = [split("\n", PVE::Tools::file_get_contents($link_map_fn))];
+		for my $line (@$lines) {
+		    next if $line !~ m/^\Q$receiver\E (\d+)$/;
+		    if (($1 + $per_user_limit) > $starttime->[0]) {
+			die "Too many requests for '$receiver', only one request per hour is permitted. ".
+			"Please try again later\n";
+		    } else {
+			last;
+		    }
+		}
+	    }
+	});
+	die $@ if $@;
+
+	# we are allowed to send mail, lock and update file and send
+	PVE::Tools::lock_file("${link_map_fn}.lck", 10, sub {
+	    my $newdata = "";
+	    if (-f $link_map_fn) {
+		my $data = PVE::Tools::file_get_contents($link_map_fn);
+		for my $line (split("\n", $data)) {
+		    if ($line =~ m/^(.*) (\d+)$/) {
+			if (($2 + $per_user_limit) > $starttime->[0]) {
+			    $newdata .= $line . "\n";
+			}
+		    }
+		}
+	    }
+	    $newdata .= "$receiver $starttime->[0]\n";
+	    PVE::Tools::file_set_contents($link_map_fn, $newdata);
+	});
+	die $@ if $@;
+
+	send_link_mail($cfg, $receiver);
+
+	return undef;
+    }});
+
 1;
diff --git a/src/PMG/HTTPServer.pm b/src/PMG/HTTPServer.pm
index eb48b5f..3dc9655 100755
--- a/src/PMG/HTTPServer.pm
+++ b/src/PMG/HTTPServer.pm
@@ -58,6 +58,7 @@ sub auth_handler {
 
     # explicitly allow some calls without auth
     if (($rel_uri eq '/access/domains' && $method eq 'GET') ||
+	($rel_uri eq '/quarantine/sendlink' && ($method eq 'GET' || $method eq 'POST')) ||
 	($rel_uri eq '/access/ticket' && ($method eq 'GET' || $method eq 'POST'))) {
 	$require_auth = 0;
     }
-- 
2.20.1





^ permalink raw reply	[flat|nested] 6+ messages in thread

* [pmg-devel] [PATCH pmg-gui v3 1/1] add 'Request Quarantine Link' Button to LoginView
  2020-11-18 10:59 [pmg-devel] [PATCH pmg-api/gui v3] add quarantine self service button Dominik Csapak
                   ` (2 preceding siblings ...)
  2020-11-18 10:59 ` [pmg-devel] [PATCH pmg-api v3 3/3] api2/quarantine: add global sendlink api call Dominik Csapak
@ 2020-11-18 10:59 ` Dominik Csapak
  2020-11-18 16:56 ` [pmg-devel] applied: [PATCH pmg-api/gui v3] add quarantine self service button Thomas Lamprecht
  4 siblings, 0 replies; 6+ messages in thread
From: Dominik Csapak @ 2020-11-18 10:59 UTC (permalink / raw)
  To: pmg-devel

if the template has 'quarantinelink' enabled, we
show a button 'Request Quarantine Link' on the quarantine login ui

there a user can enter their e-mail and request a link to the quarantine

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 js/LoginView.js   | 33 +++++++++++++++++++++++++++++++++
 pmg-index.html.tt |  3 ++-
 2 files changed, 35 insertions(+), 1 deletion(-)

diff --git a/js/LoginView.js b/js/LoginView.js
index 8e610aa..4770494 100644
--- a/js/LoginView.js
+++ b/js/LoginView.js
@@ -10,6 +10,8 @@ Ext.define('PMG.LoginView', {
 
 	    let realmfield = me.lookup('realmfield');
 
+	    me.lookup('quarantineButton').setVisible(!!Proxmox.QuarantineLink);
+
 	    if (view.targetview !== 'quarantineview') {
 		return;
 	    }
@@ -65,6 +67,30 @@ Ext.define('PMG.LoginView', {
 	    }
 	},
 
+	openQuarantineLinkWindow: function() {
+	    let me = this;
+	    me.lookup('loginwindow').setVisible(false);
+	    Ext.create('Proxmox.window.Edit', {
+		title: gettext('Request Quarantine Link'),
+		url: '/quarantine/sendlink',
+		isCreate: true,
+		submitText: gettext('OK'),
+		method: 'POST',
+		items: [
+		    {
+			xtype: 'proxmoxtextfield',
+			name: 'mail',
+			fieldLabel: gettext('Your E-Mail'),
+		    },
+		],
+		listeners: {
+		    destroy: function() {
+			me.lookup('loginwindow').show(true);
+		    },
+		},
+	    }).show();
+	},
+
 	control: {
 	    'field[name=lang]': {
 		change: function(f, value) {
@@ -76,6 +102,9 @@ Ext.define('PMG.LoginView', {
 		    window.location.reload();
 		},
 	    },
+	    'button[reference=quarantineButton]': {
+		click: 'openQuarantineLinkWindow',
+	    },
 	    'button[reference=loginButton]': {
 		click: 'submitForm',
 	    },
@@ -172,6 +201,10 @@ Ext.define('PMG.LoginView', {
                         },
 		    ],
 		    buttons: [
+			{
+			    text: gettext('Request Quarantine Link'),
+			    reference: 'quarantineButton',
+			},
 			{
 			    text: gettext('Login'),
 			    reference: 'loginButton',
diff --git a/pmg-index.html.tt b/pmg-index.html.tt
index 4faf0cf..4a29ba2 100644
--- a/pmg-index.html.tt
+++ b/pmg-index.html.tt
@@ -30,7 +30,8 @@
         Setup: { auth_cookie_name: 'PMGAuthCookie' },
         NodeName: '[% nodename %]',
         UserName: '[% username %]',
-        CSRFPreventionToken: '[% token %]'
+        CSRFPreventionToken: '[% token %]',
+        QuarantineLink: [% IF quarantinelink %] true [% ELSE %] false [% END %],
       };
     </script>
     <script type="text/javascript" src="/proxmoxlib.js?ver=[% wtversion %]"></script>
-- 
2.20.1





^ permalink raw reply	[flat|nested] 6+ messages in thread

* [pmg-devel] applied: [PATCH pmg-api/gui v3] add quarantine self service button
  2020-11-18 10:59 [pmg-devel] [PATCH pmg-api/gui v3] add quarantine self service button Dominik Csapak
                   ` (3 preceding siblings ...)
  2020-11-18 10:59 ` [pmg-devel] [PATCH pmg-gui v3 1/1] add 'Request Quarantine Link' Button to LoginView Dominik Csapak
@ 2020-11-18 16:56 ` Thomas Lamprecht
  4 siblings, 0 replies; 6+ messages in thread
From: Thomas Lamprecht @ 2020-11-18 16:56 UTC (permalink / raw)
  To: Dominik Csapak, pmg-devel

On 18.11.20 11:59, Dominik Csapak wrote:
> adds an option/api call to request an quarantine link for an
> email whose domain is in the relay domains
> 
> for now, we do not expose that option to the ui, but this can easily be
> added if wanted
> 
> NOTES on security:
> 
> this adds a world reachable api call, that can potentially send e-mails
> to users that belong to a relay domain
> 
> we ratelimit 1 request/5sec and 1 request/user/hour so that a dos is infeasible
> 
> for now all text is hardcoded, templates could be used later on
> (if users want that)
> 
> changes from v2:
> * introduce ratelimit
> * factor out the sending sub (for readability)
> * change the gui window to only have an 'OK' button (without reset)
> 
> changes from v1:
> * move config to 'spamquar' section
> * show button also on admin interface
> 
> pmg-api:
> 
> Dominik Csapak (3):
>   refactor domain_regex to Utils
>   add 'quarantinelink' to spamquar config
>   api2/quarantine: add global sendlink api call
> 
>  src/PMG/API2/Quarantine.pm  | 126 ++++++++++++++++++++++++++++++++++++
>  src/PMG/CLI/pmgqm.pm        |  29 +--------
>  src/PMG/Config.pm           |   6 ++
>  src/PMG/HTTPServer.pm       |   1 +
>  src/PMG/Service/pmgproxy.pm |   4 ++
>  src/PMG/Utils.pm            |  26 ++++++++
>  6 files changed, 165 insertions(+), 27 deletions(-)
> 
> pmg-gui:
> 
> Dominik Csapak (1):
>   add 'Request Quarantine Link' Button to LoginView
> 
>  js/LoginView.js   | 33 +++++++++++++++++++++++++++++++++
>  pmg-index.html.tt |  3 ++-
>  2 files changed, 35 insertions(+), 1 deletion(-)
> 



applied, with some followups:
* use built-in time() to get seconds since epoch
* sleep a bit, especially more on the rate limit cases
* code consitency

one thing which feels a bit "flawed" is the fact that if one knows or guesses
correctly a valid domain, the can DOS this requester for all other valid ones by
simply looping and sending a request for "$name++@$domain" where $name gets changed
slightly each round - this way others race with them to get in between the small
timeframe where the mtime is old enough again and the "attacker" gets through a
request again.

But, this can be somewhat solved by fail2ban and it's opt-in anyway.





^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2020-11-18 16:56 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-11-18 10:59 [pmg-devel] [PATCH pmg-api/gui v3] add quarantine self service button Dominik Csapak
2020-11-18 10:59 ` [pmg-devel] [PATCH pmg-api v3 1/3] refactor domain_regex to Utils Dominik Csapak
2020-11-18 10:59 ` [pmg-devel] [PATCH pmg-api v3 2/3] add 'quarantinelink' to spamquar config Dominik Csapak
2020-11-18 10:59 ` [pmg-devel] [PATCH pmg-api v3 3/3] api2/quarantine: add global sendlink api call Dominik Csapak
2020-11-18 10:59 ` [pmg-devel] [PATCH pmg-gui v3 1/1] add 'Request Quarantine Link' Button to LoginView Dominik Csapak
2020-11-18 16:56 ` [pmg-devel] applied: [PATCH pmg-api/gui v3] add quarantine self service button Thomas Lamprecht

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