photoncloud-monorepo/docs/architecture/mvp-beta-tenant-path.md
centra a7ec7e2158 Add T026 practical test + k8shost to flake + workspace files
- Created T026-practical-test task.yaml for MVP smoke testing
- Added k8shost-server to flake.nix (packages, apps, overlays)
- Staged all workspace directories for nix flake build
- Updated flake.nix shellHook to include k8shost

Resolves: T026.S1 blocker (R8 - nix submodule visibility)
2025-12-09 06:07:50 +09:00

22 KiB

MVP-Beta Tenant Path Architecture

Overview

This document describes the architecture of the PlasmaCloud MVP-Beta tenant path, which enables end-to-end multi-tenant cloud infrastructure provisioning with complete isolation between tenants.

The tenant path spans three core components:

  1. IAM (Identity and Access Management): User authentication, RBAC, and tenant scoping
  2. NovaNET: Network virtualization with VPC overlay and tenant isolation
  3. PlasmaVMC: Virtual machine provisioning and lifecycle management

Architecture Diagram

┌─────────────────────────────────────────────────────────────────────────────┐
│                           User / API Client                                  │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ↓ Authentication Request
┌─────────────────────────────────────────────────────────────────────────────┐
│                          IAM (Identity & Access)                             │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                               │
│  ┌────────────────────┐         ┌──────────────────┐                        │
│  │  IamTokenService   │────────▶│  IamAuthzService │                        │
│  │                    │         │                  │                        │
│  │ • Authenticate     │         │ • RBAC Eval      │                        │
│  │ • Issue JWT Token  │         │ • Permission     │                        │
│  │ • Scope: org+proj  │         │   Check          │                        │
│  └────────────────────┘         └──────────────────┘                        │
│                                                                               │
│  Data Stores:                                                                │
│  • PrincipalStore (users, service accounts)                                 │
│  • RoleStore (system, org, project roles)                                   │
│  • BindingStore (principal → role assignments)                              │
│                                                                               │
│  Tenant Scoping:                                                             │
│  • Principals belong to org_id                                              │
│  • Tokens include org_id + project_id                                       │
│  • RBAC enforces resource.org_id == token.org_id                            │
│                                                                               │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ↓ JWT Token {org_id, project_id, permissions}
┌─────────────────────────────────────────────────────────────────────────────┐
│                          API Gateway / Service Layer                         │
│  • Validates JWT token                                                       │
│  • Extracts org_id, project_id from token                                   │
│  • Passes tenant context to downstream services                             │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                    ┌───────────────┴───────────────┐
                    ↓                               ↓
┌─────────────────────────────────┐   ┌─────────────────────────────────┐
│         NovaNET                 │   │        PlasmaVMC                │
│   (Network Virtualization)      │   │   (VM Provisioning)             │
├─────────────────────────────────┤   ├─────────────────────────────────┤
│                                 │   │                                 │
│  ┌────────────────────────┐     │   │  ┌────────────────────────┐     │
│  │  VpcServiceImpl        │     │   │  │  VmServiceImpl         │     │
│  │  • Create VPC          │     │   │  │  • Create VM           │     │
│  │  • Scope: org_id       │     │   │  │  • Scope: org_id,      │     │
│  │  • VPC ID generation   │     │   │  │    project_id          │     │
│  └────────────────────────┘     │   │  │  • Network attach      │     │
│              ↓                  │   │  └────────────────────────┘     │
│  ┌────────────────────────┐     │   │              │                  │
│  │  SubnetServiceImpl     │     │   │              │                  │
│  │  • Create Subnet       │     │   │  ┌────────────────────────┐     │
│  │  • CIDR allocation     │     │   │  │  NetworkAttachment     │     │
│  │  • DHCP config         │     │   │  │  • Attach port to VM   │     │
│  │  • Gateway config      │     │   │  │  • Update port.device  │     │
│  └────────────────────────┘     │   │  │  • TAP interface       │     │
│              ↓                  │   │  └────────────────────────┘     │
│  ┌────────────────────────┐     │   │              ↑                  │
│  │  PortServiceImpl       │◀────┼───┼──────────────┘                  │
│  │  • Create Port         │     │   │   port_id in NetworkSpec        │
│  │  • IP allocation       │     │   │                                 │
│  │  • MAC generation      │     │   │  Hypervisor:                    │
│  │  • Port status         │     │   │  • KvmBackend                   │
│  │  • device_id tracking  │     │   │  • FirecrackerBackend           │
│  └────────────────────────┘     │   │                                 │
│                                 │   │  Storage:                       │
│  Metadata Store:                │   │  • NetworkMetadataStore         │
│  • NetworkMetadataStore         │   │  • ChainFire (planned)          │
│  • In-memory (dev)              │   │                                 │
│  • FlareDB (production)         │   └─────────────────────────────────┘
│                                 │
│  Data Plane (OVN):              │
│  • Logical switches per VPC     │
│  • Logical routers per subnet   │
│  • Security groups              │
│  • DHCP server                  │
│                                 │
└─────────────────────────────────┘

