MikroTik: mikrotik-ssh command on Debian / Rapsbian

At home I have a MikroTik router/firewall; specifically an RB2011UIAS-RM. My Virgin Super Hub is in modem-only mode, leaving the MikroTik to handle all of the networking functions.

Just as a bit of an introduction / side-note, MikroTiks are amazing hobbyist routers. There is a fair learning curve depending on where your networking knowledge is, but once you understand it you can do so much with this device. And if your networking knowledge is novice but you want to further it, this is probably the best device to start learning on. For example, it will force you to actually understand NAT, rather than simplifications like “port forwarding”; which do have their place, but not if you’re trying to become a networking expert. And for what they are, they are surprisingly affordable!

How about in business? Well, I don’t think I’m the best person comment on it as a Cisco replacement. In a MikroTik training course I met a few people working for small/medium ISPs – some who thought these devices were great and looked forward to getting them in production, and others whose companies were already using them and just needed trained up themselves. I trust their judgement.

I can speak however from a DrayTek replacement point-of-view. From a technical perspective it’s a no-brainer, but from a business perspective? “Anyone can configure a DrayTek“, but to configure a MikroTik you really need to know what you’re doing. This is a huge disadvantage if your IT team has a mix of experts and novices. I don’t believe in making things difficult to ensure the job security of experts, even when I’m that expert myself.

So despite my love for them and my mild dislike of DrayTeks (moderate dislike of the 3900 series), in business I would only use a MikroTik for specific use cases where a DrayTek isn’t suitable and other appropriate devices are well out of budget.

mikrotik-ssh

Back on topic. I have several scripts that run on my Raspberry Pis to manipulate the MikroTik (more on those another time). Now the MikroTik does have an API, but I find the best way to manage it is using good old SSH, and I believe a lot of Linux admins would agree.

When having unattended scripts connect to the MikroTik, obviously they need to authenticate somehow. I’m not going to explain why the answer involves SSH private/public key authentication as that’s covered plenty elsewhere. But the question is how to implement it?

  • One option I played with was to have the private key group-readable. However SSH deeply complains about keys that aren’t 0600 (I think it might even refuse to run?), and it feels wrong having the key itself sitting there readable by the unprivileged service accounts my scripts need to run as.
  • Another option I considered was using setuid on the script. However by default you can’t do this on Linux as it’s a security concern. This Stack Exchange post has a good explanation as to why this is.

What I came up with is this:

  • A bash script that connects to the MikroTik by SSH.
  • A user account mikrotik-ssh dedicated to running the above script. The SSH private key is only readable by this account.
  • Sudoers configuration that allows the relevant user accounts to run that script as mikrotik-ssh without password.

Is this perfect? Using sudo to manage principle of least privilege for automated scripts (as opposed to interactively) is not a technique I have seen very often, so I imagine there is some disadvantage to this method. But I believe it is fairly secure nonetheless, and certainly better than the other two options. Although a compromised service account can SSH to the MikroTik, it cannot get the private key from the Linux filesystem itself – assuming my script does not have a vulnerability, e.g. one that that allows it to execute arbitrary local commands.

So, let’s get started:

# MikroTiks use Microsoft-style line endings, so we want dos2unix to convert them to Unix-style line endings.
> sudo apt-get install dos2unix

# We create a service account and group, with a home directory
> sudo useradd --create-home --system --user-group --shell /usr/sbin/nologin mikrotik-ssh

# We generate an SSH key for that user.
> sudo -u mikrotik-ssh ssh-keygen

# We copy the public key to the MikroTik.  By scping we also ensure it is added to known_hosts
> sudo -u mikrotik-ssh scp /home/mikrotik-ssh/.ssh/id_rsa.pub admin@mikrotik:/

One thing we want is to enable persistent SSH sessions. Keeping SSH sessions open after they have been otherwise exited allows much faster connections, meaning our scripts can repeatedly reconnect to the MikroTik quickly to run new commands. Courtesy of this Rackspace blog post:

> sudo -u mikrotik-ssh mkdir /home/mikrotik-ssh/.ssh/sockets
> sudo -u mikrotik-ssh nano /home/mikrotik-ssh/.ssh/config
Host *
ControlMaster auto
ControlPath ~/.ssh/sockets/%r@%h-%p
ControlPersist 600

Now we want to create a dedicated user on the MikroTik. You can do this however you feel like; I prefer WinBox. Go to System > Users. First, on the Groups tab, create a group and give it the permissions your scripts will need. If you don’t yet know what permissions are required. going by principle of least privilege, you’re probably best to start with just ssh, read and write, and then work your way up when commands fail due to lack of permissions. Below is what mine has ended up as after a few months’ of scripting.

Then, on the Users tab, create the user and make it a member of that group. Optionally you can restrict this user to connections from specific IP addresses.

Then, on the SSH Keys tab, import the key from the filesystem and assign it to the user you have created.

Now back on Linux, create the mikrotik-ssh script. I have it split into an include script (reused elsewhere) and the main script.

