1

I have an icecast server running on port 8000. I can access the server fine as LINK1 (see below) from the internet. In addition, I created a small HTML page with some info that also embeds the HTML5 audio player. Works fine if accessed as http host dot domain dot com.

<html>
    <head>
        <title>Radio</title>
    </head>
    <body>
        <p>Some text</p>
        <audio controls>
          <source src="LINK1" type="audio/ogg">
        </audio>
    </body>
</html>

The problem is that if port 8000 is blocked on the listeners computer, they can't hear the stream. So, I tried to use mod_proxy and mod_rewrite but with no avail. If I use the below config, I can bypass the port 8000 and it streams fine on port 80, but http host dot domain dot com shows only the audio player, not the whole HTML page. I tried different combinations and googling and I just can't figure it out.

My question is if this is a viable solution. Having Apache on 80 and icecast 8000 proxying thru the same port? Do I need two IPs maybe? What is the proper config then if I use two IPs. Here is my Apache virtual host config.

ProxyRequests Off
ProxyPass / LINK1
ProxyPassReverse / LINK1
#RewriteEngine On
#RewriteRule ^/mount\.ogg$ LINK2 [P]

Mind that mount.ogg is a non-existing file. It's just a mount point for icecast.

==>Posting links here (need at least 10 reputation) LINK1: http://host.domain.com:8000/mount.ogg LINK2: http://host.domain.com/mount.ogg

Thanks

Kliment
  • 11
  • 1
  • 4

4 Answers4

1

I didn't want to have to choose between Icecast running on port 80 and all my Apache virtual hosts, running also on port 80, on my sole external IP address.

I didn't want either to open port 8000 on my firewall because I wanted all users being able to reach Icecast even the ones behind enterprise firewalls.

So I managed to run Apache 2.2 listening on port 80 and Icecast 2.4.1 listening on port 8000 on the same host.

I added a new virtual host radio.domain.com (listening on port 80) which is routing the Icecast traffic to/from the local Icecast server (listening on port 8000):

<VirtualHost *:80>
    ServerName radio.domain.com
    ServerAdmin hostmaster@domain.com
    ProxyPreserveHost On
    ProxyPass / http://localhost:8000/
    ProxyPassReverse / http://localhost:8000/
</VirtualHost>

I was then able to connect audio clients like VLC/Winamp to Icecast mount points using addresses like http://radio.domain.com/my_stream.

However, when connecting to the Icecast virtual host http://radio.domain.com/ web page, the m3u and the xspf files where still exposing the URL http://radio.domain.com:8000/my_stream with that annoying port 8000. Then people downloading those files weren't able to connect to the Icecast server because they were trying to connect on the wrong port. The same was occuring with the YP updates on dir.xiph.org.

I then downloaded the Icecast 2.4.1 source code, and modified it to add a new option:

<exposed-port>80</exposed-port>

You can find that patch on https://damiengarrido.wordpress.com/2015/03/22/icecast-reachable-behind-reverse-proxy/

I can paste the patch in here if needed.

0

The preferred way is to have Icecast listen directly to port 80, it is a HTTP server after all.

Reverse proxying it is not recommended for many reasons.

  • many long lived connections, if not configured correctly will bring down apache
  • some legacy functionality (SOURCE, STATS) not reachable, only available over plain HTTP in 2.4.1 and future versions of Icecast
  • the YP public directory reporting code can not be made reverse proxy aware, this will break listings
  • web interface and generated playlists are at the least not easy to get right

To sum it up, if you are working on a production setup and are a professional who knows exactly what they are doing, it's possible, but not easy. If you are just casually thinking "oh hey, let's just reverse proxy this like everything else", then you're in for some pain.

Get an additional IP, make Icecast listen to port 80 on that, simple.

