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
|
# 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 server certificate
DEFAULT_CERTIFICATE = os.path.join(os.getcwd(), "server.pem")
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_CERTIFICATE, 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=(key, 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()
|