Hi @Olu ,
Ok, I got a working demo that can be replicated by anyone.
If you have time and want to replicate the “problem/behavior”, I’m pasting the steps below.
If you think posting in github page as a bug would be better, let me know please.
We’ll build a Flask/Python app hosting the following endpoint/API:
GET - <TYK_GW_ENDPOINT>/myapp/token/renew
This requires two headers, one to authenticate in the Tyk endpoint and another one to identify the key that is calling the API.
Authorization: ‘Bearer <token>’
x-tykc-key: ‘<token>’
We will run two tests:
1- Running the App locally and calling the Flask API, directly from your computer, which will call tyk gateway as per the code. This will work all the times.
2- Calling the same Flask API hosted in a Kubernetes cluster (where Tyk GW is installed). This only works under a certain condition to be met.
On the second test, there are two cenarios:
1- When you hit the API for the first time with the proper settings (/myapp/token/renew), it will show the success message, but the key will not be updated.
2- Then, we’ll fake a wrong api call like /myapp/token/renew123 (the page will fail!)
3- You’ll finally hit the correct endpoint just like in step .1 , you’ll notice that the key will be updated successfully.
If you hit the correct endpoint again, it doesn’t work. But if you fake an incorrect one like /myapp/token/renew123 and try again, it will work.
Also, in every attempt, the return message shows as “modified”
To test locally:
Clone the repository
git clone https://github.com/dszortyka/poc-tyk-cli
cd poc-tyk-cli
Run pipenv to build minimum packages for the application
pipenv install --python 3.9
pipenv shell
define variables
declare -x TYK_API_KEY=<tyk api key to manage tyk gw>
declare -x TYK_GATEWAY_ENV=<the tyk gateway host only, without tyk endpoint)
run the application locally and pay attention to the logs. They are the same ones to when running from a Kubernetes POD.
python3 myapp.py
hit the application endpoint using insomnia or postman or curl
curl..
check the logs and ensure the key was updated using Tyk GW APIs.
URL: <TYK_GATEWAY_ENDPOINT>/tyk/keys/eyJvcmciOiIxIiwiaWQiOiJiZWMxNWEzNjJmNmI0NWZiYWE3Y2FjZjEyYmRmYzE5OCIsImgiOiJtdXJtdXIxMjgifQ==
Response: <Response [200]>
Key Retrieved from Tyk GW:
{'last_check': 0, 'allowance': 1000, 'rate': 0, 'per': 1, 'throttle_interval': 0, 'throttle_retry_limit': 0, 'max_query_depth': 0, 'date_created': '2023-08-28T15:59:24.727409646Z', 'expires': 1701014366, 'quota_max': 3000, 'quota_renews': 1693239067, 'quota_remaining': 2999, 'quota_renewal_rate': 60, 'access_rights': {'myapp': {'api_name': 'myapp', 'api_id': 'myapp', 'versions': ['Default'], 'allowed_urls': [{'url': '/myapp/token/renew(.*)$', 'methods': ['GET', 'POST', 'PUT', 'DELETE', 'HEAD']}, {'url': '/myapp/token/refresh(.*)$', 'methods': ['GET', 'POST', 'PUT', 'DELETE', 'HEAD']}], 'restricted_types': None, 'allowed_types': None, 'limit': {'rate': 0, 'per': 1, 'throttle_interval': 0, 'throttle_retry_limit': 0, 'max_query_depth': 0, 'quota_max': 3000, 'quota_renews': 1693239067, 'quota_remaining': 2999, 'quota_renewal_rate': 60}, 'field_access_rights': None, 'disable_introspection': False, 'allowance_scope': ''}}, 'org_id': '1', 'oauth_client_id': '', 'oauth_keys': None, 'certificate': '', 'basic_auth_data': {'password': '', 'hash_type': ''}, 'jwt_data': {'secret': ''}, 'hmac_enabled': False, 'enable_http_signature_validation': False, 'hmac_string': '', 'rsa_certificate_id': '', 'is_inactive': False, 'apply_policy_id': '', 'apply_policies': ['myapp'], 'data_expires': 0, 'monitor': {'trigger_limits': None}, 'enable_detail_recording': False, 'enable_detailed_recording': False, 'meta_data': {}, 'tags': [], 'alias': 'myapp', 'last_updated': '1693238627', 'id_extractor_deadline': 0, 'session_lifetime': 0}
Current value for Expires: 1701014366
New value for expires: 1701014367
New Payload
{'last_check': 0, 'allowance': 1000, 'rate': 0, 'per': 1, 'throttle_interval': 0, 'throttle_retry_limit': 0, 'max_query_depth': 0, 'date_created': '2023-08-28T15:59:24.727409646Z', 'expires': 1701014367, 'quota_max': 3000, 'quota_renews': 1693239067, 'quota_remaining': 2999, 'quota_renewal_rate': 60, 'access_rights': {'myapp': {'api_name': 'myapp', 'api_id': 'myapp', 'versions': ['Default'], 'allowed_urls': [{'url': '/myapp/token/renew(.*)$', 'methods': ['GET', 'POST', 'PUT', 'DELETE', 'HEAD']}, {'url': '/myapp/token/refresh(.*)$', 'methods': ['GET', 'POST', 'PUT', 'DELETE', 'HEAD']}], 'restricted_types': None, 'allowed_types': None, 'limit': {'rate': 0, 'per': 1, 'throttle_interval': 0, 'throttle_retry_limit': 0, 'max_query_depth': 0, 'quota_max': 3000, 'quota_renews': 1693239067, 'quota_remaining': 2999, 'quota_renewal_rate': 60}, 'field_access_rights': None, 'disable_introspection': False, 'allowance_scope': ''}}, 'org_id': '1', 'oauth_client_id': '', 'oauth_keys': None, 'certificate': '', 'basic_auth_data': {'password': '', 'hash_type': ''}, 'jwt_data': {'secret': ''}, 'hmac_enabled': False, 'enable_http_signature_validation': False, 'hmac_string': '', 'rsa_certificate_id': '', 'is_inactive': False, 'apply_policy_id': '', 'apply_policies': ['myapp'], 'data_expires': 0, 'monitor': {'trigger_limits': None}, 'enable_detail_recording': False, 'enable_detailed_recording': False, 'meta_data': {}, 'tags': [], 'alias': 'myapp', 'last_updated': '1693238627', 'id_extractor_deadline': 0, 'session_lifetime': 0}
Response from Update command:
{"key":"eyJvcmciOiIxIiwiaWQiOiJiZWMxNWEzNjJmNmI0NWZiYWE3Y2FjZjEyYmRmYzE5OCIsImgiOiJtdXJtdXIxMjgifQ==","status":"ok","action":"modified"}
To test it in a Kubernetes cluster
Build the image
docker build --no-cache --platform linux/amd64 --build-arg http_proxy \
--build-arg https_proxy --build-arg --build-arg GIT_COMMIT=$(git rev-parse HEAD) \
-t <YOUR_ARTIFACTORY_REPOSITORY_OF_CHOICE>myapp:dev -f Dockerfile .
Deploy it in a kubernetes cluster where Tyk Gateway is running
kubectl create ns myapp
kubectl config set-context --current --namespace=myapp
kubectl apply -f deploy-k8s.yaml
Deploy the API and the Policy, Using the Swagger API List:
Assign the Policy to an existing key or create a new Key with the policy assigned to it.
Go into the POD and start the python application
# get pod name
kubectl get pods|grep myapp
# open bash
kubectl exec -it myapp-767c8bc686-7h7fn -- bash
# run the app
python3 myapp.py
From this point, Tyk Gateway has the API definition and will be able to proxy the request because the “myapp” service is up and running.
-
Open your Insomnia/Postman or Curl and hit the Tyk Gateway endpoint for MyApp.
Check the logs
Notice that the logs shows the key was updated properly, even when it was not.
Go back and check the key (expires) value, it’s not updated.
-
Then, try to modify the endpoint to /myapp/token/renew123 . The request will fail, ok.
Go back and adjust to the correct endpoint /myapp/token/renew , only then the key will be updated.
And if you try to hit the same correct endpoint more than once, it won’t work. It’s like you always have to “clean up” and try again.
-
Below message is always triggered, but only works when I hit first /myapp/token/renew123 and then hit the correct one /myapp/token/renew
time="Aug 28 16:26:42" level=info msg="Reset quota for key." inbound-key="****fQ==" key="quota-eyJvcmciOiIxIiwiaWQiOiJiZWMxNWEzNjJmNmI0NWZiYWE3Y2FjZjEyYmRmYzE5OCIsImgiOiJtdXJtdXIxMjgifQ==" prefix=auth-mgr
time="Aug 28 16:26:42" level=info msg="Key added or updated." api_id=-- expires=1701014367 key="****fQ==" org_id=1 path=-- prefix=api server_name=system user_id=system user_ip=--
time="Aug 28 16:26:42" level=debug msg="EVENT FIRED: TokenUpdated"
time="Aug 28 16:26:42" level=debug msg=Finished api_id=myapp api_name=myapp mw=ReverseProxy ns=95812551 org_id=1
- Kubernetes POD Logs:
root@myapp-767c8bc686-7h7fn:/mnt/app# python3 myapp.py
* Serving Flask app 'myapp'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:5000
* Running on http://100.64.17.202:5000
Press CTRL+C to quit
URL: https://<AWS_LOAD_BALANCER>.us-east-1.elb.amazonaws.com/tyk/keys/eyJvcmciOiIxIiwiaWQiOiJiZWMxNWEzNjJmNmI0NWZiYWE3Y2FjZjEyYmRmYzE5OCIsImgiOiJtdXJtdXIxMjgifQ==
Response: <Response [200]>
Key Retrieved from Tyk GW:
{'last_check': 0, 'allowance': 1000, 'rate': 0, 'per': 1, 'throttle_interval': 0, 'throttle_retry_limit': 0, 'max_query_depth': 0, 'date_created': '2023-08-28T15:59:24.727409646Z', 'expires': 1701014367, 'quota_max': 3000, 'quota_renews': 1693246538, 'quota_remaining': 2999, 'quota_renewal_rate': 60, 'access_rights': {'myapp': {'api_name': 'myapp', 'api_id': 'myapp', 'versions': ['Default'], 'allowed_urls': [{'url': '/myapp/token/renew(.*)$', 'methods': ['GET', 'POST', 'PUT', 'DELETE', 'HEAD']}, {'url': '/myapp/token/refresh(.*)$', 'methods': ['GET', 'POST', 'PUT', 'DELETE', 'HEAD']}], 'restricted_types': None, 'allowed_types': None, 'limit': {'rate': 0, 'per': 1, 'throttle_interval': 0, 'throttle_retry_limit': 0, 'max_query_depth': 0, 'quota_max': 3000, 'quota_renews': 1693246538, 'quota_remaining': 2999, 'quota_renewal_rate': 60}, 'field_access_rights': None, 'disable_introspection': False, 'allowance_scope': ''}}, 'org_id': '1', 'oauth_client_id': '', 'oauth_keys': None, 'certificate': '', 'basic_auth_data': {'password': '', 'hash_type': ''}, 'jwt_data': {'secret': ''}, 'hmac_enabled': False, 'enable_http_signature_validation': False, 'hmac_string': '', 'rsa_certificate_id': '', 'is_inactive': False, 'apply_policy_id': '', 'apply_policies': ['myapp'], 'data_expires': 0, 'monitor': {'trigger_limits': None}, 'enable_detail_recording': False, 'enable_detailed_recording': False, 'meta_data': {}, 'tags': [], 'alias': 'myapp', 'last_updated': '1693240002', 'id_extractor_deadline': 0, 'session_lifetime': 0}
Current value for Expires: 1701014367
New value for expires: 1701014368
New Payload
{'last_check': 0, 'allowance': 1000, 'rate': 0, 'per': 1, 'throttle_interval': 0, 'throttle_retry_limit': 0, 'max_query_depth': 0, 'date_created': '2023-08-28T15:59:24.727409646Z', 'expires': 1701014368, 'quota_max': 3000, 'quota_renews': 1693246538, 'quota_remaining': 2999, 'quota_renewal_rate': 60, 'access_rights': {'myapp': {'api_name': 'myapp', 'api_id': 'myapp', 'versions': ['Default'], 'allowed_urls': [{'url': '/myapp/token/renew(.*)$', 'methods': ['GET', 'POST', 'PUT', 'DELETE', 'HEAD']}, {'url': '/myapp/token/refresh(.*)$', 'methods': ['GET', 'POST', 'PUT', 'DELETE', 'HEAD']}], 'restricted_types': None, 'allowed_types': None, 'limit': {'rate': 0, 'per': 1, 'throttle_interval': 0, 'throttle_retry_limit': 0, 'max_query_depth': 0, 'quota_max': 3000, 'quota_renews': 1693246538, 'quota_remaining': 2999, 'quota_renewal_rate': 60}, 'field_access_rights': None, 'disable_introspection': False, 'allowance_scope': ''}}, 'org_id': '1', 'oauth_client_id': '', 'oauth_keys': None, 'certificate': '', 'basic_auth_data': {'password': '', 'hash_type': ''}, 'jwt_data': {'secret': ''}, 'hmac_enabled': False, 'enable_http_signature_validation': False, 'hmac_string': '', 'rsa_certificate_id': '', 'is_inactive': False, 'apply_policy_id': '', 'apply_policies': ['myapp'], 'data_expires': 0, 'monitor': {'trigger_limits': None}, 'enable_detail_recording': False, 'enable_detailed_recording': False, 'meta_data': {}, 'tags': [], 'alias': 'myapp', 'last_updated': '1693240002', 'id_extractor_deadline': 0, 'session_lifetime': 0}
Response from Update command:
{"key":"eyJvcmciOiIxIiwiaWQiOiJiZWMxNWEzNjJmNmI0NWZiYWE3Y2FjZjEyYmRmYzE5OCIsImgiOiJtdXJtdXIxMjgifQ==","status":"ok","action":"modified"}
100.64.21.160 - - [28/Aug/2023 18:15:59] "GET /token/renew HTTP/1.1" 200 -
Response: <Response [200]>
Key Retrieved from Tyk GW:
{'last_check': 0, 'allowance': 1000, 'rate': 0, 'per': 1, 'throttle_interval': 0, 'throttle_retry_limit': 0, 'max_query_depth': 0, 'date_created': '2023-08-28T15:59:24.727409646Z', 'expires': 1701014367, 'quota_max': 3000, 'quota_renews': 1693246760, 'quota_remaining': 2999, 'quota_renewal_rate': 60, 'access_rights': {'myapp': {'api_name': 'myapp', 'api_id': 'myapp', 'versions': ['Default'], 'allowed_urls': [{'url': '/myapp/token/renew(.*)$', 'methods': ['GET', 'POST', 'PUT', 'DELETE', 'HEAD']}, {'url': '/myapp/token/refresh(.*)$', 'methods': ['GET', 'POST', 'PUT', 'DELETE', 'HEAD']}], 'restricted_types': None, 'allowed_types': None, 'limit': {'rate': 0, 'per': 1, 'throttle_interval': 0, 'throttle_retry_limit': 0, 'max_query_depth': 0, 'quota_max': 3000, 'quota_renews': 1693246760, 'quota_remaining': 2999, 'quota_renewal_rate': 60}, 'field_access_rights': None, 'disable_introspection': False, 'allowance_scope': ''}}, 'org_id': '1', 'oauth_client_id': '', 'oauth_keys': None, 'certificate': '', 'basic_auth_data': {'password': '', 'hash_type': ''}, 'jwt_data': {'secret': ''}, 'hmac_enabled': False, 'enable_http_signature_validation': False, 'hmac_string': '', 'rsa_certificate_id': '', 'is_inactive': False, 'apply_policy_id': '', 'apply_policies': ['myapp'], 'data_expires': 0, 'monitor': {'trigger_limits': None}, 'enable_detail_recording': False, 'enable_detailed_recording': False, 'meta_data': {}, 'tags': [], 'alias': 'myapp', 'last_updated': '1693240002', 'id_extractor_deadline': 0, 'session_lifetime': 0}
Current value for Expires: 1701014367
New value for expires: 1701014368
New Payload
{'last_check': 0, 'allowance': 1000, 'rate': 0, 'per': 1, 'throttle_interval': 0, 'throttle_retry_limit': 0, 'max_query_depth': 0, 'date_created': '2023-08-28T15:59:24.727409646Z', 'expires': 1701014368, 'quota_max': 3000, 'quota_renews': 1693246760, 'quota_remaining': 2999, 'quota_renewal_rate': 60, 'access_rights': {'myapp': {'api_name': 'myapp', 'api_id': 'myapp', 'versions': ['Default'], 'allowed_urls': [{'url': '/myapp/token/renew(.*)$', 'methods': ['GET', 'POST', 'PUT', 'DELETE', 'HEAD']}, {'url': '/myapp/token/refresh(.*)$', 'methods': ['GET', 'POST', 'PUT', 'DELETE', 'HEAD']}], 'restricted_types': None, 'allowed_types': None, 'limit': {'rate': 0, 'per': 1, 'throttle_interval': 0, 'throttle_retry_limit': 0, 'max_query_depth': 0, 'quota_max': 3000, 'quota_renews': 1693246760, 'quota_remaining': 2999, 'quota_renewal_rate': 60}, 'field_access_rights': None, 'disable_introspection': False, 'allowance_scope': ''}}, 'org_id': '1', 'oauth_client_id': '', 'oauth_keys': None, 'certificate': '', 'basic_auth_data': {'password': '', 'hash_type': ''}, 'jwt_data': {'secret': ''}, 'hmac_enabled': False, 'enable_http_signature_validation': False, 'hmac_string': '', 'rsa_certificate_id': '', 'is_inactive': False, 'apply_policy_id': '', 'apply_policies': ['myapp'], 'data_expires': 0, 'monitor': {'trigger_limits': None}, 'enable_detail_recording': False, 'enable_detailed_recording': False, 'meta_data': {}, 'tags': [], 'alias': 'myapp', 'last_updated': '1693240002', 'id_extractor_deadline': 0, 'session_lifetime': 0}
Response from Update command:
{"key":"eyJvcmciOiIxIiwiaWQiOiJiZWMxNWEzNjJmNmI0NWZiYWE3Y2FjZjEyYmRmYzE5OCIsImgiOiJtdXJtdXIxMjgifQ==","status":"ok","action":"modified"}
Hit #1: → doesn’t work
Hit #2: → force wrong api
Hit #3 → same api, then it works