Billionmail/BillionMail · error

Failed to change DKIM private key permissions: %v

Error message

Failed to change DKIM private key permissions: %v

What it means

After generating keys, getDKIMRecordWithKeySize runs os.Chmod(dkimPriPath, 0644) so Rspamd can read the private key. This error wraps a chmod failure. Without readable permissions, DKIM signing will fail later even though the keys exist.

Source

Thrown at core/internal/service/domains/domains.go:571

		var res *v2.ExecResult
		res, err = dk.ExecCommandByName(context.Background(), consts.SERVICES.Rspamd, []string{"rspamadm", "dkim_keygen", "-s", selector, "-b", fmt.Sprintf("%d", keySize), "-d", domain, "-k", fmt.Sprintf("/var/lib/rspamd/dkim/%s/%s.private", domain, selector)}, "root")
		if err != nil {
			err = fmt.Errorf("Failed to generate DKIM key pair: %v", err)
			return
		}

		if res != nil {
			_, err = public.WriteFile(dkimPubPath, res.Output)
			if err != nil {
				err = fmt.Errorf("Failed to write DKIM public key: %v", err)
				return
			}
		}

		// update dkim private key file permission to 0644
		err = os.Chmod(dkimPriPath, 0644)
		if err != nil {
			err = fmt.Errorf("Failed to change DKIM private key permissions: %v", err)
			return
		}

		// Skip DKIM signing config for relay-mapped domains — relay provider signs
		relayDomains, relayErr := GetRelayDomains(context.Background())
		if relayErr != nil {
			g.Log().Warning(context.Background(), "Failed to check relay domains for DKIM signing:", relayErr)
			relayDomains = make(map[string]bool)
		}

		if !relayDomains[domain] {
			// build DKIM Sign config
			signConf := fmt.Sprintf(`
#%s_DKIM_BEGIN
%s {
   selectors [
    {
      path: "/var/lib/rspamd/dkim/%s/default.private";

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Check the wrapped error for EPERM/EACCES and run the process with rights over the dkim directory, or chown the files to the service user
  2. Confirm dkimPriPath exists (a prior generation failure would surface here) and points at the rspamd volume mount
  3. Verify the rspamd container user can read 0644 files on that volume
  4. Retry GetDKIMRecord after fixing mount/permission issues

Example fix

// before
err = os.Chmod(dkimPriPath, 0644)
if err != nil { err = fmt.Errorf("Failed to change DKIM private key permissions: %v", err); return }
// after
if _, statErr := os.Stat(dkimPriPath); statErr != nil {
    err = fmt.Errorf("DKIM private key missing before chmod: %v", statErr)
    return
}
if err = os.Chmod(dkimPriPath, 0644); err != nil {
    err = fmt.Errorf("Failed to chmod DKIM private key %s (check volume ownership): %v", dkimPriPath, err)
    return
}
Defensive patterns

Strategy: validation

Validate before calling

info, err := os.Stat(dkimPriPath)
if err != nil { return fmt.Errorf("private key missing: %v", err) }
if info.Mode().Perm() == 0644 { return nil } // already correct

Try / catch

if err := os.Chmod(dkimPriPath, 0644); err != nil {
    if errors.Is(err, os.ErrPermission) {
        logger.Warnf(ctx, "cannot chmod %s (not owner); signing may fail", dkimPriPath)
    }
    return fmt.Errorf("Failed to change DKIM private key permissions: %v", err)
}

Prevention

When it happens

Trigger: getDKIMRecordWithKeySize when os.Chmod on the freshly generated private key fails — file owned by another uid (rspamd container user vs host process), read-only mount, or the private path doesn't exist because generation silently failed earlier.

Common situations: Host process runs as non-root while key files are root-owned; DKIM volume mounted with restrictive options; AppArmor/SELinux denials; race where keygen wrote to a container-internal path not mapped to dkimPriPath.

Related errors


AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05). Data as JSON: /api/errors/84fcf7aebe866e8e. Report an issue: GitHub.