Using TLS Certificates to authenticate against Zabbix API with Nginx
We build a fair bit of software against the Zabbix API in our products. This means that we have multiple users in several Zabbix instances. Of course, this leads to identity management issues at scale.
Part of this problem is that storing usernames and passwords either in code, or configuration files, is a security nightmare that is doomed to fail.
In this post, I'll go through how to replace username & password authentication with (short lived) TLS client certificates, using Nginx, PHP, FastCGI and the Zabbix API.
Fortunately, Zabbix supports various forms of authentication:
- Internal, using username + password
- LDAP
- HTTP auth
However, rather than using passwords, we want to use short-lived TLS client certificates, as managed by Caramel.
With Apache and
Lighttpd,
using client side TLS certificates is as simple as to enable authentication.
For Apache you set FakeBasicAuth
and in Lighttpd you set
ssl.verifyclient.username
and you're done.
Sadly, that's not the case with Nginx. There, you have to fake it.
First, you set up ssl_verify_client on
for the server
section of the
domain that you want to authenticate with.
Then, you need to get a username, in the http
section define the following
variable:
map $ssl_client_s_dn $ssl_client_s_dn_cn {
default "should_not_happen";
~/CN=(?<CN>[^/]+) $CN;
}
After that, you have to inject this authentication into your PHP process. You do this by adding the PHP_AUTH_USER into the fastcgi environment.
location ~ \.php$ {
fastcgi_pass unix:/var/run/my.socket;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param PHP_AUTH_USER $ssl_client_s_dn_cn;
}
And that is enough to make PHP believe that you are using HTTP Auth.
Now the only thing that's left to do is to make sure your user
field in the
Zabbix API login
matches the CommonName of your certificate ( See the above "map" command )
We do it the simple way, and store the cert in a file matching the CN.
import os
import pyzabbix
secrets = '/my/storage/for/secrets'
public = '/my/storage/for/public'
name = 'Username'
key = os.path.join(secrets, name + ".key")
cert = os.path.join(public, name + ".key")
zapi = pyzabbix.ZabbixAPI("http://zabbixserver.example.com")
zapi.session.
zapi.session.verify = "/etc/pki/tls/certs/ca.caramel.crt"
zapi.session.cert = (cert, key)
zapi.login("http user", "Password is Ignored")