Embed SageMaker MLflow в SSO-портал: гайд по presigned URL | AiManual
AiManual Logo Ai / Manual.
28 Май 2026 Гайд

Больше никаких ссылок в Slack: как встроить SageMaker MLflow в корпоративный портал с SSO

Пошаговое руководство, как встроить SageMaker MLflow в корпоративный портал с SSO через iframe и presigned URLs. Избавляемся от ручной раздачи ссылок для ML-ком

Ты senior DevOps, твоя ML-команда выросла до 20 человек. Каждый раз, когда нужно глянуть эксперимент в MLflow, кто-то просит ссылку. Ты генерируешь presigned URL через AWS Console, кидаешь в Slack, через час ссылка истекает. И всё по новой. Знакомая боль?

💡
В 2026 году SageMaker MLflow Tracking Server — зрелая фича, но управление доступом через presigned URLs всё ещё напоминает костыль. Пора это automate.

Решение — встроить MLflow прямо в ваш внутренний портал, который уже привязан к корпоративному SSO. Никакой ручной раздачи ссылок. Один iframe, динамическая генерация presigned URL на лету, полный контроль доступа.

Проклятие presigned URL (и почему это не твоя вина)

Когда Amazon анонсировал нативный MLflow в SageMaker, все радовались. Интеграция с эксперимент-трекингом из коробки, Managed MLflow Tracking Server — красота. Но архитектура доступа построена на presigned URLs: AWS генерирует короткоживущую ссылку на UI сервера, которая валидна от 1 до 12 часов.

Проблема в масштабировании:

  • Ссылку нужно каждый раз обновлять.
  • Нельзя встроить в корпоративный портал — браузер блокирует кросс-доменные запросы.
  • Интеграция с SSO (Okta, Azure AD, Cognito) отсутствует.

Звучит логично, но на практике: аналитик просыпается, открывает портал, а там пустой iframe. Ссылка протухла. Никто не хочет писать бота для обновления presigned URL каждые 8 часов.

Как это работает в правильном мире (архитектура)

Нам нужен middleware, который берёт на себя генерацию presigned URL на основе сессии пользователя из SSO. Схема простая:

  1. Пользователь логинится в корпоративном портале через SSO (например, Cognito).
  2. Портал получает JWT токен.
  3. Фронтенд вызывает Lambda/API Gateway endpoint, передавая JWT.
  4. Lambda проверяет права (через IAM или SSO groups), генерирует новый presigned URL для MLflow Tracking Server.
  5. Портал вставляет iframe с этим URL.
  6. Каждые N минут фронтенд обновляет ссылку через фоновый запрос.

⚠️ Важно: presigned URL генерируется от имени IAM-роли Lambda, а не пользователя. Чтобы разграничить права (один видит только свои эксперименты, другой — все), нужно использовать STS AssumeRole с тегированием или отдельные MLflow Tracking Servers под каждую команду.

Строим: пошаговый план без магии

1 Создаём SageMaker MLflow Tracking Server

Если у тебя ещё нет — создай через AWS Console или CloudFormation. На 2026 рекомендую версию MLflow 2.18.x (последняя стабильная с поддержкой embed режима). Убедись, что стоит флаг AllowPresignedUrlEmbedding=true — это новая опция, появившаяся ещё в 2025.

aws sagemaker create-mlflow-tracking-server \
    --tracking-server-name mlflow-embed-demo \
    --mlflow-version 2.18.0 \
    --artifact-store-uri s3://mlflow-artifacts-123 \
    --role-arn arn:aws:iam::123456789012:role/SageMakerMLflowRole \
    --allow-presigned-url-embedding

После создания получишь TrackingServerArn. Запомни — он понадобится для генерации presigned URL.

2 Lambda-функция для генерации ссылок

Создаём Python Lambda с ролью, у которой есть права sagemaker:CreatePresignedMlflowTrackingServerUrl. Вот правильный код (никаких устаревших SDK — используем boto3 1.36+):

import boto3
import os
import json
from datetime import datetime, timezone

sagemaker = boto3.client('sagemaker')

def lambda_handler(event, context):
    # Получаем user из JWT (проверка делается на API Gateway level)
    claims = event['requestContext']['authorizer']['claims']
    user = claims.get('cognito:username', 'unknown')

    # Генерация presigned URL на 4 часа
    response = sagemaker.create_presigned_mlflow_tracking_server_url(
        TrackingServerName=os.environ['TRACKING_SERVER_NAME'],
        SessionExpirationDurationInSeconds=14400  # 4h
    )

    url = response['AuthorizedUrl']

    # Логируем для аудита
    print(f"[{datetime.now(timezone.utc).isoformat()}] User {user} generated URL")

    return {
        'statusCode': 200,
        'headers': {
            'Content-Type': 'application/json',
            'Access-Control-Allow-Origin': os.environ['ALLOWED_ORIGIN']
        },
        'body': json.dumps({'url': url})
    }

Обрати внимание: SessionExpirationDurationInSeconds — максимальное значение 43200 (12 часов). Для embedded приложений рекомендую 14400 (4 часа) и обновлять по таймеру.

3 API Gateway с авторизатором Cognito

Создаём HTTP API (или REST), интегрируем с Lambda. Включаем авторизатор Cognito — JWT токены. Важно: в настройках CORS укажи origin твоего портала (не звездочку, если хочешь безопасно).

# cloudformation template fragment
ApiGatewayApi:
  Type: AWS::ApiGatewayV2::Api
  Properties:
    Name: mlflow-presigned-api
    ProtocolType: HTTP
    CorsConfiguration:
      AllowOrigins: ["https://portal.mycompany.com"]
      AllowMethods: ["GET", "OPTIONS"]
      AllowHeaders: ["authorization", "content-type"]

