vagrant-box-s3 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 857a4297cb7c77e6fc882a495a81de46e19ec48481e69394450349f4d2ab1462
4
- data.tar.gz: 54fbc90963cefabf0318fadf73ef4e2370c4438862c75a00041fa727fd4f0780
3
+ metadata.gz: 9a2bc5d3cadb3cdcb261f206f5ca46fe0b210ef3514bcc9072176d85222f9e24
4
+ data.tar.gz: 9044ae220e16806ba95a48c94bf147bf5df4b418cd8181bf1a9cab7ac04219ef
5
5
  SHA512:
6
- metadata.gz: 15d7d800c9636bdcc0fbcab656b46d041f57ee018ac20220689135952289603d9dece21ef7b5c7b8700ca9672ba4e7c4231e09d25b1eade790cc7fdd05e7b39b
7
- data.tar.gz: 141d2ca407dfda8c5fa8516732dbbafea8c19a15d9c451645a817b14f177a68362797fb22b57726472acbf9a352758440fc376e2f82b08c97031d6e9be8b6296
6
+ metadata.gz: 24cf532f08f551c08dec67c11b94672bf660d73b985d2d4b35ab35e6413c6d9b6ff709b15088192e7fd006cbd0c76e11dbb0bea8f5f9dafd914e411eeaa9f9e3
7
+ data.tar.gz: a15234a433f1aa2d3f2968ad98c2fc9fb29e11d269361cbd6fc2fbeb10c4fdaaa4dd47d7e4a395fc6fcec5bbb24a4dec184493bc43e4781594a8a8b4e4ca71cf
data/README.md CHANGED
@@ -11,8 +11,9 @@ Use Vagrant boxes stored in Amazon S3 private buckets.
11
11
 
12
12
  ## Features
13
13
 
14
- This plugin works by monkey patching `Vagrant::Util::Downloader`, extending the core Downloader class in Vagrant to
15
- override the `execute_curl` method to replace S3 box URLs with pre-signed S3 URLs.
14
+ This plugin works using the `authenticate_box_url` hook to replace S3 URLs with presigned URLs and monkey
15
+ patching `Vagrant::Util::Downloader`, extending the core Downloader class in Vagrant to override the `head` method
16
+ used when fetching box metadata URLs from S3.
16
17
 
17
18
  ## Installation
18
19
 
@@ -55,22 +56,22 @@ You can also use your credentials file to create a profile. Select the appropria
55
56
 
56
57
  You can use any valid HTTP(S) URL for your box URL:
57
58
 
58
- #### Path-Style URLs
59
-
60
- Specify the bucket name in the path of the URL. AWS has deprecated path-style URLs, but they might still be seen or used in legacy systems.
59
+ #### Virtual-Hosted-Style URLs
61
60
 
62
- - Format: https://s3.Region.amazonaws.com/bucket-name/key-name
63
- - Example: https://s3.eu-west-1.amazonaws.com/mybucket/mybox.box
61
+ Virtual-hosted-style URLs use the bucket name as a subdomain. This is the recommended and most commonly used format.
64
62
 
63
+ | Format | Example |
64
+ |----------------------------------------------|------------------------------------------------------------|
65
+ | `https://BUCKET.s3.REGION.amazonaws.com/KEY` | `https://mybucket.s3.eu-west-1.amazonaws.com/mybox.box` |
65
66
 
66
- - Format: https://s3-Region.amazonaws.com/bucket-name/keyname
67
- - Example: https://s3-eu-west-1.amazonaws.com/bucket-name/mybox.box
67
+ #### Path-Style URLs
68
68
 
69
- #### Virtual-Hosted-Style URLs
70
- Virtual-hosted-style URLs use the bucket name as a subdomain. This is the recommended and most commonly used format.
69
+ Specify the bucket name in the path of the URL. AWS has deprecated path-style URLs, but they might still be seen or used in legacy systems.
71
70
 
72
- - Format: https://bucket-name.s3.Region.amazonaws.com/key-name
73
- - Example: https://mybucket.s3.eu-west-1.amazonaws.com/mybox.box
71
+ | Format | Example |
72
+ |----------------------------------------------|------------------------------------------------------------|
73
+ | `https://s3.REGION.amazonaws.com/BUCKET/KEY` | `https://s3.eu-west-1.amazonaws.com/mybucket/mybox.box` |
74
+ | `https://s3-REGION.amazonaws.com/BUCKET/KEY` | `https://s3-eu-west-1.amazonaws.com/bucket-name/mybox.box` |
74
75
 
75
76
  ### IAM configuration
76
77
 
@@ -123,15 +124,15 @@ Update the current version in `lib/vagrant-box-s3/version.rb`.
123
124
 
124
125
  ### Dev build and test
125
126
 
126
- To build the plugin, use `rake build`, this will create a file with the current version number, e.g. `pkg/vagrant-box-s3-0.1.2.gem`.
127
+ To build the plugin, use `rake build`, this will create a file with the current version number, e.g. `pkg/vagrant-box-s3-{VERSION}.gem`.
127
128
 
128
129
  Remove the old version:
129
130
 
130
- vagrant plugin uninstall vagrant-box-s3
131
+ vagrant plugin uninstall vagrant-box-s3 --local
131
132
 
132
133
  Testing the plugin requires installing into vagrant from the build:
133
134
 
134
- vagrant plugin install ../vagrant-box-s3/pkg/vagrant-box-s3-0.1.2.gem
135
+ vagrant plugin install ../vagrant-box-s3/pkg/vagrant-box-s3-{VERSION}.gem
135
136
 
136
137
  Then running a command that will trigger box URL related actions, such as `vagrant up`, `vagrant box update` etc. with the `--debug` flag.
137
138
 
@@ -4,47 +4,24 @@ module Vagrant
4
4
  module Util
5
5
  class Downloader
6
6
 
7
- def aws_auth_download(options, subprocess_options, &data_proc)
8
- # Get URL from options, which is the last option in the list.
9
- url = options.last
7
+ alias_method :original_head, :head
10
8
 
11
- # Determine method from curl command -I flag existence.
12
- method = options.any? { |o| o == '-I' } ? :head_object : :get_object
9
+ def head
10
+ if ::VagrantPlugins::BoxS3::Utils.is_s3_manifest(@source)
11
+ options, subprocess_options = self.options
12
+ options.unshift("-i")
13
+ options << @source
13
14
 
14
- # Generate pre-signed URL from S3 URL.
15
- presigned_url = VagrantPlugins::BoxS3::Utils.presign_url(method, url, @logger)
15
+ @logger.info("HEAD (Override): #{@source}")
16
+ result = execute_curl(options, subprocess_options)
16
17
 
17
- # Update URL in options.
18
- url.replace(presigned_url.to_s)
19
-
20
- # Call original execute_curl (aliased).
21
- execute_curl_without_aws_auth(options, subprocess_options, &data_proc)
22
-
23
- rescue Aws::Errors::MissingCredentialsError, Aws::Sigv4::Errors::MissingCredentialsError => e
24
- message = "Missing AWS credentials: #{e.message}"
25
- @logger.error(message) if defined?(@logger)
26
- raise Errors::DownloaderError, message: message
27
- rescue Aws::S3::Errors::Forbidden => e
28
- message = "403 Forbidden: #{e.message}"
29
- raise Errors::DownloaderError, message: message
30
- rescue => e
31
- raise Errors::DownloaderError, message: e
32
- end
33
-
34
- def execute_curl_with_aws_auth(options, subprocess_options, &data_proc)
35
- options = options.dup
36
- url = options.find { |o| o =~ /^http/ }
37
-
38
- if url && url.include?('amazonaws.com')
39
- aws_auth_download(options, subprocess_options, &data_proc)
18
+ headers, _body = result.stdout.split("\r\n\r\n", 2)
19
+ headers
40
20
  else
41
- execute_curl_without_aws_auth(options, subprocess_options, &data_proc)
21
+ original_head
42
22
  end
43
23
  end
44
24
 
45
- alias execute_curl_without_aws_auth execute_curl
46
- alias execute_curl execute_curl_with_aws_auth
47
-
48
25
  end
49
26
  end
50
27
  end
@@ -0,0 +1,70 @@
1
+ require 'log4r'
2
+ require 'vagrant-box-s3/utils'
3
+
4
+ module VagrantPlugins
5
+ module BoxS3
6
+ class Urls
7
+ def initialize(app, env)
8
+ @app = app
9
+ @logger = Log4r::Logger.new('vagrant::plugins::box_s3')
10
+ end
11
+
12
+ def call(env)
13
+ # Assume 'env[:box_urls]' contains the original URLs that need authentication
14
+ original_urls = env[:box_urls]
15
+
16
+ # Your logic to authenticate the URLs goes here
17
+ # This is just an example, replace it with your actual authentication mechanism
18
+ authenticated_urls = original_urls.map do |url|
19
+ presign_url(url)
20
+ end
21
+
22
+ # Ensure the authenticated URLs are set back in the environment
23
+ env[:box_urls] = authenticated_urls
24
+
25
+ # Continue the middleware chain
26
+ @app.call(env)
27
+ end
28
+
29
+ private
30
+
31
+ # Pre-sign an s3 URL, with given method.
32
+ def presign_url(url)
33
+
34
+ # Check if the URL is an S3 URL.
35
+ if !Utils.is_s3_url(url)
36
+ @logger.info("Skipping presigner for #{url}")
37
+ return url
38
+ end
39
+
40
+ @logger.info("Discovered S3 URL: #{url}")
41
+
42
+ region, bucket, key = Utils.parse_s3_url(url)
43
+
44
+ profile = ENV['AWS_PROFILE']
45
+
46
+ @logger.debug("Region: #{region}")
47
+ @logger.debug("Bucket: #{bucket}")
48
+ @logger.debug("Key: #{key}")
49
+ @logger.debug("Profile: #{profile}")
50
+
51
+ client = Aws::S3::Client.new(
52
+ profile: profile,
53
+ region: region
54
+ )
55
+ presigner = Aws::S3::Presigner.new(client: client)
56
+
57
+ presigned_url = presigner.presigned_url(
58
+ :get_object,
59
+ bucket: bucket,
60
+ key: key,
61
+ expires_in: 3600
62
+ ).to_s
63
+
64
+ @logger.debug("Pre-signed URL: #{presigned_url}")
65
+
66
+ return presigned_url
67
+ end
68
+ end
69
+ end
70
+ end
@@ -7,13 +7,32 @@ module VagrantPlugins
7
7
 
8
8
  # Match host style URLs, e.g.
9
9
  # https://bucket-name.s3.Region.amazonaws.com/key-name
10
- S3_URL_HOST_REGEX = %r{^https?://([\w\-\.]+)\.s3\.([\w\-]+)\.amazonaws\.com/([^?]+)}
10
+ # https://bucket-name.s3-Region.amazonaws.com/key-name
11
+ S3_URL_HOST_REGEX = %r{^https?://([\w\-\.]+)\.s3[-\.]([\w\-]+)\.amazonaws\.com/([^?]+)}
11
12
 
12
13
  # Match path style URLs e.g.
13
14
  # https://s3.Region.amazonaws.com/bucket-name/key-name
14
15
  # https://s3-Region.amazonaws.com/bucket-name/keyname
15
16
  S3_URL_PATH_REGEX = %r{^https?://s3[-\.]([\w\-]+)\.amazonaws\.com/([^/]+)/([^?]+)}
16
17
 
18
+ # Check if URL matches S3 URLs.
19
+ def self.is_s3_url(url)
20
+ # Check if the URL matches either the host style or path style S3 URL
21
+ matches_host_style = !!(url =~ S3_URL_HOST_REGEX)
22
+ matches_path_style = !!(url =~ S3_URL_PATH_REGEX)
23
+
24
+ # Return true if either match is found, false otherwise
25
+ matches_host_style || matches_path_style
26
+ end
27
+
28
+ # Check if the URL is an S3 URL and the filename is 'manifest.json'
29
+ def self.is_s3_manifest(url)
30
+ uri = URI.parse(url)
31
+ filename = File.basename(uri.path)
32
+
33
+ return is_s3_url(url) && filename == 'manifest.json'
34
+ end
35
+
17
36
  # Parse an s3 URL.
18
37
  def self.parse_s3_url(url)
19
38
  region = bucket = key = nil
@@ -32,66 +51,6 @@ module VagrantPlugins
32
51
  return region, bucket, key
33
52
  end
34
53
 
35
- # Remove presigned URL params from box URL.
36
- # We do this as URLs can be cached and do not want to want
37
- # to include previously presigned URL parameters.
38
- def self.remove_presigned_params(url)
39
- # Parse the URL
40
- uri = URI.parse(url)
41
-
42
- # Split the query string into parameters
43
- query_params = URI.decode_www_form(uri.query || '').to_h
44
-
45
- # Remove any parameters that start with 'X-Amz-'
46
- query_params.reject! { |k, _| k.start_with?('X-Amz-') }
47
-
48
- # Check if there are any query parameters left
49
- if query_params.empty?
50
- # If no query parameters left, set uri.query to nil to remove the '?'
51
- uri.query = nil
52
- else
53
- # Reconstruct the query string without AWS presigned parameters
54
- uri.query = URI.encode_www_form(query_params)
55
- end
56
-
57
- return uri.to_s
58
- end
59
-
60
- # Pre-sign an s3 URL, with given method.
61
- def self.presign_url(method, url, logger)
62
-
63
- url = remove_presigned_params(url)
64
-
65
- logger.info("BoxS3: Generating signed URL for #{method.upcase}")
66
- logger.info("BoxS3: Discovered S3 URL: #{url}")
67
-
68
- region, bucket, key = parse_s3_url(url)
69
-
70
- profile = ENV['AWS_PROFILE']
71
-
72
- logger.debug("BoxS3: Region: #{region}")
73
- logger.debug("BoxS3: Bucket: #{bucket}")
74
- logger.debug("BoxS3: Key: #{key}")
75
- logger.debug("BoxS3: Profile: #{profile}")
76
-
77
- client = Aws::S3::Client.new(
78
- profile: profile,
79
- region: region
80
- )
81
- presigner = Aws::S3::Presigner.new(client: client)
82
-
83
- presigned_url = presigner.presigned_url(
84
- method,
85
- bucket: bucket,
86
- key: key,
87
- expires_in: 3600
88
- ).to_s
89
-
90
- logger.debug("BoxS3: Pre-signed URL: #{presigned_url}")
91
-
92
- return presigned_url
93
- end
94
-
95
54
  end
96
55
  end
97
56
  end
@@ -1,5 +1,5 @@
1
1
  module VagrantPlugins
2
2
  module BoxS3
3
- VERSION = '0.1.3'
3
+ VERSION = '0.1.5'
4
4
  end
5
5
  end
@@ -1,4 +1,5 @@
1
1
  require 'vagrant'
2
+ require 'vagrant-box-s3/urls'
2
3
 
3
4
  module VagrantPlugins
4
5
  module BoxS3
@@ -9,6 +10,10 @@ module VagrantPlugins
9
10
  require_relative 'vagrant-box-s3/downloader'
10
11
  end
11
12
 
13
+ action_hook(:vagrant_box_s3_url, :authenticate_box_url) do |hook|
14
+ hook.prepend(Urls)
15
+ end
16
+
12
17
  end
13
18
  end
14
19
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vagrant-box-s3
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steve Whiteley
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-02-19 00:00:00.000000000 Z
11
+ date: 2024-10-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk-s3
@@ -63,6 +63,7 @@ files:
63
63
  - README.md
64
64
  - lib/vagrant-box-s3.rb
65
65
  - lib/vagrant-box-s3/downloader.rb
66
+ - lib/vagrant-box-s3/urls.rb
66
67
  - lib/vagrant-box-s3/utils.rb
67
68
  - lib/vagrant-box-s3/version.rb
68
69
  homepage: https://github.com/memiah/vagrant-box-s3