Merge branch 'master' into add_dns01_challenge

This commit is contained in:
Wilfried Teiken 2016-04-25 00:40:27 -04:00
commit 6196cf0aa7
481 changed files with 10037 additions and 8331 deletions

4
.gitignore vendored
View file

@ -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/

View file

@ -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

View file

@ -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

View file

@ -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" ]

View file

@ -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

View file

@ -1,4 +1,4 @@
Let's Encrypt Python Client
Certbot ACME Client
Copyright (c) Electronic Frontier Foundation and others
Licensed Apache Version 2.0

View file

@ -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 *

View file

@ -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
View file

@ -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
View file

@ -0,0 +1,4 @@
[pep8]
# E265 block comment should start with '# '
# E501 line too long (X > 79 characters)
ignore = E265,E501

View file

@ -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"

View file

@ -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):

View file

@ -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

View file

@ -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)

View file

@ -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:

View file

@ -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],),
))

View file

@ -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)

View file

@ -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

View file

@ -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))

View file

@ -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=[

View 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

View file

@ -0,0 +1 @@
Apache plugin for Certbot

View file

@ -0,0 +1 @@
"""Certbot Apache plugin."""

View file

@ -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):

View 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

View file

@ -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 = /\\\\./

View file

@ -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

View file

@ -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 = [

View file

@ -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}"

View file

@ -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.

View file

@ -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

View file

@ -0,0 +1 @@
"""Certbot Apache Tests"""

View file

@ -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

View file

@ -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

View file

@ -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):

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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)

View file

@ -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:

View file

@ -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

View file

@ -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"),

View file

@ -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)

View file

@ -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(

View file

@ -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,

View file

@ -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

View file

@ -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

View file

@ -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