Protecting Services with Guardrails
Note
In this scenario we expect the platform is equipped with the Envoy Gateway infrastructure configured for guardrails management.
This tutorial shows how to protect services with Guardrails.
We will use a simple API to be protected by guardrails. The api makes devision operation given the input data.
Setting up the Service
We will use a simple Python Serverless function created with the Python runtime.
import nuclio_sdk
import os
import json
def handler_serve(context: nuclio_sdk.Context, event: nuclio_sdk.Event):
if isinstance(event.body, bytes):
body = json.loads(event.body)
else:
body = event.body
divident = body['divident']
divisor = body['divisor']
return {"result": divident/divisor}
Register the function in platform:
func = project.new_function(name="divisor",
kind="python",
python_version="PYTHON3_10",
code_src="src/divisor.py",
handler="handler_serve"
)
Run and test the unprotected function:
run = func.run(action="serve", wait=True)
# Test the function
import requests
data = {
'divident': 42,
'divisor': 6
}
res = requests.post(f"http://{run.refresh().status.service['url']}", json=data)
res.json()
Setting up the Guardrail
We will use a simple Guardrail to protect the API implemented with Guardrail runtime. The function controls the input data to avoid devision by zero.
import nuclio_sdk
import os
import json
def handler_serve(context: nuclio_sdk.Context, event: nuclio_sdk.Event):
if isinstance(event.body, bytes):
body = json.loads(event.body)
else:
body = event.body
divident = body['divident'] if 'divident' in body else 0
divisor = body['divisor'] if 'divisor' in body else 0
if divident and divisor and divisor > 0:
return event.body
return context.Response(body="Invalid input data",
headers={},
content_type='text/plain',
status_code=400)
Register the function in platform and run it:
guardrail_func = project.new_function(name="divisor-guardrail",
kind="guardrail",
python_version="PYTHON3_10",
code_src="src/divisor_guardrail_service.py",
handler="handler_serve",
processing_mode="preprocessor"
)
run = guardrail_func.run(action="serve")
Use the Guardrail to Protect the API
To protect the service instance with guardrails, we rely on the corresponding gateway and use Envoy Gateway extension for the runs. Specifically, when the service enable the extension, we obtain:
- the service exposed also behind the preconfigured service Envoy gateway (see the gatewayInfo in service status);
- if the guardrails are configured, the gateway controls the traffic using the ExtProc extension that interacts with the guardrails to implement pre/post processing logic.
guardrail_url = guardrail_run.refresh().status.service['url']
run = func.run(action="serve", extensions=[{
"kind": "envoygw",
"name": "gw",
"spec": {
"guardrails": [guardrail_url]
}
}])
The protected endpoint of service gateway may be obtained as follows:
PROTECTED_ENDPOINT = f"http://{run.status.gatewayInfo['gatewayEndpoint']}/{run.id}"
Test the protected service:
import requests
data = {
'divident': 42,
'divisor': 0
}
res = requests.post(PROTECTED_ENDPOINT, json=data)
res.text