Русский
Русский
English
Статистика
Реклама

Ssh

Перевод Прописываем процедуру экстренного доступа к хостам SSH с аппаратными ключами

11.07.2020 14:21:13 | Автор: admin


В этом посте мы разработаем процедуру для экстренного доступа к хостам SSH, используя аппаратные ключи безопасности в автономном режиме. Это всего лишь один из подходов, и вы можете адаптировать его под себя. Мы будем хранить центр сертификации SSH для наших хостов на аппаратном ключе безопасности. Эта схема будет работать практически на любом OpenSSH, включая SSH с единым входом.

Зачем всё это? Ну, это вариант на крайний случай. Это бэкдор, который позволит вам получить доступ к своему серверу в том случае, когда по какой-то причине больше ничего не помогает.


Зачем использовать сертификаты вместо открытых / закрытых ключей для экстренного доступа?

  • В отличие от открытых ключей, срок действия сертификатов может быть очень коротким. Вы можете сгенерировать сертификат, действительный в течение 1 минуты или даже 5 секунд. По истечении этого срока сертификат станет непригодным для новых подключений. Это идеально подходит для экстренного доступа.
  • Вы можете создать сертификат для любой учётной записи на своих хостах и при необходимости отправлять такие вот одноразовые сертификаты коллегам.

Что вам понадобится


  • Аппаратные ключи безопасности, которые поддерживают резидентные ключи.
    Резидентные ключи это криптографические ключи, которые полностью хранятся внутри ключа безопасности. Иногда они защищены буквенно-цифровым PIN-кодом. Открытая часть резидентного ключа может быть экспортирована из ключа безопасности, при необходимости вместе с дескриптором закрытого ключа. Поддержку резидентных ключей, имеют, например, USB-ключи серии Yubikey 5. Желательно, чтобы они предназначались только для экстренного доступа к хосту. Для этого поста я буду использовать только один ключ, но у вас должен быть дополнительный для резервного копирования.
  • Безопасное место для хранения этих ключей.
  • OpenSSH версии 8.2 или выше на вашем локальном компьютере и на серверах, к которым вы хотите получить экстренный доступ. Ubuntu 20.04 поставляется с OpenSSH 8.2.
  • (необязательно, но желательно) Средство CLI для проверки сертификатов.

Подготовка


Для начала нужно создать центр сертификации, который будет находиться на аппаратном ключе безопасности. Вставьте ключ и запустите:

$ ssh-keygen -t ecdsa-sk -f sk-user-ca -O resident -C [security key ID]

В качестве комментария (-C) я указал yubikey-9-512-742@smallstep.com, чтобы не забыть, к какому ключу безопасности относится этот центр сертификации.

Кроме добавления ключа к Yubikey, локально будет сгенерировано два файла:

  1. sk-user-ca, дескриптор ключа, который ссылается на закрытый ключ, хранящийся в ключе безопасности,
  2. sk-user-ca.pub, который будет открытым ключом для вашего центра сертификации.


Но не волнуйтесь, на Yubikey хранится ещё один закрытый ключ, который невозможно извлечь. Поэтому тут всё надёжно.

На хостах от имени пользователя root добавьте (если ещё не добавили) в конфигурацию вашего SSHD (/etc/ssh/sshd_config) следующее:

TrustedUserCAKeys /etc/ssh/ca.pub

Затем на хосте добавьте открытый ключ (sk-user-ca.pub) в /etc/ssh/ca.pub
Перезагрузите сервер:

systemctl sshd restart

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

$ ssh-keygen -t ecdsa -f emergency

Сертификаты и SSH-пары
Иногда так и тянет использовать сертификат как замену пары открытый / закрытый ключ. Но для аутентификации пользователя одного сертификата недостаточно. Каждый сертификат также имеет закрытый ключ, связанный с ним. Вот почему нам нужно сгенерировать эту пару экстренных ключей, прежде чем мы выдадим себе сертификат. Важно то, что подписанный сертификат мы показываем серверу, с указанием пары ключей, для которых у нас есть закрытый ключ.

Таким образом, обмен открытыми ключами всё ещё жив-здоров. Это прокатывает даже с сертификатами. Сертификаты просто избавляют сервер от необходимости хранить открытые ключи.


Далее создайте сам сертификат. Мне нужна авторизация пользователя ubuntu в 10-минутном интервале. Вы можете сделать по-своему.

$ ssh-keygen -s sk-user-ca -I test-key -n ubuntu -V -5m:+5m emergency

Вам будет предложено подписать сертификат с помощью отпечатка пальцев. Вы можете добавить дополнительные имена пользователей, разделенные запятыми, например, -n ubuntu,carl,ec2-user

Всё, теперь вас есть сертификат! Далее нужно указать правильные разрешения:

$ chmod 600 emergency-cert.pub

После этого вы можете ознакомится с содержимым вашего сертификата:

$ step ssh inspect emergency-cert.pub


Вот как выглядит мой:

emergency-cert.pub        Type: ecdsa-sha2-nistp256-cert-v01@openssh.com user certificate        Public key: ECDSA-CERT SHA256:EJSfzfQv1UK44/LOKhBbuh5oRMqxXGBSr+UAzA7cork        Signing CA: SK-ECDSA SHA256:kLJ7xfTTPQN0G/IF2cq5TB3EitaV4k3XczcBZcLPQ0E        Key ID: "test-key"        Serial: 0        Valid: from 2020-06-24T16:53:03 to 2020-06-24T17:03:03        Principals:                ubuntu        Critical Options: (none)        Extensions:                permit-X11-forwarding                permit-agent-forwarding                permit-port-forwarding                permit-pty                permit-user-rc

Здесь открытый ключ это созданный нами ключ emergency, а с центром сертификации связан sk-user-ca.

Наконец-то мы готовы запустить команду SSH:

$ ssh -i emergency ubuntu@my-hostnameubuntu@my-hostname:~$

  1. Теперь вы можете создавать сертификаты для любого пользователя на хосте, который доверяет вашему центру сертификации.
  2. Вы можете удалить emergency. Вы можете сохранить sk-user-ca, но вам это не нужно, поскольку он также находится на ключе безопасности. Возможно, вы также захотите удалить исходный открытый ключ PEM со своих хостов (например, в ~/.ssh/authorized_keys для пользователя ubuntu), если вы использовали его для экстренного доступа.


Экстренный доступ: план действий


Вставьте ключ безопасности и запустите команду:

$ ssh-add -K

Таким образом вы добавите открытый ключ центра сертификации и дескриптор ключа в SSH-агент.
Теперь экспортируйте открытый ключ, чтобы сделать сертификат:

$ ssh-add -L | tail -1 > sk-user-ca.pub

Создайте сертификат со сроком годности, например, не более часа:

$ ssh-keygen -t ecdsa -f emergency$ ssh-keygen -Us sk-user-ca.pub -I test-key -n [username] -V -5m:+60m emergency$ chmod 600 emergency-cert.pub

И теперь вновь SSH:

$ ssh -i emergency username@host

Если ваш файл .ssh/config вызывает какие-то проблемы при подключении, вы можете запустить ssh с параметром -F none, чтобы обойтись без него. Если вам нужно отправить сертификат коллеге, самый простой и безопасный вариант это Magic Wormhole. Для этого понадобится всего два файла в нашем случае это emergency и emergency-cert.pub.

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



На правах рекламы


Эпичные серверы это дешёвые VPS с мощными процессорами от AMD, частота ядра CPU до 3.4 GHz. Максимальная конфигурация позволяет решить практически любые задачи 128 ядер CPU, 512 ГБ RAM, 4000 ГБ NVMe. Присоединяйтесь!

Подробнее..

Перевод FritzFrog новое поколение ботнетов

24.08.2020 18:21:28 | Автор: admin

Краткое содержание


  • Guardicore обнаружили сложный ботнет пиринговой (P2P) сети FritzFrog, который еще с января 2020 года активно взламывал SSH серверы.
  • Вредоносное ПО на Golang: FritzFrog исполняет модульный, мультипоточный и безфайловый вредоносный код на Golang, который не оставляет следов на жестком диске зараженного устройства.
  • Активное таргетирование государственных, образовательных, финансовых и прочих ресурсов: FritzFrog пытался брутфорсить и распространяться на десятках миллионов IP адресов правительственных офисов, образовательных учреждений, медицинских центров, банков и множества телекоммуникационных компаний. Среди них успешно подвержены атаке оказались более чем 500 серверов, включая известные университеты США и Европы, и одну железнодорожную компанию.
  • Сложность: FritzFrog полностью проприетарен, его имплементация P2P написана с нуля, что говорит о высоком уровне профессионализма его создателей в области разработки ПО.
  • Перехват: Guardcore Labs разработали клиентскую программу на Golang, способную перехватывать P2P соединения FritzFrog и подключаться к сети как пир.
  • Принадлежность: мы не смогли определить конкретную группу, ответственную за создание FritzFrog, однако текущий ботнет частично похож на ранее известный ботнет Rakos.

Введение


FritzFrog это очень изощренный пиринговый ботнет, который активно взламывает SSH серверы по всему миру. Благодаря своей децентрализованной структуре он распределяет контроль по всем своим узлам. В этой сети нет единой точки отказа, и пиры постоянно общаются друг с другом, чтобы поддерживать ее в устойчивом, обновляемом и постоянно активном состоянии. P2P соединение проводится через зашифрованный канал с использованием AES для симметричного шифрования и протокола Диффи-Хеллмана для обмена ключами.

В отличие от других P2P ботнетов, FritzFrog уникален набором своих свойств: он безфайловый, поскольку собирает и исполняет пакеты прямо в памяти; несмотря на эффективное равномерное распределение целей в своей сети, он очень агрессивно их брутфорсит; его проприетарные P2P протоколы не основаны ни на одной из ныне известных реализаций.

Написанный на Golang вредоносный код очень переменчив и не оставляет следов на жестком диске. Он создает бэкдор в виде публичного SSH ключа и тем самым открывает злоумышленникам постоянный доступ к устройству жертвы. С самого начала его активности мы выявили 20 различных версий исполняемого вредоносного ПО.

В этом посте мы расскажем, каким образом была раскрыта деятельность FritzFrog, а так же каковы природа его P2P сети и внутреннее устройство вредоносного кода включая процесс заражения, шифрование команд и изменчивое поведение.

Guardicore Labs предоставили доступ к Github репозиторию со скриптом для обнаружения этого вредоносного ПО и списком индикаторов компрометации (IoC) его деятельности.


Географическое распределение зараженных узлов. Наиболее подверженными атакам странами оказались США, Китай и Южная Корея.

Исследование FritzFrog


Впервые Guardcore Labs обратили внимание на деятельность FritzFrog в ходе исследования Botnet Encyclopedia. 9 января были обнаружены новые атаки с исполнением вредоносных процессов ifconfig и nginx. Мы начали отслеживать стабильный и значимый рост вредоносной деятельности, которая вскоре достигла 13 тысяч атак на Guardcore Global Sensors Network (GGSN). За все время мы отследили 20 различных версий бинарников FritzFrog.


График демонстрирует количество атак FritzFrog на GGSN.

Удивительным оказалось то, что вредоносный код на первый взгляд не связывался с каким-либо сервером командования и контроля (CNC). Только когда мы начали серьезно исследовать ботнет, мы поняли что никакого сервера не было и в помине.

Для перехвата сети ботнета Guardcore Labs разработали на Golang клиент, способный обмениваться ключами с вредоносным ПО, а так же отправлять команды и получать ответы. Эта программа, которую мы затем назвали фроггер, позволила нам исследовать природу и задачи сети, и благодаря фроггеру мы добавили в сеть наши собственные узлы, сумев подсоединиться к ботнету, и поучаствовали в передаче данных активного P2P трафика.

FritzFrog брутфорсил миллионы IP адресов, среди которых оказались правительственные офисы, образовательные учреждения, медицинские центры, банки и множество телекоммуникационных компаний. Из них успешно подвержены атаке оказались более чем 500 серверов, включая известные университеты США и Европы, и одну железнодорожную компанию.

Новое поколение P2P


Почему Новое поколение?


У FritzFrog есть уникальный набор свойств, который сильно выделяет его на фоне прочих сетевых угроз:

  • Безфайловость: FritzFrog работает без рабочей директории, а обмен файлами происходит прямо в памяти через массивы двоичных данных (BLOB).
  • Постоянные обновления: базы данных целей и пораженных устройств обновляются плавно и органично.
  • Агрессивность: брутфорс ведется с использованием обширного словаря. Для примера, недавно обнаруженный P2P ботнет DDG в поле логина использовал только root.
  • Эффективность: Цели равномерно распределены между узлами.
  • Проприетарность: P2P протокол ботнета полностью проприетарен и не основывается на каком-либо из известных P2P протоколов, например TP.

Как только жертва оказывается успешно взломана, на ней запускается UPX-запакованный вредоносный код, который затем тут же сам себя удаляет. Для минимизации подозрений вредоносные процессы исполняются под наименованиями ifconfig и nginx. В самом начале своей работы вредоносный код прослушивает порт 1234 в ожидании команд. Первые полученные команды синхронизируют жертву с базой данных пиров сети и целей брутфорса.


Кластер узлов сети FritzFrog. Каждый узел это зараженный SSH сервер. Размер узлов демонстрирует их связность с остальной сетью.

Трафик на нестандартном порте, например 1234, легко заметить и заблокировать файерволом либо любой другой системой защиты. Поэтому разработчики FritzFrog подошли к проблеме творчески и вместо прямой передачи команд через порт 1234 злоумышленник подключается к жертве через SSH и запускает на устройстве netcat клиент, который в свою очередь соединяется с сервером ботнета. Таким образом, любая команда будет передана через SSH как ввод netcat и без труда достигнет вредоносного кода.


FritzFrog туннелирует свои P2P команды через классический SSH порт, для чего пользуется локальным netcat клиентом зараженного устройства.

Злоумышленники FritzFrog внедрили зашифрованный командный канал с более чем 30 различными командами. Параметры команд и отклики передаются в указанных структурах данных и выпускаются (мобилизуются) в формате JSON. Перед отправлением данные зашифровываются симметричным шифрованием AES и кодируются в Base64. Для обмена ключами участвующие в передаче данных узлы используют протокол Диффи-Хеллмана.



Узлы в сети FritzFrog поддерживают тесный контакт, постоянно пингуя друг друга для проверки соединения, обмена пирами и целями и взаимной синхронизации. Узлы также участвуют в искусном избирательном процессе, который влияет на распределение целей брутфорса в сети. Наблюдения Guardcore Labs подтверждают, что цели в сети распределены равномерно и никакие два узла не будут пытаться взломать одну и ту же цель.

Погружение во вредоносный код


Бинарник FritzFrog это продвинутый вредоносный код на Golang. Он полностью работает в памяти, каждый узел с вредоносным кодом хранит в памяти всю базу данных целей и пиров. Вредоносный код создает несколько потоков для одновременной обработки различных задач в соответствии с приведенной ниже таблицей.

FritzFrog определяет состояния управления жертвой и целевым устройством следующим образом:
  1. Target (цель): устройство из запроса цели будет затем передано модулю Cracker, который в свою очередь постарается просканировать и взломать его.
  2. Deploy (развертывание): успешно взломанное устройство встает в очередь на заражение вредоносным кодом через модуль DeployMgmt.
  3. Owned (владение): успешно зараженное устройство будет добавлено в P2P сеть модулем Owned.




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


Рабочая функция в дизассемблере. Каждая ветка соответствует поддерживаемому P2P функционалу.

Вредоносный код несет временный характер: пусть он и пытается пережить системные перезагрузки, для будущего доступа к взломанной цели сохраняется бэкдор, чьи логин и пароль хранятся у пиров сети. Вредоносный код добавляет публичный SSH-RSA ключ в файл authorized_keys. Столь простой бэкдор позволяет злоумышленнику с секретным частым ключом аутентифицироваться без пароля, на случай если изначальный пароль оказался изменен. Единственный используемый FritzFrog публичный ключ приведен ниже.

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDJYZIsncBTFc+iCRHXkeGfFA67j+kUVf7h/IL+sh0RXJn7yDN0vEXz7ig73hC//2/71sND+x+Wu0zytQhZxrCPzimSyC8FJCRtcqDATSjvWsIoI4j/AJyKk5k3fCzjPex3moc48TEYiSbAgXYVQ62uNhx7ylug50nTcUH1BNKDiknXjnZfueiqAO1vcgNLH4qfqIj7WWXu8YgFJ9qwYmwbMm+S7jYYgCtD107bpSR7/WoXSr1/SJLGX6Hg1sTet2USiNevGbfqNzciNxOp08hHQIYp2W9sMuo02pXj9nEoiximR4gSKrNoVesqNZMcVA0Kku01uOuOBAOReN7KJQBt

Вредоносный файл прогоняет всевозможные команды оболочки на локальном устройстве, некоторые по несколько раз, для отслеживания состояния системы. Например, он прогоняет free m для проверки доступной оперативной памяти, uptime, journalctl s @0 u sshd для отслеживания SSH логинов, и прочие команды для вывода статистики нагрузки процессора. Эта статистика оказывается доступна другим узлам в сети, и используется для принятия различных решений, например запускать ли криптомайнер на устройстве или нет. Если решение принято, вредоносный код запускает отдельный процесс, libexec, для майнинга Monero. Этот майнер основан на популярном майнере XMRig и связывается с публичным пулом web.xmrpool.eu через порт 5555.

Злобная торрентоподобная сеть


FritzFrog полагается на способность делиться файлами по всей сети, как для заражения новых устройств, так и для запуска вредоносных элементов, например криптомайнера Monero.

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

Когда узел А хочет получить файл от своего пира, узла B, он моет выслать узлу В запрос getblobstats чтобы узнать какими массивами он владеет. Затем узел А может получить конкретный массив через его хэш, как с помощью P2P команды getbin, так и с помощью HTTP по адресу http://1234/. Как только узел А получает все массивы, он собирает файл через модуль Assemble и запускает его.


Результат команды getblolbstats. Каждый узел в сети сообщает, каким он обладает массивом в соответствии с поддерживаемым списком файлов.

Присвоение


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

Даже при сравнении с другими P2P ботнетами FritzFrog остается уникальным: он не использует IRC как это делает IRCflu, в отличие от DDG он работает прямо в памяти, и он запускается на Unix-устройствах в противовес ботнету InterPlanetary Storm. Если он на кого и похож, особенно в плане наименования функций и нумерации версий, так это на Rakos, P2P ботнет на Golang, проанализированный ESET еще в 2016 году.

Отслеживание действий И смягчение последствий


Guardcore Labs предоставили скрипт по отслеживанию FritzFrog для запуска на SSH серверах. Он ищет следующие индикаторы ботнета:
  • Запуск процессов nginx, ifconfig или libexec, чей исполняемый файл более в системе не существует (как можно видеть ниже).
  • Прослушивание порта 1234.

В дополнение к этому, TCP трафик через порт 5555 может указывать на сетевой трафик к пулу Monero.

ubuntu@ip-111-11-11-11:~$ ./detect_fritzfrog.sh
FritzFrog Detection Script by Guardicore Labs
=============================================

[*] Fileless process nginx is running on the server.
[*] Listening on port 1234
[*] There is evidence of FritzFrog's malicious activity on this machine.

FritzFrog эксплуатирует особенность большинства систем сетевой защиты по принуждению трафика только через порт и протокол. Исключить подобные угрозы можно основанными на процессах правилами сегментации.

Слабые пароли оказываются ключевой уязвимостью для атак FritzFrog. Мы рекомендуем использовать сильные пароли и публичные ключи авторизации, что намного безопаснее. Кроме того, критически важно исключить публичный ключ FritzFrog из файла authorization_keys чтобы не дать злоумышленникам доступ к устройству. Роутеры и IoT устройства обычно раскрывают свой SSH и потому становятся уязвимы для атак FritzFrog; мы рекомендуем сменить таким устройствам SSH порт или, если функционал не используется, полностью отключить SSH.
Подробнее..

Перевод Примеры грамотного применения SSH-шаблонов

17.02.2021 20:19:33 | Автор: admin


SSH-сертификаты очень мощный инструмент. Первоначально в удостоверяющем центре step-ca мы реализовали только минимальный набор функций для аутентификации по сертификатам пользователя и хоста. Затем добавили шаблоны сертификатов X.509, а ещё в августе прошлого года и SSH-шаблоны, в версии 0.15.2. Наконец, мы задокументировали эту функцию и готовы о ней рассказать.

Шаблоны для сертификатов SSH действуют аналогично шаблонам X.509: это JSON-файлы, написанные в Go text/template. Они применяются для настройки SSH-сертификатов, которые выдаёт step-ca. Давайте посмотрим, что представляют собой эти шаблоны и как их можно использовать.

По умолчанию шаблон пользовательского SSH-сертификата выглядит так:

{"type": {{ toJson .Type }},"keyId": {{ toJson .KeyID }},"principals": {{ toJson .Principals }},"extensions": {{ toJson .Extensions }},"criticalOptions": {{ toJson .CriticalOptions }}}

А вот SSH-сертификат, выданный по этому шаблону:

$ step ssh inspect id_ct-cert.pubid_ct-cert.pub:        Type: ecdsa-sha2-nistp256-cert-v01@openssh.com user certificate        Public key: ECDSA-CERT SHA256:iczSh1XiBBE36yfJcDidgp6fqY3qWx1RtEwFfAN9jDs        Signing CA: ECDSA SHA256:MKwRQ/SDKk/pCJbbCk5bfhZACjSjv7uZXLyc5n4Wx6k        Key ID: "carl@smallstep.com"        Serial: 2831574724231262409        Valid: from 2020-11-17T16:48:11 to 2020-11-18T08:49:11        Principals:                carl                carl@smallstep.com        Critical Options: (none)        Extensions:                permit-X11-forwarding                permit-agent-forwarding                permit-port-forwarding                permit-pty                permit-user-rc

Он позволяет юзеру carl (или carl@smallstep.com) пройти аутентификацию на любом SSH-хосте, который доверяет моему SSH CA. Сертификат включает в себя некоторые основные расширения:

  • permit-x11-forwarding: Разрешает переадресацию X11 (с помощью ssh -X) для запуска удалённых программ X11 на локальном дисплее.
  • permit-agent-forwarding: Разрешает переадресацию агента (с помощью ssh -A) для пересылки ключей из локального агента SSH на удалённый хост (подробнее про агента SSH см. здесь).
  • permit-port-forwarding: Разрешает переадресацию портов (туннель) с локального на удалённый порт (ssh -L) или с удалённого на локальный (ssh -R).
  • permit-pty: Очень важное расширение. Если хотите открыть интерактивную сессию в консоли, хост должен выделить вам pty (псевдо-tty). В противном случае не предусмотрено никакой интерактивности. Например, для проверки SSH-аутентификации на GitHub можно запустить ssh -T git@github.com (-T отключает запрос на pty).
  • permit-user-rc: Запуск личного RC-файла после подключения (находится в ~/.ssh/rc на удалённом хосте).

Сертификаты пользователя и хоста поддерживают расширения и критические параметры, но OpenSSH не определяет никаких встроенных расширений или критических параметров для сертификатов хоста. Таким образом, всё самое интересное происходит именно с сертификатами пользователя, поэтому в данной статье рассмотрим только их.

Примеры шаблонов сертификатов


Внесём несколько изменений в дефолтный шаблон.

Запрещаем переадресацию агента и портов

Если пользователи подключаются к внутренним хостам через хост-бастион, то хорошо бы запретить перенаправление портов по соображениям безопасности. Вы же не хотите, чтобы юзеры перенаправили трафик с продакшн-сервера MySQL на свой localhost. Так же и переадресация агента сопряжена с риском для безопасности. Вот шаблон, который просто удаляет эти два расширения из сертификатов SSH:

{"type": {{ toJson .Type }},"keyId": {{ toJson .KeyID }},"principals": {{ toJson .Principals }},"extensions": {           "permit-x11-forwarding": "",           "permit-pty": "",           "permit-user-rc": ""  },"criticalOptions": {{ toJson .CriticalOptions }}}

Встраиваем директиву force-command

ForceCommand это серверная директива конфигурации SSHD, которая вместо интерактивного терминала запускает на хосте альтернативную команду. Но с тем же эффектом можно встроить force-command прямо в сертификат в раздел Critical Options:. Может пригодиться для служебных аккаунтов, которым необходимо выполнить только одну команду, например, запустить задание в удалённой системе.

Ограничиваем соединения по адресам

Чтобы ограничить область использования сертификата, в него можно встроить список разрешённых IP-адресов (блоков CIDR).

Вот шаблон сертификата, который использует и source-address, и force-command.

{"type": {{ toJson .Type }},"keyId": {{ toJson .KeyID }},"principals": {{ toJson .Principals }},"extensions": {{ toJson .Extensions }},"criticalOptions": {"force-command": "echo \"Hello World\"","source-address": "10.20.30.0/24,1.1.1.1/32"}}

Это нормальный пример, но здесь список IP жёстко зафиксирован и не изменяется. А нам обычно хочется использовать разные списки адресов для разных пользователей. Давайте попробуем

Вставляем разные значения для разных юзеров

Очевидно, пользователям нельзя давать право редактировать диапазон адресов. Следовательно, динамические значения должны поступать из надёжного источника.

Для этого в step-ca можно через поставщика OpenID Connect (OIDC) настроить провайдера OAuth на добавление к токену нестандартных требований (custom claim), содержащих блоки адресов CIRD, которые мы хотим добавить в сертификат этого пользователя.

Поставщик OIDC представляет собой идеальный способ выдачи SSH-сертификатов в step-ca. В статье DIY Single Sign-On for SSH я рассказывал, как настроить SSH CA, чтобы он выдавал краткосрочные SSH-сертификаты по ID-токенам от доверенного провайдера OAuth. Если step-ca настроен как доверенный клиент OAuth, он будет считывать поле email из токена ID и извлекать оттуда список участников SSH-сертификата (например, по полю carl@smallstep.com сгенерируются сертификаты для carl и carl@smallstep.com).

Но OIDC позволяет через шаблоны считать из токенов ID и другие поля. Вот где начинается настоящая магия. Таким образом, добавляем в каталог пользователей на стороне провайдера идентификации отдельное поле source_address и отражаем его в нашем ID-токене. Затем через шаблон SSH можно ввести значение из токена в сертификат. Вот шаблон:

{"type": {{ toJson .Type }},"keyId": {{ toJson .KeyID }},"principals": {{ toJson .Principals }},"extensions": {{ toJson .Extensions }},{{ if .Token.source_address }}"criticalOptions": {"source-address": "{{ .Token.source_address }}"}{{ else }}"criticalOptions": {{ toJson .CriticalOptions }}{{ end }}}

Аутентификация на GitHub по сертификату

Рассмотрим ещё один пример custom claim. С помощью GitHub Enterprise Cloud или GitHub Enterprise Server можно настроить GitHub на использование SSH-сертификатов. В частности, GitHub будет доверять вашему центру сертификации SSH. Но чтобы всё заработало, нужно создать для каждого пользователя отдельный SSH-сертификат с расширением login@github.com, в котором прописывается имя пользователя на GitHub. С помощью этого расширения сертификат аутентифицирует пользователя на GitHub Enterprise. И это здорово: один и тот же сертификат позволяет и подключиться к вашему серверу по SSH, и запушить код на GitHub.

Вот шаблон сертификата с поддержкой индивидуального расширения GitHub:

{"type": {{ toJson .Type }},"keyId": {{ toJson .KeyID }},"principals": {{ toJson .Principals }},"criticalOptions": {{ toJson .CriticalOptions }},{{ if .Token.ghu }}"extensions": {  "login@github.com": {{ toJson .Token.ghu }}}{{ else }}"extensions": {{ toJson .Extensions }}{{ end }}}

Для использования шаблона нужно добавить к токенам идентификации OIDC индивидуальное требование ghu (GitHub Username). Давайте подробно рассмотрим, как создать этот custom claim с помощью вашего провайдера OAuth.

Регистрация заявления у провайдера идентификации

Не все провайдеры идентификации поддерживают индивидуальные требования, но если поддержка всё-таки есть, то процесс довольно похож. Вот как это делается с помощью Okta:

  1. Добавьте приложение OAuth в Okta и установите доверие к нему у поставщика OIDC в step-ca, как описано в статье DIY SSO for SSH.


  2. Добавьте новое поле в каталог пользователей Okta (например, GitHub Username)
  3. Добавьте к OIDC-токену индивидуальное требование, например, с сокращённым названием ghu
  4. Теперь заполняем поле для тестового юзера и проверяем требование. В Okta есть инструмент тестирования токена ID. Или можно использовать step для проверки всего потока OAuth:

    OIDC_ENDPOINT="http://personeltest.ru/aways/[your organization].okta.com/oauth2/default/.well-known/openid-configuration"CLIENT_ID="[your OAuth client ID]"CLIENT_SECRET="[your OAuth client secret]"step oauth --oidc --provider $OIDC_ENDPOINT \    --client-id $CLIENT_ID --client-secret $CLIENT_SECRET \    --listen=":10000" --bare |step crypto jwt inspect --insecure
    

  5. Наконец, настройте step-ca для использования этого шаблона. Конфигурация поставщика должна ссылаться на файл шаблона:

    {  "provisioners": [    {      "type": "OIDC",      "name": "Okta",      "clientID": "[your OAuth client ID]",      "clientSecret": "[your OAuth client secret]",      "configurationEndpoint": "https://[your organization].okta.com/oauth2/default/.well-known/openid-configuration",      "listenAddress": ":10000",      "options": {        "ssh": {            "templateFile": "templates/certs/ssh/github.tpl"        }      }    },      ...  ]}
    

Что дальше


Мы добавили в документацию раздел о шаблонах SSH, где более подробно рассматриваются все параметры и переменные.

Если есть вопросы не стесняйтесь задавать.
Подробнее..

Перевод Как я сломал и починил кластер Kubernetes, работающий на Raspberry Pi

21.04.2021 20:21:36 | Автор: admin

Я доигрался с обновлениями и это привело к катастрофе: все ноды внезапно перестали видеть сетевые интерфейсы, и, сколько я ни бился, оживить кластер у меня не получилось.

Мой домашний кластерёнок вырос в зрелый кластер из шести нод (всё благодаря супруге, которая знала, что мне подарить на день рождения, естественно, Raspberry Pi, и не один!), и я встал перед выбором либо ещё раз выполнить собственные инструкции из статьи об установке кластера Kubernetes на Raspberry Pi, либо, применив системную инженерию (DevOps и SRE), полностью автоматизировать процессы переделки кластера и создания системы управления кластером. Эту статью можно считать прямым дополнением к моей первой статье Как я собрал домашний кластер Kubernetes на базе Raspberry Pi.


Соображения о времени и трудозатратах

На тот момент у меня было два варианта, я обдумывал их на ночь глядя. Оба получались затратными по времени.

Вариант 1

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

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

Затем всё чистится под ноль и начинается снова. Я проделывал это уже не единожды. Но, в конце концов, никому не будет вреда, если попытаться ещё раз.

Вариант 2

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

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

Хроника автоматизации. Начало

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

Принцип 1. Настройка кластера Kubernetes на Raspberry Pi выполняется в три этапа: это настройка карты памяти, настройка нод на системном уровне и развёртывание ресурсов Kubernetes.

Принцип 2. На моём старом Intel NUC работает NFS-сервер, подключённый к хранилищу DROBO. Было бы заманчиво использовать его в качестве постоянного общего хранилища для всех нод.

Принцип 3. Кластер Raspberry Pi работает в моей домашней сети VLAN, поэтому вопросы безопасности меня волнуют не особо. Все службы и ноды должны быть легко доступны без всяких хитростей с именами и паролями.

Итак, держа всё это в голове, я приступил к программированию моего маленького Франкенштейна. Для повторения результатов (то есть для того, чтобы система заработала) вам понадобятся:

  • Mac (для форматирования карты). Если выберу время для установки Linux VM, попытаюсь обновить скрипт обнаружения платформы.

  • Ansible (я использовал версию 2.10.6).

  • Terraform (я использовал версию 0.13.4, но 0.14.8 тоже подойдёт).

  • Make, полезный инструмент, чтобы не колдовать над параметрами.

Кластер rPi. Первый шаг

В качестве жёсткого диска Raspberry Pi использует карту памяти. Возможно, это не вполне оптимальное и не самое быстрое решение для чтения и записи, но его должно быть достаточно для игр и любительских проектов.

Что происходит на первом шаге?

  • Форматируется карта памяти.

  • Карта памяти делится на два раздела: 1 ГБ плюс то, что останется.

  • Образ Alpine Linux копируется на карту памяти.

  • Создаётся системный оверлей.

Системный оверлей настраивает eth0 на "неразборчивый" (promisc) режим, это нужно для работы MetalLB, и разрешает подключение SSH к нодам Raspberry Pi без пароля.

Важно: проверьте источник 001-prepare-card.sh и убедитесь, что /dev/disk5 это именно вставленная карта памяти, иначе можно потерять данные.

Результат: подготовка шести карт памяти займёт около одной минуты.

Кластер rPi. Второй шаг

Начинается самое интересное. Итак, вы вставили в Raspberry Pi карты памяти, подсоединили все кабели (сетевые и питания) и загрузили систему. Теперь нужно получить IP-адреса устройств. Это можно сделать, либо подключив экран к каждому из них и запустив команду ifconfig eth0, либо зайдя в маршрутизатор и проверив информацию на нём. Введите в файл pi-hosts.txt соответствующие значения.

[masters]pi0 ansible_host=192.168.50.132 # Pi0[workers]pi1 ansible_host=192.168.50.135 # Pi1pi3 ansible_host=192.168.50.60  # Pi3pi4 ansible_host=192.168.50.36  # Pi4pi2 ansible_host=192.168.50.85  # Pi2pi5 ansible_host=192.168.50.230 # Pi5

Важно: для работы некоторых программ может потребоваться имя узла pi0.

Добавьте в файл ~/.ssh/config следующую строку, она даст root-доступ для всех нод с именами pi*.

Host pi?  User root  Hostname %h.local

Теперь наши микровычислители (вот видите, насколько я стар!) готовы, и нам нужно подготовить их к запуску Ansible. Это можно легко сделать с помощью скрипта 001-prepare-ansible.sh, который подключиться по ssh к каждому определённому в файле pi-hosts серверу, на каждом сервере настроит chrony для NTP и установит интерпретатор Python.

Важно: возможно, потребуется открыть файл rpi.yaml и изменить раздел vars в соответствии с вашими предпочтениями. Я поступил именно так.

После этого шага запускаем на Ansible команду ansible-playbook rpi.yaml -f 10, которая выполнит следующие действия:

ОБЩИЕ:

  • Установит необходимые пакеты.

  • Разобьёт на разделы и отформатирует карту памяти RPI.

  • Настроит параметры "большого" раздела как системного диска.

  • Добавит записи в файл fstab.

  • Подтвердит изменения.

  • Перезапустит Pi, чтобы тот загрузился с "постоянного" раздела.

KUBEMASTER:

  • Настроит мастер-ноду с помощью kubeadm.

  • Сохранит токены локально (в файле static/token_file).

  • Определит на Pi пользователя с правами root с доступом к kubectl.

  • Сохранит настройки Kubernetes локально (в файле static/kubectl.conf).

KUBEWORKER:

  • Скопирует токены на рабочие ноды.

  • После этого через файл токенов рабочие ноды будут присоединены к мастер-ноде.

  • Скопирует kubectl.conf на root-пользователей рабочих нод.

БАЗОВЕ:

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

  • Установит на ноды py3-pip, PyYaml и Helm.

Если вы дошли до этого места, мои поздравления! Вы только что создали базовый кластер Kubernetes, который делать пока ничего не умеет, но готов научиться, если уделить ему немного внимания. Всё оказалось довольно элементарно нужно просто запустить несколько скриптов и дождаться их завершения. Я считаю, что это определённо лучшее решение, чем возиться руками.

Важно: скрипты можно запускать сколько угодно раз. Переформатировать карты памяти после каждого раза не нужно.

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

Кластер rPi. Третий шаг

После успешного выполнения двух предыдущих шагов кластер Pi готов к первым развёртываниям. Настройка осуществляется в несколько шагов, и, конечно, эти шаги также можно автоматизировать, об этом позаботится Terraform.

Вначале посмотрим на конфигурацию.

# Variables used for barebone kubernetes setupnetwork_subnet    = "192.168.50"net_hosts = {  adguard = "240"  adguard_catchall = "249"  traefik = "234"  torrent_rpc = "245"}nfs_storage = {  general = "/media/nfs"  torrent = "/mnt/drobo-storage/docker-volumes/torrent"  adguard = "/mnt/drobo-storage/docker-volumes/adguard"}# ENV variable: TRAEFIK_API_KEY sets traefik_api_key# ENV variable: GH_USER, GH_PAT for authentication with private containers

Кластер запускается в сети по адресу 192.168.50.0/24, но по умолчанию MetalLB будет использовать "конец" пула сетевых адресов с адресами 200-250. Поскольку у меня есть домашний торрент-сервер и DNS от Adguard, мне нужно задать для них конкретные адреса. Также мне нужен обслуживающий дашборды и прочие инструменты балансировщик нагрузки Traefik.

Важные замечания:

Значения nfs_*_path должны быть совместимы с настройками, заданными на втором шаге.

Убедитесь, что в конфигурационный файл Kubernetes ~/.kube/config is добавлены данные из файла static/kubernetes.conf. В качестве контекстного имени я использую home-k8s.

Что делает Terraform?

Устанавливает flannel, а также патч конфигурационных параметров для host-gw; устанавливает metalLB и задает сетевые параметры var.network_subnet 200250.

Устанавливает прокси Traefik и открывает к нему доступ в домашней сети через балансировщик нагрузки metalLB. Доступ к самому дашборду Traefik осуществляется по traefik.local.

Дашборд Traefik запускается на кластере Pi.Дашборд Traefik запускается на кластере Pi.

Устанавливает DNS-службу Adguard с запросами к хранилищам данных (persistent volumes claims) с использованием NFS; открывает доступ к дашборду (adguard.local) через Traefik и к самой службе через выделенный в домашней сети IP-адрес.

Adguard Home запускается на кластере Pi.Adguard Home запускается на кластере Pi.

Устанавливает и развёртывает на всех нодах стек мониторинга Prometheus и Grafana. Вносит изменения в DaemonSet Prometheus, устраняя необходимость в монтировании томов. Также через Traefik определяет Grafana как grafana.local. Имя и пароль пользователя Grafana по умолчанию admin:admin. В Grafana уже есть предустановленный плагин devopsprodigy-kubegraf-app. Я считаю его лучшим для мониторинга кластеров.

На кластере Pi запускается дашборд Grafana.На кластере Pi запускается дашборд Grafana.

Устанавливает дашборд Kubernetes и через Traefik определяет его как k8s.local.

Дашборд Kubernetes запускается на кластере Pi.Дашборд Kubernetes запускается на кластере Pi.

Устанавливает и развёртывает торрент-сервер (rTorrent) с веб-интерфейсом Flood. Дашборд отображается как torrent.local. Для хранения данных (в том числе конфигурационных) дашборд использует множество точек монтирования. Причина, по которой значение репликации должно быть установлено в 1, объясняется просто. У rTorrent есть проблемы с файлами блокировки, и, поскольку эта программа использует разделяемый каталог, она просто не запустится, если будет обнаружен файл блокировки. У меня rTorrent настроен на прослушивание на порте 23340.

Поскольку Raspberry Pi запускается с карты памяти, эта карта из-за постоянных операций чтения-записи со временем может выйти из строя. Поэтому я решил регулярно делать резервные копии etcd на NFS. Программа резервного копирования запускается раз в сутки с параметрами, устанавливаемыми Terraform. Каждая резервная копия весит около 32 мегабайт.

Запуск Terraform

Чтобы несколько упростить задачу, я создал Makefile, который может оказаться полезным при настройке. Возможно, понадобится установить следующие переменные окружения:

TRAEFIK_API_KEY // Traefik API keyGH_USER // Github userGH_PAT // Github Personal Access Token

Важно: учётные данные Github сейчас не задействованы, но в ближайшее время я планирую добавить функцию аутентификации для извлечения частных образов из GHCR.

ADDITIONAL_ARGS=-var 'traefik_api_key=$(TRAEFIK_API_KEY)' -var "github_user=$(GH_USER)" -var "github_pat=$(GH_TOKEN)"apply:cd infrastructure; terraform apply $(ADDITIONAL_ARGS) -auto-approve -var-file ../variables.tfvarsplan:cd infrastructure; terraform plan $(ADDITIONAL_ARGS) -var-file ../variables.tfvarsdestroy:cd infrastructure; terraform destroy $(ADDITIONAL_ARGS) -var-file ../variables.tfvarsdestroy-target:cd infrastructure; terraform destroy $(ADDITIONAL_ARGS) -var-file ../variables.tfvars -target $(TARGET)refresh:cd infrastructure; terraform refresh $(ADDITIONAL_ARGS) -var-file ../variables.tfvarsinit:cd infrastructure; rm -fr .terraform; terraform initimport:cd infrastructure; terraform import $(ADDITIONAL_ARGS) -var-file ../variables.tfvars $(ARGS)lint:terraform fmt -recursive infrastructure/

Заключительные замечания

Полный код можно взять на GitHub. Им можно свободно пользоваться и менять как угодно (как обычно, ваши комментарии и замечания горячо приветствуются). Я также опубликовал переделанные образы Docker (rTorrent и Flood) с multiarch-архитектурой, поддерживающей процессоры ARM64. Я довольно часто вычищаю весь кластер и делаю сборку с нуля, используя упомянутый репозиторий, а по мере появления новых функций я буду вносить в него соответствующие изменения.

Автоматизация экономит время и нервы и надо признать! хорошо выполняет свою работу. Именно поэтому DevOps-инженеров так ценят на рынке (зарплаты с пятью нулями в месяц уже обычное дело). Если хотите так же приходите учиться, помимо современных учебных программ, у нас есть менторы из ведущих компаний Рунета, которые поделятся с вами своим практическим опытом.

Узнайте, как прокачаться и в других специальностях или освоить их с нуля:

Другие профессии и курсы
Подробнее..

Перевод 5 приемов и хитростей для работы с SSH и кое-что еще

24.11.2020 20:17:07 | Автор: admin
image

В этой статье мы поговорим о полезных приемах и командах при работе с SSH. А именно:
  • Как использовать двухфакторную аутентификацию для SSH-подключений.
  • Безопасное использование проброса ключа (agent forwarding).
  • Завершение зависшей сессии.
  • Оставляем терминал открытым при выходе или разрыве связи.
  • Расшариваем удаленный терминал с другом (без Zoom!).


Многофакторная аутентификация


Для того, чтобы активировать ее, есть целых пять способов.

1. Обновляем OpenSSH и используем аппаратные токены. В феврале этого года в OpenSSH была добавлена поддержка токенов FIDO U2F (Universal Second Factor). Что сказать это отлично, но есть один нюанс.

Дело в том, что обновление добавляет новые типы ключей для поддержки токенов. Но использовать эту функцию можно лишь при обновлении клиента и сервера до версии 8.2 или более поздней. Текущую версию клиента можно проверить для этого есть команда ssh -V. Что касается удаленного сервера, то здесь стоит воспользоваться nc [servername] 22.

Кроме того, были добавлены два новых типа ключей ecdsa-sk и ed25519-sk. Для них же есть и сертификаты. Для того, чтобы создать ключевые файлы, необходимо вставить токен и выполнить команду

$ ssh-keygen -t ecdsa-sk -f ~/.ssh/id_ecdsa_sk.

Что она делает? Создает открытый и закрытый ключи, которые привязаны к U2F токену. Закрытый ключ на токене используется для расшифровки закрытого ключа, который хранится на диске.

Еще одна возможность задание в качестве второго фактора пароль для ключевых файлов. Дело в том, что OpenSSH работает с еще одним вариантом генерации sk. Таким образом, ключевые файлы хранятся на аппаратном токене, и они всегда с собой. Для того, чтобы создать резидентный ключ, нужно воспользоваться командой:

$ ssh-keygen -t ecdsa-sk -O resident -f ~/.ssh/id_ecdsa_sk.

Ну а для того, чтобы перенести ключевой файл на новую машину, требуется вставить носитель и выполнить команду $ ssh-add -K. Помните токен нужно активировать при подключении.

2. Используем PIV+PKCS11 и Yubikey. В том случае, если нужно подключаться к машинам, где установлены более ранние версии SSH-сервера, есть еще одна возможность. Вот подробная инструкция U2F+SSH с PIV/PKCS11. Немного сложно, но оно стоит того.

3. Третий способ использование yubikey-agent. Вот сам SSH-агент для Yubikeys, его создал Filipo Valsorda.

4. Touch ID и sekey. Еще один способ заключается в использовании Sekey агента с открытым исходным кодом. Он сохраняет закрытые ключи в системе secure enclave для MacOS и дает возможность запускать функцию подписания посредством Touch ID.

5. Наконец, использование Single Sign On SSH. Вот здесь размещена инструкция по настройке. Преимущество Single Sign On SSH в том, что пользователь получает возможность использовать политику безопасности поставщика учетных записей, включая поддержку многофакторной аутентификации.

Безопасный проброс ключа (agent forwarding)


Для чего используется проброс ключа? Для доступа удаленного хоста к локальному SSH-агенту пользователя. Дело в том, что когда SSH-клиент использует проброс ключа (чаще всего активируется ssh -A), то в ходе соединения присутствуют 2 канала. Первый это интерактивная сессия пользователя, второй канал проброса ключа.

Локальный SSH-агент создает IPC-сокет, который подключается через этот канал к удаленному хосту. Это достаточно опасно, поскольку у пользователя с правами root а удаленном хосте есть доступ к локальному SSH-агенту подключившегося пользователя. Таким образом, его можно использовать для доступа к ресурсам сети от имени этого пользователя. И если работать со стандартным SSH-агентом, о проблеме не узнать. Но вот с U2F-ключом или Sekey эту проблему можно убрать.

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

Выход из подвисшей SSH-сессии


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

1. Автоматически при разрыве сети. Для этого в файл SSH-конфигурации, .ssh/config, необходимо добавить:
ServerAliveInterval 5
ServerAliveCountMax 1


В этом случае ssh станет проверять соединение, отправляя echo-запросы на удаленный хост через определенные промежутки времени. Они задаются параметром ServerAliveInterval. Если без ответа остается больше, чем ServerAliveCountMax запросов, SSH закрывает соединение.

2. Разрыв сессии. ssh использует символ ~ в качестве управляющей последовательности по умолчанию. Команда ~ закрывает текущее соединение и возвращает в терминал.

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

Оставляем терминал на удаленном хосте открытым


Есть два варианта для сохранения сессии во время переключения между сетями.

1. Использование Mosh или Eternal Terminal

Если нужно надежное и не закрывающееся соединение, даже во время переключения между сетями, то есть выход нужно использовать Mosh mobile shell. Mosh называют защищенную оболочку, которая использует SSH для инициализации сессии (handshake). После этого она переключается на собственный зашифрованный канал, который весьма стабилен. Он, например, может обрабатывать разные ситуации, включая разрывы связи, изменение IP-адреса устройства, большие лаги при передаче данных и все прочее. Все это благодаря UDP и протоколу синхронизации, который использует Mosh.

Для работы с ним нужно, прежде всего, установить его как на сервере, так и клиенте, открыв порты с 60000 по 61000 для входящего UDP трафика на вашем удаленном хосте. После этого нужно просто набрать mosh user@server для подключения.

Mosh работает на уровне экранов терминала и нажатий клавиш, что дает ему большое количество плюсов по сравнению с SSH, передающим бинарный поток данных с обычного ввода вывода между клиентами и сервером. Если же нужно синхронизировать лишь экран терминала и нажатия клавиш, то прерванное соединение восстанавливается значительно быстрее. Тому же SSH необходимо хранить в буфере и пересылать все, а вот Mosh требуется лишь сохранять нажатия клавиш, синхронизируя последнее состояние терминального окна с клиентом.

2. Использование tmux. Для того, чтобы подключаться и отключаться при необходимости, сохраняя ту же сессию на удаленном хосте, стоит использовать мультиплексор терминала, который называется tmux. В том случае, если SSH-соединение отваливается, нужно снова подключиться и набрать tmux attach. После этого пользователь возвращается в сессию tmux.

Он предлагает также несколько дополнительных функций, включая табы, панели, такие же, как в терминале macOS, плюс возможность работать с терминалом вместе с другим пользователем. Еще больше возможностей можно получить при помощи Byobu это пакет, добавляющий большое количество удобных функций и сочетания клавиш. Он поставляется с Ubuntu, а в macOS его можно запустить при помощи Homebrew.

Расшариваем удаленный терминал


Бывают ситуации, когда необходимо расшарить SSH-сессию например, во время решения сложных проблем с серверами. Лучший способ сделать это tmux. Для решения задачи нужно сделать вот что:
  • Убедиться в том, что tmux установлен на сервере в DMZ (или локации, к которой нужно подключиться).
  • Обоим пользователям требуется подключиться к серверу через SSH с одним и тем же аккаунтом.
  • Один из пользователей должен запустить tmux для создания tmux-сессии.
  • Второй пользователь выполняет команду tmux attach.
  • Все готово!

Если же нужна тонкая настройка мульти пользовательских сессий, стоит воспользоваться tmate. Это улучшенный форк tmux.

Советы от эксперта


Перевод мы решили дополнить собственными советами надеемся, что они тоже пригодятся. Своим полезным опытом поделился Максим Клочков, старший консультант центра сетевых решений компании Инфосистемы Джет и преподаватель курса Профессия Специалист по кибербезопасности в Skillbox.

Проброс TCP-портов


У ssh есть два полезных параметра: -L и -R.

Параметр -L организует на нашем локальном компьютере открытый порт TCP, при попытке установить TCP-соединение на который происходит прозрачный проброс этого соединения в туннель ssh, и далее установление соединения с удаленного компьютера.

