What are the authentication mechanisms available for Confd REST API?

Is X509 authentication (certificate/keys) possible for Confd REST northbound interface in 2019?
Is there any plan of adding other authentication mechanisms for Confd REST/NETCONF any time soon?
I’m asking this question to get any updates for an answer to a similar question asked in 2015

UPDATE: A demo project can be found here: https://github.com/ConfD-Developer/ConfD-Demos/tree/master/tls-restconf

Glad you asked. See ConfD UG 7.2 or later under “Storing TLS data in database”.
Here is a simple RESTCONF x509 example for ConfD 7.2:

$ pwd
/Users/tailf/confd-7.2.1/examples.confd/restconf/basic
$ diff -u confd.conf.old confd.conf
--- confd.conf.old	2019-10-19 14:37:52.000000000 +0200
+++ confd.conf	2019-11-20 06:23:51.000000000 +0100
@@ -230,10 +230,14 @@
     <docroot>.</docroot>
     <transport>
       <tcp>
+        <enabled>false</enabled>
+      </tcp>
+      <ssl>
         <enabled>true</enabled>
         <ip>127.0.0.1</ip>
-        <port>8008</port>
-      </tcp>
+        <port>8888</port>
+        <readFromDb>true</readFromDb>
+        <protocols>tlsv1.2</protocols>
+service         <verify>3</verify>
+      </ssl>
     </transport>
   </webui>

Let’s drop a bash script into the example to setup the TLS key config

$ cat ./setup.sh
#!/bin/bash
# generate two root CA private key and certificate (PKCS#8 encoded)
openssl req -x509 -newkey rsa:2048 -nodes -subj "/CN=localhost" \
        -keyout root-ca-key-1.pem -out root-ca-cert-1.pem
# generate PKCS#8 unencrypted private key, certificate request (CSR), and
# certificate
# private key
openssl genpkey -algorithm rsa -pkeyopt rsa_keygen_bits:2048 \
        -out localhost.key.pem
# certificate request
openssl req -new -sha256 -key localhost.key.pem -subj "/CN=localhost" \
        -out localhost.csr.pem
# signed public key certificate
openssl x509 -req -CA root-ca-cert-1.pem  -CAkey root-ca-key-1.pem \
        -CAcreateserial -sha256 \
        -in localhost.csr.pem -out localhost.cert.pem
# build the examples.confd/restconf/basic example
make clean all
# generate the TLS key config.
# See CONFD_DIR/src/confd/yang/tailf-tls.yang for details
echo "<?xml version=\"1.0\"?>
<config xmlns=\"http://tail-f.com/ns/config/1.0\">
  <tls xmlns=\"http://tail-f.com/ns/tls\">
        <certificate>
          <cert-data>" > ./confd-cdb/tls_data_nopw.xml
cat localhost.cert.pem >> ./confd-cdb/tls_data_nopw.xml
echo "          </cert-data>
        </certificate>
        <private-key>
          <key-data>" >> ./confd-cdb/tls_data_nopw.xml
cat localhost.key.pem >> ./confd-cdb/tls_data_nopw.xml
echo "          </key-data>
         </private-key>
         <ca-certificates>
            <name>rsa-1</name>
            <cacert-data>" >> ./confd-cdb/tls_data_nopw.xml
cat root-ca-cert-1.pem >> ./confd-cdb/tls_data_nopw.xml
echo "            </cacert-data>
        </ca-certificates>
     </tls>
    </config>" >> ./confd-cdb/tls_data_nopw.xml
# start ConfD
make start
# wait for ConfD to start
confd --wait-started

What happens if we run it?

$ ./setup.sh
Generating a RSA private key
...................................+++++
..............+++++
writing new private key to 'root-ca-key-1.pem'
-----
...+++++
......+++++
Signature ok
subject=/CN=localhost
Getting CA Private Key
rm -rf \
		*.o *.a *.xso *.fxs *.xsd *.ccl \
		*_proto.h \
		./confd-cdb *.db aaa_cdb.* \
		rollback*/rollback{0..999} rollback{0..999} \
		cli-history \
		host.key host.cert ssh-keydir \
		*.log confderr.log.* \
		etc *.access \
		running.invalid global.data _tmp* local.data
a=dhcp-ann.yang; \
	/Users/tailf/confd-7.2.1/bin/confdc -c -o dhcp.fxs `[ -e $a ] && echo "-a $a"` -- dhcp.yang
