Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,17 @@ Launch `configure-module`, by setting the following parameters:
- `lets_encrypt`: Set to `true` to enable automatic SSL certificate generation via Let's Encrypt for both domains.
- `ldap_domain`: The LDAP domain name to be used for authentication.
- `mail_from`: The mail sender address for notification mails
- `nethvoice_auth_url`: The NethVoice authentication URL to enable NethVoice integration, if set the `matrix2acrobits` service will be configured and started.

Example:

api-cli run module/matrix1/configure-module --data '{"synapse_domain_name": "matrix.example.com", "element_domain_name": "chat.example.com", "cinny_domain_name": "cinny.example.com", "lets_encrypt": true, "ldap_domain": "users.example.com", "mail_from": "noreply@example.com"}'
api-cli run module/matrix1/configure-module --data '{"synapse_domain_name": "matrix.example.com", "element_domain_name": "chat.example.com", "cinny_domain_name": "cinny.example.com", "lets_encrypt": true, "ldap_domain": "users.example.com", "mail_from": "noreply@example.com", "nethvoice_auth_url": "https://nethvoice.nethserver.org/freepbx/rest/testextauth"}'

The above command will:
- Start and configure the Synapse Matrix homeserver with LDAP authentication
- Deploy Element Web client and Cinny client configured to connect to the local Synapse instance
- Set up Traefik routes for both domains with automatic SSL certificates
- Start Matrix2Acrobits Application Service

Access your Matrix installation:
- Matrix server: `https://matrix.example.com`
Expand Down
4 changes: 2 additions & 2 deletions build-images.sh
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ buildah add "${container}" ui/dist /ui
# Setup the entrypoint, ask to reserve three TCP ports and set a rootless container
buildah config --entrypoint=/ \
--label="org.nethserver.authorizations=traefik@node:routeadm cluster:accountconsumer" \
--label="org.nethserver.tcp-ports-demand=4" \
--label="org.nethserver.tcp-ports-demand=5" \
--label="org.nethserver.rootfull=0" \
--label="org.nethserver.max-per-node=1" \
--label="org.nethserver.images=docker.io/postgres:14.19-alpine ghcr.io/element-hq/synapse:v1.138.2 ghcr.io/element-hq/element-web:v1.12.0 ghcr.io/cinnyapp/cinny:v4.10.2" \
--label="org.nethserver.images=docker.io/postgres:14.19-alpine ghcr.io/element-hq/synapse:v1.138.2 ghcr.io/element-hq/element-web:v1.12.0 ghcr.io/cinnyapp/cinny:v4.10.2 ghcr.io/nethesis/matrix2acrobits:0.0.4" \
"${container}"
# Commit the image
buildah commit "${container}" "${repobase}/${reponame}"
Expand Down
67 changes: 67 additions & 0 deletions imageroot/actions/configure-module/20configure
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ cinny_domain_name = request.get('cinny_domain_name', '')
lets_encrypt = request.get('lets_encrypt', False)
ldap_domain = request.get('ldap_domain', '')
mail_from = request.get('mail_from', '')
nethvoice_auth_url = request.get('nethvoice_auth_url', '')

if not synapse_domain_name:
print("ERROR: Synapse_domain_name is required", file=sys.stderr)
Expand All @@ -38,6 +39,7 @@ env_vars['CINNY_DOMAIN_NAME'] = cinny_domain_name
env_vars['LDAP_DOMAIN'] = ldap_domain
env_vars['MAIL_FROM'] = mail_from
env_vars['LETS_ENCRYPT'] = '1' if lets_encrypt else '0'
env_vars['NETHVOICE_AUTH_URL'] = nethvoice_auth_url

# Get allocated TCP ports from environment - NS8 allocates sequential ports
tcp_ports = os.environ.get('TCP_PORTS', '20001').split(',')
Expand All @@ -47,6 +49,7 @@ env_vars['SYNAPSE_PORT'] = tcp_ports[0]
env_vars['ELEMENT_WEB_PORT'] = tcp_ports[1]
env_vars['CINNY_PORT'] = tcp_ports[2]
env_vars['POSTGRES_PORT'] = tcp_ports[3]
env_vars['M2A_PROXY_PORT'] = tcp_ports[4]

# Write updated configuration
agent.write_envfile("config.env", env_vars)
Expand Down Expand Up @@ -117,3 +120,67 @@ else:

# Bind the new domain, overriding previous values (unbind)
agent.bind_user_domains([ldap_domain])


