Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using stream - websocket - error 403 handshake #372

Open
4 tasks
stevendeporre1 opened this issue Dec 10, 2020 · 33 comments
Open
4 tasks

Using stream - websocket - error 403 handshake #372

stevendeporre1 opened this issue Dec 10, 2020 · 33 comments
Labels
a:bug Something isn't working

Comments

@stevendeporre1
Copy link

stevendeporre1 commented Dec 10, 2020

Have you read the documentation?

  • [*] Yes, but it does not include related information regarding my question.
  • Yes, but the steps described in the documentation do not work on my machine.
  • Yes, but I am having difficulty understanding it and wants clarification.

You are setting up gotify in

  • Docker
  • Linux native platform
  • [*] Windows native platform

Describe your problem
Hi, I'm trying to fetch messages through the stream websocket and I always get a 403 handshake error. I've tried every token available and can't find a solution.

`
<script type="text/javascript">
var socket = new WebSocket('ws://localhost/stream?token=xxxxxxxxx');
socket.onerror = function() {
console.log()
};

    socket.onopen = function() {
       // alert('handshake successfully established. May send data now...');
	   socket.send("Hi there from browser.");
    };
	socket.onmessage = function (evt) {
            //alert("About to receive data");
            var received_msg = evt.data;
            alert("Message received = "+received_msg);
        };
    socket.onclose = function() {
        alert('connection closed');
    };
</script>

`

What am I doing wrong? Even though the demand for extra documentation was declined in another issue, I'd love to see more than the swagger file.

@stevendeporre1 stevendeporre1 added the question Further information is requested label Dec 10, 2020
@jmattheis
Copy link
Member

Try setting server.stream.allowedOrigins to the host from where you try to access the websocket:

server:
  stream:
    allowedorigins: # allowed origins for websocket connections (same origin is always allowed)
#      - ".+.example.com"
#      - "otherdomain.com"

@stevendeporre1
Copy link
Author

Tried it, but without success. (still handshake 403)

Can the token be the cause for this error? I'm using one of my client tokens.

@jmattheis
Copy link
Member

Please show your configuration file.

@stevendeporre1
Copy link
Author

# Example configuration file for the server.
# Save it to `config.yml` when edited

server:
  keepaliveperiodseconds: 0 # 0 = use system default; set the interval in which keepalive packages will be sent. Only change this value if you know what you are doing.
  listenaddr: "" # the address to bind on, leave empty to bind on all addresses
  port: 8081 # the port the HTTP server will listen on

  ssl:
    enabled: false # if https should be enabled
    redirecttohttps: true # redirect to https if site is accessed by http
    listenaddr: "" # the address to bind on, leave empty to bind on all addresses
    port: 443 # the https port
    certfile: # the cert file (leave empty when using letsencrypt)
    certkey: # the cert key (leave empty when using letsencrypt)
    letsencrypt:
      enabled: false # if the certificate should be requested from letsencrypt
      accepttos: false # if you accept the tos from letsencrypt
      cache: data/certs # the directory of the cache from letsencrypt
      hosts: # the hosts for which letsencrypt should request certificates
#      - mydomain.tld
#      - myotherdomain.tld

  responseheaders: # response headers are added to every response (default: none)
#    X-Custom-Header: "custom value"

  cors: # Sets cors headers only when needed and provides support for multiple allowed origins. Overrides Access-Control-* Headers in response headers.
    alloworigins:
#      - ".+.example.com"
#      - "otherdomain.com"
    allowmethods:
#      - "GET"
#      - "POST"
    allowheaders:
#      - "Authorization"
#      - "content-type"
  stream:
    pingperiodseconds: 45 # the interval in which websocket pings will be sent. Only change this value if you know what you are doing.
    allowedorigins: # allowed origins for websocket connections (same origin is always allowed)
       - "openhab"
#      - "otherdomain.com"

database: # for database see (configure database section)
  dialect: sqlite3
  connection: data/gotify.db

defaultuser: # on database creation, gotify creates an admin user
  name: admin # the username of the default user
  pass: admin # the password of the default user
passstrength: 10 # the bcrypt password strength (higher = better but also slower)
uploadedimagesdir: data/images # the directory for storing uploaded images
pluginsdir: data/plugins # the directory where plugin resides

@stevendeporre1
Copy link
Author

tried it commented out, because I'm sending from the same machine, but with same result.

@jmattheis
Copy link
Member

what is the url gotify is running on and from which url you try to access it?

@stevendeporre1
Copy link
Author

server listening on: openhab:8081 . trying to reach from openhab:80 (which is my local raspberry Pi)

@jmattheis
Copy link
Member

add openhab:8081 as allowed origin.

@stevendeporre1
Copy link
Author

stevendeporre1 commented Dec 15, 2020

still the same and I can see the request coming in.

@jmattheis
Copy link
Member

You are right, this is a bug. You can work around it by also setting server -> cors -> allowedorigins to openhab.

@jmattheis jmattheis added a:bug Something isn't working and removed question Further information is requested labels Dec 16, 2020
@stevendeporre1
Copy link
Author

stevendeporre1 commented Dec 17, 2020

It works! Thank you very much!

@tnyeanderson
Copy link

tnyeanderson commented Nov 12, 2021

Having this same issue. I am using Caddy to locally serve a web interface for my app (web.heart.lan) and a gotify server (push.heart.lan). If I go to push.heart.lan in my browser and run the following in the console, it connects and works:

sock1 = new WebSocket('wss://push.heart.lan/stream?token=MYVALIDTOKEN');

The same code run in the console of web.heart.lan does not work and gives the 403 error.

Here is my gotify config (trying everything):

server:
  cors: # Sets cors headers only when needed and provides support for multiple allowed origins. Overrides Access-Control-* Headers in response headers.
    alloworigins:
      - ".*" 
  stream: # Sets cors headers only when needed and provides support for multiple allowed origins. Overrides Access-Control-* Headers in response headers.
    allowedorigins:
      - ".*"
      - "web.heart.lan"
      - ".+.heart.lan"

And here is my Caddyfile if it matters:

