Bring missing features back to stock firmware.

Requirements

  • Own a domain name

Custom DDNS

The stock firmware can only use the specified public DDNS services(e.g. Asus DDNS), so we need customize it.

In this case, the domain are managed through Cloudflare DNS. For other providers, write scripts yourself or web search for finished works.

First, create a scirpt to update the DNS records.

Implementation

#!/usr/bin/env sh

# Cloudflare API v4
CLOUDFLARE_API="https://api.cloudflare.com/client/v4"
# X-Auth-Email
CLOUDFLARE_EMAIL="[email protected]"
# X-Auth-Key
CLOUDFLARE_KEY="_EXAMPLE_"
# Zone ID
CLOUDFLARE_ZONE_ID="_EXAMPLE_"
# Domain ID
DOMAIN_ID="_EXAMPLE_"
# Domain
DOMAIN="example.humorce.com"

PREV_WAN_IP_FILE="/jffs/prev_wan_ip"

get_json_value(){
  local json=$1
  local key=$2
  if [[ -z "$3" ]]; then
    local num=1
  else
    local num=$3
  fi
  local value=$(echo "${json}" | awk -F"[,:}]" '{for(i=1;i<=NF;i++){if($i~/'${key}'\042/){print $(i+1)}}}' | tr -d '"' | sed -n ${num}p)
  echo ${value}
}

# Get WAN IP
WAN_IP=$(curl -s ip.sb)

PREV_WAN_IP=""

if [ -f "$PREV_WAN_IP_FILE" ]; then
  PREV_WAN_IP=$(cat $PREV_WAN_IP_FILE)
fi

if [ "$WAN_IP" = "$PREV_WAN_IP" ]; then
  RESULT="UNCHANGED."
else
  API_RESULT=$(curl -s -X PUT "$CLOUDFLARE_API/zones/$CLOUDFLARE_ZONE_ID/dns_records/$DOMAIN_ID" \
  -H "X-Auth-Email: $CLOUDFLARE_EMAIL" \
  -H "X-Auth-Key: $CLOUDFLARE_KEY" \
  -H "Content-Type: application/json" \
  --data '{"type":"A","name":"'$DOMAIN'","content":"'$WAN_IP'","proxied":false}')
  SUCCESS=$(get_json_value $API_RESULT success)
  if [ SUCCESS ]; then
    RESULT="UPDATED."
    echo $WAN_IP > $PREV_WAN_IP_FILE
  else
    RESULT="FAILED."
  fi
fi

echo $(date +"%Y-%m-%d %H:%M:%S")" | $WAN_IP | $RESULT"

The script is saved to /jffs/scripts/customDDNS.

Most of the services can access via domain after domain DNS records is updated.

For services from client, port forwarding needs to be configured.

cannot access Web UI now, only HTTPS is supported when accessing the Web UI from WAN.

