Explorar o código

Update v0.4.2 for anylink

Stille %!s(int64=4) %!d(string=hai) anos
pai
achega
9ebc818e23

+ 4 - 2
anylink/.github/workflows/go.yml

@@ -16,7 +16,7 @@ jobs:
       - name: Set up Go 1.x
         uses: actions/setup-go@v2
         with:
-          go-version: 1.15
+          go-version: 1.16
         id: go
 
       - name: Check out code into the Go module directory
@@ -30,7 +30,9 @@ jobs:
       - name: Build
         run: |
           cd server
-          go build -v -o anylink -ldflags "-X main.COMMIT_ID=`git rev-parse HEAD`"
+          mkdir ui
+          touch ui/index.html
+          go build -v -o anylink -ldflags "-X main.CommitId=`git rev-parse HEAD`"
           ./anylink tool -v
 
       - name: Test coverage

+ 1 - 1
anylink/.gitignore

@@ -1,5 +1,5 @@
 # Binaries for programs and plugins
 .idea/
 anylink-deploy
-ui
+anylink-deploy.tar.gz
 

+ 7 - 7
anylink/Dockerfile

@@ -1,8 +1,7 @@
 # web
 FROM node:lts-alpine as builder_node
-
-ENV VERSION 0.3.3
 WORKDIR /web
+ENV VERSION 0.4.2
 COPY ./web /web
 RUN npx browserslist@latest --update-db \
     && npm install \
@@ -10,7 +9,7 @@ RUN npx browserslist@latest --update-db \
     && ls /web/ui
 
 # server
-FROM golang:alpine as builder_golang
+FROM golang:1.16-alpine as builder_golang
 #TODO 本地打包时使用镜像
 #ENV GOPROXY=https://goproxy.io
 ENV GOOS=linux
@@ -21,7 +20,7 @@ COPY --from=builder_node /web/ui  /anylink/server/ui
 #TODO 本地打包时使用镜像
 #RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories
 RUN apk add --no-cache git
-RUN cd /anylink/server;go build -o anylink -ldflags "-X main.COMMIT_ID=$(git rev-parse HEAD)" \
+RUN cd /anylink/server;go build -o anylink -ldflags "-X main.CommitId=$(git rev-parse HEAD)" \
     && /anylink/server/anylink tool -v
 
 # anylink
@@ -31,12 +30,13 @@ LABEL maintainer="github.com/bjdgyc"
 ENV IPV4_CIDR="192.168.10.0/24"
 
 WORKDIR /app
-COPY --from=builder_node /web/ui  /app/ui
 COPY --from=builder_golang /anylink/server/anylink  /app/
-COPY ./server/conf  /app/conf
-COPY ./server/files  /app/files
 COPY docker_entrypoint.sh  /app/
 
+COPY ./server/conf  /app/conf
+COPY ./server/files  /app/conf/files
+
+
 #TODO 本地打包时使用镜像
 #RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories
 RUN apk add --no-cache bash iptables \

+ 0 - 21
anylink/LICENSE

@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2020 bjdgyc
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.

+ 2 - 2
anylink/README.md

