Skip to content

Commit 04ee7da

Browse files
authored
Merge pull request #198 from keboola/marek-CFT-3325-mTLS
Update: CFT-3325 - mTLS
2 parents 5dc2c54 + 26c22be commit 04ee7da

File tree

14 files changed

+310
-61
lines changed

14 files changed

+310
-61
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,6 @@ vendor/
1414
/python-sync-actions/src/test.py
1515
/python-sync-actions/data
1616
__pycache__/
17+
docker/keys/*
18+
!docker/keys/genkeys.sh
19+
!docker/keys/keys-to-config-json.py

.python-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.12

README.md

Lines changed: 73 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ Example:
2929

3030
```
3131
https://yourDomain.zendesk.com/api/v2/
32-
```
32+
```
3333

3434
-- OR --
3535

@@ -148,7 +148,7 @@ Moved to [new docs](https://developers.keboola.com/extend/generic-extractor/conf
148148
- **authentication.type**: `query`
149149
- **authentication.query.apiKey**: `{"attr": "apiKey"}`
150150
- this will look for the *apiKey* query parameter value in the config attribute named *apiKey*
151-
- **authentication.query.sig**:
151+
- **authentication.query.sig**:
152152
```
153153
{
154154
"function": "md5",
@@ -169,7 +169,7 @@ Moved to [new docs](https://developers.keboola.com/extend/generic-extractor/conf
169169
}
170170
]
171171
}
172-
```
172+
```
173173
- this will generate a *sig* parameter value from MD5 of merged configuration table attributes *apiKey* and *secret*, followed by current *time()* at the time of the request (time() being the PHP function)
174174
- Allowed functions are listed below in the *User functions* section
175175
- If you're using any config parameter by using `"attr": "parameterName"`, it has to be identical string to the one in the actual config, including eventual `#` if KBC Docker's encryption is used.
@@ -468,7 +468,7 @@ Moved to [new docs](https://developers.keboola.com/extend/generic-extractor/conf
468468
- sets which query parameter should contain the limit value (default to `limit`)
469469
- **pagination.offsetParam**(optional)
470470
- sets which query parameter should contain the offset value (default to `offset`)
471-
471+
472472
```
473473
"api": {
474474
"pagination": {
@@ -478,7 +478,7 @@ Moved to [new docs](https://developers.keboola.com/extend/generic-extractor/conf
478478
"offsetParam": "offset"
479479
}
480480
}
481-
```
481+
```
482482
483483
- **pagination.firstPageParams**(optional)
484484
- Whether or not include limit and offset params in the first request (default to `true`)
@@ -769,7 +769,7 @@ Moved to [new docs](https://developers.keboola.com/extend/generic-extractor/conf
769769
}
770770
]
771771
}
772-
```
772+
```
773773
- **dataType**: Type of data returned by the endpoint. It also describes a table name, where the results will be stored
774774
- **dataField**: Allows to override which field of the response will be exported.
775775
- If there's multiple arrays in the response "root" the extractor may not know which array to export and fail
@@ -842,7 +842,7 @@ Moved to [new docs](https://developers.keboola.com/extend/generic-extractor/conf
842842
"data": {"object": "can't really parse this!"}
843843
}
844844
]
845-
```
845+
```
846846
847847
- To be able to work with such response, set `"responseFilter": "data"` - it should be a path within each object of the response array, **not** including the key of the response array
848848
- To filter values within nested arrays, use `"responseFilter": "data.array[].key"`
@@ -1218,14 +1218,77 @@ Best way to create and test new configurations is run extractor in docker contai
12181218
# Running tests:
12191219
```
12201220
docker compose run --rm tests
1221-
```
1221+
```
12221222
12231223
or (with local source code and vendor copy)
12241224
12251225
```
12261226
docker compose run --rm tests-local
1227-
```
1227+
```
1228+
1229+
# Debugging mTLS support
1230+
1231+
## Generating certificates
1232+
1233+
Generate CA, server and client certificates:
1234+
1235+
```
1236+
# starting from project root
1237+
cd docker/keys
1238+
./genkeys.sh
1239+
```
1240+
1241+
The script also creates a `config.json` file with the following structure to be pasted into your own `config.json`:
1242+
1243+
```
1244+
"api": {
1245+
"baseUrl": "https://server.local/",
1246+
"caCertificate": "-- rootCA.crt --",
1247+
"#clientCertificate": "-- client.crt bundled with client.key --",
1248+
}
1249+
```
1250+
1251+
## Testing in PHP
1252+
1253+
```
1254+
to be written…
1255+
```
1256+
1257+
## Testing in Python
1258+
1259+
1. Run local nginx server:
1260+
```
1261+
# starting from project root
1262+
cd python-sync-actions
1263+
docker compose up server.local
1264+
```
1265+
1. Create a `config.json` file in `python-sync-actions/data` with the following content:
1266+
```
1267+
{
1268+
"parameters": {
1269+
"__SELECTED_JOB": "0",
1270+
"config": {
1271+
"jobs": [
1272+
{
1273+
"__NAME": "mTLS check",
1274+
"endpoint": "",
1275+
"method": "GET"
1276+
}
1277+
]
1278+
},
1279+
"api": {
1280+
}
1281+
}
1282+
}
1283+
```
1284+
1. Paste the `"api"` section generated in the `Generate certificates section` into the newly created config file.
1285+
1. Run the python-sync-actions component:
1286+
```
1287+
cd python-sync-actions
1288+
docker compose up dev
1289+
```
1290+
12281291
1229-
## License
1292+
# License
12301293
12311294
MIT licensed, see [LICENSE](./LICENSE) file.

docker-compose.yml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
version: '3'
21
services:
32
app: &app
43
build: .
@@ -35,3 +34,12 @@ services:
3534
links:
3635
- jsontest:jsontest-behind-proxy
3736

37+
server.local:
38+
image: nginx:alpine
39+
ports:
40+
- "443:443"
41+
volumes:
42+
- ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
43+
- ./docker/keys/server.crt:/etc/nginx/server.crt
44+
- ./docker/keys/server.key:/etc/nginx/server.key
45+
- ./docker/keys/rootCA.crt:/etc/nginx/ca.crt

docker/keys/genkeys.sh

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
echo "creating rootCA"
2+
openssl genrsa -out rootCA.key 4096
3+
openssl req -x509 -new -nodes -key rootCA.key -subj "/C=CZ/ST=CZ/O=authority" -days 1024 -out rootCA.crt
4+
5+
echo "creating server keys"
6+
openssl genrsa -out server.key 2048
7+
# SAN is required as it is the main place where modern clients check host name (in fact CN can be ignored -- and is by e.g. chrome or requests)
8+
openssl req -new -key server.key -subj "/C=CZ/ST=CZ/O=mytest/CN=server.local" -addext "subjectAltName=DNS:server.local" -out server.csr
9+
# Extensions such as SAN are not coppied by default from CSR when creating the certificate, -copy_extensions is required (semi-recent addition to OpenSSL)
10+
openssl x509 -req -in server.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out server.crt -days 500 -copy_extensions copy
11+
12+
echo "creating client keys"
13+
openssl genrsa -out client.key 2048
14+
openssl req -new -key client.key -subj "/C=CZ/ST=CZ/O=mytest/CN=client.local" -addext "subjectAltName=DNS:client.local" -out client.csr
15+
openssl x509 -req -in client.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out client.crt -days 500 -copy_extensions copy
16+
17+
python3 keys-to-config-json.py

docker/keys/keys-to-config-json.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#!/usr/bin/env python3
2+
3+
import json
4+
5+
6+
with open("rootCA.crt") as f:
7+
ca_cert = f.read()
8+
9+
with open("client.crt") as f:
10+
client_cert = f.read()
11+
12+
with open("client.key") as f:
13+
client_key = f.read()
14+
15+
with open("config.json", "w") as f:
16+
json.dump(
17+
{
18+
"api": {
19+
"baseUrl": "https://server.local/",
20+
"caCertificate": ca_cert,
21+
"#clientCertificate": client_cert + client_key,
22+
}
23+
},
24+
f,
25+
indent=4,
26+
)

docker/nginx/default.conf

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
server {
2+
listen 443 ssl;
3+
server_name server.local;
4+
ssl_certificate /etc/nginx/server.crt;
5+
ssl_certificate_key /etc/nginx/server.key;
6+
7+
ssl_client_certificate /etc/nginx/ca.crt;
8+
ssl_verify_client optional;
9+
10+
location / {
11+
if ($ssl_client_verify != SUCCESS) {
12+
return 403;
13+
}
14+
15+
return 200 '{"name": "Nginx", "type": "server"}';
16+
}
17+
}

python-sync-actions/Dockerfile

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,19 @@
11
FROM nikolaik/python-nodejs:python3.12-nodejs18
2-
ENV PYTHONIOENCODING utf-8
32

4-
COPY /src /code/src/
5-
COPY /tests /code/tests/
6-
COPY requirements.txt /code/requirements.txt
7-
COPY flake8.cfg /code/flake8.cfg
8-
9-
# install gcc to be able to build packages - e.g. required by regex, dateparser, also required for pandas
10-
RUN apt-get update && apt-get install -y build-essential curl
3+
RUN apt-get update && apt-get install -y curl
114

12-
# Install curlconverter using npm
135
RUN npm install --global curlconverter
146

15-
167
RUN pip install flake8
178

9+
COPY requirements.txt /code/requirements.txt
1810
RUN pip install -r /code/requirements.txt
1911

12+
COPY flake8.cfg /code/flake8.cfg
2013

14+
COPY /src /code/src/
15+
COPY /tests /code/tests/
2116

2217
WORKDIR /code/
2318

24-
2519
CMD ["python", "-u", "/code/src/component.py"]

python-sync-actions/docker-compose.yml

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
version: "2"
21
services:
32
# for development purposes
43
dev:
54
build: .
65
volumes:
76
- ./:/code
87
- ./data:/data
8+
- ../docker/keys:/code/keys
99
environment:
1010
- KBC_DATADIR=./data
1111
test:
@@ -35,8 +35,20 @@ services:
3535
tty: true
3636
stdin_open: true
3737
ports:
38-
- "8888:80"
38+
- 8888:80
3939
volumes:
4040
- ./tests/calls:/examples/
4141
environment:
42-
- KBC_EXAMPLES_DIR=/examples/
42+
- KBC_EXAMPLES_DIR=/examples/
43+
44+
# i was about to create a common network with the generic-extractor compose file
45+
# to avoid duplication, but that would create an unnecessary dependency
46+
server.local:
47+
image: nginx:alpine
48+
ports:
49+
- "443:443"
50+
volumes:
51+
- ../docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
52+
- ../docker/keys/server.crt:/etc/nginx/server.crt
53+
- ../docker/keys/server.key:/etc/nginx/server.key
54+
- ../docker/keys/rootCA.crt:/etc/nginx/ca.crt
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
keboola.component
2+
dataconf
3+
keboola.http-client
4+
keboola.utils
5+
keboola.json-to-csv==0.0.12
6+
mock==5.1.0
7+
freezegun==1.5.1
8+
nested-lookup==0.2.25
9+
python-dateutil==2.9.0.post0

0 commit comments

Comments
 (0)