mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-03-11 09:04:54 +00:00
Debian has a patch to configuration to change port 5443 to 5280 in ejabberd package version 18.12. However, 5443 is the correct port to host BOSH. So, change it back. In 19.x, this behavior has changed to use both ports 5443 (for BOSH) and 5280 (for web administration). Closes: #1640. Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org> Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
322 lines
12 KiB
Python
Executable File
322 lines
12 KiB
Python
Executable File
#!/usr/bin/python3
|
|
#
|
|
# This file is part of FreedomBox.
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU Affero General Public License as
|
|
# published by the Free Software Foundation, either version 3 of the
|
|
# License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU Affero General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
"""
|
|
Configuration helper for the ejabberd service
|
|
"""
|
|
|
|
import argparse
|
|
import os
|
|
import pathlib
|
|
import shutil
|
|
import socket
|
|
import subprocess
|
|
from distutils.version import LooseVersion as LV
|
|
|
|
import ruamel.yaml
|
|
|
|
from plinth import action_utils
|
|
from plinth.modules import config
|
|
from plinth.modules.letsencrypt import LIVE_DIRECTORY as LE_LIVE_DIRECTORY
|
|
|
|
EJABBERD_CONFIG = '/etc/ejabberd/ejabberd.yml'
|
|
EJABBERD_BACKUP = '/var/log/ejabberd/ejabberd.dump'
|
|
EJABBERD_BACKUP_NEW = '/var/log/ejabberd/ejabberd_new.dump'
|
|
EJABBERD_ORIG_CERT = '/etc/ejabberd/ejabberd.pem'
|
|
IQDISC_DEPRECATED_VERSION = LV('18.03')
|
|
MOD_IRC_DEPRECATED_VERSION = LV('18.06')
|
|
|
|
|
|
def parse_arguments():
|
|
"""Return parsed command line arguments as dictionary"""
|
|
parser = argparse.ArgumentParser()
|
|
subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
|
|
|
|
# Preseed debconf values before packages are installed.
|
|
pre_install = subparsers.add_parser(
|
|
'pre-install',
|
|
help='Preseed debconf values before packages are installed.')
|
|
pre_install.add_argument(
|
|
'--domainname',
|
|
help='The domain name that will be used by the XMPP service.')
|
|
|
|
# Setup ejabberd configuration
|
|
setup = subparsers.add_parser('setup', help='Setup ejabberd configuration')
|
|
setup.add_argument(
|
|
'--domainname',
|
|
help='The domain name that will be used by the XMPP service.')
|
|
|
|
# Prepare ejabberd for hostname change
|
|
pre_hostname_change = subparsers.add_parser(
|
|
'pre-change-hostname', help='Prepare ejabberd for nodename change')
|
|
pre_hostname_change.add_argument('--old-hostname',
|
|
help='Previous hostname')
|
|
pre_hostname_change.add_argument('--new-hostname', help='New hostname')
|
|
|
|
# Update ejabberd nodename
|
|
hostname_change = subparsers.add_parser('change-hostname',
|
|
help='Update ejabberd nodename')
|
|
hostname_change.add_argument('--old-hostname', help='Previous hostname')
|
|
hostname_change.add_argument('--new-hostname', help='New hostname')
|
|
|
|
# Add a domain name to ejabberd
|
|
add_domain = subparsers.add_parser('add-domain',
|
|
help='Add a domain name to ejabberd')
|
|
add_domain.add_argument('--domainname', help='New domain name')
|
|
|
|
# Switch/check Message Archive Management (MAM) in ejabberd config
|
|
help_MAM = 'Switch or check Message Archive Management (MAM).'
|
|
mam = subparsers.add_parser('mam', help=help_MAM)
|
|
mam.add_argument('command', choices=('enable', 'disable', 'status'),
|
|
help=help_MAM)
|
|
|
|
help_LE = "Add/drop Let's Encrypt certificate if configured domain matches"
|
|
letsencrypt = subparsers.add_parser('letsencrypt', help=help_LE)
|
|
letsencrypt.add_argument('command', choices=('add', 'drop'), help=help_LE)
|
|
letsencrypt.add_argument('--domain', help='Domain name to drop.')
|
|
|
|
subparsers.required = True
|
|
return parser.parse_args()
|
|
|
|
|
|
def subcommand_pre_install(arguments):
|
|
"""Preseed debconf values before packages are installed."""
|
|
domainname = arguments.domainname
|
|
if not domainname:
|
|
# If new domainname is blank, use hostname instead.
|
|
domainname = socket.gethostname()
|
|
|
|
action_utils.debconf_set_selections(
|
|
['ejabberd ejabberd/hostname string ' + domainname])
|
|
|
|
|
|
def subcommand_setup(arguments):
|
|
"""Enabled LDAP authentication"""
|
|
with open(EJABBERD_CONFIG, 'r') as file_handle:
|
|
conf = ruamel.yaml.round_trip_load(file_handle, preserve_quotes=True)
|
|
|
|
for listen_port in conf['listen']:
|
|
if 'tls' in listen_port:
|
|
listen_port['tls'] = False
|
|
|
|
conf['auth_method'] = 'ldap'
|
|
conf['ldap_servers'] = [
|
|
ruamel.yaml.scalarstring.DoubleQuotedScalarString('localhost')
|
|
]
|
|
conf['ldap_base'] = ruamel.yaml.scalarstring.DoubleQuotedScalarString(
|
|
'ou=users,dc=thisbox')
|
|
|
|
with open(EJABBERD_CONFIG, 'w') as file_handle:
|
|
ruamel.yaml.round_trip_dump(conf, file_handle)
|
|
|
|
upgrade_config(arguments.domainname)
|
|
|
|
try:
|
|
subprocess.check_output(['ejabberdctl', 'restart'])
|
|
except subprocess.CalledProcessError as err:
|
|
print('Failed to restart ejabberd with new configuration: %s', err)
|
|
|
|
|
|
def upgrade_config(domain):
|
|
"""Fix the config file by removing deprecated settings"""
|
|
current_version = _get_version()
|
|
if not current_version:
|
|
print('Warning: Unable to get ejabberd version.')
|
|
|
|
with open(EJABBERD_CONFIG, 'r') as file_handle:
|
|
conf = ruamel.yaml.round_trip_load(file_handle, preserve_quotes=True)
|
|
|
|
# Check if `iqdisc` is present and remove it
|
|
if 'mod_mam' in conf['modules'] and \
|
|
(not current_version or current_version > IQDISC_DEPRECATED_VERSION):
|
|
conf['modules']['mod_mam'].pop('iqdisc', None)
|
|
|
|
# check if mod_irc is present in modules and remove it
|
|
if 'mod_irc' in conf['modules'] and \
|
|
(not current_version or current_version > MOD_IRC_DEPRECATED_VERSION):
|
|
conf['modules'].pop('mod_irc')
|
|
|
|
# Debian has a patch to configuration to change port 5443 to 5280 in
|
|
# ejabberd package version 18.12. However, 5443 is the correct port to host
|
|
# BOSH. So, change it back. In 19.x, this behavior has changed to use both
|
|
# ports 5443 (for BOSH) and 5280 (for web administration).
|
|
bosh_port = any((True for listen_port in conf['listen']
|
|
if listen_port['port'] == 5443))
|
|
if not bosh_port:
|
|
for listen_port in conf['listen']:
|
|
if listen_port['port'] == 5280:
|
|
listen_port['port'] = 5443
|
|
|
|
cert_dir = pathlib.Path('/etc/ejabberd/letsencrypt') / domain
|
|
cert_file = str(cert_dir / 'ejabberd.pem')
|
|
cert_file = ruamel.yaml.scalarstring.DoubleQuotedScalarString(cert_file)
|
|
conf['s2s_certfile'] = cert_file
|
|
for listen_port in conf['listen']:
|
|
if 'certfile' in listen_port:
|
|
listen_port['certfile'] = cert_file
|
|
|
|
# Write changes back to the file
|
|
with open(EJABBERD_CONFIG, 'w') as file_handle:
|
|
ruamel.yaml.round_trip_dump(conf, file_handle)
|
|
|
|
|
|
def subcommand_pre_change_hostname(arguments):
|
|
"""Prepare ejabberd for hostname change"""
|
|
if not shutil.which('ejabberdctl'):
|
|
print('ejabberdctl not found. Is ejabberd installed?')
|
|
return
|
|
|
|
old_hostname = arguments.old_hostname
|
|
new_hostname = arguments.new_hostname
|
|
|
|
subprocess.call(['ejabberdctl', 'backup', EJABBERD_BACKUP])
|
|
try:
|
|
subprocess.check_output([
|
|
'ejabberdctl', 'mnesia-change-nodename',
|
|
'ejabberd@' + old_hostname, 'ejabberd@' + new_hostname,
|
|
EJABBERD_BACKUP, EJABBERD_BACKUP_NEW
|
|
])
|
|
os.remove(EJABBERD_BACKUP)
|
|
except subprocess.CalledProcessError as err:
|
|
print('Failed to change hostname in ejabberd backup database: %s', err)
|
|
|
|
|
|
def subcommand_change_hostname(arguments):
|
|
"""Update ejabberd with new hostname"""
|
|
if not shutil.which('ejabberdctl'):
|
|
print('ejabberdctl not found. Is ejabberd installed?')
|
|
return
|
|
|
|
action_utils.service_stop('ejabberd')
|
|
subprocess.call(['pkill', '-u', 'ejabberd'])
|
|
|
|
# Make sure there aren't files in the Mnesia spool dir
|
|
os.makedirs('/var/lib/ejabberd/oldfiles', exist_ok=True)
|
|
subprocess.call('mv /var/lib/ejabberd/*.* /var/lib/ejabberd/oldfiles/',
|
|
shell=True)
|
|
|
|
action_utils.service_start('ejabberd')
|
|
|
|
# restore backup database
|
|
if os.path.exists(EJABBERD_BACKUP_NEW):
|
|
try:
|
|
subprocess.check_output(
|
|
['ejabberdctl', 'restore', EJABBERD_BACKUP_NEW])
|
|
os.remove(EJABBERD_BACKUP_NEW)
|
|
except subprocess.CalledProcessError as err:
|
|
print('Failed to restore ejabberd backup database: %s', err)
|
|
else:
|
|
print('Could not load ejabberd backup database: %s not found' %
|
|
EJABBERD_BACKUP_NEW)
|
|
|
|
|
|
def subcommand_add_domain(arguments):
|
|
"""Update ejabberd with new domainname"""
|
|
if not shutil.which('ejabberdctl'):
|
|
print('ejabberdctl not found. Is ejabberd installed?')
|
|
return
|
|
|
|
domainname = arguments.domainname
|
|
|
|
# Add updated domainname to ejabberd hosts list.
|
|
with open(EJABBERD_CONFIG, 'r') as file_handle:
|
|
conf = ruamel.yaml.round_trip_load(file_handle, preserve_quotes=True)
|
|
|
|
conf['hosts'].append(
|
|
ruamel.yaml.scalarstring.DoubleQuotedScalarString(domainname))
|
|
|
|
conf['hosts'] = list(set(conf['hosts']))
|
|
|
|
with open(EJABBERD_CONFIG, 'w') as file_handle:
|
|
ruamel.yaml.round_trip_dump(conf, file_handle)
|
|
|
|
|
|
def subcommand_mam(argument):
|
|
"""Enable, disable, or get status of Message Archive Management (MAM)."""
|
|
|
|
with open(EJABBERD_CONFIG, 'r') as file_handle:
|
|
conf = ruamel.yaml.round_trip_load(file_handle, preserve_quotes=True)
|
|
|
|
if 'modules' not in conf:
|
|
print('Found no "modules" entry in ejabberd configuration file.')
|
|
return
|
|
|
|
if argument.command == 'status':
|
|
if 'mod_mam' in conf['modules']:
|
|
print('enabled')
|
|
return
|
|
else:
|
|
print('disabled')
|
|
return
|
|
|
|
if argument.command == 'enable':
|
|
# Explicitly set the recommended / default settings for mod_mam,
|
|
# see https://docs.ejabberd.im/admin/configuration/#mod-mam.
|
|
settings_mod_mam = {
|
|
'mod_mam': {
|
|
'db_type':
|
|
'mnesia', # default is 'mnesia' (w/o set default_db)
|
|
'default': 'never', # policy, default 'never'
|
|
'request_activates_archiving': False, # default False
|
|
'assume_mam_usage': False, # for non-ack'd msgs, default False
|
|
'cache_size': 1000, # default is 1000 items
|
|
'cache_life_time': 3600 # default is 3600 seconds = 1h
|
|
}
|
|
}
|
|
conf['modules'].update(settings_mod_mam)
|
|
elif argument.command == 'disable':
|
|
# disable modules by erasing from config file
|
|
if 'mod_mam' in conf['modules']:
|
|
conf['modules'].pop('mod_mam')
|
|
else:
|
|
print("Unknown command: %s" % argument.command)
|
|
return
|
|
|
|
with open(EJABBERD_CONFIG, 'w') as file_handle:
|
|
ruamel.yaml.round_trip_dump(conf, file_handle)
|
|
|
|
if action_utils.service_is_running('ejabberd'):
|
|
subprocess.call(['ejabberdctl', 'reload_config'])
|
|
|
|
|
|
def _get_version():
|
|
""" Get the current ejabberd version """
|
|
try:
|
|
output = subprocess.check_output(['ejabberdctl',
|
|
'status']).decode('utf-8')
|
|
except subprocess.CalledProcessError:
|
|
return None
|
|
|
|
version_info = output.strip().split('\n')[-1].split()
|
|
if version_info:
|
|
version = str(version_info[1])
|
|
return LV(version)
|
|
return None
|
|
|
|
|
|
def main():
|
|
"""Parse arguments and perform all duties"""
|
|
arguments = parse_arguments()
|
|
|
|
subcommand = arguments.subcommand.replace('-', '_')
|
|
subcommand_method = globals()['subcommand_' + subcommand]
|
|
subcommand_method(arguments)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|