mirror of
https://github.com/certbot/certbot.git
synced 2026-06-08 16:22:18 -04:00
Fixing conflicts
This commit is contained in:
commit
a1cb24b3ef
90 changed files with 3450 additions and 924 deletions
25
.travis.yml
25
.travis.yml
|
|
@ -4,26 +4,18 @@ cache:
|
|||
directories:
|
||||
- $HOME/.cache/pip
|
||||
|
||||
services:
|
||||
- rabbitmq
|
||||
- mariadb
|
||||
# apacheconftest
|
||||
#- apache2
|
||||
# This makes sure we get a host with docker-compose present.
|
||||
dist: trusty
|
||||
|
||||
# http://docs.travis-ci.com/user/ci-environment/#CI-environment-OS
|
||||
# gimme has to be kept in sync with Boulder's Go version setting in .travis.yml
|
||||
before_install:
|
||||
- 'dpkg -s libaugeas0'
|
||||
- '[ "xxx$BOULDER_INTEGRATION" = "xxx" ] || eval "$(gimme 1.5.1)"'
|
||||
|
||||
# using separate envs with different TOXENVs creates 4x1 Travis build
|
||||
# matrix, which allows us to clearly distinguish which component under
|
||||
# test has failed
|
||||
env:
|
||||
global:
|
||||
- GOPATH=/tmp/go
|
||||
- PATH=$GOPATH/bin:$PATH
|
||||
- GO15VENDOREXPERIMENT=1 # Fixes problems with vendor directories
|
||||
- BOULDERPATH=$PWD/boulder/
|
||||
|
||||
matrix:
|
||||
include:
|
||||
|
|
@ -93,7 +85,6 @@ addons:
|
|||
- boulder
|
||||
- boulder-mysql
|
||||
- boulder-rabbitmq
|
||||
mariadb: "10.0"
|
||||
apt:
|
||||
sources:
|
||||
- augeas
|
||||
|
|
@ -109,13 +100,11 @@ addons:
|
|||
# For certbot-nginx integration testing
|
||||
- nginx-light
|
||||
- openssl
|
||||
# For Boulder integration testing
|
||||
- rsyslog
|
||||
# for apacheconftest
|
||||
#- apache2
|
||||
#- libapache2-mod-wsgi
|
||||
#- libapache2-mod-macro
|
||||
#- sudo
|
||||
- apache2
|
||||
- libapache2-mod-wsgi
|
||||
- libapache2-mod-macro
|
||||
- sudo
|
||||
|
||||
install: "travis_retry pip install tox coveralls"
|
||||
script: 'travis_retry tox && ([ "xxx$BOULDER_INTEGRATION" = "xxx" ] || ./tests/travis-integration.sh)'
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ ChangeLog
|
|||
|
||||
Please note:
|
||||
the change log will only get updated after first release - for now please use the
|
||||
`commit log <https://github.com/letsencrypt/letsencrypt/commits/master>`_.
|
||||
`commit log <https://github.com/certbot/certbot/commits/master>`_.
|
||||
|
||||
To see the changes in a given release, inspect the github milestone for the
|
||||
release. For instance:
|
||||
|
||||
https://github.com/letsencrypt/letsencrypt/issues?utf8=%E2%9C%93&q=milestone%3A0.3.0
|
||||
https://github.com/certbot/certbot/issues?utf8=%E2%9C%93&q=milestone%3A0.3.0
|
||||
|
|
|
|||
|
|
@ -15,4 +15,4 @@ to the Sphinx generated docs is provided below.
|
|||
|
||||
-->
|
||||
|
||||
https://letsencrypt.readthedocs.org/en/latest/contributing.html
|
||||
https://certbot.eff.org/docs/contributing.html
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ WORKDIR /opt/certbot
|
|||
# If <dest> doesn't exist, it is created along with all missing
|
||||
# directories in its path.
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
COPY letsencrypt-auto-source/letsencrypt-auto /opt/certbot/src/letsencrypt-auto-source/letsencrypt-auto
|
||||
RUN /opt/certbot/src/letsencrypt-auto-source/letsencrypt-auto --os-packages-only && \
|
||||
|
|
|
|||
105
README.rst
105
README.rst
|
|
@ -3,51 +3,71 @@
|
|||
Disclaimer
|
||||
==========
|
||||
|
||||
The Let's Encrypt Client is **BETA SOFTWARE**. It contains plenty of bugs and
|
||||
rough edges, and should be tested thoroughly in staging environments before use
|
||||
on production systems.
|
||||
Certbot (previously, the Let's Encrypt client) is **BETA SOFTWARE**. It
|
||||
contains plenty of bugs and rough edges, and should be tested thoroughly in
|
||||
staging environments before use on production systems.
|
||||
|
||||
For more information regarding the status of the project, please see
|
||||
https://letsencrypt.org. Be sure to checkout the
|
||||
`Frequently Asked Questions (FAQ) <https://community.letsencrypt.org/t/frequently-asked-questions-faq/26#topic-title>`_.
|
||||
|
||||
About the Let's Encrypt Client
|
||||
About Certbot
|
||||
==============================
|
||||
|
||||
The Let's Encrypt Client is a fully-featured, extensible client for the Let's
|
||||
Certbot is a fully-featured, extensible client for the Let's
|
||||
Encrypt CA (or any other CA that speaks the `ACME
|
||||
<https://github.com/ietf-wg-acme/acme/blob/master/draft-ietf-acme-acme.md>`_
|
||||
protocol) that can automate the tasks of obtaining certificates and
|
||||
configuring webservers to use them. This client runs on Unix-based operating
|
||||
systems.
|
||||
|
||||
Until May 2016, Certbot was named simply ``letsencrypt`` or ``letsencrypt-auto``,
|
||||
depending on install method. Instructions on the Internet, and some pieces of the
|
||||
software, may still refer to this older name.
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
If you'd like to contribute to this project please read `Developer Guide
|
||||
<https://certbot.eff.org/docs/contributing.html>`_.
|
||||
|
||||
.. _installation:
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
If ``letsencrypt`` is packaged for your Unix OS, you can install it from
|
||||
there, and run it by typing ``letsencrypt``. Because not all operating
|
||||
systems have packages yet, we provide a temporary solution via the
|
||||
``letsencrypt-auto`` wrapper script, which obtains some dependencies
|
||||
from your OS and puts others in a python virtual environment::
|
||||
If ``certbot`` (or ``letsencrypt``) is packaged for your Unix OS (visit
|
||||
certbot.eff.org_ to find out), you can install it
|
||||
from there, and run it by typing ``certbot`` (or ``letsencrypt``). Because
|
||||
not all operating systems have packages yet, we provide a temporary solution
|
||||
via the ``certbot-auto`` wrapper script, which obtains some dependencies from
|
||||
your OS and puts others in a python virtual environment::
|
||||
|
||||
user@webserver:~$ git clone https://github.com/letsencrypt/letsencrypt
|
||||
user@webserver:~$ cd letsencrypt
|
||||
user@webserver:~/letsencrypt$ ./letsencrypt-auto --help
|
||||
user@webserver:~$ wget https://dl.eff.org/certbot-auto
|
||||
user@webserver:~$ chmod a+x ./certbot-auto
|
||||
user@webserver:~$ ./certbot-auto --help
|
||||
|
||||
Or for full command line help, type::
|
||||
.. hint:: The certbot-auto download is protected by HTTPS, which is pretty good, but if you'd like to
|
||||
double check the integrity of the ``certbot-auto`` script, you can use these steps for verification before running it::
|
||||
|
||||
./letsencrypt-auto --help all
|
||||
user@server:~$ wget -N https://dl.eff.org/certbot-auto.asc
|
||||
user@server:~$ gpg2 --recv-key A2CFB51FA275A7286234E7B24D17C995CD9775F2
|
||||
user@server:~$ gpg2 --trusted-key 4D17C995CD9775F2 --verify certbot-auto.asc certbot-auto
|
||||
|
||||
``letsencrypt-auto`` updates to the latest client release automatically. And
|
||||
since ``letsencrypt-auto`` is a wrapper to ``letsencrypt``, it accepts exactly
|
||||
And for full command line help, you can type::
|
||||
|
||||
./certbot-auto --help all
|
||||
|
||||
``certbot-auto`` updates to the latest client release automatically. And
|
||||
since ``certbot-auto`` is a wrapper to ``certbot``, it accepts exactly
|
||||
the same command line flags and arguments. More details about this script and
|
||||
other installation methods can be found `in the User Guide
|
||||
<https://letsencrypt.readthedocs.org/en/latest/using.html#installation>`_.
|
||||
<https://certbot.eff.org/docs/using.html#installation>`_.
|
||||
|
||||
How to run the client
|
||||
---------------------
|
||||
|
||||
In many cases, you can just run ``letsencrypt-auto`` or ``letsencrypt``, and the
|
||||
In many cases, you can just run ``certbot-auto`` or ``certbot``, and the
|
||||
client will guide you through the process of obtaining and installing certs
|
||||
interactively.
|
||||
|
||||
|
|
@ -56,7 +76,7 @@ For instance, if you want to obtain a cert for ``example.com``,
|
|||
``www.example.com``, and ``other.example.net``, using the Apache plugin to both
|
||||
obtain and install the certs, you could do this::
|
||||
|
||||
./letsencrypt-auto --apache -d example.com -d www.example.com -d other.example.net
|
||||
./certbot-auto --apache -d example.com -d www.example.com -d other.example.net
|
||||
|
||||
(The first time you run the command, it will make an account, and ask for an
|
||||
email and agreement to the Let's Encrypt Subscriber Agreement; you can
|
||||
|
|
@ -65,7 +85,7 @@ automate those with ``--email`` and ``--agree-tos``)
|
|||
If you want to use a webserver that doesn't have full plugin support yet, you
|
||||
can still use "standalone" or "webroot" plugins to obtain a certificate::
|
||||
|
||||
./letsencrypt-auto certonly --standalone --email admin@example.com -d example.com -d www.example.com -d other.example.net
|
||||
./certbot-auto certonly --standalone --email admin@example.com -d example.com -d www.example.com -d other.example.net
|
||||
|
||||
|
||||
Understanding the client in more depth
|
||||
|
|
@ -73,21 +93,21 @@ Understanding the client in more depth
|
|||
|
||||
To understand what the client is doing in detail, it's important to
|
||||
understand the way it uses plugins. Please see the `explanation of
|
||||
plugins <https://letsencrypt.readthedocs.org/en/latest/using.html#plugins>`_ in
|
||||
plugins <https://certbot.eff.org/docs/using.html#plugins>`_ in
|
||||
the User Guide.
|
||||
|
||||
Links
|
||||
=====
|
||||
|
||||
Documentation: https://letsencrypt.readthedocs.org
|
||||
Documentation: https://certbot.eff.org/docs
|
||||
|
||||
Software project: https://github.com/letsencrypt/letsencrypt
|
||||
Software project: https://github.com/certbot/certbot
|
||||
|
||||
Notes for developers: https://letsencrypt.readthedocs.org/en/latest/contributing.html
|
||||
Notes for developers: https://certbot.eff.org/docs/contributing.html
|
||||
|
||||
Main Website: https://letsencrypt.org/
|
||||
|
||||
IRC Channel: #letsencrypt on `Freenode`_
|
||||
IRC Channel: #letsencrypt on `Freenode`_ or #certbot on `OFTC`_
|
||||
|
||||
Community: https://community.letsencrypt.org
|
||||
|
||||
|
|
@ -103,12 +123,12 @@ email to client-dev+subscribe@letsencrypt.org)
|
|||
|
||||
|
||||
|
||||
.. |build-status| image:: https://travis-ci.org/letsencrypt/letsencrypt.svg?branch=master
|
||||
:target: https://travis-ci.org/letsencrypt/letsencrypt
|
||||
.. |build-status| image:: https://travis-ci.org/certbot/certbot.svg?branch=master
|
||||
:target: https://travis-ci.org/certbot/certbot
|
||||
:alt: Travis CI status
|
||||
|
||||
.. |coverage| image:: https://coveralls.io/repos/letsencrypt/letsencrypt/badge.svg?branch=master
|
||||
:target: https://coveralls.io/r/letsencrypt/letsencrypt
|
||||
.. |coverage| image:: https://coveralls.io/repos/certbot/certbot/badge.svg?branch=master
|
||||
:target: https://coveralls.io/r/certbot/certbot
|
||||
:alt: Coverage status
|
||||
|
||||
.. |docs| image:: https://readthedocs.org/projects/letsencrypt/badge/
|
||||
|
|
@ -128,16 +148,15 @@ System Requirements
|
|||
===================
|
||||
|
||||
The Let's Encrypt Client presently only runs on Unix-ish OSes that include
|
||||
Python 2.6 or 2.7; Python 3.x support will be added after the Public Beta
|
||||
launch. The client requires root access in order to write to
|
||||
``/etc/letsencrypt``, ``/var/log/letsencrypt``, ``/var/lib/letsencrypt``; to
|
||||
bind to ports 80 and 443 (if you use the ``standalone`` plugin) and to read and
|
||||
modify webserver configurations (if you use the ``apache`` or ``nginx``
|
||||
plugins). If none of these apply to you, it is theoretically possible to run
|
||||
without root privileges, but for most users who want to avoid running an ACME
|
||||
client as root, either `letsencrypt-nosudo
|
||||
<https://github.com/diafygi/letsencrypt-nosudo>`_ or `simp_le
|
||||
<https://github.com/kuba/simp_le>`_ are more appropriate choices.
|
||||
Python 2.6 or 2.7; Python 3.x support will hopefully be added in the future. The
|
||||
client requires root access in order to write to ``/etc/letsencrypt``,
|
||||
``/var/log/letsencrypt``, ``/var/lib/letsencrypt``; to bind to ports 80 and 443
|
||||
(if you use the ``standalone`` plugin) and to read and modify webserver
|
||||
configurations (if you use the ``apache`` or ``nginx`` plugins). If none of
|
||||
these apply to you, it is theoretically possible to run without root privileges,
|
||||
but for most users who want to avoid running an ACME client as root, either
|
||||
`letsencrypt-nosudo <https://github.com/diafygi/letsencrypt-nosudo>`_ or
|
||||
`simp_le <https://github.com/kuba/simp_le>`_ are more appropriate choices.
|
||||
|
||||
The Apache plugin currently requires a Debian-based OS with augeas version
|
||||
1.0; this includes Ubuntu 12.04+ and Debian 7+.
|
||||
|
|
@ -152,10 +171,10 @@ Current Features
|
|||
- standalone (runs its own simple webserver to prove you control a domain)
|
||||
- webroot (adds files to webroot directories in order to prove control of
|
||||
domains and obtain certs)
|
||||
- nginx/0.8.48+ (highly experimental, not included in letsencrypt-auto)
|
||||
- nginx/0.8.48+ (highly experimental, not included in certbot-auto)
|
||||
|
||||
* The private key is generated locally on your system.
|
||||
* Can talk to the Let's Encrypt CA or optionally to other ACME
|
||||
* Can talk to the Let's Encrypt CA or optionally to other ACME
|
||||
compliant services.
|
||||
* Can get domain-validated (DV) certificates.
|
||||
* Can revoke certificates.
|
||||
|
|
@ -170,4 +189,6 @@ Current Features
|
|||
|
||||
|
||||
.. _Freenode: https://webchat.freenode.net?channels=%23letsencrypt
|
||||
.. _OFTC: https://webchat.oftc.net?channels=%23certbot
|
||||
.. _client-dev: https://groups.google.com/a/letsencrypt.org/forum/#!forum/client-dev
|
||||
.. _certbot.eff.org: https://certbot.eff.org/
|
||||
|
|
|
|||
|
|
@ -512,6 +512,10 @@ class ClientNetwork(object): # pylint: disable=too-many-instance-attributes
|
|||
self.verify_ssl = verify_ssl
|
||||
self._nonces = set()
|
||||
self.user_agent = user_agent
|
||||
self.session = requests.Session()
|
||||
|
||||
def __del__(self):
|
||||
self.session.close()
|
||||
|
||||
def _wrap_in_jws(self, obj, nonce):
|
||||
"""Wrap `JSONDeSerializable` object in JWS.
|
||||
|
|
@ -606,7 +610,7 @@ class ClientNetwork(object): # pylint: disable=too-many-instance-attributes
|
|||
kwargs['verify'] = self.verify_ssl
|
||||
kwargs.setdefault('headers', {})
|
||||
kwargs['headers'].setdefault('User-Agent', self.user_agent)
|
||||
response = requests.request(method, url, *args, **kwargs)
|
||||
response = self.session.request(method, url, *args, **kwargs)
|
||||
logging.debug('Received %s. Headers: %s. Content: %r',
|
||||
response, response.headers, response.content)
|
||||
return response
|
||||
|
|
|
|||
|
|
@ -484,9 +484,11 @@ class ClientNetworkTest(unittest.TestCase):
|
|||
def test_check_response_not_ok_jobj_no_error(self):
|
||||
self.response.ok = False
|
||||
self.response.json.return_value = {}
|
||||
# pylint: disable=protected-access
|
||||
self.assertRaises(
|
||||
errors.ClientError, self.net._check_response, self.response)
|
||||
with mock.patch('acme.client.messages.Error.from_json') as from_json:
|
||||
from_json.side_effect = jose.DeserializationError
|
||||
# pylint: disable=protected-access
|
||||
self.assertRaises(
|
||||
errors.ClientError, self.net._check_response, self.response)
|
||||
|
||||
def test_check_response_not_ok_jobj_error(self):
|
||||
self.response.ok = False
|
||||
|
|
@ -528,40 +530,49 @@ class ClientNetworkTest(unittest.TestCase):
|
|||
self.assertEqual(
|
||||
self.response, self.net._check_response(self.response))
|
||||
|
||||
@mock.patch('acme.client.requests')
|
||||
def test_send_request(self, mock_requests):
|
||||
mock_requests.request.return_value = self.response
|
||||
def test_send_request(self):
|
||||
self.net.session = mock.MagicMock()
|
||||
self.net.session.request.return_value = self.response
|
||||
# pylint: disable=protected-access
|
||||
self.assertEqual(self.response, self.net._send_request(
|
||||
'HEAD', 'url', 'foo', bar='baz'))
|
||||
mock_requests.request.assert_called_once_with(
|
||||
'HEAD', 'url', 'foo', verify=mock.ANY, bar='baz', headers=mock.ANY)
|
||||
'HEAD', 'http://example.com/', 'foo', bar='baz'))
|
||||
self.net.session.request.assert_called_once_with(
|
||||
'HEAD', 'http://example.com/', 'foo',
|
||||
headers=mock.ANY, verify=mock.ANY, bar='baz')
|
||||
|
||||
@mock.patch('acme.client.requests')
|
||||
def test_send_request_verify_ssl(self, mock_requests):
|
||||
def test_send_request_verify_ssl(self):
|
||||
# pylint: disable=protected-access
|
||||
for verify in True, False:
|
||||
mock_requests.request.reset_mock()
|
||||
mock_requests.request.return_value = self.response
|
||||
self.net.session = mock.MagicMock()
|
||||
self.net.session.request.return_value = self.response
|
||||
self.net.verify_ssl = verify
|
||||
# pylint: disable=protected-access
|
||||
self.assertEqual(
|
||||
self.response, self.net._send_request('GET', 'url'))
|
||||
mock_requests.request.assert_called_once_with(
|
||||
'GET', 'url', verify=verify, headers=mock.ANY)
|
||||
self.response,
|
||||
self.net._send_request('GET', 'http://example.com/'))
|
||||
self.net.session.request.assert_called_once_with(
|
||||
'GET', 'http://example.com/', verify=verify, headers=mock.ANY)
|
||||
|
||||
@mock.patch('acme.client.requests')
|
||||
def test_send_request_user_agent(self, mock_requests):
|
||||
mock_requests.request.return_value = self.response
|
||||
def test_send_request_user_agent(self):
|
||||
self.net.session = mock.MagicMock()
|
||||
# pylint: disable=protected-access
|
||||
self.net._send_request('GET', 'url', headers={'bar': 'baz'})
|
||||
mock_requests.request.assert_called_once_with(
|
||||
'GET', 'url', verify=mock.ANY,
|
||||
self.net._send_request('GET', 'http://example.com/',
|
||||
headers={'bar': 'baz'})
|
||||
self.net.session.request.assert_called_once_with(
|
||||
'GET', 'http://example.com/', verify=mock.ANY,
|
||||
headers={'User-Agent': 'acme-python-test', 'bar': 'baz'})
|
||||
|
||||
self.net._send_request('GET', 'url', headers={'User-Agent': 'foo2'})
|
||||
mock_requests.request.assert_called_with(
|
||||
'GET', 'url', verify=mock.ANY, headers={'User-Agent': 'foo2'})
|
||||
self.net._send_request('GET', 'http://example.com/',
|
||||
headers={'User-Agent': 'foo2'})
|
||||
self.net.session.request.assert_called_with(
|
||||
'GET', 'http://example.com/',
|
||||
verify=mock.ANY, headers={'User-Agent': 'foo2'})
|
||||
|
||||
def test_del(self):
|
||||
sess = mock.MagicMock()
|
||||
self.net.session = sess
|
||||
del self.net
|
||||
sess.close.assert_called_once()
|
||||
|
||||
@mock.patch('acme.client.requests')
|
||||
def test_requests_error_passthrough(self, mock_requests):
|
||||
|
|
@ -614,14 +625,16 @@ class ClientNetworkWithMockedResponseTest(unittest.TestCase):
|
|||
return self.checked_response
|
||||
|
||||
def test_head(self):
|
||||
self.assertEqual(self.response, self.net.head('url', 'foo', bar='baz'))
|
||||
self.assertEqual(self.response, self.net.head(
|
||||
'http://example.com/', 'foo', bar='baz'))
|
||||
self.send_request.assert_called_once_with(
|
||||
'HEAD', 'url', 'foo', bar='baz')
|
||||
'HEAD', 'http://example.com/', 'foo', bar='baz')
|
||||
|
||||
def test_get(self):
|
||||
self.assertEqual(self.checked_response, self.net.get(
|
||||
'url', content_type=self.content_type, bar='baz'))
|
||||
self.send_request.assert_called_once_with('GET', 'url', bar='baz')
|
||||
'http://example.com/', content_type=self.content_type, bar='baz'))
|
||||
self.send_request.assert_called_once_with(
|
||||
'GET', 'http://example.com/', bar='baz')
|
||||
|
||||
def test_post(self):
|
||||
# pylint: disable=protected-access
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
"""Crypto utilities."""
|
||||
import binascii
|
||||
import contextlib
|
||||
import logging
|
||||
import re
|
||||
|
|
@ -203,7 +204,7 @@ def gen_ss_cert(key, domains, not_before=None,
|
|||
"""
|
||||
assert domains, "Must provide one or more hostnames for the cert."
|
||||
cert = OpenSSL.crypto.X509()
|
||||
cert.set_serial_number(1337)
|
||||
cert.set_serial_number(int(binascii.hexlify(OpenSSL.rand.bytes(16)), 16))
|
||||
cert.set_version(2)
|
||||
|
||||
extensions = [
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ import unittest
|
|||
import six
|
||||
from six.moves import socketserver # pylint: disable=import-error
|
||||
|
||||
import OpenSSL
|
||||
|
||||
from acme import errors
|
||||
from acme import jose
|
||||
from acme import test_util
|
||||
|
|
@ -126,5 +128,23 @@ class PyOpenSSLCertOrReqSANTest(unittest.TestCase):
|
|||
self._get_idn_names())
|
||||
|
||||
|
||||
class RandomSnTest(unittest.TestCase):
|
||||
"""Test for random certificate serial numbers."""
|
||||
|
||||
def setUp(self):
|
||||
self.cert_count = 5
|
||||
self.serial_num = []
|
||||
self.key = OpenSSL.crypto.PKey()
|
||||
self.key.generate_key(OpenSSL.crypto.TYPE_RSA, 2048)
|
||||
|
||||
def test_sn_collisions(self):
|
||||
from acme.crypto_util import gen_ss_cert
|
||||
|
||||
for _ in range(self.cert_count):
|
||||
cert = gen_ss_cert(self.key, ['dummy'], force_san=True)
|
||||
self.serial_num.append(cert.get_serial_number())
|
||||
self.assertTrue(len(set(self.serial_num)) > 1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main() # pragma: no cover
|
||||
|
|
|
|||
|
|
@ -37,9 +37,9 @@ class Error(jose.JSONObjectWithFields, errors.Error):
|
|||
)
|
||||
)
|
||||
|
||||
typ = jose.Field('type')
|
||||
typ = jose.Field('type', omitempty=True, default='about:blank')
|
||||
title = jose.Field('title', omitempty=True)
|
||||
detail = jose.Field('detail')
|
||||
detail = jose.Field('detail', omitempty=True)
|
||||
|
||||
@property
|
||||
def description(self):
|
||||
|
|
@ -143,12 +143,6 @@ class Directory(jose.JSONDeSerializable):
|
|||
|
||||
def __init__(self, jobj):
|
||||
canon_jobj = util.map_keys(jobj, self._canon_key)
|
||||
if not set(canon_jobj).issubset(
|
||||
set(self._REGISTERED_TYPES).union(['meta'])):
|
||||
# TODO: acme-spec is not clear about this: 'It is a JSON
|
||||
# dictionary, whose keys are the "resource" values listed
|
||||
# in {{https-requests}}'
|
||||
raise ValueError('Wrong directory fields')
|
||||
# TODO: check that everything is an absolute URL; acme-spec is
|
||||
# not clear on that
|
||||
self._jobj = canon_jobj
|
||||
|
|
@ -171,10 +165,7 @@ class Directory(jose.JSONDeSerializable):
|
|||
@classmethod
|
||||
def from_json(cls, jobj):
|
||||
jobj['meta'] = cls.Meta.from_json(jobj.pop('meta', {}))
|
||||
try:
|
||||
return cls(jobj)
|
||||
except ValueError as error:
|
||||
raise jose.DeserializationError(str(error))
|
||||
return cls(jobj)
|
||||
|
||||
|
||||
class Resource(jose.JSONObjectWithFields):
|
||||
|
|
|
|||
|
|
@ -28,6 +28,14 @@ class ErrorTest(unittest.TestCase):
|
|||
self.error_custom = Error(typ='custom', detail='bar')
|
||||
self.jobj_cusom = {'type': 'custom', 'detail': 'bar'}
|
||||
|
||||
def test_default_typ(self):
|
||||
from acme.messages import Error
|
||||
self.assertEqual(Error().typ, 'about:blank')
|
||||
|
||||
def test_from_json_empty(self):
|
||||
from acme.messages import Error
|
||||
self.assertEqual(Error(), Error.from_json('{}'))
|
||||
|
||||
def test_from_json_hashable(self):
|
||||
from acme.messages import Error
|
||||
hash(Error.from_json(self.error.to_json()))
|
||||
|
|
@ -97,9 +105,9 @@ class DirectoryTest(unittest.TestCase):
|
|||
),
|
||||
})
|
||||
|
||||
def test_init_wrong_key_value_error(self):
|
||||
def test_init_wrong_key_value_success(self): # pylint: disable=no-self-use
|
||||
from acme.messages import Directory
|
||||
self.assertRaises(ValueError, Directory, {'foo': 'bar'})
|
||||
Directory({'foo': 'bar'})
|
||||
|
||||
def test_getitem(self):
|
||||
self.assertEqual('reg', self.dir['new-reg'])
|
||||
|
|
@ -127,10 +135,9 @@ class DirectoryTest(unittest.TestCase):
|
|||
},
|
||||
})
|
||||
|
||||
def test_from_json_deserialization_error_on_wrong_key(self):
|
||||
def test_from_json_deserialization_unknown_key_success(self): # pylint: disable=no-self-use
|
||||
from acme.messages import Directory
|
||||
self.assertRaises(
|
||||
jose.DeserializationError, Directory.from_json, {'foo': 'bar'})
|
||||
Directory.from_json({'foo': 'bar'})
|
||||
|
||||
|
||||
class RegistrationTest(unittest.TestCase):
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from setuptools import setup
|
|||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.6.0.dev0'
|
||||
version = '0.7.0.dev0'
|
||||
|
||||
# Please update tox.ini when modifying dependency version requirements
|
||||
install_requires = [
|
||||
|
|
|
|||
|
|
@ -45,8 +45,8 @@ autoload xfm
|
|||
let dels (s:string) = del s s
|
||||
|
||||
(* deal with continuation lines *)
|
||||
let sep_spc = del /([ \t]+|[ \t]*\\\\\r?\n[ \t]*)/ " "
|
||||
let sep_osp = del /([ \t]*|[ \t]*\\\\\r?\n[ \t]*)/ ""
|
||||
let sep_spc = del /([ \t]+|[ \t]*\\\\\r?\n[ \t]*)+/ " "
|
||||
let sep_osp = del /([ \t]*|[ \t]*\\\\\r?\n[ \t]*)*/ ""
|
||||
let sep_eq = del /[ \t]*=[ \t]*/ "="
|
||||
|
||||
let nmtoken = /[a-zA-Z:_][a-zA-Z0-9:_.-]*/
|
||||
|
|
@ -58,8 +58,8 @@ let empty = Util.empty_dos
|
|||
let indent = Util.indent
|
||||
|
||||
(* borrowed from shellvars.aug *)
|
||||
let char_arg_dir = /([^\\ '"{\t\r\n]|[^ '"{\t\r\n]+[^\\ \t\r\n])|\\\\"|\\\\'/
|
||||
let char_arg_sec = /([^\\ '"\t\r\n>]|[^ '"\t\r\n>]+[^\\ \t\r\n>])|\\\\"|\\\\'/
|
||||
let char_arg_dir = /([^\\ '"{\t\r\n]|[^ '"{\t\r\n]+[^\\ \t\r\n])|\\\\"|\\\\'|\\\\ /
|
||||
let char_arg_sec = /([^\\ '"\t\r\n>]|[^ '"\t\r\n>]+[^\\ \t\r\n>])|\\\\"|\\\\'|\\\\ /
|
||||
let char_arg_wl = /([^\\ '"},\t\r\n]|[^ '"},\t\r\n]+[^\\ '"},\t\r\n])/
|
||||
|
||||
let cdot = /\\\\./
|
||||
|
|
|
|||
|
|
@ -124,13 +124,16 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
self.assoc = dict()
|
||||
# Outstanding challenges
|
||||
self._chall_out = set()
|
||||
# Maps enhancements to vhosts we've enabled the enhancement for
|
||||
self._enhanced_vhosts = defaultdict(set)
|
||||
|
||||
# These will be set in the prepare function
|
||||
self.parser = None
|
||||
self.version = version
|
||||
self.vhosts = None
|
||||
self._enhance_func = {"redirect": self._enable_redirect,
|
||||
"ensure-http-header": self._set_http_header}
|
||||
"ensure-http-header": self._set_http_header,
|
||||
"staple-ocsp": self._enable_ocsp_stapling}
|
||||
|
||||
@property
|
||||
def mod_ssl_conf(self):
|
||||
|
|
@ -593,8 +596,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
:type addr: :class:`~certbot_apache.obj.Addr`
|
||||
|
||||
"""
|
||||
loc = parser.get_aug_path(self.parser.loc["name"])
|
||||
|
||||
loc = parser.get_aug_path(self.parser.loc["name"])
|
||||
if addr.get_port() == "443":
|
||||
path = self.parser.add_dir_to_ifmodssl(
|
||||
loc, "NameVirtualHost", [str(addr)])
|
||||
|
|
@ -944,7 +947,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
######################################################################
|
||||
def supported_enhancements(self): # pylint: disable=no-self-use
|
||||
"""Returns currently supported enhancements."""
|
||||
return ["redirect", "ensure-http-header"]
|
||||
return ["redirect", "ensure-http-header", "staple-ocsp"]
|
||||
|
||||
def enhance(self, domain, enhancement, options=None):
|
||||
"""Enhance configuration.
|
||||
|
|
@ -971,6 +974,68 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
logger.warn("Failed %s for %s", enhancement, domain)
|
||||
raise
|
||||
|
||||
def _enable_ocsp_stapling(self, ssl_vhost, unused_options):
|
||||
"""Enables OCSP Stapling
|
||||
|
||||
In OCSP, each client (e.g. browser) would have to query the
|
||||
OCSP Responder to validate that the site certificate was not revoked.
|
||||
|
||||
Enabling OCSP Stapling, would allow the web-server to query the OCSP
|
||||
Responder, and staple its response to the offered certificate during
|
||||
TLS. i.e. clients would not have to query the OCSP responder.
|
||||
|
||||
OCSP Stapling enablement on Apache implicitly depends on
|
||||
SSLCertificateChainFile being set by other code.
|
||||
|
||||
.. note:: This function saves the configuration
|
||||
|
||||
:param ssl_vhost: Destination of traffic, an ssl enabled vhost
|
||||
:type ssl_vhost: :class:`~letsencrypt_apache.obj.VirtualHost`
|
||||
|
||||
:param unused_options: Not currently used
|
||||
:type unused_options: Not Available
|
||||
|
||||
:returns: Success, general_vhost (HTTP vhost)
|
||||
:rtype: (bool, :class:`~letsencrypt_apache.obj.VirtualHost`)
|
||||
|
||||
"""
|
||||
min_apache_ver = (2, 3, 3)
|
||||
if self.get_version() < min_apache_ver:
|
||||
raise errors.PluginError(
|
||||
"Unable to set OCSP directives.\n"
|
||||
"Apache version is below 2.3.3.")
|
||||
|
||||
if "socache_shmcb_module" not in self.parser.modules:
|
||||
self.enable_mod("socache_shmcb")
|
||||
|
||||
# Check if there's an existing SSLUseStapling directive on.
|
||||
use_stapling_aug_path = self.parser.find_dir("SSLUseStapling",
|
||||
"on", start=ssl_vhost.path)
|
||||
if not use_stapling_aug_path:
|
||||
self.parser.add_dir(ssl_vhost.path, "SSLUseStapling", "on")
|
||||
|
||||
ssl_vhost_aug_path = parser.get_aug_path(ssl_vhost.filep)
|
||||
|
||||
# Check if there's an existing SSLStaplingCache directive.
|
||||
stapling_cache_aug_path = self.parser.find_dir('SSLStaplingCache',
|
||||
None, ssl_vhost_aug_path)
|
||||
|
||||
# We'll simply delete the directive, so that we'll have a
|
||||
# consistent OCSP cache path.
|
||||
if stapling_cache_aug_path:
|
||||
self.aug.remove(
|
||||
re.sub(r"/\w*$", "", stapling_cache_aug_path[0]))
|
||||
|
||||
self.parser.add_dir_to_ifmodssl(ssl_vhost_aug_path,
|
||||
"SSLStaplingCache",
|
||||
["shmcb:/var/run/apache2/stapling_cache(128000)"])
|
||||
|
||||
msg = "OCSP Stapling was enabled on SSL Vhost: %s.\n"%(
|
||||
ssl_vhost.filep)
|
||||
self.save_notes += msg
|
||||
self.save()
|
||||
logger.info(msg)
|
||||
|
||||
def _set_http_header(self, ssl_vhost, header_substring):
|
||||
"""Enables header that is identified by header_substring on ssl_vhost.
|
||||
|
||||
|
|
@ -1058,9 +1123,6 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
:param unused_options: Not currently used
|
||||
:type unused_options: Not Available
|
||||
|
||||
:returns: Success, general_vhost (HTTP vhost)
|
||||
:rtype: (bool, :class:`~certbot_apache.obj.VirtualHost`)
|
||||
|
||||
:raises .errors.PluginError: If no viable HTTP host can be created or
|
||||
used for the redirect.
|
||||
|
||||
|
|
@ -1083,6 +1145,10 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
"redirection")
|
||||
self._create_redirect_vhost(ssl_vhost)
|
||||
else:
|
||||
if general_vh in self._enhanced_vhosts["redirect"]:
|
||||
logger.debug("Already enabled redirect for this vhost")
|
||||
return
|
||||
|
||||
# Check if Certbot redirection already exists
|
||||
self._verify_no_certbot_redirect(general_vh)
|
||||
|
||||
|
|
@ -1118,6 +1184,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
(general_vh.filep, ssl_vhost.filep))
|
||||
self.save()
|
||||
|
||||
self._enhanced_vhosts["redirect"].add(general_vh)
|
||||
logger.info("Redirecting vhost in %s to ssl vhost in %s",
|
||||
general_vh.filep, ssl_vhost.filep)
|
||||
|
||||
|
|
@ -1177,10 +1244,14 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
:type vhost: :class:`~certbot_apache.obj.VirtualHost`
|
||||
|
||||
"""
|
||||
rewrite_engine_path = self.parser.find_dir("RewriteEngine", "on",
|
||||
rewrite_engine_path_list = self.parser.find_dir("RewriteEngine", "on",
|
||||
start=vhost.path)
|
||||
if rewrite_engine_path:
|
||||
return self.parser.get_arg(rewrite_engine_path[0])
|
||||
if rewrite_engine_path_list:
|
||||
for re_path in rewrite_engine_path_list:
|
||||
# A RewriteEngine directive may also be included in per
|
||||
# directory .htaccess files. We only care about the VirtualHost.
|
||||
if 'VirtualHost' in re_path:
|
||||
return self.parser.get_arg(re_path)
|
||||
return False
|
||||
|
||||
def _create_redirect_vhost(self, ssl_vhost):
|
||||
|
|
@ -1202,6 +1273,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
# Make a new vhost data structure and add it to the lists
|
||||
new_vhost = self._create_vhost(parser.get_aug_path(redirect_filepath))
|
||||
self.vhosts.append(new_vhost)
|
||||
self._enhanced_vhosts["redirect"].add(new_vhost)
|
||||
|
||||
# Finally create documentation for the change
|
||||
self.save_notes += ("Created a port 80 vhost, %s, for redirection to "
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
RewriteCond %{HTTP:Content-Disposition} \.php [NC]
|
||||
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /.+/trackback/?\ HTTP/ [NC]
|
||||
|
|
@ -0,0 +1,247 @@
|
|||
#ATTENTION!
|
||||
#
|
||||
#DO NOT MODIFY THIS FILE BECAUSE IT WAS GENERATED AUTOMATICALLY,
|
||||
#SO ALL YOUR CHANGES WILL BE LOST THE NEXT TIME THE FILE IS GENERATED.
|
||||
|
||||
NameVirtualHost 192.168.100.218:80
|
||||
NameVirtualHost 10.128.178.192:80
|
||||
|
||||
NameVirtualHost 192.168.100.218:443
|
||||
NameVirtualHost 10.128.178.192:443
|
||||
|
||||
|
||||
ServerName "254020-web1.example.com"
|
||||
ServerAdmin "name@example.com"
|
||||
|
||||
DocumentRoot "/tmp"
|
||||
|
||||
<IfModule mod_logio.c>
|
||||
LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" plesklog
|
||||
</IfModule>
|
||||
<IfModule !mod_logio.c>
|
||||
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" plesklog
|
||||
</IfModule>
|
||||
|
||||
TraceEnable off
|
||||
|
||||
ServerTokens ProductOnly
|
||||
|
||||
<Directory "/var/www/vhosts">
|
||||
AllowOverride "All"
|
||||
Options SymLinksIfOwnerMatch
|
||||
Order allow,deny
|
||||
Allow from all
|
||||
|
||||
<IfModule sapi_apache2.c>
|
||||
php_admin_flag engine off
|
||||
</IfModule>
|
||||
|
||||
<IfModule mod_php5.c>
|
||||
php_admin_flag engine off
|
||||
</IfModule>
|
||||
|
||||
</Directory>
|
||||
|
||||
<Directory "/usr/lib/mailman">
|
||||
AllowOverride All
|
||||
Options SymLinksIfOwnerMatch
|
||||
Order allow,deny
|
||||
Allow from all
|
||||
<IfModule sapi_apache2.c>
|
||||
php_admin_flag engine off
|
||||
</IfModule>
|
||||
<IfModule mod_php5.c>
|
||||
php_admin_flag engine off
|
||||
</IfModule>
|
||||
</Directory>
|
||||
|
||||
<IfModule mod_headers.c>
|
||||
Header add X-Powered-By PleskLin
|
||||
</IfModule>
|
||||
|
||||
<IfModule mod_jk.c>
|
||||
JkWorkersFile "/etc/httpd/conf/workers.properties"
|
||||
JkLogFile /var/log/httpd/mod_jk.log
|
||||
JkLogLevel info
|
||||
</IfModule>
|
||||
|
||||
#Include "/etc/httpd/conf/plesk.conf.d/ip_default/*.conf"
|
||||
|
||||
|
||||
<VirtualHost \
|
||||
192.168.100.218:80 \
|
||||
10.128.178.192:80 \
|
||||
\
|
||||
>
|
||||
ServerName "default"
|
||||
UseCanonicalName Off
|
||||
DocumentRoot "/tmp"
|
||||
ScriptAlias /cgi-bin/ "/var/www/vhosts/default/cgi-bin"
|
||||
|
||||
|
||||
<IfModule mod_ssl.c>
|
||||
SSLEngine off
|
||||
</IfModule>
|
||||
|
||||
<Directory "/var/www/vhosts/default/cgi-bin">
|
||||
AllowOverride None
|
||||
Options None
|
||||
Order allow,deny
|
||||
Allow from all
|
||||
</Directory>
|
||||
|
||||
<Directory /var/www/vhosts/default/htdocs>
|
||||
|
||||
<IfModule sapi_apache2.c>
|
||||
php_admin_flag engine on
|
||||
</IfModule>
|
||||
|
||||
<IfModule mod_php5.c>
|
||||
php_admin_flag engine on
|
||||
</IfModule>
|
||||
|
||||
</Directory>
|
||||
|
||||
</VirtualHost>
|
||||
|
||||
<IfModule mod_ssl.c>
|
||||
|
||||
<VirtualHost \
|
||||
192.168.100.218:443 \
|
||||
\
|
||||
>
|
||||
ServerName "default-192_168_100_218"
|
||||
UseCanonicalName Off
|
||||
DocumentRoot "/tmp"
|
||||
ScriptAlias /cgi-bin/ "/var/www/vhosts/default/cgi-bin"
|
||||
|
||||
|
||||
SSLEngine on
|
||||
SSLVerifyClient none
|
||||
#SSLCertificateFile "/usr/local/psa/var/certificates/cert-9MgutN"
|
||||
|
||||
#SSLCACertificateFile "/usr/local/psa/var/certificates/cert-s6Wx3P"
|
||||
|
||||
<Directory "/var/www/vhosts/default/cgi-bin">
|
||||
AllowOverride None
|
||||
Options None
|
||||
Order allow,deny
|
||||
Allow from all
|
||||
</Directory>
|
||||
|
||||
<Directory /var/www/vhosts/default/htdocs>
|
||||
|
||||
<IfModule sapi_apache2.c>
|
||||
php_admin_flag engine on
|
||||
</IfModule>
|
||||
|
||||
<IfModule mod_php5.c>
|
||||
php_admin_flag engine on
|
||||
</IfModule>
|
||||
|
||||
</Directory>
|
||||
|
||||
</VirtualHost>
|
||||
<VirtualHost \
|
||||
10.128.178.192:443 \
|
||||
\
|
||||
>
|
||||
ServerName "default-10_128_178_192"
|
||||
UseCanonicalName Off
|
||||
DocumentRoot "/tmp"
|
||||
ScriptAlias /cgi-bin/ "/var/www/vhosts/default/cgi-bin"
|
||||
|
||||
|
||||
SSLEngine on
|
||||
SSLVerifyClient none
|
||||
#SSLCertificateFile "/usr/local/psa/var/certificates/certxfb6025"
|
||||
|
||||
|
||||
<Directory "/var/www/vhosts/default/cgi-bin">
|
||||
AllowOverride None
|
||||
Options None
|
||||
Order allow,deny
|
||||
Allow from all
|
||||
</Directory>
|
||||
|
||||
<Directory /var/www/vhosts/default/htdocs>
|
||||
|
||||
<IfModule sapi_apache2.c>
|
||||
php_admin_flag engine on
|
||||
</IfModule>
|
||||
|
||||
<IfModule mod_php5.c>
|
||||
php_admin_flag engine on
|
||||
</IfModule>
|
||||
|
||||
</Directory>
|
||||
|
||||
</VirtualHost>
|
||||
|
||||
</IfModule>
|
||||
|
||||
|
||||
<VirtualHost \
|
||||
192.168.100.218:80 \
|
||||
10.128.178.192:80 \
|
||||
\
|
||||
>
|
||||
DocumentRoot "/tmp"
|
||||
ServerName lists
|
||||
ServerAlias lists.*
|
||||
UseCanonicalName Off
|
||||
|
||||
ScriptAlias "/mailman/" "/usr/lib/mailman/cgi-bin/"
|
||||
|
||||
Alias "/icons/" "/var/www/icons/"
|
||||
Alias "/pipermail/" "/var/lib/mailman/archives/public/"
|
||||
|
||||
<IfModule mod_ssl.c>
|
||||
SSLEngine off
|
||||
</IfModule>
|
||||
|
||||
|
||||
<Directory /var/lib/mailman/archives/>
|
||||
Options FollowSymLinks
|
||||
Order allow,deny
|
||||
Allow from all
|
||||
</Directory>
|
||||
|
||||
</VirtualHost>
|
||||
|
||||
<IfModule mod_ssl.c>
|
||||
<VirtualHost \
|
||||
192.168.100.218:443 \
|
||||
10.128.178.192:443 \
|
||||
\
|
||||
>
|
||||
DocumentRoot "/tmp"
|
||||
ServerName lists
|
||||
ServerAlias lists.*
|
||||
UseCanonicalName Off
|
||||
|
||||
ScriptAlias "/mailman/" "/usr/lib/mailman/cgi-bin/"
|
||||
|
||||
Alias "/icons/" "/var/www/icons/"
|
||||
Alias "/pipermail/" "/var/lib/mailman/archives/public/"
|
||||
|
||||
SSLEngine on
|
||||
SSLVerifyClient none
|
||||
#SSLCertificateFile "/usr/local/psa/var/certificates/certxfb6025"
|
||||
|
||||
|
||||
<Directory /var/lib/mailman/archives/>
|
||||
Options FollowSymLinks
|
||||
Order allow,deny
|
||||
Allow from all
|
||||
</Directory>
|
||||
|
||||
</VirtualHost>
|
||||
</IfModule>
|
||||
|
||||
<IfModule mod_rpaf.c>
|
||||
RPAFproxy_ips 192.168.100.218 10.128.178.192
|
||||
</IfModule>
|
||||
<IfModule mod_rpaf-2.0.c>
|
||||
RPAFproxy_ips 192.168.100.218 10.128.178.192
|
||||
</IfModule>
|
||||
|
|
@ -15,6 +15,7 @@ from certbot import errors
|
|||
from certbot.tests import acme_util
|
||||
|
||||
from certbot_apache import configurator
|
||||
from certbot_apache import parser
|
||||
from certbot_apache import obj
|
||||
|
||||
from certbot_apache.tests import util
|
||||
|
|
@ -85,7 +86,8 @@ class MultipleVhostsTest(util.ApacheTest):
|
|||
mock_getutility.notification = mock.MagicMock(return_value=True)
|
||||
names = self.config.get_all_names()
|
||||
self.assertEqual(names, set(
|
||||
["certbot.demo", "encryption-example.demo", "ip-172-30-0-17", "*.blue.purple.com"]))
|
||||
["certbot.demo", "ocspvhost.com", "encryption-example.demo",
|
||||
"ip-172-30-0-17", "*.blue.purple.com"]))
|
||||
|
||||
@mock.patch("zope.component.getUtility")
|
||||
@mock.patch("certbot_apache.configurator.socket.gethostbyaddr")
|
||||
|
|
@ -100,14 +102,24 @@ class MultipleVhostsTest(util.ApacheTest):
|
|||
obj.Addr(("zombo.com",)),
|
||||
obj.Addr(("192.168.1.2"))]),
|
||||
True, False)
|
||||
|
||||
self.config.vhosts.append(vhost)
|
||||
|
||||
names = self.config.get_all_names()
|
||||
self.assertEqual(len(names), 6)
|
||||
self.assertEqual(len(names), 7)
|
||||
self.assertTrue("zombo.com" in names)
|
||||
self.assertTrue("google.com" in names)
|
||||
self.assertTrue("certbot.demo" in names)
|
||||
|
||||
def test_bad_servername_alias(self):
|
||||
ssl_vh1 = obj.VirtualHost(
|
||||
"fp1", "ap1", set([obj.Addr(("*", "443"))]),
|
||||
True, False)
|
||||
# pylint: disable=protected-access
|
||||
self.config._add_servernames(ssl_vh1)
|
||||
self.assertTrue(
|
||||
self.config._add_servername_alias("oy_vey", ssl_vh1) is None)
|
||||
|
||||
def test_add_servernames_alias(self):
|
||||
self.config.parser.add_dir(
|
||||
self.vh_truth[2].path, "ServerAlias", ["*.le.co"])
|
||||
|
|
@ -124,7 +136,7 @@ class MultipleVhostsTest(util.ApacheTest):
|
|||
|
||||
"""
|
||||
vhs = self.config.get_virtual_hosts()
|
||||
self.assertEqual(len(vhs), 7)
|
||||
self.assertEqual(len(vhs), 8)
|
||||
found = 0
|
||||
|
||||
for vhost in vhs:
|
||||
|
|
@ -135,7 +147,7 @@ class MultipleVhostsTest(util.ApacheTest):
|
|||
else:
|
||||
raise Exception("Missed: %s" % vhost) # pragma: no cover
|
||||
|
||||
self.assertEqual(found, 7)
|
||||
self.assertEqual(found, 8)
|
||||
|
||||
# Handle case of non-debian layout get_virtual_hosts
|
||||
with mock.patch(
|
||||
|
|
@ -143,7 +155,7 @@ class MultipleVhostsTest(util.ApacheTest):
|
|||
) as mock_conf:
|
||||
mock_conf.return_value = False
|
||||
vhs = self.config.get_virtual_hosts()
|
||||
self.assertEqual(len(vhs), 7)
|
||||
self.assertEqual(len(vhs), 8)
|
||||
|
||||
@mock.patch("certbot_apache.display_ops.select_vhost")
|
||||
def test_choose_vhost_none_avail(self, mock_select):
|
||||
|
|
@ -224,16 +236,18 @@ class MultipleVhostsTest(util.ApacheTest):
|
|||
# Assume only the two default vhosts.
|
||||
self.config.vhosts = [
|
||||
vh for vh in self.config.vhosts
|
||||
if vh.name not in ["certbot.demo", "encryption-example.demo"]
|
||||
if vh.name not in ["certbot.demo",
|
||||
"encryption-example.demo",
|
||||
"ocspvhost.com"]
|
||||
and "*.blue.purple.com" not in vh.aliases
|
||||
]
|
||||
|
||||
self.assertEqual(
|
||||
self.config._find_best_vhost("example.demo"), self.vh_truth[2])
|
||||
self.config._find_best_vhost("encryption-example.demo"),
|
||||
self.vh_truth[2])
|
||||
|
||||
def test_non_default_vhosts(self):
|
||||
# pylint: disable=protected-access
|
||||
self.assertEqual(len(self.config._non_default_vhosts()), 5)
|
||||
self.assertEqual(len(self.config._non_default_vhosts()), 6)
|
||||
|
||||
def test_is_site_enabled(self):
|
||||
"""Test if site is enabled.
|
||||
|
|
@ -539,7 +553,7 @@ class MultipleVhostsTest(util.ApacheTest):
|
|||
self.assertEqual(self.config.is_name_vhost(self.vh_truth[0]),
|
||||
self.config.is_name_vhost(ssl_vhost))
|
||||
|
||||
self.assertEqual(len(self.config.vhosts), 8)
|
||||
self.assertEqual(len(self.config.vhosts), 9)
|
||||
|
||||
def test_clean_vhost_ssl(self):
|
||||
# pylint: disable=protected-access
|
||||
|
|
@ -726,16 +740,15 @@ class MultipleVhostsTest(util.ApacheTest):
|
|||
|
||||
def test_get_all_certs_keys(self):
|
||||
c_k = self.config.get_all_certs_keys()
|
||||
|
||||
self.assertEqual(len(c_k), 2)
|
||||
self.assertEqual(len(c_k), 3)
|
||||
cert, key, path = next(iter(c_k))
|
||||
self.assertTrue("cert" in cert)
|
||||
self.assertTrue("key" in key)
|
||||
self.assertTrue("default-ssl" in path)
|
||||
self.assertTrue("default-ssl" in path or "ocsp-ssl" in path)
|
||||
|
||||
def test_get_all_certs_keys_malformed_conf(self):
|
||||
self.config.parser.find_dir = mock.Mock(
|
||||
side_effect=[["path"], [], ["path"], []])
|
||||
side_effect=[["path"], [], ["path"], [], ["path"], []])
|
||||
c_k = self.config.get_all_certs_keys()
|
||||
|
||||
self.assertFalse(c_k)
|
||||
|
|
@ -756,15 +769,20 @@ class MultipleVhostsTest(util.ApacheTest):
|
|||
def test_supported_enhancements(self):
|
||||
self.assertTrue(isinstance(self.config.supported_enhancements(), list))
|
||||
|
||||
@mock.patch("certbot_apache.configurator.ApacheConfigurator._get_http_vhost")
|
||||
@mock.patch("certbot_apache.display_ops.select_vhost")
|
||||
@mock.patch("certbot.le_util.exe_exists")
|
||||
def test_enhance_unknown_vhost(self, mock_exe):
|
||||
def test_enhance_unknown_vhost(self, mock_exe, mock_sel_vhost, mock_get):
|
||||
self.config.parser.modules.add("rewrite_module")
|
||||
mock_exe.return_value = True
|
||||
ssl_vh = obj.VirtualHost(
|
||||
"fp", "ap", set([obj.Addr(("*", "443"))]),
|
||||
ssl_vh1 = obj.VirtualHost(
|
||||
"fp1", "ap1", set([obj.Addr(("*", "443"))]),
|
||||
True, False)
|
||||
ssl_vh.name = "satoshi.com"
|
||||
self.config.vhosts.append(ssl_vh)
|
||||
ssl_vh1.name = "satoshi.com"
|
||||
self.config.vhosts.append(ssl_vh1)
|
||||
mock_sel_vhost.return_value = None
|
||||
mock_get.return_value = None
|
||||
|
||||
self.assertRaises(
|
||||
errors.PluginError,
|
||||
self.config.enhance, "satoshi.com", "redirect")
|
||||
|
|
@ -774,6 +792,85 @@ class MultipleVhostsTest(util.ApacheTest):
|
|||
errors.PluginError,
|
||||
self.config.enhance, "certbot.demo", "unknown_enhancement")
|
||||
|
||||
@mock.patch("certbot.le_util.run_script")
|
||||
@mock.patch("certbot.le_util.exe_exists")
|
||||
def test_ocsp_stapling(self, mock_exe, mock_run_script):
|
||||
self.config.parser.update_runtime_variables = mock.Mock()
|
||||
self.config.parser.modules.add("mod_ssl.c")
|
||||
self.config.get_version = mock.Mock(return_value=(2, 4, 7))
|
||||
mock_exe.return_value = True
|
||||
|
||||
# This will create an ssl vhost for certbot.demo
|
||||
self.config.enhance("certbot.demo", "staple-ocsp")
|
||||
|
||||
self.assertTrue("socache_shmcb_module" in self.config.parser.modules)
|
||||
self.assertTrue(mock_run_script.called)
|
||||
|
||||
# Get the ssl vhost for certbot.demo
|
||||
ssl_vhost = self.config.assoc["certbot.demo"]
|
||||
|
||||
ssl_use_stapling_aug_path = self.config.parser.find_dir(
|
||||
"SSLUseStapling", "on", ssl_vhost.path)
|
||||
|
||||
self.assertEqual(len(ssl_use_stapling_aug_path), 1)
|
||||
|
||||
ssl_vhost_aug_path = parser.get_aug_path(ssl_vhost.filep)
|
||||
stapling_cache_aug_path = self.config.parser.find_dir('SSLStaplingCache',
|
||||
"shmcb:/var/run/apache2/stapling_cache(128000)",
|
||||
ssl_vhost_aug_path)
|
||||
|
||||
self.assertEqual(len(stapling_cache_aug_path), 1)
|
||||
|
||||
@mock.patch("certbot.le_util.exe_exists")
|
||||
def test_ocsp_stapling_twice(self, mock_exe):
|
||||
self.config.parser.update_runtime_variables = mock.Mock()
|
||||
self.config.parser.modules.add("mod_ssl.c")
|
||||
self.config.parser.modules.add("socache_shmcb_module")
|
||||
self.config.get_version = mock.Mock(return_value=(2, 4, 7))
|
||||
mock_exe.return_value = True
|
||||
|
||||
# Checking the case with already enabled ocsp stapling configuration
|
||||
self.config.enhance("ocspvhost.com", "staple-ocsp")
|
||||
|
||||
# Get the ssl vhost for letsencrypt.demo
|
||||
ssl_vhost = self.config.assoc["ocspvhost.com"]
|
||||
|
||||
ssl_use_stapling_aug_path = self.config.parser.find_dir(
|
||||
"SSLUseStapling", "on", ssl_vhost.path)
|
||||
|
||||
self.assertEqual(len(ssl_use_stapling_aug_path), 1)
|
||||
|
||||
ssl_vhost_aug_path = parser.get_aug_path(ssl_vhost.filep)
|
||||
stapling_cache_aug_path = self.config.parser.find_dir('SSLStaplingCache',
|
||||
"shmcb:/var/run/apache2/stapling_cache(128000)",
|
||||
ssl_vhost_aug_path)
|
||||
|
||||
self.assertEqual(len(stapling_cache_aug_path), 1)
|
||||
|
||||
|
||||
@mock.patch("certbot.le_util.exe_exists")
|
||||
def test_ocsp_unsupported_apache_version(self, mock_exe):
|
||||
mock_exe.return_value = True
|
||||
self.config.parser.update_runtime_variables = mock.Mock()
|
||||
self.config.parser.modules.add("mod_ssl.c")
|
||||
self.config.parser.modules.add("socache_shmcb_module")
|
||||
self.config.get_version = mock.Mock(return_value=(2, 2, 0))
|
||||
|
||||
self.assertRaises(errors.PluginError,
|
||||
self.config.enhance, "certbot.demo", "staple-ocsp")
|
||||
|
||||
|
||||
def test_get_http_vhost_third_filter(self):
|
||||
ssl_vh = obj.VirtualHost(
|
||||
"fp", "ap", set([obj.Addr(("*", "443"))]),
|
||||
True, False)
|
||||
ssl_vh.name = "satoshi.com"
|
||||
self.config.vhosts.append(ssl_vh)
|
||||
|
||||
# pylint: disable=protected-access
|
||||
http_vh = self.config._get_http_vhost(ssl_vh)
|
||||
self.assertTrue(http_vh.ssl == False)
|
||||
|
||||
@mock.patch("certbot.le_util.run_script")
|
||||
@mock.patch("certbot.le_util.exe_exists")
|
||||
def test_http_header_hsts(self, mock_exe, _):
|
||||
|
|
@ -899,7 +996,7 @@ class MultipleVhostsTest(util.ApacheTest):
|
|||
def test_redirect_with_existing_rewrite(self, mock_exe, _):
|
||||
self.config.parser.update_runtime_variables = mock.Mock()
|
||||
mock_exe.return_value = True
|
||||
self.config.get_version = mock.Mock(return_value=(2, 2))
|
||||
self.config.get_version = mock.Mock(return_value=(2, 2, 0))
|
||||
|
||||
# Create a preexisting rewrite rule
|
||||
self.config.parser.add_dir(
|
||||
|
|
@ -938,15 +1035,31 @@ class MultipleVhostsTest(util.ApacheTest):
|
|||
self.assertRaises(
|
||||
errors.PluginError, self.config._enable_redirect, ssl_vh, "")
|
||||
|
||||
def test_redirect_twice(self):
|
||||
def test_redirect_two_domains_one_vhost(self):
|
||||
# Skip the enable mod
|
||||
self.config.parser.modules.add("rewrite_module")
|
||||
self.config.get_version = mock.Mock(return_value=(2, 3, 9))
|
||||
|
||||
self.config.enhance("encryption-example.demo", "redirect")
|
||||
self.config.enhance("red.blue.purple.com", "redirect")
|
||||
verify_no_redirect = ("certbot_apache.configurator."
|
||||
"ApacheConfigurator._verify_no_certbot_redirect")
|
||||
with mock.patch(verify_no_redirect) as mock_verify:
|
||||
self.config.enhance("green.blue.purple.com", "redirect")
|
||||
self.assertFalse(mock_verify.called)
|
||||
|
||||
def test_redirect_from_previous_run(self):
|
||||
# Skip the enable mod
|
||||
self.config.parser.modules.add("rewrite_module")
|
||||
self.config.get_version = mock.Mock(return_value=(2, 3, 9))
|
||||
|
||||
self.config.enhance("red.blue.purple.com", "redirect")
|
||||
# Clear state about enabling redirect on this run
|
||||
# pylint: disable=protected-access
|
||||
self.config._enhanced_vhosts["redirect"].clear()
|
||||
|
||||
self.assertRaises(
|
||||
errors.PluginEnhancementAlreadyPresent,
|
||||
self.config.enhance, "encryption-example.demo", "redirect")
|
||||
self.config.enhance, "green.blue.purple.com", "redirect")
|
||||
|
||||
def test_create_own_redirect(self):
|
||||
self.config.parser.modules.add("rewrite_module")
|
||||
|
|
@ -957,7 +1070,7 @@ class MultipleVhostsTest(util.ApacheTest):
|
|||
|
||||
# pylint: disable=protected-access
|
||||
self.config._enable_redirect(self.vh_truth[1], "")
|
||||
self.assertEqual(len(self.config.vhosts), 8)
|
||||
self.assertEqual(len(self.config.vhosts), 9)
|
||||
|
||||
def test_create_own_redirect_for_old_apache_version(self):
|
||||
self.config.parser.modules.add("rewrite_module")
|
||||
|
|
@ -968,7 +1081,7 @@ class MultipleVhostsTest(util.ApacheTest):
|
|||
|
||||
# pylint: disable=protected-access
|
||||
self.config._enable_redirect(self.vh_truth[1], "")
|
||||
self.assertEqual(len(self.config.vhosts), 8)
|
||||
self.assertEqual(len(self.config.vhosts), 9)
|
||||
|
||||
def test_sift_line(self):
|
||||
# pylint: disable=protected-access
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
<IfModule mod_ssl.c>
|
||||
SSLStaplingCache shmcb:/var/run/apache2/stapling_cache(128000)
|
||||
<VirtualHost 10.2.3.4:443>
|
||||
# The ServerName directive sets the request scheme, hostname and port that
|
||||
# the server uses to identify itself. This is used when creating
|
||||
# redirection URLs. In the context of virtual hosts, the ServerName
|
||||
# specifies what hostname must appear in the request's Host: header to
|
||||
# match this virtual host. For the default virtual host (this file) this
|
||||
# value is not decisive as it is used as a last resort host regardless.
|
||||
# However, you must set it for any further virtual host explicitly.
|
||||
ServerName ocspvhost.com
|
||||
|
||||
ServerAdmin webmaster@dumpbits.com
|
||||
DocumentRoot /var/www/html
|
||||
|
||||
# Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
|
||||
# error, crit, alert, emerg.
|
||||
# It is also possible to configure the loglevel for particular
|
||||
# modules, e.g.
|
||||
#LogLevel info ssl:warn
|
||||
|
||||
ErrorLog ${APACHE_LOG_DIR}/error.log
|
||||
CustomLog ${APACHE_LOG_DIR}/access.log combined
|
||||
|
||||
# For most configuration files from conf-available/, which are
|
||||
# enabled or disabled at a global level, it is possible to
|
||||
# include a line for only one particular virtual host. For example the
|
||||
# following line enables the CGI configuration for this host only
|
||||
# after it has been globally disabled with "a2disconf".
|
||||
#Include conf-available/serve-cgi-bin.conf
|
||||
SSLCertificateFile /etc/apache2/certs/certbot-cert_5.pem
|
||||
SSLCertificateKeyFile /etc/apache2/ssl/key-certbot_15.pem
|
||||
SSLUseStapling on
|
||||
</VirtualHost>
|
||||
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
|
||||
</IfModule>
|
||||
|
|
@ -0,0 +1 @@
|
|||
../sites-available/ocsp-ssl.conf
|
||||
|
|
@ -1,2 +1,3 @@
|
|||
sites-available/certbot.conf, certbot.demo
|
||||
sites-available/encryption-example.conf, encryption-example.demo
|
||||
sites-available/ocsp-ssl.conf, ocspvhost.com
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import shutil
|
|||
|
||||
import mock
|
||||
|
||||
from certbot import errors
|
||||
from certbot.plugins import common_test
|
||||
|
||||
from certbot_apache import obj
|
||||
|
|
@ -137,6 +138,16 @@ class TlsSniPerformTest(util.ApacheTest):
|
|||
set([obj.Addr.fromstring("*:443")]),
|
||||
self.sni._get_addrs(self.achalls[0]))
|
||||
|
||||
def test_get_addrs_no_vhost_found(self):
|
||||
self.sni.configurator.choose_vhost = mock.Mock(
|
||||
side_effect=errors.MissingCommandlineFlag(
|
||||
"Failed to run Apache plugin non-interactively"))
|
||||
|
||||
# pylint: disable=protected-access
|
||||
self.assertEqual(
|
||||
set([obj.Addr.fromstring("*:443")]),
|
||||
self.sni._get_addrs(self.achalls[0]))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main() # pragma: no cover
|
||||
|
|
|
|||
|
|
@ -156,8 +156,12 @@ def get_vh_truth(temp_dir, config_name):
|
|||
os.path.join(prefix, "wildcard.conf"),
|
||||
os.path.join(aug_pre, "wildcard.conf/VirtualHost"),
|
||||
set([obj.Addr.fromstring("*:80")]), False, False,
|
||||
"ip-172-30-0-17", aliases=["*.blue.purple.com"])
|
||||
]
|
||||
"ip-172-30-0-17", aliases=["*.blue.purple.com"]),
|
||||
obj.VirtualHost(
|
||||
os.path.join(prefix, "ocsp-ssl.conf"),
|
||||
os.path.join(aug_pre, "ocsp-ssl.conf/IfModule/VirtualHost"),
|
||||
set([obj.Addr.fromstring("10.2.3.4:443")]), True, True,
|
||||
"ocspvhost.com")]
|
||||
return vh_truth
|
||||
|
||||
return None # pragma: no cover
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import os
|
|||
import logging
|
||||
|
||||
from certbot.plugins import common
|
||||
from certbot.errors import PluginError, MissingCommandlineFlag
|
||||
|
||||
from certbot_apache import obj
|
||||
from certbot_apache import parser
|
||||
|
|
@ -116,12 +117,21 @@ class ApacheTlsSni01(common.TLSSNI01):
|
|||
|
||||
def _get_addrs(self, achall):
|
||||
"""Return the Apache addresses needed for TLS-SNI-01."""
|
||||
vhost = self.configurator.choose_vhost(achall.domain, temp=True)
|
||||
# TODO: Checkout _default_ rules.
|
||||
addrs = set()
|
||||
default_addr = obj.Addr(("*", str(
|
||||
self.configurator.config.tls_sni_01_port)))
|
||||
|
||||
try:
|
||||
vhost = self.configurator.choose_vhost(achall.domain, temp=True)
|
||||
except (PluginError, MissingCommandlineFlag):
|
||||
# We couldn't find the virtualhost for this domain, possibly
|
||||
# because it's a new vhost that's not configured yet (GH #677),
|
||||
# or perhaps because there were multiple <VirtualHost> sections
|
||||
# in the config file (GH #1042). See also GH #2600.
|
||||
addrs.add(default_addr)
|
||||
return addrs
|
||||
|
||||
for addr in vhost.addrs:
|
||||
if "_default_" == addr.get_addr():
|
||||
addrs.add(default_addr)
|
||||
|
|
|
|||
|
|
@ -314,5 +314,5 @@ texinfo_documents = [
|
|||
intersphinx_mapping = {
|
||||
'python': ('https://docs.python.org/', None),
|
||||
'acme': ('https://acme-python.readthedocs.org/en/latest/', None),
|
||||
'certbot': ('https://letsencrypt.readthedocs.org/en/latest/', None),
|
||||
'certbot': ('https://certbot.eff.org/docs/', None),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from setuptools import setup
|
|||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.6.0.dev0'
|
||||
version = '0.7.0.dev0'
|
||||
|
||||
# Please update tox.ini when modifying dependency version requirements
|
||||
install_requires = [
|
||||
|
|
|
|||
1088
certbot-auto
Executable file
1088
certbot-auto
Executable file
File diff suppressed because it is too large
Load diff
|
|
@ -311,7 +311,7 @@ texinfo_documents = [
|
|||
intersphinx_mapping = {
|
||||
'python': ('https://docs.python.org/', None),
|
||||
'acme': ('https://acme-python.readthedocs.org/en/latest/', None),
|
||||
'certbot': ('https://letsencrypt.readthedocs.org/en/latest/', None),
|
||||
'certbot': ('https://certbot.eff.org/docs/', None),
|
||||
'certbot-apache': (
|
||||
'https://letsencrypt-apache.readthedocs.org/en/latest/', None),
|
||||
'certbot-nginx': (
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from setuptools import setup
|
|||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.6.0.dev0'
|
||||
version = '0.7.0.dev0'
|
||||
|
||||
install_requires = [
|
||||
'certbot=={0}'.format(version),
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ class RawNginxParser(object):
|
|||
right_bracket = Literal("}").suppress()
|
||||
semicolon = Literal(";").suppress()
|
||||
space = White().suppress()
|
||||
key = Word(alphanums + "_/")
|
||||
key = Word(alphanums + "_/+-.")
|
||||
# Matches anything that is not a special character AND any chars in single
|
||||
# or double quotes
|
||||
value = Regex(r"((\".*\")?(\'.*\')?[^\{\};,]?)+")
|
||||
|
|
@ -30,10 +30,11 @@ class RawNginxParser(object):
|
|||
assignment = (key + Optional(space + value, default=None) + semicolon)
|
||||
location_statement = Optional(space + modifier) + Optional(space + location)
|
||||
if_statement = Literal("if") + space + Regex(r"\(.+\)") + space
|
||||
map_statement = Literal("map") + space + Regex(r"\S+") + space + Regex(r"\$\S+") + space
|
||||
block = Forward()
|
||||
|
||||
block << Group(
|
||||
(Group(key + location_statement) ^ Group(if_statement)) +
|
||||
(Group(key + location_statement) ^ Group(if_statement) ^ Group(map_statement)) +
|
||||
left_bracket +
|
||||
Group(ZeroOrMore(Group(comment | assignment) | block)) +
|
||||
right_bracket)
|
||||
|
|
|
|||
|
|
@ -307,5 +307,5 @@ texinfo_documents = [
|
|||
intersphinx_mapping = {
|
||||
'python': ('https://docs.python.org/', None),
|
||||
'acme': ('https://acme-python.readthedocs.org/en/latest/', None),
|
||||
'certbot': ('https://letsencrypt.readthedocs.org/en/latest/', None),
|
||||
'certbot': ('https://certbot.eff.org/docs/', None),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from setuptools import setup
|
|||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.6.0.dev0'
|
||||
version = '0.7.0.dev0'
|
||||
|
||||
# Please update tox.ini when modifying dependency version requirements
|
||||
install_requires = [
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
"""Certbot client."""
|
||||
|
||||
# version number like 1.2.3a0, must have at least 2 parts, like 1.2
|
||||
__version__ = '0.6.0.dev0'
|
||||
__version__ = '0.7.0.dev0'
|
||||
|
|
|
|||
115
certbot/cli.py
115
certbot/cli.py
|
|
@ -6,10 +6,8 @@ import logging
|
|||
import logging.handlers
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
import configargparse
|
||||
import OpenSSL
|
||||
import six
|
||||
|
||||
import certbot
|
||||
|
|
@ -37,8 +35,14 @@ helpful_parser = None
|
|||
# should only be used for purposes where inability to detect letsencrypt-auto
|
||||
# fails safely
|
||||
|
||||
fragment = os.path.join(".local", "share", "certbot")
|
||||
cli_command = "letsencrypt-auto" if fragment in sys.argv[0] else "certbot"
|
||||
LEAUTO = "letsencrypt-auto"
|
||||
if "CERTBOT_AUTO" in os.environ:
|
||||
# if we're here, this is probably going to be certbot-auto, unless the
|
||||
# user saved the script under a different name
|
||||
LEAUTO = os.path.basename(os.environ["CERTBOT_AUTO"])
|
||||
|
||||
fragment = os.path.join(".local", "share", "letsencrypt")
|
||||
cli_command = LEAUTO if fragment in sys.argv[0] else "certbot"
|
||||
|
||||
# Argparse's help formatting has a lot of unhelpful peculiarities, so we want
|
||||
# to replace as much of it as we can...
|
||||
|
|
@ -141,6 +145,22 @@ def usage_strings(plugins):
|
|||
return USAGE % (apache_doc, nginx_doc), SHORT_USAGE
|
||||
|
||||
|
||||
def possible_deprecation_warning(config):
|
||||
"A deprecation warning for users with the old, not-self-upgrading letsencrypt-auto."
|
||||
if cli_command != LEAUTO:
|
||||
return
|
||||
if config.no_self_upgrade:
|
||||
# users setting --no-self-upgrade might be hanging on a clent version like 0.3.0
|
||||
# or 0.5.0 which is the new script, but doesn't set CERTBOT_AUTO; they don't
|
||||
# need warnings
|
||||
return
|
||||
if "CERTBOT_AUTO" not in os.environ:
|
||||
logger.warn("You are running with an old copy of letsencrypt-auto that does "
|
||||
"not receive updates, and is less reliable than more recent versions. "
|
||||
"We recommend upgrading to the latest certbot-auto script, or using native "
|
||||
"OS packages.")
|
||||
|
||||
|
||||
class _Default(object):
|
||||
"""A class to use as a default to detect if a value is set by a user"""
|
||||
|
||||
|
|
@ -314,36 +334,41 @@ class HelpfulArgumentParser(object):
|
|||
|
||||
# Do any post-parsing homework here
|
||||
|
||||
if self.verb == "renew" and not parsed_args.dialog_mode:
|
||||
parsed_args.noninteractive_mode = True
|
||||
|
||||
if parsed_args.staging or parsed_args.dry_run:
|
||||
if parsed_args.server not in (flag_default("server"), constants.STAGING_URI):
|
||||
conflicts = ["--staging"] if parsed_args.staging else []
|
||||
conflicts += ["--dry-run"] if parsed_args.dry_run else []
|
||||
raise errors.Error("--server value conflicts with {0}".format(
|
||||
" and ".join(conflicts)))
|
||||
|
||||
parsed_args.server = constants.STAGING_URI
|
||||
|
||||
if parsed_args.dry_run:
|
||||
if self.verb not in ["certonly", "renew"]:
|
||||
raise errors.Error("--dry-run currently only works with the "
|
||||
"'certonly' or 'renew' subcommands (%r)" % self.verb)
|
||||
parsed_args.break_my_certs = parsed_args.staging = True
|
||||
if glob.glob(os.path.join(parsed_args.config_dir, constants.ACCOUNTS_DIR, "*")):
|
||||
# The user has a prod account, but might not have a staging
|
||||
# one; we don't want to start trying to perform interactive registration
|
||||
parsed_args.tos = True
|
||||
parsed_args.register_unsafely_without_email = True
|
||||
self.set_test_server(parsed_args)
|
||||
|
||||
if parsed_args.csr:
|
||||
if parsed_args.allow_subset_of_names:
|
||||
raise errors.Error("--allow-subset-of-names "
|
||||
"cannot be used with --csr")
|
||||
self.handle_csr(parsed_args)
|
||||
|
||||
hooks.validate_hooks(parsed_args)
|
||||
|
||||
return parsed_args
|
||||
|
||||
def set_test_server(self, parsed_args):
|
||||
"""We have --staging/--dry-run; perform sanity check and set config.server"""
|
||||
|
||||
if parsed_args.server not in (flag_default("server"), constants.STAGING_URI):
|
||||
conflicts = ["--staging"] if parsed_args.staging else []
|
||||
conflicts += ["--dry-run"] if parsed_args.dry_run else []
|
||||
raise errors.Error("--server value conflicts with {0}".format(
|
||||
" and ".join(conflicts)))
|
||||
|
||||
parsed_args.server = constants.STAGING_URI
|
||||
|
||||
if parsed_args.dry_run:
|
||||
if self.verb not in ["certonly", "renew"]:
|
||||
raise errors.Error("--dry-run currently only works with the "
|
||||
"'certonly' or 'renew' subcommands (%r)" % self.verb)
|
||||
parsed_args.break_my_certs = parsed_args.staging = True
|
||||
if glob.glob(os.path.join(parsed_args.config_dir, constants.ACCOUNTS_DIR, "*")):
|
||||
# The user has a prod account, but might not have a staging
|
||||
# one; we don't want to start trying to perform interactive registration
|
||||
parsed_args.tos = True
|
||||
parsed_args.register_unsafely_without_email = True
|
||||
|
||||
def handle_csr(self, parsed_args):
|
||||
"""Process a --csr flag."""
|
||||
if parsed_args.verb != "certonly":
|
||||
|
|
@ -351,21 +376,11 @@ class HelpfulArgumentParser(object):
|
|||
"when obtaining a new or replacement "
|
||||
"via the certonly command. Please try the "
|
||||
"certonly command instead.")
|
||||
if parsed_args.allow_subset_of_names:
|
||||
raise errors.Error("--allow-subset-of-names cannot be used with --csr")
|
||||
|
||||
try:
|
||||
csr = le_util.CSR(file=parsed_args.csr[0], data=parsed_args.csr[1], form="der")
|
||||
typ = OpenSSL.crypto.FILETYPE_ASN1
|
||||
domains = crypto_util.get_sans_from_csr(csr.data, OpenSSL.crypto.FILETYPE_ASN1)
|
||||
except OpenSSL.crypto.Error:
|
||||
try:
|
||||
e1 = traceback.format_exc()
|
||||
typ = OpenSSL.crypto.FILETYPE_PEM
|
||||
csr = le_util.CSR(file=parsed_args.csr[0], data=parsed_args.csr[1], form="pem")
|
||||
domains = crypto_util.get_sans_from_csr(csr.data, typ)
|
||||
except OpenSSL.crypto.Error:
|
||||
logger.debug("DER CSR parse error %s", e1)
|
||||
logger.debug("PEM CSR parse error %s", traceback.format_exc())
|
||||
raise errors.Error("Failed to parse CSR file: {0}".format(parsed_args.csr[0]))
|
||||
csrfile, contents = parsed_args.csr[0:2]
|
||||
typ, csr, domains = crypto_util.import_csr_file(csrfile, contents)
|
||||
|
||||
# This is not necessary for webroot to work, however,
|
||||
# obtain_certificate_from_csr requires parsed_args.domains to be set
|
||||
|
|
@ -688,6 +703,9 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False):
|
|||
helpful.add(
|
||||
"security", "--rsa-key-size", type=int, metavar="N",
|
||||
default=flag_default("rsa_key_size"), help=config_help("rsa_key_size"))
|
||||
helpful.add(
|
||||
"security", "--must-staple", action="store_true",
|
||||
help=config_help("must_staple"), dest="must_staple", default=False)
|
||||
helpful.add(
|
||||
"security", "--redirect", action="store_true",
|
||||
help="Automatically redirect all HTTP traffic to HTTPS for the newly "
|
||||
|
|
@ -712,9 +730,20 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False):
|
|||
" https:// for every http:// resource.", dest="uir", default=None)
|
||||
helpful.add(
|
||||
"security", "--no-uir", action="store_false",
|
||||
help=" Do not automatically set the \"Content-Security-Policy:"
|
||||
help="Do not automatically set the \"Content-Security-Policy:"
|
||||
" upgrade-insecure-requests\" header to every HTTP response.",
|
||||
dest="uir", default=None)
|
||||
helpful.add(
|
||||
"security", "--staple-ocsp", action="store_true",
|
||||
help="Enables OCSP Stapling. A valid OCSP response is stapled to"
|
||||
" the certificate that the server offers during TLS.",
|
||||
dest="staple", default=None)
|
||||
helpful.add(
|
||||
"security", "--no-staple-ocsp", action="store_false",
|
||||
help="Do not automatically enable OCSP Stapling.",
|
||||
dest="staple", default=None)
|
||||
|
||||
|
||||
helpful.add(
|
||||
"security", "--strict-permissions", action="store_true",
|
||||
help="Require that all configuration files are owned by the current "
|
||||
|
|
@ -729,7 +758,8 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False):
|
|||
" certificate lineage. You can try it with `--dry-run` first. For"
|
||||
" more fine-grained control, you can renew individual lineages with"
|
||||
" the `certonly` subcommand. Hooks are available to run commands "
|
||||
" before and after renewal; see XXX for more information on these.")
|
||||
" before and after renewal; see"
|
||||
" https://certbot.eff.org/docs/using.html#renewal for more information on these.")
|
||||
|
||||
helpful.add(
|
||||
"renew", "--pre-hook",
|
||||
|
|
@ -741,7 +771,8 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False):
|
|||
"renew", "--post-hook",
|
||||
help="Command to be run in a shell after attempting to obtain/renew "
|
||||
" certificates. Can be used to deploy renewed certificates, or to restart"
|
||||
" any servers that were stopped by --pre-hook.")
|
||||
" any servers that were stopped by --pre-hook. This is only run if"
|
||||
" an attempt was made to obtain/renew a certificate.")
|
||||
helpful.add(
|
||||
"renew", "--renew-hook",
|
||||
help="Command to be run in a shell once for each successfully renewed certificate."
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ from certbot import interfaces
|
|||
from certbot import le_util
|
||||
from certbot import reverter
|
||||
from certbot import storage
|
||||
from certbot import cli
|
||||
|
||||
from certbot.display import ops as display_ops
|
||||
from certbot.display import enhancements
|
||||
|
|
@ -245,8 +246,9 @@ class Client(object):
|
|||
domains,
|
||||
self.config.allow_subset_of_names)
|
||||
|
||||
domains = [a.body.identifier.value.encode('ascii')
|
||||
for a in authzr]
|
||||
auth_domains = set(a.body.identifier.value.encode('ascii')
|
||||
for a in authzr)
|
||||
domains = [d for d in domains if d in auth_domains]
|
||||
|
||||
# Create CSR from names
|
||||
key = crypto_util.init_save_key(
|
||||
|
|
@ -316,23 +318,30 @@ class Client(object):
|
|||
|
||||
cert_pem = OpenSSL.crypto.dump_certificate(
|
||||
OpenSSL.crypto.FILETYPE_PEM, certr.body.wrapped)
|
||||
cert_file, act_cert_path = le_util.unique_file(cert_path, 0o644)
|
||||
|
||||
cert_file, abs_cert_path = _open_pem_file('cert_path', cert_path)
|
||||
|
||||
try:
|
||||
cert_file.write(cert_pem)
|
||||
finally:
|
||||
cert_file.close()
|
||||
logger.info("Server issued certificate; certificate written to %s",
|
||||
act_cert_path)
|
||||
abs_cert_path)
|
||||
|
||||
cert_chain_abspath = None
|
||||
fullchain_abspath = None
|
||||
if chain_cert:
|
||||
if not chain_cert:
|
||||
return abs_cert_path, None, None
|
||||
else:
|
||||
chain_pem = crypto_util.dump_pyopenssl_chain(chain_cert)
|
||||
cert_chain_abspath = _save_chain(chain_pem, chain_path)
|
||||
fullchain_abspath = _save_chain(cert_pem + chain_pem,
|
||||
fullchain_path)
|
||||
|
||||
return os.path.abspath(act_cert_path), cert_chain_abspath, fullchain_abspath
|
||||
chain_file, abs_chain_path =\
|
||||
_open_pem_file('chain_path', chain_path)
|
||||
fullchain_file, abs_fullchain_path =\
|
||||
_open_pem_file('fullchain_path', fullchain_path)
|
||||
|
||||
_save_chain(chain_pem, chain_file)
|
||||
_save_chain(cert_pem + chain_pem, fullchain_file)
|
||||
|
||||
return abs_cert_path, abs_chain_path, abs_fullchain_path
|
||||
|
||||
def deploy_certificate(self, domains, privkey_path,
|
||||
cert_path, chain_path, fullchain_path):
|
||||
|
|
@ -395,7 +404,8 @@ class Client(object):
|
|||
supported = self.installer.supported_enhancements()
|
||||
redirect = config.redirect if "redirect" in supported else False
|
||||
hsts = config.hsts if "ensure-http-header" in supported else False
|
||||
uir = config.uir if "ensure-http-header" in supported else False
|
||||
uir = config.uir if "ensure-http-header" in supported else False
|
||||
staple = config.staple if "staple-ocsp" in supported else False
|
||||
|
||||
if redirect is None:
|
||||
redirect = enhancements.ask("redirect")
|
||||
|
|
@ -409,9 +419,11 @@ class Client(object):
|
|||
if uir:
|
||||
self.apply_enhancement(domains, "ensure-http-header",
|
||||
"Upgrade-Insecure-Requests")
|
||||
if staple:
|
||||
self.apply_enhancement(domains, "staple-ocsp")
|
||||
|
||||
msg = ("We were unable to restart web server")
|
||||
if redirect or hsts or uir:
|
||||
if redirect or hsts or uir or staple:
|
||||
with error_handler.ErrorHandler(self._rollback_and_restart, msg):
|
||||
self.installer.restart()
|
||||
|
||||
|
|
@ -561,24 +573,35 @@ def view_config_changes(config, num=None):
|
|||
rev.recovery_routine()
|
||||
rev.view_config_changes(num)
|
||||
|
||||
def _open_pem_file(cli_arg_path, pem_path):
|
||||
"""Open a pem file.
|
||||
|
||||
def _save_chain(chain_pem, chain_path):
|
||||
If cli_arg_path was set by the client, open that.
|
||||
Otherwise, uniquify the file path.
|
||||
|
||||
:param str cli_arg_path: the cli arg name, e.g. cert_path
|
||||
:param str pem_path: the pem file path to open
|
||||
|
||||
:returns: a tuple of file object and its absolute file path
|
||||
|
||||
"""
|
||||
if cli.set_by_cli(cli_arg_path):
|
||||
return le_util.safe_open(pem_path, chmod=0o644),\
|
||||
os.path.abspath(pem_path)
|
||||
else:
|
||||
uniq = le_util.unique_file(pem_path, 0o644)
|
||||
return uniq[0], os.path.abspath(uniq[1])
|
||||
|
||||
def _save_chain(chain_pem, chain_file):
|
||||
"""Saves chain_pem at a unique path based on chain_path.
|
||||
|
||||
:param str chain_pem: certificate chain in PEM format
|
||||
:param str chain_path: candidate path for the cert chain
|
||||
|
||||
:returns: absolute path to saved cert chain
|
||||
:rtype: str
|
||||
:param str chain_file: chain file object
|
||||
|
||||
"""
|
||||
chain_file, act_chain_path = le_util.unique_file(chain_path, 0o644)
|
||||
try:
|
||||
chain_file.write(chain_pem)
|
||||
finally:
|
||||
chain_file.close()
|
||||
|
||||
logger.info("Cert chain written to %s", act_chain_path)
|
||||
|
||||
# This expects a valid chain file
|
||||
return os.path.abspath(act_chain_path)
|
||||
logger.info("Cert chain written to %s", chain_file.name)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
"""
|
||||
import logging
|
||||
import os
|
||||
import traceback
|
||||
|
||||
import OpenSSL
|
||||
import pyrfc3339
|
||||
|
|
@ -75,9 +76,11 @@ def init_save_csr(privkey, names, path, csrname="csr-certbot.pem"):
|
|||
:rtype: :class:`certbot.le_util.CSR`
|
||||
|
||||
"""
|
||||
csr_pem, csr_der = make_csr(privkey.pem, names)
|
||||
|
||||
config = zope.component.getUtility(interfaces.IConfig)
|
||||
|
||||
csr_pem, csr_der = make_csr(privkey.pem, names,
|
||||
must_staple=config.must_staple)
|
||||
|
||||
# Save CSR
|
||||
le_util.make_or_verify_dir(path, 0o755, os.geteuid(),
|
||||
config.strict_permissions)
|
||||
|
|
@ -92,7 +95,7 @@ def init_save_csr(privkey, names, path, csrname="csr-certbot.pem"):
|
|||
|
||||
|
||||
# Lower level functions
|
||||
def make_csr(key_str, domains):
|
||||
def make_csr(key_str, domains, must_staple=False):
|
||||
"""Generate a CSR.
|
||||
|
||||
:param str key_str: PEM-encoded RSA key.
|
||||
|
|
@ -111,13 +114,19 @@ def make_csr(key_str, domains):
|
|||
req.get_subject().CN = domains[0]
|
||||
# TODO: what to put into req.get_subject()?
|
||||
# TODO: put SAN if len(domains) > 1
|
||||
req.add_extensions([
|
||||
extensions = [
|
||||
OpenSSL.crypto.X509Extension(
|
||||
"subjectAltName",
|
||||
critical=False,
|
||||
value=", ".join("DNS:%s" % d for d in domains)
|
||||
),
|
||||
])
|
||||
)
|
||||
]
|
||||
if must_staple:
|
||||
extensions.append(OpenSSL.crypto.X509Extension(
|
||||
"1.3.6.1.5.5.7.1.24",
|
||||
critical=False,
|
||||
value="DER:30:03:02:01:05"))
|
||||
req.add_extensions(extensions)
|
||||
req.set_version(2)
|
||||
req.set_pubkey(pkey)
|
||||
req.sign(pkey, "sha256")
|
||||
|
|
@ -171,6 +180,30 @@ def csr_matches_pubkey(csr, privkey):
|
|||
return False
|
||||
|
||||
|
||||
def import_csr_file(csrfile, data):
|
||||
"""Import a CSR file, which can be either PEM or DER.
|
||||
|
||||
:param str csrfile: CSR filename
|
||||
:param str data: contents of the CSR file
|
||||
|
||||
:returns: (`OpenSSL.crypto.FILETYPE_PEM` or `OpenSSL.crypto.FILETYPE_ASN1`,
|
||||
le_util.CSR object representing the CSR,
|
||||
list of domains requested in the CSR)
|
||||
:rtype: tuple
|
||||
|
||||
"""
|
||||
for form, typ in (("der", OpenSSL.crypto.FILETYPE_ASN1,),
|
||||
("pem", OpenSSL.crypto.FILETYPE_PEM,),):
|
||||
try:
|
||||
domains = get_names_from_csr(data, typ)
|
||||
except OpenSSL.crypto.Error:
|
||||
logger.debug("CSR parse error (form=%s, typ=%s):", form, typ)
|
||||
logger.debug(traceback.format_exc())
|
||||
continue
|
||||
return typ, le_util.CSR(file=csrfile, data=data, form=form), domains
|
||||
raise errors.Error("Failed to parse CSR file: {0}".format(csrfile))
|
||||
|
||||
|
||||
def make_key(bits):
|
||||
"""Generate PEM encoded RSA key.
|
||||
|
||||
|
|
@ -220,15 +253,20 @@ def pyopenssl_load_certificate(data):
|
|||
str(error) for error in openssl_errors)))
|
||||
|
||||
|
||||
def _get_sans_from_cert_or_req(cert_or_req_str, load_func,
|
||||
typ=OpenSSL.crypto.FILETYPE_PEM):
|
||||
def _load_cert_or_req(cert_or_req_str, load_func,
|
||||
typ=OpenSSL.crypto.FILETYPE_PEM):
|
||||
try:
|
||||
cert_or_req = load_func(typ, cert_or_req_str)
|
||||
return load_func(typ, cert_or_req_str)
|
||||
except OpenSSL.crypto.Error as error:
|
||||
logger.exception(error)
|
||||
raise
|
||||
|
||||
|
||||
def _get_sans_from_cert_or_req(cert_or_req_str, load_func,
|
||||
typ=OpenSSL.crypto.FILETYPE_PEM):
|
||||
# pylint: disable=protected-access
|
||||
return acme_crypto_util._pyopenssl_cert_or_req_san(cert_or_req)
|
||||
return acme_crypto_util._pyopenssl_cert_or_req_san(_load_cert_or_req(
|
||||
cert_or_req_str, load_func, typ))
|
||||
|
||||
|
||||
def get_sans_from_cert(cert, typ=OpenSSL.crypto.FILETYPE_PEM):
|
||||
|
|
@ -259,6 +297,25 @@ def get_sans_from_csr(csr, typ=OpenSSL.crypto.FILETYPE_PEM):
|
|||
csr, OpenSSL.crypto.load_certificate_request, typ)
|
||||
|
||||
|
||||
def get_names_from_csr(csr, typ=OpenSSL.crypto.FILETYPE_PEM):
|
||||
"""Get a list of domains from a CSR, including the CN if it is set.
|
||||
|
||||
:param str csr: CSR (encoded).
|
||||
:param typ: `OpenSSL.crypto.FILETYPE_PEM` or `OpenSSL.crypto.FILETYPE_ASN1`
|
||||
|
||||
:returns: A list of domain names.
|
||||
:rtype: list
|
||||
|
||||
"""
|
||||
loaded_csr = _load_cert_or_req(
|
||||
csr, OpenSSL.crypto.load_certificate_request, typ)
|
||||
# Use a set to avoid duplication with CN and Subject Alt Names
|
||||
domains = set(d for d in (loaded_csr.get_subject().CN,) if d is not None)
|
||||
# pylint: disable=protected-access
|
||||
domains.update(acme_crypto_util._pyopenssl_cert_or_req_san(loaded_csr))
|
||||
return list(domains)
|
||||
|
||||
|
||||
def dump_pyopenssl_chain(chain, filetype=OpenSSL.crypto.FILETYPE_PEM):
|
||||
"""Dump certificate chain into a bundle.
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ def _validate_hook(shell_cmd, hook_name):
|
|||
:raises .errors.HookCommandNotFound: if the command is not found
|
||||
"""
|
||||
if shell_cmd:
|
||||
cmd = shell_cmd.partition(" ")[0]
|
||||
cmd = shell_cmd.split(None, 1)[0]
|
||||
if not _prog(cmd):
|
||||
path = os.environ["PATH"]
|
||||
msg = "Unable to find {2}-hook command {0} in the PATH.\n(PATH is {1})".format(
|
||||
|
|
@ -39,7 +39,7 @@ def pre_hook(config):
|
|||
if config.pre_hook and not pre_hook.already:
|
||||
logger.info("Running pre-hook command: %s", config.pre_hook)
|
||||
_run_hook(config.pre_hook)
|
||||
pre_hook.already = True
|
||||
pre_hook.already = True
|
||||
|
||||
pre_hook.already = False
|
||||
|
||||
|
|
@ -50,6 +50,11 @@ def post_hook(config, final=False):
|
|||
we're called with final=True before actually doing anything.
|
||||
"""
|
||||
if config.post_hook:
|
||||
if not pre_hook.already:
|
||||
logger.info("No renewals attempted, so not running post-hook")
|
||||
if config.verb != "renew":
|
||||
logger.warn("Sanity failure in renewal hooks")
|
||||
return
|
||||
if final or config.verb != "renew":
|
||||
logger.info("Running post-hook command: %s", config.post_hook)
|
||||
_run_hook(config.post_hook)
|
||||
|
|
|
|||
|
|
@ -200,6 +200,10 @@ class IConfig(zope.interface.Interface):
|
|||
email = zope.interface.Attribute(
|
||||
"Email used for registration and recovery contact.")
|
||||
rsa_key_size = zope.interface.Attribute("Size of the RSA key.")
|
||||
must_staple = zope.interface.Attribute(
|
||||
"Whether to request the OCSP Must Staple certificate extension. "
|
||||
"Additional setup may be required after issuance. This does not "
|
||||
"currently autoconfigure web servers for OCSP stapling. ")
|
||||
|
||||
config_dir = zope.interface.Attribute("Configuration directory.")
|
||||
work_dir = zope.interface.Attribute("Working directory.")
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
"""Utilities for all Certbot."""
|
||||
import argparse
|
||||
import collections
|
||||
# distutils.version under virtualenv confuses pylint
|
||||
# For more info, see: https://github.com/PyCQA/pylint/issues/73
|
||||
import distutils.version # pylint: disable=import-error,no-name-in-module
|
||||
import errno
|
||||
import logging
|
||||
import os
|
||||
|
|
@ -151,7 +154,8 @@ def _unique_file(path, filename_pat, count, mode):
|
|||
while True:
|
||||
current_path = os.path.join(path, filename_pat(count))
|
||||
try:
|
||||
return safe_open(current_path, chmod=mode), current_path
|
||||
return safe_open(current_path, chmod=mode),\
|
||||
os.path.abspath(current_path)
|
||||
except OSError as err:
|
||||
# "File exists," is okay, try a different name.
|
||||
if err.errno != errno.EEXIST:
|
||||
|
|
@ -342,3 +346,17 @@ def enforce_domain_sanity(domain):
|
|||
if not fqdn.match(domain):
|
||||
raise errors.ConfigurationError("Requested domain {0} is not a FQDN".format(domain))
|
||||
return domain
|
||||
|
||||
|
||||
def get_strict_version(normalized):
|
||||
"""Converts a normalized version to a strict version.
|
||||
|
||||
:param str normalized: normalized version string
|
||||
|
||||
:returns: An equivalent strict version
|
||||
:rtype: distutils.version.StrictVersion
|
||||
|
||||
"""
|
||||
# strict version ending with "a" and a number designates a pre-release
|
||||
# pylint: disable=no-member
|
||||
return distutils.version.StrictVersion(normalized.replace(".dev", "a"))
|
||||
|
|
|
|||
|
|
@ -94,10 +94,10 @@ def _auth_from_domains(le_client, config, domains, lineage=None):
|
|||
if lineage is False:
|
||||
raise errors.Error("Certificate could not be obtained")
|
||||
finally:
|
||||
hooks.post_hook(config)
|
||||
hooks.post_hook(config, final=False)
|
||||
|
||||
if not config.dry_run and not config.verb == "renew":
|
||||
_report_new_cert(lineage.cert, lineage.fullchain)
|
||||
_report_new_cert(config, lineage.cert, lineage.fullchain)
|
||||
|
||||
return lineage, action
|
||||
|
||||
|
|
@ -267,7 +267,7 @@ def _find_domains(config, installer):
|
|||
return domains
|
||||
|
||||
|
||||
def _report_new_cert(cert_path, fullchain_path):
|
||||
def _report_new_cert(config, cert_path, fullchain_path):
|
||||
"""Reports the creation of a new certificate to the user.
|
||||
|
||||
:param str cert_path: path to cert
|
||||
|
|
@ -285,12 +285,15 @@ def _report_new_cert(cert_path, fullchain_path):
|
|||
# Unless we're in .csr mode and there really isn't one
|
||||
and_chain = "has "
|
||||
path = cert_path
|
||||
|
||||
verbswitch = ' with the "certonly" option' if config.verb == "run" else ""
|
||||
# XXX Perhaps one day we could detect the presence of known old webservers
|
||||
# and say something more informative here.
|
||||
msg = ("Congratulations! Your certificate {0} been saved at {1}."
|
||||
" Your cert will expire on {2}. To obtain a new version of the "
|
||||
"certificate in the future, simply run Certbot again."
|
||||
.format(and_chain, path, expiry))
|
||||
msg = ('Congratulations! Your certificate {0} been saved at {1}.'
|
||||
' Your cert will expire on {2}. To obtain a new or tweaked version of this '
|
||||
'certificate in the future, simply run {3} again{4}. '
|
||||
'To non-interactively renew *all* of your ceriticates, run "{3} renew"'
|
||||
.format(and_chain, path, expiry, cli.cli_command, verbswitch))
|
||||
reporter_util.add_message(msg, reporter_util.MEDIUM_PRIORITY)
|
||||
|
||||
|
||||
|
|
@ -485,7 +488,7 @@ def _csr_obtain_cert(config, le_client):
|
|||
else:
|
||||
cert_path, _, cert_fullchain = le_client.save_certificate(
|
||||
certr, chain, config.cert_path, config.chain_path, config.fullchain_path)
|
||||
_report_new_cert(cert_path, cert_fullchain)
|
||||
_report_new_cert(config, cert_path, cert_fullchain)
|
||||
|
||||
|
||||
def obtain_cert(config, plugins, lineage=None):
|
||||
|
|
@ -661,6 +664,7 @@ def main(cli_args=sys.argv[1:]):
|
|||
le_util.make_or_verify_dir(
|
||||
config.logs_dir, 0o700, os.geteuid(), "--strict-permissions" in cli_args)
|
||||
setup_logging(config, _cli_log_handler, logfile='letsencrypt.log')
|
||||
cli.possible_deprecation_warning(config)
|
||||
|
||||
logger.debug("certbot version: %s", certbot.__version__)
|
||||
# do not log `config`, as it contains sensitive data (e.g. revoke --key)!
|
||||
|
|
@ -687,11 +691,6 @@ def main(cli_args=sys.argv[1:]):
|
|||
displayer = display_util.NoninteractiveDisplay(sys.stdout)
|
||||
elif config.text_mode:
|
||||
displayer = display_util.FileDisplay(sys.stdout)
|
||||
elif config.dialog_mode:
|
||||
displayer = display_util.NcursesDisplay()
|
||||
elif config.verb == "renew":
|
||||
config.noninteractive_mode = True
|
||||
displayer = display_util.NoninteractiveDisplay(sys.stdout)
|
||||
else:
|
||||
displayer = display_util.NcursesDisplay()
|
||||
zope.component.provideUtility(displayer)
|
||||
|
|
|
|||
|
|
@ -120,6 +120,14 @@ def supported_challenges_validator(data):
|
|||
|
||||
"""
|
||||
challs = data.split(",")
|
||||
|
||||
# tls-sni-01 was dvsni during private beta
|
||||
if "dvsni" in challs:
|
||||
logger.info("Updating legacy standalone_supported_challenges value")
|
||||
challs = [challenges.TLSSNI01.typ if chall == "dvsni" else chall
|
||||
for chall in challs]
|
||||
data = ",".join(challs)
|
||||
|
||||
unrecognized = [name for name in challs
|
||||
if name not in challenges.Challenge.TYPES]
|
||||
if unrecognized:
|
||||
|
|
|
|||
|
|
@ -85,6 +85,11 @@ class SupportedChallengesValidatorTest(unittest.TestCase):
|
|||
def test_not_subset(self):
|
||||
self.assertRaises(argparse.ArgumentTypeError, self._call, "dns")
|
||||
|
||||
def test_dvsni(self):
|
||||
self.assertEqual("tls-sni-01", self._call("dvsni"))
|
||||
self.assertEqual("http-01,tls-sni-01", self._call("http-01,dvsni"))
|
||||
self.assertEqual("tls-sni-01,http-01", self._call("dvsni,http-01"))
|
||||
|
||||
|
||||
class AuthenticatorTest(unittest.TestCase):
|
||||
"""Tests for certbot.plugins.standalone.Authenticator."""
|
||||
|
|
|
|||
|
|
@ -181,12 +181,8 @@ to serve all files under specified web root ({0})."""
|
|||
os.chown(self.full_roots[name], stat_path.st_uid,
|
||||
stat_path.st_gid)
|
||||
except OSError as exception:
|
||||
if exception.errno == errno.EACCES:
|
||||
logger.debug("Insufficient permissions to change owner and uid - ignoring")
|
||||
else:
|
||||
raise errors.PluginError(
|
||||
"Couldn't create root for {0} http-01 "
|
||||
"challenge responses: {1}", name, exception)
|
||||
logger.info("Unable to change owner and uid of webroot directory")
|
||||
logger.debug("Error was: %s", exception)
|
||||
|
||||
except OSError as exception:
|
||||
if exception.errno != errno.EEXIST:
|
||||
|
|
@ -235,14 +231,9 @@ to serve all files under specified web root ({0})."""
|
|||
logger.debug("All challenges cleaned up, removing %s",
|
||||
root_path)
|
||||
except OSError as exc:
|
||||
if exc.errno == errno.ENOTEMPTY:
|
||||
logger.debug("Challenges cleaned up but %s not empty",
|
||||
root_path)
|
||||
elif exc.errno == errno.EACCES:
|
||||
logger.debug("Challenges cleaned up but no permissions for %s",
|
||||
root_path)
|
||||
else:
|
||||
raise
|
||||
logger.info(
|
||||
"Unable to clean up challenge directory %s", root_path)
|
||||
logger.debug("Error was: %s", exc)
|
||||
|
||||
|
||||
class _WebrootMapAction(argparse.Action):
|
||||
|
|
|
|||
|
|
@ -138,15 +138,10 @@ class AuthenticatorTest(unittest.TestCase):
|
|||
os.chmod(self.path, 0o700)
|
||||
|
||||
@mock.patch("certbot.plugins.webroot.os.chown")
|
||||
def test_failed_chown_eacces(self, mock_chown):
|
||||
def test_failed_chown(self, mock_chown):
|
||||
mock_chown.side_effect = OSError(errno.EACCES, "msg")
|
||||
self.auth.perform([self.achall]) # exception caught and logged
|
||||
|
||||
@mock.patch("certbot.plugins.webroot.os.chown")
|
||||
def test_failed_chown_not_eacces(self, mock_chown):
|
||||
mock_chown.side_effect = OSError()
|
||||
self.assertRaises(errors.PluginError, self.auth.perform, [])
|
||||
|
||||
def test_perform_permissions(self):
|
||||
self.auth.prepare()
|
||||
|
||||
|
|
@ -200,7 +195,7 @@ class AuthenticatorTest(unittest.TestCase):
|
|||
os.rmdir(leftover_path)
|
||||
|
||||
@mock.patch('os.rmdir')
|
||||
def test_cleanup_permission_denied(self, mock_rmdir):
|
||||
def test_cleanup_failure(self, mock_rmdir):
|
||||
self.auth.prepare()
|
||||
self.auth.perform([self.achall])
|
||||
|
||||
|
|
@ -212,19 +207,6 @@ class AuthenticatorTest(unittest.TestCase):
|
|||
self.assertFalse(os.path.exists(self.validation_path))
|
||||
self.assertTrue(os.path.exists(self.root_challenge_path))
|
||||
|
||||
@mock.patch('os.rmdir')
|
||||
def test_cleanup_oserror(self, mock_rmdir):
|
||||
self.auth.prepare()
|
||||
self.auth.perform([self.achall])
|
||||
|
||||
os_error = OSError()
|
||||
os_error.errno = errno.ENOENT
|
||||
mock_rmdir.side_effect = os_error
|
||||
|
||||
self.assertRaises(OSError, self.auth.cleanup, [self.achall])
|
||||
self.assertFalse(os.path.exists(self.validation_path))
|
||||
self.assertTrue(os.path.exists(self.root_challenge_path))
|
||||
|
||||
|
||||
class WebrootActionTest(unittest.TestCase):
|
||||
"""Tests for webroot argparse actions."""
|
||||
|
|
|
|||
|
|
@ -301,7 +301,10 @@ def _renew_describe_results(config, renew_successes, renew_failures,
|
|||
def renew_all_lineages(config):
|
||||
"""Examine each lineage; renew if due and report results"""
|
||||
|
||||
if config.domains != []:
|
||||
# This is trivially False if config.domains is empty
|
||||
if any(domain not in config.webroot_map for domain in config.domains):
|
||||
# If more plugins start using cli.add_domains,
|
||||
# we may want to only log a warning here
|
||||
raise errors.Error("Currently, the renew verb is only capable of "
|
||||
"renewing all installed certificates that are due "
|
||||
"to be renewed; individual domains cannot be "
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import shutil
|
|||
import time
|
||||
import traceback
|
||||
|
||||
|
||||
import zope.component
|
||||
|
||||
from certbot import constants
|
||||
|
|
@ -489,7 +490,7 @@ class Reverter(object):
|
|||
|
||||
if not os.path.exists(changes_since_path):
|
||||
logger.info("Rollback checkpoint is empty (no changes made?)")
|
||||
with open(self.config.changes_since_path) as f:
|
||||
with open(changes_since_path, 'w') as f:
|
||||
f.write("No changes\n")
|
||||
|
||||
# Add title to self.config.in_progress_dir CHANGES_SINCE
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import configobj
|
|||
import parsedatetime
|
||||
import pytz
|
||||
|
||||
import certbot
|
||||
from certbot import constants
|
||||
from certbot import crypto_util
|
||||
from certbot import errors
|
||||
|
|
@ -17,6 +18,7 @@ from certbot import le_util
|
|||
logger = logging.getLogger(__name__)
|
||||
|
||||
ALL_FOUR = ("cert", "privkey", "chain", "fullchain")
|
||||
CURRENT_VERSION = le_util.get_strict_version(certbot.__version__)
|
||||
|
||||
|
||||
def config_with_defaults(config=None):
|
||||
|
|
@ -63,6 +65,7 @@ def write_renewal_config(o_filename, n_filename, target, relevant_data):
|
|||
|
||||
"""
|
||||
config = configobj.ConfigObj(o_filename)
|
||||
config["version"] = certbot.__version__
|
||||
for kind in ALL_FOUR:
|
||||
config[kind] = target[kind]
|
||||
|
||||
|
|
@ -78,6 +81,10 @@ def write_renewal_config(o_filename, n_filename, target, relevant_data):
|
|||
if k not in relevant_data:
|
||||
del config["renewalparams"][k]
|
||||
|
||||
if "renew_before_expiry" not in config:
|
||||
default_interval = constants.RENEWER_DEFAULTS["renew_before_expiry"]
|
||||
config.initial_comment = ["renew_before_expiry = " + default_interval]
|
||||
|
||||
# TODO: add human-readable comments explaining other available
|
||||
# parameters
|
||||
logger.debug("Writing new config %s.", n_filename)
|
||||
|
|
@ -255,6 +262,14 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes
|
|||
"renewal config file {0} is missing a required "
|
||||
"file reference".format(self.configfile))
|
||||
|
||||
conf_version = self.configuration.get("version")
|
||||
if (conf_version is not None and
|
||||
le_util.get_strict_version(conf_version) > CURRENT_VERSION):
|
||||
logger.warning(
|
||||
"Attempting to parse the version %s renewal configuration "
|
||||
"file found at %s with version %s of Certbot. This might not "
|
||||
"work.", conf_version, config_filename, certbot.__version__)
|
||||
|
||||
self.cert = self.configuration["cert"]
|
||||
self.privkey = self.configuration["privkey"]
|
||||
self.chain = self.configuration["chain"]
|
||||
|
|
|
|||
|
|
@ -357,8 +357,9 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods
|
|||
['-d', '204.11.231.35'])
|
||||
|
||||
def test_csr_with_besteffort(self):
|
||||
args = ["--csr", CSR, "--allow-subset-of-names"]
|
||||
self.assertRaises(errors.Error, self._call, args)
|
||||
self.assertRaises(
|
||||
errors.Error, self._call,
|
||||
'certonly --csr {0} --allow-subset-of-names'.format(CSR).split())
|
||||
|
||||
def test_run_with_csr(self):
|
||||
# This is an error because you can only use --csr with certonly
|
||||
|
|
@ -369,6 +370,17 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods
|
|||
return
|
||||
assert False, "Expected supplying --csr to fail with default verb"
|
||||
|
||||
def test_csr_with_no_domains(self):
|
||||
self.assertRaises(
|
||||
errors.Error, self._call,
|
||||
'certonly --csr {0}'.format(
|
||||
test_util.vector_path('csr-nonames.pem')).split())
|
||||
|
||||
def test_csr_with_inconsistent_domains(self):
|
||||
self.assertRaises(
|
||||
errors.Error, self._call,
|
||||
'certonly -d example.org --csr {0}'.format(CSR).split())
|
||||
|
||||
def _get_argument_parser(self):
|
||||
plugins = disco.PluginsRegistry.find_all()
|
||||
return functools.partial(cli.prepare_and_parse_args, plugins)
|
||||
|
|
@ -720,6 +732,12 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods
|
|||
self._test_renew_common(renewalparams=renewalparams,
|
||||
assert_oc_called=True)
|
||||
|
||||
def test_renew_with_webroot_map(self):
|
||||
renewalparams = {'authenticator': 'webroot'}
|
||||
self._test_renew_common(
|
||||
renewalparams=renewalparams, assert_oc_called=True,
|
||||
args=['renew', '--webroot-map', '{"example.com": "/tmp"}'])
|
||||
|
||||
def test_renew_reconstitute_error(self):
|
||||
# pylint: disable=protected-access
|
||||
with mock.patch('certbot.main.renewal._reconstitute') as mock_reconstitute:
|
||||
|
|
|
|||
|
|
@ -134,57 +134,39 @@ class ClientTest(unittest.TestCase):
|
|||
|
||||
self.acme.fetch_chain.assert_called_once_with(mock.sentinel.certr)
|
||||
|
||||
# FIXME move parts of this to test_cli.py...
|
||||
@mock.patch("certbot.client.logger")
|
||||
def test_obtain_certificate_from_csr(self, mock_logger):
|
||||
self._mock_obtain_certificate()
|
||||
from certbot import cli
|
||||
test_csr = le_util.CSR(form="der", file=None, data=CSR_SAN)
|
||||
mock_parsed_args = mock.MagicMock()
|
||||
# The CLI should believe that this is a certonly request, because
|
||||
# a CSR would not be allowed with other kinds of requests!
|
||||
mock_parsed_args.verb = "certonly"
|
||||
with mock.patch("certbot.client.le_util.CSR") as mock_CSR:
|
||||
mock_CSR.return_value = test_csr
|
||||
mock_parsed_args.domains = self.eg_domains[:]
|
||||
mock_parser = mock.MagicMock(cli.HelpfulArgumentParser)
|
||||
cli.HelpfulArgumentParser.handle_csr(mock_parser, mock_parsed_args)
|
||||
auth_handler = self.client.auth_handler
|
||||
|
||||
# Now provoke an inconsistent domains error...
|
||||
mock_parsed_args.domains.append("hippopotamus.io")
|
||||
self.assertRaises(errors.ConfigurationError,
|
||||
cli.HelpfulArgumentParser.handle_csr, mock_parser, mock_parsed_args)
|
||||
|
||||
authzr = self.client.auth_handler.get_authorizations(self.eg_domains, False)
|
||||
|
||||
self.assertEqual(
|
||||
(mock.sentinel.certr, mock.sentinel.chain),
|
||||
self.client.obtain_certificate_from_csr(
|
||||
self.eg_domains,
|
||||
test_csr,
|
||||
authzr=authzr))
|
||||
# and that the cert was obtained correctly
|
||||
self._check_obtain_certificate()
|
||||
|
||||
# Test for authzr=None
|
||||
self.assertEqual(
|
||||
(mock.sentinel.certr, mock.sentinel.chain),
|
||||
self.client.obtain_certificate_from_csr(
|
||||
self.eg_domains,
|
||||
test_csr,
|
||||
authzr=None))
|
||||
|
||||
self.client.auth_handler.get_authorizations.assert_called_with(
|
||||
self.eg_domains)
|
||||
|
||||
# Test for no auth_handler
|
||||
self.client.auth_handler = None
|
||||
self.assertRaises(
|
||||
errors.Error,
|
||||
self.client.obtain_certificate_from_csr,
|
||||
authzr = auth_handler.get_authorizations(self.eg_domains, False)
|
||||
self.assertEqual(
|
||||
(mock.sentinel.certr, mock.sentinel.chain),
|
||||
self.client.obtain_certificate_from_csr(
|
||||
self.eg_domains,
|
||||
test_csr)
|
||||
mock_logger.warning.assert_called_once_with(mock.ANY)
|
||||
test_csr,
|
||||
authzr=authzr))
|
||||
# and that the cert was obtained correctly
|
||||
self._check_obtain_certificate()
|
||||
|
||||
# Test for authzr=None
|
||||
self.assertEqual(
|
||||
(mock.sentinel.certr, mock.sentinel.chain),
|
||||
self.client.obtain_certificate_from_csr(
|
||||
self.eg_domains,
|
||||
test_csr,
|
||||
authzr=None))
|
||||
auth_handler.get_authorizations.assert_called_with(self.eg_domains)
|
||||
|
||||
# Test for no auth_handler
|
||||
self.client.auth_handler = None
|
||||
self.assertRaises(
|
||||
errors.Error,
|
||||
self.client.obtain_certificate_from_csr,
|
||||
self.eg_domains,
|
||||
test_csr)
|
||||
mock_logger.warning.assert_called_once_with(mock.ANY)
|
||||
|
||||
@mock.patch("certbot.client.crypto_util")
|
||||
def test_obtain_certificate(self, mock_crypto_util):
|
||||
|
|
@ -201,7 +183,8 @@ class ClientTest(unittest.TestCase):
|
|||
|
||||
authzr = []
|
||||
|
||||
for domain in domains:
|
||||
# domain ordering should not be affected by authorization order
|
||||
for domain in reversed(domains):
|
||||
authzr.append(
|
||||
mock.MagicMock(
|
||||
body=mock.MagicMock(
|
||||
|
|
@ -220,7 +203,9 @@ class ClientTest(unittest.TestCase):
|
|||
mock.sentinel.key, domains, self.config.csr_dir)
|
||||
self._check_obtain_certificate()
|
||||
|
||||
def test_save_certificate(self):
|
||||
@mock.patch("certbot.cli.helpful_parser")
|
||||
def test_save_certificate(self, mock_parser):
|
||||
# pylint: disable=too-many-locals
|
||||
certs = ["matching_cert.pem", "cert.pem", "cert-san.pem"]
|
||||
tmp_path = tempfile.mkdtemp()
|
||||
os.chmod(tmp_path, 0o755) # TODO: really??
|
||||
|
|
@ -231,6 +216,10 @@ class ClientTest(unittest.TestCase):
|
|||
candidate_cert_path = os.path.join(tmp_path, "certs", "cert.pem")
|
||||
candidate_chain_path = os.path.join(tmp_path, "chains", "chain.pem")
|
||||
candidate_fullchain_path = os.path.join(tmp_path, "chains", "fullchain.pem")
|
||||
mock_parser.verb = "certonly"
|
||||
mock_parser.args = ["--cert-path", candidate_cert_path,
|
||||
"--chain-path", candidate_chain_path,
|
||||
"--fullchain-path", candidate_fullchain_path]
|
||||
|
||||
cert_path, chain_path, fullchain_path = self.client.save_certificate(
|
||||
certr, chain_cert, candidate_cert_path, candidate_chain_path,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import zope.component
|
|||
|
||||
from certbot import errors
|
||||
from certbot import interfaces
|
||||
from certbot import le_util
|
||||
from certbot.tests import test_util
|
||||
|
||||
|
||||
|
|
@ -95,6 +96,25 @@ class MakeCSRTest(unittest.TestCase):
|
|||
['example.com', 'www.example.com'], get_sans_from_csr(
|
||||
csr_der, OpenSSL.crypto.FILETYPE_ASN1))
|
||||
|
||||
def test_must_staple(self):
|
||||
# TODO: Fails for RSA256_KEY
|
||||
csr_pem, _ = self._call(
|
||||
RSA512_KEY, ['example.com', 'www.example.com'], must_staple=True)
|
||||
csr = OpenSSL.crypto.load_certificate_request(
|
||||
OpenSSL.crypto.FILETYPE_PEM, csr_pem)
|
||||
|
||||
# In pyopenssl 0.13 (used with TOXENV=py26-oldest and py27-oldest), csr
|
||||
# objects don't have a get_extensions() method, so we skip this test if
|
||||
# the method isn't available.
|
||||
if hasattr(csr, 'get_extensions'):
|
||||
# NOTE: Ideally we would filter by the TLS Feature OID, but
|
||||
# OpenSSL.crypto.X509Extension doesn't give us the extension's raw OID,
|
||||
# and the shortname field is just "UNDEF"
|
||||
must_staple_exts = [e for e in csr.get_extensions()
|
||||
if e.get_data() == "0\x03\x02\x01\x05"]
|
||||
self.assertEqual(len(must_staple_exts), 1,
|
||||
"Expected exactly one Must Staple extension")
|
||||
|
||||
|
||||
class ValidCSRTest(unittest.TestCase):
|
||||
"""Tests for certbot.crypto_util.valid_csr."""
|
||||
|
|
@ -140,6 +160,44 @@ class CSRMatchesPubkeyTest(unittest.TestCase):
|
|||
test_util.load_vector('csr.pem'), RSA256_KEY))
|
||||
|
||||
|
||||
class ImportCSRFileTest(unittest.TestCase):
|
||||
"""Tests for certbot.certbot_util.import_csr_file."""
|
||||
|
||||
@classmethod
|
||||
def _call(cls, *args, **kwargs):
|
||||
from certbot.crypto_util import import_csr_file
|
||||
return import_csr_file(*args, **kwargs)
|
||||
|
||||
def test_der_csr(self):
|
||||
csrfile = test_util.vector_path('csr.der')
|
||||
data = test_util.load_vector('csr.der')
|
||||
|
||||
self.assertEqual(
|
||||
(OpenSSL.crypto.FILETYPE_ASN1,
|
||||
le_util.CSR(file=csrfile,
|
||||
data=data,
|
||||
form="der"),
|
||||
["example.com"],),
|
||||
self._call(csrfile, data))
|
||||
|
||||
def test_pem_csr(self):
|
||||
csrfile = test_util.vector_path('csr.pem')
|
||||
data = test_util.load_vector('csr.pem')
|
||||
|
||||
self.assertEqual(
|
||||
(OpenSSL.crypto.FILETYPE_PEM,
|
||||
le_util.CSR(file=csrfile,
|
||||
data=data,
|
||||
form="pem"),
|
||||
["example.com"],),
|
||||
self._call(csrfile, data))
|
||||
|
||||
def test_bad_csr(self):
|
||||
self.assertRaises(errors.Error, self._call,
|
||||
test_util.vector_path('cert.pem'),
|
||||
test_util.load_vector('cert.pem'))
|
||||
|
||||
|
||||
class MakeKeyTest(unittest.TestCase): # pylint: disable=too-few-public-methods
|
||||
"""Tests for certbot.crypto_util.make_key."""
|
||||
|
||||
|
|
@ -215,6 +273,36 @@ class GetSANsFromCSRTest(unittest.TestCase):
|
|||
[], self._call(test_util.load_vector('csr-nosans.pem')))
|
||||
|
||||
|
||||
class GetNamesFromCSRTest(unittest.TestCase):
|
||||
"""Tests for certbot.crypto_util.get_names_from_csr."""
|
||||
@classmethod
|
||||
def _call(cls, *args, **kwargs):
|
||||
from certbot.crypto_util import get_names_from_csr
|
||||
return get_names_from_csr(*args, **kwargs)
|
||||
|
||||
def test_extract_one_san(self):
|
||||
self.assertEqual(['example.com'], self._call(
|
||||
test_util.load_vector('csr.pem')))
|
||||
|
||||
def test_extract_two_sans(self):
|
||||
self.assertEqual(set(('example.com', 'www.example.com',)), set(
|
||||
self._call(test_util.load_vector('csr-san.pem'))))
|
||||
|
||||
def test_extract_six_sans(self):
|
||||
self.assertEqual(
|
||||
set(self._call(test_util.load_vector('csr-6sans.pem'))),
|
||||
set(("example.com", "example.org", "example.net",
|
||||
"example.info", "subdomain.example.com",
|
||||
"other.subdomain.example.com",)))
|
||||
|
||||
def test_parse_non_csr(self):
|
||||
self.assertRaises(OpenSSL.crypto.Error, self._call, "hello there")
|
||||
|
||||
def test_parse_no_sans(self):
|
||||
self.assertEqual(["example.org"],
|
||||
self._call(test_util.load_vector('csr-nosans.pem')))
|
||||
|
||||
|
||||
class CertLoaderTest(unittest.TestCase):
|
||||
"""Tests for certbot.crypto_util.pyopenssl_load_certificate"""
|
||||
|
||||
|
|
|
|||
|
|
@ -56,14 +56,22 @@ class HookTest(unittest.TestCase):
|
|||
return mock_logger.warning
|
||||
|
||||
def test_pre_hook(self):
|
||||
hooks.pre_hook.already = False
|
||||
config = mock.MagicMock(pre_hook="true")
|
||||
self._test_a_hook(config, hooks.pre_hook, 1)
|
||||
config = mock.MagicMock(pre_hook="")
|
||||
self._test_a_hook(config, hooks.pre_hook, 0)
|
||||
|
||||
def test_post_hook(self):
|
||||
hooks.pre_hook.already = False
|
||||
# if pre-hook isn't called, post-hook shouldn't be
|
||||
config = mock.MagicMock(post_hook="true", verb="splonk")
|
||||
self._test_a_hook(config, hooks.post_hook, 0)
|
||||
|
||||
config = mock.MagicMock(post_hook="true", verb="splonk")
|
||||
self._test_a_hook(config, hooks.pre_hook, 1)
|
||||
self._test_a_hook(config, hooks.post_hook, 2)
|
||||
|
||||
config = mock.MagicMock(post_hook="true", verb="renew")
|
||||
self._test_a_hook(config, hooks.post_hook, 0)
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import unittest
|
|||
import mock
|
||||
import six
|
||||
|
||||
import certbot
|
||||
from certbot import errors
|
||||
|
||||
|
||||
|
|
@ -339,5 +340,32 @@ class EnforceDomainSanityTest(unittest.TestCase):
|
|||
u"eichh\u00f6rnchen.example.com")
|
||||
|
||||
|
||||
class GetStrictVersionTest(unittest.TestCase):
|
||||
"""Tests for certbot.le_util.get_strict_version."""
|
||||
|
||||
@classmethod
|
||||
def _call(cls, *args, **kwargs):
|
||||
from certbot.le_util import get_strict_version
|
||||
return get_strict_version(*args, **kwargs)
|
||||
|
||||
def test_two_dev_versions(self):
|
||||
self.assertTrue(
|
||||
self._call("0.0.0.dev20151006") < self._call("0.0.0.dev20151008"))
|
||||
|
||||
def test_one_dev_one_release_version(self):
|
||||
self.assertTrue(self._call("1.0.0.dev0") < self._call("1.0.0"))
|
||||
self.assertTrue(self._call("1.0.0") < self._call("1.0.1.dev0"))
|
||||
|
||||
def test_two_release_versions(self):
|
||||
self.assertTrue(self._call("0.0.0") < self._call("0.0.1"))
|
||||
self.assertTrue(self._call("0.0.0") < self._call("0.1.0"))
|
||||
self.assertTrue(self._call("0.0.0") < self._call("1.0.0"))
|
||||
|
||||
def test_current_version(self):
|
||||
current_version = self._call(certbot.__version__)
|
||||
self.assertTrue(self._call("0.6.0") < current_version)
|
||||
self.assertTrue(current_version < self._call("99.99.99"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main() # pragma: no cover
|
||||
|
|
|
|||
|
|
@ -34,6 +34,20 @@ class ReverterCheckpointLocalTest(unittest.TestCase):
|
|||
|
||||
logging.disable(logging.NOTSET)
|
||||
|
||||
@mock.patch("certbot.reverter.Reverter._read_and_append")
|
||||
def test_no_change(self, mock_read):
|
||||
mock_read.side_effect = OSError("cannot even")
|
||||
try:
|
||||
self.reverter.add_to_checkpoint(self.sets[0], "save1")
|
||||
except OSError:
|
||||
pass
|
||||
self.reverter.finalize_checkpoint("blah")
|
||||
path = os.listdir(self.reverter.config.backup_dir)[0]
|
||||
no_change = os.path.join(self.reverter.config.backup_dir, path, "CHANGES_SINCE")
|
||||
with open(no_change, "r") as f:
|
||||
x = f.read()
|
||||
self.assertTrue("No changes" in x)
|
||||
|
||||
def test_basic_add_to_temp_checkpoint(self):
|
||||
# These shouldn't conflict even though they are both named config.txt
|
||||
self.reverter.add_to_temp_checkpoint(self.sets[0], "save1")
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import configobj
|
|||
import mock
|
||||
import pytz
|
||||
|
||||
import certbot
|
||||
from certbot import configuration
|
||||
from certbot import errors
|
||||
from certbot.storage import ALL_FOUR
|
||||
|
|
@ -137,6 +138,28 @@ class RenewableCertTests(BaseRenewableCertTest):
|
|||
self.assertRaises(errors.CertStorageError, storage.RenewableCert,
|
||||
config.filename, self.cli_config)
|
||||
|
||||
def test_no_renewal_version(self):
|
||||
from certbot import storage
|
||||
|
||||
self._write_out_ex_kinds()
|
||||
self.assertTrue("version" not in self.config)
|
||||
|
||||
with mock.patch("certbot.storage.logger") as mock_logger:
|
||||
storage.RenewableCert(self.config.filename, self.cli_config)
|
||||
self.assertFalse(mock_logger.warning.called)
|
||||
|
||||
def test_renewal_newer_version(self):
|
||||
from certbot import storage
|
||||
|
||||
self._write_out_ex_kinds()
|
||||
self.config["version"] = "99.99.99"
|
||||
self.config.write()
|
||||
|
||||
with mock.patch("certbot.storage.logger") as mock_logger:
|
||||
storage.RenewableCert(self.config.filename, self.cli_config)
|
||||
self.assertTrue(mock_logger.warning.called)
|
||||
self.assertTrue("version" in mock_logger.warning.call_args[0][0])
|
||||
|
||||
def test_consistent(self):
|
||||
# pylint: disable=too-many-statements,protected-access
|
||||
oldcert = self.test_rc.cert
|
||||
|
|
@ -760,11 +783,14 @@ class RenewableCertTests(BaseRenewableCertTest):
|
|||
with open(temp2, "r") as f:
|
||||
content = f.read()
|
||||
# useful value was updated
|
||||
assert "useful = new_value" in content
|
||||
self.assertTrue("useful = new_value" in content)
|
||||
# associated comment was preserved
|
||||
assert "A useful value" in content
|
||||
self.assertTrue("A useful value" in content)
|
||||
# useless value was deleted
|
||||
assert "useless" not in content
|
||||
self.assertTrue("useless" not in content)
|
||||
# check version was stored
|
||||
self.assertTrue("version = {0}".format(certbot.__version__) in content)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main() # pragma: no cover
|
||||
|
|
|
|||
8
certbot/tests/testdata/csr-nonames.pem
vendored
Normal file
8
certbot/tests/testdata/csr-nonames.pem
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIH/MIGqAgEAMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
|
||||
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwXDANBgkqhkiG9w0BAQEF
|
||||
AANLADBIAkEArHVztFHtH92ucFJD/N/HW9AsdRsUuHUBBBDlHwNlRd3fp580rv2+
|
||||
6QWE30cWgdmJS86ObRz6lUTor4R0T+3C5QIDAQABoAAwDQYJKoZIhvcNAQELBQAD
|
||||
QQBt9XLSZ9DGfWcGGaBUTCiSY7lWBegpNlCeo8pK3ydWmKpjcza+j7lF5paph2LH
|
||||
lKWVQ8+xwYMscGWK0NApHGco
|
||||
-----END CERTIFICATE REQUEST-----
|
||||
|
|
@ -17,15 +17,13 @@ Autoupdates
|
|||
Within certain limits, TLS server software can choose what kind of
|
||||
cryptography to use when a client connects. These choices can affect
|
||||
security, compatibility, and performance in complex ways. Most of
|
||||
these options are independent of a particular certificate. The Let's
|
||||
Encrypt client tries to provide defaults that we think are most useful
|
||||
to our users.
|
||||
these options are independent of a particular certificate. Certbot
|
||||
tries to provide defaults that we think are most useful to our users.
|
||||
|
||||
As described below, the Let's Encrypt client will default to modifying
|
||||
As described below, Certbot will default to modifying
|
||||
server software's cryptographic settings to keep these up-to-date with
|
||||
what we think are appropriate defaults when new versions of the Let's
|
||||
Encrypt client are installed (for example, by an operating system package
|
||||
manager).
|
||||
what we think are appropriate defaults when new versions of the Certbot
|
||||
are installed (for example, by an operating system package manager).
|
||||
|
||||
When this feature is implemented, this document will be updated
|
||||
to describe how to disable these automatic changes.
|
||||
|
|
@ -54,7 +52,7 @@ improve, others' security. But important information that improves our
|
|||
understanding of the state of the art is published regularly.
|
||||
|
||||
When enabling TLS support in a compatible web server (which is a separate
|
||||
step from obtaining a certificate), Let's Encrypt has the ability to
|
||||
step from obtaining a certificate), Certbot has the ability to
|
||||
update that web server's TLS configuration. Again, this is *different
|
||||
from the cryptographic particulars of the certificate itself*; the
|
||||
certificate as of the initial release will be RSA-signed using one of
|
||||
|
|
@ -80,30 +78,29 @@ art. However, the Let's Encrypt certificate authority does *not*
|
|||
dictate end-users' security policy, and any site is welcome to change
|
||||
its preferences in accordance with its own policy or its administrators'
|
||||
preferences, and use different cryptographic mechanisms or parameters,
|
||||
or a different priority order, than the defaults provided by the Let's
|
||||
Encrypt client.
|
||||
or a different priority order, than the defaults provided by Certbot.
|
||||
|
||||
If you don't use the Let's Encrypt client to configure your server
|
||||
directly, because the client doesn't integrate with your server software
|
||||
or because you chose not to use this integration, then the cryptographic
|
||||
defaults haven't been modified, and the cryptography chosen by the server
|
||||
will still be whatever the default for your software was. For example,
|
||||
if you obtain a certificate using *standalone* mode and then manually
|
||||
install it in an IMAP or LDAP server, your cryptographic settings will
|
||||
not be modified by the client in any way.
|
||||
If you don't use Certbot to configure your server directly, because the
|
||||
client doesn't integrate with your server software or because you chose
|
||||
not to use this integration, then the cryptographic defaults haven't been
|
||||
modified, and the cryptography chosen by the server will still be whatever
|
||||
the default for your software was. For example, if you obtain a
|
||||
certificate using *standalone* mode and then manually install it in an IMAP
|
||||
or LDAP server, your cryptographic settings will not be modified by the
|
||||
client in any way.
|
||||
|
||||
|
||||
Sources of defaults
|
||||
-------------------
|
||||
|
||||
Initially, the Let's Encrypt client will configure users' servers to
|
||||
use the cryptographic defaults recommended by the Mozilla project.
|
||||
These settings are well-reasoned recommendations that carefully
|
||||
consider client software compatibility. They are described at
|
||||
Initially, Certbot will configure users' servers to use the cryptographic
|
||||
defaults recommended by the Mozilla project. These settings are well-reasoned
|
||||
recommendations that carefully consider client software compatibility. They
|
||||
are described at
|
||||
|
||||
https://wiki.mozilla.org/Security/Server_Side_TLS
|
||||
|
||||
and the version implemented by the Let's Encrypt client will be the
|
||||
and the version implemented by Certbot will be the
|
||||
version that was most current as of the release date of each client
|
||||
version. Mozilla offers three separate sets of cryptographic options,
|
||||
which trade off security and compatibility differently. These are
|
||||
|
|
@ -113,12 +110,12 @@ to most-backwards compatible). The client will follow the Mozilla defaults
|
|||
for the *Intermediate* configuration by default, at least with regards to
|
||||
ciphersuites and TLS versions. Mozilla's web site describes which client
|
||||
software will be compatible with each configuration. You can also use
|
||||
the Qualys SSL Labs site, which the Let's Encrypt software will suggest
|
||||
the Qualys SSL Labs site, which Certbot will suggest
|
||||
when installing a certificate, to test your server and see whether it
|
||||
will be compatible with particular software versions.
|
||||
|
||||
It will be possible to ask the Let's Encrypt client to instead apply
|
||||
(and track) Modern or Old configurations.
|
||||
It will be possible to ask Certbot to instead apply (and track) Modern
|
||||
or Old configurations.
|
||||
|
||||
The Let's Encrypt project expects to follow the Mozilla recommendations
|
||||
in the future as those recommendations are updated. (For example, some
|
||||
|
|
@ -127,15 +124,15 @@ which uses the ChaCha and Poly1305 algorithms, and which is already
|
|||
implemented by the Chrome browser. Mozilla has delayed recommending
|
||||
``0xcc13`` over compatibility and standardization concerns, but is likely
|
||||
to recommend it in the future once these concerns have been addressed. At
|
||||
that point, the Let's Encrypt client would likely follow the Mozilla
|
||||
recommendations and favor the use of this ciphersuite as well.)
|
||||
that point, Certbot would likely follow the Mozilla recommendations and favor
|
||||
the use of this ciphersuite as well.)
|
||||
|
||||
The Let's Encrypt project may deviate from the Mozilla recommendations
|
||||
in the future if good cause is shown and we believe our users'
|
||||
priorities would be well-served by doing so. In general, please address
|
||||
relevant proposals for changing priorities to the Mozilla security
|
||||
team first, before asking the Let's Encrypt project to change the
|
||||
client's priorities. The Mozilla security team is likely to have more
|
||||
team first, before asking the Certbot developers to change
|
||||
Certbot's priorities. The Mozilla security team is likely to have more
|
||||
resources and expertise to bring to bear on evaluating reasons why its
|
||||
recommendations should be updated.
|
||||
|
||||
|
|
@ -144,8 +141,8 @@ small number of alternative configurations (apart from Modern,
|
|||
Intermediate, and Old) that there's reason to believe would be widely
|
||||
used by sysadmins; this would usually be a preferable course to modifying
|
||||
an existing configuration. For example, if many sysadmins want their
|
||||
servers configured to track a different expert recommendation, Let's
|
||||
Encrypt could add an option to do so.
|
||||
servers configured to track a different expert recommendation, Certbot
|
||||
could add an option to do so.
|
||||
|
||||
|
||||
Resources for recommendations
|
||||
|
|
@ -156,9 +153,9 @@ recommendations with sources of expert guidance on ciphersuites and other
|
|||
cryptographic parameters. We're grateful to everyone who contributed
|
||||
suggestions. The recommendations we received are available at
|
||||
|
||||
https://github.com/letsencrypt/letsencrypt/wiki/Ciphersuite-guidance
|
||||
https://github.com/certbot/certbot/wiki/Ciphersuite-guidance
|
||||
|
||||
Let's Encrypt client users are welcome to review these authorities to
|
||||
Certbot users are welcome to review these authorities to
|
||||
better inform their own cryptographic parameter choices. We also
|
||||
welcome suggestions of other resources to add to this list. Please keep
|
||||
in mind that different recommendations may reflect different priorities
|
||||
|
|
@ -172,26 +169,25 @@ This will probably look something like
|
|||
|
||||
.. code-block:: shell
|
||||
|
||||
letsencrypt --cipher-recommendations mozilla-secure
|
||||
letsencrypt --cipher-recommendations mozilla-intermediate
|
||||
letsencrypt --cipher-recommendations mozilla-old
|
||||
certbot --cipher-recommendations mozilla-secure
|
||||
certbot --cipher-recommendations mozilla-intermediate
|
||||
certbot --cipher-recommendations mozilla-old
|
||||
|
||||
to track Mozilla's *Secure*, *Intermediate*, or *Old* recommendations,
|
||||
and
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
letsencrypt --update-ciphers on
|
||||
certbot --update-ciphers on
|
||||
|
||||
to enable updating ciphers with each new Let's Encrypt client release,
|
||||
or
|
||||
to enable updating ciphers with each new Certbot release, or
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
letsencrypt --update-ciphers off
|
||||
certbot --update-ciphers off
|
||||
|
||||
to disable automatic configuration updates. These features have not yet
|
||||
been implemented and this syntax may change then they are implemented.
|
||||
been implemented and this syntax may change when they are implemented.
|
||||
|
||||
|
||||
TODO
|
||||
|
|
@ -200,7 +196,7 @@ TODO
|
|||
The status of this feature is tracked as part of issue #1123 in our
|
||||
bug tracker.
|
||||
|
||||
https://github.com/letsencrypt/letsencrypt/issues/1123
|
||||
https://github.com/certbot/certbot/issues/1123
|
||||
|
||||
Prior to implementation of #1123, the client does not actually modify
|
||||
ciphersuites (this is intended to be implemented as a "configuration
|
||||
|
|
|
|||
340
docs/cli-help.txt
Normal file
340
docs/cli-help.txt
Normal file
|
|
@ -0,0 +1,340 @@
|
|||
usage:
|
||||
certbot [SUBCOMMAND] [options] [-d domain] [-d domain] ...
|
||||
|
||||
Certbot can obtain and install HTTPS/TLS/SSL certificates. By default,
|
||||
it will attempt to use a webserver both for obtaining and installing the
|
||||
cert. Major SUBCOMMANDS are:
|
||||
|
||||
(default) run Obtain & install a cert in your current webserver
|
||||
certonly Obtain cert, but do not install it (aka "auth")
|
||||
install Install a previously obtained cert in a server
|
||||
renew Renew previously obtained certs that are near expiry
|
||||
revoke Revoke a previously obtained certificate
|
||||
rollback Rollback server configuration changes made during install
|
||||
config_changes Show changes made to server config during installation
|
||||
plugins Display information about installed plugins
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
-c CONFIG_FILE, --config CONFIG_FILE
|
||||
config file path (default: None)
|
||||
-v, --verbose This flag can be used multiple times to incrementally
|
||||
increase the verbosity of output, e.g. -vvv. (default:
|
||||
-3)
|
||||
-t, --text Use the text output instead of the curses UI.
|
||||
(default: False)
|
||||
-n, --non-interactive, --noninteractive
|
||||
Run without ever asking for user input. This may
|
||||
require additional command line flags; the client will
|
||||
try to explain which ones are required if it finds one
|
||||
missing (default: False)
|
||||
--dry-run Perform a test run of the client, obtaining test
|
||||
(invalid) certs but not saving them to disk. This can
|
||||
currently only be used with the 'certonly' and 'renew'
|
||||
subcommands. Note: Although --dry-run tries to avoid
|
||||
making any persistent changes on a system, it is not
|
||||
completely side-effect free: if used with webserver
|
||||
authenticator plugins like apache and nginx, it makes
|
||||
and then reverts temporary config changes in order to
|
||||
obtain test certs, and reloads webservers to deploy
|
||||
and then roll back those changes. It also calls --pre-
|
||||
hook and --post-hook commands if they are defined
|
||||
because they may be necessary to accurately simulate
|
||||
renewal. --renew-hook commands are not called.
|
||||
(default: False)
|
||||
--register-unsafely-without-email
|
||||
Specifying this flag enables registering an account
|
||||
with no email address. This is strongly discouraged,
|
||||
because in the event of key loss or account compromise
|
||||
you will irrevocably lose access to your account. You
|
||||
will also be unable to receive notice about impending
|
||||
expiration or revocation of your certificates. Updates
|
||||
to the Subscriber Agreement will still affect you, and
|
||||
will be effective 14 days after posting an update to
|
||||
the web site. (default: False)
|
||||
-m EMAIL, --email EMAIL
|
||||
Email used for registration and recovery contact.
|
||||
(default: None)
|
||||
-d DOMAIN, --domains DOMAIN, --domain DOMAIN
|
||||
Domain names to apply. For multiple domains you can
|
||||
use multiple -d flags or enter a comma separated list
|
||||
of domains as a parameter. (default: [])
|
||||
--user-agent USER_AGENT
|
||||
Set a custom user agent string for the client. User
|
||||
agent strings allow the CA to collect high level
|
||||
statistics about success rates by OS and plugin. If
|
||||
you wish to hide your server OS version from the Let's
|
||||
Encrypt server, set this to "". (default: None)
|
||||
|
||||
automation:
|
||||
Arguments for automating execution & other tweaks
|
||||
|
||||
--keep-until-expiring, --keep, --reinstall
|
||||
If the requested cert matches an existing cert, always
|
||||
keep the existing one until it is due for renewal (for
|
||||
the 'run' subcommand this means reinstall the existing
|
||||
cert) (default: False)
|
||||
--expand If an existing cert covers some subset of the
|
||||
requested names, always expand and replace it with the
|
||||
additional names. (default: False)
|
||||
--version show program's version number and exit
|
||||
--force-renewal, --renew-by-default
|
||||
If a certificate already exists for the requested
|
||||
domains, renew it now, regardless of whether it is
|
||||
near expiry. (Often --keep-until-expiring is more
|
||||
appropriate). Also implies --expand. (default: False)
|
||||
--allow-subset-of-names
|
||||
When performing domain validation, do not consider it
|
||||
a failure if authorizations can not be obtained for a
|
||||
strict subset of the requested domains. This may be
|
||||
useful for allowing renewals for multiple domains to
|
||||
succeed even if some domains no longer point at this
|
||||
system. This option cannot be used with --csr.
|
||||
(default: False)
|
||||
--agree-tos Agree to the ACME Subscriber Agreement (default:
|
||||
False)
|
||||
--account ACCOUNT_ID Account ID to use (default: None)
|
||||
--duplicate Allow making a certificate lineage that duplicates an
|
||||
existing one (both can be renewed in parallel)
|
||||
(default: False)
|
||||
--os-packages-only (letsencrypt-auto only) install OS package
|
||||
dependencies and then stop (default: False)
|
||||
--no-self-upgrade (letsencrypt-auto only) prevent the letsencrypt-auto
|
||||
script from upgrading itself to newer released
|
||||
versions (default: False)
|
||||
-q, --quiet Silence all output except errors. Useful for
|
||||
automation via cron. Implies --non-interactive.
|
||||
(default: False)
|
||||
|
||||
testing:
|
||||
The following flags are meant for testing purposes only! Do NOT change
|
||||
them, unless you really know what you're doing!
|
||||
|
||||
--debug Show tracebacks in case of errors, and allow
|
||||
letsencrypt-auto execution on experimental platforms
|
||||
(default: False)
|
||||
--no-verify-ssl Disable SSL certificate verification. (default: False)
|
||||
--tls-sni-01-port TLS_SNI_01_PORT
|
||||
Port number to perform tls-sni-01 challenge. Boulder
|
||||
in testing mode defaults to 5001. (default: 443)
|
||||
--http-01-port HTTP01_PORT
|
||||
Port used in the SimpleHttp challenge. (default: 80)
|
||||
--break-my-certs Be willing to replace or renew valid certs with
|
||||
invalid (testing/staging) certs (default: False)
|
||||
--test-cert, --staging
|
||||
Use the staging server to obtain test (invalid) certs;
|
||||
equivalent to --server https://acme-
|
||||
staging.api.letsencrypt.org/directory (default: False)
|
||||
|
||||
security:
|
||||
Security parameters & server settings
|
||||
|
||||
--rsa-key-size N Size of the RSA key. (default: 2048)
|
||||
--redirect Automatically redirect all HTTP traffic to HTTPS for
|
||||
the newly authenticated vhost. (default: None)
|
||||
--no-redirect Do not automatically redirect all HTTP traffic to
|
||||
HTTPS for the newly authenticated vhost. (default:
|
||||
None)
|
||||
--hsts Add the Strict-Transport-Security header to every HTTP
|
||||
response. Forcing browser to use always use SSL for
|
||||
the domain. Defends against SSL Stripping. (default:
|
||||
False)
|
||||
--no-hsts Do not automatically add the Strict-Transport-Security
|
||||
header to every HTTP response. (default: False)
|
||||
--uir Add the "Content-Security-Policy: upgrade-insecure-
|
||||
requests" header to every HTTP response. Forcing the
|
||||
browser to use https:// for every http:// resource.
|
||||
(default: None)
|
||||
--no-uir Do not automatically set the "Content-Security-Policy:
|
||||
upgrade-insecure-requests" header to every HTTP
|
||||
response. (default: None)
|
||||
--strict-permissions Require that all configuration files are owned by the
|
||||
current user; only needed if your config is somewhere
|
||||
unsafe like /tmp/ (default: False)
|
||||
|
||||
renew:
|
||||
The 'renew' subcommand will attempt to renew all certificates (or more
|
||||
precisely, certificate lineages) you have previously obtained if they are
|
||||
close to expiry, and print a summary of the results. By default, 'renew'
|
||||
will reuse the options used to create obtain or most recently successfully
|
||||
renew each certificate lineage. You can try it with `--dry-run` first. For
|
||||
more fine-grained control, you can renew individual lineages with the
|
||||
`certonly` subcommand. Hooks are available to run commands before and
|
||||
after renewal; see https://certbot.eff.org/docs/using.html#renewal for
|
||||
more information on these.
|
||||
|
||||
--pre-hook PRE_HOOK Command to be run in a shell before obtaining any
|
||||
certificates. Intended primarily for renewal, where it
|
||||
can be used to temporarily shut down a webserver that
|
||||
might conflict with the standalone plugin. This will
|
||||
only be called if a certificate is actually to be
|
||||
obtained/renewed. (default: None)
|
||||
--post-hook POST_HOOK
|
||||
Command to be run in a shell after attempting to
|
||||
obtain/renew certificates. Can be used to deploy
|
||||
renewed certificates, or to restart any servers that
|
||||
were stopped by --pre-hook. (default: None)
|
||||
--renew-hook RENEW_HOOK
|
||||
Command to be run in a shell once for each
|
||||
successfully renewed certificate.For this command, the
|
||||
shell variable $RENEWED_LINEAGE will point to
|
||||
theconfig live subdirectory containing the new certs
|
||||
and keys; the shell variable $RENEWED_DOMAINS will
|
||||
contain a space-delimited list of renewed cert domains
|
||||
(default: None)
|
||||
|
||||
certonly:
|
||||
Options for modifying how a cert is obtained
|
||||
|
||||
--csr CSR Path to a Certificate Signing Request (CSR) in DER
|
||||
format; note that the .csr file *must* contain a
|
||||
Subject Alternative Name field for each domain you
|
||||
want certified. Currently --csr only works with the
|
||||
'certonly' subcommand' (default: None)
|
||||
|
||||
install:
|
||||
Options for modifying how a cert is deployed
|
||||
|
||||
revoke:
|
||||
Options for revocation of certs
|
||||
|
||||
rollback:
|
||||
Options for reverting config changes
|
||||
|
||||
--checkpoints N Revert configuration N number of checkpoints.
|
||||
(default: 1)
|
||||
|
||||
plugins:
|
||||
Plugin options
|
||||
|
||||
--init Initialize plugins. (default: False)
|
||||
--prepare Initialize and prepare plugins. (default: False)
|
||||
--authenticators Limit to authenticator plugins only. (default: None)
|
||||
--installers Limit to installer plugins only. (default: None)
|
||||
|
||||
config_changes:
|
||||
Options for showing a history of config changes
|
||||
|
||||
--num NUM How many past revisions you want to be displayed
|
||||
(default: None)
|
||||
|
||||
paths:
|
||||
Arguments changing execution paths & servers
|
||||
|
||||
--cert-path CERT_PATH
|
||||
Path to where cert is saved (with auth --csr),
|
||||
installed from or revoked. (default: None)
|
||||
--key-path KEY_PATH Path to private key for cert installation or
|
||||
revocation (if account key is missing) (default: None)
|
||||
--fullchain-path FULLCHAIN_PATH
|
||||
Accompanying path to a full certificate chain (cert
|
||||
plus chain). (default: None)
|
||||
--chain-path CHAIN_PATH
|
||||
Accompanying path to a certificate chain. (default:
|
||||
None)
|
||||
--config-dir CONFIG_DIR
|
||||
Configuration directory. (default: /etc/letsencrypt)
|
||||
--work-dir WORK_DIR Working directory. (default: /var/lib/letsencrypt)
|
||||
--logs-dir LOGS_DIR Logs directory. (default: /var/log/letsencrypt)
|
||||
--server SERVER ACME Directory Resource URI. (default:
|
||||
https://acme-v01.api.letsencrypt.org/directory)
|
||||
|
||||
plugins:
|
||||
Certbot client supports an extensible plugins architecture. See 'certbot
|
||||
plugins' for a list of all installed plugins and their names. You can
|
||||
force a particular plugin by setting options provided below. Running
|
||||
--help <plugin_name> will list flags specific to that plugin.
|
||||
|
||||
-a AUTHENTICATOR, --authenticator AUTHENTICATOR
|
||||
Authenticator plugin name. (default: None)
|
||||
-i INSTALLER, --installer INSTALLER
|
||||
Installer plugin name (also used to find domains).
|
||||
(default: None)
|
||||
--configurator CONFIGURATOR
|
||||
Name of the plugin that is both an authenticator and
|
||||
an installer. Should not be used together with
|
||||
--authenticator or --installer. (default: None)
|
||||
--apache Obtain and install certs using Apache (default: False)
|
||||
--nginx Obtain and install certs using Nginx (default: False)
|
||||
--standalone Obtain certs using a "standalone" webserver. (default:
|
||||
False)
|
||||
--manual Provide laborious manual instructions for obtaining a
|
||||
cert (default: False)
|
||||
--webroot Obtain certs by placing files in a webroot directory.
|
||||
(default: False)
|
||||
|
||||
nginx:
|
||||
Nginx Web Server - currently doesn't work
|
||||
|
||||
--nginx-server-root NGINX_SERVER_ROOT
|
||||
Nginx server root directory. (default: /etc/nginx)
|
||||
--nginx-ctl NGINX_CTL
|
||||
Path to the 'nginx' binary, used for 'configtest' and
|
||||
retrieving nginx version number. (default: nginx)
|
||||
|
||||
standalone:
|
||||
Automatically use a temporary webserver
|
||||
|
||||
--standalone-supported-challenges STANDALONE_SUPPORTED_CHALLENGES
|
||||
Supported challenges. Preferred in the order they are
|
||||
listed. (default: tls-sni-01,http-01)
|
||||
|
||||
manual:
|
||||
Manually configure an HTTP server
|
||||
|
||||
--manual-test-mode Test mode. Executes the manual command in subprocess.
|
||||
(default: False)
|
||||
--manual-public-ip-logging-ok
|
||||
Automatically allows public IP logging. (default:
|
||||
False)
|
||||
|
||||
webroot:
|
||||
Place files in webroot directory
|
||||
|
||||
--webroot-path WEBROOT_PATH, -w WEBROOT_PATH
|
||||
public_html / webroot path. This can be specified
|
||||
multiple times to handle different domains; each
|
||||
domain will have the webroot path that preceded it.
|
||||
For instance: `-w /var/www/example -d example.com -d
|
||||
www.example.com -w /var/www/thing -d thing.net -d
|
||||
m.thing.net` (default: [])
|
||||
--webroot-map WEBROOT_MAP
|
||||
JSON dictionary mapping domains to webroot paths; this
|
||||
implies -d for each entry. You may need to escape this
|
||||
from your shell. E.g.: --webroot-map
|
||||
'{"eg1.is,m.eg1.is":"/www/eg1/", "eg2.is":"/www/eg2"}'
|
||||
This option is merged with, but takes precedence over,
|
||||
-w / -d entries. At present, if you put webroot-map in
|
||||
a config file, it needs to be on a single line, like:
|
||||
webroot-map = {"example.com":"/var/www"}. (default:
|
||||
{})
|
||||
|
||||
apache:
|
||||
Apache Web Server - Alpha
|
||||
|
||||
--apache-enmod APACHE_ENMOD
|
||||
Path to the Apache 'a2enmod' binary. (default:
|
||||
a2enmod)
|
||||
--apache-dismod APACHE_DISMOD
|
||||
Path to the Apache 'a2dismod' binary. (default:
|
||||
a2dismod)
|
||||
--apache-le-vhost-ext APACHE_LE_VHOST_EXT
|
||||
SSL vhost configuration extension. (default: -le-
|
||||
ssl.conf)
|
||||
--apache-server-root APACHE_SERVER_ROOT
|
||||
Apache server root directory. (default: /etc/apache2)
|
||||
--apache-vhost-root APACHE_VHOST_ROOT
|
||||
Apache server VirtualHost configuration root (default:
|
||||
/etc/apache2/sites-available)
|
||||
--apache-challenge-location APACHE_CHALLENGE_LOCATION
|
||||
Directory path for challenge configuration. (default:
|
||||
/etc/apache2)
|
||||
--apache-handle-modules APACHE_HANDLE_MODULES
|
||||
Let installer handle enabling required modules for
|
||||
you.(Only Ubuntu/Debian currently) (default: True)
|
||||
--apache-handle-sites APACHE_HANDLE_SITES
|
||||
Let installer handle enabling sites for you.(Only
|
||||
Ubuntu/Debian currently) (default: True)
|
||||
|
||||
null:
|
||||
Null Installer
|
||||
16
docs/conf.py
16
docs/conf.py
|
|
@ -64,8 +64,8 @@ source_suffix = '.rst'
|
|||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'Let\'s Encrypt'
|
||||
copyright = u'2014-2015, Let\'s Encrypt Project'
|
||||
project = u'Certbot'
|
||||
copyright = u'2014-2016 - The Certbot software and documentation are licensed under the Apache 2.0 license as described at https://eff.org/cb-license '
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
|
|
@ -225,7 +225,7 @@ html_static_path = ['_static']
|
|||
#html_search_scorer = 'scorer.js'
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'LetsEncryptdoc'
|
||||
htmlhelp_basename = 'Certbotdoc'
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
|
|
@ -247,8 +247,8 @@ latex_elements = {
|
|||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
('index', 'LetsEncrypt.tex', u'Let\'s Encrypt Documentation',
|
||||
u'Let\'s Encrypt Project', 'manual'),
|
||||
('index', 'Certbot.tex', u'Certbot Documentation',
|
||||
u'Certbot Project', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
|
|
@ -277,7 +277,7 @@ latex_documents = [
|
|||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'certbot', u'Let\'s Encrypt Documentation',
|
||||
('index', 'certbot', u'Certbot Documentation',
|
||||
[project], 7),
|
||||
('man/certbot', 'certbot', u'certbot script documentation',
|
||||
[project], 1),
|
||||
|
|
@ -293,8 +293,8 @@ man_pages = [
|
|||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
('index', 'LetsEncrypt', u'Let\'s Encrypt Documentation',
|
||||
u'Let\'s Encrypt Project', 'LetsEncrypt', 'One line description of project.',
|
||||
('index', 'Certbot', u'Certbot Documentation',
|
||||
u'Certbot Project', 'Certbot', 'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ once:
|
|||
|
||||
.. code-block:: shell
|
||||
|
||||
git clone https://github.com/letsencrypt/letsencrypt
|
||||
cd letsencrypt
|
||||
git clone https://github.com/certbot/certbot
|
||||
cd certbot
|
||||
./letsencrypt-auto-source/letsencrypt-auto --os-packages-only
|
||||
./tools/venv.sh
|
||||
|
||||
|
|
@ -57,8 +57,8 @@ your pull request must have thorough unit test coverage, pass our
|
|||
`integration`_ tests, and be compliant with the :ref:`coding style
|
||||
<coding-style>`.
|
||||
|
||||
.. _github issue tracker: https://github.com/letsencrypt/letsencrypt/issues
|
||||
.. _Good Volunteer Task: https://github.com/letsencrypt/letsencrypt/issues?q=is%3Aopen+is%3Aissue+label%3A%22Good+Volunteer+Task%22
|
||||
.. _github issue tracker: https://github.com/certbot/certbot/issues
|
||||
.. _Good Volunteer Task: https://github.com/certbot/certbot/issues?q=is%3Aopen+is%3Aissue+label%3A%22Good+Volunteer+Task%22
|
||||
|
||||
Testing
|
||||
-------
|
||||
|
|
@ -175,8 +175,8 @@ Configurators may implement just one of those).
|
|||
There are also `~certbot.interfaces.IDisplay` plugins,
|
||||
which implement bindings to alternative UI libraries.
|
||||
|
||||
.. _interfaces.py: https://github.com/letsencrypt/letsencrypt/blob/master/certbot/interfaces.py
|
||||
.. _plugins/common.py: https://github.com/letsencrypt/letsencrypt/blob/master/certbot/plugins/common.py#L34
|
||||
.. _interfaces.py: https://github.com/certbot/certbot/blob/master/certbot/interfaces.py
|
||||
.. _plugins/common.py: https://github.com/certbot/certbot/blob/master/certbot/plugins/common.py#L34
|
||||
|
||||
|
||||
Authenticators
|
||||
|
|
@ -297,7 +297,7 @@ Please:
|
|||
4. Remember to use ``pylint``.
|
||||
|
||||
.. _Google Python Style Guide:
|
||||
https://google-styleguide.googlecode.com/svn/trunk/pyguide.html
|
||||
https://google.github.io/styleguide/pyguide.html
|
||||
.. _Sphinx-style: http://sphinx-doc.org/
|
||||
.. _PEP 8 - Style Guide for Python Code:
|
||||
https://www.python.org/dev/peps/pep-0008
|
||||
|
|
@ -323,7 +323,7 @@ Steps:
|
|||
See `Known Issues`_. If it's not a known issue, fix any errors.
|
||||
|
||||
.. _Known Issues:
|
||||
https://github.com/letsencrypt/letsencrypt/wiki/Known-issues
|
||||
https://github.com/certbot/certbot/wiki/Known-issues
|
||||
|
||||
Updating the documentation
|
||||
==========================
|
||||
|
|
@ -333,7 +333,7 @@ commands:
|
|||
|
||||
.. code-block:: shell
|
||||
|
||||
make -C docs clean html
|
||||
make -C docs clean html man
|
||||
|
||||
This should generate documentation in the ``docs/_build/html``
|
||||
directory.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
Welcome to the Let's Encrypt client documentation!
|
||||
Welcome to the Certbot documentation!
|
||||
==================================================
|
||||
|
||||
.. toctree::
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
============
|
||||
Introduction
|
||||
============
|
||||
=====================
|
||||
README / Introduction
|
||||
=====================
|
||||
|
||||
.. include:: ../README.rst
|
||||
.. include:: ../CHANGES.rst
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
.. program-output:: certbot --help all
|
||||
.. literalinclude:: cli-help.txt
|
||||
|
|
|
|||
|
|
@ -3,4 +3,4 @@ Packaging Guide
|
|||
===============
|
||||
|
||||
Documentation can be found at
|
||||
https://github.com/letsencrypt/letsencrypt/wiki/Packaging.
|
||||
https://github.com/certbot/certbot/wiki/Packaging.
|
||||
|
|
|
|||
264
docs/using.rst
264
docs/using.rst
|
|
@ -5,66 +5,38 @@ User Guide
|
|||
.. contents:: Table of Contents
|
||||
:local:
|
||||
|
||||
.. _installation:
|
||||
Getting Certbot
|
||||
===============
|
||||
|
||||
Installation
|
||||
============
|
||||
To get specific instructions for installing Certbot on your OS, we recommend
|
||||
visiting certbot.eff.org_. If you're offline, you can find some general
|
||||
instructions `in the README / Introduction <intro.html#installation>`__
|
||||
|
||||
.. _letsencrypt-auto:
|
||||
__ installation_
|
||||
.. _certbot.eff.org: https://certbot.eff.org
|
||||
|
||||
letsencrypt-auto
|
||||
----------------
|
||||
.. _certbot-auto:
|
||||
|
||||
``letsencrypt-auto`` is a wrapper which installs some dependencies
|
||||
from your OS standard package repositories (e.g. using `apt-get` or
|
||||
`yum`), and for other dependencies it sets up a virtualized Python
|
||||
environment with packages downloaded from PyPI [#venv]_. It also
|
||||
provides automated updates.
|
||||
The name of the certbot command
|
||||
-------------------------------
|
||||
|
||||
To install and run the client, just type...
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
./letsencrypt-auto
|
||||
|
||||
.. hint:: During the beta phase, Let's Encrypt enforces strict rate limits on
|
||||
the number of certificates issued for one domain. It is recommended to
|
||||
initially use the test server via `--test-cert` until you get the desired
|
||||
certificates.
|
||||
|
||||
Throughout the documentation, whenever you see references to
|
||||
``letsencrypt`` script/binary, you can substitute in
|
||||
``letsencrypt-auto``. For example, to get basic help you would type:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
./letsencrypt-auto --help
|
||||
|
||||
or for full help, type:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
./letsencrypt-auto --help all
|
||||
|
||||
|
||||
``letsencrypt-auto`` is the recommended method of running the Let's Encrypt
|
||||
client beta releases on systems that don't have a packaged version. Debian,
|
||||
Arch Linux, Gentoo, FreeBSD, and OpenBSD now have native packages, so on those
|
||||
systems you can just install ``letsencrypt`` (and perhaps
|
||||
``letsencrypt-apache``). If you'd like to run the latest copy from Git, or
|
||||
run your own locally modified copy of the client, follow the instructions in
|
||||
the :doc:`contributing`. Some `other methods of installation`_ are discussed
|
||||
below.
|
||||
Many platforms now have native packages that give you a ``certbot`` or (for
|
||||
older packages) ``letsencrypt`` command you can run. On others, the
|
||||
``certbot-auto`` / ``letsencrypt-auto`` installer and wrapper script is a
|
||||
stand-in. Throughout the documentation, whenever you see references to
|
||||
``certbot`` script/binary, you should substitute in the name of the command
|
||||
that certbot.eff.org_ told you to use on your system (``certbot``,
|
||||
``letsencrypt``, or ``certbot-auto``).
|
||||
|
||||
|
||||
Plugins
|
||||
=======
|
||||
|
||||
The Let's Encrypt client supports a number of different "plugins" that can be
|
||||
The Certbot client supports a number of different "plugins" that can be
|
||||
used to obtain and/or install certificates. Plugins that can obtain a cert
|
||||
are called "authenticators" and can be used with the "certonly" command.
|
||||
Plugins that can install a cert are called "installers". Plugins that do both
|
||||
can be used with the "letsencrypt run" command, which is the default.
|
||||
can be used with the "certbot run" command, which is the default.
|
||||
|
||||
=========== ==== ==== ===============================================================
|
||||
Plugin Auth Inst Notes
|
||||
|
|
@ -79,27 +51,10 @@ standalone_ Y N Uses a "standalone" webserver to obtain a cert. Requires
|
|||
webserver is not supported or not desired.
|
||||
manual_ Y N Helps you obtain a cert by giving you instructions to perform
|
||||
domain validation yourself.
|
||||
nginx_ Y Y Very experimental and not included in letsencrypt-auto_.
|
||||
nginx_ Y Y Very experimental and not included in certbot-auto_.
|
||||
=========== ==== ==== ===============================================================
|
||||
|
||||
There are also a number of third-party plugins for the client, provided by other developers:
|
||||
|
||||
=========== ==== ==== ===============================================================
|
||||
Plugin Auth Inst Notes
|
||||
=========== ==== ==== ===============================================================
|
||||
plesk_ Y Y Integration with the Plesk web hosting tool
|
||||
haproxy_ Y Y Integration with the HAProxy load balancer
|
||||
s3front_ Y Y Integration with Amazon CloudFront distribution of S3 buckets
|
||||
gandi_ Y Y Integration with Gandi's hosting products and API
|
||||
=========== ==== ==== ===============================================================
|
||||
|
||||
.. _plesk: https://github.com/plesk/letsencrypt-plesk
|
||||
.. _haproxy: https://code.greenhost.net/open/letsencrypt-haproxy
|
||||
.. _s3front: https://github.com/dlapiduz/letsencrypt-s3front
|
||||
.. _gandi: https://github.com/Gandi/letsencrypt-gandi
|
||||
|
||||
Future plugins for IMAP servers, SMTP servers, IRC servers, etc, are likely to
|
||||
be installers but not authenticators.
|
||||
There are many third-party-plugins_ available.
|
||||
|
||||
Apache
|
||||
------
|
||||
|
|
@ -124,22 +79,22 @@ or ``--webroot-path /usr/share/nginx/html`` are two common webroot paths.
|
|||
|
||||
If you're getting a certificate for many domains at once, the plugin
|
||||
needs to know where each domain's files are served from, which could
|
||||
potentially be a separate directory for each domain. When requested a
|
||||
potentially be a separate directory for each domain. When requesting a
|
||||
certificate for multiple domains, each domain will use the most recently
|
||||
specified ``--webroot-path``. So, for instance,
|
||||
|
||||
::
|
||||
|
||||
letsencrypt certonly --webroot -w /var/www/example/ -d www.example.com -d example.com -w /var/www/other -d other.example.net -d another.other.example.net
|
||||
certbot certonly --webroot -w /var/www/example/ -d www.example.com -d example.com -w /var/www/other -d other.example.net -d another.other.example.net
|
||||
|
||||
would obtain a single certificate for all of those names, using the
|
||||
``/var/www/example`` webroot directory for the first two, and
|
||||
``/var/www/other`` for the second two.
|
||||
|
||||
The webroot plugin works by creating a temporary file for each of your requested
|
||||
domains in ``${webroot-path}/.well-known/acme-challenge``. Then the Let's
|
||||
Encrypt validation server makes HTTP requests to validate that the DNS for each
|
||||
requested domain resolves to the server running letsencrypt. An example request
|
||||
domains in ``${webroot-path}/.well-known/acme-challenge``. Then the Let's Encrypt
|
||||
validation server makes HTTP requests to validate that the DNS for each
|
||||
requested domain resolves to the server running certbot. An example request
|
||||
made to your web server would look like:
|
||||
|
||||
::
|
||||
|
|
@ -173,7 +128,7 @@ specified port using each requested domain name.
|
|||
Manual
|
||||
------
|
||||
|
||||
If you'd like to obtain a cert running ``letsencrypt`` on a machine
|
||||
If you'd like to obtain a cert running ``certbot`` on a machine
|
||||
other than your target webserver or perform the steps for domain
|
||||
validation yourself, you can use the manual plugin. While hidden from
|
||||
the UI, you can use the plugin to obtain a cert by specifying
|
||||
|
|
@ -184,18 +139,51 @@ be on a different computer.
|
|||
Nginx
|
||||
-----
|
||||
|
||||
In the future, if you're running Nginx you can use this plugin to
|
||||
automatically obtain and install your certificate. The Nginx plugin
|
||||
is still experimental, however, and is not installed with
|
||||
letsencrypt-auto_. If installed, you can select this plugin on the
|
||||
command line by including ``--nginx``.
|
||||
In the future, if you're running Nginx you will hopefully be able to use this
|
||||
plugin to automatically obtain and install your certificate. The Nginx plugin is
|
||||
still experimental, however, and is not installed with certbot-auto_. If
|
||||
installed, you can select this plugin on the command line by including
|
||||
``--nginx``.
|
||||
|
||||
.. _third-party-plugins:
|
||||
|
||||
Third-party plugins
|
||||
-------------------
|
||||
|
||||
These plugins are listed at
|
||||
https://github.com/letsencrypt/letsencrypt/wiki/Plugins. If you're
|
||||
interested, you can also :ref:`write your own plugin <dev-plugin>`.
|
||||
There are also a number of third-party plugins for the client, provided by
|
||||
other developers. Many are beta/experimental, but some are already in
|
||||
widespread use:
|
||||
|
||||
=========== ==== ==== ===============================================================
|
||||
Plugin Auth Inst Notes
|
||||
=========== ==== ==== ===============================================================
|
||||
plesk_ Y Y Integration with the Plesk web hosting tool
|
||||
haproxy_ Y Y Integration with the HAProxy load balancer
|
||||
s3front_ Y Y Integration with Amazon CloudFront distribution of S3 buckets
|
||||
gandi_ Y Y Integration with Gandi's hosting products and API
|
||||
varnish_ Y N Obtain certs via a Varnish server
|
||||
external_ Y N A plugin for convenient scripting (See also ticket 2782_)
|
||||
icecast_ N Y Deploy certs to Icecast 2 streaming media servers
|
||||
pritunl_ N Y Install certs in pritunl distributed OpenVPN servers
|
||||
proxmox_ N Y Install certs in Proxmox Virtualization servers
|
||||
postfix_ N Y STARTTLS Everywhere is becoming a Certbot Postfix/Exim plugin
|
||||
=========== ==== ==== ===============================================================
|
||||
|
||||
.. _plesk: https://github.com/plesk/letsencrypt-plesk
|
||||
.. _haproxy: https://code.greenhost.net/open/letsencrypt-haproxy
|
||||
.. _s3front: https://github.com/dlapiduz/letsencrypt-s3front
|
||||
.. _gandi: https://github.com/Gandi/letsencrypt-gandi
|
||||
.. _icecast: https://github.com/e00E/lets-encrypt-icecast
|
||||
.. _varnish: http://git.sesse.net/?p=letsencrypt-varnish-plugin
|
||||
.. _2782: https://github.com/certbot/certbot/issues/2782
|
||||
.. _pritunl: https://github.com/kharkevich/letsencrypt-pritunl
|
||||
.. _proxmox: https://github.com/kharkevich/letsencrypt-proxmox
|
||||
.. _external: https://github.com/marcan/letsencrypt-external
|
||||
.. _postfix: https://github.com/EFForg/starttls-everywhere
|
||||
|
||||
If you're interested, you can also :ref:`write your own plugin <dev-plugin>`.
|
||||
|
||||
|
||||
|
||||
Renewal
|
||||
=======
|
||||
|
|
@ -204,23 +192,36 @@ Renewal
|
|||
days). Make sure you renew the certificates at least once in 3
|
||||
months.
|
||||
|
||||
The ``letsencrypt`` client now supports a ``renew`` action to check
|
||||
The ``certbot`` client now supports a ``renew`` action to check
|
||||
all installed certificates for impending expiry and attempt to renew
|
||||
them. The simplest form is simply
|
||||
|
||||
``letsencrypt renew``
|
||||
``certbot renew``
|
||||
|
||||
This will attempt to renew any previously-obtained certificates that
|
||||
expire in less than 30 days. The same plugin and options that were used
|
||||
at the time the certificate was originally issued will be used for the
|
||||
renewal attempt, unless you specify other plugins or options.
|
||||
|
||||
You can also specify hooks to be run before or after a certificate is
|
||||
renewed. For example, if you want to use the standalone_ plugin to renew
|
||||
your certificates, you may want to use a command like
|
||||
|
||||
``certbot renew --standalone --pre-hook "service nginx stop" --post-hook "service nginx start"``
|
||||
|
||||
This will stop Nginx so standalone can bind to the necessary ports and
|
||||
then restart Nginx after the plugin is finished. The hooks will only be
|
||||
run if a certificate is due for renewal, so you can run this command
|
||||
frequently without unnecessarily stopping your webserver. More
|
||||
information about renewal hooks can be found by running
|
||||
``certbot --help renew``.
|
||||
|
||||
If you're sure that this command executes successfully without human
|
||||
intervention, you can add the command to ``crontab`` (since certificates
|
||||
are only renewed when they're determined to be near expiry, the command
|
||||
can run on a regular basis, like every week or every day); note that
|
||||
the current version provides detailed output describing either renewal
|
||||
success or failure.
|
||||
can run on a regular basis, like every week or every day). In that case,
|
||||
you are likely to want to use the ``-q`` or ``--quiet`` quiet flag to
|
||||
silence all output except errors.
|
||||
|
||||
The ``--force-renew`` flag may be helpful for automating renewal;
|
||||
it causes the expiration time of the certificate(s) to be ignored when
|
||||
|
|
@ -229,9 +230,9 @@ certificate regardless of its age. (This form is not appropriate to run
|
|||
daily because each certificate will be renewed every day, which will
|
||||
quickly run into the certificate authority rate limit.)
|
||||
|
||||
Note that options provided to ``letsencrypt renew`` will apply to
|
||||
Note that options provided to ``certbot renew`` will apply to
|
||||
*every* certificate for which renewal is attempted; for example,
|
||||
``letsencrypt renew --rsa-key-size 4096`` would try to replace every
|
||||
``certbot renew --rsa-key-size 4096`` would try to replace every
|
||||
near-expiry certificate with an equivalent certificate using a 4096-bit
|
||||
RSA public key. If a certificate is successfully renewed using
|
||||
specified options, those options will be saved and used for future
|
||||
|
|
@ -240,10 +241,12 @@ renewals of that certificate.
|
|||
|
||||
An alternative form that provides for more fine-grained control over the
|
||||
renewal process (while renewing specified certificates one at a time),
|
||||
is ``letsencrypt certonly`` with the complete set of subject domains of
|
||||
a specific certificate specified via `-d` flags, like
|
||||
is ``certbot certonly`` with the complete set of subject domains of
|
||||
a specific certificate specified via `-d` flags. You may also want to
|
||||
include the ``-n`` or ``--noninteractive`` flag to prevent blocking on
|
||||
user input (which is useful when running the command from cron).
|
||||
|
||||
``letsencrypt certonly -d example.com -d www.example.com``
|
||||
``certbot certonly -n -d example.com -d www.example.com``
|
||||
|
||||
(All of the domains covered by the certificate must be specified in
|
||||
this case in order to renew and replace the old certificate rather
|
||||
|
|
@ -256,21 +259,25 @@ The ``certonly`` form attempts to renew one individual certificate.
|
|||
Please note that the CA will send notification emails to the address
|
||||
you provide if you do not renew certificates that are about to expire.
|
||||
|
||||
Let's Encrypt is working hard on improving the renewal process, and we
|
||||
Certbot is working hard on improving the renewal process, and we
|
||||
apologize for any inconveniences you encounter in integrating these
|
||||
commands into your individual environment.
|
||||
|
||||
.. _command-line:
|
||||
|
||||
Command line options
|
||||
====================
|
||||
|
||||
Certbot supports a lot of command line options. Here's the full list, from
|
||||
``certbot --help all``:
|
||||
|
||||
.. literalinclude:: cli-help.txt
|
||||
|
||||
.. _where-certs:
|
||||
|
||||
Where are my certificates?
|
||||
==========================
|
||||
|
||||
First of all, we encourage you to use Apache or nginx installers, both
|
||||
which perform the certificate management automatically. If, however,
|
||||
you prefer to manage everything by hand, this section provides
|
||||
information on where to find necessary files.
|
||||
|
||||
All generated keys and issued certificates can be found in
|
||||
``/etc/letsencrypt/live/$domain``. Rather than copying, please point
|
||||
your (web) server configuration directly to those files (or create
|
||||
|
|
@ -287,7 +294,7 @@ The following files are available:
|
|||
Private key for the certificate.
|
||||
|
||||
.. warning:: This **must be kept secret at all times**! Never share
|
||||
it with anyone, including Let's Encrypt developers. You cannot
|
||||
it with anyone, including Certbot developers. You cannot
|
||||
put it into a safe, however - your server still needs to access
|
||||
this file in order for SSL/TLS to work.
|
||||
|
||||
|
|
@ -330,8 +337,8 @@ will cause nasty errors served through the browsers!
|
|||
|
||||
.. note:: All files are PEM-encoded (as the filename suffix
|
||||
suggests). If you need other format, such as DER or PFX, then you
|
||||
could convert using ``openssl``, but this means you will not
|
||||
benefit from automatic renewal_!
|
||||
could convert using ``openssl``. You can automate that with
|
||||
``--renew-hook`` if you're using automatic renewal_.
|
||||
|
||||
|
||||
.. _config-file:
|
||||
|
|
@ -340,7 +347,7 @@ Configuration file
|
|||
==================
|
||||
|
||||
It is possible to specify configuration file with
|
||||
``letsencrypt-auto --config cli.ini`` (or shorter ``-c cli.ini``). An
|
||||
``certbot-auto --config cli.ini`` (or shorter ``-c cli.ini``). An
|
||||
example configuration file is shown below:
|
||||
|
||||
.. include:: ../examples/cli.ini
|
||||
|
|
@ -359,13 +366,14 @@ By default, the following locations are searched:
|
|||
Getting help
|
||||
============
|
||||
|
||||
If you're having problems you can chat with us on `IRC (#letsencrypt @
|
||||
Freenode) <https://webchat.freenode.net?channels=%23letsencrypt>`_ or
|
||||
get support on our `forums <https://community.letsencrypt.org>`_.
|
||||
If you're having problems you can chat with us on `IRC (#certbot @
|
||||
OFTC) <https://webchat.oftc.net?channels=%23certbot>`_ or at
|
||||
`IRC (#letsencrypt @ freenode) <https://webchat.freenode.net?channels=%23letsencrypt>`_
|
||||
or get support on the Let's Encrypt `forums <https://community.letsencrypt.org>`_.
|
||||
|
||||
If you find a bug in the software, please do report it in our `issue
|
||||
tracker
|
||||
<https://github.com/letsencrypt/letsencrypt/issues>`_. Remember to
|
||||
<https://github.com/certbot/certbot/issues>`_. Remember to
|
||||
give us as much information as possible:
|
||||
|
||||
- copy and paste exact command line used and the output (though mind
|
||||
|
|
@ -373,9 +381,9 @@ give us as much information as possible:
|
|||
information, including your email and domains)
|
||||
- copy and paste logs from ``/var/log/letsencrypt`` (though mind they
|
||||
also might contain personally identifiable information)
|
||||
- copy and paste ``letsencrypt --version`` output
|
||||
- copy and paste ``certbot --version`` output
|
||||
- your operating system, including specific version
|
||||
- specify which installation_ method you've chosen
|
||||
- specify which installation method you've chosen
|
||||
|
||||
Other methods of installation
|
||||
=============================
|
||||
|
|
@ -390,10 +398,10 @@ plugins cannot reach it from inside the Docker container.
|
|||
|
||||
You should definitely read the :ref:`where-certs` section, in order to
|
||||
know how to manage the certs
|
||||
manually. https://github.com/letsencrypt/letsencrypt/wiki/Ciphersuite-guidance
|
||||
manually. https://github.com/certbot/certbot/wiki/Ciphersuite-guidance
|
||||
provides some information about recommended ciphersuites. If none of
|
||||
these make much sense to you, you should definitely use the
|
||||
letsencrypt-auto_ method, which enables you to use installer plugins
|
||||
certbot-auto_ method, which enables you to use installer plugins
|
||||
that cover both of those hard topics.
|
||||
|
||||
If you're still not convinced and have decided to use this method,
|
||||
|
|
@ -402,7 +410,7 @@ to, `install Docker`_, then issue the following command:
|
|||
|
||||
.. code-block:: shell
|
||||
|
||||
sudo docker run -it --rm -p 443:443 -p 80:80 --name letsencrypt \
|
||||
sudo docker run -it --rm -p 443:443 -p 80:80 --name certbot \
|
||||
-v "/etc/letsencrypt:/etc/letsencrypt" \
|
||||
-v "/var/lib/letsencrypt:/var/lib/letsencrypt" \
|
||||
quay.io/letsencrypt/letsencrypt:latest auth
|
||||
|
|
@ -432,21 +440,27 @@ Operating System Packages
|
|||
|
||||
.. code-block:: shell
|
||||
|
||||
sudo pacman -S letsencrypt letsencrypt-apache
|
||||
sudo pacman -S letsencrypt
|
||||
|
||||
**Debian**
|
||||
|
||||
If you run Debian Stretch or Debian Sid, you can install letsencrypt packages.
|
||||
If you run Debian Stretch or Debian Sid, you can install certbot packages.
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
sudo apt-get update
|
||||
sudo apt-get install letsencrypt python-letsencrypt-apache
|
||||
sudo apt-get install certbot python-certbot-apache
|
||||
|
||||
If you don't want to use the Apache plugin, you can omit the
|
||||
``python-letsencrypt-apache`` package.
|
||||
``python-certbot-apache`` package.
|
||||
|
||||
Packages for Debian Jessie are coming in the next few weeks.
|
||||
Packages exist for Debian Jessie via backports. First you'll have to follow the
|
||||
instructions at http://backports.debian.org/Instructions/ to enable the Jessie backports
|
||||
repo, if you have not already done so. Then run:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
sudo apt-get install letsencrypt python-letsencrypt-apache -t jessie-backports
|
||||
|
||||
**Fedora**
|
||||
|
||||
|
|
@ -456,7 +470,7 @@ Packages for Debian Jessie are coming in the next few weeks.
|
|||
|
||||
**Gentoo**
|
||||
|
||||
The official Let's Encrypt client is available in Gentoo Portage. If you
|
||||
The official Certbot client is available in Gentoo Portage. If you
|
||||
want to use the Apache plugin, it has to be installed separately:
|
||||
|
||||
.. code-block:: shell
|
||||
|
|
@ -465,8 +479,12 @@ want to use the Apache plugin, it has to be installed separately:
|
|||
emerge -av app-crypt/letsencrypt-apache
|
||||
|
||||
Currently, only the Apache plugin is included in Portage. However, if you
|
||||
want the nginx plugin, you can use Layman to add the mrueg overlay which
|
||||
does include the nginx plugin package:
|
||||
Warning!
|
||||
You can use Layman to add the mrueg overlay which does include a package for the
|
||||
Certbot Nginx plugin, however, this plugin is known to be buggy and should only
|
||||
be used with caution after creating a backup up your Nginx configuration.
|
||||
We strongly recommend you use the app-crypt/letsencrypt package instead until
|
||||
the Nginx plugin is ready.
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
|
|
@ -503,7 +521,7 @@ Note: this change is not required for the other plugins.
|
|||
**Other Operating Systems**
|
||||
|
||||
OS packaging is an ongoing effort. If you'd like to package
|
||||
Let's Encrypt client for your distribution of choice please have a
|
||||
Certbot for your distribution of choice please have a
|
||||
look at the :doc:`packaging`.
|
||||
|
||||
|
||||
|
|
@ -519,19 +537,19 @@ whole process is described in the :doc:`contributing`.
|
|||
environment, e.g. ``sudo python setup.py install``, ``sudo pip
|
||||
install``, ``sudo ./venv/bin/...``. These modes of operation might
|
||||
corrupt your operating system and are **not supported** by the
|
||||
Let's Encrypt team!
|
||||
Certbot team!
|
||||
|
||||
|
||||
Comparison of different methods
|
||||
-------------------------------
|
||||
|
||||
Unless you have a very specific requirements, we kindly ask you to use
|
||||
the letsencrypt-auto_ method. It's the fastest, the most thoroughly
|
||||
Unless you have a very specific requirements, we kindly suggest that you use
|
||||
the certbot-auto_ method. It's the fastest, the most thoroughly
|
||||
tested and the most reliable way of getting our software and the free
|
||||
SSL certificates!
|
||||
TLS/SSL certificates!
|
||||
|
||||
Beyond the methods discussed here, other methods may be possible, such as
|
||||
installing Let's Encrypt directly with pip from PyPI or downloading a ZIP
|
||||
installing Certbot directly with pip from PyPI or downloading a ZIP
|
||||
archive from GitHub may be technically possible but are not presently
|
||||
recommended or supported.
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ here = os.path.abspath(os.path.dirname(__file__))
|
|||
readme = read_file(os.path.join(here, 'README.rst'))
|
||||
|
||||
|
||||
version = '0.6.0.dev0'
|
||||
version = '0.7.0.dev0'
|
||||
|
||||
|
||||
# This package is a simple shim around certbot-apache
|
||||
|
|
|
|||
269
letsencrypt-auto
269
letsencrypt-auto
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Download and run the latest release version of the Let's Encrypt client.
|
||||
# Download and run the latest release version of the Certbot client.
|
||||
#
|
||||
# NOTE: THIS SCRIPT IS AUTO-GENERATED AND SELF-UPDATING
|
||||
#
|
||||
|
|
@ -19,11 +19,36 @@ XDG_DATA_HOME=${XDG_DATA_HOME:-~/.local/share}
|
|||
VENV_NAME="letsencrypt"
|
||||
VENV_PATH=${VENV_PATH:-"$XDG_DATA_HOME/$VENV_NAME"}
|
||||
VENV_BIN="$VENV_PATH/bin"
|
||||
LE_AUTO_VERSION="0.5.0"
|
||||
LE_AUTO_VERSION="0.6.0"
|
||||
BASENAME=$(basename $0)
|
||||
USAGE="Usage: $BASENAME [OPTIONS]
|
||||
A self-updating wrapper script for the Certbot ACME client. When run, updates
|
||||
to both this script and certbot will be downloaded and installed. After
|
||||
ensuring you have the latest versions installed, certbot will be invoked with
|
||||
all arguments you have provided.
|
||||
|
||||
Help for certbot itself cannot be provided until it is installed.
|
||||
|
||||
--debug attempt experimental installation
|
||||
-h, --help print this help
|
||||
-n, --non-interactive, --noninteractive run without asking for user input
|
||||
--no-self-upgrade do not download updates
|
||||
--os-packages-only install OS dependencies and exit
|
||||
-v, --verbose provide more output
|
||||
|
||||
All arguments are accepted and forwarded to the Certbot client when run."
|
||||
|
||||
while getopts ":hnv" arg; do
|
||||
case $arg in
|
||||
h)
|
||||
HELP=1;;
|
||||
n)
|
||||
ASSUME_YES=1;;
|
||||
v)
|
||||
VERBOSE=1;;
|
||||
esac
|
||||
done
|
||||
|
||||
# This script takes the same arguments as the main letsencrypt program, but it
|
||||
# additionally responds to --verbose (more output) and --debug (allow support
|
||||
# for experimental platforms)
|
||||
for arg in "$@" ; do
|
||||
case "$arg" in
|
||||
--debug)
|
||||
|
|
@ -34,25 +59,26 @@ for arg in "$@" ; do
|
|||
# Do not upgrade this script (also prevents client upgrades, because each
|
||||
# copy of the script pins a hash of the python client)
|
||||
NO_SELF_UPGRADE=1;;
|
||||
--help)
|
||||
HELP=1;;
|
||||
--noninteractive|--non-interactive)
|
||||
ASSUME_YES=1;;
|
||||
--verbose)
|
||||
VERBOSE=1;;
|
||||
[!-]*|-*[!v]*|-)
|
||||
# Anything that isn't -v, -vv, etc.: that is, anything that does not
|
||||
# start with a -, contains anything that's not a v, or is just "-"
|
||||
;;
|
||||
*) # -v+ remains.
|
||||
VERBOSE=1;;
|
||||
esac
|
||||
done
|
||||
|
||||
# letsencrypt-auto needs root access to bootstrap OS dependencies, and
|
||||
# letsencrypt itself needs root access for almost all modes of operation
|
||||
# certbot-auto needs root access to bootstrap OS dependencies, and
|
||||
# certbot itself needs root access for almost all modes of operation
|
||||
# The "normal" case is that sudo is used for the steps that need root, but
|
||||
# this script *can* be run as root (not recommended), or fall back to using
|
||||
# `su`
|
||||
SUDO_ENV=""
|
||||
export CERTBOT_AUTO="$0"
|
||||
if test "`id -u`" -ne "0" ; then
|
||||
if command -v sudo 1>/dev/null 2>&1; then
|
||||
SUDO=sudo
|
||||
SUDO_ENV="CERTBOT_AUTO=$0"
|
||||
else
|
||||
echo \"sudo\" is not available, will use \"su\" for installation steps...
|
||||
# Because the parameters in `su -c` has to be a string,
|
||||
|
|
@ -81,6 +107,12 @@ else
|
|||
SUDO=
|
||||
fi
|
||||
|
||||
if [ $BASENAME = "letsencrypt-auto" ]; then
|
||||
# letsencrypt-auto does not respect --help or --yes for backwards compatibility
|
||||
ASSUME_YES=1
|
||||
HELP=0
|
||||
fi
|
||||
|
||||
ExperimentalBootstrap() {
|
||||
# Arguments: Platform name, bootstrap function name
|
||||
if [ "$DEBUG" = 1 ]; then
|
||||
|
|
@ -151,30 +183,45 @@ BootstrapDebCommon() {
|
|||
augeas_pkg="libaugeas0 augeas-lenses"
|
||||
AUGVERSION=`apt-cache show --no-all-versions libaugeas0 | grep ^Version: | cut -d" " -f2`
|
||||
|
||||
if [ "$ASSUME_YES" = 1 ]; then
|
||||
YES_FLAG="-y"
|
||||
fi
|
||||
|
||||
AddBackportRepo() {
|
||||
# ARGS:
|
||||
BACKPORT_NAME="$1"
|
||||
BACKPORT_SOURCELINE="$2"
|
||||
echo "To use the Apache Certbot plugin, augeas needs to be installed from $BACKPORT_NAME."
|
||||
if ! grep -v -e ' *#' /etc/apt/sources.list | grep -q "$BACKPORT_NAME" ; then
|
||||
# This can theoretically error if sources.list.d is empty, but in that case we don't care.
|
||||
if ! grep -v -e ' *#' /etc/apt/sources.list.d/* 2>/dev/null | grep -q "$BACKPORT_NAME"; then
|
||||
/bin/echo -n "Installing augeas from $BACKPORT_NAME in 3 seconds..."
|
||||
sleep 1s
|
||||
/bin/echo -ne "\e[0K\rInstalling augeas from $BACKPORT_NAME in 2 seconds..."
|
||||
sleep 1s
|
||||
/bin/echo -e "\e[0K\rInstalling augeas from $BACKPORT_NAME in 1 second ..."
|
||||
sleep 1s
|
||||
if echo $BACKPORT_NAME | grep -q wheezy ; then
|
||||
/bin/echo '(Backports are only installed if explicitly requested via "apt-get install -t wheezy-backports")'
|
||||
if [ "$ASSUME_YES" = 1 ]; then
|
||||
/bin/echo -n "Installing augeas from $BACKPORT_NAME in 3 seconds..."
|
||||
sleep 1s
|
||||
/bin/echo -ne "\e[0K\rInstalling augeas from $BACKPORT_NAME in 2 seconds..."
|
||||
sleep 1s
|
||||
/bin/echo -e "\e[0K\rInstalling augeas from $BACKPORT_NAME in 1 second ..."
|
||||
sleep 1s
|
||||
add_backports=1
|
||||
else
|
||||
read -p "Would you like to enable the $BACKPORT_NAME repository [Y/n]? " response
|
||||
case $response in
|
||||
[yY][eE][sS]|[yY]|"")
|
||||
add_backports=1;;
|
||||
*)
|
||||
add_backports=0;;
|
||||
esac
|
||||
fi
|
||||
if [ "$add_backports" = 1 ]; then
|
||||
$SUDO sh -c "echo $BACKPORT_SOURCELINE >> /etc/apt/sources.list.d/$BACKPORT_NAME.list"
|
||||
$SUDO apt-get update
|
||||
fi
|
||||
|
||||
$SUDO sh -c "echo $BACKPORT_SOURCELINE >> /etc/apt/sources.list.d/$BACKPORT_NAME.list"
|
||||
$SUDO apt-get update
|
||||
fi
|
||||
fi
|
||||
$SUDO apt-get install -y --no-install-recommends -t "$BACKPORT_NAME" $augeas_pkg
|
||||
augeas_pkg=
|
||||
|
||||
if [ "$add_backports" != 0 ]; then
|
||||
$SUDO apt-get install $YES_FLAG --no-install-recommends -t "$BACKPORT_NAME" $augeas_pkg
|
||||
augeas_pkg=
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -186,12 +233,12 @@ BootstrapDebCommon() {
|
|||
AddBackportRepo precise-backports "deb http://archive.ubuntu.com/ubuntu precise-backports main restricted universe multiverse"
|
||||
else
|
||||
echo "No libaugeas0 version is available that's new enough to run the"
|
||||
echo "Let's Encrypt apache plugin..."
|
||||
echo "Certbot apache plugin..."
|
||||
fi
|
||||
# XXX add a case for ubuntu PPAs
|
||||
fi
|
||||
|
||||
$SUDO apt-get install -y --no-install-recommends \
|
||||
$SUDO apt-get install $YES_FLAG --no-install-recommends \
|
||||
python \
|
||||
python-dev \
|
||||
$virtualenv \
|
||||
|
|
@ -212,9 +259,10 @@ BootstrapDebCommon() {
|
|||
|
||||
BootstrapRpmCommon() {
|
||||
# Tested with:
|
||||
# - Fedora 22, 23 (x64)
|
||||
# - Fedora 20, 21, 22, 23 (x64)
|
||||
# - Centos 7 (x64: on DigitalOcean droplet)
|
||||
# - CentOS 7 Minimal install in a Hyper-V VM
|
||||
# - CentOS 6 (EPEL must be installed manually)
|
||||
|
||||
if type dnf 2>/dev/null
|
||||
then
|
||||
|
|
@ -228,54 +276,62 @@ BootstrapRpmCommon() {
|
|||
exit 1
|
||||
fi
|
||||
|
||||
pkgs="
|
||||
gcc
|
||||
dialog
|
||||
augeas-libs
|
||||
openssl
|
||||
openssl-devel
|
||||
libffi-devel
|
||||
redhat-rpm-config
|
||||
ca-certificates
|
||||
"
|
||||
|
||||
# Some distros and older versions of current distros use a "python27"
|
||||
# instead of "python" naming convention. Try both conventions.
|
||||
if ! $SUDO $tool install -y \
|
||||
python \
|
||||
python-devel \
|
||||
python-virtualenv \
|
||||
python-tools \
|
||||
python-pip
|
||||
then
|
||||
if ! $SUDO $tool install -y \
|
||||
python27 \
|
||||
python27-devel \
|
||||
python27-virtualenv \
|
||||
python27-tools \
|
||||
python27-pip
|
||||
then
|
||||
echo "Could not install Python dependencies. Aborting bootstrap!"
|
||||
exit 1
|
||||
fi
|
||||
if $SUDO $tool list python >/dev/null 2>&1; then
|
||||
pkgs="$pkgs
|
||||
python
|
||||
python-devel
|
||||
python-virtualenv
|
||||
python-tools
|
||||
python-pip
|
||||
"
|
||||
else
|
||||
pkgs="$pkgs
|
||||
python27
|
||||
python27-devel
|
||||
python27-virtualenv
|
||||
python27-tools
|
||||
python27-pip
|
||||
"
|
||||
fi
|
||||
|
||||
if ! $SUDO $tool install -y \
|
||||
gcc \
|
||||
dialog \
|
||||
augeas-libs \
|
||||
openssl \
|
||||
openssl-devel \
|
||||
libffi-devel \
|
||||
redhat-rpm-config \
|
||||
ca-certificates
|
||||
then
|
||||
echo "Could not install additional dependencies. Aborting bootstrap!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
if $SUDO $tool list installed "httpd" >/dev/null 2>&1; then
|
||||
if ! $SUDO $tool install -y mod_ssl
|
||||
then
|
||||
echo "Apache found, but mod_ssl could not be installed."
|
||||
fi
|
||||
pkgs="$pkgs
|
||||
mod_ssl
|
||||
"
|
||||
fi
|
||||
|
||||
if [ "$ASSUME_YES" = 1 ]; then
|
||||
yes_flag="-y"
|
||||
fi
|
||||
|
||||
if ! $SUDO $tool install $yes_flag $pkgs; then
|
||||
echo "Could not install OS dependencies. Aborting bootstrap!"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
BootstrapSuseCommon() {
|
||||
# SLE12 don't have python-virtualenv
|
||||
|
||||
$SUDO zypper -nq in -l \
|
||||
if [ "$ASSUME_YES" = 1 ]; then
|
||||
zypper_flags="-nq"
|
||||
install_flags="-l"
|
||||
fi
|
||||
|
||||
$SUDO zypper $zypper_flags in $install_flags \
|
||||
python \
|
||||
python-devel \
|
||||
python-virtualenv \
|
||||
|
|
@ -310,8 +366,12 @@ BootstrapArchCommon() {
|
|||
# pacman -T exits with 127 if there are missing dependencies
|
||||
missing=$($SUDO pacman -T $deps) || true
|
||||
|
||||
if [ "$ASSUME_YES" = 1 ]; then
|
||||
noconfirm="--noconfirm"
|
||||
fi
|
||||
|
||||
if [ "$missing" ]; then
|
||||
$SUDO pacman -S --needed $missing
|
||||
$SUDO pacman -S --needed $missing $noconfirm
|
||||
fi
|
||||
}
|
||||
|
||||
|
|
@ -426,7 +486,7 @@ Bootstrap() {
|
|||
elif grep -iq "Amazon Linux" /etc/issue ; then
|
||||
ExperimentalBootstrap "Amazon Linux" BootstrapRpmCommon
|
||||
else
|
||||
echo "Sorry, I don't know how to bootstrap Let's Encrypt on your operating system!"
|
||||
echo "Sorry, I don't know how to bootstrap Certbot on your operating system!"
|
||||
echo
|
||||
echo "You will need to bootstrap, configure virtualenv, and run pip install manually."
|
||||
echo "Please see https://letsencrypt.readthedocs.org/en/latest/contributing.html#prerequisites"
|
||||
|
|
@ -446,7 +506,8 @@ if [ "$1" = "--le-auto-phase2" ]; then
|
|||
shift 1 # the --le-auto-phase2 arg
|
||||
if [ -f "$VENV_BIN/letsencrypt" ]; then
|
||||
# --version output ran through grep due to python-cryptography DeprecationWarnings
|
||||
INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | grep ^letsencrypt | cut -d " " -f 2)
|
||||
# grep for both certbot and letsencrypt until certbot and shim packages have been released
|
||||
INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | grep "^certbot\|^letsencrypt" | cut -d " " -f 2)
|
||||
else
|
||||
INSTALLED_VERSION="none"
|
||||
fi
|
||||
|
|
@ -465,8 +526,8 @@ if [ "$1" = "--le-auto-phase2" ]; then
|
|||
# There is no $ interpolation due to quotes on starting heredoc delimiter.
|
||||
# -------------------------------------------------------------------------
|
||||
cat << "UNLIKELY_EOF" > "$TEMP_DIR/letsencrypt-auto-requirements.txt"
|
||||
# This is the flattened list of packages letsencrypt-auto installs. To generate
|
||||
# this, do `pip install --no-cache-dir -e acme -e . -e letsencrypt-apache`, and
|
||||
# This is the flattened list of packages certbot-auto installs. To generate
|
||||
# this, do `pip install --no-cache-dir -e acme -e . -e certbot-apache`, and
|
||||
# then use `hashin` or a more secure method to gather the hashes.
|
||||
|
||||
argparse==1.4.0 \
|
||||
|
|
@ -645,15 +706,21 @@ mock==1.0.1 \
|
|||
|
||||
# THE LINES BELOW ARE EDITED BY THE RELEASE SCRIPT; ADD ALL DEPENDENCIES ABOVE.
|
||||
|
||||
acme==0.5.0 \
|
||||
--hash=sha256:ceb4127c13213f0006a564be82176b968c6b374d20d9fc78555d0658a252b275 \
|
||||
--hash=sha256:0605c63c656d33c883a05675f5db9cfb85d503f2771c885031800e0da7631abd
|
||||
letsencrypt==0.5.0 \
|
||||
--hash=sha256:f90f883e99cdbdf8142335bdbf4f74a8af143ee4b4ec60fb49c6e47418c1114c \
|
||||
--hash=sha256:e38a2b70b82be79bc195307652244a3e012ec73d897d4dbd3f80cf698496d15a
|
||||
letsencrypt-apache==0.5.0 \
|
||||
--hash=sha256:a767882164a7b09d9c12c80684a28a782135fdaf35654ef5a02c0b7b1d27ab8d \
|
||||
--hash=sha256:c20e7b9c517aa4a7d70e6bd9382da7259f00bc191b9e60d8e312e48837a00c41
|
||||
acme==0.6.0 \
|
||||
--hash=sha256:cbe4e7a340a19725a8740ed86e30abdbe18fc22c4c6022b7a8e56642d502bcc3 \
|
||||
--hash=sha256:ec4e6009dfbd629b58473eb06bbebfd9fb2a79fc8831c149e9205bc38a98ecc6
|
||||
certbot==0.6.0 \
|
||||
--hash=sha256:a893632d228864b0a751db9f3fdd93439ed34b988ea21b64fb0f0fa2ceded6a2 \
|
||||
--hash=sha256:80b0b7dc5afeec2816ef638a61e7c628d73cd72666eebf4984be426d1c2b492d
|
||||
certbot-apache==0.6.0 \
|
||||
--hash=sha256:0ab077f0913b81ed5c1b141c3a7c4c0228ef3738d8d61a93db794d9a80718d43 \
|
||||
--hash=sha256:1cfbe751209079a803758f472200816fac559f2a36fdd582d25e3ba5601423a1
|
||||
letsencrypt==0.6.0 \
|
||||
--hash=sha256:93196c7dcd57272a753e525d145c5a9987c8968c22ec954bcf83dcc9d2499a76 \
|
||||
--hash=sha256:a16d6c395f1bf5fd61a28ef83dc78f42dbecbad9d00be6236f2ad8915645c154
|
||||
letsencrypt-apache==0.6.0 \
|
||||
--hash=sha256:02fadc52a0796e53978c508beec9c53e1fc047660240832b9bde5d53ab3a1379 \
|
||||
--hash=sha256:1c5522d94d7750bdb9bfa6201d2c263e914f662c9d0079e673167233cf4364f1
|
||||
|
||||
UNLIKELY_EOF
|
||||
# -------------------------------------------------------------------------
|
||||
|
|
@ -823,18 +890,30 @@ UNLIKELY_EOF
|
|||
fi
|
||||
echo "Installation succeeded."
|
||||
fi
|
||||
echo "Requesting root privileges to run letsencrypt..."
|
||||
echo " " $SUDO "$VENV_BIN/letsencrypt" "$@"
|
||||
$SUDO "$VENV_BIN/letsencrypt" "$@"
|
||||
echo "Requesting root privileges to run certbot..."
|
||||
if [ -z "$SUDO_ENV" ] ; then
|
||||
# SUDO is su wrapper / noop
|
||||
echo " " $SUDO "$VENV_BIN/letsencrypt" "$@"
|
||||
$SUDO "$VENV_BIN/letsencrypt" "$@"
|
||||
else
|
||||
# sudo
|
||||
echo " " $SUDO "$SUDO_ENV" "$VENV_BIN/letsencrypt" "$@"
|
||||
$SUDO "$SUDO_ENV" "$VENV_BIN/letsencrypt" "$@"
|
||||
fi
|
||||
|
||||
else
|
||||
# Phase 1: Upgrade letsencrypt-auto if neceesary, then self-invoke.
|
||||
# Phase 1: Upgrade certbot-auto if neceesary, then self-invoke.
|
||||
#
|
||||
# Each phase checks the version of only the thing it is responsible for
|
||||
# upgrading. Phase 1 checks the version of the latest release of
|
||||
# letsencrypt-auto (which is always the same as that of the letsencrypt
|
||||
# package). Phase 2 checks the version of the locally installed letsencrypt.
|
||||
# certbot-auto (which is always the same as that of the certbot
|
||||
# package). Phase 2 checks the version of the locally installed certbot.
|
||||
|
||||
if [ ! -f "$VENV_BIN/letsencrypt" ]; then
|
||||
if [ "$HELP" = 1 ]; then
|
||||
echo "$USAGE"
|
||||
exit 0
|
||||
fi
|
||||
# If it looks like we've never bootstrapped before, bootstrap:
|
||||
Bootstrap
|
||||
fi
|
||||
|
|
@ -953,7 +1032,7 @@ def verified_new_le_auto(get, tag, temp_dir):
|
|||
stderr=dev_null)
|
||||
except CalledProcessError as exc:
|
||||
raise ExpectedError("Couldn't verify signature of downloaded "
|
||||
"letsencrypt-auto.", exc)
|
||||
"certbot-auto.", exc)
|
||||
|
||||
|
||||
def main():
|
||||
|
|
@ -978,29 +1057,27 @@ if __name__ == '__main__':
|
|||
UNLIKELY_EOF
|
||||
# ---------------------------------------------------------------------------
|
||||
DeterminePythonVersion
|
||||
REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version`
|
||||
if [ "$LE_AUTO_VERSION" != "$REMOTE_VERSION" ]; then
|
||||
echo "Upgrading letsencrypt-auto $LE_AUTO_VERSION to $REMOTE_VERSION..."
|
||||
if ! REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version` ; then
|
||||
echo "WARNING: unable to check for updates."
|
||||
elif [ "$LE_AUTO_VERSION" != "$REMOTE_VERSION" ]; then
|
||||
echo "Upgrading certbot-auto $LE_AUTO_VERSION to $REMOTE_VERSION..."
|
||||
|
||||
# Now we drop into Python so we don't have to install even more
|
||||
# dependencies (curl, etc.), for better flow control, and for the option of
|
||||
# future Windows compatibility.
|
||||
"$LE_PYTHON" "$TEMP_DIR/fetch.py" --le-auto-script "v$REMOTE_VERSION"
|
||||
|
||||
# Install new copy of letsencrypt-auto.
|
||||
# Install new copy of certbot-auto.
|
||||
# TODO: Deal with quotes in pathnames.
|
||||
echo "Replacing letsencrypt-auto..."
|
||||
echo "Replacing certbot-auto..."
|
||||
# Clone permissions with cp. chmod and chown don't have a --reference
|
||||
# option on OS X or BSD, and stat -c on Linux is stat -f on OS X and BSD:
|
||||
echo " " $SUDO cp -p "$0" "$TEMP_DIR/letsencrypt-auto.permission-clone"
|
||||
$SUDO cp -p "$0" "$TEMP_DIR/letsencrypt-auto.permission-clone"
|
||||
echo " " $SUDO cp "$TEMP_DIR/letsencrypt-auto" "$TEMP_DIR/letsencrypt-auto.permission-clone"
|
||||
$SUDO cp "$TEMP_DIR/letsencrypt-auto" "$TEMP_DIR/letsencrypt-auto.permission-clone"
|
||||
# Using mv rather than cp leaves the old file descriptor pointing to the
|
||||
# original copy so the shell can continue to read it unmolested. mv across
|
||||
# filesystems is non-atomic, doing `rm dest, cp src dest, rm src`, but the
|
||||
# cp is unlikely to fail (esp. under sudo) if the rm doesn't.
|
||||
echo " " $SUDO mv -f "$TEMP_DIR/letsencrypt-auto.permission-clone" "$0"
|
||||
$SUDO mv -f "$TEMP_DIR/letsencrypt-auto.permission-clone" "$0"
|
||||
# TODO: Clean up temp dir safely, even if it has quotes in its path.
|
||||
rm -rf "$TEMP_DIR"
|
||||
|
|
|
|||
11
letsencrypt-auto-source/certbot-auto.asc
Normal file
11
letsencrypt-auto-source/certbot-auto.asc
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
-----BEGIN PGP SIGNATURE-----
|
||||
Version: GnuPG v1
|
||||
|
||||
iQEcBAABAgAGBQJXM9ZDAAoJEE0XyZXNl3XyzGkH/2KeR0jYxXKlvwfCkxU6hSC0
|
||||
eXcxZVQk59hCSvkNGE6Mj6rwQcyjSqmRp14MaJpq7NZADN6F+HWb6VB/Wq6moMQs
|
||||
PJtthqwhF767Qg+Py9Hp6XmlKscjXB6AKCVxq5TBwEIOTtj0rhQRLF9/+GW6jFuf
|
||||
kT6aUcDWNjOyWWUtp9vOVprDtegrltp0/2DNitlvPu263pKC+7I3GyLTq4fKP4EE
|
||||
auZSAhFry9SNR3Usf2wD3kzhvLSrT3h9Yh5oA04oaX9H6e86EHwt6RJJRHpg8s6b
|
||||
e0CBIIuaRJEmdiMUWlV/gAfH6M2PbG1wtJdxc0ThNEoWAjTsopr61BoHJ3cpCy4=
|
||||
=+e7/
|
||||
-----END PGP SIGNATURE-----
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Download and run the latest release version of the Let's Encrypt client.
|
||||
# Download and run the latest release version of the Certbot client.
|
||||
#
|
||||
# NOTE: THIS SCRIPT IS AUTO-GENERATED AND SELF-UPDATING
|
||||
#
|
||||
|
|
@ -19,11 +19,25 @@ XDG_DATA_HOME=${XDG_DATA_HOME:-~/.local/share}
|
|||
VENV_NAME="letsencrypt"
|
||||
VENV_PATH=${VENV_PATH:-"$XDG_DATA_HOME/$VENV_NAME"}
|
||||
VENV_BIN="$VENV_PATH/bin"
|
||||
LE_AUTO_VERSION="0.6.0.dev0"
|
||||
LE_AUTO_VERSION="0.7.0.dev0"
|
||||
BASENAME=$(basename $0)
|
||||
USAGE="Usage: $BASENAME [OPTIONS]
|
||||
A self-updating wrapper script for the Certbot ACME client. When run, updates
|
||||
to both this script and certbot will be downloaded and installed. After
|
||||
ensuring you have the latest versions installed, certbot will be invoked with
|
||||
all arguments you have provided.
|
||||
|
||||
Help for certbot itself cannot be provided until it is installed.
|
||||
|
||||
--debug attempt experimental installation
|
||||
-h, --help print this help
|
||||
-n, --non-interactive, --noninteractive run without asking for user input
|
||||
--no-self-upgrade do not download updates
|
||||
--os-packages-only install OS dependencies and exit
|
||||
-v, --verbose provide more output
|
||||
|
||||
All arguments are accepted and forwarded to the Certbot client when run."
|
||||
|
||||
# This script takes the same arguments as the main letsencrypt program, but it
|
||||
# additionally responds to --verbose (more output) and --debug (allow support
|
||||
# for experimental platforms)
|
||||
for arg in "$@" ; do
|
||||
case "$arg" in
|
||||
--debug)
|
||||
|
|
@ -34,25 +48,43 @@ for arg in "$@" ; do
|
|||
# Do not upgrade this script (also prevents client upgrades, because each
|
||||
# copy of the script pins a hash of the python client)
|
||||
NO_SELF_UPGRADE=1;;
|
||||
--help)
|
||||
HELP=1;;
|
||||
--noninteractive|--non-interactive)
|
||||
ASSUME_YES=1;;
|
||||
--verbose)
|
||||
VERBOSE=1;;
|
||||
[!-]*|-*[!v]*|-)
|
||||
# Anything that isn't -v, -vv, etc.: that is, anything that does not
|
||||
# start with a -, contains anything that's not a v, or is just "-"
|
||||
;;
|
||||
*) # -v+ remains.
|
||||
VERBOSE=1;;
|
||||
-[!-]*)
|
||||
while getopts ":hnv" short_arg $arg; do
|
||||
case "$short_arg" in
|
||||
h)
|
||||
HELP=1;;
|
||||
n)
|
||||
ASSUME_YES=1;;
|
||||
v)
|
||||
VERBOSE=1;;
|
||||
esac
|
||||
done;;
|
||||
esac
|
||||
done
|
||||
|
||||
# letsencrypt-auto needs root access to bootstrap OS dependencies, and
|
||||
# letsencrypt itself needs root access for almost all modes of operation
|
||||
if [ $BASENAME = "letsencrypt-auto" ]; then
|
||||
# letsencrypt-auto does not respect --help or --yes for backwards compatibility
|
||||
ASSUME_YES=1
|
||||
HELP=0
|
||||
fi
|
||||
|
||||
# certbot-auto needs root access to bootstrap OS dependencies, and
|
||||
# certbot itself needs root access for almost all modes of operation
|
||||
# The "normal" case is that sudo is used for the steps that need root, but
|
||||
# this script *can* be run as root (not recommended), or fall back to using
|
||||
# `su`
|
||||
SUDO_ENV=""
|
||||
export CERTBOT_AUTO="$0"
|
||||
if test "`id -u`" -ne "0" ; then
|
||||
if command -v sudo 1>/dev/null 2>&1; then
|
||||
SUDO=sudo
|
||||
SUDO_ENV="CERTBOT_AUTO=$0"
|
||||
else
|
||||
echo \"sudo\" is not available, will use \"su\" for installation steps...
|
||||
# Because the parameters in `su -c` has to be a string,
|
||||
|
|
@ -151,30 +183,45 @@ BootstrapDebCommon() {
|
|||
augeas_pkg="libaugeas0 augeas-lenses"
|
||||
AUGVERSION=`apt-cache show --no-all-versions libaugeas0 | grep ^Version: | cut -d" " -f2`
|
||||
|
||||
if [ "$ASSUME_YES" = 1 ]; then
|
||||
YES_FLAG="-y"
|
||||
fi
|
||||
|
||||
AddBackportRepo() {
|
||||
# ARGS:
|
||||
BACKPORT_NAME="$1"
|
||||
BACKPORT_SOURCELINE="$2"
|
||||
echo "To use the Apache Certbot plugin, augeas needs to be installed from $BACKPORT_NAME."
|
||||
if ! grep -v -e ' *#' /etc/apt/sources.list | grep -q "$BACKPORT_NAME" ; then
|
||||
# This can theoretically error if sources.list.d is empty, but in that case we don't care.
|
||||
if ! grep -v -e ' *#' /etc/apt/sources.list.d/* 2>/dev/null | grep -q "$BACKPORT_NAME"; then
|
||||
/bin/echo -n "Installing augeas from $BACKPORT_NAME in 3 seconds..."
|
||||
sleep 1s
|
||||
/bin/echo -ne "\e[0K\rInstalling augeas from $BACKPORT_NAME in 2 seconds..."
|
||||
sleep 1s
|
||||
/bin/echo -e "\e[0K\rInstalling augeas from $BACKPORT_NAME in 1 second ..."
|
||||
sleep 1s
|
||||
if echo $BACKPORT_NAME | grep -q wheezy ; then
|
||||
/bin/echo '(Backports are only installed if explicitly requested via "apt-get install -t wheezy-backports")'
|
||||
if [ "$ASSUME_YES" = 1 ]; then
|
||||
/bin/echo -n "Installing augeas from $BACKPORT_NAME in 3 seconds..."
|
||||
sleep 1s
|
||||
/bin/echo -ne "\e[0K\rInstalling augeas from $BACKPORT_NAME in 2 seconds..."
|
||||
sleep 1s
|
||||
/bin/echo -e "\e[0K\rInstalling augeas from $BACKPORT_NAME in 1 second ..."
|
||||
sleep 1s
|
||||
add_backports=1
|
||||
else
|
||||
read -p "Would you like to enable the $BACKPORT_NAME repository [Y/n]? " response
|
||||
case $response in
|
||||
[yY][eE][sS]|[yY]|"")
|
||||
add_backports=1;;
|
||||
*)
|
||||
add_backports=0;;
|
||||
esac
|
||||
fi
|
||||
if [ "$add_backports" = 1 ]; then
|
||||
$SUDO sh -c "echo $BACKPORT_SOURCELINE >> /etc/apt/sources.list.d/$BACKPORT_NAME.list"
|
||||
$SUDO apt-get update
|
||||
fi
|
||||
|
||||
$SUDO sh -c "echo $BACKPORT_SOURCELINE >> /etc/apt/sources.list.d/$BACKPORT_NAME.list"
|
||||
$SUDO apt-get update
|
||||
fi
|
||||
fi
|
||||
$SUDO apt-get install -y --no-install-recommends -t "$BACKPORT_NAME" $augeas_pkg
|
||||
augeas_pkg=
|
||||
|
||||
if [ "$add_backports" != 0 ]; then
|
||||
$SUDO apt-get install $YES_FLAG --no-install-recommends -t "$BACKPORT_NAME" $augeas_pkg
|
||||
augeas_pkg=
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -186,12 +233,12 @@ BootstrapDebCommon() {
|
|||
AddBackportRepo precise-backports "deb http://archive.ubuntu.com/ubuntu precise-backports main restricted universe multiverse"
|
||||
else
|
||||
echo "No libaugeas0 version is available that's new enough to run the"
|
||||
echo "Let's Encrypt apache plugin..."
|
||||
echo "Certbot apache plugin..."
|
||||
fi
|
||||
# XXX add a case for ubuntu PPAs
|
||||
fi
|
||||
|
||||
$SUDO apt-get install -y --no-install-recommends \
|
||||
$SUDO apt-get install $YES_FLAG --no-install-recommends \
|
||||
python \
|
||||
python-dev \
|
||||
$virtualenv \
|
||||
|
|
@ -212,9 +259,10 @@ BootstrapDebCommon() {
|
|||
|
||||
BootstrapRpmCommon() {
|
||||
# Tested with:
|
||||
# - Fedora 22, 23 (x64)
|
||||
# - Fedora 20, 21, 22, 23 (x64)
|
||||
# - Centos 7 (x64: on DigitalOcean droplet)
|
||||
# - CentOS 7 Minimal install in a Hyper-V VM
|
||||
# - CentOS 6 (EPEL must be installed manually)
|
||||
|
||||
if type dnf 2>/dev/null
|
||||
then
|
||||
|
|
@ -228,54 +276,62 @@ BootstrapRpmCommon() {
|
|||
exit 1
|
||||
fi
|
||||
|
||||
pkgs="
|
||||
gcc
|
||||
dialog
|
||||
augeas-libs
|
||||
openssl
|
||||
openssl-devel
|
||||
libffi-devel
|
||||
redhat-rpm-config
|
||||
ca-certificates
|
||||
"
|
||||
|
||||
# Some distros and older versions of current distros use a "python27"
|
||||
# instead of "python" naming convention. Try both conventions.
|
||||
if ! $SUDO $tool install -y \
|
||||
python \
|
||||
python-devel \
|
||||
python-virtualenv \
|
||||
python-tools \
|
||||
python-pip
|
||||
then
|
||||
if ! $SUDO $tool install -y \
|
||||
python27 \
|
||||
python27-devel \
|
||||
python27-virtualenv \
|
||||
python27-tools \
|
||||
python27-pip
|
||||
then
|
||||
echo "Could not install Python dependencies. Aborting bootstrap!"
|
||||
exit 1
|
||||
fi
|
||||
if $SUDO $tool list python >/dev/null 2>&1; then
|
||||
pkgs="$pkgs
|
||||
python
|
||||
python-devel
|
||||
python-virtualenv
|
||||
python-tools
|
||||
python-pip
|
||||
"
|
||||
else
|
||||
pkgs="$pkgs
|
||||
python27
|
||||
python27-devel
|
||||
python27-virtualenv
|
||||
python27-tools
|
||||
python27-pip
|
||||
"
|
||||
fi
|
||||
|
||||
if ! $SUDO $tool install -y \
|
||||
gcc \
|
||||
dialog \
|
||||
augeas-libs \
|
||||
openssl \
|
||||
openssl-devel \
|
||||
libffi-devel \
|
||||
redhat-rpm-config \
|
||||
ca-certificates
|
||||
then
|
||||
echo "Could not install additional dependencies. Aborting bootstrap!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
if $SUDO $tool list installed "httpd" >/dev/null 2>&1; then
|
||||
if ! $SUDO $tool install -y mod_ssl
|
||||
then
|
||||
echo "Apache found, but mod_ssl could not be installed."
|
||||
fi
|
||||
pkgs="$pkgs
|
||||
mod_ssl
|
||||
"
|
||||
fi
|
||||
|
||||
if [ "$ASSUME_YES" = 1 ]; then
|
||||
yes_flag="-y"
|
||||
fi
|
||||
|
||||
if ! $SUDO $tool install $yes_flag $pkgs; then
|
||||
echo "Could not install OS dependencies. Aborting bootstrap!"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
BootstrapSuseCommon() {
|
||||
# SLE12 don't have python-virtualenv
|
||||
|
||||
$SUDO zypper -nq in -l \
|
||||
if [ "$ASSUME_YES" = 1 ]; then
|
||||
zypper_flags="-nq"
|
||||
install_flags="-l"
|
||||
fi
|
||||
|
||||
$SUDO zypper $zypper_flags in $install_flags \
|
||||
python \
|
||||
python-devel \
|
||||
python-virtualenv \
|
||||
|
|
@ -310,8 +366,12 @@ BootstrapArchCommon() {
|
|||
# pacman -T exits with 127 if there are missing dependencies
|
||||
missing=$($SUDO pacman -T $deps) || true
|
||||
|
||||
if [ "$ASSUME_YES" = 1 ]; then
|
||||
noconfirm="--noconfirm"
|
||||
fi
|
||||
|
||||
if [ "$missing" ]; then
|
||||
$SUDO pacman -S --needed $missing
|
||||
$SUDO pacman -S --needed $missing $noconfirm
|
||||
fi
|
||||
}
|
||||
|
||||
|
|
@ -365,7 +425,8 @@ BootstrapMac() {
|
|||
|
||||
$pkgcmd augeas
|
||||
$pkgcmd dialog
|
||||
if [ "$(which python)" = "/System/Library/Frameworks/Python.framework/Versions/2.7/bin/python" ]; then
|
||||
if [ "$(which python)" = "/System/Library/Frameworks/Python.framework/Versions/2.7/bin/python" \
|
||||
-o "$(which python)" = "/usr/bin/python" ]; then
|
||||
# We want to avoid using the system Python because it requires root to use pip.
|
||||
# python.org, MacPorts or HomeBrew Python installations should all be OK.
|
||||
echo "Installing python..."
|
||||
|
|
@ -375,7 +436,8 @@ BootstrapMac() {
|
|||
# Workaround for _dlopen not finding augeas on OS X
|
||||
if [ "$pkgman" = "port" ] && ! [ -e "/usr/local/lib/libaugeas.dylib" ] && [ -e "/opt/local/lib/libaugeas.dylib" ]; then
|
||||
echo "Applying augeas workaround"
|
||||
$SUDO ln -s /opt/local/lib/libaugeas.dylib /usr/local/lib
|
||||
$SUDO mkdir -p /usr/local/lib/
|
||||
$SUDO ln -s /opt/local/lib/libaugeas.dylib /usr/local/lib/
|
||||
fi
|
||||
|
||||
if ! hash pip 2>/dev/null; then
|
||||
|
|
@ -391,6 +453,11 @@ BootstrapMac() {
|
|||
fi
|
||||
}
|
||||
|
||||
BootstrapSmartOS() {
|
||||
pkgin update
|
||||
pkgin -y install 'gcc49' 'py27-augeas' 'py27-virtualenv'
|
||||
}
|
||||
|
||||
|
||||
# Install required OS packages:
|
||||
Bootstrap() {
|
||||
|
|
@ -423,10 +490,12 @@ Bootstrap() {
|
|||
ExperimentalBootstrap "FreeBSD" BootstrapFreeBsd
|
||||
elif uname | grep -iq Darwin ; then
|
||||
ExperimentalBootstrap "Mac OS X" BootstrapMac
|
||||
elif grep -iq "Amazon Linux" /etc/issue ; then
|
||||
elif [ -f /etc/issue ] && grep -iq "Amazon Linux" /etc/issue ; then
|
||||
ExperimentalBootstrap "Amazon Linux" BootstrapRpmCommon
|
||||
elif [ -f /etc/product ] && grep -q "Joyent Instance" /etc/product ; then
|
||||
ExperimentalBootstrap "Joyent SmartOS Zone" BootstrapSmartOS
|
||||
else
|
||||
echo "Sorry, I don't know how to bootstrap Let's Encrypt on your operating system!"
|
||||
echo "Sorry, I don't know how to bootstrap Certbot on your operating system!"
|
||||
echo
|
||||
echo "You will need to bootstrap, configure virtualenv, and run pip install manually."
|
||||
echo "Please see https://letsencrypt.readthedocs.org/en/latest/contributing.html#prerequisites"
|
||||
|
|
@ -446,7 +515,8 @@ if [ "$1" = "--le-auto-phase2" ]; then
|
|||
shift 1 # the --le-auto-phase2 arg
|
||||
if [ -f "$VENV_BIN/letsencrypt" ]; then
|
||||
# --version output ran through grep due to python-cryptography DeprecationWarnings
|
||||
INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | grep ^letsencrypt | cut -d " " -f 2)
|
||||
# grep for both certbot and letsencrypt until certbot and shim packages have been released
|
||||
INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | grep "^certbot\|^letsencrypt" | cut -d " " -f 2)
|
||||
else
|
||||
INSTALLED_VERSION="none"
|
||||
fi
|
||||
|
|
@ -462,11 +532,12 @@ if [ "$1" = "--le-auto-phase2" ]; then
|
|||
|
||||
echo "Installing Python packages..."
|
||||
TEMP_DIR=$(TempDir)
|
||||
trap 'rm -rf "$TEMP_DIR"' EXIT
|
||||
# There is no $ interpolation due to quotes on starting heredoc delimiter.
|
||||
# -------------------------------------------------------------------------
|
||||
cat << "UNLIKELY_EOF" > "$TEMP_DIR/letsencrypt-auto-requirements.txt"
|
||||
# This is the flattened list of packages letsencrypt-auto installs. To generate
|
||||
# this, do `pip install --no-cache-dir -e acme -e . -e letsencrypt-apache`, and
|
||||
# This is the flattened list of packages certbot-auto installs. To generate
|
||||
# this, do `pip install --no-cache-dir -e acme -e . -e certbot-apache`, and
|
||||
# then use `hashin` or a more secure method to gather the hashes.
|
||||
|
||||
argparse==1.4.0 \
|
||||
|
|
@ -645,15 +716,21 @@ mock==1.0.1 \
|
|||
|
||||
# THE LINES BELOW ARE EDITED BY THE RELEASE SCRIPT; ADD ALL DEPENDENCIES ABOVE.
|
||||
|
||||
acme==0.5.0 \
|
||||
--hash=sha256:ceb4127c13213f0006a564be82176b968c6b374d20d9fc78555d0658a252b275 \
|
||||
--hash=sha256:0605c63c656d33c883a05675f5db9cfb85d503f2771c885031800e0da7631abd
|
||||
letsencrypt==0.5.0 \
|
||||
--hash=sha256:f90f883e99cdbdf8142335bdbf4f74a8af143ee4b4ec60fb49c6e47418c1114c \
|
||||
--hash=sha256:e38a2b70b82be79bc195307652244a3e012ec73d897d4dbd3f80cf698496d15a
|
||||
letsencrypt-apache==0.5.0 \
|
||||
--hash=sha256:a767882164a7b09d9c12c80684a28a782135fdaf35654ef5a02c0b7b1d27ab8d \
|
||||
--hash=sha256:c20e7b9c517aa4a7d70e6bd9382da7259f00bc191b9e60d8e312e48837a00c41
|
||||
acme==0.6.0 \
|
||||
--hash=sha256:cbe4e7a340a19725a8740ed86e30abdbe18fc22c4c6022b7a8e56642d502bcc3 \
|
||||
--hash=sha256:ec4e6009dfbd629b58473eb06bbebfd9fb2a79fc8831c149e9205bc38a98ecc6
|
||||
certbot==0.6.0 \
|
||||
--hash=sha256:a893632d228864b0a751db9f3fdd93439ed34b988ea21b64fb0f0fa2ceded6a2 \
|
||||
--hash=sha256:80b0b7dc5afeec2816ef638a61e7c628d73cd72666eebf4984be426d1c2b492d
|
||||
certbot-apache==0.6.0 \
|
||||
--hash=sha256:0ab077f0913b81ed5c1b141c3a7c4c0228ef3738d8d61a93db794d9a80718d43 \
|
||||
--hash=sha256:1cfbe751209079a803758f472200816fac559f2a36fdd582d25e3ba5601423a1
|
||||
letsencrypt==0.6.0 \
|
||||
--hash=sha256:93196c7dcd57272a753e525d145c5a9987c8968c22ec954bcf83dcc9d2499a76 \
|
||||
--hash=sha256:a16d6c395f1bf5fd61a28ef83dc78f42dbecbad9d00be6236f2ad8915645c154
|
||||
letsencrypt-apache==0.6.0 \
|
||||
--hash=sha256:02fadc52a0796e53978c508beec9c53e1fc047660240832b9bde5d53ab3a1379 \
|
||||
--hash=sha256:1c5522d94d7750bdb9bfa6201d2c263e914f662c9d0079e673167233cf4364f1
|
||||
|
||||
UNLIKELY_EOF
|
||||
# -------------------------------------------------------------------------
|
||||
|
|
@ -813,7 +890,6 @@ UNLIKELY_EOF
|
|||
PIP_OUT=`"$VENV_BIN/pip" install --no-cache-dir --require-hashes -r "$TEMP_DIR/letsencrypt-auto-requirements.txt" 2>&1`
|
||||
PIP_STATUS=$?
|
||||
set -e
|
||||
rm -rf "$TEMP_DIR"
|
||||
if [ "$PIP_STATUS" != 0 ]; then
|
||||
# Report error. (Otherwise, be quiet.)
|
||||
echo "Had a problem while installing Python packages:"
|
||||
|
|
@ -823,18 +899,32 @@ UNLIKELY_EOF
|
|||
fi
|
||||
echo "Installation succeeded."
|
||||
fi
|
||||
echo "Requesting root privileges to run letsencrypt..."
|
||||
echo " " $SUDO "$VENV_BIN/letsencrypt" "$@"
|
||||
$SUDO "$VENV_BIN/letsencrypt" "$@"
|
||||
if [ -n "$SUDO" ]; then
|
||||
# SUDO is su wrapper or sudo
|
||||
echo "Requesting root privileges to run certbot..."
|
||||
echo " $VENV_BIN/letsencrypt" "$@"
|
||||
fi
|
||||
if [ -z "$SUDO_ENV" ] ; then
|
||||
# SUDO is su wrapper / noop
|
||||
$SUDO "$VENV_BIN/letsencrypt" "$@"
|
||||
else
|
||||
# sudo
|
||||
$SUDO "$SUDO_ENV" "$VENV_BIN/letsencrypt" "$@"
|
||||
fi
|
||||
|
||||
else
|
||||
# Phase 1: Upgrade letsencrypt-auto if neceesary, then self-invoke.
|
||||
# Phase 1: Upgrade certbot-auto if neceesary, then self-invoke.
|
||||
#
|
||||
# Each phase checks the version of only the thing it is responsible for
|
||||
# upgrading. Phase 1 checks the version of the latest release of
|
||||
# letsencrypt-auto (which is always the same as that of the letsencrypt
|
||||
# package). Phase 2 checks the version of the locally installed letsencrypt.
|
||||
# certbot-auto (which is always the same as that of the certbot
|
||||
# package). Phase 2 checks the version of the locally installed certbot.
|
||||
|
||||
if [ ! -f "$VENV_BIN/letsencrypt" ]; then
|
||||
if [ "$HELP" = 1 ]; then
|
||||
echo "$USAGE"
|
||||
exit 0
|
||||
fi
|
||||
# If it looks like we've never bootstrapped before, bootstrap:
|
||||
Bootstrap
|
||||
fi
|
||||
|
|
@ -844,8 +934,8 @@ else
|
|||
fi
|
||||
|
||||
if [ "$NO_SELF_UPGRADE" != 1 ]; then
|
||||
echo "Checking for new version..."
|
||||
TEMP_DIR=$(TempDir)
|
||||
trap 'rm -rf "$TEMP_DIR"' EXIT
|
||||
# ---------------------------------------------------------------------------
|
||||
cat << "UNLIKELY_EOF" > "$TEMP_DIR/fetch.py"
|
||||
"""Do downloading and JSON parsing without additional dependencies. ::
|
||||
|
|
@ -918,7 +1008,7 @@ def latest_stable_version(get):
|
|||
"""Return the latest stable release of letsencrypt."""
|
||||
metadata = loads(get(
|
||||
environ.get('LE_AUTO_JSON_URL',
|
||||
'https://pypi.python.org/pypi/letsencrypt/json')))
|
||||
'https://pypi.python.org/pypi/certbot/json')))
|
||||
# metadata['info']['version'] actually returns the latest of any kind of
|
||||
# release release, contrary to https://wiki.python.org/moin/PyPIJSON.
|
||||
# The regex is a sufficient regex for picking out prereleases for most
|
||||
|
|
@ -937,7 +1027,7 @@ def verified_new_le_auto(get, tag, temp_dir):
|
|||
"""
|
||||
le_auto_dir = environ.get(
|
||||
'LE_AUTO_DIR_TEMPLATE',
|
||||
'https://raw.githubusercontent.com/letsencrypt/letsencrypt/%s/'
|
||||
'https://raw.githubusercontent.com/certbot/certbot/%s/'
|
||||
'letsencrypt-auto-source/') % tag
|
||||
write(get(le_auto_dir + 'letsencrypt-auto'), temp_dir, 'letsencrypt-auto')
|
||||
write(get(le_auto_dir + 'letsencrypt-auto.sig'), temp_dir, 'letsencrypt-auto.sig')
|
||||
|
|
@ -953,7 +1043,7 @@ def verified_new_le_auto(get, tag, temp_dir):
|
|||
stderr=dev_null)
|
||||
except CalledProcessError as exc:
|
||||
raise ExpectedError("Couldn't verify signature of downloaded "
|
||||
"letsencrypt-auto.", exc)
|
||||
"certbot-auto.", exc)
|
||||
|
||||
|
||||
def main():
|
||||
|
|
@ -978,32 +1068,28 @@ if __name__ == '__main__':
|
|||
UNLIKELY_EOF
|
||||
# ---------------------------------------------------------------------------
|
||||
DeterminePythonVersion
|
||||
REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version`
|
||||
if [ "$LE_AUTO_VERSION" != "$REMOTE_VERSION" ]; then
|
||||
echo "Upgrading letsencrypt-auto $LE_AUTO_VERSION to $REMOTE_VERSION..."
|
||||
if ! REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version` ; then
|
||||
echo "WARNING: unable to check for updates."
|
||||
elif [ "$LE_AUTO_VERSION" != "$REMOTE_VERSION" ]; then
|
||||
echo "Upgrading certbot-auto $LE_AUTO_VERSION to $REMOTE_VERSION..."
|
||||
|
||||
# Now we drop into Python so we don't have to install even more
|
||||
# dependencies (curl, etc.), for better flow control, and for the option of
|
||||
# future Windows compatibility.
|
||||
"$LE_PYTHON" "$TEMP_DIR/fetch.py" --le-auto-script "v$REMOTE_VERSION"
|
||||
|
||||
# Install new copy of letsencrypt-auto.
|
||||
# Install new copy of certbot-auto.
|
||||
# TODO: Deal with quotes in pathnames.
|
||||
echo "Replacing letsencrypt-auto..."
|
||||
echo "Replacing certbot-auto..."
|
||||
# Clone permissions with cp. chmod and chown don't have a --reference
|
||||
# option on OS X or BSD, and stat -c on Linux is stat -f on OS X and BSD:
|
||||
echo " " $SUDO cp -p "$0" "$TEMP_DIR/letsencrypt-auto.permission-clone"
|
||||
$SUDO cp -p "$0" "$TEMP_DIR/letsencrypt-auto.permission-clone"
|
||||
echo " " $SUDO cp "$TEMP_DIR/letsencrypt-auto" "$TEMP_DIR/letsencrypt-auto.permission-clone"
|
||||
$SUDO cp "$TEMP_DIR/letsencrypt-auto" "$TEMP_DIR/letsencrypt-auto.permission-clone"
|
||||
# Using mv rather than cp leaves the old file descriptor pointing to the
|
||||
# original copy so the shell can continue to read it unmolested. mv across
|
||||
# filesystems is non-atomic, doing `rm dest, cp src dest, rm src`, but the
|
||||
# cp is unlikely to fail (esp. under sudo) if the rm doesn't.
|
||||
echo " " $SUDO mv -f "$TEMP_DIR/letsencrypt-auto.permission-clone" "$0"
|
||||
$SUDO mv -f "$TEMP_DIR/letsencrypt-auto.permission-clone" "$0"
|
||||
# TODO: Clean up temp dir safely, even if it has quotes in its path.
|
||||
rm -rf "$TEMP_DIR"
|
||||
fi # A newer version is available.
|
||||
fi # Self-upgrading is allowed.
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -20,10 +20,24 @@ VENV_NAME="letsencrypt"
|
|||
VENV_PATH=${VENV_PATH:-"$XDG_DATA_HOME/$VENV_NAME"}
|
||||
VENV_BIN="$VENV_PATH/bin"
|
||||
LE_AUTO_VERSION="{{ LE_AUTO_VERSION }}"
|
||||
BASENAME=$(basename $0)
|
||||
USAGE="Usage: $BASENAME [OPTIONS]
|
||||
A self-updating wrapper script for the Certbot ACME client. When run, updates
|
||||
to both this script and certbot will be downloaded and installed. After
|
||||
ensuring you have the latest versions installed, certbot will be invoked with
|
||||
all arguments you have provided.
|
||||
|
||||
Help for certbot itself cannot be provided until it is installed.
|
||||
|
||||
--debug attempt experimental installation
|
||||
-h, --help print this help
|
||||
-n, --non-interactive, --noninteractive run without asking for user input
|
||||
--no-self-upgrade do not download updates
|
||||
--os-packages-only install OS dependencies and exit
|
||||
-v, --verbose provide more output
|
||||
|
||||
All arguments are accepted and forwarded to the Certbot client when run."
|
||||
|
||||
# This script takes the same arguments as the main letsencrypt program, but it
|
||||
# additionally responds to --verbose (more output) and --debug (allow support
|
||||
# for experimental platforms)
|
||||
for arg in "$@" ; do
|
||||
case "$arg" in
|
||||
--debug)
|
||||
|
|
@ -34,25 +48,43 @@ for arg in "$@" ; do
|
|||
# Do not upgrade this script (also prevents client upgrades, because each
|
||||
# copy of the script pins a hash of the python client)
|
||||
NO_SELF_UPGRADE=1;;
|
||||
--help)
|
||||
HELP=1;;
|
||||
--noninteractive|--non-interactive)
|
||||
ASSUME_YES=1;;
|
||||
--verbose)
|
||||
VERBOSE=1;;
|
||||
[!-]*|-*[!v]*|-)
|
||||
# Anything that isn't -v, -vv, etc.: that is, anything that does not
|
||||
# start with a -, contains anything that's not a v, or is just "-"
|
||||
;;
|
||||
*) # -v+ remains.
|
||||
VERBOSE=1;;
|
||||
-[!-]*)
|
||||
while getopts ":hnv" short_arg $arg; do
|
||||
case "$short_arg" in
|
||||
h)
|
||||
HELP=1;;
|
||||
n)
|
||||
ASSUME_YES=1;;
|
||||
v)
|
||||
VERBOSE=1;;
|
||||
esac
|
||||
done;;
|
||||
esac
|
||||
done
|
||||
|
||||
# letsencrypt-auto needs root access to bootstrap OS dependencies, and
|
||||
if [ $BASENAME = "letsencrypt-auto" ]; then
|
||||
# letsencrypt-auto does not respect --help or --yes for backwards compatibility
|
||||
ASSUME_YES=1
|
||||
HELP=0
|
||||
fi
|
||||
|
||||
# certbot-auto needs root access to bootstrap OS dependencies, and
|
||||
# certbot itself needs root access for almost all modes of operation
|
||||
# The "normal" case is that sudo is used for the steps that need root, but
|
||||
# this script *can* be run as root (not recommended), or fall back to using
|
||||
# `su`
|
||||
SUDO_ENV=""
|
||||
export CERTBOT_AUTO="$0"
|
||||
if test "`id -u`" -ne "0" ; then
|
||||
if command -v sudo 1>/dev/null 2>&1; then
|
||||
SUDO=sudo
|
||||
SUDO_ENV="CERTBOT_AUTO=$0"
|
||||
else
|
||||
echo \"sudo\" is not available, will use \"su\" for installation steps...
|
||||
# Because the parameters in `su -c` has to be a string,
|
||||
|
|
@ -122,6 +154,7 @@ DeterminePythonVersion() {
|
|||
{{ bootstrappers/gentoo_common.sh }}
|
||||
{{ bootstrappers/free_bsd.sh }}
|
||||
{{ bootstrappers/mac.sh }}
|
||||
{{ bootstrappers/smartos.sh }}
|
||||
|
||||
# Install required OS packages:
|
||||
Bootstrap() {
|
||||
|
|
@ -154,8 +187,10 @@ Bootstrap() {
|
|||
ExperimentalBootstrap "FreeBSD" BootstrapFreeBsd
|
||||
elif uname | grep -iq Darwin ; then
|
||||
ExperimentalBootstrap "Mac OS X" BootstrapMac
|
||||
elif grep -iq "Amazon Linux" /etc/issue ; then
|
||||
elif [ -f /etc/issue ] && grep -iq "Amazon Linux" /etc/issue ; then
|
||||
ExperimentalBootstrap "Amazon Linux" BootstrapRpmCommon
|
||||
elif [ -f /etc/product ] && grep -q "Joyent Instance" /etc/product ; then
|
||||
ExperimentalBootstrap "Joyent SmartOS Zone" BootstrapSmartOS
|
||||
else
|
||||
echo "Sorry, I don't know how to bootstrap Certbot on your operating system!"
|
||||
echo
|
||||
|
|
@ -177,7 +212,8 @@ if [ "$1" = "--le-auto-phase2" ]; then
|
|||
shift 1 # the --le-auto-phase2 arg
|
||||
if [ -f "$VENV_BIN/letsencrypt" ]; then
|
||||
# --version output ran through grep due to python-cryptography DeprecationWarnings
|
||||
INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | grep ^letsencrypt | cut -d " " -f 2)
|
||||
# grep for both certbot and letsencrypt until certbot and shim packages have been released
|
||||
INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | grep "^certbot\|^letsencrypt" | cut -d " " -f 2)
|
||||
else
|
||||
INSTALLED_VERSION="none"
|
||||
fi
|
||||
|
|
@ -193,6 +229,7 @@ if [ "$1" = "--le-auto-phase2" ]; then
|
|||
|
||||
echo "Installing Python packages..."
|
||||
TEMP_DIR=$(TempDir)
|
||||
trap 'rm -rf "$TEMP_DIR"' EXIT
|
||||
# There is no $ interpolation due to quotes on starting heredoc delimiter.
|
||||
# -------------------------------------------------------------------------
|
||||
cat << "UNLIKELY_EOF" > "$TEMP_DIR/letsencrypt-auto-requirements.txt"
|
||||
|
|
@ -209,7 +246,6 @@ UNLIKELY_EOF
|
|||
PIP_OUT=`"$VENV_BIN/pip" install --no-cache-dir --require-hashes -r "$TEMP_DIR/letsencrypt-auto-requirements.txt" 2>&1`
|
||||
PIP_STATUS=$?
|
||||
set -e
|
||||
rm -rf "$TEMP_DIR"
|
||||
if [ "$PIP_STATUS" != 0 ]; then
|
||||
# Report error. (Otherwise, be quiet.)
|
||||
echo "Had a problem while installing Python packages:"
|
||||
|
|
@ -219,18 +255,32 @@ UNLIKELY_EOF
|
|||
fi
|
||||
echo "Installation succeeded."
|
||||
fi
|
||||
echo "Requesting root privileges to run certbot..."
|
||||
echo " " $SUDO "$VENV_BIN/letsencrypt" "$@"
|
||||
$SUDO "$VENV_BIN/letsencrypt" "$@"
|
||||
if [ -n "$SUDO" ]; then
|
||||
# SUDO is su wrapper or sudo
|
||||
echo "Requesting root privileges to run certbot..."
|
||||
echo " $VENV_BIN/letsencrypt" "$@"
|
||||
fi
|
||||
if [ -z "$SUDO_ENV" ] ; then
|
||||
# SUDO is su wrapper / noop
|
||||
$SUDO "$VENV_BIN/letsencrypt" "$@"
|
||||
else
|
||||
# sudo
|
||||
$SUDO "$SUDO_ENV" "$VENV_BIN/letsencrypt" "$@"
|
||||
fi
|
||||
|
||||
else
|
||||
# Phase 1: Upgrade letsencrypt-auto if neceesary, then self-invoke.
|
||||
# Phase 1: Upgrade certbot-auto if neceesary, then self-invoke.
|
||||
#
|
||||
# Each phase checks the version of only the thing it is responsible for
|
||||
# upgrading. Phase 1 checks the version of the latest release of
|
||||
# letsencrypt-auto (which is always the same as that of the certbot
|
||||
# certbot-auto (which is always the same as that of the certbot
|
||||
# package). Phase 2 checks the version of the locally installed certbot.
|
||||
|
||||
if [ ! -f "$VENV_BIN/letsencrypt" ]; then
|
||||
if [ "$HELP" = 1 ]; then
|
||||
echo "$USAGE"
|
||||
exit 0
|
||||
fi
|
||||
# If it looks like we've never bootstrapped before, bootstrap:
|
||||
Bootstrap
|
||||
fi
|
||||
|
|
@ -240,40 +290,36 @@ else
|
|||
fi
|
||||
|
||||
if [ "$NO_SELF_UPGRADE" != 1 ]; then
|
||||
echo "Checking for new version..."
|
||||
TEMP_DIR=$(TempDir)
|
||||
trap 'rm -rf "$TEMP_DIR"' EXIT
|
||||
# ---------------------------------------------------------------------------
|
||||
cat << "UNLIKELY_EOF" > "$TEMP_DIR/fetch.py"
|
||||
{{ fetch.py }}
|
||||
UNLIKELY_EOF
|
||||
# ---------------------------------------------------------------------------
|
||||
DeterminePythonVersion
|
||||
REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version`
|
||||
if [ "$LE_AUTO_VERSION" != "$REMOTE_VERSION" ]; then
|
||||
echo "Upgrading letsencrypt-auto $LE_AUTO_VERSION to $REMOTE_VERSION..."
|
||||
if ! REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version` ; then
|
||||
echo "WARNING: unable to check for updates."
|
||||
elif [ "$LE_AUTO_VERSION" != "$REMOTE_VERSION" ]; then
|
||||
echo "Upgrading certbot-auto $LE_AUTO_VERSION to $REMOTE_VERSION..."
|
||||
|
||||
# Now we drop into Python so we don't have to install even more
|
||||
# dependencies (curl, etc.), for better flow control, and for the option of
|
||||
# future Windows compatibility.
|
||||
"$LE_PYTHON" "$TEMP_DIR/fetch.py" --le-auto-script "v$REMOTE_VERSION"
|
||||
|
||||
# Install new copy of letsencrypt-auto.
|
||||
# Install new copy of certbot-auto.
|
||||
# TODO: Deal with quotes in pathnames.
|
||||
echo "Replacing letsencrypt-auto..."
|
||||
echo "Replacing certbot-auto..."
|
||||
# Clone permissions with cp. chmod and chown don't have a --reference
|
||||
# option on OS X or BSD, and stat -c on Linux is stat -f on OS X and BSD:
|
||||
echo " " $SUDO cp -p "$0" "$TEMP_DIR/letsencrypt-auto.permission-clone"
|
||||
$SUDO cp -p "$0" "$TEMP_DIR/letsencrypt-auto.permission-clone"
|
||||
echo " " $SUDO cp "$TEMP_DIR/letsencrypt-auto" "$TEMP_DIR/letsencrypt-auto.permission-clone"
|
||||
$SUDO cp "$TEMP_DIR/letsencrypt-auto" "$TEMP_DIR/letsencrypt-auto.permission-clone"
|
||||
# Using mv rather than cp leaves the old file descriptor pointing to the
|
||||
# original copy so the shell can continue to read it unmolested. mv across
|
||||
# filesystems is non-atomic, doing `rm dest, cp src dest, rm src`, but the
|
||||
# cp is unlikely to fail (esp. under sudo) if the rm doesn't.
|
||||
echo " " $SUDO mv -f "$TEMP_DIR/letsencrypt-auto.permission-clone" "$0"
|
||||
$SUDO mv -f "$TEMP_DIR/letsencrypt-auto.permission-clone" "$0"
|
||||
# TODO: Clean up temp dir safely, even if it has quotes in its path.
|
||||
rm -rf "$TEMP_DIR"
|
||||
fi # A newer version is available.
|
||||
fi # Self-upgrading is allowed.
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,11 @@ BootstrapArchCommon() {
|
|||
# pacman -T exits with 127 if there are missing dependencies
|
||||
missing=$($SUDO pacman -T $deps) || true
|
||||
|
||||
if [ "$ASSUME_YES" = 1 ]; then
|
||||
noconfirm="--noconfirm"
|
||||
fi
|
||||
|
||||
if [ "$missing" ]; then
|
||||
$SUDO pacman -S --needed $missing
|
||||
$SUDO pacman -S --needed $missing $noconfirm
|
||||
fi
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,30 +34,45 @@ BootstrapDebCommon() {
|
|||
augeas_pkg="libaugeas0 augeas-lenses"
|
||||
AUGVERSION=`apt-cache show --no-all-versions libaugeas0 | grep ^Version: | cut -d" " -f2`
|
||||
|
||||
if [ "$ASSUME_YES" = 1 ]; then
|
||||
YES_FLAG="-y"
|
||||
fi
|
||||
|
||||
AddBackportRepo() {
|
||||
# ARGS:
|
||||
BACKPORT_NAME="$1"
|
||||
BACKPORT_SOURCELINE="$2"
|
||||
echo "To use the Apache Certbot plugin, augeas needs to be installed from $BACKPORT_NAME."
|
||||
if ! grep -v -e ' *#' /etc/apt/sources.list | grep -q "$BACKPORT_NAME" ; then
|
||||
# This can theoretically error if sources.list.d is empty, but in that case we don't care.
|
||||
if ! grep -v -e ' *#' /etc/apt/sources.list.d/* 2>/dev/null | grep -q "$BACKPORT_NAME"; then
|
||||
/bin/echo -n "Installing augeas from $BACKPORT_NAME in 3 seconds..."
|
||||
sleep 1s
|
||||
/bin/echo -ne "\e[0K\rInstalling augeas from $BACKPORT_NAME in 2 seconds..."
|
||||
sleep 1s
|
||||
/bin/echo -e "\e[0K\rInstalling augeas from $BACKPORT_NAME in 1 second ..."
|
||||
sleep 1s
|
||||
if echo $BACKPORT_NAME | grep -q wheezy ; then
|
||||
/bin/echo '(Backports are only installed if explicitly requested via "apt-get install -t wheezy-backports")'
|
||||
if [ "$ASSUME_YES" = 1 ]; then
|
||||
/bin/echo -n "Installing augeas from $BACKPORT_NAME in 3 seconds..."
|
||||
sleep 1s
|
||||
/bin/echo -ne "\e[0K\rInstalling augeas from $BACKPORT_NAME in 2 seconds..."
|
||||
sleep 1s
|
||||
/bin/echo -e "\e[0K\rInstalling augeas from $BACKPORT_NAME in 1 second ..."
|
||||
sleep 1s
|
||||
add_backports=1
|
||||
else
|
||||
read -p "Would you like to enable the $BACKPORT_NAME repository [Y/n]? " response
|
||||
case $response in
|
||||
[yY][eE][sS]|[yY]|"")
|
||||
add_backports=1;;
|
||||
*)
|
||||
add_backports=0;;
|
||||
esac
|
||||
fi
|
||||
if [ "$add_backports" = 1 ]; then
|
||||
$SUDO sh -c "echo $BACKPORT_SOURCELINE >> /etc/apt/sources.list.d/$BACKPORT_NAME.list"
|
||||
$SUDO apt-get update
|
||||
fi
|
||||
|
||||
$SUDO sh -c "echo $BACKPORT_SOURCELINE >> /etc/apt/sources.list.d/$BACKPORT_NAME.list"
|
||||
$SUDO apt-get update
|
||||
fi
|
||||
fi
|
||||
$SUDO apt-get install -y --no-install-recommends -t "$BACKPORT_NAME" $augeas_pkg
|
||||
augeas_pkg=
|
||||
|
||||
if [ "$add_backports" != 0 ]; then
|
||||
$SUDO apt-get install $YES_FLAG --no-install-recommends -t "$BACKPORT_NAME" $augeas_pkg
|
||||
augeas_pkg=
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -74,7 +89,7 @@ BootstrapDebCommon() {
|
|||
# XXX add a case for ubuntu PPAs
|
||||
fi
|
||||
|
||||
$SUDO apt-get install -y --no-install-recommends \
|
||||
$SUDO apt-get install $YES_FLAG --no-install-recommends \
|
||||
python \
|
||||
python-dev \
|
||||
$virtualenv \
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@ BootstrapMac() {
|
|||
|
||||
$pkgcmd augeas
|
||||
$pkgcmd dialog
|
||||
if [ "$(which python)" = "/System/Library/Frameworks/Python.framework/Versions/2.7/bin/python" ]; then
|
||||
if [ "$(which python)" = "/System/Library/Frameworks/Python.framework/Versions/2.7/bin/python" \
|
||||
-o "$(which python)" = "/usr/bin/python" ]; then
|
||||
# We want to avoid using the system Python because it requires root to use pip.
|
||||
# python.org, MacPorts or HomeBrew Python installations should all be OK.
|
||||
echo "Installing python..."
|
||||
|
|
@ -26,7 +27,8 @@ BootstrapMac() {
|
|||
# Workaround for _dlopen not finding augeas on OS X
|
||||
if [ "$pkgman" = "port" ] && ! [ -e "/usr/local/lib/libaugeas.dylib" ] && [ -e "/opt/local/lib/libaugeas.dylib" ]; then
|
||||
echo "Applying augeas workaround"
|
||||
$SUDO ln -s /opt/local/lib/libaugeas.dylib /usr/local/lib
|
||||
$SUDO mkdir -p /usr/local/lib/
|
||||
$SUDO ln -s /opt/local/lib/libaugeas.dylib /usr/local/lib/
|
||||
fi
|
||||
|
||||
if ! hash pip 2>/dev/null; then
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
BootstrapRpmCommon() {
|
||||
# Tested with:
|
||||
# - Fedora 22, 23 (x64)
|
||||
# - Fedora 20, 21, 22, 23 (x64)
|
||||
# - Centos 7 (x64: on DigitalOcean droplet)
|
||||
# - CentOS 7 Minimal install in a Hyper-V VM
|
||||
# - CentOS 6 (EPEL must be installed manually)
|
||||
|
||||
if type dnf 2>/dev/null
|
||||
then
|
||||
|
|
@ -16,46 +17,49 @@ BootstrapRpmCommon() {
|
|||
exit 1
|
||||
fi
|
||||
|
||||
pkgs="
|
||||
gcc
|
||||
dialog
|
||||
augeas-libs
|
||||
openssl
|
||||
openssl-devel
|
||||
libffi-devel
|
||||
redhat-rpm-config
|
||||
ca-certificates
|
||||
"
|
||||
|
||||
# Some distros and older versions of current distros use a "python27"
|
||||
# instead of "python" naming convention. Try both conventions.
|
||||
if ! $SUDO $tool install -y \
|
||||
python \
|
||||
python-devel \
|
||||
python-virtualenv \
|
||||
python-tools \
|
||||
python-pip
|
||||
then
|
||||
if ! $SUDO $tool install -y \
|
||||
python27 \
|
||||
python27-devel \
|
||||
python27-virtualenv \
|
||||
python27-tools \
|
||||
python27-pip
|
||||
then
|
||||
echo "Could not install Python dependencies. Aborting bootstrap!"
|
||||
exit 1
|
||||
fi
|
||||
if $SUDO $tool list python >/dev/null 2>&1; then
|
||||
pkgs="$pkgs
|
||||
python
|
||||
python-devel
|
||||
python-virtualenv
|
||||
python-tools
|
||||
python-pip
|
||||
"
|
||||
else
|
||||
pkgs="$pkgs
|
||||
python27
|
||||
python27-devel
|
||||
python27-virtualenv
|
||||
python27-tools
|
||||
python27-pip
|
||||
"
|
||||
fi
|
||||
|
||||
if ! $SUDO $tool install -y \
|
||||
gcc \
|
||||
dialog \
|
||||
augeas-libs \
|
||||
openssl \
|
||||
openssl-devel \
|
||||
libffi-devel \
|
||||
redhat-rpm-config \
|
||||
ca-certificates
|
||||
then
|
||||
echo "Could not install additional dependencies. Aborting bootstrap!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
if $SUDO $tool list installed "httpd" >/dev/null 2>&1; then
|
||||
if ! $SUDO $tool install -y mod_ssl
|
||||
then
|
||||
echo "Apache found, but mod_ssl could not be installed."
|
||||
fi
|
||||
pkgs="$pkgs
|
||||
mod_ssl
|
||||
"
|
||||
fi
|
||||
|
||||
if [ "$ASSUME_YES" = 1 ]; then
|
||||
yes_flag="-y"
|
||||
fi
|
||||
|
||||
if ! $SUDO $tool install $yes_flag $pkgs; then
|
||||
echo "Could not install OS dependencies. Aborting bootstrap!"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
|
|
|||
4
letsencrypt-auto-source/pieces/bootstrappers/smartos.sh
Normal file
4
letsencrypt-auto-source/pieces/bootstrappers/smartos.sh
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
BootstrapSmartOS() {
|
||||
pkgin update
|
||||
pkgin -y install 'gcc49' 'py27-augeas' 'py27-virtualenv'
|
||||
}
|
||||
|
|
@ -1,7 +1,12 @@
|
|||
BootstrapSuseCommon() {
|
||||
# SLE12 don't have python-virtualenv
|
||||
|
||||
$SUDO zypper -nq in -l \
|
||||
if [ "$ASSUME_YES" = 1 ]; then
|
||||
zypper_flags="-nq"
|
||||
install_flags="-l"
|
||||
fi
|
||||
|
||||
$SUDO zypper $zypper_flags in $install_flags \
|
||||
python \
|
||||
python-devel \
|
||||
python-virtualenv \
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ def latest_stable_version(get):
|
|||
"""Return the latest stable release of letsencrypt."""
|
||||
metadata = loads(get(
|
||||
environ.get('LE_AUTO_JSON_URL',
|
||||
'https://pypi.python.org/pypi/letsencrypt/json')))
|
||||
'https://pypi.python.org/pypi/certbot/json')))
|
||||
# metadata['info']['version'] actually returns the latest of any kind of
|
||||
# release release, contrary to https://wiki.python.org/moin/PyPIJSON.
|
||||
# The regex is a sufficient regex for picking out prereleases for most
|
||||
|
|
@ -87,7 +87,7 @@ def verified_new_le_auto(get, tag, temp_dir):
|
|||
"""
|
||||
le_auto_dir = environ.get(
|
||||
'LE_AUTO_DIR_TEMPLATE',
|
||||
'https://raw.githubusercontent.com/letsencrypt/letsencrypt/%s/'
|
||||
'https://raw.githubusercontent.com/certbot/certbot/%s/'
|
||||
'letsencrypt-auto-source/') % tag
|
||||
write(get(le_auto_dir + 'letsencrypt-auto'), temp_dir, 'letsencrypt-auto')
|
||||
write(get(le_auto_dir + 'letsencrypt-auto.sig'), temp_dir, 'letsencrypt-auto.sig')
|
||||
|
|
@ -103,7 +103,7 @@ def verified_new_le_auto(get, tag, temp_dir):
|
|||
stderr=dev_null)
|
||||
except CalledProcessError as exc:
|
||||
raise ExpectedError("Couldn't verify signature of downloaded "
|
||||
"letsencrypt-auto.", exc)
|
||||
"certbot-auto.", exc)
|
||||
|
||||
|
||||
def main():
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# This is the flattened list of packages letsencrypt-auto installs. To generate
|
||||
# This is the flattened list of packages certbot-auto installs. To generate
|
||||
# this, do `pip install --no-cache-dir -e acme -e . -e certbot-apache`, and
|
||||
# then use `hashin` or a more secure method to gather the hashes.
|
||||
|
||||
|
|
@ -178,12 +178,18 @@ mock==1.0.1 \
|
|||
|
||||
# THE LINES BELOW ARE EDITED BY THE RELEASE SCRIPT; ADD ALL DEPENDENCIES ABOVE.
|
||||
|
||||
acme==0.5.0 \
|
||||
--hash=sha256:ceb4127c13213f0006a564be82176b968c6b374d20d9fc78555d0658a252b275 \
|
||||
--hash=sha256:0605c63c656d33c883a05675f5db9cfb85d503f2771c885031800e0da7631abd
|
||||
letsencrypt==0.5.0 \
|
||||
--hash=sha256:f90f883e99cdbdf8142335bdbf4f74a8af143ee4b4ec60fb49c6e47418c1114c \
|
||||
--hash=sha256:e38a2b70b82be79bc195307652244a3e012ec73d897d4dbd3f80cf698496d15a
|
||||
letsencrypt-apache==0.5.0 \
|
||||
--hash=sha256:a767882164a7b09d9c12c80684a28a782135fdaf35654ef5a02c0b7b1d27ab8d \
|
||||
--hash=sha256:c20e7b9c517aa4a7d70e6bd9382da7259f00bc191b9e60d8e312e48837a00c41
|
||||
acme==0.6.0 \
|
||||
--hash=sha256:cbe4e7a340a19725a8740ed86e30abdbe18fc22c4c6022b7a8e56642d502bcc3 \
|
||||
--hash=sha256:ec4e6009dfbd629b58473eb06bbebfd9fb2a79fc8831c149e9205bc38a98ecc6
|
||||
certbot==0.6.0 \
|
||||
--hash=sha256:a893632d228864b0a751db9f3fdd93439ed34b988ea21b64fb0f0fa2ceded6a2 \
|
||||
--hash=sha256:80b0b7dc5afeec2816ef638a61e7c628d73cd72666eebf4984be426d1c2b492d
|
||||
certbot-apache==0.6.0 \
|
||||
--hash=sha256:0ab077f0913b81ed5c1b141c3a7c4c0228ef3738d8d61a93db794d9a80718d43 \
|
||||
--hash=sha256:1cfbe751209079a803758f472200816fac559f2a36fdd582d25e3ba5601423a1
|
||||
letsencrypt==0.6.0 \
|
||||
--hash=sha256:93196c7dcd57272a753e525d145c5a9987c8968c22ec954bcf83dcc9d2499a76 \
|
||||
--hash=sha256:a16d6c395f1bf5fd61a28ef83dc78f42dbecbad9d00be6236f2ad8915645c154
|
||||
letsencrypt-apache==0.6.0 \
|
||||
--hash=sha256:02fadc52a0796e53978c508beec9c53e1fc047660240832b9bde5d53ab3a1379 \
|
||||
--hash=sha256:1c5522d94d7750bdb9bfa6201d2c263e914f662c9d0079e673167233cf4364f1
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ def run_le_auto(venv_dir, base_url, **kwargs):
|
|||
d = dict(XDG_DATA_HOME=venv_dir,
|
||||
# URL to PyPI-style JSON that tell us the latest released version
|
||||
# of LE:
|
||||
LE_AUTO_JSON_URL=base_url + 'letsencrypt/json',
|
||||
LE_AUTO_JSON_URL=base_url + 'certbot/json',
|
||||
# URL to dir containing letsencrypt-auto and letsencrypt-auto.sig:
|
||||
LE_AUTO_DIR_TEMPLATE=base_url + '%s/',
|
||||
# The public key corresponding to signing.key:
|
||||
|
|
@ -231,7 +231,7 @@ class AutoTests(TestCase):
|
|||
* The OpenSSL sig mismatches.
|
||||
|
||||
For tests which get to the end, we run merely ``letsencrypt --version``.
|
||||
The functioning of the rest of the letsencrypt script is covered by other
|
||||
The functioning of the rest of the certbot script is covered by other
|
||||
test suites.
|
||||
|
||||
"""
|
||||
|
|
@ -258,7 +258,7 @@ class AutoTests(TestCase):
|
|||
with ephemeral_dir() as venv_dir:
|
||||
# This serves a PyPI page with a higher version, a GitHub-alike
|
||||
# with a corresponding le-auto script, and a matching signature.
|
||||
resources = {'letsencrypt/json': dumps({'releases': {'99.9.9': None}}),
|
||||
resources = {'certbot/json': dumps({'releases': {'99.9.9': None}}),
|
||||
'v99.9.9/letsencrypt-auto': NEW_LE_AUTO,
|
||||
'v99.9.9/letsencrypt-auto.sig': NEW_LE_AUTO_SIG}
|
||||
with serving(resources) as base_url:
|
||||
|
|
@ -277,7 +277,7 @@ class AutoTests(TestCase):
|
|||
ok_(re.match(r'letsencrypt \d+\.\d+\.\d+',
|
||||
err.strip().splitlines()[-1]))
|
||||
# Make a few assertions to test the validity of the next tests:
|
||||
self.assertIn('Upgrading letsencrypt-auto ', out)
|
||||
self.assertIn('Upgrading certbot-auto ', out)
|
||||
self.assertIn('Creating virtual environment...', out)
|
||||
|
||||
# Now we have le-auto 99.9.9 and LE 99.9.9 installed. This
|
||||
|
|
@ -286,14 +286,14 @@ class AutoTests(TestCase):
|
|||
# Test when neither phase-1 upgrade nor phase-2 upgrade is
|
||||
# needed (probably a common case):
|
||||
out, err = run_letsencrypt_auto()
|
||||
self.assertNotIn('Upgrading letsencrypt-auto ', out)
|
||||
self.assertNotIn('Upgrading certbot-auto ', out)
|
||||
self.assertNotIn('Creating virtual environment...', out)
|
||||
|
||||
# Test when a phase-1 upgrade is not needed but a phase-2
|
||||
# upgrade is:
|
||||
set_le_script_version(venv_dir, '0.0.1')
|
||||
out, err = run_letsencrypt_auto()
|
||||
self.assertNotIn('Upgrading letsencrypt-auto ', out)
|
||||
self.assertNotIn('Upgrading certbot-auto ', out)
|
||||
self.assertIn('Creating virtual environment...', out)
|
||||
|
||||
def test_openssl_failure(self):
|
||||
|
|
@ -301,8 +301,8 @@ class AutoTests(TestCase):
|
|||
with ephemeral_dir() as venv_dir:
|
||||
# Serve an unrelated hash signed with the good key (easier than
|
||||
# making a bad key, and a mismatch is a mismatch):
|
||||
resources = {'': '<a href="letsencrypt/">letsencrypt/</a>',
|
||||
'letsencrypt/json': dumps({'releases': {'99.9.9': None}}),
|
||||
resources = {'': '<a href="certbot/">certbot/</a>',
|
||||
'certbot/json': dumps({'releases': {'99.9.9': None}}),
|
||||
'v99.9.9/letsencrypt-auto': build_le_auto(version='99.9.9'),
|
||||
'v99.9.9/letsencrypt-auto.sig': signed('something else')}
|
||||
with serving(resources) as base_url:
|
||||
|
|
@ -312,16 +312,16 @@ class AutoTests(TestCase):
|
|||
except CalledProcessError as exc:
|
||||
eq_(exc.returncode, 1)
|
||||
self.assertIn("Couldn't verify signature of downloaded "
|
||||
"letsencrypt-auto.",
|
||||
"certbot-auto.",
|
||||
exc.output)
|
||||
else:
|
||||
self.fail('Signature check on letsencrypt-auto erroneously passed.')
|
||||
self.fail('Signature check on certbot-auto erroneously passed.')
|
||||
|
||||
def test_pip_failure(self):
|
||||
"""Make sure pip stops us if there is a hash mismatch."""
|
||||
with ephemeral_dir() as venv_dir:
|
||||
resources = {'': '<a href="letsencrypt/">letsencrypt/</a>',
|
||||
'letsencrypt/json': dumps({'releases': {'99.9.9': None}})}
|
||||
resources = {'': '<a href="certbot/">certbot/</a>',
|
||||
'certbot/json': dumps({'releases': {'99.9.9': None}})}
|
||||
with serving(resources) as base_url:
|
||||
# Build a le-auto script embedding a bad requirements file:
|
||||
install_le_auto(
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ here = os.path.abspath(os.path.dirname(__file__))
|
|||
readme = read_file(os.path.join(here, 'README.rst'))
|
||||
|
||||
|
||||
version = '0.6.0.dev0'
|
||||
version = '0.7.0.dev0'
|
||||
|
||||
|
||||
# This package is a simple shim around certbot-nginx
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ readme = read_file(os.path.join(here, 'README.rst'))
|
|||
install_requires = ['certbot']
|
||||
|
||||
|
||||
version = '0.6.0.dev0'
|
||||
version = '0.7.0.dev0'
|
||||
|
||||
|
||||
setup(
|
||||
|
|
|
|||
|
|
@ -307,5 +307,5 @@ texinfo_documents = [
|
|||
intersphinx_mapping = {
|
||||
'python': ('https://docs.python.org/', None),
|
||||
'acme': ('https://acme-python.readthedocs.org/en/latest/', None),
|
||||
'certbot': ('https://letsencrypt.readthedocs.org/en/latest/', None),
|
||||
'certbot': ('https://certbot.eff.org/docs/', None),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from setuptools import setup
|
|||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.6.0.dev0'
|
||||
version = '0.7.0.dev0'
|
||||
|
||||
install_requires = [
|
||||
'setuptools', # pkg_resources
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ here = os.path.abspath(os.path.dirname(__file__))
|
|||
readme = read_file(os.path.join(here, 'README.rst'))
|
||||
|
||||
|
||||
version = '0.6.0.dev0'
|
||||
version = '0.7.0.dev0'
|
||||
|
||||
|
||||
# This package is a simple shim around letshelp-certbot
|
||||
|
|
|
|||
3
setup.py
3
setup.py
|
|
@ -39,7 +39,7 @@ install_requires = [
|
|||
'ConfigArgParse>=0.9.3',
|
||||
'configobj',
|
||||
'cryptography>=0.7', # load_pem_x509_certificate
|
||||
'parsedatetime',
|
||||
'parsedatetime>=1.3', # Calendar.parseDT
|
||||
'psutil>=2.1.0', # net_connections introduced in 2.1.0
|
||||
'PyOpenSSL',
|
||||
'pyrfc3339',
|
||||
|
|
@ -71,6 +71,7 @@ dev_extras = [
|
|||
'nose',
|
||||
'nosexcover',
|
||||
'pep8',
|
||||
'pkginfo<=1.2.1',
|
||||
'pylint==1.4.2', # upstream #248
|
||||
'tox',
|
||||
'twine',
|
||||
|
|
|
|||
|
|
@ -1,40 +1,10 @@
|
|||
#!/bin/bash
|
||||
# Download and run Boulder instance for integration testing
|
||||
|
||||
# ugh, go version output is like:
|
||||
# go version go1.4.2 linux/amd64
|
||||
GOVER=`go version | cut -d" " -f3 | cut -do -f2`
|
||||
|
||||
# version comparison
|
||||
function verlte {
|
||||
#OS X doesn't support version sorting; emulate with sed
|
||||
if [ `uname` == 'Darwin' ]; then
|
||||
[ "$1" = "`echo -e \"$1\n$2\" | sed 's/\b\([0-9]\)\b/0\1/g' \
|
||||
| sort | sed 's/\b0\([0-9]\)/\1/g' | head -n1`" ]
|
||||
else
|
||||
[ "$1" = "`echo -e "$1\n$2" | sort -V | head -n1`" ]
|
||||
fi
|
||||
}
|
||||
|
||||
if ! verlte 1.5 "$GOVER" ; then
|
||||
echo "We require go version 1.5 or later; you have... $GOVER"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
set -xe
|
||||
|
||||
# `/...` avoids `no buildable Go source files` errors, for more info
|
||||
# see `go help packages`
|
||||
go get -d github.com/letsencrypt/boulder/...
|
||||
cd $GOPATH/src/github.com/letsencrypt/boulder
|
||||
# goose is needed for ./test/create_db.sh
|
||||
wget https://github.com/jsha/boulder-tools/raw/master/goose.gz && \
|
||||
mkdir $GOPATH/bin && \
|
||||
zcat goose.gz > $GOPATH/bin/goose && \
|
||||
chmod +x $GOPATH/bin/goose
|
||||
./test/create_db.sh
|
||||
go run cmd/rabbitmq-setup/main.go -server amqp://localhost
|
||||
# listenbuddy is needed for ./start.py
|
||||
go get github.com/jsha/listenbuddy
|
||||
cd -
|
||||
# Check out special branch until latest docker changes land in Boulder master.
|
||||
git clone https://github.com/letsencrypt/boulder $BOULDERPATH
|
||||
cd $BOULDERPATH
|
||||
sed -i 's/FAKE_DNS: .*/FAKE_DNS: 172.17.42.1/' docker-compose.yml
|
||||
docker-compose up -d
|
||||
|
||||
|
|
|
|||
|
|
@ -43,8 +43,8 @@ export CSR_PATH="${root}/csr.der" KEY_PATH="${root}/key.pem" \
|
|||
common auth --csr "$CSR_PATH" \
|
||||
--cert-path "${root}/csr/cert.pem" \
|
||||
--chain-path "${root}/csr/chain.pem"
|
||||
openssl x509 -in "${root}/csr/0000_cert.pem" -text
|
||||
openssl x509 -in "${root}/csr/0000_chain.pem" -text
|
||||
openssl x509 -in "${root}/csr/cert.pem" -text
|
||||
openssl x509 -in "${root}/csr/chain.pem" -text
|
||||
|
||||
common --domains le3.wtf install \
|
||||
--cert-path "${root}/csr/cert.pem" \
|
||||
|
|
|
|||
|
|
@ -2,27 +2,8 @@
|
|||
|
||||
# >>>> only tested on Ubuntu 14.04LTS <<<<
|
||||
|
||||
# non-interactive install of mariadb and other dependencies
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
sudo debconf-set-selections <<< 'mariadb-server mysql-server/root_password password PASS'
|
||||
sudo debconf-set-selections <<< 'mariadb-server mysql-server/root_password_again password PASS'
|
||||
apt-get -y --no-upgrade install git make libltdl3-dev mariadb-server rabbitmq-server
|
||||
sudo mysql -uroot -pPASS -e "SET PASSWORD = PASSWORD(\'\');"
|
||||
|
||||
# install go
|
||||
wget https://storage.googleapis.com/golang/go1.5.1.linux-amd64.tar.gz
|
||||
tar xzvf go1.5.1.linux-amd64.tar.gz
|
||||
mkdir gocode
|
||||
echo "export GOROOT=/home/ubuntu/go \n\
|
||||
export GOPATH=/home/ubuntu/gocode\n\
|
||||
export PATH=/home/ubuntu/go/bin:/home/ubuntu/gocode/bin:$PATH" >> .bashrc
|
||||
|
||||
# install boulder and its go dependencies
|
||||
go get -d github.com/letsencrypt/boulder/...
|
||||
cd $GOPATH/src/github.com/letsencrypt/boulder
|
||||
wget https://github.com/jsha/boulder-tools/raw/master/goose.gz
|
||||
mkdir $GOPATH/bin
|
||||
zcat goose.gz > $GOPATH/bin/goose
|
||||
chmod +x $GOPATH/bin/goose
|
||||
./test/create_db.sh
|
||||
go get github.com/jsha/listenbuddy
|
||||
# Check out special branch until latest docker changes land in Boulder master.
|
||||
git clone https://github.com/letsencrypt/boulder $BOULDERPATH
|
||||
cd $BOULDERPATH
|
||||
sed -i 's/FAKE_DNS: .*/FAKE_DNS: 172.17.42.1/' docker-compose.yml
|
||||
docker-compose up -d
|
||||
|
|
|
|||
|
|
@ -6,14 +6,9 @@ set -o errexit
|
|||
|
||||
source .tox/$TOXENV/bin/activate
|
||||
|
||||
export LETSENCRYPT_PATH=`pwd`
|
||||
until curl http://boulder:4000/directory 2>/dev/null; do
|
||||
echo waiting for boulder
|
||||
sleep 1
|
||||
done
|
||||
|
||||
cd $GOPATH/src/github.com/letsencrypt/boulder/
|
||||
|
||||
# boulder's integration-test.py has code that knows to start and wait for the
|
||||
# boulder processes to start reliably and then will run the certbot
|
||||
# boulder-interation.sh on its own. The --letsencrypt flag says to run only the
|
||||
# certbot tests (instead of any other client tests it might run). We're
|
||||
# going to want to define a more robust interaction point between the boulder
|
||||
# and certbot tests, but that will be better built off of this.
|
||||
python test/integration-test.py --letsencrypt
|
||||
./tests/boulder-integration.sh
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ virtualenv --no-site-packages $VENV_NAME $VENV_ARGS
|
|||
# Separately install setuptools and pip to make sure following
|
||||
# invocations use latest
|
||||
pip install -U setuptools
|
||||
pip install -U pip
|
||||
# --force-reinstall used to fix broken pip installation on some systems
|
||||
pip install --force-reinstall -U pip
|
||||
pip install "$@"
|
||||
|
||||
set +x
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ export GPG_TTY=$(tty)
|
|||
PORT=${PORT:-1234}
|
||||
|
||||
# subpackages to be released
|
||||
SUBPKGS=${SUBPKGS:-"acme certbot-apache certbot-nginx letshelp-certbot"}
|
||||
SUBPKGS=${SUBPKGS:-"acme certbot-apache certbot-nginx letshelp-certbot letsencrypt letsencrypt-apache letsencrypt-nginx letshelp-letsencrypt"}
|
||||
subpkgs_modules="$(echo $SUBPKGS | sed s/-/_/g)"
|
||||
# certbot_compatibility_test is not packaged because:
|
||||
# - it is not meant to be used by anyone else than Certbot devs
|
||||
|
|
@ -145,6 +145,9 @@ pip install \
|
|||
kill $!
|
||||
cd ~-
|
||||
|
||||
# get a snapshot of the CLI help for the docs
|
||||
certbot --help all > docs/cli-help.txt
|
||||
|
||||
# freeze before installing anything else, so that we know end-user KGS
|
||||
# make sure "twine upload" doesn't catch "kgs"
|
||||
if [ -d ../kgs ] ; then
|
||||
|
|
@ -162,12 +165,12 @@ done
|
|||
deactivate
|
||||
|
||||
# pin pip hashes of the things we just built
|
||||
for pkg in acme certbot certbot-apache ; do
|
||||
for pkg in acme certbot certbot-apache letsencrypt letsencrypt-apache ; do
|
||||
echo $pkg==$version \\
|
||||
pip hash dist."$version/$pkg"/*.{whl,gz} | grep "^--hash" | python2 -c 'from sys import stdin; input = stdin.read(); print " ", input.replace("\n--hash", " \\\n --hash"),'
|
||||
done > /tmp/hashes.$$
|
||||
|
||||
if ! wc -l /tmp/hashes.$$ | grep -qE "^\s*9 " ; then
|
||||
if ! wc -l /tmp/hashes.$$ | grep -qE "^\s*15 " ; then
|
||||
echo Unexpected pip hash output
|
||||
exit 1
|
||||
fi
|
||||
|
|
@ -187,10 +190,17 @@ while ! openssl dgst -sha256 -verify $RELEASE_OPENSSL_PUBKEY -signature \
|
|||
read -p "Please correctly sign letsencrypt-auto with offline-signrequest.sh"
|
||||
done
|
||||
|
||||
# This signature is not quite as strong, but easier for people to verify out of band
|
||||
gpg -u "$RELEASE_GPG_KEY" --detach-sign --armor --sign letsencrypt-auto-source/letsencrypt-auto
|
||||
# We can't rename the openssl letsencrypt-auto.sig for compatibility reasons,
|
||||
# but we can use the right name for cerbot-auto.asc from day one
|
||||
mv letsencrypt-auto-source/letsencrypt-auto.asc letsencrypt-auto-source/certbot-auto.asc
|
||||
|
||||
# copy leauto to the root, overwriting the previous release version
|
||||
cp -p letsencrypt-auto-source/letsencrypt-auto certbot-auto
|
||||
cp -p letsencrypt-auto-source/letsencrypt-auto letsencrypt-auto
|
||||
|
||||
git add letsencrypt-auto letsencrypt-auto-source
|
||||
git add certbot-auto letsencrypt-auto letsencrypt-auto-source docs/cli-help.txt
|
||||
git diff --cached
|
||||
git commit --gpg-sign="$RELEASE_GPG_KEY" -m "Release $version"
|
||||
git tag --local-user "$RELEASE_GPG_KEY" --sign --message "Release $version" "$tag"
|
||||
|
|
|
|||
Loading…
Reference in a new issue