DEV Community

loading...

Edge Router X | OpenWRT | Tailscale

Cason Adams
Updated on ・2 min read

Tailscale is an awesome opensource project. It leverages WireGuard to create a light weight VPN like connection.

I am using an EdgeRouter X with OpenWRT on it. This router is using a mipsle chipset.

Getting started

Build from source below or download the static bin file from builds

Clone the tailscale repo

git clone https://github.com/tailscale/tailscale.git

Build the mipsle binaries

Other options

GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build -o tailscale tailscale.com/cmd/tailscale
Enter fullscreen mode Exit fullscreen mode
GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build -o tailscaled tailscale.com/cmd/tailscaled
Enter fullscreen mode Exit fullscreen mode

Copy binaries to /usr/sbin/

scp <bins> root@<router-ip>:/usr/sbin/

Install deps

opkg update
opkg install ca-bundle kmod-tun
Enter fullscreen mode Exit fullscreen mode

Create /etc/init.d/tailscaled

NOTE line /usr/sbin/tailscale up is commented out remove comment after logging in manually

#!/bin/sh /etc/rc.common
### BEGIN INIT INFO
# Provides:       tailscaled
# Description:    tailscaled daemon service
### END INIT INFO

USE_PROCD=1

# starts after network starts
START=21
# stops before networking stops
STOP=89

PROG=/usr/sbin/tailscaled

start_service() {
        echo "starting tailscaled"
        procd_open_instance
        procd_set_param env SERVICE_RUN_MODE=1
        procd_set_param command $PROG -state /etc/tailscale/tailscaled.state
        procd_set_param pidfile /var/run/tailscaled.pid
        procd_set_param stdout 1
        procd_set_param stderr 1
        procd_set_param respawn
        procd_close_instance
        # /usr/sbin/tailscale up
}

service_triggers() {
        procd_add_reload_trigger "tailscaled"
}
Enter fullscreen mode Exit fullscreen mode

Make the file executable

chmod +x /etc/init.d/tailscaled
Enter fullscreen mode Exit fullscreen mode

Test service

/etc/init.d/tailscaled start
/usr/sbin/tailscale up

This should prompt with a url. Use this to authorize the device. If everything is good. Remove the comment in the /etc/init.d/tailscaled (tailscale up)

Enable service

/etc/init.d/tailscaled enable

Reboot

reboot
Verify things are working

Discussion (7)

Collapse
ichangemymind profile image
iChangeMyMind

I'm using:

Model:  Xiaomi Redmi Router AC2100
Architecture:   MediaTek MT7621 ver:1 eco:3
Firmware Version:   OpenWrt SNAPSHOT r15976-8078d89a53 / LuCI Master git-21.050.34860-b8d2bcd
Kernel Version: 5.4.100
Enter fullscreen mode Exit fullscreen mode

and for the tailscale I downloaded from pkgs.tailscale.com/unstable/#static

I copied your procd script and after I chmod +x and run it I got this error

root@OpenWrt:~# /etc/init.d/tailscaled start
': No such file or directory.common
Enter fullscreen mode Exit fullscreen mode

when I run tailscaled alone without using the procd it works but when I run it using procd I got that error again

how do I fix this?

Collapse
casonadams profile image
Cason Adams Author

What do your other service scripts look like that in, that /etc/init.d/ dir?

Maybe taking the first line from one of them and replace the first line in this script will correct the issue you are seeing?

Collapse
ichangemymind profile image
iChangeMyMind

here the inside of cron looks like:

#!/bin/sh /etc/rc.common
# Copyright (C) 2006-2011 OpenWrt.org

START=50

USE_PROCD=1
PROG=/usr/sbin/crond

validate_cron_section() {
    uci_validate_section system system "${1}" \
        'cronloglevel:uinteger'
}

