#!/usr/bin/env bash # Create a name-constrained private CA and a server certificate for IP-only access. # # scripts/make-tls.sh [...] (default output dir: deploy/tls) # TLS_DIR=/path scripts/make-tls.sh # # The CA carries a critical nameConstraints extension that permits only the listed # IP addresses and no DNS names, so importing ca.crt as a trusted root lets a browser # accept certificates from this CA for those addresses only. The server certificate # is an end-entity certificate (CA:FALSE) with the addresses as subjectAltName. # # Distribute only ca.crt. Keep ca.key and server.key on the server host. set -euo pipefail if [[ $# -lt 1 ]]; then echo "usage: $0 [...]" >&2 exit 2 fi ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" TLS_DIR="${TLS_DIR:-$ROOT/deploy/tls}" CA_DAYS="${CA_DAYS:-3650}" LEAF_DAYS="${LEAF_DAYS:-825}" # Chrome and Safari reject server certificates valid for longer. for ip in "$@"; do if [[ ! "$ip" =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}$ ]]; then echo "only IPv4 addresses are supported: $ip" >&2 exit 2 fi done if [[ -e "$TLS_DIR/server.key" || -e "$TLS_DIR/server.crt" ]]; then echo "refusing to overwrite $TLS_DIR/server.*; delete them to rotate the server certificate" >&2 exit 1 fi if [[ -e "$TLS_DIR/ca.key" && ! -e "$TLS_DIR/ca.crt" ]] || [[ ! -e "$TLS_DIR/ca.key" && -e "$TLS_DIR/ca.crt" ]]; then echo "incomplete CA in $TLS_DIR (need both ca.key and ca.crt, or neither)" >&2 exit 1 fi umask 077 mkdir -p "$TLS_DIR" permitted="" san="" for ip in "$@"; do permitted+="permitted;IP:${ip}/255.255.255.255," san+="IP:${ip}," done permitted="${permitted%,}" san="${san%,}" # Only the listed addresses may appear in certificates from this CA. Any DNS name is # forced to match the reserved .invalid domain, which no real host can, so this root # can never vouch for a hostname. cat > "$TLS_DIR/server.ext" </dev/null 2>&1 fi openssl req -newkey ec -pkeyopt ec_paramgen_curve:P-256 -nodes \ -keyout "$TLS_DIR/server.key" -out "$TLS_DIR/server.csr" -subj "/CN=Confluence Research server ($1)" \ -config <(printf '[req]\ndistinguished_name=dn\n[dn]\n') >/dev/null 2>&1 openssl x509 -req -in "$TLS_DIR/server.csr" -CA "$TLS_DIR/ca.crt" -CAkey "$TLS_DIR/ca.key" \ -CAcreateserial -out "$TLS_DIR/server.crt" -days "$LEAF_DAYS" \ -extfile "$TLS_DIR/server.ext" >/dev/null 2>&1 rm -f "$TLS_DIR/server.csr" "$TLS_DIR/ca.srl" "$TLS_DIR/server.ext" chmod 0644 "$TLS_DIR/ca.crt" "$TLS_DIR/server.crt" # Fails if an existing CA does not permit one of the requested addresses. if ! openssl verify -CAfile "$TLS_DIR/ca.crt" "$TLS_DIR/server.crt" >/dev/null; then echo "server certificate does not verify under $TLS_DIR/ca.crt (CA name constraints do not cover $*?)" >&2 rm -f "$TLS_DIR/server.key" "$TLS_DIR/server.crt" exit 1 fi cat <