mirror of
https://github.com/certbot/certbot.git
synced 2026-05-28 04:34:11 -04:00
Merge branch 'master' into add_dns01_challenge
This commit is contained in:
commit
6196cf0aa7
481 changed files with 10037 additions and 8331 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -6,7 +6,10 @@ dist*/
|
|||
/venv*/
|
||||
/kgs/
|
||||
/.tox/
|
||||
/releases/
|
||||
letsencrypt.log
|
||||
certbot.log
|
||||
letsencrypt-auto-source/letsencrypt-auto.sig.lzma.base64
|
||||
|
||||
# coverage
|
||||
.coverage
|
||||
|
|
@ -26,3 +29,4 @@ letsencrypt.log
|
|||
# letstest
|
||||
tests/letstest/letest-*/
|
||||
tests/letstest/*.pem
|
||||
tests/letstest/venv/
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ load-plugins=linter_plugin
|
|||
# --enable=similarities". If you want to run only the classes checker, but have
|
||||
# no Warning level messages displayed, use"--disable=all --enable=classes
|
||||
# --disable=W"
|
||||
disable=fixme,locally-disabled,abstract-class-not-used,abstract-class-little-used,bad-continuation,too-few-public-methods,no-self-use,invalid-name,too-many-instance-attributes
|
||||
disable=fixme,locally-disabled,abstract-class-not-used,abstract-class-little-used,bad-continuation,too-few-public-methods,no-self-use,invalid-name,too-many-instance-attributes,cyclic-import
|
||||
# abstract-class-not-used cannot be disabled locally (at least in
|
||||
# pylint 1.4.1), same for abstract-class-little-used
|
||||
|
||||
|
|
|
|||
50
.travis.yml
50
.travis.yml
|
|
@ -23,35 +23,52 @@ env:
|
|||
global:
|
||||
- GOPATH=/tmp/go
|
||||
- PATH=$GOPATH/bin:$PATH
|
||||
- GO15VENDOREXPERIMENT=1 # Fixes problems with vendor directories
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- python: "2.6"
|
||||
env: TOXENV=py26 BOULDER_INTEGRATION=1
|
||||
sudo: true
|
||||
after_failure:
|
||||
- sudo cat /var/log/mysql/error.log
|
||||
- ps aux | grep mysql
|
||||
- python: "2.6"
|
||||
env: TOXENV=py26-oldest BOULDER_INTEGRATION=1
|
||||
# Disabled for now due to requiring sudo -> causing more boulder integration
|
||||
# DNS timeouts :(
|
||||
# - python: "2.7"
|
||||
# env: TOXENV=apacheconftest
|
||||
sudo: true
|
||||
after_failure:
|
||||
- sudo cat /var/log/mysql/error.log
|
||||
- ps aux | grep mysql
|
||||
- python: "2.7"
|
||||
env: TOXENV=apacheconftest
|
||||
sudo: required
|
||||
- python: "2.7"
|
||||
env: TOXENV=py27 BOULDER_INTEGRATION=1
|
||||
sudo: true
|
||||
after_failure:
|
||||
- sudo cat /var/log/mysql/error.log
|
||||
- ps aux | grep mysql
|
||||
- python: "2.7"
|
||||
env: TOXENV=py27-oldest BOULDER_INTEGRATION=1
|
||||
- python: "2.7"
|
||||
env: TOXENV=cover
|
||||
sudo: true
|
||||
after_failure:
|
||||
- sudo cat /var/log/mysql/error.log
|
||||
- ps aux | grep mysql
|
||||
- python: "2.7"
|
||||
env: TOXENV=lint
|
||||
- sudo: required
|
||||
env: TOXENV=le_auto
|
||||
services: docker
|
||||
before_install:
|
||||
addons:
|
||||
- python: "2.7"
|
||||
env: TOXENV=cover
|
||||
- python: "3.3"
|
||||
env: TOXENV=py33
|
||||
- python: "3.4"
|
||||
env: TOXENV=py34
|
||||
- python: "3.5"
|
||||
env: TOXENV=py35
|
||||
- sudo: required
|
||||
env: TOXENV=le_auto
|
||||
services: docker
|
||||
before_install:
|
||||
|
||||
# Only build pushes to the master branch, PRs, and branches beginning with
|
||||
# `test-`. This reduces the number of simultaneous Travis runs, which speeds
|
||||
|
|
@ -65,9 +82,17 @@ branches:
|
|||
sudo: false
|
||||
|
||||
addons:
|
||||
# make sure simplehttp simple verification works (custom /etc/hosts)
|
||||
# Custom /etc/hosts required for simple verification of http-01
|
||||
# and tls-sni-01, and for certbot_test_nginx
|
||||
hosts:
|
||||
- le.wtf
|
||||
- le1.wtf
|
||||
- le2.wtf
|
||||
- le3.wtf
|
||||
- nginx.wtf
|
||||
- boulder
|
||||
- boulder-mysql
|
||||
- boulder-rabbitmq
|
||||
mariadb: "10.0"
|
||||
apt:
|
||||
sources:
|
||||
|
|
@ -81,13 +106,12 @@ addons:
|
|||
- libssl-dev
|
||||
- libffi-dev
|
||||
- ca-certificates
|
||||
# For letsencrypt-nginx integration testing
|
||||
# For certbot-nginx integration testing
|
||||
- nginx-light
|
||||
- openssl
|
||||
# For Boulder integration testing
|
||||
- rsyslog
|
||||
# for apacheconftest
|
||||
#- realpath
|
||||
#- apache2
|
||||
#- libapache2-mod-wsgi
|
||||
#- libapache2-mod-macro
|
||||
|
|
|
|||
45
Dockerfile
45
Dockerfile
|
|
@ -10,11 +10,11 @@ MAINTAINER William Budington <bill@eff.org>
|
|||
EXPOSE 443
|
||||
|
||||
# TODO: make sure --config-dir and --work-dir cannot be changed
|
||||
# through the CLI (letsencrypt-docker wrapper that uses standalone
|
||||
# through the CLI (certbot-docker wrapper that uses standalone
|
||||
# authenticator and text mode only?)
|
||||
VOLUME /etc/letsencrypt /var/lib/letsencrypt
|
||||
|
||||
WORKDIR /opt/letsencrypt
|
||||
WORKDIR /opt/certbot
|
||||
|
||||
# no need to mkdir anything:
|
||||
# https://docs.docker.com/reference/builder/#copy
|
||||
|
|
@ -22,8 +22,8 @@ WORKDIR /opt/letsencrypt
|
|||
# directories in its path.
|
||||
|
||||
|
||||
COPY letsencrypt-auto-source/letsencrypt-auto /opt/letsencrypt/src/letsencrypt-auto-source/letsencrypt-auto
|
||||
RUN /opt/letsencrypt/src/letsencrypt-auto-source/letsencrypt-auto --os-packages-only && \
|
||||
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 && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/* \
|
||||
/tmp/* \
|
||||
|
|
@ -33,34 +33,37 @@ RUN /opt/letsencrypt/src/letsencrypt-auto-source/letsencrypt-auto --os-packages-
|
|||
# Dockerfile we make sure we cache as much as possible
|
||||
|
||||
|
||||
COPY setup.py README.rst CHANGES.rst MANIFEST.in /opt/letsencrypt/src/
|
||||
COPY setup.py README.rst CHANGES.rst MANIFEST.in letsencrypt-auto-source/pieces/pipstrap.py /opt/certbot/src/
|
||||
|
||||
# all above files are necessary for setup.py, however, package source
|
||||
# code directory has to be copied separately to a subdirectory...
|
||||
# all above files are necessary for setup.py and venv setup, however,
|
||||
# package source code directory has to be copied separately to a
|
||||
# subdirectory...
|
||||
# https://docs.docker.com/reference/builder/#copy: "If <src> is a
|
||||
# directory, the entire contents of the directory are copied,
|
||||
# including filesystem metadata. Note: The directory itself is not
|
||||
# copied, just its contents." Order again matters, three files are far
|
||||
# more likely to be cached than the whole project directory
|
||||
|
||||
COPY letsencrypt /opt/letsencrypt/src/letsencrypt/
|
||||
COPY acme /opt/letsencrypt/src/acme/
|
||||
COPY letsencrypt-apache /opt/letsencrypt/src/letsencrypt-apache/
|
||||
COPY letsencrypt-nginx /opt/letsencrypt/src/letsencrypt-nginx/
|
||||
COPY certbot /opt/certbot/src/certbot/
|
||||
COPY acme /opt/certbot/src/acme/
|
||||
COPY certbot-apache /opt/certbot/src/certbot-apache/
|
||||
COPY certbot-nginx /opt/certbot/src/certbot-nginx/
|
||||
|
||||
|
||||
RUN virtualenv --no-site-packages -p python2 /opt/letsencrypt/venv && \
|
||||
/opt/letsencrypt/venv/bin/pip install \
|
||||
-e /opt/letsencrypt/src/acme \
|
||||
-e /opt/letsencrypt/src \
|
||||
-e /opt/letsencrypt/src/letsencrypt-apache \
|
||||
-e /opt/letsencrypt/src/letsencrypt-nginx
|
||||
RUN virtualenv --no-site-packages -p python2 /opt/certbot/venv
|
||||
|
||||
# PATH is set now so pipstrap upgrades the correct (v)env
|
||||
ENV PATH /opt/certbot/venv/bin:$PATH
|
||||
RUN /opt/certbot/venv/bin/python /opt/certbot/src/pipstrap.py && \
|
||||
/opt/certbot/venv/bin/pip install \
|
||||
-e /opt/certbot/src/acme \
|
||||
-e /opt/certbot/src \
|
||||
-e /opt/certbot/src/certbot-apache \
|
||||
-e /opt/certbot/src/certbot-nginx
|
||||
|
||||
# install in editable mode (-e) to save space: it's not possible to
|
||||
# "rm -rf /opt/letsencrypt/src" (it's stays in the underlaying image);
|
||||
# "rm -rf /opt/certbot/src" (it's stays in the underlaying image);
|
||||
# this might also help in debugging: you can "docker run --entrypoint
|
||||
# bash" and investigate, apply patches, etc.
|
||||
|
||||
ENV PATH /opt/letsencrypt/venv/bin:$PATH
|
||||
|
||||
ENTRYPOINT [ "letsencrypt" ]
|
||||
ENTRYPOINT [ "certbot" ]
|
||||
|
|
|
|||
|
|
@ -9,11 +9,11 @@ MAINTAINER Yan <yan@eff.org>
|
|||
EXPOSE 443
|
||||
|
||||
# TODO: make sure --config-dir and --work-dir cannot be changed
|
||||
# through the CLI (letsencrypt-docker wrapper that uses standalone
|
||||
# through the CLI (certbot-docker wrapper that uses standalone
|
||||
# authenticator and text mode only?)
|
||||
VOLUME /etc/letsencrypt /var/lib/letsencrypt
|
||||
|
||||
WORKDIR /opt/letsencrypt
|
||||
WORKDIR /opt/certbot
|
||||
|
||||
# no need to mkdir anything:
|
||||
# https://docs.docker.com/reference/builder/#copy
|
||||
|
|
@ -22,8 +22,8 @@ WORKDIR /opt/letsencrypt
|
|||
|
||||
# TODO: Install non-default Python versions for tox.
|
||||
# TODO: Install Apache/Nginx for plugin development.
|
||||
COPY letsencrypt-auto-source/letsencrypt-auto /opt/letsencrypt/src/letsencrypt-auto-source/letsencrypt-auto
|
||||
RUN /opt/letsencrypt/src/letsencrypt-auto-source/letsencrypt-auto --os-packages-only && \
|
||||
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 && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/* \
|
||||
/tmp/* \
|
||||
|
|
@ -32,7 +32,7 @@ RUN /opt/letsencrypt/src/letsencrypt-auto-source/letsencrypt-auto --os-packages-
|
|||
# the above is not likely to change, so by putting it further up the
|
||||
# Dockerfile we make sure we cache as much as possible
|
||||
|
||||
COPY setup.py README.rst CHANGES.rst MANIFEST.in linter_plugin.py tox.cover.sh tox.ini pep8.travis.sh .pep8 .pylintrc /opt/letsencrypt/src/
|
||||
COPY setup.py README.rst CHANGES.rst MANIFEST.in linter_plugin.py tox.cover.sh tox.ini pep8.travis.sh .pep8 .pylintrc /opt/certbot/src/
|
||||
|
||||
# all above files are necessary for setup.py, however, package source
|
||||
# code directory has to be copied separately to a subdirectory...
|
||||
|
|
@ -42,27 +42,27 @@ COPY setup.py README.rst CHANGES.rst MANIFEST.in linter_plugin.py tox.cover.sh t
|
|||
# copied, just its contents." Order again matters, three files are far
|
||||
# more likely to be cached than the whole project directory
|
||||
|
||||
COPY letsencrypt /opt/letsencrypt/src/letsencrypt/
|
||||
COPY acme /opt/letsencrypt/src/acme/
|
||||
COPY letsencrypt-apache /opt/letsencrypt/src/letsencrypt-apache/
|
||||
COPY letsencrypt-nginx /opt/letsencrypt/src/letsencrypt-nginx/
|
||||
COPY letshelp-letsencrypt /opt/letsencrypt/src/letshelp-letsencrypt/
|
||||
COPY letsencrypt-compatibility-test /opt/letsencrypt/src/letsencrypt-compatibility-test/
|
||||
COPY tests /opt/letsencrypt/src/tests/
|
||||
COPY certbot /opt/certbot/src/certbot/
|
||||
COPY acme /opt/certbot/src/acme/
|
||||
COPY certbot-apache /opt/certbot/src/certbot-apache/
|
||||
COPY certbot-nginx /opt/certbot/src/certbot-nginx/
|
||||
COPY letshelp-certbot /opt/certbot/src/letshelp-certbot/
|
||||
COPY certbot-compatibility-test /opt/certbot/src/certbot-compatibility-test/
|
||||
COPY tests /opt/certbot/src/tests/
|
||||
|
||||
RUN virtualenv --no-site-packages -p python2 /opt/letsencrypt/venv && \
|
||||
/opt/letsencrypt/venv/bin/pip install \
|
||||
-e /opt/letsencrypt/src/acme \
|
||||
-e /opt/letsencrypt/src \
|
||||
-e /opt/letsencrypt/src/letsencrypt-apache \
|
||||
-e /opt/letsencrypt/src/letsencrypt-nginx \
|
||||
-e /opt/letsencrypt/src/letshelp-letsencrypt \
|
||||
-e /opt/letsencrypt/src/letsencrypt-compatibility-test \
|
||||
-e /opt/letsencrypt/src[dev,docs]
|
||||
RUN virtualenv --no-site-packages -p python2 /opt/certbot/venv && \
|
||||
/opt/certbot/venv/bin/pip install \
|
||||
-e /opt/certbot/src/acme \
|
||||
-e /opt/certbot/src \
|
||||
-e /opt/certbot/src/certbot-apache \
|
||||
-e /opt/certbot/src/certbot-nginx \
|
||||
-e /opt/certbot/src/letshelp-certbot \
|
||||
-e /opt/certbot/src/certbot-compatibility-test \
|
||||
-e /opt/certbot/src[dev,docs]
|
||||
|
||||
# install in editable mode (-e) to save space: it's not possible to
|
||||
# "rm -rf /opt/letsencrypt/src" (it's stays in the underlaying image);
|
||||
# "rm -rf /opt/certbot/src" (it's stays in the underlaying image);
|
||||
# this might also help in debugging: you can "docker run --entrypoint
|
||||
# bash" and investigate, apply patches, etc.
|
||||
|
||||
ENV PATH /opt/letsencrypt/venv/bin:$PATH
|
||||
ENV PATH /opt/certbot/venv/bin:$PATH
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
Let's Encrypt Python Client
|
||||
Certbot ACME Client
|
||||
Copyright (c) Electronic Frontier Foundation and others
|
||||
Licensed Apache Version 2.0
|
||||
|
||||
|
|
|
|||
|
|
@ -5,4 +5,4 @@ include LICENSE.txt
|
|||
include linter_plugin.py
|
||||
recursive-include docs *
|
||||
recursive-include examples *
|
||||
recursive-include letsencrypt/tests/testdata *
|
||||
recursive-include certbot/tests/testdata *
|
||||
|
|
|
|||
18
README.rst
18
README.rst
|
|
@ -18,16 +18,17 @@ The Let's Encrypt Client 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.
|
||||
configuring webservers to use them. This client runs on Unix-based operating
|
||||
systems.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
If ``letsencrypt`` is packaged for your 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 ``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::
|
||||
|
||||
user@webserver:~$ git clone https://github.com/letsencrypt/letsencrypt
|
||||
user@webserver:~$ cd letsencrypt
|
||||
|
|
@ -90,6 +91,11 @@ IRC Channel: #letsencrypt on `Freenode`_
|
|||
|
||||
Community: https://community.letsencrypt.org
|
||||
|
||||
ACME spec: http://ietf-wg-acme.github.io/acme/
|
||||
|
||||
ACME working area in github: https://github.com/ietf-wg-acme/acme
|
||||
|
||||
|
||||
Mailing list: `client-dev`_ (to subscribe without a Google account, send an
|
||||
email to client-dev+subscribe@letsencrypt.org)
|
||||
|
||||
|
|
|
|||
9
Vagrantfile
vendored
9
Vagrantfile
vendored
|
|
@ -5,10 +5,19 @@
|
|||
VAGRANTFILE_API_VERSION = "2"
|
||||
|
||||
# Setup instructions from docs/contributing.rst
|
||||
# Script installs dependencies for tox and boulder integration
|
||||
$ubuntu_setup_script = <<SETUP_SCRIPT
|
||||
cd /vagrant
|
||||
./letsencrypt-auto-source/letsencrypt-auto --os-packages-only
|
||||
./tools/venv.sh
|
||||
wget https://storage.googleapis.com/golang/go1.5.3.linux-amd64.tar.gz -P /tmp/
|
||||
sudo tar -C /usr/local -xzf /tmp/go1.5.3.linux-amd64.tar.gz
|
||||
if ! grep -Fxq "export GOROOT=/usr/local/go" /home/vagrant/.profile ; then echo "export GOROOT=/usr/local/go" >> /home/vagrant/.profile; fi
|
||||
if ! grep -Fxq "export PATH=\\$GOROOT/bin:\\$PATH" /home/vagrant/.profile ; then echo "export PATH=\\$GOROOT/bin:\\$PATH" >> /home/vagrant/.profile; fi
|
||||
if ! grep -Fxq "export GOPATH=\\$HOME/go" /home/vagrant/.profile ; then echo "export GOPATH=\\$HOME/go" >> /home/vagrant/.profile; fi
|
||||
if ! grep -Fxq "cd /vagrant/; ./tests/boulder-start.sh &" /etc/rc.local ; then sed -i -e '$i \cd /vagrant/; ./tests/boulder-start.sh &\n' /etc/rc.local; fi
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
sudo -E apt-get -q -y install git make libltdl-dev mariadb-server rabbitmq-server nginx-light
|
||||
SETUP_SCRIPT
|
||||
|
||||
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||
|
|
|
|||
4
acme/.pep8
Normal file
4
acme/.pep8
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
[pep8]
|
||||
# E265 block comment should start with '# '
|
||||
# E501 line too long (X > 79 characters)
|
||||
ignore = E265,E501
|
||||
|
|
@ -13,7 +13,6 @@ from acme import errors
|
|||
from acme import crypto_util
|
||||
from acme import fields
|
||||
from acme import jose
|
||||
from acme import other
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -35,14 +34,6 @@ class Challenge(jose.TypedJSONObjectWithFields):
|
|||
return UnrecognizedChallenge.from_json(jobj)
|
||||
|
||||
|
||||
class ContinuityChallenge(Challenge): # pylint: disable=abstract-method
|
||||
"""Client validation challenges."""
|
||||
|
||||
|
||||
class DVChallenge(Challenge): # pylint: disable=abstract-method
|
||||
"""Domain validation challenges."""
|
||||
|
||||
|
||||
class ChallengeResponse(jose.TypedJSONObjectWithFields):
|
||||
# _fields_to_partial_json | pylint: disable=abstract-method
|
||||
"""ACME challenge response."""
|
||||
|
|
@ -77,8 +68,8 @@ class UnrecognizedChallenge(Challenge):
|
|||
return cls(jobj)
|
||||
|
||||
|
||||
class _TokenDVChallenge(DVChallenge):
|
||||
"""DV Challenge with token.
|
||||
class _TokenChallenge(Challenge):
|
||||
"""Challenge with token.
|
||||
|
||||
:ivar bytes token:
|
||||
|
||||
|
|
@ -148,7 +139,7 @@ class KeyAuthorizationChallengeResponse(ChallengeResponse):
|
|||
return True
|
||||
|
||||
|
||||
class KeyAuthorizationChallenge(_TokenDVChallenge):
|
||||
class KeyAuthorizationChallenge(_TokenChallenge):
|
||||
# pylint: disable=abstract-class-little-used,too-many-ancestors
|
||||
"""Challenge based on Key Authorization.
|
||||
|
||||
|
|
@ -527,108 +518,8 @@ class TLSSNI01(KeyAuthorizationChallenge):
|
|||
return self.response(account_key).gen_cert(key=kwargs.get('cert_key'))
|
||||
|
||||
|
||||
@Challenge.register
|
||||
class RecoveryContact(ContinuityChallenge):
|
||||
"""ACME "recoveryContact" challenge.
|
||||
|
||||
:ivar unicode activation_url:
|
||||
:ivar unicode success_url:
|
||||
:ivar unicode contact:
|
||||
|
||||
"""
|
||||
typ = "recoveryContact"
|
||||
|
||||
activation_url = jose.Field("activationURL", omitempty=True)
|
||||
success_url = jose.Field("successURL", omitempty=True)
|
||||
contact = jose.Field("contact", omitempty=True)
|
||||
|
||||
|
||||
@ChallengeResponse.register
|
||||
class RecoveryContactResponse(ChallengeResponse):
|
||||
"""ACME "recoveryContact" challenge response.
|
||||
|
||||
:ivar unicode token:
|
||||
|
||||
"""
|
||||
typ = "recoveryContact"
|
||||
token = jose.Field("token", omitempty=True)
|
||||
|
||||
|
||||
@Challenge.register
|
||||
class ProofOfPossession(ContinuityChallenge):
|
||||
"""ACME "proofOfPossession" challenge.
|
||||
|
||||
:ivar .JWAAlgorithm alg:
|
||||
:ivar bytes nonce: Random data, **not** base64-encoded.
|
||||
:ivar hints: Various clues for the client (:class:`Hints`).
|
||||
|
||||
"""
|
||||
typ = "proofOfPossession"
|
||||
|
||||
NONCE_SIZE = 16
|
||||
|
||||
class Hints(jose.JSONObjectWithFields):
|
||||
"""Hints for "proofOfPossession" challenge.
|
||||
|
||||
:ivar JWK jwk: JSON Web Key
|
||||
:ivar tuple cert_fingerprints: `tuple` of `unicode`
|
||||
:ivar tuple certs: Sequence of :class:`acme.jose.ComparableX509`
|
||||
certificates.
|
||||
:ivar tuple subject_key_identifiers: `tuple` of `unicode`
|
||||
:ivar tuple issuers: `tuple` of `unicode`
|
||||
:ivar tuple authorized_for: `tuple` of `unicode`
|
||||
|
||||
"""
|
||||
jwk = jose.Field("jwk", decoder=jose.JWK.from_json)
|
||||
cert_fingerprints = jose.Field(
|
||||
"certFingerprints", omitempty=True, default=())
|
||||
certs = jose.Field("certs", omitempty=True, default=())
|
||||
subject_key_identifiers = jose.Field(
|
||||
"subjectKeyIdentifiers", omitempty=True, default=())
|
||||
serial_numbers = jose.Field("serialNumbers", omitempty=True, default=())
|
||||
issuers = jose.Field("issuers", omitempty=True, default=())
|
||||
authorized_for = jose.Field("authorizedFor", omitempty=True, default=())
|
||||
|
||||
@certs.encoder
|
||||
def certs(value): # pylint: disable=missing-docstring,no-self-argument
|
||||
return tuple(jose.encode_cert(cert) for cert in value)
|
||||
|
||||
@certs.decoder
|
||||
def certs(value): # pylint: disable=missing-docstring,no-self-argument
|
||||
return tuple(jose.decode_cert(cert) for cert in value)
|
||||
|
||||
alg = jose.Field("alg", decoder=jose.JWASignature.from_json)
|
||||
nonce = jose.Field(
|
||||
"nonce", encoder=jose.encode_b64jose, decoder=functools.partial(
|
||||
jose.decode_b64jose, size=NONCE_SIZE))
|
||||
hints = jose.Field("hints", decoder=Hints.from_json)
|
||||
|
||||
|
||||
@ChallengeResponse.register
|
||||
class ProofOfPossessionResponse(ChallengeResponse):
|
||||
"""ACME "proofOfPossession" challenge response.
|
||||
|
||||
:ivar bytes nonce: Random data, **not** base64-encoded.
|
||||
:ivar acme.other.Signature signature: Sugnature of this message.
|
||||
|
||||
"""
|
||||
typ = "proofOfPossession"
|
||||
|
||||
NONCE_SIZE = ProofOfPossession.NONCE_SIZE
|
||||
|
||||
nonce = jose.Field(
|
||||
"nonce", encoder=jose.encode_b64jose, decoder=functools.partial(
|
||||
jose.decode_b64jose, size=NONCE_SIZE))
|
||||
signature = jose.Field("signature", decoder=other.Signature.from_json)
|
||||
|
||||
def verify(self):
|
||||
"""Verify the challenge."""
|
||||
# self.signature is not Field | pylint: disable=no-member
|
||||
return self.signature.verify(self.nonce)
|
||||
|
||||
|
||||
@Challenge.register # pylint: disable=too-many-ancestors
|
||||
class DNS(_TokenDVChallenge):
|
||||
class DNS(_TokenChallenge):
|
||||
"""ACME "dns" challenge."""
|
||||
typ = "dns"
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ from six.moves.urllib import parse as urllib_parse # pylint: disable=import-err
|
|||
|
||||
from acme import errors
|
||||
from acme import jose
|
||||
from acme import other
|
||||
from acme import test_util
|
||||
|
||||
|
||||
|
|
@ -411,233 +410,6 @@ class TLSSNI01Test(unittest.TestCase):
|
|||
mock_gen_cert.assert_called_once_with(key=mock.sentinel.cert_key)
|
||||
|
||||
|
||||
class RecoveryContactTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
from acme.challenges import RecoveryContact
|
||||
self.msg = RecoveryContact(
|
||||
activation_url='https://example.ca/sendrecovery/a5bd99383fb0',
|
||||
success_url='https://example.ca/confirmrecovery/bb1b9928932',
|
||||
contact='c********n@example.com')
|
||||
self.jmsg = {
|
||||
'type': 'recoveryContact',
|
||||
'activationURL': 'https://example.ca/sendrecovery/a5bd99383fb0',
|
||||
'successURL': 'https://example.ca/confirmrecovery/bb1b9928932',
|
||||
'contact': 'c********n@example.com',
|
||||
}
|
||||
|
||||
def test_to_partial_json(self):
|
||||
self.assertEqual(self.jmsg, self.msg.to_partial_json())
|
||||
|
||||
def test_from_json(self):
|
||||
from acme.challenges import RecoveryContact
|
||||
self.assertEqual(self.msg, RecoveryContact.from_json(self.jmsg))
|
||||
|
||||
def test_from_json_hashable(self):
|
||||
from acme.challenges import RecoveryContact
|
||||
hash(RecoveryContact.from_json(self.jmsg))
|
||||
|
||||
def test_json_without_optionals(self):
|
||||
del self.jmsg['activationURL']
|
||||
del self.jmsg['successURL']
|
||||
del self.jmsg['contact']
|
||||
|
||||
from acme.challenges import RecoveryContact
|
||||
msg = RecoveryContact.from_json(self.jmsg)
|
||||
|
||||
self.assertTrue(msg.activation_url is None)
|
||||
self.assertTrue(msg.success_url is None)
|
||||
self.assertTrue(msg.contact is None)
|
||||
self.assertEqual(self.jmsg, msg.to_partial_json())
|
||||
|
||||
|
||||
class RecoveryContactResponseTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
from acme.challenges import RecoveryContactResponse
|
||||
self.msg = RecoveryContactResponse(token='23029d88d9e123e')
|
||||
self.jmsg = {
|
||||
'resource': 'challenge',
|
||||
'type': 'recoveryContact',
|
||||
'token': '23029d88d9e123e',
|
||||
}
|
||||
|
||||
def test_to_partial_json(self):
|
||||
self.assertEqual(self.jmsg, self.msg.to_partial_json())
|
||||
|
||||
def test_from_json(self):
|
||||
from acme.challenges import RecoveryContactResponse
|
||||
self.assertEqual(
|
||||
self.msg, RecoveryContactResponse.from_json(self.jmsg))
|
||||
|
||||
def test_from_json_hashable(self):
|
||||
from acme.challenges import RecoveryContactResponse
|
||||
hash(RecoveryContactResponse.from_json(self.jmsg))
|
||||
|
||||
def test_json_without_optionals(self):
|
||||
del self.jmsg['token']
|
||||
|
||||
from acme.challenges import RecoveryContactResponse
|
||||
msg = RecoveryContactResponse.from_json(self.jmsg)
|
||||
|
||||
self.assertTrue(msg.token is None)
|
||||
self.assertEqual(self.jmsg, msg.to_partial_json())
|
||||
|
||||
|
||||
class ProofOfPossessionHintsTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
jwk = KEY.public_key()
|
||||
issuers = (
|
||||
'C=US, O=SuperT LLC, CN=SuperTrustworthy Public CA',
|
||||
'O=LessTrustworthy CA Inc, CN=LessTrustworthy But StillSecure',
|
||||
)
|
||||
cert_fingerprints = (
|
||||
'93416768eb85e33adc4277f4c9acd63e7418fcfe',
|
||||
'16d95b7b63f1972b980b14c20291f3c0d1855d95',
|
||||
'48b46570d9fc6358108af43ad1649484def0debf',
|
||||
)
|
||||
subject_key_identifiers = ('d0083162dcc4c8a23ecb8aecbd86120e56fd24e5')
|
||||
authorized_for = ('www.example.com', 'example.net')
|
||||
serial_numbers = (34234239832, 23993939911, 17)
|
||||
|
||||
from acme.challenges import ProofOfPossession
|
||||
self.msg = ProofOfPossession.Hints(
|
||||
jwk=jwk, issuers=issuers, cert_fingerprints=cert_fingerprints,
|
||||
certs=(CERT,), subject_key_identifiers=subject_key_identifiers,
|
||||
authorized_for=authorized_for, serial_numbers=serial_numbers)
|
||||
|
||||
self.jmsg_to = {
|
||||
'jwk': jwk,
|
||||
'certFingerprints': cert_fingerprints,
|
||||
'certs': (jose.encode_b64jose(OpenSSL.crypto.dump_certificate(
|
||||
OpenSSL.crypto.FILETYPE_ASN1, CERT.wrapped)),),
|
||||
'subjectKeyIdentifiers': subject_key_identifiers,
|
||||
'serialNumbers': serial_numbers,
|
||||
'issuers': issuers,
|
||||
'authorizedFor': authorized_for,
|
||||
}
|
||||
self.jmsg_from = self.jmsg_to.copy()
|
||||
self.jmsg_from.update({'jwk': jwk.to_json()})
|
||||
|
||||
def test_to_partial_json(self):
|
||||
self.assertEqual(self.jmsg_to, self.msg.to_partial_json())
|
||||
|
||||
def test_from_json(self):
|
||||
from acme.challenges import ProofOfPossession
|
||||
self.assertEqual(
|
||||
self.msg, ProofOfPossession.Hints.from_json(self.jmsg_from))
|
||||
|
||||
def test_from_json_hashable(self):
|
||||
from acme.challenges import ProofOfPossession
|
||||
hash(ProofOfPossession.Hints.from_json(self.jmsg_from))
|
||||
|
||||
def test_json_without_optionals(self):
|
||||
for optional in ['certFingerprints', 'certs', 'subjectKeyIdentifiers',
|
||||
'serialNumbers', 'issuers', 'authorizedFor']:
|
||||
del self.jmsg_from[optional]
|
||||
del self.jmsg_to[optional]
|
||||
|
||||
from acme.challenges import ProofOfPossession
|
||||
msg = ProofOfPossession.Hints.from_json(self.jmsg_from)
|
||||
|
||||
self.assertEqual(msg.cert_fingerprints, ())
|
||||
self.assertEqual(msg.certs, ())
|
||||
self.assertEqual(msg.subject_key_identifiers, ())
|
||||
self.assertEqual(msg.serial_numbers, ())
|
||||
self.assertEqual(msg.issuers, ())
|
||||
self.assertEqual(msg.authorized_for, ())
|
||||
|
||||
self.assertEqual(self.jmsg_to, msg.to_partial_json())
|
||||
|
||||
|
||||
class ProofOfPossessionTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
from acme.challenges import ProofOfPossession
|
||||
hints = ProofOfPossession.Hints(
|
||||
jwk=KEY.public_key(), cert_fingerprints=(),
|
||||
certs=(), serial_numbers=(), subject_key_identifiers=(),
|
||||
issuers=(), authorized_for=())
|
||||
self.msg = ProofOfPossession(
|
||||
alg=jose.RS256, hints=hints,
|
||||
nonce=b'xD\xf9\xb9\xdbU\xed\xaa\x17\xf1y|\x81\x88\x99 ')
|
||||
|
||||
self.jmsg_to = {
|
||||
'type': 'proofOfPossession',
|
||||
'alg': jose.RS256,
|
||||
'nonce': 'eET5udtV7aoX8Xl8gYiZIA',
|
||||
'hints': hints,
|
||||
}
|
||||
self.jmsg_from = {
|
||||
'type': 'proofOfPossession',
|
||||
'alg': jose.RS256.to_json(),
|
||||
'nonce': 'eET5udtV7aoX8Xl8gYiZIA',
|
||||
'hints': hints.to_json(),
|
||||
}
|
||||
|
||||
def test_to_partial_json(self):
|
||||
self.assertEqual(self.jmsg_to, self.msg.to_partial_json())
|
||||
|
||||
def test_from_json(self):
|
||||
from acme.challenges import ProofOfPossession
|
||||
self.assertEqual(
|
||||
self.msg, ProofOfPossession.from_json(self.jmsg_from))
|
||||
|
||||
def test_from_json_hashable(self):
|
||||
from acme.challenges import ProofOfPossession
|
||||
hash(ProofOfPossession.from_json(self.jmsg_from))
|
||||
|
||||
|
||||
class ProofOfPossessionResponseTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
# acme-spec uses a confusing example in which both signature
|
||||
# nonce and challenge nonce are the same, don't make the same
|
||||
# mistake here...
|
||||
signature = other.Signature(
|
||||
alg=jose.RS256, jwk=KEY.public_key(),
|
||||
sig=b'\xa7\xc1\xe7\xe82o\xbc\xcd\xd0\x1e\x010#Z|\xaf\x15\x83'
|
||||
b'\x94\x8f#\x9b\nQo(\x80\x15,\x08\xfcz\x1d\xfd\xfd.\xaap'
|
||||
b'\xfa\x06\xd1\xa2f\x8d8X2>%d\xbd%\xe1T\xdd\xaa0\x18\xde'
|
||||
b'\x99\x08\xf0\x0e{',
|
||||
nonce=b'\x99\xc7Q\xb3f2\xbc\xdci\xfe\xd6\x98k\xc67\xdf',
|
||||
)
|
||||
|
||||
from acme.challenges import ProofOfPossessionResponse
|
||||
self.msg = ProofOfPossessionResponse(
|
||||
nonce=b'xD\xf9\xb9\xdbU\xed\xaa\x17\xf1y|\x81\x88\x99 ',
|
||||
signature=signature)
|
||||
|
||||
self.jmsg_to = {
|
||||
'resource': 'challenge',
|
||||
'type': 'proofOfPossession',
|
||||
'nonce': 'eET5udtV7aoX8Xl8gYiZIA',
|
||||
'signature': signature,
|
||||
}
|
||||
self.jmsg_from = {
|
||||
'resource': 'challenge',
|
||||
'type': 'proofOfPossession',
|
||||
'nonce': 'eET5udtV7aoX8Xl8gYiZIA',
|
||||
'signature': signature.to_json(),
|
||||
}
|
||||
|
||||
def test_verify(self):
|
||||
self.assertTrue(self.msg.verify())
|
||||
|
||||
def test_to_partial_json(self):
|
||||
self.assertEqual(self.jmsg_to, self.msg.to_partial_json())
|
||||
|
||||
def test_from_json(self):
|
||||
from acme.challenges import ProofOfPossessionResponse
|
||||
self.assertEqual(
|
||||
self.msg, ProofOfPossessionResponse.from_json(self.jmsg_from))
|
||||
|
||||
def test_from_json_hashable(self):
|
||||
from acme.challenges import ProofOfPossessionResponse
|
||||
hash(ProofOfPossessionResponse.from_json(self.jmsg_from))
|
||||
|
||||
|
||||
class DNSTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
"""ACME client API."""
|
||||
import collections
|
||||
import datetime
|
||||
from email.utils import parsedate_tz
|
||||
import heapq
|
||||
import logging
|
||||
import time
|
||||
|
|
@ -11,7 +12,6 @@ from six.moves import http_client # pylint: disable=import-error
|
|||
import OpenSSL
|
||||
import requests
|
||||
import sys
|
||||
import werkzeug
|
||||
|
||||
from acme import errors
|
||||
from acme import jose
|
||||
|
|
@ -248,6 +248,9 @@ class Client(object): # pylint: disable=too-many-instance-attributes
|
|||
def retry_after(cls, response, default):
|
||||
"""Compute next `poll` time based on response ``Retry-After`` header.
|
||||
|
||||
Handles integers and various datestring formats per
|
||||
https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.37
|
||||
|
||||
:param requests.Response response: Response from `poll`.
|
||||
:param int default: Default value (in seconds), used when
|
||||
``Retry-After`` header is not present or invalid.
|
||||
|
|
@ -260,12 +263,16 @@ class Client(object): # pylint: disable=too-many-instance-attributes
|
|||
try:
|
||||
seconds = int(retry_after)
|
||||
except ValueError:
|
||||
# pylint: disable=no-member
|
||||
decoded = werkzeug.parse_date(retry_after) # RFC1123
|
||||
if decoded is None:
|
||||
seconds = default
|
||||
else:
|
||||
return decoded
|
||||
# The RFC 2822 parser handles all of RFC 2616's cases in modern
|
||||
# environments (primarily HTTP 1.1+ but also py27+)
|
||||
when = parsedate_tz(retry_after)
|
||||
if when is not None:
|
||||
try:
|
||||
tz_secs = datetime.timedelta(when[-1] if when[-1] else 0)
|
||||
return datetime.datetime(*when[:7]) - tz_secs
|
||||
except (ValueError, OverflowError):
|
||||
pass
|
||||
seconds = default
|
||||
|
||||
return datetime.datetime.now() + datetime.timedelta(seconds=seconds)
|
||||
|
||||
|
|
@ -357,7 +364,7 @@ class Client(object): # pylint: disable=too-many-instance-attributes
|
|||
attempts = collections.defaultdict(int)
|
||||
exhausted = set()
|
||||
|
||||
# priority queue with datetime (based on Retry-After) as key,
|
||||
# priority queue with datetime.datetime (based on Retry-After) as key,
|
||||
# and original Authorization Resource as value
|
||||
waiting = [(datetime.datetime.now(), authzr) for authzr in authzrs]
|
||||
# mapping between original Authorization Resource and the most
|
||||
|
|
|
|||
|
|
@ -222,6 +222,17 @@ class ClientTest(unittest.TestCase):
|
|||
datetime.datetime(2015, 3, 27, 0, 0, 10),
|
||||
self.client.retry_after(response=self.response, default=10))
|
||||
|
||||
@mock.patch('acme.client.datetime')
|
||||
def test_retry_after_overflow(self, dt_mock):
|
||||
dt_mock.datetime.now.return_value = datetime.datetime(2015, 3, 27)
|
||||
dt_mock.timedelta = datetime.timedelta
|
||||
dt_mock.datetime.side_effect = datetime.datetime
|
||||
|
||||
self.response.headers['Retry-After'] = "Tue, 116 Feb 2016 11:50:00 MST"
|
||||
self.assertEqual(
|
||||
datetime.datetime(2015, 3, 27, 0, 0, 10),
|
||||
self.client.retry_after(response=self.response, default=10))
|
||||
|
||||
@mock.patch('acme.client.datetime')
|
||||
def test_retry_after_seconds(self, dt_mock):
|
||||
dt_mock.datetime.now.return_value = datetime.datetime(2015, 3, 27)
|
||||
|
|
|
|||
|
|
@ -123,6 +123,12 @@ class Directory(jose.JSONDeSerializable):
|
|||
|
||||
_REGISTERED_TYPES = {}
|
||||
|
||||
class Meta(jose.JSONObjectWithFields):
|
||||
"""Directory Meta."""
|
||||
terms_of_service = jose.Field('terms-of-service', omitempty=True)
|
||||
website = jose.Field('website', omitempty=True)
|
||||
caa_identities = jose.Field('caa-identities', omitempty=True)
|
||||
|
||||
@classmethod
|
||||
def _canon_key(cls, key):
|
||||
return getattr(key, 'resource_type', key)
|
||||
|
|
@ -137,10 +143,11 @@ class Directory(jose.JSONDeSerializable):
|
|||
|
||||
def __init__(self, jobj):
|
||||
canon_jobj = util.map_keys(jobj, self._canon_key)
|
||||
if not set(canon_jobj).issubset(self._REGISTERED_TYPES):
|
||||
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}}'z
|
||||
# in {{https-requests}}'
|
||||
raise ValueError('Wrong directory fields')
|
||||
# TODO: check that everything is an absolute URL; acme-spec is
|
||||
# not clear on that
|
||||
|
|
@ -163,6 +170,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:
|
||||
|
|
|
|||
|
|
@ -90,6 +90,11 @@ class DirectoryTest(unittest.TestCase):
|
|||
self.dir = Directory({
|
||||
'new-reg': 'reg',
|
||||
mock.MagicMock(resource_type='new-cert'): 'cert',
|
||||
'meta': Directory.Meta(
|
||||
terms_of_service='https://example.com/acme/terms',
|
||||
website='https://www.example.com/',
|
||||
caa_identities=['example.com'],
|
||||
),
|
||||
})
|
||||
|
||||
def test_init_wrong_key_value_error(self):
|
||||
|
|
@ -111,9 +116,16 @@ class DirectoryTest(unittest.TestCase):
|
|||
def test_getattr_fails_with_attribute_error(self):
|
||||
self.assertRaises(AttributeError, self.dir.__getattr__, 'foo')
|
||||
|
||||
def test_to_partial_json(self):
|
||||
self.assertEqual(
|
||||
self.dir.to_partial_json(), {'new-reg': 'reg', 'new-cert': 'cert'})
|
||||
def test_to_json(self):
|
||||
self.assertEqual(self.dir.to_json(), {
|
||||
'new-reg': 'reg',
|
||||
'new-cert': 'cert',
|
||||
'meta': {
|
||||
'terms-of-service': 'https://example.com/acme/terms',
|
||||
'website': 'https://www.example.com/',
|
||||
'caa-identities': ['example.com'],
|
||||
},
|
||||
})
|
||||
|
||||
def test_from_json_deserialization_error_on_wrong_key(self):
|
||||
from acme.messages import Directory
|
||||
|
|
@ -271,10 +283,8 @@ class AuthorizationTest(unittest.TestCase):
|
|||
ChallengeBody(uri='http://challb2', status=STATUS_VALID,
|
||||
chall=challenges.DNS(
|
||||
token=b'DGyRejmCefe7v4NfDGDKfA')),
|
||||
ChallengeBody(uri='http://challb3', status=STATUS_VALID,
|
||||
chall=challenges.RecoveryContact()),
|
||||
)
|
||||
combinations = ((0, 2), (1, 2))
|
||||
combinations = ((0,), (1,))
|
||||
|
||||
from acme.messages import Authorization
|
||||
from acme.messages import Identifier
|
||||
|
|
@ -300,8 +310,8 @@ class AuthorizationTest(unittest.TestCase):
|
|||
|
||||
def test_resolved_combinations(self):
|
||||
self.assertEqual(self.authz.resolved_combinations, (
|
||||
(self.challbs[0], self.challbs[2]),
|
||||
(self.challbs[1], self.challbs[2]),
|
||||
(self.challbs[0],),
|
||||
(self.challbs[1],),
|
||||
))
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,67 +0,0 @@
|
|||
"""Other ACME objects."""
|
||||
import functools
|
||||
import logging
|
||||
import os
|
||||
|
||||
from acme import jose
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Signature(jose.JSONObjectWithFields):
|
||||
"""ACME signature.
|
||||
|
||||
:ivar .JWASignature alg: Signature algorithm.
|
||||
:ivar bytes sig: Signature.
|
||||
:ivar bytes nonce: Nonce.
|
||||
:ivar .JWK jwk: JWK.
|
||||
|
||||
"""
|
||||
NONCE_SIZE = 16
|
||||
"""Minimum size of nonce in bytes."""
|
||||
|
||||
alg = jose.Field('alg', decoder=jose.JWASignature.from_json)
|
||||
sig = jose.Field('sig', encoder=jose.encode_b64jose,
|
||||
decoder=jose.decode_b64jose)
|
||||
nonce = jose.Field(
|
||||
'nonce', encoder=jose.encode_b64jose, decoder=functools.partial(
|
||||
jose.decode_b64jose, size=NONCE_SIZE, minimum=True))
|
||||
jwk = jose.Field('jwk', decoder=jose.JWK.from_json)
|
||||
|
||||
@classmethod
|
||||
def from_msg(cls, msg, key, nonce=None, nonce_size=None, alg=jose.RS256):
|
||||
"""Create signature with nonce prepended to the message.
|
||||
|
||||
:param bytes msg: Message to be signed.
|
||||
|
||||
:param key: Key used for signing.
|
||||
:type key: `cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey`
|
||||
(optionally wrapped in `.ComparableRSAKey`).
|
||||
|
||||
:param bytes nonce: Nonce to be used. If None, nonce of
|
||||
``nonce_size`` will be randomly generated.
|
||||
:param int nonce_size: Size of the automatically generated nonce.
|
||||
Defaults to :const:`NONCE_SIZE`.
|
||||
|
||||
:param .JWASignature alg:
|
||||
|
||||
"""
|
||||
nonce_size = cls.NONCE_SIZE if nonce_size is None else nonce_size
|
||||
nonce = os.urandom(nonce_size) if nonce is None else nonce
|
||||
|
||||
msg_with_nonce = nonce + msg
|
||||
sig = alg.sign(key, nonce + msg)
|
||||
logger.debug('%r signed as %r', msg_with_nonce, sig)
|
||||
|
||||
return cls(alg=alg, sig=sig, nonce=nonce,
|
||||
jwk=alg.kty(key=key.public_key()))
|
||||
|
||||
def verify(self, msg):
|
||||
"""Verify the signature.
|
||||
|
||||
:param bytes msg: Message that was used in signing.
|
||||
|
||||
"""
|
||||
# self.alg is not Field, but JWA | pylint: disable=no-member
|
||||
return self.alg.verify(self.jwk.key, self.nonce + msg, self.sig)
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
"""Tests for acme.sig."""
|
||||
import unittest
|
||||
|
||||
from acme import jose
|
||||
from acme import test_util
|
||||
|
||||
|
||||
KEY = test_util.load_rsa_private_key('rsa512_key.pem')
|
||||
|
||||
|
||||
class SignatureTest(unittest.TestCase):
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
"""Tests for acme.sig.Signature."""
|
||||
|
||||
def setUp(self):
|
||||
self.msg = b'message'
|
||||
self.sig = (b'IC\xd8*\xe7\x14\x9e\x19S\xb7\xcf\xec3\x12\xe2\x8a\x03'
|
||||
b'\x98u\xff\xf0\x94\xe2\xd7<\x8f\xa8\xed\xa4KN\xc3\xaa'
|
||||
b'\xb9X\xc3w\xaa\xc0_\xd0\x05$y>l#\x10<\x96\xd2\xcdr\xa3'
|
||||
b'\x1b\xa1\xf5!f\xef\xc64\xb6\x13')
|
||||
self.nonce = b'\xec\xd6\xf2oYH\xeb\x13\xd5#q\xe0\xdd\xa2\x92\xa9'
|
||||
|
||||
self.alg = jose.RS256
|
||||
self.jwk = jose.JWKRSA(key=KEY.public_key())
|
||||
|
||||
b64sig = ('SUPYKucUnhlTt8_sMxLiigOYdf_wlOLXPI-o7aRLTsOquVjDd6r'
|
||||
'AX9AFJHk-bCMQPJbSzXKjG6H1IWbvxjS2Ew')
|
||||
b64nonce = '7Nbyb1lI6xPVI3Hg3aKSqQ'
|
||||
self.jsig_to = {
|
||||
'nonce': b64nonce,
|
||||
'alg': self.alg,
|
||||
'jwk': self.jwk,
|
||||
'sig': b64sig,
|
||||
}
|
||||
|
||||
self.jsig_from = {
|
||||
'nonce': b64nonce,
|
||||
'alg': self.alg.to_partial_json(),
|
||||
'jwk': self.jwk.to_partial_json(),
|
||||
'sig': b64sig,
|
||||
}
|
||||
|
||||
from acme.other import Signature
|
||||
self.signature = Signature(
|
||||
alg=self.alg, sig=self.sig, nonce=self.nonce, jwk=self.jwk)
|
||||
|
||||
def test_attributes(self):
|
||||
self.assertEqual(self.signature.nonce, self.nonce)
|
||||
self.assertEqual(self.signature.alg, self.alg)
|
||||
self.assertEqual(self.signature.sig, self.sig)
|
||||
self.assertEqual(self.signature.jwk, self.jwk)
|
||||
|
||||
def test_verify_good_succeeds(self):
|
||||
self.assertTrue(self.signature.verify(self.msg))
|
||||
|
||||
def test_verify_bad_fails(self):
|
||||
self.assertFalse(self.signature.verify(self.msg + b'x'))
|
||||
|
||||
@classmethod
|
||||
def _from_msg(cls, *args, **kwargs):
|
||||
from acme.other import Signature
|
||||
return Signature.from_msg(*args, **kwargs)
|
||||
|
||||
def test_create_from_msg(self):
|
||||
signature = self._from_msg(self.msg, KEY, self.nonce)
|
||||
self.assertEqual(self.signature, signature)
|
||||
|
||||
def test_create_from_msg_random_nonce(self):
|
||||
signature = self._from_msg(self.msg, KEY)
|
||||
self.assertEqual(signature.alg, self.alg)
|
||||
self.assertEqual(signature.jwk, self.jwk)
|
||||
self.assertTrue(signature.verify(self.msg))
|
||||
|
||||
def test_to_partial_json(self):
|
||||
self.assertEqual(self.signature.to_partial_json(), self.jsig_to)
|
||||
|
||||
def test_from_json(self):
|
||||
from acme.other import Signature
|
||||
self.assertEqual(
|
||||
self.signature, Signature.from_json(self.jsig_from))
|
||||
|
||||
def test_from_json_non_schema_errors(self):
|
||||
from acme.other import Signature
|
||||
jwk = self.jwk.to_partial_json()
|
||||
self.assertRaises(
|
||||
jose.DeserializationError, Signature.from_json, {
|
||||
'alg': 'RS256', 'sig': 'x', 'nonce': '', 'jwk': jwk})
|
||||
self.assertRaises(
|
||||
jose.DeserializationError, Signature.from_json, {
|
||||
'alg': 'RS256', 'sig': '', 'nonce': 'x', 'jwk': jwk})
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main() # pragma: no cover
|
||||
|
|
@ -42,7 +42,7 @@ csr = OpenSSL.crypto.load_certificate_request(
|
|||
OpenSSL.crypto.FILETYPE_ASN1, pkg_resources.resource_string(
|
||||
'acme', os.path.join('testdata', 'csr.der')))
|
||||
try:
|
||||
acme.request_issuance(csr, (authzr,))
|
||||
acme.request_issuance(jose.util.ComparableX509(csr), (authzr,))
|
||||
except messages.Error as error:
|
||||
print ("This script is doomed to fail as no authorization "
|
||||
"challenges are ever solved. Error from server: {0}".format(error))
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from setuptools import setup
|
|||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.5.0.dev0'
|
||||
version = '0.6.0.dev0'
|
||||
|
||||
# Please update tox.ini when modifying dependency version requirements
|
||||
install_requires = [
|
||||
|
|
@ -18,9 +18,10 @@ install_requires = [
|
|||
'pyrfc3339',
|
||||
'pytz',
|
||||
'requests',
|
||||
'setuptools', # pkg_resources
|
||||
# For pkg_resources. >=1.0 so pip resolves it to a version cryptography
|
||||
# will tolerate; see #2599:
|
||||
'setuptools>=1.0',
|
||||
'six',
|
||||
'werkzeug',
|
||||
]
|
||||
|
||||
# env markers in extras_require cause problems with older pip: #517
|
||||
|
|
@ -61,7 +62,7 @@ setup(
|
|||
version=version,
|
||||
description='ACME protocol implementation in Python',
|
||||
url='https://github.com/letsencrypt/letsencrypt',
|
||||
author="Let's Encrypt Project",
|
||||
author="Certbot Project",
|
||||
author_email='client-dev@letsencrypt.org',
|
||||
license='Apache License 2.0',
|
||||
classifiers=[
|
||||
|
|
|
|||
7
certbot-apache/MANIFEST.in
Normal file
7
certbot-apache/MANIFEST.in
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
include LICENSE.txt
|
||||
include README.rst
|
||||
recursive-include docs *
|
||||
recursive-include certbot_apache/tests/testdata *
|
||||
include certbot_apache/centos-options-ssl-apache.conf
|
||||
include certbot_apache/options-ssl-apache.conf
|
||||
recursive-include certbot_apache/augeas_lens *.aug
|
||||
1
certbot-apache/README.rst
Normal file
1
certbot-apache/README.rst
Normal file
|
|
@ -0,0 +1 @@
|
|||
Apache plugin for Certbot
|
||||
1
certbot-apache/certbot_apache/__init__.py
Normal file
1
certbot-apache/certbot_apache/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
"""Certbot Apache plugin."""
|
||||
|
|
@ -3,11 +3,11 @@ import logging
|
|||
|
||||
import augeas
|
||||
|
||||
from letsencrypt import errors
|
||||
from letsencrypt import reverter
|
||||
from letsencrypt.plugins import common
|
||||
from certbot import errors
|
||||
from certbot import reverter
|
||||
from certbot.plugins import common
|
||||
|
||||
from letsencrypt_apache import constants
|
||||
from certbot_apache import constants
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -16,14 +16,14 @@ class AugeasConfigurator(common.Plugin):
|
|||
"""Base Augeas Configurator class.
|
||||
|
||||
:ivar config: Configuration.
|
||||
:type config: :class:`~letsencrypt.interfaces.IConfig`
|
||||
:type config: :class:`~certbot.interfaces.IConfig`
|
||||
|
||||
:ivar aug: Augeas object
|
||||
:type aug: :class:`augeas.Augeas`
|
||||
|
||||
:ivar str save_notes: Human-readable configuration change notes
|
||||
:ivar reverter: saves and reverts checkpoints
|
||||
:type reverter: :class:`letsencrypt.reverter.Reverter`
|
||||
:type reverter: :class:`certbot.reverter.Reverter`
|
||||
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
2
certbot-apache/certbot_apache/augeas_lens/README
Normal file
2
certbot-apache/certbot_apache/augeas_lens/README
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
Certbot includes the very latest Augeas lenses in order to ship bug fixes
|
||||
to Apache configuration handling bugs as quickly as possible
|
||||
|
|
@ -45,9 +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 = Sep.opt_space
|
||||
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:_.-]*/
|
||||
|
|
@ -60,7 +59,7 @@ 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>]|\\\\"|\\\\'/
|
||||
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 = /\\\\./
|
||||
|
|
@ -13,18 +13,18 @@ import zope.interface
|
|||
|
||||
from acme import challenges
|
||||
|
||||
from letsencrypt import errors
|
||||
from letsencrypt import interfaces
|
||||
from letsencrypt import le_util
|
||||
from certbot import errors
|
||||
from certbot import interfaces
|
||||
from certbot import le_util
|
||||
|
||||
from letsencrypt.plugins import common
|
||||
from certbot.plugins import common
|
||||
|
||||
from letsencrypt_apache import augeas_configurator
|
||||
from letsencrypt_apache import constants
|
||||
from letsencrypt_apache import display_ops
|
||||
from letsencrypt_apache import tls_sni_01
|
||||
from letsencrypt_apache import obj
|
||||
from letsencrypt_apache import parser
|
||||
from certbot_apache import augeas_configurator
|
||||
from certbot_apache import constants
|
||||
from certbot_apache import display_ops
|
||||
from certbot_apache import tls_sni_01
|
||||
from certbot_apache import obj
|
||||
from certbot_apache import parser
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
|
|
@ -60,6 +60,8 @@ logger = logging.getLogger(__name__)
|
|||
# sites-available doesn't allow immediate find_dir search even with save()
|
||||
# and load()
|
||||
|
||||
@zope.interface.implementer(interfaces.IAuthenticator, interfaces.IInstaller)
|
||||
@zope.interface.provider(interfaces.IPluginFactory)
|
||||
class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
# pylint: disable=too-many-instance-attributes,too-many-public-methods
|
||||
"""Apache configurator.
|
||||
|
|
@ -68,20 +70,18 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
14.04 Apache 2.4 and it works for Ubuntu 12.04 Apache 2.2
|
||||
|
||||
:ivar config: Configuration.
|
||||
:type config: :class:`~letsencrypt.interfaces.IConfig`
|
||||
:type config: :class:`~certbot.interfaces.IConfig`
|
||||
|
||||
:ivar parser: Handles low level parsing
|
||||
:type parser: :class:`~letsencrypt_apache.parser`
|
||||
:type parser: :class:`~certbot_apache.parser`
|
||||
|
||||
:ivar tup version: version of Apache
|
||||
:ivar list vhosts: All vhosts found in the configuration
|
||||
(:class:`list` of :class:`~letsencrypt_apache.obj.VirtualHost`)
|
||||
(:class:`list` of :class:`~certbot_apache.obj.VirtualHost`)
|
||||
|
||||
:ivar dict assoc: Mapping between domains and vhosts
|
||||
|
||||
"""
|
||||
zope.interface.implements(interfaces.IAuthenticator, interfaces.IInstaller)
|
||||
zope.interface.classProvides(interfaces.IPluginFactory)
|
||||
|
||||
description = "Apache Web Server - Alpha"
|
||||
|
||||
|
|
@ -205,7 +205,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
installed, the VirtualHost is enabled if it isn't already.
|
||||
|
||||
.. todo:: Might be nice to remove chain directive if none exists
|
||||
This shouldn't happen within letsencrypt though
|
||||
This shouldn't happen within certbot though
|
||||
|
||||
:raises errors.PluginError: When unable to deploy certificate due to
|
||||
a lack of directives
|
||||
|
|
@ -290,7 +290,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
:param bool temp: whether the vhost is only used temporarily
|
||||
|
||||
:returns: ssl vhost associated with name
|
||||
:rtype: :class:`~letsencrypt_apache.obj.VirtualHost`
|
||||
:rtype: :class:`~certbot_apache.obj.VirtualHost`
|
||||
|
||||
:raises .errors.PluginError: If no vhost is available or chosen
|
||||
|
||||
|
|
@ -342,6 +342,22 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
self.assoc[target_name] = vhost
|
||||
return vhost
|
||||
|
||||
def included_in_wildcard(self, names, target_name):
|
||||
"""Helper function to see if alias is covered by wildcard"""
|
||||
target_name = target_name.split(".")[::-1]
|
||||
wildcards = [domain.split(".")[1:] for domain in
|
||||
names if domain.startswith("*")]
|
||||
for wildcard in wildcards:
|
||||
if len(wildcard) > len(target_name):
|
||||
continue
|
||||
for idx, segment in enumerate(wildcard[::-1]):
|
||||
if segment != target_name[idx]:
|
||||
break
|
||||
else:
|
||||
# https://docs.python.org/2/tutorial/controlflow.html#break-and-continue-statements-and-else-clauses-on-loops
|
||||
return True
|
||||
return False
|
||||
|
||||
def _find_best_vhost(self, target_name):
|
||||
"""Finds the best vhost for a target_name.
|
||||
|
||||
|
|
@ -351,16 +367,21 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
:returns: VHost or None
|
||||
|
||||
"""
|
||||
# Points 4 - Servername SSL
|
||||
# Points 3 - Address name with SSL
|
||||
# Points 2 - Servername no SSL
|
||||
# Points 6 - Servername SSL
|
||||
# Points 5 - Wildcard SSL
|
||||
# Points 4 - Address name with SSL
|
||||
# Points 3 - Servername no SSL
|
||||
# Points 2 - Wildcard no SSL
|
||||
# Points 1 - Address name with no SSL
|
||||
best_candidate = None
|
||||
best_points = 0
|
||||
for vhost in self.vhosts:
|
||||
if vhost.modmacro is True:
|
||||
continue
|
||||
if target_name in vhost.get_names():
|
||||
names = vhost.get_names()
|
||||
if target_name in names:
|
||||
points = 3
|
||||
elif self.included_in_wildcard(names, target_name):
|
||||
points = 2
|
||||
elif any(addr.get_addr() == target_name for addr in vhost.addrs):
|
||||
points = 1
|
||||
|
|
@ -370,7 +391,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
continue # pragma: no cover
|
||||
|
||||
if vhost.ssl:
|
||||
points += 2
|
||||
points += 3
|
||||
|
||||
if points > best_points:
|
||||
best_points = points
|
||||
|
|
@ -451,7 +472,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
"""Helper function for get_virtual_hosts().
|
||||
|
||||
:param host: In progress vhost whose names will be added
|
||||
:type host: :class:`~letsencrypt_apache.obj.VirtualHost`
|
||||
:type host: :class:`~certbot_apache.obj.VirtualHost`
|
||||
|
||||
"""
|
||||
# Take the final ServerName as each overrides the previous
|
||||
|
|
@ -477,7 +498,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
:param str path: Augeas path to virtual host
|
||||
|
||||
:returns: newly created vhost
|
||||
:rtype: :class:`~letsencrypt_apache.obj.VirtualHost`
|
||||
:rtype: :class:`~certbot_apache.obj.VirtualHost`
|
||||
|
||||
"""
|
||||
addrs = set()
|
||||
|
|
@ -513,7 +534,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
def get_virtual_hosts(self):
|
||||
"""Returns list of virtual hosts found in the Apache configuration.
|
||||
|
||||
:returns: List of :class:`~letsencrypt_apache.obj.VirtualHost`
|
||||
:returns: List of :class:`~certbot_apache.obj.VirtualHost`
|
||||
objects found in configuration
|
||||
:rtype: list
|
||||
|
||||
|
|
@ -525,6 +546,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
paths = self.aug.match(
|
||||
("/files%s//*[label()=~regexp('%s')]" %
|
||||
(vhost_path, parser.case_i("VirtualHost"))))
|
||||
paths = [path for path in paths if
|
||||
os.path.basename(path) == "VirtualHost"]
|
||||
for path in paths:
|
||||
new_vhost = self._create_vhost(path)
|
||||
realpath = os.path.realpath(new_vhost.filep)
|
||||
|
|
@ -549,7 +572,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
now NameVirtualHosts. If version is earlier than 2.4, check if addr
|
||||
has a NameVirtualHost directive in the Apache config
|
||||
|
||||
:param letsencrypt_apache.obj.Addr target_addr: vhost address
|
||||
:param certbot_apache.obj.Addr target_addr: vhost address
|
||||
|
||||
:returns: Success
|
||||
:rtype: bool
|
||||
|
|
@ -567,7 +590,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
"""Adds NameVirtualHost directive for given address.
|
||||
|
||||
:param addr: Address that will be added as NameVirtualHost directive
|
||||
:type addr: :class:`~letsencrypt_apache.obj.Addr`
|
||||
:type addr: :class:`~certbot_apache.obj.Addr`
|
||||
|
||||
"""
|
||||
loc = parser.get_aug_path(self.parser.loc["name"])
|
||||
|
|
@ -656,7 +679,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
"""Checks to see if the server is ready for SNI challenges.
|
||||
|
||||
:param addrs: Addresses to check SNI compatibility
|
||||
:type addrs: :class:`~letsencrypt_apache.obj.Addr`
|
||||
:type addrs: :class:`~certbot_apache.obj.Addr`
|
||||
|
||||
"""
|
||||
# Version 2.4 and later are automatically SNI ready.
|
||||
|
|
@ -674,15 +697,15 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
|
||||
Duplicates vhost and adds default ssl options
|
||||
New vhost will reside as (nonssl_vhost.path) +
|
||||
``letsencrypt_apache.constants.os_constant("le_vhost_ext")``
|
||||
``certbot_apache.constants.os_constant("le_vhost_ext")``
|
||||
|
||||
.. note:: This function saves the configuration
|
||||
|
||||
:param nonssl_vhost: Valid VH that doesn't have SSLEngine on
|
||||
:type nonssl_vhost: :class:`~letsencrypt_apache.obj.VirtualHost`
|
||||
:type nonssl_vhost: :class:`~certbot_apache.obj.VirtualHost`
|
||||
|
||||
:returns: SSL vhost
|
||||
:rtype: :class:`~letsencrypt_apache.obj.VirtualHost`
|
||||
:rtype: :class:`~certbot_apache.obj.VirtualHost`
|
||||
|
||||
:raises .errors.PluginError: If more than one virtual host is in
|
||||
the file or if plugin is unable to write/read vhost files.
|
||||
|
|
@ -869,10 +892,13 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
if not vh_p:
|
||||
return
|
||||
vh_path = vh_p[0]
|
||||
if (self.parser.find_dir("ServerName", target_name, start=vh_path, exclude=False)
|
||||
or self.parser.find_dir("ServerAlias", target_name, start=vh_path, exclude=False)):
|
||||
if (self.parser.find_dir("ServerName", target_name,
|
||||
start=vh_path, exclude=False) or
|
||||
self.parser.find_dir("ServerAlias", target_name,
|
||||
start=vh_path, exclude=False)):
|
||||
return
|
||||
if not self.parser.find_dir("ServerName", None, start=vh_path, exclude=False):
|
||||
if not self.parser.find_dir("ServerName", None,
|
||||
start=vh_path, exclude=False):
|
||||
self.parser.add_dir(vh_path, "ServerName", target_name)
|
||||
else:
|
||||
self.parser.add_dir(vh_path, "ServerAlias", target_name)
|
||||
|
|
@ -885,7 +911,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
https://httpd.apache.org/docs/2.2/mod/core.html#namevirtualhost
|
||||
|
||||
:param vhost: New virtual host that was recently created.
|
||||
:type vhost: :class:`~letsencrypt_apache.obj.VirtualHost`
|
||||
:type vhost: :class:`~certbot_apache.obj.VirtualHost`
|
||||
|
||||
"""
|
||||
need_to_save = False
|
||||
|
|
@ -895,9 +921,10 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
for addr in vhost.addrs:
|
||||
# In Apache 2.2, when a NameVirtualHost directive is not
|
||||
# set, "*" and "_default_" will conflict when sharing a port
|
||||
addrs = set((addr,))
|
||||
if addr.get_addr() in ("*", "_default_"):
|
||||
addrs = [obj.Addr((a, addr.get_port(),))
|
||||
for a in ("*", "_default_")]
|
||||
addrs.update(obj.Addr((a, addr.get_port(),))
|
||||
for a in ("*", "_default_"))
|
||||
|
||||
for test_vh in self.vhosts:
|
||||
if (vhost.filep != test_vh.filep and
|
||||
|
|
@ -907,6 +934,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
self.add_name_vhost(addr)
|
||||
logger.info("Enabling NameVirtualHosts on %s", addr)
|
||||
need_to_save = True
|
||||
break
|
||||
|
||||
if need_to_save:
|
||||
self.save()
|
||||
|
|
@ -923,9 +951,9 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
|
||||
:param str domain: domain to enhance
|
||||
:param str enhancement: enhancement type defined in
|
||||
:const:`~letsencrypt.constants.ENHANCEMENTS`
|
||||
:const:`~certbot.constants.ENHANCEMENTS`
|
||||
:param options: options for the enhancement
|
||||
See :const:`~letsencrypt.constants.ENHANCEMENTS`
|
||||
See :const:`~certbot.constants.ENHANCEMENTS`
|
||||
documentation for appropriate parameter.
|
||||
|
||||
:raises .errors.PluginError: If Enhancement is not supported, or if
|
||||
|
|
@ -953,14 +981,14 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
.. note:: This function saves the configuration
|
||||
|
||||
:param ssl_vhost: Destination of traffic, an ssl enabled vhost
|
||||
:type ssl_vhost: :class:`~letsencrypt_apache.obj.VirtualHost`
|
||||
:type ssl_vhost: :class:`~certbot_apache.obj.VirtualHost`
|
||||
|
||||
:param header_substring: string that uniquely identifies a header.
|
||||
e.g: Strict-Transport-Security, Upgrade-Insecure-Requests.
|
||||
:type str
|
||||
|
||||
:returns: Success, general_vhost (HTTP vhost)
|
||||
:rtype: (bool, :class:`~letsencrypt_apache.obj.VirtualHost`)
|
||||
:rtype: (bool, :class:`~certbot_apache.obj.VirtualHost`)
|
||||
|
||||
:raises .errors.PluginError: If no viable HTTP host can be created or
|
||||
set with header header_substring.
|
||||
|
|
@ -988,7 +1016,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
contains the string header_substring.
|
||||
|
||||
:param ssl_vhost: vhost to check
|
||||
:type vhost: :class:`~letsencrypt_apache.obj.VirtualHost`
|
||||
:type vhost: :class:`~certbot_apache.obj.VirtualHost`
|
||||
|
||||
:param header_substring: string that uniquely identifies a header.
|
||||
e.g: Strict-Transport-Security, Upgrade-Insecure-Requests.
|
||||
|
|
@ -1025,13 +1053,13 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
.. note:: This function saves the configuration
|
||||
|
||||
:param ssl_vhost: Destination of traffic, an ssl enabled vhost
|
||||
:type ssl_vhost: :class:`~letsencrypt_apache.obj.VirtualHost`
|
||||
:type ssl_vhost: :class:`~certbot_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`)
|
||||
:rtype: (bool, :class:`~certbot_apache.obj.VirtualHost`)
|
||||
|
||||
:raises .errors.PluginError: If no viable HTTP host can be created or
|
||||
used for the redirect.
|
||||
|
|
@ -1055,11 +1083,11 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
"redirection")
|
||||
self._create_redirect_vhost(ssl_vhost)
|
||||
else:
|
||||
# Check if LetsEncrypt redirection already exists
|
||||
self._verify_no_letsencrypt_redirect(general_vh)
|
||||
# Check if Certbot redirection already exists
|
||||
self._verify_no_certbot_redirect(general_vh)
|
||||
|
||||
# Note: if code flow gets here it means we didn't find the exact
|
||||
# letsencrypt RewriteRule config for redirection. Finding
|
||||
# certbot RewriteRule config for redirection. Finding
|
||||
# another RewriteRule is likely to be fine in most or all cases,
|
||||
# but redirect loops are possible in very obscure cases; see #1620
|
||||
# for reasoning.
|
||||
|
|
@ -1073,7 +1101,12 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
# even with save() and load()
|
||||
if not self._is_rewrite_engine_on(general_vh):
|
||||
self.parser.add_dir(general_vh.path, "RewriteEngine", "on")
|
||||
|
||||
names = ssl_vhost.get_names()
|
||||
for idx, name in enumerate(names):
|
||||
args = ["%{SERVER_NAME}", "={0}".format(name), "[OR]"]
|
||||
if idx == len(names) - 1:
|
||||
args.pop()
|
||||
self.parser.add_dir(general_vh.path, "RewriteCond", args)
|
||||
if self.get_version() >= (2, 3, 9):
|
||||
self.parser.add_dir(general_vh.path, "RewriteRule",
|
||||
constants.REWRITE_HTTPS_ARGS_WITH_END)
|
||||
|
|
@ -1088,17 +1121,17 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
logger.info("Redirecting vhost in %s to ssl vhost in %s",
|
||||
general_vh.filep, ssl_vhost.filep)
|
||||
|
||||
def _verify_no_letsencrypt_redirect(self, vhost):
|
||||
"""Checks to see if a redirect was already installed by letsencrypt.
|
||||
def _verify_no_certbot_redirect(self, vhost):
|
||||
"""Checks to see if a redirect was already installed by certbot.
|
||||
|
||||
Checks to see if virtualhost already contains a rewrite rule that is
|
||||
identical to Letsencrypt's redirection rewrite rule.
|
||||
identical to Certbot's redirection rewrite rule.
|
||||
|
||||
:param vhost: vhost to check
|
||||
:type vhost: :class:`~letsencrypt_apache.obj.VirtualHost`
|
||||
:type vhost: :class:`~certbot_apache.obj.VirtualHost`
|
||||
|
||||
:raises errors.PluginEnhancementAlreadyPresent: When the exact
|
||||
letsencrypt redirection WriteRule exists in virtual host.
|
||||
certbot redirection WriteRule exists in virtual host.
|
||||
"""
|
||||
rewrite_path = self.parser.find_dir(
|
||||
"RewriteRule", None, start=vhost.path)
|
||||
|
|
@ -1121,13 +1154,13 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
for matches in rewrite_args_dict.values():
|
||||
if [self.aug.get(x) for x in matches] in redirect_args:
|
||||
raise errors.PluginEnhancementAlreadyPresent(
|
||||
"Let's Encrypt has already enabled redirection")
|
||||
"Certbot has already enabled redirection")
|
||||
|
||||
def _is_rewrite_exists(self, vhost):
|
||||
"""Checks if there exists a RewriteRule directive in vhost
|
||||
|
||||
:param vhost: vhost to check
|
||||
:type vhost: :class:`~letsencrypt_apache.obj.VirtualHost`
|
||||
:type vhost: :class:`~certbot_apache.obj.VirtualHost`
|
||||
|
||||
:returns: True if a RewriteRule directive exists.
|
||||
:rtype: bool
|
||||
|
|
@ -1141,7 +1174,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
"""Checks if a RewriteEngine directive is on
|
||||
|
||||
:param vhost: vhost to check
|
||||
:type vhost: :class:`~letsencrypt_apache.obj.VirtualHost`
|
||||
:type vhost: :class:`~certbot_apache.obj.VirtualHost`
|
||||
|
||||
"""
|
||||
rewrite_engine_path = self.parser.find_dir("RewriteEngine", "on",
|
||||
|
|
@ -1154,10 +1187,10 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
"""Creates an http_vhost specifically to redirect for the ssl_vhost.
|
||||
|
||||
:param ssl_vhost: ssl vhost
|
||||
:type ssl_vhost: :class:`~letsencrypt_apache.obj.VirtualHost`
|
||||
:type ssl_vhost: :class:`~certbot_apache.obj.VirtualHost`
|
||||
|
||||
:returns: tuple of the form
|
||||
(`success`, :class:`~letsencrypt_apache.obj.VirtualHost`)
|
||||
(`success`, :class:`~certbot_apache.obj.VirtualHost`)
|
||||
:rtype: tuple
|
||||
|
||||
"""
|
||||
|
|
@ -1243,6 +1276,10 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
for http_vh in candidate_http_vhs:
|
||||
if http_vh.same_server(ssl_vhost):
|
||||
return http_vh
|
||||
# Third filter - if none with same names, return generic
|
||||
for http_vh in candidate_http_vhs:
|
||||
if http_vh.same_server(ssl_vhost, generic=True):
|
||||
return http_vh
|
||||
|
||||
return None
|
||||
|
||||
|
|
@ -1332,7 +1369,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
.. todo:: Make sure link is not broken...
|
||||
|
||||
:param vhost: vhost to enable
|
||||
:type vhost: :class:`~letsencrypt_apache.obj.VirtualHost`
|
||||
:type vhost: :class:`~certbot_apache.obj.VirtualHost`
|
||||
|
||||
:raises .errors.NotSupportedError: If filesystem layout is not
|
||||
supported.
|
||||
|
|
@ -1415,7 +1452,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
if not le_util.exe_exists(self.conf("dismod")):
|
||||
raise errors.MisconfigurationError(
|
||||
"Unable to find a2dismod, please make sure a2enmod and "
|
||||
"a2dismod are configured correctly for letsencrypt.")
|
||||
"a2dismod are configured correctly for certbot.")
|
||||
|
||||
self.reverter.register_undo_command(
|
||||
temp, [self.conf("dismod"), mod_name])
|
||||
|
|
@ -1429,7 +1466,6 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
|
||||
"""
|
||||
self.config_test()
|
||||
logger.debug(self.reverter.view_config_changes(for_logging=True))
|
||||
self._reload()
|
||||
|
||||
def _reload(self):
|
||||
|
|
@ -1599,11 +1635,11 @@ def get_file_path(vhost_path):
|
|||
|
||||
def install_ssl_options_conf(options_ssl):
|
||||
"""
|
||||
Copy Let's Encrypt's SSL options file into the system's config dir if
|
||||
Copy Certbot's SSL options file into the system's config dir if
|
||||
required.
|
||||
"""
|
||||
# XXX if we ever try to enforce a local privilege boundary (eg, running
|
||||
# letsencrypt for unprivileged users via setuid), this function will need
|
||||
# certbot for unprivileged users via setuid), this function will need
|
||||
# to be modified.
|
||||
|
||||
# XXX if the user is in security-autoupdate mode, we should be willing to
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
"""Apache plugin constants."""
|
||||
import pkg_resources
|
||||
from letsencrypt import le_util
|
||||
from certbot import le_util
|
||||
|
||||
|
||||
CLI_DEFAULTS_DEBIAN = dict(
|
||||
|
|
@ -18,7 +18,7 @@ CLI_DEFAULTS_DEBIAN = dict(
|
|||
handle_sites=True,
|
||||
challenge_location="/etc/apache2",
|
||||
MOD_SSL_CONF_SRC=pkg_resources.resource_filename(
|
||||
"letsencrypt_apache", "options-ssl-apache.conf")
|
||||
"certbot_apache", "options-ssl-apache.conf")
|
||||
)
|
||||
CLI_DEFAULTS_CENTOS = dict(
|
||||
server_root="/etc/httpd",
|
||||
|
|
@ -35,14 +35,14 @@ CLI_DEFAULTS_CENTOS = dict(
|
|||
handle_sites=False,
|
||||
challenge_location="/etc/httpd/conf.d",
|
||||
MOD_SSL_CONF_SRC=pkg_resources.resource_filename(
|
||||
"letsencrypt_apache", "centos-options-ssl-apache.conf")
|
||||
"certbot_apache", "centos-options-ssl-apache.conf")
|
||||
)
|
||||
CLI_DEFAULTS_GENTOO = dict(
|
||||
server_root="/etc/apache2",
|
||||
vhost_root="/etc/apache2/vhosts.d",
|
||||
vhost_files="*.conf",
|
||||
version_cmd=['/usr/sbin/apache2', '-v'],
|
||||
define_cmd=['/usr/sbin/apache2', '-t', '-D', 'DUMP_RUN_CFG'],
|
||||
define_cmd=['apache2ctl', 'virtualhosts'],
|
||||
restart_cmd=['apache2ctl', 'graceful'],
|
||||
conftest_cmd=['apache2ctl', 'configtest'],
|
||||
enmod=None,
|
||||
|
|
@ -52,7 +52,24 @@ CLI_DEFAULTS_GENTOO = dict(
|
|||
handle_sites=False,
|
||||
challenge_location="/etc/apache2/vhosts.d",
|
||||
MOD_SSL_CONF_SRC=pkg_resources.resource_filename(
|
||||
"letsencrypt_apache", "options-ssl-apache.conf")
|
||||
"certbot_apache", "options-ssl-apache.conf")
|
||||
)
|
||||
CLI_DEFAULTS_DARWIN = dict(
|
||||
server_root="/etc/apache2",
|
||||
vhost_root="/etc/apache2/other",
|
||||
vhost_files="*.conf",
|
||||
version_cmd=['/usr/sbin/httpd', '-v'],
|
||||
define_cmd=['/usr/sbin/httpd', '-t', '-D', 'DUMP_RUN_CFG'],
|
||||
restart_cmd=['apachectl', 'graceful'],
|
||||
conftest_cmd=['apachectl', 'configtest'],
|
||||
enmod=None,
|
||||
dismod=None,
|
||||
le_vhost_ext="-le-ssl.conf",
|
||||
handle_mods=False,
|
||||
handle_sites=False,
|
||||
challenge_location="/etc/apache2/other",
|
||||
MOD_SSL_CONF_SRC=pkg_resources.resource_filename(
|
||||
"certbot_apache", "options-ssl-apache.conf")
|
||||
)
|
||||
CLI_DEFAULTS = {
|
||||
"debian": CLI_DEFAULTS_DEBIAN,
|
||||
|
|
@ -61,7 +78,8 @@ CLI_DEFAULTS = {
|
|||
"centos linux": CLI_DEFAULTS_CENTOS,
|
||||
"fedora": CLI_DEFAULTS_CENTOS,
|
||||
"red hat enterprise linux server": CLI_DEFAULTS_CENTOS,
|
||||
"gentoo base system": CLI_DEFAULTS_GENTOO
|
||||
"gentoo base system": CLI_DEFAULTS_GENTOO,
|
||||
"darwin": CLI_DEFAULTS_DARWIN,
|
||||
}
|
||||
"""CLI defaults."""
|
||||
|
||||
|
|
@ -69,7 +87,7 @@ MOD_SSL_CONF_DEST = "options-ssl-apache.conf"
|
|||
"""Name of the mod_ssl config file as saved in `IConfig.config_dir`."""
|
||||
|
||||
AUGEAS_LENS_DIR = pkg_resources.resource_filename(
|
||||
"letsencrypt_apache", "augeas_lens")
|
||||
"certbot_apache", "augeas_lens")
|
||||
"""Path to the Augeas lens directory"""
|
||||
|
||||
REWRITE_HTTPS_ARGS = [
|
||||
|
|
@ -4,10 +4,10 @@ import os
|
|||
|
||||
import zope.component
|
||||
|
||||
from letsencrypt import errors
|
||||
from letsencrypt import interfaces
|
||||
from certbot import errors
|
||||
from certbot import interfaces
|
||||
|
||||
import letsencrypt.display.util as display_util
|
||||
import certbot.display.util as display_util
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
@ -50,7 +50,7 @@ def _vhost_menu(domain, vhosts):
|
|||
|
||||
if free_chars < 2:
|
||||
logger.debug("Display size is too small for "
|
||||
"letsencrypt_apache.display_ops._vhost_menu()")
|
||||
"certbot_apache.display_ops._vhost_menu()")
|
||||
# This runs the edge off the screen, but it doesn't cause an "error"
|
||||
filename_size = 1
|
||||
disp_name_size = 1
|
||||
|
|
@ -83,7 +83,8 @@ def _vhost_menu(domain, vhosts):
|
|||
code, tag = zope.component.getUtility(interfaces.IDisplay).menu(
|
||||
"We were unable to find a vhost with a ServerName "
|
||||
"or Address of {0}.{1}Which virtual host would you "
|
||||
"like to choose?".format(domain, os.linesep),
|
||||
"like to choose?\n(note: conf files with multiple "
|
||||
"vhosts are not yet supported)".format(domain, os.linesep),
|
||||
choices, help_label="More Info", ok_label="Select")
|
||||
except errors.MissingCommandlineFlag as e:
|
||||
msg = ("Failed to run Apache plugin non-interactively{1}{0}{1}"
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
"""Module contains classes used by the Apache Configurator."""
|
||||
import re
|
||||
|
||||
from letsencrypt.plugins import common
|
||||
from certbot.plugins import common
|
||||
|
||||
|
||||
class Addr(common.Addr):
|
||||
|
|
@ -189,22 +189,29 @@ class VirtualHost(object): # pylint: disable=too-few-public-methods
|
|||
return True
|
||||
return False
|
||||
|
||||
def same_server(self, vhost):
|
||||
def same_server(self, vhost, generic=False):
|
||||
"""Determines if the vhost is the same 'server'.
|
||||
|
||||
Used in redirection - indicates whether or not the two virtual hosts
|
||||
serve on the exact same IP combinations, but different ports.
|
||||
The generic flag indicates that that we're trying to match to a
|
||||
default or generic vhost
|
||||
|
||||
.. todo:: Handle _default_
|
||||
|
||||
"""
|
||||
|
||||
if vhost.get_names() != self.get_names():
|
||||
return False
|
||||
if not generic:
|
||||
if vhost.get_names() != self.get_names():
|
||||
return False
|
||||
|
||||
# If equal and set is not empty... assume same server
|
||||
if self.name is not None or self.aliases:
|
||||
return True
|
||||
# If equal and set is not empty... assume same server
|
||||
if self.name is not None or self.aliases:
|
||||
return True
|
||||
# If we're looking for a generic vhost,
|
||||
# don't return one with a ServerName
|
||||
elif self.name:
|
||||
return False
|
||||
|
||||
# Both sets of names are empty.
|
||||
|
||||
|
|
@ -6,9 +6,9 @@ import os
|
|||
import re
|
||||
import subprocess
|
||||
|
||||
from letsencrypt import errors
|
||||
from certbot import errors
|
||||
|
||||
from letsencrypt_apache import constants
|
||||
from certbot_apache import constants
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -477,7 +477,7 @@ class ApacheParser(object):
|
|||
# Note: This works for augeas globs, ie. *.conf
|
||||
if use_new:
|
||||
inc_test = self.aug.match(
|
||||
"/augeas/load/Httpd/incl [. ='%s']" % filepath)
|
||||
"/augeas/load/Httpd['%s' =~ glob(incl)]" % filepath)
|
||||
if not inc_test:
|
||||
# Load up files
|
||||
# This doesn't seem to work on TravisCI
|
||||
1
certbot-apache/certbot_apache/tests/__init__.py
Normal file
1
certbot-apache/certbot_apache/tests/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
"""Certbot Apache Tests"""
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
Issues for which some kind of test case should be constructable, but we do not
|
||||
currently have one:
|
||||
|
||||
https://github.com/certbot/certbot/issues/1213
|
||||
https://github.com/certbot/certbot/issues/1602
|
||||
|
||||
|
|
@ -5,9 +5,7 @@
|
|||
|
||||
export EA=/etc/apache2/
|
||||
TESTDIR="`dirname $0`"
|
||||
LEROOT="`realpath \"$TESTDIR/../../../../\"`"
|
||||
cd $TESTDIR/passing
|
||||
LETSENCRYPT="${LETSENCRYPT:-$LEROOT/venv/bin/letsencrypt}"
|
||||
|
||||
function CleanupExit() {
|
||||
echo control c, exiting tests...
|
||||
|
|
@ -21,13 +19,13 @@ function Setup() {
|
|||
if [ "$APPEND_APACHECONF" = "" ] ; then
|
||||
sudo cp "$f" "$EA"/sites-available/
|
||||
sudo ln -sf "$EA/sites-available/$f" "$EA/sites-enabled/$f"
|
||||
sudo echo """
|
||||
echo "
|
||||
<VirtualHost *:80>
|
||||
ServerName example.com
|
||||
DocumentRoot /tmp/
|
||||
ErrorLog /tmp/error.log
|
||||
CustomLog /tmp/requests.log combined
|
||||
</VirtualHost>""" >> $EA/sites-available/throwaway-example.conf
|
||||
</VirtualHost>" | sudo tee $EA/sites-available/throwaway-example.conf >/dev/null
|
||||
else
|
||||
TMP="/tmp/`basename \"$APPEND_APACHECONF\"`.$$"
|
||||
sudo cp -a "$APPEND_APACHECONF" "$TMP"
|
||||
|
|
@ -61,7 +59,7 @@ trap CleanupExit INT
|
|||
for f in *.conf ; do
|
||||
echo -n testing "$f"...
|
||||
Setup
|
||||
RESULT=`echo c | sudo "$LETSENCRYPT" -vvvv --debug --staging --apache --register-unsafely-without-email --agree-tos certonly -t 2>&1`
|
||||
RESULT=`echo c | sudo $(command -v certbot) -vvvv --debug --staging --apache --register-unsafely-without-email --agree-tos certonly -t 2>&1`
|
||||
if echo $RESULT | grep -Eq \("Which names would you like"\|"mod_macro is not yet"\) ; then
|
||||
echo passed
|
||||
else
|
||||
|
|
@ -39,7 +39,7 @@
|
|||
# certificate chain for the server certificate. Alternatively
|
||||
# the referenced file can be the same as SSLCertificateFile
|
||||
# when the CA certificates are directly appended to the server
|
||||
# certificate for convinience.
|
||||
# certificate for convenience.
|
||||
#SSLCertificateChainFile /etc/apache2/ssl.crt/server-ca.crt
|
||||
|
||||
# Certificate Authority (CA):
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<VirtualHost *:80 [::]:80>
|
||||
DocumentRoot /tmp
|
||||
ServerName example.com
|
||||
ServerAlias www.example.com
|
||||
CustomLog ${APACHE_LOG_DIR}/example.log combined
|
||||
<Directory "/tmp">
|
||||
AllowOverride All
|
||||
</Directory>
|
||||
</VirtualHost>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<VirtualHost *:443 [::]:443>
|
||||
DocumentRoot /tmp
|
||||
ServerName example.com
|
||||
ServerAlias www.example.com
|
||||
CustomLog ${APACHE_LOG_DIR}/example.log combined
|
||||
<Directory "/tmp">
|
||||
AllowOverride All
|
||||
</Directory>
|
||||
|
||||
SSLEngine on
|
||||
|
||||
SSLHonorCipherOrder On
|
||||
SSLProtocol all -SSLv2 -SSLv3
|
||||
SSLCipherSuite "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH +aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS"
|
||||
|
||||
SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
|
||||
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
|
||||
</VirtualHost>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<VirtualHost [::]:80 *:80>
|
||||
DocumentRoot /tmp
|
||||
ServerName example.com
|
||||
ServerAlias www.example.com
|
||||
CustomLog ${APACHE_LOG_DIR}/example.log combined
|
||||
<Directory "/tmp">
|
||||
AllowOverride All
|
||||
</Directory>
|
||||
</VirtualHost>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<VirtualHost [::]:443 *:443>
|
||||
DocumentRoot /tmp
|
||||
ServerName example.com
|
||||
ServerAlias www.example.com
|
||||
CustomLog ${APACHE_LOG_DIR}/example.log combined
|
||||
<Directory "/tmp">
|
||||
AllowOverride All
|
||||
</Directory>
|
||||
|
||||
SSLEngine on
|
||||
|
||||
SSLHonorCipherOrder On
|
||||
SSLProtocol all -SSLv2 -SSLv3
|
||||
SSLCipherSuite "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH +aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS"
|
||||
|
||||
SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
|
||||
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
|
||||
</VirtualHost>
|
||||
|
|
@ -0,0 +1,284 @@
|
|||
|
||||
NameVirtualHost 0.0.0.0:7080
|
||||
NameVirtualHost [00000:000:000:000::0]:7080
|
||||
NameVirtualHost 0.0.0.0:7080
|
||||
|
||||
NameVirtualHost 127.0.0.1:7080
|
||||
NameVirtualHost 0.0.0.0:7081
|
||||
NameVirtualHost [0000:000:000:000::2]:7081
|
||||
NameVirtualHost 0.0.0.0:7081
|
||||
|
||||
NameVirtualHost 127.0.0.1:7081
|
||||
|
||||
ServerName "example.com"
|
||||
ServerAdmin "srv@example.com"
|
||||
|
||||
DocumentRoot /tmp
|
||||
|
||||
<IfModule mod_logio.c>
|
||||
LogFormat "%a %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" plesklog
|
||||
</IfModule>
|
||||
<IfModule !mod_logio.c>
|
||||
LogFormat "%a %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_security2.c>
|
||||
SecRuleEngine DetectionOnly
|
||||
SecRequestBodyAccess On
|
||||
SecRequestBodyLimit 134217728
|
||||
SecResponseBodyAccess Off
|
||||
SecResponseBodyLimit 524288
|
||||
SecAuditEngine On
|
||||
SecAuditLog "/var/log/modsec_audit.log"
|
||||
SecAuditLogType serial
|
||||
</IfModule>
|
||||
|
||||
#Include "/etc/httpd/conf/plesk.conf.d/ip_default/*.conf"
|
||||
|
||||
<VirtualHost \
|
||||
0.0.0.0:7080 \
|
||||
[00000:000:000:0000::2]:7080 \
|
||||
0.0.0.0:7080 \
|
||||
127.0.0.1:7080 \
|
||||
>
|
||||
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 \
|
||||
0.0.0.0:7081 \
|
||||
127.0.0.1:7081 \
|
||||
>
|
||||
ServerName "default-0_0_0_0"
|
||||
UseCanonicalName Off
|
||||
DocumentRoot /tmp
|
||||
ScriptAlias "/cgi-bin/" "/var/www/vhosts/default/cgi-bin"
|
||||
|
||||
SSLEngine on
|
||||
SSLVerifyClient none
|
||||
SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
|
||||
|
||||
<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 \
|
||||
[00000:000:000:000::2]:7081 \
|
||||
127.0.0.1:7081 \
|
||||
>
|
||||
ServerName "default-0000_000_000_00000__2"
|
||||
UseCanonicalName Off
|
||||
DocumentRoot /tmp
|
||||
ScriptAlias "/cgi-bin/" "/var/www/vhosts/default/cgi-bin"
|
||||
|
||||
SSLEngine on
|
||||
SSLVerifyClient none
|
||||
SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
|
||||
|
||||
<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 \
|
||||
0.0.0.0:7081 \
|
||||
127.0.0.1:7081 \
|
||||
>
|
||||
ServerName "default-0_0_0_0"
|
||||
UseCanonicalName Off
|
||||
DocumentRoot /tmp
|
||||
ScriptAlias "/cgi-bin/" "/var/www/vhosts/default/cgi-bin"
|
||||
|
||||
SSLEngine on
|
||||
SSLVerifyClient none
|
||||
SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
|
||||
|
||||
#SSLCACertificateFile "/usr/local/psa/var/certificates/cert-nLy6Z1"
|
||||
|
||||
<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 \
|
||||
0.0.0.0:7080 \
|
||||
[0000:000:000:000::2]:7080 \
|
||||
0.0.0.0:7080 \
|
||||
127.0.0.1:7080 \
|
||||
>
|
||||
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 \
|
||||
0.0.0.0:7081 \
|
||||
[00000:000:000:0000::2]:7081 \
|
||||
0.0.0.0:7081 \
|
||||
127.0.0.1:7081 \
|
||||
>
|
||||
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 /etc/ssl/certs/ssl-cert-snakeoil.pem
|
||||
|
||||
<Directory "/var/lib/mailman/archives/">
|
||||
Options FollowSymLinks
|
||||
Order allow,deny
|
||||
Allow from all
|
||||
</Directory>
|
||||
|
||||
</VirtualHost>
|
||||
</IfModule>
|
||||
|
||||
<IfModule mod_rpaf.c>
|
||||
RPAFproxy_ips 0.0.0.0 [00000:000:000:00000::2] 0.0.0.0
|
||||
</IfModule>
|
||||
<IfModule mod_rpaf-2.0.c>
|
||||
RPAFproxy_ips 0.0.0.0 [0000:000:000:0000::2] 0.0.0.0
|
||||
</IfModule>
|
||||
<IfModule mod_remoteip.c>
|
||||
RemoteIPInternalProxy 0.0.0.0 [0000:000:000:0000::2] 0.0.0.0
|
||||
RemoteIPHeader X-Forwarded-For
|
||||
</IfModule>
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
"""Test for letsencrypt_apache.augeas_configurator."""
|
||||
"""Test for certbot_apache.augeas_configurator."""
|
||||
import os
|
||||
import shutil
|
||||
import unittest
|
||||
|
||||
import mock
|
||||
|
||||
from letsencrypt import errors
|
||||
from certbot import errors
|
||||
|
||||
from letsencrypt_apache.tests import util
|
||||
from certbot_apache.tests import util
|
||||
|
||||
|
||||
class AugeasConfiguratorTest(util.ApacheTest):
|
||||
|
|
@ -20,7 +20,7 @@ class AugeasConfiguratorTest(util.ApacheTest):
|
|||
self.config_path, self.vhost_path, self.config_dir, self.work_dir)
|
||||
|
||||
self.vh_truth = util.get_vh_truth(
|
||||
self.temp_dir, "debian_apache_2_4/two_vhost_80")
|
||||
self.temp_dir, "debian_apache_2_4/multiple_vhosts")
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.config_dir)
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
"""Tests for letsencrypt_apache.parser."""
|
||||
"""Tests for certbot_apache.parser."""
|
||||
import os
|
||||
import shutil
|
||||
import unittest
|
||||
|
||||
from letsencrypt import errors
|
||||
from certbot import errors
|
||||
|
||||
from letsencrypt_apache.tests import util
|
||||
from certbot_apache.tests import util
|
||||
|
||||
|
||||
class ComplexParserTest(util.ParserTest):
|
||||
|
|
@ -88,7 +88,7 @@ class ComplexParserTest(util.ParserTest):
|
|||
|
||||
def verify_fnmatch(self, arg, hit=True):
|
||||
"""Test if Include was correctly parsed."""
|
||||
from letsencrypt_apache import parser
|
||||
from certbot_apache import parser
|
||||
self.parser.add_dir(parser.get_aug_path(self.parser.loc["default"]),
|
||||
"Include", [arg])
|
||||
if hit:
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
# pylint: disable=too-many-public-methods
|
||||
"""Test for letsencrypt_apache.configurator."""
|
||||
"""Test for certbot_apache.configurator."""
|
||||
import os
|
||||
import shutil
|
||||
import socket
|
||||
|
|
@ -9,28 +9,28 @@ import mock
|
|||
|
||||
from acme import challenges
|
||||
|
||||
from letsencrypt import achallenges
|
||||
from letsencrypt import errors
|
||||
from certbot import achallenges
|
||||
from certbot import errors
|
||||
|
||||
from letsencrypt.tests import acme_util
|
||||
from certbot.tests import acme_util
|
||||
|
||||
from letsencrypt_apache import configurator
|
||||
from letsencrypt_apache import obj
|
||||
from certbot_apache import configurator
|
||||
from certbot_apache import obj
|
||||
|
||||
from letsencrypt_apache.tests import util
|
||||
from certbot_apache.tests import util
|
||||
|
||||
|
||||
class TwoVhost80Test(util.ApacheTest):
|
||||
class MultipleVhostsTest(util.ApacheTest):
|
||||
"""Test two standard well-configured HTTP vhosts."""
|
||||
|
||||
def setUp(self): # pylint: disable=arguments-differ
|
||||
super(TwoVhost80Test, self).setUp()
|
||||
super(MultipleVhostsTest, self).setUp()
|
||||
|
||||
self.config = util.get_apache_configurator(
|
||||
self.config_path, self.vhost_path, self.config_dir, self.work_dir)
|
||||
self.config = self.mock_deploy_cert(self.config)
|
||||
self.vh_truth = util.get_vh_truth(
|
||||
self.temp_dir, "debian_apache_2_4/two_vhost_80")
|
||||
self.temp_dir, "debian_apache_2_4/multiple_vhosts")
|
||||
|
||||
def mock_deploy_cert(self, config):
|
||||
"""A test for a mock deploy cert"""
|
||||
|
|
@ -38,7 +38,7 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
|
||||
def mocked_deploy_cert(*args, **kwargs):
|
||||
"""a helper to mock a deployed cert"""
|
||||
with mock.patch("letsencrypt_apache.configurator.ApacheConfigurator.enable_mod"):
|
||||
with mock.patch("certbot_apache.configurator.ApacheConfigurator.enable_mod"):
|
||||
config.real_deploy_cert(*args, **kwargs)
|
||||
self.config.deploy_cert = mocked_deploy_cert
|
||||
return self.config
|
||||
|
|
@ -48,14 +48,14 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
shutil.rmtree(self.config_dir)
|
||||
shutil.rmtree(self.work_dir)
|
||||
|
||||
@mock.patch("letsencrypt_apache.configurator.le_util.exe_exists")
|
||||
@mock.patch("certbot_apache.configurator.le_util.exe_exists")
|
||||
def test_prepare_no_install(self, mock_exe_exists):
|
||||
mock_exe_exists.return_value = False
|
||||
self.assertRaises(
|
||||
errors.NoInstallationError, self.config.prepare)
|
||||
|
||||
@mock.patch("letsencrypt_apache.parser.ApacheParser")
|
||||
@mock.patch("letsencrypt_apache.configurator.le_util.exe_exists")
|
||||
@mock.patch("certbot_apache.parser.ApacheParser")
|
||||
@mock.patch("certbot_apache.configurator.le_util.exe_exists")
|
||||
def test_prepare_version(self, mock_exe_exists, _):
|
||||
mock_exe_exists.return_value = True
|
||||
self.config.version = None
|
||||
|
|
@ -65,8 +65,8 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
self.assertRaises(
|
||||
errors.NotSupportedError, self.config.prepare)
|
||||
|
||||
@mock.patch("letsencrypt_apache.parser.ApacheParser")
|
||||
@mock.patch("letsencrypt_apache.configurator.le_util.exe_exists")
|
||||
@mock.patch("certbot_apache.parser.ApacheParser")
|
||||
@mock.patch("certbot_apache.configurator.le_util.exe_exists")
|
||||
def test_prepare_old_aug(self, mock_exe_exists, _):
|
||||
mock_exe_exists.return_value = True
|
||||
self.config.config_test = mock.Mock()
|
||||
|
|
@ -76,7 +76,7 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
errors.NotSupportedError, self.config.prepare)
|
||||
|
||||
def test_add_parser_arguments(self): # pylint: disable=no-self-use
|
||||
from letsencrypt_apache.configurator import ApacheConfigurator
|
||||
from certbot_apache.configurator import ApacheConfigurator
|
||||
# Weak test..
|
||||
ApacheConfigurator.add_parser_arguments(mock.MagicMock())
|
||||
|
||||
|
|
@ -85,10 +85,10 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
mock_getutility.notification = mock.MagicMock(return_value=True)
|
||||
names = self.config.get_all_names()
|
||||
self.assertEqual(names, set(
|
||||
["letsencrypt.demo", "encryption-example.demo", "ip-172-30-0-17"]))
|
||||
["certbot.demo", "encryption-example.demo", "ip-172-30-0-17", "*.blue.purple.com"]))
|
||||
|
||||
@mock.patch("zope.component.getUtility")
|
||||
@mock.patch("letsencrypt_apache.configurator.socket.gethostbyaddr")
|
||||
@mock.patch("certbot_apache.configurator.socket.gethostbyaddr")
|
||||
def test_get_all_names_addrs(self, mock_gethost, mock_getutility):
|
||||
mock_gethost.side_effect = [("google.com", "", ""), socket.error]
|
||||
notification = mock.Mock()
|
||||
|
|
@ -103,10 +103,10 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
self.config.vhosts.append(vhost)
|
||||
|
||||
names = self.config.get_all_names()
|
||||
self.assertEqual(len(names), 5)
|
||||
self.assertEqual(len(names), 6)
|
||||
self.assertTrue("zombo.com" in names)
|
||||
self.assertTrue("google.com" in names)
|
||||
self.assertTrue("letsencrypt.demo" in names)
|
||||
self.assertTrue("certbot.demo" in names)
|
||||
|
||||
def test_add_servernames_alias(self):
|
||||
self.config.parser.add_dir(
|
||||
|
|
@ -124,7 +124,7 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
|
||||
"""
|
||||
vhs = self.config.get_virtual_hosts()
|
||||
self.assertEqual(len(vhs), 6)
|
||||
self.assertEqual(len(vhs), 7)
|
||||
found = 0
|
||||
|
||||
for vhost in vhs:
|
||||
|
|
@ -135,29 +135,29 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
else:
|
||||
raise Exception("Missed: %s" % vhost) # pragma: no cover
|
||||
|
||||
self.assertEqual(found, 6)
|
||||
self.assertEqual(found, 7)
|
||||
|
||||
# Handle case of non-debian layout get_virtual_hosts
|
||||
with mock.patch(
|
||||
"letsencrypt_apache.configurator.ApacheConfigurator.conf"
|
||||
"certbot_apache.configurator.ApacheConfigurator.conf"
|
||||
) as mock_conf:
|
||||
mock_conf.return_value = False
|
||||
vhs = self.config.get_virtual_hosts()
|
||||
self.assertEqual(len(vhs), 6)
|
||||
self.assertEqual(len(vhs), 7)
|
||||
|
||||
@mock.patch("letsencrypt_apache.display_ops.select_vhost")
|
||||
@mock.patch("certbot_apache.display_ops.select_vhost")
|
||||
def test_choose_vhost_none_avail(self, mock_select):
|
||||
mock_select.return_value = None
|
||||
self.assertRaises(
|
||||
errors.PluginError, self.config.choose_vhost, "none.com")
|
||||
|
||||
@mock.patch("letsencrypt_apache.display_ops.select_vhost")
|
||||
@mock.patch("certbot_apache.display_ops.select_vhost")
|
||||
def test_choose_vhost_select_vhost_ssl(self, mock_select):
|
||||
mock_select.return_value = self.vh_truth[1]
|
||||
self.assertEqual(
|
||||
self.vh_truth[1], self.config.choose_vhost("none.com"))
|
||||
|
||||
@mock.patch("letsencrypt_apache.display_ops.select_vhost")
|
||||
@mock.patch("certbot_apache.display_ops.select_vhost")
|
||||
def test_choose_vhost_select_vhost_non_ssl(self, mock_select):
|
||||
mock_select.return_value = self.vh_truth[0]
|
||||
chosen_vhost = self.config.choose_vhost("none.com")
|
||||
|
|
@ -169,13 +169,13 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
self.assertFalse(self.vh_truth[0].ssl)
|
||||
self.assertTrue(chosen_vhost.ssl)
|
||||
|
||||
@mock.patch("letsencrypt_apache.display_ops.select_vhost")
|
||||
@mock.patch("certbot_apache.display_ops.select_vhost")
|
||||
def test_choose_vhost_select_vhost_with_temp(self, mock_select):
|
||||
mock_select.return_value = self.vh_truth[0]
|
||||
chosen_vhost = self.config.choose_vhost("none.com", temp=True)
|
||||
self.assertEqual(self.vh_truth[0], chosen_vhost)
|
||||
|
||||
@mock.patch("letsencrypt_apache.display_ops.select_vhost")
|
||||
@mock.patch("certbot_apache.display_ops.select_vhost")
|
||||
def test_choose_vhost_select_vhost_conflicting_non_ssl(self, mock_select):
|
||||
mock_select.return_value = self.vh_truth[3]
|
||||
conflicting_vhost = obj.VirtualHost(
|
||||
|
|
@ -186,10 +186,24 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
self.assertRaises(
|
||||
errors.PluginError, self.config.choose_vhost, "none.com")
|
||||
|
||||
def test_choosevhost_select_vhost_with_wildcard(self):
|
||||
chosen_vhost = self.config.choose_vhost("blue.purple.com", temp=True)
|
||||
self.assertEqual(self.vh_truth[6], chosen_vhost)
|
||||
|
||||
def test_findbest_continues_on_short_domain(self):
|
||||
# pylint: disable=protected-access
|
||||
chosen_vhost = self.config._find_best_vhost("purple.com")
|
||||
self.assertEqual(None, chosen_vhost)
|
||||
|
||||
def test_findbest_continues_on_long_domain(self):
|
||||
# pylint: disable=protected-access
|
||||
chosen_vhost = self.config._find_best_vhost("green.red.purple.com")
|
||||
self.assertEqual(None, chosen_vhost)
|
||||
|
||||
def test_find_best_vhost(self):
|
||||
# pylint: disable=protected-access
|
||||
self.assertEqual(
|
||||
self.vh_truth[3], self.config._find_best_vhost("letsencrypt.demo"))
|
||||
self.vh_truth[3], self.config._find_best_vhost("certbot.demo"))
|
||||
self.assertEqual(
|
||||
self.vh_truth[0],
|
||||
self.config._find_best_vhost("encryption-example.demo"))
|
||||
|
|
@ -210,7 +224,8 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
# Assume only the two default vhosts.
|
||||
self.config.vhosts = [
|
||||
vh for vh in self.config.vhosts
|
||||
if vh.name not in ["letsencrypt.demo", "encryption-example.demo"]
|
||||
if vh.name not in ["certbot.demo", "encryption-example.demo"]
|
||||
and "*.blue.purple.com" not in vh.aliases
|
||||
]
|
||||
|
||||
self.assertEqual(
|
||||
|
|
@ -218,7 +233,7 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
|
||||
def test_non_default_vhosts(self):
|
||||
# pylint: disable=protected-access
|
||||
self.assertEqual(len(self.config._non_default_vhosts()), 4)
|
||||
self.assertEqual(len(self.config._non_default_vhosts()), 5)
|
||||
|
||||
def test_is_site_enabled(self):
|
||||
"""Test if site is enabled.
|
||||
|
|
@ -239,9 +254,9 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
self.config.is_site_enabled,
|
||||
"irrelevant")
|
||||
|
||||
@mock.patch("letsencrypt.le_util.run_script")
|
||||
@mock.patch("letsencrypt.le_util.exe_exists")
|
||||
@mock.patch("letsencrypt_apache.parser.subprocess.Popen")
|
||||
@mock.patch("certbot.le_util.run_script")
|
||||
@mock.patch("certbot.le_util.exe_exists")
|
||||
@mock.patch("certbot_apache.parser.subprocess.Popen")
|
||||
def test_enable_mod(self, mock_popen, mock_exe_exists, mock_run_script):
|
||||
mock_popen().communicate.return_value = ("Define: DUMP_RUN_CFG", "")
|
||||
mock_popen().returncode = 0
|
||||
|
|
@ -258,7 +273,7 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
self.assertRaises(
|
||||
errors.NotSupportedError, self.config.enable_mod, "ssl")
|
||||
|
||||
@mock.patch("letsencrypt.le_util.exe_exists")
|
||||
@mock.patch("certbot.le_util.exe_exists")
|
||||
def test_enable_mod_no_disable(self, mock_exe_exists):
|
||||
mock_exe_exists.return_value = False
|
||||
self.assertRaises(
|
||||
|
|
@ -524,7 +539,7 @@ class TwoVhost80Test(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), 7)
|
||||
self.assertEqual(len(self.config.vhosts), 8)
|
||||
|
||||
def test_clean_vhost_ssl(self):
|
||||
# pylint: disable=protected-access
|
||||
|
|
@ -621,8 +636,8 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
self.config._add_name_vhost_if_necessary(self.vh_truth[0])
|
||||
self.assertEqual(self.config.save.call_count, 2)
|
||||
|
||||
@mock.patch("letsencrypt_apache.configurator.tls_sni_01.ApacheTlsSni01.perform")
|
||||
@mock.patch("letsencrypt_apache.configurator.ApacheConfigurator.restart")
|
||||
@mock.patch("certbot_apache.configurator.tls_sni_01.ApacheTlsSni01.perform")
|
||||
@mock.patch("certbot_apache.configurator.ApacheConfigurator.restart")
|
||||
def test_perform(self, mock_restart, mock_perform):
|
||||
# Only tests functionality specific to configurator.perform
|
||||
# Note: As more challenges are offered this will have to be expanded
|
||||
|
|
@ -641,7 +656,7 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
|
||||
self.assertEqual(mock_restart.call_count, 1)
|
||||
|
||||
@mock.patch("letsencrypt_apache.configurator.ApacheConfigurator.restart")
|
||||
@mock.patch("certbot_apache.configurator.ApacheConfigurator.restart")
|
||||
def test_cleanup(self, mock_restart):
|
||||
_, achall1, achall2 = self.get_achalls()
|
||||
|
||||
|
|
@ -654,7 +669,7 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
self.config.cleanup([achall2])
|
||||
self.assertTrue(mock_restart.called)
|
||||
|
||||
@mock.patch("letsencrypt_apache.configurator.ApacheConfigurator.restart")
|
||||
@mock.patch("certbot_apache.configurator.ApacheConfigurator.restart")
|
||||
def test_cleanup_no_errors(self, mock_restart):
|
||||
_, achall1, achall2 = self.get_achalls()
|
||||
|
||||
|
|
@ -666,7 +681,7 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
self.config.cleanup([achall1, achall2])
|
||||
self.assertTrue(mock_restart.called)
|
||||
|
||||
@mock.patch("letsencrypt.le_util.run_script")
|
||||
@mock.patch("certbot.le_util.run_script")
|
||||
def test_get_version(self, mock_script):
|
||||
mock_script.return_value = (
|
||||
"Server Version: Apache/2.4.2 (Debian)", "")
|
||||
|
|
@ -688,21 +703,21 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
mock_script.side_effect = errors.SubprocessError("Can't find program")
|
||||
self.assertRaises(errors.PluginError, self.config.get_version)
|
||||
|
||||
@mock.patch("letsencrypt_apache.configurator.le_util.run_script")
|
||||
@mock.patch("certbot_apache.configurator.le_util.run_script")
|
||||
def test_restart(self, _):
|
||||
self.config.restart()
|
||||
|
||||
@mock.patch("letsencrypt_apache.configurator.le_util.run_script")
|
||||
@mock.patch("certbot_apache.configurator.le_util.run_script")
|
||||
def test_restart_bad_process(self, mock_run_script):
|
||||
mock_run_script.side_effect = [None, errors.SubprocessError]
|
||||
|
||||
self.assertRaises(errors.MisconfigurationError, self.config.restart)
|
||||
|
||||
@mock.patch("letsencrypt.le_util.run_script")
|
||||
@mock.patch("certbot.le_util.run_script")
|
||||
def test_config_test(self, _):
|
||||
self.config.config_test()
|
||||
|
||||
@mock.patch("letsencrypt.le_util.run_script")
|
||||
@mock.patch("certbot.le_util.run_script")
|
||||
def test_config_test_bad_process(self, mock_run_script):
|
||||
mock_run_script.side_effect = errors.SubprocessError
|
||||
|
||||
|
|
@ -732,7 +747,7 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
self.assertTrue(isinstance(self.config.get_chall_pref(""), list))
|
||||
|
||||
def test_install_ssl_options_conf(self):
|
||||
from letsencrypt_apache.configurator import install_ssl_options_conf
|
||||
from certbot_apache.configurator import install_ssl_options_conf
|
||||
path = os.path.join(self.work_dir, "test_it")
|
||||
install_ssl_options_conf(path)
|
||||
self.assertTrue(os.path.isfile(path))
|
||||
|
|
@ -741,14 +756,14 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
def test_supported_enhancements(self):
|
||||
self.assertTrue(isinstance(self.config.supported_enhancements(), list))
|
||||
|
||||
@mock.patch("letsencrypt.le_util.exe_exists")
|
||||
@mock.patch("certbot.le_util.exe_exists")
|
||||
def test_enhance_unknown_vhost(self, mock_exe):
|
||||
self.config.parser.modules.add("rewrite_module")
|
||||
mock_exe.return_value = True
|
||||
ssl_vh = obj.VirtualHost(
|
||||
"fp", "ap", set([obj.Addr(("*", "443")),
|
||||
obj.Addr(("satoshi.com",))]),
|
||||
"fp", "ap", set([obj.Addr(("*", "443"))]),
|
||||
True, False)
|
||||
ssl_vh.name = "satoshi.com"
|
||||
self.config.vhosts.append(ssl_vh)
|
||||
self.assertRaises(
|
||||
errors.PluginError,
|
||||
|
|
@ -757,23 +772,23 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
def test_enhance_unknown_enhancement(self):
|
||||
self.assertRaises(
|
||||
errors.PluginError,
|
||||
self.config.enhance, "letsencrypt.demo", "unknown_enhancement")
|
||||
self.config.enhance, "certbot.demo", "unknown_enhancement")
|
||||
|
||||
@mock.patch("letsencrypt.le_util.run_script")
|
||||
@mock.patch("letsencrypt.le_util.exe_exists")
|
||||
@mock.patch("certbot.le_util.run_script")
|
||||
@mock.patch("certbot.le_util.exe_exists")
|
||||
def test_http_header_hsts(self, mock_exe, _):
|
||||
self.config.parser.update_runtime_variables = mock.Mock()
|
||||
self.config.parser.modules.add("mod_ssl.c")
|
||||
mock_exe.return_value = True
|
||||
|
||||
# This will create an ssl vhost for letsencrypt.demo
|
||||
self.config.enhance("letsencrypt.demo", "ensure-http-header",
|
||||
# This will create an ssl vhost for certbot.demo
|
||||
self.config.enhance("certbot.demo", "ensure-http-header",
|
||||
"Strict-Transport-Security")
|
||||
|
||||
self.assertTrue("headers_module" in self.config.parser.modules)
|
||||
|
||||
# Get the ssl vhost for letsencrypt.demo
|
||||
ssl_vhost = self.config.assoc["letsencrypt.demo"]
|
||||
# Get the ssl vhost for certbot.demo
|
||||
ssl_vhost = self.config.assoc["certbot.demo"]
|
||||
|
||||
# These are not immediately available in find_dir even with save() and
|
||||
# load(). They must be found in sites-available
|
||||
|
|
@ -788,7 +803,7 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
# skip the enable mod
|
||||
self.config.parser.modules.add("headers_module")
|
||||
|
||||
# This will create an ssl vhost for letsencrypt.demo
|
||||
# This will create an ssl vhost for certbot.demo
|
||||
self.config.enhance("encryption-example.demo", "ensure-http-header",
|
||||
"Strict-Transport-Security")
|
||||
|
||||
|
|
@ -797,21 +812,21 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
self.config.enhance, "encryption-example.demo",
|
||||
"ensure-http-header", "Strict-Transport-Security")
|
||||
|
||||
@mock.patch("letsencrypt.le_util.run_script")
|
||||
@mock.patch("letsencrypt.le_util.exe_exists")
|
||||
@mock.patch("certbot.le_util.run_script")
|
||||
@mock.patch("certbot.le_util.exe_exists")
|
||||
def test_http_header_uir(self, mock_exe, _):
|
||||
self.config.parser.update_runtime_variables = mock.Mock()
|
||||
self.config.parser.modules.add("mod_ssl.c")
|
||||
mock_exe.return_value = True
|
||||
|
||||
# This will create an ssl vhost for letsencrypt.demo
|
||||
self.config.enhance("letsencrypt.demo", "ensure-http-header",
|
||||
# This will create an ssl vhost for certbot.demo
|
||||
self.config.enhance("certbot.demo", "ensure-http-header",
|
||||
"Upgrade-Insecure-Requests")
|
||||
|
||||
self.assertTrue("headers_module" in self.config.parser.modules)
|
||||
|
||||
# Get the ssl vhost for letsencrypt.demo
|
||||
ssl_vhost = self.config.assoc["letsencrypt.demo"]
|
||||
# Get the ssl vhost for certbot.demo
|
||||
ssl_vhost = self.config.assoc["certbot.demo"]
|
||||
|
||||
# These are not immediately available in find_dir even with save() and
|
||||
# load(). They must be found in sites-available
|
||||
|
|
@ -826,7 +841,7 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
# skip the enable mod
|
||||
self.config.parser.modules.add("headers_module")
|
||||
|
||||
# This will create an ssl vhost for letsencrypt.demo
|
||||
# This will create an ssl vhost for certbot.demo
|
||||
self.config.enhance("encryption-example.demo", "ensure-http-header",
|
||||
"Upgrade-Insecure-Requests")
|
||||
|
||||
|
|
@ -835,15 +850,15 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
self.config.enhance, "encryption-example.demo",
|
||||
"ensure-http-header", "Upgrade-Insecure-Requests")
|
||||
|
||||
@mock.patch("letsencrypt.le_util.run_script")
|
||||
@mock.patch("letsencrypt.le_util.exe_exists")
|
||||
@mock.patch("certbot.le_util.run_script")
|
||||
@mock.patch("certbot.le_util.exe_exists")
|
||||
def test_redirect_well_formed_http(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))
|
||||
|
||||
# This will create an ssl vhost for letsencrypt.demo
|
||||
self.config.enhance("letsencrypt.demo", "redirect")
|
||||
# This will create an ssl vhost for certbot.demo
|
||||
self.config.enhance("certbot.demo", "redirect")
|
||||
|
||||
# These are not immediately available in find_dir even with save() and
|
||||
# load(). They must be found in sites-available
|
||||
|
|
@ -879,8 +894,8 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
# pylint: disable=protected-access
|
||||
self.assertTrue(self.config._is_rewrite_engine_on(self.vh_truth[3]))
|
||||
|
||||
@mock.patch("letsencrypt.le_util.run_script")
|
||||
@mock.patch("letsencrypt.le_util.exe_exists")
|
||||
@mock.patch("certbot.le_util.run_script")
|
||||
@mock.patch("certbot.le_util.exe_exists")
|
||||
def test_redirect_with_existing_rewrite(self, mock_exe, _):
|
||||
self.config.parser.update_runtime_variables = mock.Mock()
|
||||
mock_exe.return_value = True
|
||||
|
|
@ -892,8 +907,8 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
"UnknownTarget"])
|
||||
self.config.save()
|
||||
|
||||
# This will create an ssl vhost for letsencrypt.demo
|
||||
self.config.enhance("letsencrypt.demo", "redirect")
|
||||
# This will create an ssl vhost for certbot.demo
|
||||
self.config.enhance("certbot.demo", "redirect")
|
||||
|
||||
# These are not immediately available in find_dir even with save() and
|
||||
# load(). They must be found in sites-available
|
||||
|
|
@ -942,7 +957,7 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
|
||||
# pylint: disable=protected-access
|
||||
self.config._enable_redirect(self.vh_truth[1], "")
|
||||
self.assertEqual(len(self.config.vhosts), 7)
|
||||
self.assertEqual(len(self.config.vhosts), 8)
|
||||
|
||||
def test_create_own_redirect_for_old_apache_version(self):
|
||||
self.config.parser.modules.add("rewrite_module")
|
||||
|
|
@ -953,7 +968,7 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
|
||||
# pylint: disable=protected-access
|
||||
self.config._enable_redirect(self.vh_truth[1], "")
|
||||
self.assertEqual(len(self.config.vhosts), 7)
|
||||
self.assertEqual(len(self.config.vhosts), 8)
|
||||
|
||||
def test_sift_line(self):
|
||||
# pylint: disable=protected-access
|
||||
|
|
@ -966,7 +981,7 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
normal_target = "RewriteRule ^/(.*) http://www.a.com:1234/$1 [L,R]"
|
||||
self.assertFalse(self.config._sift_line(normal_target))
|
||||
|
||||
@mock.patch("letsencrypt_apache.configurator.zope.component.getUtility")
|
||||
@mock.patch("certbot_apache.configurator.zope.component.getUtility")
|
||||
def test_make_vhost_ssl_with_existing_rewrite_rule(self, mock_get_utility):
|
||||
self.config.parser.modules.add("rewrite_module")
|
||||
|
||||
|
|
@ -1009,7 +1024,7 @@ class TwoVhost80Test(util.ApacheTest):
|
|||
challenges.TLSSNI01(
|
||||
token="uqnaPzxtrndteOqtrXb0Asl5gOJfWAnnx6QJyvcmlDU"),
|
||||
"pending"),
|
||||
domain="letsencrypt.demo", account_key=account_key)
|
||||
domain="certbot.demo", account_key=account_key)
|
||||
|
||||
return account_key, achall1, achall2
|
||||
|
||||
|
|
@ -1,26 +1,26 @@
|
|||
"""Test for letsencrypt_apache.configurator."""
|
||||
"""Test for certbot_apache.configurator."""
|
||||
|
||||
import mock
|
||||
import unittest
|
||||
|
||||
from letsencrypt_apache import constants
|
||||
from certbot_apache import constants
|
||||
|
||||
|
||||
class ConstantsTest(unittest.TestCase):
|
||||
|
||||
@mock.patch("letsencrypt.le_util.get_os_info")
|
||||
@mock.patch("certbot.le_util.get_os_info")
|
||||
def test_get_debian_value(self, os_info):
|
||||
os_info.return_value = ('Debian', '', '')
|
||||
self.assertEqual(constants.os_constant("vhost_root"),
|
||||
"/etc/apache2/sites-available")
|
||||
|
||||
@mock.patch("letsencrypt.le_util.get_os_info")
|
||||
@mock.patch("certbot.le_util.get_os_info")
|
||||
def test_get_centos_value(self, os_info):
|
||||
os_info.return_value = ('CentOS Linux', '', '')
|
||||
self.assertEqual(constants.os_constant("vhost_root"),
|
||||
"/etc/httpd/conf.d")
|
||||
|
||||
@mock.patch("letsencrypt.le_util.get_os_info")
|
||||
@mock.patch("certbot.le_util.get_os_info")
|
||||
def test_get_default_value(self, os_info):
|
||||
os_info.return_value = ('Nonexistent Linux', '', '')
|
||||
self.assertEqual(constants.os_constant("vhost_root"),
|
||||
|
|
@ -1,38 +1,38 @@
|
|||
"""Test letsencrypt_apache.display_ops."""
|
||||
"""Test certbot_apache.display_ops."""
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
import mock
|
||||
import zope.component
|
||||
|
||||
from letsencrypt.display import util as display_util
|
||||
from letsencrypt import errors
|
||||
from certbot.display import util as display_util
|
||||
from certbot import errors
|
||||
|
||||
from letsencrypt_apache import obj
|
||||
from certbot_apache import obj
|
||||
|
||||
from letsencrypt_apache.tests import util
|
||||
from certbot_apache.tests import util
|
||||
|
||||
|
||||
class SelectVhostTest(unittest.TestCase):
|
||||
"""Tests for letsencrypt_apache.display_ops.select_vhost."""
|
||||
"""Tests for certbot_apache.display_ops.select_vhost."""
|
||||
|
||||
def setUp(self):
|
||||
zope.component.provideUtility(display_util.FileDisplay(sys.stdout))
|
||||
self.base_dir = "/example_path"
|
||||
self.vhosts = util.get_vh_truth(
|
||||
self.base_dir, "debian_apache_2_4/two_vhost_80")
|
||||
self.base_dir, "debian_apache_2_4/multiple_vhosts")
|
||||
|
||||
@classmethod
|
||||
def _call(cls, vhosts):
|
||||
from letsencrypt_apache.display_ops import select_vhost
|
||||
from certbot_apache.display_ops import select_vhost
|
||||
return select_vhost("example.com", vhosts)
|
||||
|
||||
@mock.patch("letsencrypt_apache.display_ops.zope.component.getUtility")
|
||||
@mock.patch("certbot_apache.display_ops.zope.component.getUtility")
|
||||
def test_successful_choice(self, mock_util):
|
||||
mock_util().menu.return_value = (display_util.OK, 3)
|
||||
self.assertEqual(self.vhosts[3], self._call(self.vhosts))
|
||||
|
||||
@mock.patch("letsencrypt_apache.display_ops.zope.component.getUtility")
|
||||
@mock.patch("certbot_apache.display_ops.zope.component.getUtility")
|
||||
def test_noninteractive(self, mock_util):
|
||||
mock_util().menu.side_effect = errors.MissingCommandlineFlag("no vhost default")
|
||||
try:
|
||||
|
|
@ -40,7 +40,7 @@ class SelectVhostTest(unittest.TestCase):
|
|||
except errors.MissingCommandlineFlag as e:
|
||||
self.assertTrue("VirtualHost directives" in e.message)
|
||||
|
||||
@mock.patch("letsencrypt_apache.display_ops.zope.component.getUtility")
|
||||
@mock.patch("certbot_apache.display_ops.zope.component.getUtility")
|
||||
def test_more_info_cancel(self, mock_util):
|
||||
mock_util().menu.side_effect = [
|
||||
(display_util.HELP, 1),
|
||||
|
|
@ -54,9 +54,9 @@ class SelectVhostTest(unittest.TestCase):
|
|||
def test_no_vhosts(self):
|
||||
self.assertEqual(self._call([]), None)
|
||||
|
||||
@mock.patch("letsencrypt_apache.display_ops.display_util")
|
||||
@mock.patch("letsencrypt_apache.display_ops.zope.component.getUtility")
|
||||
@mock.patch("letsencrypt_apache.display_ops.logger")
|
||||
@mock.patch("certbot_apache.display_ops.display_util")
|
||||
@mock.patch("certbot_apache.display_ops.zope.component.getUtility")
|
||||
@mock.patch("certbot_apache.display_ops.logger")
|
||||
def test_small_display(self, mock_logger, mock_util, mock_display_util):
|
||||
mock_display_util.WIDTH = 20
|
||||
mock_util().menu.return_value = (display_util.OK, 0)
|
||||
|
|
@ -64,7 +64,7 @@ class SelectVhostTest(unittest.TestCase):
|
|||
|
||||
self.assertEqual(mock_logger.debug.call_count, 1)
|
||||
|
||||
@mock.patch("letsencrypt_apache.display_ops.zope.component.getUtility")
|
||||
@mock.patch("certbot_apache.display_ops.zope.component.getUtility")
|
||||
def test_multiple_names(self, mock_util):
|
||||
mock_util().menu.return_value = (display_util.OK, 5)
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
"""Tests for letsencrypt_apache.obj."""
|
||||
"""Tests for certbot_apache.obj."""
|
||||
import unittest
|
||||
|
||||
|
||||
|
|
@ -6,8 +6,8 @@ class VirtualHostTest(unittest.TestCase):
|
|||
"""Test the VirtualHost class."""
|
||||
|
||||
def setUp(self):
|
||||
from letsencrypt_apache.obj import Addr
|
||||
from letsencrypt_apache.obj import VirtualHost
|
||||
from certbot_apache.obj import Addr
|
||||
from certbot_apache.obj import VirtualHost
|
||||
|
||||
self.addr1 = Addr.fromstring("127.0.0.1")
|
||||
self.addr2 = Addr.fromstring("127.0.0.1:443")
|
||||
|
|
@ -33,8 +33,8 @@ class VirtualHostTest(unittest.TestCase):
|
|||
self.assertFalse(self.vhost1 != self.vhost1b)
|
||||
|
||||
def test_conflicts(self):
|
||||
from letsencrypt_apache.obj import Addr
|
||||
from letsencrypt_apache.obj import VirtualHost
|
||||
from certbot_apache.obj import Addr
|
||||
from certbot_apache.obj import VirtualHost
|
||||
|
||||
complex_vh = VirtualHost(
|
||||
"fp", "vhp",
|
||||
|
|
@ -51,7 +51,7 @@ class VirtualHostTest(unittest.TestCase):
|
|||
self.addr_default]))
|
||||
|
||||
def test_same_server(self):
|
||||
from letsencrypt_apache.obj import VirtualHost
|
||||
from certbot_apache.obj import VirtualHost
|
||||
no_name1 = VirtualHost(
|
||||
"fp", "vhp", set([self.addr1]), False, False, None)
|
||||
no_name2 = VirtualHost(
|
||||
|
|
@ -74,7 +74,7 @@ class VirtualHostTest(unittest.TestCase):
|
|||
class AddrTest(unittest.TestCase):
|
||||
"""Test obj.Addr."""
|
||||
def setUp(self):
|
||||
from letsencrypt_apache.obj import Addr
|
||||
from certbot_apache.obj import Addr
|
||||
self.addr = Addr.fromstring("*:443")
|
||||
|
||||
self.addr1 = Addr.fromstring("127.0.0.1")
|
||||
|
|
@ -89,7 +89,7 @@ class AddrTest(unittest.TestCase):
|
|||
self.assertTrue(self.addr2.is_wildcard())
|
||||
|
||||
def test_get_sni_addr(self):
|
||||
from letsencrypt_apache.obj import Addr
|
||||
from certbot_apache.obj import Addr
|
||||
self.assertEqual(
|
||||
self.addr.get_sni_addr("443"), Addr.fromstring("*:443"))
|
||||
self.assertEqual(
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
"""Tests for letsencrypt_apache.parser."""
|
||||
"""Tests for certbot_apache.parser."""
|
||||
import os
|
||||
import shutil
|
||||
import unittest
|
||||
|
|
@ -6,9 +6,9 @@ import unittest
|
|||
import augeas
|
||||
import mock
|
||||
|
||||
from letsencrypt import errors
|
||||
from certbot import errors
|
||||
|
||||
from letsencrypt_apache.tests import util
|
||||
from certbot_apache.tests import util
|
||||
|
||||
|
||||
class BasicParserTest(util.ParserTest):
|
||||
|
|
@ -31,12 +31,12 @@ class BasicParserTest(util.ParserTest):
|
|||
def test_parse_file(self):
|
||||
"""Test parse_file.
|
||||
|
||||
letsencrypt.conf is chosen as the test file as it will not be
|
||||
certbot.conf is chosen as the test file as it will not be
|
||||
included during the normal course of execution.
|
||||
|
||||
"""
|
||||
file_path = os.path.join(
|
||||
self.config_path, "not-parsed-by-default", "letsencrypt.conf")
|
||||
self.config_path, "not-parsed-by-default", "certbot.conf")
|
||||
|
||||
self.parser._parse_file(file_path) # pylint: disable=protected-access
|
||||
|
||||
|
|
@ -72,7 +72,7 @@ class BasicParserTest(util.ParserTest):
|
|||
Path must be valid before attempting to add to augeas
|
||||
|
||||
"""
|
||||
from letsencrypt_apache.parser import get_aug_path
|
||||
from certbot_apache.parser import get_aug_path
|
||||
# This makes sure that find_dir will work
|
||||
self.parser.modules.add("mod_ssl.c")
|
||||
|
||||
|
|
@ -86,7 +86,7 @@ class BasicParserTest(util.ParserTest):
|
|||
self.assertTrue("IfModule" in matches[0])
|
||||
|
||||
def test_add_dir_to_ifmodssl_multiple(self):
|
||||
from letsencrypt_apache.parser import get_aug_path
|
||||
from certbot_apache.parser import get_aug_path
|
||||
# This makes sure that find_dir will work
|
||||
self.parser.modules.add("mod_ssl.c")
|
||||
|
||||
|
|
@ -100,11 +100,11 @@ class BasicParserTest(util.ParserTest):
|
|||
self.assertTrue("IfModule" in matches[0])
|
||||
|
||||
def test_get_aug_path(self):
|
||||
from letsencrypt_apache.parser import get_aug_path
|
||||
from certbot_apache.parser import get_aug_path
|
||||
self.assertEqual("/files/etc/apache", get_aug_path("/etc/apache"))
|
||||
|
||||
def test_set_locations(self):
|
||||
with mock.patch("letsencrypt_apache.parser.os.path") as mock_path:
|
||||
with mock.patch("certbot_apache.parser.os.path") as mock_path:
|
||||
|
||||
mock_path.isfile.side_effect = [False, False]
|
||||
|
||||
|
|
@ -114,7 +114,7 @@ class BasicParserTest(util.ParserTest):
|
|||
self.assertEqual(results["default"], results["listen"])
|
||||
self.assertEqual(results["default"], results["name"])
|
||||
|
||||
@mock.patch("letsencrypt_apache.parser.ApacheParser._get_runtime_cfg")
|
||||
@mock.patch("certbot_apache.parser.ApacheParser._get_runtime_cfg")
|
||||
def test_update_runtime_variables(self, mock_cfg):
|
||||
mock_cfg.return_value = (
|
||||
'ServerRoot: "/etc/apache2"\n'
|
||||
|
|
@ -139,7 +139,7 @@ class BasicParserTest(util.ParserTest):
|
|||
self.parser.update_runtime_variables()
|
||||
self.assertEqual(self.parser.variables, expected_vars)
|
||||
|
||||
@mock.patch("letsencrypt_apache.parser.ApacheParser._get_runtime_cfg")
|
||||
@mock.patch("certbot_apache.parser.ApacheParser._get_runtime_cfg")
|
||||
def test_update_runtime_vars_bad_output(self, mock_cfg):
|
||||
mock_cfg.return_value = "Define: TLS=443=24"
|
||||
self.parser.update_runtime_variables()
|
||||
|
|
@ -148,8 +148,8 @@ class BasicParserTest(util.ParserTest):
|
|||
self.assertRaises(
|
||||
errors.PluginError, self.parser.update_runtime_variables)
|
||||
|
||||
@mock.patch("letsencrypt_apache.constants.os_constant")
|
||||
@mock.patch("letsencrypt_apache.parser.subprocess.Popen")
|
||||
@mock.patch("certbot_apache.constants.os_constant")
|
||||
@mock.patch("certbot_apache.parser.subprocess.Popen")
|
||||
def test_update_runtime_vars_bad_ctl(self, mock_popen, mock_const):
|
||||
mock_popen.side_effect = OSError
|
||||
mock_const.return_value = "nonexistent"
|
||||
|
|
@ -157,7 +157,7 @@ class BasicParserTest(util.ParserTest):
|
|||
errors.MisconfigurationError,
|
||||
self.parser.update_runtime_variables)
|
||||
|
||||
@mock.patch("letsencrypt_apache.parser.subprocess.Popen")
|
||||
@mock.patch("certbot_apache.parser.subprocess.Popen")
|
||||
def test_update_runtime_vars_bad_exit(self, mock_popen):
|
||||
mock_popen().communicate.return_value = ("", "")
|
||||
mock_popen.returncode = -1
|
||||
|
|
@ -177,9 +177,9 @@ class ParserInitTest(util.ApacheTest):
|
|||
shutil.rmtree(self.config_dir)
|
||||
shutil.rmtree(self.work_dir)
|
||||
|
||||
@mock.patch("letsencrypt_apache.parser.ApacheParser._get_runtime_cfg")
|
||||
@mock.patch("certbot_apache.parser.ApacheParser._get_runtime_cfg")
|
||||
def test_unparsable(self, mock_cfg):
|
||||
from letsencrypt_apache.parser import ApacheParser
|
||||
from certbot_apache.parser import ApacheParser
|
||||
mock_cfg.return_value = ('Define: TEST')
|
||||
self.assertRaises(
|
||||
errors.PluginError,
|
||||
|
|
@ -187,13 +187,13 @@ class ParserInitTest(util.ApacheTest):
|
|||
"/dummy/vhostpath", version=(2, 2, 22))
|
||||
|
||||
def test_root_normalized(self):
|
||||
from letsencrypt_apache.parser import ApacheParser
|
||||
from certbot_apache.parser import ApacheParser
|
||||
|
||||
with mock.patch("letsencrypt_apache.parser.ApacheParser."
|
||||
with mock.patch("certbot_apache.parser.ApacheParser."
|
||||
"update_runtime_variables"):
|
||||
path = os.path.join(
|
||||
self.temp_dir,
|
||||
"debian_apache_2_4/////two_vhost_80/../two_vhost_80/apache2")
|
||||
"debian_apache_2_4/////multiple_vhosts/../multiple_vhosts/apache2")
|
||||
|
||||
parser = ApacheParser(self.aug, path,
|
||||
"/dummy/vhostpath")
|
||||
|
|
@ -201,8 +201,8 @@ class ParserInitTest(util.ApacheTest):
|
|||
self.assertEqual(parser.root, self.config_path)
|
||||
|
||||
def test_root_absolute(self):
|
||||
from letsencrypt_apache.parser import ApacheParser
|
||||
with mock.patch("letsencrypt_apache.parser.ApacheParser."
|
||||
from certbot_apache.parser import ApacheParser
|
||||
with mock.patch("certbot_apache.parser.ApacheParser."
|
||||
"update_runtime_variables"):
|
||||
parser = ApacheParser(
|
||||
self.aug, os.path.relpath(self.config_path),
|
||||
|
|
@ -211,8 +211,8 @@ class ParserInitTest(util.ApacheTest):
|
|||
self.assertEqual(parser.root, self.config_path)
|
||||
|
||||
def test_root_no_trailing_slash(self):
|
||||
from letsencrypt_apache.parser import ApacheParser
|
||||
with mock.patch("letsencrypt_apache.parser.ApacheParser."
|
||||
from certbot_apache.parser import ApacheParser
|
||||
with mock.patch("certbot_apache.parser.ApacheParser."
|
||||
"update_runtime_variables"):
|
||||
parser = ApacheParser(
|
||||
self.aug, self.config_path + os.path.sep,
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<VirtualHost *:80>
|
||||
# How well does Let's Encrypt work without a ServerName/Alias?
|
||||
# How well does Certbot work without a ServerName/Alias?
|
||||
ServerAdmin webmaster@localhost
|
||||
DocumentRoot /var/www/html
|
||||
|
||||
|
|
@ -16,8 +16,8 @@
|
|||
# /usr/share/doc/apache2/README.Debian.gz for more info.
|
||||
# If both key and certificate are stored in the same file, only the
|
||||
# SSLCertificateFile directive is needed.
|
||||
SSLCertificateFile /etc/apache2/certs/letsencrypt-cert_5.pem
|
||||
SSLCertificateKeyFile /etc/apache2/ssl/key-letsencrypt_15.pem
|
||||
SSLCertificateFile /etc/apache2/certs/certbot-cert_5.pem
|
||||
SSLCertificateKeyFile /etc/apache2/ssl/key-certbot_15.pem
|
||||
|
||||
<FilesMatch "\.(cgi|shtml|phtml|php)$">
|
||||
SSLOptions +StdEnvVars
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
# Depends: dav_svn
|
||||
<IfModule !mod_dav_svn.c>
|
||||
Include mods-enabled/dav_svn.load
|
||||
</IfModule>
|
||||
LoadModule authz_svn_module /usr/lib/apache2/modules/mod_authz_svn.so
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue