Erlang YAWS https tax - ssl

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.

Related

ClientHello Retransmitted many times followed by TCP DUP ACK during SSL handshake

we have one client and server our application running as bridge between client and server.
our module responsible for forwarding the traffic between client and server.
During SSL handshake i am trying to interrupt the client hello and respond to the client with serverhello.
we are able to interrupt and send the serverhello to the client but SSL handshake is failed.
captured the packets during SSL handshake.
i could see Server hello reached to the client interface but Client machine retransmitting the clienthello again and again could any one help on this what went wrong why the client not processing serverhello.

site works on chrome but not on curl

The error is this:
* Connected to www.****.com (213.74.254.54) port 443 (#0)
* ALPN, offering http/1.1
* TLSv1.0 (OUT), TLS header, Certificate Status (22):
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS header, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (OUT), TLS header, Unknown (21):
* TLSv1.2 (OUT), TLS alert, handshake failure (552):
* error:0A000152:SSL routines::unsafe legacy renegotiation disabled
* Closing connection 0
curl: (35) error:0A000152:SSL routines::unsafe legacy renegotiation disabled
Same URL opens just fine on chrome. I tried to copy as curl from chrome and run using curl too, same error. So maybe somehow chrome is more slack on the SSL negotiation. How can I make curl behave the same?
curl version:
curl --version
curl 7.80.0 (x86_64-apple-darwin19.6.0) libcurl/7.80.0 OpenSSL/3.0.1 zlib/1.2.11 zstd/1.5.2 libidn2/2.3.2 libpsl/0.21.1 (+libidn2/2.3.2)
Release-Date: 2021-11-10
Protocols: dict file ftp ftps gopher gophers http https imap imaps mqtt pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: alt-svc AsynchDNS HSTS HTTPS-proxy IDN IPv6 Largefile libz NTLM NTLM_WB PSL SSL TLS-SRP UnixSockets zstd
I was having the same issues after upgrading to ubutun22 and openssl 3.0.2.
Using the steps by dave_thompson_085 with client pointing to a sect with the new option did not work for me. But adding the Options = UnsafeLegacyRenegotiation line to the system_default_sect at the bottom of the file did:
openssl_conf = openssl_init
[openssl_init]
ssl_conf = ssl_configuration
[ssl_configuration]
system_default = system_default_sect
[system_default_sect]
CipherString = DEFAULT:#SECLEVEL=2
Options = UnsafeLegacyRenegotiation
Meta: this isn't really programming or development; I will delete if necessary but I think superuser would be suitable and have voted for that (but we'll see what others think). I can see equally good arguments for apple.SX or security.SX instead, but (AFAIK) those would require a mod.
While Steffen is correct the server is either badly out of date or misconfigured and should be fixed, OpenSSL below 3.0.0 (just last year) by default will connect to a non-RFC5746 server as long as the server does not actually use the unsafe 'legacy' (RFC5246 et pred) renegotiation.
Thus if you can get/use a curl using older OpenSSL it should just work. I'm not sure what the situation is for Mac on this, for example if brew or fink or similar has this available. Worst case, both OpenSSL and curl are opensource so you could build your own older versions -- and that would be ontopic for SO, but a fair bit of work.
Alternatively, as long as curl doesn't work to mess it up (which I wouldn't expect, but don't know for sure), OpenSSL 1.1.0 up can control this with runtime configuration:
Either edit your systemwide config file openssl.cnf in the directory identified by openssl version -d (affects all programs and processes) or create your own file anywhere suitable and point to it with environment variable OPENSSL_CONF. See the man page for config(5) on your system or the web.
In the default section (beginning of the file to the first line wrapped in square brackets) add if not already present an item openssl_conf = sect1 where sect1 is conventionally openssl_init but can be any section-name unique in the file.
Create sect1 if not already present, and add if not already present an item ssl_conf = sect2 where sect2 is conventionally ssl_configuration but as above.
Create sect2 if not already present, and add if not already present an item client = sect3 where sect3 is conventionally client_tls_config but as above.
Create sect3 if not already present, and add if not already present an item for Options = whose value is (or is a comma-separated list including) UnsafeLegacyServerConnect. See the man page for SSL_CONF_cmd(3) on your system or the web.

