Last active
June 11, 2026 23:00
-
-
Save afkarxyz/fcb0977530ebc1f9c819162ec3d8b120 to your computer and use it in GitHub Desktop.
Easypanel Quick Setup
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| ############################################################################### | |
| # Easypanel Quick Setup | |
| # Author: afkarxyz | |
| ############################################################################### | |
| RED='\033[0;31m' | |
| GREEN='\033[0;32m' | |
| YELLOW='\033[1;33m' | |
| BLUE='\033[0;34m' | |
| NC='\033[0m' | |
| PATCH_JS_TARGET="/etc/easypanel/patch-backend.js" | |
| PATCH_WRAPPER="/etc/easypanel/patch.sh" | |
| PATCH_WATCHER="/root/patch-easypanel.sh" | |
| PATCH_SERVICE_UNIT="/etc/systemd/system/patch-easypanel.service" | |
| PATCH_TIMER_UNIT="/etc/systemd/system/patch-easypanel-timer.service" | |
| PATCH_TIMER="/etc/systemd/system/patch-easypanel.timer" | |
| PATCH_LOG="/var/log/easypanel-patch.log" | |
| LEGACY_PATCH_DIR="/root/easypanel-patch" | |
| LEGACY_REPATCH_SCRIPT="$LEGACY_PATCH_DIR/repatch.sh" | |
| LEGACY_CUSTOM_IMAGE="easypanel/easypanel:patched" | |
| ORIGINAL_IMAGE="easypanel/easypanel:latest" | |
| get_service() { | |
| docker service ls --filter "name=easypanel" --format "{{.Name}}" | head -1 | |
| } | |
| confirm() { | |
| local reply | |
| read -p "Continue? [Y/n]: " -n 1 -r reply < /dev/tty | |
| echo | |
| [[ -z "$reply" || "$reply" =~ ^[Yy]$ ]] | |
| } | |
| ensure_docker() { | |
| if command -v docker > /dev/null 2>&1; then | |
| echo -e "${GREEN}✓ Docker detected${NC}" | |
| return 0 | |
| fi | |
| echo -e "${YELLOW}Docker not found. Installing...${NC}" | |
| curl -sSL https://get.docker.com | sh | |
| if ! command -v docker > /dev/null 2>&1; then | |
| echo -e "${RED}ERROR: Docker installation failed${NC}" | |
| return 1 | |
| fi | |
| systemctl enable --now docker > /dev/null 2>&1 || true | |
| echo -e "${GREEN}✓ Docker installed${NC}" | |
| return 0 | |
| } | |
| ensure_easypanel() { | |
| if [ -n "$(get_service)" ]; then | |
| echo -e "${GREEN}✓ Easypanel service detected${NC}" | |
| return 0 | |
| fi | |
| echo -e "${YELLOW}Easypanel not found. Installing...${NC}" | |
| docker run --rm -it \ | |
| -v /etc/easypanel:/etc/easypanel \ | |
| -v /var/run/docker.sock:/var/run/docker.sock:ro \ | |
| easypanel/easypanel setup | |
| if [ -z "$(get_service)" ]; then | |
| echo -e "${RED}ERROR: Easypanel installation failed${NC}" | |
| return 1 | |
| fi | |
| echo -e "${GREEN}✓ Easypanel installed${NC}" | |
| return 0 | |
| } | |
| write_patch_backend() { | |
| mkdir -p /etc/easypanel | |
| cat > "$PATCH_JS_TARGET" << 'PATCHJS' | |
| const fs = require("fs"); | |
| let code = fs.readFileSync("/app/backend.js", "utf-8"); | |
| let patches = 0; | |
| const fake = '({valid:!0,meta:{product_id:108143},license_key:"patched"})'; | |
| const portalFake = '({plan:{options:{advanced_monitoring:!0,notifications:!0,branding:!0,mutiple_users:!0,access_control:!0,cluster:!0}}})'; | |
| const projectLimit = "return e?.valid||t?!0:r.length<3}"; | |
| if (code.includes(projectLimit)) { | |
| code = code.replace(projectLimit, "return!0}"); | |
| patches++; | |
| console.log("[patch] Removed project limit"); | |
| } | |
| const lemonInitRegex = /global\.lemonLicenseManager=(\w+)/; | |
| const lemonMatch = code.match(lemonInitRegex); | |
| if (lemonMatch) { | |
| const varName = lemonMatch[1]; | |
| code = code.replace( | |
| lemonMatch[0], | |
| `global.lemonLicenseManager=${varName};${varName}.licensePayload=${fake}` | |
| ); | |
| patches++; | |
| console.log("[patch] Injected fake licensePayload on lemonLicenseManager (var: " + varName + ")"); | |
| } | |
| const lemonHandler = "({...await ng(),license_key:void 0})"; | |
| if (code.includes(lemonHandler)) { | |
| code = code.replace(lemonHandler, fake); | |
| patches++; | |
| console.log("[patch] Faked lemon tRPC getLicensePayload response"); | |
| } | |
| const portalHandler = "getLicensePayload:xt.query(async()=>br())"; | |
| if (code.includes(portalHandler)) { | |
| code = code.replace(portalHandler, "getLicensePayload:xt.query(async()=>" + portalFake + ")"); | |
| patches++; | |
| console.log("[patch] Faked portal tRPC getLicensePayload response"); | |
| } | |
| const whitelabelThrow = 'throw new se({code:"BAD_REQUEST",message:"You need a license that supports white-labeling."})'; | |
| if (code.includes(whitelabelThrow)) { | |
| const count = code.split(whitelabelThrow).length - 1; | |
| code = code.replaceAll(whitelabelThrow, "void 0"); | |
| patches++; | |
| console.log("[patch] Removed " + count + " white-labeling license checks"); | |
| } | |
| const customDomainThrow = 'throw new se({code:"BAD_REQUEST",message:"Custom service domain feature requires a valid license"})'; | |
| if (code.includes(customDomainThrow)) { | |
| code = code.replaceAll(customDomainThrow, "void 0"); | |
| patches++; | |
| console.log("[patch] Removed custom service domain license check"); | |
| } | |
| fs.writeFileSync("/app/backend.js", code); | |
| console.log("[patch] Done. Applied " + patches + " patches."); | |
| PATCHJS | |
| } | |
| write_patch_wrapper() { | |
| cat > "$PATCH_WRAPPER" << 'EOF' | |
| #!/bin/sh | |
| node /etc/easypanel/patch-backend.js | |
| exec node backend.js "$@" | |
| EOF | |
| chmod +x "$PATCH_WRAPPER" | |
| } | |
| write_patch_watcher() { | |
| cat > "$PATCH_WATCHER" << 'WATCHER' | |
| #!/bin/bash | |
| LOG="/var/log/easypanel-patch.log" | |
| PATCH_JS="/etc/easypanel/patch-backend.js" | |
| PATCH_WRAPPER="/etc/easypanel/patch.sh" | |
| get_service() { | |
| docker service ls --filter "name=easypanel" --format "{{.Name}}" | head -1 | |
| } | |
| SERVICE=$(get_service) | |
| if [ -z "$SERVICE" ]; then | |
| echo "[$(date)] Service not found, skipping." >> "$LOG" | |
| exit 0 | |
| fi | |
| if [ ! -f "$PATCH_JS" ] || [ ! -f "$PATCH_WRAPPER" ]; then | |
| echo "[$(date)] Patch files missing, cannot re-apply. Re-run setup script." >> "$LOG" | |
| exit 1 | |
| fi | |
| CURRENT=$(docker service inspect "$SERVICE" --format '{{json .Spec.TaskTemplate.ContainerSpec.Command}} {{json .Spec.TaskTemplate.ContainerSpec.Args}}' 2>/dev/null) | |
| if echo "$CURRENT" | grep -q "patch.sh"; then | |
| echo "[$(date)] Entrypoint already patched." >> "$LOG" | |
| exit 0 | |
| fi | |
| echo "[$(date)] Entrypoint not patched (current: $CURRENT). Re-applying..." >> "$LOG" | |
| docker service update "$SERVICE" \ | |
| --image "easypanel/easypanel:latest" \ | |
| --entrypoint "/etc/easypanel/patch.sh" \ | |
| --args "start" \ | |
| --force >> "$LOG" 2>&1 | |
| echo "[$(date)] Re-apply exit code: $?" >> "$LOG" | |
| WATCHER | |
| chmod +x "$PATCH_WATCHER" | |
| } | |
| setup_patch_service() { | |
| write_patch_watcher | |
| cat > "$PATCH_SERVICE_UNIT" << 'EOF' | |
| [Unit] | |
| Description=Auto-patch Easypanel entrypoint on container start | |
| After=docker.service | |
| Requires=docker.service | |
| [Service] | |
| Type=simple | |
| ExecStart=/bin/bash -c 'docker events --filter "image=easypanel/easypanel" --filter "event=start" --format "{{.Actor.ID}}" | while read id; do sleep 10 && /root/patch-easypanel.sh; done' | |
| Restart=always | |
| RestartSec=10 | |
| [Install] | |
| WantedBy=multi-user.target | |
| EOF | |
| cat > "$PATCH_TIMER_UNIT" << 'EOF' | |
| [Unit] | |
| Description=Periodic Easypanel patch check | |
| After=docker.service | |
| Requires=docker.service | |
| [Service] | |
| Type=oneshot | |
| ExecStart=/root/patch-easypanel.sh | |
| EOF | |
| cat > "$PATCH_TIMER" << 'EOF' | |
| [Unit] | |
| Description=Run Easypanel patch check every 5 minutes | |
| [Timer] | |
| OnBootSec=2min | |
| OnUnitActiveSec=5min | |
| Unit=patch-easypanel-timer.service | |
| [Install] | |
| WantedBy=timers.target | |
| EOF | |
| systemctl daemon-reload | |
| systemctl enable patch-easypanel > /dev/null 2>&1 | |
| systemctl restart patch-easypanel | |
| systemctl enable patch-easypanel.timer > /dev/null 2>&1 | |
| systemctl restart patch-easypanel.timer | |
| } | |
| remove_patch_service() { | |
| systemctl stop patch-easypanel > /dev/null 2>&1 || true | |
| systemctl disable patch-easypanel > /dev/null 2>&1 || true | |
| systemctl stop patch-easypanel.timer > /dev/null 2>&1 || true | |
| systemctl disable patch-easypanel.timer > /dev/null 2>&1 || true | |
| rm -f "$PATCH_SERVICE_UNIT" | |
| rm -f "$PATCH_TIMER_UNIT" | |
| rm -f "$PATCH_TIMER" | |
| rm -f "$PATCH_WATCHER" | |
| systemctl daemon-reload | |
| } | |
| remove_legacy_patch() { | |
| ( crontab -l 2>/dev/null | grep -v "easypanel-repatch\|repatch.sh" ) | crontab - 2>/dev/null || true | |
| rm -f "$LEGACY_REPATCH_SCRIPT" | |
| docker rmi "$LEGACY_CUSTOM_IMAGE" > /dev/null 2>&1 || true | |
| } | |
| apply_patch_to_service() { | |
| local service | |
| service=$(get_service) | |
| if [ -z "$service" ]; then | |
| echo -e "${RED}ERROR: Service not found${NC}" | |
| return 1 | |
| fi | |
| if ! docker service update "$service" \ | |
| --image "$ORIGINAL_IMAGE" \ | |
| --entrypoint "/etc/easypanel/patch.sh" \ | |
| --args "start" \ | |
| --force > /dev/null 2>&1; then | |
| echo -e "${RED}ERROR: Update failed${NC}" | |
| return 1 | |
| fi | |
| return 0 | |
| } | |
| is_patched() { | |
| local service | |
| local current | |
| service=$(get_service) | |
| if [ -n "$service" ]; then | |
| current=$(docker service inspect "$service" --format '{{json .Spec.TaskTemplate.ContainerSpec.Command}} {{json .Spec.TaskTemplate.ContainerSpec.Args}}' 2>/dev/null) | |
| if echo "$current" | grep -q "patch.sh"; then | |
| return 0 | |
| fi | |
| fi | |
| [ -f "$PATCH_JS_TARGET" ] || [ -f "$PATCH_WRAPPER" ] || [ -f "$PATCH_SERVICE_UNIT" ] || [ -f "$PATCH_WATCHER" ] | |
| } | |
| setup_server() { | |
| echo -e "${BLUE}========================================================${NC}" | |
| echo -e "${BLUE} Easypanel Quick Setup${NC}" | |
| echo -e "${BLUE} by afkarxyz${NC}" | |
| echo -e "${BLUE}========================================================${NC}" | |
| echo "" | |
| if [ "$EUID" -ne 0 ]; then | |
| echo -e "${RED}ERROR: Run as root${NC}" | |
| exit 1 | |
| fi | |
| if ! confirm; then | |
| exit 0 | |
| fi | |
| echo "" | |
| echo -e "${YELLOW}[1/5]${NC} Updating system packages..." | |
| apt update && apt upgrade -y | |
| echo -e "${GREEN}✓ System updated${NC}" | |
| echo "" | |
| echo -e "${YELLOW}[2/5]${NC} Configuring firewall..." | |
| ufw allow 22/tcp | |
| ufw allow 80/tcp | |
| ufw allow 443/tcp | |
| ufw --force enable | |
| echo -e "${GREEN}✓ Firewall configured (port 22, 80, 443)${NC}" | |
| echo "" | |
| echo -e "${YELLOW}[3/5]${NC} Checking Docker..." | |
| if ! ensure_docker; then | |
| exit 1 | |
| fi | |
| echo "" | |
| echo -e "${YELLOW}[4/5]${NC} Checking Easypanel..." | |
| if ! ensure_easypanel; then | |
| exit 1 | |
| fi | |
| echo "" | |
| echo -e "${YELLOW}[5/5]${NC} Setting up Docker prune cronjob..." | |
| CRON_JOB="0 * * * * docker builder prune -af && docker container prune -f >> /var/log/docker-prune.log 2>&1" | |
| ( crontab -l 2>/dev/null | grep -v "docker builder prune"; echo "$CRON_JOB" ) | crontab - | |
| echo -e "${GREEN}✓ Cronjob configured (every hour)${NC}" | |
| echo "" | |
| echo -e "${GREEN}========================================================${NC}" | |
| echo -e "${GREEN} Setup Complete!${NC}" | |
| echo -e "${GREEN}========================================================${NC}" | |
| echo "" | |
| echo -e " ${GREEN}✓${NC} System updated" | |
| echo -e " ${GREEN}✓${NC} Firewall: port 22, 80, 443 allowed" | |
| echo -e " ${GREEN}✓${NC} Docker ready" | |
| echo -e " ${GREEN}✓${NC} Easypanel service ready" | |
| echo -e " ${GREEN}✓${NC} Docker prune cronjob (every hour)" | |
| echo "" | |
| } | |
| patch_easypanel() { | |
| echo -e "${GREEN}========================================================${NC}" | |
| echo -e "${GREEN} Easypanel Quick Setup - PATCH${NC}" | |
| echo -e "${GREEN}========================================================${NC}" | |
| echo "" | |
| if [ "$EUID" -ne 0 ]; then | |
| echo -e "${RED}ERROR: Run as root${NC}" | |
| exit 1 | |
| fi | |
| if ! ensure_docker; then | |
| exit 1 | |
| fi | |
| if ! ensure_easypanel; then | |
| exit 1 | |
| fi | |
| if ! confirm; then | |
| exit 0 | |
| fi | |
| echo -e "\n${YELLOW}[1/4]${NC} Preparing backend patch files..." | |
| write_patch_backend | |
| write_patch_wrapper | |
| echo -e "${GREEN}✓ Patch files ready${NC}" | |
| echo -e "${YELLOW}[2/4]${NC} Cleaning up legacy patch method..." | |
| remove_legacy_patch | |
| echo -e "${GREEN}✓ Legacy cron/image cleanup complete${NC}" | |
| echo -e "${YELLOW}[3/4]${NC} Enabling auto re-patch service..." | |
| if ! setup_patch_service; then | |
| echo -e "${RED}ERROR: Failed to enable patch service${NC}" | |
| exit 1 | |
| fi | |
| echo -e "${GREEN}✓ Auto re-patch enabled via systemd${NC}" | |
| echo -e "${YELLOW}[4/4]${NC} Applying backend patch..." | |
| if ! apply_patch_to_service; then | |
| exit 1 | |
| fi | |
| echo -e "${GREEN}✓ Backend patch applied${NC}" | |
| echo -e "${YELLOW}Done.${NC}" | |
| echo "" | |
| echo -e "${GREEN}========================================================${NC}" | |
| echo -e "${GREEN} Patch Applied${NC}" | |
| echo -e "${GREEN}========================================================${NC}" | |
| echo "" | |
| echo -e " ${GREEN}✓${NC} Backend patch applied" | |
| echo -e " ${GREEN}✓${NC} Auto re-patch active via systemd watcher" | |
| echo -e " ${GREEN}✓${NC} Log: ${YELLOW}tail -f $PATCH_LOG${NC}" | |
| echo "" | |
| echo -e "${YELLOW}Clear browser cache (Ctrl+Shift+R)${NC}" | |
| echo "" | |
| } | |
| unpatch_easypanel() { | |
| echo -e "${RED}========================================================${NC}" | |
| echo -e "${RED} Easypanel Quick Setup - UNPATCH${NC}" | |
| echo -e "${RED}========================================================${NC}" | |
| echo "" | |
| if [ "$EUID" -ne 0 ]; then | |
| echo -e "${RED}ERROR: Run as root${NC}" | |
| exit 1 | |
| fi | |
| if ! is_patched; then | |
| echo -e "${YELLOW}Nothing to unpatch${NC}" | |
| exit 0 | |
| fi | |
| if ! confirm; then | |
| exit 0 | |
| fi | |
| echo -e "\n${YELLOW}[1/4]${NC} Finding service..." | |
| SERVICE=$(get_service) | |
| if [ -z "$SERVICE" ]; then | |
| echo -e "${RED}ERROR: Service not found${NC}" | |
| exit 1 | |
| fi | |
| echo -e "${YELLOW}[2/4]${NC} Restoring original image..." | |
| if ! docker service update "$SERVICE" \ | |
| --image "$ORIGINAL_IMAGE" \ | |
| --entrypoint "node" \ | |
| --args "backend.js start" \ | |
| --force > /dev/null 2>&1; then | |
| echo -e "${RED}ERROR: Restore failed${NC}" | |
| exit 1 | |
| fi | |
| echo -e "${YELLOW}[3/4]${NC} Removing auto re-patch..." | |
| remove_patch_service | |
| echo -e "${GREEN}✓ Auto re-patch disabled${NC}" | |
| echo -e "${YELLOW}[4/4]${NC} Cleaning up..." | |
| rm -f "$PATCH_JS_TARGET" "$PATCH_WRAPPER" | |
| remove_legacy_patch | |
| echo "" | |
| echo -e "${GREEN}========================================================${NC}" | |
| echo -e "${GREEN} Unpatch Complete${NC}" | |
| echo -e "${GREEN}========================================================${NC}" | |
| echo "" | |
| } | |
| show_menu() { | |
| while true; do | |
| echo -e "${BLUE}========================================================${NC}" | |
| echo -e "${BLUE} Easypanel Quick Setup${NC}" | |
| echo -e "${BLUE} by afkarxyz${NC}" | |
| echo -e "${BLUE}========================================================${NC}" | |
| echo "" | |
| echo -e " ${GREEN}1)${NC} Prepare Server + Patch" | |
| echo -e " ${GREEN}2)${NC} Patch Only" | |
| echo -e " ${RED}3)${NC} Unpatch" | |
| echo -e " ${YELLOW}4)${NC} Exit" | |
| echo "" | |
| read -p "Choice [1-4]: " choice < /dev/tty | |
| echo "" | |
| case $choice in | |
| 1) setup_server; patch_easypanel; break ;; | |
| 2) patch_easypanel; break ;; | |
| 3) unpatch_easypanel; break ;; | |
| 4) exit 0 ;; | |
| *) echo -e "${RED}Invalid${NC}\n" ;; | |
| esac | |
| done | |
| } | |
| if [ $# -eq 0 ]; then | |
| show_menu | |
| else | |
| case "$1" in | |
| setup|--setup|-s) setup_server ;; | |
| patch|--patch|-p) patch_easypanel ;; | |
| unpatch|--unpatch|-u) unpatch_easypanel ;; | |
| all|--all|-a) setup_server; patch_easypanel ;; | |
| --help|-h) | |
| echo "Easypanel Quick Setup by afkarxyz" | |
| echo "" | |
| echo "Usage:" | |
| echo " $0 Interactive menu" | |
| echo " $0 setup Prepare server only" | |
| echo " $0 patch Apply patch only" | |
| echo " $0 unpatch Remove patch" | |
| echo " $0 all Prepare server + patch" | |
| exit 0 | |
| ;; | |
| *) | |
| echo -e "${RED}Invalid option${NC}" | |
| exit 1 | |
| ;; | |
| esac | |
| fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.