TBR
  • 200
  • 5
  • Icecast2 doesn't work on port 80 even if you specify to run as root (which is another reason not to run it). – Kliment Dec 03 '14 at 21:11
  • ERROR: You should not run icecast2 as root Use the changeowner directive in the config file – Kliment Dec 03 '14 at 21:12
  • Icecast will listen just fine to port 80, you MUST use the change-user facility inside the config file though. – TBR Jan 28 '15 at 13:22
0

I was able to find a solution (partial) using two virtual hosts. For listeners that can access port 8000, the configuration stays the same as above minus Proxy directives. But in the HTML page for the first virtual host, I've added a link to open a new window that points to the 2nd virtual hosts where I use ProxyReverse. So, the HTML file for the 1st virtual host is

<audio controls>
    <source src="LINK1" type="audio/ogg">
</audio>
<a href="/"  onclick="window.open('LINK3', 'newwindow','width=300, height=100'); return false;"> No audio?</a>

where LINK3 is link to the 2nd virtual host e.g. host2 dot domain dot com forward slash

And then the config for that 2nd virtual host actually does the proxy reverse.

ProxyRequests Off
ProxyPass / LINK1
ProxyPassReverse / LINK1

Probably I can use a 1 pixel frame and point the link there, but I am fine with this solution as well.

Thanks

Kliment
  • 11
  • 1
  • 4
0

I had the same problem merging my home made index.php page that is running on port 443 in apache2 on Ubuntu 16.04. I wanted to use my own webpage and pass the "script" of the music stream (non-ssl) and what was currently playing (also non-ssl). What was happening is that everything would work over port 80, but when I switched to port 443, browsers (especially Chrome) would block my "unsecure scripts" and no sound would occur.

So, after searching all over the internet and finding this but not how to do it, I finally got it!

Here is my set up for those interested:

  1. Icecast 2.4.3 on Windows running on port 8080
  2. Ubuntu 16.04 LTS running on Oracle VirtualBox with almost 20 websites
  3. I use MediaMonkey 4.1.16 and a plug-in called "edcast" to connect to Icecast

Apache2 conf file:

<IfModule mod_ssl.c>
<VirtualHost *:443>
ServerName radio.domain.com
DocumentRoot /var/www/radio.domain.com

<Directory /var/www/radio.domain.com>
    Options Indexes FollowSymLinks MultiViews
    Order allow,deny
    allow from all
</Directory>

ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
SSLCertificateFile /etc/letsencrypt/live/radio.domain.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/radio.domain.com/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf

Options -Includes -ExecCGI
RewriteEngine On
RewriteCond %{THE_REQUEST} !HTTP/1.1$
RewriteRule .* - [F]

LimitRequestBody 512000
Header always set Strict-Transport-Security "max-age=63072000; includeSubdomains; preload"
Header always set X-Frame-Options DENY

FileETag None
TraceEnable off
Header set X-XSS-Protection "1; mode=block"
Timeout 60

<Proxy *>
    Order deny,allow
    Allow from all
</Proxy>

ProxyRequests Off
ProxyPreserveHost On
SSLProxyEngine On
RequestHeader set Front-End-Https "On"

ProxyPass /stream http://192.168.1.2:8080/stream #IP and port of local computer on same network
ProxyPassReverse /stream http://192.168.1.2:8080/stream #IP and port of local computer on same network
ProxyPass /np.xsl http://192.168.1.2:8080/np.xsl #np.xsl is a file that I call using ajax from my index.php page to get the track currently playing
ProxyPassReverse /np.xsl http://192.168.1.2:8080/np.xsl #np.xsl is a file that I call using ajax from my index.php page to get the track currently playing

</VirtualHost>
</IfModule>

My np.xsl file (in the Icecast "web" directory):

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output omit-xml-declaration="yes" method="text" indent="no" media-type="text/javascript" encoding="UTF-8"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/icestats">
parseMusic({
    <xsl:for-each select="source">"<xsl:value-of select="@mount"/>":{
        "server_name":"<xsl:value-of select="server_name"/>",
        "title":"<xsl:if test="artist"><xsl:value-of select="artist" /> - </xsl:if><xsl:value-of select="title" />",
        "bitrate":"<xsl:value-of select="bitrate" />"}
    <xsl:if test="position() != last()"><xsl:text>,</xsl:text>
    </xsl:if>
</xsl:for-each>});
</xsl:template>
</xsl:stylesheet>

