From 8e6b00bfee7ba09a06bf1aa19cfc758e509fbeb2 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 6 Sep 2025 14:17:56 +1200 Subject: [PATCH] Update versions.json --- CLAUDE.md | 226 ++++++++++++++++++++ cloudflare-tunnel/README.txt | 69 ++++++ cloudflare-tunnel/config/.template_info.env | 22 ++ cloudflare-tunnel/config/service.env | 25 +++ cloudflare-tunnel/destroy.sh | 26 +++ cloudflare-tunnel/install.sh | 42 ++++ cloudflare-tunnel/logs.sh | 14 ++ cloudflare-tunnel/start.sh | 46 ++++ cloudflare-tunnel/status.sh | 44 ++++ cloudflare-tunnel/stop.sh | 22 ++ cloudflare-tunnel/uninstall.sh | 27 +++ versions.json | 1 + 12 files changed, 564 insertions(+) create mode 100644 CLAUDE.md create mode 100644 cloudflare-tunnel/README.txt create mode 100644 cloudflare-tunnel/config/.template_info.env create mode 100644 cloudflare-tunnel/config/service.env create mode 100755 cloudflare-tunnel/destroy.sh create mode 100755 cloudflare-tunnel/install.sh create mode 100755 cloudflare-tunnel/logs.sh create mode 100755 cloudflare-tunnel/start.sh create mode 100755 cloudflare-tunnel/status.sh create mode 100755 cloudflare-tunnel/stop.sh create mode 100755 cloudflare-tunnel/uninstall.sh diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..4538383 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,226 @@ +# Dropshell Template Development Guide + +## Overview +Dropshell templates are service deployment configurations that allow users to easily install and manage Docker-based services on remote servers. Each template provides a standardized interface for service lifecycle management. + +## Template Architecture + +### Directory Structure +``` +template-name/ +├── config/ +│ └── service.env # Service configuration variables +├── install.sh # Installation script (REQUIRED) +├── uninstall.sh # Uninstallation script (REQUIRED) +├── start.sh # Start service script (REQUIRED) +├── stop.sh # Stop service script (REQUIRED) +├── status.sh # Check service status (REQUIRED) +├── logs.sh # View service logs (optional) +├── backup.sh # Backup service data (optional) +├── restore.sh # Restore service data (optional) +├── destroy.sh # Complete removal including data (optional) +├── ports.sh # Display exposed ports (optional) +├── ssh.sh # SSH into container (optional) +└── _volumes.sh # Volume helper functions (optional) +``` + +## Required Scripts + +### 1. install.sh +- Pull Docker images +- Create necessary volumes/directories +- Verify configuration files exist +- Stop and remove existing containers (if any) +- Start the service +- Must source `${AGENT_PATH}/common.sh` + +### 2. uninstall.sh +- Stop the running container +- Remove the container +- Optionally clean up volumes (usually not) +- Must source `${AGENT_PATH}/common.sh` + +### 3. start.sh +- Define Docker run command with all parameters +- Use `_create_and_start_container` helper function +- Verify container is running +- Must source `${AGENT_PATH}/common.sh` + +### 4. stop.sh +- Stop the running container gracefully +- Use `_stop_container` helper function +- Must source `${AGENT_PATH}/common.sh` + +### 5. status.sh +- Check if container exists and is running +- Display container status information +- Return appropriate exit codes +- Must source `${AGENT_PATH}/common.sh` + +## Configuration (service.env) + +The `config/service.env` file contains service-specific variables: + +```bash +# Service identification +CONTAINER_NAME=service-name +IMAGE_REGISTRY=docker.io +IMAGE_REPO=vendor/image +IMAGE_TAG=latest + +# Volumes (if using Docker volumes) +DATA_VOLUME=${CONTAINER_NAME}_data +CONFIG_VOLUME=${CONTAINER_NAME}_config + +# Directories (if using host paths) +DATA_PATH=${SERVICE_PATH}/data +CONFIG_PATH=${SERVICE_PATH}/config + +# Service-specific settings +PORT=8080 +ENABLE_FEATURE=true +``` + +## Common Functions (from common.sh) + +Available helper functions: +- `_die "message"` - Print error and exit +- `_check_docker_installed` - Verify Docker availability +- `_check_required_env_vars "VAR1" "VAR2"` - Validate environment +- `_create_and_start_container "$cmd" "$name"` - Start container +- `_is_container_exists "$name"` - Check if container exists +- `_is_container_running "$name"` - Check if running +- `_stop_container "$name"` - Stop container +- `_remove_container "$name"` - Remove container +- `_create_folder "$path"` - Create directory with permissions + +## Best Practices + +### 1. User Permissions +- Templates run as the `dropshell` user (non-root) +- User is in the `docker` group +- Avoid using `sudo` in scripts +- Set appropriate file permissions (usually 777 for shared volumes) + +### 2. Container Management +- Always use `--restart unless-stopped` for reliability +- Name containers consistently using `$CONTAINER_NAME` +- Use official Docker images when possible +- Pin specific versions rather than using `:latest` in production + +### 3. Data Persistence +- Use Docker volumes or host directories for persistent data +- Separate config from data volumes +- Document backup/restore procedures +- Never delete data in uninstall.sh (only in destroy.sh) + +### 4. Network Configuration +- Expose ports using `-p HOST:CONTAINER` +- Document all exposed ports +- Consider using Docker networks for multi-container setups + +### 5. Error Handling +- Use `set -e` at script start (optional, common.sh handles most) +- Check command success with proper error messages +- Use `_die` for fatal errors +- Provide meaningful feedback to users + +### 6. Environment Variables +Available from dropshell: +- `${AGENT_PATH}` - Path to agent scripts (contains common.sh) +- `${SERVICE_PATH}` - Path to service directory on server +- `${CONFIG_PATH}` - Path to service config directory +- All variables from service.env + +## Creating a New Template + +### Step 1: Create Template Structure +```bash +mkdir template-name +mkdir template-name/config +``` + +### Step 2: Create service.env +Define all configuration variables with sensible defaults. + +### Step 3: Implement Required Scripts +Start with the five required scripts, following the patterns from existing templates. + +### Step 4: Test Locally +```bash +./test.sh # Run validation tests +./test_template.sh template-name # Integration test if dropshell installed +``` + +### Step 5: Add to versions.json +```json +{ + "template-name": "1.0.0" +} +``` + +### Step 6: Document +Create a README.txt explaining: +- What the service does +- Configuration options +- Default ports and paths +- Any special requirements + +## Template Examples + +### Simple Service (watchtower) +- Single container +- Minimal configuration +- No exposed ports +- Docker socket access + +### Web Service (caddy) +- HTTP/HTTPS ports +- Config files and static content +- Multiple volumes +- SSL certificate handling + +### Complex Service (gitea-runner) +- Multiple configuration files +- Docker-in-Docker capability +- Registration process +- Cleanup procedures + +## Testing Checklist + +- [ ] All required scripts present and executable +- [ ] service.env contains necessary variables +- [ ] Scripts source common.sh correctly +- [ ] Container starts and stops properly +- [ ] Status script returns correct information +- [ ] Uninstall removes container but preserves data +- [ ] Install is idempotent (can run multiple times) +- [ ] No hardcoded paths (use environment variables) +- [ ] Error messages are clear and helpful +- [ ] Works without root/sudo access + +## Version Management + +Follow semantic versioning: +- **MAJOR**: Breaking changes to configuration or behavior +- **MINOR**: New features, backwards compatible +- **PATCH**: Bug fixes, backwards compatible + +Update version with: +```bash +./bump-version.sh template-name patch|minor|major +``` + +## Publishing + +Templates are automatically published when pushed to main branch: +1. CI runs tests +2. Detects changed templates +3. Publishes to templates.dropshell.app +4. Tags with version numbers + +Manual publishing: +```bash +export SOS_WRITE_TOKEN=your-token +./publish.sh template-name +``` \ No newline at end of file diff --git a/cloudflare-tunnel/README.txt b/cloudflare-tunnel/README.txt new file mode 100644 index 0000000..927fe35 --- /dev/null +++ b/cloudflare-tunnel/README.txt @@ -0,0 +1,69 @@ +Cloudflare Tunnel Template for Dropshell +========================================= + +This template deploys a Cloudflare Tunnel (cloudflared) to securely expose your local services +to the internet without opening firewall ports or having a public IP address. + +PREREQUISITES +------------- +1. A Cloudflare account (free tier works) +2. A domain added to Cloudflare +3. A tunnel token from the Cloudflare Zero Trust dashboard + +SETUP INSTRUCTIONS +------------------ +1. Log into Cloudflare Dashboard: https://one.dash.cloudflare.com/ +2. Navigate to: Zero Trust -> Access -> Tunnels +3. Click "Create a tunnel" +4. Choose "Cloudflared" and click Next +5. Name your tunnel (e.g., "my-server-tunnel") +6. Copy the token from the install command (it's the long string after --token) +7. Save the tunnel (you'll configure routes in the dashboard later) + +CONFIGURATION +------------- +Edit config/service.env and set: +- TUNNEL_TOKEN: Your tunnel token (required) +- TUNNEL_NAME: A friendly name for logs (optional) +- EXTRA_ARGS: Additional cloudflared arguments (optional) + +ROUTING CONFIGURATION +-------------------- +After the tunnel is running, configure routes in the Cloudflare dashboard: +1. Go to your tunnel's configuration page +2. Click "Configure" +3. Add public hostname routes to your local services: + - Subdomain: app + - Domain: yourdomain.com + - Type: HTTP + - URL: localhost:8080 (or your service's local address) + +FEATURES +-------- +- Automatic reconnection on network issues +- No firewall configuration needed +- Free SSL certificates +- DDoS protection included +- Works behind NAT/CGNAT +- Automatic updates via watchtower + +COMMON USE CASES +--------------- +- Expose web services: Route subdomain.yourdomain.com -> localhost:port +- SSH access: Configure SSH routes in dashboard +- Multiple services: Add multiple public hostname routes +- Internal services: Use private network routes for VPN-like access + +TROUBLESHOOTING +-------------- +- Check logs: ds logs [server] cloudflare-tunnel +- Verify token: Ensure TUNNEL_TOKEN is set correctly +- Check dashboard: Verify tunnel shows as "Active" in Cloudflare dashboard +- Test locally: curl http://localhost:yourport to verify service is running + +NOTES +----- +- The tunnel will automatically start on system reboot +- One tunnel can handle multiple services via dashboard routing +- Token should be kept secret - it provides full tunnel access +- Free tier allows up to 50 users for private applications \ No newline at end of file diff --git a/cloudflare-tunnel/config/.template_info.env b/cloudflare-tunnel/config/.template_info.env new file mode 100644 index 0000000..dad3425 --- /dev/null +++ b/cloudflare-tunnel/config/.template_info.env @@ -0,0 +1,22 @@ +# DO NOT EDIT THIS FILE FOR YOUR SERVICE! +# This file is replaced from the template whenever there is an update. +# Edit the service.env file to make changes. + +# Template to use - always required! +TEMPLATE=cloudflare-tunnel +REQUIRES_HOST_ROOT=false +REQUIRES_DOCKER=true +REQUIRES_DOCKER_ROOT=false + +# Service settings +CONTAINER_NAME=cloudflare-tunnel + +# Image settings +IMAGE_REGISTRY="docker.io" +IMAGE_REPO="cloudflare/cloudflared" +IMAGE_TAG="latest" + +# Tunnel settings (to be overridden in service.env) +TUNNEL_TOKEN="" +TUNNEL_NAME="dropshell-tunnel" +EXTRA_ARGS="" diff --git a/cloudflare-tunnel/config/service.env b/cloudflare-tunnel/config/service.env new file mode 100644 index 0000000..8e9455b --- /dev/null +++ b/cloudflare-tunnel/config/service.env @@ -0,0 +1,25 @@ +# Service settings for Cloudflare Tunnel +# Simple configuration - just set your tunnel token! + +# Container configuration +CONTAINER_NAME=cloudflare-tunnel +IMAGE_REGISTRY=docker.io +IMAGE_REPO=cloudflare/cloudflared +IMAGE_TAG=latest + +# REQUIRED: Your Cloudflare Tunnel token +# Get this from: https://one.dash.cloudflare.com/ -> Zero Trust -> Access -> Tunnels +# Create a tunnel and copy the token from the install command +TUNNEL_TOKEN= + +# Optional: Tunnel name (for identification in logs) +TUNNEL_NAME=dropshell-tunnel + +# Optional: Additional cloudflared arguments +# Examples: +# EXTRA_ARGS="--loglevel debug" +# EXTRA_ARGS="--metrics localhost:2000" +EXTRA_ARGS= + +# Server Settings (usually don't need to change) +SSH_USER="dropshell" \ No newline at end of file diff --git a/cloudflare-tunnel/destroy.sh b/cloudflare-tunnel/destroy.sh new file mode 100755 index 0000000..f4822e1 --- /dev/null +++ b/cloudflare-tunnel/destroy.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# shellcheck disable=SC1091 +source "${AGENT_PATH}/common.sh" +_check_required_env_vars "CONTAINER_NAME" + +echo "WARNING: This will completely remove the Cloudflare Tunnel container." +echo "The tunnel configuration in Cloudflare dashboard will remain." +echo "" +read -p "Are you sure you want to destroy the Cloudflare Tunnel? (yes/no): " confirm + +if [ "$confirm" != "yes" ]; then + echo "Destruction cancelled" + exit 0 +fi + +# Stop and remove the container +bash ./uninstall.sh + +echo "" +echo "Cloudflare Tunnel has been destroyed." +echo "" +echo "To remove the tunnel from Cloudflare completely:" +echo "1. Go to: https://one.dash.cloudflare.com/" +echo "2. Navigate to: Zero Trust -> Access -> Tunnels" +echo "3. Find your tunnel and click the three dots menu" +echo "4. Select 'Delete'" \ No newline at end of file diff --git a/cloudflare-tunnel/install.sh b/cloudflare-tunnel/install.sh new file mode 100755 index 0000000..bfc57d7 --- /dev/null +++ b/cloudflare-tunnel/install.sh @@ -0,0 +1,42 @@ +#!/bin/bash +# shellcheck disable=SC1091 +source "${AGENT_PATH}/common.sh" +_check_required_env_vars "CONTAINER_NAME" "IMAGE_REGISTRY" "IMAGE_REPO" "IMAGE_TAG" "TUNNEL_TOKEN" + +# Check if tunnel token is set +if [ -z "$TUNNEL_TOKEN" ] || [ "$TUNNEL_TOKEN" = "" ]; then + _die "TUNNEL_TOKEN is not set in config/service.env! Please add your Cloudflare Tunnel token." +fi + +# Validate token format (basic check - should be a long base64-ish string) +if [ ${#TUNNEL_TOKEN} -lt 100 ]; then + echo "Warning: TUNNEL_TOKEN seems too short. Ensure you copied the entire token." +fi + +_check_docker_installed || _die "Docker test failed, aborting installation..." + +echo "Pulling Cloudflare Tunnel image..." +docker pull "$IMAGE_REGISTRY/$IMAGE_REPO:$IMAGE_TAG" || _die "Failed to pull image $IMAGE_REGISTRY/$IMAGE_REPO:$IMAGE_TAG" + +# Stop and remove existing container if it exists +if _is_container_exists "$CONTAINER_NAME"; then + echo "Removing existing container..." + bash ./stop.sh 2>/dev/null || true + _remove_container "$CONTAINER_NAME" || true +fi + +# Start the tunnel +bash ./start.sh || _die "Failed to start Cloudflare Tunnel" + +echo "" +echo "==========================================" +echo "Cloudflare Tunnel installation complete!" +echo "==========================================" +echo "" +echo "Next steps:" +echo "1. Check tunnel status: ds status [server] cloudflare-tunnel" +echo "2. View logs: ds logs [server] cloudflare-tunnel" +echo "3. Configure routes in Cloudflare dashboard:" +echo " https://one.dash.cloudflare.com/ -> Zero Trust -> Access -> Tunnels" +echo "" +echo "Your tunnel should appear as 'Active' in the dashboard within 30 seconds." \ No newline at end of file diff --git a/cloudflare-tunnel/logs.sh b/cloudflare-tunnel/logs.sh new file mode 100755 index 0000000..c810308 --- /dev/null +++ b/cloudflare-tunnel/logs.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# shellcheck disable=SC1091 +source "${AGENT_PATH}/common.sh" +_check_required_env_vars "CONTAINER_NAME" + +if ! _is_container_exists "$CONTAINER_NAME"; then + echo "Container $CONTAINER_NAME does not exist" + exit 1 +fi + +# Show logs with follow by default +echo "Showing logs for Cloudflare Tunnel (Ctrl+C to exit)..." +echo "=======================================================" +docker logs -f "$CONTAINER_NAME" 2>&1 \ No newline at end of file diff --git a/cloudflare-tunnel/start.sh b/cloudflare-tunnel/start.sh new file mode 100755 index 0000000..1b210b7 --- /dev/null +++ b/cloudflare-tunnel/start.sh @@ -0,0 +1,46 @@ +#!/bin/bash +# shellcheck disable=SC1091 +source "${AGENT_PATH}/common.sh" +_check_required_env_vars "CONTAINER_NAME" "IMAGE_REGISTRY" "IMAGE_REPO" "IMAGE_TAG" "TUNNEL_TOKEN" + +# Check if tunnel token is set +if [ -z "$TUNNEL_TOKEN" ] || [ "$TUNNEL_TOKEN" = "" ]; then + _die "TUNNEL_TOKEN is not set in config/service.env! Please add your Cloudflare Tunnel token." +fi + +echo "Starting Cloudflare Tunnel..." + +# Build the docker run command +DOCKER_RUN_CMD="docker run -d \ + --restart unless-stopped \ + --name ${CONTAINER_NAME} \ + --network host \ + ${IMAGE_REGISTRY}/${IMAGE_REPO}:${IMAGE_TAG} \ + tunnel --no-autoupdate run --token ${TUNNEL_TOKEN}" + +# Add extra arguments if specified +if [ -n "$EXTRA_ARGS" ]; then + DOCKER_RUN_CMD="${DOCKER_RUN_CMD} ${EXTRA_ARGS}" +fi + +# Create and start the container +if ! _create_and_start_container "$DOCKER_RUN_CMD" "$CONTAINER_NAME"; then + _die "Failed to start Cloudflare Tunnel container" +fi + +# Give it a moment to connect +sleep 2 + +# Check if the container is still running (didn't crash immediately) +if ! _is_container_running "$CONTAINER_NAME"; then + echo "Container failed to start. Checking logs..." + docker logs "$CONTAINER_NAME" 2>&1 | tail -20 + _die "Cloudflare Tunnel container exited unexpectedly. Check the TUNNEL_TOKEN and logs above." +fi + +echo "" +echo "Cloudflare Tunnel started successfully!" +echo "Container: ${CONTAINER_NAME}" +echo "" +echo "The tunnel should appear as 'Active' in your Cloudflare dashboard within 30 seconds." +echo "Configure routes at: https://one.dash.cloudflare.com/ -> Zero Trust -> Access -> Tunnels" \ No newline at end of file diff --git a/cloudflare-tunnel/status.sh b/cloudflare-tunnel/status.sh new file mode 100755 index 0000000..c240440 --- /dev/null +++ b/cloudflare-tunnel/status.sh @@ -0,0 +1,44 @@ +#!/bin/bash +# shellcheck disable=SC1091 +source "${AGENT_PATH}/common.sh" +_check_required_env_vars "CONTAINER_NAME" + +echo "Cloudflare Tunnel Status" +echo "========================" + +if ! _is_container_exists "$CONTAINER_NAME"; then + echo "Status: NOT INSTALLED" + echo "" + echo "The Cloudflare Tunnel container does not exist." + echo "Run 'ds install [server] cloudflare-tunnel' to set it up." + exit 1 +fi + +if ! _is_container_running "$CONTAINER_NAME"; then + echo "Status: STOPPED" + echo "" + echo "The Cloudflare Tunnel container exists but is not running." + echo "Run 'ds start [server] cloudflare-tunnel' to start it." + exit 1 +fi + +# Get container details +CONTAINER_ID=$(_get_container_id "$CONTAINER_NAME") +CONTAINER_STATUS=$(_get_container_status "$CONTAINER_NAME") + +echo "Status: RUNNING" +echo "Container: $CONTAINER_NAME" +echo "ID: $CONTAINER_ID" +echo "State: $CONTAINER_STATUS" +echo "" + +# Show recent logs to check connection status +echo "Recent connection status:" +echo "-------------------------" +docker logs "$CONTAINER_NAME" 2>&1 | grep -E "(Registered|Connected|failed|error)" | tail -5 + +echo "" +echo "Dashboard: https://one.dash.cloudflare.com/ -> Zero Trust -> Access -> Tunnels" +echo "View full logs: ds logs [server] cloudflare-tunnel" + +exit 0 \ No newline at end of file diff --git a/cloudflare-tunnel/stop.sh b/cloudflare-tunnel/stop.sh new file mode 100755 index 0000000..c43af73 --- /dev/null +++ b/cloudflare-tunnel/stop.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# shellcheck disable=SC1091 +source "${AGENT_PATH}/common.sh" +_check_required_env_vars "CONTAINER_NAME" + +if ! _is_container_exists "$CONTAINER_NAME"; then + echo "Container $CONTAINER_NAME does not exist" + exit 0 +fi + +if ! _is_container_running "$CONTAINER_NAME"; then + echo "Container $CONTAINER_NAME is not running" + exit 0 +fi + +echo "Stopping Cloudflare Tunnel..." +_stop_container "$CONTAINER_NAME" || _die "Failed to stop container $CONTAINER_NAME" + +echo "Cloudflare Tunnel stopped" +echo "" +echo "Note: The tunnel will show as 'Inactive' in your Cloudflare dashboard." +echo "Start it again with: ds start [server] cloudflare-tunnel" \ No newline at end of file diff --git a/cloudflare-tunnel/uninstall.sh b/cloudflare-tunnel/uninstall.sh new file mode 100755 index 0000000..cbaa935 --- /dev/null +++ b/cloudflare-tunnel/uninstall.sh @@ -0,0 +1,27 @@ +#!/bin/bash +# shellcheck disable=SC1091 +source "${AGENT_PATH}/common.sh" +_check_required_env_vars "CONTAINER_NAME" + +echo "Uninstalling Cloudflare Tunnel..." + +# Stop the container if running +if _is_container_running "$CONTAINER_NAME"; then + echo "Stopping container $CONTAINER_NAME..." + _stop_container "$CONTAINER_NAME" || echo "Warning: Failed to stop container" +fi + +# Remove the container +if _is_container_exists "$CONTAINER_NAME"; then + echo "Removing container $CONTAINER_NAME..." + _remove_container "$CONTAINER_NAME" || _die "Failed to remove container" +fi + +echo "" +echo "Cloudflare Tunnel has been uninstalled." +echo "" +echo "Note: The tunnel configuration in your Cloudflare dashboard remains intact." +echo "You can reinstall anytime using the same TUNNEL_TOKEN." +echo "" +echo "To completely remove the tunnel, delete it from:" +echo "https://one.dash.cloudflare.com/ -> Zero Trust -> Access -> Tunnels" \ No newline at end of file diff --git a/versions.json b/versions.json index 4432442..f7f5fc8 100644 --- a/versions.json +++ b/versions.json @@ -1,5 +1,6 @@ { "caddy": "1.0.0", + "cloudflare-tunnel": "1.0.0", "gitea-runner-docker": "1.0.0", "simple-object-server": "1.0.0", "static-website": "1.0.0",