calcurse-caldav: Support PasswordCommand option

This commit adds a new `Auth/PasswordCommand` option
to support security best practices re: handling secrets
in CLI program configuration.

Prior to this commit, the two available options
for specifying a password were:

  1. via the `Auth/Password` config parameter, or
  2. via a `$CALCURSE_CALDAV_PASSWORD` environment variable.

The former is unsafe for obvious reasons;
the latter is unsafe because as long as the script is running,
its environment can be accessed via

    $ cat /proc/<pid>/environ

and is thus visible to anyone with access to the system.

This commit preserves preexisting behavior (for backward compatibility)
but removes all mention of option 2 from the README.
Since the README example for option 2 used a password command anyway,
there is little reason to continue its use,
and this commit recommends it be deprecated.

Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
This commit is contained in:
Ryan Lue 2022-06-30 23:04:35 -07:00 committed by Lukas Fleischer
parent 4cd300f2c4
commit e772c4b6d5
3 changed files with 24 additions and 14 deletions

View File

@ -34,13 +34,12 @@ argument. You can choose between the following initialization modes:
For subsequent calcurse-caldav invocations, you don't need to specify any
additional parameters.
You can specify a username and password for basic authentication in the
config file. Alternatively, the password can be passed securely from another
program (such as *pass*) via the `CALCURSE_CALDAV_PASSWORD` environment variable like
so:
```
CALCURSE_CALDAV_PASSWORD=$(pass show calcurse) calcurse-caldav
```
Specify your HTTP Basic authentication credentials under the config file's
`Auth` section. The most secure approach is to save your password in a CLI
encrypted password store (_e.g.,_ [pass](https://www.passwordstore.org/)), and
then set `PasswordCommand` to the shell command used to retrieve it.
If security is not a priority, you may store your password in plain text
instead.
Hooks
-----

View File

@ -6,6 +6,7 @@ import configparser
import os
import pathlib
import re
import shlex
import subprocess
import sys
import textwrap
@ -30,6 +31,7 @@ class Config:
self._map = {
'Auth': {
'Password': None,
'PasswordCommand': None,
'Username': None,
},
'CustomHeaders': {},
@ -657,9 +659,6 @@ verbose = args.verbose
debug = args.debug
debug_raw = args.debug_raw
# Read environment variables
password = os.getenv('CALCURSE_CALDAV_PASSWORD')
# Read configuration.
config = Config(configfn)
@ -674,7 +673,17 @@ path = config.get('General', 'Path')
sync_filter = config.get('General', 'SyncFilter')
verbose = verbose or config.get('General', 'Verbose')
password = password or config.get('Auth', 'Password')
if os.getenv('CALCURSE_CALDAV_PASSWORD'):
# This approach is deprecated, but preserved for backwards compatibility
password = os.getenv('CALCURSE_CALDAV_PASSWORD')
elif config.get('Auth', 'Password'):
password = config.get('Auth', 'Password')
elif config.get('Auth', 'PasswordCommand'):
tokenized_cmd = shlex.split(config.get('Auth', 'PasswordCommand'))
password = subprocess.run(tokenized_cmd, capture_output=True).stdout.decode('UTF-8')
else:
password = None
username = config.get('Auth', 'Username')
client_id = config.get('OAuth2', 'ClientID')

View File

@ -48,11 +48,13 @@ DryRun = Yes
# Enable this if you want detailed logs written to stdout.
Verbose = Yes
# Credentials for HTTP Basic Authentication. Leave this commented out if you do
# not want to use authentication.
# Credentials for HTTP Basic Authentication (if required).
# Set `Password` to your password in plaintext (unsafe),
# or `PasswordCommand` to a shell command that retrieves it (recommended).
#[Auth]
#Username = user
#Password = pass
#Password = password
#PasswordCommand = pass baikal
# Optionally specify additional HTTP headers here.
#[CustomHeaders]