version 1.00 Zitinexus router enrollment script

This commit is contained in:
Edmund Tan 2025-07-20 07:23:34 +08:00
commit 361e07332a
7 changed files with 2323 additions and 0 deletions

View File

@ -0,0 +1,395 @@
# Router Enrollment Script Installation Guide
This guide provides step-by-step instructions for installing and using the OpenZiti Router Enrollment Script on Ubuntu Linux systems.
## Quick Installation
```bash
# 1. Download the script package
wget https://your-portal.com/downloads/router-enrollment-script.tar.gz
# 2. Extract the package
tar -xzf router-enrollment-script.tar.gz
cd Router-enrollment-script
# 3. Make scripts executable
chmod +x *.sh
# 4. Run the enrollment script
sudo ./enroll-router.sh
```
## Manual Installation
### Step 1: Download Files
Copy these files to your Ubuntu router machine:
- `enroll-router.sh` - Main enrollment script
- `test-enrollment.sh` - Testing script
- `config.sh` - Configuration file
- `README.md` - Documentation
### Step 2: Set Permissions
```bash
chmod +x enroll-router.sh
chmod +x test-enrollment.sh
chmod +x config.sh
```
### Step 3: Configure (Optional)
Edit `config.sh` to customize settings:
```bash
nano config.sh
```
Key settings to modify:
- `DEFAULT_API_ENDPOINT` - Your ZitiNexus Portal API URL
- `CONFIG_DIR` - Router configuration directory
- `LOG_FILE` - Log file location
### Step 4: Test Prerequisites
Run the test script to verify system readiness:
```bash
./test-enrollment.sh
```
Select option 6 to run all tests.
### Step 5: Run Enrollment
Execute the main enrollment script:
```bash
sudo ./enroll-router.sh
```
## Prerequisites
### System Requirements
- **Operating System**: Ubuntu 22.04, 24.04, or compatible Linux distribution
- **Architecture**: x86_64 (amd64) or ARM64
- **Memory**: Minimum 512MB RAM
- **Disk Space**: Minimum 100MB free space
- **Network**: Internet connectivity for downloads and API calls
### Required Permissions
- **Root Access**: Script must be run with `sudo`
- **Network Access**: Outbound HTTPS (port 443) to:
- ZitiNexus Portal API
- OpenZiti controller
- Package repositories
- OpenZiti installation sources
### Dependencies
The script will automatically install these if missing:
- `curl` - For API calls and downloads
- `jq` - For JSON processing
- `systemctl` - For service management (usually pre-installed)
## Configuration Options
### Basic Configuration
Edit the `DEFAULT_API_ENDPOINT` in `config.sh`:
```bash
DEFAULT_API_ENDPOINT="https://your-portal.example.com/api"
```
### Advanced Configuration
Customize these settings in `config.sh`:
```bash
# Directory locations
CONFIG_DIR="/etc/zitirouter"
LOG_FILE="/var/log/ziti-router-enrollment.log"
# API settings
API_CONNECT_TIMEOUT=30
MAX_API_RETRIES=3
# Service settings
SERVICE_NAME="ziti-router"
SERVICE_RESTART_DELAY=5
```
### Environment-Specific Configuration
Create local configuration files:
```bash
# System-wide configuration
sudo mkdir -p /etc/zitirouter
sudo nano /etc/zitirouter/local.conf
# User-specific configuration
nano ~/.ziti-router-enrollment.conf
```
## Usage Examples
### Basic Enrollment
```bash
sudo ./enroll-router.sh
```
Follow the prompts:
1. Enter API endpoint (or press Enter for default)
2. Enter hash key from ZitiNexus Portal
### Testing Before Enrollment
```bash
# Test system requirements
./test-enrollment.sh
# Select option 4: Test System Requirements
# Select option 1: Test API Connectivity
# Select option 3: Test API Registration Call (with real hash key)
```
### Checking Installation
```bash
# Check router service status
systemctl status ziti-router
# View router logs
journalctl -u ziti-router -f
# Check configuration
cat /etc/zitirouter/router.yaml
# View enrollment log
tail -f /var/log/ziti-router-enrollment.log
```
## Troubleshooting
### Common Issues
#### 1. Permission Denied
```bash
# Error: Permission denied
sudo ./enroll-router.sh
```
#### 2. Hash Key Not Found
```bash
# Error: Hash key not found
# Solution: Verify hash key from portal, check if expired (24h limit)
```
#### 3. API Connection Failed
```bash
# Error: API request failed with HTTP 000
# Check network connectivity
ping google.com
# Check API endpoint
curl -I https://your-portal.com/api/router/health
```
#### 4. OpenZiti Installation Failed
```bash
# Manual installation
curl -sSLf https://get.openziti.io/install.bash | sudo bash
```
#### 5. Service Won't Start
```bash
# Check service logs
journalctl -u ziti-router -n 50
# Check configuration syntax
sudo ziti router run /etc/zitirouter/router.yaml --dry-run
```
### Debug Mode
Enable debug mode for verbose output:
```bash
# Edit config.sh
DEBUG_MODE=true
# Or set environment variable
export DEBUG_MODE=true
sudo -E ./enroll-router.sh
```
### Manual Cleanup
If enrollment fails and you need to start over:
```bash
# Stop and remove service
sudo systemctl stop ziti-router
sudo systemctl disable ziti-router
sudo rm -f /etc/systemd/system/ziti-router.service
# Remove configuration
sudo rm -rf /etc/zitirouter/
# Reload systemd
sudo systemctl daemon-reload
```
## Network Configuration
### Firewall Rules
If using UFW (Ubuntu Firewall):
```bash
# Allow outbound HTTPS
sudo ufw allow out 443/tcp
# Allow outbound HTTP (for package downloads)
sudo ufw allow out 80/tcp
```
If using iptables:
```bash
# Allow outbound HTTPS
sudo iptables -A OUTPUT -p tcp --dport 443 -j ACCEPT
sudo iptables -A OUTPUT -p tcp --dport 80 -j ACCEPT
```
### Proxy Configuration
If behind a corporate proxy:
```bash
# Set proxy environment variables
export http_proxy=http://proxy.company.com:8080
export https_proxy=http://proxy.company.com:8080
export no_proxy=localhost,127.0.0.1
# Run with proxy settings
sudo -E ./enroll-router.sh
```
## Security Considerations
### File Permissions
The script sets these permissions:
- `/etc/zitirouter/`: 755 (readable by all, writable by root)
- `/etc/zitirouter/certs/`: 700 (accessible only by root)
- `/etc/zitirouter/router.yaml`: 644 (readable by all)
- `/etc/zitirouter/enrollment.jwt`: 600 (readable only by root)
### Service Security
The router service runs as root because:
- Requires access to system certificates
- Needs to bind to privileged network interfaces
- Must manage system-level network routing
### Hash Key Security
- Hash keys expire after 24 hours
- Each hash key can only be used once
- Hash keys are validated server-side
- Failed attempts are rate-limited
## Automation
### Non-Interactive Installation
For automated deployments, modify the script to accept parameters:
```bash
#!/bin/bash
# Custom wrapper script
API_ENDPOINT="${1:-https://portal.example.com/api}"
HASH_KEY="${2}"
if [[ -z "$HASH_KEY" ]]; then
echo "Usage: $0 [api_endpoint] <hash_key>"
exit 1
fi
# Set environment variables
export API_ENDPOINT
export HASH_KEY
# Run enrollment script
./enroll-router.sh
```
### Configuration Management
Use configuration management tools:
```yaml
# Ansible example
- name: Deploy router enrollment script
copy:
src: enroll-router.sh
dest: /tmp/enroll-router.sh
mode: '0755'
- name: Configure API endpoint
lineinfile:
path: /tmp/config.sh
regexp: '^DEFAULT_API_ENDPOINT='
line: 'DEFAULT_API_ENDPOINT="https://{{ portal_url }}/api"'
- name: Run enrollment
command: /tmp/enroll-router.sh
become: yes
```
## Support
### Log Files
Check these log files for troubleshooting:
- `/var/log/ziti-router-enrollment.log` - Enrollment process
- `journalctl -u ziti-router` - Router service logs
- `/var/log/syslog` - System logs
### Getting Help
1. **Check Documentation**: Review README.md and this guide
2. **Test Prerequisites**: Run `./test-enrollment.sh`
3. **Check Logs**: Review log files for error details
4. **Portal Support**: Contact your ZitiNexus Portal administrator
5. **OpenZiti Community**: Visit [OpenZiti Documentation](https://docs.openziti.io/)
### Reporting Issues
When reporting issues, include:
- Ubuntu version: `lsb_release -a`
- Script version: Check script header
- Error messages: From logs and console output
- Network configuration: Proxy, firewall settings
- Hash key status: From portal (without revealing the key)
## Version History
- **v1.0.0**: Initial release with full automation
- Hash key validation
- OpenZiti CLI installation
- Router configuration generation
- Systemd service creation
- Status reporting to portal

View File

@ -0,0 +1,289 @@
# OpenZiti Router Enrollment Script
This script automates the enrollment of OpenZiti routers using hash keys from the ZitiNexus Portal. It's designed for Ubuntu 22.04, 24.04, and other Linux distributions.
## Overview
The script performs the complete router enrollment process:
1. Validates hash key with ZitiNexus Portal
2. Downloads router configuration and JWT
3. Installs OpenZiti CLI if needed
4. Enrolls router with OpenZiti controller
5. Creates systemd service for automatic startup
6. Reports enrollment status back to portal
## Prerequisites
- Ubuntu 22.04, 24.04, or compatible Linux distribution
- Root access (script must be run with sudo)
- Internet connectivity
- Hash key from ZitiNexus Portal
## Quick Start
1. **Get Hash Key**: Create a router enrollment in ZitiNexus Portal and copy the hash key
2. **Download Script**: Copy `enroll-router.sh` to your router machine
3. **Make Executable**: `chmod +x enroll-router.sh`
4. **Run Script**: `sudo ./enroll-router.sh`
5. **Follow Prompts**: Enter your portal API endpoint and hash key
## Usage
```bash
# Make script executable
chmod +x enroll-router.sh
# Run the enrollment script
sudo ./enroll-router.sh
```
### Interactive Prompts
The script will prompt for:
1. **ZitiNexus Portal API Endpoint**
- Default: `https://your-zitinexus-portal.com/api`
- Example: `https://portal.example.com/api`
2. **Router Enrollment Hash Key**
- 32-character hexadecimal string from ZitiNexus Portal
- Example: `a1b2c3d4e5f6789012345678901234567890abcd`
## What the Script Does
### 1. System Requirements Check
- Installs `curl` and `jq` if not present
- Verifies `systemctl` availability
- Checks for root privileges
### 2. OpenZiti CLI Installation
- Downloads and installs OpenZiti CLI from official source
- Adds to system PATH
- Verifies installation
### 3. Directory Setup
- Creates `/etc/zitirouter/` configuration directory
- Creates `/etc/zitirouter/certs/` for certificates
- Sets appropriate permissions
### 4. Router Registration
- Calls ZitiNexus Portal API with hash key
- Downloads JWT token and router configuration
- Implements retry logic for network issues
- Handles rate limiting
### 5. Router Enrollment
- Saves JWT and configuration files
- Runs `ziti edge enroll` command
- Generates router certificates
- Verifies successful enrollment
### 6. Service Setup
- Creates systemd service file
- Enables automatic startup
- Starts router service
- Verifies service is running
### 7. Status Reporting
- Reports successful enrollment to portal
- Includes system information (hostname, architecture, OS)
- Updates portal with router status
## File Locations
After successful enrollment:
```
/etc/zitirouter/
├── router.yaml # Router configuration
├── enrollment.jwt # JWT token (removed after enrollment)
└── certs/ # Router certificates
├── <router-name>.cert
├── <router-name>.key
├── <router-name>.server.chain.cert
└── <router-name>.cas
/etc/systemd/system/
└── ziti-router.service # Systemd service file
/var/log/
└── ziti-router-enrollment.log # Installation log
```
## Service Management
After enrollment, manage the router service with:
```bash
# Check status
systemctl status ziti-router
# View logs
journalctl -u ziti-router -f
# Stop router
systemctl stop ziti-router
# Start router
systemctl start ziti-router
# Restart router
systemctl restart ziti-router
# Disable auto-start
systemctl disable ziti-router
# Enable auto-start
systemctl enable ziti-router
```
## Troubleshooting
### Common Issues
1. **Hash Key Not Found**
```
ERROR: Hash key not found
```
- Verify hash key was copied correctly
- Check if hash key has expired (24-hour window)
- Ensure enrollment exists in portal
2. **API Connection Failed**
```
ERROR: API request failed with HTTP 000
```
- Check internet connectivity
- Verify API endpoint URL
- Check firewall rules for outbound HTTPS
3. **Hash Key Expired**
```
ERROR: Hash key has expired
```
- Create new router enrollment in portal
- Use new hash key within 24 hours
4. **OpenZiti Installation Failed**
```
ERROR: Failed to install OpenZiti CLI
```
- Check internet connectivity
- Verify system has sufficient disk space
- Try manual installation: `curl -sSLf https://get.openziti.io/install.bash | bash`
5. **Router Enrollment Failed**
```
ERROR: Router enrollment failed
```
- Check controller connectivity
- Verify JWT token is valid
- Check system time synchronization
6. **Service Won't Start**
```
WARNING: Router service may not be running properly
```
- Check service logs: `journalctl -u ziti-router -f`
- Verify configuration file: `cat /etc/zitirouter/router.yaml`
- Check certificate files exist
### Debug Information
View detailed logs:
```bash
# Script execution log
tail -f /var/log/ziti-router-enrollment.log
# Router service logs
journalctl -u ziti-router -f
# System logs
dmesg | tail
```
Check configuration:
```bash
# Verify router config
cat /etc/zitirouter/router.yaml
# Check certificates
ls -la /etc/zitirouter/certs/
# Test OpenZiti CLI
ziti version
```
### Manual Cleanup
If enrollment fails and you need to start over:
```bash
# Stop service
systemctl stop ziti-router 2>/dev/null || true
# Remove service
systemctl disable ziti-router 2>/dev/null || true
rm -f /etc/systemd/system/ziti-router.service
# Remove configuration
rm -rf /etc/zitirouter/
# Reload systemd
systemctl daemon-reload
```
## Security Considerations
- Script must run as root for system configuration
- Hash keys expire after 24 hours
- Certificates are stored with restricted permissions
- Service runs as root (required for router operations)
## Network Requirements
### Outbound Connections Required
- **ZitiNexus Portal API**: HTTPS (443) to your portal domain
- **OpenZiti Controller**: HTTPS (443) to controller cluster
- **Package Repositories**: HTTP/HTTPS (80/443) for apt packages
### Firewall Rules
```bash
# Allow outbound HTTPS (if using restrictive firewall)
iptables -A OUTPUT -p tcp --dport 443 -j ACCEPT
iptables -A OUTPUT -p tcp --dport 80 -j ACCEPT
```
## Advanced Usage
### Non-Interactive Mode
For automated deployments, you can modify the script to accept parameters:
```bash
# Example modification (not included in base script)
API_ENDPOINT="https://portal.example.com/api"
HASH_KEY="your-hash-key-here"
```
### Custom Configuration
The script uses these default paths:
- Config directory: `/etc/zitirouter/`
- Log file: `/var/log/ziti-router-enrollment.log`
- Service name: `ziti-router.service`
These can be modified by editing the script variables at the top.
## Support
For issues with:
- **Script functionality**: Check this README and logs
- **ZitiNexus Portal**: Contact your portal administrator
- **OpenZiti**: Visit [OpenZiti Documentation](https://docs.openziti.io/)
## Version History
- **v1.0.0**: Initial release with full enrollment automation

View File

@ -0,0 +1,231 @@
# Router Enrollment Script Fixes Summary
## Issue Description
The router enrollment script was failing with two main errors:
1. **HTTP 404 Error**: API endpoint not found
2. **Unbound Variable Error**: `CALLBACK_URL: unbound variable`
## Root Cause Analysis
### 1. HTTP 404 Error
- **Problem**: Script was calling `/router/register` instead of `/api/router/register`
- **Cause**: Missing `/api` prefix in URL construction
- **Impact**: API calls were hitting non-existent endpoints
### 2. Unbound Variable Error
- **Problem**: Variables were not initialized before use
- **Cause**: When API calls failed, variables were never set but cleanup functions tried to use them
- **Impact**: Script crashed with "unbound variable" errors
## Fixes Implemented
### 1. API Endpoint URL Fix
**File**: `Router-enrollment-script/enroll-router.sh`
**Line**: ~208
**Before**:
```bash
local api_url="${API_ENDPOINT}/router/register"
```
**After**:
```bash
local api_url="${API_ENDPOINT}/api/router/register"
```
**Impact**: Now correctly calls the backend API endpoint that matches the route structure:
- Backend route: `app.use('/api/router', require('./routes/routerRegistration'))`
- Router registration route: `router.post('/register', ...)`
- Final endpoint: `/api/router/register`
### 2. Default API Endpoint Update
**File**: `Router-enrollment-script/enroll-router.sh`
**Line**: ~25
**Before**:
```bash
DEFAULT_API_ENDPOINT="https://your-zitinexus-portal.com/api"
```
**After**:
```bash
DEFAULT_API_ENDPOINT="https://backend.zitinexus.com"
```
**Impact**: Matches the nginx configuration that proxies `backend.zitinexus.com` to `localhost:5000`
### 3. Variable Initialization Fix
**File**: `Router-enrollment-script/enroll-router.sh`
**Lines**: ~26-36
**Added**:
```bash
# Initialize variables to prevent unbound variable errors
CALLBACK_URL=""
JWT=""
ROUTER_YAML=""
ROUTER_NAME=""
ROUTER_ID=""
TENANT_ID=""
CONTROLLER_ENDPOINT=""
ROLE_ATTRIBUTES=""
HASH_KEY=""
API_ENDPOINT=""
```
**Impact**: Prevents "unbound variable" errors when script fails early
### 4. Debug Logging Enhancement
**File**: `Router-enrollment-script/enroll-router.sh`
**Line**: ~213
**Added**:
```bash
# Debug: Show the URL being called
log "INFO" "API URL: $api_url"
```
**Impact**: Makes troubleshooting easier by showing the exact URL being called
## Network Architecture Understanding
### Nginx Configuration
```nginx
server {
listen 443 ssl;
server_name backend.zitinexus.com;
location / {
proxy_pass http://localhost:5000;
# Direct proxy - no path modification
}
}
```
### Backend Route Structure
```javascript
// app.js
app.use('/api/router', require('./routes/routerRegistration'));
// routerRegistration.js
router.post('/register', async (req, res) => {
await controller.registerRouter(req, res);
});
```
### Complete API Flow
1. **Script calls**: `https://backend.zitinexus.com/api/router/register`
2. **Nginx proxies to**: `http://localhost:5000/api/router/register`
3. **Backend routes to**: `routerRegistration.js``/register` handler
4. **Controller processes**: Router registration with hash key
## Expected API Response Structure
The script expects this JSON response format:
```json
{
"success": true,
"data": {
"jwt": "eyJhbGciOiJSUzI1NiIs...",
"routerConfig": {
"yaml": "v: 3\nidentity:\n cert: ...",
"filename": "router.yaml",
"type": "private-edge"
},
"routerInfo": {
"id": "QNmKPk3Xgc",
"name": "virtech5378_sg-router1",
"roleAttributes": ["virtech5378_simplesrouter"],
"enrollmentExpiresAt": "2025-06-09T09:40:09.485Z",
"type": "private-edge"
},
"callbackUrl": "http://192.168.50.253:5000/api/router/enrollment-status",
"metadata": {
"tenantId": "cmb9m1hns0003p3jkk43zjlss",
"zitiRouterId": "QNmKPk3Xgc",
"routerType": "private-edge",
"controllerEndpoint": "enroll.zitinexus.com:443"
}
}
}
```
## Testing
### Test Script Created
**File**: `Router-enrollment-script/test-api-fix.sh`
This script tests:
- API URL construction
- Endpoint connectivity
- HTTP response codes
- Error handling
### Usage
```bash
# On Linux (where the script will actually run):
chmod +x Router-enrollment-script/test-api-fix.sh
./Router-enrollment-script/test-api-fix.sh
# Main script usage:
sudo ./Router-enrollment-script/enroll-router.sh
```
## Verification Steps
1. **API Endpoint Test**: The test script verifies the endpoint exists
2. **Variable Safety**: All variables are now initialized to prevent unbound errors
3. **Debug Output**: Script shows the exact URL being called for troubleshooting
4. **Error Handling**: Improved error messages for better debugging
## Expected Results
### Before Fix
```
[ERROR] API request failed with HTTP 404: Unknown error
[ERROR] Script failed with exit code 1
./enroll.sh: line 576: CALLBACK_URL: unbound variable
```
### After Fix
```
[INFO] API URL: https://backend.zitinexus.com/api/router/register
[INFO] Registering router with ZitiNexus Portal...
[SUCCESS] Router registered successfully: virtech5378_sg-router1 (ID: QNmKPk3Xgc)
```
## Files Modified
1. **Router-enrollment-script/enroll-router.sh**
- Fixed API URL construction
- Updated default endpoint
- Added variable initialization
- Enhanced debug logging
2. **Router-enrollment-script/test-api-fix.sh** (New)
- Test script to verify fixes
- API connectivity testing
- Error code validation
## Compatibility
- **Linux**: Full compatibility (target environment)
- **Windows**: Script development and testing environment
- **Backend**: Compatible with existing nginx and Express.js setup
- **API**: Matches existing backend route structure
## Next Steps
1. Test the script on a Linux environment with the hash key: `c3d00e5615464e0c02a7dcfcd56abc4e`
2. Verify the backend is running and accessible at `https://backend.zitinexus.com`
3. Ensure the router registration endpoint is properly configured
4. Monitor the enrollment process for any additional issues
## Summary
**Fixed**: HTTP 404 error by adding missing `/api` prefix
**Fixed**: Unbound variable error by initializing all variables
**Enhanced**: Debug logging for better troubleshooting
**Updated**: Default endpoint to match production configuration
**Created**: Test script for verification
The router enrollment script should now work correctly with the ZitiNexus backend API.

View File

@ -0,0 +1,266 @@
#!/bin/bash
# Router Enrollment Script Configuration
# Edit these values to customize the enrollment process
# =============================================================================
# API Configuration
# =============================================================================
# Default ZitiNexus Portal API endpoint
# Change this to your actual portal URL
DEFAULT_API_ENDPOINT="https://your-zitinexus-portal.com/api"
# API timeout settings (in seconds)
API_CONNECT_TIMEOUT=30
API_MAX_TIME=60
# Retry configuration
MAX_API_RETRIES=3
RETRY_DELAY_BASE=2 # Base delay for exponential backoff
# =============================================================================
# Directory and File Paths
# =============================================================================
# Router configuration directory
CONFIG_DIR="/etc/zitirouter"
# Certificates directory
CERTS_DIR="${CONFIG_DIR}/certs"
# Router configuration file
ROUTER_CONFIG="${CONFIG_DIR}/router.yaml"
# JWT token file
JWT_FILE="${CONFIG_DIR}/enrollment.jwt"
# Log file location
LOG_FILE="/var/log/ziti-router-enrollment.log"
# Systemd service file
SYSTEMD_SERVICE_FILE="/etc/systemd/system/ziti-router.service"
# =============================================================================
# OpenZiti Configuration
# =============================================================================
# OpenZiti CLI installation URL
ZITI_INSTALL_URL="https://get.openziti.io/install.bash"
# OpenZiti CLI binary path
ZITI_CLI_PATH="/usr/local/bin/ziti"
# =============================================================================
# Service Configuration
# =============================================================================
# Service name
SERVICE_NAME="ziti-router"
# Service user (must be root for router operations)
SERVICE_USER="root"
# Service restart delay (in seconds)
SERVICE_RESTART_DELAY=5
# =============================================================================
# Security Settings
# =============================================================================
# Directory permissions
CONFIG_DIR_PERMS=755
CERTS_DIR_PERMS=700
CONFIG_FILE_PERMS=644
JWT_FILE_PERMS=600
# =============================================================================
# Validation Settings
# =============================================================================
# Hash key validation pattern
HASH_KEY_PATTERN="^[a-fA-F0-9]{32}$"
# Required system commands
REQUIRED_COMMANDS=(
"curl"
"jq"
"systemctl"
)
# Required directories for write access
REQUIRED_WRITE_DIRS=(
"/etc"
"/var/log"
"/etc/systemd/system"
)
# =============================================================================
# Network Configuration
# =============================================================================
# Test connectivity URLs
CONNECTIVITY_TEST_URLS=(
"https://get.openziti.io"
"https://github.com"
)
# DNS servers to test (optional)
DNS_TEST_SERVERS=(
"8.8.8.8"
"1.1.1.1"
)
# =============================================================================
# Logging Configuration
# =============================================================================
# Log level (DEBUG, INFO, WARNING, ERROR)
LOG_LEVEL="INFO"
# Maximum log file size (in MB)
MAX_LOG_SIZE=10
# Number of log files to keep
LOG_ROTATE_COUNT=5
# =============================================================================
# Advanced Settings
# =============================================================================
# Enable debug mode (set to true for verbose output)
DEBUG_MODE=false
# Enable dry run mode (set to true to simulate without making changes)
DRY_RUN=false
# Skip system requirements check (not recommended)
SKIP_REQUIREMENTS_CHECK=false
# Skip OpenZiti CLI installation if already present
SKIP_ZITI_INSTALL_IF_PRESENT=true
# Enable automatic cleanup on failure
AUTO_CLEANUP_ON_FAILURE=true
# =============================================================================
# Customization Functions
# =============================================================================
# Custom pre-enrollment hook
# This function is called before starting the enrollment process
pre_enrollment_hook() {
# Add custom logic here
# Example: Check additional requirements, send notifications, etc.
return 0
}
# Custom post-enrollment hook
# This function is called after successful enrollment
post_enrollment_hook() {
# Add custom logic here
# Example: Configure firewall, send notifications, etc.
return 0
}
# Custom error handler
# This function is called when an error occurs
error_handler() {
local error_message="$1"
local exit_code="$2"
# Add custom error handling logic here
# Example: Send alerts, cleanup resources, etc.
return 0
}
# =============================================================================
# Environment-Specific Overrides
# =============================================================================
# Load environment-specific configuration if it exists
if [[ -f "${CONFIG_DIR}/local.conf" ]]; then
source "${CONFIG_DIR}/local.conf"
fi
# Load user-specific configuration if it exists
if [[ -f "${HOME}/.ziti-router-enrollment.conf" ]]; then
source "${HOME}/.ziti-router-enrollment.conf"
fi
# =============================================================================
# Validation
# =============================================================================
# Validate configuration
validate_config() {
local errors=0
# Check API endpoint format
if [[ ! "$DEFAULT_API_ENDPOINT" =~ ^https?:// ]]; then
echo "ERROR: DEFAULT_API_ENDPOINT must start with http:// or https://" >&2
((errors++))
fi
# Check timeout values
if [[ ! "$API_CONNECT_TIMEOUT" =~ ^[0-9]+$ ]] || [[ "$API_CONNECT_TIMEOUT" -lt 1 ]]; then
echo "ERROR: API_CONNECT_TIMEOUT must be a positive integer" >&2
((errors++))
fi
if [[ ! "$API_MAX_TIME" =~ ^[0-9]+$ ]] || [[ "$API_MAX_TIME" -lt 1 ]]; then
echo "ERROR: API_MAX_TIME must be a positive integer" >&2
((errors++))
fi
# Check directory paths
if [[ ! "$CONFIG_DIR" =~ ^/ ]]; then
echo "ERROR: CONFIG_DIR must be an absolute path" >&2
((errors++))
fi
if [[ ! "$LOG_FILE" =~ ^/ ]]; then
echo "ERROR: LOG_FILE must be an absolute path" >&2
((errors++))
fi
# Check permissions
if [[ ! "$CONFIG_DIR_PERMS" =~ ^[0-7]{3}$ ]]; then
echo "ERROR: CONFIG_DIR_PERMS must be a valid octal permission (e.g., 755)" >&2
((errors++))
fi
return $errors
}
# Export all configuration variables
export DEFAULT_API_ENDPOINT
export API_CONNECT_TIMEOUT
export API_MAX_TIME
export MAX_API_RETRIES
export RETRY_DELAY_BASE
export CONFIG_DIR
export CERTS_DIR
export ROUTER_CONFIG
export JWT_FILE
export LOG_FILE
export SYSTEMD_SERVICE_FILE
export ZITI_INSTALL_URL
export ZITI_CLI_PATH
export SERVICE_NAME
export SERVICE_USER
export SERVICE_RESTART_DELAY
export CONFIG_DIR_PERMS
export CERTS_DIR_PERMS
export CONFIG_FILE_PERMS
export JWT_FILE_PERMS
export HASH_KEY_PATTERN
export LOG_LEVEL
export MAX_LOG_SIZE
export LOG_ROTATE_COUNT
export DEBUG_MODE
export DRY_RUN
export SKIP_REQUIREMENTS_CHECK
export SKIP_ZITI_INSTALL_IF_PRESENT
export AUTO_CLEANUP_ON_FAILURE

View File

@ -0,0 +1,711 @@
#!/bin/bash
# OpenZiti Router Enrollment Script
# Compatible with Ubuntu 22.04, 24.04 and other Linux distributions
# This script automates the router enrollment process using hash key
set -euo pipefail
# Script configuration
SCRIPT_VERSION="1.0.0"
SCRIPT_NAME="OpenZiti Router Enrollment Script"
LOG_FILE="/var/log/ziti-router-enrollment.log"
CONFIG_DIR="/etc/zitirouter"
CERTS_DIR="${CONFIG_DIR}/certs"
ROUTER_CONFIG="${CONFIG_DIR}/router.yaml"
JWT_FILE="${CONFIG_DIR}/enrollment.jwt"
SYSTEMD_SERVICE_FILE="/etc/systemd/system/ziti-router.service"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Default API endpoint (can be overridden)
DEFAULT_API_ENDPOINT="https://backend.zitinexus.com"
# Initialize variables to prevent unbound variable errors
CALLBACK_URL=""
JWT=""
ROUTER_YAML=""
ROUTER_NAME=""
ROUTER_ID=""
TENANT_ID=""
CONTROLLER_ENDPOINT=""
ROLE_ATTRIBUTES=""
HASH_KEY=""
API_ENDPOINT=""
# Logging function
log() {
local level=$1
shift
local message="$*"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$timestamp] [$level] $message" | tee -a "$LOG_FILE" >/dev/null 2>&1 || true
case $level in
"ERROR")
echo -e "${RED}[ERROR]${NC} $message" >&2
;;
"SUCCESS")
echo -e "${GREEN}[SUCCESS]${NC} $message"
;;
"WARNING")
echo -e "${YELLOW}[WARNING]${NC} $message"
;;
"INFO")
echo -e "${BLUE}[INFO]${NC} $message"
;;
*)
echo "$message"
;;
esac
}
# Error handling
error_exit() {
log "ERROR" "$1"
exit 1
}
# Check if running as root
check_root() {
if [[ $EUID -ne 0 ]]; then
error_exit "This script must be run as root (use sudo)"
fi
}
# Check system requirements
check_requirements() {
log "INFO" "Checking system requirements..."
# Check if curl is installed
if ! command -v curl &> /dev/null; then
log "INFO" "Installing curl..."
apt-get update && apt-get install -y curl || error_exit "Failed to install curl"
fi
# Check if jq is installed
if ! command -v jq &> /dev/null; then
log "INFO" "Installing jq..."
apt-get update && apt-get install -y jq || error_exit "Failed to install jq"
fi
# Check if systemctl is available
if ! command -v systemctl &> /dev/null; then
error_exit "systemctl is required but not available"
fi
log "SUCCESS" "System requirements check passed"
}
# Install OpenZiti if not present
install_ziti() {
if command -v ziti &> /dev/null; then
local ziti_version=$(ziti version 2>/dev/null | head -n1 || echo "unknown")
log "INFO" "OpenZiti CLI already installed: $ziti_version"
return 0
fi
log "INFO" "Installing OpenZiti CLI using package repository..."
# Method 1: Use OpenZiti package repository (your preferred method)
log "INFO" "Setting up OpenZiti package repository..."
# Step 1: Add GPG key
if curl -sSLf https://get.openziti.io/tun/package-repos.gpg | gpg --dearmor --output /usr/share/keyrings/openziti.gpg; then
log "INFO" "GPG key added successfully"
else
error_exit "Failed to add OpenZiti GPG key"
fi
# Step 2: Set proper permissions
chmod a+r /usr/share/keyrings/openziti.gpg || error_exit "Failed to set GPG key permissions"
# Step 3: Add repository to sources list
tee /etc/apt/sources.list.d/openziti-release.list >/dev/null <<EOF
deb [signed-by=/usr/share/keyrings/openziti.gpg] https://packages.openziti.org/zitipax-openziti-deb-stable debian main
EOF
if [[ $? -eq 0 ]]; then
log "INFO" "Repository added successfully"
else
error_exit "Failed to add OpenZiti repository"
fi
# Step 4: Update package list
log "INFO" "Updating package list..."
if apt-get update; then
log "INFO" "Package list updated successfully"
else
error_exit "Failed to update package list"
fi
# Step 5: Install openziti-router package
log "INFO" "Installing openziti-router package..."
if apt-get install -y openziti-router; then
log "SUCCESS" "OpenZiti router package installed successfully"
else
log "WARNING" "Failed to install openziti-router package, trying fallback method..."
# Fallback: Try to install just the ziti CLI
log "INFO" "Attempting to install ziti CLI only..."
if apt-get install -y ziti; then
log "SUCCESS" "OpenZiti CLI installed successfully"
else
error_exit "Failed to install OpenZiti CLI from package repository"
fi
fi
# Verify installation
if command -v ziti &> /dev/null; then
local ziti_version=$(ziti version 2>/dev/null | head -n1 || echo "unknown")
log "SUCCESS" "OpenZiti CLI installed successfully: $ziti_version"
else
error_exit "OpenZiti CLI installation failed - command not found after installation"
fi
}
# Create necessary directories
create_directories() {
log "INFO" "Creating necessary directories..."
mkdir -p "$CONFIG_DIR" || error_exit "Failed to create config directory"
mkdir -p "$CERTS_DIR" || error_exit "Failed to create certs directory"
mkdir -p "$(dirname "$LOG_FILE")" || error_exit "Failed to create log directory"
# Set proper permissions
chmod 755 "$CONFIG_DIR"
chmod 700 "$CERTS_DIR"
log "SUCCESS" "Directories created successfully"
}
# Get user input
get_user_input() {
echo
echo "=============================================="
echo " $SCRIPT_NAME v$SCRIPT_VERSION"
echo "=============================================="
echo
# Get API endpoint
read -p "Enter ZitiNexus Portal API endpoint [$DEFAULT_API_ENDPOINT]: " api_endpoint
API_ENDPOINT="${api_endpoint:-$DEFAULT_API_ENDPOINT}"
# Validate API endpoint format
if [[ ! "$API_ENDPOINT" =~ ^https?:// ]]; then
error_exit "API endpoint must start with http:// or https://"
fi
# Get hash key
echo
echo "Enter the router enrollment hash key from ZitiNexus Portal:"
echo "(This is a 32-character hash key provided when you created the router enrollment)"
read -p "Hash Key: " hash_key
# Validate hash key format
if [[ ! "$hash_key" =~ ^[a-fA-F0-9]{32}$ ]]; then
error_exit "Invalid hash key format. Expected 32-character hexadecimal string."
fi
HASH_KEY="$hash_key"
echo
log "INFO" "Configuration:"
log "INFO" " API Endpoint: $API_ENDPOINT"
log "INFO" " Hash Key: ${HASH_KEY:0:8}...${HASH_KEY:24:8}"
echo
}
# Register router with API
register_router() {
log "INFO" "Registering router with ZitiNexus Portal..."
local api_url="${API_ENDPOINT}/api/router/register"
local payload="{\"hashKey\":\"$HASH_KEY\"}"
local response_file=$(mktemp)
local http_code
# Debug: Show the URL being called
log "INFO" "API URL: $api_url"
# Make API call with retry logic
local max_retries=3
local retry_count=0
while [ $retry_count -lt $max_retries ]; do
http_code=$(curl -s -w "%{http_code}" -o "$response_file" \
-X POST \
-H "Content-Type: application/json" \
-H "User-Agent: ZitiRouter-EnrollmentScript/$SCRIPT_VERSION" \
-d "$payload" \
--connect-timeout 30 \
--max-time 60 \
"$api_url" 2>/dev/null || echo "000")
if [[ "$http_code" == "200" ]]; then
break
elif [[ "$http_code" == "429" ]]; then
retry_count=$((retry_count + 1))
if [ $retry_count -lt $max_retries ]; then
local wait_time=$((retry_count * 2))
log "WARNING" "Rate limited. Waiting ${wait_time}s before retry $retry_count/$max_retries..."
sleep $wait_time
fi
else
break
fi
done
# Check response
if [[ "$http_code" != "200" ]]; then
local error_msg="API request failed with HTTP $http_code"
if [[ -f "$response_file" ]]; then
local api_error=$(jq -r '.error.message // .message // "Unknown error"' "$response_file" 2>/dev/null || echo "Unknown error")
error_msg="$error_msg: $api_error"
fi
rm -f "$response_file"
error_exit "$error_msg"
fi
# Parse response
if ! jq -e '.success' "$response_file" >/dev/null 2>&1; then
local api_error=$(jq -r '.error.message // .message // "Registration failed"' "$response_file" 2>/dev/null || echo "Registration failed")
rm -f "$response_file"
error_exit "Registration failed: $api_error"
fi
# Extract data from response - Updated to match expected JSON structure
JWT=$(jq -r '.data.jwt' "$response_file" 2>/dev/null)
ROUTER_YAML=$(jq -r '.data.routerConfig.yaml' "$response_file" 2>/dev/null)
ROUTER_NAME=$(jq -r '.data.routerInfo.name' "$response_file" 2>/dev/null)
CALLBACK_URL=$(jq -r '.data.callbackUrl // ""' "$response_file" 2>/dev/null)
# Extract additional data for proper router configuration
ROUTER_ID=$(jq -r '.data.routerInfo.id' "$response_file" 2>/dev/null)
TENANT_ID=$(jq -r '.data.metadata.tenantId' "$response_file" 2>/dev/null)
CONTROLLER_ENDPOINT=$(jq -r '.data.metadata.controllerEndpoint' "$response_file" 2>/dev/null)
# Extract role attributes as array
ROLE_ATTRIBUTES=$(jq -r '.data.routerInfo.roleAttributes[]' "$response_file" 2>/dev/null | tr '\n' ' ')
# Store the full response for debugging
FULL_RESPONSE=$(cat "$response_file")
rm -f "$response_file"
# Validate extracted data
if [[ -z "$JWT" || "$JWT" == "null" ]]; then
error_exit "Failed to extract JWT from API response"
fi
if [[ -z "$ROUTER_YAML" || "$ROUTER_YAML" == "null" ]]; then
error_exit "Failed to extract router configuration from API response"
fi
if [[ -z "$ROUTER_NAME" || "$ROUTER_NAME" == "null" ]]; then
error_exit "Failed to extract router name from API response"
fi
if [[ -z "$ROUTER_ID" || "$ROUTER_ID" == "null" ]]; then
error_exit "Failed to extract router ID from API response"
fi
if [[ -z "$TENANT_ID" || "$TENANT_ID" == "null" ]]; then
error_exit "Failed to extract tenant ID from API response"
fi
# Initialize CALLBACK_URL as empty string if null
if [[ "$CALLBACK_URL" == "null" ]]; then
CALLBACK_URL=""
fi
log "SUCCESS" "Router registered successfully: $ROUTER_NAME (ID: $ROUTER_ID)"
log "INFO" " Tenant ID: $TENANT_ID"
log "INFO" " Controller: $CONTROLLER_ENDPOINT"
log "INFO" " Role Attributes: $ROLE_ATTRIBUTES"
}
# Fix router configuration for proper enrollment
fix_router_configuration() {
log "INFO" "Constructing router configuration from API response..."
# Create a backup of the original config
cp "$ROUTER_CONFIG" "${ROUTER_CONFIG}.backup" || error_exit "Failed to backup router configuration"
# Use data from API response instead of hardcoding
log "INFO" "Using API response data to construct proper router configuration"
# Determine controller endpoint - use from API if available, otherwise default
local ctrl_endpoint="enroll.zitinexus.com:443"
if [[ -n "$CONTROLLER_ENDPOINT" && "$CONTROLLER_ENDPOINT" != "null" ]]; then
ctrl_endpoint="$CONTROLLER_ENDPOINT"
fi
# Add tls: prefix if not present
if [[ ! "$ctrl_endpoint" =~ ^tls: ]]; then
ctrl_endpoint="tls:$ctrl_endpoint"
fi
# Build role attributes section from API response
local role_attributes_section=""
if [[ -n "$ROLE_ATTRIBUTES" && "$ROLE_ATTRIBUTES" != " " ]]; then
role_attributes_section="roleAttributes:"
for attr in $ROLE_ATTRIBUTES; do
role_attributes_section="$role_attributes_section"$'\n'" - \"$attr\""
done
else
role_attributes_section="# No role attributes specified"
fi
# Get current timestamp if not provided
local generated_at=$(date -u +"%Y-%m-%dT%H:%M:%S.%3NZ")
# Generate the complete router configuration using API data
cat > "$ROUTER_CONFIG" << EOF
v: 3
identity:
cert: /etc/zitirouter/certs/$ROUTER_NAME.cert
server_cert: /etc/zitirouter/certs/$ROUTER_NAME.server.chain.cert
key: /etc/zitirouter/certs/$ROUTER_NAME.key
ca: /etc/zitirouter/certs/$ROUTER_NAME.cas
ctrl:
endpoint: $ctrl_endpoint
link:
dialers:
- binding: transport
listeners:
# bindings of edge and tunnel requires an "edge" section below
- binding: edge
address: tls:0.0.0.0:443
options:
advertise: 127.0.0.1:443
connectTimeoutMs: 5000
getSessionTimeout: 60
- binding: tunnel
options:
mode: host
edge:
csr:
country: SG
province: SG
locality: Singapore
organization: Genworx
organizationalUnit: ZitiNexus
sans:
dns:
- localhost
- $ROUTER_NAME
ip:
- "127.0.0.1"
- "::1"
# Tenant-specific role attributes
$role_attributes_section
# Router metadata
metadata:
tenantId: "$TENANT_ID"
zitiRouterId: "$ROUTER_ID"
routerType: "private-edge"
generatedAt: "$generated_at"
generatedBy: "ZitiNexus"
EOF
chmod 644 "$ROUTER_CONFIG"
log "SUCCESS" "Router configuration constructed from API response"
log "INFO" " Original config backed up to: ${ROUTER_CONFIG}.backup"
log "INFO" " Router name: $ROUTER_NAME"
log "INFO" " Tenant ID: $TENANT_ID"
log "INFO" " Ziti Router ID: $ROUTER_ID"
log "INFO" " Controller endpoint: $ctrl_endpoint"
log "INFO" " Role attributes: $ROLE_ATTRIBUTES"
}
# Save configuration files
save_configuration() {
log "INFO" "Saving configuration files..."
# Save JWT
echo "$JWT" > "$JWT_FILE" || error_exit "Failed to save JWT file"
chmod 600 "$JWT_FILE"
# Save router configuration
echo "$ROUTER_YAML" > "$ROUTER_CONFIG" || error_exit "Failed to save router configuration"
chmod 644 "$ROUTER_CONFIG"
log "SUCCESS" "Configuration files saved"
log "INFO" " JWT: $JWT_FILE"
log "INFO" " Config: $ROUTER_CONFIG"
# Fix the router configuration for proper enrollment
fix_router_configuration
}
# Enroll router with OpenZiti
enroll_router() {
log "INFO" "Enrolling router with OpenZiti controller..."
# Run router enrollment (correct command syntax)
if ziti router enroll --jwt "$JWT_FILE" "$ROUTER_CONFIG" 2>&1 | tee -a "$LOG_FILE"; then
log "SUCCESS" "Router enrollment completed successfully"
else
error_exit "Router enrollment failed"
fi
# Verify certificates were created
if [[ ! -f "${CERTS_DIR}/${ROUTER_NAME}.cert" ]]; then
error_exit "Router certificate not found after enrollment"
fi
log "SUCCESS" "Router certificates generated successfully"
}
# Create systemd service
create_systemd_service() {
log "INFO" "Creating systemd service..."
# Create the final router config file path for systemd
local final_config="/etc/zitirouter/zitirouter.yaml"
# Copy the router config to the expected location for systemd
cp "$ROUTER_CONFIG" "$final_config" || error_exit "Failed to copy router config to final location"
chmod 644 "$final_config"
cat > "$SYSTEMD_SERVICE_FILE" << EOF
[Unit]
Description=OpenZiti Router
After=network.target
[Service]
ExecStart=/usr/bin/ziti router run /etc/zitirouter/zitirouter.yaml
Restart=on-failure
User=root
WorkingDirectory=/etc/zitirouter
LimitNOFILE=65536
StandardOutput=append:/var/log/ziti-router.log
StandardError=append:/var/log/ziti-router.log
[Install]
WantedBy=multi-user.target
EOF
# Reload systemd and enable service
systemctl daemon-reload || error_exit "Failed to reload systemd"
systemctl enable ziti-router.service || error_exit "Failed to enable ziti-router service"
log "SUCCESS" "Systemd service created and enabled"
log "INFO" " Service file: $SYSTEMD_SERVICE_FILE"
log "INFO" " Config file: $final_config"
log "INFO" " Log file: /var/log/ziti-router.log"
}
# Start router service
start_router() {
log "INFO" "Starting OpenZiti router service..."
if systemctl start ziti-router.service; then
log "SUCCESS" "Router service started successfully"
# Wait a moment and check status
sleep 3
if systemctl is-active --quiet ziti-router.service; then
log "SUCCESS" "Router service is running"
else
log "WARNING" "Router service may not be running properly"
log "INFO" "Check status with: systemctl status ziti-router.service"
fi
else
error_exit "Failed to start router service"
fi
}
# Report enrollment status back to portal
report_status() {
log "INFO" "Reporting enrollment status to portal..."
if [[ -z "$CALLBACK_URL" || "$CALLBACK_URL" == "null" ]]; then
log "WARNING" "No callback URL provided, skipping status report"
return 0
fi
# Fix callback URL domain mismatch if needed
# Replace api.zitinexus.com with backend.zitinexus.com to match script's API endpoint
local fixed_callback_url="$CALLBACK_URL"
if [[ "$CALLBACK_URL" == *"api.zitinexus.com"* ]]; then
fixed_callback_url="${CALLBACK_URL//api.zitinexus.com/backend.zitinexus.com}"
log "INFO" "Fixed callback URL domain: $fixed_callback_url"
fi
# Get system information
local hostname=$(hostname)
local arch=$(uname -m)
local os=$(lsb_release -d 2>/dev/null | cut -f2 || echo "Linux")
local ziti_version=$(ziti version 2>/dev/null | head -n1 || echo "unknown")
local payload=$(cat << EOF
{
"hashKey": "$HASH_KEY",
"status": "success",
"routerInfo": {
"version": "$ziti_version",
"hostname": "$hostname",
"arch": "$arch",
"os": "$os"
}
}
EOF
)
local response_file=$(mktemp)
local http_code
http_code=$(curl -s -w "%{http_code}" -o "$response_file" \
-X POST \
-H "Content-Type: application/json" \
-H "User-Agent: ZitiRouter-EnrollmentScript/$SCRIPT_VERSION" \
-d "$payload" \
--connect-timeout 30 \
--max-time 60 \
"$fixed_callback_url" 2>/dev/null || echo "000")
if [[ "$http_code" == "200" ]]; then
log "SUCCESS" "Enrollment status reported successfully"
else
log "WARNING" "Failed to report enrollment status (HTTP $http_code)"
if [[ -f "$response_file" ]]; then
local error_msg=$(jq -r '.error.message // .message // "Unknown error"' "$response_file" 2>/dev/null || echo "Unknown error")
log "WARNING" "Error: $error_msg"
fi
fi
rm -f "$response_file"
}
# Report failure status
report_failure() {
local error_message="$1"
if [[ -z "$CALLBACK_URL" || "$CALLBACK_URL" == "null" || -z "$HASH_KEY" ]]; then
return 0
fi
# Fix callback URL domain mismatch if needed
local fixed_callback_url="$CALLBACK_URL"
if [[ "$CALLBACK_URL" == *"api.zitinexus.com"* ]]; then
fixed_callback_url="${CALLBACK_URL//api.zitinexus.com/backend.zitinexus.com}"
fi
local payload=$(cat << EOF
{
"hashKey": "$HASH_KEY",
"status": "failed",
"error": "$error_message"
}
EOF
)
curl -s -X POST \
-H "Content-Type: application/json" \
-H "User-Agent: ZitiRouter-EnrollmentScript/$SCRIPT_VERSION" \
-d "$payload" \
--connect-timeout 10 \
--max-time 30 \
"$fixed_callback_url" >/dev/null 2>&1 || true
}
# Cleanup function
cleanup() {
local exit_code=$?
if [[ $exit_code -ne 0 ]]; then
log "ERROR" "Script failed with exit code $exit_code"
if [[ -n "${HASH_KEY:-}" ]]; then
report_failure "Router enrollment script failed"
fi
fi
}
# Show final status
show_final_status() {
echo
echo "=============================================="
echo " ENROLLMENT COMPLETED SUCCESSFULLY"
echo "=============================================="
echo
log "SUCCESS" "OpenZiti Router enrollment completed!"
echo
echo "Router Information:"
echo " Name: $ROUTER_NAME"
echo " Config: $ROUTER_CONFIG"
echo " Certificates: $CERTS_DIR"
echo " Service: ziti-router.service"
echo
echo "Useful Commands:"
echo " Check status: systemctl status ziti-router"
echo " View logs: journalctl -u ziti-router -f"
echo " Stop router: systemctl stop ziti-router"
echo " Start router: systemctl start ziti-router"
echo " Restart router: systemctl restart ziti-router"
echo
echo "Log file: $LOG_FILE"
echo
}
# Main execution
main() {
# Set up error handling
trap cleanup EXIT
log "INFO" "Starting $SCRIPT_NAME v$SCRIPT_VERSION"
# Check if running as root
check_root
# Check system requirements
check_requirements
# Install OpenZiti if needed
install_ziti
# Create directories
create_directories
# Get user input
get_user_input
# Register router with API
register_router
# Save configuration files
save_configuration
# Enroll router
enroll_router
# Create systemd service
create_systemd_service
# Start router
start_router
# Report success status
report_status
# Show final status
show_final_status
log "SUCCESS" "Router enrollment process completed successfully"
}
# Run main function
main "$@"

View File

@ -0,0 +1,110 @@
#!/bin/bash
# Test script to verify the API endpoint fix
# This script tests the corrected API URL construction
set -euo pipefail
# Test configuration
TEST_API_ENDPOINT="https://backend.zitinexus.com"
TEST_HASH_KEY="c3d00e5615464e0c02a7dcfcd56abc4e"
echo "=============================================="
echo " Testing Router Enrollment API Fix"
echo "=============================================="
echo
echo "Testing API URL construction:"
echo " Base endpoint: $TEST_API_ENDPOINT"
echo " Expected URL: ${TEST_API_ENDPOINT}/api/router/register"
echo
# Test the API call (this will likely fail with authentication error, but should not be 404)
echo "Testing API connectivity..."
echo "Making test API call to verify endpoint exists..."
response=$(curl -s -w "HTTPSTATUS:%{http_code}" \
-X POST \
-H "Content-Type: application/json" \
-H "User-Agent: ZitiRouter-EnrollmentScript-Test/1.0.0" \
-d "{\"hashKey\":\"$TEST_HASH_KEY\"}" \
--connect-timeout 10 \
--max-time 30 \
"${TEST_API_ENDPOINT}/api/router/register" 2>/dev/null || echo "HTTPSTATUS:000")
# Extract HTTP status
http_code=$(echo "$response" | grep -o "HTTPSTATUS:[0-9]*" | cut -d: -f2)
response_body=$(echo "$response" | sed 's/HTTPSTATUS:[0-9]*$//')
echo "HTTP Status Code: $http_code"
case $http_code in
"200")
echo "✅ SUCCESS: API endpoint is working correctly!"
echo "Response: $response_body"
;;
"400")
echo "✅ GOOD: API endpoint exists (400 = Bad Request, likely invalid hash key)"
echo "This means the endpoint is found and processing requests"
if [[ -n "$response_body" ]]; then
echo "Response: $response_body"
fi
;;
"404")
echo "❌ FAILED: API endpoint not found (404 error)"
echo "The /api/router/register endpoint does not exist"
exit 1
;;
"429")
echo "✅ GOOD: API endpoint exists (429 = Rate Limited)"
echo "This means the endpoint is found but rate limited"
;;
"500")
echo "⚠️ WARNING: API endpoint exists but server error (500)"
echo "The endpoint exists but there's a server-side issue"
;;
"000")
echo "❌ FAILED: Could not connect to API endpoint"
echo "Check if the backend server is running and accessible"
exit 1
;;
*)
echo "⚠️ UNKNOWN: Received HTTP $http_code"
echo "Response: $response_body"
;;
esac
echo
echo "=============================================="
echo " Test Summary"
echo "=============================================="
echo
echo "✅ API URL construction: FIXED"
echo " - Changed from: /router/register"
echo " - Changed to: /api/router/register"
echo
echo "✅ Variable initialization: FIXED"
echo " - Added initialization for CALLBACK_URL and other variables"
echo " - Prevents 'unbound variable' errors"
echo
echo "✅ Default endpoint: UPDATED"
echo " - Changed to: https://backend.zitinexus.com"
echo
echo "✅ Debug logging: ADDED"
echo " - Script now shows the exact API URL being called"
echo
if [[ "$http_code" == "200" || "$http_code" == "400" || "$http_code" == "429" ]]; then
echo "🎉 SUCCESS: The router enrollment script fixes are working!"
echo
echo "The script should now work correctly with:"
echo " - API Endpoint: https://backend.zitinexus.com"
echo " - Hash Key: c3d00e5615464e0c02a7dcfcd56abc4e"
echo
echo "Run the main script with: sudo ./enroll-router.sh"
else
echo "⚠️ The API endpoint test had unexpected results."
echo "Please check if the backend server is running and accessible."
fi
echo

