Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tidied up #1815 #2297

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions tests/server.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
-----BEGIN CERTIFICATE-----
MIIFozCCA4ugAwIBAgIUYFGwS/vDAQNfUry6qiNhr7c6xvYwDQYJKoZIhvcNAQEL
BQAwYTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEaMBgGA1UEAwwRdXZpY29ybi5sb2Nh
bGhvc3QwHhcNMjQwNDA1MDc1MjU0WhcNMzQwNDAzMDc1MjU0WjBhMQswCQYDVQQG
EwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lk
Z2l0cyBQdHkgTHRkMRowGAYDVQQDDBF1dmljb3JuLmxvY2FsaG9zdDCCAiIwDQYJ
KoZIhvcNAQEBBQADggIPADCCAgoCggIBAMxgwPo6zf6NeB3UFieLSTsZ0h2HB8Nx
YlV4Mh4fhlk6YXr5xNuFCgV5Q4ZVGA5tjlphaTxSfQLpH+Wo6LX1Xj6xoPGrGtCp
rfW6VaL/AByVSXKFPsyW32BjsXgLmIg63eh9lLBjKpH/M8/YfR4xqFDnassYyRxw
OJ+OFLmKkvG8QNN4Z14sqYL5gg7bnAeCEj+kZowuaW5yCCbArwii+eGTS0P5Mufh
xD/TlclWnNA1EO1NVSbimj3FkZ3vqqRK6bUu0HEPoaoLOkdsY4JFbFMGl3x+dc9u
LJuBNFnnGHQgHwxrughAf2+tvSnOdofWUFI/1LaNdUo+qN6rATdbbyAYLfn7aR/f
PwrTmNBG8GYd3FVQEw6hfRNGr4pHo6awC7izb7DAssxDkwetxuhXXO2fB5N04UYF
ZlQAxrDCORlo9atUexZMTOvQIUHaHFeIj+HasTBs+D0gEQRgvQbA0+orpyF1yczV
7laMvY/41BOvLoPaY8GQ33WrDFxuO2tRV7RF4+5rozC951nP78dU0cG5VXS2UqIj
hz2A3iYEjSc52uKMdEdlfdPkDYI8v4qivnv7J893i3rcAWeRqzkXnHRwPfobCtG/
Nnbgo9fwuqbIVLBNAw02JCgSYbLLRxgcMwV9t8Jj5Xd06v35lL1gunsKg6aPRH7g
5hYecI271akrAgMBAAGjUzBRMB0GA1UdDgQWBBQzMQrX2PX397oBZwfl98mw1fmd
MTAfBgNVHSMEGDAWgBQzMQrX2PX397oBZwfl98mw1fmdMTAPBgNVHRMBAf8EBTAD
AQH/MA0GCSqGSIb3DQEBCwUAA4ICAQAf36TLtdQOChLeQok5Vr9x2woyxYROr3XA
V0GO/ZLxTJsRGXwb5GP/3vr1g0gmsEFWi4lYhlxmOPY12zQShf772IncgS8o2TYg
NMoEqBRlTlWVXbyHZGXcKEdQ6Vlt/yvscJ8c30AYPIJRDzsI8W8BehKMxevnSSKh
+tdViEVU1T0bck1pgLyc2gkV64ZlnjPFFT5s65TGTp+DGzsDkExMPwQ3GE0mzWEU
dKLjAHK0VHqmanXmrhWIG2/oKVQybHqP7Df+8HFz54G4ubxdixR8LU6dV630xoAt
/s6+BvdpplQoWS+WZhgFoOcpr36DoUNboZn0c0QGGKP/aqf6RwQFCyDurmkasXRi
6JGMcMt0NFDMykTkuY6UkDcOIINhUqfrNkF7qwuOwfgWB7xB2d4i/9YLofxZo3wK
Hc317ATQRgKzI/1igPaW7bk6pJxhpYCsvgWyi0MSFctvN+R3J+AKJiQAg9RmvFRJ
Xet4mnj+aEDDp7k8KemHxHN9bCIkeHU2ZvvVPS5PzSkPbK/JyfcMpraXfOAHdKVv
kRLll+7G4itsu45MzQnh6qq5bR7b18Qgee3o8iKfZKCzct7HEUU91H89jfCCl62L
V6L2pS4YXe8N3Bdjc5aSy3lAw6RiDzR3eoQaLzqLd3RyAEp7/UmptwWhaHOUNma+
IeY7UK1ylg==
-----END CERTIFICATE-----
52 changes: 52 additions & 0 deletions tests/server.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
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
n+nYHLy1vGIssY5ODapJLixk/nt/Jg==
-----END PRIVATE KEY-----
44 changes: 43 additions & 1 deletion tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import logging
import os
import socket
import ssl
import sys
import typing
from pathlib import Path
Expand All @@ -25,7 +26,7 @@
Scope,
StartResponse,
)
from uvicorn.config import Config
from uvicorn.config import Config, is_dir
from uvicorn.middleware.proxy_headers import ProxyHeadersMiddleware
from uvicorn.middleware.wsgi import WSGIMiddleware
from uvicorn.protocols.http.h11_impl import H11Protocol
Expand Down Expand Up @@ -267,6 +268,19 @@ def test_socket_bind() -> None:
sock.close()