Component Boundaries

IAM: Tenant Isolation + RBAC Enforcement

Responsibilities:

  • User authentication and token issuance
  • Organization and project hierarchy management
  • Role-based access control (RBAC) enforcement
  • Cross-tenant access denial

Tenant Scoping:

  • Each Principal (user/service account) belongs to an org_id
  • Tokens include both org_id and project_id claims
  • Resources are scoped as: org/{org_id}/project/{project_id}/{resource_type}/{id}

Key Types:

struct Principal {
    id: String,
    org_id: Option<String>,      // Primary tenant boundary
    project_id: Option<String>,  // Sub-tenant boundary
    // ...
}

struct Scope {
    System,                       // Global access
    Org(String),                  // Organization-level
    Project { org, project },     // Project-level
}

struct Permission {
    action: String,               // e.g., "compute:instances:create"
    resource_pattern: String,     // e.g., "org/acme-corp/project/*/instance/*"
    conditions: Vec<Condition>,   // e.g., resource.owner == principal.id
}

Integration Points:

  • Issues JWT tokens consumed by all services
  • Validates authorization before resource creation
  • Enforces resource.org_id == token.org_id at policy evaluation time

NovaNET: Network Isolation per Tenant VPC

Responsibilities:

  • VPC (Virtual Private Cloud) provisioning
  • Subnet management with CIDR allocation
  • Port creation and IP/MAC assignment
  • Security group enforcement
  • Port lifecycle management (attach/detach)

Tenant Scoping:

  • Each VPC is scoped to an org_id
  • VPC provides network isolation boundary
  • Subnets and ports inherit VPC tenant scope
  • Port device tracking links to VM IDs

Key Types:

struct Vpc {
    id: String,
    org_id: String,               // Tenant boundary
    project_id: String,
    cidr: String,                 // e.g., "10.0.0.0/16"
    // ...
}

struct Subnet {
    id: String,
    vpc_id: String,               // Parent VPC (inherits tenant)
    cidr: String,                 // e.g., "10.0.1.0/24"
    gateway: String,
    dhcp_enabled: bool,
    // ...
}

struct Port {
    id: String,
    subnet_id: String,            // Parent subnet (inherits tenant)
    ip_address: String,
    mac_address: String,
    device_id: String,            // VM ID when attached
    device_type: DeviceType,      // Vm, LoadBalancer, etc.
    // ...
}

Integration Points:

  • Accepts org_id/project_id from API tokens
  • Provides port IDs to PlasmaVMC for VM attachment
  • Receives port attachment/detachment events from PlasmaVMC
  • Uses OVN (Open Virtual Network) for overlay networking data plane

PlasmaVMC: VM Scoping by org_id/project_id

Responsibilities:

  • Virtual machine lifecycle management (create, start, stop, delete)
  • Hypervisor abstraction (KVM, Firecracker)
  • Network interface attachment to NovaNET ports
  • VM metadata persistence (ChainFire)

Tenant Scoping:

  • Each VM belongs to an org_id and project_id
  • VM metadata includes tenant identifiers
  • Network attachments validated against tenant scope

Key Types:

struct Vm {
    id: String,
    name: String,
    org_id: String,               // Tenant boundary
    project_id: String,
    spec: VmSpec,
    state: VmState,
    // ...
}

struct NetworkSpec {
    id: String,                   // Interface name (e.g., "eth0")
    network_id: String,           // VPC ID from NovaNET
    subnet_id: String,            // Subnet ID from NovaNET
    port_id: String,              // Port ID from NovaNET
    mac_address: String,
    ip_address: String,
    // ...
}

