SSL in Bluemix and nginx configuration - ssl

I have added a certificate in Bluemix, following this post : https://www.ibm.com/blogs/bluemix/2014/09/ssl-certificates-bluemix-custom-domains/
I can see the certificate in the domain tab, and it's the one I have uploaded.
Now I have a container running nginx, because we use it as a reverse proxy. Previously it was handling the SSL configuration, but now that it's done in Bluemix directly, we just want to accept https request, without configuring certificate.
What we did was forwarding the http requests to https, like advised in the post (explaining how to do it for node.js though). We get something like this:
server {
listen 80;
server_name *hostname.domain*;
return 301 https://$http_host$request_uri;
}
And in the 443 part, we only listen, without the ssl part:
server {
listen 443;
server_name *host.domain*;
*other stuff for reverse proxy*
}
However, when trying to access it, I get a generic error in chrome: ERR_SSL_PROTOCOL_ERROR
Firefox gives a bit more information:
An error occurred during a connection to *host.domain*. SSL received a record that exceeded the maximum permissible length. Error code: SSL_ERROR_RX_RECORD_TOO_LONG
And when I try to check the certificate from command line, I don't get any.
openssl s_client -connect *host.domain*:443
CONNECTED(00000003)
140250419918480:error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol:s23_clnt.c:782:
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 7 bytes and written 289 bytes
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
SSL-Session:
Protocol : TLSv1.2
Cipher : 0000
Session-ID:
Session-ID-ctx:
Master-Key:
Key-Arg : None
PSK identity: None
PSK identity hint: None
SRP username: None
Start Time: 1484673167
Timeout : 300 (sec)
Verify return code: 0 (ok)
---
There's no error in nginx logs, and I can't manage to tell if the issue in on Bluemix side, or in the configuration of nginx, or if nginx allows this kind of configuration where it has to handle https requests, without the certificate configuration...
Does someone have any idea?
Many thanks.
Regards.

If you want NGINX to pass-thru SSL, you have to use the stream module.

Thanks for your answers. I was not able to check your solutions, but I talked with a technical expert from IBM meanwhile, and here is what I learned.
About the SSL pass-thru, we would need to configure each component (behind the nginx) to handle the SSL, so it seems to be harder to manage. I'm not an expert though, so I'm just reporting what I had as an answer on that point.
First, what we want should be doable by removing the public IP adress of our nginx container.
Then, by creating a route from BM load balancer to our nginx container, we should solve the issue. Then the route would be configured to forward the port 443 to the nginx on port 80 (since the container is not publicly available, there no need to handle 80 AND 443).
However, Bluemix allows route only for container groups (for now?). Unfortunately, we use docker-compose that does not allow (for now?) to create container groups on BlueMix.
So the best solution was to put back the ssl configuration in nginx. The certificate being both on BlueMix domain and nginx container. And it's working fine, so we'll just improve the procedure to update the certificate, and wait until there's a need, or a new way to do it...
K.

Related

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.

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.

SSL23_GET_SERVER_HELLO:unknown protocol

I have read all postings on this error, and tried all the answers, but problem is still not resolved.
Running Debian 8 virtual machine created by Google Cloud Platform, created a Self-Signed SSL and configured the default-ssl file as per instructions, and still getting error when connecting to port 443. Here is the command that I ran and the message that I got. Any insightful help would be welcome:
openssl s_client -connect localhost:443
140700056811152:error:140770FC:SSL routines: :s23_clnt.c:782:
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 7 bytes and written 289 bytes
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
SSL-Session:
Protocol : TLSv1.2
Cipher : 0000
Session-ID:
Session-ID-ctx:
Master-Key:
Key-Arg : None
PSK identity: None
PSK identity hint: None
SRP username: None
Start Time: 1530501490
Timeout : 300 (sec)
Verify return code: 0 (ok)
Steffen, you're right; I tried to avoid redundant info, but the devil is in the detail and needed to mention. Thank you for your review and insightful comment.
The good news is that the issue is resolved and here is what I did, in detail, so that those like me who get stuck, can benefit from my experience:
In my most recent attempt that turned out to be successful, I followed the instructions for Apache on this page:
https://www.digitalocean.com/community/tutorials/how-to-install-an-ssl-certificate-from-a-commercial-certificate-authority
What made it different this time is the following:
I included reference to the intermediate certificate (the gd_xxxx_bundle.crt file, which I renamed it to ca-buncle.crt) in my default-ssl.conf file which is:
#SSLCACertificatePath /etc/ssl/certs/
#SSLCACertificateFile /etc/apache2/ssl.crt/ca-bundle.crt
In Wordpress Admin, under Settings/General, the WordPress Address (URL) and Site Address (URL) were pointing to the IP address (before I mapped it to the domain in the DNS). I changed it to https://domainname.com (contrary to some articles that mention editing wp-config.php would do, which I did and it threw me out of wp-admin)
Not sure if the following two edits have an impact on the result, but I have no desire to experiment further with it, but sharing it with you nevertheless:
After running openssl command, I specified the domain name for the prompt Common Name, which is supposed to be an optional field
I added ServerName to
/etc/apache2/sites-enabled/wordpress.conf
/etc/apache2/sites-enabled/default-ssl
/etc/apache2/sites-available/000-default.conf
Finally, there is a site that checks for the certificate:
https://decoder.link/sslchecker//443
That may shed some light on the issue if the certificate doesn't work.
Cheers.

Nginx - SSL handshake error when connecting to upstream with self signed certificate

