Skip to content

Commit 3e5b178

Browse files
authored
fix: improve Vertex AI redirect handling (#8685) (#8694)
1 parent 79d1242 commit 3e5b178

2 files changed

Lines changed: 150 additions & 46 deletions

File tree

qa/L0_vertex_ai/test.sh

Lines changed: 134 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -699,20 +699,14 @@ else
699699
fi
700700
set -e
701701

702-
# repository control (expect error)
702+
# repository control via redirect is blocked unconditionally
703703
rm -f ./curl.out
704704
set +e
705705
code=`curl -s -w %{http_code} -o ./curl.out -X POST -H "X-Vertex-Ai-Triton-Redirect: v2/repository/models/subadd/unload" localhost:8080/predict`
706-
if [ "$code" == "200" ]; then
706+
if [ "$code" != "403" ]; then
707707
cat ./curl.out
708-
echo -e "\n***\n*** Test Failed\n***"
708+
echo -e "\n***\n*** Failed. Expected model unload via redirect to return 403 (got $code)\n***"
709709
RET=1
710-
else
711-
grep "explicit model load / unload is not allowed" ./curl.out
712-
if [ $? -ne 0 ]; then
713-
echo -e "\n***\n*** Failed. Expected error on model control\n***"
714-
RET=1
715-
fi
716710
fi
717711
set -e
718712

@@ -760,62 +754,116 @@ else
760754
fi
761755
set -e
762756

763-
# Redirected management APIs should be blocked without restricted header
757+
# Redirected read-only APIs should be blocked without restricted header
764758
set +e
765759
for redirect_endpoint in \
766760
"v2" \
767761
"v2/models/identity_fp32/config" \
768762
"v2/repository/index" \
769763
"v2/systemsharedmemory/status" \
770-
"v2/cudasharedmemory/status" \
771-
"v2/systemsharedmemory/region/test/register"
764+
"v2/cudasharedmemory/status"
772765
do
773766
rm -f ./curl.out
774-
if [ "$redirect_endpoint" != "v2/systemsharedmemory/region/test/register" ]; then
775-
code=`curl -s -w %{http_code} -o ./curl.out -X POST -H "X-Vertex-Ai-Triton-Redirect: ${redirect_endpoint}" localhost:8080/predict`
776-
else
777-
code=`curl -s -w %{http_code} -o ./curl.out -X POST -H "X-Vertex-Ai-Triton-Redirect: ${redirect_endpoint}" \
778-
-H "Content-Type: application/json" -d '{"key":"test_shm","byte_size":1024}' localhost:8080/predict`
779-
fi
767+
code=`curl -s -w %{http_code} -o ./curl.out -X POST -H "X-Vertex-Ai-Triton-Redirect: ${redirect_endpoint}" localhost:8080/predict`
780768
if [ "$code" != "403" ]; then
781769
cat ./curl.out
782770
echo -e "\n***\n*** Failed. Expected ${redirect_endpoint} is restricted\n***"
783771
RET=1
784772
fi
785773
done
774+
775+
# Mutating shared memory operations are blocked through redirect
776+
rm -f ./curl.out
777+
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`
778+
if [ "$code" != "403" ]; then
779+
cat ./curl.out
780+
echo -e "\n***\n*** Failed. Expected shared memory register is blocked via redirect\n***"
781+
RET=1
782+
fi
783+
784+
# Model load redirect is unconditionally blocked through the prediction endpoint
785+
rm -f ./curl.out
786+
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`
787+
if [ "$code" != "403" ]; then
788+
cat ./curl.out
789+
echo -e "\n***\n*** Failed. Expected model load via redirect is blocked\n***"
790+
RET=1
791+
fi
792+
793+
# Model unload redirect is unconditionally blocked through the prediction endpoint
794+
rm -f ./curl.out
795+
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`
796+
if [ "$code" != "403" ]; then
797+
cat ./curl.out
798+
echo -e "\n***\n*** Failed. Expected model unload via redirect is blocked\n***"
799+
RET=1
800+
fi
801+
802+
# Statistics redirect without restricted header should be blocked
803+
rm -f ./curl.out
804+
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`
805+
if [ "$code" != "403" ]; then
806+
cat ./curl.out
807+
echo -e "\n***\n*** Failed. Expected model statistics via redirect is restricted\n***"
808+
RET=1
809+
fi
786810
set -e
787811

788-
# Restricted header should allow handler invocation
812+
# Restricted header should allow read-only handler invocation
789813
set +e
790814
for redirect_endpoint in \
791815
"v2" \
792816
"v2/models/identity_fp32/config" \
793817
"v2/repository/index" \
794818
"v2/systemsharedmemory/status" \
795-
"v2/cudasharedmemory/status" \
796-
"v2/systemsharedmemory/region/test/register"
819+
"v2/cudasharedmemory/status"
797820
do
798821
rm -f ./curl.out
799-
if [ "$redirect_endpoint" != "v2/systemsharedmemory/region/test/register" ]; then
800-
code=`curl -s -w %{http_code} -o ./curl.out -X POST -H "X-Vertex-Ai-Triton-Redirect: ${redirect_endpoint}" \
801-
-H "X-Vertex-Restricted: secret" localhost:8080/predict`
802-
if [ "$code" != "200" ]; then
803-
cat ./curl.out
804-
echo -e "\n***\n*** Failed. Expected ${redirect_endpoint} passes with restricted header\n***"
805-
RET=1
806-
fi
807-
else
808-
code=`curl -s -w %{http_code} -o ./curl.out -X POST -H "X-Vertex-Ai-Triton-Redirect: ${redirect_endpoint}" \
809-
-H "X-Vertex-Restricted: secret" -H "Content-Type: application/json" -d '{"key":"test_shm","byte_size":1024}' localhost:8080/predict`
810-
# This request may fail with a different error: "Unable to open shared memory region: 'test_shm'"
811-
if [ "$code" == "403" ]; then
812-
cat ./curl.out
813-
echo -e "\n***\n*** Failed. Expected ${redirect_endpoint} passes with restricted header\n***"
814-
RET=1
815-
fi
822+
code=`curl -s -w %{http_code} -o ./curl.out -X POST -H "X-Vertex-Ai-Triton-Redirect: ${redirect_endpoint}" \
823+
-H "X-Vertex-Restricted: secret" localhost:8080/predict`
824+
if [ "$code" != "200" ]; then
825+
cat ./curl.out
826+
echo -e "\n***\n*** Failed. Expected ${redirect_endpoint} passes with restricted header\n***"
827+
RET=1
816828
fi
817829
done
818830

831+
# Mutating shared memory operations remain blocked even with valid header
832+
rm -f ./curl.out
833+
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`
834+
if [ "$code" != "403" ]; then
835+
cat ./curl.out
836+
echo -e "\n***\n*** Failed. Expected shared memory register is blocked via redirect even with valid header\n***"
837+
RET=1
838+
fi
839+
840+
# Model load remains blocked even with valid restricted header
841+
rm -f ./curl.out
842+
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`
843+
if [ "$code" != "403" ]; then
844+
cat ./curl.out
845+
echo -e "\n***\n*** Failed. Expected model load is blocked via redirect even with valid header\n***"
846+
RET=1
847+
fi
848+
849+
# Model unload remains blocked even with valid restricted header
850+
rm -f ./curl.out
851+
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`
852+
if [ "$code" != "403" ]; then
853+
cat ./curl.out
854+
echo -e "\n***\n*** Failed. Expected model unload is blocked via redirect even with valid header\n***"
855+
RET=1
856+
fi
857+
858+
# Statistics redirect with restricted header should pass
859+
rm -f ./curl.out
860+
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`
861+
if [ "$code" == "403" ]; then
862+
cat ./curl.out
863+
echo -e "\n***\n*** Failed. Expected model statistics passes with restricted header\n***"
864+
RET=1
865+
fi
866+
819867
# Wrong restricted header should reject the request
820868
set +e
821869
for redirect_endpoint in \
@@ -845,6 +893,54 @@ set -e
845893
kill $SERVER_PID
846894
wait $SERVER_PID
847895

896+
#
897+
# HTTP max input size enforcement on Vertex AI endpoint
898+
#
899+
unset_vertex_variables
900+
export AIP_PREDICT_ROUTE="/predict"
901+
export AIP_HEALTH_ROUTE="/health"
902+
903+
SERVER_LOG="vertex_max_input_size_server.log"
904+
SERVER_ARGS="--allow-vertex-ai=true \
905+
--model-repository=restricted_single_model \
906+
--vertex-ai-default-model=identity_fp32 \
907+
--http-max-input-size=128"
908+
run_server_nowait
909+
vertex_ai_wait_for_server_ready $SERVER_PID 10
910+
if [ "$WAIT_RET" != "0" ]; then
911+
echo -e "\n***\n*** Failed to start $SERVER\n***"
912+
kill $SERVER_PID
913+
wait $SERVER_PID
914+
cat $SERVER_LOG
915+
exit 1
916+
fi
917+
918+
set +e
919+
920+
# Small payload under 128 bytes should succeed
921+
rm -f ./curl.out
922+
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`
923+
if [ "$code" != "200" ]; then
924+
cat ./curl.out
925+
echo -e "\n***\n*** Failed. Expected small payload to succeed on Vertex AI endpoint (got $code)\n***"
926+
RET=1
927+
fi
928+
929+
# Large payload over 128 bytes should be rejected
930+
rm -f ./curl.out
931+
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"}]}'
932+
code=`curl -s -w %{http_code} -o ./curl.out -X POST -H "Content-Type: application/json" -d"$LARGE_PAYLOAD" localhost:8080/predict`
933+
if [ "$code" == "200" ]; then
934+
cat ./curl.out
935+
echo -e "\n***\n*** Failed. Expected oversized payload to be rejected on Vertex AI endpoint\n***"
936+
RET=1
937+
fi
938+
939+
set -e
940+
941+
kill $SERVER_PID
942+
wait $SERVER_PID
943+
848944
if [ $RET -eq 0 ]; then
849945
echo -e "\n***\n*** Test Passed\n***"
850946
else

src/vertex_ai_server.cc

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -196,32 +196,40 @@ 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+
// Only read-only status queries are permitted through redirect.
205+
// Mutating operations (register/unregister) require the core
206+
// HTTP endpoint.
207+
LOG_VERBOSE(1) << "Vertex AI redirect blocked: " << redirect_endpoint;
208+
evhtp_send_reply(req, EVHTP_RES_FORBIDDEN);
204209
return;
205210
} else if (RE2::FullMatch(
206211
redirect_endpoint, cudasharedmemory_regex_, &region,
207212
&action)) {
208-
// cuda shared memory
209213
if (action == "status") {
210214
req->method = htp_method_GET;
215+
HandleCudaSharedMemory(req, region, action);
216+
return;
211217
}
212-
HandleCudaSharedMemory(req, region, action);
218+
LOG_VERBOSE(1) << "Vertex AI redirect blocked: " << redirect_endpoint;
219+
evhtp_send_reply(req, EVHTP_RES_FORBIDDEN);
213220
return;
214221
} else if (RE2::FullMatch(
215222
redirect_endpoint, modelcontrol_regex_, &repo_name, &kind,
216223
&model_name, &action)) {
217-
// model repository
218224
if (kind == "index") {
219225
HandleRepositoryIndex(req, repo_name);
220226
return;
221-
} else if (kind.find("models", 0) == 0) {
222-
HandleRepositoryControl(req, repo_name, model_name, action);
223-
return;
224227
}
228+
// Only repository index queries are permitted through redirect.
229+
// Model load/unload requires the core HTTP endpoint.
230+
LOG_VERBOSE(1) << "Vertex AI redirect blocked: " << redirect_endpoint;
231+
evhtp_send_reply(req, EVHTP_RES_FORBIDDEN);
232+
return;
225233
}
226234
}
227235
}

0 commit comments

Comments
 (0)