From ae7e3aed038ef23027f8a6b8e1366cd6fb84096f Mon Sep 17 00:00:00 2001 From: "Alan M. Carroll" Date: Mon, 4 Oct 2021 16:20:37 -0500 Subject: [PATCH] Doc: updates examples to include client cert authorization. --- doc/examples.en.rst | 57 ++++++++++++++++++++ test/autest/gold_tests/prod/mTLS.txnbox.yaml | 11 ++-- 2 files changed, 63 insertions(+), 5 deletions(-) diff --git a/doc/examples.en.rst b/doc/examples.en.rst index a1d0f17c..236cfb0f 100755 --- a/doc/examples.en.rst +++ b/doc/examples.en.rst @@ -164,3 +164,60 @@ containing the file name as needed. The :code:`continue` causes the invocation t :drtv:`with` even if it matches. At the end, the path is assembled and set and the query parameters cleared. +Client Certificate Authorization +================================ + +A common use of |TS| is to place it as a "side-car" in front of a service to perform various network +tasks. The task in this example is to use client certificate information to authorize access to the +proxied service. We presume client certificates are issued to provide authentication and +authorization (such as via a tool like `Athenz `__) but it would be too much +work to change the service to verify this. Instead the service can use `HTTP basic +authentication `__ and have |TS| provide that +authentication based on information in the client certificate while scrubbing the inbound request to +prevent spoofing. + +This requires additional support from |TS| to requires a client certificate and verify it is signed +by a trusted root certificate. To do this globally, add the `configuration value +`__ +to `records.config +`__ +:: + + CONFIG proxy.config.ssl.client.certification_level INT 2 + +The trusted root certificates are either the base operating system certificates or those specified +by the configuration variables +`proxy.config.ssl.CA.cert.path `__ +and +`proxy.config.ssl.CA.cert.filename `__ + +The basic setup is to check the values on the client certificate and set the ``Authorization`` field +if valid and remove it if not. + +.. literalinclude:: ../test/autest/gold_tests/prod/mTLS.txnbox.yaml + +This checks two values - the issuer (authentication) and the subject field (authorization). + +* The issuer must be exactly the expected issuer. +* Authorization is passed in the subject field, which is expected to contain + an authorization domain ("base.ex"), a key word ("role") and then the actual authorization role. + +If both are successful then the role for the request is extracted and passed to the service in +the ``Authorization`` field. Otherwise only ``GET`` and ``HEAD`` methods are allowed - if neither of +those the request is immediately rejected with a 418 status. If allowed the ``Authorization`` field +is stripped so the service can detect the lack of authorization for the request. + +Pulling the authorization value from the certificate disconnects the |TS| configuration from the +specific roles supported by the service. Whatever those roles are, the administrator can put them +in the certificate where they can be passed through. + +While it is best to adjust `remap.config +`__ to not +forward non-TLS requests, this configuration will still work correctly because the +client certificate values will be ``NIL`` for a plain text connection and therefore not match. |TS| +support is needed for the TLS case to verify the client certificate so |TxB| can trust the values +pulled from that certificate. + +If this is needed in a multi-tenant / CDN proxy, it will be necessary to use `sni.yaml +`__ to adjust the +client certificate requirements based on the SNI. diff --git a/test/autest/gold_tests/prod/mTLS.txnbox.yaml b/test/autest/gold_tests/prod/mTLS.txnbox.yaml index 0dc6aab9..c83b9507 100644 --- a/test/autest/gold_tests/prod/mTLS.txnbox.yaml +++ b/test/autest/gold_tests/prod/mTLS.txnbox.yaml @@ -4,13 +4,12 @@ txn_box: - with: [ inbound-cert-remote-issuer-field , inbound-cert-remote-subject-field ] select: - as-tuple: - - match: "TxnBox CA alpha" - - rxp: "^base.ex:role[.](.+)$" + - match: "TxnBox CA alpha" # Authenticating issuer + - prefix: "base.ex:role." # e.g. "base.ex:role.user" do: - - ua-req-field: "{1}" - - otherwise: # not Alpha mTLS - allow only GET and HEAD. + - ua-req-field: "{*}" + - otherwise: # not Alpha mTLS - allow only GET and HEAD and no authorization. do: - - ua-req-field: NULL - with: ua-req-method select: - none-of: @@ -18,3 +17,5 @@ txn_box: - match: "head" do: - proxy-reply: 418 # be distinct for testing purposes. + # valid method at this point. + - ua-req-field: NULL # get rid of the field entirely.