examples/operator-rbac/3-client-rbac-test-local.ipynb
This notebook will test Feast authentication outside of Kubernetes for local testing.
When running outside of Kubernetes, you need to manually set the service account token in the LOCAL_K8S_TOKEN environment variable. The token can be retrieved from a running pod using:
kubectl exec <pod-name> -- cat /var/run/secrets/kubernetes.io/serviceaccount/token
To authenticate Feast externally, set the retrieved token as an environment variable:
export LOCAL_K8S_TOKEN="your-service-account-token"
| User Type | ServiceAccount | RoleBinding Assigned | Expected Behavior in output |
|---|---|---|---|
| Read-Only | feast-user-sa | feast-reader | Can read from the feature store, but cannot write. |
| Unauthorized | feast-unauthorized-user-sa | None | Access should be denied in test.py. |
| Admin | feast-admin-sa | feast-writer | Can read and write feature store data. |
The Operator create client feature store ConfigMap containing the feature_store.yaml settings. We can retrieve it and port froward to local as we are testing locally.
!kubectl get configmap feast-sample-kubernetes-auth-client -n feast -o jsonpath='{.data.feature_store\.yaml}' > client/feature_repo/feature_store.yaml
Update the client/feature_repo/feature_store.yaml to look like below:
project: feast_rbac
provider: local
offline_store:
host: localhost
type: remote
port: 8081
online_store:
path: http://localhost:8082
type: remote
registry:
path: localhost:8083
registry_type: remote
auth:
type: kubernetes
entity_key_serialization_version: 3
Run Port Forwarding for All Services for local testing
import subprocess
# Define services and their local ports
services = {
"offline_store": ("feast-sample-kubernetes-auth-offline", 8081),
"online_store": ("feast-sample-kubernetes-auth-online", 8082),
"registry": ("feast-sample-kubernetes-auth-registry", 8083),
}
# Start port-forwarding for each service
port_forward_processes = {}
for name, (service, local_port) in services.items():
cmd = f"kubectl port-forward svc/{service} -n feast {local_port}:80"
process = subprocess.Popen(cmd, shell=True)
port_forward_processes[name] = process
print(f"Port forwarding {service} -> localhost:{local_port}")
Function to retrieve a Kubernetes service account token and set it as an environment variable
import subprocess
import os
def get_k8s_token(service_account):
namespace = "feast"
if not service_account:
raise ValueError("Service account name is required.")
result = subprocess.run(
["kubectl", "create", "token", service_account, "-n", namespace],
capture_output=True, text=True, check=True
)
token = result.stdout.strip()
if not token:
return None # Silently return None if token retrieval fails
os.environ["LOCAL_K8S_TOKEN"] = token
return "Token Retrieved: ***** (hidden for security)"
Generating training data. The following test functions were copied from the test_workflow.py template but we added try blocks to print only
the relevant error messages, since we expect to receive errors from the permission enforcement modules.
!cat client/feature_repo/test.py
import os
# Set the FEAST_REPO_PATH before importing check_permissions
os.environ["FEAST_REPO_PATH"] = "client/feature_repo"
Step 1: Set the Token
get_k8s_token("feast-user-sa")
Step 2: Test permission functions to validate permission on fetching online, offline or perform write operation
from client.feature_repo.test import check_permissions
# Call the function
#Run test.py script from pod to test RBAC for client-readonly-user.
# verify the logs for write operation will show below message
# --- Write to Feature Store ---
#*** PERMISSION DENIED *** User lacks permission to modify the feature store.
check_permissions()
Step 3: Test permission functions to validate permission on fetching online feature views
Required:
LOCAL_K8S_TOKEN# Run Curl command to test the RBAC for client-readonly-user.
!curl -X POST http://localhost:8082/get-online-features -H "Content-Type: application/json" -H "Authorization: Bearer $LOCAL_K8S_TOKEN" -d '{"features": ["driver_hourly_stats:conv_rate","driver_hourly_stats:acc_rate"], "entities":{"driver_id": [1001, 1002]}}'
Step 1: Test permission functions to validate unauthorized user cant access any objects
# Retrieve and store the token
get_k8s_token("feast-unauthorized-user-sa")
check_permissions()
Step 2: Run API request for Unauthorized User, Unauthorized user should not be able to even view the objects.
Required:
LOCAL_K8S_TOKEN# Run Curl command to test the RBAC for client-readonly-user.
!curl -X POST http://localhost:8082/get-online-features -H "Content-Type: application/json" -H "Authorization: Bearer $LOCAL_K8S_TOKEN" -d '{"features": ["driver_hourly_stats:conv_rate","driver_hourly_stats:acc_rate"], "entities":{"driver_id": [1001, 1002]}}'
Step 1: Test permissions function to validate Admin user can access all objects
# Retrieve and store the token
get_k8s_token("feast-admin-sa")
check_permissions()
Step 2: Run API request for Admin User, Admin user should be able to perform all operations on the objects.
Required:
LOCAL_K8S_TOKEN# Run Curl command to test the RBAC for client-readonly-user.
!curl -X POST http://localhost:8082/get-online-features -H "Content-Type: application/json" -H "Authorization: Bearer $LOCAL_K8S_TOKEN" -d '{"features": ["driver_hourly_stats:conv_rate","driver_hourly_stats:acc_rate"], "entities":{"driver_id": [1001, 1002]}}'
Note: Currently, remote materialization not available in Feast when using the Remote Client Workaround: Consider using running it from pod like
kubectl exec deploy/feast-sample-kubernetes-auth -itc online -- bash -c 'feast materialize-incremental $(date -u +"%Y-%m-%dT%H:%M:%S")
Terminate the process
for name, process in port_forward_processes.items():
process.terminate()
print(f"Stopped port forwarding for {name}")