Filemaker Server 2024, Letsencrypt, lego, Hetzner Api
Mac mini M1, macOS Sequoia 15.3, Filemaker Server 2024.
Ich möchte über die Hetzner-Api mittels lego ein Zertifikat von Letsencrypt holen ohne dafür am Filemaker Server den Port 80 zu öffnen. Das funktioniert auch mit vielen anderen DNS-Providern, eine Liste gibt es auf der lego Webseite.
Quellen
Lego: https://go-acme.github.io/lego/dns/hetzner/index.html
Hetzner-Api: https://dns.hetzner.com/api-docs
lego installieren
brew update
==> Updating Homebrew...
Already up-to-date.
brew upgrade
brew install lego
Zertifikat holen
Ich habe mir für meine Domain ein Shell-Script geschrieben. Den Api-Key muss man sich bei Hetzner in seinem Account anlegen.
nano ~/lego-run.sh
#!/bin/sh
PATH="/Users/ICH"
DOMAIN="FilemakerServer.domain.com"
EMAIL="email@domain.com"
# mein Hetzner Api-Key
KEY="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
cd ${PATH}
HETZNER_API_KEY=${KEY} /opt/homebrew/bin/lego --email ${EMAIL} --dns hetzner -d ${DOMAIN} run
chmod 750 ~/lego-run.sh
Und dann ausgeführt.
./lego-run.sh
2025/02/08 17:08:59 No key found for account email@domain.com. Generating a P256 key.
2025/02/08 17:08:59 Saved key to /Users/ICH/.lego/accounts/acme-v02.api.letsencrypt.org/email@domain.com/keys/email@domain.com.key
2025/02/08 17:08:59 [DEBUG] GET https://acme-v02.api.letsencrypt.org/directory
2025/02/08 17:09:00 Please review the TOS at https://letsencrypt.org/documents/LE-SA-v1.4-April-3-2024.pdf
Do you accept the TOS? Y/n
Y
2025/02/08 17:09:03 [INFO] acme: Registering account for email@domain.com
2025/02/08 17:09:03 [DEBUG] HEAD https://acme-v02.api.letsencrypt.org/acme/new-nonce
2025/02/08 17:09:04 [DEBUG] POST https://acme-v02.api.letsencrypt.org/acme/new-acct
!!!! HEADS UP !!!!
Your account credentials have been saved in your Let's Encrypt
configuration directory at "/Users/ICH/.lego/accounts".
You should make a secure backup of this folder now. This
configuration directory will also contain certificates and
private keys obtained from Let's Encrypt so making regular
backups of this folder is ideal.
2025/02/08 17:09:04 [INFO] [FilemakerServer.domain.com] acme: Obtaining bundled SAN certificate
2025/02/08 17:09:04 [DEBUG] POST https://acme-v02.api.letsencrypt.org/acme/new-order
2025/02/08 17:09:04 [DEBUG] POST https://acme-v02.api.letsencrypt.org/acme/authz/22165/472925
2025/02/08 17:09:04 [INFO] [FilemakerServer.domain.com] AuthURL: https://acme-v02.api.letsencrypt.org/acme/authz/225/4725
2025/02/08 17:09:04 [INFO] [FilemakerServer.domain.com] acme: Could not find solver for: tls-alpn-01
2025/02/08 17:09:04 [INFO] [FilemakerServer.domain.com] acme: Could not find solver for: http-01
2025/02/08 17:09:04 [INFO] [FilemakerServer.domain.com] acme: use dns-01 solver
2025/02/08 17:09:04 [INFO] [FilemakerServer.domain.com] acme: Preparing to solve DNS-01
2025/02/08 17:09:05 [INFO] [FilemakerServer.domain.com] acme: Trying to solve DNS-01
2025/02/08 17:09:05 [INFO] [FilemakerServer.domain.com] acme: Checking DNS record propagation. [nameservers=1.1.1.1:53,9.9.9.9:53]
2025/02/08 17:09:07 [INFO] Wait for propagation [timeout: 2m0s, interval: 2s]
2025/02/08 17:09:07 [INFO] [FilemakerServer.domain.com] acme: Waiting for DNS record propagation.
2025/02/08 17:09:10 [INFO] [FilemakerServer.domain.com] acme: Waiting for DNS record propagation.
2025/02/08 17:09:12 [INFO] [FilemakerServer.domain.com] acme: Waiting for DNS record propagation.
2025/02/08 17:09:14 [INFO] [FilemakerServer.domain.com] acme: Waiting for DNS record propagation.
2025/02/08 17:09:16 [INFO] [FilemakerServer.domain.com] acme: Waiting for DNS record propagation.
2025/02/08 17:09:18 [INFO] [FilemakerServer.domain.com] acme: Waiting for DNS record propagation.
2025/02/08 17:09:20 [DEBUG] POST https://acme-v02.api.letsencrypt.org/acme/chall/2265/425/0EHnPw
2025/02/08 17:09:20 [DEBUG] POST https://acme-v02.api.letsencrypt.org/acme/authz/225/47725
2025/02/08 17:09:24 [DEBUG] POST https://acme-v02.api.letsencrypt.org/acme/authz/2217165/47293725
2025/02/08 17:09:24 [INFO] [FilemakerServer.domain.com] The server validated our request
2025/02/08 17:09:24 [INFO] [FilemakerServer.domain.com] acme: Cleaning DNS-01 challenge
2025/02/08 17:09:25 [INFO] [FilemakerServer.domain.com] acme: Validations succeeded; requesting certificates
2025/02/08 17:09:25 [DEBUG] POST https://acme-v02.api.letsencrypt.org/acme/finalize/225/3525
2025/02/08 17:09:27 [DEBUG] POST https://acme-v02.api.letsencrypt.org/acme/cert/03c4c0
2025/02/08 17:09:27 [DEBUG] POST https://acme-v02.api.letsencrypt.org/acme/cert/03c4c0/1
2025/02/08 17:09:27 [INFO] [FilemakerServer.domain.com] Server responded with a certificate.
Das Zertifikat muss für den Import in den Ordner des Filemaker Servers kopiert werden und die Rechte müssen gesetzt werden, sonst klappt auch der Import von der Admin-Console nicht.
cp ~/.lego/certificates/*.crt /Library/FileMaker\ Server/CStore/
cp ~/.lego/certificates/*.key /Library/FileMaker\ Server/CStore/
chmod 640 /Library/FileMaker\ Server/CStore/*.crt
chmod 640 /Library/FileMaker\ Server/CStore/*.key
chown fmserver:fmsadmin /Library/FileMaker\ Server/CStore/*.crt
chown fmserver:fmsadmin /Library/FileMaker\ Server/CStore/*.key
Zertifikat automatisch erneuern
nano ~/lego-renew.sh
Dieses Script wird bei mir 1x / Woche ausgeführt. Es prüft, ob das Zertifikat erneuert werden muss. Wenn es erneuter wird, dann wird im Anschluss das Script lego-hook.sh ausgeführt.
#!/bin/sh
PATH="/Users/ICH"
DOMAIN="FilemakerServer.domain.com"
EMAIL="email@domain.com"
# mein Hetzner Api-Key
KEY="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
LOGFILE="/tmp/legorenew.log"
cd ${PATH}
HETZNER_API_KEY=${KEY} /opt/homebrew/bin/lego --email ${EMAIL} --dns hetzner -d ${DOMAIN} renew --renew-hook=${PATH}/lego-hook.sh
So sieht die Ausgabe aus, wenn das Zertifikat nicht erneuert werden muss.
./lego-renew.sh
2025/02/08 17:23:59 [DEBUG] GET https://acme-v02.api.letsencrypt.org/directory
2025/02/08 17:23:59 [DEBUG] GET https://acme-v02.api.letsencrypt.org/draft-ietf-acme-ari-03/renewalInfo/ky....._TA
2025/02/08 17:24:00 [INFO] [FilemakerServer.domain.com] acme: renewalInfo endpoint indicates that renewal is not needed
2025/02/08 17:24:00 [FilemakerServer.domain.com] The certificate expires in 89 days, the number of days defined to perform the renewal is 30: no renewal.
das Skript für den Hook
nano ~/lego-hook.sh
#!/bin/sh
# Dieses Skript wird nur ausgeführt, wenn das Skript
# lego-renew.sh die Zertifikate erneuert hat.
DOMAIN="FilemakerServer.domain.com"
SERVER_PATH="/Library/FileMaker Server/"
# Benutzername/Pass vom Filemaker-Admin
USER="ICH"
PASS="xxxxxx-xxxxx-xxxxxx"
LOGFILE="/tmp/legorenew.log"
# der Ort wo lego das Zertifikat abgelegt hat
certfile="/Users/ICH/.lego/certificates/${DOMAIN}.crt"
keyfile="/Users/ICH/.lego/certificates/${DOMAIN}.key"
# Das Zertifikat in den Order für Filemaker Server kopieren
cp "/Users/ICH/.lego/certificates/${DOMAIN}.crt" "${SERVER_PATH}CStore/${DOMAIN}.crt"
cp "/Users/ICH/.lego/certificates/${DOMAIN}.key" "${SERVER_PATH}CStore/${DOMAIN}.key"
# Die Rechte für den Filemaker Server setzen
chmod 640 "${SERVER_PATH}CStore/${DOMAIN}.crt"
chmod 640 "${SERVER_PATH}CStore/${DOMAIN}.key"
chown fmserver:fmsadmin "${SERVER_PATH}CStore/${DOMAIN}.crt"
chown fmserver:fmsadmin "${SERVER_PATH}CStore/${DOMAIN}.key"
# Das Zertifikat in den Filemaker Server importieren
fmsadmin certificate delete -y -u "${USER}" -p "${PASS}"
fmsadmin certificate import "${SERVER_PATH}CStore/${DOMAIN}.crt" --keyfile "${SERVER_PATH}CStore/${DOMAIN}.key" -y -u "${USER}" -p "${PASS}" >> "${LOGFILE}"
# Den Filemaker Server neustarten
launchctl stop com.filemaker.fms
sleep 60
launchctl start com.filemaker.fms
Die Skripte bewegen und Rechte setzen
cd ~/
sudo mv lego-*.sh /usr/local/bin/
sudo chown root:wheel /usr/local/bin/lego-*.sh
sudo chmod 750 /usr/local/bin/lego-*.sh
ln -s /usr/local/bin/lego-hook.sh
ln -s /usr/local/bin/lego-renew.sh
LaunchDaemon
Jeden Samstag früh um 4:29 Uhr soll lego-renew.sh das Zertifikat prüfen, ggf. erneuern und mittels lego-hook.sh das Zertifikat in den Filemaker Server laden und diesen dann neu starten.
sudo nano /Library/LaunchDaemons/com.filemaker.fmcertrenew.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/sbin</string>
</dict>
<key>Label</key>
<string>com.filemaker.fmcertrenew</string>
<key>ProgramArguments</key>
<array>
<string>/bin/sh</string>
<string>/usr/local/bin/lego-renew.sh</string>
</array>
<key>RunAtLoad</key>
<false/>
<key>AbandonProcessGroup</key>
<true/>
<key>StartCalendarInterval</key>
<array>
<dict>
<key>Hour</key>
<integer>4</integer>
<key>Minute</key>
<integer>29</integer>
<key>Weekday</key>
<integer>6</integer>
</dict>
</array>
</dict>
</plist>