Skip to content

Commit 671f791

Browse files
authored
Cripts: Adds some certificate introspection (#12320)
* Cripts: Adds some certificate introspection * Use the Cripts mixin string_view type * Fixes autest for Cripts, and adds some more tests
1 parent effee11 commit 671f791

File tree

15 files changed

+1281
-21
lines changed

15 files changed

+1281
-21
lines changed
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
.. Licensed to the Apache Software Foundation (ASF) under one
2+
or more contributor license agreements. See the NOTICE file
3+
distributed with this work for additional information
4+
regarding copyright ownership. The ASF licenses this file
5+
to you under the Apache License, Version 2.0 (the
6+
"License"); you may not use this file except in compliance
7+
with the License. You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing,
12+
software distributed under the License is distributed on an
13+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
KIND, either express or implied. See the License for the
15+
specific language governing permissions and limitations
16+
under the License.
17+
18+
.. include:: ../../common.defs
19+
20+
.. highlight:: cpp
21+
.. default-domain:: cpp
22+
23+
.. _cripts-certs:
24+
25+
Certificates
26+
************
27+
28+
Cripts provides a set of convenient classes for introspection into the various
29+
TLS certificates that are used. These include both the server certificates used
30+
to establish a TLS connections, as well as any client certificates used for
31+
mutual TLS.
32+
33+
In the current implementation, these objects only work on X509 certificates as
34+
associated with the ``client`` and ``server`` connections. Let's start off with
35+
a simple example of how to use these objects:
36+
37+
.. code-block:: cpp
38+
39+
do_send_response()
40+
{
41+
if (client.connection.IsTLS()) {
42+
const auto tls = cripts::Certs::Server(client.connection);
43+
44+
client.response["X-Subject"] = tls.subject;
45+
client.response["X-NotBefore"] = tls.notBefore;
46+
client.response["X-NotAfter"] = tls.notAfter;
47+
}
48+
}
49+
50+
.. _cripts-certs-objects:
51+
52+
Objects
53+
=======
54+
55+
There are two types of objects for the certificates:
56+
57+
================================= ===============================================================
58+
Object Description
59+
================================= ===============================================================
60+
``cripts::Certs::Server`` The certificate used on the connection for TLS handshakes.
61+
``cripts::Certs::Client`` The mutual TLS (mTLS) certificate used on the connection.
62+
================================= ===============================================================
63+
64+
This combined with the two kinds of connections, ``cripts::Client::Connection`` and
65+
``cripts::Server::Connection`` yields a total of four possible certificate objects. For example, to
66+
access the client mTLS provided certificate on a client connection, you would use:
67+
68+
.. code-block:: cpp
69+
70+
const auto tls = cripts::Certs::Client(cripts::Client::Connection::Get());
71+
72+
Or if you are using the convenience wrappers:
73+
74+
.. code-block:: cpp
75+
76+
const auto tls = cripts::Certs::Client(client.connection);
77+
78+
.. _cripts-certs-x509:
79+
80+
X509 Values
81+
===========
82+
83+
As part of the certificate objects, there are a number of values that can be
84+
accessed. These values are all based on the X509 standard and can be used to
85+
introspect the certificate. The following values are available:
86+
87+
================================= ===============================================================
88+
Value Description
89+
================================= ===============================================================
90+
``certificate`` The raw X509 certificate in PEM format.
91+
``signature`` The raw signature of the certificate.
92+
``subject`` The subject of the certificate.
93+
``issuer`` The issuer of the certificate.
94+
``serialNumber`` The serial number of the certificate.
95+
``notBefore`` The date and time when the certificate is valid from.
96+
``notAfter`` The date and time when the certificate is valid until.
97+
``version`` The version of the certificate.
98+
================================= ===============================================================
99+
100+
.. _cripts-certs-san:
101+
102+
SAN Values
103+
==========
104+
105+
We've made special provisions to access the Subject Alternative Name (SAN) values
106+
of the certificate. These values are often used to identify the hostnames or IP
107+
addresses that the certificate is valid for. Once you have the certificate object,
108+
you can access the SAN values as follows:
109+
110+
==================== =============== ===============================================================
111+
Field X509 field Description
112+
==================== =============== ===============================================================
113+
``.san`` na An array of tuples with type and ``string_view`` of all SANs.
114+
``.san.email`` ``GEN_EMAIL`` An array of ``string_view`` of email addresses.
115+
``.san.dns`` ``GEN_DNS`` An array of ``string_view`` of DNS names.
116+
``.san.uri`` ``GEN_URI`` An array of ``string_view`` of URIs.
117+
``.san.ipadd`` ``GEN_IPADD`` An array of ``string_view`` of IP addresses.
118+
==================== =============== ===============================================================
119+
120+
.. note::
121+
122+
These arrays are empty if no SAN values are present in the certificate. We also populate these
123+
arrays lazily, but they are kept for the lifetime of the certificate object. This means that
124+
you can access these values multiple times without incurring additional overhead. Remember
125+
that you can use the ``cripts::Net::IP`` class to convert the IP addresses into proper
126+
IP address objects if needed.
127+
128+
129+
Odds are that you will want to use one of the specific array values, such as ``.san.uri``, which is
130+
easily done in a simple loop:
131+
132+
.. code-block:: cpp
133+
134+
do_remap()
135+
{
136+
if (client.connection.IsTLS()) {
137+
const auto tls = cripts::Certs::Server(client.connection);
138+
139+
for (auto uri : tls.san.uri) {
140+
// Check the URI string_view
141+
}
142+
}
143+
}
144+
145+
146+
You can of course loop over all SAN values, which is where the type of the value would come in handy,
147+
and why this is an array of tuples. In this scenario, you would iterate over the tuples like this:
148+
149+
.. code-block:: cpp
150+
151+
do_remap()
152+
{
153+
if (client.connection.IsTLS()) {
154+
const auto tls = cripts::Certs::Server(client.connection);
155+
156+
for (const [type, san] : tls.san) {
157+
if (type == cripts::Certs::SAN::URI) {
158+
// Check the URI string here
159+
} else if (type == cripts::Certs::SAN::DNS) {
160+
// Check the DNS string here
161+
}
162+
}
163+
}
164+
}
165+
166+
In addition to traditional C++ iterators, you can also access SAN values by index. Make sure
167+
you check the size of the array first, as accessing an out-of-bounds index will give you an
168+
empty tuple. Prefer the iterator above, unless you know you want to access a specific element.
169+
170+
Example of an alternative way to loop over all SAN values:
171+
172+
.. code-block:: cpp
173+
174+
do_remap()
175+
{
176+
if (client.connection.IsTLS()) {
177+
const auto tls = cripts::Certs::Server(client.connection);
178+
179+
size_t san_count = tls.san.size();
180+
181+
for (size_t i = 0; i < san_count; ++i) {
182+
const auto [type, san] = tls.san[i];
183+
// Process the type and san as needed
184+
}
185+
}
186+
}

doc/developer-guide/cripts/cripts-connections.en.rst

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ Method Description
7171
``LocalIP()`` The server (ATS) IP address of the connection.
7272
``IsInternal()`` Returns ``true`` or ``false`` if the connection is internal to ATS.
7373
``Socket()`` Returns the raw socket structure for the connection (use with care).
74+
``IsTLS()`` Returns ``true`` if the connection is a TLS connection.
75+
``ClientCert()`` Returns the client certificiate (mTLS) for the connection (if any).
76+
``ServerCert()`` Returns the server certificate for the connection, if it's a TLS connection.
7477
======================= =========================================================================
7578

7679
The ``IP()`` and ``LocalIP()`` methods return the IP address as an object. In addition to the
@@ -102,6 +105,7 @@ Variable Description
102105
``pacing`` Configure socket pacing for the connection.
103106
``dscp`` Manage the DSCP value for the connection socket.
104107
``mark`` Manage the Mark value for the connection socket.
108+
``tls`` Access to the TLS object for the connection.
105109
======================= =========================================================================
106110

107111
For other advanced features, a Cript has access to the socket file descriptor, via the ``FD()``
@@ -165,3 +169,24 @@ Method Description
165169
.. note::
166170
All methods return string values. These are methods and not fields, so they must be called as
167171
functions.
172+
173+
.. _cripts-connections-tls:
174+
175+
TLS
176+
===
177+
178+
The ``tls`` variable provides access to the TLS object for the session. This object
179+
provides access to the TLS certificate and other TLS related information. The following methods
180+
are available:
181+
182+
======================= =========================================================================
183+
Method Description
184+
======================= =========================================================================
185+
``Connection`` Returns the connection object for the TLS connection.
186+
``GetX509()`` Returns the X509 certificate for the connection, an OpenSSL object.
187+
======================= =========================================================================
188+
189+
Both of these can return a null pointer, if the connection is not a TLS connection or
190+
if the certificate is not available. The ``GetX509()`` method can take an optional
191+
boolean argument, which indicates if the certificate should be for a mutual TLS connection. The
192+
default is ``false``, which means that the server certificate for the connection will be returned.

doc/developer-guide/cripts/index.en.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@ Cripts
3131
cripts-urls.en
3232
cripts-headers.en
3333
cripts-connections.en
34-
cripts-matcher.en
34+
cripts-certs.en
3535
cripts-crypto.en
36+
cripts-matcher.en
3637
cripts-misc.en
3738
cripts-bundles.en
3839
cripts-convenience.en

0 commit comments

Comments
 (0)