AirenSoft / OvenMediaEngine

OvenMediaEngine (OME) is a Sub-Second Latency Live Streaming Server with Large-Scale and High-Definition. #WebRTC #LLHLS
https://OvenMediaEngine.com/ome
GNU Affero General Public License v3.0
2.48k stars 1.05k forks source link

Getting CORS error and authorization error requesting RestApi with ajax #872

Closed lassos closed 1 year ago

lassos commented 1 year ago

Hello. I dont know what i am doing wrong. requesting the rest api with curl is working from any server like a charme

curl -d '{ "id" : "1", "stream": { "name": "stream" }, "protocol": "rtmp", "url":"rtmp://xxxxx:2935/rtmp_hls_dash/634110", "streamkey":"" }' \ -H 'Authorization: Basic b21lLWFjY2Vzcy10b2tlbg==' -H 'Content-Type: application/json' https://xxxx.com:8082/v1/vhosts/default/apps/app:startPush

trying to request the api via web with an ajax call due to an error.

Log:

`{"log":"\u001b[33m[2022-08-29 07:34:46.284] W [SPAPIServer-T80:49] APIController | root_controller.cpp:95 | HTTP error occurred: [HTTP] Authorization header is required to call API (403)\u001b[0m\n","stream":"stderr","time":"2022-08

And client/browser side:

app:startPush | CORS-Fehler | xhr | main.js?attr=uCOjpwbbV-mZ3OzZQ-oeiseL6lKJn2hkalIAcNLWPq5HmpGi72Gmqwt9Uz9Wvati87uSlFQC4uwD0u0qwWAw5g:3061 | 0 B | 184 ms app:startPush | 403 | preflight | Preflight | 0 B | 183 ms

api

HTML: Ajax Code:

$.ajax({ type: "POST", url: "https://xxxx.com:8082/v1/vhosts/default/apps/app:startPush", //dataType: "json", crossDomain: true, data: JSON.stringify(postData), headers: { 'Content-Type': 'application/json' "Authorization": "Basic b21lLWFjY2Vzcy10b2tlbg==" }, //beforeSend: function (xhr) { // xhr.setRequestHeader("Authorization", "Basic b21lLWFjY2Vzcy10b2tlbg==");//test64 //}, success: function (result) { console.log('ok'); console.log(result); }, error: function (xhr, ajaxOptions, thrownError) { console.log('error'); console.log(xhr.status); console.log(ajaxOptions); console.log(thrownError); } }); });

-29`

lassos commented 1 year ago

i have found a solution, i am using a proxy server in front:

location ~^(/v1/vhosts/default/apps/.)?$ { add_header 'Access-Control-Allow-Origin' ''; add_header 'Access-Control-Allow-Credentials' 'true'; add_header 'Access-Control-Allow-Headers' 'Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';

set $proxy_upstream_name "post-api-service"; proxy_pass http://127.0.0.1:8081; proxy_pass_header Authorization; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Original-URI $request_uri; proxy_set_header Authorization $http_authorization; proxy_pass_header Authorization;

}

getroot commented 1 year ago

Have you set up CrossDomains on the API server?

<Managers>
    <Host>
        <Names>
            <Name>*</Name>
        </Names>
        <TLS>
            <CertPath>path/to/file.crt</CertPath>
            <KeyPath>path/to/file.key</KeyPath>
            <ChainCertPath>path/to/file.crt</ChainCertPath>
        </TLS>
    </Host>
    <API>
        <AccessToken>ome-access-token</AccessToken>

        <CrossDomains>
            <Url>*.airensoft.com</Url>
            <Url>http://*.sub-domain.airensoft.com</Url>
            <Url>http?://airensoft.*</Url>
        </CrossDomains>
    </API>
</Managers>
lassos commented 1 year ago

yes i did

`

xxx.com /etc/letsencrypt/live/xxx.com/cert10.pem /etc/letsencrypt/live/xxx.com/privkey10.pem /etc/letsencrypt/live/xxx.com/chain10.pem
    <API>
        <AccessToken>ome-access-token</AccessToken>
        <CrossDomains>
                <Url>*.xxx.com</Url>
                <Url>http://*.stream.xxx.com</Url>
                <Url>http?://xxx.*</Url>
        </CrossDomains>
    </API>
</Managers>`
getroot commented 1 year ago

