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:
parent
4cd300f2c4
commit
e772c4b6d5
@ -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
|
For subsequent calcurse-caldav invocations, you don't need to specify any
|
||||||
additional parameters.
|
additional parameters.
|
||||||
|
|
||||||
You can specify a username and password for basic authentication in the
|
Specify your HTTP Basic authentication credentials under the config file's
|
||||||
config file. Alternatively, the password can be passed securely from another
|
`Auth` section. The most secure approach is to save your password in a CLI
|
||||||
program (such as *pass*) via the `CALCURSE_CALDAV_PASSWORD` environment variable like
|
encrypted password store (_e.g.,_ [pass](https://www.passwordstore.org/)), and
|
||||||
so:
|
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
|
||||||
CALCURSE_CALDAV_PASSWORD=$(pass show calcurse) calcurse-caldav
|
instead.
|
||||||
```
|
|
||||||
|
|
||||||
Hooks
|
Hooks
|
||||||
-----
|
-----
|
||||||
|
@ -6,6 +6,7 @@ import configparser
|
|||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
import re
|
import re
|
||||||
|
import shlex
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import textwrap
|
import textwrap
|
||||||
@ -30,6 +31,7 @@ class Config:
|
|||||||
self._map = {
|
self._map = {
|
||||||
'Auth': {
|
'Auth': {
|
||||||
'Password': None,
|
'Password': None,
|
||||||
|
'PasswordCommand': None,
|
||||||
'Username': None,
|
'Username': None,
|
||||||
},
|
},
|
||||||
'CustomHeaders': {},
|
'CustomHeaders': {},
|
||||||
@ -657,9 +659,6 @@ verbose = args.verbose
|
|||||||
debug = args.debug
|
debug = args.debug
|
||||||
debug_raw = args.debug_raw
|
debug_raw = args.debug_raw
|
||||||
|
|
||||||
# Read environment variables
|
|
||||||
password = os.getenv('CALCURSE_CALDAV_PASSWORD')
|
|
||||||
|
|
||||||
# Read configuration.
|
# Read configuration.
|
||||||
config = Config(configfn)
|
config = Config(configfn)
|
||||||
|
|
||||||
@ -674,7 +673,17 @@ path = config.get('General', 'Path')
|
|||||||
sync_filter = config.get('General', 'SyncFilter')
|
sync_filter = config.get('General', 'SyncFilter')
|
||||||
verbose = verbose or config.get('General', 'Verbose')
|
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')
|
username = config.get('Auth', 'Username')
|
||||||
|
|
||||||
client_id = config.get('OAuth2', 'ClientID')
|
client_id = config.get('OAuth2', 'ClientID')
|
||||||
|
@ -48,11 +48,13 @@ DryRun = Yes
|
|||||||
# Enable this if you want detailed logs written to stdout.
|
# Enable this if you want detailed logs written to stdout.
|
||||||
Verbose = Yes
|
Verbose = Yes
|
||||||
|
|
||||||
# Credentials for HTTP Basic Authentication. Leave this commented out if you do
|
# Credentials for HTTP Basic Authentication (if required).
|
||||||
# not want to use authentication.
|
# Set `Password` to your password in plaintext (unsafe),
|
||||||
|
# or `PasswordCommand` to a shell command that retrieves it (recommended).
|
||||||
#[Auth]
|
#[Auth]
|
||||||
#Username = user
|
#Username = user
|
||||||
#Password = pass
|
#Password = password
|
||||||
|
#PasswordCommand = pass baikal
|
||||||
|
|
||||||
# Optionally specify additional HTTP headers here.
|
# Optionally specify additional HTTP headers here.
|
||||||
#[CustomHeaders]
|
#[CustomHeaders]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user