How to Use OpenWrt self-signed Certs and get no https browsers errors?

@mk24 Thanks for that advice.

It's not clear from the How to get rid of LuCI HTTPS certificate warnings page that you have to do another step to completely get rid of browser https certificate warnings.

I read around alot and here are the most useful links I found:
OpenWrt and self-signed certificates
Getting Chrome to accept self-signed localhost certificate
Generate self-signed TLS certificate using OpenSSL

I am using Windows OS and Google Chrome to run a webpage hosted in Nginx on OpenWrt.
This solution may not be the only way or even the best way of doing this. It allows you to run websites on your local lan using https and not get annoying https browser errors.

Here is what I did:

  1. This script, genCA.sh, creates a root CA pem and key file.
  2. When you run it you just need to specify any name you like, e.g: $./genCA.sh mylanCA
  3. You add the PEM file to Chrome > Manage Certificates > Trusted Root Certification Authorities Then restart Chrome.... chrome://restart
  4. This script has to be run ONCE only.
#!/bin/bash

# Directories
cur=$(pwd)
tmp=$(mktemp -d)
scriptName=$(basename "$0")

# Certificate Variables
OUTPATH="./"
VERBOSE=0
DURATION=3650 # 10 years

C="US"
ST="California"
L="Ureka"
O="Paradise Inc"
OU="The Beach"

safeExit() {
  if [ -d "$tmp" ]; then
    if [ $VERBOSE -eq 1 ]; then
      echo "Removing temporary directory '${tmp}'"
    fi
    rm -rf "$tmp"
  fi

  trap - INT TERM EXIT
  exit
}

# Help Screen
help() {
  echo -n "${scriptName} [OPTIONS] --name=mylanCA
Generate a CA using OpenSSL which you can use to make your own self-signed Certs
 Options:
  -n|--name			Name to give your own Certificate Authority
  -h|--help			Display this help and exit
"
}

# Script starts here...
# Process Arguments
while [ "$1" != "" ]; do
  PARAM=$(echo "$1" | awk -F= '{print $1}')
  VALUE=$(echo "$1" | awk -F= '{print $2}')
  case $PARAM in
    -h|--help) help; safeExit ;;
    -n|--name) NAME=$VALUE ;;
    *) echo "ERROR: unknown parameter \"$PARAM\""; help; exit 1 ;;
  esac
  shift
done

# Prompt for variables that were not provided in arguments
checkVariables() {
  # Country
  if [ -z "$NAME" ]; then
    echo -n "Name to give your own Certificate Authority:"
    read -r NAME
  fi
}

# Build TLS Certificate
build() {
  # Sanitise domain name for file name
  FILENAME=${NAME/\*\./}
  # Generate CA key & crt
  openssl genrsa -out "${FILENAME}_CA.key" 2048
  openssl req -x509 -new -nodes -key "${FILENAME}_CA.key" -sha256 -days "${DURATION}" -out "${OUTPATH}${FILENAME}_CA.pem" -subj "/C=$C/ST=$ST/L=$L/O=$O/OU=$OU/CN=$NAME/emailAddress=info@${FILENAME}.com"

}

checkVariables
build
safeExit

Next step:

  1. This script, gen-self-signed.sh, creates the cert/key pair that are signed by the CA created above. You add them to your server block .conf file on Nginx on OpenWrt.
  2. Before you run it you need to update the info at the top of the script between the two #============== lines, with details specific to your needs.
  3. Then save the file and run the script, takes no parameters.
  4. Reload Nginx should be enough, may need to restart?
#!/bin/bash

#==================================================================
# When creating a new cert/key pair for your site you only need to
# change the details below and make sure CA_PEM and CA_KEY are in the
# same directory as this script and that CA_PEM is added to your browser.
# Then add the newly created .key/.crt pair to your webserver.

CN="mysite.lan"
NAMES="mysite.lan,www.mysite.lan,fun.mysite.lan"
IP="192.168.1.254"
C="US"
ST="New York"
L="Manhatton"
O="Studio 54"
OU="Fun Times"
DURATION=3650 # 10 years

# Your CA details
CA_PEM="mylanCA_CA.pem"
CA_KEY="mylanCA_CA.key"
#==================================================================

j=0
SAN=""
getaltnames(){
for i in $(echo $NAMES | sed "s/,/ /g")
do
	if [[ ! -z "$i" ]]; then
		j=$((j+1))
		if [ "$j" -gt 1 ]; then
			SAN="${SAN}"$'\n'
		fi
		SAN="${SAN}DNS.${j} = ${i}"
	fi
done <<< "${CN},${SAN}"
}

buildCsrCnf() {
cat << EOF > "${CN}.csr.cnf"
[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn
[dn]
C=${C}
ST=${ST}
L=${L}
O=${O}
OU=${OU}
CN=${CN}
emailAddress=info@${CN}
EOF
}

buildExtCnf() {
cat << EOF > "${CN}.v3.ext"
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
${SAN}
IP.1=${IP}
EOF
}

# Build TLS Certificate
build() {

  # Put list of sub domains (NAMES) into SAN variable (${SAN})
  getaltnames

  # CSR Configuration
  buildCsrCnf

  # Create v3.ext configuration file
  buildExtCnf

  # Server key
  openssl req -new -sha256 -nodes -out "${CN}.csr" -newkey rsa:2048 -keyout "${CN}.key" -config <( cat "${CN}.csr.cnf" )

  # Server certificate
  openssl x509 -req -in "${CN}.csr" -CA "${CA_PEM}" -CAkey "${CA_KEY}" -CAcreateserial -out "${CN}.crt" -days "${DURATION}" -sha256 -extfile "${CN}.v3.ext"
}

# Script starts here
build

Here is my new server block in /etc/nginx/mysite.lan.conf:

server {
        listen 443;
        listen [::]:443;
        include '/var/lib/nginx/lan_ssl.listen';

        server_name mysite.lan www.mysite.lan fun.mysite.lan 192.168.1.254;

        root /www/mysite;
        index index.html index.htm index.nginx-debian.html;

        ssl_certificate '/etc/nginx/conf.d/mysite.lan.crt';

        ssl_certificate_key '/etc/nginx/conf.d/mysite.lan.key';

        ssl_session_cache 'shared:SSL:32k';

        ssl_session_timeout '64m';

        location / {
                try_files $uri $uri/ =404;
        }


    access_log /var/log/nginx/mysite.lan.access.log;
    error_log /var/log/nginx/mysite.lan.error.log;


}

When I try to load a page using a local only domain name, the correct page is served and no certificate errors in the browser:

Hopefully this will save someone hours!

Flex

I have a follow up question about this in case anyone can help.