Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
8224a72
[ADD] letsencrypt (#425)
lasley May 25, 2016
2ef38f5
[ADD] disable our cronjob for demo mode
hbrunn Jun 23, 2016
77b5d50
[MIG] Make modules uninstallable
pedrobaeza Oct 6, 2016
0691e97
[MIG] Rename manifest files
pedrobaeza Oct 6, 2016
9452180
[10.0][FIX] Make letsencrypt resilient for alternate name removal. (#…
NL66278 Apr 20, 2017
4d922e6
[10.0][MIG] Migrate letsencrypt
robyf70 May 19, 2017
2b67b28
OCA Transbot updated translations from Transifex
oca-transbot May 29, 2016
4aab44f
[MIG] letsencrypt: migration to 11.0
acysos May 13, 2018
83142ee
[UPD] Update letsencrypt.pot
oca-travis Jun 17, 2018
998e1f7
Translated using Weblate (Português (Brasil))
Jun 20, 2018
356ae48
[ADD] Adds support for Acme V2
Feb 8, 2018
5538843
[IMP] Refactor and debug Let's Encrypt ACMEv2 support
janverb Nov 15, 2019
f630486
[MIG] Migrate letsencrypt 2.0.0 to Odoo 11.0
janverb Apr 22, 2020
67ab94a
[FIX] letsencrypt: Increase test coverage
janverb May 28, 2020
3be4114
[MIG] Migrate letsencrypt 2.0.0 to Odoo 12.0
janverb May 28, 2020
214054d
[UPD] letsencrypt: Update hbrunn's email address
janverb Aug 11, 2020
9d5d0b6
[IMP] letsencrypt: Handle web.base.url in a saner way
janverb Aug 11, 2020
665fc46
[IMP] letsencrypt: black, isort, prettier
NL66278 Nov 19, 2020
fe237b7
[FIX] letsencrypt: adapt to conventions.
NL66278 Nov 19, 2020
262523c
[IMP] letsencrypt: black, isort, prettier
NL66278 Apr 7, 2021
fa35a6b
[MIG] letsencrypt: Migration to 14.0
NL66278 Apr 7, 2021
270bd40
[FIX] letsencrypt. Use char instead of text fields for config.
NL66278 Apr 7, 2021
dc8d7b6
[UPD] Update letsencrypt.pot
oca-travis Apr 30, 2021
4637303
[UPD] Update letsencrypt.pot
oca-travis Sep 2, 2021
1d8a55c
Update translation files
oca-transbot Sep 2, 2021
a34e885
[MIG] letsencrypt: Migration to 15.0
NL66278 Dec 15, 2021
ebb35a8
[FIX] letsencrypt: satisfy pre-commit checks
NL66278 Feb 17, 2022
88dfdb9
[UPD] Update letsencrypt.pot
Sep 1, 2022
0de613c
[UPD] README.rst
OCA-git-bot Sep 1, 2022
96e1834
Translated using Weblate (Spanish (Argentina))
ibuioli Sep 2, 2022
266100c
[FIX] letsencrypt: workaround blocked domain name example.com
StefanRijnhart Oct 19, 2022
60f68c4
letsencrypt 15.0.1.0.1
OCA-git-bot Oct 19, 2022
deef40e
[FIX] letsencrypt: freeze acme lib before version 2.0.0
petrus-v Dec 16, 2022
2567411
letsencrypt 15.0.1.0.2
OCA-git-bot Dec 16, 2022
bcd22d2
Translated using Weblate (Italian)
mymage Mar 23, 2023
ebb4e10
Translated using Weblate (Slovenian)
sysadminmatmoz Mar 31, 2023
ebffd70
[UPD] Update letsencrypt.pot
May 25, 2023
007c6e5
Update translation files
weblate May 25, 2023
13a9462
Translated using Weblate (Spanish (Argentina))
ibuioli May 25, 2023
e78b417
[FIX] letsencrypt: remove `cryptography` from python depends
EmilioSerna Jun 6, 2023
10a55f8
letsencrypt 15.0.1.0.3
OCA-git-bot Jun 7, 2023
2b5b62a
[UPD] README.rst
OCA-git-bot Sep 3, 2023
a9633fd
Update translation files
weblate Oct 9, 2023
f4f8c98
Translated using Weblate (Spanish)
Ivorra78 Oct 11, 2023
b9cebfb
[MIG] letsencrypt: Migration to 16.0
hbrunn Apr 22, 2024
9004773
[UPD] Update letsencrypt.pot
Sep 16, 2024
79c3ae4
Translated using Weblate (Italian)
mymage Sep 19, 2024
d5cb28b
[FIX] letsencrypt: Disable tests
pedrobaeza Oct 22, 2024
18fc1ab
[BOT] post-merge updates
OCA-git-bot Oct 22, 2024
2e02963
[ADD] letsencrypt: hbrunn as maintainer
hbrunn Nov 21, 2024
9a62364
[FIX] letsencrypt: don't talk to the letsencrypt server at all
hbrunn Nov 21, 2024
088047e
[BOT] post-merge updates
OCA-git-bot Dec 12, 2024
1810938
Translated using Weblate (Italian)
mymage Jan 16, 2025
0329499
[IMP] letsencrypt: pre-commit execution
hbrunn May 3, 2025
5837c5d
[MIG] letsencrypt: Migration to 17.0
hbrunn May 3, 2025
e1469fe
[IMP] letsencrypt: pre-commit auto fixes
hbrunn Mar 14, 2026
e80e574
[MIG] letsencrypt: Migration to 18.0
hbrunn Mar 14, 2026
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
233 changes: 233 additions & 0 deletions letsencrypt/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
=============
Let's Encrypt
=============

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:cdcc2b718b9ac4d05dc0a5b5d624f71c7402f605b11447a79d1d9161938f2a2d
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--tools-lightgray.png?logo=github
:target: https://github.com/OCA/server-tools/tree/18.0/letsencrypt
:alt: OCA/server-tools
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/server-tools-18-0/server-tools-18-0-letsencrypt
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/server-tools&target_branch=18.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

This module was written to have your Odoo installation request SSL
certificates from https://letsencrypt.org automatically.

**Table of contents**

.. contents::
:local:

Installation
============

After installation, this module generates a private key for your account
at letsencrypt.org automatically in
``$data_dir/letsencrypt/account.key``. If you want or need to use your
own account key, replace the file.

For certificate requests to work, your site needs to be accessible via
plain HTTP, see below for configuration examples in case you force your
clients to the SSL version.

After installation, trigger the cronjob Update letsencrypt certificates
and watch your log for messages.

Configuration
=============

This addons requests a certificate for the domain named in the
configuration parameter ``web.base.url`` - if this comes back as
``localhost`` or the like, the module doesn't request anything.

Futher self-explanatory settings are in Settings -> General Settings.
There you can add further domains to the CSR, add a custom script that
updates your DNS and add a script that will be used to reload your web
server (if needed). The number of domains that can be added to a
certificate is `capped at
100 <https://letsencrypt.org/docs/rate-limits/>`__. A wildcard
certificate can be used to avoid that limit.

Note that all those domains must be publicly reachable on port 80 via
HTTP, and they must have an entry for ``.well-known/acme-challenge``
pointing to ``$datadir/letsencrypt/acme-challenge`` of your odoo
instance.

Since DNS changes can take some time to propagate, when we respond to a
DNS challenge and the server tries to check our response, it might fail
(and probably will). The solution to this is documented in
https://tools.ietf.org/html/rfc8555#section-8.2 and basically is a
``Retry-After`` header under which we can instruct the server to retry
the challenge. At the time these lines were written, Boulder had not
implemented this functionality. This prompted us to use
``letsencrypt.backoff`` configuration parameter, which is the amount of
minutes this module will try poll the server to retry validating the
answer to our challenge, specifically it is the ``deadline`` parameter
of ``poll_and_finalize``.

Usage
=====

The module sets up a cronjob that requests and renews certificates
automatically.

Certificates are renewed a month before they expire. Renewal is then
attempted every day until it succeeds.

After the first run, you'll find a file called ``domain.crt`` in
``$datadir/letsencrypt``, configure your SSL proxy to use this file as
certificate.

In depth configuration
----------------------

If you want to use multiple domains on your CSR then you have to
configure them from Settings -> General Settings. If you use a wildcard
in any of those domains then letsencrypt will return a DNS challenge. In
order for that challenge to be answered you will need to **either**
provide a script (as seen in General Settings) or install a module that
provides support for your DNS provider. In that module you will need to
create a function in the letsencrypt model with the name
``_respond_challenge_dns_$DNS_PROVIDER`` where ``$DNS_PROVIDER`` is the
name of your provider and can be any string with length greater than
zero, and add the name of your DNS provider in the settings dns_provider
selection field.

In any case if a script path is inserted in the settings page, it will
be run in case you want to update multiple DNS servers.

A reload command can be set in the Settings as well in case you need to
reload your web server. This by default is
``sudo /usr/sbin/service nginx reload``

You'll also need a matching sudo configuration, like:

::

your_odoo_user ALL = NOPASSWD: /usr/sbin/service nginx reload

Further, if you force users to https, you'll need something like for
nginx:

::

if ($scheme = "http") {
set $redirect_https 1;
}
if ($request_uri ~ ^/.well-known/acme-challenge/) {
set $redirect_https 0;
}
if ($redirect_https) {
rewrite ^ https://$server_name$request_uri? permanent;
}

and this for apache:

::

RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteCond %{REQUEST_URI} "!^/.well-known/"
RewriteRule ^/?(.*) https://%{SERVER_NAME}/$1 [R,L]

In case you need to redirect other nginx sites to your Odoo instance,
declare an upstream for your odoo instance and do something like:

::

location /.well-known {
proxy_pass http://yourodooupstream;
}

If you're using a multi-database installation (with or without dbfilter
option) where /web/databse/selector returns a list of more than one
database, then you need to add ``letsencrypt`` addon to wide load addons
list (by default, only ``web`` addon), setting ``--load`` option. For
example, ``--load=web,letsencrypt``

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/server-tools/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/server-tools/issues/new?body=module:%20letsencrypt%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Do not contact contributors directly about support or help with technical issues.

Credits
=======

Authors
-------

* Therp BV
* Tecnativa
* Acysos S.L

Contributors
------------

- Holger Brunn <mail@hunki-enterprises.nl>
- Antonio Espinosa <antonio.espinosa@tecnativa.com>
- Dave Lasley <dave@laslabs.com>
- Ronald Portier <ronald@therp.nl>
- Ignacio Ibeas <ignacio@acysos.com>
- George Daramouskas <gdaramouskas@therp.nl>
- Jan Verbeek <jverbeek@therp.nl>

Other credits
-------------

ACME implementation
~~~~~~~~~~~~~~~~~~~

- https://github.com/certbot/certbot/tree/0.22.x/acme

Icon
~~~~

- https://helloworld.letsencrypt.org

Maintainers
-----------

This module is maintained by the OCA.

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

.. |maintainer-hbrunn| image:: https://github.com/hbrunn.png?size=40px
:target: https://github.com/hbrunn
:alt: hbrunn

Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:

|maintainer-hbrunn|

This module is part of the `OCA/server-tools <https://github.com/OCA/server-tools/tree/18.0/letsencrypt>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
4 changes: 4 additions & 0 deletions letsencrypt/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from . import models
from . import controllers
from .hooks import post_init_hook
22 changes: 22 additions & 0 deletions letsencrypt/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# 2016-2021 Therp BV <https://therp.nl>.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
{
"name": "Let's Encrypt",
"version": "18.0.1.0.0",
"author": "Therp BV," "Tecnativa," "Acysos S.L," "Odoo Community Association (OCA)",
"website": "https://github.com/OCA/server-tools",
"license": "AGPL-3",
"category": "Tools",
"summary": "Request SSL certificates from letsencrypt.org",
"depends": ["base_setup"],
"maintainers": ["hbrunn"],
"data": [
"data/ir_config_parameter.xml",
"data/ir_cron.xml",
"views/res_config_settings.xml",
],
"demo": ["demo/ir_cron.xml"],
"post_init_hook": "post_init_hook",
"installable": True,
"external_dependencies": {"python": ["acme", "dnspython", "josepy"]},
}
2 changes: 2 additions & 0 deletions letsencrypt/controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from . import main
31 changes: 31 additions & 0 deletions letsencrypt/controllers/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Copyright 2016,2022 Therp BV <https://therp.nl>.
# Copyright 2016 Antonio Espinosa <antonio.espinosa@tecnativa.com>.
# Copyright 2018 Ignacio Ibeas <ignacio@acysos.com>.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
# pylint: disable=too-few-public-methods,no-self-use
"""This controller handles the acme challenge call from Letsencrypt."""

import logging
import os

from odoo import _, http
from odoo.http import request

from ..models.letsencrypt import _get_challenge_dir

_logger = logging.getLogger(__name__)


class Letsencrypt(http.Controller):
"""This controller handles the acme challenge call from Letsencrypt."""

@http.route("/.well-known/acme-challenge/<filename>", auth="none")
def acme_challenge(self, filename):
"""Handle the acme challenge."""
path = os.path.join(_get_challenge_dir(), filename)
try:
with open(path, encoding="utf-8") as key:
return key.read()
except OSError:
_logger.exception(_("Error opening file %s"), path)
raise request.not_found()
11 changes: 11 additions & 0 deletions letsencrypt/data/ir_config_parameter.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo noupdate="1">
<record id="config_parameter_reload" model="ir.config_parameter" forcecreate="True">
<field name="key">letsencrypt.reload_command</field>
<field name="value">sudo /usr/sbin/service nginx reload</field>
</record>
<record id="letsencrypt_backoff" model="ir.config_parameter" forcecreate="True">
<field name="key">letsencrypt.backoff</field>
<field name="value">3</field>
</record>
</odoo>
11 changes: 11 additions & 0 deletions letsencrypt/data/ir_cron.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo noupdate="1">
<record id="cronjob" model="ir.cron">
<field name="name">Check Let's Encrypt certificates</field>
<field name="model_id" ref="model_letsencrypt" />
<field name="state">code</field>
<field name="code">model._cron()</field>
<field name="interval_type">days</field>
<field name="interval_number">1</field>
</record>
</odoo>
6 changes: 6 additions & 0 deletions letsencrypt/demo/ir_cron.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="cronjob" model="ir.cron">
<field name="active" eval="False" />
</record>
</odoo>
6 changes: 6 additions & 0 deletions letsencrypt/hooks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Copyright 2016-2020 Therp BV <https://therp.nl>.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).


def post_init_hook(env):
env["letsencrypt"]._get_key("account.key")
Loading
Loading