diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml new file mode 100644 index 00000000..d96da01f --- /dev/null +++ b/.github/workflows/docker.yaml @@ -0,0 +1,53 @@ +name: docker-build-push + +on: + push: + branches: [ dev ] + tags: + - 'v*' + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + + permissions: + contents: read + + steps: + - name: Checkout + uses: actions/checkout@v5 + with: + ref: dev + fetch-depth: 1 + + # QEMU 支持 + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + # buildx + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + # 登录 Docker Hub + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + # 构建并推送 + - name: Build and push (multi-arch) + uses: docker/build-push-action@v6 + with: + context: . + file: Dockerfile + platforms: | + linux/386 + linux/amd64 + linux/arm64 + linux/arm/v7 + push: true + tags: | + ${{ secrets.DOCKERHUB_USERNAME }}/shellcrash:latest + ${{ secrets.DOCKERHUB_USERNAME }}/shellcrash:${{ github.ref_name }} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..736a2eb1 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,90 @@ +############################ +# Stage 1: builder +############################ +FROM alpine:latest AS builder + +ARG TARGETPLATFORM +ARG TZ=Asia/Shanghai +ARG S6_OVERLAY_V=v3.2.1.0 + +RUN apk add --no-cache \ + curl \ + ca-certificates \ + tar \ + gzip \ + xz \ + tzdata \ + dcron + +# 时区 +RUN ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime && \ + echo "${TZ}" > /etc/timezone + +WORKDIR /build + +#安装脚本相关文件 +COPY ShellCrash.tar.gz /tmp/ShellCrash.tar.gz +RUN set -eux; \ + mkdir -p /tmp/SC_tmp; \ + tar -zxf /tmp/ShellCrash.tar.gz -C /tmp/SC_tmp; \ + /bin/sh /tmp/SC_tmp/init.sh + +#获取内核及s6文件 +RUN set -eux; \ + case "$TARGETPLATFORM" in \ + linux/amd64) K=amd64 S=x86_64;; \ + linux/arm64) K=arm64 S=aarch64;; \ + linux/arm/v7) K=armv7 S=arm;; \ + linux/386) K=386 S=i486;; \ + *) echo "unsupported $TARGETPLATFORM" && exit 1 ;; \ + esac; \ + curl -fsSL "https://github.com/juewuy/ShellCrash/raw/update/bin/meta/clash-linux-${K}.tar.gz" -o /tmp/CrashCore.tar.gz; \ + curl -fsSL "https://github.com/just-containers/s6-overlay/releases/download/${S6_OVERLAY_V}/s6-overlay-${S}.tar.xz" -o /tmp/s6_arch.tar.xz; \ + curl -fsSL "https://github.com/just-containers/s6-overlay/releases/download/${S6_OVERLAY_V}/s6-overlay-noarch.tar.xz" -o /tmp/s6_noarch.tar.xz && ls -l /tmp + +#安装面板文件 +RUN set -eux; \ + mkdir -p /etc/ShellCrash/ruleset /etc/ShellCrash/ui; \ + curl -fsSL "https://github.com/juewuy/ShellCrash/raw/update/bin/geodata/mrs.tar.gz" | tar -zxf - -C /etc/ShellCrash/ruleset; \ + curl -fsSL "https://github.com/juewuy/ShellCrash/raw/update/bin/dashboard/zashboard.tar.gz" | tar -zxf - -C /etc/ShellCrash/ui + +############################ +# Stage 2: runtime +############################ +FROM alpine:latest + +ARG TZ=Asia/Shanghai + +LABEL org.opencontainers.image.source="https://github.com/juewuy/ShellCrash" +#安装依赖 +RUN apk add --no-cache \ + tini \ + openrc \ + wget \ + ca-certificates \ + tzdata \ + nftables \ + iproute2 \ + dcron +#清理openrc +RUN apk del openrc && rm -rf /etc/runlevels/* /run/openrc + +RUN ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime && \ + echo "${TZ}" > /etc/timezone + +#复制文件 +COPY --from=builder /etc/ShellCrash /etc/ShellCrash +COPY --from=builder /tmp/CrashCore.tar.gz /etc/ShellCrash/CrashCore.tar.gz +COPY --from=builder /etc/profile /etc/profile +COPY --from=builder /usr/bin/crash /usr/bin/crash + +#安装s6 +COPY --from=builder /tmp/s6_arch.tar.xz /tmp/s6_arch.tar.xz +COPY --from=builder /tmp/s6_noarch.tar.xz /tmp/s6_noarch.tar.xz +RUN tar -xJf /tmp/s6_noarch.tar.xz -C / && rm -rf /tmp/s6_noarch.tar.xz +RUN tar -xJf /tmp/s6_arch.tar.xz -C / && rm -rf /tmp/s6_arch.tar.xz +COPY docker/s6-rc.d /etc/s6-overlay/s6-rc.d +ENV S6_CMD_WAIT_FOR_SERVICES=1 + +ENTRYPOINT ["/init"] + diff --git a/docker/s6-rc.d/afstart/dependencies.d/shellcrash b/docker/s6-rc.d/afstart/dependencies.d/shellcrash new file mode 100644 index 00000000..e69de29b diff --git a/docker/s6-rc.d/afstart/type b/docker/s6-rc.d/afstart/type new file mode 100644 index 00000000..bdd22a18 --- /dev/null +++ b/docker/s6-rc.d/afstart/type @@ -0,0 +1 @@ +oneshot diff --git a/docker/s6-rc.d/afstart/up b/docker/s6-rc.d/afstart/up new file mode 100644 index 00000000..2d2c62ba --- /dev/null +++ b/docker/s6-rc.d/afstart/up @@ -0,0 +1,2 @@ +#!/command/execlineb -P +/etc/ShellCrash/start.sh afstart diff --git a/docker/s6-rc.d/bfstart/type b/docker/s6-rc.d/bfstart/type new file mode 100644 index 00000000..bdd22a18 --- /dev/null +++ b/docker/s6-rc.d/bfstart/type @@ -0,0 +1 @@ +oneshot diff --git a/docker/s6-rc.d/bfstart/up b/docker/s6-rc.d/bfstart/up new file mode 100644 index 00000000..a3560bd9 --- /dev/null +++ b/docker/s6-rc.d/bfstart/up @@ -0,0 +1,2 @@ +#!/command/execlineb -P +/etc/ShellCrash/start.sh bfstart diff --git a/docker/s6-rc.d/crond/run b/docker/s6-rc.d/crond/run new file mode 100644 index 00000000..9d793843 --- /dev/null +++ b/docker/s6-rc.d/crond/run @@ -0,0 +1,3 @@ +#!/command/execlineb -P +fdmove -c 2 1 +exec crond -f -l 8 \ No newline at end of file diff --git a/docker/s6-rc.d/crond/type b/docker/s6-rc.d/crond/type new file mode 100644 index 00000000..5883cff0 --- /dev/null +++ b/docker/s6-rc.d/crond/type @@ -0,0 +1 @@ +longrun diff --git a/docker/s6-rc.d/shellcrash/dependencies.d/bfstart b/docker/s6-rc.d/shellcrash/dependencies.d/bfstart new file mode 100644 index 00000000..e69de29b diff --git a/docker/s6-rc.d/shellcrash/run b/docker/s6-rc.d/shellcrash/run new file mode 100644 index 00000000..b1520b41 --- /dev/null +++ b/docker/s6-rc.d/shellcrash/run @@ -0,0 +1,6 @@ +#!/bin/sh +set -e + +. /etc/ShellCrash/configs/command.env + +exec /bin/sh -c "${COMMAND} > /dev/null" diff --git a/docker/s6-rc.d/shellcrash/type b/docker/s6-rc.d/shellcrash/type new file mode 100644 index 00000000..5883cff0 --- /dev/null +++ b/docker/s6-rc.d/shellcrash/type @@ -0,0 +1 @@ +longrun diff --git a/docker/s6-rc.d/user/contents.d/crond b/docker/s6-rc.d/user/contents.d/crond new file mode 100644 index 00000000..e69de29b diff --git a/scripts/init.sh b/scripts/init.sh index 060a855f..9ae31fc4 100644 --- a/scripts/init.sh +++ b/scripts/init.sh @@ -1,7 +1,7 @@ #!/bin/sh # Copyright (C) Juewuy -version=1.9.3beta8fix +version=1.9.3beta9 setdir() { dir_avail() { @@ -174,13 +174,11 @@ setconfig() { #脚本配置工具 [ -f "/data/etc/crontabs/root" ] && systype=mi_snapshot #小米设备 [ -w "/var/mnt/cfg/firewall" ] && systype=ng_snapshot #NETGEAR设备 #容器内环境 -grep -qE '/(docker|lxc|kubepods|crio|containerd)/' /proc/1/cgroup || [ -f /run/.containerenv ] || [ -f /.dockerenv ] && { - systype='container' - CRASHDIR='/etc/ShellCrash' -} +grep -qE '/(docker|lxc|kubepods|crio|containerd)/' /proc/1/cgroup || [ -f /run/.containerenv ] || [ -f /.dockerenv ] && systype='container' #检查环境变量 -[ -z "$CRASHDIR" -a -n "$clashdir" ] && CRASHDIR="$clashdir" -[ -z "$CRASHDIR" -a -d /tmp/SC_tmp ] && setdir +[ "$systype" = 'container' ] && CRASHDIR='/etc/ShellCrash' +[ -z "$CRASHDIR" ] && [ -n "$clashdir" ] && CRASHDIR="$clashdir" +[ -z "$CRASHDIR" ] && [ -d /tmp/SC_tmp ] && setdir #移动文件 mkdir -p ${CRASHDIR} mv -f /tmp/SC_tmp/* ${CRASHDIR} 2>/dev/null @@ -310,12 +308,19 @@ fi [ "$systype" = 'container' ] && { setconfig userguide '1' setconfig crashcore 'meta' - setconfig redir_mod "混合模式" setconfig dns_mod 'mix' setconfig firewall_area '1' setconfig firewall_mod 'nftables' - setconfig start_old '已开启' + setconfig release_type 'master' + setconfig start_old '未开启' echo "$CRASHDIR/menu.sh" >> /etc/profile + cat > /usr/bin/crash <<'EOF' +#!/bin/sh +CRASHDIR=${CRASHDIR:-/etc/ShellCrash} +export CRASHDIR +exec "$CRASHDIR/menu.sh" "$@" +EOF + chmod 755 /usr/bin/crash } setconfig systype $systype #删除临时文件 diff --git a/scripts/menu.sh b/scripts/menu.sh index 8e940f56..5248794d 100644 --- a/scripts/menu.sh +++ b/scripts/menu.sh @@ -67,6 +67,8 @@ ckstatus() { [ -n "$(find /etc/rc.d -name '*shellcrash')" ] && autostart=enable || autostart=disable elif ckcmd systemctl; then [ "$(systemctl is-enabled shellcrash.service 2>&1)" = enabled ] && autostart=enable || autostart=disable + elif grep -q 's6' /proc/1/comm; then + [ -f /etc/s6-overlay/s6-rc.d/user/contents.d/afstart ] && autostart=enable || autostart=disable elif rc-status -r >/dev/null 2>&1; then rc-update show default | grep -q "shellcrash" && autostart=enable || autostart=disable else @@ -1228,7 +1230,8 @@ setboot() { #启动相关设置 # 禁止自启动:删除各系统的启动项 [ -d /etc/rc.d ] && cd /etc/rc.d && rm -rf *shellcrash >/dev/null 2>&1 && cd - >/dev/null ckcmd systemctl && systemctl disable shellcrash.service >/dev/null 2>&1 - [rc-status -r >/dev/null 2>&1 && rc-update del shellcrash default >/dev/null + grep -q 's6' /proc/1/comm && rm -rf /etc/s6-overlay/s6-rc.d/user/contents.d/afstart + rc-status -r >/dev/null 2>&1 && rc-update del shellcrash default >/dev/null 2>&1 touch ${CRASHDIR}/.dis_startup autostart=disable echo -e "\033[33m已禁止ShellCrash开机启动!\033[0m" @@ -1236,7 +1239,8 @@ setboot() { #启动相关设置 # 允许自启动:配置各系统的启动项 [ -f /etc/rc.common -a "$(cat /proc/1/comm)" = "procd" ] && /etc/init.d/shellcrash enable ckcmd systemctl && systemctl enable shellcrash.service >/dev/null 2>&1 - rc-status -r >/dev/null 2>&1 && rc-update add shellcrash default >/dev/null + grep -q 's6' /proc/1/comm && touch /etc/s6-overlay/s6-rc.d/user/contents.d/afstart + rc-status -r >/dev/null 2>&1 && rc-update add shellcrash default >/dev/null 2>&1 rm -rf ${CRASHDIR}/.dis_startup autostart=enable echo -e "\033[32m已设置ShellCrash开机启动!\033[0m" @@ -1248,11 +1252,13 @@ setboot() { #启动相关设置 echo -e "\033[33m改为使用保守模式启动服务!!\033[0m" [ -d /etc/rc.d ] && cd /etc/rc.d && rm -rf *shellcrash >/dev/null 2>&1 && cd - >/dev/null ckcmd systemctl && systemctl disable shellcrash.service >/dev/null 2>&1 + grep -q 's6' /proc/1/comm && rm -rf /etc/s6-overlay/s6-rc.d/user/contents.d/afstart + rc-status -r >/dev/null 2>&1 && rc-update del shellcrash default >/dev/null 2>&1 start_old=已开启 setconfig start_old $start_old ${CRASHDIR}/start.sh stop else - if grep -qE 'procd|systemd' /proc/1/comm || rc-status -r >/dev/null 2>&1; then + if grep -qE 'procd|systemd|s6' /proc/1/comm || rc-status -r >/dev/null 2>&1; then echo -e "\033[32m改为使用系统守护进程启动服务!!\033[0m" ${CRASHDIR}/start.sh cronset "ShellCrash初始化" start_old=未开启 @@ -1474,7 +1480,8 @@ set_redir_mod() { #代理模式设置 } [ -n "$(ls /dev/net/tun 2>/dev/null)" ] || ip tuntap >/dev/null 2>&1 && sup_tun=1 [ -z "$firewall_area" ] && firewall_area=1 - [ -z "$firewall_mod" ] && firewall_mod=未设置 + [ -z "$redir_mod" ] && [ "$USER" = "root" -o "$USER" = "admin" ] && redir_mod='Redir模式' + [ -z "$redir_mod" ] && redir_mod='纯净模式' firewall_area_dsc=$(echo "仅局域网 仅本机 局域网+本机 纯净模式 主-旁转发($bypass_host)" | cut -d' ' -f$firewall_area) echo "-----------------------------------------------" echo -e "当前代理模式为:\033[47;30m$redir_mod\033[0m;ShellCrash核心为:\033[47;30m $crashcore \033[0m" diff --git a/scripts/shellcrash.openrc b/scripts/shellcrash.openrc index acd92ba2..a0b21567 100644 --- a/scripts/shellcrash.openrc +++ b/scripts/shellcrash.openrc @@ -68,7 +68,6 @@ stop() { # 清理 firewall、proxy "$CRASHDIR/start.sh" stop_firewall - "$CRASHDIR/start.sh" unset_proxy eend $? } diff --git a/scripts/shellcrash.procd b/scripts/shellcrash.procd index 1227661e..f884dd10 100644 --- a/scripts/shellcrash.procd +++ b/scripts/shellcrash.procd @@ -35,5 +35,4 @@ start_service() { stop_service() { procd_close_instance $CRASHDIR/start.sh stop_firewall - $CRASHDIR/start.sh unset_proxy } diff --git a/scripts/shellcrash.service b/scripts/shellcrash.service index 2b78998b..ac2bc3c0 100644 --- a/scripts/shellcrash.service +++ b/scripts/shellcrash.service @@ -10,7 +10,7 @@ StandardOutput=null ExecStartPre=/etc/ShellCrash/start.sh bfstart ExecStart=/etc/ShellCrash/CrashCore run -D /etc/ShellCrash -C /tmp/ShellCrash/jsons >/dev/null ExecStartPost=/etc/ShellCrash/start.sh afstart -ExecStopPost=/etc/ShellCrash/start.sh stop_firewall ; /etc/ShellCrash/start.sh unset_proxy +ExecStopPost=/etc/ShellCrash/start.sh stop_firewall Restart=on-abnormal RestartSec=10s LimitNOFILE=infinity diff --git a/scripts/start.sh b/scripts/start.sh index 12b3ea0d..5bd8073e 100644 --- a/scripts/start.sh +++ b/scripts/start.sh @@ -16,8 +16,8 @@ getconfig() { #读取配置及全局变量 #加载配置文件 . "$CRASHDIR"/configs/ShellCrash.cfg >/dev/null #缺省值 - [ -z "$redir_mod" ] && [ "$USER" = "root" -o "$USER" = "admin" ] && redir_mod=Redir模式 - [ -z "$redir_mod" ] && redir_mod=纯净模式 + [ -z "$redir_mod" ] && [ "$USER" = "root" -o "$USER" = "admin" ] && redir_mod='Redir模式' + [ -z "$redir_mod" ] && firewall_area='4' [ -z "$skip_cert" ] && skip_cert=已开启 [ -z "$dns_mod" ] && dns_mod=fake-ip [ -z "$ipv6_redir" ] && ipv6_redir=未开启 @@ -570,10 +570,10 @@ EOF sed -i "/#自定义策略组/d" "$TMPDIR"/proxy-groups.yaml [ -n "$(grep -Ev '^#' "$CRASHDIR"/yamls/proxy-groups.yaml 2>/dev/null)" ] && { #获取空格数 - space_name=$(grep -aE '^ *- name: ' "$TMPDIR"/proxy-groups.yaml | head -n 1 | grep -oE '^ *') - space_proxy=$(grep -A 1 'proxies:$' "$TMPDIR"/proxy-groups.yaml | grep -aE '^ *- ' | head -n 1 | grep -oE '^ *') + space_name=$(grep -aE '^ *- \{?name: ' "$TMPDIR"/proxy-groups.yaml | head -n 1 | grep -oE '^ *') + space_proxy="$space_name " #合并自定义策略组到proxy-groups.yaml - cat "$CRASHDIR"/yamls/proxy-groups.yaml | sed "/^#/d" | sed "s/#.*//g" | sed '1i\ #自定义策略组开始' | sed '$a\ #自定义策略组结束' | sed "s/^ */${space_name} /g" | sed "s/^ *- /${space_proxy}- /g" | sed "s/^ *- name: /${space_name}- name: /g" >"$TMPDIR"/proxy-groups_add.yaml + cat "$CRASHDIR"/yamls/proxy-groups.yaml | sed "/^#/d" | sed "s/#.*//g" | sed '1i\ #自定义策略组开始' | sed '$a\ #自定义策略组结束' | sed "s/^ */${space_name} /g" | sed "s/^ *- /${space_proxy}- /g" | sed "s/^ *- name: /${space_name}- name: /g" | sed "s/^ *- {name: /${space_name}- {name: /g" >"$TMPDIR"/proxy-groups_add.yaml cat "$TMPDIR"/proxy-groups.yaml >>"$TMPDIR"/proxy-groups_add.yaml mv -f "$TMPDIR"/proxy-groups_add.yaml "$TMPDIR"/proxy-groups.yaml oldIFS="$IFS" @@ -1461,10 +1461,10 @@ start_nft_wan() { #nftables公网防火墙 } start_nftables() { #nftables配置总入口 #初始化nftables - nft add table inet shellcrash - nft flush table inet shellcrash + nft add table inet shellcrash 2>/dev/null + nft flush table inet shellcrash 2>/dev/null #公网访问防火墙 - start_nft_wan + [ "$systype" != 'container' ] && start_nft_wan #启动DNS劫持 [ "$dns_no" != "已禁用" -a "$dns_redir" != "已开启" -a "$firewall_area" -le 3 ] && { [ "$lan_proxy" = true ] && start_nft_dns prerouting prerouting #局域网dns转发 @@ -1704,7 +1704,7 @@ stop_firewall() { #还原防火墙配置 #还原防火墙文件 [ -s /etc/init.d/firewall.bak ] && mv -f /etc/init.d/firewall.bak /etc/init.d/firewall #others - sed -i '/shellcrash-dns-repair/d' /etc/resolv.conf + [ "$systype" != 'container' ] && sed -i '/shellcrash-dns-repair/d' /etc/resolv.conf >/dev/null 2>&1 } #启动相关 web_save() { #最小化保存面板节点选择 @@ -2043,20 +2043,6 @@ hotupdate() { #热更新订阅 put_save http://127.0.0.1:${db_port}/configs "{\"path\":\""$CRASHDIR"/config.$format\"}" rm -rf "$TMPDIR"/CrashCore } -set_proxy() { #设置环境变量 - if [ "$local_type" = "环境变量" ]; then - [ -w ~/.bashrc ] && profile=~/.bashrc - [ -w /etc/profile ] && profile=/etc/profile - echo 'export all_proxy=http://127.0.0.1:'"$mix_port" >>$profile - echo 'export ALL_PROXY=$all_proxy' >>$profile - fi -} -unset_proxy() { #卸载环境变量 - [ -w ~/.bashrc ] && profile=~/.bashrc - [ -w /etc/profile ] && profile=/etc/profile - sed -i '/all_proxy/'d $profile - sed -i '/ALL_PROXY/'d $profile -} getconfig #读取配置及全局变量 @@ -2066,7 +2052,7 @@ start) [ -n "$(pidof CrashCore)" ] && $0 stop #禁止多实例 stop_firewall #清理路由策略 #使用不同方式启动服务 - if [ "$firewall_area" = "5" ]; then #主旁转发 + if [ "$firewall_area" = "5" ]; then #主旁转发 start_firewall elif [ "$start_old" = "已开启" ]; then bfstart && start_old @@ -2079,15 +2065,14 @@ start) systemctl daemon-reload systemctl start shellcrash.service || start_error } + elif grep -q 's6' /proc/1/comm; then + bfstart && /command/s6-svc -u /run/service/shellcrash && afstart & elif rc-status -r >/dev/null 2>&1; then rc-service shellcrash stop >/dev/null 2>&1 rc-service shellcrash start else bfstart && start_old fi - if [ "$2" = "infinity" ]; then #增加容器自启方式,请将CMD设置为"$CRASHDIR"/start.sh start infinity - sleep infinity - fi ;; stop) logger ShellCrash服务即将关闭…… @@ -2102,11 +2087,13 @@ stop) systemctl stop shellcrash.service >/dev/null 2>&1 elif [ -f /etc/rc.common -a "$(cat /proc/1/comm)" = "procd" ]; then /etc/init.d/shellcrash stop >/dev/null 2>&1 + elif grep -q 's6' /proc/1/comm; then + /command/s6-svc -d /run/service/shellcrash + stop_firewall elif rc-status -r >/dev/null 2>&1; then rc-service shellcrash stop >/dev/null 2>&1 else stop_firewall #清理路由策略 - unset_proxy #禁用本机代理 fi PID=$(pidof CrashCore) && [ -n "$PID" ] && kill -9 $PID >/dev/null 2>&1 #清理缓存目录