FastAPI with uvicorn: TLS connection dropped after Client Hello - ssl

I do run a simple FastAPI server listening on https:
uvicorn main:app --reload --ssl-keyfile=./certs/app-key.pem --ssl-certfile=./certs/full.chain.pem --port 9063 --host 192.168.2.201 --ssl-version=2
It works fine, i can use Postman to access the app via https://192.168.2.201:9063.
But my client is actually a network device (using exactly same API as Postman) and that is failing to establish TLS connection. Device is configured correctly to trust CA certificate. When troubleshooting i have found out what is happening:
network device establishing TCP connection to 192.168.2.201:9063
network device sends TLS Client Hello (TLS envelope is version 1.0, but inner Client Hello is version 1.2)
FastAPI app sends empty ACK and then FIN
So it looks like uvicorn does not like that TLS proposal: version or ciphers (my guess). This is what is happening:
I have tried all version uvicorn with --ssl-version=1 but got error invalid or unsupported protocol version 1.
When testing with nmap i can see uvicorn is supporting only TLS 1.2 and 1.3:
michal#certs % nmap --script ssl-enum-ciphers -p 9063 192.168.2.201
Starting Nmap 7.93 ( https://nmap.org ) at 2022-12-27 12:27 CET
Nmap scan report for ise.example.com (192.168.2.201)
Host is up (0.00024s latency).
PORT STATE SERVICE
443/tcp open https
| ssl-enum-ciphers:
| TLSv1.2:
| ciphers:
| TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (ecdh_x25519) - A
| TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (ecdh_x25519) - A
| compressors:
| NULL
| cipher preference: server
| TLSv1.3:
| ciphers:
| TLS_AKE_WITH_AES_256_GCM_SHA384 (ecdh_x25519) - A
| TLS_AKE_WITH_CHACHA20_POLY1305_SHA256 (ecdh_x25519) - A
| TLS_AKE_WITH_AES_128_GCM_SHA256 (ecdh_x25519) - A
| cipher preference: server
|_ least strength: A
Is there any way to troubleshoot it ? How to confirm why uvicorn is finishing that TLS session ?
Update: i was able to pass uvicorn argument --ssl-version=3 which looks like is TLS1.0, but still the network device proposal is rejected by uvicorn.
Thanks,
Michal

OK, solved the issue, the problem was in the incompatible ciphers used by default by uvicorn (no match between very specific network device proposal and uvicorn).
Once i have discovered all supported ciphers:
import ssl
ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
ciphers = ctx.get_ciphers()
v12ciphers = ":".join([cipher['name'] for cipher in ciphers if cipher['protocol'] =='TLSv1.2'])
print(v12ciphers)
And used all of those when starting uvicorn all started to work fine (using TLS 1.2):
--ssl-version=5 --ssl-ciphers="ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256"

Related

How to force 'OpenConnect' client to use TLS 1.0

I'm using 'OpenConnect version v8.05' on Red Hat Enterprise Linux 8.1 (Ootpa) in order to connect to a server.
The server only accepts SSLv3, TLSv1.0 ciphers and I don't have access to the server for security update/upgrade.
When I try to connect:
[root#RHEL8 ~]# openconnect --authenticate XXX.XXX.XXX.XXX:443 -status -msg -debug
MTU 0 too small
POST https://XXX.XXX.XXX.XXX/
Connected to XXX.XXX.XXX.XXX:443
SSL negotiation with XXX.XXX.XXX.XXX
SSL connection failure: A packet with illegal or unsupported version was received.
Failed to open HTTPS connection to XXX.XXX.XXX.XXX
Failed to obtain WebVPN cookie
I have changed OpenSSL Min SSL Protocol by changing:
/etc/crypto-policies/back-ends/opensslcnf.config
MinProtocol = TLSv1.0
Now I'm able to handshake the server using 'openssl s_client -connect'. But the openconnect client is not yet able to connect to the server.
How can I force it to use TLS 1.0?
I have filed an issue on their community issue tracker and got useful info.
It is possible to allow this insecure connection with any version newer than 8.05(currently not available on rpm repositories) as mentioned by the maintainer:
$ ./openconnect --gnutls-priority "NONE:+VERS-SSL3.0:+VERS-TLS1.0:%NO_EXTENSIONS:%SSL3_RECORD_VERSION:+3DES-CBC:+ARCFOUR-128:+MD5:+SHA1:+COMP-ALL:+KX-ALL" ***

PFSense OpenVPN TLS Handshake failed

I have a PFSense Router with a OpenVPN Server running. It was working perfectly fine. Today I tried to connect and got the following error in my OpenVPN Client:
Mon Nov 11 21:18:02 2019 TLS Error: TLS key negotiation failed to occur within 60 seconds (check your network connectivity)
Mon Nov 11 21:18:02 2019 TLS Error: TLS handshake failed
I sniffed the tcppackets incoming on the PFSense OpenVPN Server. The PFSense does not seem to answer the packets coming from my client, the packets are all the same like this (captured at the interface of the PFSense where OpenVPN Connections arrive):
1 0.000000 78.43.*.* 192.168.1.156 OpenVPN 84 MessageType: P_CONTROL_HARD_RESET_CLIENT_V2
On the PFSense the OpenVPN Server is bound to the right interface. Everything else seems normal. My Client is Pingable from the OpenVPN Server. Ports on both sides are filtered|open for OpenVPN. Can someone help?
Did you configure the Authentication TLS key in the OpenVPN ? (on both server/client or nothing)

How do i disable SSL V3/TLS 1.0 for OpenLDAP or how do i disable TLS 1.0 support on ldap port 636?

Tried the following as per this article, https://access.redhat.com/articles/1474813
Place tls.ldif config file with below config instructions in it:
dn: cn=config
changetype: modify
replace: olcTLSProtocolMin
olcTLSProtocolMin: 3.2
TLS 1.0 is still showing up as enabled for port 636. I need to enable support for tls 1.1 and higher. The above url provides solution for RHEL 7 but i am using RHEL 6, maybe that's a reason why this solution is not working for me.
This answer applies to Red Hat Identity Manager (and possibly also FreeIPA).
To set a minimum version of TLS for the Directory Server component, do the follwing:
Stop the dirsrv service: systemctl stop dirsrv#YOUR-DOMAIN.service
ensure that:sslVersionMin: TLS1.2
is set in the file /etc/dirsrv/slapd-YOURDOMAIN-COM/dse.ldif
Start the dirsrv service again: systemctl start dirsrv#YOURDOMAIN-COM.service
To check the offered TLS versions you can use the ssl-enum-ciphers script for nmap like so:
nmap --script ssl-enum-ciphers -p636 localhost
The output should only show TLS versions higher than what you specified in the dse.ldif file:
$ nmap --script ssl-enum-ciphers -p636 localhost
Starting Nmap 6.40 ( http://nmap.org ) at 2020-05-13 12:03 CEST
Nmap scan report for localhost (127.0.0.1)
Host is up (0.000070s latency).
Other addresses for localhost (not scanned): 127.0.0.1
PORT STATE SERVICE
636/tcp open ldapssl
| ssl-enum-ciphers:
| TLSv1.2:
| ciphers:
| TLS_DHE_RSA_WITH_AES_128_CBC_SHA - strong
| TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 - strong
| TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 - strong
| TLS_DHE_RSA_WITH_AES_256_CBC_SHA - strong
| TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 - strong
| TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 - strong
| TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA - strong
| TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 - strong
| TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA - strong
| TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 - strong
| TLS_RSA_WITH_AES_128_CBC_SHA - strong
| TLS_RSA_WITH_AES_128_CBC_SHA256 - strong
| TLS_RSA_WITH_AES_128_GCM_SHA256 - strong
| TLS_RSA_WITH_AES_256_CBC_SHA - strong
| TLS_RSA_WITH_AES_256_CBC_SHA256 - strong
| TLS_RSA_WITH_AES_256_GCM_SHA384 - strong
| compressors:
| NULL
|_ least strength: strong
Nmap done: 1 IP address (1 host up) scanned in 0.09 seconds
Keep in mind that this change needs to be done on all servers where the dirsrv service is running.

Erlang YAWS https tax

Since everything should be https, I enabled it, and noticed how much slower https is compared to http.
I have a Ubuntu/YAWS server in Dallas. I start YAWS using "yaws --daemon --nodebug"
If I do
time curl -i https://share.spreadsheetconverter.com/echo/
and
time curl -i http://share.spreadsheetconverter.com/echo/
from the server itself, https takes about 100ms and http 20ms, i.e. the difference is 80ms.
When we try from Sweden, Europe, https is 1400ms and http is 350ms. These figures can be logical, due to the latency over the Atlantic.
However, now to the strange thing.
I also have a Windows/IIS server in Dallas.
If I compare simple http-get request on both servers, the difference https-penalty is much bigger for the YAWS server than for IIS. (I have also tested Tomcat, and it behaves similar to IIS).
It also seems to be latency dependent, i.e. the longer from the server you are, the bigger is the difference between IIS and YAWS.
When I do similar tests with the IIS server in Dallas, from Sweden, https is 1000ms, and http is the same as for YAWS, i.e. IIS is much faster (400ms) at https than Yaws. It is almost as if YAWS makes an extra network call.
I have also been experimenting with
http://tools.pingdom.com/fpt/
and just extracted the SSL time reported by them. Notice that SSL time increases faster for YAWS
| YAWS | IIS
Dallas | 79ms | 75ms
New York | 212ms | 87ms
Amsterdam | 503ms | 315ms
Ok, what should I do?
Is there a bug in my YAWS setup?
Will placing NGINX in front of YAWS solve the problem, and let Nginx handle https?
Is there a bug in my SSL cert? Can they be faster or slower? I have checked with https://www.ssllabs.com/ssltest/analyze.html
Update 2015-08-20
I updated to yaws 2.0, and yes, the performance difference is still there.
By using
curl -v --trace-time --trace-ascii echo.log https://share.spreadsheetconverter.com/echo/
and comparing it with
curl -v --trace-time --trace-ascii server1.log https://www.spreadsheetserver.com/server1/
I compared all the rows, I see that we loose 300ms at one single row.
This is how it looks when we talk to Yaws 2.0
17:37:54.606668 == Info: TLSv1.2, TLS handshake, Finished (20):
17:37:54.606692 => Send SSL data, 16 bytes (0x10)
0000: ......Jb.9...#.^
17:37:54.758726 == Info: TLSv1.2, TLS change cipher, Client hello (1):
17:37:54.758761 <= Recv SSL data, 1 bytes (0x1)
0000: .
17:37:55.107695 == Info: TLSv1.2, TLS handshake, Finished (20):
17:37:55.107726 <= Recv SSL data, 16 bytes (0x10)
0000: ..........Y.xV.!
17:37:55.107784 == Info: SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA
and this when I talk to IIS
17:40:25.247308 == Info: TLSv1.0, TLS handshake, Finished (20):
17:40:25.247329 => Send SSL data, 16 bytes (0x10)
0000: ........f4..qh:(
17:40:25.376893 == Info: TLSv1.0, TLS change cipher, Client hello (1):
17:40:25.376925 <= Recv SSL data, 1 bytes (0x1)
0000: .
17:40:25.377081 == Info: TLSv1.0, TLS handshake, Finished (20):
17:40:25.377103 <= Recv SSL data, 16 bytes (0x10)
0000: ....C..'.A,..'R.
17:40:25.377142 == Info: SSL connection using TLSv1.0 / AES128-SHA
For both Yaws and IIS, the first "Send SSL data" takes 150 ms
For IIS, two immediate "Recv SSL data" follows with no delay.
However, in the Yaws case, we have to wait 350ms for the first "Recv SSL data", and then the next is immediate
It is like something is async in IIS, but sync in Yaws. In IIS, the data to be received is combined with the ack of the Send, but in Yaws, it is two separate requests.
All these requests have to pass the Atlantic Ocean. If I instead do it with the same data center, the differences are much much smaller.
Nginx or HAproxy can solve this problem.
You should terminate your HTTPS traffic on proxy node. And after that you should pass HTTP traffic on erlang node.
Moreover keep the erlang node in internet without proxy is not a good practice.

nginx proxy pass to sslv3 upstream

I'm struggling to proxy with nginx to an SSL upstream. I realize that proxying to HTTPS is wasteful, but here's my setup, sometimes the API is accessed directly, other times I'm using nginx to serve a JS app which is also a client of the API, CORS and browser security mandates that the JS app communicates with the same domain as the app is served from:
+--------------------+ +---------------------+
| |+-------------------->| |
| Pure HTTP API Host | | CLI Tool API Client |
| |<--------------------+| |
+--------------------+ +---------------------+
| ^ (:3152)
| |
| | | +---------------------+
| +--------------------------------| |
| | | Javascript App |
+---------------------------------->| |
| +---------------------+
|
nginx proxy for CORS
With that out of the way, here's the stack. The API Host is written in GoLang, served using a signed certificate from StartSSL:
$ openssl s_client -ssl3 -connect local.api.theproject.io:3251
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
SSL-Session:
Protocol : SSLv3
Cipher : AES256-SHA
Session-ID:
Session-ID-ctx:
Master-Key: E021B27717F5A4
Key-Arg : None
Start Time: 1377589306
Timeout : 7200 (sec)
Verify return code: 21 (unable to verify the first certificate)
I've truncated that output, but sufficed to say that Go's ListenAndServeTLS only appears to work with SSLv3, as the following fails:
$ openssl s_client -connect local.api.theproject.io:3251
CONNECTED(00000003)
35899:error:1407742E:SSL routines:SSL23_GET_SERVER_HELLO:tlsv1 alert protocol version:/SourceCache/OpenSSL098/OpenSSL098-47.1/src/ssl/s23_clnt.c:602:
Thus the problem coming out of nginx is clear:
2013/08/27 09:30:21 [error] 35674#0: *3 kevent() reported that connect() failed (61:
Connection refused) while connecting to upstream, client: 127.0.0.1, server:
local.www.theproject.io, request: "GET / HTTP/1.1", upstream: "https://[::1]:3251//",
host: "local.www.theproject.io:4443"
2013/08/27 09:30:21 [error] 35674#0: *3 SSL_do_handshake() failed (SSL: error:1407742
E:SSL routines:SSL23_GET_SERVER_HELLO:tlsv1 alert protocol version) while SSL handshaking
to upstream, client: 127.0.0.1, server: local.www.theproject.io, request: "GET /
HTTP/1.1", upstream: "https://127.0.0.1:3251//", host: "local.www.theproject.io:4443"
(Note: I'm using [::1] here, but that's not significant, it also fails, of course on 127.0.0.1)
Thus the question is, what is missing from:
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_pass https://local.api.theproject.io:3251/;
in order to get this to proxy correctly using SSLv3 internally?
Have you tried "ssl_ciphers ALL;"?
Although that's not recommended (because that allows weak ciphers), that shall narrow down the scope of the problem. If that doesn't work, most likely the cause of your problem is that the openssl you use doesn't have the suitable ciphers to complete the SSL handshake with your Go server.
Note that Go's tls package is only "partially" implemented and the supported ciphers is very limited.
There are two solutions:
You'll have to upgrade your openssl version that supports what Go's tls package already implemented. And then, of course, recompile your nginx.
You'll have to patch tls package to support whatever your current openssl ciphers provides by adding the appropriate suite ids to cipherSuites in tls/cipher_suites.go (I think)