Pure-FTPD SNI

Discussion in 'Tips/Tricks/Mods' started by remkoh, Nov 11, 2024.

  1. remkoh

    remkoh Active Member HowtoForge Supporter

    I came across this topic a while back:
    https://forum.howtoforge.com/threads/pure-ftpd-sni-with-letsencrypt.85488/#post-438345

    Post #4 forms a great baseline.
    Though it isn't truely ISPC integrated and therefor doesn't support alternative names in a certificate.

    This tutorial lets you enable SNI based certificates (including alternative names) in Pure-FTPD, making use of your ISPC database to determan which ceritificate to use and its path.
    (Based on an Ubuntu 24.04, MariaDB and Acme.sh ISPC installation)

    Summary:
    - Create file with database info (user, pass, db, host, query)
    - Create script for wrapper to use
    - Enable in wrapper
    - Startup

    Install:
    Unlike steps 1 to 3 in the original post, on Ubuntu 24.04 it's not necessary to compile and install a new Pure-FTPD package.
    Pure-CERTD already is installed, as it's part of the Pure-FTPD-Common package. It just isn't enabled yet in Pure-FTPD.
    How that is in other versions and distro's I don't know.

    Step 1 Create database variables file
    Code:
    nano /etc/pure-ftpd/db/ExtCert.conf
    Code:
    user="<db user>"
    password="<db pass>"
    dbname="<db name>"
    hosts="<db host>"
    query="SELECT domain,document_root,ssl_letsencrypt FROM web_domain WHERE domain_id = (SELECT parent_domain_id FROM web_domain WHERE domain = '$sni_name' AND (type = 'alias' OR type = 'subdomain') AND ssl_letsencrypt_exclude = 'n' AND active = 'y' AND server_id = <server id>) AND type = 'vhost' AND web_domain.ssl = 'y' AND active = 'y' AND server_id = <server id> UNION SELECT domain,document_root,ssl_letsencrypt FROM web_domain WHERE domain = '$sni_name' AND type = 'vhost' AND web_domain.ssl = 'y' AND active = 'y' AND server_id = <server id>;"
    
    Replace <...> with your server's info.
    Code:
    chmod 600 /etc/pure-ftpd/db/ExtCert.conf

    Step 2 Create TLS SNI parser shell script
    Code:
    nano /bin/pure-cert-check.sh
    Code:
    #! /usr/bin/bash
    
    #echo "$(env)" > /root/sni_log
    
    sni_name=${CERTD_SNI_NAME}
    
    source /etc/pure-ftpd/db/ExtCert.conf
    
    result=$(mariadb --host="$hosts" --user="$user" --password="$password" --database="$dbname" --silent --execute="$query")
    
    cert=$(echo $result | awk '{print $1}')
    path=$(echo $result | awk '{print $2}')
    le=$(echo $result | awk '{print $3}')
    
    if [[ $cert != '' ]] then
            if [[ $le = 'y' ]] then
                    certpath=$path"/ssl/"$cert"-le.crt"
                    keypath=$path"/ssl/"$cert"-le.key"
                    echo 'action:strict'
                    echo 'cert_file:'$certpath
                    echo 'key_file:'$keypath
                    echo 'end'
            elif [[ $le = 'n' ]] then
                    certpath=$path"/ssl/"$cert".crt"
                    keypath=$path"/ssl/"$cert".key"
                    echo 'action:strict'
                    echo 'cert_file:'$certpath
                    echo 'key_file:'$keypath
                    echo 'end'
            else
                    echo 'action:default'
                    echo 'end'
            fi
    else
            echo 'action:default'
            echo 'end'
    fi
    
    Code:
    chmod +x /bin/pure-cert-check.sh
    The script gets the SNI name used in the connection and looks it up in ISPC's database.
    If it is an alias or subdomain then the corresponding vhost is returned. LE excluded hostnames are excluded in the return.
    Furthermore the vhost's document_root is returned and if the certificate is LE or not.
    With that the path to the certificate and key are constructed.
    If the SNI name lookup doesn't return anything then Pure-FTPD's default certificate will be used.

    Step 3 Append a line in to /usr/sbin/pure-ftpd-wrapper
    Code:
    'NoTruncate' => ['-0'],
    +++ 'ExtCert' => [ '-3 %s', \&parse_string],
    'PassivePortRange' => ['-p %d:%d', \&parse_number_2],
    

    Step 4 ExtCert create (pure-ftpd.conf won't work...)
    Code:
    echo "/var/run/ftpd-certs.sock" > /etc/pure-ftpd/conf/ExtCert

    Step 5 Start pure-certd daemon

    Edit: Post #4 contains an alternative option which creates a service!!!
    Code:
    pure-certd --run /bin/pure-cert-check.sh --socket /var/run/ftpd-certs.sock --pidfile /var/run/pure-certd.pid -B
    Add a cronjob to run the command at startup
    Code:
    nano /var/spool/cron/crontabs/root
    Code:
    * * * * * /usr/local/ispconfig/server/server.sh 2>&1 | while read line; do echo `/bin/date` "$line" >> /var/log/ispconfig/cron.log; done
    * * * * * /usr/local/ispconfig/server/cron.sh 2>&1 | while read line; do echo `/bin/date` "$line" >> /var/log/ispconfig/cron.log; done
    +++ @reboot pure-certd --run /bin/pure-cert-check.sh --socket /var/run/ftpd-certs.sock --pidfile /var/run/pure-certd.pid -B
    

    Step 6
    Restart Pure-FTPD service
    Code:
    systemctl restart pure-ftpd-mysql.service
    It's not the cleanest but does exactly what it is supposed to do.
     
    Last edited: Nov 18, 2024 at 12:03 AM
    nhybgtvfr, till and ahrasis like this.
  2. nhybgtvfr

    nhybgtvfr Well-Known Member HowtoForge Supporter

    is /etc/pure-ftpd/db/ExtCert.conf actually needed?

    /etc/pure-ftpd/db/mysql.conf is already edited by ispconfig, and contains the db connection settings and already contains various queries for ispconfig. could the query not just get added to this file, and have pure-cert-check.sh reference the mysql.conf file instead?
     
  3. remkoh

    remkoh Active Member HowtoForge Supporter

    Maybe. Like I said, it's not the cleanest but it works.
    I kept it in line like Postfix. That too has multiple .cf files, each with $user, $pass, $dbname, $host and $query.
     
    Last edited: Nov 12, 2024
    ahrasis likes this.
  4. remkoh

    remkoh Active Member HowtoForge Supporter

    Or better yet, don't start it by hand and through a cronjob on reboot but create a service!

    Step 5
    Start pure-certd daemon
    Code:
    nano /etc/systemd/system/pure-certd.service
    Code:
    [Unit]
    Description=Pure FTPD Certd service
    After=mariadb.service
    StartLimitIntervalSec=0
    
    [Service]
    Type=simple
    Restart=always
    RestartSec=1
    User=root
    Group=root
    ExecStart=/sbin/pure-certd --run /bin/pure-cert-check.sh --socket /var/run/ftpd-certs.sock --pidfile /var/run/pure-certd.pid -B
    
    [Install]
    WantedBy=multi-user.target
    
    Then enable and start the service:
    Code:
    systemctl enable pure-certd.service
    Code:
    systemctl start pure-certd.service
    If you're using MySQL instead of MariaDB then change line 3 to:
    Code:
    After=mysql.service
    Continue to step 6 in the first post.
     
    Taleman and till like this.

Share This Page