Skip to content

Commit d0c7fff

Browse files
committed
Block Vertex AI redirects
1 parent 07a28c2 commit d0c7fff

2 files changed

Lines changed: 117 additions & 10 deletions

File tree

qa/L0_vertex_ai/test.sh

Lines changed: 103 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -809,7 +809,35 @@ rm -f ./curl.out
809809
code=`curl -s -w %{http_code} -o ./curl.out -X POST -H "X-Vertex-Ai-Triton-Redirect: v2/systemsharedmemory/region/test/register" -H "Content-Type: application/json" -d '{"key":"test_shm","byte_size":1024}' localhost:8080/predict`
810810
if [ "$code" != "403" ]; then
811811
cat ./curl.out
812-
echo -e "\n***\n*** Failed. Expected shared memory register operation is restricted\n***"
812+
echo -e "\n***\n*** Failed. Expected shared memory register is blocked via redirect\n***"
813+
RET=1
814+
fi
815+
816+
# Model load redirect is unconditionally blocked through the prediction
817+
# endpoint regardless of restricted header configuration (TRI-715 RCE vector)
818+
rm -f ./curl.out
819+
code=`curl -s -w %{http_code} -o ./curl.out -X POST -H "X-Vertex-Ai-Triton-Redirect: v2/repository/models/identity_fp32/load" localhost:8080/predict`
820+
if [ "$code" != "403" ]; then
821+
cat ./curl.out
822+
echo -e "\n***\n*** Failed. Expected model load via redirect is blocked\n***"
823+
RET=1
824+
fi
825+
826+
# Model unload redirect is unconditionally blocked (TRI-715 DoS vector)
827+
rm -f ./curl.out
828+
code=`curl -s -w %{http_code} -o ./curl.out -X POST -H "X-Vertex-Ai-Triton-Redirect: v2/repository/models/identity_fp32/unload" localhost:8080/predict`
829+
if [ "$code" != "403" ]; then
830+
cat ./curl.out
831+
echo -e "\n***\n*** Failed. Expected model unload via redirect is blocked\n***"
832+
RET=1
833+
fi
834+
835+
# Statistics redirect without restricted header should be blocked
836+
rm -f ./curl.out
837+
code=`curl -s -w %{http_code} -o ./curl.out -X POST -H "X-Vertex-Ai-Triton-Redirect: v2/models/identity_fp32/stats" localhost:8080/predict`
838+
if [ "$code" != "403" ]; then
839+
cat ./curl.out
840+
echo -e "\n***\n*** Failed. Expected model statistics via redirect is restricted\n***"
813841
RET=1
814842
fi
815843
set -e
@@ -856,11 +884,39 @@ if [ "$code" != "200" ]; then
856884
RET=1
857885
fi
858886

887+
# Mutating shared memory operations remain blocked even with valid header
859888
rm -f ./curl.out
860889
code=`curl -s -w %{http_code} -o ./curl.out -X POST -H "X-Vertex-Ai-Triton-Redirect: v2/systemsharedmemory/region/test/register" -H "X-Vertex-Restricted: secret" -H "Content-Type: application/json" -d '{"key":"test_shm","byte_size":1024}' localhost:8080/predict`
890+
if [ "$code" != "403" ]; then
891+
cat ./curl.out
892+
echo -e "\n***\n*** Failed. Expected shared memory register is blocked via redirect even with valid header\n***"
893+
RET=1
894+
fi
895+
896+
# Model load remains blocked even with valid restricted header
897+
rm -f ./curl.out
898+
code=`curl -s -w %{http_code} -o ./curl.out -X POST -H "X-Vertex-Ai-Triton-Redirect: v2/repository/models/identity_fp32/load" -H "X-Vertex-Restricted: secret" localhost:8080/predict`
899+
if [ "$code" != "403" ]; then
900+
cat ./curl.out
901+
echo -e "\n***\n*** Failed. Expected model load is blocked via redirect even with valid header\n***"
902+
RET=1
903+
fi
904+
905+
# Model unload remains blocked even with valid restricted header
906+
rm -f ./curl.out
907+
code=`curl -s -w %{http_code} -o ./curl.out -X POST -H "X-Vertex-Ai-Triton-Redirect: v2/repository/models/identity_fp32/unload" -H "X-Vertex-Restricted: secret" localhost:8080/predict`
908+
if [ "$code" != "403" ]; then
909+
cat ./curl.out
910+
echo -e "\n***\n*** Failed. Expected model unload is blocked via redirect even with valid header\n***"
911+
RET=1
912+
fi
913+
914+
# Statistics redirect with restricted header should pass
915+
rm -f ./curl.out
916+
code=`curl -s -w %{http_code} -o ./curl.out -X POST -H "X-Vertex-Ai-Triton-Redirect: v2/models/identity_fp32/stats" -H "X-Vertex-Restricted: secret" localhost:8080/predict`
861917
if [ "$code" == "403" ]; then
862918
cat ./curl.out
863-
echo -e "\n***\n*** Failed. Expected valid restricted header to pass restriction check (request may still fail for non-auth reasons)\n***"
919+
echo -e "\n***\n*** Failed. Expected model statistics passes with restricted header\n***"
864920
RET=1
865921
fi
866922

