passbolt/passbolt_api · critical · Cake\Http\Exception\InternalErrorException

The OpenPGP server key defined in the config cannot be…

Error message

The OpenPGP server key defined in the config cannot be opened.

What it means

After confirming the configured private key file exists, importServerKeyInKeyring reads it with file_get_contents(). If that returns false (the file exists but cannot be read/opened by the process), this InternalErrorException is thrown. It signals a filesystem-level read failure rather than a missing or malformed key.

Solutions

  1. Chown/chmod the key file so the web server user can read it: `chown www-data:www-data <keyfile> && chmod 0400 <keyfile>`.
  2. Check `ls -l` on the file and each parent directory for execute/read permission for the PHP user.
  3. If open_basedir is set, add the key's directory to it in php.ini and restart PHP-FPM/Apache.
  4. Verify the path is a regular file (`file <keyfile>`), not a directory or broken symlink target.

Example fix

// before
-rw------- root root /etc/passbolt/serverkey_private.asc
// after
chown www-data:www-data /etc/passbolt/serverkey_private.asc && chmod 0400 /etc/passbolt/serverkey_private.asc
Defensive patterns

Strategy: validation

Validate before calling

$keyPath = Configure::read('passbolt.gpg.serverKey.private');
if (!is_readable($keyPath)) {
    throw new \RuntimeException("Server key exists but is not readable by user " . get_current_user());
}

Try / catch

try {
    $backend->importServerKeyInKeyring($fingerprint, $keyPath);
} catch (\Cake\Http\Exception\InternalErrorException $e) {
    if (str_contains($e->getMessage(), 'cannot be opened')) {
        clearstatcache(true, $keyPath); // diagnose permissions before retry
    }
    throw $e;
}

Prevention

When it happens

Trigger: file_get_contents($keyFilePath) === false during importServerKeyInKeyring: permission denied on the file, unreadable directory, open_basedir restriction, or the path is a directory/unreadable special file.

Common situations: Key file owned by root with 0600 while PHP runs as www-data; restrictive SELinux/AppArmor policy; open_basedir in php.ini excluding the key path; Docker volume mounted with wrong ownership.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17). Data as JSON: /api/errors/501331edfa7b78e7. Report an issue: GitHub.

Appendix: source

Thrown at src/Utility/OpenPGP/OpenPGPBackend.php:101

     */
    public function importServerKeyInKeyring(): void
    {
        $fingerprint = Configure::read('passbolt.gpg.serverKey.fingerprint');
        $keyFilePath = Configure::read('passbolt.gpg.serverKey.private');

        // If it's not in keyring try to import it
        // Check if file containing the private key exist
        if ($keyFilePath === null) {
            throw new InternalErrorException('The secret key file is not defined.');
        }
        if (!file_exists($keyFilePath)) {
            $msg = __('The OpenPGP server key defined in the config is not found in the file system.');
            throw new InternalErrorException($msg);
        }
        $privateKey = file_get_contents($keyFilePath);
        if ($privateKey === false) {
            $msg = __('The OpenPGP server key defined in the config cannot be opened.');
            throw new InternalErrorException($msg);
        }
        if (!$this->isParsableArmoredPrivateKey($privateKey)) {
            $msg = __('The OpenPGP server key defined on file is not a valid private key.');
            throw new InternalErrorException($msg);
        }

        // try to import it
        $this->importKeyIntoKeyring($privateKey);
        if (!$this->isKeyInKeyring($fingerprint)) {
            $msg = __('There is an issue with the OpenPGP server key.') . ' ';
            $msg .= __('The fingerprint does not match the one associated with the key on file.');
            throw new InternalErrorException($msg);
        }
    }

    /**
     * Check if a message is valid.
     *

View on GitHub (pinned to 31c1bbc10f)