Below is a simple nginx configuration file enabling client certificate authentication, i.e. the user/client has to present a certificate in order to get access.
It is the result of a small proof of concept solution for the following task:
- A certain web server directory should be used as file storage
- Transport security (i.e. TLS)
- No public access, user/client authentication required, 2 options
- HTTP basic authentication (i.e. username/password)
- Client certificate authentication
- When using client certificate authentication, file upload is allowed
- When using basic authentication (username/password), file upload is not allowed, it's only allowed to access the already uploaded files
Depending on the subdomain, a different authentication method is required, e.g.
pass.upload.domain.tld
will require username and passwordcert.upload.domain.tld
will require a client certificate
Managing the certificates:
For the certificate authentication you need signed client certificates as well as the certificate of the signing CA.
For example, you can create your own root CA and create an intermediate CA certificate. Then you create one or more client certificates and sign them with the intermediate CA. Just search the interwebs on how to create and manage your own CA.
The client certificates can be handed out to your users/clients then.
Here are the different sections of the config file.
Unencrypted HTTP will be redirected to HTTPS:
server {
listen 80;
listen [::]:80;
server_name cert.upload.domain.tld pass.upload.domain.tld;
return 301 https://$host$request_uri;
}
A new server block for the client certificate authentication including the transport encryption stuff (i.e. TLS encryption):
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name cert.upload.domain.tld;
## Here comes the TLS configuration
## Powered by https://bettercrypto.org/static/applied-crypto-hardening.pdf and
## https://mozilla.github.io/server-side-tls/ssl-config-generator/
ssl_certificate /var/www/upload-poc-certs/cert.upload.domain.tld.pem;
ssl_certificate_key /var/www/upload-poc-certs/cert.upload.domain.tld.key;
ssl_session_timeout 5m;
ssl_session_cache shared:SSL:20m;
ssl_session_tickets off;
# Modern and strict configuration
ssl_protocols TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
ssl_prefer_server_ciphers on;
# Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits or more
ssl_dhparam /etc/letsencrypt/group16.pem;
# HSTS
add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;";
# Some other security related headers
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options DENY;
add_header X-XSS-Protection "1; mode=block";
add_header X-Robots-Tag none;
add_header X-Download-Options noopen;
add_header X-Permitted-Cross-Domain-Policies none;
##
## End of TLS configuration
It just needs two lines to enable client certificate authentication. The certificate file contains one or more CA certificates which are used to verify the certificates presented by the clients. In our case it would contain the certificates of our intermediate CA as well as our root CA:
# Client certificate authentication
ssl_client_certificate /var/www/upload-poc-certs/Intermediate_CA.pem;
ssl_verify_client on;
The rest is only about the configuration of the directories. Note that we switch on PUT and DELETE, which allows clients to upload files:
# Path to the root directory
root /var/www/upload-poc/;
# set max upload size
client_max_body_size 20M;
location / {
autoindex on;
client_body_temp_path /tmp;
dav_methods PUT DELETE;
}
}
Now comes another server block for the basic authentication part. It is almost the same as the one before. The only differences are:
- different
server_name
(i.e. different subdomain for access with username/password) - instead of client certificate stuff we have the two
auth_basic
lines (first one is the "realm", i.e. kind of namespace, second one is the path to the password file) location
block is different, since we don't allow uploads
The htpasswd file containing usernames/password can be created using the corresponding commandline tool htpasswd
(just search online for help).
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name pass.upload.domain.tld;
## Here comes the TLS configuration
## Powered by https://bettercrypto.org/static/applied-crypto-hardening.pdf and
## https://mozilla.github.io/server-side-tls/ssl-config-generator/
ssl_certificate /var/www/upload-poc-certs/pass.upload.domain.tld.pem;
ssl_certificate_key /var/www/upload-poc-certs/pass.upload.domain.tld.key;
ssl_session_timeout 5m;
ssl_session_cache shared:SSL:20m;
ssl_session_tickets off;
# Modern and strict configuration
ssl_protocols TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
ssl_prefer_server_ciphers on;
# Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits or more
ssl_dhparam /etc/letsencrypt/group16.pem;
# HSTS
add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;";
# Some other security related headers
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options DENY;
add_header X-XSS-Protection "1; mode=block";
add_header X-Robots-Tag none;
add_header X-Download-Options noopen;
add_header X-Permitted-Cross-Domain-Policies none;
##
## End of TLS configuration
# HTTP basic authentication (username/password)
auth_basic "upload-poc";
auth_basic_user_file /var/www/htpasswd;
# Path to the root directory
root /var/www/upload-poc/;
location / {
autoindex on;
}
}
You can open cert.upload.domain.tld in your browser and you will be prompted to select a client certificate for authentication. Depending on your browser and system, you might have to import the client certificate into the system's (or browser's) certificate store first.
If you want to test uploading a file, you can do that using curl
:
curl -v --cert client.crt.pem --key client.key.pem https://cert.upload.domain.tld --upload-file example.pdf
If you want to remove the file, you can do that as well:
curl -v --cert client.crt.pem --key client.key.pem -X "DELETE" https://cert.upload.domain.tld/example.pdf
You can now open pass.upload.domain.tld in your browser and you will be required to authenticate with username and password. Once you did that you will be able to see all the files that sit in the directory (and have been uploaded before). Uploading a file to pass.upload.domain.tld will not work.
-- Have fun!