[TUTORIAL] How-To -- Lets Encrypt and PMG

Discussion in 'Mail Gateway: Installation and configuration' started by ChFin, Feb 17, 2018.

  1. ChFin

    ChFin Member
    Proxmox Subscriber

    Joined:
    Jan 30, 2018
    Messages:
    50
    Likes Received:
    12
    Let's Encrypt is a free, automated and open certificate authority. The CA issues standard domain validation certificates. The certificates can be used for web servers, email servers, FTP servers and many more. Email encryption and code signing requires a different type of certificate that Let's encrypt doesn't issue.

    Below are a few links that you might want to read:

    You can find the documentation here:
    https://letsencrypt.org/docs/

    Important: What you need to know about TLS-SNI validation issues
    https://community.letsencrypt.org/t...to-know-about-tls-sni-validation-issues/50811

    For Let's Encrypt and DNS CAA records read this document:
    https://letsencrypt.org/docs/caa/

    If you don't care about CAA, you don't have to do anything but in case of errors check the CAA error section
    in the document above.

    If you would like to use CAA records, check out the CAA generator of:
    https://sslmate.com/caa/


    Proxmox Mail Gateway uses Keys and certificate to make secure connections. The application uses the keys and certificates stored at:
    /etc/pmg/pmg-api.pem -- Key and certificate (combined) used be the HTTPs server (API)
    /etc/pmg/pmg-tls.pem -- Key and certificate (combined) to encrypt mail traffic (TLS)

    Be aware, that the application itself can overwrite some key and certificate files, e.g. when you disable & enable the TLS settings in the mail proxy configuration.

    To replace the TLS certificates we request certificates from the Let's encrypt CA. We do that with the certbot application.

    Preconditions
    Firewall http/inbound is open.
    Hostname is properly set
    PMG Mail Proxy configuration has enabled TLS and TLS logging

    Installation
    It is recommended to use the certbot application from the stretch-backports repository. To install files from stretch-backports add the repository to your sources.list:

    # vi /etc/apt/sources.list

    Add the line
    deb http://ftp.debian.org/debian stretch-backports main

    After you edited the file run:

    # apt-get update

    To install certbot run:

    # apt-get install certbot -t stretch-backports

    Before we request the certificate we create a post-hook script in our /root directory.

    # cd /root
    # vi certbot-post-hook.sh


    Code:
    #!/bin/bash
    # post-hook see renewalparams in /etc/letsencrypt/renewal/$(hostname -f).conf
    
    # replace mail certificate
    cat /etc/letsencrypt/live/$(hostname -f)/fullchain.pem /etc/letsencrypt/live/$(hostname -f)/privkey.pem >/etc/pmg/pmg-tls.pem
    chown root:root /etc/pmg/pmg-tls.pem
    chmod 600 /etc/pmg/pmg-tls.pem
    
    # replace http certificate
    cat /etc/letsencrypt/live/$(hostname -f)/fullchain.pem /etc/letsencrypt/live/$(hostname -f)/privkey.pem >/etc/pmg/pmg-api.pem
    chown root:www-data /etc/pmg/pmg-api.pem
    chmod 640 /etc/pmg/pmg-api.pem
    
    systemctl restart pmgproxy
    Set access permissions to your post-hook script:

    # chmod 700 certbot-post-hook.sh

    Now you can request the certificate:

    # certbot certonly --authenticator standalone --preferred-challenges http --post-hook "/root/certbot-post-hook.sh" -d $(hostname -f)

    Enter your email address, agree to the terms of service and answer the question if you would like to share your email address.

    Congratulations you have requested and installed (via post-hook) your Let's encrypt certificate.

    Automated renewal

    The debian package comes with a cron job and a systemd timer.

    The cron job won't execute the renew command when you are running systemd (if /run/systemd/system is detected). It's done via certbot.timer
    For automatic renewal just make sure certbot.timer is enabled & started. Post-Hook and Preferred Challenges were stored in /etc/letsencrypt/renewal/($hostname -f).conf during certificate request.

    # systemctl status certbot.timer

    Status should be enabled/active (waiting).

    Check your certificate in the browser and watch the TLS log output in /var/log/mail.log.

    Verify your mail server tls encryption here: https://ssl-tools.net/mailservers
     
    #1 ChFin, Feb 17, 2018
    Last edited: Feb 17, 2018
    guletz, eric_f, uy01 and 1 other person like this.
  2. DerDanilo

    DerDanilo Member
    Proxmox Subscriber

    Joined:
    Jan 21, 2017
    Messages:
    251
    Likes Received:
    21
    Using acme.sh doesn't require any additional repository nor any other tool. :)
     
  3. ChFin

    ChFin Member
    Proxmox Subscriber

    Joined:
    Jan 30, 2018
    Messages:
    50
    Likes Received:
    12
    Certbot is recommended by Let's Encrypt and most people should start with it. If certbot does not meet your needs you are free to try a 3rd party client, but keep in mind that Let’s Encrypt does not control or review third party clients and cannot make any guarantees about their safety or reliability.
     
  4. DerDanilo

    DerDanilo Member
    Proxmox Subscriber

    Joined:
    Jan 21, 2017
    Messages:
    251
    Likes Received:
    21
    You are totally right. I almost always choose acme.sh for is simplicity. It almost does not have any dependencies and runs out of the box as long as you have bash available. Also the content of the whole script is available online.

    As you can see here PVE uses acme.sh already for PVE setup as possible option.

    Everybody choose what he/she wants. But I'd rather choose a script which access rights I can easily limit than any package I have to install.
     
    #4 DerDanilo, Feb 18, 2018
    Last edited: Feb 19, 2018
  5. DerDanilo

    DerDanilo Member
    Proxmox Subscriber

    Joined:
    Jan 21, 2017
    Messages:
    251
    Likes Received:
    21
    Here is an alternative script when using acme.sh.

    I will push the complete manual to my Github repository once I find time for that.

    Code:
    #!/bin/bash
    
    # How to install cert after acme initial cert was pulled
    # chmod +x /root/acme-post-hook.sh
    # acme.sh --install-cert -d $(hostname -f) --key-file /root/.acme.sh/$(hostname -f)/$(hostname -f).key.pem --fullchain-file /root/.acme.sh/$(hostname -f)/fullchain.pem --reloadcmd "bash /root/acme-post-hook.sh"
    
    # replace mail certificate
    cat /root/.acme.sh/$(hostname -f)/fullchain.pem /root/.acme.sh/$(hostname -f)/$(hostname -f).key.pem >/etc/pmg/pmg-tls.pem
    chown root:root /etc/pmg/pmg-tls.pem
    chmod 600 /etc/pmg/pmg-tls.pem
    
    # replace http certificate - we don't need this if you are using an external proxy and don't want to update each fingerprint every time the certificate renewal took place.
    #cat /root/.acme.sh/$(hostname -f)/fullchain.pem /root/.acme.sh/$(hostname -f)/$(hostname -f).key.pem >/etc/pmg/pmg-api.pem
    #chown root:www-data /etc/pmg/pmg-api.pem
    #chmod 640 /etc/pmg/pmg-api.pem
    
    systemctl restart pmgproxy
    # If you are using haproxy as local reverse proxy
    # systemctl restart haproxy
    
     
    #5 DerDanilo, Feb 19, 2018
    Last edited: Feb 19, 2018
  6. Davide Bozzelli

    Joined:
    Feb 6, 2018
    Messages:
    71
    Likes Received:
    4
    I started using acme.sh but in the end I've preferred certbot over acme.sh.
    One nice thing of certbot is that it ships with a systemd timer so you could integrate it on the services page of the web ui and start/stop see the log.
     
  7. DerDanilo

    DerDanilo Member
    Proxmox Subscriber

    Joined:
    Jan 21, 2017
    Messages:
    251
    Likes Received:
    21
    Creating a service (including timer) is easy. One should have proper monitoring anyways. Watching some gui entry for a renewal service that runs sometimes does not add much value for me.
     
  8. BJ78945

    BJ78945 New Member

    Joined:
    Apr 15, 2015
    Messages:
    27
    Likes Received:
    3
    Thanks for your How-To

    I would add something for cluster and multidomain. You cannot use it out of the box but perhaps it helps some people.

    I use two domains with two diffrent dns server providers. I have some domaisn for mx and the hostnames itself so I need an multidomain cert. If the cert changes cluster functiones are broken because of the changing fingerprint.

    So 1. I need to update DNS records while generating the certs and 2. I have to update the fingerprints in cluster config.

    I found some tips on the internet (github, AnalogJ, lexicon (Could not insert url sorry)) and added some code. Could be improved!

    My solution (you need to change this for your needs!):

    Code:
    certbot certonly --manual --preferred-challenges dns --manual-auth-hook "/etc/letsencrypt/certbot.default.sh auth" --manual-cleanup-hook "/etc/letsencrypt/certbot.default.sh cleanup" --post-hook="/etc/letsencrypt/certbot-post-hook.sh" -d host1 -d host2 -d mx1 -d mx2
    
    /etc/letsencrypt/certbot.default.sh
    Code:
    #!/usr/bin/env bash
    #
    
    set -euf -o pipefail
    
    PROVIDER1="abc"
    PROVIDER1_CREDENTIALS=("XXX")
    PROVIDER2="def"
    PROVIDER2_CREDENTIALS=("XXX")
    PROVIDER_UPDATE_DELAY=30
    
    # To be invoked via Certbot's --manual-auth-hook
    function auth {
        if [[ "${CERTBOT_DOMAIN}" == *"domain1"* ]]; then
            lexicon "${PROVIDER1}" "${PROVIDER1_CREDENTIALS[@]}" \
            create "${CERTBOT_DOMAIN}" TXT --name "_acme-challenge.${CERTBOT_DOMAIN}" --content "${CERTBOT_VALIDATION}"
        else
            lexicon "${PROVIDER2}" "${PROVIDER2_CREDENTIALS[@]}" \
            create "${CERTBOT_DOMAIN}" TXT --name "_acme-challenge.${CERTBOT_DOMAIN}" --content "${CERTBOT_VALIDATION}"
        fi
    
        sleep "${PROVIDER_UPDATE_DELAY}"
    }
    
    # To be invoked via Certbot's --manual-cleanup-hook
    function cleanup {
        if [[ "${CERTBOT_DOMAIN}" == *"domain1"* ]]; then
            lexicon "${PROVIDER1}" "${PROVIDER1_CREDENTIALS[@]}" \
            delete "${CERTBOT_DOMAIN}" TXT --name "_acme-challenge.${CERTBOT_DOMAIN}" --content "${CERTBOT_VALIDATION}"
        else
            lexicon "${PROVIDER2}" "${PROVIDER2_CREDENTIALS[@]}" \
            delete "${CERTBOT_DOMAIN}" TXT --name "_acme-challenge.${CERTBOT_DOMAIN}" --content "${CERTBOT_VALIDATION}"
        fi
    }
    
    HANDLER=$1; shift;
    if [ -n "$(type -t $HANDLER)" ] && [ "$(type -t $HANDLER)" = function ]; then
      $HANDLER "$@"
    fi
    
    /etc/letsencrypt/certbot-post-hook.sh
    Code:
    #!/bin/bash
    
    # replace mail certificate
    cat /etc/letsencrypt/live/host1/fullchain.pem /etc/letsencrypt/live/host1/privkey.pem >/etc/pmg/pmg-tls.pem
    chown root:root /etc/pmg/pmg-tls.pem
    chmod 600 /etc/pmg/pmg-tls.pem
    scp /etc/pmg/pmg-tls.pem host2:/etc/pmg/pmg-tls.pem
    
    # replace http certificate
    cat /etc/letsencrypt/live/host1/fullchain.pem /etc/letsencrypt/live/host1/privkey.pem >/etc/pmg/pmg-api.pem
    chown root:www-data /etc/pmg/pmg-api.pem
    chmod 640 /etc/pmg/pmg-api.pem
    scp /etc/pmg/pmg-api.pem host2:/etc/pmg/pmg-api.pem
    
    # generiere cert hash und teile mit cluster nodes
    HASH="$(openssl x509 -in /etc/pmg/pmg-api.pem -noout -fingerprint -sha256 | cut -d'=' -f2)"
    cp /etc/pmg/cluster.conf /etc/pmg/cluster.conf.bkp
    sed "s/.*fingerprint.*/        fingerprint "$HASH"/" /etc/pmg/cluster.conf.bkp > /etc/pmg/cluster.conf
    scp /etc/pmg/cluster.conf host2:/etc/pmg/cluster.conf
    
    # starte dienste neu
    systemctl restart pmgproxy
    ssh root@host2 'systemctl restart pmgproxy'
    
    Later I would add TLSA records update for DANE. Record generating is possible with
    Code:
    tlsa --create --selector 1 --port 25 --certificate /etc/letsencrypt/live/host1/cert.pem host1
    ... host2
    ... mx1
    ... mx2
    
    Only some string magic and lexicon commands are missing.
     
    #8 BJ78945, Apr 27, 2018
    Last edited: Apr 27, 2018
    guletz and DerDanilo like this.
  9. Jarkko

    Jarkko New Member

    Joined:
    Mar 6, 2019
    Messages:
    10
    Likes Received:
    0
    Thanks for this one

    Here is what i use to put new hash from cert per server:

    Code:
    #!/bin/bash
    HASH="$(openssl x509 -in /etc/pmg/pmg-api.pem -noout -fingerprint -sha256 | cut -d'=' -f2)"
    sed -i "0,/fingerprint/{s/.*fingerprint.*/        fingerprint ${HASH}/}" /etc/pmg/cluster.conf
    It works on nodes too, just adjust the "0" to find your node side fingerprint.
     
    #9 Jarkko, Mar 8, 2019
    Last edited: Mar 8, 2019
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice