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

403 when passing Origin header with Environment config #633

Open
3 of 6 tasks
Ataye opened this issue Feb 7, 2024 · 7 comments
Open
3 of 6 tasks

403 when passing Origin header with Environment config #633

Ataye opened this issue Feb 7, 2024 · 7 comments
Labels
a:feature New feature or request in:server

Comments

@Ataye
Copy link

Ataye commented Feb 7, 2024

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
I have Gotify 2.4.0 running in Docker (using Caprover), with nginx proxy. Web UI runs fine (same origin). Using Postman from Windows also submits messages just fine. The problem is the cors allow origin seems to not be validating the Origin header and logging 403.

I have the following environment variables set (square brackets represent the UI textboxes of Caprover):

[GOTIFY_SERVER_CORS_ALLOWORIGINS] [- \".*\"]
[GOTIFY_SERVER_RESPONSEHEADERS] [X-Custom-Header: test only]

The Server is running from hosting.my-server.com, and Brave is running from localhost:3000'. Brave passes Origin as http://localhost:3000/` as it should as its cross-origin. A postman request with NO Origin results in 200 status, but if i add the same Origin header it fails with 403.

I have tried a bunch of different origin header values but none of them seem to work. Im guessing nginx proxy is not sending Origin but rather X-Forwarded-For which Gotify isn't reading. Or my regex is wrong. Surely .* will match anything?

Adding 'Access-Control-Allow-Origin' '*' to nginx only replies to the client with accepted, Gotify still logs 403.

What can i do here?

Any errors, logs, or other information that might help us identify your problem

Gotify logs for successful and failed requests:

2024-02-07T00:29:49.945130827Z 2024-02-07T00:29:49Z | 200 | 2.456346ms | 1.146.13.223 | POST "/message"
2024-02-07T00:29:50.127354894Z 2024-02-07T00:29:50Z | 200 | 230.165µs | 1.146.13.223 | GET "/static/defaultapp.png"
2024-02-07T00:29:56.480660944Z 2024-02-07T00:29:56Z | 403 | 36.116µs | 1.146.13.223 | POST "/message"

Proxy settings for nginx:

			    proxy_pass $upstream;
			    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;
@Ataye Ataye added the question Further information is requested label Feb 7, 2024
@jmattheis
Copy link
Member

Hey could you try configuring it like this?

version: "3"

services:
  server:
    image: gotify/server:2.4.0
    ports:
      - 8080:80
    environment:
      - GOTIFY_SERVER_CORS_ALLOWORIGINS=[.*]
    volumes:
      - "./gotify_data:/app/data"

This works for me when accessing cross domain:
image

@Ataye
Copy link
Author

Ataye commented Feb 8, 2024

Unfortunately i don't have access to the config file (actually, there is no config file, its all managed via env variables). It's running via Caprover which uses Docker under the hood. No access to the Dockerfile. It passes any environment vars through though, and i've checked this with printenv after an interactive shell:

GOTIFY_SERVER_RESPONSEHEADERS=X-Custom-Header: test only
HOME=/root
GOTIFY_SERVER_STREAM_ALLOWEDORIGINS=- \"*\"\n- \"localhost\"
TERM=xterm
SHLVL=1
GOTIFY_SERVER_PORT=80
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
GOTIFY_SERVER_CORS_ALLOWORIGINS=- \".+.example.com\"\n- \"otherdomain.com\"
_=/usr/bin/printenv
OLDPWD=/app

And with the new env:

GOTIFY_SERVER_CORS_ALLOWORIGINS=[.*]

Caprover does map a volume however, so i did create a simple config there but still no joy. This is the config:

server:
  responseheaders:
    X-Custom-Header: "has a config!"
cors:
  alloworigins:
    - [.*]
    - ".*"
  allowmethods:
    - "OPTIONS"
    - "GET"
    - "POST"

Interstingly, your screenshot doesn't show the preflight OPTIONS call so i guess it assumes its a simple call and skips it. I've switched to vite (from next.js) and axios (rather than fetch) and i am getting the same results. The response from the OPTIONS call is 204 (which is ok), and returns these response headers:

Access-Control-Allow-Methods: \"GET\"\N- \"POST\"\N- \"OPTIONS\"
Access-Control-Allow-Origin: http://localhost:5174
Access-Control-Max-Age: 43200
Connection: keep-alive
Content-Type: application/json
Date: Thu, 08 Feb 2024 22:40:13 GMT
Server: nginx
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
X-Custom-Header: has a config!

Why is Allowed-Methods still in the env format (as per the docs), and not parsed out to GET, POST, OPTIONS? Anyway, it returns the correct Allow-Origin so the resulting POST should work:

image

(I removed the X-Gotify-Key header from the call and appended to the url) Log is showing the 204:

2024-02-08T22:40:13.737045928Z 2024-02-08T22:40:13Z | 204 | 37.285µs | 1.146.13.223 | OPTIONS "/message?token=[masked]"
2024-02-08T22:47:07.871513655Z 2024-02-08T22:47:07Z | 204 | 47.468µs | 1.146.13.223 | OPTIONS "/message?token=[masked]"
2024-02-08T22:54:53.848293265Z 2024-02-08T22:54:53Z | 204 | 80.573µs | 1.146.13.223 | OPTIONS "/message?token=[masked]"

Postman continues to work correctly with and without the Origin header being sent, but it doesn't care about cors.

@jmattheis
Copy link
Member

jmattheis commented Feb 9, 2024

Don't mix environment config and file config. I've updated the environment config in the docs https://gotify.net/docs/config could you retry it in this format?

I've this docker-compose:

version: "3"

services:
  server:
    image: gotify/server:2.4.0
    ports:
      - 8080:80
    environment:
      - GOTIFY_SERVER_CORS_ALLOWORIGINS=[dyne\.local:3000]
      - GOTIFY_SERVER_CORS_ALLOWMETHODS=[GET, POST]
      - GOTIFY_SERVER_CORS_ALLOWHEADERS=[x-gotify-key]
      - 'GOTIFY_SERVER_RESPONSEHEADERS={X-Custom-Header: "custom value", x-other: value}'
    volumes:
      - "./gotify_data:/app/data"

with this html script:

<script>
  fetch("http://localhost:8080/message?message=x", {
    method: "POST",
    headers: { "x-gotify-key": "AgarXhfGWhnzOae" },
  });
</script>

This works fine for me, now with the options/preflight request:

Responses

Normal Response

image

OPTIONS Response

image

If you set the environment variables similar to this and it still doesn't work, then caprover does something weird with environment variables.

@Ataye
Copy link
Author

Ataye commented Feb 9, 2024

Ok. First up, yes, that fixed the Origin issue with the env vars!! So i removed the temp config i made, and set the env vars:

image

And now i can see the correct Allow-Methods being returned, yay!

However, your code with fetch does not work :(

image

But this does:

    fetch(`${HOST}/message?token=${APP_KEY}&message=x2`, {
      method: "POST",
    });

And so does this:

    fetch(`${HOST}/message?token=${APP_KEY}`, {
      method: "POST",
      headers: { 
        "Content-Type": "application/x-www-form-urlencoded"
      },
      body: "message=urlencoded from react with fetch"
    });

And this:

axios.post(
      `${HOST}/message?token=${APP_KEY}`, 
      {
        message: 'urlencoded from react with axios'
      },
      {
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
      },
    })

If i put the token in the headers it fails, which indicates its something to do with the proxy. I've explicitly tried setting proxy_pass_request_headers on; but doesn't make a difference. It's also not complaining about needing to provide an access token when so seems to be sending the token through though!

What's more, if i send json it sets the content-type header accordingly and whether i have token in headers or in the QS it causes a preflight cors OPTIONS check and returns 204 and fails cors.

So summary: Setting token in Header does not work. Posting as json does not work. Posting with X-Gotify header works in Postman, as does json format!!!

Responses Options

image

Normal

image

Payload

image

And here are the various functions i've used:

Fetch requests working / not working
/// WORKS
const doFetch1 = async () => {
  axios.post(
    `${HOST}/message?token=${APP_KEY}`, 
    {
      message: 'urlencoded from react with axios'
    },
    {
    headers: {
      'Content-Type': 'multipart/form-data'
    },
  })
}

/// DOESN't WORK
const doFetch2 = async () => {
  fetch(`${HOST}/message?message=x1`, {
    method: "POST",
    headers: { "x-gotify-key": APP_KEY },
  });
}

/// WORKS
const doFetch3 = async () => {
  fetch(`${HOST}/message?token=${APP_KEY}&message=x2`, {
    method: "POST",
  });
}

/// WORKS
const doFetch4 = async () => {
  fetch(`${HOST}/message?token=${APP_KEY}`, {
    method: "POST",
    headers: { 
      "Content-Type": "application/x-www-form-urlencoded"
    },
    body: "message=urlencoded from react with fetch"
  });
}

/// DOESN'T WORK
const doFetch5 = async () => {
  fetch(`${HOST}/message?token=${APP_KEY}`, {
    method: "POST",
    headers: { 
      "Content-Type": "application/json"
    },
    body: { message:"json from react with fetch" }
  });
}

/// DOESN'T WORK
const doFetch6 = async () => {
  axios.post(
    `${HOST}/message?token=${APP_KEY}`, 
    {
      message: 'json content type message'
    })
}

It's interesting json doesn't work as the library i've used "gotify-client": "^0.4.2", createMessage uses json as its message format, which means that library doesn't work for me (plus it sets the X-Gotify-Key header, also a no-go).

https://github.com/hywax/gotify-client/blob/fedddd34f119cfa748f7fac19f640a2e8a4a085e/src/api/MessageApi.ts#L30

And i haven't even gotten to testing websocket yet, now that message create is working for me :/

@jmattheis
Copy link
Member

Have a look at the browser console, it says why the CORS request fails. Headers like Content-Type and X-Gotify-Key must be whitelisted in cors. You can do this with GOTIFY_SERVER_CORS_ALLOWHEADERS=[X-Gotify-Key, Content-Type]

With this config requests with json and token inside the header work fine.

  fetch("http://localhost:8080/message", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-Gotify-Key": "AgarXhfGWhnzOae",
    },
    body: JSON.stringify({ message: "json from react with fetch" }),
  });

