Mutual TLS authentication¶
ntc-rosetta-conf
utilizes mutual TLS (mTLS) for authentication purposes. In this document we are going to quickly see how mutual TLS authentication works in the context of ntc-rosetta-conf
Intro¶
This introduction mTLS doesn’t aim to be an in-depth explanation of how it works, there are plenty of resources out there that aim to do that. Here we are going to just introduce the basic concepts so we have a basic understanding to operate ntc-rosetta-conf
The basic idea of mTLS is that both the client and the server will validate the other party by looking at their certificate. This is done by checking that the certificate authority that signed the other party is (a) the one we expect, (b) cert hasn’t expired and (c) cert hasn’t been revoked. Let’s see an illustration:
+--------------------------------------------+ +--------------------------------------------+
| client CA | | server CA |
|(certificate authority to sign client certs)| |(certificate authority to sign server certs)|
+--------------------------------------------+ +--------------------------------------------+
____/ | \____ ____/ | \____
| | | | | |
+---------+ +---------+ +---------+ +---------+ +---------+ +---------+
| client1 | | client2 | | client3 | | server1 | | server2 | | server3 |
+---------+ +---------+ +---------+ +---------+ +---------+ +---------+
mTLS Authentication Overview
==============================================================================
Client| 1. Start tls handshake |Server
| ---------------------------------------------------------------------------> |
| A. Send server certificate and request one to the client |
| <--------------------------------------------------------------------------- |
| 2. Validate server cert is valid by checking it was signed by "server CA" |
| 3. Send client certificate |
| ---------------------------------------------------------------------------> |
| B. Validate client cert is valid by checking it was signed by "client CA" |
==============================================================================
Note
This is gross oversimplification of how TLS and mTLS work. Refer to more in-depth guides/documentation if you are interested in knowing how it works.
Tutorial¶
Let’s test the theory. Before we start, if you want to follow this tutorial you need:
- Docker. Alternatively, you can install easypki and use it outside the docker container.
- Create folder
pki_auto_generated_dir
in your current path:mkdir pki_auto_generated_dir
Clients¶
First, we need to provide our users with client certificates, so let’s create a couple.
Creating the client CA¶
To generate client certificates we are going to need a CA. Ideally, such CA would be an intermediate CA signed by a valid CA but as this is dev we are going to just create a root CA:
$ docker run -v $PWD:/certs -w /certs \
creatdevsolutions/easypki create \
--ca \
--filename client_ca \
--expire 365 \
--private-key-size 2048 \
--organization "NTC" \
--organizational-unit "Eng" \
--locality "Stockholm" \
--country "Sweden" \
--province "Stockholm" \
ntc-rosetta-conf-client-authority
Now, you can find under pki_auto_generated_dir/client_ca/{certs,keys}
our CA cert and key:
$ ls pki_auto_generated_dir/client_ca/{certs,keys}
pki_auto_generated_dir/client_ca/certs:
client_ca.crt
pki_auto_generated_dir/client_ca/keys:
client_ca.key
Creating client certificates¶
Now that we have a CA to generate client certificates let’s create a couple for Jane and John:
$ docker run -v $PWD:/certs -w /certs \
creatdevsolutions/easypki create \
--client \
--ca-name "client_ca" \
--expire 365 \
--private-key-size 2048 \
--organization "NTC" \
--organizational-unit "Eng" \
--locality "Stockholm" \
--country "Sweden" \
--province "Stockholm" \
--email "jane@networktocode.com" \
jane@networktocode.com
$ docker run -v $PWD:/certs -w /certs \
creatdevsolutions/easypki create \
--client \
--ca-name "client_ca" \
--expire 365 \
--private-key-size 2048 \
--organization "NTC" \
--organizational-unit "Eng" \
--locality "Stockholm" \
--country "Sweden" \
--province "Stockholm" \
--email "john@networktocode.com" \
john@networktocode.com
Generated certs and keys will be under the same client_ca
folder from before:
$ls pki_auto_generated_dir/client_ca/{certs,keys}
pki_auto_generated_dir/client_ca/certs:
client_ca.crt jane@networktocode.com.crt john@networktocode.com.crt
pki_auto_generated_dir/client_ca/keys:
client_ca.key jane@networktocode.com.key john@networktocode.com.key
Server certificates¶
Now we are going to need a server certificate for each instance of ntc-rosetta-conf
.
Server CA¶
As with the client CA ideally you’d use an intermediate CA signed by a valid CA but, as this is dev, we are going to create our own root CA:
$ docker run -v $PWD:/certs -w /certs \
creatdevsolutions/easypki create \
--ca \
--filename server_ca \
--expire 365 \
--private-key-size 2048 \
--organization "NTC" \
--organizational-unit "Eng" \
--locality "Stockholm" \
--country "Sweden" \
--province "Stockholm" \
ntc-rosetta-conf-server-authority
This time, we will find certs and keys under pki_auto_generated_dir/server_ca/{certs,keys}
:
$ ls pki_auto_generated_dir/server_ca/{certs,keys}
pki_auto_generated_dir/server_ca/certs:
server_ca.crt
pki_auto_generated_dir/server_ca/keys:
server_ca.key
Creating server certificates¶
Now it’s time to generate the certificates for our ntc-rosetta-conf
instances:
$ docker run -v $PWD:/certs -w /certs \
creatdevsolutions/easypki create \
--ca-name "server_ca" \
--expire 365 \
--private-key-size 2048 \
--organization "NTC" \
--organizational-unit "Eng" \
--locality "Stockholm" \
--country "Sweden" \
--province "Stockholm" \
rtr00.lab.local
$ docker run -v $PWD:/certs -w /certs \
creatdevsolutions/easypki create \
--ca-name "server_ca" \
--expire 365 \
--private-key-size 2048 \
--organization "NTC" \
--organizational-unit "Eng" \
--locality "Stockholm" \
--country "Sweden" \
--province "Stockholm" \
rtr01.lab.local
Certs and keys will be under the same path as the server CA:
$ ls pki_auto_generated_dir/server_ca/{certs,keys}
pki_auto_generated_dir/server_ca/certs:
rtr00.lab.local.crt rtr01.lab.local.crt server_ca.crt
pki_auto_generated_dir/server_ca/keys:
rtr00.lab.local.key rtr01.lab.local.key server_ca.key
Starting ntc-rosetta-conf¶
Now that everything is ready, let’s start ntc-rosetta-conf
. Note the options for --ssl-crt
(server cert), --ssl-key
(server key) and --ca-crt
(client CA cert):
$ ntc-rosetta-conf serve \
--datamodel openconfig \
--pid-file /tmp/ntc-rosetta-conf-demo.pid \
--log-level debug \
--data-file data.json \
--port 8443 \
--ssl-crt pki_auto_generated_dir/server_ca/certs/rtr00.lab.local.crt \
--ssl-key pki_auto_generated_dir/server_ca/keys/rtr00.lab.local.key \
--ca-crt pki_auto_generated_dir/client_ca/certs/client_ca.crt
2019-07-17 11:54:59,599 INFO NTC Rosetta Conf version TBD
2019-07-17 11:54:59,601 INFO Using config:
GLOBAL:
DATA_JSON_FILE: data.json
LOGFILE: '-'
LOG_DBG_MODULES:
- '*'
LOG_LEVEL: debug
PERSISTENT_CHANGES: true
PIDFILE: /tmp/ntc-rosetta-conf-demo.pid
TIMEZONE: GMT
VALIDATE_TRANSACTIONS: true
YANG_LIB_DIR: asda
HTTP_SERVER:
API_ROOT: /restconf
API_ROOT_RUNNING: /restconf_running
CA_CERT: pki_auto_generated_dir/client_ca/certs/client_ca.crt
DBG_DISABLE_CERTS: false
DOC_DEFAULT_NAME: index.html
DOC_ROOT: doc-root
LISTEN_LOCALHOST_ONLY: false
PORT: 8443
SERVER_NAME: jetconf-h2
SERVER_SSL_CERT: pki_auto_generated_dir/server_ca/certs/rtr00.lab.local.crt
SERVER_SSL_PRIVKEY: pki_auto_generated_dir/server_ca/keys/rtr00.lab.local.key
UPLOAD_SIZE_LIMIT: 1
NACM:
ALLOWED_USERS: []
ENABLED: true
2019-07-17 11:55:00,571 INFO Server started on ('::', 8443, 0, 0)
Client¶
Now we can use curl to query ntc-rosetta-conf
. Let’s start by trying to use curl without using any client cert:
$ curl \
--cacert pki_auto_generated_dir/server_ca/certs/server_ca.crt \
-X GET \
https://rtr00.lab.local:8443/restconf/data/openconfig-interfaces:interfaces
curl: (56) OpenSSL SSL_read: SSL_ERROR_SYSCALL, errno 104
We got an SSL error. This is because the handshake failed as the server requested a client certificate. Let’s try passing our client cert and key this time:
$ curl \
--cacert pki_auto_generated_dir/server_ca/certs/server_ca.crt \
--cert pki_auto_generated_dir/client_ca/certs/jane@networktocode.com.crt \
--key pki_auto_generated_dir/client_ca/keys/jane@networktocode.com.key \
-X GET \
https://rtr00.lab.local:8443/restconf/data/openconfig-interfaces:interfaces
{
"openconfig-interfaces:interfaces": {
"interface": []
}
}
Now we managed to authenticate ourselves with the server.
Note
make sure that rtr00.lab.local
resolves the correct IP. You can do that for the sake of testing by editing /etc/hosts
.
Note
You probably noticed the line --cacert pki_auto_generated_dir/server_ca/certs/server_ca.crt
. We need that option because we are using self-signed certificates.