Last updated: 2019-01-11
Added basic authentication to protect against the path traversal bug mentioned below.
- I assume you remotely know what you’re doing;
- All commands are run as root;
- You have an A-record pointing to your IP (out of scope for this HowTo);
- You need ports 80 and 443 open on your router and pointing to OSMC (port 80 is mandatory for certificate renewal, and 443 eventually as well due to the configuration).
Warning: Disable port forwarding on port 443 when on Kodi v17 (current OSMC stable). Your certificate will still renew due to the nginx configuration. This is due to the following vulnerabilities:
https://www.cvedetails.com/vulnerability-list/vendor_id-16145/product_id-36080/Kodi-Kodi.html
Once on Kodi v18, enable port-forwarding at your own risk, as the developers of Kodi say the WebUI should not be public-facing.
Why
- Protect your login information;
- Access the WebUI securely from outside your network.
Packages
Lets start off by installing the necessary packages, all available in the regular repositories. We’ll also stop nginx as it starts automatically.
apt install nginx-extras certbot apache2-utils
systemctl stop nginx
LetsEncrypt
The following will generate a new certificate. Make sure to replace the -d option with your own domain. You’ll be asked for an e-mail address for renewal reminders.
certbot certonly --rsa-key-size 4096 --standalone -d yourdomain.tld
This is a cronjob for automatically checking and renewing your certificate.
cat << "EOF" > /etc/cron.daily/certbot-renew
#!/bin/bash
certbot renew --rsa-key-size 4096 --webroot -w /var/www/letsencrypt --post-hook "systemctl reload nginx" 2>&1
EOF
chmod +x /etc/cron.daily/certbot-renew
mkdir -p /var/www/letsencrypt
chown -Rf www-data: /var/www/letsencrypt
You can test this later, once nginx is running, with this command:
certbot renew --rsa-key-size 4096 --webroot -w /var/www/letsencrypt --dry-run
Nginx
Lets put all the log files in the right place with the right permissions and generate a dhparam file. We also remove the default nginx site.
mkdir -p /var/log/nginx/yourdomain.tld
touch /var/log/nginx/yourdomain.tld/{access.log,error.log}
chown -Rf www-data: /var/log/nginx/
openssl dhparam -dsaparam -out /etc/ssl/certs/dhparam.pem 4096
rm /etc/nginx/sites-enabled/default
Add the following into your /etc/nginx/nginx.conf, somewhere in the logging section, for the logging:
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
Next up are snippets that can be used for future configurations. Be sure to edit the file pertaining to your domain.
cat << "EOF" > /etc/nginx/snippets/gzip.conf
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_min_length 1024;
gzip_comp_level 5;
gzip_proxied expired no-cache no-store private auth;
gzip_disable "MSIE 1-6\.";
gzip_types text/plain text/css text/xml application/javascript application/atom+xml application/rss+xml image/svg+xml image/x-ms-bmp application/json;
EOF
cat << "EOF" > /etc/nginx/snippets/location-letsencrypt.conf
location ^~ /.well-known/acme-challenge/ {
default_type "text/plain";
root /var/www/letsencrypt;
}
EOF
cat << "EOF" > /etc/nginx/snippets/ssl-params.conf
# from https://cipherli.st/
# and https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html
ssl_protocols TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384";
ssl_ecdh_curve "secp521r1:secp384r1";
ssl_buffer_size 8k;
ssl_session_timeout 10m;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 127.0.0.1 1.1.1.1 1.0.0.1 valid=300s;
resolver_timeout 5s;
add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header X-Robots-Tag none;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
EOF
cat << "EOF" > /etc/nginx/snippets/ssl-yourdomain.tld.conf
ssl_certificate /etc/letsencrypt/live/yourdomain.tld/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.tld/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/yourdomain.tld/fullchain.pem;
EOF
Set a username and password using the htpasswd tool in apache2-utils for logging into Chorus. This will protect you from the path traversal bug in Kodi v17. Change the username accordingly.
htpasswd /etc/nginx/.htpasswd USERNAME
Finally, the actual config file for the WebUI proxying. Don’t forget to adjust according to your DNS record.
cat << "EOF" > /etc/nginx/sites-available/yourdomain.tld
upstream kodi {
server 127.0.0.1:8080;
keepalive 512;
}
server {
listen 80;
server_name yourdomain.tld;
include snippets/location-letsencrypt.conf;
location / {
return 301 https://$server_name$request_uri;
}
}
server {
listen 443 ssl http2;
server_name yourdomain.tld;
include snippets/gzip.conf;
include snippets/ssl-params.conf;
include snippets/ssl-yourdomain.tld.conf;
include snippets/location-letsencrypt.conf;
access_log /var/log/nginx/yourdomain.tld/access.log main;
error_log /var/log/nginx/yourdomain.tld/error.log warn;
location / {
auth_basic "Reserved";
auth_basic_user_file .htpasswd;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_pass http://kodi$request_uri;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# _Shouldn't_ need this if you turned on proxy headers? (Settings -> Web interface -> Reverse proxy support)
location ~ ^/(image|jsonrpc) {
proxy_pass http://kodi;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
EOF
ln -sr /etc/nginx/sites-available/yourdomain.tld /etc/nginx/sites-enabled/yourdomain.tld
If you’ve already set a username and password for the Chorus within Kodi, you can configure Nginx to authenticate automatically. First, you need to convert a “USERNAME:PASSWORD” string into base64.
echo -n "USERNAME:PASSWORD" | openssl base64
Then paste the output into an nginx proxy_set_header in the /etc/nginx/sites-available/yourdomain.tld. Here’s an example.
proxy_set_header Authorization "Basic VVNFUk5BTUU6UEFTU1dPUkQ=";
You can test the configuration with nginx -t and if all is well, start nginx.
nginx -t
systemctl start nginx && systemctl enable nginx
From here on you can protect it further with Fail2ban reading your nginx logs, for instance.