Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Cookbook for EC2 Instance Metadata Service IMDSV2 configuration (SSRF patch) #122

Merged
merged 3 commits into from
Apr 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions libraries/s3_file.rb
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def self.with_region_detect(region = nil)
end
end

def self.do_request(method, url, bucket, path, *args, public_bucket)
def self.do_request(method, url, bucket, path, *args, public_bucket: public_bucket)
region = args[3]
url = build_endpoint_url(bucket, region) if url.nil?

Expand Down Expand Up @@ -120,11 +120,11 @@ def self.build_endpoint_url(bucket, region)
end
end

def self.get_md5_from_s3(bucket, url, path, *args, public_bucket)
def self.get_md5_from_s3(bucket, url, path, *args, public_bucket: nil)
if public_bucket
get_digests_from_s3(bucket, url, path, public_bucket)["md5"]
get_digests_from_s3(bucket, url, path, public_bucket: public_bucket)["md5"]
else
get_digests_from_s3(bucket, url, path, args[0], args[1], args[2], args[3], public_bucket)["md5"]
get_digests_from_s3(bucket, url, path, args[0], args[1], args[2], args[3], public_bucket: public_bucket)["md5"]
end
end

Expand All @@ -135,8 +135,8 @@ def self.get_digests_from_headers(headers)
return {"md5" => etag}.merge(digests)
end

def self.get_digests_from_s3(bucket, url, path, *args, public_bucket,timeout=300,open_timeout=10,retries=5)
now, auth_string = get_s3_auth("HEAD", bucket,path,aws_access_key_id,aws_secret_access_key, token)
def self.get_digests_from_s3(bucket, url, path, *args, timeout: 300,open_timeout: 10, retries: 5, public_bucket: public_bucket)
now, auth_string = get_s3_auth("HEAD", bucket, path, args[1], args[2], args[3])
max_tries = retries + 1
headers = build_headers(now, auth_string, token)
saved_exception = nil
Expand Down Expand Up @@ -185,15 +185,15 @@ def self.validate_download_checksum(response)
end


def self.get_from_s3(bucket, url, path, aws_access_key_id, aws_secret_access_key, token, public_bucket, verify_md5=false, region = nil)
def self.get_from_s3(bucket, url, path, aws_access_key_id, aws_secret_access_key, token, public_bucket: public_bucket, verify_md5: false, region: nil)
response = nil
retries = 5
for attempts in 0..retries
begin
if public_bucket
response = do_request("GET", url, bucket, path, public_bucket)
response = do_request("GET", url, bucket, path, public_bucket: public_bucket)
else
response = do_request("GET", url, bucket, path, args[0], args[1], args[2], args[3], public_bucket)
response = do_request("GET", url, bucket, path, args[0], args[1], args[2], args[3], public_bucket: public_bucket)
end
# check the length of the downloaded object,
# make sure we didn't get nailed by
Expand Down
14 changes: 8 additions & 6 deletions providers/default.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,16 @@
aws_secret_access_key = ''
token = ''
else
get_token = Proc.new { client.put('http://169.254.169.254/latest/api/token/', nil, {:'X-aws-ec2-metadata-token-ttl-seconds' => '60'})&.body }

instance_profile_base_url = 'http://169.254.169.254/latest/meta-data/iam/security-credentials/'
begin
instance_profiles = client.get(instance_profile_base_url)
instance_profiles = client.get(instance_profile_base_url, {:'X-aws-ec2-metadata-token' => get_token.call()})
rescue client::ResourceNotFound, Errno::ETIMEDOUT # set 404 on an EC2 instance
raise ArgumentError.new 'No credentials provided and no instance profile on this machine.'
end
instance_profile_name = instance_profiles.split.first
instance_profile = JSON.load(client.get(instance_profile_base_url + instance_profile_name))
instance_profile = JSON.load(client.get(instance_profile_base_url + instance_profile_name), {:'X-aws-ec2-metadata-token' => get_token.call()})

aws_access_key_id = instance_profile['AccessKeyId']
aws_secret_access_key = instance_profile['SecretAccessKey']
Expand All @@ -44,8 +46,8 @@
if region.nil?
dynamic_doc_base_url = 'http://169.254.169.254/latest/dynamic/instance-identity/document'
begin
dynamic_doc = JSON.load(client.get(dynamic_doc_base_url))
region = dynamic_doc['region']
dynamic_doc = JSON.load(client.get(dynamic_doc_base_url, {:'X-aws-ec2-metadata-token' => get_token.call()}))
region = dynamic_doc && dynamic_doc['region']
rescue Exception => e
Chef::Log.debug "Unable to auto-detect region from instance-identity document: #{e.message}"
end
Expand All @@ -54,7 +56,7 @@
end

if ::File.exists?(new_resource.path)
s3_etag = S3FileLib::get_md5_from_s3(new_resource.bucket, new_resource.s3_url, remote_path, aws_access_key_id, aws_secret_access_key, token, new_resource.public_bucket)
s3_etag = S3FileLib::get_md5_from_s3(new_resource.bucket, new_resource.s3_url, remote_path, aws_access_key_id, aws_secret_access_key, token, public_bucket: new_resource.public_bucket)

if decryption_key.nil?
if new_resource.decrypted_file_checksum.nil?
Expand Down Expand Up @@ -92,7 +94,7 @@
end

if download
response = S3FileLib::get_from_s3(new_resource.bucket, new_resource.s3_url, remote_path, aws_access_key_id, aws_secret_access_key, token,region, new_resource.verify_md5, new_resource.public_bucket)
response = S3FileLib::get_from_s3(new_resource.bucket, new_resource.s3_url, remote_path, aws_access_key_id, aws_secret_access_key, token, region: region, verify_md5: new_resource.verify_md5, public_bucket: new_resource.public_bucket)

# not simply using the file resource here because we would have to buffer
# whole file into memory in order to set content this solves
Expand Down