Loomio with docker-compose behind nginx proxy with SAML (/cable error)

Hi,
I'm using a personal configuration using loomio-deploy for the Loomio instance of my organization.
I'm getting an error (look a bit similar from this which make Loomio refuse SAML login...
My nginx logs are
nginx.1 | 2020/03/18 16:35:46 [error] 1759#1759: *13363 connect() failed (111: Connection refused) while connecting to upstream, client: 77.198.223.246, server: agora.xxxxx-xxxxx.org, request: "GET /cable HTTP/1.1", upstream: "http://172.22.0.21:3000/cable", host: "agora.xxxxx-xxxxx.org"
My Loomio app logs are
An unauthorized connection attempt was rejected
Failed to upgrade to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: upgrade, HTTP_UPGRADE: websocket)
Finished "/cable/" [WebSocket] for 77.198.223.246 at 2020-03-18 16:36:27 +0000
Finished "/cable/" [WebSocket] for 77.198.223.246 at 2020-03-18 16:36:27 +0000
Do you have any idea how to fix this ? If you need more information, just tell me !
Thanks

Colin Fletcher Wed 18 Mar 2020 8:04PM
I'm sorry, I read your post too quickly. I don't have a SAML login setup, so can't be experiencing the same problem.
That said, the reason I thought I could help is that I recognized the log errors. Are you hosting Loomio on its own server as recommended, or are you trying to host it behind your own reverse proxy? Can you outline your setup?
If the latter, the app logs look like the errors I was getting when I didn't have the /cable path proxied separately, or when it was listed in the config file after the root path. I don't know how nginx does things, but my apache settings look like this:
ProxyPass "/cable" "ws://localhost:3000/cable"
ProxyPass "/" "http://localhost:3000/"
There's more; I've posted the full virtualhost config file here:
https://www.loomio.org/d/79SCuOd6/how-to-use-apache-as-a-front-end-in-a-self-hosted-installation

