commit 361e07332a0c152b3895cfcd193ff925510757aa Author: Edmund Tan Date: Sun Jul 20 07:23:34 2025 +0800 version 1.00 Zitinexus router enrollment script diff --git a/Router-enrollment-script/INSTALLATION.md b/Router-enrollment-script/INSTALLATION.md new file mode 100644 index 0000000..73b693e --- /dev/null +++ b/Router-enrollment-script/INSTALLATION.md @@ -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] " + 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 diff --git a/Router-enrollment-script/README.md b/Router-enrollment-script/README.md new file mode 100644 index 0000000..a1e5afa --- /dev/null +++ b/Router-enrollment-script/README.md @@ -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 + ├── .cert + ├── .key + ├── .server.chain.cert + └── .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 diff --git a/Router-enrollment-script/ROUTER_ENROLLMENT_SCRIPT_FIXES_SUMMARY.md b/Router-enrollment-script/ROUTER_ENROLLMENT_SCRIPT_FIXES_SUMMARY.md new file mode 100644 index 0000000..ccdca65 --- /dev/null +++ b/Router-enrollment-script/ROUTER_ENROLLMENT_SCRIPT_FIXES_SUMMARY.md @@ -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. diff --git a/Router-enrollment-script/config.sh b/Router-enrollment-script/config.sh new file mode 100644 index 0000000..915d885 --- /dev/null +++ b/Router-enrollment-script/config.sh @@ -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 diff --git a/Router-enrollment-script/enroll-router.sh b/Router-enrollment-script/enroll-router.sh new file mode 100644 index 0000000..9fc42b0 --- /dev/null +++ b/Router-enrollment-script/enroll-router.sh @@ -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 < /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 "$@" diff --git a/Router-enrollment-script/test-api-fix.sh b/Router-enrollment-script/test-api-fix.sh new file mode 100644 index 0000000..22ba83c --- /dev/null +++ b/Router-enrollment-script/test-api-fix.sh @@ -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 diff --git a/Router-enrollment-script/test-enrollment.sh b/Router-enrollment-script/test-enrollment.sh new file mode 100644 index 0000000..a16b67d --- /dev/null +++ b/Router-enrollment-script/test-enrollment.sh @@ -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 "$@"