7 Commits

Author SHA1 Message Date
bjd
9e0da33c6a 增加重连功能,可动态修改连接参数 2021-01-25 16:03:33 +08:00
bjdgyc
3bb771971c Create CONTRIBUTING.md 2021-01-25 15:15:10 +08:00
bjd
dd83b330eb 发送邮件增加ssl功能 2021-01-22 10:55:49 +08:00
bjd
73d1edd62f 添加邮件发送错误信息 2021-01-12 19:43:41 +08:00
bjd
3ebb669558 增加了CIDR判断和返回数据的debug信息 2021-01-12 17:30:31 +08:00
bjd
a72fc63c06 修复ui路径404的问题 2021-01-11 18:45:40 +08:00
bjd
206cfebb9a 修改go版本为1.15 2021-01-08 13:09:04 +08:00
17 changed files with 111 additions and 73 deletions

View File

@@ -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.16 go-version: 1.15
id: go id: go
- name: Check out code into the Go module directory - name: Check out code into the Go module directory

View File

@@ -25,7 +25,7 @@ AnyLink 服务端仅在CentOS7测试通过如需要安装在其他系统
## Installation ## Installation
> 升级 go version = 1.16 > 升级 go version = 1.15
```shell ```shell
git clone https://github.com/bjdgyc/anylink.git git clone https://github.com/bjdgyc/anylink.git
@@ -101,13 +101,12 @@ iptables -t nat -A POSTROUTING -s 192.168.10.0/255.255.255.0 -o eth0 -j MASQUERA
1. 创建桥接网卡 1. 创建桥接网卡
``` ```
注意 server.toml 的ip参数需要与 bridge.sh 的配置参数一致 注意 server.toml 的ip参数需要与 bridge-init.sh 的配置参数一致
``` ```
2. 修改 bridge.sh 内的参数 2. 修改 bridge-init.sh 内的参数
``` ```
# file: ./bridge.sh
eth="eth0" eth="eth0"
eth_ip="192.168.1.4" eth_ip="192.168.1.4"
eth_netmask="255.255.255.0" eth_netmask="255.255.255.0"
@@ -115,16 +114,26 @@ eth_broadcast="192.168.1.255"
eth_gateway="192.168.1.1" eth_gateway="192.168.1.1"
``` ```
3. 执行 bridge.sh 文件 3. 执行 bridge-init.sh 文件
``` ```
sh bridge.sh sh bridge-init.sh
``` ```
## Soft ## Soft
相关软件下载: https://gitee.com/bjdgyc/anylink-soft 相关软件下载: https://gitee.com/bjdgyc/anylink-soft
## Discussion
![qq.png](https://gitee.com/bjdgyc/anylink/raw/master/screenshot/qq.png)
添加QQ群: 567510628
## Contribution
欢迎提交 PR、Issues感谢为AnyLink做出贡献
## Other Screenshot ## Other Screenshot
![system.jpg](https://gitee.com/bjdgyc/anylink/raw/master/screenshot/system.jpg) ![system.jpg](https://gitee.com/bjdgyc/anylink/raw/master/screenshot/system.jpg)

View File

@@ -56,7 +56,7 @@ func authMiddleware(next http.Handler) http.Handler {
route := mux.CurrentRoute(r) route := mux.CurrentRoute(r)
name := route.GetName() name := route.GetName()
// fmt.Println("bb", r.URL.Path, name) // fmt.Println("bb", r.URL.Path, name)
if name == "login" || name == "static" { if utils.InArrStr([]string{"login", "index", "static"}, name) {
// 不进行鉴权 // 不进行鉴权
next.ServeHTTP(w, r) next.ServeHTTP(w, r)
return return

View File

@@ -100,7 +100,11 @@ func UserSet(w http.ResponseWriter, r *http.Request) {
// 发送邮件 // 发送邮件
if data.SendEmail { if data.SendEmail {
userAccountMail(data) err = userAccountMail(data)
if err != nil {
RespError(w, RespInternalErr, err)
return
}
} }
RespSucess(w, nil) RespSucess(w, nil)
@@ -171,6 +175,13 @@ func UserOffline(w http.ResponseWriter, r *http.Request) {
RespSucess(w, nil) RespSucess(w, nil)
} }
func UserReline(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
token := r.FormValue("token")
sessdata.CloseCSess(token)
RespSucess(w, nil)
}
type userAccountMailData struct { type userAccountMailData struct {
Issuer string Issuer string
LinkAddr string LinkAddr string

View File

@@ -68,7 +68,9 @@ func SendMail(subject, to, htmlBody string) error {
server.Port = dataSmtp.Port server.Port = dataSmtp.Port
server.Username = dataSmtp.Username server.Username = dataSmtp.Username
server.Password = dataSmtp.Password server.Password = dataSmtp.Password
// server.Encryption = mail.EncryptionTLS if dataSmtp.UseSSl {
server.Encryption = mail.EncryptionSSL
}
// Since v2.3.0 you can specified authentication type: // Since v2.3.0 you can specified authentication type:
// - PLAIN (default) // - PLAIN (default)

View File

@@ -2,7 +2,6 @@
package admin package admin
import ( import (
"embed"
"net/http" "net/http"
"net/http/pprof" "net/http/pprof"
@@ -10,21 +9,18 @@ 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)). r.Handle("/", http.RedirectHandler("/ui/", http.StatusFound)).Name("index")
Name("static") r.PathPrefix("/ui/").Handler(
r.PathPrefix("/ui/").Handler(http.FileServer( http.StripPrefix("/ui/", http.FileServer(http.Dir(base.Cfg.UiPath))),
http.FS(UiPath), ).Name("static")
)).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)
r.HandleFunc("/set/soft", SetSoft) r.HandleFunc("/set/soft", SetSoft)
@@ -39,6 +35,7 @@ func StartAdmin() {
r.HandleFunc("/user/del", UserDel) r.HandleFunc("/user/del", UserDel)
r.HandleFunc("/user/online", UserOnline) r.HandleFunc("/user/online", UserOnline)
r.HandleFunc("/user/offline", UserOffline) r.HandleFunc("/user/offline", UserOffline)
r.HandleFunc("/user/reline", UserReline)
r.HandleFunc("/user/otp_qr", UserOtpQr) r.HandleFunc("/user/otp_qr", UserOtpQr)
r.HandleFunc("/user/ip_map/list", UserIpMapList) r.HandleFunc("/user/ip_map/list", UserIpMapList)
r.HandleFunc("/user/ip_map/detail", UserIpMapDetail) r.HandleFunc("/user/ip_map/detail", UserIpMapDetail)

View File

@@ -2,5 +2,5 @@ package base
const ( const (
APP_NAME = "AnyLink" APP_NAME = "AnyLink"
APP_VER = "0.0.6" APP_VER = "0.1.0"
) )

View File

@@ -39,7 +39,8 @@ 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:"证书密钥"`
DownFilesPath string `json:"down_files_path" info:"外部下载文件路径"` UiPath string `toml:"ui_path" info:"ui文件路径"`
DownFilesPath string `toml:"down_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,6 +83,7 @@ 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.DownFilesPath = getAbsPath(base, Cfg.DownFilesPath)
if len(Cfg.JwtSecret) < 20 { if len(Cfg.JwtSecret) < 20 {

View File

@@ -8,6 +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" down_files_path = "../down_files"
log_level = "info" log_level = "info"
@@ -21,7 +22,7 @@ admin_pass = "$2a$10$UQ7C.EoPifDeJh6d8.31TeSPQU7hM/NOM2nixmBucJpAuXDQNqNke"
jwt_secret = "" jwt_secret = ""
#vpn服务对外地址 #vpn服务对外地址,影响开通邮件二维码
link_addr = "vpn.xx.com" link_addr = "vpn.xx.com"
#前台服务监听地址 #前台服务监听地址

View File

@@ -4,7 +4,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"net" "net"
"strings"
"time" "time"
"github.com/bjdgyc/anylink/base" "github.com/bjdgyc/anylink/base"
@@ -19,12 +18,15 @@ type GroupLinkAcl struct {
// 自上而下匹配 默认 allow * * // 自上而下匹配 默认 allow * *
Action string `json:"action"` // allow、deny Action string `json:"action"` // allow、deny
Val string `json:"val"` Val string `json:"val"`
Port uint8 `json:"port"` Port uint16 `json:"port"`
IpNet *net.IPNet `json:"-"` IpNet *net.IPNet `json:"ip_net"`
Note string `json:"note"`
} }
type ValData struct { type ValData struct {
Val string `json:"val"` Val string `json:"val"`
IpMask string `json:"ip_mask"`
Note string `json:"note"`
} }
type Group struct { type Group struct {
@@ -70,15 +72,18 @@ func SetGroup(g *Group) error {
} }
} }
if len(clientDns) == 0 { if len(clientDns) == 0 {
return errors.New("DNS错误") return errors.New("DNS 错误")
} }
g.ClientDns = clientDns g.ClientDns = clientDns
routeInclude := []ValData{} routeInclude := []ValData{}
for _, v := range g.RouteInclude { for _, v := range g.RouteInclude {
if v.Val != "" { if v.Val != "" {
v1, _ := parseIpNet(v.Val) ipMask, _, err := parseIpNet(v.Val)
vn := ValData{Val: v1} if err != nil {
return errors.New("RouteInclude 错误" + err.Error())
}
vn := ValData{Val: v.Val, IpMask: ipMask}
routeInclude = append(routeInclude, vn) routeInclude = append(routeInclude, vn)
} }
} }
@@ -86,8 +91,11 @@ func SetGroup(g *Group) error {
routeExclude := []ValData{} routeExclude := []ValData{}
for _, v := range g.RouteExclude { for _, v := range g.RouteExclude {
if v.Val != "" { if v.Val != "" {
v1, _ := parseIpNet(v.Val) ipMask, _, err := parseIpNet(v.Val)
vn := ValData{Val: v1} if err != nil {
return errors.New("RouteExclude 错误" + err.Error())
}
vn := ValData{Val: v.Val, IpMask: ipMask}
routeExclude = append(routeExclude, vn) routeExclude = append(routeExclude, vn)
} }
} }
@@ -96,13 +104,13 @@ func SetGroup(g *Group) error {
linkAcl := []GroupLinkAcl{} linkAcl := []GroupLinkAcl{}
for _, v := range g.LinkAcl { for _, v := range g.LinkAcl {
if v.Val != "" { if v.Val != "" {
v1, v2 := parseIpNet(v.Val) _, ipNet, err := parseIpNet(v.Val)
if v2 != nil { if err != nil {
vn := v return errors.New("GroupLinkAcl 错误" + err.Error())
vn.Val = v1
vn.IpNet = v2
linkAcl = append(linkAcl, vn)
} }
vn := v
vn.IpNet = ipNet
linkAcl = append(linkAcl, vn)
} }
} }
g.LinkAcl = linkAcl g.LinkAcl = linkAcl
@@ -113,24 +121,14 @@ func SetGroup(g *Group) error {
return err return err
} }
func parseIpNet(s string) (string, *net.IPNet) { func parseIpNet(s string) (string, *net.IPNet, error) {
ips := strings.Split(s, "/") ip, ipNet, err := net.ParseCIDR(s)
if len(ips) != 2 { if err != nil {
return "", nil return "", nil, err
}
ip := net.ParseIP(ips[0])
mask := net.ParseIP(ips[1])
if strings.Contains(ips[0], ".") {
ip = ip.To4()
mask = mask.To4()
} }
ipmask := net.IPMask(mask) mask := net.IP(ipNet.Mask)
ip0 := ip.Mask(ipmask) ipMask := fmt.Sprintf("%s/%s", ip, mask)
ipNetS := fmt.Sprintf("%s/%s", ip0, mask) return ipMask, ipNet, nil
ipNet := &net.IPNet{IP: ip0, Mask: ipmask}
return ipNetS, ipNet
} }

View File

@@ -38,6 +38,7 @@ type SettingSmtp struct {
Username string `json:"username"` Username string `json:"username"`
Password string `json:"password"` Password string `json:"password"`
From string `json:"from"` From string `json:"from"`
UseSSl bool `json:"use_ssl"`
} }
type SettingOther struct { type SettingOther struct {

View File

@@ -1,23 +1,28 @@
#!/usr/bin/env bash #!/usr/bin/env bash
git clone https://github.com/bjdgyc/anylink-web.git #编译二进制文件
go build -o anylink -ldflags "-X main.COMMIT_ID=`git rev-parse HEAD`"
#编译前端项目
git clone https://github.com/bjdgyc/anylink-web.git
cd anylink-web cd anylink-web
#国内可替换源加快速度
#npm install --registry=https://registry.npm.taobao.org
#npm run build --registry=https://registry.npm.taobao.org
npm install npm install
npm run build npm run build
cd ../ cd ../
cp -r anylink-web/ui .
go build -o anylink -ldflags "-X main.COMMIT_ID=`git rev-parse HEAD`"
#整理部署文件 #整理部署文件
mkdir anylink-deploy mkdir anylink-deploy
cd anylink-deploy
cp -r ../anylink . cp -r anylink anylink-deploy
cp -r ../conf . cp -r anylink-web/ui anylink-deploy
cp -r ../down_files . cp -r conf anylink-deploy
cp -r down_files anylink-deploy
#注意使用root权限运行 #注意使用root权限运行
#cd anylink-deploy
#sudo ./anylink -conf="conf/server.toml" #sudo ./anylink -conf="conf/server.toml"

View File

@@ -49,12 +49,12 @@ func LinkCstp(conn net.Conn, cSess *sessdata.ConnSession) {
switch hdata[6] { switch hdata[6] {
case 0x07: // KEEPALIVE case 0x07: // KEEPALIVE
// do nothing // do nothing
base.Debug("recv keepalive", cSess.IpAddr) // base.Debug("recv keepalive", cSess.IpAddr)
case 0x05: // DISCONNECT case 0x05: // DISCONNECT
base.Debug("DISCONNECT", cSess.IpAddr) base.Debug("DISCONNECT", cSess.IpAddr)
return return
case 0x03: // DPD-REQ case 0x03: // DPD-REQ
base.Debug("recv DPD-REQ", cSess.IpAddr) // base.Debug("recv DPD-REQ", cSess.IpAddr)
if payloadOut(cSess, sessdata.LTypeIPData, 0x04, nil) { if payloadOut(cSess, sessdata.LTypeIPData, 0x04, nil) {
return return
} }

View File

@@ -1,6 +1,7 @@
package handler package handler
import ( import (
"bytes"
"fmt" "fmt"
"log" "log"
"net" "net"
@@ -86,11 +87,11 @@ func LinkTunnel(w http.ResponseWriter, r *http.Request) {
} }
// 允许的路由 // 允许的路由
for _, v := range cSess.Group.RouteInclude { for _, v := range cSess.Group.RouteInclude {
w.Header().Add("X-CSTP-Split-Include", v.Val) w.Header().Add("X-CSTP-Split-Include", v.IpMask)
} }
// 不允许的路由 // 不允许的路由
for _, v := range cSess.Group.RouteExclude { for _, v := range cSess.Group.RouteExclude {
w.Header().Add("X-CSTP-Split-Exclude", v.Val) w.Header().Add("X-CSTP-Split-Exclude", v.IpMask)
} }
w.Header().Set("X-CSTP-Lease-Duration", fmt.Sprintf("%d", base.Cfg.IpLease)) // ip地址租期 w.Header().Set("X-CSTP-Lease-Duration", fmt.Sprintf("%d", base.Cfg.IpLease)) // ip地址租期
@@ -130,8 +131,11 @@ func LinkTunnel(w http.ResponseWriter, r *http.Request) {
// w.Header().Set("X-CSTP-Post-Auth-XML", ``) // w.Header().Set("X-CSTP-Post-Auth-XML", ``)
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
// h := w.Header().Clone() h := w.Header().Clone()
// h.Write(os.Stdout) headers := make([]byte, 0)
buf := bytes.NewBuffer(headers)
h.Write(buf)
base.Debug(string(buf.Bytes()))
hj := w.(http.Hijacker) hj := w.(http.Hijacker)
conn, _, err := hj.Hijack() conn, _, err := hj.Hijack()

View File

@@ -1,26 +1,23 @@
// AnyLink 是一个企业级远程办公vpn软件可以支持多人同时在线使用。 // AnyLink 是一个企业级远程办公vpn软件可以支持多人同时在线使用。
// +build linux
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()

BIN
screenshot/qq.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -296,6 +296,17 @@ func CloseSess(token string) {
sess.CSess.Close() sess.CSess.Close()
} }
func CloseCSess(token string) {
sessMux.Lock()
defer sessMux.Unlock()
sess, ok := sessions[token]
if !ok {
return
}
sess.CSess.Close()
}
func DelSessByStoken(stoken string) { func DelSessByStoken(stoken string) {
stoken = strings.TrimSpace(stoken) stoken = strings.TrimSpace(stoken)
sarr := strings.Split(stoken, "@") sarr := strings.Split(stoken, "@")