После параметра -L указывается аргумент в следующем виде:

ssh -L XXX:server1:YYY login@server2

Здесь server2 удаленный сервер, на который мы заходим по ssh, login имя пользователя, под которым мы заходим на удаленный сервер, XXX номер порта, который надо организовать на локальном компьютере, server1 хост, на который надо пробросить соединение с этого порта, и наконец YYY порт, на который надо пробросить это соединение.

Рассмотрим два примера.

Пример 1.

  1. Мы находимся на сервере spb.company1.ru, и хотим протестировать веб-приложение, которое обращается к базе по адресу central-db.company1.ru
  2. Эта база доступна только с сервера moscow.company1.ru, на порту 9999
  3. Мы организуем проброс локального порта 55555 к базе данных через ssh-туннель следующим образом: ssh -L 55555:central-db.company1.ru:9999 login@moscow.company1.ru
  4. После этого разворачиваем на нашем локальном сервере веб-приложение, и в качестве базы указыаем не central-db.company1.ru:9999, а localhost:55555 это соединение будет проброшено на сервер moscow.company1.ru, и уже этот сервер выполнит соединение с базой данных на хосте central-db.company1.ru, порт 9999

Пример 2.

  1. Мы находимся на сервере spb.company1.ru, и хотим показать заказчику тестовый веб-сайт по адресу moscow.company1.ru, порт 5000.
  2. К этому сайту нет доступа из Интернета, есть только внутренний доступ с того же хоста, где он развернут, т. е. с moscow.company1.ru
  3. Мы организуем проброс локального порта 80 через ssh-туннель следующим образом ssh -L 80:localhost:5000 login@moscow.company1.ru
  4. Если теперь пользователь зайдет браузером по ссылке spb.company1.ru, то соединение будет проброшено через ssh-туннель к серверу moscow.company1.ru, и уже этот сервер выполнит установку соединения по адресу localhost:5000, т. е. к нашему тестовому веб-сайту.

Параметр -R организует на удаленном сервере открытый порт TCP, при попытке установить TCP-соединение на который просходит прозрачный проброс этого соединения в туннель ssh, и далее установление соединения с локального сервера.

После параметра -R указывается аргумент в следующем виде:

ssh -R XXX:server1:YYY login@server2

Здесь server2 удаленный сервер, на который мы заходим по ssh, login имя пользователя, под которым мы заходим на удаленный сервер, XXX номер порта, который надо организовать на удаленном сервере, server1 хост, на который надо пробросить соединение с удаленного сервера, и наконец YYY порт, на который надо пробросить это соединение.

Рассмотрим два примера.

Пример 1.

  • Мы находимся на сервере moscow.company1.ru, и с этого сервера доступна база данных на сервере central-db.company1.ru, на порту 9999.
  • Мы разворачиваем веб-приложение на сервере spb.company1.ru, и с этого сервера база данных недоступна.
  • Мы организуем проброс порта с сервера spb.company1.ru к базе данных следующим образом: ssh -R 55555:central-db.company1.ru:9999 login@spb.company1.ru
  • После этого мы разворачиваем на сервере spb.company1.ru наше веб-приложение, но в качестве базы указываем не central-db.company1.ru:9999, поскольку этот сервер нам недоступен, а localhost:55555 это соединение будет проброшено на нашу локальную машину через ssh-туннель, и далее локальная машина установит соединение на сервер central-db.company1.ru, порт 9999.

Пример 2.

  • Мы находимся на сервере moscow.company1.ru, и на нашем сервере развернут тестовый веб-сайт, на порту 5000.
  • Этот тестовый веб-сайт доступен только локально, соединения из Интернета к нему запрещены, но мы хотим продемонстрировать этот сайт заказчику, который может заходить веб-браузером на сервер spb.company1.ru
  • Мы организуем проброс порта с сервера spb.company1.ru следующим образом: ssh -R 80:localhost:5000 login@spb.company1.ru
  • Если теперь пользователь зайдет браузером по ссылке spb.company1.ru, то соединение будет проброшено через ssh-туннель на наш локальный сервер moscow.company1.ru, и уже с него будет установлено соединение на localhost:5000 то есть к нашему тестовому веб-сайту.

А какими хитростями можете поделиться вы?

По теме:

Подробнее..

Сборник полезных ссылок для системного администратора

14.08.2020 14:14:13 | Автор: admin


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

Когда вы системный администратор, то отвечаете не за что-то одно, а сразу на всё. Причем горящие задачи частенько возникают самым внезапным образом. Если не заниматься чем-то каждый день, оно рано или поздно вылетает из головы, и вы просто не можете вспомнить нужные команды и опции именно в том момент, когда кругом всё горит и рушится. Поэтому мы подготовили 10 полезных шпаргалок, которые пригодятся вне зависимости от того, насколько вы крутой сисадмин:

  • Сети
    PDF-шпаргалка по утилитам и командам Linux для управления серверами и сетями.
  • Файрвол
    Эта шпаргалка поможет прокачать знания по безопасности Linux.
  • Продвинутый SSH
    Для большинства SSH это просто инструмент для удаленного входа. А на самом деле он может гораздо больше.
  • Пользователи и разрешения
    Здесь собраны примеры и команды, которые пригодятся при управлении пользователями и разрешениями.
  • Базовые команды
    Linux-команды для рутинных задач: навигация и управление файлами, установка софта на типовых дистрибутивах, сервисы.
  • Git
    Сегодня это де-факто стандарт управления версиями, пора научиться работать с ним эффективно.
  • Curl
    Типовые синтаксисы и сценарии применения утилиты curl, в том числе, как использовать ее для запроса API.
  • SELinux
    Полезное руководство по использованию и работе с Security-Enhanced Linux.
  • Kubectl
    9 важных команд kubectl для устранения неполадок и управления кластерами Kubernetes.
  • awk
    Памятка по типовым функциями awk.

Бонус: Bash-сценарии руководство для сисадминов



Эта электронная книга расскажет, почему shell-сценарии это лучший способ автоматизации задач, и научит создавать простые сценарии.

Почтовая рассылка Enable Sysadmin

Руководства, инструкции, учебные курсы, секреты-советы и многое другое.
Подробнее..

Перевод Запускаем командную строку Linux на iOS

02.10.2020 12:06:25 | Автор: admin


А вы знали, что можно запустить командную строку Linux на устройстве iOS? Возможно, вы спросите: Зачем мне пользоваться текстовыми приложениями на iPhone? Справедливый вопрос. Но если вы читаете Opensource.com, то, вероятно, знаете на него ответ: пользователи Linux хотят иметь возможность работать с ним на любом устройстве и хотят пользоваться собственными настройками.

Но больше всего они жаждут решения сложных задач.

У меня есть семилетний iPad 2 Mini, который по-прежнему неплохо подходит для чтения электронных книг и других задач. Однако я хочу использовать его и для доступа к командной строке приложений с моим набором программ и скриптов, без которых не могу работать. Мне нужно окружение, к которому я привык, а также моя стандартная среда разработки. И вот как мне удалось этого добиться.

Подключение к клавиатуре


Работать с командной строкой для программирования через экранную клавиатуру телефона или планшета довольно неудобно. Я рекомендую подключить внешнюю клавиатуру, или через Bluetooth, или воспользовавшись адаптером подключения камеры, чтобы подключить проводную клавиатуру (я выбрал второе). При подключении разделённой клавиатуры Kinesis Advantage к iPhone 6 получается странное устройство, напоминающее корпоративный кибердек из классической ролевой игры Shadowrun.

Устанавливаем оболочку на iOS


Для запуска полнофункциональной системы Linux на iOS есть два варианта:

  • Secure shell (SSH), подключаемая к компьютеру с Linux
  • Запуск виртуальной системы с помощью Alpine Linux с iSH, который является open source, но должен устанавливаться с помощью проприетарного приложения TestFlight компании Apple

В качестве альтернативы существуют два приложения-эмулятора терминала в open source, предоставляющие возможность работы с open-source-инструментами в ограниченном окружении. Это самый урезанный вариант на самом деле так вы запускаете не Linux, а инструменты Linux. При работе с этими приложениями существуют жёсткие ограничения возможностей, однако вы получаете частичную функциональность командной строки.

Прежде чем перейти к сложным решениями, я рассмотрю простейший способ.

Вариант 1: оболочка в песочнице


Один из самых простых способов установить приложение для iOS LibTerm. Это опенсорсная командная оболочка в песочнице с поддержкой более 80 команд по цене в ноль долларов. В комплекте с ней поставляются Python 2.7, Python 3.7, Lua, C, Clang и многое другое.

Примерно такой же функциональностью обладает a-Shell, описываемая разработчиками как тестовый интерфейс пользователя для платформы с экранным вводом. Исходники a-Shell выложены open source, она находится в активной разработке, предоставляет доступ к файловой системе и поставляется с Lua, Python, Tex, Vim, JavaScript, C и C++, а также с Clang и Clang++. Она даже позволяет устанавливать с помощью pip пакеты Python.

Вариант 2: SSH


Ещё одним шагом вперёд по сравнению со скачиванием приложения является настройка SSH-клиента. Уже долгое время мы могли использовать любое из множества приложений SSH-клиентов для iOS, чтобы подключаться к серверу, на котором запущен Linux или BSD. Преимущество использования SSH заключается в том, что на сервере может работать любой дистрибутив с любым программным обеспечением. Вы работаете удалённо и результаты работы просто передаются в эмулятор терминала на устройстве iOS.

Blink shell это популярное платное SSH-приложение в open source. Если не обращать внимания на маленький экран устройства, то использование этого ПО похоже на подключение к серверу через любую другую командную строку. Терминал Blink замечательно выглядит, имеет множество готовых тем и функцию создания собственных, в том числе возможность настройки и добавления новых шрифтов.

Вариант 3: запуск Linux


Использование SSH для подключения к серверу под Linux это отличный способ доступа к командной строке, но для него требуется внешний сервер и сетевое подключение. Это не самая большая преграда, но её нельзя полностью игнорировать, поэтому вам может потребоваться работать с Linux без сервера.

Если это ваш случай, то вам нужно будет сделать ещё один шаг вперёд. TestFlight это проприетарный сервис для установки разрабатываемых приложений ещё до того, как они были выложены в Apple App Store. Приложение TestFlight можно установить из App Store, а затем пользоваться тестовыми приложениями. Приложения в TestFlight позволяют в течение ограниченного времени работать с ними ограниченному количеству бета-тестеров (обычно до 10 000). Чтобы скачать тестовое приложение, нужно перейти со своего устройства по ссылке, которая обычно находится на веб-сайте разработчика тестового приложения.

Запуск Alpine Linux с iSH


iSH это open-source-приложение TestFlight, запускающее виртуальную машину с готовым дистрибутивом Alpine Linux (немного потрудившись, можно запускать и другие дистрибутивы).

Важная особенность: приложение экспериментальное. Так как iSH сейчас пока являются тестовым приложением, не ожидайте постоянной и надёжной работы. Приложения TestFlight ограничены по времени. Моя текущая сборка будет работать всего 60 дней. Это означает, что через 60 дней меня исключат и придётся снова присоединяться к следующему этапу тестирования iSH. Более того, я потеряю все свои файлы, если не экспортирую их с помощью Files на iOS или не скопирую их на Git-хост или через SSH. Иными словами: Не надейтесь, что всё это будет продолжать работать! Не помещайте в систему ничего важного для вас! Выполняйте резервное копирование в отдельное место!

Установка iSH


Начните с установки TestFlight из App Store. Затем установите iSH, получив ссылку на установку с веб-сайта приложения. Есть ещё один способ установки с использованием AltStore, но я его не пробовал. Или, если у вас есть платный аккаунт разработчика, можете скачать репозиторий iSH с GitHub и установить его самостоятельно.

При помощи ссылки TestFlight установит приложение iSH на ваше устройство. Как и в случае с любым другим приложением, на экране появится значок.

Управление пакетами


iSH выполняет эмулятор x86 с Alpine Linux. Alpine это крошечный дистрибутив размером меньше 5 МБ. Я впервые работал с Alpine, поэтому думал, что такой минимализм будет раздражать, но на самом деле он мне очень понравился.


В Alpine используется менеджер пакетов apk, который проще, чем даже apt или pacman.

Как установить пакет:

apk add package

Как удалить пакет:

apk del package

Как узнать другие команды и информацию:

apk --help

Обновление менеджера пакетов:

apk updateapk upgrade

Установка текстового редактора


Стандартным текстовым редактором Alpine является Vi, но я предпочитаю Vim, поэтому установил его:

apk add vim

При желании можно установить Nano или Emacs.

Смена оболочки


Не знаю, как насчёт вас, а мне нужна была fish shell. Другие люди предпочитают Bash или Zsh. Однако в Alpine используется ash! Ash это форк оболочки Dash, которая сама является форком оригинального ash, или Almquist shell. Её приоритетом является скорость. Я решил обменять скорость на встроенное автодополнение, цвета, управление клавишами Vim и подсветку синтаксиса, которые я люблю и знаю по fish shell.

Установка fish:

apk add fish

Если вам нужна Bash с её автодополнением и man-страницами, то установите их:

apk add bash bash-doc bash-completion

Минималистичная идеология Alpine обычно означает, что некоторые программы, которые в других дистрибутивах являются одним пакетом, будут разделены на несколько пакетов поменьше. Также это означает, что вы можете настраивать и уменьшать размер системы ровно так, как вам этого хочется.

Подробнее об установке Bash можно узнать из этого туториала.

Смена оболочки по умолчанию


После установки fish можно временно переключиться на неё, введя fish и перейдя в оболочку. Но я хочу сделать fish оболочкой по умолчанию, а команда chsh, которую я использовал в других дистрибутивах, не работала.

Сначала узнаем, куда установилась fish:

which fish

Вот, что получилось у меня:

/usr/bin/fish

Далее изменим login shell на fish. Можете использовать любой удобный вам редактор. Если в новичок, то установите Nano (командой apk add nano), чтобы можно было отредактировать файлы конфигурации и сохранить их через CTRL+X, подтвердить и выйти.

Но я использовал Vim:

vim /etc/passwd

У меня первая строка была такой:

root:x:0:0:root:/root:/bin/ash

Чтобы сделать fish оболочкой по умолчанию, изменим эту строку на следующую:

root:x:0:0:root:/root:/usr/bin/fish

Затем сохраним файл и выйдем.

Я уверен, что существует хороший способ изменить путь к оболочке, чтобы её можно было использовать сразу. Но я его не знаю, поэтому рекомендую вернутся в браузер приложений, принудительно выполнить выход из оболочки и для надёжности выключить и перезагрузить iPad или iPhone. Снова откройте iSH и теперь кроме сообщения Welcome to Alpine! и информации о запуске с apk вы увидите стандартное приветственное сообщение логина fish: Welcome to fish, the friendly interactive shell. Ура!


Настройка Python и pip


Я решил добавить Python (версию 3.x), не только для того, чтобы писать код, но и потому, что я пользуюсь несколькими программами на Python. Установим его:

apk add python3

Хотя Python 2.x устарел, можно установить и его:

apk add python

Установим менеджер пакетов Python под названием pip и setuptools:

python3 -m ensurepip --default-pip

Для установки и настройки менеджера пакетов понадобится какое-то время, поэтому просто подождите.

Затем можно будет скачать инструмент для передачи файлов по сети curl:

apk add curl

Читаем мануалы


Fish использует встроенное автодополнение на основе man-страниц. Как и другие пользователи командной строки, я пользуюсь мануалом man, а в Alpine он не установлен. Поэтому я установил его с терминальным пейджером less:

apk add man man-pages less less-doc

В дополнение к man я использую великолепный проект tldr pages, предоставляющий упрощённые и управляемые сообществом man-страницы.

Я установил его с помощью pip:

pip install tldr

Команда tldr соединяется с вебом для получения страниц, когда встречает запрос на новую страницу. Если вам нужно узнать, как пользоваться командой, то вы можете написать что-то типа tldr curl и получить описание на простом английском языке и хорошие примеры по использованию команды.

Разумеется, всю эту работу по установке можно автоматизировать с помощью dotfiles или скрипта установки, но на самом деле это не очень соответствует идеологии Alpine настройке минимальной установки чётко под свои потребности. Кроме того, это ведь заняло так много времени, правда?

Дополнительная информация


В Вики iSH есть страница "what works" с отчётами о том, какие пакеты сейчас работают. Кстати, похоже, что npm сейчас не работает.

На ещё одной вики-странице объясняется, как получить доступ к файлам iSH из приложения iOS Files. Это один из способов, которым можно перемещать и копировать файлы.

Можно также установить Git (да! apk add git ) и пушить свою работу в удалённый репозиторий или передавать его на сервер через SSH. И, разумеется, можно скачивать и запускать любое количество замечательных open-source-проектов с GitHub.

Подробнее об iSH можно узнать по этим ссылкам:


На правах рекламы


Вдсина предлагает виртуальные серверы на Linux или Windows. Используем исключительно брендовое оборудование, лучшую в своём роде панель управления серверами собственной разработки и одни из лучших дата-центров в России и ЕС. Поспешите заказать!

Подробнее..

Из песочницы CIFS over SSH штатными средствами Windows 10

17.11.2020 00:09:32 | Автор: admin

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


Однажды в потребовалось мне организовать доступ к серверу по протоколу SMB и в поиске решения я наткнулся на следующую статью: Mounting your Nikhef home directory using SSH for Windows 8. Это было простое и удобное решение, которое использовало Putty. Чуть позже мне пришлось настраивать это решение на другом компьютере и я понял, что Putty тут лишний с тех пор как в Windows 10 появился встроенный ssh-клиент на основе OpenSSH.


Под катом идентичная схема, только с использованием OpenSSH под Windows 10.


У меня схема организована следующим образом:


  1. На сервере запущена Samba, от имени пользователя www-data расшарена корневая папка с сайтами. Доступ к серверу только через ssh с авторизацией по ключу. Сервер за NATом, порт проброшен только для ssh.
  2. В процессе входа в аккаунт на домашней машине на Windows 10 через встроенный в систему OpenSSH устанавливается соединение с сервером с авторизацией по ключу.
  3. Туннелируется порт 445 удаленной машины на локальный порт 44445 сетевого loopback-адаптера доступного по адресу 10.255.255.1
  4. На loopback-адаптере 10.255.255.1 порт 44445 проксируется на локальный 445. Таким образом при подключении к \\10.255.255.1\ открывается удаленная шара с файлами (которая, при необходимости монтируется как сетевой диск).

Всё это автоматом лень торжествует. Безопасно, быстро и нативно выглядит. Любым редактором могу открывать и править файлы на удаленном сервере как у себя на локальном без проблем с загрузкой правленых файлов и установкой им необходимых разрешений. При этом нет проблем с безопасностью Samba.


Итак сперва по шагам:


На стороне WINDOWS


Должен быть установлены OpenSSH. В Windows 10 и Windows Server 2019 появился встроенный SSH клиент на основе OpenSSH. Им мы и воспользуемся. Сначала убедимся что он установлен наберем в командной строке


ssh


Если видим исполнение команды все "Ок", клиент присутствует в системе.


Шаг 1. Настройка сетевого адаптера


Устанавливаем loopback-адаптер в системе. Мы будем обращаться по адресу к локальному адаптеру.


hdwwiz.exe


Запустится Мастер установки оборудования (Здесь я пользуюсь русской Windows 10 Pro).


Далее -> Установка оборудования, выбранного из списка вручную -> Сетевые адаптеры -> Microsoft > Адаптер замыкания на себя Microsoft KM-Test -> Далее


Уверен, что эти шаги можно сделать из командной строки, но не нашел способ установки драйвера без devcon.exe, потому и не стал заморачиваться с автоматизацией этого шага.


Далее уже в CMD (от имени Администратора).


netsh interface show interface


Видим появился второй адаптер. У меня он называется Ethernet 2.


Теперь настроим адрес для этого адаптера




Или из командной строки:


netsh interface ip set address name="Ethernet 2" source=static address=10.255.255.1 mask=255.255.255.0

В результате у нас появился адаптер локально доступный по адресу 10.255.255.1


Теперь проблема в том, что нам необходимо получить доступ к общей папке через TCP-порт 445, но при загрузке Windows этот порт захватывается системным драйвером lanmanserver для всех интерфейсов. Отложив запуск драйвера lanmanserver и установив правило portproxy, мы можем обойти это.


Далее в командной строке от имени администратора меняем режим запуска сетевых служб (пробел после start= обязателен!!!):


sc config lanmanserver start= demandsc config iphlpsvc start= auto

и настраиваем для адаптера с адресом 10.255.255.1 проксирование порта 44445 на локальный порт 445


netsh interface portproxy add v4tov4 listenaddress=10.255.255.1 listenport=445 connectaddress=10.255.255.1 connectport=44445

Теперь необходимо перезагрузиться, чтобы схема перехвата порта у службы lanmanserver сработала.


Проверяем что прослушивание порта осуществляется нашим loopback-адаптером, просмотрев открытые в системе порты


netstat -an | find ":445 "

Если мы видим


TCP    10.255.255.1:445    0.0.0.0:0       LISTENING

значит все в порядке и порт прослушивается на нужном адресе. Если же мы видим "0.0.0.0:445" значит в нашей схеме что-то не сработало правильно. Проверить правила переадресации портов можно командой


netsh interface portproxy show v4tov4

Шаг 2. Ключ и рабочий скрипт


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


mkdir %APPDATA%\CIFSoverSSHcd %APPDATA%\CIFSoverSSH

Генерируем ключ для ssh-авторизации (назовем его, например: cifsoversshkey)


ssh-keygen -t rsa -N "" -f cifsoversshkey 

В результате будет сгенерирована пара открытого и закрытого ключа. Для того, чтобы OpenSSH не выдавал сообщение UNPROTECTED PRIVATE KEY FILE! нужно изменить права на файл ключа. Задачу мы будем запускать для одного пользователя, от имени которого мы собираемся работать в Windows. Можно через GUI, но мне мне показалось что картинок уже достаточно. В Windows это сделаем следующей командой:


icacls cifsoversshkey /RESETicacls cifsoversshkey /grant Имя_Пользователя:F /inheritance:r

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


icacls cifsoversshkey 

Должны быть права только для текущего пользователя, иначе файл ключа не будет принят программой OpenSSH и соединение не будет подниматься!


Создадим в текущей папке пакетный файл cifsoverssh.cmd следующего содержания:


call cmd /c start "" /B C:\Windows\System32\OpenSSH\ssh.exe user@111.111.111.111 -p remoteport -i %APPDATA%\CIFSoverSSH\cifsoversshkey -L 10.255.255.1:44445:localhost:445 -N -o "StrictHostKeyChecking=no"

Где:
user@111.111.111.111 пользователь на стороне linux сервера @ адрес удаленного сервера


Шаг 3. Ярлык или задача в планировщике


Создаем ярлык для следующей команды: powershell -nologo -noninteractive -windowStyle hidden -command "%APPDATA%\CIFSoverSSH\cifsoversshkey.cmd"
Это позволит запускать наш пакетный файл через powershell без открытия окна. Если запускать его через CMD будет открываться черное окно терминала и будет висеть все время, пока соединение будет установлено, а это неудобно и некрасиво.


Для автоматизации запуска при входе в систему можно создать задачу в планировщике:


schtasks /CREATE /RU %username% /TN "CIFS over SSH" /TR "powershell.exe -nologo -noninteractive -windowStyle hidden -command %APPDATA%\CIFSoverSSH\cifsoversshkey.cmd" /SC ONLOGON /DELAY 0000:10 /IT /RL highest

На стороне клиентского компьютера Windows все приготовления были закончены.


Настройка Linux сервера


Предполагается, что ssh-сервер был предварительно настроен и включена авторизация по ключу.


Подключаемся по ssh из командной строки на windows-машине


C:\Windows\System32\OpenSSH\ssh.exe user@111.111.111.111 -p remoteport

В домашней папке пользователя, от имени которого мы будем авторизовываться при создании туннеля ищем файл ~/.ssh/authorized_keys (если файл отсутствует создадим его).


mkdir ~/.ssh && touch ~/.ssh/authorized_keys

Теперь необходимо в этот файл вставить содержимое нашего файла публичного ключа, созданного на нашей windows-машине (файл %APPDATA%\CIFSoverSSH\cifsoversshkey.pub). Откроем его в любом редакторе и вставим цепочку ключа с новой строки. Если есть другие ключи, просто вставим его с новой строки.


Устанавливаем Samba (на примере Debian)


apt update && apt install samba

Переименовываем старый файл настроек и создаем новый файл


mv /etc/samba/smb.conf /etc/samba/smb.conf.oldtouch /etc/samba/smb.conf

Открываем пустой файл настроек и приводим его к следующему виду:


