LoginSignup
8
7

More than 5 years have passed since last update.

なんちゃって metadata-server を作ってみた話

Last updated at Posted at 2014-12-02

発端

オンプレミス環境で EC2 Instance Profile を使いたいというユースケースを聞きました。

SDKやCLIの中には、EC2 Instance Profileを利用するために、
http://169.254.169.254/latest/meta-data/iam/security-credentials/
http://169.254.169.254/latest/meta-data/iam/security-credentials/role名
へアクセスを行い、テンポラリのクレデンシャルを取得する仕組みが入っています。

このURLへのアクセスが出来れば、オンプレミス環境でもEC2 Instance Profileと同じような事が出来るんじゃないの?というのが発端で、実際に作ってみました。

コード

#!/usr/bin/env ruby
require 'sinatra'
require 'aws-sdk-core'

set :bind, '0.0.0.0'

role_arn=ARGV.last || ""
abort "usage: ruby ec2role.rb [ -p PORT ] arn:aws:iam:111111111111:role/ROLENAME" unless role_arn.match /^arn:aws:iam:/

sts = Aws::STS::Client.new(region: 'us-east-1')
json="n/a"

Thread.new do
  loop do
    begin
      resp = sts.assume_role(role_arn:role_arn,role_session_name:"ec2role.rb")
    rescue => error
      abort "ERROR: unable to fetch credential / "+error.to_s
    end
    json= <<EOL
{
  "Code" : "Success",
  "AccessKeyId" : "#{resp.credentials.access_key_id}",
  "SecretAccessKey" : "#{resp.credentials.secret_access_key}",
  "Token" : "#{resp.credentials.session_token}",
  "Expiration" : "#{resp.credentials.expiration.iso8601}"
}
EOL
    puts json
    sleep resp.credentials.expiration-Time.now-60*5 # update credential 5 mins before it expires
  end
end

get '/latest/meta-data/iam/security-credentials/' do
  'temp'
end

get '/latest/meta-data/iam/security-credentials/temp' do
  json
end

使い方

まず、AssumeRoleを行うためのIAMユーザと、AssumeRoleされる側のRoleを作成します。
RoleはCross-Account access用のRoleを作成し、AssumeRoleを行うユーザを信頼します。
(同じアカウント同士であっても信頼する必要があります)
RubyのコードはIAMユーザの credential を使用して起動する必要があります。

クライアントにはLinuxを想定します(iptablesを使用するため)。
送信されるパケットのうち、169.254.169.254 宛のパケットを転送するために、

# /sbin/sysctl -w net.ipv4.ip_forward=1
# /sbin/iptables -A FORWARD -m tcp -p tcp --dst 169.254.169.254 --dport 80 -j ACCEPT
# /sbin/iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
# /sbin/iptables -t nat -A OUTPUT -m tcp -p tcp --dst 169.254.169.254 --dport 80 -j DNAT --to-destination 127.0.0.1:4567

みたいな感じで、パケットの宛先を書き換えてしまいます。
Rubyのコードを実行しているログが下記のようになります。

~/work/ec2role$ ruby ec2role.rb  arn:aws:iam::407613804811:role/power
== Sinatra/1.4.5 has taken the stage on 4567 for development with backup from Thin
Thin web server (v1.6.3 codename Protein Powder)
Maximum connections set to 1024
Listening on 0.0.0.0:4567, CTRL+C to stop
{
  "Code" : "Success",
  "AccessKeyId" : "ASIAxxxxxxxxxxxxxxxxxxxxxxx",
  "SecretAccessKey" : "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "Token" : "hogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehogehoge",
  "Expiration" : "2014-12-02T10:26:26Z"
}
127.0.0.1 - - [02/Dec/2014:18:26:47 +0900] "GET /latest/meta-data/iam/security-credentials/ HTTP/1.1" 200 4 0.0052
127.0.0.1 - - [02/Dec/2014:18:26:47 +0900] "GET /latest/meta-data/iam/security-credentials/temp HTTP/1.1" 200 554 0.0005

起動時にテンポラリのクレデンシャルを取りに行き、クライアントからアクセスが来たら渡しています。
Expireする5分前に取り直しをします。

iptablesがない場合は?

SDKやCLIには、 http://169.254.169.254/ のアドレスが埋め込まれているので、それを書き換えてしまえば使えます。
AWS CLIでは、botocore/utils.py

METADATA_SECURITY_CREDENTIALS_URL = (
#    'http://169.254.169.254/latest/meta-data/iam/security-credentials/'
    'http://localhost:4567/latest/meta-data/iam/security-credentials/'
)

みたいな感じです。

追記

某meet-upで、instance-id とかには対応できないの?というご意見をいただきました。 出来ます!!
sinatraでは ./public が static content として扱われるので、

~/work/ec2role$ mkdir -p public/latest/meta-data
~/work/ec2role$ echo -n i-12345678 > public/latest/meta-data/instance-id
~/work/ec2role$ echo hoge > public/latest/user-data

みたいにファイルを作っておけば、

$ curl http://169.254.169.254/latest/user-data
hoge
$ curl http://169.254.169.254/latest/meta-data/instance-id
i-12345678
127.0.0.1 - - [09/Dec/2014:15:44:00 +0900] "GET /latest/user-data HTTP/1.1" 200 20 0.0018
127.0.0.1 - - [09/Dec/2014:15:44:30 +0900] "GET /latest/meta-data/instance-id HTTP/1.1" 200 10 0.0009

みたいに返す事が可能です。

Have fun!

8
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
7