My index.php page:

<!DOCTYPE html>
<html>
<head>
    <link rel="icon" href="favicon.ico" type="image/x-icon">
    <link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
    <title id="track-title"></title>
    <style>
        html{width:100%;}
        body{background-color:#bfbfbf; text-align:center; font-family:Helvetica;}
        #wrapper{position:absolute; max-width:550px; left:50%; transform:translate(-50%,0); -ms-transform:translate(-50%,0); -webkit-transform:translate(-50%,0); margin-right:-50%; text-align:center; box-shadow:1px 1px 20px 5px #4d4d4d;}
    </style>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>
    <script>
    function updateTitle() {
    $.ajax({
            type: 'GET',
            url: 'https://radio.domain.com/np.xsl',
            jsonpCallback: 'parseMusic',
            dataType: 'jsonp'
    }).then(function (data) {
            var $track = $('#track-title').text(data['/stream'].title);
            var text = $track.text();
            $track.text(text.replace(" - MediaMonkey",""));
    }).fail(function (e) {
            console.log(e);
    }).always(function () {
            setTimeout(updateTitle, 5000);
    });
    }
    $(updateTitle);
    function updateTitle2() {
    $.ajax({
            type: 'GET',
            url: 'https://radio.domain.com/np.xsl',
            jsonpCallback: 'parseMusic',
            dataType: 'jsonp'
    }).then(function (data) {
            var $track = $('#track-title2').text(data['/stream'].title);
            var text = $track.text();
            $track.text(text.replace(" - MediaMonkey",""));
    }).fail(function (e) {
            console.log(e);
    }).always(function () {
            setTimeout(updateTitle2, 5000);
    });
    }
    $(updateTitle2);
    </script>
</head>
<body>
    <div id="wrapper">
    <h2>Live Radio</h2></br>
    <h4><span id="track-title2"></span></h4></br></br>
    <audio controls src="https://radio.domain.com/stream" type="audio/mp3"></audio><br></br>
    </div>
</body>
</html>

Finally my icecast.xml code:

<icecast>
<location>Minneapolips, MN</location>
<admin>info@radio.domain.com</admin>
<hostname>radio.domain.com</hostname>
<limits>
    <clients>50</clients>
    <sources>1</sources>
    <queue-size>524288</queue-size>
    <client-timeout>30</client-timeout>
    <header-timeout>15</header-timeout>
    <source-timeout>10</source-timeout>
    <burst-on-connect>1</burst-on-connect>
    <burst-size>65535</burst-size>
</limits>
<authentication>
    <source-password>hackme</source-password>
    <relay-password>hackme</relay-password>
    <admin-user>admin</admin-user>
    <admin-password>hackmemore</admin-password>
</authentication>
<listen-socket>
    <port>8080</port>
    <shoutcast-mount>/stream</shoutcast-mount>
</listen-socket>
<http-headers>
    <header name="Access-Control-Allow-Origin" value="*" />
</http-headers>
<fileserve>1</fileserve>
<paths>
    <logdir>./log</logdir>
    <webroot>./web</webroot>
    <adminroot>./admin</adminroot>
    <alias source="/" destination="/status.xsl"/>
</paths>

<logging>
    <accesslog>access.log</accesslog>
    <errorlog>error.log</errorlog>
    <loglevel>4</loglevel> <!-- 4 Debug, 3 Info, 2 Warn, 1 Error -->
    <logsize>10000</logsize> <!-- Max size of a logfile -->
</logging>
</icecast>

And what it looks like: radio2017

radio2021

iamdoubz
  • 1
  • 2