Skip to content

Commit 1d91877

Browse files
authored
Fix private hub (#6036)
* fix(iam): scope repo-level ECR actions to prevent false deny in preflight validation Split ecr_policy statements in training, serving, and hyperpod role types so that only ecr:GetAuthorizationToken (account-level) remains under Resource: "*". The repository-level actions (BatchGetImage, GetDownloadUrlForLayer, BatchCheckLayerAvailability) are now scoped to arn:aws:ecr:*:*:repository/*, which excludes them from _get_smoke_test_actions. This prevents SimulatePrincipalPolicy from returning implicitDeny for roles that correctly scope ECR permissions to specific repo ARNs (least privilege), fixing the regression that blocked deploys/training/pipelines for those customers. * fix(serve): Propagate model_reference_arn in _build_for_jumpstart _build_for_jumpstart calls get_init_kwargs which resolves model_reference_arn for private hub model references, but never copies it back to self.model_reference_arn. This causes _prepare_container_def_base to omit HubAccessConfig from the CreateModel container definition, making SageMaker fall back to direct S3 access on jumpstart-cache-prod-* and fail with AccessDenied. * add unit test
1 parent 3f810c6 commit 1d91877

2 files changed

Lines changed: 97 additions & 0 deletions

File tree

‎sagemaker-serve/src/sagemaker/serve/model_builder_servers.py‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,6 +1003,12 @@ def _build_for_jumpstart(self) -> Model:
10031003
):
10041004
self._enable_network_isolation = init_kwargs.enable_network_isolation
10051005

1006+
# Propagate model_reference_arn from init_kwargs so that
1007+
# _prepare_container_def_base can attach HubAccessConfig to the
1008+
# CreateModel request (required for private hub brokered access).
1009+
if getattr(init_kwargs, "model_reference_arn", None):
1010+
self.model_reference_arn = init_kwargs.model_reference_arn
1011+
10061012
# Handle model artifacts for fine-tuned models
10071013
if hasattr(init_kwargs, "model_data") and init_kwargs.model_data:
10081014
if (

‎sagemaker-serve/tests/unit/test_private_hub_artifact_resolution.py‎

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,5 +367,96 @@ def test_from_jumpstart_config_then_build_uses_private_hub(
367367
self.assertNotIn("jumpstart-cache-prod", mb.s3_model_data_url)
368368

369369

370+
class TestModelReferenceArnPropagation(unittest.TestCase):
371+
"""Test that _build_for_jumpstart propagates model_reference_arn to self.
372+
373+
This is the fix for V2271554863: without propagation, _prepare_container_def_base
374+
cannot attach HubAccessConfig to the CreateModel request, causing SageMaker to
375+
fall back to direct S3 access on jumpstart-cache-prod-* → AccessDenied.
376+
"""
377+
378+
MOCK_MODEL_REFERENCE_ARN = (
379+
"arn:aws:sagemaker:us-east-1:123456789012:hub-content/"
380+
"my-private-hub/ModelReference/huggingface-llm-phi-4-mini-instruct/1.1.0"
381+
)
382+
383+
def setUp(self):
384+
self.mock_session = _mock_session()
385+
386+
@_PATCH_IS_JS
387+
@patch("sagemaker.core.jumpstart.utils.validate_model_id_and_get_type", return_value=None)
388+
@patch("sagemaker.core.jumpstart.factory.utils.get_init_kwargs")
389+
@patch("sagemaker.serve.model_builder.ModelBuilder._create_model")
390+
@patch("sagemaker.serve.model_builder.ModelBuilder._prepare_for_mode")
391+
def test_model_reference_arn_propagated_to_builder(
392+
self, mock_prepare, mock_create, mock_get_kwargs, mock_validate, mock_is_js
393+
):
394+
"""model_reference_arn from get_init_kwargs must be set on self."""
395+
mock_init_kwargs = Mock()
396+
mock_init_kwargs.image_uri = (
397+
"763104351884.dkr.ecr.us-east-1.amazonaws.com/djl-inference:0.27.0-lmi10.0.0-cu124"
398+
)
399+
mock_init_kwargs.env = {}
400+
mock_init_kwargs.model_data = {
401+
"S3DataSource": {
402+
"S3Uri": "s3://my-private-hub-bucket/artifacts/model.tar.gz",
403+
"S3DataType": "S3Prefix",
404+
"CompressionType": "None",
405+
}
406+
}
407+
mock_init_kwargs.enable_network_isolation = None
408+
mock_init_kwargs.model_reference_arn = self.MOCK_MODEL_REFERENCE_ARN
409+
mock_get_kwargs.return_value = mock_init_kwargs
410+
mock_create.return_value = Mock()
411+
412+
builder = ModelBuilder(
413+
model=MOCK_MODEL_ID,
414+
role_arn=MOCK_ROLE_ARN,
415+
sagemaker_session=self.mock_session,
416+
mode=Mode.SAGEMAKER_ENDPOINT,
417+
)
418+
builder._optimizing = False
419+
builder.hub_name = MOCK_HUB_NAME
420+
builder.hub_arn = MOCK_HUB_ARN
421+
builder.model_version = MOCK_MODEL_VERSION
422+
423+
builder._build_for_jumpstart()
424+
425+
self.assertEqual(builder.model_reference_arn, self.MOCK_MODEL_REFERENCE_ARN)
426+
427+
@_PATCH_IS_JS
428+
@patch("sagemaker.core.jumpstart.utils.validate_model_id_and_get_type", return_value=None)
429+
@patch("sagemaker.core.jumpstart.factory.utils.get_init_kwargs")
430+
@patch("sagemaker.serve.model_builder.ModelBuilder._create_model")
431+
@patch("sagemaker.serve.model_builder.ModelBuilder._prepare_for_mode")
432+
def test_model_reference_arn_stays_none_without_hub(
433+
self, mock_prepare, mock_create, mock_get_kwargs, mock_validate, mock_is_js
434+
):
435+
"""Without a private hub, model_reference_arn should remain None."""
436+
mock_init_kwargs = Mock()
437+
mock_init_kwargs.image_uri = (
438+
"763104351884.dkr.ecr.us-east-1.amazonaws.com/djl-inference:0.27.0-lmi10.0.0-cu124"
439+
)
440+
mock_init_kwargs.env = {}
441+
mock_init_kwargs.model_data = "s3://jumpstart-cache-prod-us-east-1/model.tar.gz"
442+
mock_init_kwargs.enable_network_isolation = None
443+
mock_init_kwargs.model_reference_arn = None
444+
mock_get_kwargs.return_value = mock_init_kwargs
445+
mock_create.return_value = Mock()
446+
447+
builder = ModelBuilder(
448+
model=MOCK_MODEL_ID,
449+
role_arn=MOCK_ROLE_ARN,
450+
sagemaker_session=self.mock_session,
451+
mode=Mode.SAGEMAKER_ENDPOINT,
452+
)
453+
builder._optimizing = False
454+
builder.model_version = MOCK_MODEL_VERSION
455+
456+
builder._build_for_jumpstart()
457+
458+
self.assertIsNone(getattr(builder, "model_reference_arn", None))
459+
460+
370461
if __name__ == "__main__":
371462
unittest.main()

0 commit comments

Comments
 (0)