start_service() {
    [ -z "$(ls /etc/crontabs/)" ] && return 1

    loglevel="$(uci_get "system.@system[0].cronloglevel")"

    [ -z "${loglevel}" ] || {
        /sbin/validate_data uinteger "${loglevel}" 2>/dev/null
        [ "$?" -eq 0 ] || {
            echo "validation failed"
            return 1
        }
    }

    mkdir -p /var/spool/cron
    ln -s /etc/crontabs /var/spool/cron/ 2>/dev/null

    procd_open_instance
    procd_set_param command "$PROG" -f -c /etc/crontabs -l "${loglevel:-5}"
    for crontab in /etc/crontabs/*; do
         procd_set_param file "$crontab"
    done
    procd_set_param respawn
    procd_close_instance
}

service_triggers() {
    procd_add_validation validate_cron_section
}
Enter fullscreen mode Exit fullscreen mode

here the inside of boot looks like:

#!/bin/sh /etc/rc.common
# Copyright (C) 2006-2011 OpenWrt.org

START=10
STOP=90

uci_apply_defaults() {
    . /lib/functions/system.sh

    cd /etc/uci-defaults || return 0
    files="$(ls)"
    [ -z "$files" ] && return 0
    mkdir -p /tmp/.uci
    for file in $files; do
        ( . "./$(basename $file)" ) && rm -f "$file"
    done
    uci commit
}

boot() {
    [ -f /proc/mounts ] || /sbin/mount_root
    [ -f /proc/jffs2_bbc ] && echo "S" > /proc/jffs2_bbc

    mkdir -p /var/run
    mkdir -p /var/log
    mkdir -p /var/lock
    mkdir -p /var/state
    mkdir -p /var/tmp
    mkdir -p /tmp/.uci
    chmod 0700 /tmp/.uci
    touch /var/log/wtmp
    touch /var/log/lastlog
    mkdir -p /tmp/resolv.conf.d
    touch /tmp/resolv.conf.d/resolv.conf.auto
    ln -sf /tmp/resolv.conf.d/resolv.conf.auto /tmp/resolv.conf
    grep -q debugfs /proc/filesystems && /bin/mount -o noatime -t debugfs debugfs /sys/kernel/debug
    grep -q bpf /proc/filesystems && /bin/mount -o nosuid,nodev,noexec,noatime,mode=0700 -t bpf bpffs /sys/fs/bpf
    grep -q pstore /proc/filesystems && /bin/mount -o noatime -t pstore pstore /sys/fs/pstore
    [ "$FAILSAFE" = "true" ] && touch /tmp/.failsafe

    /sbin/kmodloader

    [ ! -f /etc/config/wireless ] && {
        # compat for bcm47xx and mvebu
        sleep 1
    }

    /bin/config_generate
    uci_apply_defaults

    # temporary hack until configd exists
    /sbin/reload_config
}
Enter fullscreen mode Exit fullscreen mode

and here the inside of zerotier (installed using opkg) looks like:

#!/bin/sh /etc/rc.common

START=90

USE_PROCD=1

PROG=/usr/bin/zerotier-one
CONFIG_PATH=/var/lib/zerotier-one

section_enabled() {
    config_get_bool enabled "$1" 'enabled' 0
    [ $enabled -ne 0 ]
}

start_instance() {
    local cfg="$1"
    local port secret config_path local_conf path
    local args=""

    if ! section_enabled "$cfg"; then
        echo "disabled in config"
        return 1
    fi

    config_get config_path $cfg 'config_path'
    config_get port $cfg 'port'
    config_get secret $cfg 'secret'
    config_get local_conf $cfg 'local_conf'

    path=${CONFIG_PATH}_$cfg

    # Remove existing link or folder
    rm -rf $path

    # Create link from CONFIG_PATH to config_path
    if [ -n "$config_path" -a "$config_path" != "$path" ]; then
        if [ ! -d "$config_path" ]; then
            echo "ZeroTier config_path does not exist: $config_path" 1>&2
            return
        fi

        # ensure that the symlink target exists
        mkdir -p $(dirname $path)

        ln -s $config_path $path
    fi

    mkdir -p $path/networks.d

    # link latest default config path to latest config path
    rm -f $CONFIG_PATH
    ln -s $path $CONFIG_PATH

    if [ -n "$port" ]; then
        args="$args -p${port}"
    fi

    if [ -z "$secret" ]; then
        echo "Generate secret - please wait..."
        local sf="/tmp/zt.$cfg.secret"

        zerotier-idtool generate "$sf" > /dev/null
        [ $? -ne 0 ] && return 1

        secret="$(cat $sf)"
        rm "$sf"

        uci set zerotier.$cfg.secret="$secret"
        uci commit zerotier
    fi

    if [ -n "$secret" ]; then
        echo "$secret" > $path/identity.secret
        # make sure there is not previous identity.public
        rm -f $path/identity.public
    fi

    if [ -f "$local_conf" ]; then
        ln -s "$local_conf" $path/local.conf
    fi

    add_join() {
        # an (empty) config file will cause ZT to join a network
        touch $path/networks.d/$1.conf
    }

    config_list_foreach $cfg 'join' add_join

    procd_open_instance
    procd_set_param command $PROG $args $path
    procd_set_param stderr 1
    procd_close_instance
}

start_service() {
    config_load 'zerotier'
    config_foreach start_instance 'zerotier'
}

stop_instance() {
    local cfg="$1"

    # Remove existing link or folder
    rm -rf ${CONFIG_PATH}_${cfg}
}

stop_service() {
    config_load 'zerotier'
    config_foreach stop_instance 'zerotier'
    rm -f ${CONFIG_PATH}
}
Enter fullscreen mode Exit fullscreen mode

Everything looks similar to me? :/

Thread Thread
casonadams profile image
Cason Adams Author

any luck on this one?

Thread Thread
ichangemymind profile image
iChangeMyMind

Nope, I'm going back to zerotier The speed drawback is not that bad for my workflow anyway ¯_(ツ)_/¯

Thread Thread
casonadams profile image
Cason Adams Author

I am on an older kernel

Linux OpenWrt 4.14.209 #0 SMP Sun Dec 6 07:31:03 2020 mips GNU/Linux
Enter fullscreen mode Exit fullscreen mode

so maybe there are some diffs. Tailscale is worth the pain to figure it out.

Collapse
casonadams profile image
Cason Adams Author

Thanks for the link to the unstable static bins! Saves a few steps!