I have my paperless-ngx instance behind cloudflare access, but I also want to be able to access it on my phone through paperless-mobile. It so graciously hints at the possibility of making that work by using client certificates, but making that happen isn’t super straightforward.

Here’s my notes on how to do it. I’m mostly just re-running the last few parts as I write this, as my app decided to log out and I forgot the certificate password :)

Generating a CA and credentials

#!/bin/bash
 
BOLD=$(tput bold)
CLEAR=$(tput sgr0)
 
CERT_NAME="ave.zone"
 
iterate=(server/ client/)
for dir in "${iterate[@]}"; do
  [[ ! -d "$dir" ]] && mkdir -p "$dir" \
  && echo -e "${BOLD}directory '$dir' was created ${CLEAR}"
done
 
echo -e "${BOLD}Generating RSA AES-256 Private Key for Root Certificate Authority${CLEAR}"
openssl genrsa -aes256 -out "Root.CA.$CERT_NAME.key" 4096
 
echo -e "${BOLD}Generating Certificate for Root Certificate Authority${CLEAR}"
openssl req -x509 -new -nodes -key "Root.CA.$CERT_NAME.key" -sha256 -days 1825 -out "Root.CA.$CERT_NAME.pem"
 
echo -e "${BOLD}Generating RSA Private Key for Server Certificate${CLEAR}"
openssl genrsa -out "server/$CERT_NAME.server.key" 4096
 
echo -e "${BOLD}Generating Certificate Signing Request for Server Certificate${CLEAR}"
openssl req -new -key "server/$CERT_NAME.server.key" -out "server/$CERT_NAME.server.csr"
 
echo -e "${BOLD}Generating Certificate for Server Certificate${CLEAR}"
openssl x509 -req -in "server/$CERT_NAME.server.csr" -CA "Root.CA.$CERT_NAME.pem" -CAkey "Root.CA.$CERT_NAME.key" -CAcreateserial -out "server/$CERT_NAME.server.crt" -days 1825 -sha256
 
echo -e "${BOLD}Generating RSA Private Key for Client Certificate${CLEAR}"
openssl genrsa -out "client/$CERT_NAME.client.key" 4096
 
echo -e "${BOLD}Generating Certificate Signing Request for Client Certificate${CLEAR}"
openssl req -new -key "client/$CERT_NAME.client.key" -out "client/$CERT_NAME.client.csr"
 
echo -e "${BOLD}Generating Certificate for Client Certificate${CLEAR}"
openssl x509 -req -in "client/$CERT_NAME.client.csr" -CA "Root.CA.$CERT_NAME.pem" -CAkey "Root.CA.$CERT_NAME.key" -CAcreateserial -out "client/$CERT_NAME.client.crt" -days 1825 -sha256
 
echo -e "${BOLD}Generating pfx for Client Certificate${CLEAR}"
openssl pkcs12 -inkey "client/$CERT_NAME.client.key" -in "client/$CERT_NAME.client.crt" -export -out "client/$CERT_NAME.client.pfx"
 
echo "Done!"

This is partly based on this stackoverflow answer here: https://stackoverflow.com/a/54492346 That is CC BY-SA 4.0, acpluspluscoder with changes by iChux and me.

Then you want to transfer the client/$CERT_NAME.client.pfx file to your phone somehow. How to do that is left as an exercise to the reader.

Reverse proxy configuration

You can then configure your webserver. For nginx, that looks something like this:

server {
    listen              443 ssl;
    listen              [::]:443 ssl;
    server_name         paperlessmtls.ave.zone;
    ssl_certificate     /etc/nginx/certs/paperlessmtls.ave.zone/fullchain;
    ssl_certificate_key /etc/nginx/certs/paperlessmtls.ave.zone/key;
    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_ciphers         HIGH:!aNULL:!MD5;
 
    proxy_read_timeout 300;
    client_max_body_size 300M;
    ssl_client_certificate /etc/nginx/aveca.pem;
    ssl_verify_client on;
 
    location / {
        proxy_set_header Host $host;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header X-Real-IP $remote_addr;
        proxy_pass http://172.19.0.2:8000;
    }
}
 
server {
    server_name paperlessmtls.ave.zone;
    listen 80;
 
    location / {
        return 301 https://$host$request_uri;
    }
}

The file /etc/nginx/aveca.pem is Root.CA.$CERT_NAME.pem. The files under /etc/nginx/certs/paperlessmtls.ave.zone/ are real, signed certificates from Let’s Encrypt.

Putting it all together

Ensure that your webserver works, then copy the pfx to your phone. Select the pfx within paperless-mobile, and then it should let you log in.

Enjoy!