ICECAST WITH HTTPS

1. Install and configure Icecast listenning on 127.0.0.1 port 8000 without ssl. ;)
2. Install nginx

3. Create directory .well-known in webroot ( here: /var/www/html )
4. Install certbot
5. Configure nginx:
/etc/nginx/nginx.conf:

#user www;
worker_processes 4;
#pid /run/nginx.pid;

events {
        worker_connections 768;
        # multi_accept on;
}

http {
        ##
        # Basic Settings
        ##

        sendfile on;
        tcp_nopush on;
        tcp_nodelay on;
        keepalive_timeout 65;
        types_hash_max_size 2048;
        # server_tokens off;

        # server_names_hash_bucket_size 64;
        # server_name_in_redirect off;

        include /etc/nginx/mime.types;
        default_type application/octet-stream;

        ##
        # SSL Settings
        ##

        ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
        ssl_prefer_server_ciphers on;

        ##
        # Logging Settings
        ##

        #log_format icecast_combined '$remote_addr - $remote_user [$time_local] '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent" ' '$request_time $upstream_response_time $pipe';
         log_format icecast_combined '$remote_addr - $remote_user [$time_local] '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent" ' '$upstream_response_time';
        access_log /var/log/nginx/access.log;
        error_log /var/log/nginx/error.log debug;

        ##
        # Gzip Settings
        ##

        gzip on;
        gzip_disable "msie6";

        # gzip_vary on;
        # gzip_proxied any;
        # gzip_comp_level 6;
        # gzip_buffers 16 8k;
        # gzip_http_version 1.1;
        # gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

        ##
        # Virtual Host Configs
        ##

        #include /etc/nginx/conf.d/*.conf;
        include /etc/nginx/sites-enabled/*;
}

/etc/nginx/sites-available/icecast:

server
{
  listen 80;
  server_name icecast.mydomain.com;

  location ~ /.well-known
  {
    allow all;
  }

  location /
  {
    if ($ssl_protocol = "")
    {
      rewrite ^ https://$server_name$request_uri? permanent;
    }
  }
}

#### SSL ######################################################

server
{
  #ssl on;
  ssl_certificate_key /etc/letsencrypt/live/icecast.mydomain.com/privkey.pem;
  ssl_certificate /etc/letsencrypt/live/icecast.mydomain.com/fullchain.pem;
  ssl_dhparam /etc/ssl/certs/dhparam.pem;
  # Recommended security settings from https://wiki.mozilla.org/Security  /Server_Side_TLS
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
  ssl_prefer_server_ciphers on;
  ssl_ecdh_curve secp384r1;
  ssl_session_timeout 5m;
  ssl_session_cache shared:SSL:5m;
  ssl_session_tickets off;
  ssl_stapling on;
  ssl_stapling_verify on;
  resolver 8.8.8.8 8.8.4.4 valid=300s;
  resolver_timeout 5s;
# Enable this if you want HSTS (recommended)
# With or without preload (without very secure but not recommended)
  add_header Strict-Transport-Security "max-age=15768000; includeSubdomains;"
#add_header Strict-Transport-Security "max-age=15768000; includeSubdomains; preload;"
  add_header X-Frame-Options DENY;
  add_header X-Content-Type-Options nosniff;
  listen 443 ssl;
  root /var/www/html;

  server_name icecast.mydomain.com;

  location ~ /.well-known
  {
    allow all;
  }

  location /
  {
    access_log /var/log/icecast/access_https.log icecast_combined;
    proxy_pass http://127.0.0.1:8000/;
    proxy_redirect off;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  }
}

ATTENTION: https logging
Connection via https will be always logged as 127.0.0.1 by icecast.
That’s the reason for
log_format icecast_combined … in nginx.conf
access_log /var/log/icecast/access_https.log icecast_combined; in /etc/nginx/sites-available/icecast

6. Create DH Group

openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048

7. Get Certificates

certbot certonly -a webroot --webroot-path=/var/www/html -d icecast.mydomain.com

8. Configure cert renewing

 @monthly /usr/bin/certbot renew >> /var/log/le-renew.log; /usr/bin/pkill -HUP nginx

9. Tune the m3u link to the correct mount point/port.
(Credits: https://deephouse.lv – Thank you! // Check https://deephouse.lv/audio/02_2018.mp3 )

10. RUN Icecast

 icecast -b -c /etc/icecast2/icecast.xml

11. Run nginx

nginx

More hints (HUGE THX, Michael!):

Although this configuration may work it would benefit from several changes.

In your icecast virtual hosts file you have specified two separate server blocks for icecast.mydomain.com, which adds unnecessary overhead to each request. You can combine them into a single block or if your goal is actually to redirect all http traffic to https then you can specify a catch all server block for any traffic on port 80 like this:

server {
listen 80 default_server;
return 301 https://$host$request_uri;
}

This improves on your “location /” block in a couple of ways. It eliminates the evaulation of an if condition (which you should also avoid specifying within location blocks, but that’s a different topic) and by using return instead of rewite the regular expression does not need to be processed either. If you prefer to merge the two server blocks into one you can specify an if directive within the server block but before any location blocks like this to redirect all http traffic to https:

if ($scheme != “https”) {
return 301 https://icecast.mydomain.com$request_uri;
}

The regex specified here “location ~ /.well-known” is incorrect and potentially creates a security risk. The period before the w is unescaped (should be “\.”). An unescaped period in regex means match any character and you have not specified a position in the url for this expression to appear, it will match anywhere. If you had a password protected section under the location “/private” then I could visit icecast.mydomain.com/private/pwell-known and the “allow all” directive from this location would be the match made by Nginx. To allow access to anything within the dir “/.well-known” your location block should be like this:

location ^~ /.well-known {
allow all;
}

In regex the caret specifies the beginning of the string, but in Nginx this actually means something different. Here it is not specifyingn a regex match but a prefix, and the caret tells Nginx to stop evaluating any further possible matches after this one has matched.

Finally, and most usefully, instead of having a single location block proxying the request directly to the icecast server you should create another upstream server within Nginx to receive the proxied request and relay it to the icecast server. Within it’s server block include:

set_real_ip_from 127.0.0.1;
real_ip_header X-Real-IP;
real_ip_recursive on;

and your client IP addresses should be passed to Icecast properly for the stats/logging of listener IPs instead of every connection appearing to originate from 127.0.0.1.

Advertisements

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s