Jordan Thu 19 Mar 2020 1:46AM
Hi. Thank you for your answer ! I don't think it's a SAML problem, but the same problem than you :-)
Yes i'm using Nginx as my own reverse proxy with jwilder/nginx-proxy (https://hub.docker.com/r/jwilder/nginx-proxy/)
With this service, it made automatic configuration. I don't really know how to modify my nginx config... If anyone can help ?
# yyyy.xxxx.org
upstream yyyy.xxxx.org {
## Can be connected with "speculoos-network" network
# loomio-app
server 172.22.0.20:3000;
## Can be connected with "speculoos-network" network
# loomio-worker
server 172.22.0.21:3000;
}
server {
server_name yyyy.xxxx.org;
listen 80 ;
access_log /var/log/nginx/access.log vhost;
return 301 https://$host$request_uri;
}
server {
server_name yyyy.xxxx.org;
listen 443 ssl http2 ;
access_log /var/log/nginx/access.log vhost;
ssl_session_timeout 5m;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
ssl_certificate /etc/nginx/certs/yyyy.xxxx.org.crt;
ssl_certificate_key /etc/nginx/certs/yyyy.xxxx.org.key;
ssl_dhparam /etc/nginx/certs/yyyy.xxxx.org.dhparam.pem;
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/nginx/certs/yyyy.xxxx.org.chain.pem;
add_header Strict-Transport-Security "max-age=31536000" always;
include /etc/nginx/vhost.d/default;
location / {
proxy_pass http://yyyy.xxxx.org;
}
}
Luc Machiels Fri 20 Mar 2020 9:49AM
In your env file, please add
ACTION_CABLE_URL=[ws://*/, wss://*/]
Let me know if that fixes the websocket issue. Thanks.

Jordan Fri 20 Mar 2020 12:01PM
Hi & thank you for your answer. This parameter goes in the Loomio env or Nginx env ?
Luc Machiels Fri 20 Mar 2020 12:02PM
Loomio env

Jordan Fri 20 Mar 2020 12:46PM
Ok. I refresh all my Loomio docker installation, then put your modification in my env.
I don't have "/cable" error anymore in my nginx logs. But I still have this in loomio-app logs... (and still the SAML connection error)
EDIT: I feel like it's more a SAML problem now but it's just intuition. And... the loomio/loomio:stable have a lot of warning during initialization, don't know if it can be a cause of all this bug.
source=rack-timeout id=31c7bf14-1916-4815-a657-3af87fbc77b0 timeout=15000ms state=ready
Started GET "/cable" for 77.198.223.246 at 2020-03-20 12:41:27 +0000
Started GET "/cable/" [WebSocket] for 77.198.223.246 at 2020-03-20 12:41:27 +0000
Successfully upgraded to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: upgrade, HTTP_UPGRADE: websocket)
source=rack-timeout id=31c7bf14-1916-4815-a657-3af87fbc77b0 timeout=15000ms service=26ms state=completed
An unauthorized connection attempt was rejected
Failed to upgrade to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: upgrade, HTTP_UPGRADE: websocket)
Finished "/cable/" [WebSocket] for 77.198.223.246 at 2020-03-20 12:41:27 +0000
Finished "/cable/" [WebSocket] for 77.198.223.246 at 2020-03-20 12:41:27 +0000
source=rack-timeout id=31c3bf3d-1316-4087-83b7-0217a312413f timeout=15000ms state=ready
Started GET "/saml/oauth?back_to=https://yyyy.xxxx.org/dashboard" for 77.198.223.246 at 2020-03-20 12:41:31 +0000
Processing by Identities::SamlController#oauth as HTML
Parameters: {"back_to"=>"https://yyyy.xxxx.org/dashboard"}
Redirected to https://key.xxxx.org/auth/realms/myrealm/protocol/saml?SAMLRequest=fZFdb4IwFIb%2FCne9glLwY2mAhGiWmDhndPNiN0uFgzYrLespbv77AcbMJdtuT97nOV8Jilo1PG%2FdUW%2FgvQV0Xo4I1kmjZ0ZjW4Pdgj3JAp43y5QcnWuQUyoOxopA6FNhmoj5SioFgbEH2gupEZ2QePNOJ7XoXd%2FkG5x%2F43qCWhCqRpqvdrPHdcRoY40zhVGDlXiLeUpeq33FongS%2BgKmsT8ai9i%2Fm4Tgi31ZVuPpfsJY2EURW1hodEK7lERhFPph7EfhE4v4iPGYvRBvBxaH0aKgIz5rpZH3jVLSWs2NQIlcixqQu4Jv84cl74JcXM9zizT%2FM9c9SJb0aT5MZzNlTC1NQm9ryeUjq86xmK%2BNksXZy5UyH7PuOA5S4mwLxLs3thbu764sYENFln41RDnUQqq8LC0gEppduv58ffYF
Completed 302 Found in 70ms (ActiveRecord: 0.0ms)
source=rack-timeout id=31c3bf3d-1316-4087-83b7-0217a312413f timeout=15000ms service=77ms state=completed
source=rack-timeout id=82e809e7-b956-444b-bce3-61618713a67f timeout=15000ms state=ready
Started POST "/saml/oauth" for 77.198.223.246 at 2020-03-20 12:41:31 +0000
Processing by Identities::SamlController#create as HTML
Parameters: {"SAMLResponse"=>"PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIERlc3RpbmF0aW9uPSJodHRwczovL2Fnb3JhLmFudmNvcDIxLWxpbGxlLm9yZy9zYW1sL29hdXRoIiBJRD0iSURfZGYzZWUzZTYtOTUyNC00OWZlLWI1NzMtYjBjODU1M2I1ZWZmIiBJc3N1ZUluc3RhbnQ9IjIwMjAtMDMtMjBUMTI6NDE6MzEuNDM3WiIgVmVyc2lvbj0iMi4wIj48c2FtbDpJc3N1ZXI+aHR0cHM6Ly9rZXkuYW52Y29wMjEtbGlsbGUub3JnL2F1dGgvcmVhbG1zL0FOVkNPUDIxPC9zYW1sOklzc3Vlcj48ZHNpZzpTaWduYXR1cmUgeG1sbnM6ZHNpZz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+PGRzaWc6U2lnbmVkSW5mbz48ZHNpZzpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PGRzaWc6U2lnbmF0dXJlTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxkc2lnLW1vcmUjcnNhLXNoYTI1NiIvPjxkc2lnOlJlZmVyZW5jZSBVUkk9IiNJRF9kZjNlZTNlNi05NTI0LTQ5ZmUtYjU3My1iMGM4NTUzYjVlZmYiPjxkc2lnOlRyYW5zZm9ybXM+PGRzaWc6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz48ZHNpZzpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48L2RzaWc6VHJhbnNmb3Jtcz48ZHNpZzpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNzaGEyNTYiLz48ZHNpZzpEaWdlc3RWYWx1ZT4ydkFLSWRWSEwvWGhob0pqU0QzdVhLenJPMnlzVGkyeWM3cjMrQzVKVEhjPTwvZHNpZzpEaWdlc3RWYWx1ZT48L2RzaWc6UmVmZXJlbmNlPjwvZHNpZzpTaWduZWRJbmZvPjxkc2lnOlNpZ25hdHVyZVZhbHVlPkNHN0JtZW5pUkdsOTVJMHZZZysyWWk4ZmZCSUNDYnc2b1ltMk5tTkJDMFhFMlMyRWlOd3hYUlRubkNJejNEcnV3c2R6TVVRQU9QM2pvbDhkb2lLQkZnY0U4Rm5hdnFJaGJpakpkZEFGc0IvMmRrSGlETUNBZE5WbGpPTDVFQVB3TGhQVmxrNnczSW1WL3pYVi94ZS83ZU9BVUtXTS9KbVM3L3NXWEczWEsyQVNTOFhuQXJlNjl0S3gxSHlDOEt1c203ejV0KzJkd0RBN090NzFxU3JCeGRzS1hWSnlIRjBjNUFoUzJ6UHg3Y2ppRi9JZ29aQUlySEJvQ0VYNXd5MnpIQnNGYWZMN0RRaWRRV3h0dnYwcHY3VGI1ZDB1NVBJVGdwU2l6SDJHUWpOTkhKYy9RN0xhc2tYbE0yWWlidHVHN2FhWTl4cEhPQkhmNUNjTTdCRFRhdz09PC9kc2lnOlNpZ25hdHVyZVZhbHVlPjxkc2lnOktleUluZm8+PGRzaWc6S2V5TmFtZT5tVExIMkROM19UR1N0Q2M4WU1kcm9QTGp5QnoxYmhSSGp1TWZ2NlRKMm9VPC9kc2lnOktleU5hbWU+PGRzaWc6WDUwOURhdGE+PGRzaWc6WDUwOUNlcnRpZmljYXRlPk1JSUNuekNDQVljQ0JnRnVEaVFzd2pBTkJna3Foa2lHOXcwQkFRc0ZBREFUTVJFd0R3WURWUVFEREFoQlRsWkRUMUF5TVRBZUZ3MHhPVEV3TWpjeE5qVXpNREphRncweU9URXdNamN4TmpVME5ESmFNQk14RVRBUEJnTlZCQU1NQ0VGT1ZrTlBVREl4TUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFrQXl5Wml1dXZFMXhiYXFGVitEY1dPZ2N4R2E1SWpCR29qeG1Zd1ZmQlFIc1FCUFc5VUp3QjBnYTZBRDZEUlg4Q2ljVVpKTEVZU1lwUmhxeHQ5NnhmYTZwaEQ5ZGMzOGNJbDhqU21MZEluRXA0VzAxWmVGVjdoNDVaclBjblZHK1RseWQvallNVnhJR2g2RldVd2FtR1J5ejREWEdxUTYwdFpJQlJtTTVXR1kwQ1Nwc2UrSWJVUnp4SXd6RWl6TEp2QlZZUWZxMFJWRnl2ZzN4OE8zYWR6UTZmTE5SWDNNTlVxV25iRURZTzNFR2VuN0x2eCtIUVZkRUlzNTVxNGRGOERsakNqc09CRXA2bjVpT2VDU3VTWnpCSXhpdlczSUZRZUUyWk9CVS81T3lxaGJoQkZSREg1Z2JqTUxhc3NGTDQrUFJFNGQyNGZmdzBHMW1SSE9YQXdJREFRQUJNQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0SUJBUUNCRUU1MUNjczVOYkRqdzRaRHhZV3ovV0pMMFlMb2FsNFMxNTYyaFEwTXJuNnAwOXlpSnlNR0JsaEtCT25NcDRLZ0lFZUJnSytzN0ZPZEsyMWd1VldIOUxBa1ZtRk8xSFd1amhWa0JqdXdLZ09qdlQ5V1RMTHZyV3JCdkN3NlNMamd5ZWlMVTJmOVQ2RWsyOUxlV2N4Z1pjMlFvOG5ZQ2lPOFEwbEplZXZldzdJSzZIcWFYeEJEdTE2UCtGNXFBWlFZdkp6K1dqODUxdHF3Kzh1RDl2ZDg0SnF1TldlM0pxeFoxMlNRa3JLYjRLT290YlBOSXBxUWpPMDVQMkNZQWExSXlsbFpVUDljWEtlVi9PT1pIZWR5Rk5MYnJ5WGRyY0tjRTJUL1B6MENsZlZkcXpJdGtvQm9BSXB0cDF3MlRBd1BrVkptWmR3R0dKcWkvaHdqTE9pMTwvZHNpZzpYNTA5Q2VydGlmaWNhdGU+PC9kc2lnOlg1MDlEYXRhPjxkc2lnOktleVZhbHVlPjxkc2lnOlJTQUtleVZhbHVlPjxkc2lnOk1vZHVsdXM+a0F5eVppdXV2RTF4YmFxRlYrRGNXT2djeEdhNUlqQkdvanhtWXdWZkJRSHNRQlBXOVVKd0IwZ2E2QUQ2RFJYOENpY1VaSkxFWVNZcFJocXh0OTZ4ZmE2cGhEOWRjMzhjSWw4alNtTGRJbkVwNFcwMVplRlY3aDQ1WnJQY25WRytUbHlkL2pZTVZ4SUdoNkZXVXdhbUdSeXo0RFhHcVE2MHRaSUJSbU01V0dZMENTcHNlK0liVVJ6eEl3ekVpekxKdkJWWVFmcTBSVkZ5dmczeDhPM2FkelE2ZkxOUlgzTU5VcVduYkVEWU8zRUdlbjdMdngrSFFWZEVJczU1cTRkRjhEbGpDanNPQkVwNm41aU9lQ1N1U1p6Qkl4aXZXM0lGUWVFMlpPQlUvNU95cWhiaEJGUkRINWdiak1MYXNzRkw0K1BSRTRkMjRmZncwRzFtUkhPWEF3PT08L2RzaWc6TW9kdWx1cz48ZHNpZzpFeHBvbmVudD5BUUFCPC9kc2lnOkV4cG9uZW50PjwvZHNpZzpSU0FLZXlWYWx1ZT48L2RzaWc6S2V5VmFsdWU+PC9kc2lnOktleUluZm8+PC9kc2lnOlNpZ25hdHVyZT48c2FtbHA6U3RhdHVzPjxzYW1scDpTdGF0dXNDb2RlIFZhbHVlPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6c3RhdHVzOlJlc3BvbmRlciI+PHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6SW52YWxpZE5hbWVJRFBvbGljeSIvPjwvc2FtbHA6U3RhdHVzQ29kZT48L3NhbWxwOlN0YXR1cz48L3NhbWxwOlJlc3BvbnNlPg=="}
/usr/local/bundle/gems/activerecord-5.2.3/lib/active_record/transactions.rb:212: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
/usr/local/bundle/gems/activerecord-5.2.3/lib/active_record/connection_adapters/abstract/database_statements.rb:260: warning: The called method `transaction' is defined here
/usr/local/bundle/gems/activerecord-5.2.3/lib/active_record/connection_adapters/abstract/transaction.rb:171: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
/usr/local/bundle/gems/activerecord-5.2.3/lib/active_record/connection_adapters/abstract/transaction.rb:97: warning: The called method `initialize' is defined here
/usr/local/bundle/gems/activemodel-5.2.3/lib/active_model/naming.rb:190: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
/usr/local/bundle/gems/i18n-1.8.2/lib/i18n.rb:195: warning: The called method `translate' is defined here
/usr/local/bundle/gems/activemodel-5.2.3/lib/active_model/translation.rb:67: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
/usr/local/bundle/gems/i18n-1.8.2/lib/i18n.rb:195: warning: The called method `translate' is defined here
/usr/local/bundle/gems/activemodel-5.2.3/lib/active_model/errors.rb:430: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
/usr/local/bundle/gems/i18n-1.8.2/lib/i18n.rb:195: warning: The called method `translate' is defined here
/usr/local/bundle/gems/actionpack-5.2.3/lib/abstract_controller/translation.rb:21: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
/usr/local/bundle/gems/i18n-1.8.2/lib/i18n.rb:195: warning: The called method `translate' is defined here
Rendering errors/400.html.haml within layouts/basic
Rendered errors/400.html.haml within layouts/basic (8.5ms)
Completed 400 Bad Request in 3377ms (Views: 3256.5ms | ActiveRecord: 6.2ms)
source=rack-timeout id=82e809e7-b956-444b-bce3-61618713a67f timeout=15000ms service=3383ms state=completed
Luc Machiels Fri 20 Mar 2020 4:09PM
It looks like you still have the websocket problem (from the nginx log). Make sure that you have in your nginx configuration:
map $http_upgrade $proxy_connection {
default upgrade;
'' close;
}
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $proxy_connection;

