• The new Python extension is now available. It allows customers to deploy and manage WSGI-based Python applications on their websites directly from Plesk.
  • The Plesk Mobile apps for iOS and Android and the Plesk Mobile Center extension have been deprecated, and are no longer available for new installations. In Plesk Obsidian 18.0.84, the Plesk Mobile Center extension will be removed from servers during the upgrade, and the Plesk Mobile apps will lose their server management features.
  • The Social Login extension has been deprecated and will be removed in Plesk Obsidian 18.0.84. After that, it will no longer be possible to log in to Plesk with a Google, Facebook, GitHub account, or with a passkey. Plesk usernames and passwords, as well as logging in with a WebPros Account, are not affected.
Resource icon

Instruction security.txt (RFC 9116) generator - PGP signing for many domains - Plesk server, SSH or FTP-only 1.0.0

No permission to download
Hi all,

I would like to share a small Bash script that creates and renews signed security.txt files (RFC 9116) and the matching pgp-key.txt for any number of domains. I wrote it for my own Plesk server first and then made it universal, so it also works on a VPS without root and even on FTP-only web space.

What it does
For every domain in a list it writes two files to <webroot>/.well-known/:
  • security.txt - Contact, Expires, Encryption, Canonical, Preferred-Languages, clear-signed
  • pgp-key.txt - the public key, referenced by the Encryption field
Each domain gets its own RSA-4096 key (UID security@<domain>) in an isolated GnuPG keyring, so your normal keyring is never touched. Every run sets Expires to "now + 365 days", so a monthly scheduled task keeps the files from ever expiring.

Features
  • Three profiles in one script, detected automatically or forced with -p:
    • server - root on a server such as Plesk: many domains, chown per domain, config in /etc/security-txt/
    • ssh - normal SSH user (VPS, web space): everything below $HOME, no chown
    • ftp - runs on your own computer (Linux, macOS, WSL) and writes to an output folder that you upload; it reminds you of the renewal date
  • GnuPG is optional: without gpg the script says so and writes an unsigned security.txt (valid according to RFC 9116)
  • Simple domain list domain|webroot|owner|group|contact and the commands add, remove, list, run
  • add example.com is enough on Plesk: the webroot defaults to /var/www/vhosts/<domain>/httpdocs, owner and group are read from that folder, and the files are created at once
  • check fetches the files over HTTPS and tests reachability, content type, expiry date and signature
  • import-plesk finds Plesk domains that are not in the list yet (dry run by default, --apply adds them)
  • random passphrase generated on the first run and stored in the keyring folder (mode 600); an existing keyring and passphrase can be reused
  • files are replaced atomically, parallel runs are blocked with a lock, temporary files are cleaned up
  • configurable contact address, Preferred-Languages, optional www Canonical line, validity in days

Requirements
Linux with Bash, awk and curl (for check); GnuPG 2.1.22 or newer is optional but recommended. I developed and tested it with Bash 5.2 and GnuPG 2.4. The macOS date fallback and the old Bash 3.2 are untested.

Quick start: Plesk server or any server as root
Code:
install -m 0755 -o root -g root security-txt.sh /usr/local/sbin/security-txt.sh
install -d -m 0755 -o root -g root /etc/security-txt

Code:
security-txt.sh add example.com
security-txt.sh add shop.example.com /var/www/vhosts/example.com/shop.example.com
security-txt.sh list
security-txt.sh check example.com

Monthly renewal as a Plesk scheduled task: Tools & Settings > Scheduled Tasks > Add Task, task type "Run a command", system user root, command:
Code:
/usr/local/sbin/security-txt.sh run

or with cron:
Code:
cat > /etc/cron.d/security-txt <<'EOF'
0 0 1 * * root /usr/local/sbin/security-txt.sh run >/dev/null
EOF
chmod 0644 /etc/cron.d/security-txt

On the first run the script creates /root/.security_txt_gpg (700) with a random passphrase in passphrase (600). Please back up this folder together with the passphrase file. /etc/security-txt/ must only be writable by root, because the script runs as root and uses the paths and owners from the list.

Optional settings go into /etc/security-txt/security-txt.conf:
Code:
LANGUAGES="en, de"
WWW_CANONICAL=1

To add all hosted domains from Plesk at once, look at the dry run first:
Code:
security-txt.sh import-plesk
security-txt.sh import-plesk --apply
security-txt.sh run

Quick start: SSH without root
Code:
mkdir -p ~/bin
cp security-txt.sh ~/bin/security-txt.sh
chmod 0700 ~/bin/security-txt.sh

Code:
~/bin/security-txt.sh add example.com ~/httpdocs
~/bin/security-txt.sh check example.com

Configuration goes to ~/.config/security-txt/, the keyring to ~/.security_txt_gpg (700). The public files are written with mode 644 so that the web server can read them. Schedule it with crontab -e:
Code:
0 0 1 * * $HOME/bin/security-txt.sh run >/dev/null

Quick start: FTP-only web space
Run it on your own computer (Linux, macOS, or WSL on Windows). On Linux and WSL force the profile once:
Code:
mkdir -p ~/bin
cp security-txt.sh ~/bin/security-txt.sh
chmod 0700 ~/bin/security-txt.sh
echo 'export SECURITY_TXT_PROFILE=ftp' >> ~/.bashrc

Open a new terminal, then:
Code:
~/bin/security-txt.sh add example.com

Upload the folder ~/security-txt-output/example.com/.well-known/ into the web root of the domain (your FTP client must show hidden files), then verify:
Code:
~/bin/security-txt.sh check example.com

The script prints the expiry date and the date by which to renew. Put it in your calendar; to renew, run ~/bin/security-txt.sh run and upload again.

Things to know
  • The mailbox security@<domain> should exist, or set another address in the contact field of the list.
  • RFC 9116 recommends an Expires date less than a year ahead. The default is 365 days. Set DAYS_VALID=350 if a checker complains.
  • The signature check in check verifies against the key published on the same host. It proves integrity, not the identity of the key owner.
  • In the server profile the script runs as root and writes into folders owned by the domain users. Use it where you trust the domain owners; otherwise let each user run the ssh profile.
  • Subdomains are separate hosts: add each one with its webroot (add sub.example.com /path/to/webroot), keep WWW_CANONICAL=0 if your list contains subdomains. Two entries cannot share a webroot: add refuses it and run warns about duplicates in a hand-edited list.
  • If an old keyring exists without a passphrase file, the script stops and tells you how to store the passphrase once.

Documentation
The attachments contain the script and the full documentation in English and German (installation for all three profiles, permissions, configuration, scheduling, troubleshooting) plus a FAQ that explains every message of the script and what to do about it:
  • security-txt.sh
  • README.md (English) and README.de.md (Deutsch)
  • FAQ.md (English) and FAQ.de.md (Deutsch)
  • CHANGELOG.md
  • LICENSE (MIT)
Script version: (security-txt.sh --version). The script uses Semantic Versioning; changes are listed in the changelog. To update, just replace the script file: your list, settings and keyring stay as they are.

The script is released under the MIT License, so you can use, change and share it freely.

Feedback, bug reports and suggestions are welcome.

Regards
Tessa
Author
tessa67
Downloads
1
Views
24
First release
Last update
Rating
0.00 star(s) 0 ratings
Back
Top