Secure Remote RESTful Administration with the Remote Administration Daemon

Version 4

    by Gary Pennington and Glynn Foster

     

    Learn how to configure an Oracle Solaris 11.3 system to provide secure administration over the new REST APIs using the Oracle Solaris Remote Administration Daemon (RAD).

     

    Introduction

     

    One of the new enhancements included in Oracle Solaris 11 is the Remote Administration Daemon (also known as RAD). RAD provides a set of programmatic interfaces to allow administrators to manage Oracle Solaris 11 subsystems such as Oracle Solaris Zones, the ZFS file system, the Service Management Facility (SMF), and more. A previous article, "Getting Started with the Remote Administration Daemon on Oracle Solaris 11," covered how to use RAD using C, Python, Java, and REST programmatic interfaces. This article shows administrators how they can configure an Oracle Solaris 11.3 system to enable REST-based calls over HTTPS using the Transport Layer Security (TLS) protocol.

     

    RAD REST Bindings

     

    In Oracle Solaris 11.3, a new REST interface to RAD has been added. REST APIs are an increasingly popular way of interacting with system services across the network over both HTTP and HTTPS using an encoding payload such as JSON or XML. Oracle Solaris 11.3 includes a new SMF service instance, svc:/system/rad:local-http, that is responsible for facilitating RAD communication from HTTP clients. Given that HTTP connections are not encrypted, the default configuration allows only for accepting connections from a local host on a UNIX socket. With a little configuration, administrators can open up a public port and provide secure transport over TLS.

     

    Creating a New RAD Service Instance

     

    The first step we need to do is to configure RAD to accept connections from a remote host over HTTP. In Oracle Solaris 11.3, there are currently three SMF service instances that are responsible for handling RAD connections:

     

    • The svc:/system/rad:local service instance manages local connections through UNIX sockets.
    • The svc:/system/rad:remote service instance manages secure remote TLS connections over TCP sockets.
    • The svc:/system/rad:local-http service instance provides access for local connections over HTTP.

     

    At the time of this writing, there is no system-provided service instance to manage secure remote connections over HTTP, so we need to create one—it is likely that one will be provided in a future release of Oracle Solaris.

     

    To do this, we need to create an SMF manifest. There are a number of ways to achieve this—either by taking a copy of the existing /lib/svc/manifest/system/rad.xml manifest and modifying it, or by using svcbundle(1M) to generate one and refining it afterwards. The key elements that the service instance needs to expose relate to key and certificate handling (using the PEM format common for most certificate authorities), the port to accept connections from, and the RAD protocol being used.

     

    For convenience, the full SMF manifest is provided below:

     

    # cat rad-remote-http.xml

    <?xml version="1.0" ?>

    <!DOCTYPE service_bundle

    SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>

     

    <service_bundle type="manifest" name="site/rad">

       <service version="1" type="service" name="site/rad">

           <dependency restart_on="none" type="service"

               name="multi_user_dependency" grouping="require_all">

               <service_fmri value="svc:/milestone/multi-user"/>

           </dependency>

           <exec_method name='start' type='method' exec='/usr/lib/rad/rad -sp' timeout_seconds='0'/>

           <exec_method name='stop' type='method' exec=':kill' timeout_seconds='0'/>

           <instance name='remote-http' enabled='false' complete='true'>

             <property_group name='ssl_port' type='xport_tls'>

               <propval name='certificate' type='astring' value='/etc/rad/cert.pem'/>

               <propval name='generate' type='boolean' value='true'/>

               <propval name='localonly' type='boolean' value='false'/>

               <propval name='pam_service' type='astring' value='rad-tls'/>

               <propval name='port' type='integer' value='12303'/>

               <propval name='privatekey' type='astring' value='/etc/rad/key.pem'/>

               <propval name='proto' type='astring' value='rad_http'/>

               <propval name='value_authorization' type='astring' value='solaris.smf.value.rad'/>

             </property_group>

             <property_group name='config' type='application'>

               <property name='moduledir' type='astring'>

                 <astring_list>

                   <value_node value='/usr/lib/rad/transport'/>

                   <value_node value='/usr/lib/rad/protocol'/>

                   <value_node value='/usr/lib/rad/module'/>

                   <value_node value='/usr/lib/rad/site-modules'/>

                 </astring_list>

               </property>

             </property_group>

           </instance>

           <template>

               <common_name>

                     <loctext xml:lang="C">Remote RAD HTTP</loctext>

               </common_name>

               <description>

                   <loctext xml:lang="C">RAD connections over REST (HTTP)</loctext>

               </description>

           </template>

       </service>

    </service_bundle>

     

    From the SMF manifest above, we have created a new SMF instance at svc:/site/rad:remote-http. We can see that the start method will call the /usr/lib/rad/rad daemon. We also include a number of properties within the ssl_port property group: certificate and privatekey provide credentials we need to make a secure connection over HTTPS (RAD will automatically generate these using self-signed certificates), port defines the port number on which to accept connections and is set to 12303, and proto defines that the RAD protocol to use is HTTP (as indicated by rad-http).

     

    The next step is to copy the manifest to our site-wide SMF manifest location, /lib/svc/manifest/site, and restart the svc:/system/manifest-import:default service instance:

     

     

    root@solaris:~# cp rad-remote-http.xml /lib/svc/manifest/site

    root@solaris:~# svcadm restart manifest-import

    root@solaris:~# svcadm enable rad:remote-http

    root@solaris:~# svcs rad:remote-http

    STATE          STIME    FMRI

    online         17:46:59 svc:/site/rad:remote-http

     

    If we now try to make a simple request to https://<hostname>:12303 using a web browser, we will get back the following snippet of JSON:

     

    {

            "status": "illegal access",

            "payload": {

                    "Message": "Response content type 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' requested

                    yet only 'application/json' is supported by the origin server.",

                    "HTTP Method": "GET",

                    "URI": "/",

                    "RAD Operation": null

            }

    }

     

    From the output above, we can see that we're connecting to a valid port with a RAD server on the back end, and it accepts only application/json as input.

     

    Setting up a RAD Connection

     

    As detailed in "Getting Started with the Remote Administration Daemon on Oracle Solaris 11," we need to set up the RAD connection. As before, we'll send a POST request to the /api/com.oracle.solaris.rad.authentication/1.0/Session API, and we'll also provide some credentials included in a JSON file called body.json, as follows:

     

    {

          "username": "root",

          "password": "solaris11",

          "scheme": "pam",

          "preserve": true,

          "timeout": -1

    }

     

    We can now use curl(1) with a slightly different set of command-line options that point to our SSL key and certificate pair (which we have copied over to the system from where we are making the RAD queries), saving the authentication token to a file called cookie.txt:

     

    # curl -H "Content-type: application/json" -X POST \

    --data-binary @body.json \

    --cert solaris-cert.pem --key solaris-key.pem -k \

    https://solaris.us.oracle.com:12303/api/com.oracle.solaris.rad.authentication/1.0/Session \

    -v -c cookie.txt -b cookie.txt

    *   Trying 10.134.79.148...

    * Failed to set TCP_KEEPALIVE on fd 4

    * Connected to solaris.us.oracle.com (10.134.79.148) port 12303 (#0)

    * successfully set certificate verify locations:

    *   CAfile: none

      CApath: /etc/openssl/certs

    * TLSv1.2, TLS handshake, Client hello (1):

    * TLSv1.2, TLS handshake, Server hello (2):

    * TLSv1.2, TLS handshake, CERT (11):

    * TLSv1.2, TLS handshake, Server finished (14):

    * TLSv1.2, TLS handshake, Client key exchange (16):

    * TLSv1.2, TLS change cipher, Client hello (1):

    * TLSv1.2, TLS handshake, Finished (20):

    * TLSv1.2, TLS change cipher, Client hello (1):

    * TLSv1.2, TLS handshake, Finished (20):

    * SSL connection using TLSv1.2 / AES256-GCM-SHA384

    * Server certificate:

    *        subject: CN=Remote Administration Daemon @ dcsw-79-148

    *        start date: 2015-07-26 23:35:37 GMT

    *        expire date: 2025-07-23 23:35:37 GMT

    *        issuer: CN=Remote Administration Daemon @ dcsw-79-148

    *        SSL certificate verify result: self signed certificate (18), continuing anyway.

    > POST /api/com.oracle.solaris.rad.authentication/1.0/Session HTTP/1.1

    > User-Agent: curl/7.40.0

    > Host: solaris.us.oracle.com:12303

    > Accept: */*

    > Content-type: application/json

    > Content-Length: 128

    >

    * upload completely sent off: 128 out of 128 bytes

    < HTTP/1.1 201 Created

    < Connection: Keep-Alive

    < Content-Length: 164

    < Expires: 0

    < Pragma: no-cache

    < Cache-Control: no-cache, no-store, must-revalidate

    < Location: /api/com.oracle.solaris.rad.authentication/1.0/Session/_rad_reference/3328

    * Added cookie _rad_instance="3328" for domain solaris.us.oracle.com, path /api, expire 1437962830

    < Set-Cookie: _rad_instance=3328; Path=/api; Max-Age=3600; HttpOnly

    * Added cookie _rad_token="613891db-9cd8-44fe-90a2-d6d8d94c2fb3" for

    domain solaris.us.oracle.com, path /api, expire 1437962830

    < Set-Cookie: _rad_token=613891db-9cd8-44fe-90a2-d6d8d94c2fb3;

    Path=/api; Max-Age=3600; HttpOnly

    < Date: Mon, 27 Jul 2015 00:59:48 GMT

    <

    {

            "status": "success",

            "payload": {

                    "href": "/api/com.oracle.solaris.rad.authentication/1.0/Session/_rad_reference/3328"

            }

    * Connection #0 to host solaris.us.oracle.com left intact

    }

     

    We can examine the cookie that has been created:

     

    # cat cookie.txt

    # Netscape HTTP Cookie File

    # http://curl.haxx.se/docs/http-cookies.html

    # This file was generated by libcurl! Edit at your own risk.

     

    #HttpOnly_solaris.us.oracle.com  FALSE  /api  FALSE  1437962830  _rad_instance  3328

    #HttpOnly_solaris.us.oracle.com  FALSE  /api  FALSE  1437962830  _rad_token     613891db-9cd8-44fe-90a2-d6d8d94c2fb3

     

    Querying Oracle Solaris Zones

     

    Now that we have our connection token, we can query the system. For example, we will use the /api/com.oracle.solaris.rad.zonemgr/1.0/Zone API to query the zones that have been configured on our host:

     

    # curl -H "Content-type: application/json" -X GET \

    --cert solaris-cert.pem --key solaris.pem -k \

    https://solaris.us.oracle.com:12303/api/com.oracle.solaris.rad.zonemgr/1.0/Zone -b cookie.txt

    {

            "status": "success",

            "payload": [

                    {

                            "href": "api/com.oracle.solaris.rad.zonemgr/1.2/Zone/zone1"

                    }

            ]

    }

     

    From the output above, we can see that we have a single zone called zone1. This is confirmed by running zoneadm(1M) from the host directly:

     

    root@solaris:~# zoneadm list -cv

      ID NAME             STATUS      PATH                         BRAND      IP   

       0 global           running     /                            solaris    shared

       - zone1            configured  /system/zones/zone1          solaris    excl

     

    Summary

     

    The Oracle Solaris Remote Administration Daemon (RAD) provides a powerful set of administrative interfaces to allow local and remote configuration of a mix of technologies, including Oracle Solaris Zones, ZFS, SMF, and others. These programmatic interfaces provide a central point for exposing the capabilities of Oracle Solaris to consumers using common development runtimes, including C, Java, Python, and REST APIs. This capability allows secure remote administration at scale, which is critical for a typical cloud environment.

     

    See Also

     

     

    Also see these additional resources:

     

     

    About the Authors

     

    Gary Pennington is a principal software engineer for the Oracle Solaris Core Technologies/System Management Group. He has worked on many parts of Oracle Solaris over the last 15 years, including resource management, virtualization, and RAD. Currently he is the technical lead for RAD.

     

    Glynn Foster is a principal product manager for Oracle Solaris. He is responsible for a number of technology areas including OpenStack, the Oracle Solaris Image Packaging System, installation, and configuration management.

     

     

    Revision 1.0, 07/29/2015

     

    Follow us:
    Blog | Facebook | Twitter | YouTube