mkdir -p ./confd-cdb
cp /Users/tailf/confd-7.2.1/var/confd/cdb/aaa_init.xml ./confd-cdb
ln -s /Users/tailf/confd-7.2.1/etc/confd/ssh ssh-keydir
rm -f confd-cdb/dhcp_init.xml ; cp dhcp_init.xml confd-cdb/dhcp_init.xml
/Users/tailf/confd-7.2.1/bin/confdc --emit-h dhcp.h dhcp.fxs
cc -c -o actions.o actions.c -Wall -g -I/Users/tailf/confd-7.2.1/include -I/opt/local/include
cc -o actions actions.o /Users/tailf/confd-7.2.1/lib/libconfd.a -lpthread -lm -L/opt/local/lib
ld: warning: directory not found for option '-L/opt/local/lib'
Build complete
/Users/tailf/confd-7.2.1/bin/confd --stop || true
DEBUG EOF on socket to ConfD
killall actions || true
No matching processes belonging to you were found
/Users/tailf/confd-7.2.1/bin/confd -c confd.conf --addloadpath /Users/tailf/confd-7.2.1/etc/confd
/Users/tailf/confd-7.2.1/bin/confd_load -C dhcp_oper.xml
./actions &
TRACE Connected (maapi) to ConfD
TRACE MAAPI_LOAD_ALL_NS
TRACE MAAPI_LOAD_MNS_MAPS
TRACE MAAPI_LOAD_HASH_DB
TRACE Connected (dp) to ConfD
TRACE Received daemon id 0
TRACE Connected (dp) to ConfD
TRACE Picked up old user session: 12 for user:system ctx:system
TRACE Picked up old user session: 11 for user:system ctx:system
TRACE Picked up old user session: 10 for user:system ctx:system
TRACE Picked up old user session: 1 for user:system ctx:system

Quick sanity test: using the self-signed CA certificate and generating a client certificate from that CA certificate for “mutual” authentication.

$ openssl genpkey -algorithm rsa -pkeyopt rsa_keygen_bits:2048 -out client.key.pem
$ openssl req -new -sha256 -key client.key.pem -subj "/CN=client" -out \
        client.csr.pem
$ openssl x509 -req -CA root-ca-cert-1.pem  -CAkey root-ca-key-1.pem \
        -CAcreateserial -sha256 -in client.csr.pem -out client.cert.pem
