如何知道机器是否为EC2实例


43

我想在作为EC2实例的主机上运行一些脚本,但是我不知道如何确定主机确实是EC2实例。

我做了一些测试,但这还不够:

  • 测试二进制ec2_userdata是否可用(但这并不总是正确的)
  • 测试“ http://169.254.169.254/latest/meta-data ”的可用性(但这将永远是正确的吗?这是什么“神奇IP”?)


实际上,这是一个APIPA地址,用作元数据检索等关键服务的参考是很奇怪的。
Matthieu Cerda

2
EC2的IP范围是公开的(尽管有时会有所不同)。如果您了解最新列表,则可以根据该范围检查实例IP。
Karma Fusebox

2
如果您要使用EC2,请不要依赖169.254.169.254,并且只有 EC2-类似EC2的系统(如Eucalyptus)也支持它。volve.eucalyptus.com/customer/portal/articles/…–
ceejayoz

1
您是否需要这种方法来对付已扎根于主机上的攻击者,并试图欺骗您以为这是出于自己恶意目的的EC2实例?如果这样做,那就会困难得多。
Mike Scott

Answers:


3

好吧,实际上,有一种非常简单的方法来检测主机是否为EC2实例:检查公共IP的反向查找。EC2的反向功能很难错过。

另外,如果您未进行修改,则主机名应该是您的反向名称,这样可以更轻松地发现它。

您可能还使用了您所说的“神奇IP”,因为它确实是获取EC2实例标签的标准方法,但是,如果您不在EC2网络上,则必须等待超时,通常这不是理想的...

如果这些方法还不够,只需对您的IP进行Whois检查并检查您是否在Amazon EC2 IP阻止范围之内。

编辑:您可以使用此小外壳程序位:

#!/bin/bash
LOCAL_HOSTNAME=$(hostname -d)
if [[ ${LOCAL_HOSTNAME} =~ .*\.amazonaws\.com ]]
then
        echo "This is an EC2 instance"
else
        echo "This is not an EC2 instance, or a reverse-customized one"
fi

不过,[[是一种bashism。您也可以使用Python或Perl单行代码YMMV。


13
这在VPC或更改了主机名的环境中不起作用;例如。如果您的计算机在domain.local中
Preflightsiren

2
主机名位注定会失败。
丹·普里兹

3
hostname -d返回eu-west-1.compute.internal
Bulletmagnet

42

更改了Hannes的答案,以避免出现错误消息,并在脚本中包括示例用法:

if [ -f /sys/hypervisor/uuid ] && [ `head -c 3 /sys/hypervisor/uuid` == ec2 ]; then
    echo yes
else
    echo no
fi

这在Windows实例中不起作用。与curl相比,优点是EC2和非EC2都接近瞬时。



3
我喜欢这种方法。请注意,在管理程序下运行的非EC2系统可能会生成以ec2-误报开头的UUID 。只有在您使用填充该文件的管理程序的情况下,这种情况才不太可能(256分之一的机会)。这就是为什么上面链接的文档说“您可能正在查看EC2实例”的原因。
Nate

1
@Nate,很好,但那不应该是4096分之一的机会吗?(16 x 16 x 16)
万用表

2
@Wildcard:我无法编辑我的评论,但这是正确的。
内特

7
危险!这种方法已经为我们可靠地工作了很多年……直到最近,使用没有此文件的最新c5和m5类型。所以我必须添加169.254.169.254的后备检查来处理这些实例。
Josh Kupershmidt

20

首先,由于现有答案存在以下细微问题,并且收到有关我对@qwertzguy答案的评论的问题后,我感到有必要发布新答案。以下是当前答案的问题:

  1. 接受的答案从@MatthieuCerda绝对不可靠地工作,至少不是我签反对任何VPC实例。(在我的实例中,我获得了VPC名称hostname -d,用于内部DNS,而不是其中包含“ amazonaws.com”的任何名称。)
  2. @qwertzguy 的最高投票答案不适用于没有此文件的新m5或c5实例。尽管此主题的文档页面上确实显示“ ... 如果 / sys / hypervisor / uuid存在...” ,但Amazon忽略了记录此行为更改AFAIK的记录。我问AWS支持人员此更改是否是有意的,请参阅下文†。
  3. @Jer答案不一定在所有地方都有效,因为instance-data.ec2.internalDNS查找可能不起作用。在刚刚测试过的Ubuntu EC2 VPC实例上,我看到: $ curl http://instance-data.ec2.internal curl: (6) Could not resolve host: instance-data.ec2.internal 这将导致依赖此方法的代码错误地得出结论,认为它不在EC2上!
  4. 答案用dmidecode从@tamale可以工作,但依赖于你。),其dmidecode可在您的实例,和b。),具有root或sudo从代码中使用密码的能力。
  5. 回答检查/ SYS /设备/虚拟/ DMI / ID / bios_version从@spkane是危险的误导!我查了一个Ubuntu的14.04 M5例如,找来bios_version1.0。该文件完全没有在Amazon的文档中记录,因此我真的不会依赖它。
  6. @ Chris-Montanaro答案的第一部分是检查不可靠的第三方URL并将其用于whois结果,这在几个级别上都是有问题的。请注意,该答案中建议的URL现在是404页!即使你没有发现,没有工作第三方服务,这将是相对慢(相比于本地检查文件),并可能碰到限速问题或网络问题,或者可能是您的EC2实例,甚至没有外部网络访问。
  7. @ Chris-Montanaro回答中的第二个建议是检查http://169.254.169.254/,这是更好的选择,但另一位评论者指出,其他云提供程序也使该实例元数据URL可用,因此您必须小心避免错误积极的。而且它仍然会比本地文件慢很多,我已经看到这种检查在负载很重的实例上特别慢(返回几秒钟)。另外,您应该记住传递一个-mor --max-time参数来卷曲,以防止它长时间挂起,尤其是在非EC2实例中,该地址可能导致无处挂起(如@algal的答案)。