# Enable or disable matrix2acrobits proxy routes depending on nethvoice_auth_url
m2a_port = env_vars.get('M2A_PROXY_PORT', '')
module_id = os.environ.get('MODULE_ID', 'matrix1')

if nethvoice_auth_url:
proxy_url = 'http://127.0.0.1:' + (m2a_port if m2a_port else '8080')
# create /m2a route
response = agent.tasks.run(
agent_id=agent.resolve_agent_id('traefik@node'),
action='set-route',
data={
'instance': module_id + '-m2a',
'name': module_id + '-m2a',
'host': synapse_domain_name,
'path': '/m2a',
'url': proxy_url,
'lets_encrypt': lets_encrypt,
'strip_prefix': True,
},
)
agent.assert_exp(response['exit_code'] == 0)

# create push notification route
response = agent.tasks.run(
agent_id=agent.resolve_agent_id('traefik@node'),
action='set-route',
data={
'instance': module_id + '-push',
'name': module_id + '-push',
'host': synapse_domain_name,
'path': '/_matrix/push/v1/notify',
'url': proxy_url,
'lets_encrypt': lets_encrypt,
'strip_prefix': False,
},
)
agent.assert_exp(response['exit_code'] == 0)

m2a = {
'MATRIX_HOMESERVER_URL': 'https://' + synapse_domain_name,
'PROXY_PORT': env_vars['M2A_PROXY_PORT'],
'AS_USER_ID': '@_acrobits_proxy:' + synapse_domain_name,
'EXT_AUTH_URL': nethvoice_auth_url,
'LOGLEVEL': 'info',
}
agent.write_envfile("m2a.env", m2a)
else:
response = agent.tasks.run(
agent_id=agent.resolve_agent_id('traefik@node'),
action='delete-route',
data={
'instance': module_id + '-m2a'
},
)

response = agent.tasks.run(
agent_id=agent.resolve_agent_id('traefik@node'),
action='delete-route',
data={
'instance': module_id + '-push'
},
)
11 changes: 11 additions & 0 deletions imageroot/actions/configure-module/80start_services
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,14 @@ if [[ -n ${CINNY_DOMAIN_NAME} ]]; then
else
systemctl --user stop cinny.service
fi

# Manage matrix2acrobits service
if [[ -n ${NETHVOICE_AUTH_URL} ]]; then
# Enable NethVoice Matrix Auth service
systemctl --user enable matrix2acrobits.service
# Restart NethVoice Matrix Auth service
systemctl --user restart matrix2acrobits.service
else
# Disable and stop NethVoice Matrix Auth service
systemctl --user disable --now matrix2acrobits.service
fi
17 changes: 16 additions & 1 deletion imageroot/actions/configure-module/validate-input.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"cinny_domain_name": "cinny.example.com",
"lets_encrypt": true,
"ldap_domain": "users.example.com",
"mail_from": "noreply@example.com"
"mail_from": "noreply@example.com",
"nethvoice_auth_url": "https://voice.nethserver.org/freepbx/rest/testextauth"
}
],
"type": "object",
Expand Down Expand Up @@ -73,5 +74,19 @@
}
]
}
,
"nethvoice_auth_url": {
"description": "NethVoice Authentication URL",
"oneOf": [
{
"type": "string",
"format": "uri"
},
{
"type": "string",
"maxLength": 0
}
]
}
}
}
21 changes: 21 additions & 0 deletions imageroot/actions/create-module/30grants
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/bin/bash

#
# Copyright (C) 2025 Nethesis S.r.l.
# SPDX-License-Identifier: GPL-3.0-or-later
#

set -e # exit immediately if an error occurs
exec 1>&2 # ensure any output is sent to stderr

#
# Allow other modules to configure Matrix and read the
# module configuration
#
redis-exec SADD "${AGENT_ID}/roles/matrixadm" "configure-module" "get-*"

#
# Allow the instance itself to run some subtasks during restore and clone actions
#
redis-exec SADD "${AGENT_ID}/roles/selfadm" \
"configure-module"
2 changes: 2 additions & 0 deletions imageroot/actions/get-configuration/20read
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ if os.path.exists("config.env"):
config['mail_from'] = env_vars.get('MAIL_FROM', '')
config['lets_encrypt'] = env_vars.get('LETS_ENCRYPT', '0') == '1'
config['domains_list'] = domains
config['nethvoice_auth_url'] = env_vars.get('NETHVOICE_AUTH_URL', '')
else:
# No configuration file found, return empty configuration
config['synapse_domain_name'] = ''
Expand All @@ -44,5 +45,6 @@ else:
config['mail_from'] = ''
config['lets_encrypt'] = False
config['domains_list'] = domains
config['nethvoice_auth_url'] = ''