def test_is_dir() -> None:
class P:
@staticmethod
def is_absolute():
return True

@staticmethod
def is_dir():
raise OSError

assert not is_dir(path=P) # type: ignore


def test_ssl_config(
tls_ca_certificate_pem_path: str,
tls_ca_certificate_private_key_path: str,
Expand All @@ -281,6 +295,34 @@ def test_ssl_config(
assert config.is_ssl is True


def ssl_context():
context = ssl.SSLContext(int(ssl.PROTOCOL_TLS_SERVER))
allowed_ciphers = "DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK"
context.set_ciphers(allowed_ciphers)
list_options = [ssl.OP_NO_RENEGOTIATION]
for each_option in list_options:
context.options |= each_option
return context


def test_ssl_context() -> None:
config = Config(app=asgi_app, ssl_context=ssl_context)
config.load()
if config.ssl_context is not None:
assert ssl.PROTOCOL_TLS_SERVER is config.ssl_version
assert "TLSv1" in config.ssl_ciphers
if config.ssl is not None:
assert ssl.OP_NO_RENEGOTIATION in config.ssl.options


def test_ssl_context_load_cert() -> None:
config = Config(
app=asgi_app, ssl_context=ssl_context, ssl_certfile="tests/server.crt", ssl_keyfile="tests/server.key"
)
config.load()
assert config.ssl_context is not None


def test_ssl_config_combined(tls_certificate_key_and_chain_path: str) -> None:
config = Config(
app=asgi_app,
Expand Down
38 changes: 36 additions & 2 deletions uvicorn/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,27 @@
logger = logging.getLogger("uvicorn.error")


def update_ssl_context(
ctx: ssl.SSLContext,
certfile: str | os.PathLike[str] | None,
keyfile: str | os.PathLike[str] | None,
password: str | None,
cert_reqs: int,
ca_certs: str | os.PathLike[str] | None,
ciphers: str | None,
) -> ssl.SSLContext:
get_password = (lambda: password) if password else None
if certfile and keyfile:
ctx.load_cert_chain(certfile, keyfile, get_password)
if cert_reqs:
ctx.verify_mode = ssl.VerifyMode(cert_reqs)
if ca_certs:
ctx.load_verify_locations(ca_certs)
if ciphers:
ctx.set_ciphers(ciphers)
return ctx


def create_ssl_context(
certfile: str | os.PathLike[str],
keyfile: str | os.PathLike[str] | None,
Expand Down Expand Up @@ -220,6 +241,7 @@ def __init__(
ssl_cert_reqs: int = ssl.CERT_NONE,
ssl_ca_certs: str | None = None,
ssl_ciphers: str = "TLSv1",
ssl_context: Callable[[], Any] | None = None,
headers: list[tuple[str, str]] | None = None,
factory: bool = False,
h11_max_incomplete_event_size: int | None = None,
Expand Down Expand Up @@ -264,6 +286,7 @@ def __init__(
self.ssl_cert_reqs = ssl_cert_reqs
self.ssl_ca_certs = ssl_ca_certs
self.ssl_ciphers = ssl_ciphers
self.ssl_context = ssl_context
self.headers: list[tuple[str, str]] = headers or []
self.encoded_headers: list[tuple[bytes, bytes]] = []
self.factory = factory
Expand Down Expand Up @@ -395,9 +418,19 @@ def configure_logging(self) -> None:
def load(self) -> None:
assert not self.loaded

if self.is_ssl:
if self.ssl_context:
self.ssl: ssl.SSLContext | None = update_ssl_context(
self.ssl_context(),
keyfile=self.ssl_keyfile,
certfile=self.ssl_certfile,
password=self.ssl_keyfile_password,
cert_reqs=self.ssl_cert_reqs,
ca_certs=self.ssl_ca_certs,
ciphers=self.ssl_ciphers,
)
elif self.is_ssl and not self.ssl_context:
assert self.ssl_certfile
self.ssl: ssl.SSLContext | None = create_ssl_context(
self.ssl = create_ssl_context(
keyfile=self.ssl_keyfile,
certfile=self.ssl_certfile,
password=self.ssl_keyfile_password,
Expand All @@ -406,6 +439,7 @@ def load(self) -> None:
ca_certs=self.ssl_ca_certs,
ciphers=self.ssl_ciphers,
)

else:
self.ssl = None

Expand Down
2 changes: 2 additions & 0 deletions uvicorn/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,7 @@ def run(
ssl_cert_reqs: int = ssl.CERT_NONE,
ssl_ca_certs: str | None = None,
ssl_ciphers: str = "TLSv1",
ssl_context: Callable[[], Any] | None = None,
headers: list[tuple[str, str]] | None = None,
use_colors: bool | None = None,
app_dir: str | None = None,
Expand Down Expand Up @@ -556,6 +557,7 @@ def run(
ssl_cert_reqs=ssl_cert_reqs,
ssl_ca_certs=ssl_ca_certs,
ssl_ciphers=ssl_ciphers,
ssl_context=ssl_context,
headers=headers,
use_colors=use_colors,
factory=factory,
Expand Down
Loading