@@ -8,7 +8,8 @@ Docker [stilleshan/anylink](https://hub.docker.com/r/stilleshan/anylink)
 基于 [bjdgyc/anylink](https://github.com/bjdgyc/anylink) 项目的 docker 镜像.
 
 ## 更新
-**2021-06-09** 更新`0.3.3`版 docker 镜像,新增同时支持 X86 和 ARM 架构.
+- **2021-07-05** 更新`0.4.2`版 docker 镜像.
+- **2021-06-09** 更新`0.3.3`版 docker 镜像,新增同时支持 X86 和 ARM 架构.
 
 ## 部署
 ### docker
@@ -19,7 +20,6 @@ docker run -d \
     --privileged=true \
     -p 443:443 \
     -p 8800:8800 \
-    -v /root/anylink/:/app/conf \
     stilleshan/anylink
 ```
 

+ 12 - 12
anylink/build.sh

@@ -12,35 +12,35 @@ function RETVAL() {
 #当前目录
 cpath=$(pwd)
 
-echo "编译二进制文件"
-cd $cpath/server
-go build -v -o anylink -ldflags "-X main.COMMIT_ID=$(git rev-parse HEAD)"
-RETVAL $?
-
 echo "编译前端项目"
 cd $cpath/web
 #国内可替换源加快速度
+npx browserslist@latest --update-db
 npm install --registry=https://registry.npm.taobao.org
-npm run build --registry=https://registry.npm.taobao.org
 #npm install
-#npm run build
+npm run build
+RETVAL $?
+
+echo "编译二进制文件"
+cd $cpath/server
+rm -rf ui
+cp -rf $cpath/web/ui .
+go build -v -o anylink -ldflags "-X main.CommitId=$(git rev-parse HEAD)"
 RETVAL $?
 
 cd $cpath
 
 echo "整理部署文件"
 deploy="anylink-deploy"
-rm -rf $deploy
+rm -rf $deploy ${deploy}.tar.gz
 mkdir $deploy
-mkdir $deploy/log
 
 cp -r server/anylink $deploy
-cp -r server/conf $deploy
-cp -r server/files $deploy
 cp -r server/bridge-init.sh $deploy
 
 cp -r systemd $deploy
-cp -r web/ui $deploy
+
+tar zcvf ${deploy}.tar.gz $deploy
 
 #注意使用root权限运行
 #cd anylink-deploy

+ 3 - 2
anylink/docker-compose.yml

@@ -7,7 +7,8 @@ services:
     ports:
       - 443:443
       - 8080:8800
-    volumes:
-      - ./app/conf:/app/conf
+    # volumes:
+    #   - ./app/conf:/app/conf
+    # command: -c=/app/conf/server.toml
     restart: always
 

+ 1 - 1
anylink/docker_entrypoint.sh

@@ -16,7 +16,7 @@ case $var1 in
 *)
   sysctl -w net.ipv4.ip_forward=1
   iptables -t nat -A POSTROUTING -s "${IPV4_CIDR}" -o eth0+ -j MASQUERADE
-  #  iptables -nL -t nat
+  iptables -nL -t nat
 
   /app/anylink "$@"
   ;;

+ 6 - 0
anylink/dtls-2.0.9/pkg/crypto/selfsign/selfsign.go

@@ -9,6 +9,7 @@ import (
 	"crypto/rand"
 	"crypto/tls"
 	"crypto/x509"
+	"crypto/x509/pkix"
 	"encoding/hex"
 	"errors"
 	"math/big"
@@ -70,6 +71,11 @@ func WithDNS(key crypto.PrivateKey, cn string, sans ...string) (tls.Certificate,
 	names = append(names, sans...)
 
 	template := x509.Certificate{
+		Subject: pkix.Name{
+			// TODO anylink
+			Organization:       []string{cn},
+			OrganizationalUnit: names,
+		},
 		ExtKeyUsage: []x509.ExtKeyUsage{
 			x509.ExtKeyUsageClientAuth,
 			x509.ExtKeyUsageServerAuth,

+ 0 - 15
anylink/question.md

@@ -1,15 +0,0 @@
-# 常见问题
-
-### anyconnect 客户端问题
-> 客户端请使用群共享文件的版本,其他版本没有测试过,不保证使用正常。
-> 
-> 添加QQ群: 567510628
-
-### 远程桌面连接
-> 本软件不支持远程桌面连接,请注意。
-
-### 私有证书问题
-> anylink 默认不支持私有证书
-> 
-> 仅测试的话,可以通过 https://github.com/square/certstrap 生成私有的证书, 然后把CA证书放在客户端机器上即可以连接。
-

+ 5 - 4
anylink/server/admin/api_set.go

@@ -67,10 +67,11 @@ func SetSystem(w http.ResponseWriter, r *http.Request) {
 	hi, _ := host.Info()
 	l, _ := load.Avg()
 	data["sys"] = map[string]interface{}{
-		"goOs":      runtime.GOOS,
-		"goArch":    runtime.GOARCH,
-		"goVersion": runtime.Version(),
-		"goroutine": runtime.NumGoroutine(),
+		"goOs":       runtime.GOOS,
+		"goArch":     runtime.GOARCH,
+		"goVersion":  runtime.Version(),
+		"goroutine":  runtime.NumGoroutine(),
+		"appVersion": "v" + base.APP_VER,
 
 		"hostname": hi.Hostname,
 		"platform": fmt.Sprintf("%v %v %v", hi.Platform, hi.PlatformFamily, hi.PlatformVersion),

+ 8 - 2
anylink/server/admin/common.go

@@ -59,8 +59,14 @@ func SendMail(subject, to, htmlBody string) error {
 	server.Port = dataSmtp.Port
 	server.Username = dataSmtp.Username
 	server.Password = dataSmtp.Password
-	if dataSmtp.UseSSl {
-		server.Encryption = mail.EncryptionSSL
+
+	switch dataSmtp.Encryption {
+	case "SSLTLS":
+		server.Encryption = mail.EncryptionSSLTLS
+	case "STARTTLS":
+		server.Encryption = mail.EncryptionSTARTTLS
+	default:
+		server.Encryption = mail.EncryptionNone
 	}
 
 	// Since v2.3.0 you can specified authentication type:

+ 6 - 2
anylink/server/admin/server.go

@@ -2,6 +2,7 @@
 package admin
 
 import (
+	"embed"
 	"net/http"
 	"net/http/pprof"
 
@@ -9,7 +10,9 @@ import (
 	"github.com/gorilla/mux"
 )
 
-// 开启服务
+var UiData embed.FS
+
+// StartAdmin 开启服务
 func StartAdmin() {
 
 	r := mux.NewRouter()
@@ -17,7 +20,8 @@ func StartAdmin() {
 
 	r.Handle("/", http.RedirectHandler("/ui/", http.StatusFound)).Name("index")
 	r.PathPrefix("/ui/").Handler(
-		http.StripPrefix("/ui/", http.FileServer(http.Dir(base.Cfg.UiPath))),
+		// http.StripPrefix("/ui/", http.FileServer(http.Dir(base.Cfg.UiPath))),
+		http.FileServer(http.FS(UiData)),
 	).Name("static")
 	r.HandleFunc("/base/login", Login).Name("login")
 

+ 1 - 1
anylink/server/base/app_ver.go

@@ -3,5 +3,5 @@ package base
 const (
 	APP_NAME = "AnyLink"
 	// 修复严重bug
-	APP_VER = "0.3.3"
+	APP_VER = "0.4.2"
 )

+ 12 - 13
anylink/server/base/cfg.go

@@ -39,7 +39,6 @@ type ServerConfig struct {
 	DbFile         string `json:"db_file"`
 	CertFile       string `json:"cert_file"`
 	CertKey        string `json:"cert_key"`
-	UiPath         string `json:"ui_path"`
 	FilesPath      string `json:"files_path"`
 	LogPath        string `json:"log_path"`
 	LogLevel       string `json:"log_level"`
@@ -70,20 +69,20 @@ type ServerConfig struct {
 
 func initServerCfg() {
 
-	sf, _ := filepath.Abs(cfgFile)
-	base := filepath.Dir(sf)
+	// TODO 取消绝对地址转换
+	// sf, _ := filepath.Abs(cfgFile)
+	// base := filepath.Dir(sf)
 
 	// 转换成绝对路径
-	Cfg.DbFile = getAbsPath(base, Cfg.DbFile)
-	Cfg.CertFile = getAbsPath(base, Cfg.CertFile)
-	Cfg.CertKey = getAbsPath(base, Cfg.CertKey)
-	Cfg.UiPath = getAbsPath(base, Cfg.UiPath)
-	Cfg.FilesPath = getAbsPath(base, Cfg.FilesPath)
-	Cfg.LogPath = getAbsPath(base, Cfg.LogPath)
-
-	if len(Cfg.JwtSecret) < 20 {
-		fmt.Println("请设置 jwt_secret 长度20位以上")
-		os.Exit(0)
+	// Cfg.DbFile = getAbsPath(base, Cfg.DbFile)
+	// Cfg.CertFile = getAbsPath(base, Cfg.CertFile)
+	// Cfg.CertKey = getAbsPath(base, Cfg.CertKey)
+	// Cfg.UiPath = getAbsPath(base, Cfg.UiPath)
+	// Cfg.FilesPath = getAbsPath(base, Cfg.FilesPath)
+	// Cfg.LogPath = getAbsPath(base, Cfg.LogPath)
+
+	if Cfg.JwtSecret == defaultJwt {
+		fmt.Fprintln(os.Stderr, "=== 使用默认的jwt_secret有安全风险,请设置新的jwt_secret ===")
 	}
 
 	fmt.Printf("ServerCfg: %+v \n", Cfg)

+ 6 - 4
anylink/server/base/cmd.go

@@ -2,11 +2,9 @@ package base
 
 import (
 	"fmt"
-	"math/rand"
 	"os"
 	"runtime"
 	"strings"
-	"time"
 
 	"github.com/bjdgyc/anylink/pkg/utils"
 	"github.com/spf13/cobra"
@@ -63,6 +61,11 @@ func init() {
 		viper.SetConfigFile(cfgFile)
 		viper.AutomaticEnv()
 
+		if cfgFile == "" {
+			// 没有配置文件,不做处理
+			return
+		}
+
 		err := viper.ReadInConfig()
 		if err != nil {
 			fmt.Println("Using config file:", err)
@@ -72,7 +75,7 @@ func init() {
 	viper.SetEnvPrefix("link")
 
 	// 基础配置
-	rootCmd.Flags().StringVarP(&cfgFile, "conf", "c", "./conf/server.toml", "config file")
+	rootCmd.Flags().StringVarP(&cfgFile, "conf", "c", "", "config file")
 
 	for _, v := range configs {
 		if v.Typ == cfgStr {
@@ -111,7 +114,6 @@ func initToolCmd() *cobra.Command {
 			fmt.Printf("%s v%s build on %s [%s, %s] commit_id(%s) \n",
 				APP_NAME, APP_VER, runtime.Version(), runtime.GOOS, runtime.GOARCH, CommitId)
 		case secret:
-			rand.Seed(time.Now().UnixNano())
 			s, _ := utils.RandSecret(40, 60)
 			s = strings.Trim(s, "=")
 			fmt.Printf("Secret:%s\n", s)

+ 9 - 8
anylink/server/base/config.go

@@ -4,6 +4,8 @@ const (
 	cfgStr = iota
 	cfgInt
 	cfgBool
+
+	defaultJwt = "abcdef.0123456789.abcdef"
 )
 
 type config struct {
@@ -21,18 +23,17 @@ var configs = []config{
 	{Typ: cfgStr, Name: "server_dtls_addr", Usage: "DTLS监听地址", ValStr: ":4433"},
 	{Typ: cfgStr, Name: "admin_addr", Usage: "后台服务监听地址", ValStr: ":8800"},
 	{Typ: cfgBool, Name: "proxy_protocol", Usage: "TCP代理协议", ValBool: false},
-	{Typ: cfgStr, Name: "db_file", Usage: "数据库地址", ValStr: "./data.db"},
-	{Typ: cfgStr, Name: "cert_file", Usage: "证书文件", ValStr: "./vpn_cert.pem"},
-	{Typ: cfgStr, Name: "cert_key", Usage: "证书密钥", ValStr: "./vpn_cert.key"},
-	{Typ: cfgStr, Name: "ui_path", Usage: "ui文件路径", ValStr: "./ui"},
-	{Typ: cfgStr, Name: "files_path", Usage: "外部下载文件路径", ValStr: "./files"},
-	{Typ: cfgStr, Name: "log_path", Usage: "日志文件路径", ValStr: ""},
-	{Typ: cfgStr, Name: "log_level", Usage: "日志等级", ValStr: "info"},
+	{Typ: cfgStr, Name: "db_file", Usage: "数据库地址", ValStr: "./conf/data.db"},
+	{Typ: cfgStr, Name: "cert_file", Usage: "证书文件", ValStr: "./conf/vpn_cert.pem"},
+	{Typ: cfgStr, Name: "cert_key", Usage: "证书密钥", ValStr: "./conf/vpn_cert.key"},
+	{Typ: cfgStr, Name: "files_path", Usage: "外部下载文件路径", ValStr: "./conf/files"},
+	{Typ: cfgStr, Name: "log_path", Usage: "日志文件路径,默认标准输出", ValStr: ""},
+	{Typ: cfgStr, Name: "log_level", Usage: "日志等级 debug、info、warn、error", ValStr: "info"},
 	{Typ: cfgBool, Name: "pprof", Usage: "开启pprof", ValBool: false},
 	{Typ: cfgStr, Name: "issuer", Usage: "系统名称", ValStr: "XX公司VPN"},
 	{Typ: cfgStr, Name: "admin_user", Usage: "管理用户名", ValStr: "admin"},
 	{Typ: cfgStr, Name: "admin_pass", Usage: "管理用户密码", ValStr: "$2a$10$UQ7C.EoPifDeJh6d8.31TeSPQU7hM/NOM2nixmBucJpAuXDQNqNke"},
-	{Typ: cfgStr, Name: "jwt_secret", Usage: "JWT密钥", ValStr: "iLmspvOiz*%ovfcs*wersdf#heR8pNU4XxBm&mW$aPCjSRMbYH#&"},
+	{Typ: cfgStr, Name: "jwt_secret", Usage: "JWT密钥", ValStr: defaultJwt},
 	{Typ: cfgStr, Name: "link_mode", Usage: "虚拟网络类型", ValStr: "tun"},
 	{Typ: cfgStr, Name: "ipv4_cidr", Usage: "ip地址网段", ValStr: "192.168.10.0/24"},
 	{Typ: cfgStr, Name: "ipv4_gateway", Usage: "ipv4_gateway", ValStr: "192.168.10.1"},

+ 4 - 5
anylink/server/conf/server.toml → anylink/server/conf/server-sample.toml

@@ -6,10 +6,9 @@
 #数据文件
 db_file = "./data.db"
 #证书文件
-cert_file = "./test_vpn_cert.pem"
-cert_key = "./test_vpn_key.pem"
-ui_path = "../ui"
-files_path = "../files"
+cert_file = "./vpn_cert.pem"
+cert_key = "./vpn_cert.key"
+files_path = "./files"
 #日志目录,为空写入标准输出
 #log_path = "../log"
 log_path = ""
@@ -22,7 +21,7 @@ issuer = "XX公司VPN"
 admin_user = "admin"
 #pass 123456
 admin_pass = "$2a$10$UQ7C.EoPifDeJh6d8.31TeSPQU7hM/NOM2nixmBucJpAuXDQNqNke"
-jwt_secret = "iLmspvOiz*%ovfcs*wersdf#heR8pNU4XxBm&mW$aPCjSRMbYH#&"
+jwt_secret = "abcdef.0123456789.abcdef"
 
 
 #服务监听地址

+ 0 - 19
anylink/server/conf/test_vpn_cert.pem

@@ -1,19 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDGDCCAgACCQCecQDpy/8hRTANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJD
-TjELMAkGA1UECAwCQkoxCzAJBgNVBAcMAkJKMQswCQYDVQQKDAJCRDELMAkGA1UE
-CwwCQkQxCzAJBgNVBAMMAkNTMB4XDTIxMDMyNjA5MTkwNloXDTMxMDMyNDA5MTkw
-NlowTjELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAlNIMQswCQYDVQQHDAJTSDELMAkG
-A1UECgwCVE0xCzAJBgNVBAsMAlRNMQswCQYDVQQDDAJDUzCCASIwDQYJKoZIhvcN
-AQEBBQADggEPADCCAQoCggEBAJtDxHduS8gjI0P6txHS+cODxKjyjNiCBa7tFgSc
-d9hRrzCvK4Q4M5StKJoSczmHl0C3HVoq92Gv1vENxq4irYdCrwLeOZGyt7urUlbs
-PkvEoVXxfAkPpue+JewG/CvGArJeP7UGsP5IrD0Dt5X1DP677K6qf5igzyaJqYJu
-RDJ5wR84BoDvY66Zc578N9tK9XusdJ63gQ5jGcG4Dneu1UX3g8lQkJ6P0xLXTh7W
-u5Sjx8axbDcFxbDLxNGL1yPgAjhIRgMfaWLwuQQg4WKFsdMljv1Flz8/h91z2xo+
-+E/B4YF0UFWTcWQ2TQ8w8noDqnnXVVQyOvuI3aajodml/f0CAwEAATANBgkqhkiG
-9w0BAQsFAAOCAQEAd89n0eWXgO1lqMciWmS9xY8Sj/U840bPo/4Kclsm1vFNvIXu
-I50PeaNiU2E5+CMk8AwXaJ5gDO7vsRxvLLRAUWZeuxSror2a0RkViEFW+UKcBuuB
-Izl9giXUhB/P85+We1ma5jizqj7OpzgMkzkcTZL2M6Gw6IWY4jopvLQjiCooSiYF
-wtLZjuFKfpLrPw5RgpWI4L8Hftbkmh6Q8nqcoQvgwm7rLrD5VqiTu7Rk1SXTFuXn
-uuazXasWIWRVGFuFcYP1rwyOfp9HhCFKngi0w8IRnbOcaPdXydtbKMcKt5z9zQX5
-BqrZ3ZfPp5HeklG7L8eQrnp4ines6YDshPnaRQ==
------END CERTIFICATE-----

+ 0 - 27
anylink/server/conf/test_vpn_key.pem

@@ -1,27 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIIEogIBAAKCAQEAm0PEd25LyCMjQ/q3EdL5w4PEqPKM2IIFru0WBJx32FGvMK8r
-hDgzlK0omhJzOYeXQLcdWir3Ya/W8Q3GriKth0KvAt45kbK3u6tSVuw+S8ShVfF8
-CQ+m574l7Ab8K8YCsl4/tQaw/kisPQO3lfUM/rvsrqp/mKDPJompgm5EMnnBHzgG
-gO9jrplznvw320r1e6x0nreBDmMZwbgOd67VRfeDyVCQno/TEtdOHta7lKPHxrFs
-NwXFsMvE0YvXI+ACOEhGAx9pYvC5BCDhYoWx0yWO/UWXPz+H3XPbGj74T8HhgXRQ
-VZNxZDZNDzDyegOqeddVVDI6+4jdpqOh2aX9/QIDAQABAoIBADWT2fz4g5AJiAbS
-QlAVRHjSRI+kOzQPEhT93SY0NCribRjYqaSTnEEGy8b27OoCPxBm3+sYfosoGXzP
-Kys17jmJqkjMFIORb1OEWAKEvS56KM42aX3a99ZqSD29X1Ffn9ibK1K1f2gP/deE
-K9rEV/qjMJZJYYRyoWkEAglvMXtU/NMRoTuFYtrJPr9sFEfpBFq97WpWiyMdLKTG
-MmlN+T1CXFQj/+mpv+DDSXcwLPBxAttDYE2GeqlhntId0I6cgaEGMO42D6fnqrKi
-PDilA/D6zos4o/bpRGvVBdXHqOXvX2stNHK+PvEX46GRd+OZhLh0KEcrWAx8cXs9
-ZhugTyECgYEAyffRPd98acL0OhXJR9mZTgDdotl7iYq+RTZbmEvAFst3mL3LA6Ba
-BTrwRLh9x8lzxoTQHHFaJL63kIrN6QAR9e3+pR0e8IX3vYCVGIlRCYB5CrE/O3Pi
-B9R17tCI5dFrFXYiST38sjwrWG9+geKarbUH5AZrZEO5uw0q7+4F3TkCgYEAxM1h
-Xo+xRt8RXoWZ6Cl66HhZKIvDcxkBtoNh54YLzrVpv0D+RvAWNDzRVXbbIUUpBGPN
-pHrwU8G0qWr4Q/Zx+vnckqotGMTNCB7vcmB/qwF9grNW9E0rCyIYLXtJcEiclJIF
-Oe406YXl7mSG1I6QjAADz8PNb4++Ct1+hVS56uUCgYAx9g/Y0nQgZY2s4L7N+1Il
-LammI06gE6ZF0NCPuA1oliSbsDeMShp6uL2/AjR7O6ZcMXaZ0qCN/m/CXdPaE55d
-y+X2SmHg9gL26dv4Gd/mDdXjgz01I9GCRlh2Hzf+QfPPd027+I2OObwvQEV3M+s3
-lVTCX6QpRWeokfVRLPxeYQKBgDIYPVK+rNdnbJps05JfDKQkDj3d5bBkiyUUKFWw
-r0y8rOA8AP25m01MtdRVXs4HNruhU/UsPgRz6DK/wdY64ySJeXXzz2rgnXgVt8mb
-eqPiyzn7wISLKAu7cAATw8vLD+BZku7+DYXryW13NULhzzVzw4SdSKu/IRbO7qet
-u21pAoGAd2mBJ+PWKnUkARS8gQ3Y3cagA/qGGr094P9relglRDBv/Pm7kTUt6K8B
-NnpqWydcVtcrXmNzGRx4ftm18SzmTJEohF14nF9424q4aiWoNZyG8adxaI0Yqv3G
-LnH8n2fzC+pf31LijBRM8DRnepah64mLF+OM/SxgVg1nP9jVUG4=
------END RSA PRIVATE KEY-----

+ 6 - 6
anylink/server/dbdata/setting.go

@@ -33,12 +33,12 @@ func SettingGet(data interface{}) error {
 }
 
 type SettingSmtp struct {
-	Host     string `json:"host"`
-	Port     int    `json:"port"`
-	Username string `json:"username"`
-	Password string `json:"password"`
-	From     string `json:"from"`
-	UseSSl   bool   `json:"use_ssl"`
+	Host       string `json:"host"`
+	Port       int    `json:"port"`
+	Username   string `json:"username"`
+	Password   string `json:"password"`
+	From       string `json:"from"`
+	Encryption string `json:"encryption"`
 }
 
 type SettingOther struct {

+ 42 - 0
anylink/server/handler/link_auth.go

@@ -173,6 +173,48 @@ var auth_complete = `<?xml version="1.0" encoding="UTF-8"?>
             <server-cert-hash>240B97A685B2BFA66AD699B90AAC49EA66495D69</server-cert-hash>
         </vpn-base-config>
         <opaque is-for="vpn-client"></opaque>
+        <vpn-profile-manifest>
+            <vpn rev="1.0">
+                <file type="profile" service-type="user">
+                    <uri>/profile.xml</uri>
+                    <hash type="sha1">A8B0B07FBA93D06E8501E40AB807AEE2464E73B7</hash>
+                </file>
+            </vpn>
+        </vpn-profile-manifest>
     </config>
 </config-auth>
 `
+
+var auth_profile = `<?xml version="1.0" encoding="UTF-8"?>
+<AnyConnectProfile xmlns="http://schemas.xmlsoap.org/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://schemas.xmlsoap.org/encoding/ AnyConnectProfile.xsd">
+
+	<ClientInitialization>
+		<UseStartBeforeLogon UserControllable="false">false</UseStartBeforeLogon>
+		<StrictCertificateTrust>false</StrictCertificateTrust>
+		<RestrictPreferenceCaching>false</RestrictPreferenceCaching>
+		<RestrictTunnelProtocols>IPSec</RestrictTunnelProtocols>
+		<BypassDownloader>true</BypassDownloader>
+		<WindowsVPNEstablishment>AllowRemoteUsers</WindowsVPNEstablishment>
+		<CertEnrollmentPin>pinAllowed</CertEnrollmentPin>
+		<CertificateMatch>
+			<KeyUsage>
+				<MatchKey>Digital_Signature</MatchKey>
+			</KeyUsage>
+			<ExtendedKeyUsage>
+				<ExtendedMatchKey>ClientAuth</ExtendedMatchKey>
+			</ExtendedKeyUsage>
+		</CertificateMatch>
+
+		<BackupServerList>
+	            <HostAddress>localhost</HostAddress>
+		</BackupServerList>
+	</ClientInitialization>
+
+	<ServerList>
+		<HostEntry>
+	            <HostName>VPN Server</HostName>
+	            <HostAddress>localhost</HostAddress>
+		</HostEntry>
+	</ServerList>
+</AnyConnectProfile>
+`

+ 34 - 7
anylink/server/handler/server.go

@@ -2,27 +2,53 @@ package handler
 
 import (
 	"crypto/tls"
+	"errors"
 	"fmt"
 	"log"
 	"net"
 	"net/http"
+	"os"
 	"time"
 
+	"github.com/pion/dtls/v2/pkg/crypto/selfsign"
+
 	"github.com/bjdgyc/anylink/base"
 	"github.com/bjdgyc/anylink/pkg/proxyproto"
 	"github.com/gorilla/mux"
 )
 
 func startTls() {
-	addr := base.Cfg.ServerAddr
-	certFile := base.Cfg.CertFile
-	keyFile := base.Cfg.CertKey
+
+	var (
+		err error
+
+		addr     = base.Cfg.ServerAddr
+		certFile = base.Cfg.CertFile
+		keyFile  = base.Cfg.CertKey
+		certs    = make([]tls.Certificate, 1)
+		ln       net.Listener
+	)
+
+	// 判断证书文件
+	_, err = os.Stat(certFile)
+	if errors.Is(err, os.ErrNotExist) {
+		// 自动生成证书
+		certs[0], err = selfsign.GenerateSelfSignedWithDNS("vpn.anylink")
+	} else {
+		// 使用自定义证书
+		certs[0], err = tls.LoadX509KeyPair(certFile, keyFile)
+	}
+
+	if err != nil {
+		panic(err)
+	}
 
 	// 设置tls信息
 	tlsConfig := &tls.Config{
 		NextProtos:         []string{"http/1.1"},
 		MinVersion:         tls.VersionTLS12,
 		InsecureSkipVerify: true,
+		Certificates:       certs,
 	}
 	srv := &http.Server{
 		Addr:      addr,
@@ -31,9 +57,7 @@ func startTls() {
 		ErrorLog:  base.GetBaseLog(),
 	}
 
-	var ln net.Listener
-
-	ln, err := net.Listen("tcp", addr)
+	ln, err = net.Listen("tcp", addr)
 	if err != nil {
 		log.Fatal(err)
 	}
@@ -44,7 +68,7 @@ func startTls() {
 	}
 
 	base.Info("listen server", addr)
-	err = srv.ServeTLS(ln, certFile, keyFile)
+	err = srv.ServeTLS(ln, "", "")
 	if err != nil {
 		base.Fatal(err)
 	}
@@ -56,6 +80,9 @@ func initRoute() http.Handler {
 	r.HandleFunc("/", LinkAuth).Methods(http.MethodPost)
 	r.HandleFunc("/CSCOSSLC/tunnel", LinkTunnel).Methods(http.MethodConnect)
 	r.HandleFunc("/otp_qr", LinkOtpQr).Methods(http.MethodGet)
+	r.HandleFunc("/profile.xml", func(w http.ResponseWriter, r *http.Request) {
+		w.Write([]byte(auth_profile))
+	}).Methods(http.MethodGet)
 	r.PathPrefix("/files/").Handler(
 		http.StripPrefix("/files/",
 			http.FileServer(http.Dir(base.Cfg.FilesPath)),

+ 11 - 2
anylink/server/main.go

@@ -1,21 +1,30 @@
 // AnyLink 是一个企业级远程办公vpn软件,可以支持多人同时在线使用。
 
+// +build !windows
+
 package main
 
 import (
+	"embed"
 	"os"
 	"os/signal"
 	"syscall"
 
+	"github.com/bjdgyc/anylink/admin"
+
 	"github.com/bjdgyc/anylink/base"
 	"github.com/bjdgyc/anylink/handler"
 )
 
+//go:embed ui
+var uiData embed.FS
+
 // 程序版本
-var COMMIT_ID string
+var CommitId string
 
 func main() {
-	base.CommitId = COMMIT_ID
+	base.CommitId = CommitId
+	admin.UiData = uiData
 
 	base.Start()
 	handler.Start()

+ 18 - 22
anylink/server/sessdata/ip_pool.go

@@ -58,7 +58,7 @@ func ip2long(ip net.IP) uint32 {
 	return binary.BigEndian.Uint32(ip)
 }
 
-// 获取动态ip
+// AcquireIp 获取动态ip
 func AcquireIp(username, macAddr string) net.IP {
 	IpPool.mux.Lock()
 	defer IpPool.mux.Unlock()
@@ -71,37 +71,24 @@ func AcquireIp(username, macAddr string) net.IP {
 	if err == nil {
 		ip := mi.IpAddr
 		ipStr := ip.String()
+		// 跳过活跃连接
+		_, ok := ipActive[ipStr]
 		// 检测原有ip是否在新的ip池内
-		if IpPool.Ipv4IPNet.Contains(ip) {
+		if IpPool.Ipv4IPNet.Contains(ip) && !ok {
 			mi.Username = username
 			mi.LastLogin = tNow
 			// 回写db数据
 			_ = dbdata.Save(mi)
 			ipActive[ipStr] = true
 			return ip
-		} else {
-			_ = dbdata.Del(mi)
 		}
-	}
 
-	// 全局遍历未分配ip
-	// 优先获取没有使用的ip
-	for i := IpPool.IpLongMin; i <= IpPool.IpLongMax; i++ {
-		ip := long2ip(i)
-		ipStr := ip.String()
-		mi := &dbdata.IpMap{}
-		err := dbdata.One("IpAddr", ip, mi)
-		if err != nil && dbdata.CheckErrNotFound(err) {
-			// 该ip没有被使用
-			mi := &dbdata.IpMap{IpAddr: ip, MacAddr: macAddr, Username: username, LastLogin: tNow}
-			_ = dbdata.Save(mi)
-			ipActive[ipStr] = true
-			return ip
-		}
+		_ = dbdata.Del(mi)
+
 	}
 
 	farIp := &dbdata.IpMap{LastLogin: tNow}
-	// 遍历超过租期ip
+	// 全局遍历超过租期ip
 	for i := IpPool.IpLongMin; i <= IpPool.IpLongMax; i++ {
 		ip := long2ip(i)
 		ipStr := ip.String()
@@ -112,11 +99,20 @@ func AcquireIp(username, macAddr string) net.IP {
 		}
 
 		v := &dbdata.IpMap{}
-		err := dbdata.One("IpAddr", ip, v)
+		err = dbdata.One("IpAddr", ip, v)
 		if err != nil {
+			if dbdata.CheckErrNotFound(err) {
+				// 该ip没有被使用
+				mi = &dbdata.IpMap{IpAddr: ip, MacAddr: macAddr, Username: username, LastLogin: tNow}
+				_ = dbdata.Save(mi)
+				ipActive[ipStr] = true
+				return ip
+			}
 			base.Error(err)
 			return nil
 		}
+
+		// 跳过ip保留
 		if v.Keep {
 			continue
 		}
@@ -124,7 +120,7 @@ func AcquireIp(username, macAddr string) net.IP {
 		// 已经超过租期
 		if tNow.Sub(v.LastLogin) > time.Duration(base.Cfg.IpLease)*time.Second {
 			_ = dbdata.Del(v)
-			mi := &dbdata.IpMap{IpAddr: ip, MacAddr: macAddr, Username: username, LastLogin: tNow}
+			mi = &dbdata.IpMap{IpAddr: ip, MacAddr: macAddr, Username: username, LastLogin: tNow}
 			// 重写db数据
 			_ = dbdata.Save(mi)
 			ipActive[ipStr] = true

+ 62 - 108
anylink/web/package-lock.json

@@ -9,6 +9,7 @@
       "version": "0.1.0",
       "dependencies": {
         "axios": "^0.20.0",
+        "chokidar": "^3.5.2",
         "core-js": "^3.6.5",
         "echarts": "^4.9.0",
         "element-ui": "^2.4.5",
@@ -2429,11 +2430,9 @@
       "dev": true
     },
     "node_modules/anymatch": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npm.taobao.org/anymatch/download/anymatch-3.1.1.tgz",
-      "integrity": "sha1-xV7PAhheJGklk5kxDBc84xIzsUI=",
-      "dev": true,
-      "optional": true,
+      "version": "3.1.2",
+      "resolved": "https://registry.npm.taobao.org/anymatch/download/anymatch-3.1.2.tgz?cache=0&sync_timestamp=1617747502795&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fanymatch%2Fdownload%2Fanymatch-3.1.2.tgz",
+      "integrity": "sha1-wFV8CWrzLxBhmPT04qODU343hxY=",
       "dependencies": {
         "normalize-path": "^3.0.0",
         "picomatch": "^2.0.4"
@@ -2883,8 +2882,6 @@
       "version": "2.1.0",
       "resolved": "https://registry.npm.taobao.org/binary-extensions/download/binary-extensions-2.1.0.tgz",
       "integrity": "sha1-MPpAyef+B9vIlWeM0ocCTeokHdk=",
-      "dev": true,
-      "optional": true,
       "engines": {
         "node": ">=8"
       }
@@ -3422,10 +3419,14 @@
       }
     },
     "node_modules/caniuse-lite": {
-      "version": "1.0.30001135",
-      "resolved": "https://registry.npm.taobao.org/caniuse-lite/download/caniuse-lite-1.0.30001135.tgz?cache=0&sync_timestamp=1600754676334&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcaniuse-lite%2Fdownload%2Fcaniuse-lite-1.0.30001135.tgz",
-      "integrity": "sha1-mVseuUQEo8mg12AMETybsn8s2Ko=",
-      "dev": true
+      "version": "1.0.30001240",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001240.tgz",
+      "integrity": "sha512-nb8mDzfMdxBDN7ZKx8chWafAdBp5DAAlpWvNyUGe5tcDWd838zpzDN3Rah9cjCqhfOKkrvx40G2SDtP0qiWX/w==",
+      "dev": true,
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/browserslist"
+      }
     },
     "node_modules/case-sensitive-paths-webpack-plugin": {
       "version": "2.3.0",
@@ -3469,31 +3470,29 @@
       "dev": true
     },
     "node_modules/chokidar": {
-      "version": "3.4.2",
-      "resolved": "https://registry.npm.taobao.org/chokidar/download/chokidar-3.4.2.tgz?cache=0&sync_timestamp=1596728935229&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fchokidar%2Fdownload%2Fchokidar-3.4.2.tgz",
-      "integrity": "sha1-ONyOZY3sOAl0HrPve7Ckf+QkIy0=",
-      "dev": true,
-      "optional": true,
+      "version": "3.5.2",
+      "resolved": "https://registry.nlark.com/chokidar/download/chokidar-3.5.2.tgz?cache=0&sync_timestamp=1623763452074&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fchokidar%2Fdownload%2Fchokidar-3.5.2.tgz",
+      "integrity": "sha1-26OXb8rbAW9m/TZQIdkWANAcHnU=",
       "dependencies": {
-        "anymatch": "~3.1.1",
+        "anymatch": "~3.1.2",
         "braces": "~3.0.2",
-        "fsevents": "~2.1.2",
-        "glob-parent": "~5.1.0",
+        "glob-parent": "~5.1.2",
         "is-binary-path": "~2.1.0",
         "is-glob": "~4.0.1",
         "normalize-path": "~3.0.0",
-        "readdirp": "~3.4.0"
+        "readdirp": "~3.6.0"
       },
       "engines": {
         "node": ">= 8.10.0"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.2"
       }
     },
     "node_modules/chokidar/node_modules/braces": {
       "version": "3.0.2",
       "resolved": "https://registry.npm.taobao.org/braces/download/braces-3.0.2.tgz",
       "integrity": "sha1-NFThpGLujVmeI23zNs2epPiv4Qc=",
-      "dev": true,
-      "optional": true,
       "dependencies": {
         "fill-range": "^7.0.1"
       },
@@ -3505,8 +3504,6 @@
       "version": "7.0.1",
       "resolved": "https://registry.npm.taobao.org/fill-range/download/fill-range-7.0.1.tgz",
       "integrity": "sha1-GRmmp8df44ssfHflGYU12prN2kA=",
-      "dev": true,
-      "optional": true,
       "dependencies": {
         "to-regex-range": "^5.0.1"
       },
@@ -3518,8 +3515,6 @@
       "version": "7.0.0",
       "resolved": "https://registry.npm.taobao.org/is-number/download/is-number-7.0.0.tgz",
       "integrity": "sha1-dTU0W4lnNNX4DE0GxQlVUnoU8Ss=",
-      "dev": true,
-      "optional": true,
       "engines": {
         "node": ">=0.12.0"
       }
@@ -3528,8 +3523,6 @@
       "version": "5.0.1",
       "resolved": "https://registry.npm.taobao.org/to-regex-range/download/to-regex-range-5.0.1.tgz",
       "integrity": "sha1-FkjESq58jZiKMmAY7XL1tN0DkuQ=",
-      "dev": true,
-      "optional": true,
       "dependencies": {
         "is-number": "^7.0.0"
       },
@@ -6375,14 +6368,10 @@
       "dev": true
     },
     "node_modules/fsevents": {
-      "version": "2.1.3",
-      "resolved": "https://registry.npm.taobao.org/fsevents/download/fsevents-2.1.3.tgz",
-      "integrity": "sha1-+3OHA66NL5/pAMM4Nt3r7ouX8j4=",
-      "dev": true,
+      "version": "2.3.2",
+      "resolved": "https://registry.npm.taobao.org/fsevents/download/fsevents-2.3.2.tgz",
+      "integrity": "sha1-ilJveLj99GI7cJ4Ll1xSwkwC/Ro=",
       "optional": true,
-      "os": [
-        "darwin"
-      ],
       "engines": {
         "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
       }
@@ -6465,10 +6454,9 @@
       }
     },
     "node_modules/glob-parent": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npm.taobao.org/glob-parent/download/glob-parent-5.1.1.tgz",
-      "integrity": "sha1-tsHvQXxOVmPqSY8cRa+saRa7wik=",
-      "dev": true,
+      "version": "5.1.2",
+      "resolved": "https://registry.nlark.com/glob-parent/download/glob-parent-5.1.2.tgz?cache=0&sync_timestamp=1620073438189&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fglob-parent%2Fdownload%2Fglob-parent-5.1.2.tgz",
+      "integrity": "sha1-hpgyxYA0/mikCTwX3BXoNA2EAcQ=",
       "dependencies": {
         "is-glob": "^4.0.1"
       },
@@ -7397,8 +7385,6 @@
       "version": "2.1.0",
       "resolved": "https://registry.npm.taobao.org/is-binary-path/download/is-binary-path-2.1.0.tgz",
       "integrity": "sha1-6h9/O4DwZCNug0cPhsCcJU+0Wwk=",
-      "dev": true,
-      "optional": true,
       "dependencies": {
         "binary-extensions": "^2.0.0"
       },
@@ -7537,7 +7523,6 @@
       "version": "2.1.1",
       "resolved": "https://registry.npm.taobao.org/is-extglob/download/is-extglob-2.1.1.tgz",
       "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
-      "dev": true,
       "engines": {
         "node": ">=0.10.0"
       }
@@ -7555,7 +7540,6 @@
       "version": "4.0.1",
       "resolved": "https://registry.npm.taobao.org/is-glob/download/is-glob-4.0.1.tgz",
       "integrity": "sha1-dWfb6fL14kZ7x3q4PEopSCQHpdw=",
-      "dev": true,
       "dependencies": {
         "is-extglob": "^2.1.1"
       },
@@ -8805,7 +8789,6 @@
       "version": "3.0.0",
       "resolved": "https://registry.npm.taobao.org/normalize-path/download/normalize-path-3.0.0.tgz",
       "integrity": "sha1-Dc1p/yOhybEf0JeDFmRKA4ghamU=",
-      "dev": true,
       "engines": {
         "node": ">=0.10.0"
       }
@@ -9512,11 +9495,9 @@
       "dev": true
     },
     "node_modules/picomatch": {
-      "version": "2.2.2",
-      "resolved": "https://registry.npm.taobao.org/picomatch/download/picomatch-2.2.2.tgz",
-      "integrity": "sha1-IfMz6ba46v8CRo9RRupAbTRfTa0=",
-      "dev": true,
-      "optional": true,
+      "version": "2.3.0",
+      "resolved": "https://registry.nlark.com/picomatch/download/picomatch-2.3.0.tgz",
+      "integrity": "sha1-8fBh3o9qS/AiiS4tEoI0+5gwKXI=",
       "engines": {
         "node": ">=8.6"
       }
@@ -10556,11 +10537,9 @@
       }
     },
     "node_modules/readdirp": {
-      "version": "3.4.0",
-      "resolved": "https://registry.npm.taobao.org/readdirp/download/readdirp-3.4.0.tgz",
-      "integrity": "sha1-n9zN+ekVWAVEkiGsZF6DA6tbmto=",
-      "dev": true,
-      "optional": true,
+      "version": "3.6.0",
+      "resolved": "https://registry.npm.taobao.org/readdirp/download/readdirp-3.6.0.tgz?cache=0&sync_timestamp=1615717369278&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Freaddirp%2Fdownload%2Freaddirp-3.6.0.tgz",
+      "integrity": "sha1-dKNwvYVxFuJFspzJc0DNQxoCpsc=",
       "dependencies": {
         "picomatch": "^2.2.1"
       },
@@ -16342,11 +16321,9 @@
       "dev": true
     },
     "anymatch": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npm.taobao.org/anymatch/download/anymatch-3.1.1.tgz",
-      "integrity": "sha1-xV7PAhheJGklk5kxDBc84xIzsUI=",
-      "dev": true,
-      "optional": true,
+      "version": "3.1.2",
+      "resolved": "https://registry.npm.taobao.org/anymatch/download/anymatch-3.1.2.tgz?cache=0&sync_timestamp=1617747502795&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fanymatch%2Fdownload%2Fanymatch-3.1.2.tgz",
+      "integrity": "sha1-wFV8CWrzLxBhmPT04qODU343hxY=",
       "requires": {
         "normalize-path": "^3.0.0",
         "picomatch": "^2.0.4"
@@ -16733,9 +16710,7 @@
     "binary-extensions": {
       "version": "2.1.0",
       "resolved": "https://registry.npm.taobao.org/binary-extensions/download/binary-extensions-2.1.0.tgz",
-      "integrity": "sha1-MPpAyef+B9vIlWeM0ocCTeokHdk=",
-      "dev": true,
-      "optional": true
+      "integrity": "sha1-MPpAyef+B9vIlWeM0ocCTeokHdk="
     },
     "bindings": {
       "version": "1.5.0",
@@ -17216,9 +17191,9 @@
       }
     },
     "caniuse-lite": {
-      "version": "1.0.30001135",
-      "resolved": "https://registry.npm.taobao.org/caniuse-lite/download/caniuse-lite-1.0.30001135.tgz?cache=0&sync_timestamp=1600754676334&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcaniuse-lite%2Fdownload%2Fcaniuse-lite-1.0.30001135.tgz",
-      "integrity": "sha1-mVseuUQEo8mg12AMETybsn8s2Ko=",
+      "version": "1.0.30001240",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001240.tgz",
+      "integrity": "sha512-nb8mDzfMdxBDN7ZKx8chWafAdBp5DAAlpWvNyUGe5tcDWd838zpzDN3Rah9cjCqhfOKkrvx40G2SDtP0qiWX/w==",
       "dev": true
     },
     "case-sensitive-paths-webpack-plugin": {
@@ -17257,28 +17232,24 @@
       "dev": true
     },
     "chokidar": {
-      "version": "3.4.2",
-      "resolved": "https://registry.npm.taobao.org/chokidar/download/chokidar-3.4.2.tgz?cache=0&sync_timestamp=1596728935229&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fchokidar%2Fdownload%2Fchokidar-3.4.2.tgz",
-      "integrity": "sha1-ONyOZY3sOAl0HrPve7Ckf+QkIy0=",
-      "dev": true,
-      "optional": true,
+      "version": "3.5.2",
+      "resolved": "https://registry.nlark.com/chokidar/download/chokidar-3.5.2.tgz?cache=0&sync_timestamp=1623763452074&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fchokidar%2Fdownload%2Fchokidar-3.5.2.tgz",
+      "integrity": "sha1-26OXb8rbAW9m/TZQIdkWANAcHnU=",
       "requires": {
-        "anymatch": "~3.1.1",
+        "anymatch": "~3.1.2",
         "braces": "~3.0.2",
-        "fsevents": "~2.1.2",
-        "glob-parent": "~5.1.0",
+        "fsevents": "~2.3.2",
+        "glob-parent": "~5.1.2",
         "is-binary-path": "~2.1.0",
         "is-glob": "~4.0.1",
         "normalize-path": "~3.0.0",
-        "readdirp": "~3.4.0"
+        "readdirp": "~3.6.0"
       },
       "dependencies": {
         "braces": {
           "version": "3.0.2",
           "resolved": "https://registry.npm.taobao.org/braces/download/braces-3.0.2.tgz",
           "integrity": "sha1-NFThpGLujVmeI23zNs2epPiv4Qc=",
-          "dev": true,
-          "optional": true,
           "requires": {
             "fill-range": "^7.0.1"
           }
@@ -17287,8 +17258,6 @@
           "version": "7.0.1",
           "resolved": "https://registry.npm.taobao.org/fill-range/download/fill-range-7.0.1.tgz",
           "integrity": "sha1-GRmmp8df44ssfHflGYU12prN2kA=",
-          "dev": true,
-          "optional": true,
           "requires": {
             "to-regex-range": "^5.0.1"
           }
@@ -17296,16 +17265,12 @@
         "is-number": {
           "version": "7.0.0",
           "resolved": "https://registry.npm.taobao.org/is-number/download/is-number-7.0.0.tgz",
-          "integrity": "sha1-dTU0W4lnNNX4DE0GxQlVUnoU8Ss=",
-          "dev": true,
-          "optional": true
+          "integrity": "sha1-dTU0W4lnNNX4DE0GxQlVUnoU8Ss="
         },
         "to-regex-range": {
           "version": "5.0.1",
           "resolved": "https://registry.npm.taobao.org/to-regex-range/download/to-regex-range-5.0.1.tgz",
           "integrity": "sha1-FkjESq58jZiKMmAY7XL1tN0DkuQ=",
-          "dev": true,
-          "optional": true,
           "requires": {
             "is-number": "^7.0.0"
           }
@@ -19688,10 +19653,9 @@
       "dev": true
     },
     "fsevents": {
-      "version": "2.1.3",
-      "resolved": "https://registry.npm.taobao.org/fsevents/download/fsevents-2.1.3.tgz",
-      "integrity": "sha1-+3OHA66NL5/pAMM4Nt3r7ouX8j4=",
-      "dev": true,
+      "version": "2.3.2",
+      "resolved": "https://registry.npm.taobao.org/fsevents/download/fsevents-2.3.2.tgz",
+      "integrity": "sha1-ilJveLj99GI7cJ4Ll1xSwkwC/Ro=",
       "optional": true
     },
     "function-bind": {
@@ -19757,10 +19721,9 @@
       }
     },
     "glob-parent": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npm.taobao.org/glob-parent/download/glob-parent-5.1.1.tgz",
-      "integrity": "sha1-tsHvQXxOVmPqSY8cRa+saRa7wik=",
-      "dev": true,
+      "version": "5.1.2",
+      "resolved": "https://registry.nlark.com/glob-parent/download/glob-parent-5.1.2.tgz?cache=0&sync_timestamp=1620073438189&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fglob-parent%2Fdownload%2Fglob-parent-5.1.2.tgz",
+      "integrity": "sha1-hpgyxYA0/mikCTwX3BXoNA2EAcQ=",
       "requires": {
         "is-glob": "^4.0.1"
       }
@@ -20528,8 +20491,6 @@
       "version": "2.1.0",
       "resolved": "https://registry.npm.taobao.org/is-binary-path/download/is-binary-path-2.1.0.tgz",
       "integrity": "sha1-6h9/O4DwZCNug0cPhsCcJU+0Wwk=",
-      "dev": true,
-      "optional": true,
       "requires": {
         "binary-extensions": "^2.0.0"
       }
@@ -20635,8 +20596,7 @@
     "is-extglob": {
       "version": "2.1.1",
       "resolved": "https://registry.npm.taobao.org/is-extglob/download/is-extglob-2.1.1.tgz",
-      "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
-      "dev": true
+      "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI="
     },
     "is-fullwidth-code-point": {
       "version": "3.0.0",
@@ -20648,7 +20608,6 @@
       "version": "4.0.1",
       "resolved": "https://registry.npm.taobao.org/is-glob/download/is-glob-4.0.1.tgz",
       "integrity": "sha1-dWfb6fL14kZ7x3q4PEopSCQHpdw=",
-      "dev": true,
       "requires": {
         "is-extglob": "^2.1.1"
       }
@@ -21695,8 +21654,7 @@
     "normalize-path": {
       "version": "3.0.0",
       "resolved": "https://registry.npm.taobao.org/normalize-path/download/normalize-path-3.0.0.tgz",
-      "integrity": "sha1-Dc1p/yOhybEf0JeDFmRKA4ghamU=",
-      "dev": true
+      "integrity": "sha1-Dc1p/yOhybEf0JeDFmRKA4ghamU="
     },
     "normalize-range": {
       "version": "0.1.2",
@@ -22276,11 +22234,9 @@
       "dev": true
     },
     "picomatch": {
-      "version": "2.2.2",
-      "resolved": "https://registry.npm.taobao.org/picomatch/download/picomatch-2.2.2.tgz",
-      "integrity": "sha1-IfMz6ba46v8CRo9RRupAbTRfTa0=",
-      "dev": true,
-      "optional": true
+      "version": "2.3.0",
+      "resolved": "https://registry.nlark.com/picomatch/download/picomatch-2.3.0.tgz",
+      "integrity": "sha1-8fBh3o9qS/AiiS4tEoI0+5gwKXI="
     },
     "pify": {
       "version": "4.0.1",
@@ -23181,11 +23137,9 @@
       }
     },
     "readdirp": {
-      "version": "3.4.0",
-      "resolved": "https://registry.npm.taobao.org/readdirp/download/readdirp-3.4.0.tgz",
-      "integrity": "sha1-n9zN+ekVWAVEkiGsZF6DA6tbmto=",
-      "dev": true,
-      "optional": true,
+      "version": "3.6.0",
+      "resolved": "https://registry.npm.taobao.org/readdirp/download/readdirp-3.6.0.tgz?cache=0&sync_timestamp=1615717369278&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Freaddirp%2Fdownload%2Freaddirp-3.6.0.tgz",
+      "integrity": "sha1-dKNwvYVxFuJFspzJc0DNQxoCpsc=",
       "requires": {
         "picomatch": "^2.2.1"
       }

+ 1 - 0
anylink/web/package.json

@@ -9,6 +9,7 @@
   },
   "dependencies": {
     "axios": "^0.20.0",
+    "chokidar": "^3.5.2",
     "core-js": "^3.6.5",
     "echarts": "^4.9.0",
     "element-ui": "^2.4.5",

+ 6 - 2
anylink/web/src/pages/set/Other.vue

@@ -15,8 +15,12 @@
           <el-form-item label="密码" prop="password">
             <el-input v-model="dataSmtp.password"></el-input>
           </el-form-item>
-          <el-form-item label="启用SSL" prop="use_ssl">
-            <el-switch v-model="dataSmtp.use_ssl"></el-switch>
+          <el-form-item label="加密类型" prop="encryption">
+            <el-radio-group v-model="dataSmtp.encryption">
+              <el-radio label="None">None</el-radio>
+              <el-radio label="SSLTLS">SSLTLS</el-radio>
+              <el-radio label="STARTTLS">STARTTLS</el-radio>
+            </el-radio-group>
           </el-form-item>
           <el-form-item label="邮件from" prop="from">
             <el-input v-model="dataSmtp.from"></el-input>

+ 2 - 1
anylink/web/src/pages/set/System.vue

@@ -46,8 +46,9 @@
 
     <el-card v-if="system.sys" style="margin-top: 10px">
       <div slot="header">
-        <span>go运行环境</span>
+        <span>运行环境</span>
       </div>
+      <Cell left="软件版本" :right="system.sys.appVersion" divider/>
       <Cell left="GO版本" :right="system.sys.goOs" divider/>
       <Cell left="GoArch" :right="system.sys.goArch" divider/>
       <Cell left="GoVersion" :right="system.sys.goVersion" divider/>