letsencrypt: Remove renewal hooks implementation

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: Joseph Nuthalapati <njoseph@thoughtworks.com>
This commit is contained in:
Sunil Mohan Adapa 2019-06-11 19:30:37 -07:00 committed by Joseph Nuthalapati
parent a134311663
commit d8647aaf18
No known key found for this signature in database
GPG Key ID: 5398F00A2FA43C35
5 changed files with 54 additions and 646 deletions

View File

@ -22,15 +22,11 @@ Configuration helper for Let's Encrypt.
import argparse
import json
import os
import shutil
import re
import subprocess
import sys
import re
import configobj
from plinth import action_utils
from plinth.errors import ActionError
from plinth.modules import config
from plinth.modules import letsencrypt as le
TEST_MODE = False
@ -69,43 +65,18 @@ def parse_arguments():
delete_parser.add_argument('--domain', required=True,
help='Domain name to delete certificate of')
help_hooks = 'FreedomBox management of certificates (for current domain).'
manage_hook_parser = subparsers.add_parser('manage_hooks', help=help_hooks)
manage_hook_parser.add_argument('command', help=help_hooks,
choices=('enable', 'disable', 'status'))
manage_hook_parser.add_argument('--domain',
help='Domain for hook management command.')
help_module_arg = 'For enable: Also use LE cert with other FreedomBox ' \
'apps.'
manage_hook_parser.add_argument('--modules', help=help_module_arg,
nargs='+', default=[],
choices=le.MODULES_WITH_HOOKS)
help_hooks = 'Does nothing, kept for compatibility.'
subparser = subparsers.add_parser('run_pre_hooks', help=help_hooks)
subparser.add_argument('--domain')
subparser.add_argument('--modules', nargs='+', default=[])
help_domain_arg = 'Domain name to run the hook scripts with.'
help_module_arg = 'Include hooks from the provided module names.'
help_pre_hooks = 'Maintenance tasks before a cert is obtained or renewed.'
run_pre_hooks_parser = subparsers.add_parser('run_pre_hooks',
help=help_pre_hooks)
run_pre_hooks_parser.add_argument('--domain', help=help_domain_arg)
run_pre_hooks_parser.add_argument('--modules', help=help_module_arg,
nargs='+', default=[],
choices=le.MODULES_WITH_HOOKS)
subparser = subparsers.add_parser('run_renew_hooks', help=help_hooks)
subparser.add_argument('--domain')
subparser.add_argument('--modules', nargs='+', default=[])
help_renew_hooks = 'Maintenance tasks after a cert is actually renewed.'
run_renew_hooks_parser = subparsers.add_parser('run_renew_hooks',
help=help_renew_hooks)
run_renew_hooks_parser.add_argument('--domain', help=help_domain_arg)
run_renew_hooks_parser.add_argument('--modules', help=help_module_arg,
nargs='+', default=[],
choices=le.MODULES_WITH_HOOKS)
help_post_hooks = 'Maintenance tasks after a cert is obtained or renewed.'
run_post_hooks_parser = subparsers.add_parser('run_post_hooks',
help=help_post_hooks)
run_post_hooks_parser.add_argument('--domain', help=help_domain_arg)
run_post_hooks_parser.add_argument('--modules', help=help_module_arg,
nargs='+', default=[],
choices=le.MODULES_WITH_HOOKS)
subparser = subparsers.add_parser('run_post_hooks', help=help_hooks)
subparser.add_argument('--domain')
subparser.add_argument('--modules', nargs='+', default=[])
subparsers.required = True
return parser.parse_args()
@ -231,259 +202,43 @@ def subcommand_obtain(arguments):
setup_webserver_config(domain, webserver_change)
def subcommand_manage_hooks(arguments):
def subcommand_run_pre_hooks(_):
"""Do nothing, kept for legacy LE configuration.
If new version of Plinth is deployed and before it can update the Let's
Encrypt configuration and remove these old hooks, if a renew operation is
run, then we don't want it to exit with non-zero error code because this
hook could not be run.
Remove at some point in the future.
"""
Enable/disable/status of certbot's pre-, renew-, & post-hooks for renewal.
Enable edits renewal config of current domain (RENEWAL_DIR/DOMAIN.config),
and creates a backup beforehand that is restored if disable is called.
Commands disable and status work without error on any domain string.
def subcommand_run_renew_hooks(_):
"""Do nothing, kept for legacy LE configuration.
If new version of Plinth is deployed and before it can update the Let's
Encrypt configuration and remove these old hooks, if a renew operation is
run, then we don't want it to exit with non-zero error code because this
hook could not be run.
Remove at some point in the future.
"""
if arguments.command not in ('enable', 'disable', 'status'):
print('Aborted: Unknown command "%s".' % arguments.command)
sys.exit(1)
cmd_is_enable = arguments.command == 'enable'
current_domain = config.get_domainname()
if not arguments.domain:
arguments.domain = current_domain
if cmd_is_enable:
if not current_domain:
print('Aborted: No current domain set.')
sys.exit(2)
if not arguments.domain == current_domain:
print('Aborted: Passed domain (%s) is not current domain (%s).' %
(arguments.domain, current_domain))
sys.exit(3)
domain_status = get_status()
if current_domain not in domain_status:
print('Aborted: Current domain (%s) has no LE certificate.' %
current_domain)
sys.exit(4)
config_path = RENEWAL_DIRECTORY + arguments.domain + '.conf'
if not os.path.exists(config_path):
msg, code = ('Aborted', 5) if cmd_is_enable else ('Disabled', 0)
print('%s: No certbot config file at %s.' % (msg, config_path))
sys.exit(code)
config_certbot = configobj.ConfigObj(config_path)
if 'renewalparams' not in config_certbot:
msg, code = ('Aborted', 6) if cmd_is_enable else ('Disabled', 0)
print('%s: No section [renewalparams] in config file at %s.' %
(msg, config_path))
sys.exit(code)
script_path = os.path.realpath(__file__)
config_backup_path = config_path + '_plinth_backup'
call_pre = script_path + ' run_pre_hooks --domain ' + arguments.domain
call_renew = script_path + ' run_renew_hooks --domain ' + arguments.domain
call_post = script_path + ' run_post_hooks --domain ' + arguments.domain
config_plinth = {
'renewalparams': {
'authenticator': AUTHENTICATOR,
# 'webroot_path': [WEB_ROOT_PATH], # removed by renew...
'webroot_map': {
arguments.domain: WEB_ROOT_PATH
},
'installer': 'None',
'pre_hook': call_pre,
'renew_hook': call_renew,
'post_hook': call_post
}
}
comment_plinth = '# This file was edited by Plinth.'
config_edited_by_plinth = any([
'edited by plinth' in line.lower()
for line in config_certbot.initial_comment
])
if arguments.command == 'status':
# check for presence of expected minimal configuration
config_checks = [
(entry in config_certbot['renewalparams'])
and (str(config_plinth['renewalparams'][entry]) in str(
config_certbot['renewalparams'][entry]))
for entry in config_plinth['renewalparams'].keys()
]
if not all(config_checks):
print('disabled')
sys.exit(0)
# is enabled; check for which selected modules (only for renew_hook)
cmd_str = config_certbot['renewalparams']['renew_hook']
module_list = []
for mod_str in le.MODULES_WITH_HOOKS:
mod_pattern = 'letsencrypt .*--modules .*%s.*' % mod_str
match = re.search(mod_pattern, cmd_str)
if match is not None:
module_list.append(mod_str)
if module_list != []:
print('enabled, with modules: ' + ', '.join(module_list))
else:
print('enabled, without modules')
elif arguments.command == 'enable':
if not config_edited_by_plinth:
shutil.copy(config_path, config_backup_path)
config_certbot.initial_comment.append(comment_plinth)
if arguments.modules != []:
call_renew += ' --modules ' + ' '.join(arguments.modules)
config_plinth['renewalparams']['renew_hook'] = call_renew
config_certbot['renewalparams'].update(config_plinth['renewalparams'])
config_certbot.write()
if arguments.modules != []:
print('enabled, with modules: ' + ', '.join(arguments.modules))
else:
print('enabled, without modules')
elif arguments.command == 'disable':
# if changed, restore from backup; refuse disabling if no backup exists
if not config_edited_by_plinth:
print('Disabled: Nothing to do, hook management was not enabled.')
elif os.path.exists(config_backup_path):
shutil.move(config_backup_path, config_path)
print('disabled successfully')
else:
print('Aborted: No backup config file at %s.' % config_backup_path)
sys.exit(7)
sys.exit(0)
def subcommand_run_pre_hooks(arguments):
def subcommand_run_post_hooks(_):
"""Do nothing, kept for legacy LE configuration.
If new version of Plinth is deployed and before it can update the Let's
Encrypt configuration and remove these old hooks, if a renew operation is
run, then we don't want it to exit with non-zero error code because this
hook could not be run.
Remove at some point in the future.
"""
Execute all needed maintenance tasks BEFORE a cert is obtained/renewed.
If registered as certbot's pre-hook, this script gets ALWAYS executed when
certbot attempts a renewal, irrespective of necessity/success (2x per day).
"""
# Require current domain, to avoid confusion (e.g. call from old cron job).
if not arguments.domain:
print('Aborted: You must specify the current domain.')
sys.exit(1)
current_domain = config.get_domainname()
if not arguments.domain == current_domain:
print('Aborted: Current domain is %s, but called for %s.' %
(current_domain, arguments.domain))
sys.exit(2)
sys.exit(0)
def subcommand_run_renew_hooks(arguments):
"""
Execute ALL maintenance tasks when (just after) certbot renews a cert, i.e.
run all tasks of every app/plinth module that supports usage of LE certs.
If registered as certbot's renew-hook, this script gets ONLY executed when
certbot successfully renewed a certificate; with Debian default config,
this means it would run about once every 60 days (renewals get executed
if a cert is <30 days before expiry, and current default is 90 days).
Errors will be logged by certbot to /var/log/letsencrypt/letsencrypt.log.
"""
# Require current domain, to avoid confusion (e.g. call from old cron job).
if not arguments.domain:
print('Aborted: You must specify the current domain.')
sys.exit(1)
current_domain = config.get_domainname()
if not arguments.domain == current_domain:
print('Aborted: Current domain is %s, but called for %s.' %
(current_domain, arguments.domain))
sys.exit(2)
if action_utils.service_is_running('apache2'):
action_utils.service_restart('apache2')
for module in arguments.modules:
# If >1 modules, collect errors and raise just one in the end,
# for certbot to log ALL failed attempts, not just 1st fail.
error_messages = []
try:
_run_action(module, ['letsencrypt', 'add'])
except Exception as err:
error_messages.append(err.message)
if error_messages:
raise ActionError(message="\n".join(error_messages))
sys.exit(3)
sys.exit(0)
def _run_action(action, action_options=None):
"""
Run a specific action from another module, for the run_renew_hooks command.
This function is a simplified version of plinth/actions.py, to enable
somewhat safe calls of other actions from outside the FreedomBox Service
(Plinth) process. The comments about the action contracts refer to
plinth/actions.py.
"""
if action_options is None:
action_options = []
# Contract 3A and 3B: don't call anything outside of the actions directory.
# Assume the current path is the actions directly.
script_path = os.path.realpath(__file__)
actions_dir, _ = os.path.split(script_path)
if os.sep in action:
raise ValueError('Action cannot contain: ' + os.sep)
cmd = os.path.join(actions_dir, action)
if not os.path.realpath(cmd).startswith(actions_dir):
raise ValueError('Action has to be in directory %s' % actions_dir)
# Contract 3C: interpret shell escape sequences as literal file names.
# Contract 3E: fail if the action doesn't exist or exists elsewhere.
if not os.access(cmd, os.F_OK):
raise ValueError('Action must exist in action directory.')
cmd = [cmd]
# Contract: 3C, 3D: don't allow shell special characters in
# options be interpreted by the shell.
if action_options:
if not isinstance(action_options, (list, tuple)):
raise ValueError('Options must be list or tuple.')
cmd += list(action_options) # No escaping necessary
# Contract 3C: don't interpret shell escape sequences.
# Contract 5 (and 6-ish).
proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, shell=False)
output, error = proc.communicate()
output, error = output.decode(), error.decode()
if proc.returncode != 0:
raise ActionError(action, output, error)
def subcommand_run_post_hooks(arguments):
"""
Execute all needed maintenance tasks AFTER a cert is obtained/renewed.
If registered as certbot's post-hook, this script gets ALWAYS executed when
certbot attempts a renewal, irrespective of necessity/success (2x per day).
"""
# Require current domain, to avoid confusion (e.g. call from old cron job).
if not arguments.domain:
print('Aborted: You must specify the current domain.')
sys.exit(1)
current_domain = config.get_domainname()
if not arguments.domain == current_domain:
print('Aborted: Current domain is %s, but called for %s.' %
(current_domain, arguments.domain))
sys.exit(2)
sys.exit(0)
def subcommand_delete(arguments):

View File

@ -25,7 +25,7 @@ from django.utils.translation import ugettext_lazy as _
from plinth import action_utils, actions
from plinth import app as app_module
from plinth import cfg, menu, module_loader
from plinth import cfg, menu
from plinth.errors import ActionError
from plinth.modules import config, names
from plinth.signals import domain_added, domain_removed, domainname_change
@ -62,7 +62,6 @@ description = [
manual_page = 'LetsEncrypt'
MODULES_WITH_HOOKS = ['ejabberd', 'matrixsynapse']
LIVE_DIRECTORY = '/etc/letsencrypt/live/'
logger = logging.getLogger(__name__)
@ -120,56 +119,12 @@ def try_action(domain, action):
actions.superuser_run('letsencrypt', [action, '--domain', domain])
def enable_renewal_management(domain):
if domain == config.get_domainname():
try:
actions.superuser_run('letsencrypt', ['manage_hooks', 'enable'])
logger.info(
_('Certificate renewal management enabled for {domain}.').
format(domain=domain))
except ActionError as exception:
logger.error(
_('Failed to enable certificate renewal management for '
'{domain}: {error}').format(domain=domain,
error=exception.args[2]))
def on_domainname_change(sender, old_domainname, new_domainname, **kwargs):
"""Disable renewal hook management after a domain name change."""
"""Drop the certificate after a domain name change."""
del sender # Unused
del new_domainname # Unused
del kwargs # Unused
for module in MODULES_WITH_HOOKS:
actions.superuser_run(
module, ['letsencrypt', 'drop', '--domain', old_domainname],
run_in_background=True)
actions.superuser_run(
'letsencrypt', ['manage_hooks', 'disable', '--domain', old_domainname],
run_in_background=True)
def get_manage_hooks_status():
"""Return status of hook management for current domain."""
try:
output = actions.superuser_run('letsencrypt',
['manage_hooks', 'status'])
except ActionError:
return False
return output.strip()
def get_installed_modules():
installed_modules = [
module_name
for module_name, module in module_loader.loaded_modules.items()
if module_name in MODULES_WITH_HOOKS
and module.setup_helper.get_state() == 'up-to-date'
]
return installed_modules
def on_domain_added(sender, domain_type='', name='', description='',
services=None, **kwargs):
@ -189,7 +144,6 @@ def on_domain_added(sender, domain_type='', name='', description='',
if sender != 'test' and name:
logger.info("Obtaining a Let\'s Encrypt certificate for " + name)
try_action(name, 'obtain')
enable_renewal_management(name)
return True
except ActionError as ex:
return False
@ -220,8 +174,6 @@ def get_status():
curr_dom,
'has_cert': (curr_dom in status['domains']
and status['domains'][curr_dom]['certificate_available']),
'manage_hooks_status':
get_manage_hooks_status()
}
status['current_domain'] = current_domain

View File

@ -35,7 +35,7 @@
{% block configuration %}
<h3>{% trans "Configuration" %}</h3>
<h3>{% trans "Status" %}</h3>
{% if status.domains %}
<table class="table table-bordered table-condensed table-striped">
@ -135,191 +135,6 @@
{% include "diagnostics_button.html" with module="letsencrypt" enabled=True %}
<h4>{% trans "Certificate renewal management and use by other modules" %}</h4>
<p class="help-block">
{% blocktrans trimmed %}
If you have a Let's Encrypt certificate for your current domain, you may
let {{ box_name }} manage its renewal process. This also enables other apps
to use that certificate, so that users would not prompted with security
warnings when using them.
{% endblocktrans %}
</p>
{% if status.current_domain.name %}
<form class="form" method="post"
action="{% url 'letsencrypt:toggle_hooks' status.current_domain.name %}">
{% csrf_token %}
<div class="checkbox">
<label>
{% if status.current_domain.has_cert %}
<input type="checkbox" name="toggle_hooks" id="id_toggle_hooks"
{% if 'enabled' in status.current_domain.manage_hooks_status %}
checked
{% endif %}
onchange="this.form.submit();">
</input>
<noscript>
<button class="btn btn-sm btn-default" type="submit">
{% trans "Update config" %}</button>
</noscript>
{% else %}
<input type="checkbox" name="toggle_hooks" id="id_toggle_hooks" disabled></input>
<noscript>
<button class="btn btn-sm btn-default disabled" type="submit">
{% trans "Update config" %}</button>
</noscript>
{% endif %}
<span>
{% blocktrans trimmed with current_domain=status.current_domain.name %}
Let {{ box_name }} manage certificate renewal of
<b>{{ current_domain }}</b>
{% endblocktrans %}
</span>
</label>
</div>
<p class="help-block">
{% if status.current_domain.has_cert %}
{% blocktrans trimmed %}
If enabled, {{ box_name }} can make sure that all apps can use the
certificate as soon as it is renewed.
{% endblocktrans %}
{% else %}
{% blocktrans trimmed %}
<b>No certificate available for the current domain.</b>
First obtain a certificate to enable its management.
{% endblocktrans %}
{% endif %}
</p>
</form>
<form class="form" method="post"
action="{% url 'letsencrypt:toggle_module' domain=status.current_domain.name module='ejabberd' %}">
{% csrf_token %}
<div class="checkbox">
<label>
{% if 'ejabberd' in installed_modules and 'enabled' in status.current_domain.manage_hooks_status %}
<input type="checkbox" name="ejabberd" id="ejabberd"
{% if 'ejabberd' in status.current_domain.manage_hooks_status %}
checked
{% endif %}
onchange="this.form.submit();">
</input>
<noscript>
<button class="btn btn-sm btn-default" type="submit">
{% trans "Update config" %}</button>
</noscript>
{% else %}
<input type="checkbox" name="ejabberd" id="ejabberd" disabled></input>
<noscript>
<button class="btn btn-sm btn-default disabled" type="submit">
{% trans "Update config" %}</button>
</noscript>
{% endif %}
<span>
{% if 'ejabberd' in installed_modules and 'enabled' in status.current_domain.manage_hooks_status %}
{% blocktrans trimmed with current_domain=status.current_domain.name %}
Use certificate of {{ current_domain }} for <b>ejabberd</b>
{% endblocktrans %}
{% else %}
{% blocktrans trimmed %}
Use certificate of the current domain for <b>ejabberd</b>
{% endblocktrans %}
{% endif %}
</span>
</label>
</div>
<p class="help-block">
{% url 'ejabberd:index' as ejabberd_url %}
{% if 'ejabberd' in installed_modules and 'enabled' in status.current_domain.manage_hooks_status %}
{% blocktrans trimmed %}
If enabled, the app <a href="{{ ejabberd_url }}">ejabberd</a>
will also use the Let's Encrypt certificate.
This will reduce warnings about self-signed certificates in
client applications, and enable more wide-spread federation with
other XMPP servers in the Internet.
{% endblocktrans %}
{% elif 'ejabberd' not in installed_modules %}
{% blocktrans trimmed %}
This feature only makes sense if you are using the
<a href="{{ ejabberd_url }}">ejabberd</a> chat server app.
{% endblocktrans %}
{% else %}
{% blocktrans trimmed %}
To use a Let's Encrypt certificate for
<a href="{{ ejabberd_url }}">ejabberd</a> chat server app, you
must first enable certificate renewal of the current domain.
{% endblocktrans %}
{% endif %}
</p>
</form>
<form class="form" method="post"
action="{% url 'letsencrypt:toggle_module' domain=status.current_domain.name module='matrixsynapse' %}">
{% csrf_token %}
<div class="checkbox">
<label>
{% if 'matrixsynapse' in installed_modules and 'enabled' in status.current_domain.manage_hooks_status %}
<input type="checkbox" name="matrixsynapse" id="matrixsynapse"
{% if 'matrixsynapse' in status.current_domain.manage_hooks_status %}
checked
{% endif %}
onchange="this.form.submit();">
</input>
<noscript>
<button class="btn btn-sm btn-default" type="submit">
{% trans "Update config" %}</button>
</noscript>
{% else %}
<input type="checkbox" name="matrixsynapse" id="matrixsynapse"
disabled></input>
<noscript>
<button class="btn btn-sm btn-default disabled" type="submit">
{% trans "Update config" %}</button>
</noscript>
{% endif %}
<span>
{% if 'matrixsynapse' in installed_modules and 'enabled' in status.current_domain.manage_hooks_status %}
{% blocktrans trimmed with current_domain=status.current_domain.name %}
Use certificate of {{ current_domain }} for <b>matrixsynapse</b>
{% endblocktrans %}
{% else %}
{% blocktrans trimmed %}
Use certificate of the current domain for <b>matrixsynapse</b>
{% endblocktrans %}
{% endif %}
</span>
</label>
</div>
<p class="help-block">
{% url 'matrixsynapse:index' as matrixsynapse_url %}
{% if 'matrixsynapse' in installed_modules and 'enabled' in status.current_domain.manage_hooks_status %}
{% blocktrans trimmed %}
If enabled, the app <a href="{{ matrixsynapse_url }}">Matrix Synapse</a>
will also use the same Let's Encrypt certificate.
Other Matrix Synapse instances running version 1.0 or greater expect
your server to produce a valid TLS certificate. Federation with other
instances will not work without this.
{% endblocktrans %}
{% elif 'matrixsynapse' not in installed_modules %}
{% blocktrans trimmed %}
This feature only makes sense if you are using the
<a href="{{ matrixsynapse_url }}">Matrix Synapse</a> chat server app.
{% endblocktrans %}
{% else %}
{% blocktrans trimmed %}
To use a Let's Encrypt certificate for
<a href="{{ matrixsynapse_url }}">Matrix Synapse</a> chat server app, you
must first enable certificate renewal of the current domain.
{% endblocktrans %}
{% endif %}
</p>
</form>
{% else %}
{% blocktrans trimmed %}
<b>No current domain is configured.</b>
First configure a domain to enable management of its certificates.
{% endblocktrans %}
{% endif %}
{% else %}
{% url 'config:index' as config_url %}
{% blocktrans trimmed %}

View File

@ -14,14 +14,13 @@
# 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/>.
#
"""
URLs for the Let's Encrypt module.
"""
from django.conf.urls import url
from . import views
from . import views
urlpatterns = [
url(r'^sys/letsencrypt/$', views.index, name='index'),
@ -31,8 +30,4 @@ urlpatterns = [
name='obtain'),
url(r'^sys/letsencrypt/delete/(?P<domain>[^/]+)/$', views.delete,
name='delete'),
url(r'^sys/letsencrypt/toggle_hooks/(?P<domain>[^/]+)/$',
views.toggle_hooks, name='toggle_hooks'),
url(r'^sys/letsencrypt/toggle_module/(?P<domain>[^/]+)/'
'(?P<module>[^/]+)/$', views.toggle_module, name='toggle_module'),
]

View File

@ -29,7 +29,7 @@ from django.views.decorators.http import require_POST
from plinth import actions
from plinth.errors import ActionError
from plinth.modules import config, letsencrypt
from plinth.modules import letsencrypt
logger = logging.getLogger(__name__)
@ -43,7 +43,6 @@ def index(request):
'description': letsencrypt.description,
'status': status,
'manual_page': letsencrypt.manual_page,
'installed_modules': letsencrypt.get_installed_modules()
})
@ -60,8 +59,8 @@ def revoke(request, domain):
except ActionError as exception:
messages.error(
request,
_('Failed to revoke certificate for domain {domain}: {error}')
.format(domain=domain, error=exception.args[2]))
_('Failed to revoke certificate for domain {domain}: {error}').
format(domain=domain, error=exception.args[2]))
return redirect(reverse_lazy('letsencrypt:index'))
@ -75,125 +74,17 @@ def obtain(request, domain):
request,
_('Certificate successfully obtained for domain {domain}').format(
domain=domain))
enable_renewal_management(request, domain)
except ActionError as exception:
messages.error(
request,
_('Failed to obtain certificate for domain {domain}: {error}')
.format(domain=domain, error=exception.args[2]))
return redirect(reverse_lazy('letsencrypt:index'))
def enable_renewal_management(request, domain):
if domain == config.get_domainname():
try:
actions.superuser_run('letsencrypt', ['manage_hooks', 'enable'])
messages.success(
request,
_('Certificate renewal management enabled for {domain}.')
.format(domain=domain))
except ActionError as exception:
messages.error(
request,
_('Failed to enable certificate renewal management for '
'{domain}: {error}').format(domain=domain,
error=exception.args[2]))
@require_POST
def toggle_hooks(request, domain):
"""Toggle pointing of certbot's hooks to FreedomBox.
For the current domain.
"""
manage_hooks_status = letsencrypt.get_manage_hooks_status()
subcommand = 'disable' if 'enabled' in manage_hooks_status else 'enable'
try:
if subcommand == 'disable':
enabled_modules = [
module for module in letsencrypt.MODULES_WITH_HOOKS
if module in manage_hooks_status
]
for module in enabled_modules:
actions.superuser_run(module, ['letsencrypt', 'drop'],
run_in_background=True)
actions.superuser_run('letsencrypt', ['manage_hooks', subcommand])
if subcommand == 'enable':
msg = _('Certificate renewal management enabled for {domain}.')\
.format(domain=domain)
else:
msg = _('Certificate renewal management disabled for {domain}.')\
.format(domain=domain)
messages.success(request, msg)
except ActionError as exception:
messages.error(
request,
_('Failed to switch certificate renewal management for {domain}: '
'{error}').format(domain=domain, error=exception.args[2]))
return redirect(reverse_lazy('letsencrypt:index'))
@require_POST
def toggle_module(request, domain, module):
"""Toggle usage of LE cert for a module name, for the current domain."""
manage_hooks_status = letsencrypt.get_manage_hooks_status()
enabled_modules = [
module for module in letsencrypt.MODULES_WITH_HOOKS
if module in manage_hooks_status
]
if module in enabled_modules:
mod_le_arg = 'drop'
enabled_modules.remove(module)
else:
mod_le_arg = 'add'
enabled_modules.append(module)
module_args = ['letsencrypt', mod_le_arg]
le_arguments = ['manage_hooks', 'enable']
if not enabled_modules == []:
le_arguments.extend(['--modules'] + enabled_modules)
try:
actions.superuser_run(module, module_args)
actions.superuser_run('letsencrypt', le_arguments)
messages.success(request,
_('Switched use of certificate for app {module}')
.format(module=module))
except ActionError as exception:
messages.error(
request,
_('Failed to switch certificate use for app {module}: {error}')
.format(module=module, error=exception.args[2]))
_('Failed to obtain certificate for domain {domain}: {error}').
format(domain=domain, error=exception.args[2]))
return redirect(reverse_lazy('letsencrypt:index'))
@require_POST
def delete(request, domain):
"""Delete a certificate for a given domain, and cleanup renewal config."""
manage_hooks_status = letsencrypt.get_manage_hooks_status()
enabled_modules = [
module for module in letsencrypt.MODULES_WITH_HOOKS
if module in manage_hooks_status
]
try:
for module in enabled_modules:
actions.superuser_run(module, ['letsencrypt', 'drop'],
run_in_background=True)
actions.superuser_run('letsencrypt',
['manage_hooks', 'disable', '--domain', domain])
except ActionError as exception:
messages.error(
request,
_('Failed to disable certificate renewal management for {domain}: '
'{error}').format(domain=domain, error=exception.args[2]))
try:
actions.superuser_run('letsencrypt', ['delete', '--domain', domain])
messages.success(
@ -203,7 +94,7 @@ def delete(request, domain):
except ActionError as exception:
messages.error(
request,
_('Failed to delete certificate for domain {domain}: {error}')
.format(domain=domain, error=exception.args[2]))
_('Failed to delete certificate for domain {domain}: {error}').
format(domain=domain, error=exception.args[2]))
return redirect(reverse_lazy('letsencrypt:index'))