Сюрпризы chef-a или история одного расследования

в 9:06, , рубрики: acronis, chef, ESXi, GCE, Google, virtual machine, Блог компании Acronis, Inc, виртуализация, расследование

imageНе так давно мы в компании Acronis перешли на провиженинг части наших виртуальных машин на Chef. Ничего особенно нового: все виртуальные машины создаются посредством ESXI, а центральный chef-server раздает им свои рецепты, тем самым автоматически поднимая на них окружение, исходя из их ролей. Такая система работала без проблем и сбоев довольно долго. Она освободила нас от большого количества ручной работы, постоянного контроля за окружением машин и необходимостью помнить какое ПО и настройки на них стоят, ведь достаточно открыть веб-консоль chef-server-а, выбрать нужную нам ноду и увидеть все ее роли и настройки.

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

Если заинтересовались, добро пожаловать под кат.

Сначала я, как всегда, попросил нашу IT службу поднять на ESXI пустую Debian виртуалку. Стандартные настройки, ничего лишнего. Такое уже делалось не раз и не два, и процесс был отлажен, поэтому никакого подвоха я не ожидал. Получив на руки IP и креды для доступа, я выполнил стандартную команду knife bootstrap, чтобы установить на нее chef-client и подключить к центральному chef-server-у. Все прошло без проблем, и я полез в веб-интерфейс, чтобы все установить необходимые роли и прописать атрибуты для новой ноды.

Здесь надо отметить, что при установке, chef пробегается по всей виртуалке и собирает все необходимые данные о системе: параметры железа, настройки ОС итд. После чего отправляет их на chef-server, где их можно увидеть в настройках ноды. Обычно этих данных довольно много, и я редко когда в них заглядываю, но в этот раз мое внимание привлек тот факт, что у этой новой ноды их как-то очень мало. Сравнив ее атрибуты с атрибутами других машин я еще раз в этом убедился. Но самое странное было то, что последний отображаемый атрибут новой ноды назывался gce и отсутствовал у остальных машин. Кроме того, если его развернуть, отображалось пустое ничего.

image

Т.к. виртуалку планировалась отправить напрямую в продакшн, меня такая ситуация не устраивала, и я решил раскопать, что же с ней не так. Первым делом я заглянул в консоль браузера и увидел там странную ошибку о том, что браузер отказывается загрузить iframe с http://www2.searchnut.com/?subid=704003&domain= на странице с https (веб-консоль chef-server открывается только по https). Загуглив этот адрес, я увидел кучу результатов, рассказывающих, что это разновидность malware и даже предлагающих варианты ее удалить. Заглянув в исходный код страницы, я увидел, что в том месте, где обычно выводится значение атрибута, вывелось штук 6 одинаковых кусков html-кода:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
  <head>
    <title>metadata.google.internal [6]</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta http-equiv="pragma" content="no-cache">
    <style>
    body { margin:0; padding:0; }
    </style>
  </head>
  <body scroll="no">
    <div style="position:absolute; top:0px; left:0px; width:100%; height:100%;">
    <div id="plBanner"><script id="parklogic" type="text/javascript" src="http://parking.parklogic.com/page/enhance.js?pcId=5&domain="></script></div>
      <iframe src="http://www2.searchnut.com/?subid=704003&domain=" style="height:100%; width:100%; border:none;" frameborder="0" scrolling="yes" id="park" />
    </div>
  </body>
</html>

На тот момент вариантов было не много. Я рашил, что либо виртуалку успели поломать, либо поломали сам chef и внедрили в пакет какую-то дрянь. В любом случае, ничего хорошего ожидать не приходилось, и я решил писать напрямую в support chef-а. Описав проблему и отправив тикет, я параллельно развернул еще одну виртуалку у себя на машине, и провел над ней те же действия. Какого же было мое удивление, когда я увидел, что никакого gce на ней нет! Она работала без проблем, как и все остальные.

Пока ждал ответа от саппорта, я решил поискать, что же может значить аббревиатура gce. Оказалось, что за GCE стоял Google Compute Engine, то есть клауд от Гугла, который позволял хостить у себя виртуалки, и chef может их провижинить.

К этому моменту мне ответили из саппорта и предложили выполнить команду ohai -l debug > ohai.log 2>&1, чтобы увидеть, как этот атрибут вообще появляется. Важно отметить, что Ohai — часть системы chef, которая как раз и собирает всю информацию о системе. В логах я увидел следующее:

[2014-10-03T08:27:30-04:00] DEBUG: has_ec2_mac? == false
[2014-10-03T08:27:30-04:00] DEBUG: looks_like_ec2? == false
    [2014-10-03T08:27:30-04:00] DEBUG: can_metadata_connect? == true
    [2014-10-03T08:27:30-04:00] DEBUG: looks_like_gce? == true
