This is an automated email from the ASF dual-hosted git repository.
amc pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/master by this push:
new b156314 TLS Bridge: Fix error where connect failure lead to a silent
timeout. The error reporting back to the original user agent was also improved.
The documentation was updated with more details on configuring without
disabling remap required.
b156314 is described below
commit b156314dd8d7c6f918a3154b33340a80bc0dcf95
Author: Alan M. Carroll <[email protected]>
AuthorDate: Wed Nov 28 13:19:38 2018 -0600
TLS Bridge: Fix error where connect failure lead to a silent timeout.
The error reporting back to the original user agent was also improved.
The documentation was updated with more details on configuring without
disabling remap required.
---
.../plugins/example-plugins/tls_bridge.en.rst | 84 ++++++++++++++--------
plugins/experimental/tls_bridge/tls_bridge.cc | 39 ++++------
2 files changed, 67 insertions(+), 56 deletions(-)
diff --git a/doc/developer-guide/plugins/example-plugins/tls_bridge.en.rst
b/doc/developer-guide/plugins/example-plugins/tls_bridge.en.rst
index 0530afa..85836c2 100644
--- a/doc/developer-guide/plugins/example-plugins/tls_bridge.en.rst
+++ b/doc/developer-guide/plugins/example-plugins/tls_bridge.en.rst
@@ -20,10 +20,10 @@
|Name|
**********
-This plugin is used to provide secured TLS tunnels for connections between a
Client and a Service
-via two gateway |TS| instances. By configuring the |TS| instances the level of
security in the
-tunnel can be easily controlled for all communications across the tunnels
without having to update
-the client or service.
+This plugin is used to provide TLS tunnels for connections between a Client
and a Service via two
+gateway |TS| instances using explicit proxying. By configuring the |TS|
instances the level of
+security in the tunnel can be easily controlled for all communications across
the tunnels without
+having to update the client or service.
Description
===========
@@ -47,24 +47,26 @@ The tunnel is sustained by two instances of |TS|.
[Ingress ATS] ..> [tls_bridge\nPlugin] : Uses
-The ingress |TS| accepts a connection from the Client. This connection gets
intercepted by the
-|Name| plugin inside |TS|. The plugin then makes a TLS connection to the peer
|TS| using the
-configured level of security. The original request from the Client to the
ingress |TS| is then sent
-to the peer |TS| to create a connection from the peer |TS| to the Service.
After this the
-Client has a virtual circut to the Service and can use any TCP based
communication (including TLS).
-Effectively the plugin causes the connectivity to work as if the Client had
done the ``CONNECT``
-directly to the peer |TS|. Note this means the DNS lookup for the Service is
done by the peer |TS|,
-not the ingress |TS|.
+The ingress |TS| accepts an HTTP ``CONNECT`` request from the Client. This
connection gets
+intercepted by the |Name| plugin inside |TS| if the destination matches one of
the configured
+destinations. The plugin then makes a TLS connection to the peer |TS| using
the configured level of
+security. The original ``CONNECT`` request from the Client to the ingress |TS|
is then sent to the
+peer |TS| to create a connection from the peer |TS| to the Service. After this
the Client has a
+virtual circut to the Service and can use any TCP based communication
(including TLS). Effectively
+the plugin causes the explicit proxy to work as if the Client had done the
``CONNECT`` directly to
+the peer |TS|. Note this means the DNS lookup for the Service is done by the
peer |TS|, not the
+ingress |TS|.
-The plugin is configured with a mapping of Service names to peer |TS|
instances. The Service
-names are URLs which will in the original HTTP request made by the Client
after connecting to the
-ingress |TS|. This means the FQDN for the Service is not resolved in the
environment of the peer
+The plugin is configured with a mapping of Service names to peer |TS|
instances. The Service names
+are URLs which will be in the original HTTP request made by the Client after
connecting to the
+ingress |TS|. This means the FQDN for the Service is resolved in the
environment of the peer
|TS| and not the ingress |TS|.
Configuration
=============
-|Name| requires at least two instances of |TS| (Ingress and Peer).
+|Name| requires at least two instances of |TS| (Ingress and Peer). The client
connects to the
+ingress |TS|, and the peer |TS| connects to the service.
#. Disable caching on |TS| in ``records.config``::
@@ -72,32 +74,46 @@ Configuration
#. Configure the ports.
- * The Peer |TS| must be listening on an SSL enabled proxy port. For
instance, if the proxy port for the Peer is 4443, then configuration in
``records.config`` would have::
+ * The Peer |TS| must be listening on an SSL enabled proxy port. For
instance, if the proxy port
+ for the Peer is 4443, then configuration in ``records.config`` would
have::
CONFIG proxy.config.http.server_ports STRING 4443:ssl
- * The Ingress |TS| must allow ``CONNECT`` to the Peer proxy port. This
would be set in ``records.config`` by::
+ * The Ingress |TS| must allow ``CONNECT`` to the Peer proxy port. This
would be set in
+ ``records.config`` by::
CONFIG proxy.config.http.connect_ports STRING 4443
The Ingress |TS| also needs ``proxy.config.http.server_ports``
configured to have proxy ports
to which the Client can connect.
-#. Remap is not required, however, |TS| requires remap in order to accept the
request. This can be done by disabling the remap requirement::
+#. Remap on the ingress is not required, however, |TS| requires remap in order
to accept the
+ request. This can be done by disabling the remap requirement::
CONFIG proxy.config.url_remap.remap_required INT 0
- In this case |TS| will act as an open proxy which is unlikely to be a good
idea. |TS| will need
- to run in a restricted environment or use access control (via
``ip_allow.config`` or
- ``iptables``).
+ In this case |TS| will act as an open proxy which is unlikely to be a good
idea, therefore if
+ this approach is used |TS| will need to run in a restricted environment or
use access control
+ (via ``ip_allow.config`` or ``iptables``).
- If this is unsuitable then an identity remap rule can be added for the peer
|TS|. If the peer |TS|
- was named "peer.ats" then the remap rule would be ::
+ If this is unsuitable then an identity remap rule can be added for the peer
|TS|. If the peer
+ |TS| was named "peer.ats" and it listens on port 4443, then the remap rule
would be ::
- remap https://peer.ats https://peer.ats
+ remap https://peer.ats:4443 https://peer.ats:4443
Remapping will be disabled for the user agent connection and so it will not
need a rule.
+#. If remap is required on the peer to enable the outbound connection from the
peer to the service
+ (it is not explicitly disabled) the destination port must be
+ explicitly stated [#]_. E.g. ::
+
+ map https://service:4443 https://service:4443
+
+ Note this remap rule cannot alter the actual HTTP transactions between the
client and service
+ because those happen inside what is effectively a tunnel between the client
and service, supported
+ by the two |TS| instances. This rule just allows the ``CONNECT`` sent from
the ingress to cause a
+ tunnel connection from the peer to the service.
+
#. Configure the Ingress |TS| to verify the Peer server certificate::
CONFIG proxy.config.ssl.client.verify.server.policy STRING ENFORCED
@@ -119,16 +135,21 @@ Configuration
#. Enable the |Name| plugin in ``plugin.config``. The plugin is configured by
arguments in
``plugin.config``. These are arguments are in pairs of a *destination* and
a *peer*. The
destination is an anchored regular expression which is matched against the
host name in the Client
- ``CONNECT``. The destinations are checked in order and the first match is
used to select the Peer
+ ``CONNECT``. The destinations are checked in order and the first match is
used to select the peer
|TS|. The peer should be an FQDN or IP address with an optional port. For
the example above, if
- the Peer |TS| was named "peer.example.com" on port 4443 and the Service at
``*.service.com``, the
- peer argument would be "peer.example.com:4443". In ``plugin.config`` this
would be::
+ the Peer |TS| was named "peer.ats" on port 4443 and the Service at
``*.service.com``, the
+ peer argument would be "peer.ats:4443". In ``plugin.config`` this would be::
- tls_bridge.so .*[.]service[.]com peer.example.com:4443
+ tls_bridge.so .*[.]service[.]com peer.ats:4443
Note the '.' characters are escaped with brackets so that, for instance,
"someservice.com" does
not match the rule.
+ If there was another service, "\*.altsvc.ats", via a different peer
"altpeer.ats" on port 4443,
+ the configuration would be ::
+
+ tls_bridge.so .*[.]service[.]com peer.ats:4443 .*[.]altsvc.ats
altpeer.ats:4443
+
Notes
=====
@@ -255,3 +276,8 @@ Debugging
---------
Debugging messages for the plugin can be enabled with the "tls_bridge" debug
tag.
+
+
+.. rubric:: Footnotes
+
+.. [#] This is likely due to a bug in |TS|, currently under investigation.
diff --git a/plugins/experimental/tls_bridge/tls_bridge.cc
b/plugins/experimental/tls_bridge/tls_bridge.cc
index 94657ac..7cfe1e5 100644
--- a/plugins/experimental/tls_bridge/tls_bridge.cc
+++ b/plugins/experimental/tls_bridge/tls_bridge.cc
@@ -186,8 +186,6 @@ struct Bridge {
TSHttpStatus _out_response_code = TS_HTTP_STATUS_NONE;
/// Response reason, if not TS_HTTP_STATUS_OK
std::string _out_response_reason;
- /// Is the response to the user agent suspended?
- bool _ua_response_suspended = false;
/// Bridge requires a continuation for scheduling and the transaction.
Bridge(TSCont cont, TSHttpTxn txn, TextView peer);
@@ -231,7 +229,7 @@ Bridge::net_accept(TSVConn vc)
char buff[1024];
int64_t n = snprintf(buff, sizeof(buff), CONNECT_FORMAT,
static_cast<int>(_peer.size()), _peer.data());
- TSDebug(PLUGIN_TAG, "Received UA VConn");
+ TSDebug(PLUGIN_TAG, "Received UA VConn, connecting to peer %.*s",
int(_peer.size()), _peer.data());
// UA side intercepted.
_ua.init(vc);
_ua.do_read(_self_cont, INT64_MAX);
@@ -315,12 +313,6 @@ Bridge::check_outbound_OK()
}
// 519 is POOMA, useful for debugging, but may want to change this
later.
_out_response_code = c ? c : static_cast<TSHttpStatus>(519);
- if (_ua_response_suspended) {
- this->update_ua_response();
- TSHttpTxnReenable(_ua_txn, TS_EVENT_HTTP_CONTINUE);
- _ua_response_suspended = false;
- TSDebug(PLUGIN_TAG, "TXN resumed");
- }
_out.consume(block.data() - raw.data());
zret = true;
TSDebug(PLUGIN_TAG, "Outbound status %d", c);
@@ -415,24 +407,19 @@ Bridge::eos(TSVIO vio)
}
_out.do_close();
_ua.do_close();
- _out_resp_state = EOS;
- if (_ua_response_suspended) {
- TSHttpTxnReenable(_ua_txn, TS_EVENT_HTTP_CONTINUE);
+ if (_out_resp_state != ERROR) {
+ _out_resp_state = EOS;
}
}
void
Bridge::send_response_cb()
{
- // If the upstream response hasn't been parsed yet, make the UA response
wait for that.
- // Set a flag so the upstream response parser knows to update response and
reenable.
- if (_out_resp_state < OK) {
- _ua_response_suspended = true;
- TSDebug(PLUGIN_TAG, "TXN suspended");
- } else { // Already have all the data needed to do the update, so do it and
move on.
- this->update_ua_response();
- TSHttpTxnReenable(_ua_txn, TS_EVENT_HTTP_CONTINUE);
- }
+ // This happens either after the upstream connection and the writing the
response there,
+ // or because the upstream connection was blocked. In either case the
upstream work is
+ // done and the original transaction can proceed.
+ this->update_ua_response();
+ TSHttpTxnReenable(_ua_txn, TS_EVENT_HTTP_CONTINUE);
}
void
@@ -441,12 +428,10 @@ Bridge::update_ua_response()
TSMBuffer mbuf;
TSMLoc hdr_loc;
if (TS_SUCCESS == TSHttpTxnClientRespGet(_ua_txn, &mbuf, &hdr_loc)) {
- // A 200 for @a out_response_code only means there wasn't an internal
failure on the upstream
- // CONNECT. Network and other failures get reported in this response. This
response code will
- // be more accurate, so use it unless it's 200, in which case use the
stored response code if
- // that's not 200.
- TSHttpStatus status = TSHttpHdrStatusGet(mbuf, hdr_loc);
- if (TS_HTTP_STATUS_OK == status && TS_HTTP_STATUS_OK !=
_out_response_code) {
+ // If there is a non-200 upstream code then that's the most accurate
because it was from
+ // an actual upstream connection. Otherwise, let the original connection
response code
+ // ride.
+ if (_out_response_code != TS_HTTP_STATUS_OK && _out_response_code !=
TS_HTTP_STATUS_NONE) {
TSHttpHdrStatusSet(mbuf, hdr_loc, _out_response_code);
if (!_out_response_reason.empty()) {
TSHttpHdrReasonSet(mbuf, hdr_loc, _out_response_reason.data(),
_out_response_reason.size());