The built-in web server(httpd) and supporting service has some flaws, e.g. certificates/key(/etc/*.pem) will re-generate when each restart httpd, cannot be replaced and saved correctly, I tried some methods from people1, but it never works properly.

Reverse proxy is great idea for bypass these flaws.

SSL Certificate

An SSL certificate displays important information for verifying the owner of a website and encrypting web traffic with SSL/TLS, including the public key, the issuer of the certificate, and the associated subdomains.2

Download acme.sh

acme.sh is a pure Unix shell script implementing ACME client protocol.3

# git clone https://github.com/acmesh-official/acme.sh.git /jffs/acme.sh --depth=1

Try when done:

# /jffs/acme.sh/acme.sh --version

Issue Certificate (Updated: June 6, 2021)

From acme.sh v3.0.0, acme.sh is using Zerossl as default ca, you must register the account first(one-time) before you can issue new certs.4

Example for issue a ZeroSSL wildcard certificate:

Register your account following this doc: https://github.com/acmesh-official/acme.sh/wiki/ZeroSSL.com-CA

Set environment variables for Cloudflare DNS API:

# export CF_Token="_YOUR_TOKEN_"
# export CF_Account_ID="_YOUR_ACCOUNT_ID_"

Issue certificate:

# /jffs/acme.sh/acme.sh --home /jffs/acme.sh --issue --dns dns_cf -d humorce.com -d '*.humorce.com'

For more information, visit acme.sh project page.

Web Server

Install Nginx

# opkg install nginx

Alternative package: lighttpd

Nginx Configuration

The nginx configuration files is stored in /opt/etc/nginx. NGINXConfig will be useful if you are not good at this.

example nginx.conf for reverse proxy the Web UI listen on port 443:

user nobody;
worker_processes 1;

events {
    worker_connections 64;
}

http {
    charset utf-8;
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    server_tokens off;
    log_not_found off;

    # MIME
    include mime.types;
    default_type application/octet-stream;

    server {
        listen 443 ssl;
        server_name example.humorce.com;

        # SSL
        ssl_session_timeout 1d;
        ssl_session_cache shared:SSL:10m;
        ssl_session_tickets off;

        # certificates
        ssl_certificate /jffs/etc/cert.pem;
        ssl_certificate_key /jffs/etc/key.pem;

        # Mozilla Intermediate configuration
        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;

        location / {
            proxy_http_version                 1.1;
            proxy_cache_bypass                 $http_upgrade;

            # Proxy headers
            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-Host  $host;
            proxy_set_header X-Forwarded-Port  $server_port;

            # Proxy timeouts
            proxy_connect_timeout              60s;
            proxy_send_timeout                 60s;
            proxy_read_timeout                 60s;

            proxy_redirect off;
            proxy_pass http://127.0.0.1;
        }

        location = /favicon.ico {
            log_not_found off;
            access_log    off;
        }

        # gzip
        gzip            on;
        gzip_vary       on;
        gzip_proxied    any;
        gzip_comp_level 6;
        gzip_types      text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml;
    }
}

By default, port 443 is occupied by httpd, see optional.

Install Certificate

Install certificate for nginx.

# /jffs/acme.sh/acme.sh --home /jffs/acme.sh --install-cert -d humorce.com \
--key-file /jffs/etc/key.pem \
--fullchain-file /jffs/etc/cert.pem \
--reloadcmd "/opt/etc/init.d/S80nginx restart"

The Web UI can access from WAN via https://example.humorce.com, after certificate installed and Nginx restarted.

Optional

If the ISP does not block the HTTPS port, change the AiCloud web access port(default: 443) to another one, and then the web server can listen on it.

Attention (Updated: October 15, 2020)

In mainland China, the ISP may stop service to you, if the ISP scans that you are hosting a web server on the internet through personal broadband.

Personally, exposing the router’s Web UI to the internet is not recommended.

BE CAREFUL ABOUT YOUR PRIVACY AND LEGAL RISKS.

Cron Jobs

The cerificate and WAN IP will expires, setup cron jobs to keep them up to date.

Cron Schedule Sample

  • customDDNS check at every 15 minutes
  • acme.sh check at daily 00:00

vi /jffs/scripts/cronJobs:

#!/usr/bin/env sh

# acme.sh
/jffs/acme.sh/acme.sh --cron --home /jffs/acme.sh > /home/root/acme.sh.log
/usr/sbin/cru a 'acme.sh' '0 0 * * * /jffs/acme.sh/acme.sh --cron --home /jffs/acme.sh > /home/root/acme.sh.log'

# customDDNS
/jffs/scripts/customDDNS >> /home/root/customDDNS.log
cru a 'customDDNS' '*/15 * * * * /jffs/scripts/customDDNS >> /home/root/customDDNS.log'

Run at Startup

Attach these line to /jffs/scripts/usbMount.

...
# cronJobs
/jffs/scripts/cronJobs
...

References