[2014-10-03T08:27:31-04:00] DEBUG: has_euca_mac? == false
[2014-10-03T08:27:31-04:00] DEBUG: has_euca_mac? == false
[2014-10-03T08:27:31-04:00] DEBUG: has_euca_mac? == false
[2014-10-03T08:27:31-04:00] DEBUG: looks_like_euca? == false

Самое важное я выделил отступом. По какой-то причине, Ohai решил, что эта виртуалка хостится в Google Cloud, хотя это было не так. Кроме того, меня привлекла строка can_metadata_connect, которая на всех других виртуалках показывала false. Задав резонный вопрос в IT, я получил вполне резонный ответ — это обычная ESXI виртуалка на нашем родном сервере, ни о каком GCE и речи идти не может.

Chef, как и Ohai — opensource продукты и их исходиники можно найти в github, куда я и направился. Поискав в исходниках Ohai по строке looks_like_gce, я наткнулся на интересный кусок кода в файле gce_metadata.rb:

GCE_METADATA_ADDR = "metadata.google.internal" unless defined?(GCE_METADATA_ADDR)
GCE_METADATA_URL = "/computeMetadata/v1beta1/?recursive=true" unless defined?(GCE_METADATA_URL)

def can_metadata_connect?(addr, port, timeout=2)
  t = Socket.new(Socket::Constants::AF_INET, Socket::Constants::SOCK_STREAM, 0)
  saddr = Socket.pack_sockaddr_in(port, addr)
  connected = false

  begin
    t.connect_nonblock(saddr)
  rescue Errno::EINPROGRESS
    r,w,e = IO::select(nil,[t],nil,timeout)
    if !w.nil?
      connected = true
    else
      begin
        t.connect_nonblock(saddr)
      rescue Errno::EISCONN
        t.close
        connected = true
      rescue SystemCallError
      end
    end
  rescue SystemCallError
  end
  Ohai::Log.debug("can_metadata_connect? == #{connected}")
  connected
end

Из него следовало, что если Ohai может с виртуалки запросить ресурс metadata.google.internal, и получить ответ, то машина автоматически считается GCE. Поискав по строке metadata.google.internal в гугл, я нашел, что это адрес внутреннего API Google Cloud, который доступен только из его сети, а, соответственно, получить доступ к нему со своей ноды я никак не мог.

Проверив это убеждение на старых виртуалках, я увидел:

$ wget http://metadata.google.internal
--2014-10-04 13:47:29--  http://metadata.google.internal/
Resolving metadata.google.internal... failed: nodename nor servname provided, or not known.
wget: unable to resolve host address ‘metadata.google.internal’

Но с этой новой виртуалки запрос проходил без проблем:

$ wget http://metadata.google.internal.com
--2014-10-04 13:50:38--  http://metadata.google.internal.com/
Resolving metadata.google.internal.com... 74.200.250.131
Connecting to metadata.google.internal.com|74.200.250.131|:80... connected.
HTTP request sent, awaiting response... 302 Found
Location: http://ww2.internal.com [following]
--2014-10-04 13:50:39--  http://ww2.internal.com/
Resolving ww2.internal.com... 208.73.211.246, 208.73.211.166, 208.73.211.232, ...
Connecting to ww2.internal.com|208.73.211.246|:80... connected.
HTTP request sent, awaiting response... 200 (OK)
Length: 1372 (1.3K) [text/html]
Saving to: ‘index.html’

100%[==============================================>] 1,372       --.-K/s   in 0s

2014-10-04 13:50:40 (28.4 MB/s) - ‘index.html’ saved [1372/1372]

Заглянув в содержание index.html, я увидел тот самый злополучный HTML код. Но как это могло быть? Ведь все виртуалки абсолютно одинаковые, используют одинаковый DNS 8.8.8.8. И что за ww2.internal.com, на который идет запрос? Запустив nslookup я увидел:

$ nslookup metadata.google.internal
Server:     8.8.8.8
Address:    8.8.8.8#53

Non-authoritative answer:
metadata.google.internal.com    canonical name = internal.com.
Name:   internal.com
Address: 74.200.250.131

metadata.google.internal почему-то резолвился в metadata.google.internal.com, и в этом была вся беда. Быстро заглянув в /etc/resolv.conf, я увидел строчку search com, которой на остальных машинах отродясь не было.

Ответ оказался прост. При создании этой виртуалки, ей было дано название new-site.com. Инсталлер операционки подхватывал это название, отделял com и добавлял в resolv.conf автоматически.

Таким образом, когда Ohai пробегался по системе и делал запрос к metadata.google.internal, то получал ответ с metadata.google.internal.com, и думал, что он на GCE машине. После чего делал запросы к GCE API, получая в ответ лишь эти странички с iframe-ом.

Выходит, что каждый, кто назовет свою виртуалку что-нибудь.com автоматически получит такую проблему. Естественно, я отписался в support chef-а, где меня заверили, что отправят тикет напрямую ребятам, которые пишут этот Ohai. Так что, надеюсь, скоро эту проблему поправят.

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

Автор: maxme

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js