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

Add support for authenticated proxies #1129

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
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
2 changes: 1 addition & 1 deletion .travis.s3cfg
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ multipart_chunk_size_mb = 15
multipart_max_chunks = 10000
preserve_attrs = True
progress_meter = True
proxy_host =
proxy_host =
proxy_port = 0
put_continue = False
recursive = False
Expand Down
17 changes: 13 additions & 4 deletions S3/ConnMan.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from .Custom_httplib27 import httplib
import ssl

from base64 import b64encode
from logging import debug
from threading import Semaphore
from time import time
Expand All @@ -26,7 +27,7 @@

from .Config import Config
from .Exceptions import ParameterError, S3SSLCertificateError
from .Utils import getBucketFromHostname
from .Utils import getBucketFromHostname, deunicodise_s, unicodise_s



Expand Down Expand Up @@ -229,14 +230,22 @@ def __init__(self, id, hostname, ssl, cfg):
self.c = httplib.HTTPConnection(self.hostname, self.port)
debug(u'non-proxied HTTPConnection(%s, %s)', self.hostname, self.port)
else:
headers = {}
proxy_hostname = cfg.proxy_host
if '@' in cfg.proxy_host:
credential, proxy_hostname = cfg.proxy_host.split('@')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is probably better to use split('@', 1) to avoid unexpected issues.

# FIXME: Following line can't handle username or password including colon
proxy_username, proxy_password = credential.split(':')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same a previous, split(':', 1)

headers['Proxy-Authorization'] = 'Basic ' + \
unicodise_s(b64encode(deunicodise_s(('%s:%s' % (proxy_username, proxy_password)))))
if ssl:
self.c = http_connection._https_connection(cfg.proxy_host, cfg.proxy_port)
self.c = http_connection._https_connection(proxy_hostname, cfg.proxy_port)
debug(u'proxied HTTPSConnection(%s, %s)', cfg.proxy_host, cfg.proxy_port)
port = self.port and self.port or 443
self.c.set_tunnel(self.hostname, port)
self.c.set_tunnel(self.hostname, port, headers)
debug(u'tunnel to %s, %s', self.hostname, port)
else:
self.c = httplib.HTTPConnection(cfg.proxy_host, cfg.proxy_port)
self.c = httplib.HTTPConnection(proxy_hostname, cfg.proxy_port)
debug(u'proxied HTTPConnection(%s, %s)', cfg.proxy_host, cfg.proxy_port)
# No tunnel here for the moment

Expand Down
32 changes: 17 additions & 15 deletions s3cmd
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ except ImportError:
# python2 fallback code
from distutils.spawn import find_executable as which

try:
# python 3 support
from urlparse import urlparse
except ImportError:
from urllib.parse import urlparse
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a detail, but you can probably exchange the imports and have the python3 "from urlib.parse import urlparse" case first as it should now be the default case.



def output(message):
sys.stdout.write(message + "\n")
Expand Down Expand Up @@ -2342,21 +2348,13 @@ def run_configure(config_file, args):
("gpg_passphrase", "Encryption password", "Encryption password is used to protect your files from reading\nby unauthorized persons while in transfer to S3"),
("gpg_command", "Path to GPG program"),
("use_https", "Use HTTPS protocol", "When using secure HTTPS protocol all communication with Amazon S3\nservers is protected from 3rd party eavesdropping. This method is\nslower than plain HTTP, and can only be proxied with Python 2.7 or newer"),
("proxy_host", "HTTP Proxy server name", "On some networks all internet access must go through a HTTP proxy.\nTry setting it here if you can't connect to S3 directly"),
("proxy_host", "username:[email protected]", "HTTP Proxy server name", "On some networks all internet access must go through a HTTP proxy.\nTry setting it here if you can't connect to S3 directly"),
("proxy_port", "HTTP Proxy server port"),
]
## Option-specfic defaults
if getattr(cfg, "gpg_command") == "":
setattr(cfg, "gpg_command", which("gpg"))

if getattr(cfg, "proxy_host") == "" and os.getenv("http_proxy"):
autodetected_encoding = locale.getpreferredencoding() or "UTF-8"
re_match=re.match(r"(http://)?([^:]+):(\d+)",
unicodise_s(os.getenv("http_proxy"), autodetected_encoding))
if re_match:
setattr(cfg, "proxy_host", re_match.groups()[1])
setattr(cfg, "proxy_port", re_match.groups()[2])

try:
# Support for python3
# raw_input only exists in py2 and was renamed to input in py3
Expand All @@ -2372,12 +2370,16 @@ def run_configure(config_file, args):
for option in options:
prompt = option[1]
## Option-specific handling
if option[0] == 'proxy_host' and getattr(cfg, 'use_https') == True and sys.hexversion < 0x02070000:
setattr(cfg, option[0], "")
continue
if option[0] == 'proxy_port' and getattr(cfg, 'proxy_host') == "":
setattr(cfg, option[0], 0)
continue
if option[0] == "proxy_host":
autodetected_encoding = locale.getpreferredencoding() or "UTF-8"
key = "https_proxy" if getattr(cfg, 'use_https') else "http_proxy"
url = urlparse(unicodise_s(os.getenv(key), autodetected_encoding))
if url.port is None:
port = 80 if url.scheme == "http" else 443 # httplib supports HTTP(S) proxy only
else:
port = url.port
setattr(cfg, "proxy_host", "%s:%s@%s" % (url.username, url.password, url.hostname))
setattr(cfg, "proxy_port", port)

try:
val = getattr(cfg, option[0])
Expand Down