I mean, <Managers><API><CrossDomains> In your settings it is commented out.

getroot commented 1 year ago

You just edited it.

How about putting * ? Still not working?

And it's not a good idea to open OME's API server for anyone to access. This is an API for linking with other servers. This way anyone can delete your application.

lassos commented 1 year ago

yes i know, but in Server.xml it is correct

`

xxx.com /etc/letsencrypt/live/xxx.com/cert10.pem /etc/letsencrypt/live/xxx.com/privkey10.pem /etc/letsencrypt/live/xxx.com/chain10.pem
    <API>
        <AccessToken>ome-access-token</AccessToken>
        <CrossDomains>
                <Url>*.xxx.com</Url>
                <Url>http://*.stream.xxx.com</Url>
                <Url>http?://xxx.*</Url>
        </CrossDomains>
    </API>
</Managers>`

still cors error in chrome browser, sending via proxy server is working, sending direct to media server not

lassos commented 1 year ago

You just edited it.

How about putting * ? Still not working?

And it's not a good idea to open OME's API server for anyone to access. This is an API for linking with other servers. This way anyone can delete your application.

i have read that setting "*" is not working with ssl. so you mean

<Name>*</Name> instead of <Name>xxx.com</Name> ?

tried also setting "*", it is the same

dimiden commented 1 year ago

@lassos For TLS to work, <Names> should be specified as xxx.com, not *. The URL values listed in CrossDomains must be the URL of the page calling the OME API. For example, I set up CORS on page 127.0.0.1:5501 to invoke the 192.168.0.160:48085/v1/vhosts API (where 192.168.0.160:48085 is the API of OME):

...
    <Managers>
        <API>
            <CrossDomains>
                <Url>http://127.0.0.1:5501</Url>
            </CrossDomains>
...

As a result, it's worked normally as follows: image

If the CORS is still not working in your environment, please check again if the CORS is set according to the Origin header. (Also, if you capture and upload the Network tab of the browser with the screenshot I uploaded, I can check if there are another problems.)

P.S. To allow for all URLs, you can just use * for <Url> as follows (very easy solution):

...
    <Managers>
        <API>
            <CrossDomains>
                <Url>*</Url>
            </CrossDomains>
...
lassos commented 1 year ago

thank you very much, but i think i have done this in the same way:

`

xxx.com /etc/letsencrypt/live/tr9ts1nmd9.com/cert10.pem /etc/letsencrypt/live/tr9ts1nmd9.com/privkey10.pem /etc/letsencrypt/live/tr9ts1nmd9.com/chain10.pem
    <API>
        <AccessToken>ome-access-token</AccessToken>
        <CrossDomains>
                        <Url>https://xxx.com</Url>
        </CrossDomains>
    </API>
</Managers>`

`

wildcard due to the same cor error

        <CrossDomains>
            <Url>*</Url>
        </CrossDomains>`

https://xxx.com/owenmedia.html

press buton and see result on your own

dimiden commented 1 year ago

@lassos I checked the URL you posted, and the browser is not sending the Origin header in request header. (Origin header must be present for CORS processing in OME.) Is <Url> set to *? If not, could you change the setting to *?

image

lassos commented 1 year ago

see original from Server.xml, off course if have reinitiated docker with new config:

sudo docker cp /opt/ovenmediaengine/ome-edge-conf/Server.xml 70b9b6c7d1d7:/opt/ovenmediaengine/bin/edge_conf/Server.xml sudo docker cp /opt/ovenmediaengine/ome-origin-conf/Server.xml 70b9b6c7d1d7:/opt/ovenmediaengine/bin/origin_conf/Server.xml sudo docker restart 70b9b6c7d1d7

dimiden commented 1 year ago

@lassos I just found the path to this problem! If there is an Authorization header when Chrome requests the API, Chrome will send a preflight request before the API request, but OME's API server doesn't handle the preflight request.

The situation is as follows:

  1. Chrome sends a preflight request to determine if Authorization header can be used.
  2. The OME API server checks for the presence of an Authorization header to authenticate a preflight request like a normal request (Here is the problem)
  3. 403 Forbidden error occurred because preflight does not have Authorization header
  4. Chrome won't send GET/POST request due to preflight failure

