Многофакторная аутентификация SSH с помощью tru.ID

Secure Shell (SSH) – это сетевой протокол, который позволяет пользователям безопасно подключаться с одного компьютера к другому удаленно. Большинство людей, использующих SSH, являются системными администраторами, и наиболее часто этот протокол используется для удаленного управления сервером.

Неважно, на каком этапе своей технической карьеры вы находитесь; если вы работаете с серверами, SSH как протокол понадобится вам для модификации или обслуживания удаленных серверов в бесчисленных случаях. Возможно, вы повысили безопасность своего сервера, используя SSH-сертификат вместо имени пользователя и пароля. Однако кто-то со злым умыслом все равно может получить доступ к этим методам аутентификации. Вот почему стоит включить многофакторную аутентификацию (MFA) для вашего SSH-сервера.

API tru.ID PhoneCheck подтверждает принадлежность номера мобильного телефона, проверяя наличие активной SIM-карты с тем же номером. Этот метод не только создает удобство для пользователей, но и значительно более безопасен, чем традиционные методы, такие как SMS OTP. API tru.ID обращается непосредственно к мобильной сети, а не полагается на уязвимые протоколы SS7, обеспечивая проверку SIM-карты в режиме реального времени менее чем за 2 секунды.

Прежде чем начать

Чтобы следовать этому руководству, вам понадобятся:

  • Учетная запись tru.ID
  • Мобильный телефон с активной SIM-картой для передачи данных
  • Docker, установленный локально
  • Node, установленный локально

Начало работы

На GitHub была создана ветка репозитория, содержащая базовый код, необходимый для начала работы.

В терминале клонируйте ветку starter-files для этого репозитория с помощью следующей команды:

git clone -b starter-files git@github.com:tru-ID/tru-id-ssh-auth.git
Войти в полноэкранный режим Выйти из полноэкранного режима

Если вас интересует готовый код, вы можете найти полный пример в ветке main. Чтобы получить этот код, выполните:

git clone -b main git@github.com:tru-ID/tru-id-ssh-auth.git
Войти в полноэкранный режим Выйти из полноэкранного режима

Для выполнения API-запросов PhoneCheck требуется учетная запись tru.ID, поэтому прежде чем продолжить, убедитесь, что вы ее создали.

Теперь перейдите в консоль tru.ID и создайте проект. После создания проекта, прежде чем закрыть окно или уйти, убедитесь, что вы скачали файл учетных данных tru.json и переместили его в корневой каталог каталога проекта tru-id-ssh-auth.

В файле учетных данных вы найдете ваш project_id, project_name, доступные вашему проекту диапазоны, а также ваши client_id и client_secret, которые вы будете использовать для создания токена аутентификации для выполнения запросов API.

Определите основные переменные

По всему коду вы будете повторно использовать несколько битов информации; это такие переменные, как, например, URL PhoneCheck и целевой каталог, в который установлен ваш проект. Ниже строки VERSION=1 в файле ssh-auth добавьте следующее:

# Base URL information
DATA_RESIDENCY=eu
BASE_URL="https://$DATA_RESIDENCY.api.tru.id"

# API Check URLs
PHONE_CHECK_URL="$BASE_URL/phone_check/v0.2/checks"
GET_PHONE_CHECK_URL="$BASE_URL/phone_check/v0.2/checks/"
CREATE_AUTH_TOKEN_URL="$BASE_URL/oauth2/v1/token"

APP_ROOT=`dirname $0`
CONFIG_FILE="$APP_ROOT/tru-id-ssh-auth.conf"
DEST_DIRECTORY="/usr/local/bin/"

# Empty Global Variables
BASIC_AUTH_USER=
BASIC_AUTH_PASSWORD=
ACCESS_TOKEN=
Войти в полноэкранный режим Выйти из полноэкранного режима

Вы можете изменить только два значения:

Создать установку

Большая часть этого проекта будет находиться в одном единственном файле bash, с несколькими доступными командами. Первая – это команда install, которая копирует соответствующий код в нужный каталог установки. Вы также примените команду ForceCommand к конфигурационному файлу SSHd, который вы добавите позже в учебнике.