[global]realm = webserverserver string = Web serverworkgroup = WORKGROUP# Setup charsetsdos charset = cp1251unix charset = utf8# Disable printersload printers = Noshow add printer wizard = noprintcap name = /dev/nulldisable spoolss = yes# Setup logginglog file = /var/log/smbd.logmax log size = 50max xmit = 65536debug level = 1# Setup daemon settingsdomain master = Nopreferred master = Yessocket options = IPTOS_LOWDELAY TCP_NODELAY SO_SNDBUF=65536 SO_RCVBUF=65536 SO_KEEPALIVEos level = 65use sendfile = Yesdns proxy = Nodont descend = /proc,/dev,/etcdeadtime = 15# Enable synlinksunix extensions = Nowide links = yesfollow symlinks = yes# Securtity settingssecurity = usermap to guest = Bad Passwordguest account = nobodycreate mask = 0664directory mask = 0775hide dot files = yesclient min protocol = SMB2client max protocol = SMB3[ShareName]comment = Sites folderpath = /home/webforce user = www-dataforce group = www-dataread only = Noguest ok = Yeswritable = yescreate mask = 0664directory mask = 2775

В последней секции мы настраиваем непосредственно шару. В названии секции указываем имя шары ShareName. Path = путь к файлам, которые мы хотим расшарить. В параметрах force user и force group указываем linux-пользователя, от имени которого будут сохраняться файлы при изменении и создании в шаре. Так как у меня там лежат файлы для веб-сервера у меня пользователь www-data


Перезапускаем Samba


systemctl restart smbd

Отключаемся и выходим в командную строку Windows


exit

Всё готово. Теперь остается только запустить наш ярлык или выйти из профиля пользователя windows и снова войти (если вы создали задачу в планировщике).


После этого ваша удаленная шара будет доступна по адресу \10.255.255.1\ShareName Можно даже монтировать её как сетевой диск.

Подробнее..

Ossh параллельное выполнение команд на многих серверах

15.03.2021 04:23:51 | Автор: admin
Иногда бывает нужно запустить патч Бармина какую-то команду на многих серверах и желательно не ждать слишком долго результатов выполнения. Для этого я написал ossh (One SSH to rule them all). Вот пример его работы:

$ wc -l /tmp/ossh.ips21418 /tmp/ossh.ips$ time ossh -n -h /tmp/ossh.ips -c uptime -p 1000 >/tmp/ossh.outreal    3m10.310suser    0m30.970ssys     0m19.282s$ grep 'load average' /tmp/ossh.out | sort -n -k5 | tail -n110.23.91.97   [1]  13:37:55 up 828 days,  2:34,  0 users,  load average: 8.29, 4.45, 3.90$

В данном примере в файле /tmp/ossh.ips находится 21418 ip адресов машин. -n означает, что не нужно делать реверс запросы, чтобы определить имя по адресу. -c uptime задает команду, которую я хочу выполнить. -p 1000 позволяет использовать до 1000 соединений одновременно. Как видно из вывода отработала команда достаточно быстро.

Что еще умеет ossh?

$ ossh -?Usage: ossh [-?AinPv] [-c COMMAND] [-C COMMAND_FILE] [-H HOST_STRING] [-h HOST_FILE] [-I FILTER] [-k PRIVATE_KEY] [-l USER] [-o PORT] [-p PARALLELISM] [-T TIMEOUT] [-t TIMEOUT] [parameters ...] -?, --help        Show help -A, --askpass     Prompt for a password for ssh connects -c, --command=COMMAND                   Command to run -C, --command-file=COMMAND_FILE                   file with commands to run -H, --host=HOST_STRING                   Add the given HOST_STRING to the list of hosts -h, --hosts=HOST_FILE                   Read hosts from file -i, --ignore-failures                   Ignore connection failures in the preconnect mode -I, --inventory=FILTER                   Use FILTER expression to select hosts from inventory -k, --key=PRIVATE_KEY                   Use this private key -l, --user=USER   Username for connections [$LOGNAME] -n, --showip      In the output show ips instead of names -o, --port=PORT   Port to connect to [22] -p, --par=PARALLELISM                   How many hosts to run simultaneously [512] -P, --preconnect  Connect to all hosts before running command -T, --connect-timeout=TIMEOUT                   Connect timeout in seconds [60] -t, --timeout=TIMEOUT                   Run timeout in seconds -v, --verbose     Verbose output$

Список хостов можно задать либо прямо в командной строке через опцию -H (в случае нескольких хостов их надо перечислить через пробел, а весь список взять в кавычки как в примерах ниже) либо загрузить из файла при помощи опции -h. Строки в файле, начинающиеся с #, игнорируются. Адрес может содержать порт: my.host:2222. Можно использовать brace expansion: host{1,3..5}.com превратится в host1.com host3.com host4.com host5.com. И -H и -h можно использовать многократно.

Для авторизации будут использованы
  • пароль, который ossh запросит при использовании опции -A
  • ssh ключ, заданный опцией -k
  • ssh-agent (в этом случае у вас должна быть определена переменная окружения SSH_AUTH_SOCK)

Именно в таком порядке.

Иногда бывает нужно убедиться, что вы можете залогиниться на все машины прежде чем выполнить команду. Для этого есть опция -P. По умолчанию если хоть одна машина будет недоступна ossh завершится с ошибкой. Если вы хотите игнорировать неудачные соединения используйте опцию -i.

Ossh может использовать вашу систему инвентаризации. Для этого в путях должна находится команда ossh-inventory, которой будут переданы параметры опции -I. Эта опция может быть использована многократно. Команда ossh-inventory должна выдавать на стандартный вывод строки в формате:
имя_машины адрес_машины

Где адрес_машины может быть как днс именем, так и ip адресом.

Команды для выполнения задаются опциями -C (читать из файла) или -c (брать из командной строки). Эти опции могут использоваться многократно. При наличии и -C и -c сперва выполнятся команды из файлов, потом из командной строки.

Помимо просто выполнения команд при помощи ossh можно стримить логи в реальном времени:

$ ossh -H "web05 web06" -c "tail -f -c 0 /var/log/nginx/access.log|grep --line-buffered Wget"web05 192.168.1.23 - - [22/Jun/2016:12:24:02 -0700] "GET / HTTP/1.1" 200 1532 "-" "Wget/1.15 (linux-gnu)"web05 192.168.1.49 - - [22/Jun/2016:12:24:07 -0700] "GET / HTTP/1.1" 200 1532 "-" "Wget/1.15 (linux-gnu)"web06 192.168.1.117 - - [22/Jun/2016:12:24:23 -0700] "GET / HTTP/1.1" 200 1532 "-" "Wget/1.15 (linux-gnu)"web05 192.168.1.29 - - [22/Jun/2016:12:24:30 -0700] "GET / HTTP/1.1" 200 1532 "-" "Wget/1.15 (linux-gnu)"...


Вот симуляция rolling deployment:

$ ossh -p 1 -H "test0{1..3}" -c "sleep 10 && date"test01 Wed Jun 22 12:38:24 PDT 2016test02 Wed Jun 22 12:38:34 PDT 2016test03 Wed Jun 22 12:38:44 PDT 2016$


Видно, что команды выполняются на машинах последовательно. В каждый момент времени задействована только одна машина. Для настоящего деплоймента sleep 10 && date надо заменить на, к примеру, apt-get install -y your_package.

Именно для деплоймента была написана самая первая версия ossh. Кто-то спросит почему я не использовал какую-то общепринятую систему управления конфигурациями? Дело в том, что это было в далеком 2013-м году и мы использовали chef. Было ясно, что chef нас не устраивает в частности неопределенностью когда именно будут применены изменения (chef-client отрабатывал раз в 30 минут). Для того, чтобы согласованно выкатывать изменения на многих машинах некоторые разработчики применяли грязный хак: chef-client не работал постоянно, а однократно запускался (через ssh) только в тот момент, когда было нужно сделать деплоймент. Уже в тот момент шла работа по замене chef на salt, но переход был не простым и завершение его требовало дополнительного времени. Мы же разрабатывали новый сервис, который требовал частых деплойментов и раскатывался единственным дебиановским пакетом. Сперва мы использовали утилиту knife из состава chef. Эта утилита позволяла соединяться по ssh с нужными серверами и выполнять на них команды. В какой-то моент я понял, что chef в данном случае является лишним звеном и написал ossh.

Важно отметить, что ossh является инструментом для разрешения масштабных и нестандартных проблем. Если необходимость использовать ossh возникает часто это повод задуматься все ли у вас хорошо с инфраструктурой и управлением серверами. Вот некоторые ситуации, в которых ossh помог лично мне:
  • Однажды я наводил порядок в /root/.ssh/authorized_keys на большом количестве серверов (на тот момент их было около 7000). Разработчики прописали туда свои ключи, в частности для процессов обновления своих сервисов. Нужно было получить список всех ключей, использованных на всех машинах, и убедиться, что удаление этих ключей не приведет к катастрофическим последствиям.
  • Для безболезненного прохождения leap second
  • Когда мы боролись с TCP SACK PANIC правила для iptables выкатывались системой управления конфигураций. Чтобы убедиться, что все хорошо, я проверил наличие нужных правил при помощи ossh. И это было совсем не зря, обнаружились машины, на которых правила не применились.
  • Иногда мне приходится создавать тестовые среды состоящие из сотен (а иногда из тысяч) машин. Часто эти машины изолированы от production сети и не доступны для штатной системы управления конфигурациями. В подобных ситуациях конфигурирование машин можно проводить при помощи ossh.

Предвижу вопрос почему я не использовал готовое решение. Как я упомянул выше необходимость в прогонах команд на тысячах машин первый раз возникла у меня в 2013-м году. На тот момент мне удалось найти только parallel ssh, который не устроил меня следующим:
  • Мне не удалось поднять параллелизм выше 150, начали возникать ошибки при соединении с удаленными серверами
  • parallel ssh накапливал весь вывод и выдавал его по завершении команды. Стримить логи, к примеру, с его помощью было невозможно
  • Вывод parallel ssh был (лично для меня) неудобен для парсинга

Исходно ossh был написан на ruby, для увеличения производительности я задействовал event machine, а потом и fiber-ы. Относительно недавно я переписал ossh на go. Буду признателен если go-эксперты (я таковым на данный момент не являюсь) посмотрят на мой код и укажут на возможные способы улучшить его.
Подробнее..

NetSarang xShell мощный клиент SSH

21.10.2020 16:06:58 | Автор: admin
image

Всё ещё пользуетесь Putty + WinSCP/FileZilla?
Тогда рекомендуем обратить внимание на такое ПО как xShell

Он поддерживает не только SSH протокол, но и другие. Например, telnet или rlogin
Одновременно можно подключаться к нескольким серверам (механизм вкладок).
Нет необходимости каждый раз вводить данные, их можно запомнить.
Начиная с 6й версии появился русский интерфейс, понимает все русские кодировки, в том числе и UTF-8.
Поддерживает как подключение по паролю, так и подключение по ключу.

Более того, теперь для управления файлами по ftp/sftp не нужно отдельно запускать WinSCP или FileZilla.
Разработчики xShell учли ваши нужды и сделали также xFtp, который поддерживает обычный FTP и SFTP.
А самое главное то, что xFtp можно запустить прямо из активной ssh сессии и он сразу подключится к этому
конкретному серверу в режиме передачи файлов (используя sFtp протокол). Но вы можете и
самостоятельно открыть xFtp и подключится к любому из серверов.

Также в комплекте поставляется генератор публичных/приватных ключей и менеджер по их управлению.

image

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

www.netsarang.com/ru/free-for-home-school

Заполнить поля, обязательно email, к которому у вас есть доступ, туда будет выслана ссылка для скачивания.

image

Скачиваем, устанавливаем оба приложения. Запускаем.

После запуска видим окошко со списком сохранённых сессий, пока оно пустое. Нажимаем новая

image

Заполняем данные для подключение, порт/хост/ип адрес, а также желаемое имя сессии.
Далее переходим в аутентиикация и заполняем логин с паролем

image

Далее Ок и подключаемся к серверу.

Для xFTP всё тоже самое. Единственное что необходимо выбрать протокол, по умолчанию будет sFTP, можно выбрать обычный FTP.

Самое удобоное то выделенный текст автоматически копируется в буфер обмена
(Инструменты опции клавиатура и мышь копировать отмеченный текст в буфер обмена)

image

Подключатся можно не только по паролю, но и используя ключ, что куда безопаснее и удобнее.

Необходимо сгенерировать наш ключ, если быть точнее пару публичный/приватный ключи.

Запускаем Xagent (устанавливается в комплекте)

Видим список ключей, пока он пуст. Нажимаем Manage Keys, затем Generate
Type RSA
Length 4096 bits минимум.

image

Нажием Next, ждём. Затем ещё раз Next

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

image

Далее Next и видим собственно наш ПУБЛИЧНЙ ключ. Его используем для подключения к серверу. Один ключ можно использовать на многих серверах, что удобно.

На этом генерация закончена, но ещё не всё.
Необходимо добавить ключ на сервере
Подключаемя к серверу по ssh и переходим в /root/.ssh

root@alexhost# cd /root/.ssh

на что в 90% случаев получаем ошибку -bash: cd: /root/.ssh: No such file or directory
это нормально, данная папка отсутствует если на сервере ранее не генерировались ключи.

Необходимо подобным образом сгенерировать и ключ самого сервера.

root@alexhost# ssh-keygen -t rsa -b 4096

Нам предложит путь куда сохранить файл ключа
Соглашаемся на умолчание /root/.ssh/id_rsa нажатием Enter
Далее пароль на файл ключа и подтверждение, либо оставить пустым и Enter.

опять заходить в /root/.ssh

root@alexhost# cd /root/.ssh

Необходимо создать файл authorized_keys

root@alexhost# nano authorized_keys

Вставляем в него наш ключ в текстовом виде полученный выше

image

Сохраняем, выходим.
Ctrl + O
Ctrl + X

Переходим в xShell, вызываем список сохранённых сессий (Alt+O)

image

находим нашу сессию, нажимаем свойства, переходим на аутентификация

в поле метод выбираем public key
в поле пользовательский ключ выбираем наш созданный ранее ключ, сохраняем подключаемся.

image

Клиент использует ПРИВАТНЙ ключ, на сервере прописывается ПУБЛИЧНЙ

Приватный ключ можно перенести на другой ваш пк, если вы хотите с него подключатся.

В Xagent manage keys, выбираем ключ Export, сохраняем.
На другом пк Xagent manage keys Import, выбираем, добавляем. Если ключ был защищён паролем, пароль будет запрошен в этот момент.

Ключ можно прописать любому пользователю, не только root.
Путь стандартный /домашняя_папка_пользователя/.ssh/authorized_keys
Для юзера alexhost, например, по умолчанию это будет /home/alexhost/.ssh/authorized_keys

image
Подробнее..

Использование локального .bashrc через ssh и консолидация истории выполнения команд

24.11.2020 08:05:01 | Автор: admin
Если вам приходится работать с большим количеством удаленных машин через ssh то возникает вопрос как унифицировать shell окружение на этих машинах. Копировать заранее .bashrc не очень удобно, а зачастую невозможно. Давайте рассмотрим вариант копирования непосредственно в процессе соединения:

[ -z "$PS1" ] && returnsshb() {    scp ~/.bashrc ${1}:    ssh $1}# the rest of the .bashrcalias c=cat...

Это очень наивный способ с несколькими очевидными недостатками:

  • Можно затереть уже существующий .bashrc
  • Вместо одного соединения мы устанавливаем 2
  • Как следствие авторизоваться придется тоже 2 раза
  • Аргумент функции может быть только адресом удаленной машины

Улучшенный вариант:

[ -z "$PS1" ] && returnsshb() {    local ssh="ssh -S ~/.ssh/control-socket-$(tr -cd '[:alnum:]' < /dev/urandom|head -c8)"    $ssh -fNM "$@"    $ssh placeholder "cat >~/.bash-ssh" <~/.bashrc    $ssh "$@" -t "bash --rcfile ~/.bash-ssh -i"    $ssh placeholder -O exit >/dev/null 2>&1}# the rest of the .bashrcalias c=cat...

Теперь мы используем только одно соединение за счет мультиплексирования. .bashrc копируется в файл, который не используется bash по умолчанию и мы явно указываем его через опцию --rcfile. Аргументом функции может быть не только адрес удаленной машины, но и другие опции ssh.

На этом в принципе можно было бы и остановиться, но полученное решение обладает неприятным недостатком. Если вы запустите screen или tmux, то будет использоваться тот .bashrc, который находится на удаленной машине и все ваши алиасы и функции потеряются. К счастью это можно побороть. Для этого надо создать скрипт-обертку, который мы объявим нашим новым шеллом. Давайте для простоты предположим, что скрипт-обертка на удаленной машине у нас уже есть и находится в ~/bin/bash-ssh. Выглядит скрипт вот так::

#!/bin/bashexec /bin/bash --rcfile ~/.bash-ssh $@

А .bashrc так:

[ -n "$SSH_TTY" ] && export SHELL="$HOME/bin/bash-ssh"[ -z "$PS1" ] && returnsshb() {    local ssh="ssh -S ~/.ssh/control-socket-$(tr -cd '[:alnum:]' < /dev/urandom|head -c8)"    $ssh -fNM "$@"    $ssh placeholder "cat >~/.bash-ssh" <~/.bashrc    $ssh "$@" -t "bash --rcfile ~/.bash-ssh -i"    $ssh placeholder -O exit >/dev/null 2>&1}# the rest of the .bashrcalias c=cat...

Если существует переменная SSH_TTY мы понимаем, что находимся на удаленной машине и переопределяем переменную SHELL. С этого момента при запуске нового интерактивного шелла будет запускаться скрипт, который будет стартовать bash с нестандартным конфигом, сохраненный при установлении ssh сессии.

Для получения удобного рабочего решения осталось придумать как создавать на удаленной машине скрипт-обертку. В принципе можно создавать его в сохраняемом нами конфиге баша вот так:

[ -n "$SSH_TTY" ] && {    mkdir -p "$HOME/bin"    export SHELL="$HOME/bin/bash-ssh"    echo -e '#!/bin/bash\nexec /bin/bash --rcfile ~/.bash-ssh "$@"' >$SHELL    chmod +x $SHELL}

Но на самом деле можно обойтись единственным файлом ~/.bash-ssh:

#!/bin/bash[ -n "$SSH_TTY" ] && [ "${BASH_SOURCE[0]}" == "${0}" ] && exec bash --rcfile "$SHELL" "$@"[ -z "$PS1" ] && returnsshb() {    local ssh="ssh -S ~/.ssh/control-socket-$(tr -cd '[:alnum:]' < /dev/urandom|head -c8)"    $ssh -fNM "$@"    $ssh placeholder "cat >~/.bash-ssh" <~/.bashrc    $ssh "$@" -t 'SHELL=~/.bash-ssh; chmod +x $SHELL; bash --rcfile $SHELL -i'    $ssh placeholder -O exit >/dev/null 2>&1}# the rest of the .bashrcalias c=cat...

Теперь файл ~/.bash-ssh является одновременно и самостоятельным скриптом и конфигом bash. Работает это так. На локальной машине команды после [ -n "$SSH_TTY" ] игнорируются. На удаленной машине функция sshb создает файл ~/.bash-ssh и использует его как конфиг для запуска интерактивной сессии. Конструкция [ "${BASH_SOURCE[0]}" == "${0}" ] позволяет определить подгружается файл другим скриптом или запущен как самостоятельный скрипт. В результате, когда ~/.bash-ssh используется
  • как конфиг exec игнорируется
  • как скрипт управление переходит башу и исполнение ~/.bash-ssh заканчивается тем самым exec-ом.


Теперь при коннекте по ssh ваше окружение везде будет выглядеть одинаково. Так работать гораздо удобнее, но история выполнения команд будет оставаться на машинах, с которыми вы соединялись. Лично мне бы хотелось сохранять историю локально, чтобы иметь возможность освежить в памяти, что именно я делал на каких-то машинах в прошлом. Для того, чтобы это сделать нам нужны следующие компоненты:
  • Tcp сервер на локальной машине, который бы принимал данные с сокета и перенаправлял их в файл
  • Форвард слушающего порта этого сервера на машину, с которой мы коннектимся по ssh
  • PROMPT_COMMAND в установках bash, который бы по завершению команды отправлял обновление истории на отфорварженный порт
Это можно реализовать так:

#!/bin/bash[ -n "$SSH_TTY" ] && [ "${BASH_SOURCE[0]}" == "${0}" ] && exec bash --rcfile "$SHELL" "$@"[ -z "$PS1" ] && return[ -z "$SSH_TTY" ] && {    history_port=26574    netstat -lnt|grep -q ":${history_port}\b" || {        umask 077 && nc -kl 127.0.0.1 "$history_port" >>~/.bash_eternal_history &    }}HISTSIZE=$((1024 * 1024))HISTFILESIZE=$HISTSIZEHISTTIMEFORMAT='%t%F %T%t'update_eternal_history() {    local histfile_size=$(stat -c %s $HISTFILE)    history -a    ((histfile_size == $(stat -c %s $HISTFILE))) && return    local history_line="${USER}\t${HOSTNAME}\t${PWD}\t$(history 1)"    local history_sink=$(readlink ~/.bash-ssh.history 2>/dev/null)    [ -n "$history_sink" ] && echo -e "$history_line" >"$history_sink" 2>/dev/null && return    local old_umask=$(umask)    umask 077    echo -e "$history_line" >> ~/.bash_eternal_history    umask $old_umask}[[ "$PROMPT_COMMAND" == *update_eternal_history* ]] || export PROMPT_COMMAND="update_eternal_history;$PROMPT_COMMAND"sshb() {    local ssh="ssh -S ~/.ssh/control-socket-$(tr -cd '[:alnum:]' < /dev/urandom|head -c8)"    $ssh -fNM "$@"    local bashrc=~/.bashrc    [ -r ~/.bash-ssh ] && bashrc=~/.bash-ssh && history_port=$(basename $(readlink ~/.bash-ssh.history))    local history_remote_port="$($ssh -O forward -R 0:127.0.0.1:$history_port placeholder)"    $ssh placeholder "cat >~/.bash-ssh; ln -nsf /dev/tcp/127.0.0.1/$history_remote_port ~/.bash-ssh.history" < $bashrc    $ssh "$@" -t 'SHELL=~/.bash-ssh; chmod +x $SHELL; bash --rcfile $SHELL -i'    $ssh placeholder -O exit >/dev/null 2>&1}# the rest of the .bashrcalias c=cat...

Блок после [ -z "$SSH_TTY" ] срабатывает только на локальной машине. Мы проверяем занят ли порт и если нет запускаем на нем netcat, вывод которого перенаправлен в файл.

Функция update_eternal_history вызывается непосредственно перед выводом на экран подсказки bash. Эта функция проверяет не была ли последняя команда дубликатом и если нет отправляет ее на отфорварженный порт. Если порт не сконфигурирован (в случае локальной машины) или если при отправке произошла ошибка сохранение идет в локальный файл.

Функция дополнилась установлением форвардинга порта и созданием симлинки, которая будет использоваться update_eternal_history для отправки данных на сервер.

Это решение не лишено недостатков:
  • Порт для netcat захардкожен, есть шанс нарваться на конфликт
  • Форвард порта дает возможность любому человеку со злыми намерениями заспамить вашу историю мусорными данными
  • Если вы создаете цепочку соединений (машина А-машина Б-машина В), то данные будут нормально передаваться с В на А, но в случае обрыва соединения между А и Б и установления нового соединения Б будет продолжать форвардить старый порт и данные с В не достигнут А, они будут сохраняться на Б

Мой собственный .bashrc можно посмотреть тут.

Если у вас есть идеи как можно улучшить предложенное решение пожалуйста делитесь в комментариях.
Подробнее..
Категории: *nix , Bash , Ssh

Доступ к ssh серверу через очень зарегулированное подключение

07.12.2020 04:14:37 | Автор: admin
Эта статья является результатом посещения мной автосервиса. В ожидании машины я подключил свой ноутбук к гостевой wifi-сети и читал новости. К своему удивлению я обнаружил, что некоторые сайты я посетить не могу. Зная про sshuttle (и будучи большим поклонником этого проекта) я попытался установить sshuttle сессию со своим сервером, но не тут-то было. Порт 22 был наглухо заблокирован. При этом nginx на порту 443 отвечал нормально. К следующему посещению автосервиса я установил на сервер мультиплексор sslh. Сервер работает под управлением gentoo и я добавил следующую строчку в файл /etc/conf.d/sslh:
DAEMON_OPTS="-p 0.0.0.0:443 --ssl 127.0.0.1:8443 --ssh 127.0.0.1:22 --user nobody"

В зависимости от типа коннекта, соединения на порт 443 либо пробрасываются на локальный порт
  • 8433 в случае https (на этом порту работает nginx)
  • 22 в случае ssh
