The NetApp Manageability SDK, or NMSDK, provides resources that you can use to develop applications which monitor and manage NetApp storage systems running ONTAP.  As a long time NetApp Systems Engineer, with an interest in automation, I’ve been using the NMSDK for over a decade.

Despite having some significant experience scripting with the NMSDK, and helping customers do the same, I never was pushed to use anything other than regular old username and password based authentication over HTTPS.  When I was recently asked by a customer to help them avoid storing passwords in their scripts that manage ONTAP systems, I assumed I would quickly find some details on the topic.  What I quickly realized was that our documentation and sample code for this particular aspect of NMSDK integration was difficult to find!

After a bit more digging into various sources, and testing on a lab system, I had it working without too much trouble. Like most things, this is really easy to do once you have seen it happen.  So, let’s get to it!

The first step is to generate a self-signed certificate, using OpenSSL.  In my example, I generate this certificate from a Linux system.  I will point out a few important details about this command:

  • Notice the “-days 1095” argument. 1095 days is 3 years, so you would need to renew this certificate after 3 years.
  • The {COUNTRY}, {STATE}, {CITY}, and {ORG} are all relatively unimportant sections of the -subj
  • The CN={USER} section of the -subj argument is very important! You will need to have a user of the same name configured on your ONTAP cluster (or on a SVM), and the role of this ONTAP user will define what kind of API calls you are allowed to make when authenticated with this certificate.
linux1$ openssl req -x509 -nodes -days 1095 -newkey rsa:2048 -keyout yourKeyFile.key -out yourPemFile.pem \
-subj "/C={COUNTRY};/ST={STATE}/L={CITY}/O={ORG}/CN={USER}"

Now that we have seen the syntax and a bit of explanation to go with it, here is an example of actually running this command.  Note that in this example, my ONTAP user will have to be cert_user.

linux1$ openssl req -x509 -nodes -days 1095 -newkey rsa:2048 -keyout test.key -out test.pem \
-subj "/C=US/ST=NC/L=RTP/O=NetApp/CN=cert_user"
Generating a 2048 bit RSA private key
..............................................................+++
............................................................................................+++
writing new private key to 'test.key'
-----
linux1$

OK, we have our certificate and key file created, let’s get ONTAP configured.  The commands below show the syntax for making this happen. Again, take special note of the {USER} argument in the security login create command.

cluster1:: security certificate install -type client-ca -vserver {VSERVER}
 --- Cut and paste the cert including BEGIN and END statements from yourPemFile.pem ---
cluster1:: security ssl modify -vserver {VSERVER} -client-enabled true
cluster1:: security login create -user-or-group-name {USER} -application ontapi -authmethod cert \
-role {ROLE} -vserver {VSERVER}

Again, now that we have seen the syntax, let’s take a look at an actual example of making this happen.  As you would expect, I’m using the cert_user account, and in my example I’m giving full admin privileges to this user.  You can create a different role for your user if so required.  You can also configure your role, user, and certificate for a SVM other than the main cluster management SVM, if that meets your needs.

cluster1:: security certificate install -type client-ca -vserver cluster1

