-
-
Notifications
You must be signed in to change notification settings - Fork 754
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
Support custom SSL Context #806
Comments
related #776 |
I have some questions @samypr100 :)
|
Hi @euri10, thanks for taking time and considering this.
|
can you point me where, this is interesting. ok @samypr100 now that #807 and #808 got merged you can use the fixtures and expand on them for you PR should you be fancy providing one, something like the below should work ?
once there is a PR layout then maybe we can discuss more proactively about what to do with the CLI |
Absolutely! Thanks! |
I have submitted a PR related to this issue- added param of ssl.Options list to the config of ssl_context, we are trying to resolve a vulnerability of DoS by setting the ssl.OP_NO_RENEGOTIATION to the ssl_options of ssl_context. |
PR welcome for what @samypr100 proposes. |
hello @Kludex I have started working on passing ssl_context directly to uvicorn.run via config.py as below.
in main.py and in the config.py
and it didnt like the passing of ssl.SSLContext object into Config, on the server startup its failing to pickle the SSLContext object. here is stack trace of the server startup error:
I need to know how we can pass the ssl_context into uvicorn.run if this is not the correct way.. please advise. |
Can we instead pass a function that returns a def ssl_context_factory() -> SSLContext:
...
if __name__ == "__main__":
uvicorn.run("main.app", ssl_context_factory=ssl_context_factory, reload=True) |
@Kludex something like this worked not sure if you meant to do this way..
in config.py
but its not much of different than passing all parameters that are needed for creating a ssl_context inside config.py, I dont think we can create a custom ssl_context object and pass it into config.py |
I think the idea is more to call the factory when needed on each worker. Similar to: benoitc/gunicorn#2649 |
thanks for the hint, here is the PR - #1815, please review |
In Gunicorn version 21.0+ the code that mapped the CLI flag --ssl-version TLSv1_2 into an int was removed. This causes the uvicorn workers to always throw errors when trying to set a specific tls version and cyphers. |
hi @desean1625 currently I guess we are on 0.24.0 Uvicorn and passing ssl_version=int(ssl.PROTOCOL_TLS_SERVER) and ssl_ciphers=allowed_ciphers to the uvicorn.run() for our use case, if we are going to get impacted with new change, what will be the alternative for this change. |
@aswanidutt87 yes with gunicorn 21.0 and uvicorn 0.24 I am seeing this because a change in gunicorn.
I tried I suppose my question is would uvicorn add a check to parse |
This PR has been un attended since a year now, I am trying to add a custom ssl to the uvicorn as we need ssl_options and somebody in future might need something else in ssl_context so thought adding a custom ssl_context pass to uvicorn.run() a good idea, but couldn't go much far. @Kludex helped me until some point reviewing the changes and its been an year. |
I am also interested in the ability to pass an SSL context. my use case is to have HTTPS using PSK instead of certificates |
@Kludex , the need for passing custom ssl_context to the uvicorn is gaining momentum, please help me close this PR, its been two years that we are struggling to close this.
|
@Kludex Has this feature been added yet? Seems like a pretty reasonable option as users may want to setup their own SSL context instead of being limited by basic parameters. |
The shortest workaround I've found thus far, is the following. config = uvicorn.Config(
app,
host="127.0.0.1",
port=1789,
# We could define it here, and override the default context
# But omitting these two make sure no context is created:
# ssl_keyfile=privkey,
# ssl_certfile=certificate,
log_level="info",
reload=False
)
ssl_ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
get_password = (lambda: password) if password else None
# `cert_file` and `key_file_name` are paths that `ssl*.load_cert_chain()` can access:
ssl_ctx.load_cert_chain(certfile=cert_file_name, keyfile=key_file_name, password=get_password)
ssl_ctx.verify_mode = ssl.VerifyMode(ssl.CERT_NONE)
# Options omitted here, that the default context creation handles:
# ssl_ctx.load_verify_locations(ca_certs)
# ssl_ctx.set_ciphers(ciphers)
config.load() # Manually calling the .load() to trigger needed actions outeside of HTTPS
# calling .loaded() multiple times will do nothing the
# following times, because unicorn
# caches of it's loaded or not.
# And once that's done, we force in the ssl context
# since it won't be replaced after .loaded ()
config.ssl = ssl_ctx
uvicorn = uvicorn.Server(
config
)
uvicorn.run() This is part of a larger snippet which is slightly unrelated to this conversation: import fastapi
import datetime
import tempfile
import ssl
import uvicorn
from cryptography import x509
from cryptography.x509.oid import NameOID
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
app = fastapi.FastAPI()
password = "passphrase"
key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
)
# Write our key to disk for safe keeping
privkey = key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=serialization.BestAvailableEncryption(password.encode()),
)
subject = issuer = x509.Name([
x509.NameAttribute(NameOID.COUNTRY_NAME, "US"),
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "California"),
x509.NameAttribute(NameOID.LOCALITY_NAME, "San Francisco"),
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "My Company"),
x509.NameAttribute(NameOID.COMMON_NAME, "mysite.com"),
])
cert = x509.CertificateBuilder().subject_name(
subject
).issuer_name(
issuer
).public_key(
key.public_key()
).serial_number(
x509.random_serial_number()
).not_valid_before(
datetime.datetime.now(datetime.timezone.utc)
).not_valid_after(
# Our certificate will be valid for 10 days
datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(days=10)
).add_extension(
x509.SubjectAlternativeName([x509.DNSName("localhost")]),
critical=False,
# Sign our certificate with our private key
).sign(key, hashes.SHA256())
# Write our certificate out to disk.
certificate = cert.public_bytes(serialization.Encoding.PEM)
config = uvicorn.Config(
app,
host="127.0.0.1",
port=1789,
# We could define it here, and override the default context
# But omitting these two make sure no context is created:
# ssl_keyfile=privkey,
# ssl_certfile=certificate,
log_level="info",
reload=False
)
ssl_ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
get_password = (lambda: password) if password else None
with tempfile.NamedTemporaryFile("wb") as cert_file, tempfile.NamedTemporaryFile("wb") as key_file:
cert_file.write(certificate)
cert_file.flush()
cert_file_name = cert_file.name
key_file.write(privkey)
key_file.flush()
key_file_name = key_file.name
ssl_ctx.load_cert_chain(certfile=cert_file_name, keyfile=key_file_name, password=get_password)
ssl_ctx.verify_mode = ssl.VerifyMode(ssl.CERT_NONE)
# Options omitted here, that the default context creation handles:
# ssl_ctx.load_verify_locations(ca_certs)
# ssl_ctx.set_ciphers(ciphers)
config.load() # Manually calling the .load() to trigger needed actions outeside of HTTPS
# And once that's done, we force in the ssl context
config.ssl = ssl_ctx
uvicorn = uvicorn.Server(
config
)
uvicorn.run() The code could be reduced to use existing certificates. |
Checklist
Is your feature related to a problem? Please describe.
I would like to pass an existing SSL Context to
uvicorn.run()
. For example, I have a certificate that needs a password to load. Typically I would do that by setting up a context like so:The current options are limited to these kinds of advance scenarios and I'd like to avoid keep adding/requesting
--ssl-xyz
options for each of those scenarios. I know I can decrypt the key before loading it into python, but I'm limited on the environment I need to deploy on since I'm given the encrypted key and the password via a secret.Describe the solution you would like.
Adding the ability to pass a
ssl_context
touvicorn.run
in python code that supersedes any of thessl_*
settings if provided.Example changes in
uvicorn/config.py
:Describe alternatives you considered
Searched source code to see if there was a way to pass a custom context to no avail.
Additional context
Since ssl context is createe via python, it would not quite be supported via command line. Unless we want to get fancy. I can attempt to do a PR if permitted. Thanks!
Important
The text was updated successfully, but these errors were encountered: