Class: VagrantEc2Metadata::Server

Inherits:
Object
  • Object
show all
Defined in:
lib/vagrant-ec2-metadata/server.rb

Instance Method Summary collapse

Constructor Details

#initialize(config, port, options, env) ⇒ Server

Returns a new instance of Server.



18
19
20
21
22
23
24
# File 'lib/vagrant-ec2-metadata/server.rb', line 18

def initialize(config, port, options, env)
  @config = config
  @port = port
  @options = options
  @env = env
  @imdsv2_token = "supersecrettoken"
end

Instance Method Details

#startObject



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/vagrant-ec2-metadata/server.rb', line 26

def start
  WEBrick::Daemon.start if @options[:daemonize]
  server = WEBrick::HTTPServer.new(Port: @port)

  trap "INT" do
    server.shutdown
  end

  server.mount_proc "/" do |req, res|
    # Only allow requests from IP addresses that we own, which the VM will normally share
    addr = Addrinfo.new(req.peeraddr)
    addr = addr.ipv6_to_ipv4 if addr.ipv6_v4mapped?
    remote_ip = addr.ip_address
    if !Socket.ip_address_list.select { |a| a.ipv4_private? || a.ipv4_loopback? }.map(&:ip_address).include?(remote_ip)
      res.status = 403 # Forbidden
      next
    end

    if req.request_method == "PUT"
      if req.path == "/latest/api/token"
        res.body = @imdsv2_token
      end
      next
    end

    if @config.require_tokens && (!req.header["x-aws-ec2-metadata-token"] || req.header["x-aws-ec2-metadata-token"][0] != @imdsv2_token)
      res.status = 401 # Unauthorized
      next
    end

    # This endpoint is all we handle right now
    if !req.path.start_with?("/latest/meta-data/iam/security-credentials")
      res.status = 404
      next
    end

    if req.path == "/latest/meta-data/iam/security-credentials"
      # The Go SDK sends the request here first, then gets redirected to the correct path.. https://github.com/aws/aws-sdk-go/pull/2002
      res.status = 301
      res["Location"] = "/latest/meta-data/iam/security-credentials/"
    elsif req.path == "/latest/meta-data/iam/security-credentials/"
      res.body = "role"
    else
      sts = ::Aws::STS::Client.new(profile: @config.profile)
      if @config.role_arn
        resp = sts.assume_role({
          duration_seconds: 3600,
          role_arn: @config.role_arn,
          role_session_name: "vagrant",
        })
        creds = resp.credentials
      else
        resp = sts.get_session_token({
          duration_seconds: 3600,
        })
        creds = resp.credentials
      end

      res.body = <<~EOF
        {
          "Code" : "Success",
          "LastUpdated" : "#{Time.now.strftime("%Y-%m-%dT%H:%M:%SZ")}",
          "Type" : "AWS-HMAC",
          "AccessKeyId" : "#{creds.access_key_id}",
          "SecretAccessKey" : "#{creds.secret_access_key}",
          "Token" : "#{creds.session_token}",
          "Expiration" : "#{creds.expiration.strftime("%Y-%m-%dT%H:%M:%SZ")}"
        }
      EOF
    end
  end

  server.start
end