Но при попытке установить ssh соединение с сервером меня опять постигла неудача. Видимо фильтрация осуществлялась не просто по портам, но также использовался deep packet investigation. Таким образом задача усложняется. Ssh трафик надо обернуть в https. К счастью это не сложно благодаря проекту websocat. На странице проекта вы можете найти много собранных бинарников. Если по какой-то причине вы хотите собрать бинарник самостоятельно из исходников это тоже не очень сложно. Я делаю это при помощи packer от hashicorp при помощи вот такой конфигурации:
{  "min_packer_version": "1.6.5",  "builders": [    {      "type": "docker",      "image": "ubuntu:20.04",      "privileged": true,      "discard": true,      "volumes": {        "{{pwd}}": "/output"      }    }  ],  "provisioners": [    {      "type": "shell",      "skip_clean": true,      "environment_vars": [        "DEBIAN_FRONTEND=noninteractive"      ],      "inline": [        "apt-get update && apt-get install -y git curl gcc libssl-dev pkg-config gcc-arm-linux-gnueabihf",        "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs >/tmp/rustup.sh && chmod +x /tmp/rustup.sh && /tmp/rustup.sh -y",        "git clone https://github.com/vi/websocat.git && cd websocat/",        ". $HOME/.cargo/env && cargo build --release --features=ssl",        "printf '[target.armv7-unknown-linux-gnueabihf]\nlinker = \"arm-linux-gnueabihf-gcc\"\n' >$HOME/.cargo/config",        "rustup target add armv7-unknown-linux-gnueabihf",        "cargo build --target=armv7-unknown-linux-gnueabihf --release",        "strip target/release/websocat",        "tar czf /output/websocat.tgz target/armv7-unknown-linux-gnueabihf/release/websocat target/release/websocat",        "chown --reference=/output /output/websocat.tgz"      ]    }  ]}