########################
###     Variables    ###
########################

# MikroTik username and hostname / IP
mikrotikuser="svc_ssh"
mikrotikhost="mikrotik"
mikrotiksshport=22

privatekey=/home/mikrotik-ssh/.ssh/id_rsa
publickey=/home/mikrotik-ssh/.ssh/id_rsa.pub

linuxuser=mikrotik-ssh

########################
###   Running User   ###
########################

# Get the Linux user's ID
set +o errexit
linuxuserid=$(id -u $linuxuser)
idstatus=$?
set -o errexit

if [[ $idstatus != 0 ]]
then
    echo "User $linuxuser for running MikroTik commands does not seem to exist on this system."
    exit 1
fi

# Set variable as per whether we are running as mikrotik-ssh user or not
if [[ $EUID -eq $linuxuserid ]]
then
    runasmikrotiksshuser=true
else
    runasmikrotiksshuser=false
fi

# And similarly with root
if [[ $EUID -eq 0 ]]
then
    runasroot=true
else
    runasroot=false
fi

########################
###   Check Prereq   ###
########################

# Some prerequisites we can only check when running as mikrotik-ssh / root
if [[ ("$runasmikrotiksshuser" = "true") || ("$runasroot" = "true") ]]
then
    # Check for SSH keys keys
    
    if [[ ! (-f "$privatekey") ]]
    then
        echo "Private key not found at $privatekey"
        exit 1
    fi

    if [[ ! (-f "$publickey") ]]
    then
        echo "Public key not found at $publickey"
        exit 1
    fi
fi
########################
###      Include     ###
########################

source /usr/local/bin/mikrotik-include

########################
###       Begin      ###
########################

# If not running as mikrotik-ssh user or root then we sudo to mikrotik-ssh
if [[ ("$runasmikrotiksshuser" = "false") && ("$runasroot" = "false") ]]; then
    echo "Rerunning $0 sudoed to user $linuxuser" 1>&2
    # $0 is this file as it was called (e.g. /usr/local/bin/mikrotik-ssh).
    # $@ is the rest of the arguments.
    sudo -u "$linuxuser" "$0" "$@"
    exit $?
fi

# Otherwise we continue

if [[ -n "$@" ]]
then
    # If running non-interactively (command passed)
    # MikroTik output includes CR-LF.  dos2unix to remove the CR
    ssh -l "$mikrotikuser" -p"$mikrotiksshport" -i "$privatekey" "$mikrotikhost" "$@" | dos2unix
else
    # If running interactively (no command passed)
    ssh -l "$mikrotikuser" -p"$mikrotiksshport" -i "$privatekey" "$mikrotikhost"
fi

> sudo chown root:mikrotik-ssh /usr/local/bin/mikrotik-ssh
> sudo chmod 755 /usr/local/bin/mikrotik-ssh

We’re going to use the mikrotik-ssh group for those who should be able to sudo as mikrotik-ssh without a password. For examplem if apache runs my scripts, we add the www-data user to this group:

> sudo usermod -a -G mikrotik-ssh www-data

Then we modify the sudoers file. Or what I prefer, create a new file under sudoers.d so that the main sudoers file is untouched.

> sudo visudo -f /etc/sudoers.d/mikrotik-ssh
# Allow the mikrotik-ssh group to run all MikroTik commands without a password.
%mikrotik-ssh   ALL=(mikrotik-ssh) NOPASSWD: /usr/local/bin/mikrotik-ssh

And that’s it! If you run mikrotik-ssh without any arguments, if you are a member of the mikrotik-ssh group then you will immediately SSH to the MikroTik without having to enter any password as per the sudoers configuration. If you pass it with arguments, they will be executed and the output returned.

> mikrotik-ssh /system identity print
Rerunning /usr/local/bin/mikrotik-ssh sudoed to user mikrotik-ssh
  name: MikroTik

So again, the way this is set up, assuming there are no vulnerabilities in the mikrotik-ssh script itself, all this allows you to do is SSH to the MikroTik without password, and without access to the underlying private key.

Of course you may not want to give service accounts for scripts the (vulner)ability of being able to perform arbitrary SSH commands on your MikroTik. In fact many people would argue that it doesn’t matter if the private key is kept from you if you have this access! (I would slightly disagree – security through obscurity is not security, but that doesn’t necessarily mean that security through obscurity is worse than nothing).

In any case another option is not to put mikrotik-ssh in sudoers, but to build further scripts that call mikrotik-ssh that are more restricted in their function, and add those scripts to sudoers. Put on your “SQL Injection Prevention” hat – lock down heavily what arguments these scripts accept (IP address regex?) and how they are processed. Or if the use-case permits, have them accept no arguments at all. It may help if you move as much of the scripting as possible from the Linux side to the MikroTik side, with MikroTik scripts.

Do you see any other security concerns with this method? Do you know a better way? Feel free to leave a comment below.

Leave a Comment

Your email address will not be published. Required fields are marked *