https.py 5.3 KB

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