However, we have decided to lower the priority of this issue.

The reason is,

  1. Calling the API directly from the browser is STRONGLY NOT RECOMMENDED because it causes other security issues. For example, an attacker can use the API to terminate a stream that is being sent from your server or to change the server's configuration settings, etc.
  2. If you are using this feature for testing because it is in the development stage, it is recommended to use the workaround that sets the Authorization header with the NGINX proxy between the browser and OME.

Anyway, this is obviously a bug, so I'm going to solve this problem soon when I can afford it.

lassos commented 1 year ago

@lassos I just found the path to this problem! If there is an Authorization header when Chrome requests the API, Chrome will send a preflight request before the API request, but OME's API server doesn't handle the preflight request.

The situation is as follows:

  1. Chrome sends a preflight request to determine if Authorization header can be used.
  2. The OME API server checks for the presence of an Authorization header to authenticate a preflight request like a normal request (Here is the problem)
  3. 403 Forbidden error occurred because preflight does not have Authorization header
  4. Chrome won't send GET/POST request due to preflight failure

However, we have decided to lower the priority of this issue.

The reason is,

  1. Calling the API directly from the browser is STRONGLY NOT RECOMMENDED because it causes other security issues. For example, an attacker can use the API to terminate a stream that is being sent from your server or to change the server's configuration settings, etc.
  2. If you are using this feature for testing because it is in the development stage, it is recommended to use the workaround that sets the Authorization header with the NGINX proxy between the browser and OME.

Anyway, this is obviously a bug, so I'm going to solve this problem soon when I can afford it.

this is exactly what i have done , see some threads above

i have found a solution, i am using a proxy server in front:

location ~^(/v1/vhosts/default/apps/.)?$ { add_header 'Access-Control-Allow-Origin' ''; add_header 'Access-Control-Allow-Credentials' 'true'; add_header 'Access-Control-Allow-Headers' 'Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';

set $proxy_upstream_name "post-api-service"; proxy_pass http://127.0.0.1:8081/; proxy_pass_header Authorization;

proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Original-URI $request_uri; proxy_set_header Authorization $http_authorization; proxy_pass_header Authorization; }

dimiden commented 1 year ago

@lassos Yes, you're right. You've already given me enough hints, but it took me a while to collect and understand the fragmented hints. And I wanted to find out the cause of the problem :) Thank for your reporting!

lassos commented 1 year ago

so seems to be the restapi is not callable via browser with nginx as frontend

so this command from ssh works

curl -d '{ "id" : "999", "stream": { "name": "stream" }, "protocol": "rtmp", "url":"rtmp://xxx:2935/rtmp_hls_dash_gaby/634110", "streamkey":"" }' \ -H 'Authorization: Basic b21lLWFjY2Vzcy10b2tlbg==' -H 'Content-Type: application/json' https://xxxx.com:8082/v1/vhosts/default/apps/app:startPush

starting same with ajax code via web browser logs says 200 ok, but also an error

{"log":"\u001b[33m[2022-08-30 08:31:07.387] W [SPAPISvr-T8081:47] APIController | controller.h:154 | HTTP error occurred: [HTTP] Success (200)\u001b[0m\n","stream":"stderr","time":"2022-08-30T08:31:07.387494143Z"}

so if we just can control it via ssh it makes not sense for us, the sense of an api is the ability to access it. authttoken is for security so why you should not access the api via webbrowser.

so we need this just for pushing streams. acutally we are using nginx rtmp module and wanted to switch. but seems the api is still beta state

getroot commented 1 year ago

@lassos

There seems to be a misunderstanding.

What @dimiden says is not recommended to call the API with SSH and CURL. What we recommend is to call OvenMediaEngine's API through another system. The CORS, Preflight problem only occurs in web browsers. Calling the API from your backend system or from Nginx is no problem.

OvenMediaEngine's API was not originally designed to be called directly from a web browser. The CORS functionality of the API is only provided for easy testing by someone requesting it. And what dimiden is saying is that they fix it because it's a bug, even for testing purposes. You should never open your API to be called at java script level in web browser. That means anyone can kill the OvenMediaEngine's stream.

