mirror of https://github.com/bjdgyc/anylink.git
升级go version 1.16,ui文件嵌入go二进制内
This commit is contained in:
parent
d8b55de5b5
commit
b515406635
|
@ -16,7 +16,7 @@ jobs:
|
||||||
- name: Set up Go 1.x
|
- name: Set up Go 1.x
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: ^1.14
|
go-version: ^1.16
|
||||||
id: go
|
id: go
|
||||||
|
|
||||||
- name: Check out code into the Go module directory
|
- name: Check out code into the Go module directory
|
||||||
|
|
82
README.md
82
README.md
|
@ -21,11 +21,13 @@ AnyLink 服务端仅在CentOS7测试通过,如需要安装在其他系统,
|
||||||
|
|
||||||
## Screenshot
|
## Screenshot
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
```
|
> 升级 go version >= 1.16
|
||||||
|
|
||||||
|
```shell
|
||||||
rootPath=`pwd`
|
rootPath=`pwd`
|
||||||
|
|
||||||
git clone https://github.com/bjdgyc/anylink.git
|
git clone https://github.com/bjdgyc/anylink.git
|
||||||
|
@ -36,12 +38,13 @@ npm install
|
||||||
npm run build
|
npm run build
|
||||||
|
|
||||||
cd $rootPath/anylink
|
cd $rootPath/anylink
|
||||||
|
cp -r $rootPath/anylink-web/ui .
|
||||||
go build -o anylink -ldflags "-X main.COMMIT_ID=`git rev-parse HEAD`"
|
go build -o anylink -ldflags "-X main.COMMIT_ID=`git rev-parse HEAD`"
|
||||||
|
|
||||||
#整理部署文件
|
#整理部署文件
|
||||||
mkdir $rootPath/anylink-deploy
|
mkdir $rootPath/anylink-deploy
|
||||||
cd $rootPath/anylink-deploy
|
cd $rootPath/anylink-deploy
|
||||||
cp -r $rootPath/anylink-web/ui .
|
|
||||||
cp -r $rootPath/anylink/anylink .
|
cp -r $rootPath/anylink/anylink .
|
||||||
cp -r $rootPath/anylink/conf .
|
cp -r $rootPath/anylink/conf .
|
||||||
cp -r $rootPath/anylink/downfiles .
|
cp -r $rootPath/anylink/downfiles .
|
||||||
|
@ -71,7 +74,15 @@ sudo ./anylink -conf="conf/server.toml"
|
||||||
|
|
||||||
默认配置文件内有详细的注释,根据注释填写配置即可。
|
默认配置文件内有详细的注释,根据注释填写配置即可。
|
||||||
|
|
||||||
- [conf/server.toml](https://github.com/bjdgyc/anylink/blob/master/conf/server.toml)
|
```shell
|
||||||
|
# 生成后台密码
|
||||||
|
./anylink -passwd 123456
|
||||||
|
|
||||||
|
# 生成jwt密钥
|
||||||
|
./anylink -secret
|
||||||
|
```
|
||||||
|
|
||||||
|
[conf/server.toml](https://github.com/bjdgyc/anylink/blob/master/conf/server.toml)
|
||||||
|
|
||||||
## Setting
|
## Setting
|
||||||
|
|
||||||
|
@ -82,43 +93,48 @@ sudo ./anylink -conf="conf/server.toml"
|
||||||
### tun设置
|
### tun设置
|
||||||
|
|
||||||
1. 开启服务器转发
|
1. 开启服务器转发
|
||||||
```
|
|
||||||
# flie: /etc/sysctl.conf
|
|
||||||
net.ipv4.ip_forward = 1
|
|
||||||
|
|
||||||
#执行如下命令
|
```shell
|
||||||
sysctl -w net.ipv4.ip_forward=1
|
# flie: /etc/sysctl.conf
|
||||||
```
|
net.ipv4.ip_forward = 1
|
||||||
|
|
||||||
|
#执行如下命令
|
||||||
|
sysctl -w net.ipv4.ip_forward=1
|
||||||
|
```
|
||||||
|
|
||||||
2. 设置nat转发规则
|
2. 设置nat转发规则
|
||||||
```
|
|
||||||
# eth0为服务器内网网卡
|
```shell
|
||||||
iptables -t nat -A POSTROUTING -s 192.168.10.0/255.255.255.0 -o eth0 -j MASQUERADE
|
# eth0为服务器内网网卡
|
||||||
```
|
iptables -t nat -A POSTROUTING -s 192.168.10.0/255.255.255.0 -o eth0 -j MASQUERADE
|
||||||
|
```
|
||||||
|
|
||||||
3. 使用AnyConnect客户端连接即可
|
3. 使用AnyConnect客户端连接即可
|
||||||
|
|
||||||
### tap设置
|
### tap设置
|
||||||
|
|
||||||
1. 创建桥接网卡
|
1. 创建桥接网卡
|
||||||
```
|
|
||||||
注意 server.toml 的ip参数,需要与 bridge.sh 的配置参数一致
|
```
|
||||||
```
|
注意 server.toml 的ip参数,需要与 bridge.sh 的配置参数一致
|
||||||
|
```
|
||||||
|
|
||||||
2. 修改 bridge.sh 内的参数
|
2. 修改 bridge.sh 内的参数
|
||||||
```
|
|
||||||
# file: ./bridge.sh
|
```
|
||||||
eth="eth0"
|
# file: ./bridge.sh
|
||||||
eth_ip="192.168.1.4"
|
eth="eth0"
|
||||||
eth_netmask="255.255.255.0"
|
eth_ip="192.168.1.4"
|
||||||
eth_broadcast="192.168.1.255"
|
eth_netmask="255.255.255.0"
|
||||||
eth_gateway="192.168.1.1"
|
eth_broadcast="192.168.1.255"
|
||||||
```
|
eth_gateway="192.168.1.1"
|
||||||
|
```
|
||||||
|
|
||||||
3. 执行 bridge.sh 文件
|
3. 执行 bridge.sh 文件
|
||||||
```
|
|
||||||
sh bridge.sh
|
```
|
||||||
```
|
sh bridge.sh
|
||||||
|
```
|
||||||
|
|
||||||
## Soft
|
## Soft
|
||||||
|
|
||||||
|
@ -126,11 +142,11 @@ sudo ./anylink -conf="conf/server.toml"
|
||||||
|
|
||||||
## Other Screenshot
|
## Other Screenshot
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,8 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/bjdgyc/anylink/pkg/utils"
|
|
||||||
|
|
||||||
"github.com/bjdgyc/anylink/base"
|
"github.com/bjdgyc/anylink/base"
|
||||||
|
"github.com/bjdgyc/anylink/pkg/utils"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
package admin
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"embed"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/pprof"
|
"net/http/pprof"
|
||||||
|
|
||||||
|
@ -9,11 +10,20 @@ import (
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var UiPath embed.FS
|
||||||
|
|
||||||
// 开启服务
|
// 开启服务
|
||||||
func StartAdmin() {
|
func StartAdmin() {
|
||||||
|
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
r.Use(authMiddleware)
|
r.Use(authMiddleware)
|
||||||
|
|
||||||
|
r.Handle("/", http.RedirectHandler("/ui/", http.StatusFound)).
|
||||||
|
Name("static")
|
||||||
|
r.PathPrefix("/ui/").Handler(http.FileServer(
|
||||||
|
http.FS(UiPath),
|
||||||
|
)).Name("static")
|
||||||
|
|
||||||
r.HandleFunc("/base/login", Login).Name("login")
|
r.HandleFunc("/base/login", Login).Name("login")
|
||||||
r.HandleFunc("/set/home", SetHome)
|
r.HandleFunc("/set/home", SetHome)
|
||||||
r.HandleFunc("/set/system", SetSystem)
|
r.HandleFunc("/set/system", SetSystem)
|
||||||
|
@ -49,8 +59,6 @@ func StartAdmin() {
|
||||||
r.HandleFunc("/debug/pprof", location("/debug/pprof/"))
|
r.HandleFunc("/debug/pprof", location("/debug/pprof/"))
|
||||||
r.PathPrefix("/debug/pprof/").HandlerFunc(pprof.Index)
|
r.PathPrefix("/debug/pprof/").HandlerFunc(pprof.Index)
|
||||||
|
|
||||||
r.PathPrefix("/").Handler(http.FileServer(http.Dir(base.Cfg.UiPath))).Name("static")
|
|
||||||
|
|
||||||
base.Info("Listen admin", base.Cfg.AdminAddr)
|
base.Info("Listen admin", base.Cfg.AdminAddr)
|
||||||
err := http.ListenAndServe(base.Cfg.AdminAddr, r)
|
err := http.ListenAndServe(base.Cfg.AdminAddr, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -3,6 +3,7 @@ package base
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -38,8 +39,7 @@ type ServerConfig struct {
|
||||||
DbFile string `toml:"db_file" info:"数据库地址"`
|
DbFile string `toml:"db_file" info:"数据库地址"`
|
||||||
CertFile string `toml:"cert_file" info:"证书文件"`
|
CertFile string `toml:"cert_file" info:"证书文件"`
|
||||||
CertKey string `toml:"cert_key" info:"证书密钥"`
|
CertKey string `toml:"cert_key" info:"证书密钥"`
|
||||||
UiPath string `toml:"ui_path" info:"ui文件路径"`
|
DownFilesPath string `json:"down_files_path" info:"外部下载文件路径"`
|
||||||
FilesPath string `toml:"files_path" info:"外部下载文件路径"`
|
|
||||||
LogLevel string `toml:"log_level" info:"日志等级"`
|
LogLevel string `toml:"log_level" info:"日志等级"`
|
||||||
Issuer string `toml:"issuer" info:"系统名称"`
|
Issuer string `toml:"issuer" info:"系统名称"`
|
||||||
AdminUser string `toml:"admin_user" info:"管理用户名"`
|
AdminUser string `toml:"admin_user" info:"管理用户名"`
|
||||||
|
@ -82,8 +82,12 @@ func initServerCfg() {
|
||||||
Cfg.DbFile = getAbsPath(base, Cfg.DbFile)
|
Cfg.DbFile = getAbsPath(base, Cfg.DbFile)
|
||||||
Cfg.CertFile = getAbsPath(base, Cfg.CertFile)
|
Cfg.CertFile = getAbsPath(base, Cfg.CertFile)
|
||||||
Cfg.CertKey = getAbsPath(base, Cfg.CertKey)
|
Cfg.CertKey = getAbsPath(base, Cfg.CertKey)
|
||||||
Cfg.UiPath = getAbsPath(base, Cfg.UiPath)
|
Cfg.DownFilesPath = getAbsPath(base, Cfg.DownFilesPath)
|
||||||
Cfg.FilesPath = getAbsPath(base, Cfg.FilesPath)
|
|
||||||
|
if len(Cfg.JwtSecret) < 20 {
|
||||||
|
fmt.Println("请设置 jwt_secret 长度20位以上")
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Printf("ServerCfg: %+v \n", Cfg)
|
fmt.Printf("ServerCfg: %+v \n", Cfg)
|
||||||
}
|
}
|
||||||
|
|
16
base/flag.go
16
base/flag.go
|
@ -3,8 +3,11 @@ package base
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/bjdgyc/anylink/pkg/utils"
|
"github.com/bjdgyc/anylink/pkg/utils"
|
||||||
)
|
)
|
||||||
|
@ -16,13 +19,16 @@ var (
|
||||||
serverFile string
|
serverFile string
|
||||||
// pass明文
|
// pass明文
|
||||||
passwd string
|
passwd string
|
||||||
|
// 生成密钥
|
||||||
|
secret bool
|
||||||
// 显示版本信息
|
// 显示版本信息
|
||||||
rev bool
|
rev bool
|
||||||
)
|
)
|
||||||
|
|
||||||
func initFlag() {
|
func initFlag() {
|
||||||
flag.StringVar(&serverFile, "conf", "./conf/server.toml", "server config file path")
|
flag.StringVar(&serverFile, "conf", "./conf/server.toml", "server config file path")
|
||||||
flag.StringVar(&passwd, "passwd", "", "the password plaintext")
|
flag.StringVar(&passwd, "passwd", "", "convert the password plaintext")
|
||||||
|
flag.BoolVar(&secret, "secret", false, "generate a random jwt secret")
|
||||||
flag.BoolVar(&rev, "rev", false, "display version info")
|
flag.BoolVar(&rev, "rev", false, "display version info")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
@ -32,6 +38,14 @@ func initFlag() {
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if secret {
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
s, _ := utils.RandSecret(40, 60)
|
||||||
|
s = strings.Trim(s, "=")
|
||||||
|
fmt.Printf("Secret:%s\n", s)
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
if rev {
|
if rev {
|
||||||
fmt.Printf("%s v%s build on %s [%s, %s] commit_id(%s) \n",
|
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)
|
APP_NAME, APP_VER, runtime.Version(), runtime.GOOS, runtime.GOARCH, CommitId)
|
||||||
|
|
|
@ -18,7 +18,7 @@ const (
|
||||||
var (
|
var (
|
||||||
baseLog *log.Logger
|
baseLog *log.Logger
|
||||||
baseLevel int
|
baseLevel int
|
||||||
level map[int]string
|
levels map[int]string
|
||||||
)
|
)
|
||||||
|
|
||||||
func initLog() {
|
func initLog() {
|
||||||
|
@ -27,7 +27,7 @@ func initLog() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func logLevel2Int(l string) int {
|
func logLevel2Int(l string) int {
|
||||||
level = map[int]string{
|
levels = map[int]string{
|
||||||
_Debug: "Debug",
|
_Debug: "Debug",
|
||||||
_Info: "Info",
|
_Info: "Info",
|
||||||
_Warn: "Warn",
|
_Warn: "Warn",
|
||||||
|
@ -35,7 +35,7 @@ func logLevel2Int(l string) int {
|
||||||
_Fatal: "Fatal",
|
_Fatal: "Fatal",
|
||||||
}
|
}
|
||||||
lvl := _Info
|
lvl := _Info
|
||||||
for k, v := range level {
|
for k, v := range levels {
|
||||||
if strings.ToLower(l) == strings.ToLower(v) {
|
if strings.ToLower(l) == strings.ToLower(v) {
|
||||||
lvl = k
|
lvl = k
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ func logLevel2Int(l string) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
func output(l int, s ...interface{}) {
|
func output(l int, s ...interface{}) {
|
||||||
lvl := fmt.Sprintf("[%s] ", level[l])
|
lvl := fmt.Sprintf("[%s] ", levels[l])
|
||||||
baseLog.Output(3, lvl+fmt.Sprintln(s...))
|
baseLog.Output(3, lvl+fmt.Sprintln(s...))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,7 @@ db_file = "./data.db"
|
||||||
#证书文件
|
#证书文件
|
||||||
cert_file = "./vpn_cert.pem"
|
cert_file = "./vpn_cert.pem"
|
||||||
cert_key = "./vpn_cert.key"
|
cert_key = "./vpn_cert.key"
|
||||||
ui_path = "../ui"
|
down_files_path = "../down_files"
|
||||||
files_path = "../downfiles"
|
|
||||||
|
|
||||||
log_level = "info"
|
log_level = "info"
|
||||||
|
|
||||||
|
@ -19,7 +18,7 @@ issuer = "XX公司VPN"
|
||||||
admin_user = "admin"
|
admin_user = "admin"
|
||||||
#pass 123456
|
#pass 123456
|
||||||
admin_pass = "$2a$10$UQ7C.EoPifDeJh6d8.31TeSPQU7hM/NOM2nixmBucJpAuXDQNqNke"
|
admin_pass = "$2a$10$UQ7C.EoPifDeJh6d8.31TeSPQU7hM/NOM2nixmBucJpAuXDQNqNke"
|
||||||
jwt_secret = "7IrsKW3JuDJ68TPPrdsfweDFYJrO1Xg7JcdsfasMv3P3"
|
jwt_secret = ""
|
||||||
|
|
||||||
|
|
||||||
#vpn服务对外地址
|
#vpn服务对外地址
|
||||||
|
|
|
@ -69,6 +69,9 @@ func SetGroup(g *Group) error {
|
||||||
clientDns = append(clientDns, v)
|
clientDns = append(clientDns, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(clientDns) == 0 {
|
||||||
|
return errors.New("DNS错误")
|
||||||
|
}
|
||||||
g.ClientDns = clientDns
|
g.ClientDns = clientDns
|
||||||
|
|
||||||
routeInclude := []ValData{}
|
routeInclude := []ValData{}
|
||||||
|
|
121
dbdata/user.go
121
dbdata/user.go
|
@ -2,6 +2,8 @@ package dbdata
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/bjdgyc/anylink/pkg/utils"
|
"github.com/bjdgyc/anylink/pkg/utils"
|
||||||
|
@ -23,44 +25,6 @@ type User struct {
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证用户登陆信息
|
|
||||||
func CheckUser(name, pwd, group string) error {
|
|
||||||
// return nil
|
|
||||||
|
|
||||||
pl := len(pwd)
|
|
||||||
if name == "" || pl < 6 {
|
|
||||||
return errors.New("密码错误")
|
|
||||||
}
|
|
||||||
v := &User{}
|
|
||||||
err := One("Username", name, v)
|
|
||||||
if err != nil || v.Status != 1 {
|
|
||||||
return errors.New("用户名错误")
|
|
||||||
}
|
|
||||||
pass := pwd[:pl-6]
|
|
||||||
// if !utils.PasswordVerify(pass, v.Password) {
|
|
||||||
if pass != v.PinCode {
|
|
||||||
return errors.New("密码错误")
|
|
||||||
}
|
|
||||||
otp := pwd[pl-6:]
|
|
||||||
totp := gotp.NewDefaultTOTP(v.OtpSecret)
|
|
||||||
unix := time.Now().Unix()
|
|
||||||
verify := totp.Verify(otp, int(unix))
|
|
||||||
if !verify {
|
|
||||||
return errors.New("动态码错误")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 判断用户组信息
|
|
||||||
if !utils.InArrStr(v.Groups, group) {
|
|
||||||
return errors.New("用户组错误")
|
|
||||||
}
|
|
||||||
groupData := &Group{}
|
|
||||||
err = One("Name", group, groupData)
|
|
||||||
if err != nil || groupData.Status != 1 {
|
|
||||||
return errors.New("用户组错误")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func SetUser(v *User) error {
|
func SetUser(v *User) error {
|
||||||
var err error
|
var err error
|
||||||
if v.Username == "" || len(v.Groups) == 0 {
|
if v.Username == "" || len(v.Groups) == 0 {
|
||||||
|
@ -96,3 +60,84 @@ func SetUser(v *User) error {
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 验证用户登陆信息
|
||||||
|
func CheckUser(name, pwd, group string) error {
|
||||||
|
// return nil
|
||||||
|
|
||||||
|
pl := len(pwd)
|
||||||
|
if name == "" || pl < 6 {
|
||||||
|
return fmt.Errorf("%s %s", name, "密码错误")
|
||||||
|
}
|
||||||
|
v := &User{}
|
||||||
|
err := One("Username", name, v)
|
||||||
|
if err != nil || v.Status != 1 {
|
||||||
|
return fmt.Errorf("%s %s", name, "用户名错误")
|
||||||
|
}
|
||||||
|
// 判断用户组信息
|
||||||
|
if !utils.InArrStr(v.Groups, group) {
|
||||||
|
return fmt.Errorf("%s %s", name, "用户组错误")
|
||||||
|
}
|
||||||
|
groupData := &Group{}
|
||||||
|
err = One("Name", group, groupData)
|
||||||
|
if err != nil || groupData.Status != 1 {
|
||||||
|
return fmt.Errorf("%s %s", name, "用户组错误")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断otp信息
|
||||||
|
otp := pwd[pl-6:]
|
||||||
|
if !checkOtp(name, otp) {
|
||||||
|
return fmt.Errorf("%s %s", name, "动态码错误")
|
||||||
|
}
|
||||||
|
totp := gotp.NewDefaultTOTP(v.OtpSecret)
|
||||||
|
unix := time.Now().Unix()
|
||||||
|
verify := totp.Verify(otp, int(unix))
|
||||||
|
if !verify {
|
||||||
|
return fmt.Errorf("%s %s", name, "动态码错误")
|
||||||
|
}
|
||||||
|
|
||||||
|
pinCode := pwd[:pl-6]
|
||||||
|
if pinCode != v.PinCode {
|
||||||
|
return fmt.Errorf("%s %s", name, "密码错误")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
userOtpMux = sync.Mutex{}
|
||||||
|
userOtp = map[string]time.Time{}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
go func() {
|
||||||
|
expire := time.Second * 60
|
||||||
|
|
||||||
|
for range time.Tick(time.Second * 10) {
|
||||||
|
tnow := time.Now()
|
||||||
|
userOtpMux.Lock()
|
||||||
|
for k, v := range userOtp {
|
||||||
|
if tnow.After(v.Add(expire)) {
|
||||||
|
delete(userOtp, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
userOtpMux.Unlock()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 令牌只能使用一次
|
||||||
|
func checkOtp(username, otp string) bool {
|
||||||
|
key := fmt.Sprintf("%s:%s", username, otp)
|
||||||
|
|
||||||
|
userOtpMux.Lock()
|
||||||
|
defer userOtpMux.Unlock()
|
||||||
|
|
||||||
|
if _, ok := userOtp[key]; ok {
|
||||||
|
// 已经存在
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
userOtp[key] = time.Now()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
|
@ -2,3 +2,4 @@
|
||||||
|
|
||||||
*
|
*
|
||||||
!.gitignore
|
!.gitignore
|
||||||
|
!index.html
|
|
@ -0,0 +1,10 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Title</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
1
go.mod
1
go.mod
|
@ -22,4 +22,5 @@ require (
|
||||||
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9
|
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9
|
||||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11
|
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11
|
||||||
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324
|
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
|
||||||
)
|
)
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -87,6 +87,8 @@ golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxb
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
|
google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
|
||||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|
|
@ -2,6 +2,7 @@ package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -14,6 +15,17 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func LinkAuth(w http.ResponseWriter, r *http.Request) {
|
func LinkAuth(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// 判断anyconnect客户端
|
||||||
|
userAgent := strings.ToLower(r.UserAgent())
|
||||||
|
x_Aggregate_Auth := r.Header.Get("X-Aggregate-Auth")
|
||||||
|
x_Transcend_Version := r.Header.Get("X-Transcend-Version")
|
||||||
|
if !(strings.Contains(userAgent, "anyconnect") &&
|
||||||
|
x_Aggregate_Auth == "1" && x_Transcend_Version == "1") {
|
||||||
|
w.WriteHeader(http.StatusForbidden)
|
||||||
|
fmt.Fprintf(w, "error request")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
body, err := ioutil.ReadAll(r.Body)
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
|
|
@ -20,7 +20,7 @@ func LinkCstp(conn net.Conn, cSess *sessdata.ConnSession) {
|
||||||
err error
|
err error
|
||||||
n int
|
n int
|
||||||
dataLen uint16
|
dataLen uint16
|
||||||
dead = time.Duration(cSess.CstpDpd*2) * time.Second
|
dead = time.Duration(cSess.CstpDpd+5) * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
go cstpWrite(conn, cSess)
|
go cstpWrite(conn, cSess)
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/bjdgyc/anylink/base"
|
"github.com/bjdgyc/anylink/base"
|
||||||
"github.com/bjdgyc/anylink/dbdata"
|
|
||||||
"github.com/bjdgyc/anylink/sessdata"
|
"github.com/bjdgyc/anylink/sessdata"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -51,7 +50,7 @@ func LinkTunnel(w http.ResponseWriter, r *http.Request) {
|
||||||
masterSecret := r.Header.Get("X-DTLS-Master-Secret")
|
masterSecret := r.Header.Get("X-DTLS-Master-Secret")
|
||||||
localIp := r.Header.Get("X-Cstp-Local-Address-Ip4")
|
localIp := r.Header.Get("X-Cstp-Local-Address-Ip4")
|
||||||
mobile := r.Header.Get("X-Cstp-License")
|
mobile := r.Header.Get("X-Cstp-License")
|
||||||
platform := r.Header.Get("X-AnyConnect-Identifier-Platform")
|
|
||||||
cSess.SetMtu(cstpMtu)
|
cSess.SetMtu(cstpMtu)
|
||||||
cSess.MasterSecret = masterSecret
|
cSess.MasterSecret = masterSecret
|
||||||
cSess.RemoteAddr = r.RemoteAddr
|
cSess.RemoteAddr = r.RemoteAddr
|
||||||
|
@ -67,12 +66,6 @@ func LinkTunnel(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
cSess.CstpDpd = cstpDpd
|
cSess.CstpDpd = cstpDpd
|
||||||
|
|
||||||
// iPhone手机需要最少一个dns
|
|
||||||
if platform == "apple-ios" && len(cSess.Group.ClientDns) == 0 {
|
|
||||||
dnsVal := dbdata.ValData{Val: "114.114.114.114"}
|
|
||||||
cSess.Group.ClientDns = append(cSess.Group.ClientDns, dnsVal)
|
|
||||||
}
|
|
||||||
|
|
||||||
base.Debug(cSess.IpAddr, cSess.MacHw, sess.Username, mobile)
|
base.Debug(cSess.IpAddr, cSess.MacHw, sess.Username, mobile)
|
||||||
|
|
||||||
// 返回客户端数据
|
// 返回客户端数据
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/bjdgyc/anylink/base"
|
"github.com/bjdgyc/anylink/base"
|
||||||
|
@ -18,6 +19,7 @@ func startTls() {
|
||||||
certFile := base.Cfg.CertFile
|
certFile := base.Cfg.CertFile
|
||||||
keyFile := base.Cfg.CertKey
|
keyFile := base.Cfg.CertKey
|
||||||
|
|
||||||
|
logger := log.New(os.Stdout, "[SERVER]", log.Lshortfile|log.Ldate)
|
||||||
// 设置tls信息
|
// 设置tls信息
|
||||||
tlsConfig := &tls.Config{
|
tlsConfig := &tls.Config{
|
||||||
NextProtos: []string{"http/1.1"},
|
NextProtos: []string{"http/1.1"},
|
||||||
|
@ -27,6 +29,7 @@ func startTls() {
|
||||||
Addr: addr,
|
Addr: addr,
|
||||||
Handler: initRoute(),
|
Handler: initRoute(),
|
||||||
TLSConfig: tlsConfig,
|
TLSConfig: tlsConfig,
|
||||||
|
ErrorLog: logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
var ln net.Listener
|
var ln net.Listener
|
||||||
|
@ -50,13 +53,13 @@ func startTls() {
|
||||||
|
|
||||||
func initRoute() http.Handler {
|
func initRoute() http.Handler {
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
// r.HandleFunc("/", checkLinkClient(LinkHome)).Methods(http.MethodGet)
|
r.HandleFunc("/", LinkHome).Methods(http.MethodGet)
|
||||||
r.HandleFunc("/", checkLinkClient(LinkAuth)).Methods(http.MethodPost)
|
r.HandleFunc("/", LinkAuth).Methods(http.MethodPost)
|
||||||
r.HandleFunc("/CSCOSSLC/tunnel", LinkTunnel).Methods(http.MethodConnect)
|
r.HandleFunc("/CSCOSSLC/tunnel", LinkTunnel).Methods(http.MethodConnect)
|
||||||
r.HandleFunc("/otp_qr", LinkOtpQr).Methods(http.MethodGet)
|
r.HandleFunc("/otp_qr", LinkOtpQr).Methods(http.MethodGet)
|
||||||
r.PathPrefix("/files/").Handler(
|
r.PathPrefix("/down_files/").Handler(
|
||||||
http.StripPrefix("/files/",
|
http.StripPrefix("/down_files/",
|
||||||
http.FileServer(http.Dir(base.Cfg.FilesPath)),
|
http.FileServer(http.Dir(base.Cfg.DownFilesPath)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
r.NotFoundHandler = http.HandlerFunc(notFound)
|
r.NotFoundHandler = http.HandlerFunc(notFound)
|
||||||
|
|
8
main.go
8
main.go
|
@ -2,21 +2,29 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"embed"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/bjdgyc/anylink/admin"
|
||||||
"github.com/bjdgyc/anylink/base"
|
"github.com/bjdgyc/anylink/base"
|
||||||
"github.com/bjdgyc/anylink/handler"
|
"github.com/bjdgyc/anylink/handler"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//go:embed ui/*
|
||||||
|
var UiPath embed.FS
|
||||||
|
|
||||||
// 程序版本
|
// 程序版本
|
||||||
var COMMIT_ID string
|
var COMMIT_ID string
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
base.CommitId = COMMIT_ID
|
base.CommitId = COMMIT_ID
|
||||||
|
admin.UiPath = UiPath
|
||||||
|
|
||||||
base.Start()
|
base.Start()
|
||||||
handler.Start()
|
handler.Start()
|
||||||
|
|
||||||
signalWatch()
|
signalWatch()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,8 +25,8 @@ const (
|
||||||
delmiter = "$"
|
delmiter = "$"
|
||||||
)
|
)
|
||||||
|
|
||||||
func saltSecret() (string, error) {
|
func RandSecret(min int, max int) (string, error) {
|
||||||
rb := make([]byte, randInt(10, 100))
|
rb := make([]byte, randInt(min, max))
|
||||||
_, err := rand.Read(rb)
|
_, err := rand.Read(rb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
|
|
@ -145,8 +145,8 @@ func (s *Session) NewConn() *ConnSession {
|
||||||
macHw, err := net.ParseMAC(macAddr)
|
macHw, err := net.ParseMAC(macAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sum := md5.Sum([]byte(s.UniqueIdGlobal))
|
sum := md5.Sum([]byte(s.UniqueIdGlobal))
|
||||||
macHw = sum[8:13] // 5个byte
|
macHw = sum[0:5] // 5个byte
|
||||||
macHw = append([]byte{0x00}, macHw...)
|
macHw = append([]byte{0x02}, macHw...)
|
||||||
macAddr = macHw.String()
|
macAddr = macHw.String()
|
||||||
}
|
}
|
||||||
ip := AcquireIp(username, macAddr)
|
ip := AcquireIp(username, macAddr)
|
||||||
|
|
Loading…
Reference in New Issue