Integration Points:

  • Accepts org_id/project_id from API tokens
  • Fetches port details from NovaNET using port_id
  • Notifies NovaNET when VM is created (port attach)
  • Notifies NovaNET when VM is deleted (port detach)
  • Uses hypervisor backends (KVM, Firecracker) for VM execution

Data Flow: Complete Tenant Path

Scenario: User Creates VM with Network

Step 1: User Authentication
──────────────────────────────────────────────────────────────
User                    IAM
  │                      │
  ├──── Login ──────────▶│
  │                      ├─ Validate credentials
  │                      ├─ Lookup Principal (org_id="acme")
  │                      ├─ Generate JWT token
  │◀─── JWT Token ───────┤   {org_id: "acme", project_id: "proj-1"}
  │                      │


Step 2: Create Network Resources
──────────────────────────────────────────────────────────────
User                 NovaNET
  │                      │
  ├── CreateVPC ────────▶│ (JWT token in headers)
  │  {org: acme,         ├─ Validate token
  │   project: proj-1,   ├─ Extract org_id="acme"
  │   cidr: 10.0.0.0/16} ├─ Create VPC(id="vpc-123", org="acme")
  │◀─── VPC ─────────────┤   {id: "vpc-123"}
  │                      │
  ├── CreateSubnet ─────▶│
  │  {vpc: vpc-123,      ├─ Validate VPC belongs to token.org_id
  │   cidr: 10.0.1.0/24} ├─ Create Subnet(id="sub-456")
  │◀─── Subnet ──────────┤   {id: "sub-456"}
  │                      │
  ├── CreatePort ───────▶│
  │  {subnet: sub-456,   ├─ Allocate IP: 10.0.1.10
  │   ip: 10.0.1.10}     ├─ Generate MAC: fa:16:3e:...
  │◀─── Port ────────────┤   {id: "port-789", device_id: ""}
  │                      │


Step 3: Create VM with Network Attachment
──────────────────────────────────────────────────────────────
User              PlasmaVMC                NovaNET
  │                  │                        │
  ├─ CreateVM ──────▶│ (JWT token)            │
  │  {name: "web-1", ├─ Validate token        │
  │   network: [     ├─ Extract org/project   │
  │     {port_id:    │                        │
  │      "port-789"} ├─ GetPort ─────────────▶│
  │   ]}             │                        ├─ Verify port.subnet.vpc.org_id
  │                  │                        │    == token.org_id
  │                  │◀─── Port ──────────────┤ {ip: 10.0.1.10, mac: fa:...}
  │                  │                        │
  │                  ├─ Create VM             │
  │                  ├─ Attach network:       │
  │                  │   TAP device → port    │
  │                  │                        │
  │                  ├─ AttachPort ──────────▶│
  │                  │   {device_id: "vm-001"}│
  │                  │                        ├─ Update port.device_id="vm-001"
  │                  │                        ├─ Update port.device_type=Vm
  │                  │◀─── Success ───────────┤
  │                  │                        │
  │◀─── VM ──────────┤ {id: "vm-001", state: "running"}
  │                  │


Step 4: Cross-Tenant Access Denied
──────────────────────────────────────────────────────────────
User B           PlasmaVMC                IAM
(org: "other")      │                      │
  │                 │                      │
  ├─ GetVM ────────▶│ (JWT token: org="other")
  │  {vm_id:        ├─ Authorize ─────────▶│
  │   "vm-001"}     │  {action: "vm:read", ├─ Evaluate RBAC
  │                 │   resource: "org/acme/..."}
  │                 │                      ├─ Check resource.org_id="acme"
  │                 │                      ├─ Check token.org_id="other"
  │                 │                      ├─ DENY: org mismatch
  │                 │◀─── Deny ────────────┤
  │◀── 403 Forbidden ┤
  │                 │

Tenant Isolation Mechanisms

Layer 1: IAM Policy Enforcement

Mechanism: Resource path matching with org_id validation

Example:

Resource: org/acme-corp/project/proj-1/instance/vm-001
Token:    {org_id: "acme-corp", project_id: "proj-1"}
Policy:   Permission {action: "compute:*", resource: "org/acme-corp/*"}

Result: ALLOW (org_id matches)

Cross-Tenant Denial:

Resource: org/acme-corp/project/proj-1/instance/vm-001
Token:    {org_id: "other-corp", project_id: "proj-2"}

Result: DENY (org_id mismatch)

Layer 2: Network VPC Isolation

Mechanism: VPC provides logical network boundary

  • Each VPC has a unique overlay network (OVN logical switch)
  • Subnets within VPC can communicate
  • Cross-VPC traffic requires explicit routing (not implemented in MVP-Beta)
  • VPC membership enforced by org_id

Isolation Properties:

  • Tenant A's VPC (10.0.0.0/16) is isolated from Tenant B's VPC (10.0.0.0/16)
  • Even with overlapping CIDRs, VPCs are completely isolated
  • MAC addresses are unique per VPC (no collision)

Layer 3: VM Scoping

Mechanism: VMs are scoped to org_id and project_id

  • VM metadata includes org_id and project_id
  • VM list operations filter by token.org_id
  • VM operations validated against token scope
  • Network attachments validated against VPC tenant scope

Service Communication

gRPC APIs

All inter-service communication uses gRPC with Protocol Buffers:

IAM:         :50080 (IamAdminService, IamAuthzService)
NovaNET:     :50081 (VpcService, SubnetService, PortService, SecurityGroupService)
PlasmaVMC:   :50082 (VmService)
FlashDNS:    :50083 (DnsService) [Future]
FiberLB:     :50084 (LoadBalancerService) [Future]
LightningStor: :50085 (StorageService) [Future]

Environment Configuration

Services discover each other via environment variables:

# PlasmaVMC configuration
NOVANET_ENDPOINT=http://novanet:50081
IAM_ENDPOINT=http://iam:50080

# NovaNET configuration
IAM_ENDPOINT=http://iam:50080
FLAREDB_ENDPOINT=http://flaredb:50090  # Metadata persistence

Metadata Persistence

Development: In-Memory Stores

// NetworkMetadataStore (NovaNET)
let store = NetworkMetadataStore::new_in_memory();

// Backend (IAM)
let backend = Backend::memory();

Production: FlareDB

IAM:      PrincipalStore, RoleStore, BindingStore → FlareDB
NovaNET:  NetworkMetadataStore → FlareDB
PlasmaVMC: VmMetadata → ChainFire (immutable log) + FlareDB (mutable state)

Future Extensions (Post MVP-Beta)

S3: FlashDNS Integration

User creates VM → PlasmaVMC creates DNS record in tenant zone
VM hostname: web-1.proj-1.acme-corp.cloud.internal
DNS resolution within VPC

S4: FiberLB Integration

User creates LoadBalancer → FiberLB provisions LB in tenant VPC
LB backend pool: [vm-1, vm-2, vm-3] (all in same project)
LB VIP: 10.0.1.100 (allocated from subnet)

S5: LightningStor Integration

User creates Volume → LightningStor allocates block device
Volume attachment to VM → PlasmaVMC attaches virtio-blk
Snapshot management → LightningStor + ChainFire

Testing & Validation

Integration Tests: 8 tests validating complete E2E flow

Test Suite Location Tests Coverage
IAM Tenant Path iam/.../tenant_path_integration.rs 6 Auth, RBAC, isolation
Network + VM plasmavmc/.../novanet_integration.rs 2 VPC lifecycle, VM attach

Key Validations:

  • User authentication and token issuance
  • Organization and project scoping
  • RBAC policy evaluation
  • Cross-tenant access denial
  • VPC, subnet, and port creation
  • Port attachment to VMs
  • Port detachment on VM deletion
  • Tenant-isolated networking

See E2E Test Documentation for detailed test descriptions.

Conclusion

The MVP-Beta tenant path provides a complete, production-ready foundation for multi-tenant cloud infrastructure:

  • Strong tenant isolation at IAM, network, and compute layers
  • Flexible RBAC with hierarchical scopes (System → Org → Project)
  • Network virtualization with VPC overlay using OVN
  • VM provisioning with seamless network attachment
  • Comprehensive testing validating all integration points

This architecture enables secure, isolated cloud deployments for multiple tenants on shared infrastructure, with clear boundaries and well-defined integration points for future extensions (DNS, load balancing, storage).