AuthN / authZ

Client certificate authentication

Client certificate authentication means that the client sends an X.509 certificate when they connect over TLS. The load balancer verifies the client’s identity based on the certificate. Typically, client certificates are digitally signed with your organization’s CA certificate. When a client presents one, you can verify whether it was indeed signed by your CA. If not, deny the request. You would give a unique certificate to each client to which you want to grant access.

Create a Certificate Authority Jump to heading

Creating a client certificate that’s signed by your organization’s self-signed CA (Certificate Authority) is deemed secure in this scenario, as opposed to procuring one from an external CA. Your organization acts as the Certificate Authority, authenticating these certificates when they are presented through HTTPS requests by clients.

Creating a Certificate Authority involves generating a CA key pair, creating a self-signed CA certificate, and configuring the CA to sign other certificates. You can accomplish it all with a single openssl command.

  1. Generate a CA key pair and self-signed certificate:

    nix
    openssl req \
    -newkey rsa:2048 \
    -nodes \
    -x509 \
    -days 3650 \
    -keyout privateCA.pem \
    -out cacert.crt
    nix
    openssl req \
    -newkey rsa:2048 \
    -nodes \
    -x509 \
    -days 3650 \
    -keyout privateCA.pem \
    -out cacert.crt
    Learn more about this command

    The options of this command break down as follows:

    • openssl req creates and processes certificate signing requests (CSRs) and certificates.
    • -newkey rsa:2048 specifies the generation of a new RSA key pair with a key size of 2048 bits.
    • -nodes indicates that the private key should not be encrypted with a passphrase and will be saved in an unencrypted format.
    • -x509 tells OpenSSL to generate a self-signed certificate instead of a CSR.
    • -days 3650 specifies the validity period of the certificate as 3650 days (approximately 10 years).
    • -keyout designates the file name of privateCA.pem for the private key.
    • -out cacert.crt sets the certificate.

    The command will also prompt you to enter information for the certificate, such as the Common Name (CN), organization details, and so on. Fill in the required fields accordingly.

    nix
    You are about to be asked to enter information that will be incorporated
    into your certificate request.
    What you are about to enter is what is called a Distinguished Name or a DN.
    There are quite a few fields but you can leave some blank
    For some fields there will be a default value,
    If you enter '.', the field will be left blank.
    -----
    Country Name (2 letter code) [AU]:
    State or Province Name (full name) [Some-State]:
    Locality Name (eg, city) []:
    Organization Name (eg, company) [Internet Widgits Pty Ltd]:
    Organizational Unit Name (eg, section) []:
    Common Name (e.g. server FQDN or YOUR name) []:
    Email Address []:
    nix
    You are about to be asked to enter information that will be incorporated
    into your certificate request.
    What you are about to enter is what is called a Distinguished Name or a DN.
    There are quite a few fields but you can leave some blank
    For some fields there will be a default value,
    If you enter '.', the field will be left blank.
    -----
    Country Name (2 letter code) [AU]:
    State or Province Name (full name) [Some-State]:
    Locality Name (eg, city) []:
    Organization Name (eg, company) [Internet Widgits Pty Ltd]:
    Organizational Unit Name (eg, section) []:
    Common Name (e.g. server FQDN or YOUR name) []:
    Email Address []:
  2. To display the details of the CA certificate and verify its information, use the openssl x509 command:

    nix
    openssl x509 -in cacert.crt -text -noout
    nix
    openssl x509 -in cacert.crt -text -noout

    After following these steps, you should have the self-signed certificate cacert.crt. Remember to adjust the file names and paths according to your specific setup. Keep the CA key privateCA.pem secret.

Create a client certificate Jump to heading

A client certificate is a type of digital certificate used to verify the identity of a user or device trying to access server resources.