Jordan Fri 20 Mar 2020 4:32PM
Yes it's on my nginx configuration..
Thank you for you time >.<
Fabio dos Santos Wed 15 Apr 2020 1:02AM
Just to add confirmation to this:
I'm trying to run Loomio on Linode.com, as part of a non-profits website. I got loomio setup appropriately. Can load the website on the browser,, but have not been able to setup the email correctly yet.
I had a similar error, which I observed in the logs (docker-compose logs -f).
Something about /cable/ and [websocket].
I entered the loomio-deploy/ngix/conf.d/custom.conf and added the information on the post above, by @Luc Machiels .
Fabio dos Santos Wed 15 Apr 2020 1:04AM
Now I get the following error:
loomio-app | Started GET "/cable" for 177.194.12.130 at 2020-04-15 00:56:43 +0000
loomio-app | Started GET "/cable/"[non-WebSocket] for 177.194.12.130 at 2020-04-15 00:56:43 +0000
loomio-app | Failed to upgrade to WebSocket (REQUESTMETHOD: GET, HTTPCONNECTION: upgrade, upgrade, HTTP_UPGRADE: websocket, websocket)
loomio-app | Finished "/cable/"[non-WebSocket] for 177.194.12.130 at 2020-04-15 00:56:43 +0000
I don't really understand what this error is about, but would appreciate any feedback.
Also. This does not really seem to be related to the email problem.
One issue at a time, I supposed!
Thanks for the wonderful program!

Colin Fletcher Wed 15 Apr 2020 1:52AM
I think I've seen this error. I'm using apache, but I expect it's the same problem if you can figure out how to address it with nginx. Here's a snippit from my apache solution:
# The loomio code is running without encryption, because the apache
# front end is providing that. So the code listening for websockets
# doesn't like that the Origin header begins with "https://". Looking
# through the source, if FORCE_SSL is off it just wants whatever the value
# of CANONICAL_NAME is in the env file.
RequestHeader edit Origin "https://" ""
The line removes the protocol from the Origin header in the request as it passes through the reverse proxy, which turns it into what the Loomio backend is expecting.
Though, now that I think about it, I seem to remember Loomio complaining about an Origin mismatch in the logs for that error. Well, it's something to try, anyway.
Colin Fletcher · Wed 18 Mar 2020 5:48PM
Oh, I got this!
I'm hosting behind apache, but had to solve the same problem -- I'm sure the principle is the same. Let me find my configuration and get back to you.