karlheyes / icecast-kh

KH branch of icecast
GNU General Public License v2.0
298 stars 107 forks source link

Relay authentication support #302

Closed kschouw closed 4 years ago

kschouw commented 4 years ago

Hi Karl, thanks for the amazing work you've been doing.

I would like to be able to serve different intro audio, based on client IP, from a relay

When using a mount point this is possible by providing it with the authentication settings and specifying the listener_add URL and icecast-auth-user: withintro auth header:

<authentication type="url">
        <option name="listener_add" value="https://somewebsite.com/auth"/>
        <option name="auth_header" value="icecast-auth-user: withintro"/>
        <option name="headers" value="x-forwarded-for"/>
</authentication>

Where https://somewebsite.com/auth returns the icecast-auth-user: withintro header with audio data in the body of the response.

I'm trying to do the same for <relay> which might look something like this:

<relay>
    <mount>/stream.aac</mount>
    <server>127.0.0.1</server>
    <retry-delay>5</retry-delay>
    <authentication type="url">
        <option name="listener_add" value="https://somewebsite.com/auth"/>
        <option name="auth_header" value="icecast-auth-user: withintro"/>
        <option name="headers" value="x-forwarded-for"/>
    </authentication>
</relay>

Currently relay passes the authentication on the master endpoint which calls listener_add - but relay does not include the x-forwarded-for header so the master only gets the relay IP and not the end-user client IP, and it only plays the intro for the first request and never thereafter.

Would this be possible to implement?

Thanks again, Kyle.

karlheyes commented 4 years ago

It looks like you think the relay is acting like a per-listener proxy. The relay configures the feed coming in from the remote server, and as such just deals with that part of the connection. The authentication block you mention sits inside a mount block where mount-name is /stream.aac in your example. Remeber mount blocks can apply to source clients or relays. The remote feed will not know who is connecting locally.

karl.

kschouw commented 4 years ago

The master server does have the authentication block inside its config - sorry I did not paste the full example:

<!-- MASTER SERVER MOUNT -->
<mount>
        <mount-name>/stream.aac</mount-name>
        <authentication type="url">
            <option name="listener_add" value="https://somewebsite.com/auth"/>
            <option name="auth_header" value="icecast-auth-user: withintro"/>
            <option name="headers" value="x-forwarded-for"/>
        </authentication>
</mount>

The current relay that I have setup looks like this:

<!-- EDGE SERVER RELAY -->
<relay>
    <mount>/stream.aac</mount>
    <server>{master_server_ip}</server>
    <retry-delay>5</retry-delay>
</relay>

So in this case the relay does its initial connection to the master server when a user connects and the intro file plays perfectly however subsequent request do not play the intro file. This made me think the listener_add is only called on relay connection and then persisted to that relay? Which does make sense.

My main question is would it be possible for the relay to mimic the authentication and listener_add functionality for the relay itself whenever a user connects? In this case authentication is handled by the relay and not the master server.

Thanks again for your time and patience :)

karlheyes commented 4 years ago

ah right. In this case the relay is connecting, the relay is being treated as a listener and getting an intro then continues. In such a situation, the relay, is not aware of the intro content as such, only as part of the stream. The xml is not transferred, there is no guarentee that the settings on the master should be the same as the relay. You can of course set that up yourself on the relay but in some cases the authentication is not even externally accessible.

karl.

kschouw commented 4 years ago

Yes exactly. The relay thinks its getting a regular stream from the master server it doesn't know of any authentication or intro being played. The only reason the authentication is present on the master node is because there is no support for it to be done on the relay itself specifically for intro files.

The ideal behaviour in this case would be if relay option supported authentication listener_add & withintro which would solve the problem much like mount does.

Eg:

<relay>
    <mount>/stream.aac</mount>
    <server>127.0.0.1</server>
    <retry-delay>5</retry-delay>
    <authentication type="url">
        <option name="listener_add" value="https://somewebsite.com/auth"/>
        <option name="auth_header" value="icecast-auth-user: withintro"/>
    </authentication>
</relay>

That being said, does it make sense to implement something like this as a feature? I could try and attempt to add this myself however being a PHP developer its one hell of a learning curve going to c.

Thanks, Kyle.

karlheyes commented 4 years ago

I'm not sure what you are intending with this that is not already done. The mount block for a relay can do authentication including and intro handling.

karl

kschouw commented 4 years ago

I've tried setting the mount in the relay:

<relay>
    <mount>
        <mount-name>/stream.aac</mount-name>
        <authentication type="url">
            <option name="listener_add" value="https://somewebsite.com/auth"/>
            <option name="auth_header" value="icecast-auth-user: withintro"/>
            <option name="headers" value="x-forwarded-for"/>
        </authentication>
    </mount>
    <server>{master_server_ip}</server>
    <retry-delay>5</retry-delay>
</relay>

Here are the logs:

[2020-05-27  10:00:58] WARN slave/slave_startup process has 999999 max file descriptor limit
[2020-05-27  10:01:00] EROR fserve/find_fh missing name
[2020-05-27  10:01:21] WARN fserve/fserve_client_create req for file "/usr/local/share/icecast/web/stream.aac" No such file or directory

Icecast starts normally but the relay to that endpoint is not working. Is there a problem with my config?

karlheyes commented 4 years ago

the mount block does not reside within the relay block. There is a relay block with relay settings, and a mount block with mount settings (including auth)

....
  <relay>
       <local-mount>/stream.aac</local-mount>
      <server>...</server>
       ....
  </relay>
   <mount>
       <mount-name>/stream.aac</mount-name>
       <authentication type="url">
       ....
   </mount>
kschouw commented 4 years ago

Thanks Karl, I'll give that a shot.