So finally I bought a Ultimker 3 and it is freaking awesome. I especially like the fact that it has a very nice built in web interface, Cura Connect, allowing you to view the live feed, abort print jobs, reprint, etc.

BUT IT IS USELESS AS IS FOR HOME USERS

The thing is, the web interface does not have any form of authentication, making openning a port on your browser and exposing it extremly dangerous. And why use this in your network if the printer is just steps away?

What I really want is to be able to start a print from anywhere while keeping an eye on it. Ultimaker Cloud allows me to do the first half, but falls short on the second half.

Well, I just have to come up with my own solution.

What you need:

  • Router with OpenWrt
  • Your own Domain
  • Linux Environment, Linux subsystem for windows does NOT work
  • Basic computer skills and a bit of patience.

What we are doing (TL;DR):

  1. Build a custom nginx package on your Linux environment for your router
  2. Setup dynamic dns on your router
  3. Create two cnames to point to your router
  4. Get a free SSL certificate from Let’s Encrypt for those two cnames
  5. Create two separate nginx reverse proxies, one for your cura connect web page, one for your camera.
    • use sub_filter to swap the camera ip address to the cname
    • use basic auth for cura connect page
  6. Expose router ports to the nginx server

Let’s get started

Build Nginx with SSL

on your linux dev box, setup build environment

sudo apt-get install build-essential git-core subversion libssl-dev libncurses5-dev unzip gawk zlib1g-dev libncurses5-dev gcc-multilib flex gettext mercurial

Clone openWrt

git clone https://github.com/openwrt/openwrt.git
cd openwrt

Enter the config menu

make menuconfig

Select the correct target system and subtargets for your router, for example:

Target System : Atheros AR7xxx/AR9xxx
Subtarget : Generic
Target Profile : TP-Link TL-WR1043N/ND

Build OpenWrt

make V=s

This would take a while, about 1 hour.

After this is done, you can build Nginx:

scripts/feeds update
scripts/feeds search nginx
scripts/feeds install nginx
make menuconfig

Go to:

network
   Web Servers/proxies
       nginx : M
       nginx : ENTER
          Configuration
              Enable SSL module: Y

Build Nginx

time make -j5

Find built ipk pacakge, transfer it to your router and install it

find . -type f -name nginx\*ipk

Setup Dynamic DNS (Use your own preferred dns service if you wish)

Install luci-app-ddns on your router through the UI

Register an account at afraid: https://freedns.afraid.org

Add a free subdomain in the Dynamic DNS page, then copy the direct url link, grab the key behind the update.php?

Create a new entry in your Dynamic DNS page on your router UI

Use afraid-keyauth and paste your key in it. Setup everything else accordingly

After this, create two new CNAME and point them to the domain you just created for your Dnymaic DNS.

Get SSL Certificates from Let’s Encrypt

Install luci-app-acme on your router through the UI

Configure port forwarding in your router to forward both external 443 and 80 to your router’s 443 and 80.

Create a new entry in your ACME certs page in your router, add both cname to it, enable debug and save.

Either restart your browser, or ssh into your router and

/etc/init.d/acme restart

Check your system log for acme, if all is working, disable and remove the port forwarding, note where the cert is stored.

Edit nginx config

First generate a htpasswd file, this is gonna be your login username and password, replace the username in the command

printf "YOURUSERNAME:`openssl passwd -apr1`\n" >> .htpasswd

ssh into your router and with your favorite text editor, open up your /etc/nginx/nginx.config. Use this example file as reference, replace the !!xxx!! with correct value. Example:

user root;
worker_processes  1;

events {
    worker_connections  1024;
}

http {
  server_names_hash_bucket_size 64;
  include       mime.types;
  sendfile        on;
  keepalive_timeout  65;

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

  server {
    error_log /var/log/nginx/ultimaker_error.log;
    access_log /var/log/nginx/ultimaker_access.log;

    listen 8443;
    server_name !!your-cname!!;

    ssl_certificate           !!path-to-your-cert!!
    ssl_certificate_key       !!path-to-your-cert-key!!

    ssl on;
    ssl_session_cache  builtin:1000  shared:SSL:10m;
    ssl_protocols  TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;
    ssl_prefer_server_ciphers on;

    location / {
      proxy_set_header        Host $host;
      proxy_set_header        X-Real-IP $remote_addr;
      proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header        X-Forwarded-Proto $scheme;

      auth_basic "Restricted Content";
      auth_basic_user_file !!/path-to-your-htpasswd!!;

      proxy_pass          http://!!ultimaker_ip!!:80;
      proxy_read_timeout  90;

      proxy_redirect http://!!ultimaker_ip!!:80 https://!!your-cname!!;

      sub_filter_once off;
      sub_filter_types *;
      sub_filter 'http://"+r.ip_address+":8080' 'https://!!your-cname2!!';
      sub_filter 'http://"+c+":8080' 'https://!!your-cname2!!';
    }
  }

  server {
    error_log /var/log/nginx/ultimaker-cam_error.log;
    access_log /var/log/nginx/ultimaker-cam_access.log;
    listen 8443;
    server_name !!your-cname2!!;

    ssl_certificate !!path-to-your-cert!!
    ssl_certificate_key !!path-to-your-cert-key!!

    ssl on;
    ssl_session_cache  builtin:1000  shared:SSL:10m;
    ssl_protocols  TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;
    ssl_prefer_server_ciphers on;

    location / {
     proxy_set_header        Host $host;
     proxy_set_header        X-Real-IP $remote_addr;
     proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
     proxy_set_header        X-Forwarded-Proto $scheme;

     proxy_pass       http://!!ultimaker_ip!!:8080;
     proxy_read_timeout 90;

     proxy_redirect http://!!ultimaker_ip:8080!! https://!!your-cname2!!;
    }
  }
}

Let me explain it a little bit

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

This redirects http to https

sub_filter_once off;
sub_filter_types *;
sub_filter 'http://"+r.ip_address+":8080' 'https://!!your-cname2!!';
sub_filter 'http://"+c+":8080' 'https://!!your-cname2!!';

This replaces the video links on your page to the camera cname, your stuff might be a little different, use chrome page inspector to find these urls

auth_basic "Restricted Content";
auth_basic_user_file !!/path-to-your-htpasswd!!;

This adds authentication to your cura connect page

To finish up, setup port forwarding

Forward external 80 to your router’s 8081, Forward external 443 to yur router’s 8443.

Check your work by turning on your Ultimaker and load up your cname with your browser