Начните с поиска функции require_curl в файле ssh-auth. Ниже этой функции require_curl добавьте следующий код, который будет вашей новой функцией install():

function install() {
    config_file="${DEST_DIRECTORY}tru-id-ssh-auth/tru-id-ssh-auth.conf"

    set -e
    echo "Making directory in ${DEST_DIRECTORY}"
    mkdir "/usr/local/bin/tru-id-ssh-auth"
    set +e

    if [[ ! -r `dirname "${DEST_DIRECTORY}"` ]]
    then
      echo "${DEST_DIRECTORY} is not writable. Try again using sudo"
      return $FAIL
    fi

    echo "Copying /root/tru-id-ssh-auth/ to ${DEST_DIRECTORY}..."
    cp -r "/root/tru-id-ssh-auth" "${DEST_DIRECTORY}"

    echo "Setting up permissions..."
    chmod 755 $DEST_DIRECTORY

    if [[ ! -f ${config_file} ]]
    then
      echo "Generating initial config on ${config_file}..."
      echo "header=tru.ID SSH Auth initialised." > "${config_file}"
    else
      echo "A config file was found on ${config_file}. Edit it manually if you want to change the API key"
    fi

    chmod 644 ${config_file}

    echo ""
    echo "To enable tru.ID authentication on a user the following command: "
    echo ""

    echo "   ${DEST_DIRECTORY}tru-id-ssh-auth/ssh-auth register-user <username> <phone-number-inc-country-code>"
    echo "   Example: ${DEST_DIRECTORY}tru-id-ssh-auth/ssh-auth register-user test 447000000000"

    echo ""
    echo "To uninstall tru.ID SSH type:"
    echo ""
    echo "   ${DEST_DIRECTORY}tru-id-ssh-auth/ssh-auth uninstall"
    echo ""
    echo "      Restart the SSH server to apply changes"
    echo ""
}
Вход в полноэкранный режим Выйти из полноэкранного режима

В этой новой функции есть несколько шагов. К ним относятся:

  • Создайте каталог для размещения установленного приложения, /usr/local/bin/tru-id-ssh-auth.
  • Проверить, что каталог доступен для записи. Если нет, то выдайте ошибку.
  • Скопируйте файлы проекта во вновь созданный каталог назначения.
  • Проверьте, существует ли файл конфигурации. Если нет, то создайте его.
  • Вывести текст в Terminal, инструктируя пользователей, как использовать и удалить приложение.

Как уже говорилось ранее, для деинсталляции этого скрипта необходима дополнительная функциональность. Ниже созданной функции install() добавьте следующую функцию uninstall():

function uninstall() {
    find_sshd_config

    if [[ $1 != "quiet" ]]
    then
      echo "Uninstalling tru.ID SSH from $SSHD_CONFIG..."
    fi

    if [[ -w $SSHD_CONFIG ]]
    then
        sed -ie '/^ForceCommand.*tru-id-ssh-auth.*/d' $SSHD_CONFIG
    fi

    if [[ $1 != "quiet" ]]
    then
        echo "tru.ID SSH was uninstalled."
        echo "Now restart the ssh server to apply changes and then remove ${DEST_DIRECTORY}/tru-id-ssh-auth"
    fi
}
Вход в полноэкранный режим Выход из полноэкранного режима

Первая строка в этой новой функции должна вызвать функцию find_sshd_config(), поэтому добавим следующее:

function find_sshd_config() {
    echo "Trying to find sshd_config file"
    if [[ -f /etc/sshd_config ]]
    then
        SSHD_CONFIG="/etc/sshd_config"
    elif [[ -f /etc/ssh/sshd_config ]]
    then
        SSHD_CONFIG="/etc/ssh/sshd_config"
    else
        echo "Cannot find sshd_config in your server. tru.ID SSH Auth will be enabled when you add your specific ForceCommand to the sshd config file."
    fi
}
Вход в полноэкранный режим Выйти из полноэкранного режима

Новая функция находит файл sshd_config вашего сервера и сохраняет его расположение в качестве глобальной переменной.

Функции install() и uninstall() недоступны, пока вы не добавите их в качестве параметра. В списке команд для этого проекта найдите строку:

case $1 in
Войти в полноэкранный режим Выйти из полноэкранного режима

Этот раздел кода содержит функциональность, которая понимает различные аргументы в команде при запуске вашего сценария bash. Поэтому ниже строки case добавьте следующие два новых аргумента командной строки:

install)
    check_dependencies
    install
    ;;
uninstall)
    uninstall
    ;;
Войти в полноэкранный режим Выйти из полноэкранного режима

Регистрация пользователей

Процесс аутентификации SIM требует индивидуальной регистрации пользователей путем ввода номера телефона и связывания его с именем пользователя, чтобы можно было пройти верификацию с помощью API PhoneCheck от tru.ID.

Для регистрации нового пользователя создайте новую функцию register_user(), скопировав приведенный ниже пример в ваш файл ssh_auth:

function register_user() {
  config_file="${DEST_DIRECTORY}tru-id-ssh-auth/tru-id-ssh-auth.conf"

  if [[ $2 && $3 ]]
  then
      echo "user=$2:$3" >> ${config_file}
      echo "" >> ${config_file}
      echo "User was registered"
  else
      echo "Cannot register user"
  fi
}
Вход в полноэкранный режим Выход из полноэкранного режима

Ваша новая функция register_user() находит файл tru-id-ssh-auth.conf и добавляет следующую строку: user={имя пользователя}:{номер телефона}.

В нижней части файла найдите строку case $1 in. Ниже этой строки включите функцию register_user() в качестве аргумента командной строки, добавив следующее:

    register-user)
        check_dependencies
        register_user "$@"
        ;;
Войти в полноэкранный режим Выйти из полноэкранного режима

Разрешить пользователю вход

После успешной проверки PhoneCheck будет вызвана функция run_shell, которая сообщит плагину, что пользователю разрешен доступ к его SSH-сессии. В файле ssh-auth добавьте следующую функцию:

# Once successful, allows user to proceed with access to server
function run_shell() {
    if [[ "$SSH_ORIGINAL_COMMAND" != "" ]]
    then
        exec /bin/bash -c "${SSH_ORIGINAL_COMMAND}"
    elif [ $SHELL ]
    then
        exec -l $SHELL
    fi

    exit $?
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Создание PhoneCheck

Получение учетных данных

Для выполнения вызовов API tru.ID вам необходимо получить учетные данные вашего проекта, которые вы можете найти в файле tru.json, перенесенном в ваш проект ранее. Приведенная ниже функция get_credentials() извлекает client_id и client_secret из этого файла, а затем устанавливает их в качестве двух глобальных переменных: BASIC_AUTH_USER и BASIC_AUTH_PASSWORD. Добавьте эту новую функцию в ваш файл ssh-auth:

function get_credentials() {
  FILE="${DEST_DIRECTORY}tru-id-ssh-auth/tru.json"

  if [ -f "$FILE" ]; then
    BASIC_AUTH_USER=$(jq -r ".credentials[].client_id" ${FILE})
    BASIC_AUTH_PASSWORD=$(jq -r ".credentials[].client_secret" ${FILE})

    return $OK
  fi

  echo "Unable to retrieve project credentials. Please make sure your project directory has a `tru.json` file."
  exit $FAIL
}
Вход в полноэкранный режим Выйти из полноэкранного режима

Создание маркеров доступа

Следующей частью создания PhoneCheck через API является добавление функциональности для генерации маркеров доступа с использованием учетных данных вашего проекта tru.ID. Эта новая функциональность будет делать POST запрос на https://{DATA_RESIDENCY}.api.tru.id/oauth2/v1/token, с заголовком, содержащим ваши BASIC_AUTH_USER и BASIC_AUTH_PASSWORD, сопряженные с : между ними, а затем вся строка кодируется в base64. Полученный токен доступа будет использоваться для дальнейших безопасных запросов к API tru.ID.

Скопируйте приведенную ниже функцию create_access_token() в ваш файл ssh_auth.

# Creates an access token needed to create a PhoneCheck request.
function create_access_token() {
    get_credentials

    CREDENTIALS=$(echo -ne "$BASIC_AUTH_USER:$BASIC_AUTH_PASSWORD" | base64 -w 0);

    # Make request to get access token
    response=`curl 
        --header "Authorization: Basic $CREDENTIALS" 
        --header "Content-Type: application/x-www-form-urlencoded" 
        --request POST $CREATE_AUTH_TOKEN_URL
        --data-urlencode "grant_type=client_credentials" 
        --data-urlencode "scope=phone_check coverage" --silent`

    curl_exit_code=$?

    # Parses response to get the access token
    ACCESS_TOKEN=$(jq -r .access_token <<< "${response}" )

    if [ $curl_exit_code -ne 0 ]
    then
        echo $curl_exit_code
        echo "Error running curl"
    fi
}
Вход в полноэкранный режим Выход из полноэкранного режима

Создайте PhoneCheck

Вы создали функции, позволяющие получать учетные данные вашего проекта из файла tru.json; ваша следующая функция использует учетные данные из этого файла для создания маркера доступа, который вы будете использовать для аутентифицированных вызовов API.

Следующим шагом в этом руководстве является инициализация PhoneCheck, которая будет делать следующее:

  • Получать текущего пользователя, пытающегося войти в систему.
  • Прочитайте конфигурационный файл, чтобы узнать, зарегистрирован ли у этого пользователя номер телефона, чтобы потребовался шаг MFA.
  • Сделайте POST запрос к https://{DATA_RESIDENCY}.api.tru.id/phone_check/v0.2/checks" с номером телефона пользователя.

Скопируйте приведенную ниже функцию в ваш файл ssh_auth, чтобы добавить описанную функциональность в ваш проект:

function create_check() {
    config_file="${DEST_DIRECTORY}tru-id-ssh-auth/tru-id-ssh-auth.conf"
    current_user=$USER;
    phone_number="";

    while read line; do
        if [[ $line =~ user=$current_user: ]] ; then
            phone_number=$(echo "${line}" | sed s/user=${current_user}://g);
        fi
    done <${config_file}

    if [ "$phone_number" == "" ]; then
        echo "Phone Number is empty"

        return 0
    fi

    # Checking Credentials are installed
    get_credentials

    # Creating an Access Token
    create_access_token

    # Making a Phone Check request"
    response=`curl 
        --header "Authorization: Bearer $ACCESS_TOKEN" 
        --header "Content-Type: application/json" 
        --request POST $PHONE_CHECK_URL
        --data-raw "{"phone_number":"${phone_number}"}" --silent`

    curl_exit_code=$?

    if [ $curl_exit_code -ne 0 ]
    then
        echo "Error running curl"
        return 1;
    fi

    # Handling Phone Check Response
    check_id=$(jq -r .check_id <<< "${response}" )
    status=$(jq -r .status <<< "${response}" )
    check_url=$(jq -r ._links.check_url.href <<< "${response}" )

    return $CHECK_STATUS;
}
Вход в полноэкранный режим Выйти из полноэкранного режима

Генерировать QR-код

Когда пользователь создал PhoneCheck, следующим шагом в потоке MFA будет открытие на устройстве пользователя URL-адреса проверки своего оператора мобильной сети.

Самый простой способ для пользователя открыть URL – это QR-код. Если вы проверите свой Dockerfile в каталоге проекта, вы уже установили библиотеку qr, когда собирали контейнер Docker. Поэтому следующим шагом будет получение check_url из ответа PhoneCheck. Этот check_url должен быть преобразован в QR-код и отображен для пользователя.

Внутри create_check() найдите строку check_url=$(jq -r ._links.check_url.href <<< "${ответ}" ), а ниже добавьте следующее:

# Generate QR code
qr --ascii "${check_url}" > "qrcode.txt"

sleep 1

cat ~/qrcode.txt
Войти в полноэкранный режим Выйти из полноэкранного режима

Настроить опрос

Следующим шагом в этом процессе будет проверка конечной точки API tru.ID https://{DATA_RESIDENCY}.api.tru.id/phone_check/v0.2/checks/{check_id}, чтобы определить, было ли соответствие между номером телефона и запросом на URL проверки или нет. После создания чека пользователю дается двухминутное окно для открытия URL. Приложение будет делать запросы к API каждые пять секунд для обновления статуса.

Если значение статуса COMPLETED и тело ответа содержит значение match как true, то пропустите пользователя; в противном случае откажите в доступе к серверу.

Скопируйте новую функцию start_polling() в ваш файл ssh_auth:

function start_polling() {
    # Check every 5 seconds for status on Check.
    interval_in_seconds=5
    CHECK_STATUS=$FAIL

    while true;
    do
        # Check status of phone check
        response=`curl 
            --header "Authorization: Bearer $ACCESS_TOKEN" 
            --header "Content-Type: application/json" 
            --request GET $GET_PHONE_CHECK_URL/${check_id}  --silent`

        curl_exit_code=$?

        if [ $curl_exit_code -ne 0 ]
        then
            echo "Error running curl"

            return $FAIL;
        fi

        status=$(jq -r .status <<< "${response}" )
        match=$(jq -r .match <<< "${response}" )

        # If check is complete, output
        if [[ "$status" != "PENDING" && "$status" != "ACCEPTED" ]]; then
            if [ "$status" == "COMPLETED" ]; then
                if [ "$match" == "true" ]; then
                  CHECK_STATUS=$OK;

                  run_shell
                else
                  echo "No match found!"
                  CHECK_STATUS=$FAIL

                  return $FAIL;
                fi
            elif [ "$status" == "EXPIRED" ]; then
                echo "Check Expired";
                CHECK_STATUS=$FAIL

                return $FAIL;
            elif [ "$status" == "ERROR" ]; then
                echo "Check Error Received";
                CHECK_STATUS=$FAIL

                return $FAIL;
            else
                echo "$status"
                echo "404, no status was found";
                CHECK_STATUS=$FAIL

                return $FAIL;
            fi

            break;
        fi

        # Otherwise continue
        sleep $interval_in_seconds;
    done
}
Вход в полноэкранный режим Выйти из полноэкранного режима

Вам нужно вызвать эту новую функцию внутри вашей функции create_check(). Найдите строку cat ~/qrcode.txt, а ниже добавьте следующую строку, чтобы вызвать функцию start_polling:

# Start polling
start_polling
Войти в полноэкранный режим Выйти из полноэкранного режима

Добавьте create_check в качестве аргумента командной строки, найдя строку: case $1 in, и добавив следующее:

    create-check)
        check_dependencies
        create_check "$@"
        ;;
Войти в полноэкранный режим Выйти из полноэкранного режима

Добавьте ForceCommand

В вашем файле ssh-auth вы уже вызвали функцию add_force_command, но она еще не существует для добавления ForceCommand в ваш файл sshd_config. Поэтому добавьте эту функцию:

function add_force_command() {
    echo "Trying to add force command to $SSHD_CONFIG"
    find_sshd_config
    auth_ssh_command="$1"

    if [[ -w $SSHD_CONFIG ]]
    then
      echo "Adding 'ForceCommand ${auth_ssh_command} login' to ${SSHD_CONFIG}"
      uninstall "quiet" # remove previous installations

      echo -e "nForceCommand ${auth_ssh_command} login" >> ${SSHD_CONFIG}
      echo ""

      check_sshd_config_file

      echo "    MAKE SURE YOU DO NOT MOVE/REMOVE ${auth_ssh_command} BEFORE UNINSTALLING tru.ID SSH Plugin"
      sleep 5
    fi
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Если вы следуете этому руководству, используя Docker, откройте файл sshd_config в каталоге вашего проекта и в нижней части этого файла добавьте следующую команду force:

ForceCommand /usr/local/bin/tru-id-ssh-auth/ssh-auth create-check
Войти в полноэкранный режим Выйти из полноэкранного режима

После добавления вы можете перейти к завершению PhoneCheck. Однако, если вы устанавливаете программу на сервер, пожалуйста, продолжите инструкцию ниже:

Примечание: Если вы следуете этому руководству, используя контейнер Docker, вам не понадобится этот фрагмент кода; однако при установке на сервер, отличный от Docker, эта строка необходима, и ее нужно не комментировать. Найдите строку chmod 644 ${config_file} внутри вашей функции install() и добавьте следующую; ее также нужно будет откомментировать.

    # When following this tutorial, leave the line below commented out.
    # Restarting your SSH server within Docker will restart the whole Docker container.
    # add_force_command "${DEST_DIRECTORY}tru-id-ssh-auth/ssh-auth create-check"
Вход в полноэкранный режим Выход из полноэкранного режима

Завершение PhoneCheck

Последним шагом в процессе PhoneCheck является завершение PhoneCheck. Когда мобильное устройство открывает URL проверки оператора мобильной сети, оно перенаправляется обратно на ваш redirect_url, который при последнем запросе будет иметь ответ, содержащий code. Этот код необходимо отправить через API с вашими учетными данными, чтобы завершить PhoneCheck.

В вашем репозитории starter-files вы найдете шаблон веб-сервера, встроенного в node. Это веб-сервер, который будет содержать код вашего redirect_url, который будет выполнять эту функциональность.

Установите сторонние зависимости

В терминале перейдите в каталог webserver и выполните следующую команду для установки сторонних библиотек, необходимых для этого руководства. Эти библиотеки включают express для функциональности веб-сервера, ngrok для обеспечения общедоступного URL и http-signature для проверки подписи в запросе API.

npm install
Вход в полноэкранный режим Выход из полноэкранного режима

Создайте веб-крючок

Найдите конечную точку:

app.get("/", async (req, res) => {
  res.send('hello');
});
Войти в полноэкранный режим Выйти из полноэкранного режима

И замените его на:

app.get("/complete-check", async (req, res) => {
  if (!req.query) {
    res.status(400).send("body missing");

    return;
  }

  const { code, check_id } = await req.query;

  if (!code) {
    res.status(400).send("code missing");

    return;
  }

  if (!check_id) {
    res.status(400).send("check_id missing");

    return;
  }

  if (req.query.redirect_url) {
    const verified = await tru.verifySignature(req.query.redirect_url);

    if (!verified) {
      res.status(400).send("signature not verified");

      return;
    }
  }

});
Войти в полноэкранный режим Выйти из полноэкранного режима

В приведенном выше примере вы создаете новый вебхук /complete-check, который доступен с помощью запроса GET. Этот запрос принимает три параметра в URL, check_id, code и redirect_url. Эти параметры используются для завершения PhoneCheck. После проверки на то, что они были включены, код проверяет, действительна ли подпись для url перенаправления.

Далее, в директории webserver создайте новый файл tru.js, в котором вы добавите функциональность API tru.ID. В этом файле первым делом добавьте зависимости, которые будут использоваться, а также определите глобальные переменные и объекты. Добавьте следующее:

const moment = require("moment");
const fetch = require("node-fetch");
const httpSignature = require("http-signature");
const jwksClient = require("jwks-rsa");
const config = require("../tru.json");

const tru_api_base_url = 'https://eu.api.tru.id';
const keyClient = jwksClient({
  jwksUri: `${tru_api_base_url}/.well-known/jwks.json`,
});

// token cache in memory
const TOKEN = {
  accessToken: undefined,
  expiresAt: undefined,
};
Войти в полноэкранный режим Выйти из полноэкранного режима

При выполнении запроса к конечной точке API tru.ID вам понадобится токен доступа:

async function getAccessToken() {
  // check if existing valid token
  if (TOKEN.accessToken !== undefined && TOKEN.expiresAt !== undefined) {
    // we already have an access token let's check if it's not expired
    // I'm removing 1 minute just in case it's about to expire better refresh it anyway
    if (
      moment()
        .add(1, "minute")
        .isBefore(moment(new Date(TOKEN.expiresAt)))
    ) {
      // token not expired
      return TOKEN.accessToken;
    }
  }

  const url = `${tru_api_base_url}/oauth2/v1/token`;

  const toEncode = `${config.credentials[0].client_id}:${config.credentials[0].client_secret}`;
  const auth = Buffer.from(toEncode).toString('base64');

  const requestHeaders = {
    Authorization: `Basic ${auth}`,
    "Content-Type": "application/x-www-form-urlencoded",
  };

  const res = await fetch(url, {
    method: "post",
    headers: requestHeaders,
    body: new URLSearchParams({
      grant_type: "client_credentials",
      scope: "phone_check coverage",
    }),
  });

  if (!res.ok) {
    return res.status(400).body("Unable to create access token")
  }

  const json = await res.json();

  // update token cache in memory
  TOKEN.accessToken = json.access_token;
  TOKEN.expiresAt = moment().add(json.expires_in, "seconds").toString();

  return json.access_token;
}
Войти в полноэкранный режим Выход из полноэкранного режима

Необходима новая функция patchPhoneCheck, которая будет делать PATCH запрос к API tru.ID: /phone_check/v0.2/checks/${checkId}, с кодом, содержащимся в теле. Это метод проверки того, что владелец SIM-карты был тем, кто запросил PhoneCheck. В ваш файл tru.js добавьте следующую новую функцию:

async function patchPhoneCheck(checkId, code) {
  const url = `${tru_api_base_url}/phone_check/v0.2/checks/${checkId}`;
  const body = [{ op: "add", path: "/code", value: code }];

  const token = await getAccessToken();
  const requestHeaders = {
    Authorization: `Bearer ${token}`,
    "Content-Type": "application/json-patch+json",
  };

  const res = await fetch(url, {
    method: "patch",
    headers: requestHeaders,
    body: JSON.stringify(body),
  });

  if (!res.ok) {
    return res;
  }

  const json = await res.json();

  return json;
}
Вход в полноэкранный режим Выйти из полноэкранного режима

Как объяснялось ранее, вам необходимо проверить подпись, предоставленную с помощью redirect_url, чтобы убедиться, что она не была изменена каким-либо образом. Это можно сделать с помощью следующей функции, поэтому добавьте ее в свой файл:

async function verifySignature(originalUrl) {
  try {
    const url = new URL(originalUrl);
    const signature = Buffer.from(
      url.searchParams.get("authorization"),
      "base64"
    ).toString("utf-8");
    const date = Buffer.from(url.searchParams.get("date"), "base64").toString(
      "utf-8"
    );

    url.searchParams.delete("authorization");
    url.searchParams.delete("date");

    const originalRequest = {
      url: `${url.pathname}${url.search}`,
      method: "get",
      hostname: url.hostname,
      headers: {
        date,
        host: url.host,
        authorization: signature,
      },
    };

    const parsedOriginalRequest = httpSignature.parseRequest(originalRequest, {
      clockSkew: 300,
    });

    const jwk = await keyClient.getSigningKey(parsedOriginalRequest.keyId);
    const verified = httpSignature.verifySignature(
      parsedOriginalRequest,
      jwk.getPublicKey()
    );
    return verified;
  } catch (err) {
    console.error(err);
    return false;
  }
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Две новые функции patchPhoneCheck и verifySignature должны быть доступны в вашем файле index.js, поэтому в нижней части файла tru.js добавьте следующие экспорты для этих двух функций:

module.exports = {
  patchPhoneCheck,
  verifySignature
};
Войти в полноэкранный режим Выйти из полноэкранного режима

Вернитесь в свой файл index.js, вверху среди require, добавьте строку:

const tru = require("./tru");
Войти в полноэкранный режим Выйти из полноэкранного режима

Найдите конечную точку complete-check, и в нижней части этой функции добавьте следующее:

  try {
    const check = await tru.patchPhoneCheck(check_id, code);

    if (check.status === "COMPLETED" && check.match) {
      res.status(200).send('Verification complete, please close this tab and return to your SSH session.');

      return;
    } else {
      // verification failed = user not authenticated
      res.status(401).send("Verification failed, false match");

      return;
    }
  } catch (err) {
    console.log(err);

    if (err.status) {
      res.status(err.status || 500).send(err.message);
    } else {
      res.status(500).send("Unexpected Server error");
    }

    return;
  }
Войти в полноэкранный режим Выйти из полноэкранного режима

Приведенный выше код вызывает patchPhoneCheck, созданный в вашем файле tru.js, который делает PATCH запрос к API tru.ID с телом, содержащим code, необходимый для завершения процесса PhoneCheck.

Теперь в терминале, в директории webserver, выполните следующую команду и обратите внимание на вывод ngrok url:

npm start
Войти в полноэкранный режим Выйти из полноэкранного режима

В вашем файле ssh-auth найдите строку: # API Check URLs и ниже нее добавьте следующее, заменив <Your NGROK URL> на ngrok URL, который вы записали в предыдущем шаге:

BACKEND_SERVER="<Your NGROK URL>"
REDIRECT_URL="$BACKEND_SERVER/complete-check"
Войдите в полноэкранный режим Выйдите из полноэкранного режима

Чтобы конечным редиректом был указанный вами URL webhook, найдите строку ниже:

--data-raw "{"phone_number":"${phone_number}"}" --silent`
Войти в полноэкранный режим Выйти из полноэкранного режима

И замените ее на:

--data-raw "{"phone_number":"${phone_number}", "redirect_url":"${REDIRECT_URL}"}" --silent`
Enter fullscreen mode Выйти из полноэкранного режима

Настройка контейнера Docker

В этом руководстве используется контейнер Docker для целей разработки. Чтобы создать этот контейнер Docker и запустить его, вам потребуется:

  • Создать и запустить свой контейнер Docker. В ходе этого процесса внутренний порт 22 (ssh) будет сопоставлен с внешним портом 223.
  • Откройте контейнер Docker с помощью сессии bash.

Итак, в Терминале выполните следующие две команды:

docker-compose up --build -d
docker-compose exec ssh bash
Войти в полноэкранный режим Выйти из полноэкранного режима

Установка плагина SSH

Теперь вы добавили многофакторную аутентификацию в процесс аутентификации SSH. Когда ваш контейнер Docker создан и запущен, перейдите в каталог проекта в том же экземпляре терминала. По умолчанию это каталог /root/tru-id-ssh-auth/. Затем выполните команду ./ssh-auth install для установки вашей копии каталога проекта в /usr/local/bin/.

Примечание: Это определено в вашем Dockerfile в строке: ADD . /root/tru-id-ssh-auth.

cd /root/tru-id-ssh-auth/
./ssh-auth install
Вход в полноэкранный режим Выйти из полноэкранного режима

Команда ./ssh-auth install выполнит следующее:

  • Скопируйте каталог проекта из /root/tru-id-ssh-auth/ в /usr/local/bin/tru-id-ssh-auth/.
  • Создайте конфигурационный файл /usr/local/bin/tru-id-ssh-auth/tru-id-ssh-auth.conf.

Регистрация пользователя

Установив плагин, теперь необходимо включить проверку пользователя (пользователей). Это сохранит имя и номер телефона пользователя в недавно созданном вами конфигурационном файле. Затем приложение сравнит их с учетными данными, введенными при попытке пользователя войти в систему. В том же Терминале выполните следующую команду, заменив заполнители на ваши действительные данные:

Примечание: Пользователь и пароль Docker – test.

/usr/local/bin/tru-id-ssh-auth/ssh-auth register-user <username> <phone-number-inc-country-code>
# For example: /usr/local/bin/tru-id-ssh-auth/ssh-auth register-user test 447000000000
Войдите в полноэкранный режим Выход из полноэкранного режима

Попытка входа в систему

Теперь вы все настроили. Чтобы проверить, все ли работает, откройте новый сеанс терминала и выполните следующую команду для входа в SSH-сервер:

ssh test@127.0.0.1 -p 223
Войти в полноэкранный режим Выйти из полноэкранного режима

В примере конфигурации Docker используется имя пользователя test и пароль test.

Завершение

Вот и все! Теперь вы ввели шаг многофакторной аутентификации для процесса SSH аутентификации вашего сервера, используя API tru.ID PhoneCheck. Прелесть этого метода заключается в том, что он ограничивает ввод пользователя, требуя от него только ввести свои учетные данные SSH, а затем отсканировать QR-код, ему не нужно ждать, пока код придет, например, через SMS или другим способом. Процесс MFA выполняется в фоновом режиме после сканирования QR-кода на мобильном устройстве.

Ресурсы

  • Руководство по интеграции tru.ID PhoneCheck
  • Справочник по API PhoneCheck

Оцените статью
Procodings.ru
Добавить комментарий