此外,我看不到有人提到过Amazon记录的检查(possible)file的备用记录/sys/devices/virtual/dmi/id/product_uuid

谁知道确定您是否在EC2上运行可能会如此复杂?好的,既然我们已经列出了列出的方法中的(大多数)问题,这里是一个建议的bash代码段,用于检查您是否在EC2上运行。我认为这几乎可以在几乎所有Linux实例上正常工作,Windows实例是读者的练习。

#!/bin/bash

# This first, simple check will work for many older instance types.
if [ -f /sys/hypervisor/uuid ]; then
  # File should be readable by non-root users.
  if [ `head -c 3 /sys/hypervisor/uuid` == "ec2" ]; then
    echo yes
  else
    echo no
  fi

# This check will work on newer m5/c5 instances, but only if you have root!
elif [ -r /sys/devices/virtual/dmi/id/product_uuid ]; then
  # If the file exists AND is readable by us, we can rely on it.
  if [ `head -c 3 /sys/devices/virtual/dmi/id/product_uuid` == "EC2" ]; then
    echo yes
  else
    echo no
  fi

else
  # Fallback check of http://169.254.169.254/. If we wanted to be REALLY
  # authoritative, we could follow Amazon's suggestions for cryptographically
  # verifying their signature, see here:
  #    https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-identity-documents.html
  # but this is almost certainly overkill for this purpose (and the above
  # checks of "EC2" prefixes have a higher false positive potential, anyway).
  if $(curl -s -m 5 http://169.254.169.254/latest/dynamic/instance-identity/document | grep -q availabilityZone) ; then
    echo yes
  else
    echo no
  fi

fi

显然,您可以通过进行更多的后备检查来扩展此功能,并包含有关处理的偏执狂,例如/sys/hypervisor/uuid,偶然发生的误报会从“ ec2”开始等等。但这是一个很好的解决方案,可用于说明目的,并且几乎可以用于所有非病理用例。

[†]从AWS支持获得有关c5 / m5实例更改的以下解释:

C5和M5实例使用新的虚拟机监控程序堆栈,并且相关的内核驱动程序不会像其他/较旧的实例类型使用的Xen驱动程序那样在sysfs(安装在/ sys中)中创建文件。检测操作系统是否在EC2实例上运行的最佳方法是考虑所链接文档中列出的各种可能性。


4
是的,2018年的同行旅行者...这是您一直在寻找的答案。
russellpierce '18年

阅读/ sys / devices / virtual / dmi / id / product_uuid也需要root权限
Thayne

@Thayne正确-这就是该elif块上方的注释,这就是elif测试使用-r测试运算符的原因,该运算符检查文件是否存在以及您是否对该文件具有读取权限。
Josh Kupershmidt

关于169.254.169.254元数据的其他说明-在启动时并不总是准备就绪。如果您需要用于引导脚本的元数据,则将需要继续轮询,直到准备就绪为止。我看到实例开始运行其cloud-init引导脚本后最多需要30秒。
vacri

15

通过EC2内部域名而不是IP查找元数据,如果您不在EC2上,它将返回快速DNS故障,并避免IP冲突或路由问题:

curl -s http://instance-data.ec2.internal && echo "EC2 instance!" || echo "Non EC2 instance!"

在某些发行版中,非常基本的系统,或者在安装初期就无法使用curl。使用wget代替:

wget -q http://instance-data.ec2.internal && echo "EC2 instance!" || echo "Non EC2 instance!"

4
不幸的是,似乎在VPC中失败了!
阿什2015年

2
也不要使用感叹号字符双引号里面-你的回声可炸毁-bash: !": event not found。请对这些使用单引号echo
Josh Kupershmidt 2015年

1
这可能假设服务器仍在使用了解ec2.internal区域的EC2 DNS服务器,并且没有人将/etc/resolv.conf更改为8.8.8.8或滚动了自​​己的DNS基础结构。
拉蒙特

1
AWS似乎已经打破了这一点。我无法再解析instance-data.ec2.internal。不过,至少到目前为止,instance-data.us-west-2.compute.internal仍然有效。
布赖恩·拉尔森

14

如果目标是判断它是EC2实例还是其他类型的云实例(如google),则dmidecode效果很好,不需要联网。我喜欢这种方法与其他方法,因为EC2和GCE的元数据URL路径不同。

# From a google compute VM
$ sudo dmidecode -s bios-version
Google

# From an amazon ec2 VM
$ sudo dmidecode -s bios-version
4.2.amazon

我希望它在其他VM环境中甚至在真正的硬件上都能正常工作-我不希望任何硬件供应商都可以在BIOS版本中显示“ amazon”的系统中发货...
Guss

在我的Ubuntu EC2实例上,此返回1.0-不提及amazon
内特

5

主机名可能会更改,请针对您的公共IP运行Whois:

if [[ ! -z $(whois $(curl -s shtuff.it/myip/short) | grep -i amazon) ]]; then 
  echo "I'm Amazon"
else 
  echo "I'm not Amazon"
fi

或点击AWS元数据网址

if [[ ! -z $(curl -s http://169.254.169.254/1.0/) ]]; then 
  echo "I'm Amazon"
else 
  echo "I'm not Amazon"
fi

2
如果未在EC2上运行,请在第二个curl语句中添加--connect-timeout 1以快速失败。
乔纳森·奥利弗

1
FWIW使用元数据URL 可以表明它正在作为云实例运行,但是不能最终确定它是否特别是EC2。OpenStack和Eucalyptus也使用相同的元数据URI。我知道这很麻烦,但是对于我的工作来说,哪个云提供商很重要。
EmmEff

5

这也适用于ec2中的Linux主机,并且不需要网络和任何相关的超时:

grep -q amazon /sys/devices/virtual/dmi/id/bios_version

这行得通,因为Amazon定义该条目的方式如下:

$ cat /sys/devices/virtual/dmi/id/bios_version 4.2.amazon


2018-05-01; 在运行Ubuntu的M5实例上似乎无效。
russellpierce '18年

在我的Ubuntu EC2实例上,返回1.0。没有提及amazon
内特

3
test -f /sys/hypervisor/uuid -a `head -c 3 /sys/hypervisor/uuid` == ec2 && echo yes

但我不知道这在各个发行版之间的可移植性如何。


2
好吧,它肯定不会在Windows EC2实例上起作用。
ceejayoz

1
我更喜欢这种方法,因为它不涉及可能由于各种原因而挂起的网络交互。不保证将超时用于HTTP交换以防止挂起。我不在乎Windows实例。
汉尼斯,2015年

那正是我所需要的!比卷曲东西更好,谢谢!
qwertzguy 2015年

1
考虑使用完整的UUID,以防万一其他供应商的管理程序UUID也以“ ec2”开头。发生这种情况的可能性是4096分之一,这是不可忽略的。
汉尼斯2015年

1
实际上,比较整个UUID无效,因为我在野外看到了多个不同的虚拟机管理程序UUID。它们都以“ ec2”开头,因此此答案按原样起作用。
汉尼斯

3

快速回答:

if [[ -f /sys/devices/virtual/dmi/id/product_uuid ]] && \
    grep -q "^EC2" /sys/devices/virtual/dmi/id/product_uuid
then
    echo "IS EC2"
else
    echo "NOT EC2"
fi

我使用这里发布的答案之一已经有一年多了-但它不适用于新的'c5'实例类型(我现在正在努力从'c4'升级)。

我喜欢这种解决方案,因为它似乎在将来最不可能突破。

在较旧的实例类型和较新的实例类型上,此文件存在并以“ EC2”开头。我检查了在VirtualBox上运行的Ubuntu(我也需要支持),它包含字符串'VirtualBox'。

正如以前的海报所指出的(但很容易错过)-有关于如何执行此操作的Amazon文档-包括我的答案。

https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/identify_ec2_instances.html


2

也许您可以使用“因素”:

“ Facter是一个跨平台的库,用于检索简单的操作系统事实,例如操作系统,Linux发行版或MAC地址。”

http://www.puppetlabs.com/puppet/related-projects/facter/

例如,如果我们看一下ec2事实(facter-1.6.12 / lib / facter / ec2.rb):

require 'facter/util/ec2'
require 'open-uri'

def metadata(id = "")
  open("http://169.254.169.254/2008-02-01/meta-data/#{id||=''}").read.
    split("\n").each do |o|
    key = "#{id}#{o.gsub(/\=.*$/, '/')}"
    if key[-1..-1] != '/'
      value = open("http://169.254.169.254/2008-02-01/meta-data/#{key}").read.
        split("\n")
      symbol = "ec2_#{key.gsub(/\-|\//, '_')}".to_sym
      Facter.add(symbol) { setcode { value.join(',') } }
    else
      metadata(key)
    end
  end
end

def userdata()
  begin
    value = open("http://169.254.169.254/2008-02-01/user-data/").read.split
    Facter.add(:ec2_userdata) { setcode { value } }
  rescue OpenURI::HTTPError
  end
end

if (Facter::Util::EC2.has_euca_mac? || Facter::Util::EC2.has_openstack_mac? ||
    Facter::Util::EC2.has_ec2_arp?) && Facter::Util::EC2.can_connect?

  metadata
  userdata
else
  Facter.debug "Not an EC2 host"
end

1

如果已安装curl,则在EC2中运行时,此命令将返回0;否则,则返回非零:

curl --max-time 3 http://169.254.169.254/latest/meta-data/ami-id 2>/dev/null 1>/dev/null`

它尝试拉出声明AMI-ID的EC2元数据。如果3秒钟后仍未成功,则假定它不在EC2中运行。


0

参加这个聚会有点晚,但是我遇到了这篇文章,然后找到了这个AWS文档:

对于确定且经过密码验证的EC2实例的方法,请检查实例身份文档,包括其签名。这些文档可在每个EC2实例上的本地不可路由地址http://169.254.169.254/latest/dynamic/instance-identity/中找到。

https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/identify_ec2_instances.html

当然,尽管您可以像这样设置curl超时,但是这需要网络开销:

curl -s --connect-timeout 5 http://169.254.169.254/latest/dynamic/instance-identity/

将超时设置为5s。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.