Dynamic One-time Passwords with FreeRADIUS

In large data centers, administrators often have to manage dozens or even hundreds of network devices. Managing passwords for each user and device can then be very tedious and time-consuming. In this tutorial, we will provide a real-world example using the Dynamic One-time Password Feature of suSSHi.

A One-time Password (OTP) is an automatically generated numeric or alphanumeric string of characters that authenticates a user for a single transaction or login session. One-time Passwords avoid a number of shortcomings that are associated with traditional (static) passwords. The most important shortcoming that is addressed by OTPs is that in contrast to static passwords, they are not vulnerable to replay attacks. Additionally, an OTP is more secure than a static password, especially a user-created password, which can be weak and reused across multiple accounts.

In the following, we will explain what is needed to use this feature with FreeRADIUS, which claims to be “the most widely used RADIUS server in the world”.

Note

This knowledgebase article requires a running FreeRADIUS server and integrated target devices.

Workflow

When a user initiates a connection to an allowed target via a suSSHi Gateway, suSSHi Chef creates an individual One-Time Password for this connection. This One-Time Password will be send to the target by the suSSHi Gateway which than can be verified by an API call within the configured validity time.

Configuration

Note

For this knowledgebase article, we are using do.tp for YOUR-REALM.

suSSHi Chef

Access Profile

In suSSHi Chef, create a new Access Profile with the following parameters:

Parameter

Example Value

Name

Dynamic One-Time Password

Pref. Target Authentication #1

Keyboard Interactive

Pref. Target Authentication #2

Password

Target Password Source

Dynamic One-Time Password

Overwrite Target User

$1@YOUR-REALM

Target Password length

32

Max Validity

5

Now you can assign this Access Profile to an Access Rule.

Api Token

Next, create an API Token for the FreeRADIUS instance(s) in order to validate One-Time Passwords generated by suSSHi Chef.

Parameter

Example Value

Application Name

FreeRADIUS

Dynamic OTP Target Authentication

Validate

FreeRADIUS

Requirements

  1. Virtual Server Support (Version >= 2.0)

  2. Modules realm and rest enabled

Environment variables

Note

Please change the example values below to match your setup.

Key

Value

REST_API_ENDPOINT

https://susshi-chef.example.com/api/v1/operations/dotp/validate

REST_API_APPLICATION

FreeRADIUS

REST_API_APPLICATION

d32ac3cd71a94282ad284c76b6ebb92e23738268a59a941203918fd27a045fbb

When a user tries to log in (with a realm) on a FreeRADIUS backed system, the realm module splits the User-Name attribute into user and realm portions. The server will then proxy the request to the given realm, which are defined in the proxy.conf file

/etc/raddb/proxy.conf

proxy server {
    default_fallback = no
}

realm do.tp {
    virtual_server = do.tp-prod
}

realm LOCAL {
}

In our configuration, the realm do.tp is handled by a virtual server named do.tp-prod.

/etc/raddb/sites-enabled/do.tp-prod

server do.tp-prod {
    authorize {
        filter_username
        preprocess
        pap

        if (&User-Password) {
            update control {
                &control:REST-HTTP-Header += "Api-Application: $ENV{REST_API_APPLICATION}"
                &control:REST-HTTP-Header += "Api-Token: $ENV{REST_API_TOKEN}"
                Auth-Type := 'rest-prod'
            }
        }
    }

    authenticate {
        Auth-Type PAP {
            pap
        }

        rest-prod
    }
}

The virtual server then communicates with the suSSHi Chef REST APIs though the rest module and the configuration named rest-prod.

/etc/raddb/mods-enabled/rest-prod

rest rest-prod {
    connect_uri = "$ENV{REST_API_ENDPOINT}"

    authenticate {
        uri = "${..connect_uri}"
        method = 'post'
        require_auth
        body = 'json'
        data = '{"target_username": "%{User-Name}", "target_password": "%{User-Password}"}'
    }

    pool {
        start = 0
        min = 1
        uses = 0
        retry_delay = 30
        lifetime = 0
        idle_timeout = 60
    }
}