Knowledge base

Lock down permissions on configuration files

Protecting sensitive information
The configuration is a secrets store

The manual devotes a separate section to this, and for good reason: PowerMTA configuration files hold submission passwords, API keys and private DKIM signing keys — in plain text. Otherwise the engine will not accept them.

There are two main files: config with the settings themselves and config-defaults with the defaults. But not only they need protection: the main file can include others with the include line, and the manual names them directly — “they may also contain sensitive information,” especially where signing keys are kept.

The commands are given literally: owner pmta:pmta, permissions 640. The owner matters as much as the permissions: the engine runs as the pmta user and must be able to read its configuration. A file with 640 permissions and owner root cannot be read by anyone but root — including the engine itself.

Secrets can be moved out of the file. The manual suggests keeping signing keys in Vault and referring to them from the configuration (vault-support); then the file holds a reference, not the key itself.

The main trap is protecting the main file and forgetting the included ones. The include line pastes in the contents of another file, and our whole layout rests on it: the sending address blocks with signing keys are in a separate file. 640 permissions on the main file with 644 on the included one protect nothing — the secrets are precisely in the second. The same goes for the key directory: a private key with correct permissions in a directory open for reading is reachable by name.
What the Hub does. It sets permissions itself, on every file delivery, and more strictly than the manual requires: the main file and the addresses file get owner pmta:pmta and permissions 640; the telemetry token file gets 600, only the engine reads it; the certificate for TLS submission gets the directory owner and 600; the signing key directory gets 700, and the keys inside it 600.

The operator has nothing to do: the Hub delivers the files, and their permissions are its responsibility, not the operator’s.

⚠️ One subtlety in our code. The owner change is executed with the error suppressed, and permissions are then set unconditionally. If the node has no pmta user — for example, the engine is not installed yet — the file will get permissions 640 and owner root, and the engine will not be able to read it later. This will surface not on delivery but at the first start.

Recommended values

What should end up on the node:

Path Owner Permissions
main configuration file pmta:pmta 640
file with address blocks pmta:pmta 640
signing key directory the engine directory’s owner 700
private keys inside the same 600
telemetry token file pmta:pmta 600

Permissions 640 are the manual’s minimum, not the ceiling. Anything read only by the engine itself and not needed by its group can happily live with 600. The difference matters where the node has other services in the same group.

On Windows the procedure is different: the system account is set as the owner, and the users group is denied read and write.

The permissions table and the advice about stricter permissions come from Hub practice. From the manual here come the composition of secrets in the file, the commands for Linux and Windows, the requirement to extend them to included files and the suggestion to move keys to Vault.
Example configuration
# permissions are set for the whole bundle, not for one file
chown pmta:pmta /etc/pmta/config /etc/pmta/config-defaults
chmod 640       /etc/pmta/config /etc/pmta/config-defaults
chown pmta:pmta /etc/pmta/virtualhost.txt && chmod 640 /etc/pmta/virtualhost.txtchmod 700       /etc/pmta/dkim && chmod 600 /etc/pmta/dkim/*
The highlighted line is about the included file, the very one holding the sending address blocks with signing keys. It is the easiest to forget, and it has more secrets than the main one.
yellow — — directive names, blue — blocks. They are highlighted the same way in the text of the card.
▲

Common mistakes and problems

what people get wrong and how it ends3 mistakes▶

Protecting the main file while leaving included ones open

§ 1.6.3

What it costsThe manual names included files separately: they hold private signing keys. In our setup the included file holds all the sending address blocks, that is, more secrets than the main one. Protecting the main file while the included one is open protects nothing.
How to spot itThe main file has 640 permissions, while the file with address blocks has 644 or the owner root.
How to do it rightSet permissions on the whole bundle at once, including the key directory. Find included files by searching for the include line, not from memory.

Setting permissions without checking the owner

§ 1.6.3

What it costsPermissions 640 without the pmta owner mean nobody but root can read the file — the engine included. The configuration looks protected, but the node does not start.
How to spot itAfter the permission change the engine does not start or starts with the previous configuration; the startup log shows an access denial.
How to do it rightChange the owner and permissions in one command and verify the result with a configuration parse, not by looking at the file list.

Locking down keys while leaving their directory open

§ 1.6.3

What it costsThe directory permissions decide whether file names in it can be seen. A private key with 600 permissions in a directory with 755 permissions is visible by name, and a key name usually contains the domain — that is, it reveals the list of domains the node signs for.
How to spot itThe key directory has 755 permissions, the files inside have 600.
How to do it rightLock down the whole directory with 700 permissions. That is exactly what the Hub does when issuing keys.
Compatibility
PowerMTA 6.0
described in the manual of this version · platform: any
The vendor does not state the first version for every directive — there is no note here. The name also appears in the 5.0 edition of the manual.
How to check on the node
Check both the permissions and that the engine still reads its configuration after the change:
ls -l /etc/pmta/config /etc/pmta/config-defaults /etc/pmta/virtualhost.txt
ls -ld /etc/pmta/dkim && ls -l /etc/pmta/dkim/
pmta test config
Expected: pmta:pmta and 640 on configuration files, 700 on the key directory and 600 inside it. The third command matters just as much: read permission is verified not by eye but by the engine having parsed the file.
In the Hub
Cost of a mistake
safe: a wrong value breaks nothing
Scenarios
Node security