I am trying to proxy a old server running with self signed certificate.
Simple nginx conf:
server {
listen 8009;
location / {
proxy_ssl_verify off;
proxy_ssl_session_reuse off;
proxy_pass https://192.168.10.20:8009/;
}
}
I get SSL Handshake error in nginx log.
2018/05/02 11:31:39 [crit] 3500#2284: *1 SSL_do_handshake() failed (SSL: error:14082174:SSL routines:ssl3_check_cert_and_algorithm:dh key too small) while SSL handshaking to upstream, client: 127.0.0.1, server: , request: "GET /ping HTTP/1.1", upstream: "https://192.168.10.20:8009/ping", host: "localhost:8009"
I was hoping that adding the "proxy_ssl_verify off;" will ignore all the SSL errors but does not seem to .
ssl3_check_cert_and_algorithm:dh key too small
The problem is that the old server is providing a DH key which is considered insecure (logjam attack). This has nothing to do with certificate validation and thus trying to disable certificate validation will not help - and is a bad idea anyway.
Instead this problem need to be fixed at the server side to provide stronger DH parameters. Alternatively one might try to enforce nginx to not use DH ciphers in the first place by using the proxy_ssl_ciphers parameter. Which ciphers can be chosen there depends on what the old server supports but you might try something like HIGH:!DH as argument which allows nginx to offer all strong ciphers except the DH ciphers.

Nginx Proxy pass certificate autentificate to MS IIS

Nginx 1.9.5 (linux Centos7)--> MS IIS 8.5
So i try to use nginx as client revers proxy for IIS where need client certificate authentication at IIS level.
nginx:443->>IIS:443+client certificate authentications.
example location proxy pass
also here are commented commands which i try.
location ^~ /test/ {
#proxy_buffering off;
#proxy_http_version 1.0;
#proxy_request_buffering off;
#proxy_set_header Connection "Keep-Alive";
#proxy_set_header X-SSL-CERT $ssl_client_cert;
# proxy_ssl_name domain.lv;
#proxy_ssl_trusted_certificate /etc/nginx/ssl/root/CA.pem;
#proxy_ssl_verify_depth 2;
proxy_set_header HOST domain.com;
proxy_ssl_certificate /etc/nginx/ssl/test.pem;
proxy_ssl_certificate_key /etc/nginx/ssl/test_key.pem;
proxy_ssl_verify off;
proxy_pass https://10.2.4.101/;
}
At IIS simple.
create new website.
import CA cert in trusted root.
set ssl cert required.
Test what i get :
Directly browser to IIS client cert required--worked.
Nginx to other nginx client cert required--worked.
Nginx to IIS client cert ignore--worked
Nginx to IIS client cert required or accept - NOT work
ERROR:
Nginx side:
*4622 upstream timed out (110: Connection timed out) while reading response header from upstream
IIS side:
500 0 64 119971
So i hope someone could know why?
EDIT
1. also try from different server with nginx 1.8 nothing helped..
proxy_ssl_verify off;
proxy_ssl_certificate /etc/nginx/ssl/test/test.pem;
proxy_ssl_certificate_key /etc/nginx/ssl/test/test_key.pem;
proxy_pass https://domain.com;
2.Try same with apache 2.4 all worked with
SSLProxyEngine On
SSLProxyVerify none
SSLProxyCheckPeerCN off
SSLProxyCheckPeerName off
SSLProxyCheckPeerExpire off
SSLProxyMachineCertificateFile /etc/httpd/ssl/test.pem
ProxyPass "/test" "https://domain.com"
Maybe something with ssl renegotiation in nginx???
Your hunch about TLS renegotiation is correct. Nginx has not allowed TLS renegotiation since version 0.8.23 (see http://nginx.org/en/CHANGES). However, by default IIS will use TLS renegotiation when requesting a client certificate. (I haven't been able to find the reasons for this - I would be grateful if someone could enlighten me!)
You can use a packet sniffer such as wireshark to see this in action:
IIS and Nginx first perform a TLS handshake using the server certificate only.
Nginx requests the resource.
The resource requires client authentication, so IIS sends a 'Hello Request' message to Nginx to initiate TLS renegotiation.
Nginx doesn't respond to the Hello Request as TLS renegotiation has been disabled.
IIS then closes the connection as it gets no response. (See the section on renegotiation at https://technet.microsoft.com/en-us/library/cc783349(v=ws.10).aspx)
To solve this problem, you must force IIS to request a client certificate on the initial TLS handshake. You can do this using the netsh utility from powershell or the command line:
Open a powershell prompt with administrator rights.
Enter netsh
Enter http
Enter show sslcert. You should see a list of all current SSL bindings on your machine:
Make a note of the IP:port and certificate hash of the certificate that you want to enable client certificate negotiations for. We are now going to delete this binding and re-add it with the Negotiate Client Certificate property set to enabled. In this example, the IP:port is 0.0.0.0:44300 and the certificate hash is 71472159d7233d56bc90cea6d0c26f7a29db1112.
Enter delete sslcert ipport=[IP:port from above]
Enter add sslcert ipport=[IP:port from above] certhash=[certificate hash from above] appid={[any random GUID (can be the same one from the show sslcert output)]} certstorename=MY verifyclientcertrevocation=enable verifyrevocationwithcachedclientcertonly=disable clientcertnegotiation=enable
You can now confirm that this has worked by running show sslcert again. You should see an almost identical output, but with Negotiate Client Certificate set to Enabled:
Note that this method only works for individual certificates - if you need to change or renew the certificate you will have to run these steps again. Of course, you should wrap these up in a batch script or MSI installer custom action for ease of deployment and maintenance.