OvenMediaEngine's API is for other systems that it works with. That means the server, not the browser. AuthTokens should only be shared between trusted systems. Calling the API from Java script means that the AuthToken is exposed.

It's also a good idea to put Nginx in front of OvenMediaEngine in front. But the Authorization header is injected by Nginx. And APIs such as Stream deletion should be blocked.

lassos commented 1 year ago

thanks for explanation and missundertanding

but once again, we will be able to secure the system, right now we are testing. we will able to secure the web front end. we are only publishing one stream and push it to many other server. so we are not afraid of deleting streams.

take a look at: https://tr9ts1nmd9.com/owenmedia.html

you see, OvenMediaEngine give correct json respond (console log / same like direct via ssh with curl), but nothing happened , the push stream is not generated on other server in spite of resonse message should expect.

image

try start sever and see js code .

so repsonse from webserver and log from media server missmatch

web response:

message: "OK" response: Array(1) 0: app: "app" createdTime: "2022-08-30T09:25:18.717+00:00" finishTime: "1970-01-01T00:00:00.000+00:00" id: "999" protocol: "rtmp" sentBytes: 0 sentTime: 0 sequence: 0 startTime: "1970-01-01T00:00:00.000+00:00" state: "ready" stream: {name: 'rnts', tracks: Array(0)} totalsentBytes: 0 totalsentTime: 0 url: "rtmp://stream.eronauts.com:2935/rtmp_hls_dash_gaby/634110" vhost: "default"

error log:

{"log":"\u001b[33m[2022-08-30 09:25:16.889] W [SPAPISvr-T8081:47] APIController | controller.h:154 | HTTP error occurred: [HTTP] Success (200)\u001b[0m\n","stream":"stderr","time":"2022-08-30T09:25:16.890213998Z"} {"log":"\u001b[91m[2022-08-30 09:25:18.717] E [SPAPISvr-T8081:47] Orchestrator | orchestrator.cpp:660 | Could not find Origin for the stream: [#default#app/rnts]\u001b[0m\n","stream":"stderr","time":"2022-08-30T09:25:18.71766065Z"}

getroot commented 1 year ago
  1. Do you have an app/rnts stream in your OvenMediaEngine instance? Please upload the full log file.

  2. I don't know what you mean by you're not afraid to delete the stream. When the stream is deleted, PUSH is also stopped.

lassos commented 1 year ago

ok, her is the full log

server.log

getroot commented 1 year ago
{"log":"id(3585842417), msid(0), output(stream), SourceType(Transcoder), RepresentationType(Source), Created Time (Tue Aug 30 10:51:18 2022) UUID(06603cec-1ce5-425c-899b-5478380c1595/default/#default#app/stream/o)\n","stream":"stdout","time":"2022-08-30T10:51:18.700617251Z"}
{"log":"\u0009\u003e\u003e Origin Stream Info\n","stream":"stdout","time":"2022-08-30T10:51:18.700629056Z"}
{"log":"\u0009id(38), output(stream), SourceType(Rtmp), Created Time (Tue Aug 30 10:51:18 2022)\n","stream":"stdout","time":"2022-08-30T10:51:18.700639271Z"}
{"log":"\n","stream":"stdout","time":"2022-08-30T10:51:18.700649491Z"}
{"log":"\u0009Video Track #0: Bypass(true) Bitrate(6.00Mb) codec(1, H264) resolution(1920x1080) framerate(30.00fps) KeyInterval(0) BFrames(0) timebase(1/1000)\n","stream":"stdout","time":"2022-08-30T10:51:18.70065978Z"}
{"log":"\u0009Audio Track #1: Bypass(true) Bitrate(128.00Kb) codec(6, AAC) samplerate(48.0K) format(s16, 16) channel(stereo, 2) timebase(1/1000)\n","stream":"stdout","time":"2022-08-30T10:51:18.700670899Z"}
{"log":"\u0009Audio Track #2: Bypass(false) Bitrate(128.00Kb) codec(8, OPUS) samplerate(48.0K) format(s16, 16) channel(stereo, 2) timebase(1/48000)\u001b[0m\n","stream":"stdout","time":"2022-08-30T10:51:18.700681398Z"}
...
{"log":"\u001b[33m[2022-08-30 10:51:52.311] W [SPAPISvr-T8081:47] APIController | controller.h:154  | HTTP error occurred: [HTTP] Success (200)\u001b[0m\n","stream":"stderr","time":"2022-08-30T10:51:52.312230941Z"}
{"log":"\u001b[91m[2022-08-30 10:51:54.313] E [SPAPISvr-T8081:47] Orchestrator | orchestrator.cpp:660  | Could not find Origin for the stream: [#default#app/rnts]\u001b[0m\n","stream":"stderr","time":"2022-08-30T10:51:54.313563563Z"}

