How to add 2FA to MikroTik logins

Important:

The RouterBOARD (and the device where the code is generated) must be perfectly synchronized with NTP.
Otherwise, it is impossible to log in.
No RouterBOARD sold since 2007 has an internal battery; the device ALWAYS starts with the wrong time.

ASCII-7 table:

<SPACE>!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~

Before starting, as a general rule, use only ASCII-7 characters on usernames and passwords,
and never any other characters because different versions of RouterOS, WinBox, Webfig, etc.,
have completely different responses...
Some work in ASCII-7, some in Windows-1252, some in UTF-8...
Another hint: Do not use spaces at start or at the end or spaces next to each other...


This guide is only for RouterOS v7 and tested only on 7.16.2 and 7.19.4

The scope of this guide is setting up OTP login on WinBox, WebFig, SSH, Telnet, BTest etc.

Since (for now) there isn't a dedicated field in WinBox & Co. for the OTP,
it simply needs to be added to the end of the password.

For example, if the username is myOTPuser and the password is myOTPpass,
the password to be entered becomes the password itself followed directly to the OTP 457 896 (WITHOUT THE SPACE),
such as myOTPpass457896

As you can see in the script, this is where the group the user logs in with is determined:
Mikrotik-Group:full
(default group are full,write,read and, if not specified, for default the user group is read)

For create the 40 OTP characters secret on Base32 (160 bit), use 25 characters password and paste this on terminal:
:put [:convert "LocalHostRadiusPassword!0" from=raw to=base32]
result is like:
JRXWGYLMJBXXG5CSMFSGS5LTKBQXG43XN5ZGIIJQ

On script obviously use your own RADIUS password (any adequate lenght), not LocalHostRadiusPassword

First install user-manager, if not already present, then use this script:

/user-manager
set enabled=yes
/user-manager router
add address=127.0.0.1 name="LocalHost" shared-secret="LocalHostRadiusPassword"
/radius
add address=127.0.0.1 secret="LocalHostRadiusPassword" service=login
/radius incoming
set accept=yes
/user aaa
set use-radius=yes

/user-manager user 
add attributes="Mikrotik-Group:full" name="myOTPuser" password="myOTPpass" \
    otp-secret=[:convert "LocalHostRadiusPassword!0" from=raw to=base32]

For create QR for auto setup various authenticators... do not trust...
Add manually the code:
JRXW GYLM JBXX G5CS MFSG S5LT KBQX G43X N5ZG IIJQ

Syntax for QR code generator:
otpauth://totp/MikroTik:rextended@forum.mikoritk.com?secret=JRXWGYLMJBXXG5CSMFSGS5LTKBQXG43XN5ZGIIJQ&issuer=Rextended&algorithm=SHA1&digits=6&period=30

Please add comments on how to improve this guide.

Thanks.

Special thanks to: @biki73


Caveats:
Obviously, every time you open the terminal from WebFig/Winbox,
or when you reboot the device upon reconnection,
it will ask for the password again and you must use a new... OTP...
BUT this is precisely the point of using 2FA, One Time Password, so that a password is only valid once...
(or at least the OTP are 1 milion numbers that not deterministically repeats at least every 347d 5h 20m valid for just 1m30s)

:put [convert value="LocalHostRadiusPassword!0" to=base32 from=raw]

How stupid... :person_facepalming:


I'm writing this down otherwise it will be lost.
User @biki73 (without use any word...) rightly pointed out that the base32 conversion is already integrated into RouterOS v7,
and there's no need for my script that I have wrote for v6 because base32 doesn't exist in old RouterOS.

How honest :slight_smile:

To be clear which part is not required...........
Is this what it should look like then?

/user-manager user 
add attributes="Mikrotik-Group:full" name="myOTPuser" password="myOTPpass" \
    otp-secret="LocalHostRadiusPassword!0"

OR
we dont need any /user-manager user rule at all???

Just a note: Mikrotik now supports "sk" ssh keys that are confirmed by a hardware token, such as a Yubikey.

How would that work ?
Special requirements on Yubi side ?

And who said there's an unnecessary part?

The guide is written as it should be done.

I don't fully know what you're asking, but here's my attempt at an answer.

