2020[ ![ Contributions Welcome] ( https://img.shields.io/static/v1?label=contributions&message=welcome%20%F0%9F%91%8D&color=green )] ( https://github.com/abhinavsingh/proxy.py/issues )
2121[ ![ Gitter] ( https://badges.gitter.im/proxy-py/community.svg )] ( https://gitter.im/proxy-py/community )
2222
23- [ ![ Python 3.5 ] ( https://img.shields.io/static/v1?label=Python&message=3.5 %20%7C%203.6 %20%7C%203.7 %20%7C%203.8 &color=blue )] ( https://www.python.org/ )
23+ [ ![ Python 3.x ] ( https://img.shields.io/static/v1?label=Python&message=3.6 %20%7C%203.7 %20%7C%203.8 %20%7C%203.9 &color=blue )] ( https://www.python.org/ )
2424[ ![ Checked with mypy] ( https://img.shields.io/static/v1?label=MyPy&message=checked&color=blue )] ( http://mypy-lang.org/ )
2525
2626[ ![ Become a Backer] ( https://opencollective.com/proxypy/tiers/backer.svg?avatarHeight=72 )] ( https://opencollective.com/proxypy )
@@ -63,6 +63,7 @@ Table of Contents
6363 * [ Plugin Ordering] ( #plugin-ordering )
6464* [ End-to-End Encryption] ( #end-to-end-encryption )
6565* [ TLS Interception] ( #tls-interception )
66+ * [ TLS Interception With Docker] ( #tls-interception-with-docker )
6667* [ Proxy Over SSH Tunnel] ( #proxy-over-ssh-tunnel )
6768 * [ Proxy Remote Requests Locally] ( #proxy-remote-requests-locally )
6869 * [ Proxy Local Requests Remotely] ( #proxy-local-requests-remotely )
@@ -677,7 +678,7 @@ Extend in-built Web Server to add Reverse Proxy capabilities.
677678Start `proxy.py` as:
678679
679680```bash
680- ❯ proxy \
681+ ❯ proxy --enable-web-server \
681682 --plugins proxy.plugin.ReverseProxyPlugin
682683```
683684
@@ -712,7 +713,7 @@ Demonstrates inbuilt web server routing using plugin.
712713Start `proxy.py` as:
713714
714715```bash
715- ❯ proxy \
716+ ❯ proxy --enable-web-server \
716717 --plugins proxy.plugin.WebServerPlugin
717718```
718719
@@ -777,14 +778,14 @@ TLS Interception
777778=================
778779
779780By default, `proxy.py` will not decrypt `https` traffic between client and server.
780- To enable TLS interception first generate CA certificates:
781+ To enable TLS interception first generate root CA certificates:
781782
782- ```
783- make ca-certificates
783+ ```bash
784+ ❯ make ca-certificates
784785```
785786
786787Lets also enable `CacheResponsePlugin` so that we can verify decrypted
787- response from the server. Start `proxy.py` as:
788+ response from the server. Start `proxy.py` as:
788789
789790```bash
790791❯ proxy \
@@ -794,7 +795,15 @@ response from the server. Start `proxy.py` as:
794795 --ca-signing-key-file ca-signing-key.pem
795796```
796797
797- Verify using `curl -v -x localhost:8899 --cacert ca-cert.pem https://httpbin.org/get`
798+
799+ [](https://github.com/abhinavsingh/proxy.py#flags) Also provide explicit CA bundle path needed for validation of peer certificates. See `--ca-file` flag.
800+
801+
802+ Verify TLS interception using `curl`
803+
804+ ```bash
805+ ❯ curl -v -x localhost:8899 --cacert ca-cert.pem https://httpbin.org/get
806+ ```
798807
799808```bash
800809* issuer: C=US; ST=CA; L=SanFrancisco; O=proxy.py; OU=CA; CN=Proxy PY CA; [email protected] @@ -854,9 +863,94 @@ cached file instead of plain text.
854863Now use CA flags with other
855864[plugin examples](#plugin-examples) to see them work with `https` traffic.
856865
866+ ## TLS Interception With Docker
867+
868+ Important notes about TLS Interception with Docker container:
869+
870+ - Since `v2.2.0`, `proxy.py` docker container also ships with `openssl`. This allows `proxy.py`
871+ to generate certificates on the fly for TLS Interception.
872+
873+ - For security reasons, `proxy.py` docker container doesn' t ship with CA certificates.
874+
875+ Here is how to start a ` proxy.py` docker container
876+ with TLS Interception:
877+
878+ 1. Generate CA certificates on host computer
879+
880+ ` ` ` bash
881+ ❯ make ca-certificates
882+ ` ` `
883+
884+ 2. Copy all generated certificates into a separate directory. We' ll later mount this directory into our docker container
885+
886+ ```bash
887+ ❯ mkdir /tmp/ca-certificates
888+ ❯ cp ca-cert.pem ca-key.pem ca-signing-key.pem /tmp/ca-certificates
889+ ```
890+
891+ 3. Start docker container
892+
893+ ```bash
894+ ❯ docker run -it --rm \
895+ -v /tmp/ca-certificates:/tmp/ca-certificates \
896+ -p 8899:8899 \
897+ abhinavsingh/proxy.py:latest \
898+ --hostname 0.0.0.0 \
899+ --plugins proxy.plugin.CacheResponsesPlugin \
900+ --ca-key-file /tmp/ca-certificates/ca-key.pem \
901+ --ca-cert-file /tmp/ca-certificates/ca-cert.pem \
902+ --ca-signing-key /tmp/ca-certificates/ca-signing-key.pem
903+ ```
904+
905+ - `-v /tmp/ca-certificates:/tmp/ca-certificates` flag mounts our CA certificate directory in container environment
906+ - `--plugins proxy.plugin.CacheResponsesPlugin` enables `CacheResponsesPlugin` so that we can inspect intercepted traffic
907+ - `--ca-*` flags enable TLS Interception.
908+
909+ 4. From another terminal, try TLS Interception using `curl`. You can omit `--cacert` flag if CA certificate is already trusted by the system.
910+
911+ ```bash
912+ ❯ curl -v \
913+ --cacert ca-cert.pem \
914+ -x 127.0.0.1:8899 \
915+ https://httpbin.org/get
916+ ```
917+
918+ 5. Verify `issuer` field from response headers.
919+
920+ ```bash
921+ * Server certificate:
922+ * subject: CN=httpbin.org; C=NA; ST=Unavailable; L=Unavailable; O=Unavailable; OU=Unavailable
923+ * start date: Jun 17 09:26:57 2020 GMT
924+ * expire date: Jun 17 09:26:57 2022 GMT
925+ * subjectAltName: host "httpbin.org" matched cert' s " httpbin.org"
926+ * issuer: CN=example.com
927+ * SSL certificate verify ok.
928+ ` ` `
929+
930+ 6. Back on docker terminal, copy response dump path logs.
931+
932+ ` ` ` bash
933+ ...[redacted]... [I] access_log:338 - 172.17.0.1:56498 - CONNECT httpbin.org:443 - 1031 bytes - 1216.70 ms
934+ ...[redacted]... [I] close:49 - Cached response at /tmp/httpbin.org-ae1a927d064e4ab386ea319eb38fe251.txt
935+ ` ` `
936+
937+ 7. In another terminal, ` cat` the response dump:
938+
939+ ` ` ` bash
940+ ❯ docker exec -it $( docker ps | grep proxy.py | awk ' { print $1 }' ) cat /tmp/httpbin.org-ae1a927d064e4ab386ea319eb38fe251.txt
941+ HTTP/1.1 200 OK
942+ ...[redacted]...
943+ {
944+ ...[redacted]...,
945+ " url" : " http://httpbin.org/get"
946+ }
947+ ` ` `
948+
857949Proxy Over SSH Tunnel
858950=====================
859951
952+ ** This is a WIP and may not work as documented**
953+
860954Requires ` paramiko` to work. See [requirements-tunnel.txt](https://github.com/abhinavsingh/proxy.py/blob/develop/requirements-tunnel.txt)
861955
862956# # Proxy Remote Requests Locally
@@ -1226,20 +1320,82 @@ b'GET / HTTP/1.1\r\nConnection: close\r\n\r\n'
12261320
12271321### build_http_response
12281322
1229- TODO
1323+ ```python
1324+ build_http_response(
1325+ status_code: int,
1326+ protocol_version: bytes = HTTP_1_1,
1327+ reason: Optional[bytes] = None,
1328+ headers: Optional[Dict[bytes, bytes]] = None,
1329+ body: Optional[bytes] = None) -> bytes
1330+ ```
12301331
12311332## PKI
12321333
12331334### API Usage
12341335
12351336#### gen_private_key
1337+
1338+ ```python
1339+ gen_private_key(
1340+ key_path: str,
1341+ password: str,
1342+ bits: int = 2048,
1343+ timeout: int = 10) -> bool
1344+ ```
1345+
12361346#### gen_public_key
1347+
1348+ ```python
1349+ gen_public_key(
1350+ public_key_path: str,
1351+ private_key_path: str,
1352+ private_key_password: str,
1353+ subject: str,
1354+ alt_subj_names: Optional[List[str]] = None,
1355+ extended_key_usage: Optional[str] = None,
1356+ validity_in_days: int = 365,
1357+ timeout: int = 10) -> bool
1358+ ```
1359+
12371360#### remove_passphrase
1361+
1362+ ```python
1363+ remove_passphrase(
1364+ key_in_path: str,
1365+ password: str,
1366+ key_out_path: str,
1367+ timeout: int = 10) -> bool
1368+ ```
1369+
12381370#### gen_csr
1371+
1372+ ```python
1373+ gen_csr(
1374+ csr_path: str,
1375+ key_path: str,
1376+ password: str,
1377+ crt_path: str,
1378+ timeout: int = 10) -> bool
1379+ ```
1380+
12391381#### sign_csr
12401382
1241- See [pki.py](https://github.com/abhinavsingh/proxy.py/blob/develop/proxy/common/pki.py) for
1242- method parameters and [test_pki.py](https://github.com/abhinavsingh/proxy.py/blob/develop/tests/common/test_pki.py)
1383+ ```python
1384+ sign_csr(
1385+ csr_path: str,
1386+ crt_path: str,
1387+ ca_key_path: str,
1388+ ca_key_password: str,
1389+ ca_crt_path: str,
1390+ serial: str,
1391+ alt_subj_names: Optional[List[str]] = None,
1392+ extended_key_usage: Optional[str] = None,
1393+ validity_in_days: int = 365,
1394+ timeout: int = 10) -> bool
1395+ ```
1396+
1397+ See [pki.py](https://github.com/abhinavsingh/proxy.py/blob/develop/proxy/common/pki.py) and
1398+ [test_pki.py](https://github.com/abhinavsingh/proxy.py/blob/develop/tests/common/test_pki.py)
12431399for usage examples.
12441400
12451401### CLI Usage
@@ -1256,7 +1412,7 @@ usage: pki.py [-h] [--password PASSWORD] [--private-key-path PRIVATE_KEY_PATH]
12561412 [--public-key-path PUBLIC_KEY_PATH] [--subject SUBJECT]
12571413 action
12581414
1259- proxy.py v2.1 .0 : PKI Utility
1415+ proxy.py v2.2 .0 : PKI Utility
12601416
12611417positional arguments:
12621418 action Valid actions: remove_passphrase, gen_private_key,
@@ -1310,10 +1466,31 @@ start `proxy.py` with `--threadless` flag.
13101466
13111467## SyntaxError: invalid syntax
13121468
1313- Make sure you are using ` Python 3` . Verify the version before running ` proxy.py` :
1469+ `proxy.py` is strictly typed and uses Python `typing` annotations. Example:
1470+
1471+ ```python
1472+ >>> my_strings : List[str] = []
1473+ >>> #############^^^^^^^^^#####
1474+ ```
1475+
1476+ Hence a Python version that understands typing annotations is required.
1477+ Make sure you are using `Python 3.6+`.
1478+
1479+ Verify the version before running `proxy.py`:
13141480
13151481`❯ python --version`
13161482
1483+ All `typing` annotations can be replaced with `comment-only` annotations. Example:
1484+
1485+ ```python
1486+ >>> my_strings = [] # List[str]
1487+ >>> ################^^^^^^^^^^^
1488+ ```
1489+
1490+ It will enable `proxy.py` to run on Python `pre-3.6`, even on `2.7`.
1491+ However, as all future versions of Python will support `typing` annotations,
1492+ this has not been considered.
1493+
13171494## Unable to load plugins
13181495
13191496Make sure plugin modules are discoverable by adding them to `PYTHONPATH`. Example:
@@ -1426,7 +1603,7 @@ usage: proxy [-h] [--backlog BACKLOG] [--basic-auth BASIC_AUTH]
14261603 [--static-server-dir STATIC_SERVER_DIR] [--threadless]
14271604 [--timeout TIMEOUT] [--version]
14281605
1429- proxy.py v2.1 .0
1606+ proxy.py v2.2 .0
14301607
14311608optional arguments:
14321609 -h, --help show this help message and exit
@@ -1447,6 +1624,8 @@ optional arguments:
14471624 Default: None. Signing certificate to use for signing
14481625 dynamically generated HTTPS certificates. If used,
14491626 must also pass --ca-key-file and --ca-signing-key-file
1627+ --ca-file CA_FILE Default: None. Provide path to custom CA file for peer
1628+ certificate validation. Specially useful on MacOS.
14501629 --ca-signing-key-file CA_SIGNING_KEY_FILE
14511630 Default: None. CA signing key to use for dynamic
14521631 generation of HTTPS certificates. If used, must also
0 commit comments