json.dump(config, fp=sys.stdout)
14 changes: 14 additions & 0 deletions imageroot/actions/get-configuration/validate-output.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,19 @@
}
]
}
,
"nethvoice_auth_url": {
"description": "NethVoice Authentication URL",
"oneOf": [
{
"type": "string",
"format": "uri"
},
{
"type": "string",
"maxLength": 0
}
]
}
}
}
16 changes: 15 additions & 1 deletion imageroot/bin/configure-matrix
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,11 @@ if not os.path.exists(secrets_file):
"REGISTRATION_SECRET": generate_secret(),
"MACAROON_SECRET": generate_secret(),
"FORM_SECRET": generate_secret(),
"SYNAPSE_SECRET": generate_secret()
"SYNAPSE_SECRET": generate_secret(),
"M2A_AS_TOKEN": generate_secret(64),
"M2A_HS_TOKEN": generate_secret(64)
}
secrets_env["MATRIX_AS_TOKEN"] = secrets_env['M2A_AS_TOKEN']
agent.write_envfile(secrets_file, secrets_env)

# Read generated secrets
Expand All @@ -74,6 +77,17 @@ with open(f'{template_dir}/synapse-homeserver.yaml', 'r') as f:
with open('synapse-config/homeserver.yaml', 'w') as f:
f.write(synapse_config)

# If NETHVOICE_AUTH_URL is set, append the appservice lines to the generated homeserver.yaml
if env_vars.get('NETHVOICE_AUTH_URL'):
f.write('\napp_service_config_files:\n - /data/config/matrix2acrobits.yaml\n')

# Expand matrix2acrobits.yaml into the synapse config directory
with open(f'{template_dir}/matrix2acrobits.yaml', 'r') as f:
matrix_a2a = substitute_env_vars(f.read(), env_vars)

with open('synapse-config/matrix2acrobits.yaml', 'w') as f:
f.write(matrix_a2a)

# Copy log config
with open(f'{template_dir}/synapse-log.config', 'r') as f:
log_config = f.read()
Expand Down
1 change: 1 addition & 0 deletions imageroot/etc/state-include.conf
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ state/database.env
state/config.json

volumes/synapse-data
volumes/matrix2acrobits-data
39 changes: 39 additions & 0 deletions imageroot/systemd/user/matrix2acrobits.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#
# Copyright (C) 2025 Nethesis S.r.l.
# SPDX-License-Identifier: GPL-3.0-or-later
#

#
# This systemd unit starts matrix2acrobits client using Podman.
#
[Unit]
Description=matrix2acrobits Matrix Client
After=network-online.target synapse.service
Wants=synapse.service

[Service]
Environment=PODMAN_SYSTEMD_UNIT=%n
EnvironmentFile=%S/state/environment
EnvironmentFile=%S/state/config.env
WorkingDirectory=%S/state
Restart=always
ExecStartPre=/bin/rm -f %t/matrix2acrobits.pid %t/matrix2acrobits.ctr-id
ExecStartPre=-runagent configure-matrix
ExecStart=/usr/bin/podman run \
--detach \
--conmon-pidfile=%t/matrix2acrobits.pid \
--cidfile=%t/matrix2acrobits.ctr-id \
--cgroups=no-conmon \
--replace --name=matrix2acrobits \
--network=host \
--env-file=%S/state/matrix-secrets.env \
--env-file=%S/state/m2a.env \
--volume=matrix2acrobits-data:/tmp \
${MATRIX2ACROBITS_IMAGE}
ExecStop=/usr/bin/podman stop --ignore --cidfile %t/matrix2acrobits.ctr-id -t 10
ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/matrix2acrobits.ctr-id
PIDFile=%t/matrix2acrobits.pid
Type=forking

[Install]
WantedBy=default.target
15 changes: 15 additions & 0 deletions imageroot/templates/matrix2acrobits.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
id: acrobits-proxy
url: http://127.0.0.1:${M2A_PROXY_PORT}
as_token: "${M2A_AS_TOKEN}"
hs_token: "${M2A_HS_TOKEN}"
sender_localpart: _acrobits_proxy
namespaces:
users:
- exclusive: false
regex: '@.*'
aliases:
- exclusive: false
regex: '.*'
rooms:
- exclusive: false
regex: '.*'
57 changes: 0 additions & 57 deletions ui/public/i18n/ar/translation.json

This file was deleted.

Loading
Loading