Authorizer:
  Type: AWS::ApiGatewayV2::Authorizer
  Properties:
    JwtConfiguration:
      Audience: ["your-cognito-app-client-id"]
      Issuer: !Sub "https://cognito-idp.${AWS::Region}.amazonaws.com/${UserPoolId}"

4 Фронтенд: iframe с refresh

В корпоративном портале (React, Vue, Angular — неважно) создаём компонент. Пример на React с хуком:

import { useState, useEffect, useRef } from 'react';

const MLflowEmbed = () => {
  const [url, setUrl] = useState(null);
  const [error, setError] = useState(null);
  const intervalRef = useRef(null);

  const fetchUrl = async () => {
    try {
      const token = await getIdToken(); // из Cognito SDK
      const res = await fetch('https://api.portal.com/mlflow-url', {
        headers: { 'Authorization': `Bearer ${token}` }
      });
      const data = await res.json();
      setUrl(data.url);
      setError(null);
    } catch (err) {
      setError('Не удалось получить доступ к MLflow. Обновите страницу.');
    }
  };

  useEffect(() => {
    fetchUrl();
    // Обновляем ссылку каждые 3 часа (10800 секунд)
    intervalRef.current = setInterval(fetchUrl, 10800000);
    return () => clearInterval(intervalRef.current);
  }, []);

  if (error) return 
{error}
; if (!url) return
Загрузка...
; return ( ); };

⚠️ Без sandbox атрибута браузер может не разрешить iframe из-за Content Security Policy. MLflow UI использует свои скрипты, поэтому allow-scripts обязателен. Если нужна работа с cookies (сессия), добавляй allow-same-origin.

Подводные камни, на которых я обжёгся

1. CSP и X-Frame-Options

MLflow сервер в SageMaker по умолчанию не отдаёт заголовок X-Frame-Options: ALLOW-FROM. Но он и не запрещает embed через CSP. Проблема может возникнуть, если у корпоративного портала строгая политика. Решение — добавить в портал meta-тег <meta http-equiv="Content-Security-Policy" content="frame-src https://*.sagemaker.aws.amazon.com;">.

2. Разные регионы и VPC

Если MLflow Tracking Server развёрнут в VPC без публичного доступа, presigned URL будет указывать на приватный DNS (обычно *.sagemaker.aws через PrivateLink). for iframe внутри VPN это работает, но снаружи — нет. Убедись, что портал открывается внутри корпоративной сети или через VPN.

3. Истечение сессии и таймеры

Если пользователь держит вкладку открытой 8 часов, а ссылка живёт 4, в какой-то момент iframe сломается. Решение мы показали — периодический refresh через setInterval. Но не делай слишком частые обновления: каждая генерация создаёт новую сессию, и старые не инвалидируются (они просто истекают). AWS не лимитирует количество сессий, но злоупотреблять не стоит.

4. Права на основе SSO-групп

Ты хочешь, чтобы data scientist видел только свои эксперименты, а ML engineer — все. Через один Tracking Server это не сделать на уровне IAM: presigned URL генерируется от роли Lambda, а не от пользователя. Выход — создавать отдельные Tracking Servers для разных команд и в Lambda маппить group -> server. Или дождаться (надеяться) атрибут-базированного контроля в MLflow UI.

Как НЕ надо делать (ошибки новичков)

Ошибка №1: Генерировать presigned URL на клиенте, используя AWS SDK с ключами доступа. Никогда. Ключи утекут через DevTools.

Ошибка №2: Использовать allow-popups в sandbox — MLflow не требует всплывающих окон, это лишний риск.

Ошибка №3: Кешировать URL в localStorage. Presigned URL — временный ключ, его нельзя хранить.

Ошибка №4: Забыть про Access-Control-Allow-Origin в ответе Lambda. Без этого браузер не отдаст iframe из-за CORS.

Связка с другими инструментами

Встроенный MLflow — это круто, но не забывай про полный ML pipeline. Если ты используешь сквозной трекинг через DVC, SageMaker и MLflow для аудита, который не стыдно показать регулятору — этот embed подойдёт идеально. А для кастомизации моделей от претрейна до DPO SageMaker и так предлагает нативные инструменты.

Кстати, если тебе нужно OpenAI-совместимое API для SageMaker без SigV4, есть отдельный гайд — пригодится, когда будешь строить чат-интерфейс внутри портала.

Часто задаваемые вопросы

В: Как часто нужно обновлять presigned URL?

Оптимально — раз в 3-4 часа при длительности сессии 4 часа. Запас в 1 час на случай задержек.

В: Что делать, если iframe не загружается?

Проверь Content-Security-Policy портала, X-Frame-Options, и убедись, что регион Tracking Server совпадает с регионом, где генерируется presigned URL. Также убедись, что пользователь аутентифицирован.

В: Можно ли встроить MLflow в портал без Lambda, напрямую через Cognito?

Нет, presigned URL генерируется только через AWS API с IAM-ролью. Cognito не имеет к этому отношения.

В: Как логировать доступ к MLflow через портал?

Записывай в Lambda CloudWatch Logs каждый сгенерированный URL с username и временем. Для аудита этого достаточно.

Если ты всё сделал правильно, твоя ML-команда забудет про Slack-ссылки. Они просто откроют внутренний портал, увидят там iframe с экспериментами и скажут спасибо. Но есть нюанс: через полгода они попросят встроить туда ещё и SageMaker Canvas, и Feature Store с Iceberg и streaming. Готовься — embed-архитектура масштабируется, и это прекрасно.

Подписаться на канал