-
Notifications
You must be signed in to change notification settings - Fork 0
GPG Remote is a client-server application allowing to delegate GnuPG private key operations to a remote server running in a trusted environment. Server filters client input according to specified rules, and runs GnuPG operations on behalf of a client.
License
sattva1/gpg-remote
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
GPG REMOTE ========== Motivation ---------- Using GnuPG in a networked environment always poses certain risk that a remote attacker who is able to compromise one of the client applications (e.g. MUA, IM client, etc.) could easily leak the private key by calling ``gpg --export-secret-keys``. A common mitigation of such risk are smartcards, however they are specialized hardware which a) may not be readily available, or b) could be not trusted for various of reasons. Overview -------- GPG Remote is a client-server application allowing to delegate GnuPG private key operations to a remote server running in a trusted environment. Server filters client input according to specified rules, and runs GnuPG operations on behalf of a client. GPG Remote separates GnuPG execution between a front-end client and a back-end server. The client tries to replicate GnuPG command line interface, taking up command line arguments and STDIN data. Internally, it then parses args input, figures out files which the user may want to process, packs all that into a request package, and sends it to the server. The server operating in a trusted environment is tasked to execute ``gpg`` in a safe manner. For this end it uses a whitelist of ``gpg`` command line options to filter out everything inappropriate of the received client command line arguments (especially, commands like ``--export-secret-keys``). Files received from the client are saved into temporary location, and their paths in command line arguments are updated accordingly. Finally, ``gpg`` is called, and its output (comprised of STDERR, STDOUT, exit code, as well as newly generates files) is sent back to client. Installation ------------ Make sure you have Python 3.3.x or later installed on all systems you plan to use for client and server operation. Both client and server modules are self-contained, and can be placed anywhere on the system. Running GPG Remote Client as a drop-in replacement for system-wide ``gpg`` requires ``gpgremote_client.py`` script to be moved to or symlinked from ``/usr/bin/gpg`` path. If both components are running on the same system, ensure only the server user has read-write access to GnuPG keyring files. In order to enable passphrase input over a network connection, follow these steps: 1. Make sure standard ``gpg`` ``pinentry`` application is installed on the client. 2. Install [``pyassuan``](https://pypi.python.org/pypi/pyassuan/) library on both client and server systems. 3. Ensure ``gpg-agent`` is properly configured and running on the server, and path to bundled GPG Remote ``pinentry.py`` is passed to ``gpg-agent`` using ``--pinentry-program`` option (see ``man gpg-agent`` for details). If "panic" rules support is required (see the corresponsing section below), install [``pbkdf2``](https://pypi.python.org/pypi/pbkdf2) Python module on the server system. Configuration ------------- The client reads configuration data (specifically, server listening host:port) from ``gpgremote_client.conf`` file located in ``~/.gnupg`` directory unless path is overridden with ``GNUPGHOME`` environment variable. By default server reads its configuration from ``gpgremote_server.conf`` file located in ``~/.gnupg`` (the path can be overridden with ``GNUPGHOME`` environment variable). However, specific path can be provided with ``-c``/``--config`` option to server invocation. Most server parameters can be reconfigured from the command line as well (``-h``/``--help`` will print all available options). Whitelist --------- The second part of server configuration is ``gpg`` options whitelist defined in ``whitelist.conf`` in the same directory as server config file. The syntax is simple, yet configuring the whitelist correctly is critical to server security (see _Security considerations_ section for details). 1. Lines not starting with a dash sign are ignored. 2. A single set of options per line. 3. A set is either a single option (in long or short form), or a space-separated long and short forms (in arbitrary amount and order). 4. Non dash-prefixed words in a set (if any) have special meaning: * A bracketed word is considered a wildcard parameter turning options in a set into parameterized ones. * An unbracketed word is a whitelisted parameter value, and, as such, options in a set can be passed with this value only. Multiple whitelisted values must be provided on the same line (quoting / space-escaping is supported). * If a word is bracketed ``#NO_FILES``, it means no files should be expected in arguments list for this options set (see _Security considerations_ section below). One-time passwords ------------------ An extra security measure for private key protection is _one-time passwords (OTP)_. When enabled, it will require user to enter a short random string (from a pre-generated list) along with private key passphrase. This will thwart adversary's attempts to use private key over GPG Remote by passing a sniffed passphrase. (Please note: bundled GPG Remote ``pinentry.py`` must be used, see _Installation_ section above for detailed requirements.) To use this feature, enable it in server configuration file or with ``--otp`` startup option. Then run the server with ``--gen-otp`` option and enter the number of one-time passwords to generate. The longer the list, the later it will has to be replenished, but the wider will be the window of opportunity for the attacker if OTP list gets compromised. Note: the list can be regenerated at any moment, and any passwords left in it will be invalidated; regenerating OTP list does not requires server restart. Once OTP is enabled, a next password from the list will be required each time a private key passphrase is prompted - an OTP must be appended to the end of passphrase (without spaces or other delimiters). An entered OTP is invalidated, i.e. if the passphrase is mistyped, the next OTP will be requested on each retry. Once OTP list is depleted, any private key operation will fail until the new list is generated. Please note that ``gpg-agent`` passphrase caching bypasses OTP: while the passphrase is cached, the key could be used without user interaction. "Panic" rules ------------- It is possible to configure an arbitrary amount of so called "panic" rules. These rules can be used to execute specific shell commands on the server in the event a predefined passphrase is entered in ``pinentry`` dialog. (Please note: bundled GPG Remote ``pinentry.py`` must be used, see _Installation_ section above for detailed requirements.) Each rule is specified as an entry in the server configuration file. Entry name must begin with ``panic_`` prefix followed by a unique name. Entry value consits of a space-sperated security token and shell command(s) in regular notation (i.e. no quoting or escaping is necessary), or a special command (see below). Security token is a PBKDF2 hash string generated from a passphrase that should trigger a specific rule. Running server with ``--gen-token`` option will help to generate a token for a particular passphrase. A single passphrase can trigger any amount of rules if all of them use the same passphrase protection (but not necesserily the same token literatim). A triggered command is silently executed by the server-side ``pinentry`` process with access permissions of ``gpg-agent`` parent user prior to resending the entered passphrase to ``gpg-agent``. Matched rules are executed in the order they are defined in the configuration file. The following environment variables are passed to "panic" shell commands: * ``GPG_REMOTE_PID``: PID of the GPG Remote server process. * ``GPG_REMOTE_KEYRINGS``: Space-separated list of paths to non-empty ``gpg`` keyring files. The following special commands may be used instead of shell commands in "panic" rule definitions. Please note that a single special command only can be specified for any rule: * ``STOP``: Stop GPG Remote server gracefully. Server will send the client a general error message, finish processing of any concurrent requests, clean up all the data received from the client, and exit. * ``KILL``: Terminate GPG Remote server immediately. Server will send ``SIGKILL`` signal to itself without performing any cleanup procedures. Please take into account that ``gpg-agent`` reads the private key in memory _before_ spawning ``pinentry``, and simply running ``rm``/``wipe`` to delete private keyring files will not destroy the key immediately - it is necessary to terminate the running GPG Remote server process (using ``STOP`` or ``KILL`` special commands) to prevent sending ``gpg`` operation results back to the client. Use rules chaining (by assigning the same security token / passphrase to multiple rules) to run multiple commands when needed. Security considerations ----------------------- Communication channel authentication/encryption is out of the scope of this application. The user may employ SSH or VPN tunnelling to create a trusted channel for client-server communication. The threat model and main attack scenario is a client-side remote attacker (e.g. compromised network application) exfiltrating ``gpg`` private keys. The server mitigates this risk by using ``gpg`` command line options whitelist. Note that even if keyring modifying options (e.g. ``--delete-key``, ``--import``) are not whitelisted, client user would still be able to add keys to the keyring by simply sending them to STDIN (``gpg`` processes it contextually). If this should be avoided, it's up to the server administrator to run the server as a user without write access to ``gpg`` keyring files. Remember that default ``gpg`` keyrings can be overridden with ``--no-default-keyring``, ``--secret-keyring`` and ``--keyring`` options. Another potential risk to the server is its local files exfiltration. In the naive case the user could ask the server to run ``gpg -o - --enarmor [path_to_local_file]``, and the server would happily send that file contents in STDOUT. In order to protect against such attacks the server makes sure the number of filename arguments is equal to the number of files received in client request package. (These complications are necessary as simply refusing to process requests containing server local filepaths would lead to information leakage about server filesystem contents.) However, it requires correct configuration of the server whitelist in respect to options parameter specification: in case an option accepts parameters, its set MUST include parameter wildcard/value, otherwise the server might become vulnerable to the described attack. Note also that a number of ``gpg`` command line options (namely, ``--list-keys``, ``--list-sigs``, etc.) receive arbitrary amount of non-file arguments. This case is supported with special ``[#NO_FILES]`` placeholder. If such an option is provided by the client, the server strips out any ``-o``/``--output`` options, and prevents sending any files back to the client. Files received from the client (which may contain sensitive cleartext data) are written by the server to a temporary location. By default it is a system-wide temp directory (commonly, ``/tmp``), but in case this directory is unsafe, it can be overridden using ``TEMP`` environment variable, or ``--temp`` command line option for server invocation. (Note that files aren't written directly to tempdir, but to temporary subdirectories with 0700 access mode, i.e. accessable only by GPG Remote server user). As neither client nor server employ any semantic analysis of command line arguments (i.e. does not understand the meaning of options and commands), the client assumes an option parameter or trailing argument named as an existing client local file to be a file indended for ``gpg`` processing, and optimistically sends it to the server. Note that client unconditionally writes out all files received from the server (on the assumption it has write access to a given path) without asking for overwrite if the same file exist. The client may try to cause DoS on the server by sending it excessively huge input(s). This scenario is addressed with server resources management parameters: ``size_limit``, ``threads`` and ``queue``. The first limits the size of client request package and, as a result, memory usage. The second limits the number of CPU threads used for requests processing (each request is single-threaded). Note that the total amount of RAM the server might use is around ``S * T * 2``, where ``S`` is the package size limit and ``T`` is the threads count (i.e. with default ``S=1GB`` and ``T=2`` maximum RAM usage would be 4 GB). Finally, the ``queue`` value is the amount of requests that can be queued for processing. If the value is higher than threads count then the remaining requests will wait until active ones are finished. Awaiting requests does not take up additional resources except for a socket connection. When remote passphrase input is used, an entered passphrase never touches long-lived server process memory. However it remains in the client memory for the whole duration of ``gpg`` execution, and during that period it's subject to a risk of memory swapping. Make sure client swapping device is encrypted, disabled, or other protective measures are employed. One-time passwords (OTP) is a mere access control mechanism enforced by GPG Remote. As such, it does not affects any cryptographic material, and must not be expected to deliver specific cryptographic properties, e.g. PFS. If "panic" rules are configured on the server with high hashing iterations count, an adversary can potentially deduce this fact from a delay of ``gpg`` output as user passphrase must be matched to each unique security token. It is also possible to detect "panic" rules execution if the executed command takes a long time to complete. Technical details ----------------- Communication protocol is a simple two-step request-response. Package format for both directions is as follows: ``<len_p> | <len_j> | JSON(<header>, <type>, <fields>, <files_meta>) | [binary]`` * ``len_p`` (8 bytes): Overall package length. * ``len_j`` (8 bytes): JSON packet length. * ``header`` (list): ``auth`` token (optional) and application ``version``. * ``type`` (str): Package identifier. * ``fields`` (list): Arbitrary data fields. * ``files_meta`` (dict): ``File_pathname->file_len`` mapping. * ``binary`` (bytes): Concatenated files data (optional). If authentication token is provided, it is expected to be a HMAC-SHA256 hex digest of all JSON-packed metadata, and is calculated as follows: the metadata elements (except for ``auth``) are packed as a flat list in the above mentioned order into a JSON-encoded string, which is passed to HMAC context. Authentication is currently used for server<>``pinentry`` IPC only. As such, binary data is not authenticated. Remote passphrase input is implemented using custom ``pinentry.py`` shim application. It employs the following communication steps (``pinentry`` actor below is the custom ``pinentry.py`` shim application unless otherwise stated): 1. ``server>gpg-agent``: uses ``PINENTRY_USER_DATA`` environment variable (which is passed over ``gpg > gpg-agent > pinentry`` execution stack) to provide ``pinentry`` with IPC communication details including IPC socket and session authentication key. 2. ``gpg-agent>pinentry``: calls ``pinentry`` with ``PINENTRY_USER_DATA`` environment variable and initiates Assuan protocol. 3. ``pinentry>server``: initiates IPC protocol and asks for client network connection data. 4. ``server>pinentry``: sends opened client network socket directly to the custom ``pinentry`` over IPC channel (UNIX socket). 5. ``pinentry>client``: uses the provided network connection to send the client all the required ``pinentry`` data (text strings and startup parameters) got from ``gpg-agent`` at step 2. 6. ``client``: runs standard ``pinentry`` to aquire user response data (in the form of Assuan protocol response). 7. ``client>pinentry``: sends user response data. 8. ``pinentry``: executes "panic" commands if any are triggered by the client passphrase. 9. ``pinentry>gpg-agent``: replays user response in Assuan protocol exchange. If wrong passphrase is entered, steps 2-9 are performed again up to the number of retries required by ``gpg-agent``. It should be noted that although IPC UNIX socket (used for server<>``pinentry`` communication) access is not resticted (in order to allow running server and ``gpg-agent`` under different users), server verifies authenticity of packages received using the session auth key it provides ``pinentry`` process with. The one-time passwords (OTP) list is stored on the server in plaintext. Each line of OTP list file is a colon-delimited ID number and the actual password string. "Panic" rules security token is a PBKDF2[SHA-1, HMAC] output with effective entropy limit of 256 bits. 64-bit salt value is used. Token format is a string of colon-delimited Base62-encoded elements: iterations count (in bytes representation), salt, hash. Default server listening port (29797) was produced in Python as follows: ``` #!python int.from_bytes(b'gpgremote', 'big') % 2 ** 16 ``` (Although it has been noted only ``b'te'`` bytestring has any effect in such procedure.) Issues, limitations ------------------- * Interactive console UI operations (e.g. key generation, key edit, etc.) are not supported. * Client does not support reading input from TTY, data must be piped to STDIN. * Passing file descriptors and implementing other forms of advanced IPC interaction with ``gpg`` is not supported. * No environment variables are passed from the client. If ``gpg`` must be invoked with specific environment (e.g. ``LANG``), start GPG Remote Server with all the necessary variables instead. * If GnuPG 2.x or higher is used without custom Pinentry, secret key operations would spawn standard Pinentry dialog on the server side which will prevent ``gpg`` process from terminating. This might be a feature if both GPG Remote server and client are running on the same system, otherwise it's up to the server administrator to disable ``gpg-agent`` server-side (for example, by downgrading to GnuPG 1.4.x or starting ``gpg-agent`` with ``--batch`` option). ToDo ---- * One-time passwords support. * Minimize memory footprint. Version history --------------- * 2015-03-18 - ``v1.3`` - Added support for one-time passwords. - Fixed a case with pinentry and stdin pipe. * 2015-03-16 - ``v1.2`` - Passphrase confirmation while generating "panic" security token. - Minor aesthetic and code documentation cleanups. - First stable release. * 2015-02-17 - ``v1.2b`` - Updated minimum Python version requirement to 3.3 (it was mistakenly lower). - Raised default logging verbosity to info level. - Matched "panic" rules are executed in the defined order. - New "panic" rules security token format replacing ``crypt(3)`` one. Output length limit is 256 bits now instead of 192 bits. - Optimized security token matching scheme (speed-wise) if the same token is used for multiple rules. - Changed Server<>Pinentry IPC interface and protocol. - Set IPC message size limit to 64 KB (was a possible DoS scenario). - Special "panic" commands to properly terminate server. - Fixed IPC socket permissions which prevented running server and ``gpg-agent`` under different users. - Fixed error handling if ``gpg`` executable cannot be found. - Code cleanup and reorganization. * 2015-02-06 - ``v1.1b1`` - Fixed 'ttyname' Assuan option update on the client side. - Honour PINENTRY_USER_DATA="USE_CURSES=1" environment variable. - Support for "panic" commands. * 2015-02-05 - ``v1.0b1`` - Graceful server shutdown on SIGTERM. - Custom Pinentry to support passphrase input over a network. - Updated timeout defaults to make them compatible with passphrase input. - Code cleanup. * 2015-01-27 - ``v0.9b2`` - Fixed ``--output -`` case. - Versioned protocol. - Config parser updates. - More unittest coverage. - ``README`` file updates. * 2015-01-23 - ``v0.9b1`` - First beta release. License ------- See ``COPYING``. Author ------ Vlad "SATtva" Miller [email protected] http://vladmiller.info ``0x8443620A``
About
GPG Remote is a client-server application allowing to delegate GnuPG private key operations to a remote server running in a trusted environment. Server filters client input according to specified rules, and runs GnuPG operations on behalf of a client.