web.heart.lan {
  route /* {
    reverse_proxy http://localhost:8081
  }

  tls internal
}


push.heart.lan {
  # Set the port to the one you are using in gotify
  # Websocket support, proxy headers, etc. are enabled by default
  reverse_proxy localhost:8080

  tls internal
}

Looking through the code in stream.go I don't see anything that stands out as to why this shouldn't work. The URL is retrieved from the origin header and the hostname is parsed, and an attempt is made to match the hostname against each of the allowedorigins as regex. Can you describe where the bug is or why it is happening?

Thanks for all your hard work!! :)

@jmattheis
Copy link
Member

@tnyeanderson On what port is web.heart.lan & push.heart.lan served?

@tnyeanderson
Copy link

They are both accessed via 443.

web.heart.lan is reverse proxied to localhost:8081 (where the web interface is listening)

push.heart.lan is reverse proxied to localhost:8080 (where Gotify server is listening)

Both are using self-signed certificates.

Thanks!

@jmattheis
Copy link
Member

Could you try using a non regex in server.cors.allowedorigins. Like this which allows every origin?

server:
  cors: # Sets cors headers only when needed and provides support for multiple allowed origins. Overrides Access-Control-* Headers in response headers.
    alloworigins:
      - "*" 

@tnyeanderson
Copy link

tnyeanderson commented Nov 14, 2021

I've actually tried both. Here was my original config before switching to regex:

server:
  cors: # Sets cors headers only when needed and provides support for multiple allowed origins. Overrides Access-Control-* Headers in response headers.
    alloworigins:
      - "*" 
  stream: # Sets cors headers only when needed and provides support for multiple allowed origins. Overrides Access-Control-* Headers in response headers.
    allowedorigins:
      - "*"
      - "web.heart.lan"
      - "*.heart.lan"

It did not work with this config either. I'd also like to note that it looks like the * syntax would only work in the cors.alloworigins section, but not in stream.allowedorigins section based on the code. I could be wrong though :(

EDIT: Also, just now I tried with the following config (asterisk in cors, regex in stream) which also did not work:

server:
  cors: # Sets cors headers only when needed and provides support for multiple allowed origins. Overrides Access-Control-* Headers in response headers.
    alloworigins:
      - "*" 
  stream: # Sets cors headers only when needed and provides support for multiple allowed origins. Overrides Access-Control-* Headers in response headers.
    allowedorigins:
      - ".*"
      - "web.heart.lan"
      - ".+.heart.lan"

@jmattheis
Copy link
Member

@tnyeanderson does something like this work?

  cors:
    alloworigins:
      - ".*"
  stream:
    allowedorigins:
     - ".*"

@tnyeanderson
Copy link

@jmattheis With the configuration you provided, I still get a 403 error :(

@jmattheis
Copy link
Member

In my tests, it does work. Does gotify/server log something?

@tnyeanderson
Copy link

heart-sms-push       | [GIN] 2021/11/19 - 19:44:20 | 403 |       47.09µs |       127.0.0.1 | GET      "/stream?token=[masked]"

@tnyeanderson
Copy link

tnyeanderson commented Nov 19, 2021

The first (403) is running the console code above from web.heart.lan. The second (one that works) is running the same console code on push.heart.lan

heart-sms-push       | Starting Gotify version 2.1.0@2021-09-27-16:04:38
heart-sms-push       | Started Listening for plain HTTP connection on :80
heart-sms-push       | [GIN] 2021/11/19 - 19:54:59 | 403 |      40.527µs |       127.0.0.1 | GET      "/stream?token=[masked]"
heart-sms-push       | [GIN] 2021/11/19 - 19:55:08 | 200 |     112.894µs |       127.0.0.1 | GET      "/health"
heart-sms-push       | [GIN] 2021/11/19 - 19:55:10 | 200 |     255.364µs |       127.0.0.1 | GET      "/stream?token=[masked]"

EDIT: This is with the config you shared above

@tnyeanderson
Copy link

@jmattheis Realized I forgot to mention you in my response :)

@jmattheis
Copy link
Member

jmattheis commented Nov 19, 2021

No need, I'll get notifications for everything in the gotify org (:. Can you show the whole config that you used with these changes

@tnyeanderson
Copy link

Those are my entire configs :) I don't have anything else in my config.yml

@jmattheis
Copy link
Member

My config:

# Example configuration file for the server.
# Save it to `config.yml` when edited

server:
  cors:
    alloworigins:
      - ".*"
  stream:
    allowedorigins:
     - ".*"
  port: 8080 # the port the HTTP server will listen on

image

Dunno, what's happening in your setup. Most likely, the config.yml that you've defined isn't used.

@tnyeanderson
Copy link

tnyeanderson commented Nov 19, 2021

Hmmm... I know it was being used before because I would get syntax errors when I screwed it up.

I've created a zip file that minimally reproduces the issue:
gotify-403.zip

To reproduce:

  1. Unzip and cd into the directory
  2. docker-compose up -d
  3. Add web.heart.lan and push.heart.lan to /etc/hosts for your local machine
  4. Go to both pages in your browser and accept the certificate to continue
  5. Go to the console in each page and type:
    sock1 = new WebSocket('wss://push.heart.lan/stream?token=CyFxTkNiaIg8TVn');
    
  6. push.heart.lan will connect just fine, but web.heart.lan will not.

Maybe I'm doing something wrong and you can point me in the right direction....

@jmattheis
Copy link
Member

Yep, config.yml won't be used in your example. You need to mount the config.yml on /app

services:
  heart-sms-push:
    container_name: heart-sms-push
    image: gotify/server
    ports:
      - 8080:80
    environment:
      - GOTIFY_DEFAULTUSER_PASS=custom
    volumes:
      - "./gotify_data:/app/data"
      - "./gotify_data/config.yml:/app/config.yml"

@tnyeanderson
Copy link

Face.... meet palm. It works! Is this path documented anywhere? Not sure why I was getting errors before when my config file was malformed.... must have been a fluke.

Thanks!

@jmattheis
Copy link
Member

nope, not documented. But the docs state

When using docker it is recommended to use environment variables.

https://gotify.net/docs/config

@tnyeanderson
Copy link

tnyeanderson commented Nov 19, 2021

Yeah, to be honest, I took one look at the escapes required to use env vars for these config parameters and decided to go with a regular yaml file.

Thanks again for the help!!! :)

@tienthanh2509
Copy link

tienthanh2509 commented Mar 24, 2022

Not work for me too

Workaround by deleting Origin header sent from the client browser
The config allowedorigins not working for both IP address and hostname

Sample Caddy config to fix this issues

{
	http_port 8085
	https_port 8084
	#experimental_http3
}

http:// {
	reverse_proxy {
                # Gotify server
		to http://localhost:8086
                # Drop Origin header
		header_up -Origin
	}
}

Nginx config

location / {
    proxy_set_header Origin "";

}

@mschipperheyn
Copy link

Having the same issue. Tried everything.

BTW, did you notice that the environment variable names for allowedorigins and alloworigins are inverted vis-a-vis the docs?

version: '3.1'

services:
  gotify:
    image: gotify/server-arm64
    container_name: gotify
    volumes:
      - .docker_volumes/gotify:/app/data
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    ports:
      - '8080:80'
    environment:
      - TZ=America/Chicago
      - GOTIFY_SERVER_CORS_ALLOWEDORIGINS=".*"
      - GOTIFY_SERVER_STREAM_ALLOWORIGINS=".*"
      - GOTIFY_SERVER_LOGLEVEL=debug

@mschipperheyn
Copy link

Ah, got it to work. It was in fact the ALLOW vs ALLOWED. I got thrown off because it allows you to use those vars in some scenarios and fails in some scenarios where you use it correctly. This is the winning ticket:

version: '3.1'

services:
  gotify:
    image: gotify/server-arm64
    container_name: gotify
    volumes:
      - .docker_volumes/gotify:/app/data
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    ports:
      - '8080:80'
    environment:
      - TZ=America/Chicago
      - GOTIFY_SERVER_CORS_ALLOWORIGINS=[.*]
      - GOTIFY_SERVER_STREAM_ALLOWEDORIGINS=[.*]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
a:bug Something isn't working
Development

No branches or pull requests

5 participants