diff --git a/Dockerfile.alpine b/Dockerfile.alpine new file mode 100644 index 0000000..5382ce2 --- /dev/null +++ b/Dockerfile.alpine @@ -0,0 +1,21 @@ +FROM alpine:latest + +LABEL maintainer="ultraxz@qq.com" \ + release.version="alpine:latest" \ + description="alpine with libstdc++&glibc" + +ENV LANG=C.UTF-8 LC_ALL=C.UTF-8 \ + LD_LIBRARY_PATH=/usr/local/lib:/usr/glibc-compat/lib:/opt/libs/lib:/usr/lib:/lib \ + PATH=/usr/glibc-compat/sbin:/usr/glibc-compat/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \ + GLIBC_REPO=https://github.com/sgerrand/alpine-pkg-glibc \ + GLIBC_VERSION=2.34-r0 + +RUN sed -i "s/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g" /etc/apk/repositories \ + && apk update \ + && wget -q -O /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub \ + && for pkg in glibc-${GLIBC_VERSION} glibc-bin-${GLIBC_VERSION}; \ + do wget -q ${GLIBC_REPO}/releases/download/${GLIBC_VERSION}/${pkg}.apk -O /tmp/${pkg}.apk; done \ + && apk add /tmp/glibc*.apk \ + && rm -v /tmp/glibc*.apk \ + && apk add libstdc++ \ + && rm -rf /var/cache/apk/* \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..606f887 --- /dev/null +++ b/README.md @@ -0,0 +1,59 @@ +# Easyconnect-alpine + +基于 docker-alpine 的最小化Easyconnect CLI镜像,使用dante-server提供socks连接 +移植&修改自 https://github.com/shmilee/scripts/tree/master/easyconnect-in-docker +体积变化:打包65.52 MB(shmilee)->36.71 MB(Hagb)->15.6MB, 解压后 ~100MB(Hagb)->41.6MB + + + +练习docker压榨打包产物,不考虑VNC/浏览器登录问题,采用alpine+glibc方案 + +## 结构 + +├── src +│ ├── change_authority.sh:ec资源包文件权限&所有者设置 +│ ├── Dockerfile.cli:优化版原镜像 +│ ├── Dockerfile.cli.alpine: 构建镜像 +│ ├── dpkg.cfg.excludes +│ ├── easyconnect.sh : 主逻辑 from [2][2] +│ ├── easyconn_resources_x64_7.6-378.tar.gz : ec资源包 packed from get_cli_resources.sh +│ ├── get_cli_resources.sh : 下载并打包ec资源包 +│ └── readme.md + +├── Dockerfile.alpine : alpine-glibc 基础镜像 +└── README.md + +## 使用 +### 获取镜像 +#### 自行打包 +``` +git clone --depth 1 github.com/ultranity/Easyconnect-alpine +cd Easyconnect-alpine/src +docker build --rm -t ec/alpine:cli -f Dockerfile.cli.alpine . +``` + +#### 下载导出包 +``` +wget https://github.com/ultranity/Easyconnect-alpine/releases/download/latest/uec.tar.gz -O uec.tar.gz +gunzip uec.tar.gz|docker load +rm uec.tar.gz +``` +### 启动 + +``` +docker run --device /dev/net/tun --cap-add NET_ADMIN -t -i -p 1080:1080 -e VERSION=7.6.8 -e CLI_OPTS="-d
-u -p " --name='ec' sangfor/easyconnect:cli +``` +以后只需要 `docker start ec` + + +#记录 +gcompact 可用性不如 alpine-pkg-glibc +minideb 比debian-slim小一点点(~2MB) +docker中chown会新增layer克隆文件导致镜像变大 + +glibc 2.35-r0 有bug缺少``*_chk`入口,待修复前使用2.34 + +# 参考致谢 +[1]: https://github.com/Hagb/docker-easyconnect +[2]: https://github.com/shmilee/scripts/tree/master/easyconnect-in-docker +[3]: https://github.com/sgerrand/alpine-pkg-glibc diff --git a/src/Dockerfile.cli b/src/Dockerfile.cli new file mode 100644 index 0000000..c88a3a1 --- /dev/null +++ b/src/Dockerfile.cli @@ -0,0 +1,35 @@ +# debian 10 buster +# https://hub.docker.com/_/debian/ + +FROM debian:buster-slim + +LABEL maintainer="shmilee.zju@gmail.com" \ + release.version="buster" \ + ec.versions="7.6.3 7.6.7 7.6.8" \ + description="buster with EasyConnect & run prerequisites" + +ENV LANG=C.UTF-8 LC_ALL=C.UTF-8 \ + DEBIAN_CODENAME=buster \ + DEBIAN_MIRROR=http://mirrors.163.com/debian + +COPY dpkg.cfg.excludes /etc/dpkg/dpkg.cfg.d/01_excludes +RUN echo "deb $DEBIAN_MIRROR $DEBIAN_CODENAME main contrib" > /etc/apt/sources.list \ + && echo "deb $DEBIAN_MIRROR $DEBIAN_CODENAME-updates main contrib" >> /etc/apt/sources.list \ + && apt-get update \ + && apt-get install -y --no-install-recommends --no-install-suggests \ + tini busybox iptables psmisc dante-server \ + && ln -s "$(which busybox)" /usr/local/bin/ps \ + && ln -s "$(which busybox)" /usr/local/bin/ip \ + && ln -s "$(which busybox)" /usr/local/bin/ifconfig \ + && ln -s "$(which busybox)" /usr/local/bin/route \ + && ln -s "$(which busybox)" /usr/local/bin/ping \ + && apt-get -y autoremove && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +ADD ./easyconn_resources_x64_7.6-378.tar.gz /usr/share/sangfor/EasyConnect/ +ADD ./easyconnect.sh /usr/bin/easyconnect.sh +# RUN chmod +x /usr/bin/easyconnect.sh && /usr/share/sangfor/EasyConnect/change_authority.sh + +ENTRYPOINT ["/usr/bin/tini", "--"] + +CMD ["easyconnect.sh", "2"] diff --git a/src/Dockerfile.cli.alpine b/src/Dockerfile.cli.alpine new file mode 100644 index 0000000..d598973 --- /dev/null +++ b/src/Dockerfile.cli.alpine @@ -0,0 +1,32 @@ +FROM alpine:latest + +LABEL maintainer="ultraxz@qq.com" \ + release.version="alpine:latest" \ + ec.versions="7.6.3 7.6.7 7.6.8 CLI" \ + description="alpine with EasyConnect CLI & run prerequisites" + +ENV LANG=C.UTF-8 LC_ALL=C.UTF-8 \ + LD_LIBRARY_PATH=/usr/local/lib:/usr/glibc-compat/lib:/usr/lib:/lib \ + PATH=/usr/glibc-compat/sbin:/usr/glibc-compat/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \ + GLIBC_REPO=https://github.com/sgerrand/alpine-pkg-glibc \ + GLIBC_VERSION=2.34-r0 + +RUN addgroup -S proxy && adduser -S -G proxy proxy +RUN sed -i "s/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g" /etc/apk/repositories \ + && sed -i "s/alpine.gliderlabs.com/mirrors.aliyun.com/g" /etc/apk/repositories \ + && apk update \ + && wget -q -O /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub \ + && for pkg in glibc-${GLIBC_VERSION} glibc-bin-${GLIBC_VERSION}; \ + do wget -q ${GLIBC_REPO}/releases/download/${GLIBC_VERSION}/${pkg}.apk -O /tmp/${pkg}.apk; done \ + && apk add /tmp/glibc*.apk \ + && rm -v /tmp/glibc*.apk \ + && apk add libstdc++ bash busybox iptables tini psmisc dante-server \ + && rm -rf /var/cache/apk/* + +ADD --chown=0:0 ./easyconn_resources_x64_7.6-378.tar.gz /usr/share/sangfor/EasyConnect/ +ADD --chown=0:0 ./easyconnect.sh /usr/bin/easyconnect.sh +# RUN chmod +x /usr/bin/easyconnect.sh && /usr/share/sangfor/EasyConnect/change_authority.sh + +ENTRYPOINT ["/sbin/tini", "--"] + +CMD ["easyconnect.sh", "2"] diff --git a/src/change_authority.sh b/src/change_authority.sh new file mode 100644 index 0000000..ca02b36 --- /dev/null +++ b/src/change_authority.sh @@ -0,0 +1,23 @@ +#!/bin/bash +#ResourcesDir=/usr/share/sangfor/EasyConnect/resources +ResourcesDir=./resources +usr=0 #root +#文件权限处理 +chmod +x ${ResourcesDir}/bin/easyconn +chmod +x ${ResourcesDir}/bin/ECAgent +chmod +x ${ResourcesDir}/bin/svpnservice +chmod +x ${ResourcesDir}/bin/CSClient +#保证logs文件夹存在 +mkdir -p ${ResourcesDir}/logs +chmod 777 ${ResourcesDir}/logs +###CSClient创建的域套接字的句柄在这, 加写权限 +chmod 777 ${ResourcesDir}/conf-v* -R +chmod +x ${ResourcesDir}/shell/* +#更改所有者 +chown ${usr}:${usr} ${ResourcesDir}/bin/ECAgent +chown ${usr}:${usr} ${ResourcesDir}/bin/svpnservice +chown ${usr}:${usr} ${ResourcesDir}/bin/CSClient +#添加s权限 +chmod +s ${ResourcesDir}/bin/ECAgent +chmod +s ${ResourcesDir}/bin/svpnservice +chmod +s ${ResourcesDir}/bin/CSClient diff --git a/src/dpkg.cfg.excludes b/src/dpkg.cfg.excludes new file mode 100644 index 0000000..67af1f1 --- /dev/null +++ b/src/dpkg.cfg.excludes @@ -0,0 +1,13 @@ +# put me /etc/dpkg/dpkg.cfg.d/01_excludes + +# Drop documents, keep copyright +path-exclude /usr/share/doc/* +path-include /usr/share/doc/*/copyright + +# Drop all manual pages +path-exclude /usr/share/man/* + +# Drop translations +path-exclude /usr/share/locale/* +path-include /usr/share/locale/en_US/* +path-include /usr/share/locale/locale.alias diff --git a/src/easyconn_resources_x64_7.6-378.tar.gz b/src/easyconn_resources_x64_7.6-378.tar.gz new file mode 100644 index 0000000..a38c85b Binary files /dev/null and b/src/easyconn_resources_x64_7.6-378.tar.gz differ diff --git a/src/easyconnect.sh b/src/easyconnect.sh new file mode 100644 index 0000000..bb4a3b2 --- /dev/null +++ b/src/easyconnect.sh @@ -0,0 +1,174 @@ +#!/bin/bash +# $1 wait for volume +# $2 path of hook_script.sh +sleep ${1:-5} + +## from deb postinst +EasyConnectDir=${EasyConnectDir:-/usr/share/sangfor/EasyConnect} +ResourcesDir=${EasyConnectDir}/resources +EASYCONN=${ResourcesDir}/bin/easyconn + +## run cmd in ${ResourcesDir}/bin +## from sslservice.sh EasyMonitor.sh +run_cmd() { + local cmd=$1 + local background=${2:-foreground} # background, foreground + local params="${@:3}" + if [ ! -f "${ResourcesDir}/bin/$cmd" ]; then + echo ">> '$cmd' not found in ${ResourcesDir}/bin!" + exit 21 + fi + pidof $cmd >/dev/null && killall $cmd + pidof $cmd >/dev/null && killall -9 $cmd + if [ x"$background" = "xbackground" ]; then + echo "Run CMD: ${ResourcesDir}/bin/$cmd $params &" + ${ResourcesDir}/bin/$cmd $params & + else + echo "Run CMD: ${ResourcesDir}/bin/$cmd $params" + ${ResourcesDir}/bin/$cmd $params + fi + if [ $? -eq 0 ]; then + echo "Start $cmd success!" + else + echo ">> Start $cmd fail" + exit 22 + fi +} + +## run CLI EC cmd easyconn +start_easyconn() { + local params="-v " + #[ -n "$ECADDRESS" ] && params+=" -d $ECADDRESS" + #[ -n "$ECUSER" ] && params+=" -u $ECUSER" + #[ -n "$ECPASSWD" ] && params+=" -p $ECPASSWD" + params+="$CLI_OPTS" + echo "Run CMD: $EASYCONN login $params" + $EASYCONN login $params && echo login success \ + || ($EASYCONN logout; sleep 3;$EASYCONN login $params;) +} + +## from github.com/Hagb/docker-easyconnect/ start.sh +hook_iptables() { #{{{ + local interface=${1:-tun0} + echo "Run hook_iptables" + # 不支持 nftables 时使用 iptables-legacy + # 感谢 @BoringCat https://github.com/Hagb/docker-easyconnect/issues/5 + if { [ -z "$IPTABLES_LEGACY" ] && iptables-nft -L 1>/dev/null 2>/dev/null ;} + then + update-alternatives --set iptables /sbin/iptables-nft + update-alternatives --set ip6tables /sbin/ip6tables-nft + else + update-alternatives --set iptables /sbin/iptables-legacy + update-alternatives --set ip6tables /sbin/ip6tables-legacy + fi + + # https://github.com/Hagb/docker-easyconnect/issues/20 + # https://serverfault.com/questions/302936/configuring-route-to-use-the-same-interface-for-outbound-traffic-as-that-of-inbo + iptables -t mangle -I OUTPUT -m state --state ESTABLISHED,RELATED -j CONNMARK --restore-mark + iptables -t mangle -I PREROUTING -m connmark ! --mark 0 -j CONNMARK --save-mark + iptables -t mangle -I PREROUTING -m connmark --mark 1 -j MARK --set-mark 1 + iptables -t mangle -I PREROUTING -i eth0 -j CONNMARK --set-mark 1 + ( + IFS=$'\n' + for i in $(ip route show); do + IFS=' ' + ip route add $i table 2 + done + ip rule add fwmark 1 table 2 + ) + + iptables -t nat -A POSTROUTING -o ${interface} -j MASQUERADE + + # 拒绝 interface tun0 侧主动请求的连接. + iptables -I INPUT -p tcp -j REJECT + iptables -I INPUT -i eth0 -p tcp -j ACCEPT + iptables -I INPUT -i lo -p tcp -j ACCEPT + iptables -I INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT + + # 删除深信服可能生成的一条 iptables 规则,防止其丢弃传出到宿主机的连接 + # 感谢 @stingshen https://github.com/Hagb/docker-easyconnect/issues/6 + ( while true; do sleep 5 ; iptables -D SANGFOR_VIRTUAL -j DROP 2>/dev/null ; done ) & +} #}}} + +## from github.com/Hagb/docker-easyconnect/ start.sh +hook_danted() { #{{{ + local interface=${1:-tun0} + echo "Run hook_danted" + cat >/etc/danted.conf </dev/null && killall sockd + pidof sockd >/dev/null && killall -9 sockd + (while true; do + sleep 3 + if [ -d /sys/class/net/${interface} ]; then + sockd -D -f /etc/danted.conf + echo "start dantd" + break + fi + done) & +} #}}} + +## use conf in resources/conf-v$VERSION +hook_resources_conf() { + if [ x"$VERSION" = x"7.6.3" ] || [ x"$VERSION" = x"7.6.7" ] || [ x"$VERSION" = x"7.6.8" ]; then + : + else + echo ">> Not supported EC version: $VERSION" + exit 51 + fi + echo "Run hook_resources_conf" + if [ ! -d "${ResourcesDir}/conf-v$VERSION" ]; then + echo ">> ${ResourcesDir}/conf-v$VERSION/ not found!" + exit 52 + fi + rm -f -v ${ResourcesDir}/conf + ln -sf -v conf-v$VERSION ${ResourcesDir}/conf + if [ -f /root/.easyconn ]; then + ln -sf -v /root/.easyconn ${ResourcesDir}/conf/.easyconn + fi +} + +## main +main() { + echo "Running default main ..." + hook_resources_conf + + [ -n "$IPTABLES_LEGACY" ] && hook_iptables tun0 # IPTABLES_LEGACY= + + run_cmd ECAgent background --resume + [ -n "$NODANTED" ] || (hook_danted tun0 && echo 'dantd') # -p xxx:1080 + start_easyconn + + $EASYCONN login query + + keep='K' + while [ x"$keep" != x'XXX' ]; do + read -p " -> Enter 'XXX' to exit:" keep + done + echo "Run CMD: ${EASYCONN} logout" + $EASYCONN logout +} + +## source hook script, add functions & reload change_authority, main etc. +hook_script="${2:-${EasyConnectDir}/hook_script.sh}" +if [ -f "$hook_script" ]; then + echo "source hook_script.sh ..." + source $hook_script +fi + +main diff --git a/src/get_cli_resources.sh b/src/get_cli_resources.sh new file mode 100644 index 0000000..b07d5b7 --- /dev/null +++ b/src/get_cli_resources.sh @@ -0,0 +1,85 @@ +#!/bin/bash +# Copyright (C) 2020 shmilee +# EC version to run, 7.6.3, 7.6.7, 7.6.8 + +ResourcesDir=/usr/share/sangfor/EasyConnect/resources +# EC deb cache dir, extract dir +tmpdir="../tmp" +DATAREPO="${1:-${tmpdir}/ECDATA}" +DATA_CLI="${2:-${tmpdir}/ECDATA_CLI}" + +download_extract() { + VERSION=$1 + # deb url + urlprefix='http://download.sangfor.com.cn/download/product/sslvpn/pkg' + debfile="${DATAREPO}/EasyConnect_x64_v$VERSION.deb" + if [ x"$VERSION" = x"7.6.3" ]; then + deburl="$urlprefix/linux_01/EasyConnect_x64.deb" + elif [ x"$VERSION" = x"7.6.7" ]; then + deburl="$urlprefix/linux_767/EasyConnect_x64_7_6_7_3.deb" + elif [ x"$VERSION" = x"7.6.8" ]; then + urlprefix='https://github.com/shmilee/scripts/releases/download/v0.0.1' + deburl="$urlprefix/easyconn_7.6.8.2-ubuntu_amd64.deb" + debfile="${DATAREPO}/easyconn_7.6.8.2-ubuntu_amd64.deb" + else + echo ">> Not supported EC version: $VERSION" + exit 1 + fi + # download deb + if [ ! -d "${DATAREPO}" ]; then + mkdir -pv "${DATAREPO}" + fi + if [ ! -f "${debfile}" ]; then + wget -c "${deburl}" -O "${debfile}" + fi + # extract deb + rm -rf ${tmpdir}/ec-tmp + mkdir ${tmpdir}/ec-tmp + #tar -v -x -f "${debfile}" -C ${tmpdir}/ec-tmp + #tar -v -x -f ${tmpdir}/ec-tmp/data.tar.?z -C ${tmpdir}/ec-tmp + dpkg -X "${debfile}" ${tmpdir}/ec-tmp +} + +add_common_data() { + download_extract '7.6.8' # download & extract 7.6.8 + if [ ! -d "${DATA_CLI}" ]; then + mkdir -pv "${DATA_CLI}"/resources/lib64 + fi + for d in user_cert shell logs lang bin; do + mv -v ${tmpdir}/ec-tmp/${ResourcesDir}/$d "${DATA_CLI}"/resources/$d + done + rm "${DATA_CLI}"/resources/bin/EasyMonitor + for so in libnspr4.so libnss3.so libnssutil3.so libplc4.so libplds4.so libsmime3.so; do + mv -v ${tmpdir}/ec-tmp/${ResourcesDir}/lib64/$so "${DATA_CLI}"/resources/lib64/$so + done + # conf -> conf-v7.6.8 + mv -v ${tmpdir}/ec-tmp/${ResourcesDir}/conf "${DATA_CLI}"/resources/conf-v7.6.8 +} + +add_other_conf() { + if [ ! -d "${DATA_CLI}" ]; then + mkdir -pv "${DATA_CLI}"/resources + fi + for ver in '7.6.3' '7.6.7'; do + if [ ! -d "${DATA_CLI}"/resources/conf-v$ver ]; then + download_extract $ver + mv -v ${tmpdir}/ec-tmp/${ResourcesDir}/conf "${DATA_CLI}"/resources/conf-v$ver + fi + done +} + +add_common_data +add_other_conf + + +old_PWD="$PWD" +cd "$DATA_CLI" + +echo -e '7.6.3\n7.6.7\n7.6.8' > ./support_versions +sudo ./change_authority.sh +tar czvf "${old_PWD}"/easyconn_resources_x64_7.6-378.tar.gz ./ --owner=root --group=root +cd ${old_PWD} +#cd ${tmpdir} +rm -rf ${tmpdir}/ec-tmp +#rm -r "$DATA_CLI" +#echo $DATA_CLI diff --git a/src/readme.md b/src/readme.md new file mode 100644 index 0000000..48317d6 --- /dev/null +++ b/src/readme.md @@ -0,0 +1,62 @@ +# 参考致谢 +https://github.com/Hagb/docker-easyconnect + +# build + +* [7.6.3](http://download.sangfor.com.cn/download/product/sslvpn/pkg/linux_01/EasyConnect_x64.deb) +* [7.6.7](http://download.sangfor.com.cn/download/product/sslvpn/pkg/linux_767/EasyConnect_x64_7_6_7_3.deb) +* [7.6.8](https://github.com/shmilee/scripts/releases/download/v0.0.1/easyconn_7.6.8.2-ubuntu_amd64.deb) + +```bash +./get_cli_resources.sh +export TAG=$(date +%y%m%d) +docker build --rm -t shmilee/easyconnect-cli:$TAG -f Dockerfile.cli . +``` + +``` +$ docker images +REPOSITORY TAG IMAGE ID CREATED SIZE +shmilee/easyconnect-cli 210307 b85eb30e8d2a 9 minutes ago 106MB +``` + +# run + +```bash +docker run --rm --device /dev/net/tun --cap-add NET_ADMIN -i -t \ + -p 127.0.0.1:3600:1080 \ + -e ECADDRESS=xxx.cn:443 \ + -e ECUSER=xxx \ + -e VERSION=7.6.7 \ + sangfor/easyconnect-cli:$TAG +# output +Running default main ... +Run hook_resources_conf +'/usr/share/sangfor/EasyConnect/resources/conf' -> 'conf-v7.6.7' +Run hook_danted +Run CMD: /usr/share/sangfor/EasyConnect/resources/bin/ECAgent --resume & +Start ECAgent success! +Run CMD: /usr/share/sangfor/EasyConnect/resources/bin/easyconn login -v -d xxx:443 -u xxx +No previous user logged in! +No previous user logged in! +vpn adress: vpn.xxx.cn:443 +Get https://vpn.xxx.cn:443/por/login_auth.csp ... +Get https://vpn.xxx.cn:443/por/login_auth.csp Done, code=200 +Cipher Suite: AES128-SHA +Begin detect listen port of ECAgent ... +Read listen port of ECAgent from file: 54530 +Done detect listen port of ECAgent, result: 54530! +Get https://127.0.0.1:54530/ECAgent ... +Get https://127.0.0.1:54530/ECAgent Done, code=200 +password: xxxx +Authenticating user "xxxx" by password ... +Post https://vpn.xxx.cn:443/por/login_psw.csp ... +Post https://vpn.xxx.cn:443/por/login_psw.csp Done, code=200 +Get https://127.0.0.1:54530/ECAgent ... +Get https://127.0.0.1:54530/ECAgent Done, code=200 +Get https://127.0.0.1:54530/ECAgent ... +Get https://127.0.0.1:54530/ECAgent Done, code=200 +user "xxx" login successfully! + -> Enter 'XXX' to exit:XXX +Run CMD: /usr/share/sangfor/EasyConnect/resources/bin/easyconn logout +user "xxx" is already logged out! +```