@Ataye
Copy link
Author

Ataye commented Feb 9, 2024

🤦‍♂️Ahhh, i can't believe i missed that, and i was just literally looking at the allowed headers option in the docs!!! Yesss, thank you!

Well that about sorts it. Difference between Axios and Fetch is Axios automatically stringifies the data object, fetch doesn't, which is why i just got the bad requests in the console.

Oh, i just tested standard websocket to receive messages and its happy now as well with useWebSocket passing token in the url. I tried gotify-client client.stream.streamMessages() but so far haven't been successful, with errors like Bad Request etc. Mainly because of lack of docs on how to use, so i'll dig in and figure it out.

So, the take from this i think is that there either needs to be a section in the docs like

For Cross Site calls, this is the minimum required set of config options (replace .* with the regex for your allowed origin, and x-gotify-key to allow the token in the header, and Content-Type to allow application/json etc):

GOTIFY_SERVER_CORS_ALLOWORIGINS=[.*]
GOTIFY_SERVER_STREAM_ALLOWEDORIGINS=[.*]
GOTIFY_SERVER_CORS_ALLOWMETHODS=[GET, POST]
GOTIFY_SERVER_CORS_ALLOWHEADERS=[X-Gotify-Key, Content-Type]

Or those Methods and Headers are allowed by default? (I understand the implications of allowing every Origin, so totally fine to have the user supplied something sensible).

However, i do appreciate you updating the docs with the correct format for the env vars, cheers :)

@jmattheis
Copy link
Member

I'd say adding the allowheaders and allowmethods as defaults shoulds reasonable.

@jmattheis jmattheis added a:feature New feature or request and removed question Further information is requested labels Feb 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
a:feature New feature or request in:server
Development

No branches or pull requests

3 participants