diff --git a/.gitea/workflows/docker-image.yml b/.gitea/workflows/docker-image.yml new file mode 100644 index 0000000..19fc031 --- /dev/null +++ b/.gitea/workflows/docker-image.yml @@ -0,0 +1,32 @@ +name: Build and Push Docker Image + +on: + push: + branches: + - main + +jobs: + build-and-push: + runs-on: ubuntu-latest + steps: + - name: Check Out Repo + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and Push + uses: docker/build-push-action@v5 + with: + push: true + tags: yanhao98/frp-http-server:latest + platforms: linux/amd64,linux/arm64 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..37f9142 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,22 @@ +FROM --platform=$BUILDPLATFORM fatedier/frps:v0.53.2 as origin-frps + +FROM nginx:1 +COPY --from=origin-frps /usr/bin/frps /usr/bin/frps + +COPY ./src/nginx/templates /etc/nginx/templates +COPY ./src/html /usr/share/nginx/html + +COPY ./src/dl-frpc.sh /dl-frpc.sh + +RUN set -xe \ + && mv /docker-entrypoint.sh /docker-entrypoint-nginx.sh \ + && apt-get update \ + && apt-get install --no-install-recommends --no-install-suggests -y unzip zip wget \ + && bash /dl-frpc.sh && rm /dl-frpc.sh \ + && apt-get remove --purge --auto-remove -y unzip zip wget && rm -rf /var/lib/apt/lists/* && apt-get purge -y --auto-remove + +COPY ./src/nginx/templates /etc/nginx/templates +COPY ./src/entry.sh /entry.sh +# STOPSIGNAL SIGINT +ENTRYPOINT ["/entry.sh"] +EXPOSE 80 7000 diff --git a/README.md b/README.md new file mode 100644 index 0000000..a68c2b0 --- /dev/null +++ b/README.md @@ -0,0 +1,40 @@ +# frp-http + +## Server +```bash +docker run -it --rm --name frp-http-server \ + -e FRP_SUBDOMAIN_HOST=domain.com \ + -p 80:80 \ + -p 7000:7000 \ + yanhao98/frp-http-server +``` + +### Update +```bash +docker run --rm \ + -v /var/run/docker.sock:/var/run/docker.sock \ + containrrr/watchtower \ + --cleanup --run-once \ + frp-http-server +``` + + + +## Reference +- [Dockerfile for ngx](https://github.com/nginxinc/docker-nginx/blob/master/mainline/alpine-slim/Dockerfile) +- [Dockerfile-for-frps](https://github.com/fatedier/frp/blob/dev/dockerfiles/Dockerfile-for-frps) +- [Multi-arch frps's docker image](https://github.com/cloverzrg/frps-docker/blob/master/Dockerfile) +- https://github.com/snowdreamtech/frp/blob/master/frps/amd64/Dockerfile +- [VBScript](https://www.w3school.com.cn/vbscript/index.asp) +- https://stackoverflow.com/questions/4888197/how-To-show-dos-output-when-using-vbscript-exec/4888791#4888791 + +``` +rm /var/log/nginx/access.log /var/log/nginx/error.log +https://github.com/nginxinc/docker-nginx/blob/4bf0763f4977fff7e9648add59e0540088f3ca9f/mainline/debian/Dockerfile#L102 +the nginx log is already redirected to stdout/stderr by default (see Dockerfile#L102) +if we delete the log file, nginx will create a new one, but the new one is not redirected to stdout/stderr +``` diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..53c0eac --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,11 @@ +version: '3' +services: + frp-http-server: + build: . + environment: + - FRP_SUBDOMAIN_HOST=yhfrp.tk + # volumes: + # - ./src/html/client.sh:/usr/share/nginx/html/client.sh + ports: + - "80:80" + - "7000:7000" diff --git a/src/dl-frpc.sh b/src/dl-frpc.sh new file mode 100644 index 0000000..0b0d52a --- /dev/null +++ b/src/dl-frpc.sh @@ -0,0 +1,48 @@ +#!/bin/bash +# +# Description: 只保留frpc +# +set -e + +cd /usr/share/nginx/html + +FRPS_VER=$(frps -v) + +declare -a URLS +URLS[0]="https://github.com/fatedier/frp/releases/download/v${FRPS_VER}/frp_${FRPS_VER}_darwin_arm64.tar.gz" +URLS[1]="https://github.com/fatedier/frp/releases/download/v${FRPS_VER}/frp_${FRPS_VER}_darwin_amd64.tar.gz" +URLS[2]="https://github.com/fatedier/frp/releases/download/v${FRPS_VER}/frp_${FRPS_VER}_windows_amd64.zip" +URLS[3]="https://github.com/fatedier/frp/releases/download/v${FRPS_VER}/frp_${FRPS_VER}_windows_arm64.zip" + +mkdir ./temp +cd ./temp + +declare -a FILENAMES +declare -a OUTPUTS_FILENAMES +for i in "${!URLS[@]}"; do + echo "----------------------------------------" + FILENAMES[i]=$(basename "${URLS[$i]}") # >>> frp_0.0.0_darwin_arm64.tar.gz + OUTPUTS_FILENAMES[i]=${FILENAMES[$i]//frp/frpc} # >>> frpc_0.0.0_darwin_arm64.tar.gz + echo "Downloading ${FILENAMES[$i]} ..." + wget --quiet "${URLS[$i]}" + + echo "Packing frpc ..." + if [[ "${FILENAMES[$i]}" =~ \.zip$ ]]; then + unzip -q "./${FILENAMES[$i]}" + zip -q -j "./${OUTPUTS_FILENAMES[$i]}" "./${FILENAMES[$i]//.zip/}/frpc.exe" + rm -rf "./${FILENAMES[$i]//.zip/}" + else + # [tar命令详解](https://www.cnblogs.com/duanweishi/p/16899404.html) + tar -xzf "./${FILENAMES[$i]}" + tar -czf "./${OUTPUTS_FILENAMES[$i]}" -C "./${FILENAMES[$i]//.tar.gz/}" frpc + rm -rf "./${FILENAMES[$i]//.tar.gz/}" + fi + rm -rf "./${FILENAMES[$i]}" + +done + +cd .. + +mv ./temp ./download + +echo "Done." diff --git a/src/entry.sh b/src/entry.sh new file mode 100755 index 0000000..270ccae --- /dev/null +++ b/src/entry.sh @@ -0,0 +1,49 @@ +#!/bin/bash +set -e + +print_title() { + local title="$1" # 将传入的参数赋值给 title 变量 + + # ANSI 颜色代码 + local red='\033[0;31m' # 红色 + local no_color='\033[0m' # 无颜色(用于重置颜色) + + # 打印彩色标题 + echo -e "${red}${title}${no_color}" +} + +if [ -z "${FRP_SUBDOMAIN_HOST}" ] || [ "${FRP_SUBDOMAIN_HOST}" = "domain.com" ]; then + echo "FRP_SUBDOMAIN_HOST is not set" + exit 1 +fi + +FRPS_VER=$(frps -v) +SERVER_IP=$(curl -fsSL ip.sb) + +print_title "confirm environment variables" +printf "FRP_SUBDOMAIN_HOST: %s\n" "${FRP_SUBDOMAIN_HOST}" +printf "SERVER_IP: %s\n" "${SERVER_IP}" +printf "FRPS_VER: %s\n" "${FRPS_VER}" + +sed -i "s/REPLACE_MAIN_DOMAIN/${FRP_SUBDOMAIN_HOST}/g" /usr/share/nginx/html/client.sh +sed -i "s/REPLACE_SERVER_IP/${SERVER_IP}/g" /usr/share/nginx/html/client.sh +sed -i "s/REPLACE_VERSION/${FRPS_VER}/g" /usr/share/nginx/html/client.sh +sed -i "s/REPLACE_MAIN_DOMAIN/${FRP_SUBDOMAIN_HOST}/g" /usr/share/nginx/html/client.vbs +sed -i "s/REPLACE_SERVER_IP/${SERVER_IP}/g" /usr/share/nginx/html/client.vbs +sed -i "s/REPLACE_VERSION/${FRPS_VER}/g" /usr/share/nginx/html/client.vbs + +print_title "the download folder" +ls -l /usr/share/nginx/html/download + +print_title "start nginx" +/docker-entrypoint-nginx.sh nginx -g "daemon on;" + +sleep 0.1 +print_title "start frps" +exec frps \ + --bind_port="7000" \ + --subdomain_host="${FRP_SUBDOMAIN_HOST}" \ + --vhost_http_port="7080" \ + --dashboard_port="7500" \ + --dashboard_user="" \ + --dashboard_pwd="" diff --git a/src/html/client.sh b/src/html/client.sh new file mode 100644 index 0000000..47974ac --- /dev/null +++ b/src/html/client.sh @@ -0,0 +1,108 @@ +#!/bin/bash +set -e + +MAIN_DOMAIN="REPLACE_MAIN_DOMAIN" +SERVER_IP="REPLACE_SERVER_IP" +version='REPLACE_VERSION' + +# Get options +for i in "$@"; do + case $i in + --local=*) + LOCAL="${i#*=}" + ;; + -sd=* | --subdomain=*) + SUBDOMAIN="${i#*=}" + ;; + *) + # unknown option + echo "Unknown option: $i" + ;; + esac +done + +USERNAME=$(whoami) + +# Check options +if [[ -z "$LOCAL" ]]; then + echo "Missing required option: --local." + exit 1 +fi + +# Split local +IFS=':' read -r -a LOCAL_ARRAY <<<"$LOCAL" +LOCAL_IP=${LOCAL_ARRAY[0]} +LOCAL_PORT=${LOCAL_ARRAY[1]} + +if [[ -z "$SUBDOMAIN" ]]; then + UUID="$(system_profiler SPHardwareDataType | awk '/UUID/ { print $3; }')" + UUID=$(echo "$UUID" | tr '[:upper:]' '[:lower:]') + SUBDOMAIN="${UUID}-${LOCAL_PORT}" +fi + +# ############################### Ready to run ############################### +# https://www.ruanyifeng.com/blog/2019/12/mktemp.html +TEMP_DIR=$(mktemp -d) || exit 1 +declare -r TEMP_DIR +echo "Working in $TEMP_DIR" +cleanup() { + echo "" + echo "Cleaning up..." + local target="$1" + rm -vr "$target" +} +trap 'cleanup "$TEMP_DIR"' EXIT + + +download_frpc() { + local os=$(echo "$(uname -s)" | sed 's/Darwin/darwin/g') # Darwin => darwin + local arch=$(echo "$(uname -m)" | sed 's/x86_64/amd64/g') # x86_64 => amd64 + local filename="frpc_${version}_${os}_${arch}.tar.gz" + local url="${MAIN_DOMAIN}/download/${filename}" + cd "$TEMP_DIR" || exit 1 + echo "Downloading $filename from $url..." + curl -s -L "$url" -o "./$filename" + echo "Unpacking $filename..." + tar -xzf "./$filename" +} +download_frpc + +echo "Running frpc v$(./frpc -v)..." + +for i in {1..3}; do echo -ne "\033[1A\033[K"; done + +# ############################################################################## +url="https://$SUBDOMAIN.$MAIN_DOMAIN" +spaces_needed=$((69 - ${#url})) +padding=$(printf '%*s' $spaces_needed) + +cat <<"EOF" | sed "s|URL|$url$padding|g" + ___________________________________________________________________________ + / _ _ \ +|| | | (_) || +|| | |_ _ __ _ ___ _ __ || +|| | __| |/ _` |/ _ \ '__| || +|| | |_| | (_| | __/ | || +|| \__|_|\__, |\___|_|. || +|| __/ | || +|| |___/ || +||___________________________________________________________________________|| +|| || +|| You can access your service through the following address: || +|| URL|| +|| || +|| to stop frpc, please press ^C. || +|| || +|| Enjoy! || + \___________________________________________________________________________/ +EOF + + + +./frpc http \ + --server_addr=${SERVER_IP} \ + --server_port=7000 \ + --local_ip="${LOCAL_IP}" \ + --local_port="${LOCAL_PORT}" \ + --sd="${SUBDOMAIN}" \ + --proxy_name="${USERNAME}|${LOCAL}|${SUBDOMAIN}" diff --git a/src/html/client.vbs b/src/html/client.vbs new file mode 100644 index 0000000..64d3983 --- /dev/null +++ b/src/html/client.vbs @@ -0,0 +1,134 @@ +WSH.Echo "############################" +WSH.Echo "# _ _ #" +WSH.Echo "# | | (_) #" +WSH.Echo "# | |_ _ __ _ ___ _ __ #" +WSH.Echo "# | __| |/ _` |/ _ \ '__| #" +WSH.Echo "# | |_| | (_| | __/ | #" +WSH.Echo "# \__|_|\__, |\___|_| #" +WSH.Echo "# __/ | #" +WSH.Echo "# |___/ #" +WSH.Echo "############################" +WSH.Echo "" + +MAIN_DOMAIN = "REPLACE_MAIN_DOMAIN" +SERVER_IP = "REPLACE_SERVER_IP" +VERSION = "REPLACE_VERSION" + +Set WshShell = CreateObject("WScript.Shell") +Set FSO = CreateObject("Scripting.FileSystemObject") +Set environmentVars = WScript.CreateObject("WScript.Shell").Environment("Process") +MAIN_DOMAIN_WITHOUT_COLON = Replace(MAIN_DOMAIN, ":", "_") +tempFolder = environmentVars("TEMP")&"\"&MAIN_DOMAIN_WITHOUT_COLON&"_frp" + +url_args=WScript.Arguments(0) +port = Split(url_args, "=")(1) +computerName = WshShell.RegRead("HKLM\SOFTWARE\Microsoft\Cryptography\MachineGuid") + +WSH.Echo "MachineGuid: "&computerName +WSH.Echo "tempFolder : "&tempFolder +WSH.Echo "local : "&"127.0.0.1:"&port +WSH.Echo "" + +Call createTempFiles() +Call downloadFrp() +Call startFrp() + +Sub createTempFiles () + If Not FSO.FolderExists(tempFolder) Then + FSO.CreateFolder(tempFolder) + End If +End Sub + + +Sub downloadFrp () + Dim FILENAME + If IsArm() Then + FILENAME = "frpc_"&VERSION&"_windows_arm64.zip" + Else + FILENAME = "frpc_"&VERSION&"_windows_amd64.zip" + End If + WSH.Echo "frpc filename: "&FILENAME + WSH.Echo "" + + ' WScript.Quit(0) + + curl = "curl.exe -o "&tempFolder&"\frpc.zip http://"&MAIN_DOMAIN&"/download/"&FILENAME + WSH.Echo "Downloading frpc.zip..." + WSH.Echo "" + WshShell.Run curl, 0, True + + unzip = "powershell -command Expand-Archive -Path "&tempFolder&"\frpc.zip -DestinationPath "&tempFolder&" -Force" + WSH.Echo "Unzip frpc.zip..." + WSH.Echo "" + WshShell.Run unzip, 0, True +End Sub + +' Check if the CPU is ARM +Function IsArm() + Set objWMIService = GetObject("winmgmts:\\.\root\cimv2") + Set colItems = objWMIService.ExecQuery("Select * from Win32_Processor") + For Each objItem in colItems + ' WSH.Echo "Architecture: "&objItem.Architecture + If objItem.Architecture = 12 Then + IsArm = True + Else + IsArm = False + End If + Next +End Function + +Sub OutputCustomText() + Dim url, spacesNeeded, padding, line + + url = "https://" & computerName & "-" & port & "." & MAIN_DOMAIN + spacesNeeded = 69 - Len(url) + padding = String(spacesNeeded, " ") + + Dim box() + ReDim box(17) + + box(0) = " ___________________________________________________________________________ " + box(1) = " / _ _ \ " + box(2) = "|| | | (_) ||" + box(3) = "|| | |_ _ __ _ ___ _ __ ||" + box(4) = "|| | __| |/ _` |/ _ \ '__| ||" + box(5) = "|| | |_| | (_| | __/ | ||" + box(6) = "|| \__|_|\__, |\___|_|. ||" + box(7) = "|| __/ | ||" + box(8) = "|| |___/ ||" + box(9) = "||___________________________________________________________________________||" + box(10) = "|| ||" + box(11) = "|| You can access your service through the following address: ||" + box(12) = "|| " & url & padding & "||" + box(13) = "|| ||" + box(14) = "|| to stop frpc, please press ^C. ||" + box(15) = "|| ||" + box(16) = "|| Enjoy! ||" + box(17) = " \___________________________________________________________________________/ " + + For Each line In box + WScript.Echo line + Next +End Sub + +Sub startFrp () + startCmd = "cmd /c "&tempFolder&"\frpc.exe http --server_addr="&SERVER_IP&" --server_port=7000 --proxy_name="&computerName&"-"&port&" --local_port="&port&" --sd="&computerName&"-"&port + + ' ############################# + Set objFile = FSO.CreateTextFile(tempFolder&"\start.bat", True) + objFile.WriteLine startCmd + objFile.Close + ' ############################# + + + WSH.Echo "Start frpc.exe..." + Call OutputCustomText() + + Set oExec = WshShell.Exec(startCmd) + Set oStdOut = oExec.StdOut + WSH.Echo "frpc.exe output:" + While Not oStdOut.AtEndOfStream + sLine = oStdOut.ReadLine + WScript.Echo sLine + Wend +End Sub diff --git a/src/html/index.html b/src/html/index.html new file mode 100644 index 0000000..f34d399 --- /dev/null +++ b/src/html/index.html @@ -0,0 +1,83 @@ + + + + + + + frps + + + + + +
+
+
+
+
+
+

+    
+ + + + \ No newline at end of file diff --git a/src/nginx/templates/default.conf.template b/src/nginx/templates/default.conf.template new file mode 100644 index 0000000..ed1f797 --- /dev/null +++ b/src/nginx/templates/default.conf.template @@ -0,0 +1,35 @@ +server { + listen 80; + server_name ${FRP_SUBDOMAIN_HOST}; + default_type text/plain; + charset utf-8; + root /usr/share/nginx/html; + autoindex on; + + location ~ ^/(favicon.ico|api) { + proxy_pass http://127.0.0.1:7500; + } + + location = /dash { + return 301 https://$host/dash/#/proxies/http; + } + location /dash/ { + proxy_pass http://127.0.0.1:7500; + rewrite ^/dash/(.*)$ /static/$1 break; + } + + location /client.cmd { + return 200 "cls && curl -s ${FRP_SUBDOMAIN_HOST}/client.vbs -o %temp%\${FRP_SUBDOMAIN_HOST}_frp_client.vbs && cscript %temp%\${FRP_SUBDOMAIN_HOST}_frp_client.vbs $args\n"; + } +} + +server { + listen 80; + server_name *.${FRP_SUBDOMAIN_HOST}; + + location / { + proxy_pass http://127.0.0.1:7080; # 对应的是 entry.sh 的 --vhost_http_port + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + } +}