mirror of
https://github.com/certbot/certbot.git
synced 2026-01-26 07:41:33 +03:00
Merge remote-tracking branch 'origin/master' into wmf
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -22,3 +22,7 @@ letsencrypt.log
|
||||
|
||||
# auth --cert-path --chain-path
|
||||
/*.pem
|
||||
|
||||
# letstest
|
||||
tests/letstest/letest-*/
|
||||
tests/letstest/*.pem
|
||||
|
||||
43
.travis.yml
43
.travis.yml
@@ -1,8 +1,14 @@
|
||||
language: python
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.cache/pip
|
||||
|
||||
services:
|
||||
- rabbitmq
|
||||
- mariadb
|
||||
# apacheconftest
|
||||
#- apache2
|
||||
|
||||
# http://docs.travis-ci.com/user/ci-environment/#CI-environment-OS
|
||||
# gimme has to be kept in sync with Boulder's Go version setting in .travis.yml
|
||||
@@ -17,12 +23,30 @@ env:
|
||||
global:
|
||||
- GOPATH=/tmp/go
|
||||
- PATH=$GOPATH/bin:$PATH
|
||||
matrix:
|
||||
- TOXENV=py26 BOULDER_INTEGRATION=1
|
||||
- TOXENV=py27 BOULDER_INTEGRATION=1
|
||||
- TOXENV=lint
|
||||
- TOXENV=cover
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- python: "2.6"
|
||||
env: TOXENV=py26 BOULDER_INTEGRATION=1
|
||||
- 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
|
||||
- python: "2.7"
|
||||
env: TOXENV=py27 BOULDER_INTEGRATION=1
|
||||
- python: "2.7"
|
||||
env: TOXENV=py27-oldest BOULDER_INTEGRATION=1
|
||||
- python: "2.7"
|
||||
env: TOXENV=cover
|
||||
- python: "2.7"
|
||||
env: TOXENV=lint
|
||||
- python: "3.3"
|
||||
env: TOXENV=py33
|
||||
- python: "3.4"
|
||||
env: TOXENV=py34
|
||||
- python: "3.5"
|
||||
env: TOXENV=py35
|
||||
|
||||
# Only build pushes to the master branch, PRs, and branches beginning with
|
||||
# `test-`. This reduces the number of simultaneous Travis runs, which speeds
|
||||
@@ -44,7 +68,6 @@ addons:
|
||||
sources:
|
||||
- augeas
|
||||
packages: # keep in sync with bootstrap/ubuntu.sh and Boulder
|
||||
- python
|
||||
- python-dev
|
||||
- python-virtualenv
|
||||
- gcc
|
||||
@@ -58,6 +81,12 @@ addons:
|
||||
- openssl
|
||||
# For Boulder integration testing
|
||||
- rsyslog
|
||||
# for apacheconftest
|
||||
#- realpath
|
||||
#- apache2
|
||||
#- libapache2-mod-wsgi
|
||||
#- libapache2-mod-macro
|
||||
#- sudo
|
||||
|
||||
install: "travis_retry pip install tox coveralls"
|
||||
script: 'travis_retry tox && ([ "xxx$BOULDER_INTEGRATION" = "xxx" ] || ./tests/travis-integration.sh)'
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
letsencrypt/DISCLAIMER
|
||||
@@ -49,7 +49,6 @@ COPY letsencrypt-apache /opt/letsencrypt/src/letsencrypt-apache/
|
||||
COPY letsencrypt-nginx /opt/letsencrypt/src/letsencrypt-nginx/
|
||||
|
||||
|
||||
# py26reqs.txt not installed!
|
||||
RUN virtualenv --no-site-packages -p python2 /opt/letsencrypt/venv && \
|
||||
/opt/letsencrypt/venv/bin/pip install \
|
||||
-e /opt/letsencrypt/src/acme \
|
||||
|
||||
@@ -22,8 +22,8 @@ WORKDIR /opt/letsencrypt
|
||||
|
||||
# TODO: Install non-default Python versions for tox.
|
||||
# TODO: Install Apache/Nginx for plugin development.
|
||||
COPY bootstrap/ubuntu.sh /opt/letsencrypt/src/ubuntu.sh
|
||||
RUN /opt/letsencrypt/src/ubuntu.sh && \
|
||||
COPY letsencrypt-auto-source/letsencrypt-auto /opt/letsencrypt/src/letsencrypt-auto
|
||||
RUN /opt/letsencrypt/src/letsencrypt-auto --os-packages-only && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/* \
|
||||
/tmp/* \
|
||||
@@ -32,8 +32,7 @@ RUN /opt/letsencrypt/src/ubuntu.sh && \
|
||||
# the above is not likely to change, so by putting it further up the
|
||||
# Dockerfile we make sure we cache as much as possible
|
||||
|
||||
# py26reqs.txt not installed!
|
||||
COPY setup.py README.rst CHANGES.rst MANIFEST.in DISCLAIMER 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/letsencrypt/src/
|
||||
|
||||
# all above files are necessary for setup.py, however, package source
|
||||
# code directory has to be copied separately to a subdirectory...
|
||||
|
||||
@@ -2,7 +2,7 @@ Let's Encrypt Python Client
|
||||
Copyright (c) Electronic Frontier Foundation and others
|
||||
Licensed Apache Version 2.0
|
||||
|
||||
Incorporating code from nginxparser
|
||||
The nginx plugin incorporates code from nginxparser
|
||||
Copyright (c) 2014 Fatih Erikli
|
||||
Licensed MIT
|
||||
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
include py26reqs.txt
|
||||
include README.rst
|
||||
include CHANGES.rst
|
||||
include CONTRIBUTING.md
|
||||
include LICENSE.txt
|
||||
include linter_plugin.py
|
||||
include letsencrypt/DISCLAIMER
|
||||
recursive-include docs *
|
||||
recursive-include examples *
|
||||
recursive-include letsencrypt/tests/testdata *
|
||||
|
||||
176
README.rst
176
README.rst
@@ -3,12 +3,9 @@
|
||||
Disclaimer
|
||||
==========
|
||||
|
||||
This is a **DEVELOPER PREVIEW** intended for developers and testers only.
|
||||
|
||||
**DO NOT RUN THIS CODE ON A PRODUCTION SERVER. IT WILL INSTALL CERTIFICATES
|
||||
SIGNED BY A TEST CA, AND WILL CAUSE CERT WARNINGS FOR USERS.**
|
||||
|
||||
Browser-trusted certificates will be available in the coming months.
|
||||
The Let's Encrypt Client is **BETA SOFTWARE**. It contains plenty of bugs and
|
||||
rough edges, and should be tested thoroughly in staging environments before use
|
||||
on production systems.
|
||||
|
||||
For more information regarding the status of the project, please see
|
||||
https://letsencrypt.org. Be sure to checkout the
|
||||
@@ -17,38 +14,87 @@ https://letsencrypt.org. Be sure to checkout the
|
||||
About the Let's Encrypt Client
|
||||
==============================
|
||||
|
||||
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.
|
||||
|
||||
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::
|
||||
|
||||
user@webserver:~$ git clone https://github.com/letsencrypt/letsencrypt
|
||||
user@webserver:~$ cd letsencrypt
|
||||
user@webserver:~/letsencrypt$ ./letsencrypt-auto --help
|
||||
|
||||
Or for full command line help, type::
|
||||
|
||||
./letsencrypt-auto --help all
|
||||
|
||||
``letsencrypt-auto`` updates to the latest client release automatically. And
|
||||
since ``letsencrypt-auto`` is a wrapper to ``letsencrypt``, it accepts exactly
|
||||
the same command line flags and arguments. More details about this script and
|
||||
other installation methods can be found `in the User Guide
|
||||
<https://letsencrypt.readthedocs.org/en/latest/using.html#installation>`_.
|
||||
|
||||
How to run the client
|
||||
---------------------
|
||||
|
||||
In many cases, you can just run ``letsencrypt-auto`` or ``letsencrypt``, and the
|
||||
client will guide you through the process of obtaining and installing certs
|
||||
interactively.
|
||||
|
||||
You can also tell it exactly what you want it to do from the command line.
|
||||
For instance, if you want to obtain a cert for ``thing.com``,
|
||||
``www.thing.com``, and ``otherthing.net``, using the Apache plugin to both
|
||||
obtain and install the certs, you could do this::
|
||||
|
||||
./letsencrypt-auto --apache -d thing.com -d www.thing.com -d otherthing.net
|
||||
|
||||
(The first time you run the command, it will make an account, and ask for an
|
||||
email and agreement to the Let's Encrypt Subscriber Agreement; you can
|
||||
automate those with ``--email`` and ``--agree-tos``)
|
||||
|
||||
If you want to use a webserver that doesn't have full plugin support yet, you
|
||||
can still use "standalone" or "webroot" plugins to obtain a certificate::
|
||||
|
||||
./letsencrypt-auto certonly --standalone --email admin@thing.com -d thing.com -d www.thing.com -d otherthing.net
|
||||
|
||||
|
||||
Understanding the client in more depth
|
||||
--------------------------------------
|
||||
|
||||
To understand what the client is doing in detail, it's important to
|
||||
understand the way it uses plugins. Please see the `explanation of
|
||||
plugins <https://letsencrypt.readthedocs.org/en/latest/using.html#plugins>`_ in
|
||||
the User Guide.
|
||||
|
||||
Links
|
||||
=====
|
||||
|
||||
Documentation: https://letsencrypt.readthedocs.org
|
||||
|
||||
Software project: https://github.com/letsencrypt/letsencrypt
|
||||
|
||||
Notes for developers: https://letsencrypt.readthedocs.org/en/latest/contributing.html
|
||||
|
||||
Main Website: https://letsencrypt.org/
|
||||
|
||||
IRC Channel: #letsencrypt on `Freenode`_
|
||||
|
||||
Community: https://community.letsencrypt.org
|
||||
|
||||
Mailing list: `client-dev`_ (to subscribe without a Google account, send an
|
||||
email to client-dev+subscribe@letsencrypt.org)
|
||||
|
||||
|build-status| |coverage| |docs| |container|
|
||||
|
||||
In short: getting and installing SSL/TLS certificates made easy (`watch demo video`_).
|
||||
|
||||
The Let's Encrypt Client is a tool to automatically receive and install
|
||||
X.509 certificates to enable TLS on servers. The client will
|
||||
interoperate with the Let's Encrypt CA which will be issuing browser-trusted
|
||||
certificates for free.
|
||||
|
||||
It's all automated:
|
||||
|
||||
* The tool will prove domain control to the CA and submit a CSR (Certificate
|
||||
Signing Request).
|
||||
* If domain control has been proven, a certificate will get issued and the tool
|
||||
will automatically install it.
|
||||
|
||||
All you need to do to sign a single domain is::
|
||||
|
||||
user@www:~$ sudo letsencrypt -d www.example.org certonly
|
||||
|
||||
For multiple domains (SAN) use::
|
||||
|
||||
user@www:~$ sudo letsencrypt -d www.example.org -d example.org certonly
|
||||
|
||||
and if you have a compatible web server (Apache or Nginx), Let's Encrypt can
|
||||
not only get a new certificate, but also deploy it and configure your
|
||||
server automatically!::
|
||||
|
||||
user@www:~$ sudo letsencrypt -d www.example.org run
|
||||
|
||||
|
||||
**Encrypt ALL the things!**
|
||||
|
||||
|
||||
.. |build-status| image:: https://travis-ci.org/letsencrypt/letsencrypt.svg?branch=master
|
||||
@@ -72,18 +118,38 @@ server automatically!::
|
||||
|
||||
.. _watch demo video: https://www.youtube.com/watch?v=Gas_sSB-5SU
|
||||
|
||||
System Requirements
|
||||
===================
|
||||
|
||||
The Let's Encrypt Client presently only runs on Unix-ish OSes that include
|
||||
Python 2.6 or 2.7; Python 3.x support will be added after the Public Beta
|
||||
launch. The client requires root access in order to write to
|
||||
``/etc/letsencrypt``, ``/var/log/letsencrypt``, ``/var/lib/letsencrypt``; to
|
||||
bind to ports 80 and 443 (if you use the ``standalone`` plugin) and to read and
|
||||
modify webserver configurations (if you use the ``apache`` or ``nginx``
|
||||
plugins). If none of these apply to you, it is theoretically possible to run
|
||||
without root privileges, but for most users who want to avoid running an ACME
|
||||
client as root, either `letsencrypt-nosudo
|
||||
<https://github.com/diafygi/letsencrypt-nosudo>`_ or `simp_le
|
||||
<https://github.com/kuba/simp_le>`_ are more appropriate choices.
|
||||
|
||||
The Apache plugin currently requires a Debian-based OS with augeas version
|
||||
1.0; this includes Ubuntu 12.04+ and Debian 7+.
|
||||
|
||||
|
||||
Current Features
|
||||
----------------
|
||||
================
|
||||
|
||||
* Supports multiple web servers:
|
||||
|
||||
- apache/2.x (tested and working on Ubuntu Linux)
|
||||
- nginx/0.8.48+ (under development)
|
||||
- apache/2.x (working on Debian 8+ and Ubuntu 12.04+)
|
||||
- standalone (runs its own simple webserver to prove you control a domain)
|
||||
- webroot (adds files to webroot directories in order to prove control of
|
||||
domains and obtain certs)
|
||||
- nginx/0.8.48+ (highly experimental, not included in letsencrypt-auto)
|
||||
|
||||
* The private key is generated locally on your system.
|
||||
* Can talk to the Let's Encrypt (demo) CA or optionally to other ACME
|
||||
* Can talk to the Let's Encrypt CA or optionally to other ACME
|
||||
compliant services.
|
||||
* Can get domain-validated (DV) certificates.
|
||||
* Can revoke certificates.
|
||||
@@ -92,34 +158,10 @@ Current Features
|
||||
runs https only (Apache only)
|
||||
* Fully automated.
|
||||
* Configuration changes are logged and can be reverted.
|
||||
* Text and ncurses UI.
|
||||
* Supports ncurses and text (-t) UI, or can be driven entirely from the
|
||||
command line.
|
||||
* Free and Open Source Software, made with Python.
|
||||
|
||||
|
||||
Installation Instructions
|
||||
-------------------------
|
||||
|
||||
Official **documentation**, including `installation instructions`_, is
|
||||
available at https://letsencrypt.readthedocs.org.
|
||||
|
||||
|
||||
Links
|
||||
-----
|
||||
|
||||
Documentation: https://letsencrypt.readthedocs.org
|
||||
|
||||
Software project: https://github.com/letsencrypt/letsencrypt
|
||||
|
||||
Notes for developers: https://letsencrypt.readthedocs.org/en/latest/contributing.html
|
||||
|
||||
Main Website: https://letsencrypt.org/
|
||||
|
||||
IRC Channel: #letsencrypt on `Freenode`_
|
||||
|
||||
Community: https://community.letsencrypt.org
|
||||
|
||||
Mailing list: `client-dev`_ (to subscribe without a Google account, send an
|
||||
email to client-dev+subscribe@letsencrypt.org)
|
||||
|
||||
.. _Freenode: https://freenode.net
|
||||
.. _Freenode: https://webchat.freenode.net?channels=%23letsencrypt
|
||||
.. _client-dev: https://groups.google.com/a/letsencrypt.org/forum/#!forum/client-dev
|
||||
|
||||
6
Vagrantfile
vendored
6
Vagrantfile
vendored
@@ -7,7 +7,7 @@ VAGRANTFILE_API_VERSION = "2"
|
||||
# Setup instructions from docs/contributing.rst
|
||||
$ubuntu_setup_script = <<SETUP_SCRIPT
|
||||
cd /vagrant
|
||||
./bootstrap/install-deps.sh
|
||||
./letsencrypt-auto-source/letsencrypt-auto --os-packages-only
|
||||
./bootstrap/dev/venv.sh
|
||||
SETUP_SCRIPT
|
||||
|
||||
@@ -21,6 +21,10 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||
# Cannot allocate memory" when running
|
||||
# letsencrypt.client.tests.display.util_test.NcursesDisplayTest
|
||||
v.memory = 1024
|
||||
|
||||
# Handle cases when the host is behind a private network by making the
|
||||
# NAT engine use the host's resolver mechanisms to handle DNS requests.
|
||||
v.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
383
acme/.pylintrc
Normal file
383
acme/.pylintrc
Normal file
@@ -0,0 +1,383 @@
|
||||
[MASTER]
|
||||
|
||||
# Specify a configuration file.
|
||||
#rcfile=
|
||||
|
||||
# Python code to execute, usually for sys.path manipulation such as
|
||||
# pygtk.require().
|
||||
#init-hook=
|
||||
|
||||
# Profiled execution.
|
||||
profile=no
|
||||
|
||||
# Add files or directories to the blacklist. They should be base names, not
|
||||
# paths.
|
||||
ignore=CVS
|
||||
|
||||
# Pickle collected data for later comparisons.
|
||||
persistent=yes
|
||||
|
||||
# List of plugins (as comma separated values of python modules names) to load,
|
||||
# usually to register additional checkers.
|
||||
load-plugins=linter_plugin
|
||||
|
||||
# DEPRECATED
|
||||
include-ids=no
|
||||
|
||||
# DEPRECATED
|
||||
symbols=no
|
||||
|
||||
# Use multiple processes to speed up Pylint.
|
||||
jobs=1
|
||||
|
||||
# Allow loading of arbitrary C extensions. Extensions are imported into the
|
||||
# active Python interpreter and may run arbitrary code.
|
||||
unsafe-load-any-extension=no
|
||||
|
||||
# A comma-separated list of package or module names from where C extensions may
|
||||
# be loaded. Extensions are loading into the active Python interpreter and may
|
||||
# run arbitrary code
|
||||
extension-pkg-whitelist=
|
||||
|
||||
|
||||
[MESSAGES CONTROL]
|
||||
|
||||
# Only show warnings with the listed confidence levels. Leave empty to show
|
||||
# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED
|
||||
confidence=
|
||||
|
||||
# Enable the message, report, category or checker with the given id(s). You can
|
||||
# either give multiple identifier separated by comma (,) or put this option
|
||||
# multiple time. See also the "--disable" option for examples.
|
||||
#enable=
|
||||
|
||||
# Disable the message, report, category or checker with the given id(s). You
|
||||
# can either give multiple identifiers separated by comma (,) or put this
|
||||
# option multiple times (only on the command line, not in the configuration
|
||||
# file where it should appear only once).You can also use "--disable=all" to
|
||||
# disable everything first and then reenable specific checks. For example, if
|
||||
# you want to run only the similarities checker, you can use "--disable=all
|
||||
# --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
|
||||
# bstract-class-not-used cannot be disabled locally (at least in
|
||||
# pylint 1.4.1/2)
|
||||
|
||||
|
||||
|
||||
[REPORTS]
|
||||
|
||||
# Set the output format. Available formats are text, parseable, colorized, msvs
|
||||
# (visual studio) and html. You can also give a reporter class, eg
|
||||
# mypackage.mymodule.MyReporterClass.
|
||||
output-format=text
|
||||
|
||||
# Put messages in a separate file for each module / package specified on the
|
||||
# command line instead of printing them on stdout. Reports (if any) will be
|
||||
# written in a file name "pylint_global.[txt|html]".
|
||||
files-output=no
|
||||
|
||||
# Tells whether to display a full report or only the messages
|
||||
reports=yes
|
||||
|
||||
# Python expression which should return a note less than 10 (10 is the highest
|
||||
# note). You have access to the variables errors warning, statement which
|
||||
# respectively contain the number of errors / warnings messages and the total
|
||||
# number of statements analyzed. This is used by the global evaluation report
|
||||
# (RP0004).
|
||||
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
|
||||
|
||||
# Add a comment according to your evaluation note. This is used by the global
|
||||
# evaluation report (RP0004).
|
||||
comment=no
|
||||
|
||||
# Template used to display messages. This is a python new-style format string
|
||||
# used to format the message information. See doc for all details
|
||||
#msg-template=
|
||||
|
||||
|
||||
[FORMAT]
|
||||
|
||||
# Maximum number of characters on a single line.
|
||||
max-line-length=80
|
||||
|
||||
# Regexp for a line that is allowed to be longer than the limit.
|
||||
ignore-long-lines=^\s*(# )?<?https?://\S+>?$
|
||||
|
||||
# Allow the body of an if to be on the same line as the test if there is no
|
||||
# else.
|
||||
single-line-if-stmt=no
|
||||
|
||||
# List of optional constructs for which whitespace checking is disabled
|
||||
no-space-check=trailing-comma,dict-separator
|
||||
|
||||
# Maximum number of lines in a module
|
||||
max-module-lines=1000
|
||||
|
||||
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
|
||||
# tab).
|
||||
indent-string=' '
|
||||
|
||||
# Number of spaces of indent required inside a hanging or continued line.
|
||||
indent-after-paren=4
|
||||
|
||||
# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
|
||||
expected-line-ending-format=
|
||||
|
||||
|
||||
[MISCELLANEOUS]
|
||||
|
||||
# List of note tags to take in consideration, separated by a comma.
|
||||
notes=FIXME,XXX,TODO
|
||||
|
||||
|
||||
[LOGGING]
|
||||
|
||||
# Logging modules to check that the string format arguments are in logging
|
||||
# function parameter format
|
||||
logging-modules=logging,logger
|
||||
|
||||
|
||||
[SPELLING]
|
||||
|
||||
# Spelling dictionary name. Available dictionaries: none. To make it working
|
||||
# install python-enchant package.
|
||||
spelling-dict=
|
||||
|
||||
# List of comma separated words that should not be checked.
|
||||
spelling-ignore-words=
|
||||
|
||||
# A path to a file that contains private dictionary; one word per line.
|
||||
spelling-private-dict-file=
|
||||
|
||||
# Tells whether to store unknown words to indicated private dictionary in
|
||||
# --spelling-private-dict-file option instead of raising a message.
|
||||
spelling-store-unknown-words=no
|
||||
|
||||
|
||||
[TYPECHECK]
|
||||
|
||||
# Tells whether missing members accessed in mixin class should be ignored. A
|
||||
# mixin class is detected if its name ends with "mixin" (case insensitive).
|
||||
ignore-mixin-members=yes
|
||||
|
||||
# List of module names for which member attributes should not be checked
|
||||
# (useful for modules/projects where namespaces are manipulated during runtime
|
||||
# and thus existing member attributes cannot be deduced by static analysis
|
||||
ignored-modules=
|
||||
|
||||
# List of classes names for which member attributes should not be checked
|
||||
# (useful for classes with attributes dynamically set).
|
||||
ignored-classes=SQLObject
|
||||
|
||||
# When zope mode is activated, add a predefined set of Zope acquired attributes
|
||||
# to generated-members.
|
||||
zope=no
|
||||
|
||||
# List of members which are set dynamically and missed by pylint inference
|
||||
# system, and so shouldn't trigger E0201 when accessed. Python regular
|
||||
# expressions are accepted.
|
||||
generated-members=REQUEST,acl_users,aq_parent
|
||||
|
||||
|
||||
[SIMILARITIES]
|
||||
|
||||
# Minimum lines number of a similarity.
|
||||
min-similarity-lines=4
|
||||
|
||||
# Ignore comments when computing similarities.
|
||||
ignore-comments=yes
|
||||
|
||||
# Ignore docstrings when computing similarities.
|
||||
ignore-docstrings=yes
|
||||
|
||||
# Ignore imports when computing similarities.
|
||||
ignore-imports=no
|
||||
|
||||
|
||||
[VARIABLES]
|
||||
|
||||
# Tells whether we should check for unused import in __init__ files.
|
||||
init-import=no
|
||||
|
||||
# A regular expression matching the name of dummy variables (i.e. expectedly
|
||||
# not used).
|
||||
dummy-variables-rgx=_$|dummy|unused
|
||||
|
||||
# List of additional names supposed to be defined in builtins. Remember that
|
||||
# you should avoid to define new builtins when possible.
|
||||
additional-builtins=
|
||||
|
||||
# List of strings which can identify a callback function by name. A callback
|
||||
# name must start or end with one of those strings.
|
||||
callbacks=cb_,_cb
|
||||
|
||||
|
||||
[BASIC]
|
||||
|
||||
# Required attributes for module, separated by a comma
|
||||
required-attributes=
|
||||
|
||||
# List of builtins function names that should not be used, separated by a comma
|
||||
bad-functions=map,filter,input
|
||||
|
||||
# Good variable names which should always be accepted, separated by a comma
|
||||
good-names=i,j,k,ex,Run,_,logger
|
||||
|
||||
# Bad variable names which should always be refused, separated by a comma
|
||||
bad-names=foo,bar,baz,toto,tutu,tata
|
||||
|
||||
# Colon-delimited sets of names that determine each other's naming style when
|
||||
# the name regexes allow several styles.
|
||||
name-group=
|
||||
|
||||
# Include a hint for the correct naming format with invalid-name
|
||||
include-naming-hint=no
|
||||
|
||||
# Regular expression matching correct function names
|
||||
function-rgx=[a-z_][a-z0-9_]{2,40}$
|
||||
|
||||
# Naming hint for function names
|
||||
function-name-hint=[a-z_][a-z0-9_]{2,30}$
|
||||
|
||||
# Regular expression matching correct variable names
|
||||
variable-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||
|
||||
# Naming hint for variable names
|
||||
variable-name-hint=[a-z_][a-z0-9_]{2,30}$
|
||||
|
||||
# Regular expression matching correct constant names
|
||||
const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
|
||||
|
||||
# Naming hint for constant names
|
||||
const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$
|
||||
|
||||
# Regular expression matching correct attribute names
|
||||
attr-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||
|
||||
# Naming hint for attribute names
|
||||
attr-name-hint=[a-z_][a-z0-9_]{2,30}$
|
||||
|
||||
# Regular expression matching correct argument names
|
||||
argument-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||
|
||||
# Naming hint for argument names
|
||||
argument-name-hint=[a-z_][a-z0-9_]{2,30}$
|
||||
|
||||
# Regular expression matching correct class attribute names
|
||||
class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
|
||||
|
||||
# Naming hint for class attribute names
|
||||
class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
|
||||
|
||||
# Regular expression matching correct inline iteration names
|
||||
inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
|
||||
|
||||
# Naming hint for inline iteration names
|
||||
inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$
|
||||
|
||||
# Regular expression matching correct class names
|
||||
class-rgx=[A-Z_][a-zA-Z0-9]+$
|
||||
|
||||
# Naming hint for class names
|
||||
class-name-hint=[A-Z_][a-zA-Z0-9]+$
|
||||
|
||||
# Regular expression matching correct module names
|
||||
module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
|
||||
|
||||
# Naming hint for module names
|
||||
module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
|
||||
|
||||
# Regular expression matching correct method names
|
||||
method-rgx=[a-z_][a-z0-9_]{2,49}$
|
||||
|
||||
# Naming hint for method names
|
||||
method-name-hint=[a-z_][a-z0-9_]{2,30}$
|
||||
|
||||
# Regular expression which should only match function or class names that do
|
||||
# not require a docstring.
|
||||
no-docstring-rgx=__.*__|test_[A-Za-z0-9_]*|_.*|.*Test
|
||||
|
||||
# Minimum line length for functions/classes that require docstrings, shorter
|
||||
# ones are exempt.
|
||||
docstring-min-length=-1
|
||||
|
||||
|
||||
[CLASSES]
|
||||
|
||||
# List of interface methods to ignore, separated by a comma. This is used for
|
||||
# instance to not check methods defines in Zope's Interface base class.
|
||||
ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
|
||||
|
||||
# List of method names used to declare (i.e. assign) instance attributes.
|
||||
defining-attr-methods=__init__,__new__,setUp
|
||||
|
||||
# List of valid names for the first argument in a class method.
|
||||
valid-classmethod-first-arg=cls
|
||||
|
||||
# List of valid names for the first argument in a metaclass class method.
|
||||
valid-metaclass-classmethod-first-arg=mcs
|
||||
|
||||
# List of member names, which should be excluded from the protected access
|
||||
# warning.
|
||||
exclude-protected=_asdict,_fields,_replace,_source,_make
|
||||
|
||||
|
||||
[DESIGN]
|
||||
|
||||
# Maximum number of arguments for function / method
|
||||
max-args=6
|
||||
|
||||
# Argument names that match this expression will be ignored. Default to name
|
||||
# with leading underscore
|
||||
ignored-argument-names=_.*
|
||||
|
||||
# Maximum number of locals for function / method body
|
||||
max-locals=15
|
||||
|
||||
# Maximum number of return / yield for function / method body
|
||||
max-returns=6
|
||||
|
||||
# Maximum number of branch for function / method body
|
||||
max-branches=12
|
||||
|
||||
# Maximum number of statements in function / method body
|
||||
max-statements=50
|
||||
|
||||
# Maximum number of parents for a class (see R0901).
|
||||
max-parents=12
|
||||
|
||||
# Maximum number of attributes for a class (see R0902).
|
||||
max-attributes=7
|
||||
|
||||
# Minimum number of public methods for a class (see R0903).
|
||||
min-public-methods=2
|
||||
|
||||
# Maximum number of public methods for a class (see R0904).
|
||||
max-public-methods=20
|
||||
|
||||
|
||||
[IMPORTS]
|
||||
|
||||
# Deprecated modules which should not be used, separated by a comma
|
||||
deprecated-modules=regsub,TERMIOS,Bastion,rexec
|
||||
|
||||
# Create a graph of every (i.e. internal and external) dependencies in the
|
||||
# given file (report RP0402 must not be disabled)
|
||||
import-graph=
|
||||
|
||||
# Create a graph of external dependencies in the given file (report RP0402 must
|
||||
# not be disabled)
|
||||
ext-import-graph=
|
||||
|
||||
# Create a graph of internal dependencies in the given file (report RP0402 must
|
||||
# not be disabled)
|
||||
int-import-graph=
|
||||
|
||||
|
||||
[EXCEPTIONS]
|
||||
|
||||
# Exceptions that will emit a warning when being caught. Defaults to
|
||||
# "Exception"
|
||||
overgeneral-exceptions=Exception
|
||||
@@ -1,12 +1,12 @@
|
||||
"""ACME protocol implementation.
|
||||
|
||||
This module is an implementation of the `ACME protocol`_. Latest
|
||||
supported version: `v02`_.
|
||||
supported version: `draft-ietf-acme-01`_.
|
||||
|
||||
.. _`ACME protocol`: https://github.com/letsencrypt/acme-spec
|
||||
|
||||
.. _`v02`:
|
||||
https://github.com/letsencrypt/acme-spec/commit/d328fea2d507deb9822793c512830d827a4150c4
|
||||
.. _`ACME protocol`: https://ietf-wg-acme.github.io/acme
|
||||
|
||||
.. _`draft-ietf-acme-01`:
|
||||
https://github.com/ietf-wg-acme/acme/tree/draft-ietf-acme-acme-01
|
||||
|
||||
"""
|
||||
|
||||
@@ -228,15 +228,16 @@ class HTTP01Response(KeyAuthorizationChallengeResponse):
|
||||
|
||||
"""
|
||||
|
||||
WHITESPACE_CUTSET = "\n\r\t "
|
||||
"""Whitespace characters which should be ignored at the end of the body."""
|
||||
|
||||
def simple_verify(self, chall, domain, account_public_key, port=None):
|
||||
"""Simple verify.
|
||||
|
||||
:param challenges.SimpleHTTP chall: Corresponding challenge.
|
||||
:param unicode domain: Domain name being verified.
|
||||
:param account_public_key: Public key for the key pair
|
||||
being authorized. If ``None`` key verification is not
|
||||
performed!
|
||||
:param JWK account_public_key:
|
||||
:param JWK account_public_key: Public key for the key pair
|
||||
being authorized.
|
||||
:param int port: Port used in the validation.
|
||||
|
||||
:returns: ``True`` iff validation is successful, ``False``
|
||||
@@ -266,17 +267,11 @@ class HTTP01Response(KeyAuthorizationChallengeResponse):
|
||||
logger.debug("Received %s: %s. Headers: %s", http_response,
|
||||
http_response.text, http_response.headers)
|
||||
|
||||
found_ct = http_response.headers.get(
|
||||
"Content-Type", chall.CONTENT_TYPE)
|
||||
if found_ct != chall.CONTENT_TYPE:
|
||||
logger.debug("Wrong Content-Type: found %r, expected %r",
|
||||
found_ct, chall.CONTENT_TYPE)
|
||||
return False
|
||||
|
||||
if self.key_authorization != http_response.text:
|
||||
challenge_response = http_response.text.rstrip(self.WHITESPACE_CUTSET)
|
||||
if self.key_authorization != challenge_response:
|
||||
logger.debug("Key authorization from response (%r) doesn't match "
|
||||
"HTTP response (%r)", self.key_authorization,
|
||||
http_response.text)
|
||||
challenge_response)
|
||||
return False
|
||||
|
||||
return True
|
||||
@@ -288,9 +283,6 @@ class HTTP01(KeyAuthorizationChallenge):
|
||||
response_cls = HTTP01Response
|
||||
typ = response_cls.typ
|
||||
|
||||
CONTENT_TYPE = "text/plain"
|
||||
"""Only valid value for Content-Type if the header is included."""
|
||||
|
||||
URI_ROOT_PATH = ".well-known/acme-challenge"
|
||||
"""URI root path for the server provisioned resource."""
|
||||
|
||||
@@ -342,7 +334,7 @@ class TLSSNI01Response(KeyAuthorizationChallengeResponse):
|
||||
"""
|
||||
|
||||
@property
|
||||
def z(self):
|
||||
def z(self): # pylint: disable=invalid-name
|
||||
"""``z`` value used for verification.
|
||||
|
||||
:rtype bytes:
|
||||
@@ -397,7 +389,14 @@ class TLSSNI01Response(KeyAuthorizationChallengeResponse):
|
||||
return crypto_util.probe_sni(**kwargs)
|
||||
|
||||
def verify_cert(self, cert):
|
||||
"""Verify tls-sni-01 challenge certificate."""
|
||||
"""Verify tls-sni-01 challenge certificate.
|
||||
|
||||
:param OpensSSL.crypto.X509 cert: Challenge certificate.
|
||||
|
||||
:returns: Whether the certificate was successfully verified.
|
||||
:rtype: bool
|
||||
|
||||
"""
|
||||
# pylint: disable=protected-access
|
||||
sans = crypto_util._pyopenssl_cert_or_req_san(cert)
|
||||
logging.debug('Certificate %s. SANs: %s', cert.digest('sha1'), sans)
|
||||
|
||||
@@ -13,7 +13,7 @@ from acme import other
|
||||
from acme import test_util
|
||||
|
||||
|
||||
CERT = test_util.load_cert('cert.pem')
|
||||
CERT = test_util.load_comparable_cert('cert.pem')
|
||||
KEY = jose.JWKRSA(key=test_util.load_rsa_private_key('rsa512_key.pem'))
|
||||
|
||||
|
||||
@@ -73,7 +73,8 @@ class KeyAuthorizationChallengeResponseTest(unittest.TestCase):
|
||||
def test_verify_wrong_form(self):
|
||||
from acme.challenges import KeyAuthorizationChallengeResponse
|
||||
response = KeyAuthorizationChallengeResponse(
|
||||
key_authorization='.foo.oKGqedy-b-acd5eoybm2f-NVFxvyOoET5CNy3xnv8WY')
|
||||
key_authorization='.foo.oKGqedy-b-acd5eoybm2f-'
|
||||
'NVFxvyOoET5CNy3xnv8WY')
|
||||
self.assertFalse(response.verify(self.chall, KEY.public_key()))
|
||||
|
||||
|
||||
@@ -92,7 +93,6 @@ class HTTP01ResponseTest(unittest.TestCase):
|
||||
from acme.challenges import HTTP01
|
||||
self.chall = HTTP01(token=(b'x' * 16))
|
||||
self.response = self.chall.response(KEY)
|
||||
self.good_headers = {'Content-Type': HTTP01.CONTENT_TYPE}
|
||||
|
||||
def test_to_partial_json(self):
|
||||
self.assertEqual(self.jmsg, self.msg.to_partial_json())
|
||||
@@ -113,24 +113,26 @@ class HTTP01ResponseTest(unittest.TestCase):
|
||||
@mock.patch("acme.challenges.requests.get")
|
||||
def test_simple_verify_good_validation(self, mock_get):
|
||||
validation = self.chall.validation(KEY)
|
||||
mock_get.return_value = mock.MagicMock(
|
||||
text=validation, headers=self.good_headers)
|
||||
mock_get.return_value = mock.MagicMock(text=validation)
|
||||
self.assertTrue(self.response.simple_verify(
|
||||
self.chall, "local", KEY.public_key()))
|
||||
mock_get.assert_called_once_with(self.chall.uri("local"))
|
||||
|
||||
@mock.patch("acme.challenges.requests.get")
|
||||
def test_simple_verify_bad_validation(self, mock_get):
|
||||
mock_get.return_value = mock.MagicMock(
|
||||
text="!", headers=self.good_headers)
|
||||
mock_get.return_value = mock.MagicMock(text="!")
|
||||
self.assertFalse(self.response.simple_verify(
|
||||
self.chall, "local", KEY.public_key()))
|
||||
|
||||
@mock.patch("acme.challenges.requests.get")
|
||||
def test_simple_verify_bad_content_type(self, mock_get):
|
||||
mock_get().text = self.chall.token
|
||||
self.assertFalse(self.response.simple_verify(
|
||||
def test_simple_verify_whitespace_validation(self, mock_get):
|
||||
from acme.challenges import HTTP01Response
|
||||
mock_get.return_value = mock.MagicMock(
|
||||
text=(self.chall.validation(KEY) +
|
||||
HTTP01Response.WHITESPACE_CUTSET))
|
||||
self.assertTrue(self.response.simple_verify(
|
||||
self.chall, "local", KEY.public_key()))
|
||||
mock_get.assert_called_once_with(self.chall.uri("local"))
|
||||
|
||||
@mock.patch("acme.challenges.requests.get")
|
||||
def test_simple_verify_connection_error(self, mock_get):
|
||||
@@ -272,10 +274,12 @@ class TLSSNI01ResponseTest(unittest.TestCase):
|
||||
@mock.patch('acme.challenges.TLSSNI01Response.verify_cert', autospec=True)
|
||||
def test_simple_verify(self, mock_verify_cert):
|
||||
mock_verify_cert.return_value = mock.sentinel.verification
|
||||
self.assertEqual(mock.sentinel.verification, self.response.simple_verify(
|
||||
self.chall, self.domain, KEY.public_key(),
|
||||
cert=mock.sentinel.cert))
|
||||
mock_verify_cert.assert_called_once_with(self.response, mock.sentinel.cert)
|
||||
self.assertEqual(
|
||||
mock.sentinel.verification, self.response.simple_verify(
|
||||
self.chall, self.domain, KEY.public_key(),
|
||||
cert=mock.sentinel.cert))
|
||||
mock_verify_cert.assert_called_once_with(
|
||||
self.response, mock.sentinel.cert)
|
||||
|
||||
@mock.patch('acme.challenges.TLSSNI01Response.probe_cert')
|
||||
def test_simple_verify_false_on_probe_error(self, mock_probe_cert):
|
||||
@@ -420,7 +424,7 @@ class ProofOfPossessionHintsTest(unittest.TestCase):
|
||||
'jwk': jwk,
|
||||
'certFingerprints': cert_fingerprints,
|
||||
'certs': (jose.encode_b64jose(OpenSSL.crypto.dump_certificate(
|
||||
OpenSSL.crypto.FILETYPE_ASN1, CERT)),),
|
||||
OpenSSL.crypto.FILETYPE_ASN1, CERT.wrapped)),),
|
||||
'subjectKeyIdentifiers': subject_key_identifiers,
|
||||
'serialNumbers': serial_numbers,
|
||||
'issuers': issuers,
|
||||
@@ -589,7 +593,8 @@ class DNSTest(unittest.TestCase):
|
||||
|
||||
def test_check_validation_wrong_fields(self):
|
||||
bad_validation = jose.JWS.sign(
|
||||
payload=self.msg.update(token=b'x' * 20).json_dumps().encode('utf-8'),
|
||||
payload=self.msg.update(
|
||||
token=b'x' * 20).json_dumps().encode('utf-8'),
|
||||
alg=jose.RS256, key=KEY)
|
||||
self.assertFalse(self.msg.check_validation(
|
||||
bad_validation, KEY.public_key()))
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""ACME client API."""
|
||||
import collections
|
||||
import datetime
|
||||
import heapq
|
||||
import logging
|
||||
@@ -20,7 +21,9 @@ from acme import messages
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Python does not validate certificates by default before version 2.7.9
|
||||
# Prior to Python 2.7.9 the stdlib SSL module did not allow a user to configure
|
||||
# many important security related options. On these platforms we use PyOpenSSL
|
||||
# for SSL, which does allow these options to be configured.
|
||||
# https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning
|
||||
if sys.version_info < (2, 7, 9): # pragma: no cover
|
||||
requests.packages.urllib3.contrib.pyopenssl.inject_into_urllib3()
|
||||
@@ -64,15 +67,13 @@ class Client(object): # pylint: disable=too-many-instance-attributes
|
||||
@classmethod
|
||||
def _regr_from_response(cls, response, uri=None, new_authzr_uri=None,
|
||||
terms_of_service=None):
|
||||
terms_of_service = (
|
||||
response.links['terms-of-service']['url']
|
||||
if 'terms-of-service' in response.links else terms_of_service)
|
||||
if 'terms-of-service' in response.links:
|
||||
terms_of_service = response.links['terms-of-service']['url']
|
||||
if 'next' in response.links:
|
||||
new_authzr_uri = response.links['next']['url']
|
||||
|
||||
if new_authzr_uri is None:
|
||||
try:
|
||||
new_authzr_uri = response.links['next']['url']
|
||||
except KeyError:
|
||||
raise errors.ClientError('"next" link missing')
|
||||
raise errors.ClientError('"next" link missing')
|
||||
|
||||
return messages.RegistrationResource(
|
||||
body=messages.Registration.from_json(response.json()),
|
||||
@@ -246,9 +247,7 @@ 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.
|
||||
|
||||
:param response: Response from `poll`.
|
||||
:type response: `requests.Response`
|
||||
|
||||
:param requests.Response response: Response from `poll`.
|
||||
:param int default: Default value (in seconds), used when
|
||||
``Retry-After`` header is not present or invalid.
|
||||
|
||||
@@ -323,32 +322,40 @@ class Client(object): # pylint: disable=too-many-instance-attributes
|
||||
body=jose.ComparableX509(OpenSSL.crypto.load_certificate(
|
||||
OpenSSL.crypto.FILETYPE_ASN1, response.content)))
|
||||
|
||||
def poll_and_request_issuance(self, csr, authzrs, mintime=5):
|
||||
def poll_and_request_issuance(
|
||||
self, csr, authzrs, mintime=5, max_attempts=10):
|
||||
"""Poll and request issuance.
|
||||
|
||||
This function polls all provided Authorization Resource URIs
|
||||
until all challenges are valid, respecting ``Retry-After`` HTTP
|
||||
headers, and then calls `request_issuance`.
|
||||
|
||||
.. todo:: add `max_attempts` or `timeout`
|
||||
|
||||
:param csr: CSR.
|
||||
:type csr: `OpenSSL.crypto.X509Req` wrapped in `.ComparableX509`
|
||||
|
||||
:param .ComparableX509 csr: CSR (`OpenSSL.crypto.X509Req`
|
||||
wrapped in `.ComparableX509`)
|
||||
:param authzrs: `list` of `.AuthorizationResource`
|
||||
|
||||
:param int mintime: Minimum time before next attempt, used if
|
||||
``Retry-After`` is not present in the response.
|
||||
:param int max_attempts: Maximum number of attempts (per
|
||||
authorization) before `PollError` with non-empty ``waiting``
|
||||
is raised.
|
||||
|
||||
:returns: ``(cert, updated_authzrs)`` `tuple` where ``cert`` is
|
||||
the issued certificate (`.messages.CertificateResource.),
|
||||
the issued certificate (`.messages.CertificateResource`),
|
||||
and ``updated_authzrs`` is a `tuple` consisting of updated
|
||||
Authorization Resources (`.AuthorizationResource`) as
|
||||
present in the responses from server, and in the same order
|
||||
as the input ``authzrs``.
|
||||
:rtype: `tuple`
|
||||
|
||||
:raises PollError: in case of timeout or if some authorization
|
||||
was marked by the CA as invalid
|
||||
|
||||
"""
|
||||
# pylint: disable=too-many-locals
|
||||
assert max_attempts > 0
|
||||
attempts = collections.defaultdict(int)
|
||||
exhausted = set()
|
||||
|
||||
# priority queue with datetime (based on Retry-After) as key,
|
||||
# and original Authorization Resource as value
|
||||
waiting = [(datetime.datetime.now(), authzr) for authzr in authzrs]
|
||||
@@ -370,11 +377,20 @@ class Client(object): # pylint: disable=too-many-instance-attributes
|
||||
updated_authzr, response = self.poll(updated[authzr])
|
||||
updated[authzr] = updated_authzr
|
||||
|
||||
attempts[authzr] += 1
|
||||
# pylint: disable=no-member
|
||||
if updated_authzr.body.status != messages.STATUS_VALID:
|
||||
# push back to the priority queue, with updated retry_after
|
||||
heapq.heappush(waiting, (self.retry_after(
|
||||
response, default=mintime), authzr))
|
||||
if updated_authzr.body.status not in (
|
||||
messages.STATUS_VALID, messages.STATUS_INVALID):
|
||||
if attempts[authzr] < max_attempts:
|
||||
# push back to the priority queue, with updated retry_after
|
||||
heapq.heappush(waiting, (self.retry_after(
|
||||
response, default=mintime), authzr))
|
||||
else:
|
||||
exhausted.add(authzr)
|
||||
|
||||
if exhausted or any(authzr.body.status == messages.STATUS_INVALID
|
||||
for authzr in six.itervalues(updated)):
|
||||
raise errors.PollError(exhausted, updated)
|
||||
|
||||
updated_authzrs = tuple(updated[authzr] for authzr in authzrs)
|
||||
return self.request_issuance(csr, updated_authzrs), updated_authzrs
|
||||
@@ -475,7 +491,7 @@ class Client(object): # pylint: disable=too-many-instance-attributes
|
||||
'Successful revocation must return HTTP OK status')
|
||||
|
||||
|
||||
class ClientNetwork(object):
|
||||
class ClientNetwork(object): # pylint: disable=too-many-instance-attributes
|
||||
"""Client network."""
|
||||
JSON_CONTENT_TYPE = 'application/json'
|
||||
JSON_ERROR_CONTENT_TYPE = 'application/problem+json'
|
||||
@@ -531,7 +547,7 @@ class ClientNetwork(object):
|
||||
# TODO: response.json() is called twice, once here, and
|
||||
# once in _get and _post clients
|
||||
jobj = response.json()
|
||||
except ValueError as error:
|
||||
except ValueError:
|
||||
jobj = None
|
||||
|
||||
if not response.ok:
|
||||
|
||||
@@ -34,8 +34,10 @@ class ClientTest(unittest.TestCase):
|
||||
self.net.get.return_value = self.response
|
||||
|
||||
self.directory = messages.Directory({
|
||||
messages.NewRegistration: 'https://www.letsencrypt-demo.org/acme/new-reg',
|
||||
messages.Revocation: 'https://www.letsencrypt-demo.org/acme/revoke-cert',
|
||||
messages.NewRegistration:
|
||||
'https://www.letsencrypt-demo.org/acme/new-reg',
|
||||
messages.Revocation:
|
||||
'https://www.letsencrypt-demo.org/acme/revoke-cert',
|
||||
})
|
||||
|
||||
from acme.client import Client
|
||||
@@ -127,6 +129,13 @@ class ClientTest(unittest.TestCase):
|
||||
self.response.json.return_value = self.regr.body.to_json()
|
||||
self.assertEqual(self.regr, self.client.query_registration(self.regr))
|
||||
|
||||
def test_query_registration_updates_new_authzr_uri(self):
|
||||
self.response.json.return_value = self.regr.body.to_json()
|
||||
self.response.links = {'next': {'url': 'UPDATED'}}
|
||||
self.assertEqual(
|
||||
'UPDATED',
|
||||
self.client.query_registration(self.regr).new_authzr_uri)
|
||||
|
||||
def test_agree_to_tos(self):
|
||||
self.client.update_registration = mock.Mock()
|
||||
self.client.agree_to_tos(self.regr)
|
||||
@@ -271,9 +280,9 @@ class ClientTest(unittest.TestCase):
|
||||
# result, increment clock
|
||||
clock.dt += datetime.timedelta(seconds=2)
|
||||
|
||||
if not authzr.retries: # no more retries
|
||||
if len(authzr.retries) == 1: # no more retries
|
||||
done = mock.MagicMock(uri=authzr.uri, times=authzr.times)
|
||||
done.body.status = messages.STATUS_VALID
|
||||
done.body.status = authzr.retries[0]
|
||||
return done, []
|
||||
|
||||
# response (2nd result tuple element) is reduced to only
|
||||
@@ -289,7 +298,8 @@ class ClientTest(unittest.TestCase):
|
||||
|
||||
mintime = 7
|
||||
|
||||
def retry_after(response, default): # pylint: disable=missing-docstring
|
||||
def retry_after(response, default):
|
||||
# pylint: disable=missing-docstring
|
||||
# check that poll_and_request_issuance correctly passes mintime
|
||||
self.assertEqual(default, mintime)
|
||||
return clock.dt + datetime.timedelta(seconds=response)
|
||||
@@ -302,12 +312,17 @@ class ClientTest(unittest.TestCase):
|
||||
|
||||
csr = mock.MagicMock()
|
||||
authzrs = (
|
||||
mock.MagicMock(uri='a', times=[], retries=(8, 20, 30)),
|
||||
mock.MagicMock(uri='b', times=[], retries=(5,)),
|
||||
mock.MagicMock(uri='a', times=[], retries=(
|
||||
8, 20, 30, messages.STATUS_VALID)),
|
||||
mock.MagicMock(uri='b', times=[], retries=(
|
||||
5, messages.STATUS_VALID)),
|
||||
)
|
||||
|
||||
cert, updated_authzrs = self.client.poll_and_request_issuance(
|
||||
csr, authzrs, mintime=mintime)
|
||||
csr, authzrs, mintime=mintime,
|
||||
# make sure that max_attempts is per-authorization, rather
|
||||
# than global
|
||||
max_attempts=max(len(authzrs[0].retries), len(authzrs[1].retries)))
|
||||
self.assertTrue(cert[0] is csr)
|
||||
self.assertTrue(cert[1] is updated_authzrs)
|
||||
self.assertEqual(updated_authzrs[0].uri, 'a...')
|
||||
@@ -327,6 +342,18 @@ class ClientTest(unittest.TestCase):
|
||||
])
|
||||
self.assertEqual(clock.dt, datetime.datetime(2015, 3, 27, 0, 1, 7))
|
||||
|
||||
# CA sets invalid | TODO: move to a separate test
|
||||
invalid_authzr = mock.MagicMock(
|
||||
times=[], retries=[messages.STATUS_INVALID])
|
||||
self.assertRaises(
|
||||
errors.PollError, self.client.poll_and_request_issuance,
|
||||
csr, authzrs=(invalid_authzr,), mintime=mintime)
|
||||
|
||||
# exceeded max_attemps | TODO: move to a separate test
|
||||
self.assertRaises(
|
||||
errors.PollError, self.client.poll_and_request_issuance,
|
||||
csr, authzrs, mintime=mintime, max_attempts=2)
|
||||
|
||||
def test_check_cert(self):
|
||||
self.response.headers['Location'] = self.certr.uri
|
||||
self.response.content = CERT_DER
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
"""Crypto utilities."""
|
||||
import contextlib
|
||||
import logging
|
||||
import re
|
||||
import socket
|
||||
import sys
|
||||
|
||||
from six.moves import range # pylint: disable=import-error,redefined-builtin
|
||||
|
||||
import OpenSSL
|
||||
|
||||
from acme import errors
|
||||
@@ -70,7 +69,7 @@ class SSLSocket(object): # pylint: disable=too-few-public-methods
|
||||
class FakeConnection(object):
|
||||
"""Fake OpenSSL.SSL.Connection."""
|
||||
|
||||
# pylint: disable=missing-docstring
|
||||
# pylint: disable=too-few-public-methods,missing-docstring
|
||||
|
||||
def __init__(self, connection):
|
||||
self._wrapped = connection
|
||||
@@ -161,31 +160,31 @@ def _pyopenssl_cert_or_req_san(cert_or_req):
|
||||
:rtype: `list` of `unicode`
|
||||
|
||||
"""
|
||||
# constants based on implementation of
|
||||
# OpenSSL.crypto.X509Error._subjectAltNameString
|
||||
parts_separator = ", "
|
||||
# This function finds SANs by dumping the certificate/CSR to text and
|
||||
# searching for "X509v3 Subject Alternative Name" in the text. This method
|
||||
# is used to support PyOpenSSL version 0.13 where the
|
||||
# `_subjectAltNameString` and `get_extensions` methods are not available
|
||||
# for CSRs.
|
||||
|
||||
# constants based on PyOpenSSL certificate/CSR text dump
|
||||
part_separator = ":"
|
||||
extension_short_name = b"subjectAltName"
|
||||
parts_separator = ", "
|
||||
prefix = "DNS" + part_separator
|
||||
|
||||
if hasattr(cert_or_req, 'get_extensions'): # X509Req
|
||||
extensions = cert_or_req.get_extensions()
|
||||
else: # X509
|
||||
extensions = [cert_or_req.get_extension(i)
|
||||
for i in range(cert_or_req.get_extension_count())]
|
||||
|
||||
# pylint: disable=protected-access,no-member
|
||||
label = OpenSSL.crypto.X509Extension._prefixes[OpenSSL.crypto._lib.GEN_DNS]
|
||||
assert parts_separator not in label
|
||||
prefix = label + part_separator
|
||||
|
||||
san_extensions = [
|
||||
ext._subjectAltNameString().split(parts_separator)
|
||||
for ext in extensions if ext.get_short_name() == extension_short_name]
|
||||
if isinstance(cert_or_req, OpenSSL.crypto.X509):
|
||||
func = OpenSSL.crypto.dump_certificate
|
||||
else:
|
||||
func = OpenSSL.crypto.dump_certificate_request
|
||||
text = func(OpenSSL.crypto.FILETYPE_TEXT, cert_or_req).decode("utf-8")
|
||||
# WARNING: this function does not support multiple SANs extensions.
|
||||
# Multiple X509v3 extensions of the same type is disallowed by RFC 5280.
|
||||
match = re.search(r"X509v3 Subject Alternative Name:\s*(.*)", text)
|
||||
# WARNING: this function assumes that no SAN can include
|
||||
# parts_separator, hence the split!
|
||||
sans_parts = [] if match is None else match.group(1).split(parts_separator)
|
||||
|
||||
return [part.split(part_separator)[1] for parts in san_extensions
|
||||
for part in parts if part.startswith(prefix)]
|
||||
return [part.split(part_separator)[1]
|
||||
for part in sans_parts if part.startswith(prefix)]
|
||||
|
||||
|
||||
def gen_ss_cert(key, domains, not_before=None,
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
"""Tests for acme.crypto_util."""
|
||||
import itertools
|
||||
import socket
|
||||
import threading
|
||||
import time
|
||||
import unittest
|
||||
|
||||
import six
|
||||
from six.moves import socketserver # pylint: disable=import-error
|
||||
|
||||
from acme import errors
|
||||
@@ -15,10 +17,10 @@ class SSLSocketAndProbeSNITest(unittest.TestCase):
|
||||
"""Tests for acme.crypto_util.SSLSocket/probe_sni."""
|
||||
|
||||
def setUp(self):
|
||||
self.cert = test_util.load_cert('cert.pem')
|
||||
self.cert = test_util.load_comparable_cert('cert.pem')
|
||||
key = test_util.load_pyopenssl_private_key('rsa512_key.pem')
|
||||
# pylint: disable=protected-access
|
||||
certs = {b'foo': (key, self.cert._wrapped)}
|
||||
certs = {b'foo': (key, self.cert.wrapped)}
|
||||
|
||||
from acme.crypto_util import SSLSocket
|
||||
|
||||
@@ -69,6 +71,15 @@ class PyOpenSSLCertOrReqSANTest(unittest.TestCase):
|
||||
from acme.crypto_util import _pyopenssl_cert_or_req_san
|
||||
return _pyopenssl_cert_or_req_san(loader(name))
|
||||
|
||||
@classmethod
|
||||
def _get_idn_names(cls):
|
||||
"""Returns expected names from '{cert,csr}-idnsans.pem'."""
|
||||
chars = [six.unichr(i) for i in itertools.chain(range(0x3c3, 0x400),
|
||||
range(0x641, 0x6fc),
|
||||
range(0x1820, 0x1877))]
|
||||
return [''.join(chars[i: i + 45]) + '.invalid'
|
||||
for i in range(0, len(chars), 45)]
|
||||
|
||||
def _call_cert(self, name):
|
||||
return self._call(test_util.load_cert, name)
|
||||
|
||||
@@ -82,6 +93,14 @@ class PyOpenSSLCertOrReqSANTest(unittest.TestCase):
|
||||
self.assertEqual(self._call_cert('cert-san.pem'),
|
||||
['example.com', 'www.example.com'])
|
||||
|
||||
def test_cert_hundred_sans(self):
|
||||
self.assertEqual(self._call_cert('cert-100sans.pem'),
|
||||
['example{0}.com'.format(i) for i in range(1, 101)])
|
||||
|
||||
def test_cert_idn_sans(self):
|
||||
self.assertEqual(self._call_cert('cert-idnsans.pem'),
|
||||
self._get_idn_names())
|
||||
|
||||
def test_csr_no_sans(self):
|
||||
self.assertEqual(self._call_csr('csr-nosans.pem'), [])
|
||||
|
||||
@@ -94,10 +113,18 @@ class PyOpenSSLCertOrReqSANTest(unittest.TestCase):
|
||||
|
||||
def test_csr_six_sans(self):
|
||||
self.assertEqual(self._call_csr('csr-6sans.pem'),
|
||||
["example.com", "example.org", "example.net",
|
||||
"example.info", "subdomain.example.com",
|
||||
"other.subdomain.example.com"])
|
||||
['example.com', 'example.org', 'example.net',
|
||||
'example.info', 'subdomain.example.com',
|
||||
'other.subdomain.example.com'])
|
||||
|
||||
def test_csr_hundred_sans(self):
|
||||
self.assertEqual(self._call_csr('csr-100sans.pem'),
|
||||
['example{0}.com'.format(i) for i in range(1, 101)])
|
||||
|
||||
def test_csr_idn_sans(self):
|
||||
self.assertEqual(self._call_csr('csr-idnsans.pem'),
|
||||
self._get_idn_names())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if __name__ == '__main__':
|
||||
unittest.main() # pragma: no cover
|
||||
|
||||
@@ -51,3 +51,30 @@ class MissingNonce(NonceError):
|
||||
return ('Server {0} response did not include a replay '
|
||||
'nonce, headers: {1}'.format(
|
||||
self.response.request.method, self.response.headers))
|
||||
|
||||
|
||||
class PollError(ClientError):
|
||||
"""Generic error when polling for authorization fails.
|
||||
|
||||
This might be caused by either timeout (`exhausted` will be non-empty)
|
||||
or by some authorization being invalid.
|
||||
|
||||
:ivar exhausted: Set of `.AuthorizationResource` that didn't finish
|
||||
within max allowed attempts.
|
||||
:ivar updated: Mapping from original `.AuthorizationResource`
|
||||
to the most recently updated one
|
||||
|
||||
"""
|
||||
def __init__(self, exhausted, updated):
|
||||
self.exhausted = exhausted
|
||||
self.updated = updated
|
||||
super(PollError, self).__init__()
|
||||
|
||||
@property
|
||||
def timeout(self):
|
||||
"""Was the error caused by timeout?"""
|
||||
return bool(self.exhausted)
|
||||
|
||||
def __repr__(self):
|
||||
return '{0}(exhausted={1!r}, updated={2!r})'.format(
|
||||
self.__class__.__name__, self.exhausted, self.updated)
|
||||
|
||||
@@ -29,5 +29,25 @@ class MissingNonceTest(unittest.TestCase):
|
||||
self.assertTrue("{}" in str(self.error))
|
||||
|
||||
|
||||
class PollErrorTest(unittest.TestCase):
|
||||
"""Tests for acme.errors.PollError."""
|
||||
|
||||
def setUp(self):
|
||||
from acme.errors import PollError
|
||||
self.timeout = PollError(
|
||||
exhausted=set([mock.sentinel.AR]),
|
||||
updated={})
|
||||
self.invalid = PollError(exhausted=set(), updated={
|
||||
mock.sentinel.AR: mock.sentinel.AR2})
|
||||
|
||||
def test_timeout(self):
|
||||
self.assertTrue(self.timeout.timeout)
|
||||
self.assertFalse(self.invalid.timeout)
|
||||
|
||||
def test_repr(self):
|
||||
self.assertEqual('PollError(exhausted=%s, updated={sentinel.AR: '
|
||||
'sentinel.AR2})' % repr(set()), repr(self.invalid))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main() # pragma: no cover
|
||||
|
||||
@@ -194,7 +194,7 @@ class JSONDeSerializable(object):
|
||||
:rtype: str
|
||||
|
||||
"""
|
||||
return self.json_dumps(sort_keys=True, indent=4)
|
||||
return self.json_dumps(sort_keys=True, indent=4, separators=(',', ': '))
|
||||
|
||||
@classmethod
|
||||
def json_dump_default(cls, python_object):
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
"""Tests for acme.jose.interfaces."""
|
||||
import unittest
|
||||
|
||||
import six
|
||||
|
||||
|
||||
class JSONDeSerializableTest(unittest.TestCase):
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
@@ -92,9 +90,8 @@ class JSONDeSerializableTest(unittest.TestCase):
|
||||
self.assertEqual('["foo1", "foo2"]', self.seq.json_dumps())
|
||||
|
||||
def test_json_dumps_pretty(self):
|
||||
filler = ' ' if six.PY2 else ''
|
||||
self.assertEqual(self.seq.json_dumps_pretty(),
|
||||
'[\n "foo1",{0}\n "foo2"\n]'.format(filler))
|
||||
'[\n "foo1",\n "foo2"\n]')
|
||||
|
||||
def test_json_dump_default(self):
|
||||
from acme.jose.interfaces import JSONDeSerializable
|
||||
|
||||
@@ -226,7 +226,7 @@ class JSONObjectWithFields(util.ImmutableMap, interfaces.JSONDeSerializable):
|
||||
|
||||
:param str name: Name of the field to be encoded.
|
||||
|
||||
:raises erors.SerializationError: if field cannot be serialized
|
||||
:raises errors.SerializationError: if field cannot be serialized
|
||||
:raises errors.Error: if field could not be found
|
||||
|
||||
"""
|
||||
@@ -373,7 +373,7 @@ def encode_cert(cert):
|
||||
|
||||
"""
|
||||
return encode_b64jose(OpenSSL.crypto.dump_certificate(
|
||||
OpenSSL.crypto.FILETYPE_ASN1, cert))
|
||||
OpenSSL.crypto.FILETYPE_ASN1, cert.wrapped))
|
||||
|
||||
|
||||
def decode_cert(b64der):
|
||||
@@ -398,7 +398,7 @@ def encode_csr(csr):
|
||||
|
||||
"""
|
||||
return encode_b64jose(OpenSSL.crypto.dump_certificate_request(
|
||||
OpenSSL.crypto.FILETYPE_ASN1, csr))
|
||||
OpenSSL.crypto.FILETYPE_ASN1, csr.wrapped))
|
||||
|
||||
|
||||
def decode_csr(b64der):
|
||||
|
||||
@@ -12,8 +12,8 @@ from acme.jose import interfaces
|
||||
from acme.jose import util
|
||||
|
||||
|
||||
CERT = test_util.load_cert('cert.pem')
|
||||
CSR = test_util.load_csr('csr.pem')
|
||||
CERT = test_util.load_comparable_cert('cert.pem')
|
||||
CSR = test_util.load_comparable_csr('csr.pem')
|
||||
|
||||
|
||||
class FieldTest(unittest.TestCase):
|
||||
|
||||
@@ -124,7 +124,7 @@ class Header(json_util.JSONObjectWithFields):
|
||||
@x5c.encoder
|
||||
def x5c(value): # pylint: disable=missing-docstring,no-self-argument
|
||||
return [base64.b64encode(OpenSSL.crypto.dump_certificate(
|
||||
OpenSSL.crypto.FILETYPE_ASN1, cert)) for cert in value]
|
||||
OpenSSL.crypto.FILETYPE_ASN1, cert.wrapped)) for cert in value]
|
||||
|
||||
@x5c.decoder
|
||||
def x5c(value): # pylint: disable=missing-docstring,no-self-argument
|
||||
|
||||
@@ -13,7 +13,7 @@ from acme.jose import jwa
|
||||
from acme.jose import jwk
|
||||
|
||||
|
||||
CERT = test_util.load_cert('cert.pem')
|
||||
CERT = test_util.load_comparable_cert('cert.pem')
|
||||
KEY = jwk.JWKRSA.load(test_util.load_vector('rsa512_key.pem'))
|
||||
|
||||
|
||||
@@ -68,13 +68,12 @@ class HeaderTest(unittest.TestCase):
|
||||
from acme.jose.jws import Header
|
||||
header = Header(x5c=(CERT, CERT))
|
||||
jobj = header.to_partial_json()
|
||||
cert_b64 = base64.b64encode(OpenSSL.crypto.dump_certificate(
|
||||
OpenSSL.crypto.FILETYPE_ASN1, CERT))
|
||||
cert_asn1 = OpenSSL.crypto.dump_certificate(
|
||||
OpenSSL.crypto.FILETYPE_ASN1, CERT.wrapped)
|
||||
cert_b64 = base64.b64encode(cert_asn1)
|
||||
self.assertEqual(jobj, {'x5c': [cert_b64, cert_b64]})
|
||||
self.assertEqual(header, Header.from_json(jobj))
|
||||
jobj['x5c'][0] = base64.b64encode(
|
||||
b'xxx' + OpenSSL.crypto.dump_certificate(
|
||||
OpenSSL.crypto.FILETYPE_ASN1, CERT))
|
||||
jobj['x5c'][0] = base64.b64encode(b'xxx' + cert_asn1)
|
||||
self.assertRaises(errors.DeserializationError, Header.from_json, jobj)
|
||||
|
||||
def test_find_key(self):
|
||||
|
||||
@@ -29,32 +29,41 @@ class abstractclassmethod(classmethod):
|
||||
class ComparableX509(object): # pylint: disable=too-few-public-methods
|
||||
"""Wrapper for OpenSSL.crypto.X509** objects that supports __eq__.
|
||||
|
||||
Wraps around:
|
||||
|
||||
- :class:`OpenSSL.crypto.X509`
|
||||
- :class:`OpenSSL.crypto.X509Req`
|
||||
:ivar wrapped: Wrapped certificate or certificate request.
|
||||
:type wrapped: `OpenSSL.crypto.X509` or `OpenSSL.crypto.X509Req`.
|
||||
|
||||
"""
|
||||
def __init__(self, wrapped):
|
||||
assert isinstance(wrapped, OpenSSL.crypto.X509) or isinstance(
|
||||
wrapped, OpenSSL.crypto.X509Req)
|
||||
self._wrapped = wrapped
|
||||
self.wrapped = wrapped
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self._wrapped, name)
|
||||
return getattr(self.wrapped, name)
|
||||
|
||||
def _dump(self, filetype=OpenSSL.crypto.FILETYPE_ASN1):
|
||||
# pylint: disable=missing-docstring,protected-access
|
||||
if isinstance(self._wrapped, OpenSSL.crypto.X509):
|
||||
"""Dumps the object into a buffer with the specified encoding.
|
||||
|
||||
:param int filetype: The desired encoding. Should be one of
|
||||
`OpenSSL.crypto.FILETYPE_ASN1`,
|
||||
`OpenSSL.crypto.FILETYPE_PEM`, or
|
||||
`OpenSSL.crypto.FILETYPE_TEXT`.
|
||||
|
||||
:returns: Encoded X509 object.
|
||||
:rtype: str
|
||||
|
||||
"""
|
||||
if isinstance(self.wrapped, OpenSSL.crypto.X509):
|
||||
func = OpenSSL.crypto.dump_certificate
|
||||
else: # assert in __init__ makes sure this is X509Req
|
||||
func = OpenSSL.crypto.dump_certificate_request
|
||||
return func(filetype, self._wrapped)
|
||||
return func(filetype, self.wrapped)
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, self.__class__):
|
||||
return NotImplemented
|
||||
return self._dump() == other._dump() # pylint: disable=protected-access
|
||||
# pylint: disable=protected-access
|
||||
return self._dump() == other._dump()
|
||||
|
||||
def __hash__(self):
|
||||
return hash((self.__class__, self._dump()))
|
||||
@@ -63,7 +72,7 @@ class ComparableX509(object): # pylint: disable=too-few-public-methods
|
||||
return not self == other
|
||||
|
||||
def __repr__(self):
|
||||
return '<{0}({1!r})>'.format(self.__class__.__name__, self._wrapped)
|
||||
return '<{0}({1!r})>'.format(self.__class__.__name__, self.wrapped)
|
||||
|
||||
|
||||
class ComparableKey(object): # pylint: disable=too-few-public-methods
|
||||
@@ -130,7 +139,7 @@ class ImmutableMap(collections.Mapping, collections.Hashable):
|
||||
"""Immutable key to value mapping with attribute access."""
|
||||
|
||||
__slots__ = ()
|
||||
"""Must be overriden in subclasses."""
|
||||
"""Must be overridden in subclasses."""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
if set(kwargs) != set(self.__slots__):
|
||||
|
||||
@@ -11,14 +11,17 @@ class ComparableX509Test(unittest.TestCase):
|
||||
"""Tests for acme.jose.util.ComparableX509."""
|
||||
|
||||
def setUp(self):
|
||||
# test_util.load_{csr,cert} return ComparableX509
|
||||
self.req1 = test_util.load_csr('csr.pem')
|
||||
self.req2 = test_util.load_csr('csr.pem')
|
||||
self.req_other = test_util.load_csr('csr-san.pem')
|
||||
# test_util.load_comparable_{csr,cert} return ComparableX509
|
||||
self.req1 = test_util.load_comparable_csr('csr.pem')
|
||||
self.req2 = test_util.load_comparable_csr('csr.pem')
|
||||
self.req_other = test_util.load_comparable_csr('csr-san.pem')
|
||||
|
||||
self.cert1 = test_util.load_cert('cert.pem')
|
||||
self.cert2 = test_util.load_cert('cert.pem')
|
||||
self.cert_other = test_util.load_cert('cert-san.pem')
|
||||
self.cert1 = test_util.load_comparable_cert('cert.pem')
|
||||
self.cert2 = test_util.load_comparable_cert('cert.pem')
|
||||
self.cert_other = test_util.load_comparable_cert('cert-san.pem')
|
||||
|
||||
def test_getattr_proxy(self):
|
||||
self.assertTrue(self.cert1.has_expired())
|
||||
|
||||
def test_eq(self):
|
||||
self.assertEqual(self.req1, self.req2)
|
||||
@@ -41,8 +44,8 @@ class ComparableX509Test(unittest.TestCase):
|
||||
|
||||
def test_repr(self):
|
||||
for x509 in self.req1, self.cert1:
|
||||
self.assertTrue(repr(x509).startswith(
|
||||
'<ComparableX509(<OpenSSL.crypto.X509'))
|
||||
self.assertEqual(repr(x509),
|
||||
'<ComparableX509({0!r})>'.format(x509.wrapped))
|
||||
|
||||
|
||||
class ComparableRSAKeyTest(unittest.TestCase):
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
import collections
|
||||
|
||||
from acme import challenges
|
||||
from acme import errors
|
||||
from acme import fields
|
||||
from acme import jose
|
||||
from acme import util
|
||||
|
||||
|
||||
class Error(jose.JSONObjectWithFields, Exception):
|
||||
class Error(jose.JSONObjectWithFields, errors.Error):
|
||||
"""ACME error.
|
||||
|
||||
https://tools.ietf.org/html/draft-ietf-appsawg-http-problem-00
|
||||
@@ -17,55 +18,44 @@ class Error(jose.JSONObjectWithFields, Exception):
|
||||
:ivar unicode detail:
|
||||
|
||||
"""
|
||||
ERROR_TYPE_NAMESPACE = 'urn:acme:error:'
|
||||
ERROR_TYPE_DESCRIPTIONS = {
|
||||
'badCSR': 'The CSR is unacceptable (e.g., due to a short key)',
|
||||
'badNonce': 'The client sent an unacceptable anti-replay nonce',
|
||||
'connection': 'The server could not connect to the client for DV',
|
||||
'dnssec': 'The server could not validate a DNSSEC signed domain',
|
||||
'malformed': 'The request message was malformed',
|
||||
'rateLimited': 'There were too many requests of a given type',
|
||||
'serverInternal': 'The server experienced an internal error',
|
||||
'tls': 'The server experienced a TLS error during DV',
|
||||
'unauthorized': 'The client lacks sufficient authorization',
|
||||
'unknownHost': 'The server could not resolve a domain name',
|
||||
}
|
||||
ERROR_TYPE_DESCRIPTIONS = dict(
|
||||
('urn:acme:error:' + name, description) for name, description in (
|
||||
('badCSR', 'The CSR is unacceptable (e.g., due to a short key)'),
|
||||
('badNonce', 'The client sent an unacceptable anti-replay nonce'),
|
||||
('connection', 'The server could not connect to the client to '
|
||||
'verify the domain'),
|
||||
('dnssec', 'The server could not validate a DNSSEC signed domain'),
|
||||
('invalidEmail',
|
||||
'The provided email for a registration was invalid'),
|
||||
('malformed', 'The request message was malformed'),
|
||||
('rateLimited', 'There were too many requests of a given type'),
|
||||
('serverInternal', 'The server experienced an internal error'),
|
||||
('tls', 'The server experienced a TLS error during domain '
|
||||
'verification'),
|
||||
('unauthorized', 'The client lacks sufficient authorization'),
|
||||
('unknownHost', 'The server could not resolve a domain name'),
|
||||
)
|
||||
)
|
||||
|
||||
typ = jose.Field('type')
|
||||
title = jose.Field('title', omitempty=True)
|
||||
detail = jose.Field('detail')
|
||||
|
||||
@typ.encoder
|
||||
def typ(value): # pylint: disable=missing-docstring,no-self-argument
|
||||
return Error.ERROR_TYPE_NAMESPACE + value
|
||||
|
||||
@typ.decoder
|
||||
def typ(value): # pylint: disable=missing-docstring,no-self-argument
|
||||
# pylint thinks isinstance(value, Error), so startswith is not found
|
||||
# pylint: disable=no-member
|
||||
if not value.startswith(Error.ERROR_TYPE_NAMESPACE):
|
||||
raise jose.DeserializationError('Missing error type prefix')
|
||||
|
||||
without_prefix = value[len(Error.ERROR_TYPE_NAMESPACE):]
|
||||
if without_prefix not in Error.ERROR_TYPE_DESCRIPTIONS:
|
||||
raise jose.DeserializationError('Error type not recognized')
|
||||
|
||||
return without_prefix
|
||||
|
||||
@property
|
||||
def description(self):
|
||||
"""Hardcoded error description based on its type.
|
||||
|
||||
:returns: Description if standard ACME error or ``None``.
|
||||
:rtype: unicode
|
||||
|
||||
"""
|
||||
return self.ERROR_TYPE_DESCRIPTIONS[self.typ]
|
||||
return self.ERROR_TYPE_DESCRIPTIONS.get(self.typ)
|
||||
|
||||
def __str__(self):
|
||||
if self.typ is not None:
|
||||
return ' :: '.join([self.typ, self.description, self.detail])
|
||||
else:
|
||||
return str(self.detail)
|
||||
return ' :: '.join(
|
||||
part for part in
|
||||
(self.typ, self.description, self.detail, self.title)
|
||||
if part is not None)
|
||||
|
||||
|
||||
class _Constant(jose.JSONDeSerializable, collections.Hashable):
|
||||
@@ -140,8 +130,9 @@ class Directory(jose.JSONDeSerializable):
|
||||
@classmethod
|
||||
def register(cls, resource_body_cls):
|
||||
"""Register resource."""
|
||||
assert resource_body_cls.resource_type not in cls._REGISTERED_TYPES
|
||||
cls._REGISTERED_TYPES[resource_body_cls.resource_type] = resource_body_cls
|
||||
resource_type = resource_body_cls.resource_type
|
||||
assert resource_type not in cls._REGISTERED_TYPES
|
||||
cls._REGISTERED_TYPES[resource_type] = resource_body_cls
|
||||
return resource_body_cls
|
||||
|
||||
def __init__(self, jobj):
|
||||
|
||||
@@ -8,8 +8,8 @@ from acme import jose
|
||||
from acme import test_util
|
||||
|
||||
|
||||
CERT = test_util.load_cert('cert.der')
|
||||
CSR = test_util.load_csr('csr.der')
|
||||
CERT = test_util.load_comparable_cert('cert.der')
|
||||
CSR = test_util.load_comparable_csr('csr.der')
|
||||
KEY = test_util.load_rsa_private_key('rsa512_key.pem')
|
||||
|
||||
|
||||
@@ -18,41 +18,30 @@ class ErrorTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
from acme.messages import Error
|
||||
self.error = Error(detail='foo', typ='malformed', title='title')
|
||||
self.jobj = {'detail': 'foo', 'title': 'some title'}
|
||||
|
||||
def test_typ_prefix(self):
|
||||
self.assertEqual('malformed', self.error.typ)
|
||||
self.assertEqual(
|
||||
'urn:acme:error:malformed', self.error.to_partial_json()['type'])
|
||||
self.assertEqual(
|
||||
'malformed', self.error.from_json(self.error.to_partial_json()).typ)
|
||||
|
||||
def test_typ_decoder_missing_prefix(self):
|
||||
from acme.messages import Error
|
||||
self.jobj['type'] = 'malformed'
|
||||
self.assertRaises(jose.DeserializationError, Error.from_json, self.jobj)
|
||||
self.jobj['type'] = 'not valid bare type'
|
||||
self.assertRaises(jose.DeserializationError, Error.from_json, self.jobj)
|
||||
|
||||
def test_typ_decoder_not_recognized(self):
|
||||
from acme.messages import Error
|
||||
self.jobj['type'] = 'urn:acme:error:baz'
|
||||
self.assertRaises(jose.DeserializationError, Error.from_json, self.jobj)
|
||||
|
||||
def test_description(self):
|
||||
self.assertEqual(
|
||||
'The request message was malformed', self.error.description)
|
||||
self.error = Error(
|
||||
detail='foo', typ='urn:acme:error:malformed', title='title')
|
||||
self.jobj = {
|
||||
'detail': 'foo',
|
||||
'title': 'some title',
|
||||
'type': 'urn:acme:error:malformed',
|
||||
}
|
||||
self.error_custom = Error(typ='custom', detail='bar')
|
||||
self.jobj_cusom = {'type': 'custom', 'detail': 'bar'}
|
||||
|
||||
def test_from_json_hashable(self):
|
||||
from acme.messages import Error
|
||||
hash(Error.from_json(self.error.to_json()))
|
||||
|
||||
def test_description(self):
|
||||
self.assertEqual(
|
||||
'The request message was malformed', self.error.description)
|
||||
self.assertTrue(self.error_custom.description is None)
|
||||
|
||||
def test_str(self):
|
||||
self.assertEqual(
|
||||
'malformed :: The request message was malformed :: foo',
|
||||
str(self.error))
|
||||
self.assertEqual('foo', str(self.error.update(typ=None)))
|
||||
'urn:acme:error:malformed :: The request message was '
|
||||
'malformed :: foo :: title', str(self.error))
|
||||
self.assertEqual('custom :: bar', str(self.error_custom))
|
||||
|
||||
|
||||
class ConstantTest(unittest.TestCase):
|
||||
@@ -232,7 +221,7 @@ class ChallengeBodyTest(unittest.TestCase):
|
||||
from acme.messages import Error
|
||||
from acme.messages import STATUS_INVALID
|
||||
self.status = STATUS_INVALID
|
||||
error = Error(typ='serverInternal',
|
||||
error = Error(typ='urn:acme:error:serverInternal',
|
||||
detail='Unable to communicate with DNS server')
|
||||
self.challb = ChallengeBody(
|
||||
uri='http://challb', chall=self.chall, status=self.status,
|
||||
|
||||
@@ -133,7 +133,6 @@ class HTTP01RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||
self.log_message("Serving HTTP01 with token %r",
|
||||
resource.chall.encode("token"))
|
||||
self.send_response(http_client.OK)
|
||||
self.send_header("Content-type", resource.chall.CONTENT_TYPE)
|
||||
self.end_headers()
|
||||
self.wfile.write(resource.validation.encode())
|
||||
return
|
||||
|
||||
@@ -32,11 +32,10 @@ class TLSSNI01ServerTest(unittest.TestCase):
|
||||
"""Test for acme.standalone.TLSSNI01Server."""
|
||||
|
||||
def setUp(self):
|
||||
self.certs = {
|
||||
b'localhost': (test_util.load_pyopenssl_private_key('rsa512_key.pem'),
|
||||
# pylint: disable=protected-access
|
||||
test_util.load_cert('cert.pem')._wrapped),
|
||||
}
|
||||
self.certs = {b'localhost': (
|
||||
test_util.load_pyopenssl_private_key('rsa512_key.pem'),
|
||||
test_util.load_cert('cert.pem'),
|
||||
)}
|
||||
from acme.standalone import TLSSNI01Server
|
||||
self.server = TLSSNI01Server(("", 0), certs=self.certs)
|
||||
# pylint: disable=no-member
|
||||
@@ -49,7 +48,8 @@ class TLSSNI01ServerTest(unittest.TestCase):
|
||||
|
||||
def test_it(self):
|
||||
host, port = self.server.socket.getsockname()[:2]
|
||||
cert = crypto_util.probe_sni(b'localhost', host=host, port=port, timeout=1)
|
||||
cert = crypto_util.probe_sni(
|
||||
b'localhost', host=host, port=port, timeout=1)
|
||||
self.assertEqual(jose.ComparableX509(cert),
|
||||
jose.ComparableX509(self.certs[b'localhost'][1]))
|
||||
|
||||
@@ -140,13 +140,14 @@ class TestSimpleTLSSNI01Server(unittest.TestCase):
|
||||
while max_attempts:
|
||||
max_attempts -= 1
|
||||
try:
|
||||
cert = crypto_util.probe_sni(b'localhost', b'0.0.0.0', self.port)
|
||||
cert = crypto_util.probe_sni(
|
||||
b'localhost', b'0.0.0.0', self.port)
|
||||
except errors.Error:
|
||||
self.assertTrue(max_attempts > 0, "Timeout!")
|
||||
time.sleep(1) # wait until thread starts
|
||||
else:
|
||||
self.assertEqual(jose.ComparableX509(cert),
|
||||
test_util.load_cert('cert.pem'))
|
||||
test_util.load_comparable_cert('cert.pem'))
|
||||
break
|
||||
|
||||
|
||||
|
||||
@@ -40,16 +40,24 @@ def load_cert(*names):
|
||||
"""Load certificate."""
|
||||
loader = _guess_loader(
|
||||
names[-1], OpenSSL.crypto.FILETYPE_PEM, OpenSSL.crypto.FILETYPE_ASN1)
|
||||
return jose.ComparableX509(OpenSSL.crypto.load_certificate(
|
||||
loader, load_vector(*names)))
|
||||
return OpenSSL.crypto.load_certificate(loader, load_vector(*names))
|
||||
|
||||
|
||||
def load_comparable_cert(*names):
|
||||
"""Load ComparableX509 cert."""
|
||||
return jose.ComparableX509(load_cert(*names))
|
||||
|
||||
|
||||
def load_csr(*names):
|
||||
"""Load certificate request."""
|
||||
loader = _guess_loader(
|
||||
names[-1], OpenSSL.crypto.FILETYPE_PEM, OpenSSL.crypto.FILETYPE_ASN1)
|
||||
return jose.ComparableX509(OpenSSL.crypto.load_certificate_request(
|
||||
loader, load_vector(*names)))
|
||||
return OpenSSL.crypto.load_certificate_request(loader, load_vector(*names))
|
||||
|
||||
|
||||
def load_comparable_csr(*names):
|
||||
"""Load ComparableX509 certificate request."""
|
||||
return jose.ComparableX509(load_csr(*names))
|
||||
|
||||
|
||||
def load_rsa_private_key(*names):
|
||||
|
||||
44
acme/acme/testdata/cert-100sans.pem
vendored
Normal file
44
acme/acme/testdata/cert-100sans.pem
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIHxDCCB26gAwIBAgIJAOGrG1Un9lHiMA0GCSqGSIb3DQEBCwUAMGQxCzAJBgNV
|
||||
BAgMAkNBMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMScwJQYDVQQLDB5FbGVjdHJv
|
||||
bmljIEZyb250aWVyIEZvdW5kYXRpb24xFDASBgNVBAMMC2V4YW1wbGUuY29tMB4X
|
||||
DTE2MDEwNjE5MDkzN1oXDTE2MDEwNzE5MDkzN1owZDELMAkGA1UECAwCQ0ExFjAU
|
||||
BgNVBAcMDVNhbiBGcmFuY2lzY28xJzAlBgNVBAsMHkVsZWN0cm9uaWMgRnJvbnRp
|
||||
ZXIgRm91bmRhdGlvbjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wXDANBgkqhkiG9w0B
|
||||
AQEFAANLADBIAkEArHVztFHtH92ucFJD/N/HW9AsdRsUuHUBBBDlHwNlRd3fp580
|
||||
rv2+6QWE30cWgdmJS86ObRz6lUTor4R0T+3C5QIDAQABo4IGATCCBf0wCQYDVR0T
|
||||
BAIwADALBgNVHQ8EBAMCBeAwggXhBgNVHREEggXYMIIF1IIMZXhhbXBsZTEuY29t
|
||||
ggxleGFtcGxlMi5jb22CDGV4YW1wbGUzLmNvbYIMZXhhbXBsZTQuY29tggxleGFt
|
||||
cGxlNS5jb22CDGV4YW1wbGU2LmNvbYIMZXhhbXBsZTcuY29tggxleGFtcGxlOC5j
|
||||
b22CDGV4YW1wbGU5LmNvbYINZXhhbXBsZTEwLmNvbYINZXhhbXBsZTExLmNvbYIN
|
||||
ZXhhbXBsZTEyLmNvbYINZXhhbXBsZTEzLmNvbYINZXhhbXBsZTE0LmNvbYINZXhh
|
||||
bXBsZTE1LmNvbYINZXhhbXBsZTE2LmNvbYINZXhhbXBsZTE3LmNvbYINZXhhbXBs
|
||||
ZTE4LmNvbYINZXhhbXBsZTE5LmNvbYINZXhhbXBsZTIwLmNvbYINZXhhbXBsZTIx
|
||||
LmNvbYINZXhhbXBsZTIyLmNvbYINZXhhbXBsZTIzLmNvbYINZXhhbXBsZTI0LmNv
|
||||
bYINZXhhbXBsZTI1LmNvbYINZXhhbXBsZTI2LmNvbYINZXhhbXBsZTI3LmNvbYIN
|
||||
ZXhhbXBsZTI4LmNvbYINZXhhbXBsZTI5LmNvbYINZXhhbXBsZTMwLmNvbYINZXhh
|
||||
bXBsZTMxLmNvbYINZXhhbXBsZTMyLmNvbYINZXhhbXBsZTMzLmNvbYINZXhhbXBs
|
||||
ZTM0LmNvbYINZXhhbXBsZTM1LmNvbYINZXhhbXBsZTM2LmNvbYINZXhhbXBsZTM3
|
||||
LmNvbYINZXhhbXBsZTM4LmNvbYINZXhhbXBsZTM5LmNvbYINZXhhbXBsZTQwLmNv
|
||||
bYINZXhhbXBsZTQxLmNvbYINZXhhbXBsZTQyLmNvbYINZXhhbXBsZTQzLmNvbYIN
|
||||
ZXhhbXBsZTQ0LmNvbYINZXhhbXBsZTQ1LmNvbYINZXhhbXBsZTQ2LmNvbYINZXhh
|
||||
bXBsZTQ3LmNvbYINZXhhbXBsZTQ4LmNvbYINZXhhbXBsZTQ5LmNvbYINZXhhbXBs
|
||||
ZTUwLmNvbYINZXhhbXBsZTUxLmNvbYINZXhhbXBsZTUyLmNvbYINZXhhbXBsZTUz
|
||||
LmNvbYINZXhhbXBsZTU0LmNvbYINZXhhbXBsZTU1LmNvbYINZXhhbXBsZTU2LmNv
|
||||
bYINZXhhbXBsZTU3LmNvbYINZXhhbXBsZTU4LmNvbYINZXhhbXBsZTU5LmNvbYIN
|
||||
ZXhhbXBsZTYwLmNvbYINZXhhbXBsZTYxLmNvbYINZXhhbXBsZTYyLmNvbYINZXhh
|
||||
bXBsZTYzLmNvbYINZXhhbXBsZTY0LmNvbYINZXhhbXBsZTY1LmNvbYINZXhhbXBs
|
||||
ZTY2LmNvbYINZXhhbXBsZTY3LmNvbYINZXhhbXBsZTY4LmNvbYINZXhhbXBsZTY5
|
||||
LmNvbYINZXhhbXBsZTcwLmNvbYINZXhhbXBsZTcxLmNvbYINZXhhbXBsZTcyLmNv
|
||||
bYINZXhhbXBsZTczLmNvbYINZXhhbXBsZTc0LmNvbYINZXhhbXBsZTc1LmNvbYIN
|
||||
ZXhhbXBsZTc2LmNvbYINZXhhbXBsZTc3LmNvbYINZXhhbXBsZTc4LmNvbYINZXhh
|
||||
bXBsZTc5LmNvbYINZXhhbXBsZTgwLmNvbYINZXhhbXBsZTgxLmNvbYINZXhhbXBs
|
||||
ZTgyLmNvbYINZXhhbXBsZTgzLmNvbYINZXhhbXBsZTg0LmNvbYINZXhhbXBsZTg1
|
||||
LmNvbYINZXhhbXBsZTg2LmNvbYINZXhhbXBsZTg3LmNvbYINZXhhbXBsZTg4LmNv
|
||||
bYINZXhhbXBsZTg5LmNvbYINZXhhbXBsZTkwLmNvbYINZXhhbXBsZTkxLmNvbYIN
|
||||
ZXhhbXBsZTkyLmNvbYINZXhhbXBsZTkzLmNvbYINZXhhbXBsZTk0LmNvbYINZXhh
|
||||
bXBsZTk1LmNvbYINZXhhbXBsZTk2LmNvbYINZXhhbXBsZTk3LmNvbYINZXhhbXBs
|
||||
ZTk4LmNvbYINZXhhbXBsZTk5LmNvbYIOZXhhbXBsZTEwMC5jb20wDQYJKoZIhvcN
|
||||
AQELBQADQQBEunJbKUXcyNKTSfA0pKRyWNiKmkoBqYgfZS6eHNrNH/hjFzHtzyDQ
|
||||
XYHHK6kgEWBvHfRXGmqhFvht+b1tQKkG
|
||||
-----END CERTIFICATE-----
|
||||
30
acme/acme/testdata/cert-idnsans.pem
vendored
Normal file
30
acme/acme/testdata/cert-idnsans.pem
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFNjCCBOCgAwIBAgIJAP4rNqqOKifCMA0GCSqGSIb3DQEBCwUAMGQxCzAJBgNV
|
||||
BAgMAkNBMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMScwJQYDVQQLDB5FbGVjdHJv
|
||||
bmljIEZyb250aWVyIEZvdW5kYXRpb24xFDASBgNVBAMMC2V4YW1wbGUuY29tMB4X
|
||||
DTE2MDEwNjIwMDg1OFoXDTE2MDEwNzIwMDg1OFowZDELMAkGA1UECAwCQ0ExFjAU
|
||||
BgNVBAcMDVNhbiBGcmFuY2lzY28xJzAlBgNVBAsMHkVsZWN0cm9uaWMgRnJvbnRp
|
||||
ZXIgRm91bmRhdGlvbjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wXDANBgkqhkiG9w0B
|
||||
AQEFAANLADBIAkEArHVztFHtH92ucFJD/N/HW9AsdRsUuHUBBBDlHwNlRd3fp580
|
||||
rv2+6QWE30cWgdmJS86ObRz6lUTor4R0T+3C5QIDAQABo4IDczCCA28wCQYDVR0T
|
||||
BAIwADALBgNVHQ8EBAMCBeAwggNTBgNVHREEggNKMIIDRoJiz4PPhM+Fz4bPh8+I
|
||||
z4nPis+Lz4zPjc+Oz4/PkM+Rz5LPk8+Uz5XPls+Xz5jPmc+az5vPnM+dz57Pn8+g
|
||||
z6HPos+jz6TPpc+mz6fPqM+pz6rPq8+sz63Prs+vLmludmFsaWSCYs+wz7HPss+z
|
||||
z7TPtc+2z7fPuM+5z7rPu8+8z73Pvs+/2YHZgtmD2YTZhdmG2YfZiNmJ2YrZi9mM
|
||||
2Y3ZjtmP2ZDZkdmS2ZPZlNmV2ZbZl9mY2ZnZmtmb2ZzZnS5pbnZhbGlkgmLZntmf
|
||||
2aDZodmi2aPZpNml2abZp9mo2anZqtmr2azZrdmu2a/ZsNmx2bLZs9m02bXZttm3
|
||||
2bjZudm62bvZvNm92b7Zv9qA2oHagtqD2oTahdqG2ofaiNqJ2oouaW52YWxpZIJi
|
||||
2ovajNqN2o7aj9qQ2pHaktqT2pTaldqW2pfamNqZ2pram9qc2p3antqf2qDaodqi
|
||||
2qPapNql2qbap9qo2qnaqtqr2qzardqu2q/asNqx2rLas9q02rXattq3LmludmFs
|
||||
aWSCYtq42rnautq72rzavdq+2r/bgNuB24Lbg9uE24XbhtuH24jbiduK24vbjNuN
|
||||
247bj9uQ25HbktuT25TblduW25fbmNuZ25rbm9uc253bntuf26Dbodui26PbpC5p
|
||||
bnZhbGlkgnjbpdum26fbqNup26rbq9us263brtuv27Dbsduy27PbtNu127bbt9u4
|
||||
27nbutu74aCg4aCh4aCi4aCj4aCk4aCl4aCm4aCn4aCo4aCp4aCq4aCr4aCs4aCt
|
||||
4aCu4aCv4aCw4aCx4aCy4aCz4aC04aC1LmludmFsaWSCgY/hoLbhoLfhoLjhoLnh
|
||||
oLrhoLvhoLzhoL3hoL7hoL/hoYDhoYHhoYLhoYPhoYThoYXhoYbhoYfhoYjhoYnh
|
||||
oYrhoYvhoYzhoY3hoY7hoY/hoZDhoZHhoZLhoZPhoZThoZXhoZbhoZfhoZjhoZnh
|
||||
oZrhoZvhoZzhoZ3hoZ7hoZ/hoaDhoaHhoaIuaW52YWxpZIJE4aGj4aGk4aGl4aGm
|
||||
4aGn4aGo4aGp4aGq4aGr4aGs4aGt4aGu4aGv4aGw4aGx4aGy4aGz4aG04aG14aG2
|
||||
LmludmFsaWQwDQYJKoZIhvcNAQELBQADQQAzOQL/54yXxln87/YvEQbBm9ik9zoT
|
||||
TxEkvnZ4kmTRhDsUPtRjMXhY2FH7LOtXKnJQ7POUB7AsJ2Z6uq2w623G
|
||||
-----END CERTIFICATE-----
|
||||
41
acme/acme/testdata/csr-100sans.pem
vendored
Normal file
41
acme/acme/testdata/csr-100sans.pem
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIIHNTCCBt8CAQAwZDELMAkGA1UECAwCQ0ExFjAUBgNVBAcMDVNhbiBGcmFuY2lz
|
||||
Y28xJzAlBgNVBAsMHkVsZWN0cm9uaWMgRnJvbnRpZXIgRm91bmRhdGlvbjEUMBIG
|
||||
A1UEAwwLZXhhbXBsZS5jb20wXDANBgkqhkiG9w0BAQEFAANLADBIAkEArHVztFHt
|
||||
H92ucFJD/N/HW9AsdRsUuHUBBBDlHwNlRd3fp580rv2+6QWE30cWgdmJS86ObRz6
|
||||
lUTor4R0T+3C5QIDAQABoIIGFDCCBhAGCSqGSIb3DQEJDjGCBgEwggX9MAkGA1Ud
|
||||
EwQCMAAwCwYDVR0PBAQDAgXgMIIF4QYDVR0RBIIF2DCCBdSCDGV4YW1wbGUxLmNv
|
||||
bYIMZXhhbXBsZTIuY29tggxleGFtcGxlMy5jb22CDGV4YW1wbGU0LmNvbYIMZXhh
|
||||
bXBsZTUuY29tggxleGFtcGxlNi5jb22CDGV4YW1wbGU3LmNvbYIMZXhhbXBsZTgu
|
||||
Y29tggxleGFtcGxlOS5jb22CDWV4YW1wbGUxMC5jb22CDWV4YW1wbGUxMS5jb22C
|
||||
DWV4YW1wbGUxMi5jb22CDWV4YW1wbGUxMy5jb22CDWV4YW1wbGUxNC5jb22CDWV4
|
||||
YW1wbGUxNS5jb22CDWV4YW1wbGUxNi5jb22CDWV4YW1wbGUxNy5jb22CDWV4YW1w
|
||||
bGUxOC5jb22CDWV4YW1wbGUxOS5jb22CDWV4YW1wbGUyMC5jb22CDWV4YW1wbGUy
|
||||
MS5jb22CDWV4YW1wbGUyMi5jb22CDWV4YW1wbGUyMy5jb22CDWV4YW1wbGUyNC5j
|
||||
b22CDWV4YW1wbGUyNS5jb22CDWV4YW1wbGUyNi5jb22CDWV4YW1wbGUyNy5jb22C
|
||||
DWV4YW1wbGUyOC5jb22CDWV4YW1wbGUyOS5jb22CDWV4YW1wbGUzMC5jb22CDWV4
|
||||
YW1wbGUzMS5jb22CDWV4YW1wbGUzMi5jb22CDWV4YW1wbGUzMy5jb22CDWV4YW1w
|
||||
bGUzNC5jb22CDWV4YW1wbGUzNS5jb22CDWV4YW1wbGUzNi5jb22CDWV4YW1wbGUz
|
||||
Ny5jb22CDWV4YW1wbGUzOC5jb22CDWV4YW1wbGUzOS5jb22CDWV4YW1wbGU0MC5j
|
||||
b22CDWV4YW1wbGU0MS5jb22CDWV4YW1wbGU0Mi5jb22CDWV4YW1wbGU0My5jb22C
|
||||
DWV4YW1wbGU0NC5jb22CDWV4YW1wbGU0NS5jb22CDWV4YW1wbGU0Ni5jb22CDWV4
|
||||
YW1wbGU0Ny5jb22CDWV4YW1wbGU0OC5jb22CDWV4YW1wbGU0OS5jb22CDWV4YW1w
|
||||
bGU1MC5jb22CDWV4YW1wbGU1MS5jb22CDWV4YW1wbGU1Mi5jb22CDWV4YW1wbGU1
|
||||
My5jb22CDWV4YW1wbGU1NC5jb22CDWV4YW1wbGU1NS5jb22CDWV4YW1wbGU1Ni5j
|
||||
b22CDWV4YW1wbGU1Ny5jb22CDWV4YW1wbGU1OC5jb22CDWV4YW1wbGU1OS5jb22C
|
||||
DWV4YW1wbGU2MC5jb22CDWV4YW1wbGU2MS5jb22CDWV4YW1wbGU2Mi5jb22CDWV4
|
||||
YW1wbGU2My5jb22CDWV4YW1wbGU2NC5jb22CDWV4YW1wbGU2NS5jb22CDWV4YW1w
|
||||
bGU2Ni5jb22CDWV4YW1wbGU2Ny5jb22CDWV4YW1wbGU2OC5jb22CDWV4YW1wbGU2
|
||||
OS5jb22CDWV4YW1wbGU3MC5jb22CDWV4YW1wbGU3MS5jb22CDWV4YW1wbGU3Mi5j
|
||||
b22CDWV4YW1wbGU3My5jb22CDWV4YW1wbGU3NC5jb22CDWV4YW1wbGU3NS5jb22C
|
||||
DWV4YW1wbGU3Ni5jb22CDWV4YW1wbGU3Ny5jb22CDWV4YW1wbGU3OC5jb22CDWV4
|
||||
YW1wbGU3OS5jb22CDWV4YW1wbGU4MC5jb22CDWV4YW1wbGU4MS5jb22CDWV4YW1w
|
||||
bGU4Mi5jb22CDWV4YW1wbGU4My5jb22CDWV4YW1wbGU4NC5jb22CDWV4YW1wbGU4
|
||||
NS5jb22CDWV4YW1wbGU4Ni5jb22CDWV4YW1wbGU4Ny5jb22CDWV4YW1wbGU4OC5j
|
||||
b22CDWV4YW1wbGU4OS5jb22CDWV4YW1wbGU5MC5jb22CDWV4YW1wbGU5MS5jb22C
|
||||
DWV4YW1wbGU5Mi5jb22CDWV4YW1wbGU5My5jb22CDWV4YW1wbGU5NC5jb22CDWV4
|
||||
YW1wbGU5NS5jb22CDWV4YW1wbGU5Ni5jb22CDWV4YW1wbGU5Ny5jb22CDWV4YW1w
|
||||
bGU5OC5jb22CDWV4YW1wbGU5OS5jb22CDmV4YW1wbGUxMDAuY29tMA0GCSqGSIb3
|
||||
DQEBCwUAA0EAW05UMFavHn2rkzMyUfzsOvWzVNlm43eO2yHu5h5TzDb23gkDnNEo
|
||||
duUAbQ+CLJHYd+MvRCmPQ+3ZnaPy7l/0Hg==
|
||||
-----END CERTIFICATE REQUEST-----
|
||||
27
acme/acme/testdata/csr-idnsans.pem
vendored
Normal file
27
acme/acme/testdata/csr-idnsans.pem
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIIEpzCCBFECAQAwZDELMAkGA1UECAwCQ0ExFjAUBgNVBAcMDVNhbiBGcmFuY2lz
|
||||
Y28xJzAlBgNVBAsMHkVsZWN0cm9uaWMgRnJvbnRpZXIgRm91bmRhdGlvbjEUMBIG
|
||||
A1UEAwwLZXhhbXBsZS5jb20wXDANBgkqhkiG9w0BAQEFAANLADBIAkEArHVztFHt
|
||||
H92ucFJD/N/HW9AsdRsUuHUBBBDlHwNlRd3fp580rv2+6QWE30cWgdmJS86ObRz6
|
||||
lUTor4R0T+3C5QIDAQABoIIDhjCCA4IGCSqGSIb3DQEJDjGCA3MwggNvMAkGA1Ud
|
||||
EwQCMAAwCwYDVR0PBAQDAgXgMIIDUwYDVR0RBIIDSjCCA0aCYs+Dz4TPhc+Gz4fP
|
||||
iM+Jz4rPi8+Mz43Pjs+Pz5DPkc+Sz5PPlM+Vz5bPl8+Yz5nPms+bz5zPnc+ez5/P
|
||||
oM+hz6LPo8+kz6XPps+nz6jPqc+qz6vPrM+tz67Pry5pbnZhbGlkgmLPsM+xz7LP
|
||||
s8+0z7XPts+3z7jPuc+6z7vPvM+9z77Pv9mB2YLZg9mE2YXZhtmH2YjZidmK2YvZ
|
||||
jNmN2Y7Zj9mQ2ZHZktmT2ZTZldmW2ZfZmNmZ2ZrZm9mc2Z0uaW52YWxpZIJi2Z7Z
|
||||
n9mg2aHZotmj2aTZpdmm2afZqNmp2arZq9ms2a3Zrtmv2bDZsdmy2bPZtNm12bbZ
|
||||
t9m42bnZutm72bzZvdm+2b/agNqB2oLag9qE2oXahtqH2ojaidqKLmludmFsaWSC
|
||||
YtqL2ozajdqO2o/akNqR2pLak9qU2pXaltqX2pjamdqa2pvanNqd2p7an9qg2qHa
|
||||
otqj2qTapdqm2qfaqNqp2qraq9qs2q3artqv2rDasdqy2rPatNq12rbaty5pbnZh
|
||||
bGlkgmLauNq52rrau9q82r3avtq/24DbgduC24PbhNuF24bbh9uI24nbituL24zb
|
||||
jduO24/bkNuR25Lbk9uU25XbltuX25jbmdua25vbnNud257bn9ug26Hbotuj26Qu
|
||||
aW52YWxpZIJ426Xbptun26jbqduq26vbrNut267br9uw27Hbstuz27Tbtdu227fb
|
||||
uNu527rbu+GgoOGgoeGgouGgo+GgpOGgpeGgpuGgp+GgqOGgqeGgquGgq+GgrOGg
|
||||
reGgruGgr+GgsOGgseGgsuGgs+GgtOGgtS5pbnZhbGlkgoGP4aC24aC34aC44aC5
|
||||
4aC64aC74aC84aC94aC+4aC/4aGA4aGB4aGC4aGD4aGE4aGF4aGG4aGH4aGI4aGJ
|
||||
4aGK4aGL4aGM4aGN4aGO4aGP4aGQ4aGR4aGS4aGT4aGU4aGV4aGW4aGX4aGY4aGZ
|
||||
4aGa4aGb4aGc4aGd4aGe4aGf4aGg4aGh4aGiLmludmFsaWSCROGho+GhpOGhpeGh
|
||||
puGhp+GhqOGhqeGhquGhq+GhrOGhreGhruGhr+GhsOGhseGhsuGhs+GhtOGhteGh
|
||||
ti5pbnZhbGlkMA0GCSqGSIb3DQEBCwUAA0EAeNkY0M0+kMnjRo6dEUoGE4dX9fEr
|
||||
dfGrpPUBcwG0P5QBdZJWvZxTfRl14yuPYHbGHULXeGqRdkU6HK5pOlzpng==
|
||||
-----END CERTIFICATE REQUEST-----
|
||||
@@ -28,8 +28,7 @@ acme = client.Client(DIRECTORY_URL, key)
|
||||
|
||||
regr = acme.register()
|
||||
logging.info('Auto-accepting TOS: %s', regr.terms_of_service)
|
||||
acme.update_registration(regr.update(
|
||||
body=regr.body.update(agreement=regr.terms_of_service)))
|
||||
acme.agree_to_tos(regr)
|
||||
logging.debug(regr)
|
||||
|
||||
authzr = acme.request_challenges(
|
||||
|
||||
2
acme/setup.cfg
Normal file
2
acme/setup.cfg
Normal file
@@ -0,0 +1,2 @@
|
||||
[bdist_wheel]
|
||||
universal = 1
|
||||
@@ -4,16 +4,17 @@ from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.1.0.dev0'
|
||||
version = '0.4.0.dev0'
|
||||
|
||||
# Please update tox.ini when modifying dependency version requirements
|
||||
install_requires = [
|
||||
# load_pem_private/public_key (>=0.6)
|
||||
# rsa_recover_prime_factors (>=0.8)
|
||||
'cryptography>=0.8',
|
||||
'ndg-httpsclient', # urllib3 InsecurePlatformWarning (#304)
|
||||
'pyasn1', # urllib3 InsecurePlatformWarning (#304)
|
||||
# Connection.set_tlsext_host_name (>=0.13), X509Req.get_extensions (>=0.15)
|
||||
'PyOpenSSL>=0.15',
|
||||
# Connection.set_tlsext_host_name (>=0.13)
|
||||
'PyOpenSSL>=0.13',
|
||||
'pyrfc3339',
|
||||
'pytz',
|
||||
'requests',
|
||||
@@ -23,6 +24,7 @@ install_requires = [
|
||||
]
|
||||
|
||||
# env markers in extras_require cause problems with older pip: #517
|
||||
# Keep in sync with conditional_requirements.py.
|
||||
if sys.version_info < (2, 7):
|
||||
install_requires.extend([
|
||||
# only some distros recognize stdlib argparse as already satisfying
|
||||
@@ -63,6 +65,7 @@ setup(
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.3',
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Security',
|
||||
],
|
||||
|
||||
@@ -2,6 +2,5 @@ This directory contains scripts that install necessary OS-specific
|
||||
prerequisite dependencies (see docs/using.rst).
|
||||
|
||||
General dependencies:
|
||||
- git-core: py26reqs.txt git+https://*
|
||||
- ca-certificates: communication with demo ACMO server at
|
||||
https://www.letsencrypt-demo.org, py26reqs.txt git+https://*
|
||||
https://www.letsencrypt-demo.org
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
# ./bootstrap/dev/_common_venv.sh
|
||||
|
||||
deps="
|
||||
git
|
||||
python2
|
||||
python-virtualenv
|
||||
gcc
|
||||
|
||||
@@ -24,26 +24,70 @@ apt-get update
|
||||
# distro version (#346)
|
||||
|
||||
virtualenv=
|
||||
if apt-cache show virtualenv > /dev/null ; then
|
||||
if apt-cache show virtualenv > /dev/null 2>&1; then
|
||||
virtualenv="virtualenv"
|
||||
fi
|
||||
|
||||
if apt-cache show python-virtualenv > /dev/null ; then
|
||||
if apt-cache show python-virtualenv > /dev/null 2>&1; then
|
||||
virtualenv="$virtualenv python-virtualenv"
|
||||
fi
|
||||
|
||||
augeas_pkg="libaugeas0 augeas-lenses"
|
||||
AUGVERSION=`apt-cache show --no-all-versions libaugeas0 | grep ^Version: | cut -d" " -f2`
|
||||
|
||||
AddBackportRepo() {
|
||||
# ARGS:
|
||||
BACKPORT_NAME="$1"
|
||||
BACKPORT_SOURCELINE="$2"
|
||||
if ! grep -v -e ' *#' /etc/apt/sources.list | grep -q "$BACKPORT_NAME" ; then
|
||||
# This can theoretically error if sources.list.d is empty, but in that case we don't care.
|
||||
if ! grep -v -e ' *#' /etc/apt/sources.list.d/* 2>/dev/null | grep -q "$BACKPORT_NAME"; then
|
||||
/bin/echo -n "Installing augeas from $BACKPORT_NAME in 3 seconds..."
|
||||
sleep 1s
|
||||
/bin/echo -ne "\e[0K\rInstalling augeas from $BACKPORT_NAME in 2 seconds..."
|
||||
sleep 1s
|
||||
/bin/echo -e "\e[0K\rInstalling augeas from $BACKPORT_NAME in 1 second ..."
|
||||
sleep 1s
|
||||
if echo $BACKPORT_NAME | grep -q wheezy ; then
|
||||
/bin/echo '(Backports are only installed if explicitly requested via "apt-get install -t wheezy-backports")'
|
||||
fi
|
||||
|
||||
echo $BACKPORT_SOURCELINE >> /etc/apt/sources.list.d/"$BACKPORT_NAME".list
|
||||
apt-get update
|
||||
fi
|
||||
fi
|
||||
apt-get install -y --no-install-recommends -t "$BACKPORT_NAME" $augeas_pkg
|
||||
augeas_pkg=
|
||||
|
||||
}
|
||||
|
||||
|
||||
if dpkg --compare-versions 1.0 gt "$AUGVERSION" ; then
|
||||
if lsb_release -a | grep -q wheezy ; then
|
||||
AddBackportRepo wheezy-backports "deb http://http.debian.net/debian wheezy-backports main"
|
||||
elif lsb_release -a | grep -q precise ; then
|
||||
# XXX add ARM case
|
||||
AddBackportRepo precise-backports "deb http://archive.ubuntu.com/ubuntu precise-backports main restricted universe multiverse"
|
||||
else
|
||||
echo "No libaugeas0 version is available that's new enough to run the"
|
||||
echo "Let's Encrypt apache plugin..."
|
||||
fi
|
||||
# XXX add a case for ubuntu PPAs
|
||||
fi
|
||||
|
||||
apt-get install -y --no-install-recommends \
|
||||
git \
|
||||
python \
|
||||
python-dev \
|
||||
$virtualenv \
|
||||
gcc \
|
||||
dialog \
|
||||
libaugeas0 \
|
||||
$augeas_pkg \
|
||||
libssl-dev \
|
||||
libffi-dev \
|
||||
ca-certificates \
|
||||
|
||||
|
||||
|
||||
if ! command -v virtualenv > /dev/null ; then
|
||||
echo Failed to install a working \"virtualenv\" command, exiting
|
||||
exit 1
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
PACKAGES="dev-vcs/git
|
||||
PACKAGES="
|
||||
dev-lang/python:2.7
|
||||
dev-python/virtualenv
|
||||
dev-util/dialog
|
||||
|
||||
@@ -2,14 +2,16 @@
|
||||
|
||||
# Tested with:
|
||||
# - Fedora 22, 23 (x64)
|
||||
# - Centos 7 (x64: onD igitalOcean droplet)
|
||||
# - Centos 7 (x64: on DigitalOcean droplet)
|
||||
# - CentOS 7 Minimal install in a Hyper-V VM
|
||||
|
||||
if type yum 2>/dev/null
|
||||
then
|
||||
tool=yum
|
||||
elif type dnf 2>/dev/null
|
||||
if type dnf 2>/dev/null
|
||||
then
|
||||
tool=dnf
|
||||
elif type yum 2>/dev/null
|
||||
then
|
||||
tool=yum
|
||||
|
||||
else
|
||||
echo "Neither yum nor dnf found. Aborting bootstrap!"
|
||||
exit 1
|
||||
@@ -20,28 +22,39 @@ fi
|
||||
if ! $tool install -y \
|
||||
python \
|
||||
python-devel \
|
||||
python-virtualenv
|
||||
python-virtualenv \
|
||||
python-tools \
|
||||
python-pip
|
||||
then
|
||||
if ! $tool install -y \
|
||||
python27 \
|
||||
python27-devel \
|
||||
python27-virtualenv
|
||||
python27-virtualenv \
|
||||
python27-tools \
|
||||
python27-pip
|
||||
then
|
||||
echo "Could not install Python dependencies. Aborting bootstrap!"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# "git-core" seems to be an alias for "git" in CentOS 7 (yum search fails)
|
||||
if ! $tool install -y \
|
||||
git-core \
|
||||
gcc \
|
||||
dialog \
|
||||
augeas-libs \
|
||||
openssl-devel \
|
||||
libffi-devel \
|
||||
redhat-rpm-config \
|
||||
ca-certificates
|
||||
then
|
||||
echo "Could not install additional dependencies. Aborting bootstrap!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
if $tool list installed "httpd" >/dev/null 2>&1; then
|
||||
if ! $tool install -y mod_ssl
|
||||
then
|
||||
echo "Apache found, but mod_ssl could not be installed."
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# SLE12 don't have python-virtualenv
|
||||
|
||||
zypper -nq in -l git-core \
|
||||
zypper -nq in -l \
|
||||
python \
|
||||
python-devel \
|
||||
python-virtualenv \
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
export VENV_ARGS="--python python2"
|
||||
|
||||
./bootstrap/dev/_venv_common.sh \
|
||||
-r py26reqs.txt \
|
||||
-e acme[testing] \
|
||||
-e .[dev,docs,testing] \
|
||||
-e letsencrypt-apache \
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#!/bin/sh -xe
|
||||
|
||||
pkg install -Ay \
|
||||
git \
|
||||
python \
|
||||
py27-virtualenv \
|
||||
augeas \
|
||||
|
||||
@@ -20,7 +20,7 @@ fi
|
||||
pip install -U setuptools
|
||||
pip install -U pip
|
||||
|
||||
pip install -U -r py26reqs.txt letsencrypt letsencrypt-apache # letsencrypt-nginx
|
||||
pip install -U letsencrypt letsencrypt-apache # letsencrypt-nginx
|
||||
|
||||
echo
|
||||
echo "Congratulations, Let's Encrypt has been successfully installed/updated!"
|
||||
|
||||
@@ -170,7 +170,7 @@ Changing your settings
|
||||
|
||||
This will probably look something like
|
||||
|
||||
..code-block: shell
|
||||
.. code-block:: shell
|
||||
|
||||
letsencrypt --cipher-recommendations mozilla-secure
|
||||
letsencrypt --cipher-recommendations mozilla-intermediate
|
||||
@@ -179,14 +179,14 @@ This will probably look something like
|
||||
to track Mozilla's *Secure*, *Intermediate*, or *Old* recommendations,
|
||||
and
|
||||
|
||||
..code-block: shell
|
||||
.. code-block:: shell
|
||||
|
||||
letsencrypt --update-ciphers on
|
||||
|
||||
to enable updating ciphers with each new Let's Encrypt client release,
|
||||
or
|
||||
|
||||
..code-block: shell
|
||||
.. code-block:: shell
|
||||
|
||||
letsencrypt --update-ciphers off
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ once:
|
||||
|
||||
git clone https://github.com/letsencrypt/letsencrypt
|
||||
cd letsencrypt
|
||||
./bootstrap/install-deps.sh
|
||||
./letsencrypt-auto-source/letsencrypt-auto --os-packages-only
|
||||
./bootstrap/dev/venv.sh
|
||||
|
||||
Then in each shell where you're working on the client, do:
|
||||
@@ -65,8 +65,14 @@ Testing
|
||||
|
||||
The following tools are there to help you:
|
||||
|
||||
- ``tox`` starts a full set of tests. Please make sure you run it
|
||||
before submitting a new pull request.
|
||||
- ``tox`` starts a full set of tests. Please note that it includes
|
||||
apacheconftest, which uses the system's Apache install to test config file
|
||||
parsing, so it should only be run on systems that have an
|
||||
experimental, non-production Apache2 install on them. ``tox -e
|
||||
apacheconftest`` can be used to run those specific Apache conf tests.
|
||||
|
||||
- ``tox -e py27``, ``tox -e py26`` etc, run unit tests for specific Python
|
||||
versions.
|
||||
|
||||
- ``tox -e cover`` checks the test coverage only. Calling the
|
||||
``./tox.cover.sh`` script directly (or even ``./tox.cover.sh $pkg1
|
||||
@@ -90,11 +96,32 @@ Integration testing with the boulder CA
|
||||
Generally it is sufficient to open a pull request and let Github and Travis run
|
||||
integration tests for you.
|
||||
|
||||
Mac OS X users: Run `./tests/mac-bootstrap.sh` instead of `boulder-start.sh` to
|
||||
install dependencies, configure the environment, and start boulder.
|
||||
Mac OS X users: Run ``./tests/mac-bootstrap.sh`` instead of
|
||||
``boulder-start.sh`` to install dependencies, configure the
|
||||
environment, and start boulder.
|
||||
|
||||
Otherwise, install `Go`_ 1.5, libtool-ltdl, mariadb-server and
|
||||
rabbitmq-server and then start Boulder_, an ACME CA server::
|
||||
Otherwise, install `Go`_ 1.5, ``libtool-ltdl``, ``mariadb-server`` and
|
||||
``rabbitmq-server`` and then start Boulder_, an ACME CA server.
|
||||
|
||||
If you can't get packages of Go 1.5 for your Linux system,
|
||||
you can execute the following commands to install it:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
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" ~/.profile ; then echo "export GOROOT=/usr/local/go" >> ~/.profile; fi
|
||||
if ! grep -Fxq "export PATH=\\$GOROOT/bin:\\$PATH" ~/.profile ; then echo "export PATH=\\$GOROOT/bin:\\$PATH" >> ~/.profile; fi
|
||||
|
||||
These commands download `Go`_ 1.5.3 to ``/tmp/``, extracts to ``/usr/local``,
|
||||
and then adds the export lines required to execute ``boulder-start.sh`` to
|
||||
``~/.profile`` if they were not previously added
|
||||
|
||||
Make sure you execute the following command after `Go`_ finishes installing::
|
||||
|
||||
if ! grep -Fxq "export GOPATH=\\$HOME/go" ~/.profile ; then echo "export GOPATH=\\$HOME/go" >> ~/.profile; fi
|
||||
|
||||
Afterwards, you'd be able to start Boulder_ using the following command::
|
||||
|
||||
./tests/boulder-start.sh
|
||||
|
||||
@@ -359,75 +386,37 @@ Now run tests inside the Docker image:
|
||||
Notes on OS dependencies
|
||||
========================
|
||||
|
||||
OS level dependencies are managed by scripts in ``bootstrap``. Some notes
|
||||
are provided here mainly for the :ref:`developers <hacking>` reference.
|
||||
OS-level dependencies can be installed like so:
|
||||
|
||||
In general:
|
||||
.. code-block:: shell
|
||||
|
||||
letsencrypt-auto-source/letsencrypt-auto --os-packages-only
|
||||
|
||||
In general...
|
||||
|
||||
* ``sudo`` is required as a suggested way of running privileged process
|
||||
* `Python`_ 2.6/2.7 is required
|
||||
* `Augeas`_ is required for the Python bindings
|
||||
* ``virtualenv`` and ``pip`` are used for managing other python library
|
||||
dependencies
|
||||
|
||||
.. _Python: https://wiki.python.org/moin/BeginnersGuide/Download
|
||||
.. _Augeas: http://augeas.net/
|
||||
.. _Virtualenv: https://virtualenv.pypa.io
|
||||
|
||||
Ubuntu
|
||||
------
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
sudo ./bootstrap/ubuntu.sh
|
||||
|
||||
|
||||
Debian
|
||||
------
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
sudo ./bootstrap/debian.sh
|
||||
|
||||
For squeeze you will need to:
|
||||
|
||||
- Use ``virtualenv --no-site-packages -p python`` instead of ``-p python2``.
|
||||
|
||||
|
||||
.. _`#280`: https://github.com/letsencrypt/letsencrypt/issues/280
|
||||
|
||||
|
||||
Mac OSX
|
||||
-------
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
./bootstrap/mac.sh
|
||||
|
||||
|
||||
Fedora
|
||||
------
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
sudo ./bootstrap/fedora.sh
|
||||
|
||||
|
||||
Centos 7
|
||||
--------
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
sudo ./bootstrap/centos.sh
|
||||
|
||||
|
||||
FreeBSD
|
||||
-------
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
sudo ./bootstrap/freebsd.sh
|
||||
|
||||
Bootstrap script for FreeBSD uses ``pkg`` for package installation,
|
||||
i.e. it does not use ports.
|
||||
Package installation for FreeBSD uses ``pkg``, not ports.
|
||||
|
||||
FreeBSD by default uses ``tcsh``. In order to activate virtualenv (see
|
||||
below), you will need a compatible shell, e.g. ``pkg install bash &&
|
||||
|
||||
299
docs/using.rst
299
docs/using.rst
@@ -28,13 +28,13 @@ Firstly, please `install Git`_ and run the following commands:
|
||||
git clone https://github.com/letsencrypt/letsencrypt
|
||||
cd letsencrypt
|
||||
|
||||
.. warning:: Alternatively you could `download the ZIP archive`_ and
|
||||
extract the snapshot of our repository, but it's strongly
|
||||
recommended to use the above method instead.
|
||||
|
||||
.. _`install Git`: https://git-scm.com/book/en/v2/Getting-Started-Installing-Git
|
||||
.. _`download the ZIP archive`:
|
||||
https://github.com/letsencrypt/letsencrypt/archive/master.zip
|
||||
|
||||
.. note:: On RedHat/CentOS 6 you will need to enable the EPEL_
|
||||
repository before install.
|
||||
|
||||
.. _EPEL: http://fedoraproject.org/wiki/EPEL
|
||||
|
||||
To install and run the client you just need to type:
|
||||
|
||||
@@ -42,10 +42,10 @@ To install and run the client you just need to type:
|
||||
|
||||
./letsencrypt-auto
|
||||
|
||||
.. note:: On RedHat/CentOS 6 you will need to enable the EPEL_
|
||||
repository before install.
|
||||
|
||||
.. _EPEL: http://fedoraproject.org/wiki/EPEL
|
||||
.. hint:: During the beta phase, Let's Encrypt enforces strict rate limits on
|
||||
the number of certificates issued for one domain. It is recommended to
|
||||
initially use the test server via `--test-cert` until you get the desired
|
||||
certificates.
|
||||
|
||||
Throughout the documentation, whenever you see references to
|
||||
``letsencrypt`` script/binary, you can substitute in
|
||||
@@ -61,108 +61,48 @@ or for full help, type:
|
||||
|
||||
./letsencrypt-auto --help all
|
||||
|
||||
Running with Docker
|
||||
-------------------
|
||||
|
||||
Docker_ is an amazingly simple and quick way to obtain a
|
||||
certificate. However, this mode of operation is unable to install
|
||||
certificates or configure your webserver, because our installer
|
||||
plugins cannot reach it from inside the Docker container.
|
||||
|
||||
You should definitely read the :ref:`where-certs` section, in order to
|
||||
know how to manage the certs
|
||||
manually. https://github.com/letsencrypt/letsencrypt/wiki/Ciphersuite-guidance
|
||||
provides some information about recommended ciphersuites. If none of
|
||||
these make much sense to you, you should definitely use the
|
||||
letsencrypt-auto_ method, which enables you to use installer plugins
|
||||
that cover both of those hard topics.
|
||||
|
||||
If you're still not convinced and have decided to use this method,
|
||||
from the server that the domain you're requesting a cert for resolves
|
||||
to, `install Docker`_, then issue the following command:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
sudo docker run -it --rm -p 443:443 -p 80:80 --name letsencrypt \
|
||||
-v "/etc/letsencrypt:/etc/letsencrypt" \
|
||||
-v "/var/lib/letsencrypt:/var/lib/letsencrypt" \
|
||||
quay.io/letsencrypt/letsencrypt:latest auth
|
||||
|
||||
and follow the instructions (note that ``auth`` command is explicitly
|
||||
used - no installer plugins involved). Your new cert will be available
|
||||
in ``/etc/letsencrypt/live`` on the host.
|
||||
|
||||
.. _Docker: https://docker.com
|
||||
.. _`install Docker`: https://docs.docker.com/userguide/
|
||||
|
||||
|
||||
Operating System Packages
|
||||
--------------------------
|
||||
|
||||
**FreeBSD**
|
||||
|
||||
* Port: ``cd /usr/ports/security/py-letsencrypt && make install clean``
|
||||
* Package: ``pkg install py27-letsencrypt``
|
||||
|
||||
**Arch Linux**
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
sudo pacman -S letsencrypt letsencrypt-nginx letsencrypt-apache \
|
||||
letshelp-letsencrypt
|
||||
|
||||
**Other Operating Systems**
|
||||
|
||||
Unfortunately, this is an ongoing effort. If you'd like to package
|
||||
Let's Encrypt client for your distribution of choice please have a
|
||||
look at the :doc:`packaging`.
|
||||
|
||||
|
||||
From source
|
||||
-----------
|
||||
|
||||
Installation from source is only supported for developers and the
|
||||
whole process is described in the :doc:`contributing`.
|
||||
|
||||
.. warning:: Please do **not** use ``python setup.py install`` or
|
||||
``python pip install .``. Please do **not** attempt the
|
||||
installation commands as superuser/root and/or without virtual
|
||||
environment, e.g. ``sudo python setup.py install``, ``sudo pip
|
||||
install``, ``sudo ./venv/bin/...``. These modes of operation might
|
||||
corrupt your operating system and are **not supported** by the
|
||||
Let's Encrypt team!
|
||||
|
||||
|
||||
Comparison of different methods
|
||||
-------------------------------
|
||||
|
||||
Unless you have a very specific requirements, we kindly ask you to use
|
||||
the letsencrypt-auto_ method. It's the fastest, the most thoroughly
|
||||
tested and the most reliable way of getting our software and the free
|
||||
SSL certificates!
|
||||
``letsencrypt-auto`` is the recommended method of running the Let's Encrypt
|
||||
client beta releases on systems that don't have a packaged version. Debian,
|
||||
Arch linux, FreeBSD, and OpenBSD now have native packages, so on those
|
||||
systems you can just install ``letsencrypt`` (and perhaps
|
||||
``letsencrypt-apache``). If you'd like to run the latest copy from Git, or
|
||||
run your own locally modified copy of the client, follow the instructions in
|
||||
the :doc:`contributing`. Some `other methods of installation`_ are discussed
|
||||
below.
|
||||
|
||||
|
||||
Plugins
|
||||
=======
|
||||
|
||||
=========== = = ===============================================================
|
||||
Plugin A I Notes
|
||||
=========== = = ===============================================================
|
||||
apache_ Y Y Automates obtaining and installing a cert with Apache 2.4 on
|
||||
Debian-based distributions with ``libaugeas0`` 1.0+.
|
||||
standalone_ Y N Uses a "standalone" webserver to obtain a cert.
|
||||
webroot_ Y N Obtains a cert using an already running webserver.
|
||||
manual_ Y N Helps you obtain a cert by giving you instructions to perform
|
||||
domain validation yourself.
|
||||
nginx_ Y Y Very experimental and not included in letsencrypt-auto_.
|
||||
=========== = = ===============================================================
|
||||
The Let's Encrypt client supports a number of different "plugins" that can be
|
||||
used to obtain and/or install certificates. Plugins that can obtain a cert
|
||||
are called "authenticators" and can be used with the "certonly" command.
|
||||
Plugins that can install a cert are called "installers". Plugins that do both
|
||||
can be used with the "letsencrypt run" command, which is the default.
|
||||
|
||||
=========== ==== ==== ===============================================================
|
||||
Plugin Auth Inst Notes
|
||||
=========== ==== ==== ===============================================================
|
||||
apache_ Y Y Automates obtaining and installing a cert with Apache 2.4 on
|
||||
Debian-based distributions with ``libaugeas0`` 1.0+.
|
||||
standalone_ Y N Uses a "standalone" webserver to obtain a cert.
|
||||
webroot_ Y N Obtains a cert by writing to the webroot directory of an
|
||||
already running webserver.
|
||||
manual_ Y N Helps you obtain a cert by giving you instructions to perform
|
||||
domain validation yourself.
|
||||
nginx_ Y Y Very experimental and not included in letsencrypt-auto_.
|
||||
=========== ==== ==== ===============================================================
|
||||
|
||||
Future plugins for IMAP servers, SMTP servers, IRC servers, etc, are likely to
|
||||
be installers but not authenticators.
|
||||
|
||||
Apache
|
||||
------
|
||||
|
||||
If you're running Apache 2.4 on a Debian-based OS with version 1.0+ of
|
||||
the ``libaugeas0`` package available, you can use the Apache plugin.
|
||||
This automates both obtaining and installing certs on an Apache
|
||||
This automates both obtaining *and* installing certs on an Apache
|
||||
webserver. To specify this plugin on the command line, simply include
|
||||
``--apache``.
|
||||
|
||||
@@ -184,14 +124,34 @@ Webroot
|
||||
|
||||
If you're running a webserver that you don't want to stop to use
|
||||
standalone, you can use the webroot plugin to obtain a cert by
|
||||
including ``certonly`` and ``-a webroot`` on the command line. In
|
||||
addition, you'll need to specify ``--webroot-path`` with the root
|
||||
including ``certonly`` and ``--webroot`` on the command line. In
|
||||
addition, you'll need to specify ``--webroot-path`` or ``-w`` with the root
|
||||
directory of the files served by your webserver. For example,
|
||||
``--webroot-path /var/www/html`` or
|
||||
``--webroot-path /usr/share/nginx/html`` are two common webroot paths.
|
||||
If multiple domains are specified, they must all use the same path.
|
||||
Additionally, your server must be configured to serve files from
|
||||
hidden directories.
|
||||
|
||||
If you're getting a certificate for many domains at once, each domain will use
|
||||
the most recent ``--webroot-path``. So for instance:
|
||||
|
||||
``letsencrypt certonly --webroot -w /var/www/example/ -d www.example.com -d example.com -w /var/www/eg -d eg.is -d www.eg.is``
|
||||
|
||||
Would obtain a single certificate for all of those names, using the
|
||||
``/var/www/example`` webroot directory for the first two, and
|
||||
``/var/www/eg`` for the second two.
|
||||
|
||||
The webroot plugin works by creating a temporary file for each of your requested
|
||||
domains in ``${webroot-path}/.well-known/acme-challenge``. Then the Let's
|
||||
Encrypt validation server makes HTTP requests to validate that the DNS for each
|
||||
requested domain resolves to the server running letsencrypt. An example request
|
||||
made to your web server would look like:
|
||||
|
||||
::
|
||||
|
||||
66.133.109.36 - - [05/Jan/2016:20:11:24 -0500] "GET /.well-known/acme-challenge/HGr8U1IeTW4kY_Z6UIyaakzOkyQgPr_7ArlLgtZE8SX HTTP/1.1" 200 87 "-" "Mozilla/5.0 (compatible; Let's Encrypt validation server; +https://www.letsencrypt.org)"
|
||||
|
||||
Note that to use the webroot plugin, your server must be configured to serve
|
||||
files from hidden directories.
|
||||
|
||||
|
||||
Manual
|
||||
------
|
||||
@@ -200,7 +160,7 @@ If you'd like to obtain a cert running ``letsencrypt`` on a machine
|
||||
other than your target webserver or perform the steps for domain
|
||||
validation yourself, you can use the manual plugin. While hidden from
|
||||
the UI, you can use the plugin to obtain a cert by specifying
|
||||
``certonly`` and ``-a manual`` on the command line. This requires you
|
||||
``certonly`` and ``--manual`` on the command line. This requires you
|
||||
to copy and paste commands into another terminal session.
|
||||
|
||||
Nginx
|
||||
@@ -229,10 +189,11 @@ Renewal
|
||||
In order to renew certificates simply call the ``letsencrypt`` (or
|
||||
letsencrypt-auto_) again, and use the same values when prompted. You
|
||||
can automate it slightly by passing necessary flags on the CLI (see
|
||||
`--help all`), or even further using the :ref:`config-file`. If you're
|
||||
sure that UI doesn't prompt for any details you can add the command to
|
||||
``crontab`` (make it less than every 90 days to avoid problems, say
|
||||
every month).
|
||||
`--help all`), or even further using the :ref:`config-file`. The
|
||||
``--renew-by-default`` flag may be helpful for automating renewal. If
|
||||
you're sure that UI doesn't prompt for any details you can add the
|
||||
command to ``crontab`` (make it less than every 90 days to avoid
|
||||
problems, say every month).
|
||||
|
||||
Please note that the CA will send notification emails to the address
|
||||
you provide if you do not renew certificates that are about to expire.
|
||||
@@ -279,21 +240,25 @@ The following files are available:
|
||||
``cert.pem``
|
||||
Server certificate only.
|
||||
|
||||
This is what Apache needs for `SSLCertificateFile
|
||||
This is what Apache < 2.4.8 needs for `SSLCertificateFile
|
||||
<https://httpd.apache.org/docs/2.4/mod/mod_ssl.html#sslcertificatefile>`_.
|
||||
|
||||
``chain.pem``
|
||||
All certificates that need to be served by the browser **excluding**
|
||||
server certificate, i.e. root and intermediate certificates only.
|
||||
|
||||
This is what Apache needs for `SSLCertificateChainFile
|
||||
<https://httpd.apache.org/docs/2.4/mod/mod_ssl.html#sslcertificatechainfile>`_.
|
||||
This is what Apache < 2.4.8 needs for `SSLCertificateChainFile
|
||||
<https://httpd.apache.org/docs/2.4/mod/mod_ssl.html#sslcertificatechainfile>`_,
|
||||
and what nginx >= 1.3.7 needs for `ssl_trusted_certificate
|
||||
<http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_trusted_certificate>`_.
|
||||
|
||||
``fullchain.pem``
|
||||
All certificates, **including** server certificate. This is
|
||||
concatenation of ``chain.pem`` and ``cert.pem``.
|
||||
|
||||
This is what nginx needs for `ssl_certificate
|
||||
This is what Apache >= 2.4.8 needs for `SSLCertificateFile
|
||||
<https://httpd.apache.org/docs/2.4/mod/mod_ssl.html#sslcertificatefile>`_,
|
||||
and what nginx needs for `ssl_certificate
|
||||
<http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_certificate>`_.
|
||||
|
||||
|
||||
@@ -342,7 +307,7 @@ get support on our `forums <https://community.letsencrypt.org>`_.
|
||||
If you find a bug in the software, please do report it in our `issue
|
||||
tracker
|
||||
<https://github.com/letsencrypt/letsencrypt/issues>`_. Remember to
|
||||
give us us as much information as possible:
|
||||
give us as much information as possible:
|
||||
|
||||
- copy and paste exact command line used and the output (though mind
|
||||
that the latter might include some personally identifiable
|
||||
@@ -353,6 +318,112 @@ give us us as much information as possible:
|
||||
- your operating system, including specific version
|
||||
- specify which installation_ method you've chosen
|
||||
|
||||
Other methods of installation
|
||||
=============================
|
||||
|
||||
Running with Docker
|
||||
-------------------
|
||||
|
||||
Docker_ is an amazingly simple and quick way to obtain a
|
||||
certificate. However, this mode of operation is unable to install
|
||||
certificates or configure your webserver, because our installer
|
||||
plugins cannot reach it from inside the Docker container.
|
||||
|
||||
You should definitely read the :ref:`where-certs` section, in order to
|
||||
know how to manage the certs
|
||||
manually. https://github.com/letsencrypt/letsencrypt/wiki/Ciphersuite-guidance
|
||||
provides some information about recommended ciphersuites. If none of
|
||||
these make much sense to you, you should definitely use the
|
||||
letsencrypt-auto_ method, which enables you to use installer plugins
|
||||
that cover both of those hard topics.
|
||||
|
||||
If you're still not convinced and have decided to use this method,
|
||||
from the server that the domain you're requesting a cert for resolves
|
||||
to, `install Docker`_, then issue the following command:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
sudo docker run -it --rm -p 443:443 -p 80:80 --name letsencrypt \
|
||||
-v "/etc/letsencrypt:/etc/letsencrypt" \
|
||||
-v "/var/lib/letsencrypt:/var/lib/letsencrypt" \
|
||||
quay.io/letsencrypt/letsencrypt:latest auth
|
||||
|
||||
and follow the instructions (note that ``auth`` command is explicitly
|
||||
used - no installer plugins involved). Your new cert will be available
|
||||
in ``/etc/letsencrypt/live`` on the host.
|
||||
|
||||
.. _Docker: https://docker.com
|
||||
.. _`install Docker`: https://docs.docker.com/userguide/
|
||||
|
||||
|
||||
Operating System Packages
|
||||
--------------------------
|
||||
|
||||
**FreeBSD**
|
||||
|
||||
* Port: ``cd /usr/ports/security/py-letsencrypt && make install clean``
|
||||
* Package: ``pkg install py27-letsencrypt``
|
||||
|
||||
**OpenBSD**
|
||||
|
||||
* Port: ``cd /usr/ports/security/letsencrypt/client && make install clean``
|
||||
* Package: ``pkg_add letsencrypt``
|
||||
|
||||
**Arch Linux**
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
sudo pacman -S letsencrypt letsencrypt-apache
|
||||
|
||||
**Debian**
|
||||
|
||||
If you run Debian Stretch or Debian Sid, you can install letsencrypt packages.
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
sudo apt-get update
|
||||
sudo apt-get install letsencrypt python-letsencrypt-apache
|
||||
|
||||
If you don't want to use the Apache plugin, you can omit the
|
||||
``python-letsencrypt-apache`` package.
|
||||
|
||||
Packages for Debian Jessie are coming in the next few weeks.
|
||||
|
||||
**Other Operating Systems**
|
||||
|
||||
OS packaging is an ongoing effort. If you'd like to package
|
||||
Let's Encrypt client for your distribution of choice please have a
|
||||
look at the :doc:`packaging`.
|
||||
|
||||
|
||||
From source
|
||||
-----------
|
||||
|
||||
Installation from source is only supported for developers and the
|
||||
whole process is described in the :doc:`contributing`.
|
||||
|
||||
.. warning:: Please do **not** use ``python setup.py install`` or
|
||||
``python pip install .``. Please do **not** attempt the
|
||||
installation commands as superuser/root and/or without virtual
|
||||
environment, e.g. ``sudo python setup.py install``, ``sudo pip
|
||||
install``, ``sudo ./venv/bin/...``. These modes of operation might
|
||||
corrupt your operating system and are **not supported** by the
|
||||
Let's Encrypt team!
|
||||
|
||||
|
||||
Comparison of different methods
|
||||
-------------------------------
|
||||
|
||||
Unless you have a very specific requirements, we kindly ask you to use
|
||||
the letsencrypt-auto_ method. It's the fastest, the most thoroughly
|
||||
tested and the most reliable way of getting our software and the free
|
||||
SSL certificates!
|
||||
|
||||
Beyond the methods discussed here, other methods may be possible, such as
|
||||
installing Let's Encrypt directly with pip from PyPI or downloading a ZIP
|
||||
archive from GitHub may be technically possible but are not presently
|
||||
recommended or supported.
|
||||
|
||||
|
||||
.. rubric:: Footnotes
|
||||
|
||||
|
||||
@@ -5,12 +5,13 @@
|
||||
# Use a 4096 bit RSA key instead of 2048
|
||||
rsa-key-size = 4096
|
||||
|
||||
# Always use the staging/testing server
|
||||
server = https://acme-staging.api.letsencrypt.org/directory
|
||||
|
||||
# Uncomment and update to register with the specified e-mail address
|
||||
# email = foo@example.com
|
||||
|
||||
# Uncomment and update to generate certificates for the specified
|
||||
# domains.
|
||||
# domains = example.com, www.example.com
|
||||
|
||||
# Uncomment to use a text interface instead of ncurses
|
||||
# text = True
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# Always use the staging/testing server - avoids rate limiting
|
||||
server = https://acme-staging.api.letsencrypt.org/directory
|
||||
|
||||
# This is an example configuration file for developers
|
||||
config-dir = /tmp/le/conf
|
||||
work-dir = /tmp/le/conf
|
||||
@@ -8,7 +11,6 @@ email = foo@example.com
|
||||
domains = example.com
|
||||
|
||||
text = True
|
||||
agree-dev-preview = True
|
||||
agree-tos = True
|
||||
debug = True
|
||||
# Unfortunately, it's not possible to specify "verbose" multiple times
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
:mod:`letsencrypt_apache.dvsni`
|
||||
-------------------------------
|
||||
|
||||
.. automodule:: letsencrypt_apache.dvsni
|
||||
:members:
|
||||
5
letsencrypt-apache/docs/api/tls_sni_01.rst
Normal file
5
letsencrypt-apache/docs/api/tls_sni_01.rst
Normal file
@@ -0,0 +1,5 @@
|
||||
:mod:`letsencrypt_apache.tls_sni_01`
|
||||
------------------------------------
|
||||
|
||||
.. automodule:: letsencrypt_apache.tls_sni_01
|
||||
:members:
|
||||
@@ -120,7 +120,8 @@ class AugeasConfigurator(common.Plugin):
|
||||
self.reverter.add_to_temp_checkpoint(
|
||||
save_files, self.save_notes)
|
||||
else:
|
||||
self.reverter.add_to_checkpoint(save_files, self.save_notes)
|
||||
self.reverter.add_to_checkpoint(save_files,
|
||||
self.save_notes)
|
||||
except errors.ReverterError as err:
|
||||
raise errors.PluginError(str(err))
|
||||
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
Let's Encrypt includes the very latest Augeas lenses in order to ship bug fixes
|
||||
to Apacche configuration handling bugs as quickly as possible
|
||||
to Apache configuration handling bugs as quickly as possible
|
||||
|
||||
@@ -51,7 +51,7 @@ let sep_osp = Sep.opt_space
|
||||
let sep_eq = del /[ \t]*=[ \t]*/ "="
|
||||
|
||||
let nmtoken = /[a-zA-Z:_][a-zA-Z0-9:_.-]*/
|
||||
let word = /[a-zA-Z][a-zA-Z0-9._-]*/
|
||||
let word = /[a-z][a-z0-9._-]*/i
|
||||
|
||||
let comment = Util.comment
|
||||
let eol = Util.doseol
|
||||
@@ -59,13 +59,18 @@ let empty = Util.empty_dos
|
||||
let indent = Util.indent
|
||||
|
||||
(* borrowed from shellvars.aug *)
|
||||
let char_arg_dir = /[^\\ '"\t\r\n]|\\\\"|\\\\'/
|
||||
let char_arg_dir = /([^\\ '"{\t\r\n]|[^ '"{\t\r\n]+[^\\ \t\r\n])|\\\\"|\\\\'/
|
||||
let char_arg_sec = /[^ '"\t\r\n>]|\\\\"|\\\\'/
|
||||
let char_arg_wl = /([^\\ '"},\t\r\n]|[^ '"},\t\r\n]+[^\\ '"},\t\r\n])/
|
||||
|
||||
let cdot = /\\\\./
|
||||
let cl = /\\\\\n/
|
||||
let dquot =
|
||||
let no_dquot = /[^"\\\r\n]/
|
||||
in /"/ . (no_dquot|cdot|cl)* . /"/
|
||||
let dquot_msg =
|
||||
let no_dquot = /([^ \t"\\\r\n]|[^"\\\r\n]+[^ \t"\\\r\n])/
|
||||
in /"/ . (no_dquot|cdot|cl)*
|
||||
let squot =
|
||||
let no_squot = /[^'\\\r\n]/
|
||||
in /'/ . (no_squot|cdot|cl)* . /'/
|
||||
@@ -76,12 +81,24 @@ let comp = /[<>=]?=/
|
||||
*****************************************************************)
|
||||
|
||||
let arg_dir = [ label "arg" . store (char_arg_dir+|dquot|squot) ]
|
||||
(* message argument starts with " but ends at EOL *)
|
||||
let arg_dir_msg = [ label "arg" . store dquot_msg ]
|
||||
let arg_sec = [ label "arg" . store (char_arg_sec+|comp|dquot|squot) ]
|
||||
let arg_wl = [ label "arg" . store (char_arg_wl+|dquot|squot) ]
|
||||
|
||||
(* comma-separated wordlist as permitted in the SSLRequire directive *)
|
||||
let arg_wordlist =
|
||||
let wl_start = Util.del_str "{" in
|
||||
let wl_end = Util.del_str "}" in
|
||||
let wl_sep = del /[ \t]*,[ \t]*/ ", "
|
||||
in [ label "wordlist" . wl_start . arg_wl . (wl_sep . arg_wl)* . wl_end ]
|
||||
|
||||
let argv (l:lens) = l . (sep_spc . l)*
|
||||
|
||||
let directive = [ indent . label "directive" . store word .
|
||||
(sep_spc . argv arg_dir)? . eol ]
|
||||
let directive =
|
||||
(* arg_dir_msg may be the last or only argument *)
|
||||
let dir_args = (argv (arg_dir|arg_wordlist) . (sep_spc . arg_dir_msg)?) | arg_dir_msg
|
||||
in [ indent . label "directive" . store word . (sep_spc . dir_args)? . eol ]
|
||||
|
||||
let section (body:lens) =
|
||||
(* opt_eol includes empty lines *)
|
||||
@@ -89,11 +106,17 @@ let section (body:lens) =
|
||||
let inner = (sep_spc . argv arg_sec)? . sep_osp .
|
||||
dels ">" . opt_eol . ((body|comment) . (body|empty|comment)*)? .
|
||||
indent . dels "</" in
|
||||
let kword = key word in
|
||||
let dword = del word "a" in
|
||||
[ indent . dels "<" . square kword inner dword . del ">" ">" . eol ]
|
||||
let kword = key (word - /perl/i) in
|
||||
let dword = del (word - /perl/i) "a" in
|
||||
[ indent . dels "<" . square kword inner dword . del />[ \t\n\r]*/ ">\n" ]
|
||||
|
||||
let perl_section = [ indent . label "Perl" . del /<perl>/i "<Perl>"
|
||||
. store /[^<]*/
|
||||
. del /<\/perl>/i "</Perl>" . eol ]
|
||||
|
||||
|
||||
let rec content = section (content|directive)
|
||||
| perl_section
|
||||
|
||||
let lns = (content|directive|comment|empty)*
|
||||
|
||||
@@ -104,6 +127,7 @@ let filter = (incl "/etc/apache2/apache2.conf") .
|
||||
(incl "/etc/apache2/conf-available/*.conf") .
|
||||
(incl "/etc/apache2/mods-available/*") .
|
||||
(incl "/etc/apache2/sites-available/*") .
|
||||
(incl "/etc/apache2/vhosts.d/*.conf") .
|
||||
(incl "/etc/httpd/conf.d/*.conf") .
|
||||
(incl "/etc/httpd/httpd.conf") .
|
||||
(incl "/etc/httpd/conf/httpd.conf") .
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
"""Apache Configuration based off of Augeas Configurator."""
|
||||
# pylint: disable=too-many-lines
|
||||
import filecmp
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import socket
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
import zope.component
|
||||
import zope.interface
|
||||
|
||||
from acme import challenges
|
||||
@@ -22,10 +22,11 @@ from letsencrypt.plugins import common
|
||||
from letsencrypt_apache import augeas_configurator
|
||||
from letsencrypt_apache import constants
|
||||
from letsencrypt_apache import display_ops
|
||||
from letsencrypt_apache import dvsni
|
||||
from letsencrypt_apache import tls_sni_01
|
||||
from letsencrypt_apache import obj
|
||||
from letsencrypt_apache import parser
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -86,21 +87,26 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
|
||||
@classmethod
|
||||
def add_parser_arguments(cls, add):
|
||||
add("ctl", default=constants.CLI_DEFAULTS["ctl"],
|
||||
help="Path to the 'apache2ctl' binary, used for 'configtest', "
|
||||
"retrieving the Apache2 version number, and initialization "
|
||||
"parameters.")
|
||||
add("enmod", default=constants.CLI_DEFAULTS["enmod"],
|
||||
add("enmod", default=constants.os_constant("enmod"),
|
||||
help="Path to the Apache 'a2enmod' binary.")
|
||||
add("dismod", default=constants.CLI_DEFAULTS["dismod"],
|
||||
help="Path to the Apache 'a2enmod' binary.")
|
||||
add("init-script", default=constants.CLI_DEFAULTS["init_script"],
|
||||
help="Path to the Apache init script (used for server "
|
||||
"reload/restart).")
|
||||
add("le-vhost-ext", default=constants.CLI_DEFAULTS["le_vhost_ext"],
|
||||
add("dismod", default=constants.os_constant("dismod"),
|
||||
help="Path to the Apache 'a2dismod' binary.")
|
||||
add("le-vhost-ext", default=constants.os_constant("le_vhost_ext"),
|
||||
help="SSL vhost configuration extension.")
|
||||
add("server-root", default=constants.CLI_DEFAULTS["server_root"],
|
||||
add("server-root", default=constants.os_constant("server_root"),
|
||||
help="Apache server root directory.")
|
||||
add("vhost-root", default=constants.os_constant("vhost_root"),
|
||||
help="Apache server VirtualHost configuration root")
|
||||
add("challenge-location",
|
||||
default=constants.os_constant("challenge_location"),
|
||||
help="Directory path for challenge configuration.")
|
||||
add("handle-modules", default=constants.os_constant("handle_mods"),
|
||||
help="Let installer handle enabling required modules for you." +
|
||||
"(Only Ubuntu/Debian currently)")
|
||||
add("handle-sites", default=constants.os_constant("handle_sites"),
|
||||
help="Let installer handle enabling sites for you." +
|
||||
"(Only Ubuntu/Debian currently)")
|
||||
le_util.add_deprecated_argument(add, "init-script", 1)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Initialize an Apache Configurator.
|
||||
@@ -121,12 +127,14 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
self.parser = None
|
||||
self.version = version
|
||||
self.vhosts = None
|
||||
self._enhance_func = {"redirect": self._enable_redirect}
|
||||
self._enhance_func = {"redirect": self._enable_redirect,
|
||||
"ensure-http-header": self._set_http_header}
|
||||
|
||||
@property
|
||||
def mod_ssl_conf(self):
|
||||
"""Full absolute path to SSL configuration file."""
|
||||
return os.path.join(self.config.config_dir, constants.MOD_SSL_CONF_DEST)
|
||||
return os.path.join(self.config.config_dir,
|
||||
constants.MOD_SSL_CONF_DEST)
|
||||
|
||||
def prepare(self):
|
||||
"""Prepare the authenticator/installer.
|
||||
@@ -138,41 +146,61 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
|
||||
"""
|
||||
# Verify Apache is installed
|
||||
for exe in (self.conf("ctl"), self.conf("enmod"),
|
||||
self.conf("dismod"), self.conf("init-script")):
|
||||
if not le_util.exe_exists(exe):
|
||||
raise errors.NoInstallationError
|
||||
if not le_util.exe_exists(constants.os_constant("restart_cmd")[0]):
|
||||
raise errors.NoInstallationError
|
||||
|
||||
# Make sure configuration is valid
|
||||
self.config_test()
|
||||
|
||||
self.parser = parser.ApacheParser(
|
||||
self.aug, self.conf("server-root"), self.conf("ctl"))
|
||||
# Check for errors in parsing files with Augeas
|
||||
self.check_parsing_errors("httpd.aug")
|
||||
|
||||
# Set Version
|
||||
if self.version is None:
|
||||
self.version = self.get_version()
|
||||
if self.version < (2, 2):
|
||||
if self.version < (2, 4):
|
||||
raise errors.NotSupportedError(
|
||||
"Apache Version %s not supported.", str(self.version))
|
||||
|
||||
if not self._check_aug_version():
|
||||
raise errors.NotSupportedError(
|
||||
"Apache plugin support requires libaugeas0 and augeas-lenses "
|
||||
"version 1.2.0 or higher, please make sure you have you have "
|
||||
"those installed.")
|
||||
|
||||
self.parser = parser.ApacheParser(
|
||||
self.aug, self.conf("server-root"), self.conf("vhost-root"),
|
||||
self.version)
|
||||
# Check for errors in parsing files with Augeas
|
||||
self.check_parsing_errors("httpd.aug")
|
||||
|
||||
# Get all of the available vhosts
|
||||
self.vhosts = self.get_virtual_hosts()
|
||||
|
||||
install_ssl_options_conf(self.mod_ssl_conf)
|
||||
|
||||
def _check_aug_version(self):
|
||||
""" Checks that we have recent enough version of libaugeas.
|
||||
If augeas version is recent enough, it will support case insensitive
|
||||
regexp matching"""
|
||||
|
||||
self.aug.set("/test/path/testing/arg", "aRgUMeNT")
|
||||
try:
|
||||
matches = self.aug.match(
|
||||
"/test//*[self::arg=~regexp('argument', 'i')]")
|
||||
except RuntimeError:
|
||||
self.aug.remove("/test/path")
|
||||
return False
|
||||
self.aug.remove("/test/path")
|
||||
return matches
|
||||
|
||||
def deploy_cert(self, domain, cert_path, key_path,
|
||||
chain_path=None, fullchain_path=None): # pylint: disable=unused-argument
|
||||
chain_path=None, fullchain_path=None):
|
||||
"""Deploys certificate to specified virtual host.
|
||||
|
||||
Currently tries to find the last directives to deploy the cert in
|
||||
the VHost associated with the given domain. If it can't find the
|
||||
directives, it searches the "included" confs. The function verifies that
|
||||
it has located the three directives and finally modifies them to point
|
||||
to the correct destination. After the certificate is installed, the
|
||||
VirtualHost is enabled if it isn't already.
|
||||
directives, it searches the "included" confs. The function verifies
|
||||
that it has located the three directives and finally modifies them
|
||||
to point to the correct destination. After the certificate is
|
||||
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
|
||||
@@ -182,13 +210,16 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
|
||||
"""
|
||||
vhost = self.choose_vhost(domain)
|
||||
self._clean_vhost(vhost)
|
||||
|
||||
# This is done first so that ssl module is enabled and cert_path,
|
||||
# cert_key... can all be parsed appropriately
|
||||
self.prepare_server_https("443")
|
||||
|
||||
path = {"cert_path": self.parser.find_dir("SSLCertificateFile", None, vhost.path),
|
||||
"cert_key": self.parser.find_dir("SSLCertificateKeyFile", None, vhost.path)}
|
||||
path = {"cert_path": self.parser.find_dir("SSLCertificateFile",
|
||||
None, vhost.path),
|
||||
"cert_key": self.parser.find_dir("SSLCertificateKeyFile",
|
||||
None, vhost.path)}
|
||||
|
||||
# Only include if a certificate chain is specified
|
||||
if chain_path is not None:
|
||||
@@ -205,16 +236,28 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
"Unable to find cert and/or key directives")
|
||||
|
||||
logger.info("Deploying Certificate to VirtualHost %s", vhost.filep)
|
||||
logger.debug("Apache version is %s",
|
||||
".".join(str(i) for i in self.version))
|
||||
|
||||
# Assign the final directives; order is maintained in find_dir
|
||||
self.aug.set(path["cert_path"][-1], cert_path)
|
||||
self.aug.set(path["cert_key"][-1], key_path)
|
||||
if chain_path is not None:
|
||||
if not path["chain_path"]:
|
||||
self.parser.add_dir(
|
||||
vhost.path, "SSLCertificateChainFile", chain_path)
|
||||
if self.version < (2, 4, 8) or (chain_path and not fullchain_path):
|
||||
# install SSLCertificateFile, SSLCertificateKeyFile,
|
||||
# and SSLCertificateChainFile directives
|
||||
set_cert_path = cert_path
|
||||
self.aug.set(path["cert_path"][-1], cert_path)
|
||||
self.aug.set(path["cert_key"][-1], key_path)
|
||||
if chain_path is not None:
|
||||
self.parser.add_dir(vhost.path,
|
||||
"SSLCertificateChainFile", chain_path)
|
||||
else:
|
||||
self.aug.set(path["chain_path"][-1], chain_path)
|
||||
raise errors.PluginError("--chain-path is required for your "
|
||||
"version of Apache")
|
||||
else:
|
||||
if not fullchain_path:
|
||||
raise errors.PluginError("Please provide the --fullchain-path\
|
||||
option pointing to your full chain file")
|
||||
set_cert_path = fullchain_path
|
||||
self.aug.set(path["cert_path"][-1], fullchain_path)
|
||||
self.aug.set(path["cert_key"][-1], key_path)
|
||||
|
||||
# Save notes about the transaction that took place
|
||||
self.save_notes += ("Changed vhost at %s with addresses of %s\n"
|
||||
@@ -222,21 +265,27 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
"\tSSLCertificateKeyFile %s\n" %
|
||||
(vhost.filep,
|
||||
", ".join(str(addr) for addr in vhost.addrs),
|
||||
cert_path, key_path))
|
||||
set_cert_path, key_path))
|
||||
if chain_path is not None:
|
||||
self.save_notes += "\tSSLCertificateChainFile %s\n" % chain_path
|
||||
|
||||
# Make sure vhost is enabled
|
||||
if not vhost.enabled:
|
||||
self.enable_site(vhost)
|
||||
# Make sure vhost is enabled if distro with enabled / available
|
||||
if self.conf("handle-sites"):
|
||||
if not vhost.enabled:
|
||||
self.enable_site(vhost)
|
||||
|
||||
def choose_vhost(self, target_name):
|
||||
def choose_vhost(self, target_name, temp=False):
|
||||
"""Chooses a virtual host based on the given domain name.
|
||||
|
||||
If there is no clear virtual host to be selected, the user is prompted
|
||||
with all available choices.
|
||||
|
||||
The returned vhost is guaranteed to have TLS enabled unless temp is
|
||||
True. If temp is True, there is no such guarantee and the result is
|
||||
not cached.
|
||||
|
||||
:param str target_name: domain name
|
||||
:param bool temp: whether the vhost is only used temporarily
|
||||
|
||||
:returns: ssl vhost associated with name
|
||||
:rtype: :class:`~letsencrypt_apache.obj.VirtualHost`
|
||||
@@ -251,15 +300,17 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
# Try to find a reasonable vhost
|
||||
vhost = self._find_best_vhost(target_name)
|
||||
if vhost is not None:
|
||||
if temp:
|
||||
return vhost
|
||||
if not vhost.ssl:
|
||||
vhost = self.make_vhost_ssl(vhost)
|
||||
|
||||
self.assoc[target_name] = vhost
|
||||
return vhost
|
||||
|
||||
return self._choose_vhost_from_list(target_name)
|
||||
return self._choose_vhost_from_list(target_name, temp)
|
||||
|
||||
def _choose_vhost_from_list(self, target_name):
|
||||
def _choose_vhost_from_list(self, target_name, temp=False):
|
||||
# Select a vhost from a list
|
||||
vhost = display_ops.select_vhost(target_name, self.vhosts)
|
||||
if vhost is None:
|
||||
@@ -268,11 +319,13 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
"No vhost was selected. Please specify servernames "
|
||||
"in the Apache config", target_name)
|
||||
raise errors.PluginError("No vhost selected")
|
||||
|
||||
elif temp:
|
||||
return vhost
|
||||
elif not vhost.ssl:
|
||||
addrs = self._get_proposed_addrs(vhost, "443")
|
||||
# TODO: Conflicts is too conservative
|
||||
if not any(vhost.enabled and vhost.conflicts(addrs) for vhost in self.vhosts):
|
||||
if not any(vhost.enabled and vhost.conflicts(addrs) for
|
||||
vhost in self.vhosts):
|
||||
vhost = self.make_vhost_ssl(vhost)
|
||||
else:
|
||||
logger.error(
|
||||
@@ -433,8 +486,17 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
if self.parser.find_dir("SSLEngine", "on", start=path, exclude=False):
|
||||
is_ssl = True
|
||||
|
||||
# "SSLEngine on" might be set outside of <VirtualHost>
|
||||
# Treat vhosts with port 443 as ssl vhosts
|
||||
for addr in addrs:
|
||||
if addr.get_port() == "443":
|
||||
is_ssl = True
|
||||
|
||||
filename = get_file_path(path)
|
||||
is_enabled = self.is_site_enabled(filename)
|
||||
if self.conf("handle-sites"):
|
||||
is_enabled = self.is_site_enabled(filename)
|
||||
else:
|
||||
is_enabled = True
|
||||
|
||||
macro = False
|
||||
if "/macro/" in path.lower():
|
||||
@@ -445,7 +507,6 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
self._add_servernames(vhost)
|
||||
return vhost
|
||||
|
||||
# TODO: make "sites-available" a configurable directory
|
||||
def get_virtual_hosts(self):
|
||||
"""Returns list of virtual hosts found in the Apache configuration.
|
||||
|
||||
@@ -454,15 +515,27 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
:rtype: list
|
||||
|
||||
"""
|
||||
# Search sites-available, httpd.conf for possible virtual hosts
|
||||
paths = self.aug.match(
|
||||
("/files%s/sites-available//*[label()=~regexp('%s')]" %
|
||||
(self.parser.root, parser.case_i("VirtualHost"))))
|
||||
|
||||
# Search base config, and all included paths for VirtualHosts
|
||||
vhs = []
|
||||
vhost_paths = {}
|
||||
for vhost_path in self.parser.parser_paths.keys():
|
||||
paths = self.aug.match(
|
||||
("/files%s//*[label()=~regexp('%s')]" %
|
||||
(vhost_path, parser.case_i("VirtualHost"))))
|
||||
for path in paths:
|
||||
new_vhost = self._create_vhost(path)
|
||||
realpath = os.path.realpath(new_vhost.filep)
|
||||
if realpath not in vhost_paths.keys():
|
||||
vhs.append(new_vhost)
|
||||
vhost_paths[realpath] = new_vhost.filep
|
||||
elif realpath == new_vhost.filep:
|
||||
# Prefer "real" vhost paths instead of symlinked ones
|
||||
# ex: sites-enabled/vh.conf -> sites-available/vh.conf
|
||||
|
||||
for path in paths:
|
||||
vhs.append(self._create_vhost(path))
|
||||
# remove old (most likely) symlinked one
|
||||
vhs = [v for v in vhs if v.filep != vhost_paths[realpath]]
|
||||
vhs.append(new_vhost)
|
||||
vhost_paths[realpath] = realpath
|
||||
|
||||
return vhs
|
||||
|
||||
@@ -516,26 +589,65 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
:param str port: Port to listen on
|
||||
|
||||
"""
|
||||
if "ssl_module" not in self.parser.modules:
|
||||
self.enable_mod("ssl", temp=temp)
|
||||
|
||||
self.prepare_https_modules(temp)
|
||||
# Check for Listen <port>
|
||||
# Note: This could be made to also look for ip:443 combo
|
||||
if not self.parser.find_dir("Listen", port):
|
||||
logger.debug("No Listen %s directive found. Setting the "
|
||||
"Apache Server to Listen on port %s", port, port)
|
||||
|
||||
if port == "443":
|
||||
args = [port]
|
||||
listens = [self.parser.get_arg(x).split()[0] for
|
||||
x in self.parser.find_dir("Listen")]
|
||||
# In case no Listens are set (which really is a broken apache config)
|
||||
if not listens:
|
||||
listens = ["80"]
|
||||
if port in listens:
|
||||
return
|
||||
for listen in listens:
|
||||
# For any listen statement, check if the machine also listens on
|
||||
# Port 443. If not, add such a listen statement.
|
||||
if len(listen.split(":")) == 1:
|
||||
# Its listening to all interfaces
|
||||
if port not in listens:
|
||||
if port == "443":
|
||||
args = [port]
|
||||
else:
|
||||
# Non-standard ports should specify https protocol
|
||||
args = [port, "https"]
|
||||
self.parser.add_dir_to_ifmodssl(
|
||||
parser.get_aug_path(
|
||||
self.parser.loc["listen"]), "Listen", args)
|
||||
self.save_notes += "Added Listen %s directive to %s\n" % (
|
||||
port, self.parser.loc["listen"])
|
||||
listens.append(port)
|
||||
else:
|
||||
# Non-standard ports should specify https protocol
|
||||
args = [port, "https"]
|
||||
# The Listen statement specifies an ip
|
||||
_, ip = listen[::-1].split(":", 1)
|
||||
ip = ip[::-1]
|
||||
if "%s:%s" % (ip, port) not in listens:
|
||||
if port == "443":
|
||||
args = ["%s:%s" % (ip, port)]
|
||||
else:
|
||||
# Non-standard ports should specify https protocol
|
||||
args = ["%s:%s" % (ip, port), "https"]
|
||||
self.parser.add_dir_to_ifmodssl(
|
||||
parser.get_aug_path(
|
||||
self.parser.loc["listen"]), "Listen", args)
|
||||
self.save_notes += ("Added Listen %s:%s directive to "
|
||||
"%s\n") % (ip, port,
|
||||
self.parser.loc["listen"])
|
||||
listens.append("%s:%s" % (ip, port))
|
||||
|
||||
self.parser.add_dir_to_ifmodssl(
|
||||
parser.get_aug_path(
|
||||
self.parser.loc["listen"]), "Listen", args)
|
||||
self.save_notes += "Added Listen %s directive to %s\n" % (
|
||||
port, self.parser.loc["listen"])
|
||||
def prepare_https_modules(self, temp):
|
||||
"""Helper method for prepare_server_https, taking care of enabling
|
||||
needed modules
|
||||
|
||||
:param boolean temp: If the change is temporary
|
||||
"""
|
||||
|
||||
if self.conf("handle-modules"):
|
||||
if "ssl_module" not in self.parser.modules:
|
||||
self.enable_mod("ssl", temp=temp)
|
||||
if self.version >= (2, 4) and ("socache_shmcb_module" not in
|
||||
self.parser.modules):
|
||||
self.enable_mod("socache_shmcb", temp=temp)
|
||||
|
||||
def make_addrs_sni_ready(self, addrs):
|
||||
"""Checks to see if the server is ready for SNI challenges.
|
||||
@@ -559,7 +671,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
|
||||
Duplicates vhost and adds default ssl options
|
||||
New vhost will reside as (nonssl_vhost.path) +
|
||||
``letsencrypt_apache.constants.CLI_DEFAULTS["le_vhost_ext"]``
|
||||
``letsencrypt_apache.constants.os_constant("le_vhost_ext")``
|
||||
|
||||
.. note:: This function saves the configuration
|
||||
|
||||
@@ -586,7 +698,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
(ssl_fp, parser.case_i("VirtualHost")))
|
||||
if len(vh_p) != 1:
|
||||
logger.error("Error: should only be one vhost in %s", avail_fp)
|
||||
raise errors.PluginError("Only one vhost per file is allowed")
|
||||
raise errors.PluginError("Currently, we only support "
|
||||
"configurations with one vhost per file")
|
||||
else:
|
||||
# This simplifies the process
|
||||
vh_p = vh_p[0]
|
||||
@@ -625,6 +738,39 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
else:
|
||||
return non_ssl_vh_fp + self.conf("le_vhost_ext")
|
||||
|
||||
def _sift_line(self, line):
|
||||
"""Decides whether a line should be copied to a SSL vhost.
|
||||
|
||||
A canonical example of when sifting a line is required:
|
||||
When the http vhost contains a RewriteRule that unconditionally
|
||||
redirects any request to the https version of the same site.
|
||||
e.g:
|
||||
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [L,QSA,R=permanent]
|
||||
Copying the above line to the ssl vhost would cause a
|
||||
redirection loop.
|
||||
|
||||
:param str line: a line extracted from the http vhost.
|
||||
|
||||
:returns: True - don't copy line from http vhost to SSL vhost.
|
||||
:rtype: bool
|
||||
|
||||
"""
|
||||
if not line.lstrip().startswith("RewriteRule"):
|
||||
return False
|
||||
|
||||
# According to: http://httpd.apache.org/docs/2.4/rewrite/flags.html
|
||||
# The syntax of a RewriteRule is:
|
||||
# RewriteRule pattern target [Flag1,Flag2,Flag3]
|
||||
# i.e. target is required, so it must exist.
|
||||
target = line.split()[2].strip()
|
||||
|
||||
# target may be surrounded with quotes
|
||||
if target[0] in ("'", '"') and target[0] == target[-1]:
|
||||
target = target[1:-1]
|
||||
|
||||
# Sift line if it redirects the request to a HTTPS site
|
||||
return target.startswith("https://")
|
||||
|
||||
def _copy_create_ssl_vhost_skeleton(self, avail_fp, ssl_fp):
|
||||
"""Copies over existing Vhost with IfModule mod_ssl.c> skeleton.
|
||||
|
||||
@@ -637,18 +783,38 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
# First register the creation so that it is properly removed if
|
||||
# configuration is rolled back
|
||||
self.reverter.register_file_creation(False, ssl_fp)
|
||||
sift = False
|
||||
|
||||
try:
|
||||
with open(avail_fp, "r") as orig_file:
|
||||
with open(ssl_fp, "w") as new_file:
|
||||
new_file.write("<IfModule mod_ssl.c>\n")
|
||||
for line in orig_file:
|
||||
new_file.write(line)
|
||||
if self._sift_line(line):
|
||||
if not sift:
|
||||
new_file.write(
|
||||
"# Some rewrite rules in this file were "
|
||||
"were disabled on your HTTPS site,\n"
|
||||
"# because they have the potential to "
|
||||
"create redirection loops.\n")
|
||||
sift = True
|
||||
new_file.write("# " + line)
|
||||
else:
|
||||
new_file.write(line)
|
||||
new_file.write("</IfModule>\n")
|
||||
except IOError:
|
||||
logger.fatal("Error writing/reading to file in make_vhost_ssl")
|
||||
raise errors.PluginError("Unable to write/read in make_vhost_ssl")
|
||||
|
||||
if sift:
|
||||
reporter = zope.component.getUtility(interfaces.IReporter)
|
||||
reporter.add_message(
|
||||
"Some rewrite rules copied from {0} were disabled in the "
|
||||
"vhost for your HTTPS site located at {1} because they have "
|
||||
"the potential to create redirection loops.".format(avail_fp,
|
||||
ssl_fp),
|
||||
reporter.MEDIUM_PRIORITY)
|
||||
|
||||
def _update_ssl_vhosts_addrs(self, vh_path):
|
||||
ssl_addrs = set()
|
||||
ssl_addr_p = self.aug.match(vh_path + "/arg")
|
||||
@@ -662,6 +828,30 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
|
||||
return ssl_addrs
|
||||
|
||||
def _clean_vhost(self, vhost):
|
||||
# remove duplicated or conflicting ssl directives
|
||||
self._deduplicate_directives(vhost.path,
|
||||
["SSLCertificateFile",
|
||||
"SSLCertificateKeyFile"])
|
||||
# remove all problematic directives
|
||||
self._remove_directives(vhost.path, ["SSLCertificateChainFile"])
|
||||
|
||||
def _deduplicate_directives(self, vh_path, directives):
|
||||
for directive in directives:
|
||||
while len(self.parser.find_dir(directive, None,
|
||||
vh_path, False)) > 1:
|
||||
directive_path = self.parser.find_dir(directive, None,
|
||||
vh_path, False)
|
||||
self.aug.remove(re.sub(r"/\w*$", "", directive_path[0]))
|
||||
|
||||
def _remove_directives(self, vh_path, directives):
|
||||
for directive in directives:
|
||||
while len(self.parser.find_dir(directive, None,
|
||||
vh_path, False)) > 0:
|
||||
directive_path = self.parser.find_dir(directive, None,
|
||||
vh_path, False)
|
||||
self.aug.remove(re.sub(r"/\w*$", "", directive_path[0]))
|
||||
|
||||
def _add_dummy_ssl_directives(self, vh_path):
|
||||
self.parser.add_dir(vh_path, "SSLCertificateFile",
|
||||
"insert_cert_file_path")
|
||||
@@ -686,7 +876,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
for addr in vhost.addrs:
|
||||
for test_vh in self.vhosts:
|
||||
if (vhost.filep != test_vh.filep and
|
||||
any(test_addr == addr for test_addr in test_vh.addrs) and
|
||||
any(test_addr == addr for
|
||||
test_addr in test_vh.addrs) and
|
||||
not self.is_name_vhost(addr)):
|
||||
self.add_name_vhost(addr)
|
||||
logger.info("Enabling NameVirtualHosts on %s", addr)
|
||||
@@ -695,12 +886,12 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
if need_to_save:
|
||||
self.save()
|
||||
|
||||
############################################################################
|
||||
######################################################################
|
||||
# Enhancements
|
||||
############################################################################
|
||||
######################################################################
|
||||
def supported_enhancements(self): # pylint: disable=no-self-use
|
||||
"""Returns currently supported enhancements."""
|
||||
return ["redirect"]
|
||||
return ["redirect", "ensure-http-header"]
|
||||
|
||||
def enhance(self, domain, enhancement, options=None):
|
||||
"""Enhance configuration.
|
||||
@@ -727,6 +918,74 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
logger.warn("Failed %s for %s", enhancement, domain)
|
||||
raise
|
||||
|
||||
def _set_http_header(self, ssl_vhost, header_substring):
|
||||
"""Enables header that is identified by header_substring on ssl_vhost.
|
||||
|
||||
If the header identified by header_substring is not already set,
|
||||
a new Header directive is placed in ssl_vhost's configuration with
|
||||
arguments from: constants.HTTP_HEADER[header_substring]
|
||||
|
||||
.. note:: This function saves the configuration
|
||||
|
||||
:param ssl_vhost: Destination of traffic, an ssl enabled vhost
|
||||
:type ssl_vhost: :class:`~letsencrypt_apache.obj.VirtualHost`
|
||||
|
||||
:param 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`)
|
||||
|
||||
:raises .errors.PluginError: If no viable HTTP host can be created or
|
||||
set with header header_substring.
|
||||
|
||||
"""
|
||||
if "headers_module" not in self.parser.modules:
|
||||
self.enable_mod("headers")
|
||||
|
||||
# Check if selected header is already set
|
||||
self._verify_no_matching_http_header(ssl_vhost, header_substring)
|
||||
|
||||
# Add directives to server
|
||||
self.parser.add_dir(ssl_vhost.path, "Header",
|
||||
constants.HEADER_ARGS[header_substring])
|
||||
|
||||
self.save_notes += ("Adding %s header to ssl vhost in %s\n" %
|
||||
(header_substring, ssl_vhost.filep))
|
||||
|
||||
self.save()
|
||||
logger.info("Adding %s header to ssl vhost in %s", header_substring,
|
||||
ssl_vhost.filep)
|
||||
|
||||
def _verify_no_matching_http_header(self, ssl_vhost, header_substring):
|
||||
"""Checks to see if an there is an existing Header directive that
|
||||
contains the string header_substring.
|
||||
|
||||
:param ssl_vhost: vhost to check
|
||||
:type vhost: :class:`~letsencrypt_apache.obj.VirtualHost`
|
||||
|
||||
:param header_substring: string that uniquely identifies a header.
|
||||
e.g: Strict-Transport-Security, Upgrade-Insecure-Requests.
|
||||
:type str
|
||||
|
||||
:returns: boolean
|
||||
:rtype: (bool)
|
||||
|
||||
:raises errors.PluginEnhancementAlreadyPresent When header
|
||||
header_substring exists
|
||||
|
||||
"""
|
||||
header_path = self.parser.find_dir("Header", None,
|
||||
start=ssl_vhost.path)
|
||||
if header_path:
|
||||
# "Existing Header directive for virtualhost"
|
||||
pat = '(?:[ "]|^)(%s)(?:[ "]|$)' % (header_substring.lower())
|
||||
for match in header_path:
|
||||
if re.search(pat, self.aug.get(match).lower()):
|
||||
raise errors.PluginEnhancementAlreadyPresent(
|
||||
"Existing %s header" % (header_substring))
|
||||
|
||||
def _enable_redirect(self, ssl_vhost, unused_options):
|
||||
"""Redirect all equivalent HTTP traffic to ssl_vhost.
|
||||
|
||||
@@ -771,15 +1030,32 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
"redirection")
|
||||
self._create_redirect_vhost(ssl_vhost)
|
||||
else:
|
||||
# Check if redirection already exists
|
||||
self._verify_no_redirects(general_vh)
|
||||
# Check if LetsEncrypt redirection already exists
|
||||
self._verify_no_letsencrypt_redirect(general_vh)
|
||||
|
||||
# Note: if code flow gets here it means we didn't find the exact
|
||||
# letsencrypt 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.
|
||||
if self._is_rewrite_exists(general_vh):
|
||||
logger.warn("Added an HTTP->HTTPS rewrite in addition to "
|
||||
"other RewriteRules; you may wish to check for "
|
||||
"overall consistency.")
|
||||
|
||||
# Add directives to server
|
||||
# Note: These are not immediately searchable in sites-enabled
|
||||
# even with save() and load()
|
||||
self.parser.add_dir(general_vh.path, "RewriteEngine", "on")
|
||||
self.parser.add_dir(general_vh.path, "RewriteRule",
|
||||
constants.REWRITE_HTTPS_ARGS)
|
||||
if not self._is_rewrite_engine_on(general_vh):
|
||||
self.parser.add_dir(general_vh.path, "RewriteEngine", "on")
|
||||
|
||||
if self.get_version() >= (2, 3, 9):
|
||||
self.parser.add_dir(general_vh.path, "RewriteRule",
|
||||
constants.REWRITE_HTTPS_ARGS_WITH_END)
|
||||
else:
|
||||
self.parser.add_dir(general_vh.path, "RewriteRule",
|
||||
constants.REWRITE_HTTPS_ARGS)
|
||||
|
||||
self.save_notes += ("Redirecting host in %s to ssl vhost in %s\n" %
|
||||
(general_vh.filep, ssl_vhost.filep))
|
||||
self.save()
|
||||
@@ -787,35 +1063,67 @@ 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_redirects(self, vhost):
|
||||
"""Checks to see if existing redirect is in place.
|
||||
def _verify_no_letsencrypt_redirect(self, vhost):
|
||||
"""Checks to see if a redirect was already installed by letsencrypt.
|
||||
|
||||
Checks to see if virtualhost already contains a rewrite or redirect
|
||||
returns boolean, integer
|
||||
Checks to see if virtualhost already contains a rewrite rule that is
|
||||
identical to Letsencrypt's redirection rewrite rule.
|
||||
|
||||
:param vhost: vhost to check
|
||||
:type vhost: :class:`~letsencrypt_apache.obj.VirtualHost`
|
||||
|
||||
:raises errors.PluginError: When another redirection exists
|
||||
:raises errors.PluginEnhancementAlreadyPresent: When the exact
|
||||
letsencrypt redirection WriteRule exists in virtual host.
|
||||
"""
|
||||
rewrite_path = self.parser.find_dir(
|
||||
"RewriteRule", None, start=vhost.path)
|
||||
|
||||
# There can be other RewriteRule directive lines in vhost config.
|
||||
# rewrite_args_dict keys are directive ids and the corresponding value
|
||||
# for each is a list of arguments to that directive.
|
||||
rewrite_args_dict = defaultdict(list)
|
||||
pat = r'.*(directive\[\d+\]).*'
|
||||
for match in rewrite_path:
|
||||
m = re.match(pat, match)
|
||||
if m:
|
||||
dir_id = m.group(1)
|
||||
rewrite_args_dict[dir_id].append(match)
|
||||
|
||||
if rewrite_args_dict:
|
||||
redirect_args = [constants.REWRITE_HTTPS_ARGS,
|
||||
constants.REWRITE_HTTPS_ARGS_WITH_END]
|
||||
|
||||
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")
|
||||
|
||||
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`
|
||||
|
||||
:returns: True if a RewriteRule directive exists.
|
||||
:rtype: bool
|
||||
|
||||
"""
|
||||
rewrite_path = self.parser.find_dir(
|
||||
"RewriteRule", None, start=vhost.path)
|
||||
redirect_path = self.parser.find_dir("Redirect", None, start=vhost.path)
|
||||
return bool(rewrite_path)
|
||||
|
||||
if redirect_path:
|
||||
# "Existing Redirect directive for virtualhost"
|
||||
raise errors.PluginError("Existing Redirect present on HTTP vhost.")
|
||||
if rewrite_path:
|
||||
# "No existing redirection for virtualhost"
|
||||
if len(rewrite_path) != len(constants.REWRITE_HTTPS_ARGS):
|
||||
raise errors.PluginError("Unknown Existing RewriteRule")
|
||||
for match, arg in itertools.izip(
|
||||
rewrite_path, constants.REWRITE_HTTPS_ARGS):
|
||||
if self.aug.get(match) != arg:
|
||||
raise errors.PluginError("Unknown Existing RewriteRule")
|
||||
raise errors.PluginError(
|
||||
"Let's Encrypt has already enabled redirection")
|
||||
def _is_rewrite_engine_on(self, vhost):
|
||||
"""Checks if a RewriteEngine directive is on
|
||||
|
||||
:param vhost: vhost to check
|
||||
:type vhost: :class:`~letsencrypt_apache.obj.VirtualHost`
|
||||
|
||||
"""
|
||||
rewrite_engine_path = self.parser.find_dir("RewriteEngine", "on",
|
||||
start=vhost.path)
|
||||
if rewrite_engine_path:
|
||||
return self.parser.get_arg(rewrite_engine_path[0])
|
||||
return False
|
||||
|
||||
def _create_redirect_vhost(self, ssl_vhost):
|
||||
"""Creates an http_vhost specifically to redirect for the ssl_vhost.
|
||||
@@ -852,6 +1160,12 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
if ssl_vhost.aliases:
|
||||
serveralias = "ServerAlias " + " ".join(ssl_vhost.aliases)
|
||||
|
||||
rewrite_rule_args = []
|
||||
if self.get_version() >= (2, 3, 9):
|
||||
rewrite_rule_args = constants.REWRITE_HTTPS_ARGS_WITH_END
|
||||
else:
|
||||
rewrite_rule_args = constants.REWRITE_HTTPS_ARGS
|
||||
|
||||
return ("<VirtualHost %s>\n"
|
||||
"%s \n"
|
||||
"%s \n"
|
||||
@@ -863,9 +1177,10 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
"ErrorLog /var/log/apache2/redirect.error.log\n"
|
||||
"LogLevel warn\n"
|
||||
"</VirtualHost>\n"
|
||||
% (" ".join(str(addr) for addr in self._get_proposed_addrs(ssl_vhost)),
|
||||
% (" ".join(str(addr) for
|
||||
addr in self._get_proposed_addrs(ssl_vhost)),
|
||||
servername, serveralias,
|
||||
" ".join(constants.REWRITE_HTTPS_ARGS)))
|
||||
" ".join(rewrite_rule_args)))
|
||||
|
||||
def _write_out_redirect(self, ssl_vhost, text):
|
||||
# This is the default name
|
||||
@@ -877,8 +1192,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
if len(ssl_vhost.name) < (255 - (len(redirect_filename) + 1)):
|
||||
redirect_filename = "le-redirect-%s.conf" % ssl_vhost.name
|
||||
|
||||
redirect_filepath = os.path.join(
|
||||
self.parser.root, "sites-available", redirect_filename)
|
||||
redirect_filepath = os.path.join(self.conf("vhost-root"),
|
||||
redirect_filename)
|
||||
|
||||
# Register the new file that will be created
|
||||
# Note: always register the creation before writing to ensure file will
|
||||
@@ -906,7 +1221,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
|
||||
return None
|
||||
|
||||
def _get_proposed_addrs(self, vhost, port="80"): # pylint: disable=no-self-use
|
||||
def _get_proposed_addrs(self, vhost, port="80"):
|
||||
"""Return all addrs of vhost with the port replaced with the specified.
|
||||
|
||||
:param obj.VirtualHost ssl_vhost: Original Vhost
|
||||
@@ -964,7 +1279,14 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
:rtype: bool
|
||||
|
||||
"""
|
||||
|
||||
enabled_dir = os.path.join(self.parser.root, "sites-enabled")
|
||||
if not os.path.isdir(enabled_dir):
|
||||
error_msg = ("Directory '{0}' does not exist. Please ensure "
|
||||
"that the values for --apache-handle-sites and "
|
||||
"--apache-server-root are correct for your "
|
||||
"environment.".format(enabled_dir))
|
||||
raise errors.ConfigurationError(error_msg)
|
||||
for entry in os.listdir(enabled_dir):
|
||||
try:
|
||||
if filecmp.cmp(avail_fp, os.path.join(enabled_dir, entry)):
|
||||
@@ -974,12 +1296,13 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
return False
|
||||
|
||||
def enable_site(self, vhost):
|
||||
"""Enables an available site, Apache restart required.
|
||||
"""Enables an available site, Apache reload required.
|
||||
|
||||
.. note:: Does not make sure that the site correctly works or that all
|
||||
modules are enabled appropriately.
|
||||
|
||||
.. todo:: This function should number subdomains before the domain vhost
|
||||
.. todo:: This function should number subdomains before the domain
|
||||
vhost
|
||||
|
||||
.. todo:: Make sure link is not broken...
|
||||
|
||||
@@ -1009,7 +1332,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
def enable_mod(self, mod_name, temp=False):
|
||||
"""Enables module in Apache.
|
||||
|
||||
Both enables and restarts Apache so module is active.
|
||||
Both enables and reloads Apache so module is active.
|
||||
|
||||
:param str mod_name: Name of the module to enable. (e.g. 'ssl')
|
||||
:param bool temp: Whether or not this is a temporary action.
|
||||
@@ -1051,8 +1374,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
|
||||
# Modules can enable additional config files. Variables may be defined
|
||||
# within these new configuration sections.
|
||||
# Restart is not necessary as DUMP_RUN_CFG uses latest config.
|
||||
self.parser.update_runtime_variables(self.conf("ctl"))
|
||||
# Reload is not necessary as DUMP_RUN_CFG uses latest config.
|
||||
self.parser.update_runtime_variables()
|
||||
|
||||
def _add_parser_mod(self, mod_name):
|
||||
"""Shortcut for updating parser modules."""
|
||||
@@ -1074,16 +1397,26 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
le_util.run_script([self.conf("enmod"), mod_name])
|
||||
|
||||
def restart(self):
|
||||
"""Restarts apache server.
|
||||
"""Runs a config test and reloads the Apache server.
|
||||
|
||||
.. todo:: This function will be converted to using reload
|
||||
|
||||
:raises .errors.MisconfigurationError: If unable to restart due
|
||||
to a configuration problem, or if the restart subprocess
|
||||
cannot be run.
|
||||
:raises .errors.MisconfigurationError: If either the config test
|
||||
or reload fails.
|
||||
|
||||
"""
|
||||
return apache_restart(self.conf("init-script"))
|
||||
self.config_test()
|
||||
logger.debug(self.reverter.view_config_changes(for_logging=True))
|
||||
self._reload()
|
||||
|
||||
def _reload(self):
|
||||
"""Reloads the Apache server.
|
||||
|
||||
:raises .errors.MisconfigurationError: If reload fails
|
||||
|
||||
"""
|
||||
try:
|
||||
le_util.run_script(constants.os_constant("restart_cmd"))
|
||||
except errors.SubprocessError as err:
|
||||
raise errors.MisconfigurationError(str(err))
|
||||
|
||||
def config_test(self): # pylint: disable=no-self-use
|
||||
"""Check the configuration of Apache for errors.
|
||||
@@ -1092,7 +1425,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
|
||||
"""
|
||||
try:
|
||||
le_util.run_script([self.conf("ctl"), "configtest"])
|
||||
le_util.run_script(constants.os_constant("conftest_cmd"))
|
||||
except errors.SubprocessError as err:
|
||||
raise errors.MisconfigurationError(str(err))
|
||||
|
||||
@@ -1108,10 +1441,12 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
|
||||
"""
|
||||
try:
|
||||
stdout, _ = le_util.run_script([self.conf("ctl"), "-v"])
|
||||
stdout, _ = le_util.run_script(
|
||||
constants.os_constant("version_cmd"))
|
||||
except errors.SubprocessError:
|
||||
raise errors.PluginError(
|
||||
"Unable to run %s -v" % self.conf("ctl"))
|
||||
"Unable to run %s -v" %
|
||||
constants.os_constant("version_cmd"))
|
||||
|
||||
regex = re.compile(r"Apache/([0-9\.]*)", re.IGNORECASE)
|
||||
matches = regex.findall(stdout)
|
||||
@@ -1148,26 +1483,30 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
||||
"""
|
||||
self._chall_out.update(achalls)
|
||||
responses = [None] * len(achalls)
|
||||
apache_dvsni = dvsni.ApacheDvsni(self)
|
||||
chall_doer = tls_sni_01.ApacheTlsSni01(self)
|
||||
|
||||
for i, achall in enumerate(achalls):
|
||||
# Currently also have dvsni hold associated index
|
||||
# of the challenge. This helps to put all of the responses back
|
||||
# together when they are all complete.
|
||||
apache_dvsni.add_chall(achall, i)
|
||||
# Currently also have chall_doer hold associated index of the
|
||||
# challenge. This helps to put all of the responses back together
|
||||
# when they are all complete.
|
||||
chall_doer.add_chall(achall, i)
|
||||
|
||||
sni_response = apache_dvsni.perform()
|
||||
sni_response = chall_doer.perform()
|
||||
if sni_response:
|
||||
# Must restart in order to activate the challenges.
|
||||
# Must reload in order to activate the challenges.
|
||||
# Handled here because we may be able to load up other challenge
|
||||
# types
|
||||
self.restart()
|
||||
|
||||
# TODO: Remove this dirty hack. We need to determine a reliable way
|
||||
# of identifying when the new configuration is being used.
|
||||
time.sleep(3)
|
||||
|
||||
# Go through all of the challenges and assign them to the proper
|
||||
# place in the responses return value. All responses must be in the
|
||||
# same order as the original challenges.
|
||||
for i, resp in enumerate(sni_response):
|
||||
responses[apache_dvsni.indices[i]] = resp
|
||||
responses[chall_doer.indices[i]] = resp
|
||||
|
||||
return responses
|
||||
|
||||
@@ -1196,49 +1535,11 @@ def _get_mod_deps(mod_name):
|
||||
|
||||
"""
|
||||
deps = {
|
||||
"ssl": ["setenvif", "mime", "socache_shmcb"]
|
||||
"ssl": ["setenvif", "mime"]
|
||||
}
|
||||
return deps.get(mod_name, [])
|
||||
|
||||
|
||||
def apache_restart(apache_init_script):
|
||||
"""Restarts the Apache Server.
|
||||
|
||||
:param str apache_init_script: Path to the Apache init script.
|
||||
|
||||
.. todo:: Try to use reload instead. (This caused timing problems before)
|
||||
|
||||
.. todo:: On failure, this should be a recovery_routine call with another
|
||||
restart. This will confuse and inhibit developers from testing code
|
||||
though. This change should happen after
|
||||
the ApacheConfigurator has been thoroughly tested. The function will
|
||||
need to be moved into the class again. Perhaps
|
||||
this version can live on... for testing purposes.
|
||||
|
||||
:raises .errors.MisconfigurationError: If unable to restart due to a
|
||||
configuration problem, or if the restart subprocess cannot be run.
|
||||
|
||||
"""
|
||||
try:
|
||||
proc = subprocess.Popen([apache_init_script, "restart"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
|
||||
except (OSError, ValueError):
|
||||
logger.fatal(
|
||||
"Unable to restart the Apache process with %s", apache_init_script)
|
||||
raise errors.MisconfigurationError(
|
||||
"Unable to restart Apache process with %s" % apache_init_script)
|
||||
|
||||
stdout, stderr = proc.communicate()
|
||||
|
||||
if proc.returncode != 0:
|
||||
# Enter recovery routine...
|
||||
logger.error("Apache Restart Failed!\n%s\n%s", stdout, stderr)
|
||||
raise errors.MisconfigurationError(
|
||||
"Error while restarting Apache:\n%s\n%s" % (stdout, stderr))
|
||||
|
||||
|
||||
def get_file_path(vhost_path):
|
||||
"""Get file path from augeas_vhost_path.
|
||||
|
||||
|
||||
@@ -1,15 +1,62 @@
|
||||
"""Apache plugin constants."""
|
||||
import pkg_resources
|
||||
from letsencrypt import le_util
|
||||
|
||||
|
||||
CLI_DEFAULTS = dict(
|
||||
CLI_DEFAULTS_DEBIAN = dict(
|
||||
server_root="/etc/apache2",
|
||||
ctl="apache2ctl",
|
||||
vhost_root="/etc/apache2/sites-available",
|
||||
vhost_files="*",
|
||||
version_cmd=['apache2ctl', '-v'],
|
||||
define_cmd=['apache2ctl', '-t', '-D', 'DUMP_RUN_CFG'],
|
||||
restart_cmd=['apache2ctl', 'graceful'],
|
||||
conftest_cmd=['apache2ctl', 'configtest'],
|
||||
enmod="a2enmod",
|
||||
dismod="a2dismod",
|
||||
init_script="/etc/init.d/apache2",
|
||||
le_vhost_ext="-le-ssl.conf",
|
||||
handle_mods=True,
|
||||
handle_sites=True,
|
||||
challenge_location="/etc/apache2"
|
||||
)
|
||||
CLI_DEFAULTS_CENTOS = dict(
|
||||
server_root="/etc/httpd",
|
||||
vhost_root="/etc/httpd/conf.d",
|
||||
vhost_files="*.conf",
|
||||
version_cmd=['apachectl', '-v'],
|
||||
define_cmd=['apachectl', '-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/httpd/conf.d"
|
||||
)
|
||||
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'],
|
||||
restart_cmd=['apache2ctl', 'graceful'],
|
||||
conftest_cmd=['apache2ctl', 'configtest'],
|
||||
enmod=None,
|
||||
dismod=None,
|
||||
le_vhost_ext="-le-ssl.conf",
|
||||
handle_mods=False,
|
||||
handle_sites=False,
|
||||
challenge_location="/etc/apache2/vhosts.d"
|
||||
)
|
||||
CLI_DEFAULTS = {
|
||||
"debian": CLI_DEFAULTS_DEBIAN,
|
||||
"ubuntu": CLI_DEFAULTS_DEBIAN,
|
||||
"centos": CLI_DEFAULTS_CENTOS,
|
||||
"centos linux": CLI_DEFAULTS_CENTOS,
|
||||
"fedora": CLI_DEFAULTS_CENTOS,
|
||||
"red hat enterprise linux server": CLI_DEFAULTS_CENTOS,
|
||||
"gentoo base system": CLI_DEFAULTS_GENTOO
|
||||
}
|
||||
"""CLI defaults."""
|
||||
|
||||
MOD_SSL_CONF_DEST = "options-ssl-apache.conf"
|
||||
@@ -26,4 +73,33 @@ AUGEAS_LENS_DIR = pkg_resources.resource_filename(
|
||||
|
||||
REWRITE_HTTPS_ARGS = [
|
||||
"^", "https://%{SERVER_NAME}%{REQUEST_URI}", "[L,QSA,R=permanent]"]
|
||||
"""Apache rewrite rule arguments used for redirections to https vhost"""
|
||||
"""Apache version<2.3.9 rewrite rule arguments used for redirections to
|
||||
https vhost"""
|
||||
|
||||
REWRITE_HTTPS_ARGS_WITH_END = [
|
||||
"^", "https://%{SERVER_NAME}%{REQUEST_URI}", "[END,QSA,R=permanent]"]
|
||||
"""Apache version >= 2.3.9 rewrite rule arguments used for redirections to
|
||||
https vhost"""
|
||||
|
||||
HSTS_ARGS = ["always", "set", "Strict-Transport-Security",
|
||||
"\"max-age=31536000\""]
|
||||
"""Apache header arguments for HSTS"""
|
||||
|
||||
UIR_ARGS = ["always", "set", "Content-Security-Policy",
|
||||
"upgrade-insecure-requests"]
|
||||
|
||||
HEADER_ARGS = {"Strict-Transport-Security": HSTS_ARGS,
|
||||
"Upgrade-Insecure-Requests": UIR_ARGS}
|
||||
|
||||
|
||||
def os_constant(key):
|
||||
"""Get a constant value for operating system
|
||||
:param key: name of cli constant
|
||||
:return: value of constant for active os
|
||||
"""
|
||||
os_info = le_util.get_os_info()
|
||||
try:
|
||||
constants = CLI_DEFAULTS[os_info[0].lower()]
|
||||
except KeyError:
|
||||
constants = CLI_DEFAULTS["debian"]
|
||||
return constants[key]
|
||||
|
||||
@@ -4,6 +4,7 @@ import os
|
||||
|
||||
import zope.component
|
||||
|
||||
from letsencrypt import errors
|
||||
from letsencrypt import interfaces
|
||||
|
||||
import letsencrypt.display.util as display_util
|
||||
@@ -78,11 +79,18 @@ def _vhost_menu(domain, vhosts):
|
||||
name_size=disp_name_size)
|
||||
)
|
||||
|
||||
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),
|
||||
choices, help_label="More Info", ok_label="Select")
|
||||
try:
|
||||
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),
|
||||
choices, help_label="More Info", ok_label="Select")
|
||||
except errors.MissingCommandlineFlag, e:
|
||||
msg = ("Failed to run Apache plugin non-interactively{1}{0}{1}"
|
||||
"(The best solution is to add ServerName or ServerAlias "
|
||||
"entries to the VirtualHost directives of your apache "
|
||||
"configuration files.)".format(e, os.linesep))
|
||||
raise errors.MissingCommandlineFlag, msg
|
||||
|
||||
return code, tag
|
||||
|
||||
|
||||
@@ -14,9 +14,9 @@ SSLOptions +StrictRequire
|
||||
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" vhost_combined
|
||||
LogFormat "%v %h %l %u %t \"%r\" %>s %b" vhost_common
|
||||
|
||||
CustomLog /var/log/apache2/access.log vhost_combined
|
||||
LogLevel warn
|
||||
ErrorLog /var/log/apache2/error.log
|
||||
#CustomLog /var/log/apache2/access.log vhost_combined
|
||||
#LogLevel warn
|
||||
#ErrorLog /var/log/apache2/error.log
|
||||
|
||||
# Always ensure Cookies have "Secure" set (JAH 2012/1)
|
||||
#Header edit Set-Cookie (?i)^(.*)(;\s*secure)??((\s*;)?(.*)) "$1; Secure$3$4"
|
||||
|
||||
@@ -8,6 +8,7 @@ import subprocess
|
||||
|
||||
from letsencrypt import errors
|
||||
|
||||
from letsencrypt_apache import constants
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -19,7 +20,6 @@ class ApacheParser(object):
|
||||
|
||||
:ivar str root: Normalized absolute path to the server root
|
||||
directory. Without trailing slash.
|
||||
:ivar str root: Server root
|
||||
:ivar set modules: All module names that are currently enabled.
|
||||
:ivar dict loc: Location to place directives, root - configuration origin,
|
||||
default - user config file, name - NameVirtualHost,
|
||||
@@ -28,15 +28,17 @@ class ApacheParser(object):
|
||||
arg_var_interpreter = re.compile(r"\$\{[^ \}]*}")
|
||||
fnmatch_chars = set(["*", "?", "\\", "[", "]"])
|
||||
|
||||
def __init__(self, aug, root, ctl):
|
||||
def __init__(self, aug, root, vhostroot, version=(2, 4)):
|
||||
# Note: Order is important here.
|
||||
|
||||
# This uses the binary, so it can be done first.
|
||||
# https://httpd.apache.org/docs/2.4/mod/core.html#define
|
||||
# https://httpd.apache.org/docs/2.4/mod/core.html#ifdefine
|
||||
# This only handles invocation parameters and Define directives!
|
||||
self.parser_paths = {}
|
||||
self.variables = {}
|
||||
self.update_runtime_variables(ctl)
|
||||
if version >= (2, 4):
|
||||
self.update_runtime_variables()
|
||||
|
||||
self.aug = aug
|
||||
# Find configuration root and make sure augeas can parse it.
|
||||
@@ -44,6 +46,8 @@ class ApacheParser(object):
|
||||
self.loc = {"root": self._find_config_root()}
|
||||
self._parse_file(self.loc["root"])
|
||||
|
||||
self.vhostroot = os.path.abspath(vhostroot)
|
||||
|
||||
# This problem has been fixed in Augeas 1.0
|
||||
self.standardize_excl()
|
||||
|
||||
@@ -56,9 +60,14 @@ class ApacheParser(object):
|
||||
# Set up rest of locations
|
||||
self.loc.update(self._set_locations())
|
||||
|
||||
# Must also attempt to parse sites-available or equivalent
|
||||
# Sites-available is not included naturally in configuration
|
||||
self._parse_file(os.path.join(self.root, "sites-available") + "/*")
|
||||
# Must also attempt to parse virtual host root
|
||||
self._parse_file(self.vhostroot + "/" +
|
||||
constants.os_constant("vhost_files"))
|
||||
|
||||
# check to see if there were unparsed define statements
|
||||
if version < (2, 4):
|
||||
if self.find_dir("Define", exclude=False):
|
||||
raise errors.PluginError("Error parsing runtime variables")
|
||||
|
||||
def init_modules(self):
|
||||
"""Iterates on the configuration until no new modules are loaded.
|
||||
@@ -84,29 +93,30 @@ class ApacheParser(object):
|
||||
self.modules.add(
|
||||
os.path.basename(self.get_arg(match_filename))[:-2] + "c")
|
||||
|
||||
def update_runtime_variables(self, ctl):
|
||||
def update_runtime_variables(self):
|
||||
""""
|
||||
|
||||
.. note:: Compile time variables (apache2ctl -V) are not used within the
|
||||
dynamic configuration files. These should not be parsed or
|
||||
.. note:: Compile time variables (apache2ctl -V) are not used within
|
||||
the dynamic configuration files. These should not be parsed or
|
||||
interpreted.
|
||||
|
||||
.. todo:: Create separate compile time variables... simply for arg_get()
|
||||
.. todo:: Create separate compile time variables...
|
||||
simply for arg_get()
|
||||
|
||||
"""
|
||||
stdout = self._get_runtime_cfg(ctl)
|
||||
stdout = self._get_runtime_cfg()
|
||||
|
||||
variables = dict()
|
||||
matches = re.compile(r"Define: ([^ \n]*)").findall(stdout)
|
||||
try:
|
||||
matches.remove("DUMP_RUN_CFG")
|
||||
except ValueError:
|
||||
raise errors.PluginError("Unable to parse runtime variables")
|
||||
return
|
||||
|
||||
for match in matches:
|
||||
if match.count("=") > 1:
|
||||
logger.error("Unexpected number of equal signs in "
|
||||
"apache2ctl -D DUMP_RUN_CFG")
|
||||
"runtime config dump.")
|
||||
raise errors.PluginError(
|
||||
"Error parsing Apache runtime variables")
|
||||
parts = match.partition("=")
|
||||
@@ -114,7 +124,7 @@ class ApacheParser(object):
|
||||
|
||||
self.variables = variables
|
||||
|
||||
def _get_runtime_cfg(self, ctl): # pylint: disable=no-self-use
|
||||
def _get_runtime_cfg(self): # pylint: disable=no-self-use
|
||||
"""Get runtime configuration info.
|
||||
|
||||
:returns: stdout from DUMP_RUN_CFG
|
||||
@@ -122,16 +132,18 @@ class ApacheParser(object):
|
||||
"""
|
||||
try:
|
||||
proc = subprocess.Popen(
|
||||
[ctl, "-t", "-D", "DUMP_RUN_CFG"],
|
||||
constants.os_constant("define_cmd"),
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
stdout, stderr = proc.communicate()
|
||||
|
||||
except (OSError, ValueError):
|
||||
logger.error(
|
||||
"Error accessing %s for runtime parameters!%s", ctl, os.linesep)
|
||||
"Error running command %s for runtime parameters!%s",
|
||||
constants.os_constant("define_cmd"), os.linesep)
|
||||
raise errors.MisconfigurationError(
|
||||
"Error accessing loaded Apache parameters: %s", ctl)
|
||||
"Error accessing loaded Apache parameters: %s",
|
||||
constants.os_constant("define_cmd"))
|
||||
# Small errors that do not impede
|
||||
if proc.returncode != 0:
|
||||
logger.warn("Error in checking parameter list: %s", stderr)
|
||||
@@ -166,7 +178,8 @@ class ApacheParser(object):
|
||||
# Make sure we don't cause an IndexError (end of list)
|
||||
# Check to make sure arg + 1 doesn't exist
|
||||
if (i == (len(matches) - 1) or
|
||||
not matches[i + 1].endswith("/arg[%d]" % (args + 1))):
|
||||
not matches[i + 1].endswith("/arg[%d]" %
|
||||
(args + 1))):
|
||||
filtered.append(matches[i][:-len("/arg[%d]" % args)])
|
||||
|
||||
return filtered
|
||||
@@ -300,8 +313,6 @@ class ApacheParser(object):
|
||||
for match in matches:
|
||||
dir_ = self.aug.get(match).lower()
|
||||
if dir_ == "include" or dir_ == "includeoptional":
|
||||
# start[6:] to strip off /files
|
||||
#print self._get_include_path(self.get_arg(match +"/arg")), directive, arg
|
||||
ordered_matches.extend(self.find_dir(
|
||||
directive, arg,
|
||||
self._get_include_path(self.get_arg(match + "/arg")),
|
||||
@@ -320,8 +331,8 @@ class ApacheParser(object):
|
||||
"""
|
||||
value = self.aug.get(match)
|
||||
|
||||
# No need to strip quotes for variables, as apache2ctl already does this
|
||||
# but we do need to strip quotes for all normal arguments.
|
||||
# No need to strip quotes for variables, as apache2ctl already does
|
||||
# this, but we do need to strip quotes for all normal arguments.
|
||||
|
||||
# Note: normal argument may be a quoted variable
|
||||
# e.g. strip now, not later
|
||||
@@ -443,7 +454,7 @@ class ApacheParser(object):
|
||||
https://apr.apache.org/docs/apr/2.0/apr__fnmatch_8h_source.html
|
||||
http://apache2.sourcearchive.com/documentation/2.2.16-6/apr__fnmatch_8h_source.html
|
||||
|
||||
:param str clean_fn_match: Apache style filename match, similar to globs
|
||||
:param str clean_fn_match: Apache style filename match, like globs
|
||||
|
||||
:returns: regex suitable for augeas
|
||||
:rtype: str
|
||||
@@ -461,16 +472,63 @@ class ApacheParser(object):
|
||||
:param str filepath: Apache config file path
|
||||
|
||||
"""
|
||||
use_new, remove_old = self._check_path_actions(filepath)
|
||||
# Test if augeas included file for Httpd.lens
|
||||
# Note: This works for augeas globs, ie. *.conf
|
||||
inc_test = self.aug.match(
|
||||
"/augeas/load/Httpd/incl [. ='%s']" % filepath)
|
||||
if not inc_test:
|
||||
# Load up files
|
||||
# This doesn't seem to work on TravisCI
|
||||
# self.aug.add_transform("Httpd.lns", [filepath])
|
||||
self._add_httpd_transform(filepath)
|
||||
self.aug.load()
|
||||
if use_new:
|
||||
inc_test = self.aug.match(
|
||||
"/augeas/load/Httpd/incl [. ='%s']" % filepath)
|
||||
if not inc_test:
|
||||
# Load up files
|
||||
# This doesn't seem to work on TravisCI
|
||||
# self.aug.add_transform("Httpd.lns", [filepath])
|
||||
if remove_old:
|
||||
self._remove_httpd_transform(filepath)
|
||||
self._add_httpd_transform(filepath)
|
||||
self.aug.load()
|
||||
|
||||
def _check_path_actions(self, filepath):
|
||||
"""Determine actions to take with a new augeas path
|
||||
|
||||
This helper function will return a tuple that defines
|
||||
if we should try to append the new filepath to augeas
|
||||
parser paths, and / or remove the old one with more
|
||||
narrow matching.
|
||||
|
||||
:param str filepath: filepath to check the actions for
|
||||
|
||||
"""
|
||||
|
||||
try:
|
||||
new_file_match = os.path.basename(filepath)
|
||||
existing_matches = self.parser_paths[os.path.dirname(filepath)]
|
||||
if "*" in existing_matches:
|
||||
use_new = False
|
||||
else:
|
||||
use_new = True
|
||||
if new_file_match == "*":
|
||||
remove_old = True
|
||||
else:
|
||||
remove_old = False
|
||||
except KeyError:
|
||||
use_new = True
|
||||
remove_old = False
|
||||
return use_new, remove_old
|
||||
|
||||
def _remove_httpd_transform(self, filepath):
|
||||
"""Remove path from Augeas transform
|
||||
|
||||
:param str filepath: filepath to remove
|
||||
"""
|
||||
|
||||
remove_basenames = self.parser_paths[os.path.dirname(filepath)]
|
||||
remove_dirname = os.path.dirname(filepath)
|
||||
for name in remove_basenames:
|
||||
remove_path = remove_dirname + "/" + name
|
||||
remove_inc = self.aug.match(
|
||||
"/augeas/load/Httpd/incl [. ='%s']" % remove_path)
|
||||
self.aug.remove(remove_inc[0])
|
||||
self.parser_paths.pop(remove_dirname)
|
||||
|
||||
def _add_httpd_transform(self, incl):
|
||||
"""Add a transform to Augeas.
|
||||
@@ -492,6 +550,13 @@ class ApacheParser(object):
|
||||
# Augeas uses base 1 indexing... insert at beginning...
|
||||
self.aug.set("/augeas/load/Httpd/lens", "Httpd.lns")
|
||||
self.aug.set("/augeas/load/Httpd/incl", incl)
|
||||
# Add included path to paths dictionary
|
||||
try:
|
||||
self.parser_paths[os.path.dirname(incl)].append(
|
||||
os.path.basename(incl))
|
||||
except KeyError:
|
||||
self.parser_paths[os.path.dirname(incl)] = [
|
||||
os.path.basename(incl)]
|
||||
|
||||
def standardize_excl(self):
|
||||
"""Standardize the excl arguments for the Httpd lens in Augeas.
|
||||
@@ -546,8 +611,7 @@ class ApacheParser(object):
|
||||
|
||||
def _find_config_root(self):
|
||||
"""Find the Apache Configuration Root file."""
|
||||
location = ["apache2.conf", "httpd.conf"]
|
||||
|
||||
location = ["apache2.conf", "httpd.conf", "conf/httpd.conf"]
|
||||
for name in location:
|
||||
if os.path.isfile(os.path.join(self.root, name)):
|
||||
return os.path.join(self.root, name)
|
||||
|
||||
@@ -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/letsencrypt/letsencrypt/issues/1213
|
||||
https://github.com/letsencrypt/letsencrypt/issues/1602
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
#!/bin/bash
|
||||
|
||||
# A hackish script to see if the client is behaving as expected
|
||||
# with each of the "passing" conf files.
|
||||
|
||||
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...
|
||||
if [ "$f" != "" ] ; then
|
||||
Cleanup
|
||||
fi
|
||||
exit 1
|
||||
}
|
||||
|
||||
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 """
|
||||
<VirtualHost *:80>
|
||||
ServerName example.com
|
||||
DocumentRoot /tmp/
|
||||
ErrorLog /tmp/error.log
|
||||
CustomLog /tmp/requests.log combined
|
||||
</VirtualHost>""" >> $EA/sites-available/throwaway-example.conf
|
||||
else
|
||||
TMP="/tmp/`basename \"$APPEND_APACHECONF\"`.$$"
|
||||
sudo cp -a "$APPEND_APACHECONF" "$TMP"
|
||||
sudo bash -c "cat \"$f\" >> \"$APPEND_APACHECONF\""
|
||||
fi
|
||||
}
|
||||
|
||||
function Cleanup() {
|
||||
if [ "$APPEND_APACHECONF" = "" ] ; then
|
||||
sudo rm /etc/apache2/sites-{enabled,available}/"$f"
|
||||
sudo rm $EA/sites-available/throwaway-example.conf
|
||||
else
|
||||
sudo mv "$TMP" "$APPEND_APACHECONF"
|
||||
fi
|
||||
}
|
||||
|
||||
# if our environment asks us to enable modules, do our best!
|
||||
if [ "$1" = --debian-modules ] ; then
|
||||
sudo apt-get install -y libapache2-mod-wsgi
|
||||
sudo apt-get install -y libapache2-mod-macro
|
||||
|
||||
for mod in ssl rewrite macro wsgi deflate userdir version mime setenvif ; do
|
||||
echo -n enabling $mod
|
||||
sudo a2enmod $mod
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
FAILS=0
|
||||
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`
|
||||
if echo $RESULT | grep -Eq \("Which names would you like"\|"mod_macro is not yet"\) ; then
|
||||
echo passed
|
||||
else
|
||||
echo failed
|
||||
echo $RESULT
|
||||
echo
|
||||
echo
|
||||
FAILS=`expr $FAILS + 1`
|
||||
fi
|
||||
Cleanup
|
||||
done
|
||||
if [ "$FAILS" -ne 0 ] ; then
|
||||
exit 1
|
||||
fi
|
||||
exit 0
|
||||
@@ -0,0 +1,9 @@
|
||||
<VirtualHost 173.192.30.7:80 [2607:f0d0:1005:99::3:1337]:80>
|
||||
DocumentRoot /xxxx/
|
||||
ServerName noodles.net.nz
|
||||
ServerAlias www.noodles.net.nz
|
||||
CustomLog ${APACHE_LOG_DIR}/domlogs/noodles.log combined
|
||||
<Directory "/xxxx/">
|
||||
AllowOverride All
|
||||
</Directory>
|
||||
</VirtualHost>
|
||||
@@ -0,0 +1,21 @@
|
||||
|
||||
<VirtualHost 173.192.30.7:443 [2607:f0d0:1005:99::3:1337]:443>
|
||||
DocumentRoot /xxxx/
|
||||
ServerName noodles.net.nz
|
||||
ServerAlias www.noodles.net.nz
|
||||
CustomLog ${APACHE_LOG_DIR}/domlogs/noodles.log combined
|
||||
<Directory "xxxx">
|
||||
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 /xxxx/noodles.net.nz.crt
|
||||
SSLCertificateKeyFile /xxxx/noodles.net.nz.key
|
||||
|
||||
Header set Strict-Transport-Security "max-age=31536000; preload"
|
||||
</VirtualHost>
|
||||
@@ -0,0 +1,52 @@
|
||||
<VirtualHost *:443>
|
||||
ServerAdmin webmaster@localhost
|
||||
ServerAlias www.example.com
|
||||
ServerName example.com
|
||||
DocumentRoot /var/www/example.com/www/
|
||||
SSLEngine on
|
||||
|
||||
SSLProtocol all -SSLv2 -SSLv3
|
||||
SSLCipherSuite "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRS$
|
||||
SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
|
||||
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
|
||||
|
||||
<Directory />
|
||||
Options FollowSymLinks
|
||||
AllowOverride All
|
||||
</Directory>
|
||||
<Directory /var/www/example.com/www>
|
||||
Options Indexes FollowSymLinks MultiViews
|
||||
AllowOverride All
|
||||
Order allow,deny
|
||||
allow from all
|
||||
# This directive allows us to have apache2's default start page
|
||||
# in /apache2-default/, but still have / go to the right place
|
||||
</Directory>
|
||||
|
||||
ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
|
||||
<Directory "/usr/lib/cgi-bin">
|
||||
AllowOverride None
|
||||
Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
|
||||
Order allow,deny
|
||||
Allow from all
|
||||
</Directory>
|
||||
|
||||
ErrorLog /var/log/apache2/error.log
|
||||
|
||||
# Possible values include: debug, info, notice, warn, error, crit,
|
||||
# alert, emerg.
|
||||
LogLevel warn
|
||||
|
||||
CustomLog /var/log/apache2/access.log combined
|
||||
ServerSignature On
|
||||
|
||||
Alias /apache_doc/ "/usr/share/doc/"
|
||||
<Directory "/usr/share/doc/">
|
||||
Options Indexes MultiViews FollowSymLinks
|
||||
AllowOverride None
|
||||
Order deny,allow
|
||||
Deny from all
|
||||
Allow from 127.0.0.0/255.0.0.0 ::1/128
|
||||
</Directory>
|
||||
|
||||
</VirtualHost>
|
||||
@@ -0,0 +1,295 @@
|
||||
<Directory /var/www/sjau.ch>
|
||||
AllowOverride None
|
||||
Require all denied
|
||||
</Directory>
|
||||
|
||||
<VirtualHost *:80>
|
||||
DocumentRoot /var/www/sjau.ch/web
|
||||
|
||||
ServerName sjau.ch
|
||||
ServerAlias www.sjau.ch
|
||||
ServerAdmin webmaster@sjau.ch
|
||||
|
||||
ErrorLog /var/log/ispconfig/httpd/sjau.ch/error.log
|
||||
|
||||
Alias /error/ "/var/www/sjau.ch/web/error/"
|
||||
ErrorDocument 400 /error/400.html
|
||||
ErrorDocument 401 /error/401.html
|
||||
ErrorDocument 403 /error/403.html
|
||||
ErrorDocument 404 /error/404.html
|
||||
ErrorDocument 405 /error/405.html
|
||||
ErrorDocument 500 /error/500.html
|
||||
ErrorDocument 502 /error/502.html
|
||||
ErrorDocument 503 /error/503.html
|
||||
|
||||
<IfModule mod_ssl.c>
|
||||
</IfModule>
|
||||
|
||||
<Directory /var/www/sjau.ch/web>
|
||||
# Clear PHP settings of this website
|
||||
<FilesMatch ".+\.ph(p[345]?|t|tml)$">
|
||||
SetHandler None
|
||||
</FilesMatch>
|
||||
Options +FollowSymLinks
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
<Directory /var/www/clients/client1/web2/web>
|
||||
# Clear PHP settings of this website
|
||||
<FilesMatch ".+\.ph(p[345]?|t|tml)$">
|
||||
SetHandler None
|
||||
</FilesMatch>
|
||||
Options +FollowSymLinks
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
<IfModule mod_ruby.c>
|
||||
<Directory /var/www/sjau.ch/web>
|
||||
Options +ExecCGI
|
||||
</Directory>
|
||||
RubyRequire apache/ruby-run
|
||||
#RubySafeLevel 0
|
||||
AddType text/html .rb
|
||||
AddType text/html .rbx
|
||||
<Files *.rb>
|
||||
SetHandler ruby-object
|
||||
RubyHandler Apache::RubyRun.instance
|
||||
</Files>
|
||||
<Files *.rbx>
|
||||
SetHandler ruby-object
|
||||
RubyHandler Apache::RubyRun.instance
|
||||
</Files>
|
||||
</IfModule>
|
||||
|
||||
|
||||
<IfModule mod_python.c>
|
||||
<Directory /var/www/sjau.ch/web>
|
||||
<FilesMatch "\.py$">
|
||||
SetHandler mod_python
|
||||
</FilesMatch>
|
||||
PythonHandler mod_python.publisher
|
||||
PythonDebug On
|
||||
</Directory>
|
||||
</IfModule>
|
||||
|
||||
# cgi enabled
|
||||
<Directory /var/www/clients/client1/web2/cgi-bin>
|
||||
Require all granted
|
||||
</Directory>
|
||||
ScriptAlias /cgi-bin/ /var/www/clients/client1/web2/cgi-bin/
|
||||
<FilesMatch "\.(cgi|pl)$">
|
||||
SetHandler cgi-script
|
||||
</FilesMatch>
|
||||
# suexec enabled
|
||||
<IfModule mod_suexec.c>
|
||||
SuexecUserGroup web2 client1
|
||||
</IfModule>
|
||||
# php as fast-cgi enabled
|
||||
# For config options see: http://httpd.apache.org/mod_fcgid/mod/mod_fcgid.html
|
||||
<IfModule mod_fcgid.c>
|
||||
IdleTimeout 300
|
||||
ProcessLifeTime 3600
|
||||
# MaxProcessCount 1000
|
||||
DefaultMinClassProcessCount 0
|
||||
DefaultMaxClassProcessCount 100
|
||||
IPCConnectTimeout 3
|
||||
IPCCommTimeout 600
|
||||
BusyTimeout 3600
|
||||
</IfModule>
|
||||
<Directory /var/www/sjau.ch/web>
|
||||
<FilesMatch "\.php[345]?$">
|
||||
SetHandler fcgid-script
|
||||
</FilesMatch>
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web2/.php-fcgi-starter .php
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web2/.php-fcgi-starter .php3
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web2/.php-fcgi-starter .php4
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web2/.php-fcgi-starter .php5
|
||||
Options +ExecCGI
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
<Directory /var/www/clients/client1/web2/web>
|
||||
<FilesMatch "\.php[345]?$">
|
||||
SetHandler fcgid-script
|
||||
</FilesMatch>
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web2/.php-fcgi-starter .php
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web2/.php-fcgi-starter .php3
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web2/.php-fcgi-starter .php4
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web2/.php-fcgi-starter .php5
|
||||
Options +ExecCGI
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
|
||||
# add support for apache mpm_itk
|
||||
<IfModule mpm_itk_module>
|
||||
AssignUserId web2 client1
|
||||
</IfModule>
|
||||
|
||||
<IfModule mod_dav_fs.c>
|
||||
# Do not execute PHP files in webdav directory
|
||||
<Directory /var/www/clients/client1/web2/webdav>
|
||||
<ifModule mod_security2.c>
|
||||
SecRuleRemoveById 960015
|
||||
SecRuleRemoveById 960032
|
||||
</ifModule>
|
||||
<FilesMatch "\.ph(p3?|tml)$">
|
||||
SetHandler None
|
||||
</FilesMatch>
|
||||
</Directory>
|
||||
DavLockDB /var/www/clients/client1/web2/tmp/DavLock
|
||||
# DO NOT REMOVE THE COMMENTS!
|
||||
# IF YOU REMOVE THEM, WEBDAV WILL NOT WORK ANYMORE!
|
||||
# WEBDAV BEGIN
|
||||
# WEBDAV END
|
||||
</IfModule>
|
||||
|
||||
|
||||
</VirtualHost>
|
||||
<VirtualHost [2a01:4f8:160:13a2::1002]:80>
|
||||
DocumentRoot /var/www/sjau.ch/web
|
||||
|
||||
ServerName sjau.ch
|
||||
ServerAlias www.sjau.ch
|
||||
ServerAdmin webmaster@sjau.ch
|
||||
|
||||
ErrorLog /var/log/ispconfig/httpd/sjau.ch/error.log
|
||||
|
||||
Alias /error/ "/var/www/sjau.ch/web/error/"
|
||||
ErrorDocument 400 /error/400.html
|
||||
ErrorDocument 401 /error/401.html
|
||||
ErrorDocument 403 /error/403.html
|
||||
ErrorDocument 404 /error/404.html
|
||||
ErrorDocument 405 /error/405.html
|
||||
ErrorDocument 500 /error/500.html
|
||||
ErrorDocument 502 /error/502.html
|
||||
ErrorDocument 503 /error/503.html
|
||||
|
||||
<IfModule mod_ssl.c>
|
||||
</IfModule>
|
||||
|
||||
<Directory /var/www/sjau.ch/web>
|
||||
# Clear PHP settings of this website
|
||||
<FilesMatch ".+\.ph(p[345]?|t|tml)$">
|
||||
SetHandler None
|
||||
</FilesMatch>
|
||||
Options +FollowSymLinks
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
<Directory /var/www/clients/client1/web2/web>
|
||||
# Clear PHP settings of this website
|
||||
<FilesMatch ".+\.ph(p[345]?|t|tml)$">
|
||||
SetHandler None
|
||||
</FilesMatch>
|
||||
Options +FollowSymLinks
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
<IfModule mod_ruby.c>
|
||||
<Directory /var/www/sjau.ch/web>
|
||||
Options +ExecCGI
|
||||
</Directory>
|
||||
RubyRequire apache/ruby-run
|
||||
#RubySafeLevel 0
|
||||
AddType text/html .rb
|
||||
AddType text/html .rbx
|
||||
<Files *.rb>
|
||||
SetHandler ruby-object
|
||||
RubyHandler Apache::RubyRun.instance
|
||||
</Files>
|
||||
<Files *.rbx>
|
||||
SetHandler ruby-object
|
||||
RubyHandler Apache::RubyRun.instance
|
||||
</Files>
|
||||
</IfModule>
|
||||
|
||||
|
||||
<IfModule mod_python.c>
|
||||
<Directory /var/www/sjau.ch/web>
|
||||
<FilesMatch "\.py$">
|
||||
SetHandler mod_python
|
||||
</FilesMatch>
|
||||
PythonHandler mod_python.publisher
|
||||
PythonDebug On
|
||||
</Directory>
|
||||
</IfModule>
|
||||
|
||||
# cgi enabled
|
||||
<Directory /var/www/clients/client1/web2/cgi-bin>
|
||||
Require all granted
|
||||
</Directory>
|
||||
ScriptAlias /cgi-bin/ /var/www/clients/client1/web2/cgi-bin/
|
||||
<FilesMatch "\.(cgi|pl)$">
|
||||
SetHandler cgi-script
|
||||
</FilesMatch>
|
||||
# suexec enabled
|
||||
<IfModule mod_suexec.c>
|
||||
SuexecUserGroup web2 client1
|
||||
</IfModule>
|
||||
# php as fast-cgi enabled
|
||||
# For config options see: http://httpd.apache.org/mod_fcgid/mod/mod_fcgid.html
|
||||
<IfModule mod_fcgid.c>
|
||||
IdleTimeout 300
|
||||
ProcessLifeTime 3600
|
||||
# MaxProcessCount 1000
|
||||
DefaultMinClassProcessCount 0
|
||||
DefaultMaxClassProcessCount 100
|
||||
IPCConnectTimeout 3
|
||||
IPCCommTimeout 600
|
||||
BusyTimeout 3600
|
||||
</IfModule>
|
||||
<Directory /var/www/sjau.ch/web>
|
||||
<FilesMatch "\.php[345]?$">
|
||||
SetHandler fcgid-script
|
||||
</FilesMatch>
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web2/.php-fcgi-starter .php
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web2/.php-fcgi-starter .php3
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web2/.php-fcgi-starter .php4
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web2/.php-fcgi-starter .php5
|
||||
Options +ExecCGI
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
<Directory /var/www/clients/client1/web2/web>
|
||||
<FilesMatch "\.php[345]?$">
|
||||
SetHandler fcgid-script
|
||||
</FilesMatch>
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web2/.php-fcgi-starter .php
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web2/.php-fcgi-starter .php3
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web2/.php-fcgi-starter .php4
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web2/.php-fcgi-starter .php5
|
||||
Options +ExecCGI
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
|
||||
# add support for apache mpm_itk
|
||||
<IfModule mpm_itk_module>
|
||||
AssignUserId web2 client1
|
||||
</IfModule>
|
||||
|
||||
<IfModule mod_dav_fs.c>
|
||||
# Do not execute PHP files in webdav directory
|
||||
<Directory /var/www/clients/client1/web2/webdav>
|
||||
<ifModule mod_security2.c>
|
||||
SecRuleRemoveById 960015
|
||||
SecRuleRemoveById 960032
|
||||
</ifModule>
|
||||
<FilesMatch "\.ph(p3?|tml)$">
|
||||
SetHandler None
|
||||
</FilesMatch>
|
||||
</Directory>
|
||||
DavLockDB /var/www/clients/client1/web2/tmp/DavLock
|
||||
# DO NOT REMOVE THE COMMENTS!
|
||||
# IF YOU REMOVE THEM, WEBDAV WILL NOT WORK ANYMORE!
|
||||
# WEBDAV BEGIN
|
||||
# WEBDAV END
|
||||
</IfModule>
|
||||
|
||||
|
||||
</VirtualHost>
|
||||
@@ -0,0 +1,593 @@
|
||||
<Directory /var/www/ensemen.ch>
|
||||
AllowOverride None
|
||||
Require all denied
|
||||
</Directory>
|
||||
|
||||
<VirtualHost *:80>
|
||||
DocumentRoot /var/www/ensemen.ch/web
|
||||
|
||||
ServerName ensemen.ch
|
||||
ServerAlias www.ensemen.ch
|
||||
ServerAdmin webmaster@ensemen.ch
|
||||
|
||||
ErrorLog /var/log/ispconfig/httpd/ensemen.ch/error.log
|
||||
|
||||
Alias /error/ "/var/www/ensemen.ch/web/error/"
|
||||
ErrorDocument 400 /error/400.html
|
||||
ErrorDocument 401 /error/401.html
|
||||
ErrorDocument 403 /error/403.html
|
||||
ErrorDocument 404 /error/404.html
|
||||
ErrorDocument 405 /error/405.html
|
||||
ErrorDocument 500 /error/500.html
|
||||
ErrorDocument 502 /error/502.html
|
||||
ErrorDocument 503 /error/503.html
|
||||
|
||||
<IfModule mod_ssl.c>
|
||||
</IfModule>
|
||||
|
||||
<Directory /var/www/ensemen.ch/web>
|
||||
# Clear PHP settings of this website
|
||||
<FilesMatch ".+\.ph(p[345]?|t|tml)$">
|
||||
SetHandler None
|
||||
</FilesMatch>
|
||||
Options +FollowSymLinks
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
<Directory /var/www/clients/client4/web17/web>
|
||||
# Clear PHP settings of this website
|
||||
<FilesMatch ".+\.ph(p[345]?|t|tml)$">
|
||||
SetHandler None
|
||||
</FilesMatch>
|
||||
Options +FollowSymLinks
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
<IfModule mod_ruby.c>
|
||||
<Directory /var/www/ensemen.ch/web>
|
||||
Options +ExecCGI
|
||||
</Directory>
|
||||
RubyRequire apache/ruby-run
|
||||
#RubySafeLevel 0
|
||||
AddType text/html .rb
|
||||
AddType text/html .rbx
|
||||
<Files *.rb>
|
||||
SetHandler ruby-object
|
||||
RubyHandler Apache::RubyRun.instance
|
||||
</Files>
|
||||
<Files *.rbx>
|
||||
SetHandler ruby-object
|
||||
RubyHandler Apache::RubyRun.instance
|
||||
</Files>
|
||||
</IfModule>
|
||||
|
||||
|
||||
<IfModule mod_python.c>
|
||||
<Directory /var/www/ensemen.ch/web>
|
||||
<FilesMatch "\.py$">
|
||||
SetHandler mod_python
|
||||
</FilesMatch>
|
||||
PythonHandler mod_python.publisher
|
||||
PythonDebug On
|
||||
</Directory>
|
||||
</IfModule>
|
||||
|
||||
# cgi enabled
|
||||
<Directory /var/www/clients/client4/web17/cgi-bin>
|
||||
Require all granted
|
||||
</Directory>
|
||||
ScriptAlias /cgi-bin/ /var/www/clients/client4/web17/cgi-bin/
|
||||
<FilesMatch "\.(cgi|pl)$">
|
||||
SetHandler cgi-script
|
||||
</FilesMatch>
|
||||
# suexec enabled
|
||||
<IfModule mod_suexec.c>
|
||||
SuexecUserGroup web17 client4
|
||||
</IfModule>
|
||||
# php as fast-cgi enabled
|
||||
# For config options see: http://httpd.apache.org/mod_fcgid/mod/mod_fcgid.html
|
||||
<IfModule mod_fcgid.c>
|
||||
IdleTimeout 300
|
||||
ProcessLifeTime 3600
|
||||
# MaxProcessCount 1000
|
||||
DefaultMinClassProcessCount 0
|
||||
DefaultMaxClassProcessCount 100
|
||||
IPCConnectTimeout 3
|
||||
IPCCommTimeout 600
|
||||
BusyTimeout 3600
|
||||
</IfModule>
|
||||
<Directory /var/www/ensemen.ch/web>
|
||||
<FilesMatch "\.php[345]?$">
|
||||
SetHandler fcgid-script
|
||||
</FilesMatch>
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web17/.php-fcgi-starter .php
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web17/.php-fcgi-starter .php3
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web17/.php-fcgi-starter .php4
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web17/.php-fcgi-starter .php5
|
||||
Options +ExecCGI
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
<Directory /var/www/clients/client4/web17/web>
|
||||
<FilesMatch "\.php[345]?$">
|
||||
SetHandler fcgid-script
|
||||
</FilesMatch>
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web17/.php-fcgi-starter .php
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web17/.php-fcgi-starter .php3
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web17/.php-fcgi-starter .php4
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web17/.php-fcgi-starter .php5
|
||||
Options +ExecCGI
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
|
||||
# add support for apache mpm_itk
|
||||
<IfModule mpm_itk_module>
|
||||
AssignUserId web17 client4
|
||||
</IfModule>
|
||||
|
||||
<IfModule mod_dav_fs.c>
|
||||
# Do not execute PHP files in webdav directory
|
||||
<Directory /var/www/clients/client4/web17/webdav>
|
||||
<ifModule mod_security2.c>
|
||||
SecRuleRemoveById 960015
|
||||
SecRuleRemoveById 960032
|
||||
</ifModule>
|
||||
<FilesMatch "\.ph(p3?|tml)$">
|
||||
SetHandler None
|
||||
</FilesMatch>
|
||||
</Directory>
|
||||
DavLockDB /var/www/clients/client4/web17/tmp/DavLock
|
||||
# DO NOT REMOVE THE COMMENTS!
|
||||
# IF YOU REMOVE THEM, WEBDAV WILL NOT WORK ANYMORE!
|
||||
# WEBDAV BEGIN
|
||||
# WEBDAV END
|
||||
</IfModule>
|
||||
|
||||
|
||||
</VirtualHost>
|
||||
<VirtualHost *:443>
|
||||
DocumentRoot /var/www/ensemen.ch/web
|
||||
|
||||
ServerName ensemen.ch
|
||||
ServerAlias www.ensemen.ch
|
||||
ServerAdmin webmaster@ensemen.ch
|
||||
|
||||
ErrorLog /var/log/ispconfig/httpd/ensemen.ch/error.log
|
||||
|
||||
Alias /error/ "/var/www/ensemen.ch/web/error/"
|
||||
ErrorDocument 400 /error/400.html
|
||||
ErrorDocument 401 /error/401.html
|
||||
ErrorDocument 403 /error/403.html
|
||||
ErrorDocument 404 /error/404.html
|
||||
ErrorDocument 405 /error/405.html
|
||||
ErrorDocument 500 /error/500.html
|
||||
ErrorDocument 502 /error/502.html
|
||||
ErrorDocument 503 /error/503.html
|
||||
|
||||
<IfModule mod_ssl.c>
|
||||
SSLEngine on
|
||||
SSLProtocol All -SSLv2 -SSLv3
|
||||
SSLCertificateFile /var/www/clients/client4/web17/ssl/ensemen.ch.crt
|
||||
SSLCertificateKeyFile /var/www/clients/client4/web17/ssl/ensemen.ch.key
|
||||
</IfModule>
|
||||
|
||||
<Directory /var/www/ensemen.ch/web>
|
||||
# Clear PHP settings of this website
|
||||
<FilesMatch ".+\.ph(p[345]?|t|tml)$">
|
||||
SetHandler None
|
||||
</FilesMatch>
|
||||
Options +FollowSymLinks
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
<Directory /var/www/clients/client4/web17/web>
|
||||
# Clear PHP settings of this website
|
||||
<FilesMatch ".+\.ph(p[345]?|t|tml)$">
|
||||
SetHandler None
|
||||
</FilesMatch>
|
||||
Options +FollowSymLinks
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
<IfModule mod_ruby.c>
|
||||
<Directory /var/www/ensemen.ch/web>
|
||||
Options +ExecCGI
|
||||
</Directory>
|
||||
RubyRequire apache/ruby-run
|
||||
#RubySafeLevel 0
|
||||
AddType text/html .rb
|
||||
AddType text/html .rbx
|
||||
<Files *.rb>
|
||||
SetHandler ruby-object
|
||||
RubyHandler Apache::RubyRun.instance
|
||||
</Files>
|
||||
<Files *.rbx>
|
||||
SetHandler ruby-object
|
||||
RubyHandler Apache::RubyRun.instance
|
||||
</Files>
|
||||
</IfModule>
|
||||
|
||||
|
||||
<IfModule mod_python.c>
|
||||
<Directory /var/www/ensemen.ch/web>
|
||||
<FilesMatch "\.py$">
|
||||
SetHandler mod_python
|
||||
</FilesMatch>
|
||||
PythonHandler mod_python.publisher
|
||||
PythonDebug On
|
||||
</Directory>
|
||||
</IfModule>
|
||||
|
||||
# cgi enabled
|
||||
<Directory /var/www/clients/client4/web17/cgi-bin>
|
||||
Require all granted
|
||||
</Directory>
|
||||
ScriptAlias /cgi-bin/ /var/www/clients/client4/web17/cgi-bin/
|
||||
<FilesMatch "\.(cgi|pl)$">
|
||||
SetHandler cgi-script
|
||||
</FilesMatch>
|
||||
# suexec enabled
|
||||
<IfModule mod_suexec.c>
|
||||
SuexecUserGroup web17 client4
|
||||
</IfModule>
|
||||
# php as fast-cgi enabled
|
||||
# For config options see: http://httpd.apache.org/mod_fcgid/mod/mod_fcgid.html
|
||||
<IfModule mod_fcgid.c>
|
||||
IdleTimeout 300
|
||||
ProcessLifeTime 3600
|
||||
# MaxProcessCount 1000
|
||||
DefaultMinClassProcessCount 0
|
||||
DefaultMaxClassProcessCount 100
|
||||
IPCConnectTimeout 3
|
||||
IPCCommTimeout 600
|
||||
BusyTimeout 3600
|
||||
</IfModule>
|
||||
<Directory /var/www/ensemen.ch/web>
|
||||
<FilesMatch "\.php[345]?$">
|
||||
SetHandler fcgid-script
|
||||
</FilesMatch>
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web17/.php-fcgi-starter .php
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web17/.php-fcgi-starter .php3
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web17/.php-fcgi-starter .php4
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web17/.php-fcgi-starter .php5
|
||||
Options +ExecCGI
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
<Directory /var/www/clients/client4/web17/web>
|
||||
<FilesMatch "\.php[345]?$">
|
||||
SetHandler fcgid-script
|
||||
</FilesMatch>
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web17/.php-fcgi-starter .php
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web17/.php-fcgi-starter .php3
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web17/.php-fcgi-starter .php4
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web17/.php-fcgi-starter .php5
|
||||
Options +ExecCGI
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
|
||||
# add support for apache mpm_itk
|
||||
<IfModule mpm_itk_module>
|
||||
AssignUserId web17 client4
|
||||
</IfModule>
|
||||
|
||||
<IfModule mod_dav_fs.c>
|
||||
# Do not execute PHP files in webdav directory
|
||||
<Directory /var/www/clients/client4/web17/webdav>
|
||||
<ifModule mod_security2.c>
|
||||
SecRuleRemoveById 960015
|
||||
SecRuleRemoveById 960032
|
||||
</ifModule>
|
||||
<FilesMatch "\.ph(p3?|tml)$">
|
||||
SetHandler None
|
||||
</FilesMatch>
|
||||
</Directory>
|
||||
DavLockDB /var/www/clients/client4/web17/tmp/DavLock
|
||||
# DO NOT REMOVE THE COMMENTS!
|
||||
# IF YOU REMOVE THEM, WEBDAV WILL NOT WORK ANYMORE!
|
||||
# WEBDAV BEGIN
|
||||
# WEBDAV END
|
||||
</IfModule>
|
||||
|
||||
|
||||
</VirtualHost>
|
||||
<VirtualHost [2a01:4f8:160:13a2::1017]:80>
|
||||
DocumentRoot /var/www/ensemen.ch/web
|
||||
|
||||
ServerName ensemen.ch
|
||||
ServerAlias www.ensemen.ch
|
||||
ServerAdmin webmaster@ensemen.ch
|
||||
|
||||
ErrorLog /var/log/ispconfig/httpd/ensemen.ch/error.log
|
||||
|
||||
Alias /error/ "/var/www/ensemen.ch/web/error/"
|
||||
ErrorDocument 400 /error/400.html
|
||||
ErrorDocument 401 /error/401.html
|
||||
ErrorDocument 403 /error/403.html
|
||||
ErrorDocument 404 /error/404.html
|
||||
ErrorDocument 405 /error/405.html
|
||||
ErrorDocument 500 /error/500.html
|
||||
ErrorDocument 502 /error/502.html
|
||||
ErrorDocument 503 /error/503.html
|
||||
|
||||
<IfModule mod_ssl.c>
|
||||
</IfModule>
|
||||
|
||||
<Directory /var/www/ensemen.ch/web>
|
||||
# Clear PHP settings of this website
|
||||
<FilesMatch ".+\.ph(p[345]?|t|tml)$">
|
||||
SetHandler None
|
||||
</FilesMatch>
|
||||
Options +FollowSymLinks
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
<Directory /var/www/clients/client4/web17/web>
|
||||
# Clear PHP settings of this website
|
||||
<FilesMatch ".+\.ph(p[345]?|t|tml)$">
|
||||
SetHandler None
|
||||
</FilesMatch>
|
||||
Options +FollowSymLinks
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
<IfModule mod_ruby.c>
|
||||
<Directory /var/www/ensemen.ch/web>
|
||||
Options +ExecCGI
|
||||
</Directory>
|
||||
RubyRequire apache/ruby-run
|
||||
#RubySafeLevel 0
|
||||
AddType text/html .rb
|
||||
AddType text/html .rbx
|
||||
<Files *.rb>
|
||||
SetHandler ruby-object
|
||||
RubyHandler Apache::RubyRun.instance
|
||||
</Files>
|
||||
<Files *.rbx>
|
||||
SetHandler ruby-object
|
||||
RubyHandler Apache::RubyRun.instance
|
||||
</Files>
|
||||
</IfModule>
|
||||
|
||||
|
||||
<IfModule mod_python.c>
|
||||
<Directory /var/www/ensemen.ch/web>
|
||||
<FilesMatch "\.py$">
|
||||
SetHandler mod_python
|
||||
</FilesMatch>
|
||||
PythonHandler mod_python.publisher
|
||||
PythonDebug On
|
||||
</Directory>
|
||||
</IfModule>
|
||||
|
||||
# cgi enabled
|
||||
<Directory /var/www/clients/client4/web17/cgi-bin>
|
||||
Require all granted
|
||||
</Directory>
|
||||
ScriptAlias /cgi-bin/ /var/www/clients/client4/web17/cgi-bin/
|
||||
<FilesMatch "\.(cgi|pl)$">
|
||||
SetHandler cgi-script
|
||||
</FilesMatch>
|
||||
# suexec enabled
|
||||
<IfModule mod_suexec.c>
|
||||
SuexecUserGroup web17 client4
|
||||
</IfModule>
|
||||
# php as fast-cgi enabled
|
||||
# For config options see: http://httpd.apache.org/mod_fcgid/mod/mod_fcgid.html
|
||||
<IfModule mod_fcgid.c>
|
||||
IdleTimeout 300
|
||||
ProcessLifeTime 3600
|
||||
# MaxProcessCount 1000
|
||||
DefaultMinClassProcessCount 0
|
||||
DefaultMaxClassProcessCount 100
|
||||
IPCConnectTimeout 3
|
||||
IPCCommTimeout 600
|
||||
BusyTimeout 3600
|
||||
</IfModule>
|
||||
<Directory /var/www/ensemen.ch/web>
|
||||
<FilesMatch "\.php[345]?$">
|
||||
SetHandler fcgid-script
|
||||
</FilesMatch>
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web17/.php-fcgi-starter .php
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web17/.php-fcgi-starter .php3
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web17/.php-fcgi-starter .php4
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web17/.php-fcgi-starter .php5
|
||||
Options +ExecCGI
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
<Directory /var/www/clients/client4/web17/web>
|
||||
<FilesMatch "\.php[345]?$">
|
||||
SetHandler fcgid-script
|
||||
</FilesMatch>
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web17/.php-fcgi-starter .php
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web17/.php-fcgi-starter .php3
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web17/.php-fcgi-starter .php4
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web17/.php-fcgi-starter .php5
|
||||
Options +ExecCGI
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
|
||||
# add support for apache mpm_itk
|
||||
<IfModule mpm_itk_module>
|
||||
AssignUserId web17 client4
|
||||
</IfModule>
|
||||
|
||||
<IfModule mod_dav_fs.c>
|
||||
# Do not execute PHP files in webdav directory
|
||||
<Directory /var/www/clients/client4/web17/webdav>
|
||||
<ifModule mod_security2.c>
|
||||
SecRuleRemoveById 960015
|
||||
SecRuleRemoveById 960032
|
||||
</ifModule>
|
||||
<FilesMatch "\.ph(p3?|tml)$">
|
||||
SetHandler None
|
||||
</FilesMatch>
|
||||
</Directory>
|
||||
DavLockDB /var/www/clients/client4/web17/tmp/DavLock
|
||||
# DO NOT REMOVE THE COMMENTS!
|
||||
# IF YOU REMOVE THEM, WEBDAV WILL NOT WORK ANYMORE!
|
||||
# WEBDAV BEGIN
|
||||
# WEBDAV END
|
||||
</IfModule>
|
||||
|
||||
|
||||
</VirtualHost>
|
||||
<VirtualHost [2a01:4f8:160:13a2::1017]:443>
|
||||
DocumentRoot /var/www/ensemen.ch/web
|
||||
|
||||
ServerName ensemen.ch
|
||||
ServerAlias www.ensemen.ch
|
||||
ServerAdmin webmaster@ensemen.ch
|
||||
|
||||
ErrorLog /var/log/ispconfig/httpd/ensemen.ch/error.log
|
||||
|
||||
Alias /error/ "/var/www/ensemen.ch/web/error/"
|
||||
ErrorDocument 400 /error/400.html
|
||||
ErrorDocument 401 /error/401.html
|
||||
ErrorDocument 403 /error/403.html
|
||||
ErrorDocument 404 /error/404.html
|
||||
ErrorDocument 405 /error/405.html
|
||||
ErrorDocument 500 /error/500.html
|
||||
ErrorDocument 502 /error/502.html
|
||||
ErrorDocument 503 /error/503.html
|
||||
|
||||
<IfModule mod_ssl.c>
|
||||
SSLEngine on
|
||||
SSLProtocol All -SSLv2 -SSLv3
|
||||
SSLCertificateFile /var/www/clients/client4/web17/ssl/ensemen.ch.crt
|
||||
SSLCertificateKeyFile /var/www/clients/client4/web17/ssl/ensemen.ch.key
|
||||
</IfModule>
|
||||
|
||||
<Directory /var/www/ensemen.ch/web>
|
||||
# Clear PHP settings of this website
|
||||
<FilesMatch ".+\.ph(p[345]?|t|tml)$">
|
||||
SetHandler None
|
||||
</FilesMatch>
|
||||
Options +FollowSymLinks
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
<Directory /var/www/clients/client4/web17/web>
|
||||
# Clear PHP settings of this website
|
||||
<FilesMatch ".+\.ph(p[345]?|t|tml)$">
|
||||
SetHandler None
|
||||
</FilesMatch>
|
||||
Options +FollowSymLinks
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
<IfModule mod_ruby.c>
|
||||
<Directory /var/www/ensemen.ch/web>
|
||||
Options +ExecCGI
|
||||
</Directory>
|
||||
RubyRequire apache/ruby-run
|
||||
#RubySafeLevel 0
|
||||
AddType text/html .rb
|
||||
AddType text/html .rbx
|
||||
<Files *.rb>
|
||||
SetHandler ruby-object
|
||||
RubyHandler Apache::RubyRun.instance
|
||||
</Files>
|
||||
<Files *.rbx>
|
||||
SetHandler ruby-object
|
||||
RubyHandler Apache::RubyRun.instance
|
||||
</Files>
|
||||
</IfModule>
|
||||
|
||||
|
||||
<IfModule mod_python.c>
|
||||
<Directory /var/www/ensemen.ch/web>
|
||||
<FilesMatch "\.py$">
|
||||
SetHandler mod_python
|
||||
</FilesMatch>
|
||||
PythonHandler mod_python.publisher
|
||||
PythonDebug On
|
||||
</Directory>
|
||||
</IfModule>
|
||||
|
||||
# cgi enabled
|
||||
<Directory /var/www/clients/client4/web17/cgi-bin>
|
||||
Require all granted
|
||||
</Directory>
|
||||
ScriptAlias /cgi-bin/ /var/www/clients/client4/web17/cgi-bin/
|
||||
<FilesMatch "\.(cgi|pl)$">
|
||||
SetHandler cgi-script
|
||||
</FilesMatch>
|
||||
# suexec enabled
|
||||
<IfModule mod_suexec.c>
|
||||
SuexecUserGroup web17 client4
|
||||
</IfModule>
|
||||
# php as fast-cgi enabled
|
||||
# For config options see: http://httpd.apache.org/mod_fcgid/mod/mod_fcgid.html
|
||||
<IfModule mod_fcgid.c>
|
||||
IdleTimeout 300
|
||||
ProcessLifeTime 3600
|
||||
# MaxProcessCount 1000
|
||||
DefaultMinClassProcessCount 0
|
||||
DefaultMaxClassProcessCount 100
|
||||
IPCConnectTimeout 3
|
||||
IPCCommTimeout 600
|
||||
BusyTimeout 3600
|
||||
</IfModule>
|
||||
<Directory /var/www/ensemen.ch/web>
|
||||
<FilesMatch "\.php[345]?$">
|
||||
SetHandler fcgid-script
|
||||
</FilesMatch>
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web17/.php-fcgi-starter .php
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web17/.php-fcgi-starter .php3
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web17/.php-fcgi-starter .php4
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web17/.php-fcgi-starter .php5
|
||||
Options +ExecCGI
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
<Directory /var/www/clients/client4/web17/web>
|
||||
<FilesMatch "\.php[345]?$">
|
||||
SetHandler fcgid-script
|
||||
</FilesMatch>
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web17/.php-fcgi-starter .php
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web17/.php-fcgi-starter .php3
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web17/.php-fcgi-starter .php4
|
||||
FCGIWrapper /var/www/php-fcgi-scripts/web17/.php-fcgi-starter .php5
|
||||
Options +ExecCGI
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
|
||||
# add support for apache mpm_itk
|
||||
<IfModule mpm_itk_module>
|
||||
AssignUserId web17 client4
|
||||
</IfModule>
|
||||
|
||||
<IfModule mod_dav_fs.c>
|
||||
# Do not execute PHP files in webdav directory
|
||||
<Directory /var/www/clients/client4/web17/webdav>
|
||||
<ifModule mod_security2.c>
|
||||
SecRuleRemoveById 960015
|
||||
SecRuleRemoveById 960032
|
||||
</ifModule>
|
||||
<FilesMatch "\.ph(p3?|tml)$">
|
||||
SetHandler None
|
||||
</FilesMatch>
|
||||
</Directory>
|
||||
DavLockDB /var/www/clients/client4/web17/tmp/DavLock
|
||||
# DO NOT REMOVE THE COMMENTS!
|
||||
# IF YOU REMOVE THEM, WEBDAV WILL NOT WORK ANYMORE!
|
||||
# WEBDAV BEGIN
|
||||
# WEBDAV END
|
||||
</IfModule>
|
||||
|
||||
|
||||
</VirtualHost>
|
||||
@@ -0,0 +1,37 @@
|
||||
<VirtualHost *:80>
|
||||
ServerAdmin denver@ossguy.com
|
||||
ServerName c-beta.ossguy.com
|
||||
|
||||
Alias /robots.txt /home/denver/www/c-beta.ossguy.com/static/robots.txt
|
||||
Alias /favicon.ico /home/denver/www/c-beta.ossguy.com/static/favicon.ico
|
||||
|
||||
AliasMatch /(.*\.css) /home/denver/www/c-beta.ossguy.com/static/$1
|
||||
AliasMatch /(.*\.js) /home/denver/www/c-beta.ossguy.com/static/$1
|
||||
AliasMatch /(.*\.png) /home/denver/www/c-beta.ossguy.com/static/$1
|
||||
AliasMatch /(.*\.gif) /home/denver/www/c-beta.ossguy.com/static/$1
|
||||
AliasMatch /(.*\.jpg) /home/denver/www/c-beta.ossguy.com/static/$1
|
||||
|
||||
WSGIScriptAlias / /home/denver/www/c-beta.ossguy.com/django.wsgi
|
||||
WSGIDaemonProcess c-beta-ossguy user=www-data group=www-data home=/var/www processes=5 threads=10 maximum-requests=1000 umask=0007 display-name=c-beta-ossguy
|
||||
WSGIProcessGroup c-beta-ossguy
|
||||
WSGIApplicationGroup %{GLOBAL}
|
||||
|
||||
DocumentRoot /home/denver/www/c-beta.ossguy.com/static
|
||||
|
||||
<Directory /home/denver/www/c-beta.ossguy.com/static>
|
||||
Options -Indexes +FollowSymLinks -MultiViews
|
||||
Require all granted
|
||||
AllowOverride None
|
||||
</Directory>
|
||||
|
||||
<Directory /home/denver/www/c-beta.ossguy.com/static/source>
|
||||
Options +Indexes +FollowSymLinks -MultiViews
|
||||
Require all granted
|
||||
AllowOverride None
|
||||
</Directory>
|
||||
|
||||
# Custom log file locations
|
||||
LogLevel warn
|
||||
ErrorLog /tmp/error.log
|
||||
CustomLog /tmp/access.log combined
|
||||
</VirtualHost>
|
||||
@@ -0,0 +1,6 @@
|
||||
# Modules required to parse these conf files:
|
||||
ssl
|
||||
rewrite
|
||||
macro
|
||||
wsgi
|
||||
deflate
|
||||
@@ -0,0 +1,14 @@
|
||||
<VirtualHost *:80>
|
||||
ServerAdmin root@localhost
|
||||
ServerName anarcat.wiki.orangeseeds.org:80
|
||||
|
||||
|
||||
UserDir disabled
|
||||
|
||||
RewriteEngine On
|
||||
RewriteRule ^/(.*) http\:\/\/anarc\.at\/$1 [L,R,NE]
|
||||
|
||||
ErrorLog /var/log/apache2/1531error.log
|
||||
LogLevel warn
|
||||
CustomLog /var/log/apache2/1531access.log combined
|
||||
</VirtualHost>
|
||||
@@ -0,0 +1,116 @@
|
||||
#
|
||||
# Apache/PHP/Drupal settings:
|
||||
#
|
||||
|
||||
# Protect files and directories from prying eyes.
|
||||
<FilesMatch "\.(engine|inc|info|install|make|module|profile|test|po|sh|.*sql|theme|tpl(\.php)?|xtmpl|svn-base)$|^(code-style\.pl|Entries.*|Repository|Root|Tag|Template|all-wcprops|entries|format)$">
|
||||
Order allow,deny
|
||||
</FilesMatch>
|
||||
|
||||
# Don't show directory listings for URLs which map to a directory.
|
||||
Options -Indexes
|
||||
|
||||
# Follow symbolic links in this directory.
|
||||
Options +FollowSymLinks
|
||||
|
||||
# Make Drupal handle any 404 errors.
|
||||
ErrorDocument 404 /index.php
|
||||
|
||||
# Force simple error message for requests for non-existent favicon.ico.
|
||||
<Files favicon.ico>
|
||||
# There is no end quote below, for compatibility with Apache 1.3.
|
||||
ErrorDocument 404 "The requested file favicon.ico was not found.
|
||||
</Files>
|
||||
|
||||
# Set the default handler.
|
||||
DirectoryIndex index.php
|
||||
|
||||
# Override PHP settings. More in sites/default/settings.php
|
||||
# but the following cannot be changed at runtime.
|
||||
|
||||
# PHP 4, Apache 1.
|
||||
<IfModule mod_php4.c>
|
||||
php_value magic_quotes_gpc 0
|
||||
php_value register_globals 0
|
||||
php_value session.auto_start 0
|
||||
php_value mbstring.http_input pass
|
||||
php_value mbstring.http_output pass
|
||||
php_value mbstring.encoding_translation 0
|
||||
</IfModule>
|
||||
|
||||
# PHP 4, Apache 2.
|
||||
<IfModule sapi_apache2.c>
|
||||
php_value magic_quotes_gpc 0
|
||||
php_value register_globals 0
|
||||
php_value session.auto_start 0
|
||||
php_value mbstring.http_input pass
|
||||
php_value mbstring.http_output pass
|
||||
php_value mbstring.encoding_translation 0
|
||||
</IfModule>
|
||||
|
||||
# PHP 5, Apache 1 and 2.
|
||||
<IfModule mod_php5.c>
|
||||
php_value magic_quotes_gpc 0
|
||||
php_value register_globals 0
|
||||
php_value session.auto_start 0
|
||||
php_value mbstring.http_input pass
|
||||
php_value mbstring.http_output pass
|
||||
php_value mbstring.encoding_translation 0
|
||||
</IfModule>
|
||||
|
||||
# Requires mod_expires to be enabled.
|
||||
<IfModule mod_expires.c>
|
||||
# Enable expirations.
|
||||
ExpiresActive On
|
||||
|
||||
# Cache all files for 2 weeks after access (A).
|
||||
ExpiresDefault A1209600
|
||||
|
||||
<FilesMatch \.php$>
|
||||
# Do not allow PHP scripts to be cached unless they explicitly send cache
|
||||
# headers themselves. Otherwise all scripts would have to overwrite the
|
||||
# headers set by mod_expires if they want another caching behavior. This may
|
||||
# fail if an error occurs early in the bootstrap process, and it may cause
|
||||
# problems if a non-Drupal PHP file is installed in a subdirectory.
|
||||
ExpiresActive Off
|
||||
</FilesMatch>
|
||||
</IfModule>
|
||||
|
||||
# Various rewrite rules.
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine on
|
||||
|
||||
# If your site can be accessed both with and without the 'www.' prefix, you
|
||||
# can use one of the following settings to redirect users to your preferred
|
||||
# URL, either WITH or WITHOUT the 'www.' prefix. Choose ONLY one option:
|
||||
#
|
||||
# To redirect all users to access the site WITH the 'www.' prefix,
|
||||
# (http://example.com/... will be redirected to http://www.example.com/...)
|
||||
# adapt and uncomment the following:
|
||||
# RewriteCond %{HTTP_HOST} ^example\.com$ [NC]
|
||||
# RewriteRule ^(.*)$ http://www.example.com/$1 [L,R=301]
|
||||
#
|
||||
# To redirect all users to access the site WITHOUT the 'www.' prefix,
|
||||
# (http://www.example.com/... will be redirected to http://example.com/...)
|
||||
# uncomment and adapt the following:
|
||||
# RewriteCond %{HTTP_HOST} ^www\.example\.com$ [NC]
|
||||
# RewriteRule ^(.*)$ http://example.com/$1 [L,R=301]
|
||||
|
||||
# Modify the RewriteBase if you are using Drupal in a subdirectory or in a
|
||||
# VirtualDocumentRoot and the rewrite rules are not working properly.
|
||||
# For example if your site is at http://example.com/drupal uncomment and
|
||||
# modify the following line:
|
||||
# RewriteBase /drupal
|
||||
#
|
||||
# If your site is running in a VirtualDocumentRoot at http://example.com/,
|
||||
# uncomment the following line:
|
||||
# RewriteBase /
|
||||
|
||||
# Rewrite URLs of the form 'x' to the form 'index.php?q=x'.
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_URI} !=/favicon.ico
|
||||
RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]
|
||||
</IfModule>
|
||||
|
||||
# $Id$
|
||||
@@ -0,0 +1,149 @@
|
||||
#
|
||||
# Apache/PHP/Drupal settings:
|
||||
#
|
||||
|
||||
# Protect files and directories from prying eyes.
|
||||
<FilesMatch "\.(engine|inc|info|install|make|module|profile|test|po|sh|.*sql|theme|tpl(\.php)?|xtmpl)(~|\.sw[op]|\.bak|\.orig|\.save)?$|^(\..*|Entries.*|Repository|Root|Tag|Template)$|^#.*#$|\.php(~|\.sw[op]|\.bak|\.orig\.save)$">
|
||||
Order allow,deny
|
||||
</FilesMatch>
|
||||
|
||||
# Don't show directory listings for URLs which map to a directory.
|
||||
Options -Indexes
|
||||
|
||||
# Follow symbolic links in this directory.
|
||||
Options +FollowSymLinks
|
||||
|
||||
# Make Drupal handle any 404 errors.
|
||||
ErrorDocument 404 /index.php
|
||||
|
||||
# Set the default handler.
|
||||
DirectoryIndex index.php index.html index.htm
|
||||
|
||||
# Override PHP settings that cannot be changed at runtime. See
|
||||
# sites/default/default.settings.php and drupal_environment_initialize() in
|
||||
# includes/bootstrap.inc for settings that can be changed at runtime.
|
||||
|
||||
# PHP 5, Apache 1 and 2.
|
||||
<IfModule mod_php5.c>
|
||||
php_flag magic_quotes_gpc off
|
||||
php_flag magic_quotes_sybase off
|
||||
php_flag register_globals off
|
||||
php_flag session.auto_start off
|
||||
php_value mbstring.http_input pass
|
||||
php_value mbstring.http_output pass
|
||||
php_flag mbstring.encoding_translation off
|
||||
</IfModule>
|
||||
|
||||
# Requires mod_expires to be enabled.
|
||||
<IfModule mod_expires.c>
|
||||
# Enable expirations.
|
||||
ExpiresActive On
|
||||
|
||||
# Cache all files for 2 weeks after access (A).
|
||||
ExpiresDefault A1209600
|
||||
|
||||
<FilesMatch \.php$>
|
||||
# Do not allow PHP scripts to be cached unless they explicitly send cache
|
||||
# headers themselves. Otherwise all scripts would have to overwrite the
|
||||
# headers set by mod_expires if they want another caching behavior. This may
|
||||
# fail if an error occurs early in the bootstrap process, and it may cause
|
||||
# problems if a non-Drupal PHP file is installed in a subdirectory.
|
||||
ExpiresActive Off
|
||||
</FilesMatch>
|
||||
</IfModule>
|
||||
|
||||
# Various rewrite rules.
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine on
|
||||
|
||||
# Set "protossl" to "s" if we were accessed via https://. This is used later
|
||||
# if you enable "www." stripping or enforcement, in order to ensure that
|
||||
# you don't bounce between http and https.
|
||||
RewriteRule ^ - [E=protossl]
|
||||
RewriteCond %{HTTPS} on
|
||||
RewriteRule ^ - [E=protossl:s]
|
||||
|
||||
# Make sure Authorization HTTP header is available to PHP
|
||||
# even when running as CGI or FastCGI.
|
||||
RewriteRule ^ - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
||||
|
||||
# Block access to "hidden" directories whose names begin with a period. This
|
||||
# includes directories used by version control systems such as Subversion or
|
||||
# Git to store control files. Files whose names begin with a period, as well
|
||||
# as the control files used by CVS, are protected by the FilesMatch directive
|
||||
# above.
|
||||
#
|
||||
# NOTE: This only works when mod_rewrite is loaded. Without mod_rewrite, it is
|
||||
# not possible to block access to entire directories from .htaccess, because
|
||||
# <DirectoryMatch> is not allowed here.
|
||||
#
|
||||
# If you do not have mod_rewrite installed, you should remove these
|
||||
# directories from your webroot or otherwise protect them from being
|
||||
# downloaded.
|
||||
RewriteRule "(^|/)\." - [F]
|
||||
|
||||
# If your site can be accessed both with and without the 'www.' prefix, you
|
||||
# can use one of the following settings to redirect users to your preferred
|
||||
# URL, either WITH or WITHOUT the 'www.' prefix. Choose ONLY one option:
|
||||
#
|
||||
# To redirect all users to access the site WITH the 'www.' prefix,
|
||||
# (http://example.com/... will be redirected to http://www.example.com/...)
|
||||
# uncomment the following:
|
||||
# RewriteCond %{HTTP_HOST} .
|
||||
# RewriteCond %{HTTP_HOST} !^www\. [NC]
|
||||
# RewriteRule ^ http%{ENV:protossl}://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
|
||||
#
|
||||
# To redirect all users to access the site WITHOUT the 'www.' prefix,
|
||||
# (http://www.example.com/... will be redirected to http://example.com/...)
|
||||
# uncomment the following:
|
||||
# RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
|
||||
# RewriteRule ^ http%{ENV:protossl}://%1%{REQUEST_URI} [L,R=301]
|
||||
|
||||
# Modify the RewriteBase if you are using Drupal in a subdirectory or in a
|
||||
# VirtualDocumentRoot and the rewrite rules are not working properly.
|
||||
# For example if your site is at http://example.com/drupal uncomment and
|
||||
# modify the following line:
|
||||
# RewriteBase /drupal
|
||||
#
|
||||
# If your site is running in a VirtualDocumentRoot at http://example.com/,
|
||||
# uncomment the following line:
|
||||
# RewriteBase /
|
||||
|
||||
# Pass all requests not referring directly to files in the filesystem to
|
||||
# index.php. Clean URLs are handled in drupal_environment_initialize().
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_URI} !=/favicon.ico
|
||||
RewriteRule ^ index.php [L]
|
||||
|
||||
# Rules to correctly serve gzip compressed CSS and JS files.
|
||||
# Requires both mod_rewrite and mod_headers to be enabled.
|
||||
<IfModule mod_headers.c>
|
||||
# Serve gzip compressed CSS files if they exist and the client accepts gzip.
|
||||
RewriteCond %{HTTP:Accept-encoding} gzip
|
||||
RewriteCond %{REQUEST_FILENAME}\.gz -s
|
||||
RewriteRule ^(.*)\.css $1\.css\.gz [QSA]
|
||||
|
||||
# Serve gzip compressed JS files if they exist and the client accepts gzip.
|
||||
RewriteCond %{HTTP:Accept-encoding} gzip
|
||||
RewriteCond %{REQUEST_FILENAME}\.gz -s
|
||||
RewriteRule ^(.*)\.js $1\.js\.gz [QSA]
|
||||
|
||||
# Serve correct content types, and prevent mod_deflate double gzip.
|
||||
RewriteRule .css.gz$ - [T=text/css,E=no-gzip:1]
|
||||
RewriteRule .js.gz$ - [T=text/javascript,E=no-gzip:1]
|
||||
|
||||
<FilesMatch "(\.js\.gz|\.css\.gz)$">
|
||||
# Serve correct encoding type.
|
||||
Header set Content-Encoding gzip
|
||||
# Force proxies to cache gzipped & non-gzipped css/js files separately.
|
||||
Header append Vary Accept-Encoding
|
||||
</FilesMatch>
|
||||
</IfModule>
|
||||
</IfModule>
|
||||
|
||||
# Add headers to all responses.
|
||||
<IfModule mod_headers.c>
|
||||
# Disable content sniffing, since it's an attack vector.
|
||||
Header always set X-Content-Type-Options nosniff
|
||||
</IfModule>
|
||||
@@ -0,0 +1,36 @@
|
||||
<VirtualHost *:80>
|
||||
# The ServerName directive sets the request scheme, hostname and port that
|
||||
# the server uses to identify itself. This is used when creating
|
||||
# redirection URLs. In the context of virtual hosts, the ServerName
|
||||
# specifies what hostname must appear in the request's Host: header to
|
||||
# match this virtual host. For the default virtual host (this file) this
|
||||
# value is not decisive as it is used as a last resort host regardless.
|
||||
# However, you must set it for any further virtual host explicitly.
|
||||
ServerName www.example.com
|
||||
ServerAlias example.com
|
||||
SetOutputFilter DEFLATE
|
||||
# Do not attempt to compress the following extensions
|
||||
SetEnvIfNoCase Request_URI \
|
||||
\.(?:gif|jpe?g|png|swf|flv|zip|gz|tar|mp3|mp4|m4v)$ no-gzip dont-vary
|
||||
|
||||
ServerAdmin webmaster@localhost
|
||||
DocumentRoot /var/www/proof
|
||||
|
||||
# Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
|
||||
# error, crit, alert, emerg.
|
||||
# It is also possible to configure the loglevel for particular
|
||||
# modules, e.g.
|
||||
#LogLevel info ssl:warn
|
||||
|
||||
ErrorLog ${APACHE_LOG_DIR}/error.log
|
||||
CustomLog ${APACHE_LOG_DIR}/access.log combined
|
||||
|
||||
# For most configuration files from conf-available/, which are
|
||||
# enabled or disabled at a global level, it is possible to
|
||||
# include a line for only one particular virtual host. For example the
|
||||
# following line enables the CGI configuration for this host only
|
||||
# after it has been globally disabled with "a2disconf".
|
||||
#Include conf-available/serve-cgi-bin.conf
|
||||
</VirtualHost>
|
||||
|
||||
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
|
||||
@@ -0,0 +1,136 @@
|
||||
<VirtualHost *:443>
|
||||
ServerName example.com
|
||||
ServerAlias www.example.com
|
||||
ServerAdmin webmaster@localhost
|
||||
|
||||
DocumentRoot /var/www/html
|
||||
|
||||
# Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
|
||||
# error, crit, alert, emerg.
|
||||
# It is also possible to configure the loglevel for particular
|
||||
# modules, e.g.
|
||||
#LogLevel info ssl:warn
|
||||
|
||||
ErrorLog ${APACHE_LOG_DIR}/error.log
|
||||
CustomLog ${APACHE_LOG_DIR}/access.log combined
|
||||
|
||||
# For most configuration files from conf-available/, which are
|
||||
# enabled or disabled at a global level, it is possible to
|
||||
# include a line for only one particular virtual host. For example the
|
||||
# following line enables the CGI configuration for this host only
|
||||
# after it has been globally disabled with "a2disconf".
|
||||
#Include conf-available/serve-cgi-bin.conf
|
||||
|
||||
# SSL Engine Switch:
|
||||
# Enable/Disable SSL for this virtual host.
|
||||
SSLEngine on
|
||||
|
||||
# A self-signed (snakeoil) certificate can be created by installing
|
||||
# the ssl-cert package. See
|
||||
# /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/ssl/certs/ssl-cert-snakeoil.pem
|
||||
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
|
||||
|
||||
# Server Certificate Chain:
|
||||
# Point SSLCertificateChainFile at a file containing the
|
||||
# concatenation of PEM encoded CA certificates which form the
|
||||
# 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.
|
||||
#SSLCertificateChainFile /etc/apache2/ssl.crt/server-ca.crt
|
||||
|
||||
# Certificate Authority (CA):
|
||||
# Set the CA certificate verification path where to find CA
|
||||
# certificates for client authentication or alternatively one
|
||||
# huge file containing all of them (file must be PEM encoded)
|
||||
# Note: Inside SSLCACertificatePath you need hash symlinks
|
||||
# to point to the certificate files. Use the provided
|
||||
# Makefile to update the hash symlinks after changes.
|
||||
#SSLCACertificatePath /etc/ssl/certs/
|
||||
#SSLCACertificateFile /etc/apache2/ssl.crt/ca-bundle.crt
|
||||
|
||||
# Certificate Revocation Lists (CRL):
|
||||
# Set the CA revocation path where to find CA CRLs for client
|
||||
# authentication or alternatively one huge file containing all
|
||||
# of them (file must be PEM encoded)
|
||||
# Note: Inside SSLCARevocationPath you need hash symlinks
|
||||
# to point to the certificate files. Use the provided
|
||||
# Makefile to update the hash symlinks after changes.
|
||||
#SSLCARevocationPath /etc/apache2/ssl.crl/
|
||||
#SSLCARevocationFile /etc/apache2/ssl.crl/ca-bundle.crl
|
||||
|
||||
# Client Authentication (Type):
|
||||
# Client certificate verification type and depth. Types are
|
||||
# none, optional, require and optional_no_ca. Depth is a
|
||||
# number which specifies how deeply to verify the certificate
|
||||
# issuer chain before deciding the certificate is not valid.
|
||||
#SSLVerifyClient require
|
||||
#SSLVerifyDepth 10
|
||||
|
||||
# SSL Engine Options:
|
||||
# Set various options for the SSL engine.
|
||||
# o FakeBasicAuth:
|
||||
# Translate the client X.509 into a Basic Authorisation. This means that
|
||||
# the standard Auth/DBMAuth methods can be used for access control. The
|
||||
# user name is the `one line' version of the client's X.509 certificate.
|
||||
# Note that no password is obtained from the user. Every entry in the user
|
||||
# file needs this password: `xxj31ZMTZzkVA'.
|
||||
# o ExportCertData:
|
||||
# This exports two additional environment variables: SSL_CLIENT_CERT and
|
||||
# SSL_SERVER_CERT. These contain the PEM-encoded certificates of the
|
||||
# server (always existing) and the client (only existing when client
|
||||
# authentication is used). This can be used to import the certificates
|
||||
# into CGI scripts.
|
||||
# o StdEnvVars:
|
||||
# This exports the standard SSL/TLS related `SSL_*' environment variables.
|
||||
# Per default this exportation is switched off for performance reasons,
|
||||
# because the extraction step is an expensive operation and is usually
|
||||
# useless for serving static content. So one usually enables the
|
||||
# exportation for CGI and SSI requests only.
|
||||
# o OptRenegotiate:
|
||||
# This enables optimized SSL connection renegotiation handling when SSL
|
||||
# directives are used in per-directory context.
|
||||
#SSLOptions +FakeBasicAuth +ExportCertData +StrictRequire
|
||||
<FilesMatch "\.(cgi|shtml|phtml|php)$">
|
||||
SSLOptions +StdEnvVars
|
||||
</FilesMatch>
|
||||
<Directory /usr/lib/cgi-bin>
|
||||
SSLOptions +StdEnvVars
|
||||
</Directory>
|
||||
|
||||
# SSL Protocol Adjustments:
|
||||
# The safe and default but still SSL/TLS standard compliant shutdown
|
||||
# approach is that mod_ssl sends the close notify alert but doesn't wait for
|
||||
# the close notify alert from client. When you need a different shutdown
|
||||
# approach you can use one of the following variables:
|
||||
# o ssl-unclean-shutdown:
|
||||
# This forces an unclean shutdown when the connection is closed, i.e. no
|
||||
# SSL close notify alert is send or allowed to received. This violates
|
||||
# the SSL/TLS standard but is needed for some brain-dead browsers. Use
|
||||
# this when you receive I/O errors because of the standard approach where
|
||||
# mod_ssl sends the close notify alert.
|
||||
# o ssl-accurate-shutdown:
|
||||
# This forces an accurate shutdown when the connection is closed, i.e. a
|
||||
# SSL close notify alert is send and mod_ssl waits for the close notify
|
||||
# alert of the client. This is 100% SSL/TLS standard compliant, but in
|
||||
# practice often causes hanging connections with brain-dead browsers. Use
|
||||
# this only for browsers where you know that their SSL implementation
|
||||
# works correctly.
|
||||
# Notice: Most problems of broken clients are also related to the HTTP
|
||||
# keep-alive facility, so you usually additionally want to disable
|
||||
# keep-alive for those clients, too. Use variable "nokeepalive" for this.
|
||||
# Similarly, one has to force some clients to use HTTP/1.0 to workaround
|
||||
# their broken HTTP/1.1 implementation. Use variables "downgrade-1.0" and
|
||||
# "force-response-1.0" for this.
|
||||
BrowserMatch "MSIE [2-6]" \
|
||||
nokeepalive ssl-unclean-shutdown \
|
||||
downgrade-1.0 force-response-1.0
|
||||
# MSIE 7 and newer should be able to use keepalive
|
||||
BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown
|
||||
|
||||
</VirtualHost>
|
||||
|
||||
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
|
||||
@@ -0,0 +1,32 @@
|
||||
<VirtualHost *:80>
|
||||
# The ServerName directive sets the request scheme, hostname and port that
|
||||
# the server uses to identify itself. This is used when creating
|
||||
# redirection URLs. In the context of virtual hosts, the ServerName
|
||||
# specifies what hostname must appear in the request's Host: header to
|
||||
# match this virtual host. For the default virtual host (this file) this
|
||||
# value is not decisive as it is used as a last resort host regardless.
|
||||
# However, you must set it for any further virtual host explicitly.
|
||||
ServerName www.example.com
|
||||
ServerAlias example.com
|
||||
|
||||
ServerAdmin webmaster@localhost
|
||||
DocumentRoot /var/www/html
|
||||
|
||||
# Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
|
||||
# error, crit, alert, emerg.
|
||||
# It is also possible to configure the loglevel for particular
|
||||
# modules, e.g.
|
||||
#LogLevel info ssl:warn
|
||||
|
||||
ErrorLog ${APACHE_LOG_DIR}/error.log
|
||||
CustomLog ${APACHE_LOG_DIR}/access.log combined
|
||||
|
||||
# For most configuration files from conf-available/, which are
|
||||
# enabled or disabled at a global level, it is possible to
|
||||
# include a line for only one particular virtual host. For example the
|
||||
# following line enables the CGI configuration for this host only
|
||||
# after it has been globally disabled with "a2disconf".
|
||||
#Include conf-available/serve-cgi-bin.conf
|
||||
</VirtualHost>
|
||||
|
||||
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
|
||||
@@ -0,0 +1,222 @@
|
||||
# This is the main Apache server configuration file. It contains the
|
||||
# configuration directives that give the server its instructions.
|
||||
# See http://httpd.apache.org/docs/2.4/ for detailed information about
|
||||
# the directives and /usr/share/doc/apache2/README.Debian about Debian specific
|
||||
# hints.
|
||||
#
|
||||
#
|
||||
# Summary of how the Apache 2 configuration works in Debian:
|
||||
# The Apache 2 web server configuration in Debian is quite different to
|
||||
# upstream's suggested way to configure the web server. This is because Debian's
|
||||
# default Apache2 installation attempts to make adding and removing modules,
|
||||
# virtual hosts, and extra configuration directives as flexible as possible, in
|
||||
# order to make automating the changes and administering the server as easy as
|
||||
# possible.
|
||||
|
||||
# It is split into several files forming the configuration hierarchy outlined
|
||||
# below, all located in the /etc/apache2/ directory:
|
||||
#
|
||||
# /etc/apache2/
|
||||
# |-- apache2.conf
|
||||
# | `-- ports.conf
|
||||
# |-- mods-enabled
|
||||
# | |-- *.load
|
||||
# | `-- *.conf
|
||||
# |-- conf-enabled
|
||||
# | `-- *.conf
|
||||
# `-- sites-enabled
|
||||
# `-- *.conf
|
||||
#
|
||||
#
|
||||
# * apache2.conf is the main configuration file (this file). It puts the pieces
|
||||
# together by including all remaining configuration files when starting up the
|
||||
# web server.
|
||||
#
|
||||
# * ports.conf is always included from the main configuration file. It is
|
||||
# supposed to determine listening ports for incoming connections which can be
|
||||
# customized anytime.
|
||||
#
|
||||
# * Configuration files in the mods-enabled/, conf-enabled/ and sites-enabled/
|
||||
# directories contain particular configuration snippets which manage modules,
|
||||
# global configuration fragments, or virtual host configurations,
|
||||
# respectively.
|
||||
#
|
||||
# They are activated by symlinking available configuration files from their
|
||||
# respective *-available/ counterparts. These should be managed by using our
|
||||
# helpers a2enmod/a2dismod, a2ensite/a2dissite and a2enconf/a2disconf. See
|
||||
# their respective man pages for detailed information.
|
||||
#
|
||||
# * The binary is called apache2. Due to the use of environment variables, in
|
||||
# the default configuration, apache2 needs to be started/stopped with
|
||||
# /etc/init.d/apache2 or apache2ctl. Calling /usr/bin/apache2 directly will not
|
||||
# work with the default configuration.
|
||||
|
||||
|
||||
# Global configuration
|
||||
#
|
||||
|
||||
#
|
||||
# ServerRoot: The top of the directory tree under which the server's
|
||||
# configuration, error, and log files are kept.
|
||||
#
|
||||
# NOTE! If you intend to place this on an NFS (or otherwise network)
|
||||
# mounted filesystem then please read the Mutex documentation (available
|
||||
# at <URL:http://httpd.apache.org/docs/2.4/mod/core.html#mutex>);
|
||||
# you will save yourself a lot of trouble.
|
||||
#
|
||||
# Do NOT add a slash at the end of the directory path.
|
||||
#
|
||||
#ServerRoot "/etc/apache2"
|
||||
|
||||
#
|
||||
# The accept serialization lock file MUST BE STORED ON A LOCAL DISK.
|
||||
#
|
||||
Mutex file:${APACHE_LOCK_DIR} default
|
||||
|
||||
#
|
||||
# PidFile: The file in which the server should record its process
|
||||
# identification number when it starts.
|
||||
# This needs to be set in /etc/apache2/envvars
|
||||
#
|
||||
PidFile ${APACHE_PID_FILE}
|
||||
|
||||
#
|
||||
# Timeout: The number of seconds before receives and sends time out.
|
||||
#
|
||||
Timeout 300
|
||||
|
||||
#
|
||||
# KeepAlive: Whether or not to allow persistent connections (more than
|
||||
# one request per connection). Set to "Off" to deactivate.
|
||||
#
|
||||
KeepAlive On
|
||||
|
||||
#
|
||||
# MaxKeepAliveRequests: The maximum number of requests to allow
|
||||
# during a persistent connection. Set to 0 to allow an unlimited amount.
|
||||
# We recommend you leave this number high, for maximum performance.
|
||||
#
|
||||
MaxKeepAliveRequests 100
|
||||
|
||||
#
|
||||
# KeepAliveTimeout: Number of seconds to wait for the next request from the
|
||||
# same client on the same connection.
|
||||
#
|
||||
KeepAliveTimeout 5
|
||||
|
||||
|
||||
# These need to be set in /etc/apache2/envvars
|
||||
User ${APACHE_RUN_USER}
|
||||
Group ${APACHE_RUN_GROUP}
|
||||
|
||||
#
|
||||
# HostnameLookups: Log the names of clients or just their IP addresses
|
||||
# e.g., www.apache.org (on) or 204.62.129.132 (off).
|
||||
# The default is off because it'd be overall better for the net if people
|
||||
# had to knowingly turn this feature on, since enabling it means that
|
||||
# each client request will result in AT LEAST one lookup request to the
|
||||
# nameserver.
|
||||
#
|
||||
HostnameLookups Off
|
||||
|
||||
# ErrorLog: The location of the error log file.
|
||||
# If you do not specify an ErrorLog directive within a <VirtualHost>
|
||||
# container, error messages relating to that virtual host will be
|
||||
# logged here. If you *do* define an error logfile for a <VirtualHost>
|
||||
# container, that host's errors will be logged there and not here.
|
||||
#
|
||||
ErrorLog ${APACHE_LOG_DIR}/error.log
|
||||
|
||||
#
|
||||
# LogLevel: Control the severity of messages logged to the error_log.
|
||||
# Available values: trace8, ..., trace1, debug, info, notice, warn,
|
||||
# error, crit, alert, emerg.
|
||||
# It is also possible to configure the log level for particular modules, e.g.
|
||||
# "LogLevel info ssl:warn"
|
||||
#
|
||||
LogLevel warn
|
||||
|
||||
# Include module configuration:
|
||||
IncludeOptional mods-enabled/*.load
|
||||
IncludeOptional mods-enabled/*.conf
|
||||
|
||||
# Include list of ports to listen on
|
||||
Include ports.conf
|
||||
|
||||
|
||||
# Sets the default security model of the Apache2 HTTPD server. It does
|
||||
# not allow access to the root filesystem outside of /usr/share and /var/www.
|
||||
# The former is used by web applications packaged in Debian,
|
||||
# the latter may be used for local directories served by the web server. If
|
||||
# your system is serving content from a sub-directory in /srv you must allow
|
||||
# access here, or in any related virtual host.
|
||||
<Directory />
|
||||
Options FollowSymLinks
|
||||
AllowOverride None
|
||||
Require all denied
|
||||
</Directory>
|
||||
|
||||
<Directory /usr/share>
|
||||
AllowOverride None
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
<Directory /var/www/>
|
||||
Options Indexes FollowSymLinks
|
||||
AllowOverride None
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
#<Directory /srv/>
|
||||
# Options Indexes FollowSymLinks
|
||||
# AllowOverride None
|
||||
# Require all granted
|
||||
#</Directory>
|
||||
|
||||
# AccessFileName: The name of the file to look for in each directory
|
||||
# for additional configuration directives. See also the AllowOverride
|
||||
# directive.
|
||||
#
|
||||
AccessFileName .htaccess
|
||||
|
||||
#
|
||||
# The following lines prevent .htaccess and .htpasswd files from being
|
||||
# viewed by Web clients.
|
||||
#
|
||||
<FilesMatch "^\.ht">
|
||||
Require all denied
|
||||
</FilesMatch>
|
||||
|
||||
|
||||
#
|
||||
# The following directives define some format nicknames for use with
|
||||
# a CustomLog directive.
|
||||
#
|
||||
# These deviate from the Common Log Format definitions in that they use %O
|
||||
# (the actual bytes sent including headers) instead of %b (the size of the
|
||||
# requested file), because the latter makes it impossible to detect partial
|
||||
# requests.
|
||||
#
|
||||
# Note that the use of %{X-Forwarded-For}i instead of %h is not recommended.
|
||||
# Use mod_remoteip instead.
|
||||
#
|
||||
#LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined
|
||||
LogFormat "%t \"%r\" %>s %O \"%{User-Agent}i\"" vhost_combined
|
||||
|
||||
#LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined
|
||||
#LogFormat "%h %l %u %t \"%r\" %>s %O" common
|
||||
LogFormat "- %t \"%r\" %>s %b" noip
|
||||
|
||||
LogFormat "%{Referer}i -> %U" referer
|
||||
LogFormat "%{User-agent}i" agent
|
||||
|
||||
# Include of directories ignores editors' and dpkg's backup files,
|
||||
# see README.Debian for details.
|
||||
|
||||
# Include generic snippets of statements
|
||||
IncludeOptional conf-enabled/*.conf
|
||||
|
||||
# Include the virtual host configurations:
|
||||
#IncludeOptional sites-enabled/*.conf
|
||||
|
||||
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
|
||||
@@ -0,0 +1,67 @@
|
||||
#LoadModule ssl_module modules/mod_ssl.so
|
||||
|
||||
Listen 443
|
||||
<VirtualHost *:443>
|
||||
# The ServerName directive sets the request scheme, hostname and port that
|
||||
# the server uses to identify itself. This is used when creating
|
||||
# redirection URLs. In the context of virtual hosts, the ServerName
|
||||
# specifies what hostname must appear in the request's Host: header to
|
||||
# match this virtual host. For the default virtual host (this file) this
|
||||
# value is not decisive as it is used as a last resort host regardless.
|
||||
# However, you must set it for any further virtual host explicitly.
|
||||
ServerName www.eiserneketten.de
|
||||
|
||||
SSLEngine on
|
||||
ServerAdmin webmaster@localhost
|
||||
DocumentRoot /var/www/html
|
||||
|
||||
# Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
|
||||
# error, crit, alert, emerg.
|
||||
# It is also possible to configure the loglevel for particular
|
||||
# modules, e.g.
|
||||
#LogLevel info ssl:warn
|
||||
|
||||
ErrorLog ${APACHE_LOG_DIR}/error.log
|
||||
CustomLog ${APACHE_LOG_DIR}/access.log noip
|
||||
|
||||
# For most configuration files from conf-available/, which are
|
||||
# enabled or disabled at a global level, it is possible to
|
||||
# include a line for only one particular virtual host. For example the
|
||||
# following line enables the CGI configuration for this host only
|
||||
# after it has been globally disabled with "a2disconf".
|
||||
#Include conf-available/serve-cgi-bin.conf
|
||||
<Directory />
|
||||
Options FollowSymLinks
|
||||
AllowOverride None
|
||||
Order Deny,Allow
|
||||
#Deny from All
|
||||
</Directory>
|
||||
|
||||
Alias / /eiserneketten/pages/eiserneketten.html
|
||||
SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
|
||||
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
|
||||
SSLCertificateChainFile /etc/ssl/certs/ssl-cert-snakeoil.pem
|
||||
Include /etc/letsencrypt/options-ssl-apache.conf
|
||||
|
||||
</VirtualHost>
|
||||
|
||||
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
|
||||
|
||||
#
|
||||
# Directives to allow use of AWStats as a CGI
|
||||
#
|
||||
Alias /awstatsclasses "/usr/local/awstats/wwwroot/classes/"
|
||||
Alias /awstatscss "/usr/local/awstats/wwwroot/css/"
|
||||
Alias /awstatsicons "/usr/local/awstats/wwwroot/icon/"
|
||||
ScriptAlias /awstats/ "/usr/local/awstats/wwwroot/cgi-bin/"
|
||||
|
||||
#
|
||||
# This is to permit URL access to scripts/files in AWStats directory.
|
||||
#
|
||||
<Directory "/usr/local/awstats/wwwroot">
|
||||
Options None
|
||||
AllowOverride None
|
||||
Order allow,deny
|
||||
Allow from all
|
||||
</Directory>
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<VirtualHost *:80>
|
||||
|
||||
WSGIDaemonProcess _graphite processes=5 threads=5 display-name='%{GROUP}' inactivity-timeout=120 user=www-data group=www-data
|
||||
WSGIProcessGroup _graphite
|
||||
WSGIImportScript /usr/share/graphite-web/graphite.wsgi process-group=_graphite application-group=%{GLOBAL}
|
||||
WSGIScriptAlias / /usr/share/graphite-web/graphite.wsgi
|
||||
|
||||
Alias /content/ /usr/share/graphite-web/static/
|
||||
<Location "/content/">
|
||||
SetHandler None
|
||||
</Location>
|
||||
|
||||
ErrorLog ${APACHE_LOG_DIR}/graphite-web_error.log
|
||||
|
||||
# Possible values include: debug, info, notice, warn, error, crit,
|
||||
# alert, emerg.
|
||||
LogLevel warn
|
||||
|
||||
CustomLog ${APACHE_LOG_DIR}/graphite-web_access.log combined
|
||||
|
||||
</VirtualHost>
|
||||
@@ -0,0 +1,52 @@
|
||||
<VirtualHost *:443>
|
||||
ServerAdmin webmaster@localhost
|
||||
ServerAlias www.example.com
|
||||
ServerName example.com
|
||||
DocumentRoot /var/www/example.com/www/
|
||||
SSLEngine on
|
||||
|
||||
SSLProtocol all -SSLv2 -SSLv3
|
||||
SSLCipherSuite "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRS$
|
||||
SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
|
||||
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
|
||||
|
||||
<Directory />
|
||||
Options FollowSymLinks
|
||||
AllowOverride All
|
||||
</Directory>
|
||||
<Directory /var/www/example.com/www>
|
||||
Options Indexes FollowSymLinks MultiViews
|
||||
AllowOverride All
|
||||
Order allow,deny
|
||||
allow from all
|
||||
# This directive allows us to have apache2's default start page
|
||||
# in /apache2-default/, but still have / go to the right place
|
||||
</Directory>
|
||||
|
||||
ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
|
||||
<Directory "/usr/lib/cgi-bin">
|
||||
AllowOverride None
|
||||
Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
|
||||
Order allow,deny
|
||||
Allow from all
|
||||
</Directory>
|
||||
|
||||
ErrorLog /var/log/apache2/error.log
|
||||
|
||||
# Possible values include: debug, info, notice, warn, error, crit,
|
||||
# alert, emerg.
|
||||
LogLevel warn
|
||||
|
||||
CustomLog /var/log/apache2/access.log combined
|
||||
ServerSignature On
|
||||
|
||||
Alias /apache_doc/ "/usr/share/doc/"
|
||||
<Directory "/usr/share/doc/">
|
||||
Options Indexes MultiViews FollowSymLinks
|
||||
AllowOverride None
|
||||
Order deny,allow
|
||||
Deny from all
|
||||
Allow from 127.0.0.0/255.0.0.0 ::1/128
|
||||
</Directory>
|
||||
|
||||
</VirtualHost>
|
||||
@@ -0,0 +1,33 @@
|
||||
<Macro Vhost $host $port $dir>
|
||||
<VirtualHost *:$port>
|
||||
# The ServerName directive sets the request scheme, hostname and port that
|
||||
# the server uses to identify itself. This is used when creating
|
||||
# redirection URLs. In the context of virtual hosts, the ServerName
|
||||
# specifies what hostname must appear in the request's Host: header to
|
||||
# match this virtual host. For the default virtual host (this file) this
|
||||
# value is not decisive as it is used as a last resort host regardless.
|
||||
# However, you must set it for any further virtual host explicitly.
|
||||
ServerName $host
|
||||
|
||||
ServerAdmin webmaster@localhost
|
||||
DocumentRoot $dir
|
||||
|
||||
# Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
|
||||
# error, crit, alert, emerg.
|
||||
# It is also possible to configure the loglevel for particular
|
||||
# modules, e.g.
|
||||
#LogLevel info ssl:warn
|
||||
|
||||
ErrorLog ${APACHE_LOG_DIR}/error.log
|
||||
CustomLog ${APACHE_LOG_DIR}/access.log combined
|
||||
|
||||
# For most configuration files from conf-available/, which are
|
||||
# enabled or disabled at a global level, it is possible to
|
||||
# include a line for only one particular virtual host. For example the
|
||||
# following line enables the CGI configuration for this host only
|
||||
# after it has been globally disabled with "a2disconf".
|
||||
#Include conf-available/serve-cgi-bin.conf
|
||||
</VirtualHost>
|
||||
</Macro>
|
||||
Use Vhost goxogle.com 80 /var/www/goxogle/
|
||||
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
|
||||
@@ -0,0 +1,13 @@
|
||||
Alias /owncloud /usr/share/owncloud
|
||||
|
||||
<Directory /usr/share/owncloud/>
|
||||
Options +FollowSymLinks
|
||||
AllowOverride All
|
||||
<IfVersion < 2.3>
|
||||
order allow,deny
|
||||
allow from all
|
||||
</IfVersion>
|
||||
<IfVersion >= 2.3>
|
||||
Require all granted
|
||||
</IfVersion>
|
||||
</Directory>
|
||||
@@ -0,0 +1,7 @@
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
RewriteCond %{REQUEST_URI} ^.*(,|;|:|<|>|">|"<|/|\\\.\.\\).* [NC,OR]
|
||||
RewriteCond %{REQUEST_URI} ^.*(\=|\@|\[|\]|\^|\`|\{|\}|\~).* [NC,OR]
|
||||
RewriteCond %{REQUEST_URI} ^.*(\'|%0A|%0D|%27|%3C|%3E|%00).* [NC]
|
||||
RewriteRule ^(.*)$ - [F,L]
|
||||
</IfModule>
|
||||
@@ -0,0 +1,61 @@
|
||||
# Those aliases do not work properly with several hosts on your apache server
|
||||
# Uncomment them to use it or adapt them to your configuration
|
||||
# Alias /roundcube/program/js/tiny_mce/ /usr/share/tinymce/www/
|
||||
# Alias /roundcube /var/lib/roundcube
|
||||
|
||||
# Access to tinymce files
|
||||
<Directory "/usr/share/tinymce/www/">
|
||||
Options Indexes MultiViews FollowSymLinks
|
||||
AllowOverride None
|
||||
<IfVersion >= 2.3>
|
||||
Require all granted
|
||||
</IfVersion>
|
||||
<IfVersion < 2.3>
|
||||
Order allow,deny
|
||||
Allow from all
|
||||
</IfVersion>
|
||||
</Directory>
|
||||
|
||||
<Directory /var/lib/roundcube/>
|
||||
Options +FollowSymLinks
|
||||
# This is needed to parse /var/lib/roundcube/.htaccess. See its
|
||||
# content before setting AllowOverride to None.
|
||||
AllowOverride All
|
||||
<IfVersion >= 2.3>
|
||||
Require all granted
|
||||
</IfVersion>
|
||||
<IfVersion < 2.3>
|
||||
Order allow,deny
|
||||
Allow from all
|
||||
</IfVersion>
|
||||
</Directory>
|
||||
|
||||
# Protecting basic directories:
|
||||
<Directory /var/lib/roundcube/config>
|
||||
Options -FollowSymLinks
|
||||
AllowOverride None
|
||||
</Directory>
|
||||
|
||||
<Directory /var/lib/roundcube/temp>
|
||||
Options -FollowSymLinks
|
||||
AllowOverride None
|
||||
<IfVersion >= 2.3>
|
||||
Require all denied
|
||||
</IfVersion>
|
||||
<IfVersion < 2.3>
|
||||
Order allow,deny
|
||||
Deny from all
|
||||
</IfVersion>
|
||||
</Directory>
|
||||
|
||||
<Directory /var/lib/roundcube/logs>
|
||||
Options -FollowSymLinks
|
||||
AllowOverride None
|
||||
<IfVersion >= 2.3>
|
||||
Require all denied
|
||||
</IfVersion>
|
||||
<IfVersion < 2.3>
|
||||
Order allow,deny
|
||||
Deny from all
|
||||
</IfVersion>
|
||||
</Directory>
|
||||
@@ -0,0 +1,44 @@
|
||||
<VirtualHost *:80>
|
||||
ServerName semacode.com
|
||||
ServerAlias www.semacode.com
|
||||
DocumentRoot /tmp/
|
||||
TransferLog /tmp/access
|
||||
ErrorLog /tmp/error
|
||||
Redirect /posts/rss http://semacode.com/feed
|
||||
Redirect permanent /weblog http://semacode.com/blog
|
||||
|
||||
#ProxyPreserveHost On
|
||||
# ProxyPass /past http://old.semacode.com
|
||||
#ProxyPassReverse /past http://old.semacode.com
|
||||
#<proxy>
|
||||
# Order allow,deny
|
||||
#Allow from all
|
||||
#</proxy>
|
||||
|
||||
Redirect /stylesheets/inside.css http://old.semacode.com/stylesheets/inside.css
|
||||
RedirectMatch /images/portal/(.*) http://old.semacode.com/images/portal/$1
|
||||
Redirect /images/invisible.gif http://old.semacode.com/images/invisible.gif
|
||||
RedirectMatch /javascripts/(.*) http://old.semacode.com/javascripts/$1
|
||||
|
||||
RewriteEngine on
|
||||
RewriteRule ^/past/(.*) http://old.semacode.com/past/$1 [L,P]
|
||||
RewriteCond %{HTTP_HOST} !^semacode\.com$ [NC]
|
||||
RewriteCond %{HTTP_HOST} !^$
|
||||
RewriteRule ^/(.*) http://semacode.com/$1 [L,R]
|
||||
|
||||
</VirtualHost>
|
||||
|
||||
|
||||
<VirtualHost *:80>
|
||||
ServerName old.semacode.com
|
||||
ServerAlias www.old.semacode.com
|
||||
DocumentRoot /home/simon/semacode-server/semacode/website/trunk/public
|
||||
TransferLog /tmp/access-old
|
||||
ErrorLog /tmp/error-old
|
||||
<Directory "/home/simon/semacode-server/semacode/website/trunk/public">
|
||||
Options FollowSymLinks
|
||||
AllowOverride None
|
||||
Order allow,deny
|
||||
Allow from all
|
||||
</Directory>
|
||||
</VirtualHost>
|
||||
@@ -0,0 +1 @@
|
||||
SSLRequire %{SSL_CLIENT_S_DN_CN} in {"foo@bar.com", "bar@foo.com"}
|
||||
@@ -0,0 +1,28 @@
|
||||
<IfModule mod_ssl.c>
|
||||
<VirtualHost *:443>
|
||||
ServerAdmin info@somethingnewentertainment.com
|
||||
ServerName somethingnewentertainment.com
|
||||
DocumentRoot /var/www/html
|
||||
|
||||
ErrorLog /var/log/apache2/error.log
|
||||
CustomLog /var/log/apache2/access.log combined
|
||||
|
||||
SSLEngine on
|
||||
SSLProtocol all -SSLv2 -SSLv3
|
||||
SSLHonorCipherOrder on
|
||||
SSLCipherSuite "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EEC DH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRS A RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4"
|
||||
|
||||
SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
|
||||
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
|
||||
|
||||
<FilesMatch "\.(cgi|shtml|phtml|php)$">
|
||||
SSLOptions +StdEnvVars
|
||||
</FilesMatch>
|
||||
<Directory /usr/lib/cgi-bin>
|
||||
SSLOptions +StdEnvVars
|
||||
</Directory>
|
||||
BrowserMatch "MSIE [2-6]" \
|
||||
nokeepalive ssl-unclean-shutdown \
|
||||
downgrade-1.0 force-response-1.0
|
||||
BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown
|
||||
</VirtualHost> </IfModule>
|
||||
@@ -17,7 +17,7 @@ class AugeasConfiguratorTest(util.ApacheTest):
|
||||
super(AugeasConfiguratorTest, self).setUp()
|
||||
|
||||
self.config = util.get_apache_configurator(
|
||||
self.config_path, self.config_dir, self.work_dir)
|
||||
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")
|
||||
|
||||
@@ -96,7 +96,8 @@ class ComplexParserTest(util.ParserTest):
|
||||
else:
|
||||
self.assertFalse(self.parser.find_dir("FNMATCH_DIRECTIVE"))
|
||||
|
||||
# NOTE: Only run one test per function otherwise you will have inf recursion
|
||||
# NOTE: Only run one test per function otherwise you will have
|
||||
# inf recursion
|
||||
def test_include(self):
|
||||
self.verify_fnmatch("test_fnmatch.?onf")
|
||||
|
||||
@@ -104,7 +105,8 @@ class ComplexParserTest(util.ParserTest):
|
||||
self.verify_fnmatch("../complex_parsing/[te][te]st_*.?onf")
|
||||
|
||||
def test_include_fullpath(self):
|
||||
self.verify_fnmatch(os.path.join(self.config_path, "test_fnmatch.conf"))
|
||||
self.verify_fnmatch(os.path.join(self.config_path,
|
||||
"test_fnmatch.conf"))
|
||||
|
||||
def test_include_fullpath_trailing_slash(self):
|
||||
self.verify_fnmatch(self.config_path + "//")
|
||||
|
||||
@@ -27,11 +27,22 @@ class TwoVhost80Test(util.ApacheTest):
|
||||
super(TwoVhost80Test, self).setUp()
|
||||
|
||||
self.config = util.get_apache_configurator(
|
||||
self.config_path, self.config_dir, self.work_dir)
|
||||
|
||||
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")
|
||||
|
||||
def mock_deploy_cert(self, config):
|
||||
"""A test for a mock deploy cert"""
|
||||
self.config.real_deploy_cert = self.config.deploy_cert
|
||||
|
||||
def mocked_deploy_cert(*args, **kwargs):
|
||||
"""a helper to mock a deployed cert"""
|
||||
with mock.patch("letsencrypt_apache.configurator.ApacheConfigurator.enable_mod"):
|
||||
config.real_deploy_cert(*args, **kwargs)
|
||||
self.config.deploy_cert = mocked_deploy_cert
|
||||
return self.config
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.temp_dir)
|
||||
shutil.rmtree(self.config_dir)
|
||||
@@ -54,6 +65,16 @@ 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")
|
||||
def test_prepare_old_aug(self, mock_exe_exists, _):
|
||||
mock_exe_exists.return_value = True
|
||||
self.config.config_test = mock.Mock()
|
||||
# pylint: disable=protected-access
|
||||
self.config._check_aug_version = mock.Mock(return_value=False)
|
||||
self.assertRaises(
|
||||
errors.NotSupportedError, self.config.prepare)
|
||||
|
||||
def test_add_parser_arguments(self): # pylint: disable=no-self-use
|
||||
from letsencrypt_apache.configurator import ApacheConfigurator
|
||||
# Weak test..
|
||||
@@ -90,8 +111,8 @@ class TwoVhost80Test(util.ApacheTest):
|
||||
def test_add_servernames_alias(self):
|
||||
self.config.parser.add_dir(
|
||||
self.vh_truth[2].path, "ServerAlias", ["*.le.co"])
|
||||
self.config._add_servernames(self.vh_truth[2]) # pylint: disable=protected-access
|
||||
|
||||
# pylint: disable=protected-access
|
||||
self.config._add_servernames(self.vh_truth[2])
|
||||
self.assertEqual(
|
||||
self.vh_truth[2].get_names(), set(["*.le.co", "ip-172-30-0-17"]))
|
||||
|
||||
@@ -103,7 +124,7 @@ class TwoVhost80Test(util.ApacheTest):
|
||||
|
||||
"""
|
||||
vhs = self.config.get_virtual_hosts()
|
||||
self.assertEqual(len(vhs), 5)
|
||||
self.assertEqual(len(vhs), 6)
|
||||
found = 0
|
||||
|
||||
for vhost in vhs:
|
||||
@@ -114,7 +135,15 @@ class TwoVhost80Test(util.ApacheTest):
|
||||
else:
|
||||
raise Exception("Missed: %s" % vhost) # pragma: no cover
|
||||
|
||||
self.assertEqual(found, 5)
|
||||
self.assertEqual(found, 6)
|
||||
|
||||
# Handle case of non-debian layout get_virtual_hosts
|
||||
with mock.patch(
|
||||
"letsencrypt_apache.configurator.ApacheConfigurator.conf"
|
||||
) as mock_conf:
|
||||
mock_conf.return_value = False
|
||||
vhs = self.config.get_virtual_hosts()
|
||||
self.assertEqual(len(vhs), 6)
|
||||
|
||||
@mock.patch("letsencrypt_apache.display_ops.select_vhost")
|
||||
def test_choose_vhost_none_avail(self, mock_select):
|
||||
@@ -139,11 +168,18 @@ class TwoVhost80Test(util.ApacheTest):
|
||||
self.assertFalse(self.vh_truth[0].ssl)
|
||||
self.assertTrue(chosen_vhost.ssl)
|
||||
|
||||
@mock.patch("letsencrypt_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")
|
||||
def test_choose_vhost_select_vhost_conflicting_non_ssl(self, mock_select):
|
||||
mock_select.return_value = self.vh_truth[3]
|
||||
conflicting_vhost = obj.VirtualHost(
|
||||
"path", "aug_path", set([obj.Addr.fromstring("*:443")]), True, True)
|
||||
"path", "aug_path", set([obj.Addr.fromstring("*:443")]),
|
||||
True, True)
|
||||
self.config.vhosts.append(conflicting_vhost)
|
||||
|
||||
self.assertRaises(
|
||||
@@ -162,7 +198,8 @@ class TwoVhost80Test(util.ApacheTest):
|
||||
def test_find_best_vhost_variety(self):
|
||||
# pylint: disable=protected-access
|
||||
ssl_vh = obj.VirtualHost(
|
||||
"fp", "ap", set([obj.Addr(("*", "443")), obj.Addr(("zombo.com",))]),
|
||||
"fp", "ap", set([obj.Addr(("*", "443")),
|
||||
obj.Addr(("zombo.com",))]),
|
||||
True, False)
|
||||
self.config.vhosts.append(ssl_vh)
|
||||
self.assertEqual(self.config._find_best_vhost("zombo.com"), ssl_vh)
|
||||
@@ -195,6 +232,11 @@ class TwoVhost80Test(util.ApacheTest):
|
||||
self.assertFalse(self.config.is_site_enabled(self.vh_truth[1].filep))
|
||||
self.assertTrue(self.config.is_site_enabled(self.vh_truth[2].filep))
|
||||
self.assertTrue(self.config.is_site_enabled(self.vh_truth[3].filep))
|
||||
with mock.patch("os.path.isdir") as mock_isdir:
|
||||
mock_isdir.return_value = False
|
||||
self.assertRaises(errors.ConfigurationError,
|
||||
self.config.is_site_enabled,
|
||||
"irrelevant")
|
||||
|
||||
@mock.patch("letsencrypt.le_util.run_script")
|
||||
@mock.patch("letsencrypt.le_util.exe_exists")
|
||||
@@ -236,6 +278,73 @@ class TwoVhost80Test(util.ApacheTest):
|
||||
self.config.enable_site,
|
||||
obj.VirtualHost("asdf", "afsaf", set(), False, False))
|
||||
|
||||
def test_deploy_cert_newssl(self):
|
||||
self.config = util.get_apache_configurator(
|
||||
self.config_path, self.vhost_path, self.config_dir,
|
||||
self.work_dir, version=(2, 4, 16))
|
||||
|
||||
self.config.parser.modules.add("ssl_module")
|
||||
self.config.parser.modules.add("mod_ssl.c")
|
||||
|
||||
# Get the default 443 vhost
|
||||
self.config.assoc["random.demo"] = self.vh_truth[1]
|
||||
self.config = self.mock_deploy_cert(self.config)
|
||||
self.config.deploy_cert(
|
||||
"random.demo", "example/cert.pem", "example/key.pem",
|
||||
"example/cert_chain.pem", "example/fullchain.pem")
|
||||
self.config.save()
|
||||
|
||||
# Verify ssl_module was enabled.
|
||||
self.assertTrue(self.vh_truth[1].enabled)
|
||||
self.assertTrue("ssl_module" in self.config.parser.modules)
|
||||
|
||||
loc_cert = self.config.parser.find_dir(
|
||||
"sslcertificatefile", "example/fullchain.pem",
|
||||
self.vh_truth[1].path)
|
||||
loc_key = self.config.parser.find_dir(
|
||||
"sslcertificateKeyfile", "example/key.pem", self.vh_truth[1].path)
|
||||
|
||||
# Verify one directive was found in the correct file
|
||||
self.assertEqual(len(loc_cert), 1)
|
||||
self.assertEqual(configurator.get_file_path(loc_cert[0]),
|
||||
self.vh_truth[1].filep)
|
||||
|
||||
self.assertEqual(len(loc_key), 1)
|
||||
self.assertEqual(configurator.get_file_path(loc_key[0]),
|
||||
self.vh_truth[1].filep)
|
||||
|
||||
def test_deploy_cert_newssl_no_fullchain(self):
|
||||
self.config = util.get_apache_configurator(
|
||||
self.config_path, self.vhost_path, self.config_dir,
|
||||
self.work_dir, version=(2, 4, 16))
|
||||
self.config = self.mock_deploy_cert(self.config)
|
||||
|
||||
self.config.parser.modules.add("ssl_module")
|
||||
self.config.parser.modules.add("mod_ssl.c")
|
||||
|
||||
# Get the default 443 vhost
|
||||
self.config.assoc["random.demo"] = self.vh_truth[1]
|
||||
self.assertRaises(errors.PluginError,
|
||||
lambda: self.config.deploy_cert(
|
||||
"random.demo", "example/cert.pem",
|
||||
"example/key.pem"))
|
||||
|
||||
def test_deploy_cert_old_apache_no_chain(self):
|
||||
self.config = util.get_apache_configurator(
|
||||
self.config_path, self.vhost_path, self.config_dir,
|
||||
self.work_dir, version=(2, 4, 7))
|
||||
self.config = self.mock_deploy_cert(self.config)
|
||||
|
||||
self.config.parser.modules.add("ssl_module")
|
||||
self.config.parser.modules.add("mod_ssl.c")
|
||||
|
||||
# Get the default 443 vhost
|
||||
self.config.assoc["random.demo"] = self.vh_truth[1]
|
||||
self.assertRaises(errors.PluginError,
|
||||
lambda: self.config.deploy_cert(
|
||||
"random.demo", "example/cert.pem",
|
||||
"example/key.pem"))
|
||||
|
||||
def test_deploy_cert(self):
|
||||
self.config.parser.modules.add("ssl_module")
|
||||
self.config.parser.modules.add("mod_ssl.c")
|
||||
@@ -327,6 +436,63 @@ class TwoVhost80Test(util.ApacheTest):
|
||||
|
||||
self.assertEqual(mock_add_dir.call_count, 2)
|
||||
|
||||
def test_prepare_server_https_named_listen(self):
|
||||
mock_find = mock.Mock()
|
||||
mock_find.return_value = ["test1", "test2", "test3"]
|
||||
mock_get = mock.Mock()
|
||||
mock_get.side_effect = ["1.2.3.4:80", "[::1]:80", "1.1.1.1:443"]
|
||||
mock_add_dir = mock.Mock()
|
||||
mock_enable = mock.Mock()
|
||||
|
||||
self.config.parser.find_dir = mock_find
|
||||
self.config.parser.get_arg = mock_get
|
||||
self.config.parser.add_dir_to_ifmodssl = mock_add_dir
|
||||
self.config.enable_mod = mock_enable
|
||||
|
||||
# Test Listen statements with specific ip listeed
|
||||
self.config.prepare_server_https("443")
|
||||
# Should only be 2 here, as the third interface
|
||||
# already listens to the correct port
|
||||
self.assertEqual(mock_add_dir.call_count, 2)
|
||||
|
||||
# Check argument to new Listen statements
|
||||
self.assertEqual(mock_add_dir.call_args_list[0][0][2], ["1.2.3.4:443"])
|
||||
self.assertEqual(mock_add_dir.call_args_list[1][0][2], ["[::1]:443"])
|
||||
|
||||
# Reset return lists and inputs
|
||||
mock_add_dir.reset_mock()
|
||||
mock_get.side_effect = ["1.2.3.4:80", "[::1]:80", "1.1.1.1:443"]
|
||||
|
||||
# Test
|
||||
self.config.prepare_server_https("8080", temp=True)
|
||||
self.assertEqual(mock_add_dir.call_count, 3)
|
||||
self.assertEqual(mock_add_dir.call_args_list[0][0][2],
|
||||
["1.2.3.4:8080", "https"])
|
||||
self.assertEqual(mock_add_dir.call_args_list[1][0][2],
|
||||
["[::1]:8080", "https"])
|
||||
self.assertEqual(mock_add_dir.call_args_list[2][0][2],
|
||||
["1.1.1.1:8080", "https"])
|
||||
|
||||
def test_prepare_server_https_mixed_listen(self):
|
||||
|
||||
mock_find = mock.Mock()
|
||||
mock_find.return_value = ["test1", "test2"]
|
||||
mock_get = mock.Mock()
|
||||
mock_get.side_effect = ["1.2.3.4:8080", "443"]
|
||||
mock_add_dir = mock.Mock()
|
||||
mock_enable = mock.Mock()
|
||||
|
||||
self.config.parser.find_dir = mock_find
|
||||
self.config.parser.get_arg = mock_get
|
||||
self.config.parser.add_dir_to_ifmodssl = mock_add_dir
|
||||
self.config.enable_mod = mock_enable
|
||||
|
||||
# Test Listen statements with specific ip listeed
|
||||
self.config.prepare_server_https("443")
|
||||
# Should only be 2 here, as the third interface
|
||||
# already listens to the correct port
|
||||
self.assertEqual(mock_add_dir.call_count, 0)
|
||||
|
||||
def test_make_vhost_ssl(self):
|
||||
ssl_vhost = self.config.make_vhost_ssl(self.vh_truth[0])
|
||||
|
||||
@@ -351,7 +517,67 @@ 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), 6)
|
||||
self.assertEqual(len(self.config.vhosts), 7)
|
||||
|
||||
def test_clean_vhost_ssl(self):
|
||||
# pylint: disable=protected-access
|
||||
for directive in ["SSLCertificateFile", "SSLCertificateKeyFile",
|
||||
"SSLCertificateChainFile", "SSLCACertificatePath"]:
|
||||
for _ in range(10):
|
||||
self.config.parser.add_dir(self.vh_truth[1].path,
|
||||
directive, ["bogus"])
|
||||
self.config.save()
|
||||
|
||||
self.config._clean_vhost(self.vh_truth[1])
|
||||
self.config.save()
|
||||
|
||||
loc_cert = self.config.parser.find_dir(
|
||||
'SSLCertificateFile', None, self.vh_truth[1].path, False)
|
||||
loc_key = self.config.parser.find_dir(
|
||||
'SSLCertificateKeyFile', None, self.vh_truth[1].path, False)
|
||||
loc_chain = self.config.parser.find_dir(
|
||||
'SSLCertificateChainFile', None, self.vh_truth[1].path, False)
|
||||
loc_cacert = self.config.parser.find_dir(
|
||||
'SSLCACertificatePath', None, self.vh_truth[1].path, False)
|
||||
|
||||
self.assertEqual(len(loc_cert), 1)
|
||||
self.assertEqual(len(loc_key), 1)
|
||||
|
||||
self.assertEqual(len(loc_chain), 0)
|
||||
|
||||
self.assertEqual(len(loc_cacert), 10)
|
||||
|
||||
def test_deduplicate_directives(self):
|
||||
# pylint: disable=protected-access
|
||||
DIRECTIVE = "Foo"
|
||||
for _ in range(10):
|
||||
self.config.parser.add_dir(self.vh_truth[1].path,
|
||||
DIRECTIVE, ["bar"])
|
||||
self.config.save()
|
||||
|
||||
self.config._deduplicate_directives(self.vh_truth[1].path, [DIRECTIVE])
|
||||
self.config.save()
|
||||
|
||||
self.assertEqual(
|
||||
len(self.config.parser.find_dir(
|
||||
DIRECTIVE, None, self.vh_truth[1].path, False)), 1)
|
||||
|
||||
def test_remove_directives(self):
|
||||
# pylint: disable=protected-access
|
||||
DIRECTIVES = ["Foo", "Bar"]
|
||||
for directive in DIRECTIVES:
|
||||
for _ in range(10):
|
||||
self.config.parser.add_dir(self.vh_truth[1].path,
|
||||
directive, ["baz"])
|
||||
self.config.save()
|
||||
|
||||
self.config._remove_directives(self.vh_truth[1].path, DIRECTIVES)
|
||||
self.config.save()
|
||||
|
||||
for directive in DIRECTIVES:
|
||||
self.assertEqual(
|
||||
len(self.config.parser.find_dir(
|
||||
directive, None, self.vh_truth[1].path, False)), 0)
|
||||
|
||||
def test_make_vhost_ssl_extra_vhs(self):
|
||||
self.config.aug.match = mock.Mock(return_value=["p1", "p2"])
|
||||
@@ -380,23 +606,23 @@ class TwoVhost80Test(util.ApacheTest):
|
||||
self.config._add_name_vhost_if_necessary(self.vh_truth[0])
|
||||
self.assertTrue(self.config.save.called)
|
||||
|
||||
@mock.patch("letsencrypt_apache.configurator.dvsni.ApacheDvsni.perform")
|
||||
@mock.patch("letsencrypt_apache.configurator.tls_sni_01.ApacheTlsSni01.perform")
|
||||
@mock.patch("letsencrypt_apache.configurator.ApacheConfigurator.restart")
|
||||
def test_perform(self, mock_restart, mock_dvsni_perform):
|
||||
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
|
||||
account_key, achall1, achall2 = self.get_achalls()
|
||||
|
||||
dvsni_ret_val = [
|
||||
expected = [
|
||||
achall1.response(account_key),
|
||||
achall2.response(account_key),
|
||||
]
|
||||
|
||||
mock_dvsni_perform.return_value = dvsni_ret_val
|
||||
mock_perform.return_value = expected
|
||||
responses = self.config.perform([achall1, achall2])
|
||||
|
||||
self.assertEqual(mock_dvsni_perform.call_count, 1)
|
||||
self.assertEqual(responses, dvsni_ret_val)
|
||||
self.assertEqual(mock_perform.call_count, 1)
|
||||
self.assertEqual(responses, expected)
|
||||
|
||||
self.assertEqual(mock_restart.call_count, 1)
|
||||
|
||||
@@ -440,30 +666,20 @@ class TwoVhost80Test(util.ApacheTest):
|
||||
self.assertRaises(errors.PluginError, self.config.get_version)
|
||||
|
||||
mock_script.return_value = (
|
||||
"Server Version: Apache/2.3{0} Apache/2.4.7".format(os.linesep), "")
|
||||
"Server Version: Apache/2.3{0} Apache/2.4.7".format(
|
||||
os.linesep), "")
|
||||
self.assertRaises(errors.PluginError, self.config.get_version)
|
||||
|
||||
mock_script.side_effect = errors.SubprocessError("Can't find program")
|
||||
self.assertRaises(errors.PluginError, self.config.get_version)
|
||||
|
||||
@mock.patch("letsencrypt_apache.configurator.subprocess.Popen")
|
||||
def test_restart(self, mock_popen):
|
||||
"""These will be changed soon enough with reload."""
|
||||
mock_popen().returncode = 0
|
||||
mock_popen().communicate.return_value = ("", "")
|
||||
|
||||
@mock.patch("letsencrypt_apache.configurator.le_util.run_script")
|
||||
def test_restart(self, _):
|
||||
self.config.restart()
|
||||
|
||||
@mock.patch("letsencrypt_apache.configurator.subprocess.Popen")
|
||||
def test_restart_bad_process(self, mock_popen):
|
||||
mock_popen.side_effect = OSError
|
||||
|
||||
self.assertRaises(errors.MisconfigurationError, self.config.restart)
|
||||
|
||||
@mock.patch("letsencrypt_apache.configurator.subprocess.Popen")
|
||||
def test_restart_failure(self, mock_popen):
|
||||
mock_popen().communicate.return_value = ("", "")
|
||||
mock_popen().returncode = 1
|
||||
@mock.patch("letsencrypt_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)
|
||||
|
||||
@@ -475,19 +691,21 @@ class TwoVhost80Test(util.ApacheTest):
|
||||
def test_config_test_bad_process(self, mock_run_script):
|
||||
mock_run_script.side_effect = errors.SubprocessError
|
||||
|
||||
self.assertRaises(errors.MisconfigurationError, self.config.config_test)
|
||||
self.assertRaises(errors.MisconfigurationError,
|
||||
self.config.config_test)
|
||||
|
||||
def test_get_all_certs_keys(self):
|
||||
c_k = self.config.get_all_certs_keys()
|
||||
|
||||
self.assertEqual(len(c_k), 1)
|
||||
self.assertEqual(len(c_k), 2)
|
||||
cert, key, path = next(iter(c_k))
|
||||
self.assertTrue("cert" in cert)
|
||||
self.assertTrue("key" in key)
|
||||
self.assertTrue("default-ssl.conf" in path)
|
||||
self.assertTrue("default-ssl" in path)
|
||||
|
||||
def test_get_all_certs_keys_malformed_conf(self):
|
||||
self.config.parser.find_dir = mock.Mock(side_effect=[["path"], []])
|
||||
self.config.parser.find_dir = mock.Mock(
|
||||
side_effect=[["path"], [], ["path"], []])
|
||||
c_k = self.config.get_all_certs_keys()
|
||||
|
||||
self.assertFalse(c_k)
|
||||
@@ -508,16 +726,107 @@ class TwoVhost80Test(util.ApacheTest):
|
||||
def test_supported_enhancements(self):
|
||||
self.assertTrue(isinstance(self.config.supported_enhancements(), list))
|
||||
|
||||
@mock.patch("letsencrypt.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",))]),
|
||||
True, False)
|
||||
self.config.vhosts.append(ssl_vh)
|
||||
self.assertRaises(
|
||||
errors.PluginError,
|
||||
self.config.enhance, "satoshi.com", "redirect")
|
||||
|
||||
def test_enhance_unknown_enhancement(self):
|
||||
self.assertRaises(
|
||||
errors.PluginError,
|
||||
self.config.enhance, "letsencrypt.demo", "unknown_enhancement")
|
||||
|
||||
@mock.patch("letsencrypt.le_util.run_script")
|
||||
@mock.patch("letsencrypt.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",
|
||||
"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"]
|
||||
|
||||
# These are not immediately available in find_dir even with save() and
|
||||
# load(). They must be found in sites-available
|
||||
hsts_header = self.config.parser.find_dir(
|
||||
"Header", None, ssl_vhost.path)
|
||||
|
||||
# four args to HSTS header
|
||||
self.assertEqual(len(hsts_header), 4)
|
||||
|
||||
def test_http_header_hsts_twice(self):
|
||||
self.config.parser.modules.add("mod_ssl.c")
|
||||
# skip the enable mod
|
||||
self.config.parser.modules.add("headers_module")
|
||||
|
||||
# This will create an ssl vhost for letsencrypt.demo
|
||||
self.config.enhance("encryption-example.demo", "ensure-http-header",
|
||||
"Strict-Transport-Security")
|
||||
|
||||
self.assertRaises(
|
||||
errors.PluginEnhancementAlreadyPresent,
|
||||
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")
|
||||
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",
|
||||
"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"]
|
||||
|
||||
# These are not immediately available in find_dir even with save() and
|
||||
# load(). They must be found in sites-available
|
||||
uir_header = self.config.parser.find_dir(
|
||||
"Header", None, ssl_vhost.path)
|
||||
|
||||
# four args to HSTS header
|
||||
self.assertEqual(len(uir_header), 4)
|
||||
|
||||
def test_http_header_uir_twice(self):
|
||||
self.config.parser.modules.add("mod_ssl.c")
|
||||
# skip the enable mod
|
||||
self.config.parser.modules.add("headers_module")
|
||||
|
||||
# This will create an ssl vhost for letsencrypt.demo
|
||||
self.config.enhance("encryption-example.demo", "ensure-http-header",
|
||||
"Upgrade-Insecure-Requests")
|
||||
|
||||
self.assertRaises(
|
||||
errors.PluginEnhancementAlreadyPresent,
|
||||
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")
|
||||
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")
|
||||
|
||||
@@ -537,10 +846,61 @@ class TwoVhost80Test(util.ApacheTest):
|
||||
|
||||
self.assertTrue("rewrite_module" in self.config.parser.modules)
|
||||
|
||||
def test_rewrite_rule_exists(self):
|
||||
# Skip the enable mod
|
||||
self.config.parser.modules.add("rewrite_module")
|
||||
self.config.get_version = mock.Mock(return_value=(2, 3, 9))
|
||||
self.config.parser.add_dir(
|
||||
self.vh_truth[3].path, "RewriteRule", ["Unknown"])
|
||||
# pylint: disable=protected-access
|
||||
self.assertTrue(self.config._is_rewrite_exists(self.vh_truth[3]))
|
||||
|
||||
def test_rewrite_engine_exists(self):
|
||||
# Skip the enable mod
|
||||
self.config.parser.modules.add("rewrite_module")
|
||||
self.config.get_version = mock.Mock(return_value=(2, 3, 9))
|
||||
self.config.parser.add_dir(
|
||||
self.vh_truth[3].path, "RewriteEngine", "on")
|
||||
# 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")
|
||||
def test_redirect_with_existing_rewrite(self, mock_exe, _):
|
||||
self.config.parser.update_runtime_variables = mock.Mock()
|
||||
mock_exe.return_value = True
|
||||
self.config.get_version = mock.Mock(return_value=(2, 2))
|
||||
|
||||
# Create a preexisting rewrite rule
|
||||
self.config.parser.add_dir(
|
||||
self.vh_truth[3].path, "RewriteRule", ["UnknownPattern",
|
||||
"UnknownTarget"])
|
||||
self.config.save()
|
||||
|
||||
# This will create an ssl vhost for letsencrypt.demo
|
||||
self.config.enhance("letsencrypt.demo", "redirect")
|
||||
|
||||
# These are not immediately available in find_dir even with save() and
|
||||
# load(). They must be found in sites-available
|
||||
rw_engine = self.config.parser.find_dir(
|
||||
"RewriteEngine", "on", self.vh_truth[3].path)
|
||||
rw_rule = self.config.parser.find_dir(
|
||||
"RewriteRule", None, self.vh_truth[3].path)
|
||||
|
||||
self.assertEqual(len(rw_engine), 1)
|
||||
# three args to rw_rule + 1 arg for the pre existing rewrite
|
||||
self.assertEqual(len(rw_rule), 5)
|
||||
|
||||
self.assertTrue(rw_engine[0].startswith(self.vh_truth[3].path))
|
||||
self.assertTrue(rw_rule[0].startswith(self.vh_truth[3].path))
|
||||
|
||||
self.assertTrue("rewrite_module" in self.config.parser.modules)
|
||||
|
||||
def test_redirect_with_conflict(self):
|
||||
self.config.parser.modules.add("rewrite_module")
|
||||
ssl_vh = obj.VirtualHost(
|
||||
"fp", "ap", set([obj.Addr(("*", "443")), obj.Addr(("zombo.com",))]),
|
||||
"fp", "ap", set([obj.Addr(("*", "443")),
|
||||
obj.Addr(("zombo.com",))]),
|
||||
True, False)
|
||||
# No names ^ this guy should conflict.
|
||||
|
||||
@@ -551,49 +911,74 @@ class TwoVhost80Test(util.ApacheTest):
|
||||
def test_redirect_twice(self):
|
||||
# Skip the enable mod
|
||||
self.config.parser.modules.add("rewrite_module")
|
||||
self.config.get_version = mock.Mock(return_value=(2, 3, 9))
|
||||
|
||||
self.config.enhance("encryption-example.demo", "redirect")
|
||||
self.assertRaises(
|
||||
errors.PluginError,
|
||||
errors.PluginEnhancementAlreadyPresent,
|
||||
self.config.enhance, "encryption-example.demo", "redirect")
|
||||
|
||||
def test_unknown_rewrite(self):
|
||||
# Skip the enable mod
|
||||
self.config.parser.modules.add("rewrite_module")
|
||||
self.config.parser.add_dir(
|
||||
self.vh_truth[3].path, "RewriteRule", ["Unknown"])
|
||||
self.config.save()
|
||||
self.assertRaises(
|
||||
errors.PluginError,
|
||||
self.config.enhance, "letsencrypt.demo", "redirect")
|
||||
|
||||
def test_unknown_rewrite2(self):
|
||||
# Skip the enable mod
|
||||
self.config.parser.modules.add("rewrite_module")
|
||||
self.config.parser.add_dir(
|
||||
self.vh_truth[3].path, "RewriteRule", ["Unknown", "2", "3"])
|
||||
self.config.save()
|
||||
self.assertRaises(
|
||||
errors.PluginError,
|
||||
self.config.enhance, "letsencrypt.demo", "redirect")
|
||||
|
||||
def test_unknown_redirect(self):
|
||||
# Skip the enable mod
|
||||
self.config.parser.modules.add("rewrite_module")
|
||||
self.config.parser.add_dir(
|
||||
self.vh_truth[3].path, "Redirect", ["Unknown"])
|
||||
self.config.save()
|
||||
self.assertRaises(
|
||||
errors.PluginError,
|
||||
self.config.enhance, "letsencrypt.demo", "redirect")
|
||||
|
||||
def test_create_own_redirect(self):
|
||||
self.config.parser.modules.add("rewrite_module")
|
||||
self.config.get_version = mock.Mock(return_value=(2, 3, 9))
|
||||
# For full testing... give names...
|
||||
self.vh_truth[1].name = "default.com"
|
||||
self.vh_truth[1].aliases = set(["yes.default.com"])
|
||||
|
||||
self.config._enable_redirect(self.vh_truth[1], "") # pylint: disable=protected-access
|
||||
self.assertEqual(len(self.config.vhosts), 6)
|
||||
# pylint: disable=protected-access
|
||||
self.config._enable_redirect(self.vh_truth[1], "")
|
||||
self.assertEqual(len(self.config.vhosts), 7)
|
||||
|
||||
def test_create_own_redirect_for_old_apache_version(self):
|
||||
self.config.parser.modules.add("rewrite_module")
|
||||
self.config.get_version = mock.Mock(return_value=(2, 2))
|
||||
# For full testing... give names...
|
||||
self.vh_truth[1].name = "default.com"
|
||||
self.vh_truth[1].aliases = set(["yes.default.com"])
|
||||
|
||||
# pylint: disable=protected-access
|
||||
self.config._enable_redirect(self.vh_truth[1], "")
|
||||
self.assertEqual(len(self.config.vhosts), 7)
|
||||
|
||||
def test_sift_line(self):
|
||||
# pylint: disable=protected-access
|
||||
small_quoted_target = "RewriteRule ^ \"http://\""
|
||||
self.assertFalse(self.config._sift_line(small_quoted_target))
|
||||
|
||||
https_target = "RewriteRule ^ https://satoshi"
|
||||
self.assertTrue(self.config._sift_line(https_target))
|
||||
|
||||
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")
|
||||
def test_make_vhost_ssl_with_existing_rewrite_rule(self, mock_get_utility):
|
||||
self.config.parser.modules.add("rewrite_module")
|
||||
|
||||
http_vhost = self.vh_truth[0]
|
||||
|
||||
self.config.parser.add_dir(
|
||||
http_vhost.path, "RewriteEngine", "on")
|
||||
|
||||
self.config.parser.add_dir(
|
||||
http_vhost.path, "RewriteRule",
|
||||
["^",
|
||||
"https://%{SERVER_NAME}%{REQUEST_URI}",
|
||||
"[L,QSA,R=permanent]"])
|
||||
self.config.save()
|
||||
|
||||
ssl_vhost = self.config.make_vhost_ssl(self.vh_truth[0])
|
||||
|
||||
self.assertTrue(self.config.parser.find_dir(
|
||||
"RewriteEngine", "on", ssl_vhost.path, False))
|
||||
|
||||
conf_text = open(ssl_vhost.filep).read()
|
||||
commented_rewrite_rule = ("# RewriteRule ^ "
|
||||
"https://%{SERVER_NAME}%{REQUEST_URI} "
|
||||
"[L,QSA,R=permanent]")
|
||||
self.assertTrue(commented_rewrite_rule in conf_text)
|
||||
mock_get_utility().add_message.assert_called_once_with(mock.ANY,
|
||||
mock.ANY)
|
||||
|
||||
def get_achalls(self):
|
||||
"""Return testing achallenges."""
|
||||
@@ -622,6 +1007,15 @@ class TwoVhost80Test(util.ApacheTest):
|
||||
self.assertTrue(self.config.parser.find_dir(
|
||||
"NameVirtualHost", "*:443", exclude=False))
|
||||
|
||||
def test_aug_version(self):
|
||||
mock_match = mock.Mock(return_value=["something"])
|
||||
self.config.aug.match = mock_match
|
||||
# pylint: disable=protected-access
|
||||
self.assertEquals(self.config._check_aug_version(),
|
||||
["something"])
|
||||
self.config.aug.match.side_effect = RuntimeError
|
||||
self.assertFalse(self.config._check_aug_version())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main() # pragma: no cover
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
"""Test for letsencrypt_apache.configurator."""
|
||||
|
||||
import mock
|
||||
import unittest
|
||||
|
||||
from letsencrypt_apache import constants
|
||||
|
||||
|
||||
class ConstantsTest(unittest.TestCase):
|
||||
|
||||
@mock.patch("letsencrypt.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")
|
||||
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")
|
||||
def test_get_default_value(self, os_info):
|
||||
os_info.return_value = ('Nonexistent Linux', '', '')
|
||||
self.assertEqual(constants.os_constant("vhost_root"),
|
||||
"/etc/apache2/sites-available")
|
||||
@@ -6,6 +6,7 @@ import mock
|
||||
import zope.component
|
||||
|
||||
from letsencrypt.display import util as display_util
|
||||
from letsencrypt import errors
|
||||
|
||||
from letsencrypt_apache import obj
|
||||
|
||||
@@ -31,6 +32,14 @@ class SelectVhostTest(unittest.TestCase):
|
||||
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")
|
||||
def test_noninteractive(self, mock_util):
|
||||
mock_util().menu.side_effect = errors.MissingCommandlineFlag("no vhost default")
|
||||
try:
|
||||
self._call(self.vhosts)
|
||||
except errors.MissingCommandlineFlag, e:
|
||||
self.assertTrue("VirtualHost directives" in e.message)
|
||||
|
||||
@mock.patch("letsencrypt_apache.display_ops.zope.component.getUtility")
|
||||
def test_more_info_cancel(self, mock_util):
|
||||
mock_util().menu.side_effect = [
|
||||
|
||||
@@ -47,7 +47,8 @@ class VirtualHostTest(unittest.TestCase):
|
||||
self.assertTrue(self.vhost1.conflicts([self.addr2]))
|
||||
self.assertFalse(self.vhost1.conflicts([self.addr_default]))
|
||||
|
||||
self.assertFalse(self.vhost2.conflicts([self.addr1, self.addr_default]))
|
||||
self.assertFalse(self.vhost2.conflicts([self.addr1,
|
||||
self.addr_default]))
|
||||
|
||||
def test_same_server(self):
|
||||
from letsencrypt_apache.obj import VirtualHost
|
||||
|
||||
@@ -36,7 +36,7 @@ class BasicParserTest(util.ParserTest):
|
||||
|
||||
"""
|
||||
file_path = os.path.join(
|
||||
self.config_path, "sites-available", "letsencrypt.conf")
|
||||
self.config_path, "not-parsed-by-default", "letsencrypt.conf")
|
||||
|
||||
self.parser._parse_file(file_path) # pylint: disable=protected-access
|
||||
|
||||
@@ -118,7 +118,8 @@ class BasicParserTest(util.ParserTest):
|
||||
# pylint: disable=protected-access
|
||||
path = os.path.join(self.parser.root, "httpd.conf")
|
||||
open(path, 'w').close()
|
||||
self.parser.add_dir(self.parser.loc["default"], "Include", "httpd.conf")
|
||||
self.parser.add_dir(self.parser.loc["default"], "Include",
|
||||
"httpd.conf")
|
||||
|
||||
self.assertEqual(
|
||||
path, self.parser._set_user_config_file())
|
||||
@@ -145,25 +146,26 @@ class BasicParserTest(util.ParserTest):
|
||||
expected_vars = {"TEST": "", "U_MICH": "", "TLS": "443",
|
||||
"example_path": "Documents/path"}
|
||||
|
||||
self.parser.update_runtime_variables("ctl")
|
||||
self.parser.update_runtime_variables()
|
||||
self.assertEqual(self.parser.variables, expected_vars)
|
||||
|
||||
@mock.patch("letsencrypt_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.assertRaises(
|
||||
errors.PluginError, self.parser.update_runtime_variables, "ctl")
|
||||
self.parser.update_runtime_variables()
|
||||
|
||||
mock_cfg.return_value = "Define: DUMP_RUN_CFG\nDefine: TLS=443=24"
|
||||
self.assertRaises(
|
||||
errors.PluginError, self.parser.update_runtime_variables, "ctl")
|
||||
errors.PluginError, self.parser.update_runtime_variables)
|
||||
|
||||
@mock.patch("letsencrypt_apache.constants.os_constant")
|
||||
@mock.patch("letsencrypt_apache.parser.subprocess.Popen")
|
||||
def test_update_runtime_vars_bad_ctl(self, mock_popen):
|
||||
def test_update_runtime_vars_bad_ctl(self, mock_popen, mock_const):
|
||||
mock_popen.side_effect = OSError
|
||||
mock_const.return_value = "nonexistent"
|
||||
self.assertRaises(
|
||||
errors.MisconfigurationError,
|
||||
self.parser.update_runtime_variables, "ctl")
|
||||
self.parser.update_runtime_variables)
|
||||
|
||||
@mock.patch("letsencrypt_apache.parser.subprocess.Popen")
|
||||
def test_update_runtime_vars_bad_exit(self, mock_popen):
|
||||
@@ -171,7 +173,7 @@ class BasicParserTest(util.ParserTest):
|
||||
mock_popen.returncode = -1
|
||||
self.assertRaises(
|
||||
errors.MisconfigurationError,
|
||||
self.parser.update_runtime_variables, "ctl")
|
||||
self.parser.update_runtime_variables)
|
||||
|
||||
|
||||
class ParserInitTest(util.ApacheTest):
|
||||
@@ -185,6 +187,15 @@ class ParserInitTest(util.ApacheTest):
|
||||
shutil.rmtree(self.config_dir)
|
||||
shutil.rmtree(self.work_dir)
|
||||
|
||||
@mock.patch("letsencrypt_apache.parser.ApacheParser._get_runtime_cfg")
|
||||
def test_unparsable(self, mock_cfg):
|
||||
from letsencrypt_apache.parser import ApacheParser
|
||||
mock_cfg.return_value = ('Define: TEST')
|
||||
self.assertRaises(
|
||||
errors.PluginError,
|
||||
ApacheParser, self.aug, os.path.relpath(self.config_path),
|
||||
"/dummy/vhostpath", version=(2, 2, 22))
|
||||
|
||||
def test_root_normalized(self):
|
||||
from letsencrypt_apache.parser import ApacheParser
|
||||
|
||||
@@ -193,7 +204,9 @@ class ParserInitTest(util.ApacheTest):
|
||||
path = os.path.join(
|
||||
self.temp_dir,
|
||||
"debian_apache_2_4/////two_vhost_80/../two_vhost_80/apache2")
|
||||
parser = ApacheParser(self.aug, path, "dummy_ctl")
|
||||
|
||||
parser = ApacheParser(self.aug, path,
|
||||
"/dummy/vhostpath")
|
||||
|
||||
self.assertEqual(parser.root, self.config_path)
|
||||
|
||||
@@ -202,7 +215,8 @@ class ParserInitTest(util.ApacheTest):
|
||||
with mock.patch("letsencrypt_apache.parser.ApacheParser."
|
||||
"update_runtime_variables"):
|
||||
parser = ApacheParser(
|
||||
self.aug, os.path.relpath(self.config_path), "dummy_ctl")
|
||||
self.aug, os.path.relpath(self.config_path),
|
||||
"/dummy/vhostpath")
|
||||
|
||||
self.assertEqual(parser.root, self.config_path)
|
||||
|
||||
@@ -211,7 +225,8 @@ class ParserInitTest(util.ApacheTest):
|
||||
with mock.patch("letsencrypt_apache.parser.ApacheParser."
|
||||
"update_runtime_variables"):
|
||||
parser = ApacheParser(
|
||||
self.aug, self.config_path + os.path.sep, "dummy_ctl")
|
||||
self.aug, self.config_path + os.path.sep,
|
||||
"/dummy/vhostpath")
|
||||
self.assertEqual(parser.root, self.config_path)
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
<VirtualHost 1.1.1.1>
|
||||
|
||||
ServerName invalid.net
|
||||
|
||||
</virtualHost>
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
<IfModule mod_ssl.c>
|
||||
<VirtualHost _default_:443>
|
||||
ServerAdmin webmaster@localhost
|
||||
|
||||
DocumentRoot /var/www/html
|
||||
|
||||
ErrorLog ${APACHE_LOG_DIR}/error.log
|
||||
CustomLog ${APACHE_LOG_DIR}/access.log combined
|
||||
|
||||
# A self-signed (snakeoil) certificate can be created by installing
|
||||
# the ssl-cert package. See
|
||||
# /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
|
||||
|
||||
|
||||
#SSLOptions +FakeBasicAuth +ExportCertData +StrictRequire
|
||||
<FilesMatch "\.(cgi|shtml|phtml|php)$">
|
||||
SSLOptions +StdEnvVars
|
||||
</FilesMatch>
|
||||
<Directory /usr/lib/cgi-bin>
|
||||
SSLOptions +StdEnvVars
|
||||
</Directory>
|
||||
|
||||
BrowserMatch "MSIE [2-6]" \
|
||||
nokeepalive ssl-unclean-shutdown \
|
||||
downgrade-1.0 force-response-1.0
|
||||
# MSIE 7 and newer should be able to use keepalive
|
||||
BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown
|
||||
|
||||
</VirtualHost>
|
||||
</IfModule>
|
||||
|
||||
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Test for letsencrypt_apache.dvsni."""
|
||||
"""Test for letsencrypt_apache.tls_sni_01."""
|
||||
import unittest
|
||||
import shutil
|
||||
|
||||
@@ -9,22 +9,24 @@ from letsencrypt.plugins import common_test
|
||||
from letsencrypt_apache import obj
|
||||
from letsencrypt_apache.tests import util
|
||||
|
||||
from six.moves import xrange # pylint: disable=redefined-builtin, import-error
|
||||
|
||||
class DvsniPerformTest(util.ApacheTest):
|
||||
"""Test the ApacheDVSNI challenge."""
|
||||
|
||||
class TlsSniPerformTest(util.ApacheTest):
|
||||
"""Test the ApacheTlsSni01 challenge."""
|
||||
|
||||
auth_key = common_test.TLSSNI01Test.auth_key
|
||||
achalls = common_test.TLSSNI01Test.achalls
|
||||
|
||||
def setUp(self): # pylint: disable=arguments-differ
|
||||
super(DvsniPerformTest, self).setUp()
|
||||
super(TlsSniPerformTest, self).setUp()
|
||||
|
||||
config = util.get_apache_configurator(
|
||||
self.config_path, self.config_dir, self.work_dir)
|
||||
self.config_path, self.vhost_path, self.config_dir, self.work_dir)
|
||||
config.config.tls_sni_01_port = 443
|
||||
|
||||
from letsencrypt_apache import dvsni
|
||||
self.sni = dvsni.ApacheDvsni(config)
|
||||
from letsencrypt_apache import tls_sni_01
|
||||
self.sni = tls_sni_01.ApacheTlsSni01(config)
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.temp_dir)
|
||||
@@ -58,7 +60,7 @@ class DvsniPerformTest(util.ApacheTest):
|
||||
|
||||
mock_setup_cert.assert_called_once_with(achall)
|
||||
|
||||
# Check to make sure challenge config path is included in apache config.
|
||||
# Check to make sure challenge config path is included in apache config
|
||||
self.assertEqual(
|
||||
len(self.sni.configurator.parser.find_dir(
|
||||
"Include", self.sni.challenge_conf)), 1)
|
||||
@@ -78,7 +80,8 @@ class DvsniPerformTest(util.ApacheTest):
|
||||
# pylint: disable=protected-access
|
||||
self.sni._setup_challenge_cert = mock_setup_cert
|
||||
|
||||
sni_responses = self.sni.perform()
|
||||
with mock.patch("letsencrypt_apache.configurator.ApacheConfigurator.enable_mod"):
|
||||
sni_responses = self.sni.perform()
|
||||
|
||||
self.assertEqual(mock_setup_cert.call_count, 2)
|
||||
|
||||
@@ -121,16 +124,18 @@ class DvsniPerformTest(util.ApacheTest):
|
||||
names = vhost.get_names()
|
||||
self.assertTrue(names in z_domains)
|
||||
|
||||
def test_get_dvsni_addrs_default(self):
|
||||
def test_get_addrs_default(self):
|
||||
self.sni.configurator.choose_vhost = mock.Mock(
|
||||
return_value=obj.VirtualHost(
|
||||
"path", "aug_path", set([obj.Addr.fromstring("_default_:443")]),
|
||||
"path", "aug_path",
|
||||
set([obj.Addr.fromstring("_default_:443")]),
|
||||
False, False)
|
||||
)
|
||||
|
||||
# pylint: disable=protected-access
|
||||
self.assertEqual(
|
||||
set([obj.Addr.fromstring("*:443")]),
|
||||
self.sni.get_dvsni_addrs(self.achalls[0]))
|
||||
self.sni._get_addrs(self.achalls[0]))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
@@ -23,7 +23,8 @@ from letsencrypt_apache import obj
|
||||
class ApacheTest(unittest.TestCase): # pylint: disable=too-few-public-methods
|
||||
|
||||
def setUp(self, test_dir="debian_apache_2_4/two_vhost_80",
|
||||
config_root="debian_apache_2_4/two_vhost_80/apache2"):
|
||||
config_root="debian_apache_2_4/two_vhost_80/apache2",
|
||||
vhost_root="debian_apache_2_4/two_vhost_80/apache2/sites-available"):
|
||||
# pylint: disable=arguments-differ
|
||||
super(ApacheTest, self).setUp()
|
||||
|
||||
@@ -36,16 +37,32 @@ class ApacheTest(unittest.TestCase): # pylint: disable=too-few-public-methods
|
||||
constants.MOD_SSL_CONF_DEST)
|
||||
|
||||
self.config_path = os.path.join(self.temp_dir, config_root)
|
||||
self.vhost_path = os.path.join(self.temp_dir, vhost_root)
|
||||
|
||||
self.rsa512jwk = jose.JWKRSA.load(test_util.load_vector(
|
||||
"rsa512_key.pem"))
|
||||
|
||||
# Make sure all vhosts in sites-enabled are symlinks (Python packaging
|
||||
# does not preserve symlinks)
|
||||
sites_enabled = os.path.join(self.config_path, "sites-enabled")
|
||||
if not os.path.exists(sites_enabled):
|
||||
return
|
||||
|
||||
for vhost_basename in os.listdir(sites_enabled):
|
||||
vhost = os.path.join(sites_enabled, vhost_basename)
|
||||
if not os.path.islink(vhost): # pragma: no cover
|
||||
os.remove(vhost)
|
||||
target = os.path.join(
|
||||
os.path.pardir, "sites-available", vhost_basename)
|
||||
os.symlink(target, vhost)
|
||||
|
||||
|
||||
class ParserTest(ApacheTest): # pytlint: disable=too-few-public-methods
|
||||
|
||||
def setUp(self, test_dir="debian_apache_2_4/two_vhost_80",
|
||||
config_root="debian_apache_2_4/two_vhost_80/apache2"):
|
||||
super(ParserTest, self).setUp(test_dir, config_root)
|
||||
config_root="debian_apache_2_4/two_vhost_80/apache2",
|
||||
vhost_root="debian_apache_2_4/two_vhost_80/apache2/sites-available"):
|
||||
super(ParserTest, self).setUp(test_dir, config_root, vhost_root)
|
||||
|
||||
zope.component.provideUtility(display_util.FileDisplay(sys.stdout))
|
||||
|
||||
@@ -55,11 +72,12 @@ class ParserTest(ApacheTest): # pytlint: disable=too-few-public-methods
|
||||
with mock.patch("letsencrypt_apache.parser.ApacheParser."
|
||||
"update_runtime_variables"):
|
||||
self.parser = ApacheParser(
|
||||
self.aug, self.config_path, "dummy_ctl_path")
|
||||
self.aug, self.config_path, self.vhost_path)
|
||||
|
||||
|
||||
def get_apache_configurator(
|
||||
config_path, config_dir, work_dir, version=(2, 4, 7), conf=None):
|
||||
config_path, vhost_path,
|
||||
config_dir, work_dir, version=(2, 4, 7), conf=None):
|
||||
"""Create an Apache Configurator with the specified options.
|
||||
|
||||
:param conf: Function that returns binary paths. self.conf in Configurator
|
||||
@@ -68,18 +86,16 @@ def get_apache_configurator(
|
||||
backups = os.path.join(work_dir, "backups")
|
||||
mock_le_config = mock.MagicMock(
|
||||
apache_server_root=config_path,
|
||||
apache_le_vhost_ext=constants.CLI_DEFAULTS["le_vhost_ext"],
|
||||
apache_vhost_root=vhost_path,
|
||||
apache_le_vhost_ext=constants.os_constant("le_vhost_ext"),
|
||||
apache_challenge_location=config_path,
|
||||
backup_dir=backups,
|
||||
config_dir=config_dir,
|
||||
temp_checkpoint_dir=os.path.join(work_dir, "temp_checkpoints"),
|
||||
in_progress_dir=os.path.join(backups, "IN_PROGRESS"),
|
||||
work_dir=work_dir)
|
||||
|
||||
with mock.patch("letsencrypt_apache.configurator."
|
||||
"subprocess.Popen") as mock_popen:
|
||||
# This indicates config_test passes
|
||||
mock_popen().communicate.return_value = ("Fine output", "No problems")
|
||||
mock_popen().returncode = 0
|
||||
with mock.patch("letsencrypt_apache.configurator.le_util.run_script"):
|
||||
with mock.patch("letsencrypt_apache.configurator.le_util."
|
||||
"exe_exists") as mock_exe_exists:
|
||||
mock_exe_exists.return_value = True
|
||||
@@ -128,7 +144,13 @@ def get_vh_truth(temp_dir, config_name):
|
||||
os.path.join(prefix, "mod_macro-example.conf"),
|
||||
os.path.join(aug_pre,
|
||||
"mod_macro-example.conf/Macro/VirtualHost"),
|
||||
set([obj.Addr.fromstring("*:80")]), False, True, modmacro=True)
|
||||
set([obj.Addr.fromstring("*:80")]), False, True,
|
||||
modmacro=True),
|
||||
obj.VirtualHost(
|
||||
os.path.join(prefix, "default-ssl-port-only.conf"),
|
||||
os.path.join(aug_pre, ("default-ssl-port-only.conf/"
|
||||
"IfModule/VirtualHost")),
|
||||
set([obj.Addr.fromstring("_default_:443")]), True, False),
|
||||
]
|
||||
return vh_truth
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user