Tip
This is the documentation for the v21.03 version. Looking for the documentation of the latest version? Have a look here.
RESTCONF Service Setup with Certificate-Based Authentication and NACM¶
Use Case¶
RESTCONF is desirable for its ability to implement changes to TNSR remotely using the API, but allowing remote changes to TNSR also raises security concerns. When using RESTCONF, security is extremely important to protect the integrity of the router against unauthorized changes.
Note
RESTCONF deals in JSON output and input, which is easily parsed by a variety of existing libraries for programming and scripting languages.
Example Scenario¶
In this example, TNSR will be configured to allow access via RESTCONF, but the service will be protected in several key ways:
The RESTCONF service is configured for TLS to encrypt the transport
The RESTCONF service is configured to require a client certificate, which is validated against a private Certificate Authority known to TNSR
NACM determines if the certificate common-name (username) is allowed access to view or make changes via RESTCONF
Item |
Value |
---|---|
TNSR Hostname |
tnsr.example.com |
RESTCONF Username |
myuser |
NACM Group Name |
admins |
Additional User |
anotheruser |
TNSR Setup¶
Generate Certificates¶
Create a self-signed Certificate Authority:
tnsr(config)# pki private-key selfca generate
tnsr(config)# pki signing-request set common-name selfca
tnsr(config)# pki signing-request set digest sha256
tnsr(config)# pki signing-request selfca generate
tnsr(config)# pki signing-request selfca sign self enable-ca true
Create a certificate for the user myuser
, signed by selfca
:
tnsr(config)# pki private-key myuser generate key-length 4096
tnsr(config)# pki signing-request set common-name myuser
tnsr(config)# pki signing-request set digest sha256
tnsr(config)# pki signing-request myuser generate
tnsr(config)# pki signing-request myuser sign ca-name selfca days-valid 365 digest sha512 enable-ca false
Create a certificate for the RESTCONF service to use. The common-name should be the hostname of the TNSR router, which should also exist in DNS:
tnsr(config)# pki private-key restconf generate key-length 4096
tnsr(config)# pki signing-request set common-name tnsr.example.com
tnsr(config)# pki signing-request set digest sha256
tnsr(config)# pki signing-request restconf generate
tnsr(config)# pki signing-request restconf sign ca-name selfca days-valid 365 digest sha512 enable-ca false
Setup NACM¶
Disable NACM while making changes, to avoid locking out the account making the changes:
tnsr(config)# nacm disable
Set default policies:
tnsr(config)# nacm exec-default deny
tnsr(config)# nacm read-default deny
tnsr(config)# nacm write-default deny
Setup an admin
group containing the default users plus myuser
, which
will match the common-name of the user certificate created above:
tnsr(config)# nacm group admin
tnsr(config-nacm-group)# member root
tnsr(config-nacm-group)# member tnsr
tnsr(config-nacm-group)# member myuser
tnsr(config-nacm-group)# exit
Setup rules to permit any action by members of the admin group:
tnsr(config)# nacm rule-list admin-rules
tnsr(config-nacm-rule-list)# group admin
tnsr(config-nacm-rule-list)# rule permit-all
tnsr(config-nacm-rule)# module *
tnsr(config-nacm-rule)# access-operations *
tnsr(config-nacm-rule)# action permit
tnsr(config-nacm-rule)# exit
tnsr(config-nacm-rule-list)# exit
Enable NACM:
tnsr(config)# nacm enable
tnsr(config)# exit
Enable RESTCONF¶
Enable RESTCONF and configure it for TLS and client certificate authentication:
tnsr(config)# http server
tnsr(config-http)# server certificate restconf
tnsr(config-http)# authentication type client-certificate
tnsr(config-http)# authentication client-certificate-ca selfca
tnsr(config-http)# enable restconf
Client Configuration¶
On TNSR, export the CA certificate, user certificate, and user certificate key.
Place the resulting files in a secure place on a client system, in a directory
with appropriate permissions, readable only by the user. Additionally, the
private key file must only be readable by the user. For this example, the files
will be placed in ~/tnsr/
.
First, export the CA certificate. Copy and paste this into a local file, named
tnsr-selfca.crt
:
tnsr# pki ca selfca get
-----BEGIN CERTIFICATE-----
[...]
-----END CERTIFICATE-----
Next, export the user certificate, copy and paste it and save in a local file
named tnsr-myuser.crt
:
tnsr# pki certificate myuser get
-----BEGIN CERTIFICATE-----
[...]
-----END CERTIFICATE-----
Finally, export the user certificate private key, copy and paste it and save in
a local file named tnsr-myuser.key
. Remember to protect this file so it is
only readable by this user:
tnsr# pki private-key myuser get
-----BEGIN PRIVATE KEY-----
[...]
-----END PRIVATE KEY-----
This example uses curl
to access RESTCONF, so ensure it is installed and
available on the client computer.
Example Usage¶
This simple example shows fetching the contents of an ACL from RESTCONF as well as adding a new ACL entry. There are numerous possibilities here, for more details see the REST API documentation.
In this example, there is an existing ACL named blockbadhosts
. It contains
several entries including a default allow rule with a sequence number of
5000
.
These examples are all run from the client configured above.
Note
This is a simple demonstration using cURL and shell commands. This makes it easy to demonstrate how the service works, and how RESTCONF URLs are formed, but does not make for a good practical example.
In real-world cases these types of queries would be handled by a program or script that interacts with RESTCONF, manipulating data directly and a lot of the details will be handled by RESTCONF and JSON programming libraries.
Retrive a specific ACL¶
Retrieve the entire contents of the blockbadhosts
ACL:
Command:
$ curl --cert ~/tnsr/tnsr-myuser.crt \
--key ~/tnsr/tnsr-myuser.key \
--cacert ~/tnsr/tnsr-selfca.crt \
-X GET \
https://tnsr.example.com/restconf/data/netgate-acl:acl-config/acl-table/acl-list=blockbadhosts
Output:
{
"netgate-acl:acl-list": [
{
"acl-name": "blockbadhosts",
"acl-description": "Block bad hosts",
"acl-rules": {
"acl-rule": [
{
"sequence": 1,
"action": "deny",
"ip-version": "ipv4",
"src-ip-prefix": "203.0.113.14/32"
},
{
"sequence": 2,
"action": "deny",
"ip-version": "ipv4",
"src-ip-prefix": "203.0.113.15/32"
},
{
"sequence": 555,
"action": "deny",
"ip-version": "ipv4",
"src-ip-prefix": "5.5.5.5/32"
},
{
"sequence": 5000,
"acl-rule-description": "Default Permit",
"action": "permit",
"ip-version": "ipv4"
}
]
}
}
]
}
The cURL parameters and RESTCONF URL can be dissected as follows:
Item |
Value |
---|---|
cURL Client Certificate |
|
cURL Client Certificate Key |
|
cURL CA Cert to validate TLS |
|
Request type (GET) |
|
RESTCONF Server protocol/host |
|
RESTCONF API location: |
|
ACL config area (prefix:name) |
|
ACL table |
|
ACL List, with restriction |
|
Note
Lists of items with a unique key can be restricted as shown above. The
API documentation also calls this out as well, showing an optional
={name}
in the query.
Retrieve a specific rule of a specific ACL¶
View only the default permit rule of the ACL:
Command:
$ curl --cert ~/tnsr/tnsr-myuser.crt \
--key ~/tnsr/tnsr-myuser.key \
--cacert ~/tnsr/tnsr-selfca.crt \
-X GET \
https://tnsr.example.com/restconf/data/netgate-acl:acl-config/acl-table/acl-list=blockbadhosts/acl-rules/acl-rule=5000
Output:
{
"netgate-acl:acl-rule": [
{
"sequence": 5000,
"acl-rule-description": "Default Permit",
"action": "permit",
"ip-version": "ipv4"
}
]
}
The query is nearly identical to the previous one, with the following additional components:
Item |
Value |
---|---|
ACL rules list |
acl-rules/ |
ACL rule, with restriction |
acl-rule=5000 |
Add a new rule to an existing ACL¶
Insert a new ACL rule entry with the following parameters:
Item |
Value |
---|---|
Request Type |
|
Content Type |
|
ACL Name |
blockbadhosts |
ACL Rule Sequence |
10 |
ACL Rule Action |
deny |
ACL Rule Source Address |
10.222.111.222/32 |
The new data passed in the -d
parameter is JSON but with all whitespace
removed so it can be more easily expressed on a command line.
Warning
The Content-Type
header must be set when performing a write
operation such as PUT
or PATCH
. The value of the header must reflect
the type of data being sent. These examples use JSON, so the header is set to
application/yang-data+json
. When submitting XML, it would be
application/yang-data+xml
The URL is the same as if the query is retrieving the rule in question.
Warning
Note the presence of the sequence number in both the supplied JSON data and in the URL. This must match.
Command:
$ curl --cert ~/tnsr/tnsr-myuser.crt \
--key ~/tnsr/tnsr-myuser.key \
--cacert ~/tnsr/tnsr-selfca.crt \
-H "Content-Type: application/yang-data+json" \
-X PUT \
-d '{"netgate-acl:acl-rule":[{"sequence": 10,"action":"deny","ip-version":"ipv4","src-ip-prefix":"10.222.111.222/32"}]}' \
https://tnsr.example.com/restconf/data/netgate-acl:acl-config/acl-table/acl-list=blockbadhosts/acl-rules/acl-rule=10
Output: This command has no output when it works successfully.
Retrieve the contents of the ACL again to see that the new rule is now present:
Command:
$ curl --cert ~/tnsr/tnsr-myuser.crt \
--key ~/tnsr/tnsr-myuser.key \
--cacert ~/tnsr/tnsr-selfca.crt \
-X GET \
https://tnsr.example.com/restconf/data/netgate-acl:acl-config/acl-table/acl-list=blockbadhosts
Output:
{
"netgate-acl:acl-list": [
{
"acl-name": "blockbadhosts",
"acl-description": "Block bad hosts",
"acl-rules": {
"acl-rule": [
{
"sequence": 1,
"action": "deny",
"ip-version": "ipv4",
"src-ip-prefix": "203.0.113.14/32"
},
{
"sequence": 2,
"action": "deny",
"ip-version": "ipv4",
"src-ip-prefix": "203.0.113.15/32"
},
{
"sequence": 10,
"action": "deny",
"ip-version": "ipv4",
"src-ip-prefix": "10.222.111.222/32"
},
{
"sequence": 555,
"action": "deny",
"ip-version": "ipv4",
"src-ip-prefix": "5.5.5.5/32"
},
{
"sequence": 5000,
"acl-rule-description": "Default Permit",
"action": "permit",
"ip-version": "ipv4"
}
]
}
}
]
}
Use PATCH to update data¶
When using the PUT
method, the client must supply all data in an entry to be
replaced, even when only changing one small part. This makes it difficult to
change, for example, the description of an ACL without sending the content of
the ACL back in the request.
The PATCH
method allows individual values to be replaced without requiring
all of the data to be sent. With PATCH
, the client need only send the
modified values in a query, along with enough information to uniquely identify
the entry.
For example, to update the description of the blockbadhosts
ACL using
PATCH
, the client must only include the name of the ACL and the new
description. It does not need to include the entire content of the ACL and its
rules as it would with a PUT
request.
Item |
Value |
---|---|
Request Type |
|
Content Type |
|
ACL Name |
|
ACL Description |
|
The command is formatted in a similar manner to the PUT
request in the
previous example.
Warning
The Content-Type
header must be set when performing a write
operation such as PUT
or PATCH
. The value of the header must reflect
the type of data being sent. These examples use JSON, so the header is set to
application/yang-data+json
. When submitting XML, it would be
application/yang-data+xml
Command:
$ curl --cert ~/tnsr/tnsr-myuser.crt \
--key ~/tnsr/tnsr-myuser.key \
--cacert ~/tnsr/tnsr-selfca.crt \
-H "Content-Type: application/yang-data+json" \
-X PATCH \
-d '{"netgate-acl:acl-list":[{"acl-name": "blockbadhosts","acl-description": "Block packets from bad hosts"}]}' \
https://tnsr.example.com/restconf/data/netgate-acl:acl-config/acl-table/acl-list=blockbadhosts/
Output: This command has no output when it works successfully.
Retrieve the contents of the ACL again to see that the new description is now present:
Command:
$ curl --cert ~/tnsr/tnsr-myuser.crt \
--key ~/tnsr/tnsr-myuser.key \
--cacert ~/tnsr/tnsr-selfca.crt \
-X GET \
https://tnsr.example.com/restconf/data/netgate-acl:acl-config/acl-table/acl-list=blockbadhosts
Output:
{
"netgate-acl:acl-list": [
{
"acl-name": "blockbadhosts",
"acl-description": "Block packets from bad hosts",
"acl-rules": {
"acl-rule": [
{
"sequence": 1,
"action": "deny",
"ip-version": "ipv4",
"src-ip-prefix": "203.0.113.14/32"
},
{
"sequence": 2,
"action": "deny",
"ip-version": "ipv4",
"src-ip-prefix": "203.0.113.15/32"
},
{
"sequence": 10,
"action": "deny",
"ip-version": "ipv4",
"src-ip-prefix": "10.222.111.222/32"
},
{
"sequence": 555,
"action": "deny",
"ip-version": "ipv4",
"src-ip-prefix": "5.5.5.5/32"
},
{
"sequence": 5000,
"acl-rule-description": "Default Permit",
"action": "permit",
"ip-version": "ipv4"
}
]
}
}
]
}
Remove a specific rule from an ACL¶
Say that entry is no longer needed and it is safe to remove. That can be done
with a DELETE
request for the URL corresponding to its sequence number:
Command:
$ curl --cert ~/tnsr/tnsr-myuser.crt \
--key ~/tnsr/tnsr-myuser.key \
--cacert ~/tnsr/tnsr-selfca.crt \
-X DELETE \
https://tnsr.example.com/restconf/data/netgate-acl:acl-config/acl-table/acl-list=blockbadhosts/acl-rules/acl-rule=10
Output: This does not produce any output if it completed successfully.
Retrieve the contents of the ACL again to confirm it was removed.
Adding More Users¶
To create additional RESTCONF users, only two actions are required on TNSR:
Generate a certificate for the new user, and then add the user to NACM. This
example adds a new user named anotheruser
.
Generate a new user certificate:
tnsr(config)# pki private-key anotheruser generate key-length 4096
tnsr(config)# pki signing-request set common-name anotheruser
tnsr(config)# pki signing-request set digest sha256
tnsr(config)# pki signing-request anotheruser generate
tnsr(config)# pki signing-request anotheruser sign ca-name selfca days-valid 365 digest sha512 enable-ca false
Add this user to the NACM admin
group:
tnsr(config)# nacm group admin
tnsr(config-nacm-group)# member anotheruser
tnsr(config-nacm-group)# exit
Then, the user certificate can be copied to a new client and used as explained previously.
See Also¶
Additional TNSR RESTCONF resources: