Skip to content

Commit 7ccdc2a

Browse files
authored
Merge pull request #377 from abhinavsingh/develop
v2.2.0
2 parents e827eaa + f45614a commit 7ccdc2a

37 files changed

+820
-567
lines changed

.github/workflows/test-brew.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
- name: Setup Python
1818
uses: actions/setup-python@v1
1919
with:
20-
python-version: ${{ matrix.python }}-dev
20+
python-version: ${{ matrix.python }}
2121
- name: Brew
2222
run: |
2323
brew install ./helper/homebrew/develop/proxy.rb

.github/workflows/test-docker.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
- name: Setup Python
1818
uses: actions/setup-python@v1
1919
with:
20-
python-version: ${{ matrix.python }}-dev
20+
python-version: ${{ matrix.python }}
2121
- name: Install dependencies
2222
run: |
2323
python -m pip install --upgrade pip

.github/workflows/test-library.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@ jobs:
99
strategy:
1010
matrix:
1111
os: [macOS, ubuntu, windows]
12-
python: [3.5, 3.6, 3.7, 3.8]
12+
python: [3.6, 3.7, 3.8]
1313
max-parallel: 4
1414
fail-fast: false
1515
steps:
1616
- uses: actions/checkout@v1
1717
- name: Setup Python
1818
uses: actions/setup-python@v1
1919
with:
20-
python-version: ${{ matrix.python }}-dev
20+
python-version: ${{ matrix.python }}
2121
- name: Install dependencies
2222
run: |
2323
python -m pip install --upgrade pip

Dockerfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ LABEL com.abhinavsingh.name="abhinavsingh/proxy.py" \
2020

2121
COPY --from=builder /deps /usr/local
2222

23+
# Install openssl to enable TLS interception within container
24+
RUN apk update && apk add openssl
25+
2326
EXPOSE 8899/tcp
2427
ENTRYPOINT [ "proxy" ]
2528
CMD [ "--hostname=0.0.0.0" ]

README.md

Lines changed: 193 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
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.
677678
Start `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.
712713
Start `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
779780
By 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
786787
Lets 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+
[![NOTE](https://img.shields.io/static/v1?label=MacOS&message=note&color=yellow)](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.
854863
Now 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+
857949
Proxy Over SSH Tunnel
858950
=====================
859951
952+
**This is a WIP and may not work as documented**
953+
860954
Requires `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)
12431399
for 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
12611417
positional 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
13191496
Make 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
14311608
optional 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

Comments
 (0)