Howto/SSL Zertifikate mit Let's Encrypt

Aus Freifunk MWU Wiki
Zur Navigation springen Zur Suche springen

Terminologie: cert_master: Rechner, auf dem die Zertifikate angefordert und gesammelt sind. cert_client: Rechner, auf den die Zertifikate übertragen werden.

Vorbereitungen für Cert_master:

apache, openssl installieren

Dehydrated besorgen:

git clone https://github.com/lukas2511/dehydrated

an passende stelle kopieren, /etc/dehydrated erstellen

cp dehydrated/dehydrated /usr/local/sbin/dehydrated

folgende Dateien unter /etc/dehydrated erstellen:

config: Hier werden die Optionen von dehydrated eingestellt.
########################################################
# This is the main config file for dehydrated          #
#                                                      #
# This file is looked for in the following locations:  #
# $SCRIPTDIR/config (next to this script)              #
# /usr/local/etc/dehydrated/config                     #
# /etc/dehydrated/config                               #
# ${PWD}/config (in current working-directory)         #
#                                                      #
# Default values of this config are in comments        #
########################################################

# Resolve names to addresses of IP version only. (curl)
# supported values: 4, 6
# default: 
#IP_VERSION=

# Path to certificate authority (default:
# https://acme-v01.api.letsencrypt.org/directory)
#CA="https://acme-v01.api.letsencrypt.org/directory"

# Path to license agreement (default:
# https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf)
#LICENSE="https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf"

# Which challenge should be used? Currently http-01 and dns-01 are supported
CHALLENGETYPE="http-01"

# Path to a directory containing additional config files, allowing to
# override
# the defaults found in the main configuration file. Additional config files
# in this directory needs to be named with a '.sh' ending.
# default: 
#CONFIG_D=

# Base directory for account key, generated certificates and list of domains
# (default: $SCRIPTDIR -- uses config directory if undefined)
BASEDIR=/etc/dehydrated

# File containing the list of domains to request certificates for (default:
# $BASEDIR/domains.txt)
DOMAINS_TXT="${BASEDIR}/domains.txt"

# Output directory for generated certificates
CERTDIR="${BASEDIR}/certs"

# Directory for account keys and registration information
ACCOUNTDIR="${BASEDIR}/accounts"

# Output directory for challenge-tokens to be served by webserver or
# deployed in HOOK (default: /var/www/dehydrated)
WELLKNOWN="/var/www/dehydrated"

# Default keysize for private keys (default: 4096)
#KEYSIZE="4096"

# Path to openssl config file (default:  - tries to figure out system
# default)
#OPENSSL_CNF=

# Program or function called in certain situations
#
# After generating the challenge-response, or after failed challenge (in
# this case altname is empty)
# Given arguments: clean_challenge|deploy_challenge altname token-filename
# token-content
#
# After successfully signing certificate
# Given arguments: deploy_cert domain path/to/privkey.pem path/to/cert.pem
# path/to/fullchain.pem
#
# BASEDIR and WELLKNOWN variables are exported and can be used in an
# external program
# default: 
HOOK=/etc/dehydrated/hook.sh
# Chain clean_challenge|deploy_challenge arguments together into one hook
# call per certificate (default: no)
#HOOK_CHAIN="no"

# Minimum days before expiration to automatically renew certificate
# (default: 30)
#RENEW_DAYS="30"

# Regenerate private keys instead of just signing new certificates on
# renewal (default: yes)
#PRIVATE_KEY_RENEW="yes"

# Create an extra private key for rollover (default: no)
#PRIVATE_KEY_ROLLOVER="no"

# Which public key algorithm should be used? Supported: rsa, prime256v1 and
# secp384r1
#KEY_ALGO=rsa

# E-mail to use during the registration (default: )
CONTACT_EMAIL=admin@lists.freifunk-mwu.de

# Lockfile location, to prevent concurrent access (default: $BASEDIR/lock)
#LOCKFILE="${BASEDIR}/lock"
# Minimum days before expiration to automatically renew certificate
# (default: 30)
#RENEW_DAYS="30"

# Regenerate private keys instead of just signing new certificates on
# renewal (default: yes)
#PRIVATE_KEY_RENEW="yes"

