realm / realm-object-server

Tracking of issues related to the Realm Object Server and other general issues not related to the specific SDK's
https://realm.io
293 stars 42 forks source link

Unable to connect to realm sync when ros is running behind nginx with SSL enabled #311

Closed jaisontj closed 6 years ago

jaisontj commented 6 years ago

Goals

Successfully sync with realms on ros which is running behind a nginx proxy with SSL from an Android app using the realm-java sdk.

ps: Realm studio also does not show any data (i am assuming the connection fails there as well), i just get a loading indicator after login

Expected Results

The android app should be able to connect to realm sync

Actual Results

I am getting the following logs in from the Android app

11-29 14:53:09.020 3358-3382/com.jaison.bompod D/REALM_SYNC: Connection[2]: Connecting to endpoint '104.199.209.119:443' (1/1) 11-29 14:53:09.069 3358-3382/com.jaison.bompod I/REALM_SYNC: Connection[1]: Connected to endpoint '104.199.209.119:443' (from '10.0.2.15:53953') 11-29 14:53:09.129 3358-3382/com.jaison.bompod I/REALM_SYNC: Connection[2]: Connected to endpoint '104.199.209.119:443' (from '10.0.2.15:53954') 11-29 14:53:09.263 3358-3382/com.jaison.bompod D/REALM_SYNC: Connection[1]: WebSocket::initiate_client_handshake() 11-29 14:53:09.324 3358-3382/com.jaison.bompod D/REALM_SYNC: Connection[2]: WebSocket::initiate_client_handshake() 11-29 14:53:09.355 3358-3382/com.jaison.bompod D/REALM_SYNC: Connection[1]: WebSocket::handle_http_response_received() 11-29 14:53:09.355 3358-3382/com.jaison.bompod E/REALM_SYNC: Connection[1]: HTTP: Failed to switch protocols (response status = 400) 11-29 14:53:09.355 3358-3382/com.jaison.bompod I/REALM_SYNC: Connection[1]: Connection closed due to error 11-29 14:53:09.357 3358-3382/com.jaison.bompod D/REALM_SYNC: Connection[1]: Reconnecting in 3496375 milliseconds 11-29 14:53:09.423 3358-3382/com.jaison.bompod D/REALM_SYNC: Connection[2]: WebSocket::handle_http_response_received() 11-29 14:53:09.423 3358-3382/com.jaison.bompod E/REALM_SYNC: Connection[2]: HTTP: Failed to switch protocols (response status = 400) 11-29 14:53:09.424 3358-3382/com.jaison.bompod I/REALM_SYNC: Connection[2]: Connection closed due to error 11-29 14:53:09.425 3358-3382/com.jaison.bompod D/REALM_SYNC: Connection[2]: Reconnecting in 3258990 milliseconds

nginx logs

2017/11/29 09:10:16 [info] 5#0: *655 SSL_do_handshake() failed (SSL: error:14094412:SSL routines:ssl3_read_bytes:sslv3 alert bad certificate:SSL alert number 42) while SSL handshaking, client: 10.2.94.1, server: 0.0.0.0:443

Version of Realm and Tooling

simonask commented 6 years ago

Hi @jaisontj, are you using a self-signed SSL certificate?

jaisontj commented 6 years ago

No I am using Let's Encrypt. I think the problem is mostly with my nginx conf, just that I am not sure where I am going wrong.

alebsack commented 6 years ago

Can you post your nginx configuration? IIRC, websockets need to be handled specifically in the configuration.

rubengarciam commented 6 years ago

Similar issue here. I can access it fine through the browser but, for example, Realm Studio can't connect with handshake error.

Pretty sure - as @jaisontj mentions - it is a nginx config issue.

@alebsack here's my config

/etc/nginx/snippets/ssl.conf

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
ssl_ecdh_curve secp384r1;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off; 
ssl_stapling on; 
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
ssl_dhparam /etc/ssl/certs/dhparam.pem;

And /etc/nginx/sites-available/default