Please enter Certificate: Press enter when done
-----BEGIN CERTIFICATE-----
MIIDzTCCArWgAwIBAgIJAKl+/sKKOLYNMA0GCSqGSIb3DQEBBQUAME0xCzAJBgNV
BAYTAlVTMQswCQYDVQQIEwJOQzEMMAoGA1UEBxMDUlRQMQ8wDQYDVQQKEwZOZXRB
cHAxEjAQBgNVBAMMCWNlcnRfdXNlcjAeFw0xNjEwMjAxODQ5MDNaFw0xOTEwMjAx
ODQ5MDNaME0xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJOQzEMMAoGA1UEBxMDUlRQ
MQ8wDQYDVQQKEwZOZXRBcHAxEjAQBgNVBAMMCWNlcnRfdXNlcjCCASIwDQYJKoZI
hvcNAQEBBQADggEPADCCAQoCggEBAL02yZIHw8MZxgLOFLRSIX2MicihpI6pnFH5
c7zIjw6C1gNvl8YDGcmjYzFaNoAjIKS+U2MuSf2K1/s1IIqX1RGeF3Y8rnYzvGXg
NZKweCNsZ29BmeSvlJD21dsYTpk0mhYB7hPZsyzr6EpEHVuGLunlvp3HvGAfNIln
rf/pgKUzam5Xc3lDyIz8Xl/B/RERrkdBZ2tknxXFF+/cOv3CAUBLvh2nZQqaiZmG
7SkB6ZwGODz0zY43tCd4xslCdNtULtff3CCdrM5QFKnCbXmlMpzpeNmcG+YOOnAb
npiK9lcaPvocXWzuGafd8hIJGNlbMo568NKLrEQumiWj9yMu7pcCAwEAAaOBrzCB
rDAdBgNVHQ4EFgQUHk8TAwmvX6j4TRwRLeqN+bUCyXswfQYDVR0jBHYwdIAUHk8T
AwmvX6j4TRwRLeqN+bUCyXuhUaRPME0xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJO
QzEMMAoGA1UEBxMDUlRQMQ8wDQYDVQQKEwZOZXRBcHAxEjAQBgNVBAMMCWNlcnRf
dXNlcoIJAKl+/sKKOLYNMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEB
ALkqZDjtjoCZaP1MTruwXtgyp4wvzR1cb/gXkKqTksaBhyRt22092edLbZxg8xs9
0pOOjIlRpxgYC4b+nqsE4HohYL2QNNEdpZtFWV1bTCvQfrMIiatndfsav8ofoQGb
8BRT1QFe98xb0zMRjUW9PwdssSErte9XG0kA1h2pamfZuGEP+oBtj8p073WrWmr8
ZLwvSH5VD+D6Eeu23SyVe2zSCslQqIZ3T0eIszUEYc/DWMW2lWkVmZBvyqbZqDoh
ATLQxSLdq2zAlFOKKIeA9V8BFSdl7Pc2IWGQDntTjiooN4E4Mfa/ic7H2y/2n/M/
N9lbtukAyEVUgzKKy50yebU=
-----END CERTIFICATE-----

You should keep a copy of the CA-signed digital certificate for future reference.
cluster1::
cluster1:: security ssl modify -vserver cluster1 -client-enabled true
cluster1:: 
cluster1:: security login create -user-or-group-name cert_user -application ontapi -authmethod cert \
-role admin -vserver cluster1
cluster1::

That’s it!  OK, we are done with configuration, how about some sample code that uses the NMSDK to leverage our new certificate based authentication.  First, a very simple snippet of Python code that leverages this authentication model.

#!/usr/bin/env python

import sys
sys.path.append("NMSDKpy")
from NaServer import *

cluster = "cluster1"
transport = "HTTPS"
port = 443
style = "CERTIFICATE"
cert = "test.pem"
key = "test.key"

s = NaServer(cluster, 1, 30)
s.set_transport_type(transport)
s.set_port(port)
s.set_style(style)
s.set_server_cert_verification(0)
s.set_client_cert_and_key(cert, key)

api = NaElement("system-get-version")
output = s.invoke_elem(api)
if (output.results_status() == "failed"):
    r = output.results_reason()
    print("Failed: " + str(r))
    sys.exit(2)

ontap_version = output.child_get_string("version")
print ("V: " + ontap_version)

How about another very simple example, this time with Perl.

#!/usr/bin/perl -w

use lib 'NMSDK';
use NaServer;

$cluster = "cluster1";
$transport = "HTTPS";
$port = 443;
$style = "CERTIFICATE";
$cert = "test.pem";
$key = "test.key";

$server = new NaServer($cluster, 1, 30);
$server->set_transport_type($transport);
$server->set_port($port);
$server->set_style($style);
$server->set_server_cert_verification(0);
$server->set_client_cert_and_key($cert, $key);

$output = $server->invoke('system-get-version');
if ($output->results_status() eq "failed") {
    die "Error: ", $output->results_reason();
}

$version = $output->child_get_string('version');
print "V: $version\n";

That’s it!  One last note here – if you configured your certificate and user in ONTAP as part of a data SVM, instead of as part of the main cluster management SVM, you would connect to a management LIF on that particular SVM via the cluster variable that is used when creating the NaServer object in the above scripts.  Hit me up in the comments if you have any questions or have problems getting this to work.

Michael Arndt
NetApp Systems Engineer