nexthops, by making the nexthop part of the endpoint-based
connection cache lookup key. File: smtp/smtp.h.
+20180623
+
+ TLS connection reuse: replaced random logic with TLS_MUST_MATCH()
+ when deciding under what conditions an authenticated
+ connection may be reused. Files: smtp/smtp_proto.c,
+ smtp/smtp.h.
+
+ TLS connection reuse: a tlsproxy(8) process will retire
+ after max_idle*max_use, or some sane constant if either is
+ set to zero. Files: master/event_server.c, tlsproxy/tlsproxy.c.
+
+ Documentation: automatic retirement. File: master/single_server.c.
+
+ Documentation: the connection caching limitation for SMTP
+ over TLS is now obsolete. File: proto/CONNECTION_CACHE_README.html.
MTAs, the slowest inbound MTA will attract most connections from Postfix to
that destination).
- .
Postfix 2.3 logs the use count of multiply-used connections, as shown in
the following example:
Postfix SMTP connection caching conflicts with certain applications:
- * The Postfix shared connection cache cannot be used with TLS, because saved
- TLS session information can be used only when a new connection is created
- (this limitation does not exist in connection caching implementations that
- reuse a connection only in the process that creates it). For this reason,
- the Postfix smtp(8) client always closes the connection after completing an
- attempt to deliver mail over TLS.
+ * With Postfix versions < 3.4, the Postfix shared connection cache cannot be
+ used with TLS, because an open TLS connection can be reused only in the
+ process that creates it. For this reason, the Postfix smtp(8) client
+ historically always closed the connection after completing an attempt to
+ deliver mail over TLS.
* Postfix connection caching currently does not support multiple SASL
accounts per mail server. Specifically, Postfix connection caching assumes
scache(8) -> tlsproxy(8) -> remote SMTP server
As of Postfix 3.4, TLS connection reuse is disabled by default. This may change
-once the impact on over-all performance is undestood.
+once the impact on over-all performance is understood.
C\bCl\bli\bie\ben\bnt\bt-\b-s\bsi\bid\bde\be T\bTL\bLS\bS s\bse\bes\bss\bsi\bio\bon\bn c\bca\bac\bch\bhe\be
- Log the TLS properties every time a connection is reused.
Currently, the properties are logged when a TLS session is created.
-
-- Retire a tlsproxy(8) process after max_idle*max_use seconds, even
- if it is not idle. This limits the impact of memory leaks in
- libraries or in Postfix itself.
times a connection may be reused, but this feature is unsafe as it
introduces a "fatal attractor" failure mode (when a destination has
multiple inbound MTAs, the slowest inbound MTA will attract most
-connections from Postfix to that destination). </p>.
+connections from Postfix to that destination). </p>
<p> Postfix 2.3 logs the use count of multiply-used connections,
as shown in the following example: </p>
<ul>
-<li> <p> The Postfix shared connection cache cannot be used with
-TLS, because saved TLS session information can be used only when a
-new connection is created (this limitation does not exist in
-connection caching implementations that reuse a connection only in
-the process that creates it). For this reason, the Postfix <a href="smtp.8.html">smtp(8)</a>
-client always closes the connection after completing an attempt to
-deliver mail over TLS. </p>
+<li> <p> With Postfix versions < 3.4, the Postfix shared connection
+cache cannot be used with TLS, because an open TLS connection can
+be reused only in the process that creates it. For this reason,
+the Postfix <a href="smtp.8.html">smtp(8)</a> client historically always closed the connection
+after completing an attempt to deliver mail over TLS.</p>
<li> <p> Postfix connection caching currently does not support
multiple SASL accounts per mail server. Specifically, Postfix
<pre> <a href="scache.8.html">scache(8)</a> -> <a href="tlsproxy.8.html">tlsproxy(8)</a> -> remote SMTP server</pre>
<p> As of Postfix 3.4, TLS connection reuse is disabled by default.
-This may change once the impact on over-all performance is undestood.
+This may change once the impact on over-all performance is understood.
</p>
<h3><a name="client_tls_cache">Client-side TLS session cache</a> </h3>
times a connection may be reused, but this feature is unsafe as it
introduces a "fatal attractor" failure mode (when a destination has
multiple inbound MTAs, the slowest inbound MTA will attract most
-connections from Postfix to that destination). </p>.
+connections from Postfix to that destination). </p>
<p> Postfix 2.3 logs the use count of multiply-used connections,
as shown in the following example: </p>
<ul>
-<li> <p> The Postfix shared connection cache cannot be used with
-TLS, because saved TLS session information can be used only when a
-new connection is created (this limitation does not exist in
-connection caching implementations that reuse a connection only in
-the process that creates it). For this reason, the Postfix smtp(8)
-client always closes the connection after completing an attempt to
-deliver mail over TLS. </p>
+<li> <p> With Postfix versions < 3.4, the Postfix shared connection
+cache cannot be used with TLS, because an open TLS connection can
+be reused only in the process that creates it. For this reason,
+the Postfix smtp(8) client historically always closed the connection
+after completing an attempt to deliver mail over TLS.</p>
<li> <p> Postfix connection caching currently does not support
multiple SASL accounts per mail server. Specifically, Postfix
<pre> scache(8) -> tlsproxy(8) -> remote SMTP server</pre>
<p> As of Postfix 3.4, TLS connection reuse is disabled by default.
-This may change once the impact on over-all performance is undestood.
+This may change once the impact on over-all performance is understood.
</p>
<h3><a name="client_tls_cache">Client-side TLS session cache</a> </h3>
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20180620"
+#define MAIL_RELEASE_DATE "20180624"
#define MAIL_VERSION_NUMBER "3.4"
#ifdef SNAPSHOT
/*
/* void event_server_drain()
/* DESCRIPTION
-/* This module implements a skeleton for multi-threaded
+/* This module implements a skeleton for event-driven
/* mail subsystems: mail subsystem programs that service multiple
/* clients at the same time. The resulting program expects to be run
/* from the \fBmaster\fR process.
/* .IP "CA_MAIL_SERVER_BOUNCE_INIT(const char *, const char **)"
/* Initialize the DSN filter for the bounce/defer service
/* clients with the specified map source and map names.
+/* .IP "CA_MAIL_SERVER_RETIRE_ME"
+/* Prevent a process from being reused indefinitely. After
+/* (var_max_use * var_max_idle) seconds or some sane constant,
+/* stop accepting new connections and terminate voluntarily
+/* when the process becomes idle.
/* .PP
/* event_server_disconnect() should be called by the application
/* to close a client connection.
/* result means this call should be tried again later.
/*
/* The var_use_limit variable limits the number of clients
-/* that a server can service before it commits suicide. This
+/* that a server can service before it commits suicide. This
/* value is taken from the global \fBmain.cf\fR configuration
/* file. Setting \fBvar_use_limit\fR to zero disables the
/* client limit.
exit(0);
}
+/* event_server_retire - retire when idle */
+
+static void event_server_retire(int unused_event, void *unused_context)
+{
+ if (msg_verbose)
+ msg_info("time to retire -- %s", event_server_slow_exit ?
+ "draining" : "exiting");
+ event_disable_readwrite(MASTER_STATUS_FD);
+ if (event_server_slow_exit)
+ event_server_slow_exit(event_server_name, event_server_argv);
+ else
+ event_server_exit();
+}
+
/* event_server_abort - terminate after abnormal master exit */
static void event_server_abort(int unused_event, void *unused_context)
{
if (msg_verbose)
- msg_info("master disconnect -- exiting");
+ msg_info("master disconnect -- %s", event_server_slow_exit ?
+ "draining" : "exiting");
event_disable_readwrite(MASTER_STATUS_FD);
if (event_server_slow_exit)
event_server_slow_exit(event_server_name, event_server_argv);
int redo_syslog_init = 0;
const char *dsn_filter_title;
const char **dsn_filter_maps;
+ int retire_me_from_flags = 0;
+ int retire_me = 0;
/*
* Process environment options as early as we can.
* stderr, because no-one is going to see them.
*/
opterr = 0;
- while ((c = GETOPT(argc, argv, "cdDi:lm:n:o:s:St:uvVz")) > 0) {
+ while ((c = GETOPT(argc, argv, "cdDi:lm:n:o:r:s:St:uvVz")) > 0) {
switch (c) {
case 'c':
root_dir = "setme";
redo_syslog_init = 1;
myfree(oname_val);
break;
+ case 'r':
+ if ((retire_me_from_flags = atoi(optarg)) <= 0)
+ msg_fatal("invalid retirement time: %s", optarg);
+ break;
case 's':
if ((socket_count = atoi(optarg)) <= 0)
msg_fatal("invalid socket_count: %s", optarg);
dsn_filter_maps = va_arg(ap, const char **);
bounce_client_init(dsn_filter_title, *dsn_filter_maps);
break;
+ case MAIL_SERVER_RETIRE_ME:
+ if (retire_me_from_flags > 0)
+ retire_me = retire_me_from_flags;
+ else if (var_idle_limit == 0 || var_use_limit == 0
+ || var_idle_limit > 18000 / var_use_limit)
+ retire_me = 18000;
+ else
+ retire_me = var_idle_limit * var_use_limit;
+ break;
default:
msg_panic("%s: unknown argument type: %d", myname, key);
}
*/
if (var_idle_limit > 0)
event_request_timer(event_server_timeout, (void *) 0, var_idle_limit);
+ if (retire_me)
+ event_request_timer(event_server_retire, (void *) 0, retire_me);
for (fd = MASTER_LISTEN_FD; fd < MASTER_LISTEN_FD + socket_count; fd++) {
event_enable_read(fd, event_server_accept, CAST_INT_TO_VOID_PTR(fd));
close_on_exec(fd, CLOSE_ON_EXEC);
/* Initialize the DSN filter for the bounce/defer service
/* clients with the specified map source and map names.
/* .IP "CA_MAIL_SERVER_RETIRE_ME"
-/* Terminate voluntarily when idle after (max_use * max_idle)
-/* seconds. This setting prevents a process from being reused
-/* indefinitely when var_use_limit is set to zero.
+/* Prevent a process from being reused indefinitely. After
+/* (var_max_use * var_max_idle) seconds or some sane constant,
+/* terminate voluntarily when the process becomes idle.
/* .PP
-/* The var_use_limit variable limits the number of clients that
-/* a server can service before it commits suicide.
-/* Do not change this setting before calling single_server_main().
-/* This value is taken from the global \fBmain.cf\fR configuration
-/* file. Setting \fBvar_use_limit\fR to zero disables the client limit.
-/* Specify CA_MAIL_SERVER_RETIRE_ME (see above) to limit the total
-/* process lifetime.
+/* The var_use_limit variable limits the number of clients
+/* that a server can service before it commits suicide. This
+/* value is taken from the global \fBmain.cf\fR configuration
+/* file. Setting \fBvar_use_limit\fR to zero disables the
+/* client limit.
/*
/* The var_idle_limit variable limits the time that a service
/* receives no client connection requests before it commits suicide.
static NORETURN single_server_retire(int unused_event, void *unused_context)
{
if (msg_verbose)
- msg_info("time to retire -- exiting");
+ msg_info("time to retire -- exiting");
single_server_exit();
}
int redo_syslog_init = 0;
const char *dsn_filter_title;
const char **dsn_filter_maps;
+ int retire_me_from_flags = 0;
int retire_me = 0;
/*
* stderr, because no-one is going to see them.
*/
opterr = 0;
- while ((c = GETOPT(argc, argv, "cdDi:lm:n:o:s:St:uvVz")) > 0) {
+ while ((c = GETOPT(argc, argv, "cdDi:lm:n:o:r:s:St:uvVz")) > 0) {
switch (c) {
case 'c':
root_dir = "setme";
redo_syslog_init = 1;
myfree(oname_val);
break;
+ case 'r':
+ if ((retire_me_from_flags = atoi(optarg)) <= 0)
+ msg_fatal("invalid retirement time: %s", optarg);
+ break;
case 's':
if ((socket_count = atoi(optarg)) <= 0)
msg_fatal("invalid socket_count: %s", optarg);
bounce_client_init(dsn_filter_title, *dsn_filter_maps);
break;
case MAIL_SERVER_RETIRE_ME:
- if (var_idle_limit == 0 || var_use_limit == 0
- || var_idle_limit > 86400 / var_use_limit)
- retire_me = 86400;
+ if (retire_me_from_flags > 0)
+ retire_me = retire_me_from_flags;
+ else if (var_idle_limit == 0 || var_use_limit == 0
+ || var_idle_limit > 18000 / var_use_limit)
+ retire_me = 18000;
else
retire_me = var_idle_limit * var_use_limit;
break;
#ifdef USE_TLS
#define COND_TLS_SMTP_KEY_FLAG_NEXTHOP \
- (state->tls->level > TLS_LEV_ENCRYPT ? SMTP_KEY_FLAG_NEXTHOP : 0)
+ (TLS_MUST_MATCH(state->tls->level) ? SMTP_KEY_FLAG_NEXTHOP : 0)
#else
#define COND_TLS_SMTP_KEY_FLAG_NEXTHOP \
(0)
/*
* Allow address-based reuse only for security levels that don't require
- * certificate checks.
+ * certificate checks. Not to be confused with a similar constraint in
+ * the destination label smtp_key pattern, which conditionally includes
+ * the nexthop to prevent the reuse of an authenticated connection to the
+ * same MX hostname and the same IP address, but for a different nexthop
+ * destination (just in case we start to send SNI with the nexthop, and
+ * forget to update connection cache lookup key patterns).
*/
#ifdef USE_TLS
- if (state->tls->level > TLS_LEV_ENCRYPT)
+ if (TLS_MUST_MATCH(state->tls->level))
return (0);
#endif
CA_MAIL_SERVER_PRE_INIT(pre_jail_init),
CA_MAIL_SERVER_POST_INIT(post_jail_init),
CA_MAIL_SERVER_SLOW_EXIT(tlsp_drain),
+ CA_MAIL_SERVER_RETIRE_ME,
CA_MAIL_SERVER_WATCHDOG(&var_tlsp_watchdog),
0);
}