A normal ssh key is used for authentication, however it has an annotation that it can only be accepted together with a verification against a specific FIDO/U2F token. There are many such implementations, including some that emulate it fully in software or using various TPM technologies (secure enclave, trustzone). The best way is of course with dedicated hw tokens, Yubikey is just the most well-known.

Keys of this type are generated normally via ssh-keygen by specifying a -sk key type. (There are possible additional settings.) Of course the device against which the key will be authenticated must be present.

The generated key can be used basically everywhere a normal ssh key would be, but it will require verification for each use. In more secure environments this has been in use for a very long time, and at this point ssh support for it is ubiquitous.

My spidey senses tell me that this is probably a reaction to the Cyber Resilience Act (CRA) in the EU that will probably require 2FA for administering networking stuff...

Figured it would turn out to be a requirement like this.
Time to change my old Yubikeys perhaps ...

It's been supported forever. Older ones don't support ed25519, only ecdsa. Fido2 is not required.

EDIT: Yeah, it looks like MT only supports ed25519-sk...

Great work @rextended in documenting the user manager!

Maybe you should use some rndstr to avoid cut-and-paste same codes... And perhaps variables... Here is what I tried, which worked.

Bonus It uses ANSI terminal code to generate hyperlinks that work via SSH (not in MacOS default Terminal, however) that you can use for the otpauth://... path.

Now if only MikroTik has some option to "Show 2FA Code" in WinBox settings (or better still if WinBox protocol reply that a 2FA was going to be needed) — but not much you can do about this...

One Time Setup in User Manager

This needs to be done once. If you want to store the user manager database on another disk, set userManagerDisk.


:global userManagerDisk ""
/user-manager/database set db-path="$userManagerDisk/user-manager"

:global radiusSharedSecret [:rndstr length=40]

/user-manager set enabled=yes
/user-manager/router add address=127.0.0.1 name=SystemUser2FA shared-secret=$radiusSharedSecret disabled=no

/radius add address=127.0.0.1 secret=$radiusSharedSecret service=login
/radius/incoming set accept=yes

/user/aaa set use-radius=yes

Create a new 2FA enabled user

Change the following variables as needed:


:global username "forumadmin"
:global password "forumpassword"
:global authgroup "full"

# Generates a 40 char length string to mimic SHA1 (which `:convert tranform=` does not support)
:global otpsecret [:pick [:convert to=base32 [:rndstr length=40]] 0 40]

# Add new RouterOS user to user-manager (works via AAA in /user)
/user-manager/user add attributes="Mikrotik-Group:$authgroup" name="$username" password="$password" otp-secret="$otpsecret"

# Output new user information

:put "New AAA user with 2FA created:"
:put $username
:put $password
:put ""
:put "To setup in 2FA TOTP Authenticator, include Apple Password user the following URL"

:global url "otpauth://totp/$username?secret=$otpsecret&issuer=RouterOS&algorithm=SHA1&digits=6&period=30"
:put $url

# or if use SSH from a real terminal, the following will create a clickable link in terminal using ANSI codes:

:put "\1B]8;;$url\07$url\1B]8;;\07"

I tested on MacOS, and in Tabby terminal, the clickable link works which takes you to the native MacOS Password application that support TOTP.

image

In Passwords, it will prompt you create a keychain item for it, including username and password, and will show the TOTP code with item in Password app. It does not actually use the username/password since RouterOS does not use keychain — but the TOTP code needed after password in RouterOS will be generate and works

Dangerous Cleanup Previous User Manager and RADIUS

If you need to rerun the setup script, this will NUKE any user-manager or radius config created above.

/user-manager/router remove [find]
/user-manager/user remove [find]
/radius remove [find]

FWIW, I tried using SHA512 via convert transform=sha512 and used that in URL etc... but I could not get that to work. But using a 40 char string (base32 encoded) does work. I'm not sure if a trailing = on the SHA512, or if RouterOS (or Apple) does not support hash=sha512 as OTP secret.

Nice work, but the guide was really written in a minimal way to make the concepts clear,
rather than also throwing in scripting, to make the concepts as easy as possible.

But now both are in the topic. :upside_down_face: