summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTyler Ball <tyler-ball@users.noreply.github.com>2015-12-09 14:24:08 -0700
committerTyler Ball <tyler-ball@users.noreply.github.com>2015-12-09 14:24:08 -0700
commit40bcd3accf755b739aeaea1cc325eebf43519d59 (patch)
tree5be428017e0883fdcaa19c74308bb73783270071
parent010392858c2a3a036578b681085704ed1971ab21 (diff)
parent85d0407a16fa4dfc479d550a96355a6d11f4f551 (diff)
downloadchef-40bcd3accf755b739aeaea1cc325eebf43519d59.tar.gz
Merge pull request #4237 from chef/tball/proxifier
Enabling 'knife ssl check/fetch' commands to respect proxy environment variables and moving proxy environment variables export to Chef::Config
-rw-r--r--.travis.yml19
-rw-r--r--chef-config/lib/chef-config/config.rb61
-rw-r--r--chef-config/spec/unit/config_spec.rb108
-rw-r--r--chef.gemspec2
-rw-r--r--lib/chef/application.rb84
-rw-r--r--lib/chef/application/apply.rb2
-rw-r--r--lib/chef/knife.rb1
-rw-r--r--lib/chef/knife/ssl_check.rb6
-rw-r--r--lib/chef/knife/ssl_fetch.rb5
-rw-r--r--lib/chef/mixin/proxified_socket.rb38
-rw-r--r--spec/functional/application_spec.rb2
-rw-r--r--spec/unit/application_spec.rb182
-rw-r--r--spec/unit/knife/ssl_check_spec.rb4
-rw-r--r--spec/unit/knife/ssl_fetch_spec.rb4
-rw-r--r--spec/unit/mixin/proxified_socket_spec.rb94
15 files changed, 328 insertions, 284 deletions
diff --git a/.travis.yml b/.travis.yml
index 95cdfdd9b7..78303d7591 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -118,22 +118,21 @@ matrix:
- rvm: 2.2
sudo: required
dist: trusty
- os: linux
- cache:
before_install:
- sudo apt-get update
- - sudo apt-get -y install squid3 git
+ - sudo apt-get -y install squid3 git curl
env:
- - PROXY_TESTS_DIR=/tmp/proxy_tests
- - PROXY_TESTS_REPO=$PROXY_TESTS_DIR/repo
+ global:
+ - PROXY_TESTS_DIR=proxy_tests/files/default/scripts
+ - PROXY_TESTS_REPO=$PROXY_TESTS_DIR/repo
script:
- bundle exec chef-client --version
- git clone https://github.com/chef/proxy_tests.git
- - cd proxy_tests
- - bundle exec chef-client -z -o proxy_tests::render
- #- sh /tmp/proxy_tests/setup.sh
- - bundle exec sudo -E bash /tmp/proxy_tests/run_tests.sh chef_client \* \* /tmp/out.txt
- after_script: cat /tmp/out.txt
+ - rvmsudo -E bundle exec bash $PROXY_TESTS_DIR/run_tests.sh chef_client \* \* /tmp/out.txt
+ after_script:
+ - cat /tmp/out.txt
+ - sudo cat /var/log/squid3/cache.log
+ - sudo cat /var/log/squid3/access.log
allow_failures:
- rvm: rbx
diff --git a/chef-config/lib/chef-config/config.rb b/chef-config/lib/chef-config/config.rb
index 49d775232d..113bf481ff 100644
--- a/chef-config/lib/chef-config/config.rb
+++ b/chef-config/lib/chef-config/config.rb
@@ -26,6 +26,7 @@ require 'chef-config/logger'
require 'chef-config/windows'
require 'chef-config/path_helper'
require 'mixlib/shellout'
+require 'uri'
module ChefConfig
@@ -710,6 +711,66 @@ module ChefConfig
config_context :chefdk do
end
+ configurable(:http_proxy)
+ configurable(:http_proxy_user)
+ configurable(:http_proxy_pass)
+ configurable(:https_proxy)
+ configurable(:https_proxy_user)
+ configurable(:https_proxy_pass)
+ configurable(:ftp_proxy)
+ configurable(:ftp_proxy_user)
+ configurable(:ftp_proxy_pass)
+ configurable(:no_proxy)
+
+ # Public method that users should call to export proxies to the appropriate
+ # environment variables. This method should be called after the config file is
+ # parsed and loaded.
+ # TODO add some post-file-parsing logic that automatically calls this so
+ # users don't have to
+ def self.export_proxies
+ export_proxy("http", http_proxy, http_proxy_user, http_proxy_pass) if http_proxy
+ export_proxy("https", https_proxy, https_proxy_user, https_proxy_pass) if https_proxy
+ export_proxy("ftp", ftp_proxy, ftp_proxy_user, ftp_proxy_pass) if ftp_proxy
+ export_no_proxy("no_proxy", no_proxy) if no_proxy
+ end
+
+ # Builds a proxy uri and exports it to the appropriate environment variables. Examples:
+ # http://username:password@hostname:port
+ # https://username@hostname:port
+ # ftp://hostname:port
+ # when
+ # scheme = "http", "https", or "ftp"
+ # hostport = hostname:port or scheme://hostname:port
+ # user = username
+ # pass = password
+ # @api private
+ def self.export_proxy(scheme, path, user, pass)
+ path = "#{scheme}://#{path}" unless path.include?('://')
+ # URI.split returns the following parts:
+ # [scheme, userinfo, host, port, registry, path, opaque, query, fragment]
+ parts = URI.split(URI.encode(path))
+ # URI::Generic.build requires an integer for the port, but URI::split gives
+ # returns a string for the port.
+ parts[3] = parts[3].to_i if parts[3]
+ if user && !user.empty?
+ userinfo = URI.encode(URI.encode(user), '@:')
+ if pass
+ userinfo << ":#{URI.encode(URI.encode(pass), '@:')}"
+ end
+ parts[1] = userinfo
+ end
+
+ path = URI::Generic.build(parts).to_s
+ ENV["#{scheme}_proxy".downcase] = path unless ENV["#{scheme}_proxy".downcase]
+ ENV["#{scheme}_proxy".upcase] = path unless ENV["#{scheme}_proxy".upcase]
+ end
+
+ # @api private
+ def self.export_no_proxy(value)
+ ENV['no_proxy'] = value unless ENV['no_proxy']
+ ENV['NO_PROXY'] = value unless ENV['NO_PROXY']
+ end
+
# Chef requires an English-language UTF-8 locale to function properly. We attempt
# to use the 'locale -a' command and search through a list of preferences until we
# find one that we can use. On Ubuntu systems we should find 'C.UTF-8' and be
diff --git a/chef-config/spec/unit/config_spec.rb b/chef-config/spec/unit/config_spec.rb
index 4af5d4b7c7..0a3dca5b5a 100644
--- a/chef-config/spec/unit/config_spec.rb
+++ b/chef-config/spec/unit/config_spec.rb
@@ -571,6 +571,114 @@ RSpec.describe ChefConfig::Config do
end
end
+ describe "export_proxies" do
+ let(:http_proxy) { "http://localhost:7979" }
+ let(:https_proxy) { "https://localhost:7979" }
+ let(:ftp_proxy) { "ftp://localhost:7979" }
+ let(:proxy_user) { "http_user" }
+ let(:proxy_pass) { "http_pass" }
+
+ context "when http_proxy, proxy_pass and proxy_user are set" do
+ before do
+ ChefConfig::Config.http_proxy = http_proxy
+ ChefConfig::Config.http_proxy_user = proxy_user
+ ChefConfig::Config.http_proxy_pass = proxy_pass
+ end
+ it "exports ENV['http_proxy']" do
+ expect(ENV).to receive(:[]=).with('http_proxy', "http://http_user:http_pass@localhost:7979")
+ expect(ENV).to receive(:[]=).with('HTTP_PROXY', "http://http_user:http_pass@localhost:7979")
+ ChefConfig::Config.export_proxies
+ end
+ end
+
+ context "when https_proxy, proxy_pass and proxy_user are set" do
+ before do
+ ChefConfig::Config.https_proxy = https_proxy
+ ChefConfig::Config.https_proxy_user = proxy_user
+ ChefConfig::Config.https_proxy_pass = proxy_pass
+ end
+ it "exports ENV['https_proxy']" do
+ expect(ENV).to receive(:[]=).with('https_proxy', "https://http_user:http_pass@localhost:7979")
+ expect(ENV).to receive(:[]=).with('HTTPS_PROXY', "https://http_user:http_pass@localhost:7979")
+ ChefConfig::Config.export_proxies
+ end
+ end
+
+ context "when ftp_proxy, proxy_pass and proxy_user are set" do
+ before do
+ ChefConfig::Config.ftp_proxy = ftp_proxy
+ ChefConfig::Config.ftp_proxy_user = proxy_user
+ ChefConfig::Config.ftp_proxy_pass = proxy_pass
+ end
+ it "exports ENV['ftp_proxy']" do
+ expect(ENV).to receive(:[]=).with('ftp_proxy', "ftp://http_user:http_pass@localhost:7979")
+ expect(ENV).to receive(:[]=).with('FTP_PROXY', "ftp://http_user:http_pass@localhost:7979")
+ ChefConfig::Config.export_proxies
+ end
+ end
+
+ shared_examples "no user pass" do
+ it "does not populate the user or password" do
+ expect(ENV).to receive(:[]=).with('http_proxy', "http://localhost:7979")
+ expect(ENV).to receive(:[]=).with('HTTP_PROXY', "http://localhost:7979")
+ ChefConfig::Config.export_proxies
+ end
+ end
+
+ context "when proxy_pass and proxy_user are passed as empty strings" do
+ before do
+ ChefConfig::Config.http_proxy = http_proxy
+ ChefConfig::Config.http_proxy_user = ""
+ ChefConfig::Config.http_proxy_pass = proxy_pass
+ end
+ include_examples "no user pass"
+ end
+
+ context "when proxy_pass and proxy_user are not provided" do
+ before do
+ ChefConfig::Config.http_proxy = http_proxy
+ end
+ include_examples "no user pass"
+ end
+
+ context "when the proxy is provided without a scheme" do
+ before do
+ ChefConfig::Config.http_proxy = "localhost:1111"
+ end
+ it "automatically adds the scheme to the proxy url" do
+ expect(ENV).to receive(:[]=).with('http_proxy', "http://localhost:1111")
+ expect(ENV).to receive(:[]=).with('HTTP_PROXY', "http://localhost:1111")
+ ChefConfig::Config.export_proxies
+ end
+ end
+
+ shared_examples "no export" do
+ it "does not export any proxy settings" do
+ ChefConfig::Config.export_proxies
+ expect(ENV['http_proxy']).to eq(nil)
+ expect(ENV['https_proxy']).to eq(nil)
+ expect(ENV['ftp_proxy']).to eq(nil)
+ expect(ENV['no_proxy']).to eq(nil)
+ end
+ end
+
+ context "when nothing is set" do
+ include_examples "no export"
+ end
+
+ context "when all the users and passwords are set but no proxies are set" do
+ before do
+ ChefConfig::Config.http_proxy_user = proxy_user
+ ChefConfig::Config.http_proxy_pass = proxy_pass
+ ChefConfig::Config.https_proxy_user = proxy_user
+ ChefConfig::Config.https_proxy_pass = proxy_pass
+ ChefConfig::Config.ftp_proxy_user = proxy_user
+ ChefConfig::Config.ftp_proxy_pass = proxy_pass
+ end
+ include_examples "no export"
+ end
+ end
+
describe "allowing chefdk configuration outside of chefdk" do
it "allows arbitrary settings in the chefdk config context" do
diff --git a/chef.gemspec b/chef.gemspec
index faa00e87d6..2e33ec6b4b 100644
--- a/chef.gemspec
+++ b/chef.gemspec
@@ -43,6 +43,8 @@ Gem::Specification.new do |s|
s.add_dependency "syslog-logger", "~> 1.6"
+ s.add_dependency "proxifier", "~> 1.0"
+
s.add_development_dependency "rack"
s.add_development_dependency "cheffish", "~> 1.1"
diff --git a/lib/chef/application.rb b/lib/chef/application.rb
index 970544c068..a2c415111e 100644
--- a/lib/chef/application.rb
+++ b/lib/chef/application.rb
@@ -17,7 +17,6 @@
# limitations under the License.
require 'pp'
-require 'uri'
require 'socket'
require 'chef/config'
require 'chef/config_fetcher'
@@ -47,7 +46,6 @@ class Chef
def reconfigure
configure_chef
configure_logging
- configure_proxy_environment_variables
configure_encoding
emit_warnings
end
@@ -85,6 +83,7 @@ class Chef
def configure_chef
parse_options
load_config_file
+ Chef::Config.export_proxies
end
# Parse the config file
@@ -180,14 +179,6 @@ class Chef
end
end
- # Configure and set any proxy environment variables according to the config.
- def configure_proxy_environment_variables
- configure_http_proxy
- configure_https_proxy
- configure_ftp_proxy
- configure_no_proxy
- end
-
# Sets the default external encoding to UTF-8 (users can change this, but they shouldn't)
def configure_encoding
Encoding.default_external = Chef::Config[:ruby_encoding]
@@ -302,79 +293,6 @@ class Chef
Chef::Application.fatal!("Aborting due to error in '#{config_file_path}'", 2)
end
- # Set ENV['http_proxy']
- def configure_http_proxy
- if http_proxy = Chef::Config[:http_proxy]
- http_proxy_string = configure_proxy("http", http_proxy,
- Chef::Config[:http_proxy_user], Chef::Config[:http_proxy_pass])
- env['http_proxy'] = http_proxy_string unless env['http_proxy']
- env['HTTP_PROXY'] = http_proxy_string unless env['HTTP_PROXY']
- end
- end
-
- # Set ENV['https_proxy']
- def configure_https_proxy
- if https_proxy = Chef::Config[:https_proxy]
- https_proxy_string = configure_proxy("https", https_proxy,
- Chef::Config[:https_proxy_user], Chef::Config[:https_proxy_pass])
- env['https_proxy'] = https_proxy_string unless env['https_proxy']
- env['HTTPS_PROXY'] = https_proxy_string unless env['HTTPS_PROXY']
- end
- end
-
- # Set ENV['ftp_proxy']
- def configure_ftp_proxy
- if ftp_proxy = Chef::Config[:ftp_proxy]
- ftp_proxy_string = configure_proxy("ftp", ftp_proxy,
- Chef::Config[:ftp_proxy_user], Chef::Config[:ftp_proxy_pass])
- env['ftp_proxy'] = ftp_proxy_string unless env['ftp_proxy']
- env['FTP_PROXY'] = ftp_proxy_string unless env['FTP_PROXY']
- end
- end
-
- # Set ENV['no_proxy']
- def configure_no_proxy
- if Chef::Config[:no_proxy]
- env['no_proxy'] = Chef::Config[:no_proxy] unless env['no_proxy']
- env['NO_PROXY'] = Chef::Config[:no_proxy] unless env['NO_PROXY']
- end
- end
-
- # Builds a proxy uri. Examples:
- # http://username:password@hostname:port
- # https://username@hostname:port
- # ftp://hostname:port
- # when
- # scheme = "http", "https", or "ftp"
- # hostport = hostname:port
- # user = username
- # pass = password
- def configure_proxy(scheme, path, user, pass)
- begin
- path = "#{scheme}://#{path}" unless path.include?('://')
- # URI.split returns the following parts:
- # [scheme, userinfo, host, port, registry, path, opaque, query, fragment]
- parts = URI.split(URI.encode(path))
- # URI::Generic.build requires an integer for the port, but URI::split gives
- # returns a string for the port.
- parts[3] = parts[3].to_i if parts[3]
- if user
- userinfo = URI.encode(URI.encode(user), '@:')
- if pass
- userinfo << ":#{URI.encode(URI.encode(pass), '@:')}"
- end
- parts[1] = userinfo
- end
-
- return URI::Generic.build(parts).to_s
- rescue URI::Error => e
- # URI::Error messages generally include the offending string. Including a message
- # for which proxy config item has the issue should help deduce the issue when
- # the URI::Error message is vague.
- raise Chef::Exceptions::BadProxyURI, "Cannot configure #{scheme} proxy. Does not comply with URI scheme. #{e.message}"
- end
- end
-
# This is a hook for testing
def env
ENV
diff --git a/lib/chef/application/apply.rb b/lib/chef/application/apply.rb
index 4c559542f1..6a371c89c4 100644
--- a/lib/chef/application/apply.rb
+++ b/lib/chef/application/apply.rb
@@ -124,7 +124,7 @@ class Chef::Application::Apply < Chef::Application
parse_options
Chef::Config.merge!(config)
configure_logging
- configure_proxy_environment_variables
+ Chef::Config.export_proxies
parse_json
end
diff --git a/lib/chef/knife.rb b/lib/chef/knife.rb
index 6fa29bea16..5df24faa11 100644
--- a/lib/chef/knife.rb
+++ b/lib/chef/knife.rb
@@ -401,6 +401,7 @@ class Chef
merge_configs
apply_computed_config
+ Chef::Config.export_proxies
# This has to be after apply_computed_config so that Mixlib::Log is configured
Chef::Log.info("Using configuration from #{config[:config_file]}") if config[:config_file]
end
diff --git a/lib/chef/knife/ssl_check.rb b/lib/chef/knife/ssl_check.rb
index d71eacfc7e..38b4d81bb3 100644
--- a/lib/chef/knife/ssl_check.rb
+++ b/lib/chef/knife/ssl_check.rb
@@ -29,6 +29,8 @@ class Chef
require 'uri'
require 'chef/http/ssl_policies'
require 'openssl'
+ require 'chef/mixin/proxified_socket'
+ include Chef::Mixin::ProxifiedSocket
end
banner "knife ssl check [URL] (options)"
@@ -75,7 +77,7 @@ class Chef
def verify_peer_socket
@verify_peer_socket ||= begin
- tcp_connection = TCPSocket.new(host, port)
+ tcp_connection = proxified_socket(host, port)
ssl_client = OpenSSL::SSL::SSLSocket.new(tcp_connection, verify_peer_ssl_context)
ssl_client.hostname = host
ssl_client
@@ -93,7 +95,7 @@ class Chef
def noverify_socket
@noverify_socket ||= begin
- tcp_connection = TCPSocket.new(host, port)
+ tcp_connection = proxified_socket(host, port)
OpenSSL::SSL::SSLSocket.new(tcp_connection, noverify_peer_ssl_context)
end
end
diff --git a/lib/chef/knife/ssl_fetch.rb b/lib/chef/knife/ssl_fetch.rb
index fd7d101fd8..0c1ab7ea7b 100644
--- a/lib/chef/knife/ssl_fetch.rb
+++ b/lib/chef/knife/ssl_fetch.rb
@@ -28,6 +28,8 @@ class Chef
require 'socket'
require 'uri'
require 'openssl'
+ require 'chef/mixin/proxified_socket'
+ include Chef::Mixin::ProxifiedSocket
end
banner "knife ssl fetch [URL] (options)"
@@ -71,7 +73,7 @@ class Chef
end
def remote_cert_chain
- tcp_connection = TCPSocket.new(host, port)
+ tcp_connection = proxified_socket(host, port)
shady_ssl_connection = OpenSSL::SSL::SSLSocket.new(tcp_connection, noverify_peer_ssl_context)
shady_ssl_connection.connect
shady_ssl_connection.peer_cert_chain
@@ -155,4 +157,3 @@ TRUST_TRUST
end
end
end
-
diff --git a/lib/chef/mixin/proxified_socket.rb b/lib/chef/mixin/proxified_socket.rb
new file mode 100644
index 0000000000..3fda0083c3
--- /dev/null
+++ b/lib/chef/mixin/proxified_socket.rb
@@ -0,0 +1,38 @@
+# Author:: Tyler Ball (<tball@chef.io>)
+# Copyright:: Copyright (c) 2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'proxifier'
+
+class Chef
+ module Mixin
+ module ProxifiedSocket
+
+ # This looks at the environment variables and leverages Proxifier to
+ # make the TCPSocket respect ENV['https_proxy'] or ENV['http_proxy'] if
+ # they are present
+ def proxified_socket(host, port)
+ proxy = ENV['https_proxy'] || ENV['http_proxy'] || false
+ if proxy
+ Proxifier.Proxy(proxy, no_proxy: ENV['no_proxy']).open(host, port)
+ else
+ TCPSocket.new(host, port)
+ end
+ end
+
+ end
+ end
+end
diff --git a/spec/functional/application_spec.rb b/spec/functional/application_spec.rb
index 00ff0f702a..fe656dca60 100644
--- a/spec/functional/application_spec.rb
+++ b/spec/functional/application_spec.rb
@@ -42,7 +42,7 @@ describe Chef::Application do
Chef::Config[:ftp_proxy] = nil
Chef::Config[:no_proxy] = nil
- @app.configure_proxy_environment_variables
+ Chef::Config.export_proxies
end
it "saves built proxy to ENV which shell_out can use" do
diff --git a/spec/unit/application_spec.rb b/spec/unit/application_spec.rb
index f5a2c72aa0..20b7e3a506 100644
--- a/spec/unit/application_spec.rb
+++ b/spec/unit/application_spec.rb
@@ -39,7 +39,6 @@ describe Chef::Application do
@app = Chef::Application.new
allow(@app).to receive(:configure_chef).and_return(true)
allow(@app).to receive(:configure_logging).and_return(true)
- allow(@app).to receive(:configure_proxy_environment_variables).and_return(true)
end
it "should configure chef" do
@@ -52,11 +51,6 @@ describe Chef::Application do
@app.reconfigure
end
- it "should configure environment variables" do
- expect(@app).to receive(:configure_proxy_environment_variables).and_return(true)
- @app.reconfigure
- end
-
it 'should not receive set_specific_recipes' do
expect(@app).to_not receive(:set_specific_recipes)
@app.reconfigure
@@ -101,6 +95,7 @@ describe Chef::Application do
@app = Chef::Application.new
#Chef::Config.stub(:merge!).and_return(true)
allow(@app).to receive(:parse_options).and_return(true)
+ expect(Chef::Config).to receive(:export_proxies).and_return(true)
end
it "should parse the commandline options" do
@@ -245,181 +240,6 @@ describe Chef::Application do
end
end
- describe "when configuring environment variables" do
- def configure_proxy_environment_variables_stubs
- allow(@app).to receive(:configure_http_proxy).and_return(true)
- allow(@app).to receive(:configure_https_proxy).and_return(true)
- allow(@app).to receive(:configure_ftp_proxy).and_return(true)
- allow(@app).to receive(:configure_no_proxy).and_return(true)
- end
-
- shared_examples_for "setting ENV['http_proxy']" do
- before do
- Chef::Config[:http_proxy] = http_proxy
- end
-
- it "should set ENV['http_proxy']" do
- @app.configure_proxy_environment_variables
- expect(@env['http_proxy']).to eq("#{scheme}://#{address}:#{port}")
- end
-
- it "should set ENV['HTTP_PROXY']" do
- @app.configure_proxy_environment_variables
- expect(@env['HTTP_PROXY']).to eq("#{scheme}://#{address}:#{port}")
- end
-
- describe "when Chef::Config[:http_proxy_user] is set" do
- before do
- Chef::Config[:http_proxy_user] = "username"
- end
-
- it "should set ENV['http_proxy'] with the username" do
- @app.configure_proxy_environment_variables
- expect(@env['http_proxy']).to eq("#{scheme}://username@#{address}:#{port}")
- expect(@env['HTTP_PROXY']).to eq("#{scheme}://username@#{address}:#{port}")
- end
-
- context "when :http_proxy_user contains '@' and/or ':'" do
- before do
- Chef::Config[:http_proxy_user] = "my:usern@me"
- end
-
- it "should set ENV['http_proxy'] with the escaped username" do
- @app.configure_proxy_environment_variables
- expect(@env['http_proxy']).to eq("#{scheme}://my%3Ausern%40me@#{address}:#{port}")
- expect(@env['HTTP_PROXY']).to eq("#{scheme}://my%3Ausern%40me@#{address}:#{port}")
- end
- end
-
- describe "when Chef::Config[:http_proxy_pass] is set" do
- before do
- Chef::Config[:http_proxy_pass] = "password"
- end
-
- it "should set ENV['http_proxy'] with the password" do
- @app.configure_proxy_environment_variables
- expect(@env['http_proxy']).to eq("#{scheme}://username:password@#{address}:#{port}")
- expect(@env['HTTP_PROXY']).to eq("#{scheme}://username:password@#{address}:#{port}")
- end
-
- context "when :http_proxy_pass contains '@' and/or ':'" do
- before do
- Chef::Config[:http_proxy_pass] = ":P@ssword101"
- end
-
- it "should set ENV['http_proxy'] with the escaped password" do
- @app.configure_proxy_environment_variables
- expect(@env['http_proxy']).to eq("#{scheme}://username:%3AP%40ssword101@#{address}:#{port}")
- expect(@env['HTTP_PROXY']).to eq("#{scheme}://username:%3AP%40ssword101@#{address}:#{port}")
- end
- end
- end
- end
-
- describe "when Chef::Config[:http_proxy_pass] is set (but not Chef::Config[:http_proxy_user])" do
- before do
- Chef::Config[:http_proxy_user] = nil
- Chef::Config[:http_proxy_pass] = "password"
- end
-
- it "should set ENV['http_proxy']" do
- @app.configure_proxy_environment_variables
- expect(@env['http_proxy']).to eq("#{scheme}://#{address}:#{port}")
- expect(@env['HTTP_PROXY']).to eq("#{scheme}://#{address}:#{port}")
- end
- end
- end
-
- describe "when configuring ENV['http_proxy']" do
- before do
- @env = {}
- allow(@app).to receive(:env).and_return(@env)
-
- allow(@app).to receive(:configure_https_proxy).and_return(true)
- allow(@app).to receive(:configure_ftp_proxy).and_return(true)
- allow(@app).to receive(:configure_no_proxy).and_return(true)
- end
-
- describe "when Chef::Config[:http_proxy] is not set" do
- before do
- Chef::Config[:http_proxy] = nil
- end
-
- it "should not set ENV['http_proxy']" do
- @app.configure_proxy_environment_variables
- expect(@env).to eq({})
- end
- end
-
- describe "when Chef::Config[:http_proxy] is set" do
- context "when given an FQDN" do
- let(:scheme) { "http" }
- let(:address) { "proxy.example.org" }
- let(:port) { 8080 }
- let(:http_proxy) { "#{scheme}://#{address}:#{port}" }
-
- it_should_behave_like "setting ENV['http_proxy']"
- end
-
- context "when given an HTTPS URL" do
- let(:scheme) { "https" }
- let(:address) { "proxy.example.org" }
- let(:port) { 8080 }
- let(:http_proxy) { "#{scheme}://#{address}:#{port}" }
-
- it_should_behave_like "setting ENV['http_proxy']"
- end
-
- context "when given an IP" do
- let(:scheme) { "http" }
- let(:address) { "127.0.0.1" }
- let(:port) { 22 }
- let(:http_proxy) { "#{scheme}://#{address}:#{port}" }
-
- it_should_behave_like "setting ENV['http_proxy']"
- end
-
- context "when given an IPv6" do
- let(:scheme) { "http" }
- let(:address) { "[2001:db8::1]" }
- let(:port) { 80 }
- let(:http_proxy) { "#{scheme}://#{address}:#{port}" }
-
- it_should_behave_like "setting ENV['http_proxy']"
- end
-
- context "when given without including http://" do
- let(:scheme) { "http" }
- let(:address) { "proxy.example.org" }
- let(:port) { 8181 }
- let(:http_proxy) { "#{address}:#{port}" }
-
- it_should_behave_like "setting ENV['http_proxy']"
- end
-
- context "when given the full proxy in :http_proxy only" do
- before do
- Chef::Config[:http_proxy] = "http://username:password@proxy.example.org:2222"
- Chef::Config[:http_proxy_user] = nil
- Chef::Config[:http_proxy_pass] = nil
- end
-
- it "should set ENV['http_proxy']" do
- @app.configure_proxy_environment_variables
- expect(@env['http_proxy']).to eq(Chef::Config[:http_proxy])
- end
- end
-
- context "when the config options aren't URI compliant" do
- it "raises Chef::Exceptions::BadProxyURI" do
- Chef::Config[:http_proxy] = "http://proxy.bad_example.org/:8080"
- expect { @app.configure_proxy_environment_variables }.to raise_error(Chef::Exceptions::BadProxyURI)
- end
- end
- end
- end
- end
-
describe "class method: fatal!" do
before do
allow(STDERR).to receive(:puts).with("FATAL: blah").and_return(true)
diff --git a/spec/unit/knife/ssl_check_spec.rb b/spec/unit/knife/ssl_check_spec.rb
index a9d1145f34..feeb85c8af 100644
--- a/spec/unit/knife/ssl_check_spec.rb
+++ b/spec/unit/knife/ssl_check_spec.rb
@@ -145,7 +145,7 @@ E
let(:ssl_socket) { double(OpenSSL::SSL::SSLSocket) }
before do
- expect(TCPSocket).to receive(:new).with("foo.example.com", 8443).and_return(tcp_socket)
+ expect(ssl_check).to receive(:proxified_socket).with("foo.example.com", 8443).and_return(tcp_socket)
expect(OpenSSL::SSL::SSLSocket).to receive(:new).with(tcp_socket, ssl_check.verify_peer_ssl_context).and_return(ssl_socket)
end
@@ -183,7 +183,7 @@ E
before do
@old_signal = trap(:INT, "DEFAULT")
- expect(TCPSocket).to receive(:new).
+ expect(ssl_check).to receive(:proxified_socket).
with("foo.example.com", 8443).
and_return(tcp_socket_for_debug)
expect(OpenSSL::SSL::SSLSocket).to receive(:new).
diff --git a/spec/unit/knife/ssl_fetch_spec.rb b/spec/unit/knife/ssl_fetch_spec.rb
index cd0e423459..5982ed9470 100644
--- a/spec/unit/knife/ssl_fetch_spec.rb
+++ b/spec/unit/knife/ssl_fetch_spec.rb
@@ -139,7 +139,7 @@ E
context "when the TLS connection is successful" do
before do
- expect(TCPSocket).to receive(:new).with("foo.example.com", 8443).and_return(tcp_socket)
+ expect(ssl_fetch).to receive(:proxified_socket).with("foo.example.com", 8443).and_return(tcp_socket)
expect(OpenSSL::SSL::SSLSocket).to receive(:new).with(tcp_socket, ssl_fetch.noverify_peer_ssl_context).and_return(ssl_socket)
expect(ssl_socket).to receive(:connect)
expect(ssl_socket).to receive(:peer_cert_chain).and_return([self_signed_crt])
@@ -161,7 +161,7 @@ E
let(:unknown_protocol_error) { OpenSSL::SSL::SSLError.new("SSL_connect returned=1 errno=0 state=SSLv2/v3 read server hello A: unknown protocol") }
before do
- expect(TCPSocket).to receive(:new).with("foo.example.com", 80).and_return(tcp_socket)
+ expect(ssl_fetch).to receive(:proxified_socket).with("foo.example.com", 80).and_return(tcp_socket)
expect(OpenSSL::SSL::SSLSocket).to receive(:new).with(tcp_socket, ssl_fetch.noverify_peer_ssl_context).and_return(ssl_socket)
expect(ssl_socket).to receive(:connect).and_raise(unknown_protocol_error)
diff --git a/spec/unit/mixin/proxified_socket_spec.rb b/spec/unit/mixin/proxified_socket_spec.rb
new file mode 100644
index 0000000000..88f71ae48b
--- /dev/null
+++ b/spec/unit/mixin/proxified_socket_spec.rb
@@ -0,0 +1,94 @@
+#
+# Author:: Tyler Ball (<tball@chef.io>)
+# Copyright:: Copyright (c) 2014 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+require "chef/mixin/proxified_socket"
+require "proxifier/proxy"
+
+class TestProxifiedSocket
+ include Chef::Mixin::ProxifiedSocket
+end
+
+describe Chef::Mixin::ProxifiedSocket do
+
+ before do
+ @original_env = ENV.to_hash
+ end
+
+ after do
+ ENV.clear
+ ENV.update(@original_env)
+ end
+
+ let(:host) { "host" }
+ let(:port) { 7979 }
+ let(:test_instance) { TestProxifiedSocket.new }
+ let(:socket_double) { instance_double(TCPSocket)}
+ let(:proxifier_double) { instance_double(Proxifier::Proxy) }
+ let(:http_uri) { "http://somehost:1" }
+ let(:https_uri) { "https://somehost:1" }
+ let(:no_proxy_spec) { nil }
+
+ shared_examples "proxified socket" do
+ it "wraps the Socket in a Proxifier::Proxy" do
+ expect(Proxifier).to receive(:Proxy).with(proxy_uri, no_proxy: no_proxy_spec).and_return(proxifier_double)
+ expect(proxifier_double).to receive(:open).with(host, port).and_return(socket_double)
+ expect(test_instance.proxified_socket(host, port)).to eq(socket_double)
+ end
+ end
+
+ context "when no proxy is set" do
+ it "returns a plain TCPSocket" do
+ expect(TCPSocket).to receive(:new).with(host, port).and_return(socket_double)
+ expect(test_instance.proxified_socket(host, port)).to eq(socket_double)
+ end
+ end
+
+ context "when https_proxy is set" do
+ before do
+ # I'm purposefully setting both of these because we prefer the https
+ # variable
+ ENV['https_proxy'] = https_uri
+ ENV['http_proxy'] = http_uri
+ end
+
+ let(:proxy_uri) { https_uri }
+ include_examples "proxified socket"
+
+ context "when no_proxy is set" do
+ # This is testing that no_proxy is also provided to Proxified
+ # when it is set
+ before do
+ ENV['no_proxy'] = no_proxy_spec
+ end
+
+ let(:no_proxy_spec) { "somehost1,somehost2" }
+ include_examples "proxified socket"
+ end
+ end
+
+ context "when http_proxy is set" do
+ before do
+ ENV['http_proxy'] = http_uri
+ end
+
+ let(:proxy_uri) { http_uri }
+ include_examples "proxified socket"
+ end
+
+end