mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-09-19 04:59:01 +00:00
ejabberd: Add let's encrypt component for managing certificates
Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org> Reviewed-by: Joseph Nuthalapati <njoseph@thoughtworks.com>
This commit is contained in:
parent
9fd1b95244
commit
49b543599a
111
actions/ejabberd
111
actions/ejabberd
@ -21,11 +21,10 @@ Configuration helper for the ejabberd service
|
|||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
|
import pathlib
|
||||||
import shutil
|
import shutil
|
||||||
import socket
|
import socket
|
||||||
import stat
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
|
||||||
from distutils.version import LooseVersion as LV
|
from distutils.version import LooseVersion as LV
|
||||||
|
|
||||||
import ruamel.yaml
|
import ruamel.yaml
|
||||||
@ -56,7 +55,10 @@ def parse_arguments():
|
|||||||
help='The domain name that will be used by the XMPP service.')
|
help='The domain name that will be used by the XMPP service.')
|
||||||
|
|
||||||
# Setup ejabberd configuration
|
# Setup ejabberd configuration
|
||||||
subparsers.add_parser('setup', help='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
|
# Prepare ejabberd for hostname change
|
||||||
pre_hostname_change = subparsers.add_parser(
|
pre_hostname_change = subparsers.add_parser(
|
||||||
@ -103,7 +105,7 @@ def subcommand_pre_install(arguments):
|
|||||||
input=b'ejabberd ejabberd/hostname string ' + domainname.encode())
|
input=b'ejabberd ejabberd/hostname string ' + domainname.encode())
|
||||||
|
|
||||||
|
|
||||||
def subcommand_setup(_):
|
def subcommand_setup(arguments):
|
||||||
"""Enabled LDAP authentication"""
|
"""Enabled LDAP authentication"""
|
||||||
with open(EJABBERD_CONFIG, 'r') as file_handle:
|
with open(EJABBERD_CONFIG, 'r') as file_handle:
|
||||||
conf = ruamel.yaml.round_trip_load(file_handle, preserve_quotes=True)
|
conf = ruamel.yaml.round_trip_load(file_handle, preserve_quotes=True)
|
||||||
@ -122,7 +124,7 @@ def subcommand_setup(_):
|
|||||||
with open(EJABBERD_CONFIG, 'w') as file_handle:
|
with open(EJABBERD_CONFIG, 'w') as file_handle:
|
||||||
ruamel.yaml.round_trip_dump(conf, file_handle)
|
ruamel.yaml.round_trip_dump(conf, file_handle)
|
||||||
|
|
||||||
upgrade_config()
|
upgrade_config(arguments.domainname)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
subprocess.check_output(['ejabberdctl', 'restart'])
|
subprocess.check_output(['ejabberdctl', 'restart'])
|
||||||
@ -130,7 +132,7 @@ def subcommand_setup(_):
|
|||||||
print('Failed to restart ejabberd with new configuration: %s', err)
|
print('Failed to restart ejabberd with new configuration: %s', err)
|
||||||
|
|
||||||
|
|
||||||
def upgrade_config():
|
def upgrade_config(domain):
|
||||||
"""Fix the config file by removing deprecated settings"""
|
"""Fix the config file by removing deprecated settings"""
|
||||||
current_version = _get_version()
|
current_version = _get_version()
|
||||||
if not current_version:
|
if not current_version:
|
||||||
@ -154,6 +156,14 @@ def upgrade_config():
|
|||||||
if listen_port['port'] == 5280:
|
if listen_port['port'] == 5280:
|
||||||
listen_port['port'] = 5443
|
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
|
# Write changes back to the file
|
||||||
with open(EJABBERD_CONFIG, 'w') as file_handle:
|
with open(EJABBERD_CONFIG, 'w') as file_handle:
|
||||||
ruamel.yaml.round_trip_dump(conf, file_handle)
|
ruamel.yaml.round_trip_dump(conf, file_handle)
|
||||||
@ -220,9 +230,6 @@ def subcommand_change_domainname(arguments):
|
|||||||
# If new domainname is blank, use hostname instead.
|
# If new domainname is blank, use hostname instead.
|
||||||
domainname = socket.gethostname()
|
domainname = socket.gethostname()
|
||||||
|
|
||||||
action_utils.service_stop('ejabberd')
|
|
||||||
subprocess.call(['pkill', '-u', 'ejabberd'])
|
|
||||||
|
|
||||||
# Add updated domainname to ejabberd hosts list.
|
# Add updated domainname to ejabberd hosts list.
|
||||||
with open(EJABBERD_CONFIG, 'r') as file_handle:
|
with open(EJABBERD_CONFIG, 'r') as file_handle:
|
||||||
conf = ruamel.yaml.round_trip_load(file_handle, preserve_quotes=True)
|
conf = ruamel.yaml.round_trip_load(file_handle, preserve_quotes=True)
|
||||||
@ -233,8 +240,6 @@ def subcommand_change_domainname(arguments):
|
|||||||
with open(EJABBERD_CONFIG, 'w') as file_handle:
|
with open(EJABBERD_CONFIG, 'w') as file_handle:
|
||||||
ruamel.yaml.round_trip_dump(conf, file_handle)
|
ruamel.yaml.round_trip_dump(conf, file_handle)
|
||||||
|
|
||||||
action_utils.service_start('ejabberd')
|
|
||||||
|
|
||||||
|
|
||||||
def subcommand_mam(argument):
|
def subcommand_mam(argument):
|
||||||
"""Enable, disable, or get status of Message Archive Management (MAM)."""
|
"""Enable, disable, or get status of Message Archive Management (MAM)."""
|
||||||
@ -284,90 +289,6 @@ def subcommand_mam(argument):
|
|||||||
subprocess.call(['ejabberdctl', 'reload_config'])
|
subprocess.call(['ejabberdctl', 'reload_config'])
|
||||||
|
|
||||||
|
|
||||||
def subcommand_letsencrypt(arguments):
|
|
||||||
"""
|
|
||||||
Add/drop usage of Let's Encrypt cert. The command 'add' applies only to
|
|
||||||
current domain, will be called by action 'letsencrypt run_renew_hooks',
|
|
||||||
when certbot renews the cert (if ejabberd is selected for cert use).
|
|
||||||
Drop of a cert must be possible for any domain to respond to domain change.
|
|
||||||
"""
|
|
||||||
current_domain = config.get_domainname()
|
|
||||||
|
|
||||||
with open(EJABBERD_CONFIG, 'r') as file_handle:
|
|
||||||
conf = ruamel.yaml.round_trip_load(file_handle, preserve_quotes=True)
|
|
||||||
|
|
||||||
if arguments.domain is not None and arguments.domain not in conf['hosts']:
|
|
||||||
print('Aborted: Current domain "%s" not configured for ejabberd.' %
|
|
||||||
arguments.domain)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
if arguments.command == 'add' and arguments.domain is not None \
|
|
||||||
and arguments.domain != current_domain:
|
|
||||||
print('Aborted: Only certificate of current domain "%s" can be added.'
|
|
||||||
% current_domain)
|
|
||||||
sys.exit(2)
|
|
||||||
|
|
||||||
if arguments.domain is None:
|
|
||||||
arguments.domain = current_domain
|
|
||||||
|
|
||||||
cert_folder = '/etc/ejabberd/letsencrypt/' + arguments.domain
|
|
||||||
cert_file = cert_folder + '/ejabberd.pem'
|
|
||||||
|
|
||||||
if arguments.command == 'add':
|
|
||||||
le_folder = os.path.join(LE_LIVE_DIRECTORY, current_domain)
|
|
||||||
le_privkey = os.path.join(le_folder, 'privkey.pem')
|
|
||||||
le_fullchain = os.path.join(le_folder, 'fullchain.pem')
|
|
||||||
|
|
||||||
if not os.path.exists(le_folder):
|
|
||||||
print('Aborted: No certificate directory at %s.' % le_folder)
|
|
||||||
sys.exit(3)
|
|
||||||
|
|
||||||
if not os.path.exists(cert_folder):
|
|
||||||
os.makedirs(cert_folder)
|
|
||||||
shutil.chown(cert_folder, 'ejabberd', 'ejabberd')
|
|
||||||
|
|
||||||
with open(cert_file, 'w') as outfile:
|
|
||||||
with open(le_privkey, 'r') as infile:
|
|
||||||
for line in infile:
|
|
||||||
if line.strip():
|
|
||||||
outfile.write(line)
|
|
||||||
with open(le_fullchain, 'r') as infile:
|
|
||||||
for line in infile:
|
|
||||||
if line.strip():
|
|
||||||
outfile.write(line)
|
|
||||||
shutil.chown(cert_file, 'ejabberd', 'ejabberd')
|
|
||||||
os.chmod(cert_file, stat.S_IRUSR | stat.S_IWUSR)
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
else: # arguments.command == 'drop' (ensured by parser)
|
|
||||||
orig_cert_file = ruamel.yaml.scalarstring.DoubleQuotedScalarString(
|
|
||||||
EJABBERD_ORIG_CERT)
|
|
||||||
|
|
||||||
for listen_port in conf['listen']:
|
|
||||||
if 'certfile' in listen_port \
|
|
||||||
and listen_port['certfile'] == cert_file:
|
|
||||||
listen_port['certfile'] = orig_cert_file
|
|
||||||
|
|
||||||
if conf['s2s_certfile'] == cert_file:
|
|
||||||
conf['s2s_certfile'] = orig_cert_file
|
|
||||||
|
|
||||||
if os.path.exists(cert_folder):
|
|
||||||
shutil.rmtree(cert_folder)
|
|
||||||
|
|
||||||
with open(EJABBERD_CONFIG, 'w') as file_handle:
|
|
||||||
ruamel.yaml.round_trip_dump(conf, file_handle)
|
|
||||||
|
|
||||||
if action_utils.service_is_running('ejabberd'):
|
|
||||||
action_utils.service_restart('ejabberd')
|
|
||||||
|
|
||||||
|
|
||||||
def _get_version():
|
def _get_version():
|
||||||
""" Get the current ejabberd version """
|
""" Get the current ejabberd version """
|
||||||
try:
|
try:
|
||||||
|
|||||||
@ -19,6 +19,7 @@ FreedomBox app to configure ejabberd server.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import pathlib
|
||||||
|
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
@ -30,6 +31,7 @@ from plinth.daemon import Daemon
|
|||||||
from plinth.modules import config
|
from plinth.modules import config
|
||||||
from plinth.modules.apache.components import Webserver
|
from plinth.modules.apache.components import Webserver
|
||||||
from plinth.modules.firewall.components import Firewall
|
from plinth.modules.firewall.components import Firewall
|
||||||
|
from plinth.modules.letsencrypt.components import LetsEncrypt
|
||||||
from plinth.signals import (domainname_change, post_hostname_change,
|
from plinth.signals import (domainname_change, post_hostname_change,
|
||||||
pre_hostname_change)
|
pre_hostname_change)
|
||||||
from plinth.utils import format_lazy
|
from plinth.utils import format_lazy
|
||||||
@ -42,6 +44,8 @@ managed_services = ['ejabberd']
|
|||||||
|
|
||||||
managed_packages = ['ejabberd']
|
managed_packages = ['ejabberd']
|
||||||
|
|
||||||
|
managed_paths = [pathlib.Path('/etc/ejabberd/')]
|
||||||
|
|
||||||
name = _('ejabberd')
|
name = _('ejabberd')
|
||||||
|
|
||||||
short_description = _('Chat Server')
|
short_description = _('Chat Server')
|
||||||
@ -104,6 +108,15 @@ class EjabberdApp(app_module.App):
|
|||||||
webserver = Webserver('webserver-ejabberd', 'jwchat-plinth')
|
webserver = Webserver('webserver-ejabberd', 'jwchat-plinth')
|
||||||
self.add(webserver)
|
self.add(webserver)
|
||||||
|
|
||||||
|
letsencrypt = LetsEncrypt(
|
||||||
|
'letsencrypt-ejabberd', domains=get_domains, daemons=['ejabberd'],
|
||||||
|
should_copy_certificates=True,
|
||||||
|
private_key_path='/etc/ejabberd/letsencrypt/{domain}/ejabberd.pem',
|
||||||
|
certificate_path='/etc/ejabberd/letsencrypt/{domain}/ejabberd.pem',
|
||||||
|
user_owner='ejabberd', group_owner='ejabberd',
|
||||||
|
managing_app='ejabberd')
|
||||||
|
self.add(letsencrypt)
|
||||||
|
|
||||||
daemon = Daemon('daemon-ejabberd', managed_services[0])
|
daemon = Daemon('daemon-ejabberd', managed_services[0])
|
||||||
self.add(daemon)
|
self.add(daemon)
|
||||||
|
|
||||||
@ -129,11 +142,29 @@ def setup(helper, old_version=None):
|
|||||||
|
|
||||||
helper.call('pre', actions.superuser_run, 'ejabberd',
|
helper.call('pre', actions.superuser_run, 'ejabberd',
|
||||||
['pre-install', '--domainname', domainname])
|
['pre-install', '--domainname', domainname])
|
||||||
|
# XXX: Configure all other domain names
|
||||||
helper.install(managed_packages)
|
helper.install(managed_packages)
|
||||||
helper.call('post', actions.superuser_run, 'ejabberd', ['setup'])
|
helper.call('post',
|
||||||
|
app.get_component('letsencrypt-ejabberd').setup_certificates,
|
||||||
|
[domainname])
|
||||||
|
helper.call('post', actions.superuser_run, 'ejabberd',
|
||||||
|
['setup', '--domainname', domainname])
|
||||||
helper.call('post', app.enable)
|
helper.call('post', app.enable)
|
||||||
|
|
||||||
|
|
||||||
|
def get_domains():
|
||||||
|
"""Return the list of domains that ejabberd is interested in.
|
||||||
|
|
||||||
|
XXX: Retrieve the list from ejabberd configuration.
|
||||||
|
|
||||||
|
"""
|
||||||
|
setup_helper = globals()['setup_helper']
|
||||||
|
if setup_helper.get_state() == 'needs-setup':
|
||||||
|
return []
|
||||||
|
|
||||||
|
return [config.get_domainname()]
|
||||||
|
|
||||||
|
|
||||||
def on_pre_hostname_change(sender, old_hostname, new_hostname, **kwargs):
|
def on_pre_hostname_change(sender, old_hostname, new_hostname, **kwargs):
|
||||||
"""
|
"""
|
||||||
Backup ejabberd database before hostname is changed.
|
Backup ejabberd database before hostname is changed.
|
||||||
@ -165,8 +196,8 @@ def on_domainname_change(sender, old_domainname, new_domainname, **kwargs):
|
|||||||
del kwargs # Unused
|
del kwargs # Unused
|
||||||
|
|
||||||
actions.superuser_run(
|
actions.superuser_run(
|
||||||
'ejabberd', ['change-domainname', '--domainname', new_domainname],
|
'ejabberd', ['change-domainname', '--domainname', new_domainname])
|
||||||
run_in_background=True)
|
app.get_component('letsencrypt-ejabberd').setup_certificates()
|
||||||
|
|
||||||
|
|
||||||
def diagnose():
|
def diagnose():
|
||||||
|
|||||||
@ -47,7 +47,7 @@ class EjabberdAppView(AppView):
|
|||||||
def get_context_data(self, *args, **kwargs):
|
def get_context_data(self, *args, **kwargs):
|
||||||
"""Add service to the context data."""
|
"""Add service to the context data."""
|
||||||
context = super().get_context_data(*args, **kwargs)
|
context = super().get_context_data(*args, **kwargs)
|
||||||
context['domainname'] = config.get_domainname()
|
context['domainname'] = ejabberd.get_domains()[0]
|
||||||
context['clients'] = ejabberd.clients
|
context['clients'] = ejabberd.clients
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user