Сюрпризы chef-a или история одного расследования
Не так давно мы в компании Acronis перешли на провиженинг части наших виртуальных машин на Chef. Ничего особенно нового: все виртуальные машины создаются посредством ESXI, а центральный chef-server раздает им свои рецепты, тем самым автоматически поднимая на них окружение, исходя из их ролей. Такая система работала без проблем и сбоев довольно долго. Она освободила нас от большого количества ручной работы, постоянного контроля за окружением машин и необходимостью помнить какое ПО и настройки на них стоят, ведь достаточно открыть веб-консоль chef-server-а, выбрать нужную нам ноду и увидеть все ее роли и настройки.Все было отлично до тех пор, пока нам не поставили задачу перенести один сайт с внешнего хостинга к нам на сервера, что в итоге привело к охоте на баги и расследованиям в стиле Скуби Ду.
Если заинтересовались, добро пожаловать под кат.
Сначала я, как всегда, попросил нашу IT службу поднять на ESXI пустую Debian виртуалку. Стандартные настройки, ничего лишнего. Такое уже делалось не раз и не два, и процесс был отлажен, поэтому никакого подвоха я не ожидал. Получив на руки IP и креды для доступа, я выполнил стандартную команду knife bootstrap, чтобы установить на нее chef-client и подключить к центральному chef-server-у. Все прошло без проблем, и я полез в веб-интерфейс, чтобы все установить необходимые роли и прописать атрибуты для новой ноды.
Здесь надо отметить, что при установке, chef пробегается по всей виртуалке и собирает все необходимые данные о системе: параметры железа, настройки ОС итд. После чего отправляет их на chef-server, где их можно увидеть в настройках ноды. Обычно этих данных довольно много, и я редко когда в них заглядываю, но в этот раз мое внимание привлек тот факт, что у этой новой ноды их как-то очень мало. Сравнив ее атрибуты с атрибутами других машин я еще раз в этом убедился. Но самое странное было то, что последний отображаемый атрибут новой ноды назывался gce и отсутствовал у остальных машин. Кроме того, если его развернуть, отображалось пустое ничего.
Т.к. виртуалку планировалась отправить напрямую в продакшн, меня такая ситуация не устраивала, и я решил раскопать, что же с ней не так. Первым делом я заглянул в консоль браузера и увидел там странную ошибку о том, что браузер отказывается загрузить iframe с http://www2.searchnut.com/? subid=704003&domain= на странице с https (веб-консоль chef-server открывается только по https). Загуглив этот адрес, я увидел кучу результатов, рассказывающих, что это разновидность malware и даже предлагающих варианты ее удалить. Заглянув в исходный код страницы, я увидел, что в том месте, где обычно выводится значение атрибута, вывелось штук 6 одинаковых кусков html-кода:
К этому моменту мне ответили из саппорта и предложили выполнить команду 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. Так что, надеюсь, скоро эту проблему поправят.
На этом расследование подходит к концу. Виновные наказаны, хорошие опять победили. Спасибо что были с нами!