server {
    listen 80;
    listen [::]:80 default_server ipv6only=on;
    server_name SERVER;
    return 301 https://$host$request_uri;
}
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name SERVER;

    ssl_certificate /etc/letsencrypt/live/SERVER/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/SERVER/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/SERVER/fullchain.pem;

    include /etc/nginx/snippets/ssl.conf;

    location / {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-NginX-Proxy true;
    proxy_ssl_session_reuse off;
        proxy_set_header Host $http_host;
        proxy_cache_bypass $http_upgrade;
        proxy_redirect off;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host; 
        proxy_pass http://localhost:9080/;
    }
}
rubengarciam commented 6 years ago

I can confirm the issue is in the proxy configuration for nginx.

Starting the object server without nginx in https mode (have to do it via params, as ros start doesn't retrieve any changes to src/index.ts) via:

ros start --https --https-key "PATH_TO_KEY" --https-cert "PATH_TO_CERT"

works fine and I can connect remotely using Realm Studio.

However, if I start nginx (changing the _proxypass value to https://localhost:9443 ), fails with the same handshake error.

jaisontj commented 6 years ago

This is my conf

http {
    map $http_upgrade $connection_upgrade {
        default upgrade;
        '' close;
    }

    server {
        listen 80;
        server_name ros.cumin48.hasura-app.io;
        rewrite ^ https://$server_name$request_uri? permanent;
    }

    server {
        listen 443;
        ssl on;
        ssl_certificate     /opt/openresty/nginx/certs/cumin48.hasura-app.io/ros.cumin48.hasura-app.io.fullchain.pem;
        ssl_certificate_key /opt/openresty/nginx/certs/cumin48.hasura-app.io/ros.cumin48.hasura-app.io.privkey.pem;
        ssl_session_cache   shared:SSL:50m;
        ssl_session_timeout 5m;
        ssl_prefer_server_ciphers on;
        ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4;
        ssl_stapling on;
        ssl_stapling_verify on;
        resolver 8.8.8.8;

        server_name ros.cumin48.hasura-app.io;

        location / {

            proxy_pass       http://10.3.0.42:80/;
            proxy_set_header Host              $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 $scheme;
            proxy_set_header X-Forwarded-By    "hasura-gateway";
            proxy_set_header X-Hasura-Base-Domain "cumin48.hasura-app.io";

        }

    }
}
morten-krogh commented 6 years ago

Do you use the same certificates for nginx as in the ROS example.

Are these two the same?

/opt/openresty/nginx/certs/cumin48.hasura-app.io/ros.cumin48.hasura-app.io.fullchain.pem

/etc/letsencrypt/live/SERVER/fullchain.pem

alashow commented 6 years ago

I also had this problem, when using Nginx as a proxy. Realm studio 1.18.1 would continually show and close "Checking availability" loading message. Realm studio 1.15.1/1.16.1 showed WebSocket handshake response 502-504 received error message. realm-java also was throwing bad WebSocket handshake errors.

morten-krogh commented 6 years ago

There are potentially two issues here. One is nginx as a WebSocket proxy the other is the SSL certificate check by the clients.

I suggest splitting up the two issues by first getting nginx to be a WebSocket proxy on a non-ssl port.

I think you have to add these two headers to the nginx configuration.

        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";

under proxy_pass.

Try non-ssl proxy first and then lets us see with the SSL issue afterwards.

alashow commented 6 years ago

I already had those lines in my nginx config.

To fix this, I needed to use nginx's upstream. This nginx configuration works without problems:

upstream websocket {
    server 127.0.0.1:9080;
}

server {
    server_name my.domain.com;

    listen 80;
    listen [::]:80 default_server ipv6only=on;
    return 301 https://$host$request_uri;
}

server {
    server_name my.domain.com;

    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    ssl_certificate /etc/letsencrypt/live/my.domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/my.domain.com/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/my.domain.com/fullchain.pem;

    location / {
        proxy_pass http://websocket;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        proxy_redirect off;
        proxy_cache_bypass $http_upgrade;
        proxy_ssl_session_reuse off;
        proxy_set_header Host $http_host;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-NginX-Proxy true;
    }
}
morten-krogh commented 6 years ago

@alashow Does everything work now? Including SSL?

alashow commented 6 years ago

Yep, including SSL.

morten-krogh commented 6 years ago

Good!