To create a client certificate:

  1. Create a certificate signing request with the following command, replacing the -subj values with the name and organization of the user or device:

    nix
    openssl req \
    -newkey rsa:2048 \
    -nodes \
    -days 3650 \
    -subj "/CN=exampleUser/O=exampleOrganization" \
    -keyout clientKey.pem \
    -out client.csr
    nix
    openssl req \
    -newkey rsa:2048 \
    -nodes \
    -days 3650 \
    -subj "/CN=exampleUser/O=exampleOrganization" \
    -keyout clientKey.pem \
    -out client.csr
  2. Create a certificate extensions file named client-cert-extensions.cnf, which sets extensions fields like basicConstraints to prevent the certificate from acting as a CA and keyUsage to indicate the purpose of the certificate. Add the following values:

    client-cert-extensions.cnf
    text
    basicConstraints = CA:FALSE
    keyUsage = digitalSignature
    extendedKeyUsage = clientAuth
    subjectKeyIdentifier = hash
    authorityKeyIdentifier = keyid,issuer
    client-cert-extensions.cnf
    text
    basicConstraints = CA:FALSE
    keyUsage = digitalSignature
    extendedKeyUsage = clientAuth
    subjectKeyIdentifier = hash
    authorityKeyIdentifier = keyid,issuer
  3. Use the following command to sign the certificate signing request. This uses our previously generated CSR client.csr, CA certificate cacert.crt, and CA key privateCA.pem to create a client certificate, client.crt.

    nix
    openssl x509 \
    -req \
    -in client.csr \
    -out client.crt \
    -CA cacert.crt \
    -CAkey privateCA.pem \
    -CAcreateserial \
    -days 3650 \
    -extfile client-cert-extensions.cnf
    nix
    openssl x509 \
    -req \
    -in client.csr \
    -out client.crt \
    -CA cacert.crt \
    -CAkey privateCA.pem \
    -CAcreateserial \
    -days 3650 \
    -extfile client-cert-extensions.cnf

    Successful confirmation output will look something like this, with an ‘OK’ statement above your Common Name and Organization:

    output
    text
    Certificate request self-signature ok
    subject=CN = exampleUser, O = exampleOrganization
    output
    text
    Certificate request self-signature ok
    subject=CN = exampleUser, O = exampleOrganization

    Your client certificate is now ready to use.

  4. To display the details of the client certificate and verify its information, use the openssl x509 command:

    nix
    openssl x509 -in client.crt -text -noout
    nix
    openssl x509 -in client.crt -text -noout

Verify client certificates Jump to heading

In your load balancer configuration, enable verification of client certificates by setting verify to required on a bind line. The ca-file parameter specifies the CA file to use to verify:

haproxy
frontend www
bind :443 ssl crt /certs/site.pem verify required ca-file /certs/cacert.crt
haproxy
frontend www
bind :443 ssl crt /certs/site.pem verify required ca-file /certs/cacert.crt

Note that the client certificate must contain both the public certificate and its associated private key.

Send a client certificate to servers Jump to heading

The load balancer can also send its own client certificate to backend servers. The servers would then be responsible for verifying it.

  1. In the backend, add the crt argument to the server directive. The crt parameter points to your load balancer’s client certificate file:

    haproxy
    backend webservers
    server web1 10.0.0.5:443 ssl crt /certs/load-balancer-client.crt
    haproxy
    backend webservers
    server web1 10.0.0.5:443 ssl crt /certs/load-balancer-client.crt

Certificate Revocation Lists Jump to heading

A Certificate Revocation List (CRL) contains a list of the certificates that have been revoked.

Create a CRL Jump to heading

To create a CRL:

  1. Create the directory structure and necessary files. You can choose different directory paths, however ensure all created files are in the proper locations:

    nix
    mkdir -p ./databaseCA/certs ./databaseCA/private ./databaseCA/crl
    touch ./databaseCA/index.txt ./databaseCA/serial ./databaseCA/crlnumber
    echo 01 > ./databaseCA/serial
    echo 1000 > ./databaseCA/crlnumber
    nix
    mkdir -p ./databaseCA/certs ./databaseCA/private ./databaseCA/crl
    touch ./databaseCA/index.txt ./databaseCA/serial ./databaseCA/crlnumber
    echo 01 > ./databaseCA/serial
    echo 1000 > ./databaseCA/crlnumber
  2. Copy your CA certificate and private key into the databaseCA directory.

    nix
    cp cacert.crt ./databaseCA
    cp privateCA.pem ./databaseCA
    nix
    cp cacert.crt ./databaseCA
    cp privateCA.pem ./databaseCA
  3. You’ll need a configuration file for OpenSSL. An example file named openssl.cnf can usually be found in the directories /usr/lib/ssl/ or /etc/ssl/. Copy this to the current directory. Below is an example snippet from the file, modified for the certificates we created:

    openssl.cnf
    ini
    # OpenSSL example configuration file.
    # See doc/man5/config.pod for more info.
    ####################################################################
    [ ca ]
    default_ca = CA_default # The default ca section
    ####################################################################
    [ CA_default ]
    dir = ./databaseCA # Where everything is kept
    certs = $dir/certs # Where the issued certs are kept
    crl_dir = $dir/crl # Where the issued crl are kept
    database = $dir/index.txt # database index file.
    new_certs_dir = $dir/newcerts # default place for new certs.
    certificate = $dir/cacert.crt # The CA certificate
    serial = $dir/serial # The current serial number
    crlnumber = $dir/crlnumber # the current crl number
    crl = $dir/crl.pem # The current CRL
    private_key = $dir/privateCA.pem # The private key
    x509_extensions = usr_cert # The extensions to add to the cert
    crl_extensions = crl_ext
    default_days = 3650 # how long to certify for
    default_crl_days = 30 # how long before next CRL
    default_md = sha256 # use SHA-256 by default
    preserve = no # keep passed DN ordering
    [ crl_ext ]
    authorityKeyIdentifier=keyid:always
    openssl.cnf
    ini
    # OpenSSL example configuration file.
    # See doc/man5/config.pod for more info.
    ####################################################################
    [ ca ]
    default_ca = CA_default # The default ca section
    ####################################################################
    [ CA_default ]
    dir = ./databaseCA # Where everything is kept
    certs = $dir/certs # Where the issued certs are kept
    crl_dir = $dir/crl # Where the issued crl are kept
    database = $dir/index.txt # database index file.
    new_certs_dir = $dir/newcerts # default place for new certs.
    certificate = $dir/cacert.crt # The CA certificate
    serial = $dir/serial # The current serial number
    crlnumber = $dir/crlnumber # the current crl number
    crl = $dir/crl.pem # The current CRL
    private_key = $dir/privateCA.pem # The private key
    x509_extensions = usr_cert # The extensions to add to the cert
    crl_extensions = crl_ext
    default_days = 3650 # how long to certify for
    default_crl_days = 30 # how long before next CRL
    default_md = sha256 # use SHA-256 by default
    preserve = no # keep passed DN ordering
    [ crl_ext ]
    authorityKeyIdentifier=keyid:always

    The above is just the CA_default portion of a default OpenSSL configuration, not the entire openssl.cnf file.

    In this configuration, ./databaseCA is the directory where OpenSSL will store its database of certificates, ./privateCA.pem is the CA’s private key, and ./cacert.crt is the CA’s certificate.

    • Ensure the directory and file paths match your environment, which we created in Step 1.
    • Consult the openssl ca documentation for a complete list of configuration options.
  4. Generate the file ca.crl, which will contain the list of revoked certificates:

    nix
    sudo openssl ca -config openssl.cnf -gencrl -out /ca.crl
    nix
    sudo openssl ca -config openssl.cnf -gencrl -out /ca.crl

    You can create a CRL file even before a certificate has been revoked; the revocation list will be empty inside the CRL.

