mirror of
https://github.com/internetarchive/warcprox.git
synced 2025-01-18 13:22:09 +01:00
First release
This commit is contained in:
parent
3c917d98e1
commit
16e076cf70
149
README.md
149
README.md
@ -1,4 +1,147 @@
|
|||||||
pymiproxy
|
pymiproxy - Python Micro Interceptor Proxy
|
||||||
=========
|
==========================================
|
||||||
|
|
||||||
|
A small and sweet man-in-the-middle proxy capable of doing HTTP and HTTP over SSL.
|
||||||
|
|
||||||
|
|
||||||
|
Introduction
|
||||||
|
============
|
||||||
|
|
||||||
|
pymiproxy is a small, lightweight, man-in-the-middle proxy capable of performing HTTP and HTTPS (or SSL) inspection. The
|
||||||
|
proxy provides a built-in certificate authority that is capable of generating certificates for SSL-based destinations.
|
||||||
|
Pymiproxy is also extensible and provides two methods for extending the proxy: method overloading, and a pluggable
|
||||||
|
interface. It is ideal for situations where you're in dire need of a cool proxy to tamper with out- and/or in-bound HTTP
|
||||||
|
data.
|
||||||
|
|
||||||
|
Installation Requirements
|
||||||
|
=========================
|
||||||
|
|
||||||
|
The following modules are required:
|
||||||
|
|
||||||
|
- pyOpenSSL
|
||||||
|
|
||||||
|
|
||||||
|
Installation
|
||||||
|
============
|
||||||
|
|
||||||
|
Just run the following command at the command prompt:
|
||||||
|
|
||||||
|
$ sudo python setup.py install
|
||||||
|
|
||||||
|
|
||||||
|
Usage
|
||||||
|
=====
|
||||||
|
|
||||||
|
The module offers a few examples in the code. In brief, pymiproxy can be run right-away by issuing the following command
|
||||||
|
at the the command-prompt:
|
||||||
|
|
||||||
|
$ python -m miproxy.proxy
|
||||||
|
|
||||||
|
This will invoke pymiproxy with the *DebugInterceptor* plugin which simply outputs the first 100 bytes of each request
|
||||||
|
and response. The proxy runs on port 8080 and listens on all addresses. Go ahead and give it a try.
|
||||||
|
|
||||||
|
===================================
|
||||||
|
Extending or Implementing pymiproxy
|
||||||
|
===================================
|
||||||
|
|
||||||
|
There are two ways of extending the proxy:
|
||||||
|
|
||||||
|
|
||||||
|
- Develop and register an Interceptor plugin; or
|
||||||
|
- Overload the mitm_request, and mitm_response methods in the ProxyHandler class.
|
||||||
|
|
||||||
|
|
||||||
|
The decision on which method you choose to use is entirely dependant on whether or not you wish to push the data being
|
||||||
|
intercepted through a set of interceptors or not.
|
||||||
|
|
||||||
|
-------------------
|
||||||
|
Interceptor Plugins
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
There are currently two types of interceptor plugins:
|
||||||
|
|
||||||
|
- RequestInterceptorPlugins: executed prior to sending the request to the remote server; and
|
||||||
|
- ResponseInterceptorPlugins: executed prior to sending the response back to the client.
|
||||||
|
|
||||||
|
The following flow is taken by pymiproxy in this mode:
|
||||||
|
|
||||||
|
1. Client request received
|
||||||
|
2. Client request parsed
|
||||||
|
3. Client request processed/transformed by Request Interceptor plugins
|
||||||
|
4. Updated request sent to remote server
|
||||||
|
5. Response received by remote server
|
||||||
|
6. Response processed/transformed by Response Interceptor plugins
|
||||||
|
7. Updated response sent to client
|
||||||
|
|
||||||
|
You can register as many plugins as you wish. However, keep in mind that plugins are executed in the order that they are
|
||||||
|
registered in. Take care in how you register your plugins if the result of one plugin is dependent on the result of
|
||||||
|
another.
|
||||||
|
|
||||||
|
The following is a simple code example of how to run the proxy with plugins:
|
||||||
|
|
||||||
|
from miproxy.proxy import RequestInterceptorPlugin, ResponseInterceptorPlugin, AsyncMitmProxy
|
||||||
|
|
||||||
|
class DebugInterceptor(RequestInterceptorPlugin, ResponseInterceptorPlugin):
|
||||||
|
|
||||||
|
def do_request(self, data):
|
||||||
|
print '>> %s' % repr(data[:100])
|
||||||
|
return data
|
||||||
|
|
||||||
|
def do_response(self, data):
|
||||||
|
print '<< %s' % repr(data[:100])
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
proxy = None
|
||||||
|
if not argv[1:]:
|
||||||
|
proxy = AsyncMitmProxy()
|
||||||
|
else:
|
||||||
|
proxy = AsyncMitmProxy(ca_file=argv[1])
|
||||||
|
proxy.register_interceptor(DebugInterceptor)
|
||||||
|
try:
|
||||||
|
proxy.serve_forever()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
proxy.server_close()
|
||||||
|
|
||||||
|
|
||||||
|
------------------
|
||||||
|
Method Overloading
|
||||||
|
------------------
|
||||||
|
|
||||||
|
The alternate approach to extending the proxy functionality is to subclass the ProxyHandler class and overload the
|
||||||
|
mitm_request and mitm_response methods. The following is a quick example:
|
||||||
|
|
||||||
|
from miproxy.proxy import AsyncMitmProxy
|
||||||
|
|
||||||
|
class MitmProxyHandler(ProxyHandler):
|
||||||
|
|
||||||
|
def mitm_request(self, data):
|
||||||
|
print '>> %s' % repr(data[:100])
|
||||||
|
return data
|
||||||
|
|
||||||
|
def mitm_response(self, data):
|
||||||
|
print '<< %s' % repr(data[:100])
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
proxy = None
|
||||||
|
if not argv[1:]:
|
||||||
|
proxy = AsyncMitmProxy(RequestHandlerClass=MitmProxyHandler)
|
||||||
|
else:
|
||||||
|
proxy = AsyncMitmProxy(RequestHandlerClass=MitmProxyHandler, ca_file=argv[1])
|
||||||
|
try:
|
||||||
|
proxy.serve_forever()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
proxy.server_close()
|
||||||
|
|
||||||
|
Note: In both cases, the methods that process the data need to return the data back to the proxy handler. Otherwise,
|
||||||
|
you'll get an exception.
|
||||||
|
|
||||||
|
Kudos
|
||||||
|
=====
|
||||||
|
|
||||||
|
Thanks to the great documentation at python.org, GnuCitizen's PDP for the ideas, the pyOpenSSL group for making a great
|
||||||
|
OpenSSL API.
|
||||||
|
|
||||||
A small and sweet man-in-the-middle proxy capable of doing HTTP and HTTP over SSL.
|
|
@ -2,12 +2,14 @@
|
|||||||
|
|
||||||
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
|
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
|
||||||
from SocketServer import ThreadingMixIn
|
from SocketServer import ThreadingMixIn
|
||||||
|
from os import path, mkdir, listdir
|
||||||
from httplib import HTTPResponse
|
from httplib import HTTPResponse
|
||||||
|
from tempfile import gettempdir
|
||||||
from urlparse import urlparse
|
from urlparse import urlparse
|
||||||
from ssl import wrap_socket
|
from ssl import wrap_socket
|
||||||
from os import path, mkdir
|
|
||||||
from socket import socket
|
from socket import socket
|
||||||
from re import compile
|
from re import compile
|
||||||
|
from sys import argv
|
||||||
|
|
||||||
from OpenSSL.crypto import (X509Extension, X509, dump_privatekey, dump_certificate, load_certificate, load_privatekey,
|
from OpenSSL.crypto import (X509Extension, X509, dump_privatekey, dump_certificate, load_certificate, load_privatekey,
|
||||||
PKey, TYPE_RSA, X509Req)
|
PKey, TYPE_RSA, X509Req)
|
||||||
@ -36,16 +38,25 @@ __all__ = [
|
|||||||
|
|
||||||
class CertificateAuthority(object):
|
class CertificateAuthority(object):
|
||||||
|
|
||||||
def __init__(self, ca_file='ca.pem'):
|
def __init__(self, ca_file='ca.pem', cache_dir=gettempdir()):
|
||||||
self.ca_file = 'ca.pem'
|
self.ca_file = ca_file
|
||||||
self._serial = 1
|
self.cache_dir = cache_dir
|
||||||
if not path.exists('.ssl'):
|
self._serial = self._get_serial()
|
||||||
mkdir('.ssl')
|
|
||||||
if not path.exists(ca_file):
|
if not path.exists(ca_file):
|
||||||
self._generate_ca()
|
self._generate_ca()
|
||||||
else:
|
else:
|
||||||
self._read_ca(ca_file)
|
self._read_ca(ca_file)
|
||||||
|
|
||||||
|
def _get_serial(self):
|
||||||
|
s = 1
|
||||||
|
for c in filter(lambda x: x.startswith('.pymp_'), listdir(self.cache_dir)):
|
||||||
|
c = load_certificate(FILETYPE_PEM, open(path.sep.join([self.cache_dir, c])).read())
|
||||||
|
sc = c.get_serial_number()
|
||||||
|
if sc > s:
|
||||||
|
s = sc
|
||||||
|
del c
|
||||||
|
return s
|
||||||
|
|
||||||
def _generate_ca(self):
|
def _generate_ca(self):
|
||||||
# Generate key
|
# Generate key
|
||||||
self.key = PKey()
|
self.key = PKey()
|
||||||
@ -67,7 +78,7 @@ class CertificateAuthority(object):
|
|||||||
])
|
])
|
||||||
self.cert.sign(self.key, "sha1")
|
self.cert.sign(self.key, "sha1")
|
||||||
|
|
||||||
with open('ca.pem', 'wb+') as f:
|
with open(self.ca_file, 'wb+') as f:
|
||||||
f.write(dump_privatekey(FILETYPE_PEM, self.key))
|
f.write(dump_privatekey(FILETYPE_PEM, self.key))
|
||||||
f.write(dump_certificate(FILETYPE_PEM, self.cert))
|
f.write(dump_certificate(FILETYPE_PEM, self.cert))
|
||||||
|
|
||||||
@ -76,7 +87,7 @@ class CertificateAuthority(object):
|
|||||||
self.key = load_privatekey(FILETYPE_PEM, open(file).read())
|
self.key = load_privatekey(FILETYPE_PEM, open(file).read())
|
||||||
|
|
||||||
def __getitem__(self, cn):
|
def __getitem__(self, cn):
|
||||||
cnp = path.sep.join(['.ssl', '.%s.pem' % cn])
|
cnp = path.sep.join([self.cache_dir, '.pymp_%s.pem' % cn])
|
||||||
if not path.exists(cnp):
|
if not path.exists(cnp):
|
||||||
# create certificate
|
# create certificate
|
||||||
key = PKey()
|
key = PKey()
|
||||||
@ -293,7 +304,11 @@ class DebugInterceptor(RequestInterceptorPlugin, ResponseInterceptorPlugin):
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
proxy = AsyncMitmProxy()
|
proxy = None
|
||||||
|
if not argv[1:]:
|
||||||
|
proxy = AsyncMitmProxy()
|
||||||
|
else:
|
||||||
|
proxy = AsyncMitmProxy(ca_file=argv[1])
|
||||||
proxy.register_interceptor(DebugInterceptor)
|
proxy.register_interceptor(DebugInterceptor)
|
||||||
try:
|
try:
|
||||||
proxy.serve_forever()
|
proxy.serve_forever()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user