You have app/stream but you are requesting recording with app/rnts .

lassos commented 1 year ago
{"log":"id(3585842417), msid(0), output(stream), SourceType(Transcoder), RepresentationType(Source), Created Time (Tue Aug 30 10:51:18 2022) UUID(06603cec-1ce5-425c-899b-5478380c1595/default/#default#app/stream/o)\n","stream":"stdout","time":"2022-08-30T10:51:18.700617251Z"}
{"log":"\u0009\u003e\u003e Origin Stream Info\n","stream":"stdout","time":"2022-08-30T10:51:18.700629056Z"}
{"log":"\u0009id(38), output(stream), SourceType(Rtmp), Created Time (Tue Aug 30 10:51:18 2022)\n","stream":"stdout","time":"2022-08-30T10:51:18.700639271Z"}
{"log":"\n","stream":"stdout","time":"2022-08-30T10:51:18.700649491Z"}
{"log":"\u0009Video Track #0: Bypass(true) Bitrate(6.00Mb) codec(1, H264) resolution(1920x1080) framerate(30.00fps) KeyInterval(0) BFrames(0) timebase(1/1000)\n","stream":"stdout","time":"2022-08-30T10:51:18.70065978Z"}
{"log":"\u0009Audio Track #1: Bypass(true) Bitrate(128.00Kb) codec(6, AAC) samplerate(48.0K) format(s16, 16) channel(stereo, 2) timebase(1/1000)\n","stream":"stdout","time":"2022-08-30T10:51:18.700670899Z"}
{"log":"\u0009Audio Track #2: Bypass(false) Bitrate(128.00Kb) codec(8, OPUS) samplerate(48.0K) format(s16, 16) channel(stereo, 2) timebase(1/48000)\u001b[0m\n","stream":"stdout","time":"2022-08-30T10:51:18.700681398Z"}
...
{"log":"\u001b[33m[2022-08-30 10:51:52.311] W [SPAPISvr-T8081:47] APIController | controller.h:154  | HTTP error occurred: [HTTP] Success (200)\u001b[0m\n","stream":"stderr","time":"2022-08-30T10:51:52.312230941Z"}
{"log":"\u001b[91m[2022-08-30 10:51:54.313] E [SPAPISvr-T8081:47] Orchestrator | orchestrator.cpp:660  | Could not find Origin for the stream: [#default#app/rnts]\u001b[0m\n","stream":"stderr","time":"2022-08-30T10:51:54.313563563Z"}

You have app/stream but you are requesting recording with app/rnts .

that could be not the reason

just doing the same via ssh command it is working. the same json is submitted via web

curl -d '{ "id" : "999", "stream": { "name": "stream" }, "protocol": "rtmp", "url":"rtmp://stream.xxxx.com:2935/rtmp_hls_dash_gaby/634110", "streamkey":"" }' \ -H 'Authorization: Basic b21lLWFjY2Vzcy10b2tlbg==' -H 'Content-Type: application/json' http://127.0.0.1:8081/v1/vhosts/default/apps/app:startPush

lassos commented 1 year ago

oh my god i see it now. { "name": "stream" } vs { "name": "rnts" }

lassos commented 1 year ago

thanks so much, my really last question

is this normal that the cpu is so high ? more than 51 %, we have high perfomance server. we just need the push function

image

getroot commented 1 year ago

You should look at Server.xml.

Remove all unused features like LLHLS and WEBRTC from Publisher, except for RTMP Push. If you have Opus encoding in Transcoding, disable it.

lassos commented 1 year ago

thank you, can we also disable most in providers ?

`

                    <MPEGTS />
                    <RTSPPull />
                    <WebRTC>
                        <Timeout>30000</Timeout>
                    </WebRTC>`
stale[bot] commented 1 year ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.