Asymmetric agent encryption with OPENSSL

Asymmetric agent encryption with OPENSSL

I already wrote a How-To Article about asymmetric agent encryption with GNU GPG here: Asymmetric agent encryption with GNU PGP.
Unjustly GPG has a bad reputation and is called complex and complicated, which is not really the case in my opinion. Nevertheless II provide here a secure solution using OPENSSL to encrypt agent output.
This article just show a concept how OPENSSL could be used to asymmetric encryption of the agent content. It is not the final conclusion about securing the agent and I also don’t want to discuss what are the best ciphers to be used for encryption or what is the best key length. There are tons of articles about this subject in the internet available and everybody could make his own choice.

With OPENSSL it is not possible to encrypt a content which length is longer than the length of the key used for encryption. As we cannot create infinite long key´s we need to go a little detour which at the end use again symmetric encryption for the content but in a very secure way.

As a first step we create on the agent side a random secret which we use to do the symmetric encryption of the content. The secret will be then encrypted with the public key and both, the symmetric encrypted content and the asymmetric encrypted secret is then sent to the monitoring server.
On the monitoring server the secret is then decrypted with the private key and used to decrypt the content.

1. Usage of the key and public key

How to create a private key with OPENSSL and extract the public key is meanwhile public knowledge and I dont want to explain it here again.

1.1 private key file

The key should be created on the monitoring server and put in a save directory below site user home directory. I use directory ~/.ssl . The key file and the directory access should be limited to the site user for security reasons. In case of distributed monitoring, the key could be used on the remote sites but it might be also possible to use separate key pairs per site.
Keep the key file save and dont share it.

1.2 public key

The public key must be distributed to all servers which should be monitored and accessible by the user running the agent wrapper script shown below in chapter 3.3.
Its is save to share the public key, because it could only be used to encrypt content but not to unencrypt encrypted content.

2. Agent code

I will explain the different steps one by one to give a better understanding and then show the how to bring that together.

2.1 Create the random secret.

I used OPENSSL also to create the random secret. May other tools or ways work as well. The length I used here is 8 characters and it could be increased up to 24 characters.

secret=$(openssl rand 8)

2.2 encrypt the secret

This line of code is using the public key to do the asymmetric encryption of the secret.
I also base64 encode the output just to be save and not having problems on the transport medium with unsupported code. I didnt tested without, may work or not.

openssl pkeyutl -pubin -inkey ~/.ssl/public.pem -encrypt <<< ${secret} | openssl base64

2.3 encrypt the content

Now the random secret is reused to do the symmetric encryption of the agent output. The option -md sha256 is necessary to stay compatible between different OPENSSL version. Also here I bas64 decode the encrypted contend to avoid problems on the transport layer.

openssl enc -aes-256-cbc -md sha256 -pass pass:"${secret}"  -base64 <<< "${output}"

2.3 all together

Now we can put all together in a script. I use a tabulator to separate the secret from the content.
The script is barely written just for demonstration. Of course a lot of code is missing to check all goes well.
Important for me was to keep the time the secret is in memory as a variable ($secret) as short as possible. I need to see if I find a saver solution without the variable but if a hacker has already access to the monitoring server it doesn’t really make a difference anymore.

secret=$(openssl rand 24)
openssl pkeyutl -pubin -inkey ~/.ssh/test_public.pem -encrypt <<< ${secret} | openssl base64
echo -e '\t'
openssl enc -aes-256-cbc -md sha256 -pass pass:"${secret}"  -base64 <<< "${output}"
unset secret RANDFILE output
rm -f ~/.random

To run the script by checkmk create a inetd configuration. OpenSSL encrypted Agent is listening on port 6560 in my example:

service check_mk_gpg
        type           = UNLISTED
        port           = 6560
        socket_type    = stream
        protocol       = tcp
        wait           = no
        user           = root
        server         = /usr/bin/check_mk_agent_openssl

        # listen on IPv4 AND IPv6 when available on this host
        #flags          = IPv6

        # If you use fully redundant monitoring and poll the client
        # from more then one monitoring servers in parallel you might
        # want to use the agent cache wrapper:
        #server         = /usr/bin/check_mk_caching_agent

        # configure the IP address(es) of your Nagios server here:
        #only_from      =

        # Don't be too verbose. Don't log every check. This might be
        # commented out for debugging. If this option is commented out
        # the default options will be used for this service.
        log_on_success =

        disable        = no

3. Server code

On the checkmk server side we have to do all steps from above in reverse order.

3.1 Unencrypt the secret

To unencrypt the secret we have first decode the base64 encrypted secret and the unencrypt it.

openssl base64 -d <<< "${secret}"| openssl rsautl -inkey ~/.ssl/key.pem -decrypt

3.2 Unencrypt the content

To unencrypt the content we use the prior unencrypted secret. OPENSSL expect a \n at the end of the encrypted stream which is done with the echo command.
In this example I hold the unencrypted secret in a OS variable. For whatever reason, this variable needs to be exported to be used by OPENSSL. As this is unsecure I used the -pass pass: option in the final code. See below.

openssl enc -aes-256-cbc -md sha256 -d -pass env:secret -base64 <<< $(echo -e "${content}\n")

3.3 All together

Now lets put all together in a script

The script is barely written just for demonstration. Of course a lot of code is missing to check all goes well.

IFS=$'\t' read -d '\t' secret content <<< "$(/bin/nc $1 6560)"
openssl enc -aes-256-cbc -md sha256 -d -pass pass:$(openssl base64 -d <<< "${secret}"| openssl rsautl -inkey ~/.ssl/key.pem -decrypt) -base64 <<< $(echo -e "${content}\n")
unset secret
unset content

Create a rule “Individual program call instead of agent access” to call this script instead of the OOB code.

4. Performance

With the content of a standard 1.6 Linux Agent I dont see any performance impacts. Other ciphers or key lenghts may have in different results.