HAproxy SSL handshake failure

when i use HAproxy as load balancer, at HTTP termination mode and i tail log of it
(tail -f /var/log/haproxy.log). There are 2 types of log appearing
[time] frontend_name/1: SSL handshake failure
and
[time] frontend_name~ message
frontend_name is name follow frontend keyword config in /etc/haproxy/haproxy.cfg
I don't know what /1 and ~ in log message is, and why SSL handshake failure appearing at log has ~
Can someone help me explain and fix this error?
Thanks!
~ after frontend name means connection has been established using SSL/TLS
You can find reference to it in %ft entry in the table at: https://cbonte.github.io/haproxy-dconv/2.4/configuration.html#8.2.4
About /1 in frontend_name/1: SSL handshake failure:
I can't find it in the docs, but by experimenting i found it's the number of port in frontend, to which connection was attempted and SSL handshake failed.
For config:
frontend frontend_name
bind *:443,*:444 ssl crt <path_to_cert>
bind *:445 ssl crt <path_to_cert> no-tlsv13
If i make TLS1.3 connection to port 445 (e.g. openssl s_client -connect 127.0.0.1:445 -tls1_3), i will get:
frontend_name/3: SSL handshake failure
because 445 is 3. port listed in this frontend.
[UPDATE]
I found a bit more. Error log format explains that /1 in frontend_name/1 is bind_name and can be declared:
bind *:443,*:443 ssl crt <path_to_cert> no-tlsv13 name bind_ssl_foo
will result in frontend-name/bind_ssl_foo: SSL handshake failure.
Unfortunately we can't change error log format.
To learn more we have to make that connection successful and that most likely requires us to lower security (FOR DEBUGGING ONLY!). Normal clients will still negotiate highest security they can, TLS 1.2 or 1.3.
bind *:443 ssl crt <path_to_cert> ssl-min-ver TLSv1.0
Since haproxy 2.2 default for ssl-min-ver is TLSv1.2.
Second step is to log SSL version, negotiated cipher and maybe whole cipherlist send by client by appending %sslv %sslc and maybe %[ssl_fc_cipherlist_str] to your log-format:
log-format "your_log_format_here %sslv %sslc %[ssl_fc_cipherlist_str]"
If you don't have your own log format you can extend HTTP format:
log-format "%ci:%cp [%tr] %ft %b/%s %TR/%Tw/%Tc/%Tr/%Ta %ST %B %CC %CS %tsc %ac/%fc/%bc/%sc/%rc %sq/%bq %hr %hs %{+Q}r %sslv %sslc %[ssl_fc_cipherlist_str]"
To use ssl_fc_cipherlist_str we need to set tune.ssl.capture-cipherlist-size 800 in global section, because default is 0.
sslv is SSL/TLS version client connected with.
sslc is SSL/TLS cipher client connected with.
ssl_fc_cipherlist_str is cipher list client offered when negotiating SSL/TLS connection. It can be long. Use if you are extra curious.
That will append to your logs info like this:
TLSv1 ECDHE-RSA-AES256-SHA ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-RSA-AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-RSA-AES128-SHA,AES256-SHA,AES128-SHA,TLS_EMPTY_RENEGOTIATION_INFO_SCSV
Match by IP previous errors with current entries and you will know what TLS version and ciphers they were using. Then decide whether to adjust your ciphers or force this client to upgrade their SSL software.
So all required changes below:
global
log /dev/log daemon
tune.ssl.capture-cipherlist-size 800
frontend frontend_name
bind *:443 ssl crt <path_to_cert> ssl-min-ver TLSv1.0
log-format "%ci:%cp [%tr] %ft %b/%s %TR/%Tw/%Tc/%Tr/%Ta %ST %B %CC %CS %tsc %ac/%fc/%bc/%sc/%rc %sq/%bq %hr %hs %{+Q}r %sslv %sslc %[ssl_fc_cipherlist_str]"
mode http
(...)
Again, lower security only for debugging if this connection error really is a problem for you.

TLS handshake of clients

I'm working on an academic project about TLS handshakes and i have captured some TLS traffic generated by multiple clients (google chrome, firefox...) and I want to see if for a given browser the client hello message will always be the same or no (I have removed the GREASE extensions because they are added in a random way to the client hello message and I omitted the SNI). I found that the same browser generate multiple client hello messages.
Is it normal to see such behavior or I'm doing something wrong?
A TLS handshake is done for each TCP connection involved in HTTPS and it is common that the browser uses multiple TCP connections in parallel. This is probably what you see. Multiple TLS handshakes within the same TCP connection are uncommon but might happen if a server requires a client certificate only for a specific path and thus triggers a renegotiation.

Misdirected redirect with 3 domains on EV SSL

Error:
The client needs a new connection for this request as the requested host name does not match the Server Name Indication (SNI) in use for this connection.
I recently purchased a EV SSL certificate from Comodo, installed it on my VPS (cPanel/WHM) and everything worked great. I then upgraded to http2 and am now receiving the error when switching between each website on the certificate. The 3 websites share the same IP address. From what I can tell, this may be the issue. I do not want to reissue a SSL cert for each domain as I paid for the EV multi domain cert. Is the answer to purchase 2 additional IPs and make sure each domain has its own IP? Or is there a way I can edit the virtual hosts so that I can maintain the same setup I have now?
I should mention, this is only happening on Safari, not chrome.
SSL Labs Report
https://www.ssllabs.com/ssltest/analyze.html?d=www.deschutesdesigngroup.com&s=142.4.0.142&hideResults=on
EasyApache HTTP vhost configuration
https://pastebin.com/dNeFRGWJ
EasyApache HTTPS vhost configuration
https://pastebin.com/vgWAD5mg
You have enabled HTTP/2 on only two of the three sites.
HTTP/2 will try to reuse the connection for multiple domains if both the IP address matches and the certificate covers all the necessary domains. This is the case here and so HTTP/2 is reused.
However if you run SSLLabs on all three domains you see a slight difference in the protocol used for Chrome (for example):
Chrome 70 / Win 10 RSA 2048 (SHA256) TLS 1.2 > h2
Chrome 70 / Win 10 RSA 2048 (SHA256) TLS 1.2 > http/1.1
Chrome 70 / Win 10 RSA 2048 (SHA256) TLS 1.2 > h2
And similarly further down in the ALPN setting:
ALPN Yes h2 http/1.1
ALPN Yes http/1.1
ALPN Yes h2 http/1.1
So going to the middle domain first will work as it will connect via HTTP/1.1 and so not reuse the connection. However going to the middle domain after initiating a request to either the first or last domain will attempt to reuse the HTTP/2 connection and fail as the middle domain doesn't support HTTP/2.
Web servers should return a 421 Misdirected Request status code for any requests when the browser attempts to reuse the connection when it shouldn't, to say "Yeah you really shouldn't be attempting to reuse the connection here! Can you try again on another connection please?". The same thing happens if there are different SSL/TLS setup (e.g. the cipher suite used for the connection is not accepted on the other domain).
Chrome and Firefox correctly handle the 421 response and transparently resend the requests over a new connection, which in this case then uses HTTP/1.1 (check out developer tools in the browser and you'll see this is true). Other browsers, including Safari used by iOS, have not implemented support of the relatively new 421 status code yet and so fail with an error like below:
Misdirected Request
The client needs a new connection for this request as the requested
host name does not match the Server Name Indication (SNI) in use for
this connection.
I presume there is no reason not to enable HTTP/2 on all domains and this was a misconfiguration error? If so enable HTTP/2 in all domains and your issue should be sorted.
If you do not want HTTP/2 on all domains, then you ensure the browser doesn't think it can reuse the connection. That means either using a separate IP address for that domain, or getting the certificate reissued for only two domains, and a separate certificate for the other than shouldn't share connections.