photoncloud-monorepo/docs/getting-started/tenant-onboarding.md
centra d2149b6249 fix(lightningstor): Fix SigV4 canonicalization for AWS S3 auth
- Replace form_urlencoded with RFC 3986 compliant URI encoding
- Implement aws_uri_encode() matching AWS SigV4 spec exactly
- Unreserved chars (A-Z,a-z,0-9,-,_,.,~) not encoded
- All other chars percent-encoded with uppercase hex
- Preserve slashes in paths, encode in query params
- Normalize empty paths to '/' per AWS spec
- Fix test expectations (body hash, HMAC values)
- Add comprehensive SigV4 signature determinism test

This fixes the canonicalization mismatch that caused signature
validation failures in T047. Auth can now be enabled for production.

Refs: T058.S1
2025-12-12 06:23:46 +09:00

647 lines
15 KiB
Markdown

# Tenant Onboarding Guide
## Overview
This guide walks you through the complete process of onboarding your first tenant in PlasmaCloud, from user creation through VM deployment with networking. By the end of this guide, you will have:
1. A running PlasmaCloud infrastructure (IAM, PrismNET, PlasmaVMC)
2. An authenticated user with proper RBAC permissions
3. A complete network setup (VPC, Subnet, Port)
4. A virtual machine with network connectivity
**Time to Complete**: ~15 minutes
## Prerequisites
### System Requirements
- **Operating System**: Linux (Ubuntu 20.04+ recommended)
- **Rust**: 1.70 or later
- **Cargo**: Latest version (comes with Rust)
- **Memory**: 4GB minimum (8GB recommended for VM testing)
- **Disk**: 10GB free space
### Optional Components
- **OVN (Open Virtual Network)**: For real overlay networking (not required for basic testing)
- **KVM**: For actual VM execution (tests can run in mock mode without KVM)
- **Docker**: If running services in containers
### Installation
```bash
# Install Rust (if not already installed)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env
# Verify installation
rustc --version
cargo --version
```
## Architecture Quick Reference
```
User → IAM (Auth) → Token {org_id, project_id}
┌────────────┴────────────┐
↓ ↓
PrismNET PlasmaVMC
(VPC/Subnet/Port) (VM)
↓ ↓
└──────── port_id ────────┘
```
For detailed architecture, see [Architecture Documentation](../architecture/mvp-beta-tenant-path.md).
## Step 1: Clone and Build PlasmaCloud
### Clone the Repository
```bash
# Clone the main repository
cd /home/centra/cloud
git clone https://github.com/your-org/plasmavmc.git
cd plasmavmc
# Initialize submodules (IAM, ChainFire, FlareDB, etc.)
git submodule update --init --recursive
```
### Build All Components
```bash
# Build IAM
cd /home/centra/cloud/iam
cargo build --release
# Build PrismNET
cd /home/centra/cloud/prismnet
cargo build --release
# Build PlasmaVMC
cd /home/centra/cloud/plasmavmc
cargo build --release
```
**Build Time**: 5-10 minutes (first build)
## Step 2: Start PlasmaCloud Services
Open three terminal windows to run the services:
### Terminal 1: Start IAM Service
```bash
cd /home/centra/cloud/iam
# Run IAM server on port 50080
cargo run --bin iam-server -- --port 50080
# Expected output:
# [INFO] IAM server listening on 0.0.0.0:50080
# [INFO] Principal store initialized (in-memory)
# [INFO] Role store initialized (in-memory)
# [INFO] Binding store initialized (in-memory)
```
### Terminal 2: Start PrismNET Service
```bash
cd /home/centra/cloud/prismnet
# Set environment variables
export IAM_ENDPOINT=http://localhost:50080
# Run PrismNET server on port 50081
cargo run --bin prismnet-server -- --port 50081
# Expected output:
# [INFO] PrismNET server listening on 0.0.0.0:50081
# [INFO] NetworkMetadataStore initialized (in-memory)
# [INFO] OVN integration: disabled (mock mode)
```
### Terminal 3: Start PlasmaVMC Service
```bash
cd /home/centra/cloud/plasmavmc
# Set environment variables
export NOVANET_ENDPOINT=http://localhost:50081
export IAM_ENDPOINT=http://localhost:50080
export PLASMAVMC_STORAGE_BACKEND=file
# Run PlasmaVMC server on port 50082
cargo run --bin plasmavmc-server -- --port 50082
# Expected output:
# [INFO] PlasmaVMC server listening on 0.0.0.0:50082
# [INFO] Hypervisor registry initialized
# [INFO] KVM backend registered (mock mode)
# [INFO] Connected to PrismNET: http://localhost:50081
```
**Verification**: All three services should be running without errors.
## Step 3: Create User & Authenticate
### Using grpcurl (Recommended)
Install grpcurl if not already installed:
```bash
# Install grpcurl
go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
# or on Ubuntu:
sudo apt-get install grpcurl
```
### Create Organization Admin User
```bash
# Create a principal (user) for your organization
grpcurl -plaintext -d '{
"principal": {
"id": "alice",
"name": "Alice Smith",
"email": "alice@acmecorp.com",
"org_id": "acme-corp",
"principal_type": "USER"
}
}' localhost:50080 iam.v1.IamAdminService/CreatePrincipal
# Expected response:
# {
# "principal": {
# "id": "alice",
# "name": "Alice Smith",
# "email": "alice@acmecorp.com",
# "org_id": "acme-corp",
# "principal_type": "USER",
# "created_at": "2025-12-09T10:00:00Z"
# }
# }
```
### Create OrgAdmin Role
```bash
# Create a role that grants full access to the organization
grpcurl -plaintext -d '{
"role": {
"name": "roles/OrgAdmin",
"display_name": "Organization Administrator",
"description": "Full access to all resources in the organization",
"scope": {
"org": "acme-corp"
},
"permissions": [
{
"action": "*",
"resource_pattern": "org/acme-corp/*"
}
]
}
}' localhost:50080 iam.v1.IamAdminService/CreateRole
# Expected response:
# {
# "role": {
# "name": "roles/OrgAdmin",
# "display_name": "Organization Administrator",
# ...
# }
# }
```
### Bind User to Role
```bash
# Assign the OrgAdmin role to Alice at org scope
grpcurl -plaintext -d '{
"binding": {
"id": "alice-org-admin",
"principal_ref": {
"type": "USER",
"id": "alice"
},
"role_name": "roles/OrgAdmin",
"scope": {
"org": "acme-corp"
}
}
}' localhost:50080 iam.v1.IamAdminService/CreateBinding
# Expected response:
# {
# "binding": {
# "id": "alice-org-admin",
# ...
# }
# }
```
### Issue Authentication Token
```bash
# Issue a token for Alice scoped to project-alpha
grpcurl -plaintext -d '{
"principal_id": "alice",
"org_id": "acme-corp",
"project_id": "project-alpha",
"ttl_seconds": 3600
}' localhost:50080 iam.v1.IamTokenService/IssueToken
# Expected response:
# {
# "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
# "expires_at": "2025-12-09T11:00:00Z"
# }
```
**Save the token**: You'll use this token in subsequent API calls.
```bash
export TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
```
## Step 4: Create Network Resources
### Create VPC (Virtual Private Cloud)
```bash
grpcurl -plaintext \
-H "Authorization: Bearer $TOKEN" \
-d '{
"org_id": "acme-corp",
"project_id": "project-alpha",
"name": "main-vpc",
"description": "Main VPC for project-alpha",
"cidr": "10.0.0.0/16"
}' localhost:50081 prismnet.v1.VpcService/CreateVpc
# Expected response:
# {
# "vpc": {
# "id": "vpc-1a2b3c4d",
# "org_id": "acme-corp",
# "project_id": "project-alpha",
# "name": "main-vpc",
# "cidr": "10.0.0.0/16",
# ...
# }
# }
```
**Save the VPC ID**:
```bash
export VPC_ID="vpc-1a2b3c4d"
```
### Create Subnet with DHCP
```bash
grpcurl -plaintext \
-H "Authorization: Bearer $TOKEN" \
-d "{
\"org_id\": \"acme-corp\",
\"project_id\": \"project-alpha\",
\"vpc_id\": \"$VPC_ID\",
\"name\": \"web-subnet\",
\"description\": \"Subnet for web tier\",
\"cidr\": \"10.0.1.0/24\",
\"gateway\": \"10.0.1.1\",
\"dhcp_enabled\": true
}" localhost:50081 prismnet.v1.SubnetService/CreateSubnet
# Expected response:
# {
# "subnet": {
# "id": "subnet-5e6f7g8h",
# "vpc_id": "vpc-1a2b3c4d",
# "cidr": "10.0.1.0/24",
# "gateway": "10.0.1.1",
# "dhcp_enabled": true,
# ...
# }
# }
```
**Save the Subnet ID**:
```bash
export SUBNET_ID="subnet-5e6f7g8h"
```
### Create Port (Network Interface)
```bash
grpcurl -plaintext \
-H "Authorization: Bearer $TOKEN" \
-d "{
\"org_id\": \"acme-corp\",
\"project_id\": \"project-alpha\",
\"subnet_id\": \"$SUBNET_ID\",
\"name\": \"web-server-port\",
\"description\": \"Port for web server VM\",
\"ip_address\": \"10.0.1.10\",
\"security_group_ids\": []
}" localhost:50081 prismnet.v1.PortService/CreatePort
# Expected response:
# {
# "port": {
# "id": "port-9i0j1k2l",
# "subnet_id": "subnet-5e6f7g8h",
# "ip_address": "10.0.1.10",
# "mac_address": "fa:16:3e:12:34:56",
# "device_id": "",
# "device_type": "NONE",
# ...
# }
# }
```
**Save the Port ID**:
```bash
export PORT_ID="port-9i0j1k2l"
```
## Step 5: Deploy Virtual Machine
### Create VM with Network Attachment
```bash
grpcurl -plaintext \
-H "Authorization: Bearer $TOKEN" \
-d "{
\"name\": \"web-server-1\",
\"org_id\": \"acme-corp\",
\"project_id\": \"project-alpha\",
\"hypervisor\": \"KVM\",
\"spec\": {
\"cpu\": {
\"cores\": 2,
\"threads\": 1
},
\"memory\": {
\"size_mb\": 2048
},
\"network\": [
{
\"id\": \"eth0\",
\"network_id\": \"$VPC_ID\",
\"subnet_id\": \"$SUBNET_ID\",
\"port_id\": \"$PORT_ID\",
\"model\": \"VIRTIO_NET\"
}
]
},
\"metadata\": {
\"environment\": \"production\",
\"tier\": \"web\"
}
}" localhost:50082 plasmavmc.v1.VmService/CreateVm
# Expected response:
# {
# "id": "vm-3m4n5o6p",
# "name": "web-server-1",
# "org_id": "acme-corp",
# "project_id": "project-alpha",
# "state": "RUNNING",
# "spec": {
# "cpu": { "cores": 2, "threads": 1 },
# "memory": { "size_mb": 2048 },
# "network": [
# {
# "id": "eth0",
# "port_id": "port-9i0j1k2l",
# "ip_address": "10.0.1.10",
# "mac_address": "fa:16:3e:12:34:56"
# }
# ]
# },
# ...
# }
```
**Save the VM ID**:
```bash
export VM_ID="vm-3m4n5o6p"
```
## Step 6: Verification
### Verify Port Attachment
```bash
# Check that the port is now attached to the VM
grpcurl -plaintext \
-H "Authorization: Bearer $TOKEN" \
-d "{
\"org_id\": \"acme-corp\",
\"project_id\": \"project-alpha\",
\"subnet_id\": \"$SUBNET_ID\",
\"id\": \"$PORT_ID\"
}" localhost:50081 prismnet.v1.PortService/GetPort
# Verify response shows:
# "device_id": "vm-3m4n5o6p"
# "device_type": "VM"
```
### Verify VM Network Configuration
```bash
# Get VM details
grpcurl -plaintext \
-H "Authorization: Bearer $TOKEN" \
-d "{
\"org_id\": \"acme-corp\",
\"project_id\": \"project-alpha\",
\"vm_id\": \"$VM_ID\"
}" localhost:50082 plasmavmc.v1.VmService/GetVm
# Verify response shows:
# - state: "RUNNING"
# - network[0].ip_address: "10.0.1.10"
# - network[0].mac_address: "fa:16:3e:12:34:56"
```
### Verify Cross-Tenant Isolation
Try to access the VM with a different tenant's token (should fail):
```bash
# Create a second user in a different org
grpcurl -plaintext -d '{
"principal": {
"id": "bob",
"name": "Bob Jones",
"org_id": "other-corp"
}
}' localhost:50080 iam.v1.IamAdminService/CreatePrincipal
# Issue token for Bob
grpcurl -plaintext -d '{
"principal_id": "bob",
"org_id": "other-corp",
"project_id": "project-beta"
}' localhost:50080 iam.v1.IamTokenService/IssueToken
export BOB_TOKEN="<bob's token>"
# Try to get Alice's VM (should fail)
grpcurl -plaintext \
-H "Authorization: Bearer $BOB_TOKEN" \
-d "{
\"org_id\": \"acme-corp\",
\"project_id\": \"project-alpha\",
\"vm_id\": \"$VM_ID\"
}" localhost:50082 plasmavmc.v1.VmService/GetVm
# Expected: 403 Forbidden or "Permission denied"
```
## Step 7: Cleanup (Optional)
### Delete VM
```bash
grpcurl -plaintext \
-H "Authorization: Bearer $TOKEN" \
-d "{
\"org_id\": \"acme-corp\",
\"project_id\": \"project-alpha\",
\"vm_id\": \"$VM_ID\",
\"force\": true
}" localhost:50082 plasmavmc.v1.VmService/DeleteVm
# Verify port is detached
grpcurl -plaintext \
-H "Authorization: Bearer $TOKEN" \
-d "{
\"org_id\": \"acme-corp\",
\"project_id\": \"project-alpha\",
\"subnet_id\": \"$SUBNET_ID\",
\"id\": \"$PORT_ID\"
}" localhost:50081 prismnet.v1.PortService/GetPort
# Verify: device_id should be empty
```
## Common Issues & Troubleshooting
### Issue: "Connection refused" when calling services
**Solution**: Ensure all three services are running:
```bash
# Check if services are listening
netstat -tuln | grep -E '50080|50081|50082'
# Or use lsof
lsof -i :50080
lsof -i :50081
lsof -i :50082
```
### Issue: "Permission denied" when creating resources
**Solution**: Verify token is valid and has correct scope:
```bash
# Decode JWT token to verify claims
echo $TOKEN | cut -d '.' -f 2 | base64 -d | jq .
# Should show:
# {
# "org_id": "acme-corp",
# "project_id": "project-alpha",
# "exp": <expiration timestamp>
# }
```
### Issue: Port not attaching to VM
**Solution**: Verify port exists and is in the correct tenant scope:
```bash
# List all ports in subnet
grpcurl -plaintext \
-H "Authorization: Bearer $TOKEN" \
-d "{
\"org_id\": \"acme-corp\",
\"project_id\": \"project-alpha\",
\"subnet_id\": \"$SUBNET_ID\"
}" localhost:50081 prismnet.v1.PortService/ListPorts
```
### Issue: VM creation fails with "Hypervisor error"
**Solution**: This is expected if running in mock mode without KVM. The integration tests use mock hypervisors. For real VM execution, ensure KVM is installed:
```bash
# Check KVM support
lsmod | grep kvm
# Install KVM (Ubuntu)
sudo apt-get install qemu-kvm libvirt-daemon-system
```
## Next Steps
### Run Integration Tests
Verify your setup by running the E2E tests:
```bash
# IAM tenant path tests
cd /home/centra/cloud/iam
cargo test --test tenant_path_integration
# Network + VM integration tests
cd /home/centra/cloud/plasmavmc
cargo test --test prismnet_integration -- --ignored
```
See [E2E Test Documentation](../por/T023-e2e-tenant-path/e2e_test.md) for detailed test descriptions.
### Explore Advanced Features
- **RBAC**: Create custom roles with fine-grained permissions
- **Multi-Project**: Create multiple projects within your organization
- **Security Groups**: Add firewall rules to your ports
- **VPC Peering**: Connect multiple VPCs (coming in future releases)
### Deploy to Production
For production deployments:
1. **Use FlareDB**: Replace in-memory stores with FlareDB for persistence
2. **Enable OVN**: Configure OVN for real overlay networking
3. **TLS/mTLS**: Secure gRPC connections with TLS certificates
4. **API Gateway**: Add authentication gateway for token validation
5. **Monitoring**: Set up Prometheus metrics and logging
See [Production Deployment Guide](./production-deployment.md) (coming soon).
## Architecture & References
- **Architecture Overview**: [MVP-Beta Tenant Path](../architecture/mvp-beta-tenant-path.md)
- **E2E Tests**: [Test Documentation](../por/T023-e2e-tenant-path/e2e_test.md)
- **T023 Summary**: [SUMMARY.md](../por/T023-e2e-tenant-path/SUMMARY.md)
- **Component Specs**:
- [IAM Specification](/home/centra/cloud/specifications/iam.md)
- [PrismNET Specification](/home/centra/cloud/specifications/prismnet.md)
- [PlasmaVMC Specification](/home/centra/cloud/specifications/plasmavmc.md)
## Summary
Congratulations! You've successfully onboarded your first tenant in PlasmaCloud. You have:
- ✅ Created a user with organization and project scope
- ✅ Assigned RBAC permissions (OrgAdmin role)
- ✅ Provisioned a complete network stack (VPC → Subnet → Port)
- ✅ Deployed a virtual machine with network attachment
- ✅ Verified tenant isolation works correctly
Your PlasmaCloud deployment is now ready for multi-tenant cloud workloads!
For questions or issues, please file a GitHub issue or consult the [Architecture Documentation](../architecture/mvp-beta-tenant-path.md).