Configure the load balancer Jump to heading

By using Certificate Revocation Lists, you can revoke client certificates in your load balancer environment. Revoked certificates will be denied access based on your configured CRL file.

Here is an example snippet in which we add the crl-file argument to deny access to clients who have revoked certificates:

haproxy
frontend www
mode http
bind :443 ssl crt /certs/site.pem verify required ca-file /certs/cacert.crt crl-file /ca.crl
haproxy
frontend www
mode http
bind :443 ssl crt /certs/site.pem verify required ca-file /certs/cacert.crt crl-file /ca.crl

Revoke a certificate Jump to heading

To revoke a client certificate:

  1. Invoke the openssl ca command with the -revoke argument:

    nix
    sudo openssl ca -config openssl.cnf -revoke client.crt
    nix
    sudo openssl ca -config openssl.cnf -revoke client.crt

    This will immediately revoke the certificate client.crt as confirmed by the output:

    output
    text
    Using configuration from openssl.cnf
    Revoking Certificate 01.
    Data Base Updated
    output
    text
    Using configuration from openssl.cnf
    Revoking Certificate 01.
    Data Base Updated
  2. Update the CRL file:

    nix
    sudo openssl ca -config openssl.cnf -gencrl -out /ca.crl
    nix
    sudo openssl ca -config openssl.cnf -gencrl -out /ca.crl
  3. Reload the load balancer configuration.

Show error pages for certificate errors Jump to heading

By utilizing the configuration options below, you can show users an error page based on a certificate error.

  1. Configure HTML pages on your load balancer, for example certexpired.html and certrevoked.html pages.

    • The file /certexpired.html could present a dedicated page to users whose certificate has expired, providing guidance on how to renew or request a new certificate.
    • Those with revoked certificates could be directed to the dedicated page /certrevoked.html.
  2. Edit your load balancer configuration to include http-request return directives. Also, add the crt-ignore-err argument to the bind line so that errors don’t immediately close the connection, allowing you to return a web page instead:

    haproxy
    frontend www
    mode http
    bind :443 ssl crt /certs/site.pem verify required ca-file /certs/cacert.crt crl-file /ca.crl crt-ignore-err 10,23
    http-request return status 403 content-type text/html file /certexpired.html if { ssl_c_verify 10 }
    http-request return status 403 content-type text/html file /certrevoked.html if { ssl_c_verify 23 }
    haproxy
    frontend www
    mode http
    bind :443 ssl crt /certs/site.pem verify required ca-file /certs/cacert.crt crl-file /ca.crl crt-ignore-err 10,23
    http-request return status 403 content-type text/html file /certexpired.html if { ssl_c_verify 10 }
    http-request return status 403 content-type text/html file /certrevoked.html if { ssl_c_verify 23 }
  3. Reload the load balancer configuration.

Do you have any suggestions on how we can improve the content of this page?