1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
|
# Copyright (C) 2010-2013 Sebastian Rahlf and others (see AUTHORS).
#
# This program is release under the MIT license. You can find the full text of
# the license in the LICENSE file.
import os.path
from pytest_localserver.http import ContentServer
# default key and certificate
_ROOT = os.path.abspath(os.path.dirname(__file__))
DEFAULT_KEY = os.path.join(_ROOT, "server.key")
DEFAULT_CERTIFICATE = os.path.join(_ROOT, "cert.crt")
class SecureContentServer(ContentServer):
"""
Small test server which works just like :class:`http.Server` over HTTP::
server = SecureContentServer(
port=8080, key='/srv/my.key', cert='my.certificate')
server.start()
print 'Test server running at %s' % server.url
server.serve_content(open('/path/to/some.file').read())
# any call to https://localhost:8080 will get the contents of
# /path/to/some.file as a response.
To avoid *ssl handshake failures* you can import the `pytest-localserver
CA`_ into your browser of choice.
How to create a self-signed certificate
---------------------------------------
If you want to create your own server certificate, you need `OpenSSL`_
installed on your machine. A self-signed certificate consists of a
certificate and a private key for your server. It can be created with
a command like this, using OpenSSL 1.1.1::
openssl req \
-x509 \
-newkey rsa:4096 \
-sha256 \
-days 3650 \
-nodes \
-keyout server.pem \
-out server.pem \
-subj "/CN=127.0.0.1/O=pytest-localserver/OU=Testing Dept." \
-addext "subjectAltName=DNS:localhost"
Note that both key and certificate are in a single file now named
``server.pem``.
How to create your own Certificate Authority
--------------------------------------------
Generate a server key and request for signing (csr). Make sure that the
common name (CN) is your IP address/domain name (e.g. ``localhost``). ::
openssl genpkey \
-algorithm RSA \
-pkeyopt rsa_keygen_bits:4096 \
-out server.key
openssl req \
-new \
-addext "subjectAltName=DNS:localhost" \
-key server.key \
-out server.csr
Generate your own CA. Make sure that this time the CN is *not* your IP
address/domain name (e.g. ``localhost CA``). ::
openssl genpkey \
-algorithm RSA \
-pkeyopt rsa_keygen_bits:4096 \
-aes256 \
-out ca.key
openssl req \
-new \
-x509 \
-key ca.key \
-out ca.crt
Sign the certificate signing request (csr) with the self-created CA that
you made earlier. Note that OpenSSL does not copy the subjectAltName field
from the request (csr), so you have to provide it again as a file. If you
issue subsequent certificates and your browser already knows about previous
ones simply increment the serial number. ::
echo "subjectAltName=DNS:localhost" >server-extensions.txt
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
-set_serial 01 -extfile server-extensions.txt -out server.crt
Create a single file for both key and certificate::
cat server.key server.crt > server.pem
Now you only need to import ``ca.crt`` as a CA in your browser.
Want to know more?
------------------
This information was compiled from the following sources, which you might
find helpful if you want to dig deeper into `pyOpenSSH`_, certificates and
CAs:
- http://code.activestate.com/recipes/442473/
- http://www.tc.umn.edu/~brams006/selfsign.html
-
A more advanced tutorial can be found `here`_.
.. _pytest-localserver CA: https://raw.githubusercontent.com/pytest-dev/pytest-localserver/master/pytest_localserver/ca.crt # noqa: E501
.. _pyOpenSSH: https://launchpad.net/pyopenssl
"""
def __init__(self, host="localhost", port=0, key=DEFAULT_KEY, cert=DEFAULT_CERTIFICATE):
"""
:param key: location of file containing the server private key.
:param cert: location of file containing server certificate.
"""
super().__init__(host, port, ssl_context=(cert, key))
self._cert = cert
@property
def certificate(self):
"""
Returns the path to the server's SSL/TLS certificate file.
Clients can use this path to access and verify the server's identity by
incorporating the certificate.
.. note::
Do not rely on having a stable filesystem path for the returned
certificate path across different versions or test runs.
"""
return self._cert
if __name__ == "__main__": # pragma: no cover
import sys
import time
print("Using certificate %s." % DEFAULT_CERTIFICATE)
server = SecureContentServer()
server.start()
server.logging = True
print("HTTPS server is running at %s" % server.url)
print("Type <Ctrl-C> to stop")
try:
path = sys.argv[1]
except IndexError:
path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "README.rst")
server.serve_content(open(path).read(), 302)
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
print("\rstopping...")
server.stop()
|