Клиентская сторона у меня на ubuntu 20.04, сервер работает на nvidia tegra jetson tk1, так что для него я делаю кросс-сборку под платформу armv7. Обратите внимание, что серверная сборка делается без поддержки ssl, так как ssl termination у меня осуществляет nginx, который обрабатывает входящие соединения. Конфигурация nginx выглядит вот так:
http {    server {        listen 0.0.0.0:8443 ssl;        server_name your.host.com;        ssl_certificate /etc/letsencrypt/live/your.host.com/fullchain.pem;        ssl_certificate_key /etc/letsencrypt/live/your.host.com/privkey.pem;        location /wstunnel/ {            proxy_pass http://127.0.0.1:8022;            proxy_http_version 1.1;            proxy_set_header Upgrade $http_upgrade;            proxy_set_header Connection "Upgrade";        }    }}

Websocat я запускаю из крона своего юзера:
* * * * * netstat -lnt|grep -q :8022 || $HOME/bin/websocat -E --binary ws-l:127.0.0.1:8022 tcp:127.0.0.1:22|logger -t websocat &

Теперь вы можете соединиться с вашим сервером вот так:
ssh -o ProxyCommand='websocat --binary wss://your.host.com/wstunnel/' your.host.com

Насколько заворачивание в https снижает пропускную способность? Для того, чтобы это проверить я использовал вот такую команду:
ssh -o ProxyCommand='websocat --binary wss://your.host.com/wstunnel/' your.host.com 'dd if=/dev/zero count=32768 bs=8192' >/dev/null

В моих экспериментах я получил снижение пропускной способности в 2 раза. При использовании протокола ws://, т.е. http, пропускная способность соединения идентична необернутому ssh.
А вот так можно установить сессию sshuttle:
sshuttle -e 'ssh -o ProxyCommand="websocat --binary wss://your.host.com/wstunnel/"' -r your.host.com 0/0 -x $(dig +short your.host.com)/32

При очередном визите в автосервис я убедился, что все работает как надо. В качестве приятного бонуса резко упало количество попыток залогиниться на сервер через ssh с левых адресов.
Подробнее..
Категории: *nix , Ssh

Основы Bash-скриптинга для непрограммистов

24.01.2021 20:20:26 | Автор: admin

Статья рассчитана на тех, кто не имеет или имеет мало опыта работы с командной строкой Unix/Linux, но желает научиться с ней эффективно взаимодействовать и разрабатывать скрипты для выполнения своих задач. Приведенные примеры справедливы для выполнения в командной оболочке bash операционной системы Ubuntu/Debian, но могут быть использованы и в других оболочках и ОС с учетом их специфики.

1. Командные оболочки

Существует множество дистрибутивов(форков) операционных систем(ОС) семейства Linux, наиболее известные среди них: Ubuntu, Debian, CentOS, Red Hat, Fedora, SuSE, FreeBSD, Mint.

Здесь есть большая схема со всеми форками Linux.

В каждой ОС существуют различные командные оболочки (shell), их назначение одинаково (администрирование, автоматизация, вычисления, мониторинг и т.д.), как и большинство основных команд, но реализация может отличаться. В таблице ниже приведено сравнение, в котором видно, что некоторые возможности поддерживаются не всеми оболочками.

Feature

Bourne

C

TC

Korn

Bash

Псевдонимы (синонимы)

Нет

Да

Да

Да

Да

Редактор командной строки

Нет

Нет

Да

Да

Да

Расширенные шаблоны файлов

Нет

Нет

Нет

Да

Да

Автозавершение имени файла

Нет

Да

Да

Да

Да

Стеки директорий (pushd and popd)

Нет

Да

Да

Нет

Да

История

Нет

Да

Да

Да

Да

Функции

Да

Нет

Нет

Да

Да

Горячие клавиши

Нет

Нет

Да

Нет

Да

Управление заданиями

Нет

Да

Да

Да

Да

Исправление ошибок в командах

Нет

Нет

Да

Нет

Да

Формат приглашения командной строки

Нет

Нет

Да

Нет

Да

Список доступных командных оболочек можно получить командой

cat /etc/shells

При необходимости можно установить нужную командную оболочку командой sudo apt install <имя_оболочки>. Например, для установки ksh нужно выполнить

sudo apt install ksh

2. Настройка тестовой среды

Если у вас уже есть виртуальная/реальная машина с установленным Linux, или вы знаете, как её настроить, то можно пропустить информацию из спойлера ниже. Если есть доступ по ssh к какому-либо серверу, то Вы также можете использовать его shell для тренировок. Используйте имеющийся доступ с осторожностью, не тренируйтесь на продуктовых(промышленных) серверах и других критичных окружениях. Если вы не хотите развертывать виртуальную машину(ВМ), то для тестов можно воспользоваться онлайн-терминалами, но нужно знать и учитывать их особенности и ограничения. Примеры онлайн-терминалов:
https://cocalc.com/app?anonymous=terminal
https://www.tutorialspoint.com/execute_bash_online.php
https://rextester.com/l/bash_online_compiler

Развертывание виртуальной машины

VirtualBox ПО для виртуализации, позволяющее запускать одну ОС внутри другой. Разумеется, существуют и другие способы развертывания ВМ, здесь мы рассмотрим лишь один из них. В данном случае, предполагается, что у вас ПК под управлением Windows или Mac. Для начала нужно скачать VirtualBox отсюда и установить его.

Также нужно скачать образ ВМ с установленной ОС Ubuntu отсюда. Качайте самую новую версию, убедитесь, что скачиваемый образ имеет формат VirtualBox (VDI).

Создадим виртуальную машину. Сначала создаем на локальном диске папку для размещения в ней ВМ и их файлов (у меня C:\vmachines). Распакуем скачанный образ диска ВМ в созданную папку:

Для распаковки потребуется архиватор 7zip или другой, поддерживающий формат .7z. Далее запускаем VirtualBox и выбираем меню Машина -> Создать:

В открывшемся окне указываем имя ВМ (тип и версия в большинстве случаев подтягиваются автоматически, если это понятно из имени), а также созданную нами папку. Объем оперативной памяти устанавливается автоматически, 1Гб достаточно для выполнения тестов. Выбираем Использовать существующий виртуальный жесткий диск, нажимаем значок выбора образа жесткого диска, в открывшемся окне нажимаем Добавить, выбираем распакованный ранее файл образом ВМ и нажимаем Открыть:

Нажимаем Выбрать:

И нажимаем Создать:

Выбираем в списке созданную ВМ и нажимаем Запустить:

После запуска ВМ появится окно для входа в ОС:

Выбираем учетную запись osboxes.org и вводим пароль osboxes.org. После входа в систему нажимаем Ctrl+Alt+T, у нас откроется окно терминала, в котором можно выполнять тесты:

Для удобства можно развернуть окно виртуальной машины на весь экран, окно терминала также будет развернуто. Для того, чтобы можно было вставлять данные из буфера в терминал ВМ и копировать их, можно включить в настройках ВМ общий буфер обмена в меню Устройства -> Общий буфер обмена -> Двунаправленный:

Создадим тестового пользователя.

Для создания пользователя нам понадобятся права суперпользователя (root). Для их получения выполним команду sudo su -, указав пароль текущего пользователя (osboxes.org). Далее создадим пользователя test командой adduser test. Также выдадим пользователю test права на повышение привилегий при необходимости, выполнив команду usermod -aG sudo test:

osboxes@osboxes:~$ sudo su -[sudo] password for osboxes:root@osboxes:~# adduser testAdding user `test' ...Adding new group `test' (1001) ...Adding new user `test' (1001) with group `test' ...The home directory `/home/test' already exists.  Not copying from `/etc/skel'.New password:Retype new password:passwd: password updated successfullyChanging the user information for testEnter the new value, or press ENTER for the default        Full Name []:        Room Number []:        Work Phone []:        Home Phone []:        Other []:Is the information correct? [Y/n]root@osboxes:~# usermod -aG sudo testroot@osboxes:~#

На данном этапе уже можно использовать тестовую среду, выполняя команды в терминале (Ctrl+Alt+T). Дополнительно можно настроить подключение по ssh клиентом (например, PuTTY), мы рассмотрим это отдельно.

3. Первые команды

Итак, мы подключились к терминалу и находимся в shell. Давайте сориентируемся в пространстве.Чтобы узнать имя машины(сервера), на которой мы находимся, введем hostname и нажмем Enter:

test@osboxes:~$ hostnameosboxes

Имя пользователя, под которым мы подключены, как правило отображается в приглашении командной строки (test@osboxes:~$). Если имя текущего пользователя не отображается (например, если задан другой формат приглашения), то его можно получить командой whoami, или id (также отображает группы пользователя):

test@osboxes:~$ whoamitesttest@osboxes:~$ iduid=1001(test) gid=1001(test) groups=1001(test),27(sudo)

Чтобы узнать, в какой оболочке мы находимся, нужно выполнить команду echo $SHELL:

test@osboxes:~$ echo $SHELL/bin/bash

Разберем, что произошло. Команда echo выводит значение параметра, переданного ей, в нашем случае мы передали $SHELL. Знак $ означает, что мы обращаемся к переменной, т.е. $SHELL возвращает значение переменной SHELL, заданной в окружении. Список значений всех переменных можно получить командой env. Таким образом, отобразив значение переменной, мы видим, что мы находимся в оболочке bash. Оболочка конкретного пользователя указана в файле /etc/passwd, содержимое которого можно получить так:

test@osboxes:~$ cat /etc/passwdroot:x:0:0:root:/root:/bin/bash...test:x:1001:1001:,,,:/home/test:/bin/dash

Команда cat выводит содержимое файла (часть строк в приведенном выводе пропущена и заменена на ). Из файла видим, что для пользователя test указана оболочка /bin/dash. Изменить оболочку текущего пользователя, которая будет загружаться по умолчанию, можно следующей командой(chsh сокращенное от change shell):

test@osboxes:~$ chsh -s /bin/bashPassword:

Теперь давайте проверим, в каком каталоге мы находимся, для этого выполним команду pwd:

test@osboxes:~$ pwd/home/test

Для получения списка файлов текущей директории используем команду ls. По соглашению, скрытые файлы начинаются с точки. Для отображения их с помощью команды ls нужно добавить ключ a. Чтобы отобразить список в расширенном формате, добавим ключ l. Таким образом команда и её вывод будут выглядеть так:

test@osboxes:~$ ls -altotal 36drwxr-xr-x 5 test test 4096 Nov  9 01:05 .drwxr-xr-x 5 root root 4096 Nov  8 11:39 ..-rw------- 1 test test    9 Nov  8 12:28 .bash_history-rw-r--r-- 1 test test  220 Nov  8 11:39 .bash_logout-rw-r--r-- 1 test test 3771 Nov  8 11:39 .bashrcdrwxr-xr-x 4 test test 4096 Nov  8 11:40 .cachedrwxr-xr-x 4 test test 4096 Nov  8 11:40 .configdrwxr-xr-x 3 test test 4096 Nov  8 11:40 .local-rw-r--r-- 1 test test  807 Nov  8 11:39 .profile-rw-r--r-- 1 test test    0 Nov  9 01:05 .sudo_as_admin_successfultest@osboxes:~$

Для команды ls с параметрами может быть задан синоним (alias), что упрощает её ввод. Список уже заданных синонимов можно получить командой alias:

test@osboxes:~$ aliasalias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"'alias egrep='egrep --color=auto'alias fgrep='fgrep --color=auto'alias grep='grep --color=auto'alias l='ls -CF'alias la='ls -A'alias ll='ls -alF'alias ls='ls --color=auto'

Видим, что команда ll является синонимом команды ls -alF. Синонимы могут ссылаться на другие синонимы. Так, в приведенном выше примере команда ll выполняет команду ls -alF, в которой ls в свою очередь также является синонимом команды ls --color=auto. Для любой команды с целью упрощения её ввода при частом использовании можно задать синоним. В синонимах также можно использовать переменные среды. Например, чтобы иметь возможность из любой директории получить список файлов домашней директории, можно задать синоним командой alias lshome='ls -alF $HOME', таким образом, можно выполнить:

test@osboxes:~$ cd /tmptest@osboxes:/tmp$ lshometotal 40drwxr-xr-x 5 test test 4096 Nov  9 02:29 ./drwxr-xr-x 5 root root 4096 Nov  8 11:39 ../-rw------- 1 test test   47 Nov  9 02:36 .bash_history-rw-r--r-- 1 test test  220 Nov  8 11:39 .bash_logout-rw-r--r-- 1 test test 3771 Nov  8 11:39 .bashrcdrwxr-xr-x 5 test test 4096 Nov  9 02:29 .cache/drwxr-xr-x 5 test test 4096 Nov  9 02:29 .config/drwxr-xr-x 3 test test 4096 Nov  8 11:40 .local/-rw-r--r-- 1 test test  807 Nov  8 11:39 .profile-rw-rw-r-- 1 test test   72 Nov  9 02:29 .selected_editor-rw-r--r-- 1 test test    0 Nov  9 01:05 .sudo_as_admin_successful

Здесь командой cd /tmp мы перешли в директорию /tmp, и, находясь в ней, выполнили команду lshome, которая вывела список файлов директории /home/test. При этом в синониме мы ссылаемся на переменную $HOME, в которой содержится путь к домашней директории текущего пользователя.

Алиасы задаются в файле ~/.bash_aliases. Тильда (~) означает домашнюю директорию пользователя. В нашем случае /home/test. Также можно задать их в файле ~/.bashrc. Здесь нужно сказать пару слов об этих файлах.

При запуске bash в качестве оболочки, сначала выполняется файлы (в случае их наличия), в которых могут быть заданы различные настройки профиля и выполнены различные действия в процессе входа до появления командной строки: сначала выполняется файл /etc/profile, далее bash последовательно ищет и выполняет первый из найденных файлов в следующем порядке: ~/.bash_profile, ~/.bash_login, ~/.profile. Из указанных файлов могут вызываться и другие.

Так, например, из файла ~./profile вызывается файл ~/.bashrc, из которого, в свою очередь, в числе прочих, вызывается файл .bash_aliases при его наличии. А файл /etc/profile выполняет также все файлы .sh, находящиеся в директории etc/profile.d.

В bash существуют внутренние команды, их список можно получить командой help. Помощь по конкретной внутренней команде можно получить с помощью команды help <имя_команды>, например:

test@osboxes:~$ help pwdpwd: pwd [-LP]    Print the name of the current working directory.    Options:      -L        print the value of $PWD if it names the current working                directory      -P        print the physical directory, without any symbolic links    By default, `pwd' behaves as if `-L' were specified.    Exit Status:    Returns 0 unless an invalid option is given or the current directory    cannot be read.

Некоторые внутренние команды bash дублированы в виде исполняемых файлов. Это сделано, чтобы скрипты можно было корректно выполнять в оболочках, в которых не реализованы эти команды. Например, существует исполняемый файл /usr/bin/echo, который реализует функционал внутренней команды echo. Команда help echo выведет справку по внутренней команде, а команда /usr/bin/echo help выведет справку для соответствующего исполняемого файла. Вывод справки для этих команд отличается, но в целом они реализуют идентичный функционал.

Таким образом, если в оболочке не реализована внутренняя команда echo, скрипт, содержащий вызов echo, сможет успешно выполниться, т.к. для обработки вызова будет использован исполняемый файл /usr/bin/echo. Для поиска выполненных ранее команд можно использовать клавиши Вверх и Вниз, команды из истории можно редактировать и повторно выполнять. В конце статьи приведен список полезных команд.

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

Подробнее..
Категории: *nix , Linux , Unix , Ubuntu , Bash , Bash scripting , Ssh , Virtualbox , Debian , Shells

Основы Bash-скриптинга для непрограммистов. Часть 2

30.01.2021 20:16:42 | Автор: admin

В первой части статьи мы рассмотрели командные оболочки, профили, синонимы и первые команды. Под спойлером я также рассказал, как развернуть тестовую виртуальную машину.

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

Скрипты

Для выполнения нескольких команд одним вызовом удобно использовать скрипты. Скрипт это текстовый файл, содержащий команды для shell. Это могут быть как внутренние команды shell, так и вызовы внешних исполняемых файлов.

Как правило, имя файла скрипта имеет окончание .sh, но это не является обязательным требованием и используется лишь для того, чтобы пользователю было удобнее ориентироваться по имени файла. Для интерпретатора более важным является содержимое файла, а также права доступа к нему.

Перейдем в домашнюю директорию командой cd ~ и создадим в ней с помощью редактора nano (nano script.sh)файл, содержащий 2 строки:

#!/bin/bashecho Hello!

Чтобы выйти из редактора nano после набора текста скрипта, нужно нажать Ctrl+X, далее на вопрос "Save modified buffer?" нажать Y, далее на запрос "File Name to Write:" нажать Enter. При желании можно использовать любой другой текстовый редактор.

Скрипт запускается командой ./<имя_файла>, т.е. ./ перед именем файла указывает на то, что нужно выполнить скрипт или исполняемый файл, находящийся в текущей директории. Если выполнить команду script.sh, то будет выдана ошибка, т.к. оболочка будет искать файл в директориях, указанных в переменной среды PATH, а также среди встроенных команд (таких, как, например, pwd):

test@osboxes:~$ script.shscript.sh: command not found

Ошибки не будет, если выполнять скрипт с указанием абсолютного пути, но данный подход является менее универсальным: /home/user/script.sh. Однако на данном этапе при попытке выполнить созданный файл будет выдана ошибка:

test@osboxes:~$ ./script.sh-bash: ./script.sh: Permission denied

Проверим права доступа к файлу:

test@osboxes:~$ ls -l script.sh-rw-rw-r-- 1 test test 22 Nov  9 05:27 script.sh

Из вывода команды ls видно, что отсутствуют права на выполнение. Рассмотрим подробнее на картинке:

Права доступа задаются тремя наборами: для пользователя, которому принадлежит файл; для группы, в которую входит пользователь; и для всех остальных. Здесь r, w и x означают соответственно доступ на чтение, запись и выполнение.

В нашем примере пользователь (test) имеет доступ на чтение и запись, группа также имеет доступ на чтение и запись, все остальные только на чтение. Эти права выданы в соответствии с правами, заданными по умолчанию, которые можно проверить командой umask -S. Изменить права по умолчанию можно, добавив вызов команды umask с нужными параметрами в файл профиля пользователя (файл ~/.profile), либо для всех пользователей в общесистемный профиль (файл /etc/profile).

Для того, чтобы установить права, используется команда chmod <параметры> <имя_файла>. Например, чтобы выдать права на выполнение файла всем пользователям, нужно выполнить команду:

test@osboxes:~$ chmod a+x script.sh

Чтобы выдать права на чтение и выполнение пользователю и группе:

test@osboxes:~$ chmod ug+rx script.sh

Чтобы запретить доступ на запись (изменение содержимого) файла всем:

test@osboxes:~$ chmod a-w script.sh

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

test@osboxes:~$ chmod 754 script.sh

Будут выданы права -rwxr-xr--:

test@osboxes:~$ ls -la script.sh-rwxr-xr-- 1 test test 22 Nov  9 05:27 script.sh

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

Символ перед наборами прав доступа указывает на тип файла ( означает обычный файл, d директория, l ссылка, c символьное устройство, b блочное устройство, и т. д.). Соответствие числа, его двоичного представления и прав доступ можно представить в виде таблицы:

Число

Двоичный вид

Права доступа

0

000

Нет прав

1

001

Только выполнение (x)

2

010

Только запись (w)

3

011

Запись и выполнение (wx)

4

100

Только чтение (r)

5

101

Чтение и выполнение (rx)

6

110

Чтение и запись (rw)

7

111

Чтение, запись и выполнение (rwx)

Выдав права на выполнение, можно выполнить скрипт:

test@osboxes:~$ ./script.shHello!

Первая строка в скрипте содержит текст #!/bin/bash. Пара символов #! называется Шебанг (англ. shebang) и используется для указания интерпретатору, с помощью какой оболочки выполнять указанный скрипт. Это гарантирует корректность исполнения скрипта в нужной оболочке в случае, если у пользователя будет указана другая.

Также в скриптах можно встретить строку #!/bin/sh. Но, как правило, /bin/sh является ссылкой на конкретный shell, и в нашем случае /bin/sh ссылается на /bin/dash, поэтому лучше явно указывать необходимый интерпретатор. Вторая строка содержит команду echo Hello!, результат работы которой мы видим в приведенном выводе.

Параметры скриптов

Для того, чтобы обеспечить некоторую универсальность, существует возможность при вызове передавать скрипту параметры. В этом случае вызов скрипта будет выглядеть так: <имя_скрипта> <параметр1> <параметр2> , например ./script1.sh Moscow Russia.

Для того, чтобы получить значение первого параметра, необходимо в скрипте указать $1, второго - $2, и т.д. Существует также ряд других переменных, значения которых можно использовать в скрипте:
$0 имя скрипта
$# количество переданных параметров
$$ PID(идентификатор) процесса, выполняющего скрипт
$? код завершения предыдущей команды

Создадим файл script1.sh следующего содержания:

#!/bin/bashecho Hello, $USER!printf "Specified City is: %s, Country is: %s\n" $1 $2

Выдадим права на выполнение и выполним скрипт с параметрами:

test@osboxes:~$ chmod u+x script1.shtest@osboxes:~$ ./script1.sh Moscow RussiaHello, test!Specified City is: Moscow, Country is: Russia

Мы передали 2 параметра, указывающие город и страну, и использовали их в скрипте, чтобы сформировать строку, выводимую командой printf. Также для вывода в строке Hello использовали имя пользователя из переменной USER.

Для того, чтобы передать значения параметров, состоящие из нескольких слов (содержащие пробелы), нужно заключить их в кавычки:

test@osboxes:~$ ./script1.sh "San Francisco" "United States"Hello, test!Specified City is: San Francisco, Country is: United States

При этом нужно доработать скрипт, чтобы в команду printf параметры также передавались в кавычках:

printf "Specified City is: %s, Country is: %s\n" "$1" "$2"

Из приведенных примеров видно, что при обращении к переменной для получения её значения используется символ $. Для того, чтобы сохранить значение переменной просто указывается её имя:

COUNTRY=RUSSIAecho $COUNTRY

Операторы условного выполнения, выбора и циклы

Так же, как и в языках программирования, в bash существуют операторы условного выполнения выполнение определенных действий при определенных условиях. Кроме того, существует возможность повторного выполнения определенного блока команд пока выполняется заданное условие операторы цикла. Рассмотрим каждый из них подробнее.

Оператор условного выполнения представляет собой конструкцию вида:

if [ <условие> ]then  <команда1>else  <команда2>fi

Создадим скрипт, проверяющий длину введенной строки (например, для проверки длины пароля), которая должна быть не меньше (т.е. больше) 8 символов:

#!/bin/bashecho Hello, $USER!echo -n "Enter string: "read strif [ ${#str} -lt 8 ]then  echo String is too shortelse  echo String is okfi

Выполним 2 теста, с длиной строки 5 и 8 символов:

test@osboxes:~$ ./script2.shHello, test!Enter string: abcdeString is too shorttest@osboxes:~$ ./script2.shHello, test!Enter string: abcdefghString is ok

Командой read str мы получаем значение, введенное пользователем и сохраняем его в переменную str. С помощью выражения ${#str} мы получаем длину строки в переменной str и сравниваем её с 8. Если длина строки меньше, чем 8 (-lt 8), то выдаем сообщение String is too short, иначе String is ok.

Условия можно комбинировать, например, чтобы указать, чтоб длина должна быть не меньше восьми 8 и не больше 16 символов, для условия некорректных строк нужно использовать выражение [ ${#str} -lt 8 ] || [ ${#str} -gt 16 ]. Здесь || означает логическое "ИЛИ", а для логического "И" в bash используется &&.

Условия также могут быть вложенными:

#!/bin/bashecho Hello, $USER!echo -n "Enter string: "read strif [ ${#str} -lt 8 ]then  echo String is too shortelse  if [ ${#str} -gt 16 ]  then    echo String is too long  else    echo String is ok  fifi

Здесь мы сначала проверяем, что строка меньше 8 символов, отсекая минимальные значения, и выводим "String is too short", если условие выполняется. Если условие не выполняется(строка не меньше 8 символов) - идем дальше(первый else) и проверяем, что строка больше 16 символов. Если условие выполняется - выводим "String is too long", если не выполняется(второй else) - выводим "String is ok".

Результат выполнения тестов:

test@osboxes:~$ ./script2.shHello, test!Enter string: abcdefString is too shorttest@osboxes:~$ ./script2.shHello, test!Enter string: abcdefghijklmnopqrstuvString is too longtest@osboxes:~$ ./script2.shHello, test!Enter string: abcdefghijklString is ok

Оператор выбора выглядит следующим образом:

case "$переменная" in "$значение1" ) <команда1>;; "$значение2" ) <команда2>;;esac

Создадим новый скрипт, который будет выводить количество спутников для указанной планеты:

#!/bin/bashecho -n "Enter the name of planet: "read PLANETecho -n "The $PLANET has "case $PLANET in  Mercury | Venus ) echo -n "no";;  Earth ) echo -n "one";;  Mars ) echo -n "two";;  Jupiter ) echo -n "79";;  *) echo -n "an unknown number of";;esacecho " satellite(s)."

Тест:

test@osboxes:~$ ./script3.shEnter the name of planet: MercuryThe Mercury has no satellite(s).test@osboxes:~$ ./script3.shEnter the name of planet: VenusThe Venus has no satellite(s).test@osboxes:~$ ./script3.shEnter the name of planet: EarthThe Earth has one satellite(s).test@osboxes:~$ ./script3.shEnter the name of planet: MarsThe Mars has two satellite(s).test@osboxes:~$ ./script3.shEnter the name of planet: JupiterThe Jupiter has 79 satellite(s).test@osboxes:~$ ./script3.shEnter the name of planet: Alpha555The Alpha555 has an unknown number of satellite(s).

Здесь в зависимости от введенного названия планеты скрипт выводит количество её спутников.
В case мы использовали выражение Mercury | Venus, где | означает логическое "ИЛИ" (в отличие от if, где используется ||), чтобы выводить "no" для Меркурия и Венеры, не имеющих спутников. В case также можно указывать диапазоны с помощью []. Например, скрипт для проверки принадлежности диапазону введенного символа будет выглядеть так:

#!/bin/bashecho -n "Enter key: "read -n 1 keyechocase "$key" in  [a-z]   ) echo "Lowercase";;  [A-Z]   ) echo "Uppercase";;  [0-9]   ) echo "Digit";;  *       ) echo "Something else";;esac

Мы проверяем символ на принадлежность одному из четырех диапазонов(английские символы в нижнем регистре, английские символы в верхнем регистре, цифры, все остальные символы). Результат теста:

test@osboxes:~$ ./a.shEnter key: tLowercasetest@osboxes:~$ ./a.shEnter key: PUppercasetest@osboxes:~$ ./a.shEnter key: 5Digittest@osboxes:~$ ./a.shEnter key: @Something else

Цикл может задаваться тремя разными способами:

Выполняется в интервале указанных значений (либо указанного множества):

for [ <условие> ] do <команды> done

Выполняется, пока соблюдается условие:

while [ <условие> ] do <команды> done

Выполняется, пока не перестанет соблюдаться условие:

until [ <условие> ] do <команды> done

Добавим в скрипт с планетами цикл с условием while и будем выходить из скрипта, если вместо имени планеты будет введено EXIT

#!/bin/bashPLANET="-"while [ $PLANET != "EXIT" ]do  echo -n "Enter the name of planet: "  read PLANET  if [ $PLANET != "EXIT" ]  then.    echo -n "The $PLANET has "    case $PLANET in      Mercury | Venus ) echo -n "no";;      Earth ) echo -n "one";;      Mars ) echo -n "two";;      Jupiter ) echo -n "79";;      *) echo -n "an unknown number of";;    esac  echo " satellite(s)."  fidone

Здесь мы также добавили условие, при котором оператор выбора будет выполняться только в случае, если введено не EXIT. Таким образом, мы будем запрашивать имя планеты и выводить количество её спутников до тех пор, пока не будет введено EXIT:

test@osboxes:~$ ./script4.shEnter the name of planet: EarthThe Earth has one satellite(s).Enter the name of planet: JupiterThe Jupiter has 79 satellite(s).Enter the name of planet: Planet123The Planet123 has an unknown number of satellite(s).Enter the name of planet: EXIT

Нужно отметить, что условие while [ $PLANET != "EXIT" ] можно заменить на until [ $PLANET == "EXIT" ]. == означает "равно", != означает "не равно".

Приведем пример циклов с указанием интервалов и множеств:

#!/bin/bashrm *.datecho -n "File count: "read countfor (( i=1; i<=$count; i++ ))do  head -c ${i}M </dev/urandom >myfile${i}mb.datdonels -l *.datecho -n "Delete file greater than (mb): "read maxsizefor f in *.datdo  size=$(( $(stat -c %s $f) /1024/1024))  if [ $size -gt $maxsize ]  then.    rm $f    echo Deleted file $f  fidonels -l *.datread

Сначала мы запрашиваем у пользователя количество файлов, которые необходимо сгенерировать (read count).

В первом цикле (for (( i=1; i<=$count; i++ ))) мы генерируем несколько файлов, количество которых задано в переменной count, которую введет пользователь. В команду head передаем количество мегабайт, считываемых из устройства /dev/random, чтение из которого позволяет получать случайные байты.

Символ < указывает перенаправление входного потока (/dev/urandom) для команды head.

Символ > указывает перенаправление выходного потока (вывод команды head -c ${i}M ) в файл, имя которого мы генерируем на основе постоянной строки с добавлением в неё значения переменной цикла (myfile${i}mb.dat).

Далее мы запрашиваем размер, файлы больше которого необходимо удалить.

Во втором цикле (for f in *.dat) мы перебираем все файлы .dat в текущей директории и сравниваем размер каждого файла со значением, введенным пользователем. В случае, если размер файла больше, мы удаляем этот файл.

В конце скрипта выводим список файлов .dat, чтобы отобразить список оставшихся файлов (ls -l *.dat). Результаты теста:

test@osboxes:~$ ./script5.shFile count: 10-rw-rw-r-- 1 test test 10485760 Nov  9 08:48 myfile10mb.dat-rw-rw-r-- 1 test test  1048576 Nov  9 08:48 myfile1mb.dat-rw-rw-r-- 1 test test  2097152 Nov  9 08:48 myfile2mb.dat-rw-rw-r-- 1 test test  3145728 Nov  9 08:48 myfile3mb.dat-rw-rw-r-- 1 test test  4194304 Nov  9 08:48 myfile4mb.dat-rw-rw-r-- 1 test test  5242880 Nov  9 08:48 myfile5mb.dat-rw-rw-r-- 1 test test  6291456 Nov  9 08:48 myfile6mb.dat-rw-rw-r-- 1 test test  7340032 Nov  9 08:48 myfile7mb.dat-rw-rw-r-- 1 test test  8388608 Nov  9 08:48 myfile8mb.dat-rw-rw-r-- 1 test test  9437184 Nov  9 08:48 myfile9mb.datDelete file greater than (mb): 5Deleted file myfile10mb.datDeleted file myfile6mb.datDeleted file myfile7mb.datDeleted file myfile8mb.datDeleted file myfile9mb.dat-rw-rw-r-- 1 test test 1048576 Nov  9 08:48 myfile1mb.dat-rw-rw-r-- 1 test test 2097152 Nov  9 08:48 myfile2mb.dat-rw-rw-r-- 1 test test 3145728 Nov  9 08:48 myfile3mb.dat-rw-rw-r-- 1 test test 4194304 Nov  9 08:48 myfile4mb.dat-rw-rw-r-- 1 test test 5242880 Nov  9 08:48 myfile5mb.dat

Мы создали 10 файлов (myfile1mb.dat .. myfile10mb.dat) размером от 1 до 10 мегабайт и далее удалили все файлы .dat размером больше 5 мегабайт. При этом для каждого удаляемого файла вывели сообщение о его удалении (Deleted file myfile10mb.dat). В конце вывели список оставшихся файлов (myfile1mb.dat .. myfile5mb.dat).

В следующей части мы рассмотрим функции, планировщик заданий cron, а также различные полезные команды.

Подробнее..
Категории: *nix , Linux , Unix , Ubuntu , Bash , Bash scripting , Ssh , Virtualbox , Debian , Shells

Основы Bash-скриптинга для непрограммистов. Часть 3

16.02.2021 20:15:11 | Автор: admin

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

Функции

Часто используемые, повторяющиеся блоки имеет смысл выделять в отдельные функции, чтобы при необходимости их запускать, передавая параметры.

Определение функции выглядит следующим образом:

<имя_функции>() {  <команды>  return <число>}funciton <имя_функции>() {  <команды>  return <число>}

Первый вариант ближе к синтаксису языка С и считается более переносимым, во втором варианте круглые скобки можно не указывать. Также может отсутствовать оператор return, если функция не возвращает значения.

Самый простой пример скрипта, содержащего объявление и вызов функции будет выглядеть так:

#!/bin/bashf() {  echo Test}f

Мы объявили функцию f, которая выводит слово Test, и затем вызвали её:

test@osboxes:~$ ./script6.shTest

Так же, как и скрипт, функция может принимать параметры и использовать их, ссылаясь по номеру ($1, $2, , $N). Вызов функции с параметрами в скрипте осуществляется так:

<имя функции> <параметр1> <параметр2> <параметрN>

Функция может возвращать результат своего выполнения (код завершения) в виде числового значения в диапазоне от 0 до 255. Принято считать, что если функция возвращает 0, то она выполнилась успешно, во всех остальных случаях значение содержит код ошибки. Чтобы получить код завершения функции в скрипте, необходимо обратиться к переменной $?. Добавив параметры и возвращаемое значение, получим следующий скрипт:

#!/bin/bashsumm() {  re='^[0-9]+$'  if ! [[ $1 =~ $re ]] ; then    return 1  elif ! [[ $2 =~ $re ]] ; then    return 2  else    s=$(($1 + $2))    return 0  fi}summ $1 $2case $? in 0) echo "The sum is: $s" ;; 1) echo "var1 is not a nubmer" ;; 2) echo "var2 is not a nubmer" ;; *) echo "Unknown error" ;;esac

Здесь мы создали функцию summ, которая принимает 2 параметра и с помощью регулярного выражения ^[0-9]+$ проверяет, является ли каждый из переданных параметров числом. В случае, если первый параметр не число, то код завершения функции будет 1, если второй параметр не число, то код завершения функции будет 2. Во всех остальных случаях функция вычисляет сумму переданных параметров, сохраняя результат в глобальной переменной s.

Скрипт вызывает функцию, передавая её на вход параметры, которые были переданы ему самому при вызове. Далее проверяется код завершения функции и выдается соответствующая ошибка, если код не равен 0, иначе выдается сумма, сохраненная в переменной s. Протестируем скрипт:

test@osboxes.org:~$ ./script7.sh abc 123var1 is not a nubmertest@osboxes.org:~$ ./script7.sh 234 defvar2 is not a nubmertest@osboxes.org:~$ ./script7.sh 10 15The sum is: 25

По умолчанию переменные объявляются глобальными, т.е. видны в любом блоке скрипта. Переменные, объявленные как локальные, имеют ограниченную область видимости, и видны только в пределах блока, в котором они были объявлены. В случае с функцией это означает, что локальная переменная "видна" только в теле функции, в которой она была объявлена.

Для того, чтобы объявить переменную локальной, используется слово local, например local loc_var=123. Важно отметить, все что переменные, объявляемые в теле функции, считаются необъявленными до тех пор, пока эта функция не будет вызвана.

Объединим все воедино, создав на основе рассмотренных ранее структур следующий скрипт:

#!/bin/bashclearFiles() {  rm *.dat  if [ $? -eq 0 ]  then    echo Files deleted  fi}genFiles() {  for (( i=1; i<=$1; i++ ))  do    head -c ${i}M </dev/urandom >myfile${i}mb.dat  done  ls -l *.dat}delFiles() {for f in *.dat  do    size=$(( $(stat -c %s $f) /1024/1024 ))    if [ $size -gt $1 ]    then      rm $f      echo Deleted file $f    fi  done  ls -l *.dat}showWeather() {  curl -s "https://weather-broker-cdn.api.bbci.co.uk/en/observation/rss/$1" | grep "<desc" | sed -r 's/<description>//g; s/<\/description>//g'}menu() {  clear  echo 1 - Delete all .dat files  echo 2 - Generate .dat files  echo 3 - Delete big .dat files  echo 4 - List all files  echo 5 - Planet info  echo 6 - Show weather  echo "x/q - Exit"  echo -n "Choose action: "  read -n 1 key  echo}while truedo  case "$key" in    "x" | "q" | "X" | "Q") break ;;    "1")      clearFiles      read -n 1    ;;    "2")      echo -n "File count: "      read count      genFiles $count      read -n 1    ;;    "3")      echo -n "Delete file greater than (mb): "      read maxsize      delFiles $maxsize      read -n 1    ;;    "4")      ls -la      read -n 1    ;;    "5")      ./script4.sh      read -n 1    ;;    "6")      echo -n "Enter city code: " # 524901 498817 5391959      read citycode      showWeather $citycode      read -n 1    ;;  esac  menudone

В данном скрипте мы объявили 5 функций:

  • clearFiles

  • genFiles

  • delFiles

  • showWeather

  • menu

Далее реализован бесконечный цикл с помощью оператора while с условием true, в который вложен оператор выбора в зависимости от нажатой клавиши, а также вызов функции menu для отображения списка доступных действий. Данный скрипт в интерактивном режиме позволяет выполнить следующие действия:

  • Удалить все файлы .dat в текущей директории

  • Создать указанное количество файлов

  • Удалить файлы больше определенного размера

  • Вывести список всех файлов текущей директории

  • Запустить скрипт, выдающий информацию о планетах

  • Отобразить погоду по коду указанного города

Не будем подробно разбирать все строки кода, скажем только, что в скрипте демонстрируется вызов другого скрипта, получение информации из интернет, и её парсинг (выделение нужной информации), команда break для выхода из цикла и ряд других возможностей. Предлагаю желающим самостоятельно протестировать скрипт, посмотреть, какие могут быть ошибки при его работе и при вводе различных значений, какие проверки можно добавить, и что можно улучшить. Приведем результаты тестирования:

test@osboxes.org:~$ ./script8.sh1 - Delete all .dat files2 - Generate .dat files3 - Delete big .dat files4 - List all files5 - Planet info6 - Show weatherx/q - ExitChoose action: 4total 40drwxr-xr-x 2 test test 4096 Feb 16 15:56 .drwxr-xr-x 6 root root 4096 Feb 16 15:54 ..-rw------- 1 test test   42 Feb 16 15:55 .bash_history-rw-r--r-- 1 test test  220 Feb 16 15:54 .bash_logout-rw-r--r-- 1 test test 3771 Feb 16 15:54 .bashrc-rw-r--r-- 1 test test  807 Feb 16 15:54 .profile-rw-r--r-- 1 test test 1654 Feb 16 12:40 input.xml-rwxr-xr-x 1 test test  281 Feb 16 14:02 script4.sh-rwxr-xr-x 1 test test  328 Feb 16 13:40 script7.sh-rwxr-xr-x 1 test test 1410 Feb 16 15:24 script8.sh
1 - Delete all .dat files2 - Generate .dat files3 - Delete big .dat files4 - List all files5 - Planet info6 - Show weatherx/q - ExitChoose action: 2File count: 8-rw-rw-r-- 1 test test 1048576 Feb 16 16:00 myfile1mb.dat-rw-rw-r-- 1 test test 2097152 Feb 16 16:00 myfile2mb.dat-rw-rw-r-- 1 test test 3145728 Feb 16 16:00 myfile3mb.dat-rw-rw-r-- 1 test test 4194304 Feb 16 16:00 myfile4mb.dat-rw-rw-r-- 1 test test 5242880 Feb 16 16:00 myfile5mb.dat-rw-rw-r-- 1 test test 6291456 Feb 16 16:00 myfile6mb.dat-rw-rw-r-- 1 test test 7340032 Feb 16 16:00 myfile7mb.dat-rw-rw-r-- 1 test test 8388608 Feb 16 16:00 myfile8mb.dat
1 - Delete all .dat files2 - Generate .dat files3 - Delete big .dat files4 - List all files5 - Planet info6 - Show weatherx/q - ExitChoose action: 3Delete file greater than (mb): 5Deleted file myfile6mb.datDeleted file myfile7mb.datDeleted file myfile8mb.dat-rw-rw-r-- 1 test test 1048576 Feb 16 16:00 myfile1mb.dat-rw-rw-r-- 1 test test 2097152 Feb 16 16:00 myfile2mb.dat-rw-rw-r-- 1 test test 3145728 Feb 16 16:00 myfile3mb.dat-rw-rw-r-- 1 test test 4194304 Feb 16 16:00 myfile4mb.dat-rw-rw-r-- 1 test test 5242880 Feb 16 16:00 myfile5mb.dat
1 - Delete all .dat files2 - Generate .dat files3 - Delete big .dat files4 - List all files5 - Planet info6 - Show weatherx/q - ExitChoose action: 1Files deleted
1 - Delete all .dat files2 - Generate .dat files3 - Delete big .dat files4 - List all files5 - Planet info6 - Show weatherx/q - ExitChoose action: 5Enter the name of planet: MarsThe Mars has two satellite(s).
1 - Delete all .dat files2 - Generate .dat files3 - Delete big .dat files4 - List all files5 - Planet info6 - Show weatherx/q - ExitChoose action: 6Enter city code: 524901    Latest observations for Moscow from BBC Weather, including weather, temperature and wind information      Temperature: -11C (11F), Wind Direction: Northerly, Wind Speed: 0mph, Humidity: 84%, Pressure: 1018mb, , Visibility: Moderate

Примечание: для тестирования работы с данными из Интернет (пункт 6 в меню выбора скрипта) может потребоваться установка curl, это можно сделать командой sudo apt install curl.

Планировщик заданий cron

В случае, когда есть необходимость периодического запуска скриптов, полезно использовать планировщик cron, он позволяет задать расписание запуска скрипта и не требует присутствия администратора.

Просмотр заданий пользователя выполняется командой crontab l. Для редактирования и создания новых задания используется команда crontab e. Строки для запуска команд планировщика в файле конфигурации cron имеют следующий формат:

m h dom mon dow command parameters

Где m минута, h час, dom день месяца, mon месяц, dow день недели, command команда, parameters список параметров. Наглядно этот формат можно представить так:

Например, для того, чтобы в 10 и 30 минут каждого часа каждый день месяца весь год по будням запускать команду, нужно указать следующее:

10,30 * * * 1-5 command parameter1 parameter2

Более простой пример, каждые 15 минут выполнять команду:

*/15 * * * * command

Создадим скрипт для резервного копирования домашней директории пользователя, который будет создавать новый файл бэкапа при каждом запуске:

#!/bin/bashUSER=`whoami`BACKUP_DIR=/tmp/backup_${USER}BACKUP_FILE=${USER}_$(date +%Y%m%d%M%H%S).tgzmkdir -p $BACKUP_DIRcd /tar -zcf $BACKUP_DIR/$BACKUP_FILE home/$USER

Поставим скрипт на выполнение каждый день в 22:00, выполнив команду crontab -eи добавив с помощью открывшегося редактора строку:

00 22 * * * ./backup_home.sh

Проверить, что задача добавлена в планировщик, можно командой crontab -l:

test@osboxes.org:~$ crontab -l00 22 * * * ./backup_home.sh

В результате каждый день в 22:00 будет создаваться резервная копия домашней директории пользователя (в приведенном примере для демонстрации запуск скрипта выполняется каждую минуту):

test@osboxes.org:~$ cd /tmp/backup_test/test@osboxes:/tmp/backup_test$ lltotal 80drwxrwxr-x  2 test test 4096 Feb 16 16:38 ./drwxrwxrwt 17 root root 4096 Feb 16 16:30 ../-rw-rw-r--  1 test test 4431 Feb 16 16:30 test_20210216301601.tgz-rw-rw-r--  1 test test 4431 Feb 16 16:31 test_20210216311601.tgz-rw-rw-r--  1 test test 4431 Feb 16 16:32 test_20210216321601.tgz-rw-rw-r--  1 test test 4431 Feb 16 16:33 test_20210216331601.tgz-rw-rw-r--  1 test test 4431 Feb 16 16:34 test_20210216341601.tgztest@osboxes:/tmp/backup_test$

Нужно отметить, что директория /tmp в примере использована исключительно для тестов, т.к. она предназначена для хранения временных файлов, и использовать её для хранения резервных копий нельзя. Правильное место размещения бэкапов может подсказать системный администратор.

Список полезных команд

Список встроенных команд интерпретатора: help
Помощь по команде: <команда> --help
Мануал по команде: man <команда>
Версия команды: <команда> --version
Список доступных оболочек: cat /etc/shells
Список пользователей и их оболочек: cat /etc/passwd
Текущая директория: pwd
Список файлов текущей директории: ls -la
Текущий пользователь: id
Переменные среды: set
Версия ОС: cat /etc/os-release
Версия ядра: uname -a
Получить привилегии суперпользователя: sudo su -
Установка программы в Debian: apt install mc
Посмотреть утилизацию(загрузку): top
Свободное место: df -h
Сколько занимает директория: du -ks /var/log
Конфигурация сетевых интерфейсов: ifconfig -a
Объем оперативной памяти: free -m
Информация о блочных устройствах(дисках): lsblk
Информация о процессорах: cat /proc/cpuinfo
Список установленных пакетов: apt list --installed
Список и статус сервисов: service --status-all
Перезапуск сервиса: service apache2 restart
Скачать файл: wget https://www.gnu.org/graphics/gplv3-with-text-136x68.png
Получить веб-страницу по URL: curl https://www.google.com
Показать задания планировщика: crontab -l
Редактировать задания планировщика: crontab -e
Вывести новые сообщения в системном логе: tail -f /var/log/syslog
Подсчитать количество строк в выводе команды: <команда> | wc -l
Изменить права доступа к файлу (разрешить выполнение всем): chmod a+x <файл>
Список процессов: ps -ef
Проверить, запущен ли процесс: ps -ef | grep <процесс>
Перейти в предыдущий каталог: cd -
Завершить процесс (сигнал kill): kill -9
Удаление файла: rm <имя файла>
Удаление директории: rm -rf <имя директории>
Редактировать файл: nano <имя_файла>
Топ 10 процессов по использованию памяти: ps aux | awk '{print $6/1024 " MB\t\t" $11}' | sort -nr | head

Полезные ссылки

Руководство по bash: GNU Bash manual
Расширенное руководство по Bash: Advanced Bash-Scripting Guide
Статья на Википедии: Bash
Описание команд и утилит оболочки bash: SS64
Часто задаваемые вопросы о Debian GNU/Linux: Debian FAQ

Заключение

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

На этом пока все, надеюсь, было интересно!
Какие другие полезные команды вы знаете и используете в работе?
Какие интересные конструкции приходилось использовать?
Какие задачи решали?
Всем удачного скриптинга, делитесь мнениями в комментариях!

Подробнее..
Категории: *nix , Linux , Unix , Ubuntu , Bash , Bash scripting , Ssh , Virtualbox , Debian , Shells

Из песочницы Автоматизация системных тестов на базе QEMU (Часть 12)

23.09.2020 12:10:47 | Автор: admin

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


Статья предполагает наличие следующих навыков у читателя:


  • Уверенное пользование ОС семейства Linux;
  • Базовое понимание принципов виртуализации;
  • Знакомство с гипервизором QEMU и графическим клиентом virt-manager

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


Дисклеймер

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


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


Что такое системное тестирование


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


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


  • Вид и версия ОС;
  • Разные настройки ОС (например, в области разграничения прав доступа);
  • Наличие или отсутствие определенных программ в ОС (в т.ч. драйверов);
  • Наличие сети и других компьютеров в ней;
  • Количество оперативной памяти или определённая архитектура процессора.

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


Почему именно виртуалки?


Может возникнуть вопрос, а зачем использовать именно виртуалки? Ведь для воспроизведения окружения для программы можно использовать и контейнеры (например, если Вас интересует поведение программы в связке с другими программами). И действительно, для контейнеров существует несколько решений по автоматизации тестов. Однако, существуют классы сценариев, которые нельзя протестировать с помощью контейнеров:


  1. Нельзя протестировать приложения не под Linux, а также гетерогенные стенды из нескольких машин;
  2. Нельзя протестировать GUI (или даже псевдо-GUI);
  3. Нельзя протестировать драйвера и работу с оборудованием.

Автоматизация пунктов 1 и 2 потребует от нас существенной подготовки, поэтому оставим их для одной из следующих статей. А вот для пункта 3 вполне хватит той информации, которую Вы узнаете из этой статьи. В этой статье (во второй её части) мы как раз продемонстрируем автоматизацию тестирования приложения, которое взаимодействует с оборудованием напрямую, в обход операционной системы.


Какой выбрать гипервизор?


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


Какие действия мы хотим автоматизировать?


Для того, чтобы автоматизировать системные тесты, нам нужно решить как минимум следующие вопросы:


  1. Как автоматически создать виртуалку;
  2. Как "раскатать" и настроить ОС;
  3. Как научиться управлять виртуалкой после её установки. В частности, нас интересует, как запускать процессы внутри виртуалки и копировать на неё файлы.

Заметка про тестирование методом чёрного ящика

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


Что ж, погнали!


Создаем виртуалку


Конечно, я думаю все пробовали создавать виртуальные машины вручную. Для этого в графическом интерфейсе рисуются всякие диалоговые окна, где надо выбрать конфигурацию, диски, сетевые адаптеры и прочее прочее. Так вот, для гипервизора QEMU всё это можно сделать и в консольном режиме с помощью утилиты virt-install. Давайте посмотрим как будет выглядеть простейшее создание виртуальной машины с помощью этой утилиты:


virt-install \    --name my_super_vm \    --ram 1024 \    --disk my_super_vm.qcow2,size=8 \    --cdrom /path/to/ubuntu_server.iso

Вот такой простой командой мы можем создать виртуальную машину с именем my_super_vm, 1024 Мегабайтами оперативной памяти, новым диском my_super_vm.qcow2 размером 8 Гигабайт. В виртуальном CD-приводе такой машины будет смонтирован установочный образ ubuntu_server.iso (конечно, этот образ надо предварительно скачать), который, как обычно, нужен для установки ОС на свежесозданную виртуалку.


Впрочем, если Вы выполните такую команду, то увидите как у вас запустилось графическое окно с VNC-клиентом, который подключается к виртуальной консоли виртуальной машины. В этом окне Вы увидите начало установки Ubuntu Server 18.04. Конечно, реальный человек бы смог протыкать несколько клавиш на клавиатуре и установить Ubuntu Server, но мы лишены такой роскоши, ведь мы работаем исключительно консольными командами.


Но наше положение не такое уж и безвыходное, ведь установить ОС на виртуальную машину можно и по-другому.


Создаём диск из шаблона


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


Вызов команды virt-install, приведённый выше, выполняет сразу два действия: он создаёт одновременно и машину и диск. Из-за может сложиться неверное впечатление, что эти сущности неотделимы друг от друга.


Но на самом деле никто не мешает нам отдельно создать виртуальный диск, а затем подключить его к виртуальной машине. Вы можете подготовить свой образ диска, но в этой статье мы для простоты воспользуемся уже готовым. Проект libguestfs поддерживает в свободном доступе целый репозиторий таких образов. Кроме того, в рамках этого же проекта существует утилита virt-builder, которая позволяет легко скачать нужный образ диска и доработать его "напильником" под наши нужды.


Несколько слов про проект libguestfs и почему стоит обратить на него внимание

В первую очередь Libguestfs это программная библиотека, которая позволяет легко манипулировать содержимым образом виртуальных дисков. Под манипуляцией подразумеваются такие операции как форматирование дисков, копирование файлов на диск и из него и так далее. Кроме того, в рамках проекта реализован целый набор утилит на все случаи жизни, которые используют различные возможности библиотеки. Среди них есть очень простые утилиты (в стиле Unix-way), которые выполняют всего одно действие, например virt-copy-in. С другой стороны есть команды-комбайны, которые умеют много всего и сразу, такие как virt-builder.


Итак, что же нам позволяет сделать virt-builder? С помощью этой утилиты мы можем на лету сформировать уже "подготовленный" диск с уже установленной Ubuntu Server. Сделать это можно так:


virt-builder ubuntu-18.04 \    --format qcow2 \    --output my_super_disk.qcow2

Что же здесь происходит? Мы говорим, что хотели бы создать диск в формате qcow2 (можно выбрать другой) и использовать в качестве основы шаблон ubuntu-18.04, который хранится в репозитории шаблонов в проекте libguestfs. Команда virt-builder скачает этот шаблон из Интернета и затем на его основе сгенерирует итоговый диск, в котором теперь будет установлен Ubuntu Server!


Кешируемость шаблонов

Приятной особенностью утилиты virt-builder является то, что шаблон диска кешируется после первого скачивания, поэтому последующие создания дисков будут проходить гораздо быстрее


Ну а теперь нам остаётся создать виртуальную машину, и указать в качестве диска (импортировать) подготовленный образ my_super_disk.qcow2:


virt-install \    --import \    --name my_super_vm \    --ram 1024 \    --disk my_super_vm.qcow2

Обратите внимание, что исчез параметр --cdrom, он нам больше не нужен. Также добавился параметр --import. Этот параметр указывает, что виртуалка будет загружаться не с cdrom, а с виртуального диска (то есть это влияет на Bios Boot Options виртуальной машины). Ну а т.к. диск у нас теперь содержит установленный Ubuntu Server, такая загрузка пройдёт успешно.


Попробуйте выполнить эти команды и создать таким образом виртуальную машину my_super_vm. Вы сможете убедиться, что Ubuntu Server 18.04 действительно уже установлен и успешно загружается.


Соединяем сетью виртуалку и хост


Вот так вот мы практически незаметно научились автоматически устанавливать виртуалки и даже раскатывать на них готовую ОС. Теперь настало время поговорить о канале управления виртуалкой после её установки и настройки.


Ещё раз хотелось бы отметить, что эталоном автоматизации системных тестов должна выступать автоматизация действий человека: как он нажимает на клавиши, кликает мышкой и совершает другие действия. И ещё раз напомним, что это очень сложный (хоть и правильный) путь. Мы же воспользуемся тем фактом, что нам не требуется взаимодействовать с GUI, нам пока вполне хватит возможности выполнения произвольных bash-команд на гостевой системе.


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


Альтернативные подходы

Не всегда канал управления виртуалкой через ssh является приемлемым вариантом. Такое бывает, если Вы тестируете приложение, которое каким-то образом завязано на сетевую подсистему. Например, в случае если вы как раз таки и разрабатываете ssh-сервер. Вы же не хотите, чтобы Ваши тесты полагались на работоспособность ещё неготовой программы? Либо Вы хотите проверить, как поведёт себя Ваша программа на виртуалке, где вообще нет сетевых интерфейсов.


В качестве альтернативы можно управлять гостевой системой через последовательный порт. При этом последовательный порт образует как бы трубу (pipe), проложенную между хостом и виртуалкой. Со стороны Linux-хоста эта труба видна как unix-socket, а со стороны гостевой системы как последовательное устройство. Всё, что отправляется в один конец трубы, тут же появляется из другого. Соответственно, как и в случае с ssh, на гостевой системе должен работать некий сервер, ожидающий получения данных из последовательного порта. Примером такого сервера может служить qemu-guest-agent.


В случае, если Вы работаете с гипервизором Hyper-V, то помимо последовательного порта можно попробовать воспользоваться механизмом KVP (Key-Value Pairs) или Hyper-V Sockets.


Для этого нам потребуется сделать несколько манипуляций:


  1. Создать виртуальную сеть между хостом и виртуалкой;
  2. Настроить сетевой интерфейс на виртуалке;
  3. Задать пароль от пользователя root на виртуалке;
  4. Подредактировать SSH-настройке на виртуалке, чтобы можно было соединяться по SSH от имени рута с помощью пароля.

По поводу ssh без пароля

Лучше, конечно, было бы настроить вход по ключу. Это позволило бы немного упростить финальный скрипт. Единственная причина, почему я не стал так делать это скользкий момент, связанный с sudo. Но вообще принципиальных препятствий никаких нет.


Для начала займемся созданием виртальной сети между хостом и виртуалкой. Для этого нам надо сделать две простых операции:


  1. Создать виртуальную сеть;
  2. Подключить виртуалку к этой сети (хост уже будет подключен к сети по-умолчанию).

Итак, поехали.


Для создания виртуальной сети мы воспользуемся ещё одной утилитой virsh, которая работает поверх ещё одной замечательной библиотеки libvirt.


Несколько слов про libvirt

Libvirt это, опять же, программная библиотека, предназначенная для управления гипервизорами. Проект libvirt ставит перед собой задачу объять необъятное сделать интерфейс управления, подходящий сразу для всех гипервизоров. Задача благая, но уж слишком амбициозная. На данный момент гипервизоры, отличные от QEMU, поддерживаются довольно слабо. Однако, если Вам требуется сделать что-то именно с QEMU, то проект libvirt и утилита virsh, входящая в его состав, являются самым быстрым и удобным вариантом.


В libvirt для создания различных виртуальных объектов (дисков, сетей, виртуалок) используются XML-схемы. XML-схема для создания сети выглядит примерно так:


<network>    <name>net_for_ssh</name>    <bridge name='net_for_ssh'/>    <ip address='192.168.100.1' netmask='255.255.255.0'/></network>

Где 192.168.100.1 это адрес, присваиваемый сетевому интерфейсу, который будет автоматически создан на хосте и ассоциирован с новой виртуальной сетью.


Для того, чтобы создать сеть необходимо передать путь к файлу с xml схемой сети в следующую команду:


virsh net-define net_for_ssh.xml

После создания сеть создаётся в выключенном состоянии, поэтому её ещё надо включить:


virsh net-start net_for_ssh

Теперь надо подключить виртуалку к свежесозданной сети. Для этого при создании виртуалки необходимо добавить параметр --network:


virt-install \    --import \    --name my_super_vm \    --ram 1024 \    --disk my_super_vm.qcow2 \    --network network=net_for_ssh \    --noautoconsole

Обратите также внимание на аргумент --noautoconsole, который отключает автоматическое подключение к консоли виртуалки через VNC-клиент (впрочем, если Вам всё-равно хочется зайти посмотреть на виртуалку, Вы можете воспользоваться virt-manager).


Соединение готово, но пропинговать с хоста нашу виртуалку мы всё ещё не можем: сетевой интерфейс пока не настроен.


Настройка сетевого интерфейса на виртуалке


Как же мы будем настраивать интерфейс внутри виртуалки, если мы пока не можем выполнять на ней никаких команд (SSH-канал ведь ещё не настроен)? В этом нам снова поможет библиотека libguestfs и утилита virt-builder.


Дело в том, эта библиотека позволяет, в числе прочего, копировать файлы на виртуальный диск. Как мы знаем, в Ubuntu Server 18.04 за сетевые настройки отвечает netplan, и для того, чтобы сконфигурировать сетевой интерфейс, нам достаточно подложить специальный .yaml файлик в каталог /etc/netplan. И сделать это можно с помощью той же утилиты virt-builder с помощью параметра --copy-in:


Файл с настройками сетевого интерфейса netcfg_ssh.yaml
network:  version: 2  renderer: networkd  ethernets:    ens3:      addresses:        - 192.168.100.2/24

virt-builder ubuntu-18.04 \    --format qcow2 \    --output my_super_disk.qcow2 \    --copy-in netcfg_ssh.yaml:/etc/netplan/

И теперь при создании виртуального диска после раскатывания Ubuntu Server 18.04 из шаблона virt-builder дополнительно скопирует файл netcfg_ssh.yaml и подложит его в директорию /etc/netplan/ на файловой системе виртуального диска.


Теперь виртуальная машина должна пинговаться, проверим:


ping 192.168.100.2 -c5

Почти всё сделали, осталось лишь настроить SSH.


Настраиваем SSH на виртуалке


Для создания канала управления виртуалкой осталось сделать совсем чуть чуть:


  1. Прописать пароль для root-пользователя в виртуалке;
  2. Создать ключи на виртуалке для SSH-сервера чтобы он стартовал без ругани;
  3. Прописать в конфиг SSH-сервера возможность подключаться от имени рута с использованием пароля.

Начнём с пароля для root. Здесь нас снова выручает virt-builder, который, на самом деле, позволяет Вам делать с виртуальным диском поистине удивительные вещи, одна из которых прописать пароль для root-пользоавтеля:


virt-builder ubuntu-18.04 \    --format qcow2 \    --output my_super_disk.qcow2 \    --root-password password:1111 \    --copy-in netcfg_ssh.yaml:/etc/netplan/

Теперь осталось сгенерировать ключи для SSH и подправить конфиг. Для этого нам надо всего-то надо выполнить пару команд:


ssh-keygen -Ased -i \"s/.*PermitRootLogin.*/PermitRootLogin yes/g\" /etc/ssh/sshd_config

Вот только вопрос, а как же нам выполнить эти команды? Ведь virt-builder работает только с образами дисков, никаких виртуальных машин на этом этапе не создаётся. Однако, уvirt-builder есть ещё пара козырей в рукаве. Он позволяет запускать программы гостевой системы без запуска собственно гостевой системы с помощью параметра --run-command:


virt-builder ubuntu-18.04 \    --format qcow2 \    --output my_super_disk.qcow2 \    --root-password password:1111 \    --run-command "ssh-keygen -A" \    --run-command "sed -i \"s/.*PermitRootLogin.*/PermitRootLogin yes/g\" /etc/ssh/sshd_config" \    --copy-in netcfg_ssh.yaml:/etc/netplan/

А как это работает?

Хороший вопрос. Документация libguestfs ничего не говорит нам о деталях реализации этого механизма, а исходники довольно запутаны. Но похоже, что здесь используется User Space Linux Kernel. Это не контейнеры, но очень на них похоже. В любом случае можно сделать следующие выводы:


  • в параметре --run-command можно смело вызвать любой бинарник, который есть на диске гостевой системы
  • при этом можно смело пользоваться сетью, например устанавливать пакеты с помощью apt install

И теперь всё! Канал настроен! Можно было бы даже попробовать подключиться, если бы не одно "но". После выполнения команды virt-install виртуалка только только начинает включаться. Между моментом включения виртуалки и запуском на ней ssh сервера может пройти несколько секунд. Поэтому нам потребуется организовать незатейливый механизм ожидания ssh сервера:


#!/bin/bashSSH_CMD="sshpass -p 1111 ssh -o StrictHostKeyChecking=no"while ! $SSH_CMD root@192.168.100.2 echo Hello world from my super vm!do    echo "Waiting for client VM ..."    sleep 1done

Также, мне пришлось добавить параметр -o StrictHostKeyChecking=no к команде ssh для того, чтобы запрос на добавление сервера в список доверенных серверов не блокировал выполнение скрипта. Так же мне пришлось воспользоваться утилитой sshpass для того, чтобы автоматизировать ввод пароля.


Итоги


В первой части статьи мы пока не написали ни одного реального системного теста, зато познакомились с серьёзным арсеналом утилит по работе с виртуальными машинами, научились автоматически создавать виртуалки, раскатывать на них ОС, настраивать их, а также налаживать канал управления по SSH. С этим запасом знаний мы теперь можем смело переходить к самому главному и интересному: как же, всё-таки, автоматизировать системные тесты на виртуалках.

Подробнее..

Автоматизация системных тестов на базе QEMU (Часть 22)

26.09.2020 12:23:55 | Автор: admin

Это вторая часть статьи, посвященной автоматизации системного тестирования на основе виртуальных машин. Первую часть можно найти здесь: http://personeltest.ru/aways/habr.com/ru/post/520310/


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


Дисклеймер

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


Итак, в прошлой части мы запаслись внушительным арсеналом из навыков работы с виртуальными машинами из командной строки: научились устанавливать виртуалки, раскатывать на них ОС (на примере Ubuntu Server 18.04), соединять виртуалку с хостом по сети и даже организовывать канал управления через SSH. Всё это нам пригодится в этой статье, но прежде чем перейти к практике, нужно обсудить несколько вопросов.


Что же мы хотим получить?


Самый главный вопрос, на который нужно дать ответ это "Какой результат мы хотим получить?". Да, в прошлый раз мы много говорили об автоматизации установки, развертывания и настройки виртуалок, но в отрыве от конечной цели это всё не имеет большого смысла.


Лично для меня системные тесты "всё в одном" выглядят так: я скачиваю из VCS несколько небольших файликов (сам скрипт с запуском, плюс, возможно, несколько вспомогательных артефактов), подкладываю куда нужно тестируемую программу (в виде инсталлятора или пакета, например), нажимаю одну кнопку и иду пить кофе. Когда я возвращаюсь, я хочу либо увидеть что все тесты прошли, либо что такие-то тесты сломались. Я не хочу заниматься какой-либо настройкой стенда, не хочу разворачивать виртуалки или что-то там настраивать. Хочу скачать скрипт и воспользоваться им.


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


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


Что будем тестировать


Для статьи в качестве подопытной программы я выбрал интересную кандидатуру. Мы будем тестировать мини-фаервол, написанный с ипользованием библиотеки Data Plane Development Kit (DPDK). Если вы не знакомы с DPDK не пугайтесь, мы не собираемся его изучать, мы возьмём готовое приложение из тех примеров, которые поставляются вместе с DPDK. Приложение на DPDK идеально подходит к этой статье, потому что совершенно непонятно, как именно можно автоматизировать end-to-end тесты для таких приложений.


Несколько слов про DPDK и что в нем такого особенного

DPDK (Data Plane Development Kit) под этим громким названием скрывается просто набор библиотек, написанных на языке C. Эти библиотеки упрощают создание приложений, которые занимаются обработкой сетевого трафика. Примечательной особенностью этой библиотеки является тот факт, что она взаимодействует с сетевыми адаптерами напрямую. Обычно оборудованием управляет операционная система, она же выполняет приём и обработку сетевых пакетов. Платформа DPDK позволяет отодвинуть операционную систему в сторону и взять всё управление в свои руки. Зачем это нужно? Такой подход позволяет добиться впечатляющих показателей производительности. Дело в том, что ядро операционной системы, например Linux, выполняет с сетевыми пакетами очень много манипуляций, которые нам, возможно, и не нужны. Оно и понятно, ведь Linux он как швейцарский нож, подходит для решения практически любой задачи. Если же нужно решить всего одну относительно простую задачу и сделать это максимально эффективно, то DPDK будет хорошим выбором.


Само приложение имеет очень простой принцип работы:


  1. оно принимает сетевые пакеты с одного из сетевых интерфейсов;
  2. сопоставляет полученный пакет со списком правил фильтрации;
  3. если пакет попал под правило DROP то пакет отбрасывается;
  4. если пакет попал под правило ACCEPT то пакет выплёвывается из другого сетевого адаптера;

Соответственно, нам нужно проверить, что:


  • приложение устанавливается на ОС и успешно запускается;
  • правила фильтрации отрабатывают так, как и было задумано;

План работ


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


  1. Создать три виртуальных машины (client, middlebox, server), установить везде Ubuntu Server 18.04 (например);
  2. Создать две виртуальные сети: сеть между client и middlebox (назовём эту сеть для краткости net_1) и сеть между middlebox и server (назовём её net_2);
  3. Подключить машины к этим сетям;
  4. Настроить виртуальные машины, привести их в боевое состояние;
  5. Установить наше приложение-фаервол в машину middlebox;
  6. Прогнать сами тесты.

Вот все эти вещи мы и хотим автоматизировать в нашем скрипте. При этом добавим пару оговорок:


  1. Как мы помним из первой части статьи, для выполнения команд на виртуалках нам потребуется организовать SSH-канал управления между виртуалками и хостом, это будет один из пунктов, который нам нужно будет дополнительно сделать, хотя для ручного тестирования этот пункт явно необязателен;
  2. Хоть теоретически хост может связаться с виртуалкой по SSH используя виртуальные сети net_1 и net_2, но лучше использовать для этого отдельную сеть (назовём её net_for_ssh). Глобальных причин для этого две:

  • Т.к. мы тестируем фаерволл, то не хотелось бы, чтобы управляющий трафик оказывал какое-либо влияние на ход проведения тестов;
  • В случае, если что-то пойдет не так (например, фаерволл свалится), мы бы не хотели чтобы управление у виртуалки отваливалось.

За работу!


Для тестирования мини-фаерволла мы развернём такой стенд:




С учётом знаний, которые мы получили в предыдущей части статьи, автоматизировать развёртывание стенда совсем несложно:


run_tests.sh
#!/bin/bashset -euo pipefail# =======================================# Подготовка сети net_for_ssh# =======================================virsh net-define net_for_ssh.xmlvirsh net-start net_for_ssh# =======================================# Подготовка сети net_1# =======================================virsh net-define net_1.xmlvirsh net-start net_1# =======================================# Подготовка сети net_2# =======================================virsh net-define net_2.xmlvirsh net-start net_2# =======================================# Подготовка машины client# =======================================virt-builder ubuntu-18.04 \    --format qcow2 \    --output client.qcow2 \    --install wget \    --root-password password:1111 \    --run-command "ssh-keygen -A" \    --run-command "sed -i \"s/.*PermitRootLogin.*/PermitRootLogin yes/g\" /etc/ssh/sshd_config" \    --copy-in netcfg_client.yaml:/etc/netplan/virt-install \    --import \    --name client \    --ram 1024 \    --disk client.qcow2 \    --network network=net_for_ssh \    --network network=net_1,mac=52:54:56:11:00:00 \    --noautoconsole# =======================================# Подготовка машины middlebox# =======================================virt-builder ubuntu-18.04 \    --format qcow2 \    --output middlebox.qcow2 \    --install python,daemon,libnuma1 \    --root-password password:1111 \    --run-command "ssh-keygen -A" \    --run-command "sed -i \"s/.*PermitRootLogin.*/PermitRootLogin yes/g\" /etc/ssh/sshd_config" \    --copy-in netcfg_middlebox.yaml:/etc/netplan/virt-install \    --import \    --name middlebox \    --vcpus=2,sockets=1,cores=2,threads=1 \    --cpu host \    --ram 2048 \    --disk middlebox.qcow2 \    --network network=net_for_ssh \    --network network=net_1,model=e1000 \    --network network=net_2,model=e1000 \    --noautoconsole# =======================================# Подготовка машины server# =======================================virt-builder ubuntu-18.04 \    --format qcow2 \    --output server.qcow2 \    --install nginx \    --root-password password:1111 \    --run-command "ssh-keygen -A" \    --run-command "sed -i \"s/.*PermitRootLogin.*/PermitRootLogin yes/g\" /etc/ssh/sshd_config" \    --copy-in netcfg_server.yaml:/etc/netplan/virt-install \    --import \    --name server \    --ram 1024 \    --disk server.qcow2 \    --network network=net_for_ssh \    --network network=net_2,mac=52:54:56:00:00:00 \    --noautoconsole# =======================================# Убедимся, что наши машины запустились# и доступны для команд управления# =======================================SSH_CMD="sshpass -p 1111 ssh -o StrictHostKeyChecking=no"while ! SSH_CMD root@192.168.100.2 "echo Hello world from client!" echodo    echo "Waiting for client VM ..."    sleep 1donewhile ! SSH_CMD root@192.168.100.3 "echo Hello world from middlebox!" echodo    echo "Waiting for middlebox VM ..."    sleep 1donewhile ! SSH_CMD root@192.168.100.4 "echo Hello world from server!" echodo    echo "Waiting for server VM ..."    sleep 1done

Для запуска этого скрипта потребуются следующие артефакты:


net_for_ssh.xml
<network>    <name>net_for_ssh</name>    <bridge name='net_for_ssh'/>    <ip address='192.168.100.1' netmask='255.255.255.0'/></network>

net_1.xml
<network>    <name>net_1</name>    <bridge name='net_1'/>    <ip address='192.168.101.1' netmask='255.255.255.0'/></network>

net_2.xml
<network>    <name>net_2</name>    <bridge name='net_2'/>    <ip address='192.168.102.1' netmask='255.255.255.0'/></network>

netcfg_client.yaml
network:  version: 2  renderer: networkd  ethernets:    ens3:      addresses:        - 192.168.100.2/24    ens4:      addresses:        - 192.168.101.2/24      gateway4: 192.168.101.3

netcfg_middlebox.yaml
network:  version: 2  renderer: networkd  ethernets:    ens3:      addresses:        - 192.168.100.3/24

netcfg_server.yaml
network:  version: 2  renderer: networkd  ethernets:    ens3:      addresses:        - 192.168.100.4/24    ens4:      addresses:        - 192.168.102.2/24      gateway4: 192.168.102.3

Большая часть команд и приёмов Вам должна быть уже известна из первой части статьи, а мы лишь остановимся на некоторых интересных моментах:


  1. Мы используем параметр --install команды virt-builder, чтобы установить на виртуалки дополнительные пакеты. Это просто удобное сокращение для --run-command "apt install ...". Собственно, Вам даже не обязательно знать, какой пакетный менеджер работает на гостевой системе virt-builder сам разберётся. Для машины client мы устанавливаем пакеты wget, для server nginx (чтобы тестировать фаерволл с помощью http-запросов на сервер). Для middlebox мы устанавливаем зависимости, необходимые для настройки и запуска DPDK-приложений;
  2. Для машин client и server мы указываем, какие МАС-адреса нужно присвоить сетевым адаптерам, смотрящих в сторону фаерволла. Это пригодится нам при прогоне тестов;
  3. Для машины middlebox мы указываем топологию виртуального процессора (параметр --vcpus): нам требуется один CPU c двумя ядрами без поддержки технологии hyperthreading. Два ядра это минимальное количество ядер, необходимое для запуска DPDK приложения. Кроме того мы указываем параметр --cpu host, что означает, что процессор на вируалке должен иметь те же возможности, что и процессор на хостовой системе. Дело в том, что по-умолчанию QEMU создаёт виртуальный процессор, который не поддерживает даже SSE3 инструкции. А без этого, опять же, DPDK приложение не запустится.
  4. Также для машины middlebox мы указываем модель сетевых адаптеров, участвующих в машрутизации и фильтрации трафика: e1000. Та модель адаптера, которая создаётся по-умолчанию на данный момент не поддерживается библиотекой DPDK.

На текущий момент наш скрипт run_tests.sh сможет корректно отработать только один раз (или даже вообще ни разу, если Вы проделывали у себя шаги из первой части статьи). При повторном запуске у Вас будут возникать ошибки, связанные с двумя моментами:


  1. Нельзя повторно создать уже созданную сеть;
  2. Нельзя повторно создать уже созданную виртуальную машину.

Для того, чтобы нам было проще дорабатывать скрипт run_tests.sh, нам нужно создать скрипт, который будет удалять все наши наработки. Выглядеть этот скрипт будет так:


run_clean.sh
#!/bin/bashset -euo pipefail# =======================================# Удаление машины client# =======================================if virsh list --all | grep -q " client "; then    if virsh domstate client | grep -q "running"; then        virsh destroy client    fi    virsh undefine client --snapshots-metadata --remove-all-storagefi# =======================================# Удаление машины middlebox# =======================================if virsh list --all | grep -q " middlebox "; then    if virsh domstate middlebox | grep -q "running"; then        virsh destroy middlebox    fi    virsh undefine middlebox --snapshots-metadata --remove-all-storagefi# =======================================# Удаление машины server# =======================================if virsh list --all | grep -q " server "; then    if virsh domstate server | grep -q "running"; then        virsh destroy server    fi    virsh undefine server --snapshots-metadata --remove-all-storagefi# =======================================# Удаление сети net_for_ssh# =======================================if virsh net-list --all | grep -q " net_for_ssh "; then    if virsh net-list --all | grep " net_for_ssh " | grep -q " active "; then        virsh net-destroy net_for_ssh    fi    virsh net-undefine net_for_sshfi# =======================================# Удаление сети net_1# =======================================if virsh net-list --all | grep -q " net_1 "; then    if virsh net-list --all | grep " net_1 " | grep -q " active "; then        virsh net-destroy net_1    fi    virsh net-undefine net_1fi# =======================================# Удаление сети net_2# =======================================if virsh net-list --all | grep -q " net_2 "; then    if virsh net-list --all | grep " net_2 " | grep -q " active "; then        virsh net-destroy net_2    fi    virsh net-undefine net_2fi

Вот этот скрипт, в отличие от run_tests.sh, будет отрабатывать всегда. Основные моменты в этом скрипте:


  1. Удаляет машины или сети только если они созданы (существование виртуалок проверяется с помощью команды virsh list --all, а существование сетей с помощью команды virsh net-list -all);
  2. Чтобы удалить машину/сеть, сначала нужно убедиться, что эта машина/сеть выключена, иначе удалить её не получится;
  3. Виртуалки удаляются вместе со снепшотами (параметр --snapshots-metadata) и подключенными дисками (параметр --remove-all-storage).

Пока что для повторного запуска run_tests.sh нужно запускать run_clean.sh. В дальнейшем мы доработаем run_tests.sh так, чтобы он отрабатывал и без помощи run_clean.sh.


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

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


Здесь стоит упомянуть один неприятный момент. Когда мы запускаем SSH-команды SSH добавляет публичный ключ виртуалки в файл ~/.ssh/known_hosts. Если мы удалим виртуалки, создадим её заново и попробуем поключится к ней по SSH он откажется подключаться, думая, что Вас кто-то хочет обмануть. Мы можем избежать этой ситуации подкорректировав нашу переменную SSH_CMD:


SSH_CMD="sshpass -p 1111 ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR"

Я добавил параметр -o UserKnownHostsFile=/dev/null, чтобы запуск скрипта тестового сценария никак не влиял на хостовую машину.


Немного об организации тестов


В прошлой части я упомянул, что идеальный вариант автоматизации системных тестов это автоматизация действий человека при работе за компьютером. Но работу пользователя с Ubuntu Server (без GUI) можно представить как последовательность выполнения bash-команд, чем мы и воспользовались.


В рамках тестов мы собираемся запускать относительно много bash-команд, причём на разных виртуалках. Давайте продумаем пару моментов, прежде чем приступать к делу.


Во-первых, сделаем очень простую, но в тоже время очень полезную вещь:


EXEC_CLIENT="$SSH_CMD root@192.168.100.2"EXEC_MIDDLEBOX="$SSH_CMD root@192.168.100.3"EXEC_SERVER="$SSH_CMD root@192.168.100.4"

Мы всего лишь объявили три переменные. Но это уже позволяет писать более читабельный скрипт. Например:


$EXEC_CLIENT echo hello from client$EXEC_SERVER echo hello from server

Во-вторых, что насчёт многострочных команд? Здесь нам поможет такая возможность bash-интерпретатора как Heredoc. Heredoc позволяет нам записывать тестовые сценарии в следующим виде:


$EXEC_CLIENT << EOF    echo hello from client    ls    pwdEOF$EXEC_MIDDLEBOX << EOF    echo hello from middlebox    some_another_commandEOF

Здесь EOF это всего лишь последовательность символов, которой мы обозначаем начало и конец строки. Вместо EOF Вы можете использовать другую последовательность символов, главное, чтобы она была одинаковой в начале и в конце строки.


Когда вы используете мультистрочные команды, скорее всего вы захотите добавить в начало строчку set -xeuo pipefail. Например:


$EXEC_MIDDLEBOX << EOF    set -xeuo pipefail    command1    command2 | command3EOF

Напомню на всяких случай, что означает эта команда:


  • параметр -x заставляет bash-интерпретатор печатать каждую команду перед тем как её выполнить;
  • параметр -e останавливает выполнение всего скрипта, если хотя бы одна из команд завершилась с ошибкой;
  • параметр -u останавливает выполнение всего скрипта, если Вы обратились в нём к несуществующей переменной;
  • парамерт -o pipeline останавливает конвейер, если какая-то команда в его составе завершилась с ошибкой.

Таким образом, если что-то пойдёт не так в Вашем тестовом сценарии Вы сразу об этом узнаете.


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


$EXEC_MIDDLEBOX << EOF    set -xeuo pipefail    command1    ! command2EOF

Вышеприведенный скрипт выполнится успешно только в одном единственном случае: если command1 вернёт 0, а command2 вернёт значение, отличное от нуля.


Переходим к самим тестам


В рамках статьи мы напишем три системных теста:


  1. Проверим, что приложение успешно устанавливается и запускается;
  2. Проверим, что приложение в принципе машрутизирует трафик;
  3. Проверим, что приложение может блокировать определенный вид трафика.

Давайте взглянем на первый тест:


$SCP_CMD l3fwd-acl-1.0.0.deb root@192.168.100.3:~$EXEC_MIDDLEBOX << EOF    set -xeuo pipefail    dpkg -i l3fwd-acl-1.0.0.deb    echo 256 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages    mkdir -p /mnt/huge    mount -t hugetlbfs nodev /mnt/huge    modprobe uio_pci_generic    dpdk-devbind --bind=uio_pci_generic ens4 ens5    echo "R0.0.0.0/0 192.168.102.0/24 0 : 65535 0 : 65535 0x0/0x0 1" > /etc/rule_ipv4.db    echo "R0.0.0.0/0 192.168.101.0/24 0 : 65535 0 : 65535 0x0/0x0 0" >> /etc/rule_ipv4.db    echo "R0:0:0:0:0:0:0:0/0 0:0:0:0:0:0:0:0/0 0 : 65535 0 : 65535 0x0/0x0 0" > /etc/rule_ipv6.db    daemon --name l3fwd --unsafe --output /var/log/l3fwd -- l3fwd-acl \        -l 1 \        -n 4 \        -- \        -p 0x3 \        -P \        --config="(0,0,1),(1,0,1)" \        --rule_ipv4="/etc/rule_ipv4.db" \        --rule_ipv6="/etc/rule_ipv6.db"EOF

О содержимом скрипта

Вот развёрнутый список того, что делает этот скрипт (для интересующихся):


  1. Устанавливает скопированный на виртуалку deb-пакет;
  2. Резервирует и монтирует 256 гигантских страниц по 2 мегабайта (DPDK-приложения по-умолчанию используют гигантские страницы для размещения своих данных);
  3. Подгружает poll-mode драйвер uio_pci_generic (он поставляется в составе Ubuntu Server). Этот драйвер необходим для того, чтобы DPDK приложение смогло через него получить прямой доступ к сетевым адаптерам;
  4. Отвязывает интерфейсы ens4 (в сторону клиента) и ens5 (в сторону сервера) от стандартного сетевого драйвера и привязывает их к драйверу uio_pci_generic;
  5. Создаёт файл rule_ipv4.db с правилами маршрутизации для пакетов IPv4 и кладёт туда два правила: все пакеты с адресом назначения 192.168.102.0/24 отправлять на порт 1 (то есть все пакеты от клиента к серверу надо отправлять на порт, который смотрит в сторону сервера), а все пакеты с адресом назначения 192.168.101.0/24 отправлять на порт 0 (то есть в сторону клиента);
  6. Также создаёт аналогичный файл для rule_ipv6.db, но туда кладёт одно правило по умолчанию "Все пакеты отправляй на порт 0". Реальных IPv6 пакетов генерироваться в рамках тестов не будет, но без этого файла DPDK приложение запускаться не будет;
  7. Запускает тестируемое l3fwd приложение и отправляет его работать в фон с помощью daemon. Подробно останавливаться на формате запуска не будем, но для интересующихся этот формат можно посмотреть на странице с документацией l3fwd: https://doc.dpdk.org/guides/sample_app_ug/l3_forward.html

Посмотрите, какие манипуляция с окружением нам приходится делать чтобы проверить приложение DPDK с точки зрения конечного пользователя:


  1. Установить пакет .deb в систему;
  2. Подправить параметры ядра;
  3. Смонтировать раздел к файловой системе;
  4. Загрузить модуль ядра;
  5. Привязать сетевые интерфейсы, участвующие в маршрутизации, к драйверу uio_pci_generic;
  6. Запустить приложение в фоне.

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


И этот лейтмотив только усиливается во втором тесте: вместо того, чтобы проверять работу DPDK-приложения с помощью фиксированного набора пакетов (как это можно было бы сделать в unit-тестах, например), мы будем проверять приложение так, как это бы сделал реальный человек: попробуем передать что-нибудь по сети с помощью реальных утилит и посмотрим на результат:


$EXEC_CLIENT arp -s 192.168.101.3 52:54:56:00:00:00$EXEC_SERVER arp -s 192.168.102.3 52:54:56:11:00:00$EXEC_CLIENT << EOF    set -xeuo pipefail    ping -c 5 192.168.102.2    wget --timeout=5 --tries=1 http://192.168.102.2EOF

о ситуации с ARP-записями

Перед тем, как пускать трафик, мы добавили две статические ARP-записи.
Зачем это нужно? Дело в том, что тестовое приложение l3fwd, взятое из примеров
библиотеки DPDK, настолько простое, что оно даже не обратывает протокол ARP.
Приложение l3fwd просто фильтрует трафик в соответствии с правилами, заданными
в файлах rule_ipv4.db и rule_ipv6.db, и кроме этого не делает больше ровным
счётом ничего: ни проверки чексуммы, ни фрагментации/дефрагментации пакетов, ни-че-го.
Это как раз один из способов достижения максимальной производительности: просто
не делать того, что конкретно Вам не нужно конкретно в Вашей ситуации.
Это приводит к тому, что сетевые пакеты пролетают сквозь машину middlebox
вообще без никаких изменений, хотя у него должны поменяться MAC-адреса
в Ethernet-заголовке (иначе client и server будут просто отпрасывать такие пакеты).
Мы можем закостылить эту пролему следующим образом: будем отправлять пакеты
с уже заранее изменённым destination MAC-адресом. Для этого здесь и используются
статические ARP-записи.


Третий же тест довольно очевиден и вытекает из первых двух:


# =======================================# Добавим правило, заврещающее tcp трафик# =======================================$EXEC_MIDDLEBOX << EOF    set -xeuo pipefail    daemon --name l3fwd --stop    # Это и есть запрещающее правило    echo "@0.0.0.0/0 0.0.0.0/0 0 : 65535 0 : 65535 0x06/0xff" > /etc/rule_ipv4.db    echo "R0.0.0.0/0 192.168.102.0/24 0 : 65535 0 : 65535 0x0/0x0 1" >> /etc/rule_ipv4.db    echo "R0.0.0.0/0 192.168.101.0/24 0 : 65535 0 : 65535 0x0/0x0 0" >> /etc/rule_ipv4.db    daemon --name l3fwd --unsafe --output /var/log/l3fwd -- l3fwd-acl \        -l 1 \        -n 4 \        -- \        -p 0x3 \        -P \        --config="(0,0,1),(1,0,1)" \        --rule_ipv4="/etc/rule_ipv4.db" \        --rule_ipv6="/etc/rule_ipv6.db"EOF# =======================================# Проверяем, что ping продолжает ходить,# а http трафик - перестал# =======================================$EXEC_CLIENT << EOF    set -xeuo pipefail    ping -c 5 192.168.102.2    ! wget --timeout=5 --tries=1 http://192.168.102.2EOF

Обратите внимание, что перед wget стоит восклицательный знак: это означает, что тест будет успешен, только если команда wget завершиться с ошибкой.


Дорабатываем скрипт run_tests.sh


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


Давайте поразмышляем вот над чем: весь скрипт можно условно разделить на две больших секции: до копирования сборки DPDK-приложения на middlebox и после. В чём разница? Дело в том, что по отношению к стенду (системе из виртуальных машин) сборка DPDK-приложения является внешней переменной Х. Мы заранее знаем, как поведёт себя скрипт до появления этой внешней переменной, но мы не знаем насколько успешно пройдут тесты после внедрения внешнего неизвестного элемента. Вдруг сборка окажется неработоспособной и тесты свалятся?


Поэтому мы можем внести элемент кеширования в наш скрипт. Как насчёт фиксации результатов работы скрипта на момент ровно перед появлением внешней переменной, и отката к этому состоянию при повторном запуске скрипта? Ведь мы знаем, что участок скрипта до появления неопределённого элемента должен всегда выполняться одинаково. А фиксацию можно сделать очень просто: мы можем создать снепшот, например с названием init, для каждой виртуальной машины после её первоначальной настройки.


Осталось лишь немного подправить скрипт чтобы он учитывал эту новую логику:


# =======================================# Подготовка машины client# =======================================if ! virsh list --all | grep -q " client "then    virt-builder ubuntu-18.04 \        --format qcow2 \        --output client.qcow2 \        --hostname client \        --install wget,net-tools \        --root-password password:1111 \        --run-command "ssh-keygen -A" \        --run-command "sed -i \"s/.*PermitRootLogin.*/PermitRootLogin yes/g\" /etc/ssh/sshd_config" \        --copy-in netcfg_client.yaml:/etc/netplan/    virt-install \        --import \        --name client \        --ram 1024 \        --disk client.qcow2 \        --network network=net_for_ssh \        --network network=net_1,mac=52:54:56:11:00:00 \        --noautoconsole    virsh snapshot-create-as client --name initelse    virsh snapshot-revert client --snapshotname initfi

Теперь если машина уже существует, то скрипт вместо её создания будет откатывать её к снепшоту init.


Другие снепшоты

Конечно, никто не запрещает создавать и другие снепшоты уже во время самих тестов, чтобы при необходимости откатываться к ним: например, мы могли бы создать снепшот после установки .deb-пакета и базовых настроек DPDK, после чего откатываться к нему перед началом второго теста. Здесь лучше всего действовать по ситуации.


Для сетей же достаточно просто добавить проверку, чтобы создавать их только в том случае, если они не существуют:


# =======================================# Подготовка сети net_1# =======================================if ! virsh net-list --all | grep -q " net_1 "then    virsh net-define net_1.xml    virsh net-start net_1fi

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


Репо с итоговым скриптом можно найти здесь: https://github.com/testo-lang/testo-articles/tree/master/01


Итоги


Что ж, мы проделали довольно много работы, в том числе и муторной, и самое время оглянуться назад и посмотреть, чего же мы достигли. А достигли мы немалого: мы написали скрипт, который в автоматическом режиме полностью с нуля разворачивает у нас на компьютере стенд из трёх виртуальных машин, раскатывает на этом стенде сборку тестируемого приложения, а также прогоняет настоящие системные (end-to-end) тесты для этого приложения.


Заметьте, что эти тесты проверяют работу нашего приложения от начала и до конца именно в том виде, в котором это приложение будет в реальности работать: мы поместили DPDK-приложение в конкретную среду (стенд из трех машин Ubuntu Server 18.04) и с конкретным набором реальных пользовательских проверок (вызов утилиты ping и wget). В нашем стенде нет ни единой заглушки, мы можем взять наш .deb пакет и прямо сейчас выложить его на сайте для скачивания всеми желающими. И именно поэтому такие тесты дают такую четкую уверенность, что если программа работает внутри тестов, то она точно отработает в реальных условиях (по крайней мере, в ТАКИХ ЖЕ реальных условиях). И эта уверенность дорогого стоит.


Но это ещё не всё: все наши артефакты это два скрипта (run_tests.sh и run_clean.sh), три xml-файла и три yaml-файла. Всё это текстовые файлы и идеально хранятся в любой VCS. Эти скрипты можно легко переносить между компьютерами и всё равно прогонять тесты одной кнопкой.


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


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

Подробнее..

Ограничение попыток входа в ssh с помощью fail2ban (средство от ботов подбирающих пароли через ssh)

18.05.2021 14:13:42 | Автор: admin
image

SSH довольно безопасен, особенно если вы примете разумные меры предосторожности, такие как требование аутентификации на основе пары ключей. Тем не менее, в дикой природе по-прежнему существует множество ботов, которые пытаются найти уязвимые хосты, пытаясь войти в систему с распространенными скомпрометированными именами пользователей и паролями, такими как root / root или admin / admin. Хотя маловероятно, что они добьются успеха, они все равно будут использовать вашу пропускную способность и генерировать огромное количество журналов.
Один из способов минимизировать количество попыток входа в систему методом перебора изменить порт по умолчанию, который прослушивает SSH. Однако это не считается хорошей практикой во-первых, нужно помнить, что каждый раз, когда они подключаются к серверу, следует устанавливать правильный порт, отличный от порта по умолчанию. Более того, это может создать еще одну уязвимость безопасности, если выбранный порт больше 1024. Обычно только root может связываться с номерами портов ниже 1024. Однако, если для SSH используется больший номер порта, при определенных обстоятельствах пользователи без корневого доступа может заменить демон SSH другой, возможно, вредоносной службой.
Лучший способ решить возникшую проблему использовать инструмент, который заблокирует злоумышленнику доступ к SSH-серверу. Одним из таких широко используемых инструментов является fail2ban ( www.fail2ban.org ). Анализируя журналы, fail2ban обнаруживает повторяющиеся неудачные попытки аутентификации и автоматически устанавливает правила брандмауэра для отбрасывания трафика, исходящего с IP-адреса злоумышленника.

Установка fail2ban на Ubuntu

Ручная установка
Установить fail2ban в Ubuntu (и других дистрибутивах на основе Debian) очень просто:

$ sudo apt install fail2ban

Проверяем как это работает

Вы можете проверить, запущена ли служба, с помощью следующей команды:

$ sudo systemctl status fail2ban

Вывод должен быть похож на следующий статус службы должен быть активным:

fail2ban.service Fail2Ban Service
Loaded: loaded (/lib/systemd/system/fail2ban.service; enabled; vendor preset: enabled)
Active: active (running) since Tue 2021-05-18 12:36:36 ***; ** min ago
Docs: man:fail2ban(1)
Process: 723*** ExecStartPre=/bin/mkdir -p /run/fail2ban (code=exited, status=0/SUCCESS)
Main PID: 723*** (f2b/server)
Tasks: 5 (limit: 38293)
Memory: 18.0M
CGroup: /system.slice/fail2ban.service
723488 /usr/bin/python3 /usr/bin/fail2ban-server -xf start


Посмотрим, как fail2ban изменил правила iptables:

$ sudo iptables -L -n -v

Вы также должны увидеть, что в конфигурации iptables есть новая цепочка f2b-sshd, на которую ссылается правило цепочки INPUT:

Chain INPUT (policy ACCEPT 73411 packets, 6622K bytes)
pkts bytes target prot opt in out source destination
1019 65297 f2b-sshd tcp * * 0.0.0.0/0 0.0.0.0/0 multiport dports 22

Chain f2b-sshd (1 references)
pkts bytes target prot opt in out source destination
8 480 REJECT all * * 94.191.93.46 0.0.0.0/0 reject-with icmp-port-unreachable
0 0 REJECT all * * 77.50.75.162 0.0.0.0/0 reject-with icmp-port-unreachable
10 600 REJECT all * * 51.254.143.190 0.0.0.0/0 reject-with icmp-port-unreachable
10 600 REJECT all * * 46.101.97.5 0.0.0.0/0 reject-with icmp-port-unreachable
9 540 REJECT all * * 43.129.28.88 0.0.0.0/0 reject-with icmp-port-unreachable
9 540 REJECT all * * 41.221.168.167 0.0.0.0/0 reject-with icmp-port-unreachable
9 540 REJECT all * * 35.247.219.12 0.0.0.0/0 reject-with icmp-port-unreachable
12 720 REJECT all * * 220.180.119.192 0.0.0.0/0 reject-with icmp-port-unreachable
10 600 REJECT all * * 218.75.121.75 0.0.0.0/0 reject-with icmp-port-unreachable
0 0 REJECT all * * 213.87.101.176 0.0.0.0/0 reject-with icmp-port-unreachable
0 0 REJECT all * * 192.139.15.34 0.0.0.0/0 reject-with icmp-port-unreachable
21 1260 REJECT all * * 187.104.145.210 0.0.0.0/0 reject-with icmp-port-unreachable
8 480 REJECT all * * 177.191.189.13 0.0.0.0/0 reject-with icmp-port-unreachable
10 600 REJECT all * * 159.89.82.134 0.0.0.0/0 reject-with icmp-port-unreachable
10 600 REJECT all * * 159.75.140.97 0.0.0.0/0 reject-with icmp-port-unreachable
8 480 REJECT all * * 157.92.13.105 0.0.0.0/0 reject-with icmp-port-unreachable
11 660 REJECT all * * 117.80.225.245 0.0.0.0/0 reject-with icmp-port-unreachable
9 540 REJECT all * * 106.53.121.179 0.0.0.0/0 reject-with icmp-port-unreachable
865 56057 RETURN all * * 0.0.0.0/0 0.0.0.0/0


Пакет fail2ban содержит инструмент под названием fail2ban-client. Он позволяет вам проверять статус службы и взаимодействовать с ней (например, позволяет вручную блокировать и разблокировать IP-адреса, включать и отключать тюрьмы и т. Д.)

Посмотрим, какие jails активны:

$ sudo fail2ban-client status

Status
|- Number of jail: 1
`- Jail list: sshd


Есть только один jail sshd которsq отвечает за мониторинг журналов SSH-сервера на предмет неудачного входа в систему и настройку правил брандмауэра для блокировки дальнейших попыток.
Теперь мы можем проверить статистику по sshd jail:

$ sudo fail2ban-client status sshd

Status for the jail: sshd
|- Filter
| |- Currently failed: 8
| |- Total failed: 26
| `- File list: /var/log/auth.log
`- Actions
|- Currently banned: 22
|- Total banned: 25
`- Banned IP list: 106.53.121.179 117.80.225.245 157.92.13.105 159.75.140.97 159.89.82.134 177.191.189.13 187.104.145.210 213.87.101.176 218.75.121.75 220.180.119.192 35.247.219.12 41.221.168.167 43.129.28.88 46.101.97.5 51.254.143.190 77.50.75.162 94.191.93.46 1.55.165.141 120.53.245.68 104.131.178.145 106.56.102.83 152.32.146.21


Настройка fail2ban

В большинстве случаев конфигурации по умолчанию должно быть достаточно. Тем не менее, полезно понимать, что это за значения по умолчанию и как их можно изменить в соответствии с вашими потребностями.
В стандартной конфигурации fail2ban защитит SSH-сервер и заблокирует злоумышленника на 10 минут после 5 неудачных попыток входа в систему в течение 10 минут. Файл конфигурации по умолчанию можно найти в /etc/fail2ban/jail.conf. Файл хорошо документирован и в основном не требует пояснений. Имейте в виду, что вам не следует вносить какие-либо изменения в этот файл, так как он может быть перезаписан во время обновления fail2ban.

После изменения конфигурации не забудьте перезапустить службу:

$ sudo systemctl restart fail2ban

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

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru