Compare commits

..

2 Commits

7 changed files with 203 additions and 2 deletions

View File

@ -4,6 +4,10 @@
Roles for deploying matrix infrastructure using ansible.
## Modules
- [`synapse_signing_key`](plugins/modules/synapse_signing_key.md): Module for managing / generating synapse signing keys
## Roles
- [`cinny`](roles/cinny/README.md): [Cinny](https://cinny.in/) Web Client

View File

@ -0,0 +1,33 @@
# `finallycoffee.matrix.synapse_signing_key` module
Module to generate and manage synapse signing keys.
> [!TIP]
> Supports `check mode` and `diff` rendering
## Requirements
- `python >= 3.9`
- `signed_json >= 1.1.4`
## Usage examples
```yaml
# Generate a key
- finallycoffee.matrix.synapse_signing_key:
path: "/not/there/yet/signing.key"
state: present
# Read a key from the filesystem or generate one
- finallycoffee.matrix.synapse_signing_key:
path: "/maybe/existing/signing/key"
register: key_result
- debug:
msg: "Signing key is '{{ key_result.signing_key }}'"
# Delete an existing signing key file
- finallycoffee.matrix.synapse_signing_key:
path: "/path/to/key/to/delete.key"
state: absent
```

View File

@ -0,0 +1,139 @@
#!/usr/bin/env python
__metaclass__ = type
import os
import traceback
import random, string
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from dataclasses import asdict
LIB_IMPORT_ERR = None
try:
from signedjson.key import *
HAS_LIB = True
except:
HAS_LIB = False
LIB_IMPORT_ERR = traceback.format_exc()
DOCUMENTION = r"""
---
module: synapse_signing_key
author:
- transcaffeine (transcaffeine@finally.coffee)
requirements:
- python >= 3.9
- signed_json >= 1.1.4
short_description: Generate and persist a synapse signing key
description:
- "Allows to generate, save and manipulate synapse signing keys"
options:
path:
description: "Where the signing key is saved or read from"
type: str
required: false
state:
description: "State of the key to enforce, can be used to remove keys or generate them"
type: str
choices: [present, absent]
default: present
required: false
"""
EXAMPLES = r"""
- name: Read existing signing key from filesystem
finallycoffee.matrix.synapse_signing_key:
path: "/path/to/existing-synapse.signing.key"
- name: Delete existing signing key
finallycoffee.matrix.synapse.signing_key:
path: "/this/signing/key/will/be/deleted.signing.key"
state: absent
"""
RETURN = r"""
signing_key:
description: Complete synapse signing key
returned: always
type: str
"""
def main() -> None:
_ = dict
module = AnsibleModule(
argument_spec=_(
path=_(required=False, type="str"),
state=_(
required=False,
type="str",
default="present",
choices=["present", "absent"],
),
),
supports_check_mode=True,
)
result = _(changed=False, diff={}, msg="")
failed = False
if not HAS_LIB:
module.fail_json(msg=missing_required_lib("signed_json"), exception=LIB_IMPORT_ERR)
path = module.params['path']
state = module.params['state']
existing_key_found = False
if path:
try:
keys = _read_signing_keys(module.params['path'])
existing_key_found = True
except FileNotFoundError:
existing_key_found = False
if not existing_key_found:
keys = _generate_signing_key()
result['changed'] = True
if not module.check_mode:
if state == 'present' and not existing_key_found and path:
_save_signing_keys(path, keys)
if state == 'absent' and existing_key_found:
os.remove(path)
result['changed'] = True
result['diff'] = {
"before_header": path,
"after_header": path,
"before": _render_signing_keys(keys) if existing_key_found else "",
"after": "" if state == 'absent' else _render_signing_keys(keys),
}
result['signing_key'] = _render_signing_keys(keys)
if failed:
module.fail_json(**result)
else:
module.exit_json(**result)
def _render_signing_keys(keys) -> str:
return "\n".join(_render_signing_key(k) for k in keys)
def _render_signing_key(key) -> str:
key_b64 = encode_signing_key_base64(key)
return f"{key.alg} {key.version} {key_b64}"
def _read_signing_keys(file):
with open(file, "r", opener=lambda path, f: os.open(path, f)) as stream:
return read_signing_keys(stream)
def _write_signing_keys(file, keys) -> None:
with open(file, "w", opener=lambda path, f: op.open(path, f, mode=0o640)) as stream:
write_signing_keys(strea, keys)
def _generate_signing_key():
id = ''
for i in range(0, 4):
id += random.choice(string.ascii_letters)
key_id = "a_" + id
key = generate_signing_key(key_id)
return [key]
if __name__ == "__main__":
main()

View File

@ -18,3 +18,5 @@ synapse_logging_config_file: >-
{{ synapse_config_path }}/{{ synapse_domain }}.log.config
synapse_pid_file: "{{ synapse_data_path }}/homeserver.pid"
synapse_sqlite_database_file: "{{ synapse_data_path }}/homeserver.db"
synapse_role_generate_signing_key: false

View File

@ -19,3 +19,14 @@
when: >-2
item not in hostvars[ansible_host]
or hostvars[ansible_host][item] | length == 0
- name: Ensure conditionally required variables are given
fail:
msg: "Required variable '{{ item.name }}' is undefined!"
loop: "{{ synapse_conditionally_required_variables }}"
loop_control:
label: "{{ item.name }}"
when: >-2
item.when
and (item.name not in hostvars[ansible_host]
or hostvars[ansible_host][item.name] | length == 0)

View File

@ -25,6 +25,12 @@
loop_control:
label: "{{ item.path }}"
- name: Ensure synapse signing key is generated
finallycoffee.matrix.synapse_signing_key:
path: "{{ synapse_signing_key_file }}"
state: "{{ synapse_state }}"
when: synapse_role_generate_signing_key
- name: Ensure configuration files are templated
ansible.builtin.copy:
dest: "{{ config_file.path }}"
@ -33,7 +39,9 @@
owner: "{{ config_file.owner | default(synapse_user_info.uid | default(synapse_user)) }}"
group: "{{ config_file.group | default(synapse_user_info.group | default(synapse_user)) }}"
loop: >-
{{ synapse_configs_to_write + synapse_configs | default([]) }}
{{ synapse_configs_to_write
+ (synapse_keys_to_write if not synapse_role_generate_signing_key else [])
+ synapse_configs | default([]) }}
loop_control:
loop_var: config_file
label: "{{ config_file.path }}"
@ -43,6 +51,7 @@
path: "{{ synapse_homeserver_config_file }}"
- content: "{{ synapse_log_config | to_nice_yaml(width=1000) }}"
path: "{{ synapse_logging_config_file }}"
synapse_keys_to_write:
- content: "{{ synapse_signing_key }}"
path: "{{ synapse_signing_key_file }}"
mode: "0640"

View File

@ -8,4 +8,7 @@ synapse_deployment_methods:
synapse_required_variables:
- synapse_domain
- synapse_signing_key
synapse_conditionally_required_variables:
- name: synapse_signing_key
when: "{{ not synapse_role_generate_signing_key | bool }}"