VOV Security Keys
VOV security keys provide an alternative REST authentication method to providing a username/password. Security keys are more convenient and more secure than username/password authentication. The same keys are used to allow RDS to be run in a highly secure mode where RDS network clients authenticate to RDS servers using the same keys.
- You create a public/private key pair.
- You use some other authentication means to connect to the server, and tell the server to trust your public key and associate it with your identity.
- You use your private key as part of an authentication "handshake" with the server, and it verifies your identity using your matching public key.
It’s important to remember that you do not need a new public/private key pair for each queue where you intend to use key based authentication. You can create one key pair and set up the public half of your key on all the vovservers you wish to authenticate against.
VOV security keys in no way replace the security rules and policies enforced by security.tcl or Access Control Lists. The keys are only a means to authenticate the user, after which Security Levels and Access Control Lists still come into play.
Create a Public/Private Key Pair
Creating a new VOV security key pair can be done by calling vovsecurity keygen.
Before using the vovsecurity tool, you should have previously
called vovproject enable PROJECTNAME
.
By default, vovsecurity keygen writes the newly generated key pair into $HOME/.vov/userkey. If that file already exists, you will be prompted on whether you wish to overwrite the file. Overwriting the file means you will no longer have access to the previous key pair that you may have used to establish key based authentication on other vovserver instances.
Register Your Public Key on a vovserver Instance
> vovsecurity getkey
32:wlefJq4QjTm2QgtWTUtD7kG11nCyUtrKfobW/HPfoD0=
Everything on the second line is the value of the public key that is stored in $HOME/.vov/userkey, up to and including the ‘=’ character.
vovsecurity addkey -kv 32:wlefJq4QjTm2QgtWTUtD7kG11nCyUtrKfobW/HPfoD0= -kd "My Main Key"
Enter your password:
When vovsecurity communicates with a vovserver instance, it intentionally does so via REST and uses username/password-based authentication to ensure the caller isn’t trying to impersonate another user. You will be prompted to enter your password so that the vovsecurity tool can authenticate on your behalf using password authentication. If you find entering your password to be inconvenient, you can set the environment variable VOV_PASSWORD to the value of your password. vovsecurity will use this value instead of prompting for it at the console. We generally do not recommend keeping your password set in an environment variable.
If the operation succeeds, vovsecurity will return to the command prompt with no further error messages. You may get an error if password authentication fails, or if you’ve already added your public key to the vovserver instance.
Listing Your Public Keys on a vovserver Instance
> vovsecurity listkeys
user1 32:Ihq6djlx5L9XQzRWRWy0Z2hl5fD64JhJoqa7FpDmiyg= Backup key
user1 32:+fmR1SmWValRspQjcLQ7OGX266MZj/m90B5fii3FMTY= 2nd key
user1 32:wlefJq4QjTm2QgtWTUtD7kG11nCyUtrKfobW/HPfoD0= 3rd Key
In this case, user1 has registered multiple public keys on the same vovserver instance.
Using Key Based Authentication from a Python-Based REST Client
- The webport must be non-zero
- The webprovider must be set to “internal”
- ssl.enable should be set to 1 in policy.tcl
- The REST client needs to know the user’s private key (by reading it out of ~/.vov/userkey).
- The REST client needs to know the vovsever’s public key. This can be
retrieved by calling
vovsecurity getserverkey
#
# nc_rest_101_vov_keys.py
#
import os, sys
import vov_rest_v3
url = os.environ['NC_URL']
secret_key = os.environ['MY_SECRET_KEY']
sp_key = os.environ['NC_SERVER_PUBLIC_KEY']
user = os.environ['USER']
vrest2 = vov_rest_v3.VOVRestV3()
vrest2.authorizeWithKey(url, secret_key, sp_key, username=user)
r = vrest2.submitRequest("GET", url + "/api/v3/project/1/project")
print (r)
Advanced Key Handling
The REST client will use the libsodium library to construct an encryption message
which is a combination of the user’s private key and vovserver’s public key, which
will be sent to REST at the endpoint /api/v3/token
with
grant_type
set to apikey
and
apikey
set to the contents of the encrypted message.
If you wish to handle REST sessions yourself, look inside
vov_rest_v3.py at the
_getEncryptedMessage()
procedure to see how the authentication
handshake message is composed using the user’s private key and the vovserver’s
public key. In _getEncryptedMessage()
, you can see how it decodes
the two key strings into raw bytes, creates an PyNaCl Box object using the two keys,
and then encrypts the message by calling Box.encrypt()
. The
encrypted message is then base-64 encoded and returned as a string that the
vovserver REST API can understand.
def _getEncryptedMessage(self, userseckey, serverpubkey, message=None):
"""Generated an encrypted message block using the caller's secret key and the server's public key.
:type userseckey string
:param userseckey User's secret key. It should be in base64 format with the binary length prepended,
and a colon separating the length and the base64 string.
:type serverpubkey string
:param serverpubkey vovserver's public key. It should be in base64 format with the binary length prepended,
and a colon separating the length and the base64 string.
This can be retrieved from $VOVDIR/local/registry/ as the 'CLIENT_PUBKEY' entry.
:type message string
:param message The message to encode. Optional. If None, a generic default message is used. It doesn't
effect the key authentication process in any way.
:rtype: string
:return: A string containing a base64 encoded, then url-encoded buffer.
"""
idx = userseckey.find(':')
if idx == -1:
return ""
buflen = int(userseckey[:idx])
userseckey = userseckey[idx + 1:]
seckeybuf = base64.b64decode(userseckey)
seckeybuf = seckeybuf[:buflen]
idx = serverpubkey.find(':')
if idx == -1:
return ""
buflen = int(serverpubkey[:idx])
serverpubkey = serverpubkey[idx + 1:]
pubkeybuf = base64.b64decode(serverpubkey)
pubkeybuf = pubkeybuf[:buflen]
if message is None:
message = 'VOV API Key Client Auth ' + str(time.time())
messageBuf = message.encode("utf-8")
secretKey = PrivateKey(seckeybuf)
publicKey = PublicKey(pubkeybuf)
box = Box(secretKey, publicKey)
cipher = box.encrypt(plaintext=messageBuf)
cipherLen = len(cipher)
encodedCipher = base64.b64encode(cipher)
cipherStr = str(cipherLen) + ':' + encodedCipher.decode("utf-8")
return cipherStr
_getEncryptedMessage()
procedure
above:url = 'https://hostname:9100/api/v3/token'
clientSecret = ''
postData = {}
postData["grant_type"] = "apikey"
postData["client_id"] = "Python client"
postData["client_secret"] = clientSecret
postData["username"] = “myusername”
postData["apikey"] = self._getEncryptedMessage(userseckey, serverpubkey)
session = requests.Session()
response_obj = session.post(url, timeout=self.connectionTimeout, verify=False,
data=postData)
respContent = response_obj.text
respStatus = response_obj.status_code
if (respStatus == 200):
contentJSON = json.loads(respContent)
if ("access_token" in contentJSON):
sToken = contentJSON['access_token']
Once you have the session token, you can use it for subsequent REST API requests, similar to previous REST examples which used username/password authentication to get the session token.
Deleting A Key from vovserver
vovsecurity delkey -kv 32:jA0iq1mLbdSuuDKjlPeEoHJX+fYqR/0HxpI5nhgThE8=
> vovsecurity listkeys -a
user1 32:jA0iq1mLbdSuuDKjlPeEoHJX+fYqR/0HxpI5nhgThE8= Primary Key
user2 32:Ihq6djlx5L9XQzRWRWy0Z2hl5fD64JhJoqa7FpDmiyg= Primary Key
user2 32:+fmR1SmWValRspQjcLQ7OGX266MZj/m90B5fii3FMTY= 2nd key
user2 32:wlefJq4QjTm2QgtWTUtD7kG11nCyUtrKfobW/HPfoD0= 3rd Key
> vovsecurity delkey -kv 32:jA0iq1mLbdSuuDKjlPeEoHJX+fYqR/0HxpI5nhgThE8= -u user1