feat: Add Mycelium IPv6 overlay networking
- Integrate Mycelium IPv6 overlay networking - Add Mycelium configuration options to HeroPods - Enable Mycelium setup and cleanup for containers - Include Mycelium examples and documentation
This commit is contained in:
169
examples/virt/heropods/README.md
Normal file
169
examples/virt/heropods/README.md
Normal file
@@ -0,0 +1,169 @@
|
||||
# HeroPods Examples
|
||||
|
||||
This directory contains example HeroScript files demonstrating different HeroPods use cases.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- **Linux system** (HeroPods requires Linux-specific tools: ip, iptables, nsenter, crun)
|
||||
- **Root/sudo access** (required for network configuration and container management)
|
||||
- **Podman** (optional but recommended for image management)
|
||||
- **Hero CLI** installed and configured
|
||||
|
||||
## Example Scripts
|
||||
|
||||
### 1. simple_container.heroscript
|
||||
|
||||
**Purpose**: Demonstrate basic container lifecycle management
|
||||
|
||||
**What it does**:
|
||||
|
||||
- Creates a HeroPods instance
|
||||
- Creates an Alpine Linux container
|
||||
- Starts the container
|
||||
- Executes basic commands inside the container (uname, ls, cat, ps, env)
|
||||
- Stops the container
|
||||
- Deletes the container
|
||||
|
||||
**Run it**:
|
||||
|
||||
```bash
|
||||
hero run examples/virt/heropods/simple_container.heroscript
|
||||
```
|
||||
|
||||
**Use this when**: You want to learn the basic container operations without networking complexity.
|
||||
|
||||
---
|
||||
|
||||
### 2. ipv4_connection.heroscript
|
||||
|
||||
**Purpose**: Demonstrate IPv4 networking and internet connectivity
|
||||
|
||||
**What it does**:
|
||||
|
||||
- Creates a HeroPods instance with bridge networking
|
||||
- Creates an Alpine Linux container
|
||||
- Starts the container with IPv4 networking
|
||||
- Verifies network configuration (interfaces, routes, DNS)
|
||||
- Tests DNS resolution
|
||||
- Tests HTTP/HTTPS connectivity to the internet
|
||||
- Stops and deletes the container
|
||||
|
||||
**Run it**:
|
||||
|
||||
```bash
|
||||
hero run examples/virt/heropods/ipv4_connection.heroscript
|
||||
```
|
||||
|
||||
**Use this when**: You want to verify that IPv4 bridge networking and internet access work correctly.
|
||||
|
||||
---
|
||||
|
||||
### 3. container_mycelium.heroscript
|
||||
|
||||
**Purpose**: Demonstrate Mycelium IPv6 overlay networking
|
||||
|
||||
**What it does**:
|
||||
|
||||
- Creates a HeroPods instance
|
||||
- Enables Mycelium IPv6 overlay network with all required configuration
|
||||
- Creates an Alpine Linux container
|
||||
- Starts the container with both IPv4 and IPv6 (Mycelium) networking
|
||||
- Verifies IPv6 configuration
|
||||
- Tests Mycelium IPv6 connectivity to public nodes
|
||||
- Verifies dual-stack networking (IPv4 + IPv6)
|
||||
- Stops and deletes the container
|
||||
|
||||
**Run it**:
|
||||
|
||||
```bash
|
||||
hero run examples/virt/heropods/container_mycelium.heroscript
|
||||
```
|
||||
|
||||
**Use this when**: You want to test Mycelium IPv6 overlay networking for encrypted peer-to-peer connectivity.
|
||||
|
||||
**Note**: Requires Mycelium to be installed and configured on the host system.
|
||||
|
||||
---
|
||||
|
||||
### 4. demo.heroscript
|
||||
|
||||
**Purpose**: Quick demonstration of HeroPods with both IPv4 and IPv6 networking
|
||||
|
||||
**What it does**:
|
||||
|
||||
- Combines IPv4 and Mycelium IPv6 networking in a single demo
|
||||
- Shows a complete workflow from configuration to cleanup
|
||||
- Serves as a quick reference for common operations
|
||||
|
||||
**Run it**:
|
||||
|
||||
```bash
|
||||
hero run examples/virt/heropods/demo.heroscript
|
||||
```
|
||||
|
||||
**Use this when**: You want a quick overview of HeroPods capabilities.
|
||||
|
||||
---
|
||||
|
||||
## Common Issues
|
||||
|
||||
### Permission Denied for ping/ping6
|
||||
|
||||
Alpine Linux containers don't have `CAP_NET_RAW` capability by default, which is required for ICMP packets (ping).
|
||||
|
||||
**Solution**: Use `wget`, `curl`, or `nc` for connectivity testing instead of ping.
|
||||
|
||||
### Mycelium Not Found
|
||||
|
||||
If you get errors about Mycelium not being installed:
|
||||
|
||||
**Solution**: The HeroPods Mycelium integration will automatically install Mycelium when you run `heropods.enable_mycelium`. Make sure you have internet connectivity and the required permissions.
|
||||
|
||||
### Container Already Exists
|
||||
|
||||
If you get errors about containers already existing:
|
||||
|
||||
**Solution**: Either delete the existing container manually or set `reset:true` in the `heropods.configure` action.
|
||||
|
||||
---
|
||||
|
||||
## Learning Path
|
||||
|
||||
We recommend running the examples in this order:
|
||||
|
||||
1. **simple_container.heroscript** - Learn basic container operations
|
||||
2. **ipv4_connection.heroscript** - Understand IPv4 networking
|
||||
3. **container_mycelium.heroscript** - Explore IPv6 overlay networking
|
||||
4. **demo.heroscript** - See everything together
|
||||
|
||||
---
|
||||
|
||||
## Customization
|
||||
|
||||
Feel free to modify these scripts to:
|
||||
|
||||
- Use different container images (Ubuntu, custom images, etc.)
|
||||
- Test different network configurations
|
||||
- Add your own commands and tests
|
||||
- Experiment with multiple containers
|
||||
|
||||
---
|
||||
|
||||
## Documentation
|
||||
|
||||
For more information, see:
|
||||
|
||||
- [HeroPods Main README](../../../lib/virt/heropods/readme.md)
|
||||
- [Mycelium Integration Guide](../../../lib/virt/heropods/MYCELIUM_README.md)
|
||||
- [Production Readiness Review](../../../lib/virt/heropods/PRODUCTION_READINESS_REVIEW.md)
|
||||
|
||||
---
|
||||
|
||||
## Support
|
||||
|
||||
If you encounter issues:
|
||||
|
||||
1. Check the logs in `~/.containers/logs/`
|
||||
2. Verify your system meets the prerequisites
|
||||
3. Review the error messages carefully
|
||||
4. Consult the documentation linked above
|
||||
125
examples/virt/heropods/container_mycelium.heroscript
Normal file
125
examples/virt/heropods/container_mycelium.heroscript
Normal file
@@ -0,0 +1,125 @@
|
||||
#!/usr/bin/env hero
|
||||
|
||||
// ============================================================================
|
||||
// HeroPods Example: Mycelium IPv6 Overlay Networking
|
||||
// ============================================================================
|
||||
//
|
||||
// This script demonstrates Mycelium IPv6 overlay networking:
|
||||
// - End-to-end encrypted IPv6 connectivity
|
||||
// - Peer-to-peer routing through public relay nodes
|
||||
// - Container IPv6 address assignment from host's /64 prefix
|
||||
// - Connectivity to other Mycelium nodes across the internet
|
||||
//
|
||||
// Mycelium provides each container with an IPv6 address in the 400::/7 range
|
||||
// and enables encrypted communication with other Mycelium nodes.
|
||||
// ============================================================================
|
||||
|
||||
// Step 1: Configure HeroPods instance
|
||||
// This creates a HeroPods instance with default IPv4 networking
|
||||
!!heropods.configure
|
||||
name:'mycelium_demo'
|
||||
reset:false
|
||||
use_podman:true
|
||||
|
||||
// Step 2: Enable Mycelium IPv6 overlay network
|
||||
// All parameters are required for Mycelium configuration
|
||||
!!heropods.enable_mycelium
|
||||
heropods:'mycelium_demo'
|
||||
version:'v0.5.6'
|
||||
ipv6_range:'400::/7'
|
||||
key_path:'~/hero/cfg/priv_key.bin'
|
||||
peers:[
|
||||
'tcp://185.69.166.8:9651',
|
||||
'quic://[2a02:1802:5e:0:ec4:7aff:fe51:e36b]:9651',
|
||||
'tcp://65.109.18.113:9651',
|
||||
'quic://[2a01:4f9:5a:1042::2]:9651',
|
||||
'tcp://5.78.122.16:9651',
|
||||
'quic://[2a01:4ff:1f0:8859::1]:9651',
|
||||
'tcp://5.223.43.251:9651',
|
||||
'quic://[2a01:4ff:2f0:3621::1]:9651',
|
||||
'tcp://142.93.217.194:9651',
|
||||
'quic://[2400:6180:100:d0::841:2001]:9651'
|
||||
]
|
||||
|
||||
// Step 3: Create a new Alpine Linux container
|
||||
// Alpine includes basic IPv6 networking tools
|
||||
!!heropods.container_new
|
||||
name:'mycelium_container'
|
||||
image:'custom'
|
||||
custom_image_name:'alpine_3_20'
|
||||
docker_url:'docker.io/library/alpine:3.20'
|
||||
|
||||
// Step 4: Start the container
|
||||
// This sets up both IPv4 and IPv6 (Mycelium) networking
|
||||
!!heropods.container_start
|
||||
name:'mycelium_container'
|
||||
|
||||
// Step 5: Verify IPv6 network configuration
|
||||
|
||||
// Show all network interfaces (including IPv6 addresses)
|
||||
!!heropods.container_exec
|
||||
name:'mycelium_container'
|
||||
cmd:'ip addr show'
|
||||
stdout:true
|
||||
|
||||
// Show IPv6 addresses specifically
|
||||
!!heropods.container_exec
|
||||
name:'mycelium_container'
|
||||
cmd:'ip -6 addr show'
|
||||
stdout:true
|
||||
|
||||
// Show IPv6 routing table
|
||||
!!heropods.container_exec
|
||||
name:'mycelium_container'
|
||||
cmd:'ip -6 route show'
|
||||
stdout:true
|
||||
|
||||
// Step 6: Test Mycelium IPv6 connectivity
|
||||
// Ping a known public Mycelium node to verify connectivity
|
||||
// Note: This requires the container to have CAP_NET_RAW capability for ping6
|
||||
// If ping6 fails with permission denied, this is expected behavior in Alpine
|
||||
!!heropods.container_exec
|
||||
name:'mycelium_container'
|
||||
cmd:'ping6 -c 3 400:8f3a:8d0e:3503:db8e:6a02:2e9:83dd'
|
||||
stdout:true
|
||||
|
||||
// Alternative: Test IPv6 connectivity using nc (netcat) if available
|
||||
// This doesn't require special capabilities
|
||||
!!heropods.container_exec
|
||||
name:'mycelium_container'
|
||||
cmd:'nc -6 -zv -w 3 400:8f3a:8d0e:3503:db8e:6a02:2e9:83dd 80 2>&1 || echo "nc test completed"'
|
||||
stdout:true
|
||||
|
||||
// Step 7: Show Mycelium-specific information
|
||||
|
||||
// Display the container's Mycelium IPv6 address
|
||||
!!heropods.container_exec
|
||||
name:'mycelium_container'
|
||||
cmd:'ip -6 addr show | grep "400:" || echo "No Mycelium IPv6 address found"'
|
||||
stdout:true
|
||||
|
||||
// Show IPv6 neighbors (if any)
|
||||
!!heropods.container_exec
|
||||
name:'mycelium_container'
|
||||
cmd:'ip -6 neigh show'
|
||||
stdout:true
|
||||
|
||||
// Step 8: Verify dual-stack networking (IPv4 + IPv6)
|
||||
// The container should have both IPv4 and IPv6 connectivity
|
||||
|
||||
// Test IPv4 connectivity
|
||||
!!heropods.container_exec
|
||||
name:'mycelium_container'
|
||||
cmd:'wget -O- http://google.com --timeout=5 2>&1 | head -n 5'
|
||||
stdout:true
|
||||
|
||||
// Step 9: Stop the container
|
||||
// This cleans up both IPv4 and IPv6 (Mycelium) networking
|
||||
!!heropods.container_stop
|
||||
name:'mycelium_container'
|
||||
|
||||
// Step 10: Delete the container
|
||||
// This removes the container and all associated resources
|
||||
!!heropods.container_delete
|
||||
name:'mycelium_container'
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
#!/usr/bin/env hero
|
||||
|
||||
// Configure the heropods instance
|
||||
!!heropods.configure
|
||||
name:'demo'
|
||||
reset:false
|
||||
use_podman:true
|
||||
|
||||
// Create a new Alpine container using podman to pull the image
|
||||
// Note: Hero binary is ALWAYS automatically included in all containers
|
||||
!!heropods.container_new
|
||||
name:'demo_container'
|
||||
image:'custom'
|
||||
custom_image_name:'alpine_3_20'
|
||||
docker_url:'docker.io/library/alpine:3.20'
|
||||
|
||||
// Start the container
|
||||
!!heropods.container_start
|
||||
name:'demo_container'
|
||||
|
||||
// Test network connectivity with wget (ping requires capabilities in Alpine)
|
||||
!!heropods.container_exec
|
||||
name:'demo_container'
|
||||
cmd:'wget -O- http://google.com --timeout=5 2>&1 | head -n 5'
|
||||
stdout:true
|
||||
|
||||
// Clean up
|
||||
!!heropods.container_stop
|
||||
name:'demo_container'
|
||||
|
||||
!!heropods.container_delete
|
||||
name:'demo_container'
|
||||
96
examples/virt/heropods/ipv4_connection.heroscript
Normal file
96
examples/virt/heropods/ipv4_connection.heroscript
Normal file
@@ -0,0 +1,96 @@
|
||||
#!/usr/bin/env hero
|
||||
|
||||
// ============================================================================
|
||||
// HeroPods Example: IPv4 Networking and Internet Connectivity
|
||||
// ============================================================================
|
||||
//
|
||||
// This script demonstrates IPv4 networking functionality:
|
||||
// - Bridge networking with automatic IP allocation
|
||||
// - NAT for outbound internet access
|
||||
// - DNS resolution
|
||||
// - HTTP connectivity testing
|
||||
//
|
||||
// The container gets an IP address from the bridge subnet (default: 10.10.0.0/24)
|
||||
// and can access the internet through NAT.
|
||||
// ============================================================================
|
||||
|
||||
// Step 1: Configure HeroPods instance with IPv4 networking
|
||||
// This creates a HeroPods instance with bridge networking enabled
|
||||
!!heropods.configure
|
||||
name:'ipv4_demo'
|
||||
reset:false
|
||||
use_podman:true
|
||||
bridge_name:'heropods0'
|
||||
subnet:'10.10.0.0/24'
|
||||
gateway_ip:'10.10.0.1'
|
||||
dns_servers:['8.8.8.8', '8.8.4.4']
|
||||
|
||||
// Step 2: Create a new Alpine Linux container
|
||||
// Alpine is lightweight and includes basic networking tools
|
||||
!!heropods.container_new
|
||||
name:'ipv4_container'
|
||||
image:'custom'
|
||||
custom_image_name:'alpine_3_20'
|
||||
docker_url:'docker.io/library/alpine:3.20'
|
||||
|
||||
// Step 3: Start the container
|
||||
// This sets up the veth pair and configures IPv4 networking
|
||||
!!heropods.container_start
|
||||
name:'ipv4_container'
|
||||
|
||||
// Step 4: Verify network configuration inside the container
|
||||
|
||||
// Show network interfaces and IP addresses
|
||||
!!heropods.container_exec
|
||||
name:'ipv4_container'
|
||||
cmd:'ip addr show'
|
||||
stdout:true
|
||||
|
||||
// Show routing table
|
||||
!!heropods.container_exec
|
||||
name:'ipv4_container'
|
||||
cmd:'ip route show'
|
||||
stdout:true
|
||||
|
||||
// Show DNS configuration
|
||||
!!heropods.container_exec
|
||||
name:'ipv4_container'
|
||||
cmd:'cat /etc/resolv.conf'
|
||||
stdout:true
|
||||
|
||||
// Step 5: Test DNS resolution
|
||||
// Verify that DNS queries work correctly
|
||||
!!heropods.container_exec
|
||||
name:'ipv4_container'
|
||||
cmd:'nslookup google.com'
|
||||
stdout:true
|
||||
|
||||
// Step 6: Test HTTP connectivity
|
||||
// Use wget to verify internet access (ping requires CAP_NET_RAW capability)
|
||||
!!heropods.container_exec
|
||||
name:'ipv4_container'
|
||||
cmd:'wget -O- http://google.com --timeout=5 2>&1 | head -n 10'
|
||||
stdout:true
|
||||
|
||||
// Test another website to confirm connectivity
|
||||
!!heropods.container_exec
|
||||
name:'ipv4_container'
|
||||
cmd:'wget -O- http://example.com --timeout=5 2>&1 | head -n 10'
|
||||
stdout:true
|
||||
|
||||
// Step 7: Test HTTPS connectivity (if wget supports it)
|
||||
!!heropods.container_exec
|
||||
name:'ipv4_container'
|
||||
cmd:'wget -O- https://www.google.com --timeout=5 --no-check-certificate 2>&1 | head -n 10'
|
||||
stdout:true
|
||||
|
||||
// Step 8: Stop the container
|
||||
// This removes the veth pair and cleans up network configuration
|
||||
!!heropods.container_stop
|
||||
name:'ipv4_container'
|
||||
|
||||
// Step 9: Delete the container
|
||||
// This removes the container and all associated resources
|
||||
!!heropods.container_delete
|
||||
name:'ipv4_container'
|
||||
|
||||
79
examples/virt/heropods/simple_container.heroscript
Normal file
79
examples/virt/heropods/simple_container.heroscript
Normal file
@@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env hero
|
||||
|
||||
// ============================================================================
|
||||
// HeroPods Example: Simple Container Lifecycle Management
|
||||
// ============================================================================
|
||||
//
|
||||
// This script demonstrates the basic container lifecycle operations:
|
||||
// - Creating a container
|
||||
// - Starting a container
|
||||
// - Executing commands inside the container
|
||||
// - Stopping a container
|
||||
// - Deleting a container
|
||||
//
|
||||
// No networking tests - just basic container operations.
|
||||
// ============================================================================
|
||||
|
||||
// Step 1: Configure HeroPods instance
|
||||
// This creates a HeroPods instance named 'simple_demo' with default settings
|
||||
!!heropods.configure
|
||||
name:'simple_demo'
|
||||
reset:false
|
||||
use_podman:true
|
||||
|
||||
// Step 2: Create a new Alpine Linux container
|
||||
// This pulls the Alpine 3.20 image from Docker Hub and prepares it for use
|
||||
!!heropods.container_new
|
||||
name:'simple_container'
|
||||
image:'custom'
|
||||
custom_image_name:'alpine_3_20'
|
||||
docker_url:'docker.io/library/alpine:3.20'
|
||||
|
||||
// Step 3: Start the container
|
||||
// This starts the container using crun (OCI runtime)
|
||||
!!heropods.container_start
|
||||
name:'simple_container'
|
||||
|
||||
// Step 4: Execute basic commands inside the container
|
||||
// These commands demonstrate that the container is running and functional
|
||||
|
||||
// Show kernel information
|
||||
!!heropods.container_exec
|
||||
name:'simple_container'
|
||||
cmd:'uname -a'
|
||||
stdout:true
|
||||
|
||||
// List root directory contents
|
||||
!!heropods.container_exec
|
||||
name:'simple_container'
|
||||
cmd:'ls -la /'
|
||||
stdout:true
|
||||
|
||||
// Show OS release information
|
||||
!!heropods.container_exec
|
||||
name:'simple_container'
|
||||
cmd:'cat /etc/os-release'
|
||||
stdout:true
|
||||
|
||||
// Show current processes
|
||||
!!heropods.container_exec
|
||||
name:'simple_container'
|
||||
cmd:'ps aux'
|
||||
stdout:true
|
||||
|
||||
// Show environment variables
|
||||
!!heropods.container_exec
|
||||
name:'simple_container'
|
||||
cmd:'env'
|
||||
stdout:true
|
||||
|
||||
// Step 5: Stop the container
|
||||
// This gracefully stops the container (SIGTERM, then SIGKILL if needed)
|
||||
!!heropods.container_stop
|
||||
name:'simple_container'
|
||||
|
||||
// Step 6: Delete the container
|
||||
// This removes the container and cleans up all associated resources
|
||||
!!heropods.container_delete
|
||||
name:'simple_container'
|
||||
|
||||
207
lib/virt/heropods/MYCELIUM.md
Normal file
207
lib/virt/heropods/MYCELIUM.md
Normal file
@@ -0,0 +1,207 @@
|
||||
# Mycelium IPv6 Overlay Network Integration for HeroPods
|
||||
|
||||
## Overview
|
||||
|
||||
HeroPods now supports Mycelium IPv6 overlay networking, providing end-to-end encrypted IPv6 connectivity for containers across the internet.
|
||||
|
||||
## What is Mycelium?
|
||||
|
||||
Mycelium is an IPv6 overlay network that provides:
|
||||
|
||||
- **End-to-end encrypted** connectivity in the `400::/7` address range
|
||||
- **Peer-to-peer routing** through public relay nodes
|
||||
- **Automatic address assignment** based on cryptographic keys
|
||||
- **NAT traversal** for containers behind firewalls
|
||||
|
||||
## Architecture
|
||||
|
||||
### Components
|
||||
|
||||
1. **mycelium.v** - Core Mycelium integration logic
|
||||
- Installation and service management
|
||||
- Container IPv6 configuration
|
||||
- veth pair creation for IPv6 routing
|
||||
|
||||
2. **heropods_model.v** - Configuration struct
|
||||
- `MyceliumConfig` struct with enable flag, peers, key path
|
||||
|
||||
3. **container.v** - Lifecycle integration
|
||||
- Mycelium setup during container start
|
||||
- Mycelium cleanup during container stop/delete
|
||||
|
||||
### How It Works
|
||||
|
||||
1. **Host Setup**:
|
||||
- Mycelium service runs on the host
|
||||
- Connects to public peer nodes for routing
|
||||
- Gets a unique IPv6 address in `400::/7` range
|
||||
|
||||
2. **Container Setup**:
|
||||
- Creates a veth pair (`vmy-HASH` ↔ `vmyh-HASH`)
|
||||
- Assigns container IPv6 from host's `/64` prefix
|
||||
- Configures routing through host's Mycelium interface
|
||||
|
||||
3. **Connectivity**:
|
||||
- Container can reach other Mycelium nodes via IPv6
|
||||
- Traffic is encrypted end-to-end
|
||||
- Works across NAT and firewalls
|
||||
|
||||
## Configuration
|
||||
|
||||
### Enable Mycelium
|
||||
|
||||
All parameters are **required** when enabling Mycelium:
|
||||
|
||||
```heroscript
|
||||
!!heropods.configure
|
||||
name:'demo'
|
||||
|
||||
!!heropods.enable_mycelium
|
||||
heropods:'demo'
|
||||
version:'v0.5.6'
|
||||
ipv6_range:'400::/7'
|
||||
key_path:'~/hero/cfg/priv_key.bin'
|
||||
peers:[
|
||||
'tcp://185.69.166.8:9651',
|
||||
'quic://[2a02:1802:5e:0:ec4:7aff:fe51:e36b]:9651',
|
||||
'tcp://65.109.18.113:9651'
|
||||
]
|
||||
```
|
||||
|
||||
### Configuration Parameters
|
||||
|
||||
All parameters are **required**:
|
||||
|
||||
- `version` (string): Mycelium version to install (e.g., 'v0.5.6')
|
||||
- `ipv6_range` (string): Mycelium IPv6 address range (e.g., '400::/7')
|
||||
- `key_path` (string): Path to Mycelium private key (e.g., '~/hero/cfg/priv_key.bin')
|
||||
- `peers` (array of strings): Array of Mycelium peer addresses
|
||||
|
||||
### Default Public Peers
|
||||
|
||||
You can use these public Mycelium peers:
|
||||
|
||||
```text
|
||||
tcp://185.69.166.8:9651
|
||||
quic://[2a02:1802:5e:0:ec4:7aff:fe51:e36b]:9651
|
||||
tcp://65.109.18.113:9651
|
||||
quic://[2a01:4f9:5a:1042::2]:9651
|
||||
tcp://5.78.122.16:9651
|
||||
quic://[2a01:4ff:1f0:8859::1]:9651
|
||||
tcp://5.223.43.251:9651
|
||||
quic://[2a01:4ff:2f0:3621::1]:9651
|
||||
tcp://142.93.217.194:9651
|
||||
quic://[2400:6180:100:d0::841:2001]:9651
|
||||
```
|
||||
|
||||
## Usage Example
|
||||
|
||||
See `examples/virt/heropods/container_mycelium.heroscript` for a complete example:
|
||||
|
||||
**Basic example:**
|
||||
|
||||
```heroscript
|
||||
// Configure HeroPods
|
||||
!!heropods.configure
|
||||
name:'mycelium_demo'
|
||||
|
||||
// Enable Mycelium with all required parameters
|
||||
!!heropods.enable_mycelium
|
||||
heropods:'mycelium_demo'
|
||||
version:'v0.5.6'
|
||||
ipv6_range:'400::/7'
|
||||
key_path:'~/hero/cfg/priv_key.bin'
|
||||
peers:[
|
||||
'tcp://185.69.166.8:9651',
|
||||
'quic://[2a02:1802:5e:0:ec4:7aff:fe51:e36b]:9651'
|
||||
]
|
||||
|
||||
// Create and start container
|
||||
!!heropods.container_new
|
||||
name:'my_container'
|
||||
image:'alpine_3_20'
|
||||
|
||||
!!heropods.container_start
|
||||
name:'my_container'
|
||||
|
||||
// Test Mycelium connectivity
|
||||
!!heropods.container_exec
|
||||
name:'my_container'
|
||||
cmd:'ip -6 addr show'
|
||||
stdout:true
|
||||
```
|
||||
|
||||
**Run the complete example:**
|
||||
|
||||
```bash
|
||||
hero run examples/virt/heropods/container_mycelium.heroscript
|
||||
```
|
||||
|
||||
## Network Details
|
||||
|
||||
### IPv6 Address Assignment
|
||||
|
||||
- Host gets address like: `400:1234:5678::1`
|
||||
- Container gets address like: `400:1234:5678::2`
|
||||
- Uses `/64` prefix from host's Mycelium address
|
||||
|
||||
### Routing
|
||||
|
||||
- Container → Host: via veth pair link-local addresses
|
||||
- Host → Mycelium network: via Mycelium TUN interface
|
||||
- End-to-end encryption handled by Mycelium
|
||||
|
||||
### Interface Names
|
||||
|
||||
- Container side: `vmy-HASH` (6-char hash of container name)
|
||||
- Host side: `vmyh-HASH`
|
||||
- Mycelium TUN: `mycelium0` (configurable)
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Check Mycelium Status
|
||||
|
||||
```bash
|
||||
mycelium inspect --key-file ~/hero/cfg/priv_key.bin --json
|
||||
```
|
||||
|
||||
### Verify Container IPv6
|
||||
|
||||
```bash
|
||||
# Inside container
|
||||
ip -6 addr show
|
||||
ip -6 route show
|
||||
```
|
||||
|
||||
### Test Connectivity
|
||||
|
||||
```bash
|
||||
# Ping a public Mycelium node
|
||||
ping6 -c 3 400:8f3a:8d0e:3503:db8e:6a02:2e9:83dd
|
||||
```
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **Mycelium service not running**: Check with `ps aux | grep mycelium`
|
||||
2. **No IPv6 connectivity**: Verify IPv6 forwarding is enabled: `sysctl net.ipv6.conf.all.forwarding`
|
||||
3. **Container can't reach Mycelium network**: Check routes with `ip -6 route show`
|
||||
|
||||
## Security
|
||||
|
||||
- All Mycelium traffic is end-to-end encrypted
|
||||
- Each node has a unique cryptographic identity
|
||||
- Private key stored at `~/hero/cfg/priv_key.bin` (configurable)
|
||||
- Container inherits host's Mycelium identity
|
||||
|
||||
## Performance
|
||||
|
||||
- Minimal overhead for local routing
|
||||
- Peer-to-peer routing for optimal paths
|
||||
- Automatic failover between peer nodes
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
- Per-container Mycelium identities
|
||||
- Custom routing policies
|
||||
- IPv6 firewall rules
|
||||
- Mycelium network isolation
|
||||
@@ -154,6 +154,27 @@ pub fn (mut self Container) start() ! {
|
||||
return error('Failed to setup network for container: ${err}')
|
||||
}
|
||||
|
||||
// Setup Mycelium IPv6 overlay network if enabled
|
||||
if self.factory.mycelium_config.enabled {
|
||||
container_pid := self.pid()!
|
||||
self.factory.mycelium_setup_container(self.name, container_pid) or {
|
||||
self.factory.logger.log(
|
||||
cat: 'container'
|
||||
log: 'Mycelium setup failed, stopping container: ${err}'
|
||||
logtype: .error
|
||||
) or {}
|
||||
// Stop container to clean up
|
||||
self.stop() or {
|
||||
self.factory.logger.log(
|
||||
cat: 'container'
|
||||
log: 'Failed to stop container during Mycelium cleanup: ${err}'
|
||||
logtype: .error
|
||||
) or {}
|
||||
}
|
||||
return error('Failed to setup Mycelium for container: ${err}')
|
||||
}
|
||||
}
|
||||
|
||||
self.factory.logger.log(
|
||||
cat: 'container'
|
||||
log: 'Container ${self.name} started'
|
||||
@@ -415,9 +436,21 @@ fn (mut self Container) setup_network() ! {
|
||||
//
|
||||
// Delegates to HeroPods.network_cleanup_container() which uses network_mutex
|
||||
// for thread-safe IP deallocation and network cleanup.
|
||||
// Also cleans up Mycelium IPv6 overlay network if enabled.
|
||||
fn (mut self Container) cleanup_network() ! {
|
||||
mut factory := self.factory
|
||||
factory.network_cleanup_container(self.name)!
|
||||
|
||||
// Cleanup Mycelium IPv6 overlay network if enabled
|
||||
if factory.mycelium_config.enabled {
|
||||
factory.mycelium_cleanup_container(self.name) or {
|
||||
factory.logger.log(
|
||||
cat: 'container'
|
||||
log: 'Warning: Failed to cleanup Mycelium for container ${self.name}: ${err}'
|
||||
logtype: .error
|
||||
) or {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if container exists in crun (regardless of its state)
|
||||
|
||||
@@ -32,19 +32,32 @@ pub mut:
|
||||
subnet string = '10.10.0.0/24'
|
||||
gateway_ip string = '10.10.0.1'
|
||||
dns_servers []string = ['8.8.8.8', '8.8.4.4']
|
||||
// Mycelium IPv6 overlay network configuration
|
||||
enable_mycelium bool // Enable Mycelium IPv6 overlay network
|
||||
mycelium_version string // Mycelium version to install (default: 'v0.5.6')
|
||||
mycelium_ipv6_range string // Mycelium IPv6 address range (default: '400::/7')
|
||||
mycelium_peers []string // Mycelium peer addresses (default: use public nodes)
|
||||
mycelium_key_path string = '~/hero/cfg/priv_key.bin' // Path to Mycelium private key
|
||||
}
|
||||
|
||||
pub fn new(args ArgsGet) !&HeroPods {
|
||||
mut obj := HeroPods{
|
||||
name: args.name
|
||||
reset: args.reset
|
||||
use_podman: args.use_podman
|
||||
network_config: NetworkConfig{
|
||||
name: args.name
|
||||
reset: args.reset
|
||||
use_podman: args.use_podman
|
||||
network_config: NetworkConfig{
|
||||
bridge_name: args.bridge_name
|
||||
subnet: args.subnet
|
||||
gateway_ip: args.gateway_ip
|
||||
dns_servers: args.dns_servers
|
||||
}
|
||||
mycelium_config: MyceliumConfig{
|
||||
enabled: args.enable_mycelium
|
||||
version: args.mycelium_version
|
||||
ipv6_range: args.mycelium_ipv6_range
|
||||
peers: args.mycelium_peers
|
||||
key_path: args.mycelium_key_path
|
||||
}
|
||||
}
|
||||
set(obj)!
|
||||
return get(name: args.name)!
|
||||
@@ -163,6 +176,47 @@ pub fn play(mut plbook PlayBook) ! {
|
||||
action.done = true
|
||||
}
|
||||
|
||||
// Process heropods.enable_mycelium actions
|
||||
for mut action in plbook.find(filter: 'heropods.enable_mycelium')! {
|
||||
mut p := action.params
|
||||
heropods_name := p.get_default('heropods', heropods_default)!
|
||||
mut hp := get(name: heropods_name)!
|
||||
|
||||
// Validate required parameters
|
||||
mycelium_version := p.get('version') or {
|
||||
return error('heropods.enable_mycelium: "version" is required (e.g., version:\'v0.5.6\')')
|
||||
}
|
||||
mycelium_ipv6_range := p.get('ipv6_range') or {
|
||||
return error('heropods.enable_mycelium: "ipv6_range" is required (e.g., ipv6_range:\'400::/7\')')
|
||||
}
|
||||
mycelium_key_path := p.get('key_path') or {
|
||||
return error('heropods.enable_mycelium: "key_path" is required (e.g., key_path:\'~/hero/cfg/priv_key.bin\')')
|
||||
}
|
||||
peers_array := p.get_list('peers') or {
|
||||
return error('heropods.enable_mycelium: "peers" is required. Provide array of peer addresses (e.g., peers:[\'tcp://185.69.166.8:9651\', \'quic://[2a02:1802:5e:0:ec4:7aff:fe51:e36b]:9651\'])')
|
||||
}
|
||||
|
||||
// Validate peers list is not empty
|
||||
if peers_array.len == 0 {
|
||||
return error('heropods.enable_mycelium: "peers" cannot be empty. Provide at least one peer address.')
|
||||
}
|
||||
|
||||
// Update Mycelium configuration
|
||||
hp.mycelium_config.enabled = true
|
||||
hp.mycelium_config.version = mycelium_version
|
||||
hp.mycelium_config.ipv6_range = mycelium_ipv6_range
|
||||
hp.mycelium_config.key_path = mycelium_key_path
|
||||
hp.mycelium_config.peers = peers_array
|
||||
|
||||
// Initialize Mycelium if not already done
|
||||
hp.mycelium_init()!
|
||||
|
||||
// Save updated configuration
|
||||
set(hp)!
|
||||
|
||||
action.done = true
|
||||
}
|
||||
|
||||
// Process heropods.container_new actions
|
||||
for mut action in plbook.find(filter: 'heropods.container_new')! {
|
||||
mut p := action.params
|
||||
|
||||
@@ -12,6 +12,18 @@ pub const version = '0.0.0'
|
||||
const singleton = false
|
||||
const default = true
|
||||
|
||||
// MyceliumConfig holds Mycelium IPv6 overlay network configuration
|
||||
struct MyceliumConfig {
|
||||
pub mut:
|
||||
enabled bool // Whether Mycelium is enabled
|
||||
version string // Mycelium version to install (e.g., 'v0.5.6')
|
||||
ipv6_range string // Mycelium IPv6 address range (e.g., '400::/7')
|
||||
peers []string // Mycelium peer addresses
|
||||
key_path string // Path to Mycelium private key
|
||||
mycelium_ip6 string // Host's Mycelium IPv6 address (cached)
|
||||
interface_name string // Mycelium TUN interface name (e.g., "mycelium0")
|
||||
}
|
||||
|
||||
// HeroPods factory for managing containers
|
||||
//
|
||||
// Thread Safety:
|
||||
@@ -21,17 +33,18 @@ const default = true
|
||||
@[heap]
|
||||
pub struct HeroPods {
|
||||
pub mut:
|
||||
tmux_session string // tmux session name
|
||||
containers map[string]&Container // name -> container mapping
|
||||
images map[string]&ContainerImage // name -> image mapping
|
||||
crun_configs map[string]&crun.CrunConfig // name -> crun config mapping
|
||||
base_dir string // base directory for all container data
|
||||
reset bool // will reset the heropods
|
||||
use_podman bool = true // will use podman for image management
|
||||
name string // name of the heropods
|
||||
network_config NetworkConfig @[skip; str: skip] // network configuration (automatically initialized, not serialized)
|
||||
network_mutex sync.Mutex @[skip; str: skip] // protects network_config for thread-safe concurrent access
|
||||
logger logger.Logger @[skip; str: skip] // logger instance for debugging (not serialized)
|
||||
tmux_session string // tmux session name
|
||||
containers map[string]&Container // name -> container mapping
|
||||
images map[string]&ContainerImage // name -> image mapping
|
||||
crun_configs map[string]&crun.CrunConfig // name -> crun config mapping
|
||||
base_dir string // base directory for all container data
|
||||
reset bool // will reset the heropods
|
||||
use_podman bool = true // will use podman for image management
|
||||
name string // name of the heropods
|
||||
network_config NetworkConfig @[skip; str: skip] // network configuration (automatically initialized, not serialized)
|
||||
network_mutex sync.Mutex @[skip; str: skip] // protects network_config for thread-safe concurrent access
|
||||
mycelium_config MyceliumConfig // mycelium IPv6 overlay network configuration
|
||||
logger logger.Logger @[skip; str: skip] // logger instance for debugging (not serialized)
|
||||
}
|
||||
|
||||
// obj_init performs lightweight validation and field normalization only
|
||||
@@ -69,6 +82,11 @@ fn obj_init(mycfg_ HeroPods) !HeroPods {
|
||||
mycfg.network_config.allocated_ips = map[string]string{}
|
||||
}
|
||||
|
||||
// Initialize Mycelium configuration defaults (only for non-required fields)
|
||||
if mycfg.mycelium_config.interface_name == '' {
|
||||
mycfg.mycelium_config.interface_name = 'mycelium0'
|
||||
}
|
||||
|
||||
return mycfg
|
||||
}
|
||||
|
||||
@@ -104,6 +122,11 @@ fn (mut self HeroPods) initialize() ! {
|
||||
// Initialize network layer
|
||||
self.network_init()!
|
||||
|
||||
// Initialize Mycelium IPv6 overlay network if enabled
|
||||
if self.mycelium_config.enabled {
|
||||
self.mycelium_init()!
|
||||
}
|
||||
|
||||
// Load existing images into cache
|
||||
self.load_existing_images()!
|
||||
|
||||
|
||||
422
lib/virt/heropods/mycelium.v
Normal file
422
lib/virt/heropods/mycelium.v
Normal file
@@ -0,0 +1,422 @@
|
||||
module heropods
|
||||
|
||||
import incubaid.herolib.osal.core as osal
|
||||
import incubaid.herolib.clients.mycelium
|
||||
import incubaid.herolib.installers.net.mycelium_installer
|
||||
import time
|
||||
import crypto.sha256
|
||||
|
||||
// Initialize Mycelium for HeroPods
|
||||
//
|
||||
// This method:
|
||||
// 1. Validates required configuration
|
||||
// 2. Installs Mycelium binary if not present
|
||||
// 3. Starts Mycelium service with configured peers
|
||||
// 4. Retrieves the host's Mycelium IPv6 address
|
||||
//
|
||||
// Thread Safety:
|
||||
// This is called during HeroPods initialization, before any concurrent operations.
|
||||
fn (mut self HeroPods) mycelium_init() ! {
|
||||
if !self.mycelium_config.enabled {
|
||||
return
|
||||
}
|
||||
|
||||
// Validate required configuration
|
||||
if self.mycelium_config.version == '' {
|
||||
return error('Mycelium configuration error: "version" is required. Use heropods.enable_mycelium to configure.')
|
||||
}
|
||||
if self.mycelium_config.ipv6_range == '' {
|
||||
return error('Mycelium configuration error: "ipv6_range" is required. Use heropods.enable_mycelium to configure.')
|
||||
}
|
||||
if self.mycelium_config.key_path == '' {
|
||||
return error('Mycelium configuration error: "key_path" is required. Use heropods.enable_mycelium to configure.')
|
||||
}
|
||||
if self.mycelium_config.peers.len == 0 {
|
||||
return error('Mycelium configuration error: "peers" is required. Use heropods.enable_mycelium to configure.')
|
||||
}
|
||||
|
||||
self.logger.log(
|
||||
cat: 'mycelium'
|
||||
log: 'START mycelium_init() - Initializing Mycelium IPv6 overlay network'
|
||||
) or {}
|
||||
|
||||
// Check if Mycelium is already installed and running
|
||||
if mycelium_installed := self.mycelium_check_installed() {
|
||||
if mycelium_installed {
|
||||
self.logger.log(
|
||||
cat: 'mycelium'
|
||||
log: 'Mycelium is already installed'
|
||||
logtype: .stdout
|
||||
) or {}
|
||||
} else {
|
||||
// Install Mycelium
|
||||
self.mycelium_install()!
|
||||
}
|
||||
}
|
||||
|
||||
// Start Mycelium service if not running
|
||||
if mycelium_running := self.mycelium_check_running() {
|
||||
if mycelium_running {
|
||||
self.logger.log(
|
||||
cat: 'mycelium'
|
||||
log: 'Mycelium service is already running'
|
||||
logtype: .stdout
|
||||
) or {}
|
||||
} else {
|
||||
self.mycelium_start_service()!
|
||||
}
|
||||
}
|
||||
|
||||
// Get and cache the host's Mycelium IPv6 address
|
||||
self.mycelium_get_host_address()!
|
||||
|
||||
self.logger.log(
|
||||
cat: 'mycelium'
|
||||
log: 'END mycelium_init() - Mycelium initialized successfully with address ${self.mycelium_config.mycelium_ip6}'
|
||||
logtype: .stdout
|
||||
) or {}
|
||||
}
|
||||
|
||||
// Check if Mycelium binary is installed
|
||||
fn (mut self HeroPods) mycelium_check_installed() !bool {
|
||||
return osal.cmd_exists('mycelium')
|
||||
}
|
||||
|
||||
// Check if Mycelium service is running
|
||||
fn (mut self HeroPods) mycelium_check_running() !bool {
|
||||
// Try to inspect Mycelium - if it succeeds, it's running
|
||||
mycelium.inspect(key_file_path: self.mycelium_config.key_path) or { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
// Install Mycelium binary
|
||||
fn (mut self HeroPods) mycelium_install() ! {
|
||||
self.logger.log(
|
||||
cat: 'mycelium'
|
||||
log: 'Installing Mycelium ${self.mycelium_config.version}...'
|
||||
logtype: .stdout
|
||||
) or {}
|
||||
|
||||
// Use the mycelium_installer to install
|
||||
mut installer := mycelium_installer.get(create: true)!
|
||||
installer.peers = self.mycelium_config.peers
|
||||
|
||||
// Install Mycelium using the instance method
|
||||
installer.install(reset: false)!
|
||||
|
||||
self.logger.log(
|
||||
cat: 'mycelium'
|
||||
log: 'Mycelium installed successfully'
|
||||
logtype: .stdout
|
||||
) or {}
|
||||
}
|
||||
|
||||
// Start Mycelium service
|
||||
fn (mut self HeroPods) mycelium_start_service() ! {
|
||||
self.logger.log(
|
||||
cat: 'mycelium'
|
||||
log: 'Starting Mycelium service...'
|
||||
logtype: .stdout
|
||||
) or {}
|
||||
|
||||
// Use the mycelium_installer to start the service
|
||||
mut installer := mycelium_installer.get()!
|
||||
installer.start()!
|
||||
|
||||
// Wait for Mycelium to be ready
|
||||
for i in 0 .. 50 {
|
||||
if self.mycelium_check_running()! {
|
||||
self.logger.log(
|
||||
cat: 'mycelium'
|
||||
log: 'Mycelium service started successfully'
|
||||
logtype: .stdout
|
||||
) or {}
|
||||
return
|
||||
}
|
||||
if i % 10 == 0 {
|
||||
self.logger.log(
|
||||
cat: 'mycelium'
|
||||
log: 'Waiting for Mycelium service to start... (${i}/50)'
|
||||
logtype: .stdout
|
||||
) or {}
|
||||
}
|
||||
time.sleep(100 * time.millisecond)
|
||||
}
|
||||
|
||||
return error('Mycelium service failed to start after 5 seconds')
|
||||
}
|
||||
|
||||
// Get the host's Mycelium IPv6 address
|
||||
fn (mut self HeroPods) mycelium_get_host_address() ! {
|
||||
self.logger.log(
|
||||
cat: 'mycelium'
|
||||
log: 'Retrieving host Mycelium IPv6 address...'
|
||||
logtype: .stdout
|
||||
) or {}
|
||||
|
||||
// Use mycelium inspect to get the address
|
||||
inspect_result := mycelium.inspect(key_file_path: self.mycelium_config.key_path)!
|
||||
|
||||
if inspect_result.address == '' {
|
||||
return error('Failed to get Mycelium IPv6 address from inspect')
|
||||
}
|
||||
|
||||
self.mycelium_config.mycelium_ip6 = inspect_result.address
|
||||
|
||||
self.logger.log(
|
||||
cat: 'mycelium'
|
||||
log: 'Host Mycelium IPv6 address: ${self.mycelium_config.mycelium_ip6}'
|
||||
logtype: .stdout
|
||||
) or {}
|
||||
}
|
||||
|
||||
// Setup Mycelium IPv6 networking for a container
|
||||
//
|
||||
// This method:
|
||||
// 1. Creates a veth pair for Mycelium connectivity
|
||||
// 2. Moves one end into the container's network namespace
|
||||
// 3. Assigns a Mycelium IPv6 address to the container
|
||||
// 4. Configures IPv6 forwarding and routing
|
||||
//
|
||||
// Thread Safety:
|
||||
// This is called from container.start() which is already serialized per container.
|
||||
// Multiple containers can be started concurrently, each with their own veth pair.
|
||||
fn (mut self HeroPods) mycelium_setup_container(container_name string, container_pid int) ! {
|
||||
if !self.mycelium_config.enabled {
|
||||
return
|
||||
}
|
||||
|
||||
self.logger.log(
|
||||
cat: 'mycelium'
|
||||
log: 'Setting up Mycelium IPv6 for container ${container_name} (PID: ${container_pid})'
|
||||
logtype: .stdout
|
||||
) or {}
|
||||
|
||||
// Create unique veth pair names using hash (same pattern as IPv4 networking)
|
||||
short_hash := sha256.hexhash(container_name)[..6]
|
||||
veth_container := 'vmy-${short_hash}'
|
||||
veth_host := 'vmyh-${short_hash}'
|
||||
|
||||
// Delete veth pair if it already exists (cleanup from previous run)
|
||||
osal.exec(cmd: 'ip link delete ${veth_container} 2>/dev/null', stdout: false) or {}
|
||||
osal.exec(cmd: 'ip link delete ${veth_host} 2>/dev/null', stdout: false) or {}
|
||||
|
||||
// Create veth pair
|
||||
self.logger.log(
|
||||
cat: 'mycelium'
|
||||
log: 'Creating veth pair: ${veth_container} <-> ${veth_host}'
|
||||
logtype: .stdout
|
||||
) or {}
|
||||
|
||||
osal.exec(
|
||||
cmd: 'ip link add ${veth_container} type veth peer name ${veth_host}'
|
||||
stdout: false
|
||||
)!
|
||||
|
||||
// Bring up host end
|
||||
osal.exec(
|
||||
cmd: 'ip link set ${veth_host} up'
|
||||
stdout: false
|
||||
)!
|
||||
|
||||
// Move container end into container's network namespace
|
||||
self.logger.log(
|
||||
cat: 'mycelium'
|
||||
log: 'Moving ${veth_container} into container namespace'
|
||||
logtype: .stdout
|
||||
) or {}
|
||||
|
||||
osal.exec(
|
||||
cmd: 'ip link set ${veth_container} netns ${container_pid}'
|
||||
stdout: false
|
||||
)!
|
||||
|
||||
// Configure container end inside the namespace
|
||||
// Bring up the interface
|
||||
osal.exec(
|
||||
cmd: 'nsenter -t ${container_pid} -n ip link set ${veth_container} up'
|
||||
stdout: false
|
||||
)!
|
||||
|
||||
// Get the Mycelium IPv6 prefix from the host
|
||||
// Extract the prefix from the full address (e.g., "400:1234:5678::/64" from "400:1234:5678::1")
|
||||
mycelium_prefix := self.mycelium_get_ipv6_prefix()!
|
||||
|
||||
// Assign IPv6 address to container (use ::1 in the subnet)
|
||||
container_ip6 := '${mycelium_prefix}::1/64'
|
||||
|
||||
self.logger.log(
|
||||
cat: 'mycelium'
|
||||
log: 'Assigning IPv6 address ${container_ip6} to container'
|
||||
logtype: .stdout
|
||||
) or {}
|
||||
|
||||
osal.exec(
|
||||
cmd: 'nsenter -t ${container_pid} -n ip addr add ${container_ip6} dev ${veth_container}'
|
||||
stdout: false
|
||||
)!
|
||||
|
||||
// Enable IPv6 forwarding on the host
|
||||
self.logger.log(
|
||||
cat: 'mycelium'
|
||||
log: 'Enabling IPv6 forwarding'
|
||||
logtype: .stdout
|
||||
) or {}
|
||||
|
||||
osal.exec(
|
||||
cmd: 'sysctl -w net.ipv6.conf.all.forwarding=1'
|
||||
stdout: false
|
||||
) or {
|
||||
self.logger.log(
|
||||
cat: 'mycelium'
|
||||
log: 'Warning: Failed to enable IPv6 forwarding: ${err}'
|
||||
logtype: .error
|
||||
) or {}
|
||||
}
|
||||
|
||||
// Get the link-local address of the host end of the veth pair
|
||||
veth_host_ll := self.mycelium_get_link_local_address(veth_host)!
|
||||
|
||||
// Add route in container for Mycelium traffic (400::/7 via link-local)
|
||||
self.logger.log(
|
||||
cat: 'mycelium'
|
||||
log: 'Adding route for ${self.mycelium_config.ipv6_range} via ${veth_host_ll}'
|
||||
logtype: .stdout
|
||||
) or {}
|
||||
|
||||
osal.exec(
|
||||
cmd: 'nsenter -t ${container_pid} -n ip route add ${self.mycelium_config.ipv6_range} via ${veth_host_ll} dev ${veth_container}'
|
||||
stdout: false
|
||||
)!
|
||||
|
||||
// Add route on host for container's IPv6 address
|
||||
self.logger.log(
|
||||
cat: 'mycelium'
|
||||
log: 'Adding host route for ${mycelium_prefix}::1/128'
|
||||
logtype: .stdout
|
||||
) or {}
|
||||
|
||||
osal.exec(
|
||||
cmd: 'ip route add ${mycelium_prefix}::1/128 dev ${veth_host}'
|
||||
stdout: false
|
||||
)!
|
||||
|
||||
self.logger.log(
|
||||
cat: 'mycelium'
|
||||
log: 'Mycelium IPv6 setup complete for container ${container_name}'
|
||||
logtype: .stdout
|
||||
) or {}
|
||||
}
|
||||
|
||||
// Get the IPv6 prefix from the host's Mycelium address
|
||||
//
|
||||
// Extracts the /64 prefix from the full IPv6 address
|
||||
// Example: "400:1234:5678::1" -> "400:1234:5678:"
|
||||
fn (mut self HeroPods) mycelium_get_ipv6_prefix() !string {
|
||||
if self.mycelium_config.mycelium_ip6 == '' {
|
||||
return error('Mycelium IPv6 address not set')
|
||||
}
|
||||
|
||||
// Split the address by ':' and take the first 3 parts for /64 prefix
|
||||
parts := self.mycelium_config.mycelium_ip6.split(':')
|
||||
if parts.len < 3 {
|
||||
return error('Invalid Mycelium IPv6 address format: ${self.mycelium_config.mycelium_ip6}')
|
||||
}
|
||||
|
||||
// Reconstruct the prefix (first 3 parts)
|
||||
prefix := '${parts[0]}:${parts[1]}:${parts[2]}'
|
||||
return prefix
|
||||
}
|
||||
|
||||
// Get the link-local IPv6 address of an interface
|
||||
//
|
||||
// Link-local addresses are used for routing within the same network segment
|
||||
// They start with fe80::
|
||||
fn (mut self HeroPods) mycelium_get_link_local_address(interface_name string) !string {
|
||||
self.logger.log(
|
||||
cat: 'mycelium'
|
||||
log: 'Getting link-local address for interface ${interface_name}'
|
||||
logtype: .stdout
|
||||
) or {}
|
||||
|
||||
// Get IPv6 addresses for the interface
|
||||
cmd := "ip -6 addr show dev ${interface_name} | grep 'inet6 fe80' | awk '{print \$2}' | cut -d'/' -f1"
|
||||
result := osal.exec(
|
||||
cmd: cmd
|
||||
stdout: false
|
||||
)!
|
||||
|
||||
link_local := result.output.trim_space()
|
||||
if link_local == '' {
|
||||
return error('Failed to get link-local address for interface ${interface_name}')
|
||||
}
|
||||
|
||||
self.logger.log(
|
||||
cat: 'mycelium'
|
||||
log: 'Link-local address for ${interface_name}: ${link_local}'
|
||||
logtype: .stdout
|
||||
) or {}
|
||||
|
||||
return link_local
|
||||
}
|
||||
|
||||
// Cleanup Mycelium networking for a container
|
||||
//
|
||||
// This method:
|
||||
// 1. Removes the veth pair
|
||||
// 2. Removes routes
|
||||
//
|
||||
// Thread Safety:
|
||||
// This is called from container.stop() and container.delete() which are serialized per container.
|
||||
fn (mut self HeroPods) mycelium_cleanup_container(container_name string) ! {
|
||||
if !self.mycelium_config.enabled {
|
||||
return
|
||||
}
|
||||
|
||||
self.logger.log(
|
||||
cat: 'mycelium'
|
||||
log: 'Cleaning up Mycelium IPv6 for container ${container_name}'
|
||||
logtype: .stdout
|
||||
) or {}
|
||||
|
||||
// Remove veth interfaces (they should be auto-removed when container stops, but cleanup anyway)
|
||||
short_hash := sha256.hexhash(container_name)[..6]
|
||||
veth_host := 'vmyh-${short_hash}'
|
||||
|
||||
osal.exec(
|
||||
cmd: 'ip link delete ${veth_host} 2>/dev/null'
|
||||
stdout: false
|
||||
) or {}
|
||||
|
||||
// Remove host route (if it exists)
|
||||
mycelium_prefix := self.mycelium_get_ipv6_prefix() or {
|
||||
self.logger.log(
|
||||
cat: 'mycelium'
|
||||
log: 'Warning: Could not get Mycelium prefix for cleanup: ${err}'
|
||||
logtype: .error
|
||||
) or {}
|
||||
return
|
||||
}
|
||||
|
||||
osal.exec(
|
||||
cmd: 'ip route del ${mycelium_prefix}::1/128 2>/dev/null'
|
||||
stdout: false
|
||||
) or {}
|
||||
|
||||
self.logger.log(
|
||||
cat: 'mycelium'
|
||||
log: 'Mycelium IPv6 cleanup complete for container ${container_name}'
|
||||
logtype: .stdout
|
||||
) or {}
|
||||
}
|
||||
|
||||
// Inspect Mycelium status and return information
|
||||
//
|
||||
// Returns the public key and IPv6 address of the Mycelium node
|
||||
pub fn (mut self HeroPods) mycelium_inspect() !mycelium.MyceliumInspectResult {
|
||||
if !self.mycelium_config.enabled {
|
||||
return error('Mycelium is not enabled')
|
||||
}
|
||||
|
||||
return mycelium.inspect(key_file_path: self.mycelium_config.key_path)!
|
||||
}
|
||||
@@ -79,44 +79,89 @@ container.start()!
|
||||
|
||||
```heroscript
|
||||
!!heropods.configure
|
||||
name:'demo'
|
||||
name:'my_heropods'
|
||||
reset:false
|
||||
use_podman:true
|
||||
|
||||
!!heropods.container_new
|
||||
name:'demo_container'
|
||||
name:'my_container'
|
||||
image:'custom'
|
||||
custom_image_name:'alpine_3_20'
|
||||
docker_url:'docker.io/library/alpine:3.20'
|
||||
|
||||
!!heropods.container_start
|
||||
name:'demo_container'
|
||||
name:'my_container'
|
||||
|
||||
!!heropods.container_exec
|
||||
name:'demo_container'
|
||||
name:'my_container'
|
||||
cmd:'echo "Hello from HeroPods!"'
|
||||
stdout:true
|
||||
|
||||
!!heropods.container_stop
|
||||
name:'demo_container'
|
||||
name:'my_container'
|
||||
|
||||
!!heropods.container_delete
|
||||
name:'demo_container'
|
||||
name:'my_container'
|
||||
```
|
||||
|
||||
### Mycelium IPv6 Overlay Network
|
||||
|
||||
HeroPods supports Mycelium for end-to-end encrypted IPv6 connectivity:
|
||||
|
||||
```heroscript
|
||||
!!heropods.configure
|
||||
name:'mycelium_demo'
|
||||
reset:false
|
||||
use_podman:true
|
||||
|
||||
!!heropods.enable_mycelium
|
||||
heropods:'mycelium_demo'
|
||||
version:'v0.5.6'
|
||||
ipv6_range:'400::/7'
|
||||
key_path:'~/hero/cfg/priv_key.bin'
|
||||
peers:'tcp://185.69.166.8:9651,quic://[2a02:1802:5e:0:ec4:7aff:fe51:e36b]:9651'
|
||||
|
||||
!!heropods.container_new
|
||||
name:'ipv6_container'
|
||||
image:'alpine_3_20'
|
||||
|
||||
!!heropods.container_start
|
||||
name:'ipv6_container'
|
||||
|
||||
// Container now has both IPv4 and IPv6 (Mycelium) connectivity
|
||||
```
|
||||
|
||||
See [MYCELIUM.md](./MYCELIUM.md) for detailed Mycelium configuration.
|
||||
|
||||
## Features
|
||||
|
||||
- **Container Lifecycle**: create, start, stop, delete, exec
|
||||
- **Bridge Networking**: Automatic IP allocation with NAT
|
||||
- **IPv4 Bridge Networking**: Automatic IP allocation with NAT
|
||||
- **IPv6 Mycelium Overlay**: End-to-end encrypted peer-to-peer networking
|
||||
- **Image Management**: Pull Docker images via Podman or use built-in images
|
||||
- **Resource Monitoring**: CPU and memory usage tracking
|
||||
- **Thread-Safe**: Concurrent container operations supported
|
||||
- **Configurable**: Custom network settings, DNS, resource limits
|
||||
|
||||
## More Examples
|
||||
## Examples
|
||||
|
||||
See `examples/virt/heropods/` for more detailed examples:
|
||||
See `examples/virt/heropods/` for complete working examples:
|
||||
|
||||
- `heropods.vsh` - Complete API demonstration
|
||||
- `demo.heroscript` - HeroScript usage
|
||||
- `runcommands.vsh` - Simple command execution
|
||||
### HeroScript Examples
|
||||
|
||||
- **simple_container.heroscript** - Basic container lifecycle management
|
||||
- **ipv4_connection.heroscript** - IPv4 networking and internet connectivity
|
||||
- **container_mycelium.heroscript** - Mycelium IPv6 overlay networking
|
||||
|
||||
### V Language Examples
|
||||
|
||||
- **heropods.vsh** - Complete API demonstration
|
||||
- **runcommands.vsh** - Simple command execution
|
||||
|
||||
Each example is fully documented and can be run independently. See [examples/virt/heropods/README.md](../../../examples/virt/heropods/README.md) for details.
|
||||
|
||||
## Documentation
|
||||
|
||||
- **[MYCELIUM.md](./MYCELIUM.md)** - Mycelium IPv6 overlay network integration guide
|
||||
- **[PRODUCTION_READINESS_REVIEW.md](./PRODUCTION_READINESS_REVIEW.md)** - Production readiness assessment
|
||||
- **[ACTIONABLE_RECOMMENDATIONS.md](./ACTIONABLE_RECOMMENDATIONS.md)** - Code quality recommendations
|
||||
|
||||
Reference in New Issue
Block a user