mirror of
https://github.com/bjdgyc/anylink.git
synced 2025-09-29 00:19:36 +08:00
Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
9e0da33c6a | ||
|
3bb771971c | ||
|
dd83b330eb | ||
|
73d1edd62f | ||
|
3ebb669558 | ||
|
a72fc63c06 | ||
|
206cfebb9a |
2
.github/workflows/go.yml
vendored
2
.github/workflows/go.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.16
|
||||
go-version: 1.15
|
||||
id: go
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
|
21
README.md
21
README.md
@@ -25,7 +25,7 @@ AnyLink 服务端仅在CentOS7测试通过,如需要安装在其他系统,
|
||||
|
||||
## Installation
|
||||
|
||||
> 升级 go version = 1.16
|
||||
> 升级 go version = 1.15
|
||||
|
||||
```shell
|
||||
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. 创建桥接网卡
|
||||
|
||||
```
|
||||
注意 server.toml 的ip参数,需要与 bridge.sh 的配置参数一致
|
||||
注意 server.toml 的ip参数,需要与 bridge-init.sh 的配置参数一致
|
||||
```
|
||||
|
||||
2. 修改 bridge.sh 内的参数
|
||||
2. 修改 bridge-init.sh 内的参数
|
||||
|
||||
```
|
||||
# file: ./bridge.sh
|
||||
eth="eth0"
|
||||
eth_ip="192.168.1.4"
|
||||
eth_netmask="255.255.255.0"
|
||||
@@ -115,16 +114,26 @@ eth_broadcast="192.168.1.255"
|
||||
eth_gateway="192.168.1.1"
|
||||
```
|
||||
|
||||
3. 执行 bridge.sh 文件
|
||||
3. 执行 bridge-init.sh 文件
|
||||
|
||||
```
|
||||
sh bridge.sh
|
||||
sh bridge-init.sh
|
||||
```
|
||||
|
||||
## Soft
|
||||
|
||||
相关软件下载: https://gitee.com/bjdgyc/anylink-soft
|
||||
|
||||
## Discussion
|
||||
|
||||

|
||||
|
||||
添加QQ群: 567510628
|
||||
|
||||
## Contribution
|
||||
|
||||
欢迎提交 PR、Issues,感谢为AnyLink做出贡献
|
||||
|
||||
## Other Screenshot
|
||||
|
||||

|
||||
|
@@ -56,7 +56,7 @@ func authMiddleware(next http.Handler) http.Handler {
|
||||
route := mux.CurrentRoute(r)
|
||||
name := route.GetName()
|
||||
// fmt.Println("bb", r.URL.Path, name)
|
||||
if name == "login" || name == "static" {
|
||||
if utils.InArrStr([]string{"login", "index", "static"}, name) {
|
||||
// 不进行鉴权
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
|
@@ -100,7 +100,11 @@ func UserSet(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// 发送邮件
|
||||
if data.SendEmail {
|
||||
userAccountMail(data)
|
||||
err = userAccountMail(data)
|
||||
if err != nil {
|
||||
RespError(w, RespInternalErr, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
RespSucess(w, nil)
|
||||
@@ -171,6 +175,13 @@ func UserOffline(w http.ResponseWriter, r *http.Request) {
|
||||
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 {
|
||||
Issuer string
|
||||
LinkAddr string
|
||||
|
@@ -68,7 +68,9 @@ func SendMail(subject, to, htmlBody string) error {
|
||||
server.Port = dataSmtp.Port
|
||||
server.Username = dataSmtp.Username
|
||||
server.Password = dataSmtp.Password
|
||||
// server.Encryption = mail.EncryptionTLS
|
||||
if dataSmtp.UseSSl {
|
||||
server.Encryption = mail.EncryptionSSL
|
||||
}
|
||||
|
||||
// Since v2.3.0 you can specified authentication type:
|
||||
// - PLAIN (default)
|
||||
|
@@ -2,7 +2,6 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"net/http"
|
||||
"net/http/pprof"
|
||||
|
||||
@@ -10,21 +9,18 @@ import (
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
var UiPath embed.FS
|
||||
|
||||
// 开启服务
|
||||
func StartAdmin() {
|
||||
|
||||
r := mux.NewRouter()
|
||||
r.Use(authMiddleware)
|
||||
|
||||
r.Handle("/", http.RedirectHandler("/ui/", http.StatusFound)).
|
||||
Name("static")
|
||||
r.PathPrefix("/ui/").Handler(http.FileServer(
|
||||
http.FS(UiPath),
|
||||
)).Name("static")
|
||||
|
||||
r.Handle("/", http.RedirectHandler("/ui/", http.StatusFound)).Name("index")
|
||||
r.PathPrefix("/ui/").Handler(
|
||||
http.StripPrefix("/ui/", http.FileServer(http.Dir(base.Cfg.UiPath))),
|
||||
).Name("static")
|
||||
r.HandleFunc("/base/login", Login).Name("login")
|
||||
|
||||
r.HandleFunc("/set/home", SetHome)
|
||||
r.HandleFunc("/set/system", SetSystem)
|
||||
r.HandleFunc("/set/soft", SetSoft)
|
||||
@@ -39,6 +35,7 @@ func StartAdmin() {
|
||||
r.HandleFunc("/user/del", UserDel)
|
||||
r.HandleFunc("/user/online", UserOnline)
|
||||
r.HandleFunc("/user/offline", UserOffline)
|
||||
r.HandleFunc("/user/reline", UserReline)
|
||||
r.HandleFunc("/user/otp_qr", UserOtpQr)
|
||||
r.HandleFunc("/user/ip_map/list", UserIpMapList)
|
||||
r.HandleFunc("/user/ip_map/detail", UserIpMapDetail)
|
||||
|
@@ -2,5 +2,5 @@ package base
|
||||
|
||||
const (
|
||||
APP_NAME = "AnyLink"
|
||||
APP_VER = "0.0.6"
|
||||
APP_VER = "0.1.0"
|
||||
)
|
||||
|
@@ -39,7 +39,8 @@ type ServerConfig struct {
|
||||
DbFile string `toml:"db_file" info:"数据库地址"`
|
||||
CertFile string `toml:"cert_file" 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:"日志等级"`
|
||||
Issuer string `toml:"issuer" info:"系统名称"`
|
||||
AdminUser string `toml:"admin_user" info:"管理用户名"`
|
||||
@@ -82,6 +83,7 @@ func initServerCfg() {
|
||||
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.DownFilesPath = getAbsPath(base, Cfg.DownFilesPath)
|
||||
|
||||
if len(Cfg.JwtSecret) < 20 {
|
||||
|
@@ -8,6 +8,7 @@ db_file = "./data.db"
|
||||
#证书文件
|
||||
cert_file = "./vpn_cert.pem"
|
||||
cert_key = "./vpn_cert.key"
|
||||
ui_path = "../ui"
|
||||
down_files_path = "../down_files"
|
||||
|
||||
log_level = "info"
|
||||
@@ -21,7 +22,7 @@ admin_pass = "$2a$10$UQ7C.EoPifDeJh6d8.31TeSPQU7hM/NOM2nixmBucJpAuXDQNqNke"
|
||||
jwt_secret = ""
|
||||
|
||||
|
||||
#vpn服务对外地址
|
||||
#vpn服务对外地址,影响开通邮件二维码
|
||||
link_addr = "vpn.xx.com"
|
||||
|
||||
#前台服务监听地址
|
||||
|
@@ -4,7 +4,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bjdgyc/anylink/base"
|
||||
@@ -19,12 +18,15 @@ type GroupLinkAcl struct {
|
||||
// 自上而下匹配 默认 allow * *
|
||||
Action string `json:"action"` // allow、deny
|
||||
Val string `json:"val"`
|
||||
Port uint8 `json:"port"`
|
||||
IpNet *net.IPNet `json:"-"`
|
||||
Port uint16 `json:"port"`
|
||||
IpNet *net.IPNet `json:"ip_net"`
|
||||
Note string `json:"note"`
|
||||
}
|
||||
|
||||
type ValData struct {
|
||||
Val string `json:"val"`
|
||||
Val string `json:"val"`
|
||||
IpMask string `json:"ip_mask"`
|
||||
Note string `json:"note"`
|
||||
}
|
||||
|
||||
type Group struct {
|
||||
@@ -70,15 +72,18 @@ func SetGroup(g *Group) error {
|
||||
}
|
||||
}
|
||||
if len(clientDns) == 0 {
|
||||
return errors.New("DNS错误")
|
||||
return errors.New("DNS 错误")
|
||||
}
|
||||
g.ClientDns = clientDns
|
||||
|
||||
routeInclude := []ValData{}
|
||||
for _, v := range g.RouteInclude {
|
||||
if v.Val != "" {
|
||||
v1, _ := parseIpNet(v.Val)
|
||||
vn := ValData{Val: v1}
|
||||
ipMask, _, err := parseIpNet(v.Val)
|
||||
if err != nil {
|
||||
return errors.New("RouteInclude 错误" + err.Error())
|
||||
}
|
||||
vn := ValData{Val: v.Val, IpMask: ipMask}
|
||||
routeInclude = append(routeInclude, vn)
|
||||
}
|
||||
}
|
||||
@@ -86,8 +91,11 @@ func SetGroup(g *Group) error {
|
||||
routeExclude := []ValData{}
|
||||
for _, v := range g.RouteExclude {
|
||||
if v.Val != "" {
|
||||
v1, _ := parseIpNet(v.Val)
|
||||
vn := ValData{Val: v1}
|
||||
ipMask, _, err := parseIpNet(v.Val)
|
||||
if err != nil {
|
||||
return errors.New("RouteExclude 错误" + err.Error())
|
||||
}
|
||||
vn := ValData{Val: v.Val, IpMask: ipMask}
|
||||
routeExclude = append(routeExclude, vn)
|
||||
}
|
||||
}
|
||||
@@ -96,13 +104,13 @@ func SetGroup(g *Group) error {
|
||||
linkAcl := []GroupLinkAcl{}
|
||||
for _, v := range g.LinkAcl {
|
||||
if v.Val != "" {
|
||||
v1, v2 := parseIpNet(v.Val)
|
||||
if v2 != nil {
|
||||
vn := v
|
||||
vn.Val = v1
|
||||
vn.IpNet = v2
|
||||
linkAcl = append(linkAcl, vn)
|
||||
_, ipNet, err := parseIpNet(v.Val)
|
||||
if err != nil {
|
||||
return errors.New("GroupLinkAcl 错误" + err.Error())
|
||||
}
|
||||
vn := v
|
||||
vn.IpNet = ipNet
|
||||
linkAcl = append(linkAcl, vn)
|
||||
}
|
||||
}
|
||||
g.LinkAcl = linkAcl
|
||||
@@ -113,24 +121,14 @@ func SetGroup(g *Group) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func parseIpNet(s string) (string, *net.IPNet) {
|
||||
ips := strings.Split(s, "/")
|
||||
if len(ips) != 2 {
|
||||
return "", nil
|
||||
}
|
||||
ip := net.ParseIP(ips[0])
|
||||
mask := net.ParseIP(ips[1])
|
||||
|
||||
if strings.Contains(ips[0], ".") {
|
||||
ip = ip.To4()
|
||||
mask = mask.To4()
|
||||
func parseIpNet(s string) (string, *net.IPNet, error) {
|
||||
ip, ipNet, err := net.ParseCIDR(s)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
ipmask := net.IPMask(mask)
|
||||
ip0 := ip.Mask(ipmask)
|
||||
mask := net.IP(ipNet.Mask)
|
||||
ipMask := fmt.Sprintf("%s/%s", ip, mask)
|
||||
|
||||
ipNetS := fmt.Sprintf("%s/%s", ip0, mask)
|
||||
ipNet := &net.IPNet{IP: ip0, Mask: ipmask}
|
||||
|
||||
return ipNetS, ipNet
|
||||
return ipMask, ipNet, nil
|
||||
}
|
||||
|
@@ -38,6 +38,7 @@ type SettingSmtp struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
From string `json:"from"`
|
||||
UseSSl bool `json:"use_ssl"`
|
||||
}
|
||||
|
||||
type SettingOther struct {
|
||||
|
19
deploy.sh
19
deploy.sh
@@ -1,23 +1,28 @@
|
||||
#!/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
|
||||
#国内可替换源加快速度
|
||||
#npm install --registry=https://registry.npm.taobao.org
|
||||
#npm run build --registry=https://registry.npm.taobao.org
|
||||
npm install
|
||||
npm run build
|
||||
|
||||
cd ../
|
||||
cp -r anylink-web/ui .
|
||||
go build -o anylink -ldflags "-X main.COMMIT_ID=`git rev-parse HEAD`"
|
||||
|
||||
#整理部署文件
|
||||
mkdir anylink-deploy
|
||||
cd anylink-deploy
|
||||
|
||||
cp -r ../anylink .
|
||||
cp -r ../conf .
|
||||
cp -r ../down_files .
|
||||
cp -r anylink anylink-deploy
|
||||
cp -r anylink-web/ui anylink-deploy
|
||||
cp -r conf anylink-deploy
|
||||
cp -r down_files anylink-deploy
|
||||
|
||||
#注意使用root权限运行
|
||||
#cd anylink-deploy
|
||||
#sudo ./anylink -conf="conf/server.toml"
|
||||
|
||||
|
@@ -49,12 +49,12 @@ func LinkCstp(conn net.Conn, cSess *sessdata.ConnSession) {
|
||||
switch hdata[6] {
|
||||
case 0x07: // KEEPALIVE
|
||||
// do nothing
|
||||
base.Debug("recv keepalive", cSess.IpAddr)
|
||||
// base.Debug("recv keepalive", cSess.IpAddr)
|
||||
case 0x05: // DISCONNECT
|
||||
base.Debug("DISCONNECT", cSess.IpAddr)
|
||||
return
|
||||
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) {
|
||||
return
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
@@ -86,11 +87,11 @@ func LinkTunnel(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
// 允许的路由
|
||||
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 {
|
||||
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地址租期
|
||||
@@ -130,8 +131,11 @@ func LinkTunnel(w http.ResponseWriter, r *http.Request) {
|
||||
// w.Header().Set("X-CSTP-Post-Auth-XML", ``)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
// h := w.Header().Clone()
|
||||
// h.Write(os.Stdout)
|
||||
h := w.Header().Clone()
|
||||
headers := make([]byte, 0)
|
||||
buf := bytes.NewBuffer(headers)
|
||||
h.Write(buf)
|
||||
base.Debug(string(buf.Bytes()))
|
||||
|
||||
hj := w.(http.Hijacker)
|
||||
conn, _, err := hj.Hijack()
|
||||
|
9
main.go
9
main.go
@@ -1,26 +1,23 @@
|
||||
// AnyLink 是一个企业级远程办公vpn软件,可以支持多人同时在线使用。
|
||||
|
||||
// +build linux
|
||||
|
||||
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 UiPath embed.FS
|
||||
|
||||
// 程序版本
|
||||
var COMMIT_ID string
|
||||
|
||||
func main() {
|
||||
base.CommitId = COMMIT_ID
|
||||
admin.UiPath = UiPath
|
||||
|
||||
base.Start()
|
||||
handler.Start()
|
||||
|
BIN
screenshot/qq.png
Normal file
BIN
screenshot/qq.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
@@ -296,6 +296,17 @@ func CloseSess(token string) {
|
||||
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) {
|
||||
stoken = strings.TrimSpace(stoken)
|
||||
sarr := strings.Split(stoken, "@")
|
||||
|
Reference in New Issue
Block a user