Pull to refresh

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

Reading time 6 min
Views 4.7K

Привет Хабр!

Многие разработчики недооценивают 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"

Но однажды мне пришлось поработать в проекте, где сертификатов оказалось мягко говоря много. Плюс несколько сотен виртуалок и контейнеров, где все сертификаты обновлялись вручную. Перед тем как подойти к вопросу автоматизации, было необходимо все это разобрать и причесать. Но долгое время руки просто не доходили - с одной стороны были вещи более приоритетные а сертификаты в общем напрягали нечасто. С другой стороны, опыта разгребания такого объема у меня раньше не было, и навскидку казалось, что в лоб задача решается слишком уныло и кропотливо, браться за нее по своей инициативе не хотелось - задача чисто техническая, никакие менеджеры и представители бизнеса такое перед нашей командой не ставили. Но где-то в подсознании желание все это перебрать и привести в порядок крутилось. Изредка я почитывал статьи, просматривал certificate explorer и его аналоги, но все они были под графическую оболочку 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

Tags:
Hubs:
+14
Comments 5
Comments Comments 5

Articles