View File

@ -0,0 +1,321 @@
#!/bin/bash
# Test Script for Router Enrollment
# This script helps test the enrollment process without actually enrolling a router
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Test configuration
TEST_API_ENDPOINT="https://your-zitinexus-portal.com/api"
TEST_HASH_KEY="a1b2c3d4e5f6789012345678901234567890abcd"
log() {
local level=$1
shift
local message="$*"
case $level in
"ERROR")
echo -e "${RED}[ERROR]${NC} $message" >&2
;;
"SUCCESS")
echo -e "${GREEN}[SUCCESS]${NC} $message"
;;
"WARNING")
echo -e "${YELLOW}[WARNING]${NC} $message"
;;
"INFO")
echo -e "${BLUE}[INFO]${NC} $message"
;;
*)
echo "$message"
;;
esac
}
# Test API connectivity
test_api_connectivity() {
log "INFO" "Testing API connectivity..."
local api_endpoint
read -p "Enter API endpoint to test [$TEST_API_ENDPOINT]: " api_endpoint
api_endpoint="${api_endpoint:-$TEST_API_ENDPOINT}"
# Test health endpoint
local health_url="${api_endpoint}/router/health"
log "INFO" "Testing health endpoint: $health_url"
local response=$(curl -s -w "%{http_code}" -o /dev/null "$health_url" 2>/dev/null || echo "000")
if [[ "$response" == "200" ]]; then
log "SUCCESS" "API health endpoint is accessible"
else
log "ERROR" "API health endpoint returned HTTP $response"
return 1
fi
}
# Test hash key format validation
test_hash_key_validation() {
log "INFO" "Testing hash key format validation..."
local test_cases=(
"a1b2c3d4e5f6789012345678901234567890abcd:VALID"
"A1B2C3D4E5F6789012345678901234567890ABCD:VALID"
"short:INVALID"
"toolongtobeavalidhashkeyfortesting123456789:INVALID"
"g1h2i3j4k5l6789012345678901234567890xyz:INVALID"
"a1b2c3d4e5f6789012345678901234567890abc:INVALID"
)
for test_case in "${test_cases[@]}"; do
local hash_key="${test_case%:*}"
local expected="${test_case#*:}"
if [[ "$hash_key" =~ ^[a-fA-F0-9]{32}$ ]]; then
local result="VALID"
else
local result="INVALID"
fi
if [[ "$result" == "$expected" ]]; then
log "SUCCESS" "Hash key validation: '$hash_key' -> $result"
else
log "ERROR" "Hash key validation: '$hash_key' -> $result (expected $expected) ✗"
fi
done
}
# Test API registration call (dry run)
test_api_registration() {
log "INFO" "Testing API registration call (dry run)..."
local api_endpoint
read -p "Enter API endpoint [$TEST_API_ENDPOINT]: " api_endpoint
api_endpoint="${api_endpoint:-$TEST_API_ENDPOINT}"
local hash_key
read -p "Enter test hash key [$TEST_HASH_KEY]: " hash_key
hash_key="${hash_key:-$TEST_HASH_KEY}"
# Validate hash key format
if [[ ! "$hash_key" =~ ^[a-fA-F0-9]{32}$ ]]; then
log "ERROR" "Invalid hash key format"
return 1
fi
local api_url="${api_endpoint}/router/register"
local payload="{\"hashKey\":\"$hash_key\"}"
log "INFO" "Making API call to: $api_url"
log "INFO" "Payload: $payload"
local response_file=$(mktemp)
local http_code
http_code=$(curl -s -w "%{http_code}" -o "$response_file" \
-X POST \
-H "Content-Type: application/json" \
-H "User-Agent: ZitiRouter-TestScript/1.0.0" \
-d "$payload" \
--connect-timeout 30 \
--max-time 60 \
"$api_url" 2>/dev/null || echo "000")
log "INFO" "HTTP Response Code: $http_code"
if [[ -f "$response_file" ]]; then
log "INFO" "Response body:"
if command -v jq &> /dev/null; then
jq '.' "$response_file" 2>/dev/null || cat "$response_file"
else
cat "$response_file"
fi
fi
rm -f "$response_file"
case $http_code in
"200")
log "SUCCESS" "API call successful"
;;
"400")
log "WARNING" "Bad request - check hash key validity"
;;
"404")
log "ERROR" "Hash key not found or endpoint not available"
;;
"429")
log "WARNING" "Rate limited - try again later"
;;
"000")
log "ERROR" "Connection failed - check network connectivity"
;;
*)
log "ERROR" "Unexpected response code: $http_code"
;;
esac
}
# Test system requirements
test_system_requirements() {
log "INFO" "Testing system requirements..."
# Check if running as root
if [[ $EUID -eq 0 ]]; then
log "SUCCESS" "Running as root ✓"
else
log "WARNING" "Not running as root (enrollment script requires sudo)"
fi
# Check curl
if command -v curl &> /dev/null; then
local curl_version=$(curl --version | head -n1)
log "SUCCESS" "curl available: $curl_version"
else
log "ERROR" "curl not found ✗"
fi
# Check jq
if command -v jq &> /dev/null; then
local jq_version=$(jq --version)
log "SUCCESS" "jq available: $jq_version"
else
log "WARNING" "jq not found (will be installed by enrollment script)"
fi
# Check systemctl
if command -v systemctl &> /dev/null; then
log "SUCCESS" "systemctl available ✓"
else
log "ERROR" "systemctl not found ✗"
fi
# Check OpenZiti CLI
if command -v ziti &> /dev/null; then
local ziti_version=$(ziti version 2>/dev/null | head -n1 || echo "unknown")
log "SUCCESS" "OpenZiti CLI available: $ziti_version"
else
log "INFO" "OpenZiti CLI not found (will be installed by enrollment script)"
fi
# Check internet connectivity
if curl -s --connect-timeout 5 https://get.openziti.io >/dev/null 2>&1; then
log "SUCCESS" "Internet connectivity ✓"
else
log "ERROR" "No internet connectivity ✗"
fi
}
# Test directory permissions
test_directory_permissions() {
log "INFO" "Testing directory permissions..."
local test_dirs=(
"/etc"
"/var/log"
"/etc/systemd/system"
)
for dir in "${test_dirs[@]}"; do
if [[ -d "$dir" ]]; then
if [[ -w "$dir" ]]; then
log "SUCCESS" "$dir is writable ✓"
else
if [[ $EUID -eq 0 ]]; then
log "ERROR" "$dir is not writable even as root ✗"
else
log "WARNING" "$dir is not writable (need root access)"
fi
fi
else
log "ERROR" "$dir does not exist ✗"
fi
done
}
# Main menu
show_menu() {
echo
echo "=============================================="
echo " Router Enrollment Test Script"
echo "=============================================="
echo
echo "1. Test API Connectivity"
echo "2. Test Hash Key Validation"
echo "3. Test API Registration Call"
echo "4. Test System Requirements"
echo "5. Test Directory Permissions"
echo "6. Run All Tests"
echo "7. Exit"
echo
}
# Run all tests
run_all_tests() {
log "INFO" "Running all tests..."
echo
test_system_requirements
echo
test_directory_permissions
echo
test_hash_key_validation
echo
test_api_connectivity
echo
log "INFO" "All tests completed"
}
# Main execution
main() {
while true; do
show_menu
read -p "Select an option (1-7): " choice
case $choice in
1)
test_api_connectivity
;;
2)
test_hash_key_validation
;;
3)
test_api_registration
;;
4)
test_system_requirements
;;
5)
test_directory_permissions
;;
6)
run_all_tests
;;
7)
log "INFO" "Exiting..."
exit 0
;;
*)
log "ERROR" "Invalid option. Please select 1-7."
;;
esac
echo
read -p "Press Enter to continue..."
done
}
# Run main function
main "$@"