Mikrotik RouterOS in Docker with Qemu

Good day to all! In this article I want to talk about one OpenSource project called Docker RouterOS , it was originally created as a testing ground for integration tests and nothing more, but over time, at the request of users, a number of improvements were added that expand the range of capabilities.


I will talk about the reasons for the appearance of the project, its initial iterations, as well as about the problems that I had to face.


Continued under the cut.


What for?


And this is the right question, perhaps it’s worth starting with it, in general a couple of years ago I started a project of one PHP library for working with RouterOS via the API (if the public likes this post, the article about the PHP library will not take long), as shown practice, to achieve stable operation of the library on different versions of RouterOS it is very useful to have tests.


Since it is correct to check the correct operation, such as login or the correct parsing of the data array, in the current implementation of the client API it is a non-trivial task to make a decision instead of writing various decisions to intercept requests and substitute answers to run tests on live hardware.


The initial version of the tests was carried out on my home router, and everything would be fine, but some tests changed some important settings of the router, which often led to inoperative Internet and household discontent, while other tests required an older version of RouterOS, not to roll back the OS due to of this.


And one day I was tired of it, and I began to look for a solution to the problem.


We can say it was the very moment when the idea of ​​a certain virtual environment that could be launched, say in VirtualBox, settled in my thoughts and did something good with it, without threatening to break something again.


Cloud Hosted Router , Mikrotik. CHR, , - "" . , CHR VDI/VMDK/OVA/etc. , RouterOS, .


, , 6.42 ( 6.44).


, 6.43 RouterOS API, , , .


VirtualBox , .


?


, .


?


Travis-CI, , VirtualBox Travis-CI , Travis "" .


… Docker!


?


Docker , , .


, " RouterOS Docker ?", QEMU, PC .


QEMU // , headless , Docker .


RouterOS QEMU - , , API.


image, Alpine Linux.


Version 0


:


Dockerfile
FROM alpine:3.8

RUN mkdir /routeros
WORKDIR /routeros
ADD [".", "/routeros"]

RUN apk add --no-cache --update netcat-openbsd qemu-x86_64 qemu-system-x86_64 \
 && wget "https://download.mikrotik.com/routeros/6.42.7/chr-6.42.7.vdi"

ENTRYPOINT ["/routeros/entrypoint.sh"]

WORKDIR , mkdir .


Dockerfile , alpine 3.8, , Mikrotik.


entrypoint.sh
#!/bin/sh

qemu-system-x86_64 \
    -vnc 0.0.0.0:0 \
    -m 256 \
    -hda /routeros/chr-6.42.7.vdi \
    -device e1000,netdev=net0 \
    -netdev user,id=net0,hostfwd=tcp::22-:22,hostfwd=tcp::23-:23,hostfwd=tcp::8728-:8728,hostfwd=tcp::8729-:8729

Entrypoint , , 256 , VNC 5900 , , .


, Docker, , . , , QEMU, Docker .


hostfwd , Docker , , .


Version 1


, URL , .


Dockerfile
FROM alpine:3.8

ENV ROUTEROS_VERSON="6.42.7"
ENV ROUTEROS_IMAGE="chr-$ROUTEROS_VERSON.vdi"
ENV ROUTEROS_PATH="https://download.mikrotik.com/routeros/$ROUTEROS_VERSON/$ROUTEROS_IMAGE"

RUN mkdir /routeros
WORKDIR /routeros
ADD [".", "/routeros"]

RUN apk add --no-cache --update netcat-openbsd qemu-x86_64 qemu-system-x86_64 \
 && echo ">>> $ROUTEROS_PATH" \
 && if [ ! -e "$ROUTEROS_IMAGE" ]; then wget "$ROUTEROS_PATH"; fi

# For access via VNC
EXPOSE 5900

# Default ports of RouterOS
EXPOSE 21
EXPOSE 22
EXPOSE 23
EXPOSE 80
EXPOSE 443
EXPOSE 8291
EXPOSE 8728
EXPOSE 8729

ENTRYPOINT ["/routeros/entrypoint.sh"]

EXPOS , best practice .


Dockerfile, , GitHub Docker Hub.


, Dockerfile , crontab.


cron.sh
#!/bin/bash

# Cron fix
cd "$(dirname $0)"

function getTarballs
{
    curl https://mikrotik.com/download/archive -o - 2>/dev/null | \
        grep -i vdi | \
        awk -F\" '{print $2}' | \
        sed 's:.*/::' | \
        sort -V
}

function getTag
{
    echo "$1" | sed -r 's/chr\-(.*)\.vdi/\1/gi'
}

function checkTag
{
    git rev-list "$1" 2>/dev/null
}

getTarballs | while read line; do
    tag=`getTag "$line"`
    echo ">>> $line >>> $tag"

    if [ "x$(checkTag "$tag")" == "x" ]
        then

            url="https://download.mikrotik.com/routeros/$tag/chr-$tag.vdi"
            if curl --output /dev/null --silent --head --fail "$url"; then
                echo ">>> URL exists: $url"
                sed -r "s/(ROUTEROS_VERSON=\")(.*)(\")/\1$tag\3/g" -i Dockerfile
                git commit -m "Release of RouterOS changed to $tag" -a
                git push
                git tag "$tag"
                git push --tags
            else
                echo ">>> URL don't exist: $url"
            fi

        else
            echo ">>> Tag $tag has been already created"
    fi

done

, , .


Travis-CI, , GitHub.


Version 2


, , , , RouterOS ICMP ( ) .


hostfwd, .


Qemu Docker, TAP eth0.


Debian, Alpine, , multistage . KVM, QEMU, .


VPS KVM, - KVM in KVM, Docker RouterOS KVM QEMU .


Dockerfile :


Dockerfile
FROM alpine:3.11

# For access via VNC
EXPOSE 5900

# Default ports of RouterOS
EXPOSE 21 22 23 80 443 8291 8728 8729

# Different VPN services
EXPOSE 50 51 500/udp 4500/udp 1194/tcp 1194/udp 1701 1723

# Change work dir (it will also create this folder if is not exist)
WORKDIR /routeros

# Install dependencies
RUN set -xe \
 && apk add --no-cache --update \
    netcat-openbsd qemu-x86_64 qemu-system-x86_64 \
    busybox-extras iproute2 iputils \
    bridge-utils iptables jq bash python3

# Environments which may be change
ENV ROUTEROS_VERSON="6.46.5"
ENV ROUTEROS_IMAGE="chr-$ROUTEROS_VERSON.vdi"
ENV ROUTEROS_PATH="https://download.mikrotik.com/routeros/$ROUTEROS_VERSON/$ROUTEROS_IMAGE"

# Download VDI image from remote site
RUN wget "$ROUTEROS_PATH" -O "/routeros/$ROUTEROS_IMAGE"

# Copy script to routeros folder
ADD ["./scripts", "/routeros"]

ENTRYPOINT ["/routeros/entrypoint.sh"]

best practice.


, , , , :


  • python3 — , udhcpd,
  • jq — ip addr -json
  • netcat-openbsd — ,

entrypoint :


entrypoint.sh
#!/bin/sh

###
###         ,
###      ,  
###    .
###

# Generate udhcpd configuration
/routeros/generate-dhcpd-conf.py $QEMU_BRIDGE > $DHCPD_CONF_FILE

# Get name of default interface
default=`ip -json route show | jq -r '.[] | select(.dst == "default") | .dev'`

# Finally, start udhcpd server
udhcpd -I $DUMMY_DHCPD_IP -f $DHCPD_CONF_FILE &

# And run the VM!
exec qemu-system-x86_64 \
    -nographic -serial mon:stdio \
    -vnc 0.0.0.0:0 \
    -m 256 \
    -nic tap,id=qemu0,script=$QEMU_IFUP,downscript=$QEMU_IFDOWN \
    "$@" \
    -hda $ROUTEROS_IMAGE

, , TAP . Docker RouterOS, RouterOS .


, udhcpd DNS ( 127.0.0.11) Docker Compose.


?


  • RouterOS Docker
  • , API Travis-CI
  • images RouterOS

, , . Docker Host RouterOS.


- ( ).


?



, udhcpd python - , bash.



, .



VDI RouterOS, Docker . , , RouterOS Linux Kernel , RouterOS .


Docker Host , , .



, 2010 , , LOR ON^W^W^W . Docker RouterOS , read-only mode .


, , , , , .


, , .




All Articles