Пишем панельный менеджер для сертификатов на linux shell

    Привет Хабр!

    Многие разработчики недооценивают shell скрипты, пытаясь сравнивать их с другими скриптовыми языками. Но ведь linux shell - в первую очередь оболочка операционной системы, сделанная системными инженерами для системных инженеров. И если для других языков нужно знать библиотеки, то для shell - огромное количество консольных утилит. А также понимание основных принципов работы архитектуры Линукс заметно повысит ваши навыки работы с shell скриптами.

    О сертификатах

    С момента популяризации https, работа с сертификатами теперь есть практически в каждом проекте.


    Сертификаты, которые любой может сгенерировать сам;

    Сертификаты которые выдает местный СА в компании, доверие к которому есть только внутри компании;

    Бесплатный публичный let's encrypt,

    Платные сертификаты от крупных центров авторизации (Digicert, iDenTrust и др), с дополнительными услугами, типа проверки компании или государственной аккредитацией

    Сертификаты для tls/ssl, сертификаты для авторизации, сертификаты для подписи - даже в пределах одного небольшого проекта с десятком микросервисов и несколькими тестовыми окружениями - сертификатов набирается уйма.

    При работе с множеством сертификатов, их удобно хранить не отдельными файлами, а складывать в хранилища (keystore). Самые популярные keystore форматы на сегодня - JKS (java keystore, который считается legacy с выходом 9-й джавы) и PKCS12. Последний вышел уже около 20 лет назад, и несмотря на некоторую критику, является одним из самых надежных, открытых и популярных форматов, который в скором будущем должен уже полностью вытеснить JKS.

    Я много работал в проектах, где основным языком разработки был java, поэтому при работе с keystore основной инструмент для меня это консольная утилита keytool, которая поставляется с JDK. И для большинства популярных действий у меня конечно есть микро-шпаргалка:

    Посмотреть список сертификатов:

    keytool -list -v -keystore "файл.jks" -storepass "пароль"|grep -P '(Alias name:|Entry type:|Serial number:|Owner:|Valid from:)'

    Скопировать сертификат из одного keystore в другой keystore:

    keytool -importkeystore -srckeystore "откуда.jks" -srcstorepass "пароль" -destkeystore "куда.jks" -deststorepass "пароль" -srcalias "имя сертификата" [ -destalias "новое имя" ]

    Импортировать сертификат в keystore из PEM файла:

    keytool -import -file "откуда.pem" -keystore "куда.jks" -storepass "пароль" -noprompt -alias "имя_сертификата"

    Экспортировать сертификат из keystore в файл:

    keytool -exportcert -v -alias "alias" -keystore "keystore.jks" -storepass "storepassword" -rfc -file "exportedcert.cer"

    Но однажды мне пришлось поработать в проекте, где сертификатов оказалось мягко говоря много. Плюс несколько сотен виртуалок и контейнеров, где все сертификаты обновлялись вручную. Перед тем? как подойти к вопросу автоматизации, было необходимо все это разобрать и причесать. Но долгое время руки просто не доходили - с одной стороны были вещи более приоритетные а сертификаты в общем напрягали нечасто. С другой стороны, опыта разгребания такого объема у меня раньше не было, и навскидку казалось, что в лоб задача решается слишком уныло и кропотливо, браться за нее по своей инициативе не хотелось - задача чисто техническая, никакие менеджеры и представители бизнеса такое перед нашей командой не ставили. Но где-то в подсознании желание все это перебрать и привести в порядок крутилось. Изредка я почитывал статьи, просматривал сертификат эксплореры, но все они были под графическую оболочку Windows, а политика безопасности не позволяла ставить в компании неодобренный софт на десктопы.

    Возможно, я бы собрался силой воли, помучился бы недельку, сделал бы все стандартными однострочниками+notepad+excel, но тут внезапно у меня случился больничный отпуск, а когда температура меня попустила, а на работу еще можно было не выходить, я случайно вспомнил, что считаю себя спецом по bash.

    И пусть я не хватаю лавры автора PIUPIU, но тем не менее встречайте:

    Консольный двух-панельный keystore менеджер на Linux shell

    Что умеет менеджер вкратце можно наглядно увидеть на скриншотах.

    В одно-панельном режиме:

    В двух-панельном режиме:

    Для поклонников панельных менеджеров (NC, VC, MC, FAR и др), функциональные клавиши и навигация должны быть интуитивно понятны.

    Весьма важным плюсом я считаю, что jks_mgr.sh - это просто шелл скрипт, одним файлом размером 20 килобайт (update: уже 30+ кб) (update2: уже 35+ кб).

    Никаких обсфукаций, код максимально простой - проверить на отсутствие закладок может в принципе любой джуниор - то есть такой скрипт можно использовать в любом проекте, не нарушая никаких требований по безопасности, требований по лицензионности, лоялен к производительности, установка не нужна - лишь бы на целевой машине был доступен шелл, keytool, sed и grep.

    Сперва я написал рабочий вариант на bash, но в какой-то момент подумал и переписал все башизмы в пользу POSIX совместимости. Код стал выглядеть более громоздко, зато скрипт проверенно работает в bash/ksh без проблем.
    Если я что-то пропустил - напишите в комментариях, ну или форкайте на здоровье.

    Некоторое сожаление: привычка работы с java keytool вынудила меня всю работу с хранилищами выполнять именно через keytool, который должен быть доступен в PATH (а может быть стоило разобраться и сделать все через openssl?).

    Что было самое интересное во время разработки

    Довольно много времени потратил на подстройка панелей под высоту/ширину экрана.

    Так как экран у меня перерисовывается почти с каждым нажатием, то менять ширину экрана (например если сидеть через графический ssh клиент) можно на ходу - после первого же нажатия любой клавиши экран перерисуется и все адаптируется. Пару вечеров ушло на опции отображения/скрытия столбцов - нужно было высчитать и отладить подстройку для одно-панельного и двух-панельного режима, плюс заголовки и текст считается разными формулами. Вот кусочек для определения насколько надо обрезать Alias, если экран слишком узкий и затем пример вывода заголовка:

    WindowWidth="$(tput cols)"
    if [ -n "$RFILE" ]; then # two-panel
        used=24
        [ -n "$SHOW_TYPE" ] && used=$(( $used+34 ))
        localWidth=$(( ( $WindowWidth - $used ) / 2 - 1 ))
        if [ $localWidth -ne $aliasWidth ]; then
            aliasWidth=$localWidth
            [ $aliasWidth -lt 1 ] && aliasWidth=1
            clear
        fi
    ..................................
    headerWidth=$(( $aliasWidth + 5 ))
    [ -n "$SHOW_TYPE" ] && headerWidth=$(( $headerWidth + 17 ))
    printf " store: ${blue}%-$(( $headerWidth ))s${rst}" "$LFILE"
    printf "| store: ${blue}%-$(( $headerWidth -1 ))s${rst}\n" "$RFILE"
    
    printf " %-10s" "Valid to"
    [ -n "$SHOW_TYPE" ] && printf " %-16s" "Storetype"
    printf " %-${aliasWidth}s |" "Alias"
    printf " %-10s" "Valid to"
    [ -n "$SHOW_TYPE" ] && printf " %-16s" "Storetype"
    printf " %-${aliasWidth}s\n" "Alias"

    Определение нажатия функциональных клавиш

    Специальные клавиши представляют собой строку произвольной длины, поэтому просто считать символ или два через read было некорректно. Но после некоторых эксприментов и гугления, я обнаружил что команда read знает что такое миллисекунда. Сейчас процедура выглядит вот так:

    # Special keypress could take variable amount of characters
    keypress=""
    read -rsN1 keytap
    while [ -n "$keytap" ]; do
        keypress="${keypress}${keytap}"
        read -sN1 -t 0.01 keytap
    done

    Теперь можно сравнивать $keypress с комбинациями:
     F1_KEY=$'\e[11~'
     F3_KEY=$'\e[13~'
     F10_KEY=$'\e[21~'
     UP_KEY=$'\e[A'
     DOWN_KEY=$'\e[B'
     TAB_KEY=$'\t'
     DEL_KEY=$'\e[3~'

    Я все еще не уверен, что мой вариант будет работать идеально везде - поэтому на всякий случай почти все хоткеи продублированы обычными буквами.

    В упрощенном виде я подобную навигацию уже копирую в другие свои велосипеды - в некоторых случаях это гораздо удобнее, чем стандартный select благодаря наглядности и возможности делать хоткеи.

    На текущий момент все самые часто используемые мной функции уже воплощены, и я взял передышку, да и собственно сам скрипт я начал писать чтобы получить еще немного практики с tput и навигацией, но немного увлекся.

    Я догадываюсь, что можно заметно ускорить навигацию - производительность в git-bash ужасна - мне быстрее скопировать keystore на ближайшую виртуалку, поковырять там менеджером и скопировать исправленный keystore назад. Но нужно будет переписывать много вычислений, делать независимую пагинацию для каждой панели, а на Linux все работает отлично, поэтому в текущем варианте скрипт меня устраивает и дойдут ли руки до переделки - не уверен.

    В конце нужно написать какой-то умный итог и заключение… ну он простой:

    Хочешь получить от практики удовольствие - придумай задачу, результат которой будет полезен для тебя.

    Хочешь получить от практики двойное удовольствие - придумай задачу, результат которой будет полезен и для тебя и для кого-нибудь еще.

    Сейчас моим скриптом пользуется небольшой круг людей в компании, и то, что я, не разработчик, смог написать нечто полезное и востребованное, меня очень радует.

    P.S. На фоне некоторых проблем с nginx, Wargaming и др., сразу хочу уточнить, что jks_mgr.sh был написан ИСКЛЮЧИТЕЛЬНО в нерабочее время на личном компьютере.

    P.P.S. Совсем забыл: https://github.com/sfkulyk/jks-manager

    Комментарии 5

      0
      Добавил пропущенную ссылку на гитхаб
        0
        Спасибо за проделанную работу, но насколько я понимаю, есть универсальный вариант с графическим интерфейсом под все платформы, который умеет все тоже самое.
        keystore-explorer.org/features.html
          0
          Ну какой же он универсальный, если требует графический интерфейс, который на серверном Linux практически никогда не установлен, да и смысла ставить нет, ибо куда его отображать?
          Я понимаю, что у меня поделка довольно велосипедная, но ее смысл — небольшая, простая, и вообще никаких требований кроме стандартных утилит
            0
            Спасибо за ссылку, почитал.

            Странно, что ему нужна «A Java Runtime Environment (JRE) Version 8 or above is required to run KSE.»

            А что самое важное, в нем нет возможности сравнить два кейстора и быстро копировать сертификаты между кейсторами — собственно та задача, ради которой я вообще взялся за дело.

            0
            Немного подправил работу с функциональными клавишами, возможно она теперь настолько универсальна, насколько может быть. Также добавил немного функционала (отображение Server Auth/Client Auth) и исправил ошибки.

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое