Не так давно мы в компании 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-кода:
<!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 решил, что эта виртуалка
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