@@ -877,6 +933,51 @@ set -e
877933
kill $SERVER_PID
878934
wait $SERVE_PID
879935

936+
#
937+
# HTTP max input size enforcement on Vertex AI endpoint
938+
#
939+
unset_vertex_variables
940+
export AIP_PREDICT_ROUTE="/predict"
941+
export AIP_HEALTH_ROUTE="/health"
942+
943+
SERVER_LOG="vertex_max_input_size_server.log"
944+
SERVER_ARGS="--allow-vertex-ai=true --model-repository=restricted_single_model --vertex-ai-default-model=identity_fp32 --http-max-input-size=256"
945+
run_server_nowait
946+
vertex_ai_wait_for_server_ready $SERVER_PID 10
947+
if [ "$WAIT_RET" != "0" ]; then
948+
echo -e "\n***\n*** Failed to start $SERVER\n***"
949+
kill $SERVER_PID
950+
wait $SERVER_PID
951+
cat $SERVER_LOG
952+
exit 1
953+
fi
954+
955+
set +e
956+
957+
# Small payload under 256 bytes should succeed
958+
rm -f ./curl.out
959+
code=`curl -s -w %{http_code} -o ./curl.out -X POST -H "Content-Type: application/json" -d'{"inputs":[{"name":"INPUT0","datatype":"FP32","shape":[1,1],"data":[1.0]}],"outputs":[{"name":"OUTPUT0"}]}' localhost:8080/predict`
960+
if [ "$code" != "200" ]; then
961+
cat ./curl.out
962+
echo -e "\n***\n*** Failed. Expected small payload to succeed on Vertex AI endpoint (got $code)\n***"
963+
RET=1
964+
fi
965+
966+
# Large payload over 256 bytes should be rejected
967+
rm -f ./curl.out
968+
LARGE_PAYLOAD='{"inputs":[{"name":"INPUT0","datatype":"FP32","shape":[1,16],"data":[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0,16.0]}],"outputs":[{"name":"OUTPUT0"}]}'
969+
code=`curl -s -w %{http_code} -o ./curl.out -X POST -H "Content-Type: application/json" -d"$LARGE_PAYLOAD" localhost:8080/predict`
970+
if [ "$code" == "200" ]; then
971+
cat ./curl.out
972+
echo -e "\n***\n*** Failed. Expected oversized payload to be rejected on Vertex AI endpoint\n***"
973+
RET=1
974+
fi
975+
976+
set -e
977+
978+
kill $SERVER_PID
979+
wait $SERVE_PID
980+
880981
if [ $RET -eq 0 ]; then
881982
echo -e "\n***\n*** Test Passed\n***"
882983
else

src/vertex_ai_server.cc

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -196,32 +196,38 @@ VertexAiAPIServer::Handle(evhtp_request_t* req)
196196
} else if (RE2::FullMatch(
197197
redirect_endpoint, systemsharedmemory_regex_, &region,
198198
&action)) {
199-
// system shared memory
200199
if (action == "status") {
201200
req->method = htp_method_GET;
201+
HandleSystemSharedMemory(req, region, action);
202+
return;
202203
}
203-
HandleSystemSharedMemory(req, region, action);
204+
// register/unregister are mutating operations that must not be
205+
// reachable through the prediction endpoint.
206+
LOG_VERBOSE(1) << "Vertex AI redirect blocked: " << redirect_endpoint;
207+
evhtp_send_reply(req, EVHTP_RES_FORBIDDEN);
204208
return;
205209
} else if (RE2::FullMatch(
206210
redirect_endpoint, cudasharedmemory_regex_, &region,
207211
&action)) {
208-
// cuda shared memory
209212
if (action == "status") {
210213
req->method = htp_method_GET;
214+
HandleCudaSharedMemory(req, region, action);
215+
return;
211216
}
212-
HandleCudaSharedMemory(req, region, action);
217+
LOG_VERBOSE(1) << "Vertex AI redirect blocked: " << redirect_endpoint;
218+
evhtp_send_reply(req, EVHTP_RES_FORBIDDEN);
213219
return;
214220
} else if (RE2::FullMatch(
215221
redirect_endpoint, modelcontrol_regex_, &repo_name, &kind,
216222
&model_name, &action)) {
217-
// model repository
218223
if (kind == "index") {
219224
HandleRepositoryIndex(req, repo_name);
220225
return;
221-
} else if (kind.find("models", 0) == 0) {
222-
HandleRepositoryControl(req, repo_name, model_name, action);
223-
return;
224226
}
227+
// Model load/unload must not be reachable through the prediction endpoint.
228+
LOG_VERBOSE(1) << "Vertex AI redirect blocked: " << redirect_endpoint;
229+
evhtp_send_reply(req, EVHTP_RES_FORBIDDEN);
230+
return;
225231
}
226232
}
227233
}

0 commit comments

Comments
 (0)