├── .gitignore ├── .gitmodules ├── README.md ├── hooks.sh └── run.sh /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | config.sh 3 | config 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "dehydrated"] 2 | path = dehydrated 3 | url = https://github.com/lukas2511/dehydrated.git 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dehydrated support scripts for OS X Server 2 | 3 | [dehydrated](https://github.com/lukas2511/dehydrated) (formerly [letsencrypt.sh](https://github.com/lukas2511/letsencrypt.sh)) is a nice and simple client for obtaining free SSL certificates from [Let's Encrypt](https://letsencrypt.org). 4 | This repository contains a wrapper script which automatically pulls the host names from configured SSL websites in OS X Server, requests the appropriate certificates, and calls a hook that imports them into Keychain and sets the OS X Server to use them. 5 | 6 | ## Requirements 7 | This script has been developed and tested on 8 | - OS X 10.11.4 through 10.13.6 9 | - Server.app 5.1 through 5.6.3 10 | 11 | Older versions might work too, but have not been tested. 12 | 13 | Newer versions will not work since Apple has removed the web server management tools. 14 | 15 | ## Using 16 | In Server.app, go to the _Websites_ section and create a new web site for each domain name you intend to use. 17 | If you want multiple domains to point to the same web site, you can add these under _Additional Domains_. 18 | Set the port number to 443 and choose the self-signed certificate that has been created by OS X Server. 19 | 20 | Now execute `run.sh` from this repository with sudo rights. 21 | -------------------------------------------------------------------------------- /hooks.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | function deploy_challenge { 5 | local DOMAIN="${1}" TOKEN_FILENAME="${2}" TOKEN_VALUE="${3}" 6 | } 7 | 8 | function clean_challenge { 9 | local DOMAIN="${1}" TOKEN_FILENAME="${2}" TOKEN_VALUE="${3}" 10 | } 11 | 12 | function deploy_cert { 13 | local DOMAIN="${1}" KEYFILE="${2}" CERTFILE="${3}" FULLCHAINFILE="${4}" CHAINFILE="${5}" 14 | 15 | echo $DOMAIN $KEYFILE $CERTFILE $FULLCHAINFILE $CHAINFILE 16 | security import $KEYFILE -k /Library/Keychains/System.keychain -A || true 17 | security import $FULLCHAINFILE -k /Library/Keychains/System.keychain || true 18 | 19 | siteId=$(serveradmin settings web:customSites | grep "serverName = \"$DOMAIN\"" | awk -F '=' '{print $1}' | grep -E -o '[0-9]+') 20 | fingerprint=$(openssl x509 -in $CERTFILE -sha1 -noout -fingerprint | awk -F '=' '{print $2}') 21 | fingerprint=${fingerprint//:/} 22 | 23 | oldcert=$(serveradmin settings web:customSites | grep "web:customSites:_array_index:$siteId:sslCertificateIdentifier" | awk -F '=' '{print $2}' | awk -F '"' '{print $2}') 24 | newcert="$DOMAIN.$fingerprint" 25 | identifier=$(serveradmin settings web:customSites | grep "web:customSites:_array_index:$siteId:identifier" | awk -F '=' '{print $2}' | awk -F '"' '{print $2}') 26 | siteIdentifier=$(serveradmin settings web:customSites | grep "web:customSites:_array_index:$siteId:fileName" | awk -F '=' '{print $2}' | awk -F '"' '{print $2}') 27 | siteIdentifier=$(basename $identifier) 28 | siteIdentifier=${identifier/.conf/} 29 | siteIdentifier=${identifier/0000_/} 30 | siteIdentifier=${identifier/127.0.0.1_/127.0.0.1\\:} 31 | 32 | serveradmin settings web:customSites | grep -i certificate 33 | 34 | sleep 15 # it takes a while for Server Admin to pick up the new certificate 35 | 36 | certupdate replace -c /etc/certificates/$oldcert.cert.pem -C /etc/certificates/$newcert.cert.pem 37 | 38 | serveradmin settings web:customSites | grep -i certificate 39 | } 40 | 41 | function unchanged_cert { 42 | local DOMAIN="${1}" KEYFILE="${2}" CERTFILE="${3}" FULLCHAINFILE="${4}" CHAINFILE="${5}" 43 | } 44 | 45 | HANDLER=$1 46 | shift 47 | 48 | case $HANDLER in 49 | deploy_challenge|clean_challenge|deploy_cert) 50 | $HANDLER $@ 51 | ;; 52 | esac 53 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | site_ids=$(serveradmin settings web:customSites | grep ':serverName = ' | awk -F '=' '{print $1}' | grep -E -o '[0-9]+') 4 | for site_id in $site_ids; do 5 | certificate=$(serveradmin settings web:customSites | grep "web:customSites:_array_index:$site_id:sslCertificateIdentifier" | awk -F '=' '{print $2}' | awk -F '"' '{print $2}') 6 | if [ "$certificate" = "" ]; then 7 | continue 8 | fi 9 | 10 | serverName=$(serveradmin settings web:customSites | grep "web:customSites:_array_index:$site_id:serverName" | awk -F '=' '{print $2}' | awk -F '"' '{print $2}') 11 | serverAliases=$(serveradmin settings web:customSites | grep "web:customSites:_array_index:$site_id:serverAlias" | awk -F '=' '{print $2}' | awk -F '"' '{print $2}') 12 | documentRoot=$(serveradmin settings web:customSites | grep "web:customSites:_array_index:$site_id:documentRoot" | awk -F '=' '{print $2}' | awk -F '"' '{print $2}') 13 | 14 | echo $serverName $serverAliases $certificate $documentRoot 15 | 16 | BASEDIR=$(dirname $0)/dehydrated 17 | if [ "${BASEDIR:0:1}" = "." ]; then 18 | BASEDIR=$(pwd)/$BASEDIR 19 | fi 20 | 21 | WELLKNOWN="$documentRoot/.well-known/acme-challenge" 22 | mkdir -p $WELLKNOWN 23 | curl -sL http://$serverName/.well-known/acme-challenge | grep -qv '404 Not Found' 24 | if [ "$?" != "0" ]; then 25 | echo "The acme-challenge directory appears to be inaccessible." 26 | echo "Please make sure you don't have a whole-site Redirect or ProxyPass on $serverName." 27 | exit 1 28 | fi 29 | 30 | echo "WELLKNOWN=$WELLKNOWN; BASEDIR=$BASEDIR" > config 31 | 32 | CMD=$(dirname $0)/dehydrated/dehydrated 33 | PARAMS="--cron --hook $(dirname $0)/hooks.sh --domain $serverName" 34 | for dom in $serverAliases; do 35 | PARAMS="$PARAMS --domain $dom" 36 | done 37 | 38 | $CMD $* $PARAMS 39 | done 40 | --------------------------------------------------------------------------------