Load systemd encrypted credentials
This commit is contained in:
parent
a4b65cf710
commit
4e833e147a
2
NEWS
2
NEWS
@ -1,4 +1,6 @@
|
||||
1.8.15.dev0
|
||||
* #914: Fix a confusing apparent hang when when the repository location changes, and instead
|
||||
show a helpful error message.
|
||||
* #919: Clarify the command-line help for the "--config" flag.
|
||||
* #919: Document a policy for versioning and breaking changes:
|
||||
https://torsion.org/borgmatic/docs/how-to/upgrade/#versioning-and-breaking-changes
|
||||
|
@ -39,8 +39,7 @@ def make_environment(config):
|
||||
environment_variable_name,
|
||||
) in DEFAULT_BOOL_OPTION_TO_DOWNCASE_ENVIRONMENT_VARIABLE.items():
|
||||
value = config.get(option_name)
|
||||
if value is not None:
|
||||
environment[environment_variable_name] = 'yes' if value else 'no'
|
||||
environment[environment_variable_name] = 'yes' if value else 'no'
|
||||
|
||||
for (
|
||||
option_name,
|
||||
|
@ -611,6 +611,9 @@ def log_record(suppress_log=False, **kwargs):
|
||||
return record
|
||||
|
||||
|
||||
BORG_REPOSITORY_ACCESS_ABORTED_EXIT_CODE = 62
|
||||
|
||||
|
||||
def log_error_records(
|
||||
message, error=None, levelno=logging.CRITICAL, log_command_error_output=False
|
||||
):
|
||||
@ -651,6 +654,13 @@ def log_error_records(
|
||||
)
|
||||
|
||||
yield log_record(levelno=levelno, levelname=level_name, msg=str(error))
|
||||
|
||||
if error.returncode == BORG_REPOSITORY_ACCESS_ABORTED_EXIT_CODE:
|
||||
yield log_record(
|
||||
levelno=levelno,
|
||||
levelname=level_name,
|
||||
msg='\nTo work around this, set either the "relocated_repo_access_is_ok" or "unknown_unencrypted_repo_access_is_ok" option to "true", as appropriate.',
|
||||
)
|
||||
except (ValueError, OSError) as error:
|
||||
yield log_record(levelno=levelno, levelname=level_name, msg=str(message))
|
||||
yield log_record(levelno=levelno, levelname=level_name, msg=str(error))
|
||||
|
@ -232,7 +232,11 @@ properties:
|
||||
passcommand/repokey/keyfile encryption. Note that if both
|
||||
encryption_passcommand and encryption_passphrase are set, then
|
||||
encryption_passphrase takes precedence. Defaults to not set.
|
||||
This can also be used to access encrypted systemd service
|
||||
credentials (stored as "/etc/credstore.encrypted/borgpw", or as
|
||||
files in "/etc/credstore.encrypted/borg/").
|
||||
example: "secret-tool lookup borg-repository repo-name"
|
||||
example: "cat ${CREDENTIALS_DIRECTORY}/borgpw"
|
||||
encryption_passphrase:
|
||||
type: string
|
||||
description: |
|
||||
|
@ -29,6 +29,38 @@ For example, to ask the *Pass* password manager to provide the passphrase:
|
||||
encryption_passcommand: pass path/to/borg-repokey
|
||||
```
|
||||
|
||||
### Using systemd service credentials
|
||||
|
||||
Borgmatic supports using encrypted [credentials](https://systemd.io/CREDENTIALS/).
|
||||
|
||||
Save your password as an encrypted credential to `/etc/credstore.encrypted/borgpw`, e.g.,
|
||||
|
||||
```
|
||||
# systemd-ask-password -n | systemd-creds encrypt - /etc/credstore.encrypted/borgpw
|
||||
```
|
||||
|
||||
Note that the name `borgpw` is hardcoded in the systemd service file.
|
||||
|
||||
|
||||
If you use multiple different passwords, save them as encrypted credentials to `/etc/credstore.encrypted/borg/`, e.g.,
|
||||
|
||||
```
|
||||
# mkdir /etc/credstore.encrypted/borg
|
||||
# systemd-ask-password -n | systemd-creds encrypt --name=borg_backupserver1 - /etc/credstore.encrypted/borg/backupserver1
|
||||
# systemd-ask-password -n | systemd-creds encrypt --name=borg_pw2 - /etc/credstore.encrypted/borg/pw2
|
||||
...
|
||||
```
|
||||
Ensure that the file names, (e.g. `backupserver1`) match the corresponding part of
|
||||
the `--name` option *after* the underscore (_). The `borg` folder is hardcoded in the systemd service file.
|
||||
|
||||
Then uncomment or use one of the following in your configuration file. Adjust `borg_backupserver1`
|
||||
according to the name given to the credential.
|
||||
|
||||
```yaml
|
||||
encryption_passcommand: "cat ${CREDENTIALS_DIRECTORY}/borgpw"
|
||||
encryption_passcommand: "cat ${CREDENTIALS_DIRECTORY}/borg_backupserver1"
|
||||
```
|
||||
|
||||
### Environment variable interpolation
|
||||
|
||||
<span class="minilink minilink-addedin">New in version 1.6.4</span> borgmatic
|
||||
|
@ -9,6 +9,10 @@ ConditionACPower=true
|
||||
[Service]
|
||||
Type=oneshot
|
||||
|
||||
# Load encrypted credentials.
|
||||
LoadCredentialEncrypted=borg:/etc/credstore.encrypted/borg/
|
||||
LoadCredentialEncrypted=borgpw
|
||||
|
||||
# Security settings for systemd running as root, optional but recommended to improve security. You
|
||||
# can disable individual settings if they cause problems for your use case. For more details, see
|
||||
# the systemd manual: https://www.freedesktop.org/software/systemd/man/systemd.exec.html
|
||||
|
@ -19,11 +19,15 @@ def test_make_environment_with_ssh_command_should_set_environment():
|
||||
assert environment.get('BORG_RSH') == 'ssh -C'
|
||||
|
||||
|
||||
def test_make_environment_without_configuration_should_not_set_environment():
|
||||
def test_make_environment_without_configuration_sets_certain_environment_variables():
|
||||
environment = module.make_environment({})
|
||||
|
||||
# borgmatic always sets this Borg environment variable.
|
||||
assert environment == {'BORG_EXIT_CODES': 'modern'}
|
||||
assert environment == {
|
||||
'BORG_EXIT_CODES': 'modern',
|
||||
'BORG_RELOCATED_REPO_ACCESS_IS_OK': 'no',
|
||||
'BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK': 'no',
|
||||
}
|
||||
|
||||
|
||||
def test_make_environment_with_relocated_repo_access_true_should_set_environment_yes():
|
||||
|
@ -1168,6 +1168,24 @@ def test_log_error_records_generates_output_logs_for_called_process_error_with_s
|
||||
assert any(log for log in logs if 'error output' in str(log))
|
||||
|
||||
|
||||
def test_log_error_records_generates_work_around_output_logs_for_called_process_error_with_repository_access_aborted_exit_code():
|
||||
flexmock(module).should_receive('log_record').replace_with(dict).times(4)
|
||||
flexmock(module.logger).should_receive('getEffectiveLevel').and_return(logging.WARNING)
|
||||
|
||||
logs = tuple(
|
||||
module.log_error_records(
|
||||
'Error',
|
||||
subprocess.CalledProcessError(
|
||||
module.BORG_REPOSITORY_ACCESS_ABORTED_EXIT_CODE, 'ls', 'error output'
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
assert {log['levelno'] for log in logs} == {logging.CRITICAL}
|
||||
assert any(log for log in logs if 'error output' in str(log))
|
||||
assert any(log for log in logs if 'To work around this' in str(log))
|
||||
|
||||
|
||||
def test_log_error_records_splits_called_process_error_with_multiline_ouput_into_multiple_logs():
|
||||
flexmock(module).should_receive('log_record').replace_with(dict).times(4)
|
||||
flexmock(module.logger).should_receive('getEffectiveLevel').and_return(logging.WARNING)
|
||||
|
Loading…
x
Reference in New Issue
Block a user