# Option to add CSR-flag indicating OCSP stapling to be mandatory (default:
# no)
#OCSP_MUST_STAPLE="no"

hook.sh: Funktionen, die ausgeführt werden, wenn verschiedene Aktionen durchgeführt wurden. Wir verwenden deploy_cert() für neue und verlängerte Zertifikate. Dort werden lokale (zuckerwatte) Zertifikate für den apache kopiert, Berechtigungen angepasst und apache neugestartet. Und es werden Zertifikate nach /home/cert kopiert, dieses ist das Transferverzeichnis für andere Hosts. Am Ende werden noch alte Zertifikate archiviert (dehydrated -gc).

#!/usr/bin/env bash
function deploy_challenge {
    local DOMAIN="${1}" TOKEN_FILENAME="${2}" TOKEN_VALUE="${3}"
    # This hook is called once for every domain that needs to be
    # validated, including any alternative names you may have listed.
    #
    # Parameters:
    # - DOMAIN
    #   The domain name (CN or subject alternative name) being
    #   validated.
    # - TOKEN_FILENAME
    #   The name of the file containing the token to be served for HTTP
    #   validation. Should be served by your web server as
    #   /.well-known/acme-challenge/${TOKEN_FILENAME}.
    # - TOKEN_VALUE
    #   The token value that needs to be served for validation. For DNS
    #   validation, this is what you want to put in the _acme-challenge
    #   TXT record. For HTTP validation it is the value that is expected
    #   be found in the $TOKEN_FILENAME file.
}
function clean_challenge {
    local DOMAIN="${1}" TOKEN_FILENAME="${2}" TOKEN_VALUE="${3}"
    # This hook is called after attempting to validate each domain,
    # whether or not validation was successful. Here you can delete
    # files or DNS records that are no longer needed.
    #
    # The parameters are the same as for deploy_challenge.
}
function deploy_cert {
    local DOMAIN="${1}" KEYFILE="${2}" CERTFILE="${3}" FULLCHAINFILE="${4}" CHAINFILE="${5}" TIMESTAMP="${6}"
    # This hook is called once for each certificate that has been
    # produced. Here you might, for instance, copy your new certificates
    # to service-specific locations and reload the service.
    #
    # Parameters:
    # - DOMAIN
    #   The primary domain name, i.e. the certificate common
    #   name (CN).
    # - KEYFILE
    #   The path of the file containing the private key.
    # - CERTFILE
    #   The path of the file containing the signed certificate.
    # - FULLCHAINFILE
    #   The path of the file containing the full certificate chain.
    # - CHAINFILE
    #   The path of the file containing the intermediate certificate(s).
    # - TIMESTAMP
    #   Timestamp when the specified certificate was created.
    #systemctl reload nginx
    #if [ "$DOMAIN" = "smtp.xxx.xxx" ]
    #  then
    #    systemctl restart postfix dovecot
    #fi

    echo "Renewed/Created: $DOMAIN at `date -d @$TIMESTAMP`" >&2

    # Copy local Certificates
    if [[ "$DOMAIN" == "zuckerwatte.freifunk-mwu.de" ]]
    then
        rm -r /etc/apache2/ssl/*
        mkdir -p /etc/apache2/ssl/$DOMAIN/     
        cp $KEYFILE /etc/apache2/ssl/$DOMAIN/  
        cp $CERTFILE /etc/apache2/ssl/$DOMAIN/ 
        cp $CHAINFILE /etc/apache2/ssl/$DOMAIN/
    fi


    chown www-data.www-data -R /etc/apache2/ssl/
    chmod -R 0700 /etc/apache2/ssl/
    apachectl graceful

    # Copy certs to cert home dir
    mkdir -p /home/cert/$DOMAIN/ 

    cp $KEYFILE /home/cert/$DOMAIN/
    cp $CERTFILE /home/cert/$DOMAIN/
    cp $CHAINFILE /home/cert/$DOMAIN/
    
    chmod 500 $(find /home/cert -mindepth 1 -type d )
    chmod 400 $(find /home/cert -mindepth 1 -type f )
    chown cert.cert -R /home/cert/*
    
    # cleanup unused cert files
    dehydrated -gc

}
function unchanged_cert {
    local DOMAIN="${1}" KEYFILE="${2}" CERTFILE="${3}" FULLCHAINFILE="${4}" CHAINFILE="${5}"
    # This hook is called once for each certificate that is still
    # valid and therefore wasn't reissued.
    #
    # Parameters:
    # - DOMAIN
    #   The primary domain name, i.e. the certificate common
    #   name (CN).
    # - KEYFILE
    #   The path of the file containing the private key.
    # - CERTFILE
    #   The path of the file containing the signed certificate.
    # - FULLCHAINFILE
    #   The path of the file containing the full certificate chain.
    # - CHAINFILE
    #   The path of the file containing the intermediate certificate(s).

    # cleanup unused cert files
    dehydrated -gc

}
HANDLER=$1; shift; $HANDLER $@

domains.txt: Hier werden die Domains (CNs) für die Zertifikate definiert. Der erste Eintrag ist dabei der CN = Common Name, jeder weitere wird als SAN = Subject Alternative Name eingetragen. Von diesen sind weitere 99 möglich (insgesamt also 100 Namen). Wir haben für jeden Host/jedes Gateway eigene Zertifikate um eine Angriffsfläche im Falle eines Zertifikatsverlusts zu minimieren.

#Hosts
#Zuckerwatte
zuckerwatte.freifunk-mwu.de dudel.freifunk-mwu.de einzugsgebiet.freifunk-mwu.de pad.freifunk-mwu.de rtfm.freifunk-mwu.de wiki.freifunk-mwu.de freifunk-mwu.de api.freifunk-mainz.de blog.freifunk-mainz.de einzugsgebiet.freifunk-mainz.de event.freifunk-mainz.de media.freifunk-mainz.de  mitglieder.freifunk-mainz.de pad.freifunk-mainz.de wiki.freifunk-mainz.de www.freifunk-mainz.de freifunk-mainz.de mainz.freifunk.net api.wiesbaden.freifunk.net einzugsgebiet.wiesbaden.freifunk.net media.wiesbaden.freifunk.net photos.wiesbaden.freifunk.net www.wiesbaden.freifunk.net wiesbaden.freifunk.net
#Glückskeks
glueckskeks.freifunk-mwu.de autodiscover.freifunk-mwu.de autodiscover.freifunk-mainz.de autodiscover.freifunk-wiesbaden.de lists.freifunk-mwu.de mailadmin.freifunk-mwu.de webmail.freifunk-mwu.de mail.freifunk-mwu.de
#Suesskartoffel
suesskartoffel.freifunk-mwu.de map.freifunk-mwu.de map.freifunk-mainz.de map.wiesbaden.freifunk.net map.ffmwu.org map.ffmz.org map.ffwi.org

#Gates
#Spinat
spinat.freifunk-mwu.de firmware.freifunk-mwu.de spinat.freifunk-mainz.de firmware.freifunk-mainz.de firmware.wiesbaden.freifunk.net spinat.ffmwu.org firmware.ffmwu.org spinat.ffmz.org firmware.ffmz.org spinat.ffwi.org firmware.ffwi.org
#Lotuswurzel
lotuswurzel.freifunk-mwu.de firmware.freifunk-mwu.de lotuswurzel.freifunk-mainz.de firmware.freifunk-mainz.de firmware.wiesbaden.freifunk.net lotuswurzel.ffmwu.org firmware.ffmwu.org lotuswurzel.ffmz.org firmware.ffmz.org ffmz.org lotuswurzel.ffwi.org firmware.ffwi.org
#Wasserfloh
wasserfloh.freifunk-mwu.de firmware.freifunk-mwu.de wasserfloh.freifunk-mainz.de firmware.freifunk-mainz.de firmware.wiesbaden.freifunk.net wasserfloh.ffmwu.org firmware.ffmwu.org wasserfloh.ffmz.org firmware.ffmz.org wasserfloh.ffwi.org firmware.ffwi.org 
#Ingwer
ingwer.freifunk-mwu.de firmware.freifunk-mwu.de ingwer.freifunk-mainz.de firmware.freifunk-mainz.de firmware.wiesbaden.freifunk.net ingwer.ffmwu.org firmware.ffmwu.org ingwer.ffmz.org firmware.ffmz.org ingwer.ffwi.org firmware.ffwi.org