$ curl -kivu admin:admin --cacert ./root-ca-cert-1.pem --key ./client.key.pem --cert ./client.cert.pem  https://localhost:8888/restconf/data/dhcpd:dhcp
* Expire in 0 ms for 6 (transfer 0x55b3a1704f50)
* Expire in 1 ms for 1 (transfer 0x55b3a1704f50)
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0* Expire in 0 ms for 1 (transfer 0x55b3a1704f50)
* Expire in 1 ms for 1 (transfer 0x55b3a1704f50)
* Expire in 0 ms for 1 (transfer 0x55b3a1704f50)
* Expire in 0 ms for 1 (transfer 0x55b3a1704f50)
* Expire in 1 ms for 1 (transfer 0x55b3a1704f50)
* Expire in 0 ms for 1 (transfer 0x55b3a1704f50)
* Expire in 0 ms for 1 (transfer 0x55b3a1704f50)
* Expire in 1 ms for 1 (transfer 0x55b3a1704f50)
* Expire in 0 ms for 1 (transfer 0x55b3a1704f50)
* Expire in 0 ms for 1 (transfer 0x55b3a1704f50)
* Expire in 0 ms for 1 (transfer 0x55b3a1704f50)
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Expire in 150000 ms for 3 (transfer 0x55b3a1704f50)
* Expire in 200 ms for 4 (transfer 0x55b3a1704f50)
* Connected to localhost (127.0.0.1) port 8888 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: ./root-ca-cert-1.pem
  CApath: /etc/ssl/certs
} [5 bytes data]
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
} [512 bytes data]
* TLSv1.3 (IN), TLS handshake, Server hello (2):
{ [87 bytes data]
* TLSv1.2 (IN), TLS handshake, Certificate (11):
{ [701 bytes data]
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
{ [401 bytes data]
* TLSv1.2 (IN), TLS handshake, Request CERT (13):
{ [58 bytes data]
* TLSv1.2 (IN), TLS handshake, Server finished (14):
{ [4 bytes data]
* TLSv1.2 (OUT), TLS handshake, Certificate (11):
} [1482 bytes data]
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
} [138 bytes data]
* TLSv1.2 (OUT), TLS handshake, CERT verify (15):
} [264 bytes data]
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
} [1 bytes data]
* TLSv1.2 (OUT), TLS handshake, Finished (20):
} [16 bytes data]
* TLSv1.2 (IN), TLS handshake, Finished (20):
{ [16 bytes data]
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN, server did not agree to a protocol
* Server certificate:
*  subject: CN=localhost
*  start date: Apr 15 15:54:35 2020 GMT
*  expire date: May 15 15:54:35 2020 GMT
*  issuer: CN=localhost
*  SSL certificate verify result: self signed certificate (18), continuing anyway.
* Server auth using Basic with user 'admin'
} [5 bytes data]
> GET /restconf/data/dhcpd:dhcp HTTP/1.1
> Host: localhost:8888
> Authorization: Basic YWRtaW46YWRtaW4=
> User-Agent: curl/7.64.0
> Accept: application/yang-data+xml
> 
{ [5 bytes data]
< HTTP/1.1 200 OK
< Date: Wed, 15 Apr 2020 15:54:38 GMT
< Last-Modified: Wed, 15 Apr 2020 15:54:38 GMT
< Cache-Control: private, no-cache, must-revalidate, proxy-revalidate
< Etag: "1586-966078-493561"
< Content-Type: application/yang-data+xml
< Transfer-Encoding: chunked
< Pragma: no-cache
< Content-Security-Policy: default-src 'self'; block-all-mixed-content; base-uri 'self'; frame-ancestors 'none';
< Strict-Transport-Security: max-age=15552000; includeSubDomains
< X-Content-Type-Options: nosniff
< X-Frame-Options: DENY
< X-XSS-Protection: 1; mode=block
< 
{ [5 bytes data]
HTTP/1.1 200 OK
100    58    0    58    0     0    753      0 --:--:-- --:--:-- --:--:--   753
Date: Wed, 15 Apr 2020 15:54:38 GMT
* Connection #0 to host localhost left intact
Last-Modified: Wed, 15 Apr 2020 15:54:38 GMT
Cache-Control: private, no-cache, must-revalidate, proxy-revalidate
Etag: "1586-966078-493561"
Content-Type: application/yang-data+xml
Transfer-Encoding: chunked
Pragma: no-cache
Content-Security-Policy: default-src 'self'; block-all-mixed-content; base-uri 'self'; frame-ancestors 'none';
Strict-Transport-Security: max-age=15552000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block

<dhcp xmlns="http://yang-central.org/ns/example/dhcp"  xmlns:dhcp="http://yang-central.org/ns/example/dhcp">
  <max-lease-time>7200</max-lease-time>
  <default-lease-time>600</default-lease-time>
  <subnet>
    <net>10.254.239.0/27</net>
    <range>
      <dynamic-bootp/>
      <low>10.254.239.10</low>
      <high>10.254.239.20</high>
    </range>
    <dhcp-options>
      <router>rtr-239-0-1.example.org</router>
      <router>rtr-239-0-2.example.org</router>
    </dhcp-options>
    <max-lease-time>1200</max-lease-time>
  </subnet>
  <shared-networks>
    <shared-network>
      <name>224-29</name>
      <subnet>
        <net>10.0.29.0/24</net>
        <range>
          <low>10.0.29.10</low>
          <high>10.0.29.230</high>
        </range>
        <dhcp-options>
          <router>rtr-29.example.org</router>
        </dhcp-options>
      </subnet>
      <subnet>
        <net>10.17.224.0/24</net>
        <range>
          <low>10.17.224.10</low>
          <high>10.17.224.250</high>
        </range>
        <dhcp-options>
          <router>rtr-224.example.org</router>
        </dhcp-options>
      </subnet>
    </shared-network>
  </shared-networks>
  <status>
    <leases>
      <address>10.254.239.10</address>
      <hardware>
        <type>ethernet</type>
        <address>01:21:53:a1:1a:23</address>
      </hardware>
    </leases>
    <leases>
      <address>10.254.239.12</address>
      <hardware>
        <type>ethernet</type>
        <address>01:21:53:a1:1a:43</address>
      </hardware>
    </leases>
  </status>
</dhcp>
* Connection #0 to host localhost left intact
1 Like

UPDATE: A demo project can be found here: https://github.com/ConfD-Developer/ConfD-Demos/tree/master/tls-restconf