KsaiLab
Приложение

Приложения

DOCX

Приложения

Приложение А. Пример NetworkPolicy для namespace lab-*

Ниже приведён пример манифеста Kubernetes NetworkPolicy, реализующего политику default-deny для учебного namespace с единственным разрешённым входящим соединением от Lab Controller (namespace ksailab).

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: lab-001
spec:
  podSelector: {}   # применяется ко всем Pod
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: ksailab
          podSelector:
            matchLabels:
              app: lab-session-controller
      ports:
        - protocol: TCP
          port: 8080
  egress: []        # исходящий трафик запрещён

Приложение Б. Пример SecurityContext и ResourceQuota для лабораторного Pod

Ниже приведён пример блока securityContext, применяемого к лабораторным Pod-ам платформы, и ресурсной квоты namespace.

# SecurityContext
securityContext:
  runAsNonRoot: true
  runAsUser: 1000
  allowPrivilegeEscalation: false
  readOnlyRootFilesystem: true
  seccompProfile:
    type: RuntimeDefault
  capabilities:
    drop:
      - ALL
# ResourceQuota для учебного namespace
apiVersion: v1
kind: ResourceQuota
metadata:
  name: lab-namespace-quota
  namespace: lab-001
spec:
  hard:
    pods: "3"
    requests.cpu: "500m"
    requests.memory: "512Mi"
    limits.cpu: "1000m"
    limits.memory: "1Gi"
    services: "1"
    persistentvolumeclaims: "0"

Приложение В. Структура Helm-чарта платформы

Ниже представлена структура основного Helm-чарта ksailab и фрагмент values.yaml.

ksailab/
├── Chart.yaml                      # метаданные: name, version, appVersion, dependencies
├── values.yaml                     # параметры по умолчанию (без секретов)
├── values-prod.yaml                # production-переопределения
├── templates/
│   ├── _helpers.tpl                # вспомогательные шаблоны (labels, annotations)
│   ├── deployment-api.yaml         # Deployment API-сервиса
│   ├── deployment-controller.yaml  # Deployment Lab Session Controller
│   ├── service-api.yaml            # Service для API
│   ├── ingress.yaml                # Ingress с TLS
│   ├── configmap.yaml              # параметры конфигурации
│   ├── serviceaccount.yaml         # ServiceAccount и RBAC
│   ├── networkpolicy.yaml          # NetworkPolicy для ksailab
│   ├── hpa.yaml                    # HorizontalPodAutoscaler
│   └── NOTES.txt                   # инструкция после установки
└── charts/
    ├── postgresql/                 # зависимость: Bitnami PostgreSQL
    ├── redis/                      # зависимость: Bitnami Redis
    └── keycloak/                   # зависимость: Bitnami Keycloak
# Фрагмент values.yaml
api:
  replicaCount: 2
  image:
    repository: registry.example.com/ksailab/api
    tag: ""           # переопределяется при деплое из CI/CD
  resources:
    limits:
      cpu: 500m
      memory: 512Mi
  autoscaling:
    enabled: true
    minReplicas: 2
    maxReplicas: 8
    targetCPUUtilizationPercentage: 70

labController:
  sessionTTLDefault: 3600     # TTL сессии по умолчанию, секунды
  maxConcurrentSessions: 50

postgresql:
  auth:
    existingSecret: ksailab-db-secret   # Secret создаётся вне чарта

keycloak:
  auth:
    existingSecret: ksailab-keycloak-secret

Приложение Г. Структура GitLab CI/CD конвейера

Ниже приведён сокращённый фрагмент .gitlab-ci.yml, реализующего конвейер сборки, сканирования, подписи и доставки образа.

stages:
  - lint
  - test
  - build
  - scan
  - sign
  - deploy

variables:
  IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA

lint:
  stage: lint
  image: node:20-alpine
  script:
    - npm ci && npm run lint && npm run typecheck

test:
  stage: test
  image: node:20-alpine
  script:
    - npm ci && npm run test:unit && npm run test:integration

build:
  stage: build
  script:
    - docker build -t $IMAGE_TAG . && docker push $IMAGE_TAG

scan:
  stage: scan
  image:
    name: aquasec/trivy:latest
    entrypoint: [""]
  script:
    - trivy image --exit-code 1 --severity HIGH,CRITICAL $IMAGE_TAG
  allow_failure: false    # HIGH/CRITICAL CVE блокируют конвейер

sign:
  stage: sign
  image: gcr.io/projectsigstore/cosign:latest
  only:
    - main
  script:
    - echo "$COSIGN_KEY" | cosign sign --key /dev/stdin $IMAGE_TAG

deploy-prod:
  stage: deploy
  image: alpine/helm:3.14
  only:
    - main
  when: manual            # ручное подтверждение перед деплоем в production
  script:
    - helm upgrade --install ksailab ./charts/ksailab
        --namespace ksailab
        --values values-prod.yaml
        --set api.image.tag=$CI_COMMIT_SHORT_SHA
        --wait --timeout 5m

Таблица Г.1 - Условия блокировки конвейера

УсловиеСтадияДействие
Ошибки линтера / typechecklintПрерывание конвейера
Провал юнит- или интеграционных тестовtestПрерывание конвейера
CVE HIGH/CRITICAL в образеscanПрерывание конвейера
Нет подписи CosignsignОбраз не публикуется
Нет ручного подтвержденияdeploy-prodДеплой не выполняется

Приложение Д. Карта маршрутов и ролевые сценарии клиентской части

Д.1 Карта маршрутов приложения

МаршрутДоступные ролиОписание
/ВсеРедирект на главную страницу по роли
/loginГостьKeycloak OIDC-редирект
/student/my-labsStudentСписок назначенных лабораторных работ
/student/session/:idStudentАктивная лабораторная сессия
/teacher/coursesTeacherСписок курсов преподавателя
/teacher/courses/:idTeacherУправление курсом и назначениями
/teacher/monitoringTeacherМониторинг сессий студентов
/admin/dashboardAdminСводная панель администратора
/admin/usersAdminУправление пользователями и ролями
/admin/infrastructureAdminСостояние кластера и ресурсов
/admin/audit-logAdminЖурнал аудита событий
/403ВсеСтраница отказа в доступе

Маршруты защищены компонентом <ProtectedRoute>, проверяющим наличие токена и соответствие роли. При несоответствии выполняется редирект на /403.

Д.2 Сценарий запуска лабораторной среды студентом

Страница /student/my-labs
  └─▶ [Запустить] → POST /api/sessions { lab_assignment_id }
        └─▶ Статус: Requested  → спиннер «Среда создаётся...»
              └─▶ Polling GET /api/sessions/{id} каждые 5 с
                    └─▶ Статус: Provisioning → «Настройка окружения...»
                          └─▶ Статус: Running
                                └─▶ Отображается точка подключения
                                    Таймер TTL (обратный отсчёт)
                                    Кнопка [Завершить сессию]
                                      └─▶ DELETE /api/sessions/{id}
                                            └─▶ Статус: Destroyed

Д.3 Сценарий назначения ЛР преподавателем

/teacher/courses/:id
  └─▶ [Создать назначение]
        └─▶ Форма: выбор ЛР, группа, дедлайн, TTL
              └─▶ POST /api/assignments
                    └─▶ Уведомление «Назначение создано»
                          └─▶ /teacher/monitoring
                                └─▶ Таблица активных сессий
                                    (обновление каждые 10 с)