summaryrefslogtreecommitdiffstats
path: root/src/lib/Bcfg2/Proxy.py
diff options
context:
space:
mode:
authorChris St. Pierre <chris.a.st.pierre@gmail.com>2013-02-05 14:04:09 -0500
committerChris St. Pierre <chris.a.st.pierre@gmail.com>2013-02-05 14:04:09 -0500
commit25cb6db5ccb0c8e8302c220a90344a95baf3909b (patch)
tree80d6a81f2dcd164f0b127bdfe75a4b2d833c6be6 /src/lib/Bcfg2/Proxy.py
parent5d237f71575a109c10d5aad8d70dc5dda00a2d96 (diff)
downloadbcfg2-25cb6db5ccb0c8e8302c220a90344a95baf3909b.tar.gz
bcfg2-25cb6db5ccb0c8e8302c220a90344a95baf3909b.tar.bz2
bcfg2-25cb6db5ccb0c8e8302c220a90344a95baf3909b.zip
moved some libraries in Bcfg2/ into more specific (Server/ or Client/) places
Diffstat (limited to 'src/lib/Bcfg2/Proxy.py')
-rw-r--r--src/lib/Bcfg2/Proxy.py357
1 files changed, 0 insertions, 357 deletions
diff --git a/src/lib/Bcfg2/Proxy.py b/src/lib/Bcfg2/Proxy.py
deleted file mode 100644
index 3b406c78e..000000000
--- a/src/lib/Bcfg2/Proxy.py
+++ /dev/null
@@ -1,357 +0,0 @@
-import logging
-import re
-import socket
-
-# The ssl module is provided by either Python 2.6 or a separate ssl
-# package that works on older versions of Python (see
-# http://pypi.python.org/pypi/ssl). If neither can be found, look for
-# M2Crypto instead.
-try:
- import ssl
- SSL_LIB = 'py26_ssl'
- SSL_ERROR = ssl.SSLError
-except ImportError:
- from M2Crypto import SSL
- import M2Crypto.SSL.Checker
- SSL_LIB = 'm2crypto'
- SSL_ERROR = SSL.SSLError
-
-import sys
-import time
-
-# Compatibility imports
-from Bcfg2.Compat import httplib, xmlrpclib, urlparse
-
-version = sys.version_info[:2]
-has_py26 = version >= (2, 6)
-
-__all__ = ["ComponentProxy",
- "RetryMethod",
- "SSLHTTPConnection",
- "XMLRPCTransport"]
-
-
-class ProxyError(Exception):
- """ ProxyError provides a consistent reporting interface to
- the various xmlrpclib errors that might arise (mainly
- ProtocolError and Fault) """
- def __init__(self, err):
- msg = None
- if isinstance(err, xmlrpclib.ProtocolError):
- # cut out the password in the URL
- url = re.sub(r'([^:]+):(.*?)@([^@]+:\d+/)', r'\1:******@\3',
- err.url)
- msg = "XML-RPC Protocol Error for %s: %s (%s)" % (url,
- err.errmsg,
- err.errcode)
- elif isinstance(err, xmlrpclib.Fault):
- msg = "XML-RPC Fault: %s (%s)" % (err.faultString,
- err.faultCode)
- else:
- msg = str(err)
- Exception.__init__(self, msg)
-
-class CertificateError(Exception):
- def __init__(self, commonName):
- self.commonName = commonName
- def __str__(self):
- return ("Got unallowed commonName %s from server"
- % self.commonName)
-
-_orig_Method = xmlrpclib._Method
-
-class RetryMethod(xmlrpclib._Method):
- """Method with error handling and retries built in."""
- log = logging.getLogger('xmlrpc')
- max_retries = 3
- retry_delay = 1
-
- def __call__(self, *args):
- for retry in range(self.max_retries):
- if retry >= self.max_retries - 1:
- final = True
- else:
- final = False
- msg = None
- try:
- return _orig_Method.__call__(self, *args)
- except xmlrpclib.ProtocolError:
- err = sys.exc_info()[1]
- msg = "Server failure: Protocol Error: %s %s" % \
- (err.errcode, err.errmsg)
- except xmlrpclib.Fault:
- msg = sys.exc_info()[1]
- except socket.error:
- err = sys.exc_info()[1]
- if hasattr(err, 'errno') and err.errno == 336265218:
- msg = "SSL Key error: %s" % err
- elif hasattr(err, 'errno') and err.errno == 185090050:
- msg = "SSL CA error: %s" % err
- elif final:
- msg = "Server failure: %s" % err
- except CertificateError:
- err = sys.exc_info()[1]
- msg = "Got unallowed commonName %s from server" % \
- err.commonName
- except KeyError:
- err = sys.exc_info()[1]
- msg = "Server disallowed connection: %s" % err
- except ProxyError:
- err = sys.exc_info()[1]
- msg = err
- except:
- raise
- etype, err = sys.exc_info()[:2]
- msg = "Unknown failure: %s (%s)" % (err, etype.__name__)
- if msg:
- if final:
- self.log.error(msg)
- raise ProxyError(msg)
- else:
- self.log.info(msg)
- time.sleep(self.retry_delay)
-
-xmlrpclib._Method = RetryMethod
-
-
-class SSLHTTPConnection(httplib.HTTPConnection):
- """Extension of HTTPConnection that
- implements SSL and related behaviors.
- """
-
- logger = logging.getLogger('Bcfg2.Proxy.SSLHTTPConnection')
-
- def __init__(self, host, port=None, strict=None, timeout=90, key=None,
- cert=None, ca=None, scns=None, protocol='xmlrpc/ssl'):
- """Initializes the `httplib.HTTPConnection` object and stores security
- parameters
-
- Parameters
- ----------
- host : string
- Name of host to contact
- port : int, optional
- Port on which to contact the host. If none is specified,
- the default port of 80 will be used unless the `host`
- string has a port embedded in the form host:port.
- strict : Boolean, optional
- Passed to the `httplib.HTTPConnection` constructor and if
- True, causes the `BadStatusLine` exception to be raised if
- the status line cannot be parsed as a valid HTTP 1.0 or
- 1.1 status.
- timeout : int, optional
- Causes blocking operations to timeout after `timeout`
- seconds.
- key : string, optional
- The file system path to the local endpoint's SSL key. May
- specify the same file as `cert` if using a file that
- contains both. See
- http://docs.python.org/library/ssl.html#ssl-certificates
- for details. Required if using xmlrpc/ssl with client
- certificate authentication.
- cert : string, optional
- The file system path to the local endpoint's SSL
- certificate. May specify the same file as `cert` if using
- a file that contains both. See
- http://docs.python.org/library/ssl.html#ssl-certificates
- for details. Required if using xmlrpc/ssl with client
- certificate authentication.
- ca : string, optional
- The file system path to a set of concatenated certificate
- authority certs, which are used to validate certificates
- passed from the other end of the connection.
- scns : array-like, optional
- List of acceptable server commonNames. The peer cert's
- common name must appear in this list, otherwise the
- connect() call will throw a `CertificateError`.
- protocol : {'xmlrpc/ssl', 'xmlrpc/tlsv1'}, optional
- Communication protocol to use.
-
- """
- if not has_py26:
- httplib.HTTPConnection.__init__(self, host, port, strict)
- else:
- httplib.HTTPConnection.__init__(self, host, port, strict, timeout)
- self.key = key
- self.cert = cert
- self.ca = ca
- self.scns = scns
- self.protocol = protocol
- self.timeout = timeout
-
- def connect(self):
- """Initiates a connection using previously set attributes."""
- if SSL_LIB == 'py26_ssl':
- self._connect_py26ssl()
- elif SSL_LIB == 'm2crypto':
- self._connect_m2crypto()
- else:
- raise Exception("No SSL module support")
-
- def _connect_py26ssl(self):
- """Initiates a connection using the ssl module."""
- # check for IPv6
- hostip = socket.getaddrinfo(self.host,
- self.port,
- socket.AF_UNSPEC,
- socket.SOCK_STREAM)[0][4][0]
- if ':' in hostip:
- rawsock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
- else:
- rawsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- if self.protocol == 'xmlrpc/ssl':
- ssl_protocol_ver = ssl.PROTOCOL_SSLv23
- elif self.protocol == 'xmlrpc/tlsv1':
- ssl_protocol_ver = ssl.PROTOCOL_TLSv1
- else:
- self.logger.error("Unknown protocol %s" % (self.protocol))
- raise Exception("unknown protocol %s" % self.protocol)
- if self.ca:
- other_side_required = ssl.CERT_REQUIRED
- else:
- other_side_required = ssl.CERT_NONE
- self.logger.warning("No ca is specified. Cannot authenticate the server with SSL.")
- if self.cert and not self.key:
- self.logger.warning("SSL cert specfied, but no key. Cannot authenticate this client with SSL.")
- self.cert = None
- if self.key and not self.cert:
- self.logger.warning("SSL key specfied, but no cert. Cannot authenticate this client with SSL.")
- self.key = None
-
- rawsock.settimeout(self.timeout)
- self.sock = ssl.SSLSocket(rawsock, cert_reqs=other_side_required,
- ca_certs=self.ca, suppress_ragged_eofs=True,
- keyfile=self.key, certfile=self.cert,
- ssl_version=ssl_protocol_ver)
- self.sock.connect((self.host, self.port))
- peer_cert = self.sock.getpeercert()
- if peer_cert and self.scns:
- scn = [x[0][1] for x in peer_cert['subject'] if x[0][0] == 'commonName'][0]
- if scn not in self.scns:
- raise CertificateError(scn)
- self.sock.closeSocket = True
-
- def _connect_m2crypto(self):
- """Initiates a connection using the M2Crypto module."""
-
- if self.protocol == 'xmlrpc/ssl':
- ctx = SSL.Context('sslv23')
- elif self.protocol == 'xmlrpc/tlsv1':
- ctx = SSL.Context('tlsv1')
- else:
- self.logger.error("Unknown protocol %s" % (self.protocol))
- raise Exception("unknown protocol %s" % self.protocol)
-
- if self.ca:
- # Use the certificate authority to validate the cert
- # presented by the server
- ctx.set_verify(SSL.verify_peer | SSL.verify_fail_if_no_peer_cert, depth=9)
- if ctx.load_verify_locations(self.ca) != 1:
- raise Exception('No CA certs')
- else:
- self.logger.warning("No ca is specified. Cannot authenticate the server with SSL.")
-
- if self.cert and self.key:
- # A cert/key is defined, use them to support client
- # authentication to the server
- ctx.load_cert(self.cert, self.key)
- elif self.cert:
- self.logger.warning("SSL cert specfied, but no key. Cannot authenticate this client with SSL.")
- elif self.key:
- self.logger.warning("SSL key specfied, but no cert. Cannot authenticate this client with SSL.")
-
- self.sock = SSL.Connection(ctx)
- if re.match('\\d+\\.\\d+\\.\\d+\\.\\d+', self.host):
- # host is ip address
- try:
- hostname = socket.gethostbyaddr(self.host)[0]
- except:
- # fall back to ip address
- hostname = self.host
- else:
- hostname = self.host
- try:
- self.sock.connect((hostname, self.port))
- # automatically checks cert matches host
- except M2Crypto.SSL.Checker.WrongHost:
- wr = sys.exc_info()[1]
- raise CertificateError(wr)
-
-
-class XMLRPCTransport(xmlrpclib.Transport):
- def __init__(self, key=None, cert=None, ca=None,
- scns=None, use_datetime=0, timeout=90):
- if hasattr(xmlrpclib.Transport, '__init__'):
- xmlrpclib.Transport.__init__(self, use_datetime)
- self.key = key
- self.cert = cert
- self.ca = ca
- self.scns = scns
- self.timeout = timeout
-
- def make_connection(self, host):
- host, self._extra_headers = self.get_host_info(host)[0:2]
- return SSLHTTPConnection(host,
- key=self.key,
- cert=self.cert,
- ca=self.ca,
- scns=self.scns,
- timeout=self.timeout)
-
- def request(self, host, handler, request_body, verbose=0):
- """Send request to server and return response."""
- try:
- conn = self.send_request(host, handler, request_body, False)
- response = conn.getresponse()
- errcode = response.status
- errmsg = response.reason
- headers = response.msg
- except (socket.error, SSL_ERROR):
- err = sys.exc_info()[1]
- raise ProxyError(xmlrpclib.ProtocolError(host + handler,
- 408,
- str(err),
- self._extra_headers))
-
- if errcode != 200:
- raise ProxyError(xmlrpclib.ProtocolError(host + handler,
- errcode,
- errmsg,
- headers))
-
- self.verbose = verbose
- return self.parse_response(response)
-
- if sys.hexversion < 0x03000000:
- def send_request(self, host, handler, request_body, debug):
- """ send_request() changed significantly in py3k."""
- conn = self.make_connection(host)
- xmlrpclib.Transport.send_request(self, conn, handler, request_body)
- self.send_host(conn, host)
- self.send_user_agent(conn)
- self.send_content(conn, request_body)
- return conn
-
-
-def ComponentProxy(url, user=None, password=None, key=None, cert=None, ca=None,
- allowedServerCNs=None, timeout=90, retries=3, delay=1):
-
- """Constructs proxies to components.
-
- Arguments:
- component_name -- name of the component to connect to
-
- Additional arguments are passed to the ServerProxy constructor.
-
- """
- xmlrpclib._Method.max_retries = retries
- xmlrpclib._Method.retry_delay = delay
-
- if user and password:
- method, path = urlparse(url)[:2]
- newurl = "%s://%s:%s@%s" % (method, user, password, path)
- else:
- newurl = url
- ssl_trans = XMLRPCTransport(key, cert, ca,
- allowedServerCNs, timeout=float(timeout))
- return xmlrpclib.ServerProxy(newurl, allow_none=True, transport=ssl_trans)