feat(docker/liserAllUsers): 增加docker部署方式,新增显示所有用户列表

This commit is contained in:
王永乐
2021-01-26 18:03:29 +08:00
parent 3cc518ea61
commit 3ca5ce2a99
6 changed files with 130 additions and 19 deletions

3
.gitignore vendored
View File

@@ -2,4 +2,5 @@ statik
openvpn-manager openvpn-manager
.idea .idea
.DS_Store .DS_Store
.vscode .vscode
target

View File

@@ -37,7 +37,19 @@ go get github.com/rakyll/statik
go install github.com/rakyll/statik go install github.com/rakyll/statik
statik -src=$PWD/public statik -src=$PWD/public
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o openvpn-manager main.go CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o openvpn-manager main.go
nohup ./openvpn-manager -host openvpn服务端主机IP地址 -port openvpn管理端口 -admin-passwd OpenVPN Manager管理员admin的密码 >> /var/log/openvpn-manager.log 2>&1 & nohup ./openvpn-manager -host openvpn服务端主机IP地址 -port openvpn管理端口 -admin-passwd OpenVPN Manager管理员admin的密码 -psw-file /etc/openvpn/server/psw-file >> /var/log/openvpn-manager.log 2>&1 &
```
## 3、docker方式部署
```bash
docker pull curiouser/openvpn-manager:v1
docker run \
-v $PWD/psw-file:/etc/openvpn/server/psw-file:ro \
-p 30080:9090 \
-it \
curiouser/openvpn-manager:v1 \
-host 172.16.1.2 -port 32099 -admin-passwd 12356789 -psw-file /etc/openvpn/server/psw-file
``` ```
# 四、TODO # 四、TODO
@@ -46,4 +58,5 @@ nohup ./openvpn-manager -host openvpn服务端主机IP地址 -port openvpn管理
- [ ] 增加显示最近五条登录日志 - [ ] 增加显示最近五条登录日志
- [ ] 增加新增用户的功能 - [ ] 增加新增用户的功能
- [ ] 增加对低版本OpenVPN的支持 - [ ] 增加对低版本OpenVPN的支持
- [ ] 增加Docker部署方式及二进制包下载集成 - [x] 增加Docker部署方式
- [x] 增加显示所有用户功能

26
docker/Dockerfile Normal file
View File

@@ -0,0 +1,26 @@
FROM alpine:3.11.5
ENV LANG=en_US.UTF-8 \
LANGUAGE=en_US.UTF-8 \
TZ=Asia/Shanghai
RUN sed -i "s/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g" /etc/apk/repositories \
&& apk add --no-cache dumb-init ca-certificates tzdata bash bash-doc bash-completion \
&& wget -q -O /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub \
&& wget -q https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.29-r0/glibc-2.29-r0.apk \
&& wget -q https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.29-r0/glibc-bin-2.29-r0.apk \
&& wget -q https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.29-r0/glibc-i18n-2.29-r0.apk \
&& apk add glibc-2.29-r0.apk glibc-bin-2.29-r0.apk glibc-i18n-2.29-r0.apk \
&& rm -rf /opt/jdk-8u241-linux-x64.tar.gz /usr/lib/jvm glibc-2.29-r0.apk glibc-bin-2.29-r0.apk glibc-i18n-2.29-r0.apk \
&& /usr/glibc-compat/bin/localedef --force --inputfile POSIX --charmap UTF-8 "$LANG" || true \
&& echo "export LANG=$LANG" > /etc/profile.d/locale.sh \
&& apk del glibc-i18n \
&& cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo "Asia/Shanghai" > /etc/timezone
ENTRYPOINT ["dumb-init", "--","/applications/openvpn-manager"]
COPY target /applications
WORKDIR /applications

11
docker/Makefile Normal file
View File

@@ -0,0 +1,11 @@
REGISTRY = docker.io
IMAGE_REPO = curiouser
IMAGE_NAME = openvpn-manager
IMAGE_VERSION = v1
all: build push
build:
rm -rf statik target && statik -src=public
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o target/openvpn-manager main.go
docker build --rm -f docker/Dockerfile -t ${REGISTRY}/${IMAGE_REPO}/${IMAGE_NAME}:${IMAGE_VERSION} .
push:
docker push ${REGISTRY}/${IMAGE_REPO}/${IMAGE_NAME}:${IMAGE_VERSION}

55
main.go
View File

@@ -1,13 +1,15 @@
//go:generate statik -src=./public //go:generate statik -src=$PWD/public
package main package main
import ( import (
_ "curiouser.com/openvpn-manager/statik" _ "curiouser.com/openvpn-manager/statik"
"encoding/csv"
"encoding/json" "encoding/json"
"flag" "flag"
"fmt" "fmt"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/rakyll/statik/fs" "github.com/rakyll/statik/fs"
"io/ioutil"
"log" "log"
"net" "net"
"net/http" "net/http"
@@ -32,13 +34,22 @@ type OnlineClients struct {
Onlineclients []ClientConInfo `json:"onlineclient"` Onlineclients []ClientConInfo `json:"onlineclient"`
} }
type User struct {
Username string `json:"username"`
}
type Users struct {
Users []User `json:"users"`
}
var ( var (
ip_reg = regexp.MustCompile(`(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}`) ip_reg = regexp.MustCompile(`(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}`)
port_reg = regexp.MustCompile(`\((.*?)\)`) port_reg = regexp.MustCompile(`\((.*?)\)`)
omhost string omhost string
omport string omport string
ompasswd string ompasswd string
omadminpassw string omadminpassw string
omvpnpswfile string
router *gin.Engine router *gin.Engine
authorizedRoute *gin.RouterGroup authorizedRoute *gin.RouterGroup
@@ -49,7 +60,8 @@ func init() {
flag.StringVar(&omhost, "host", "", "OpenVPN服务端地址") flag.StringVar(&omhost, "host", "", "OpenVPN服务端地址")
flag.StringVar(&omport, "port", "", "OpenVPN服务端管理端口默认为空") flag.StringVar(&omport, "port", "", "OpenVPN服务端管理端口默认为空")
flag.StringVar(&ompasswd, "passwd", "", "OpenVPN服务端管理端口密码") flag.StringVar(&ompasswd, "passwd", "", "OpenVPN服务端管理端口密码")
flag.StringVar(&omadminpassw,"admin-passwd","","OpenVPN Manager管理员admin的密码") flag.StringVar(&omadminpassw, "admin-passwd", "", "OpenVPN Manager管理员admin的密码")
flag.StringVar(&omvpnpswfile, "psw-file", "", "OpenVPN用于验证用户的密码文件")
flag.Parse() flag.Parse()
gin.SetMode(gin.ReleaseMode) gin.SetMode(gin.ReleaseMode)
@@ -64,8 +76,8 @@ func init() {
func main() { func main() {
if omhost == "" && omport == "" && omadminpassw == ""{ if omhost == "" && omport == "" && omadminpassw == "" && omvpnpswfile == "" {
fmt.Println("没有设置OpenVPN服务端的主机IP地址、管理端口及管理员密码请在启动命令后添加'-host''-port''-admin-passwd'参数设置") fmt.Println("没有设置OpenVPN服务端的主机IP地址、管理端口及管理员密码请在启动命令后添加'-host''-port''-admin-passwd''-omvpnpswfile'参数设置")
os.Exit(0) os.Exit(0)
} else if omhost == "" { } else if omhost == "" {
fmt.Println("OpenVPN服务端主机IP地址没有设置无法启动。请在启动命令后添加'-host'参数设置IP地址") fmt.Println("OpenVPN服务端主机IP地址没有设置无法启动。请在启动命令后添加'-host'参数设置IP地址")
@@ -73,9 +85,12 @@ func main() {
} else if omport == "" { } else if omport == "" {
fmt.Println("OpenVPN管理端口没有设置无法启动。请在启动命令后添加'-port'参数设置管理端口号") fmt.Println("OpenVPN管理端口没有设置无法启动。请在启动命令后添加'-port'参数设置管理端口号")
os.Exit(0) os.Exit(0)
}else if omadminpassw == "" { } else if omadminpassw == "" {
fmt.Println("OpenVPN Manager管理员admin用户的密码没有设置无法启动。请在启动命令后添加'-admin-passwd'参数进行设置") fmt.Println("OpenVPN Manager管理员admin用户的密码没有设置无法启动。请在启动命令后添加'-admin-passwd'参数进行设置")
os.Exit(0) os.Exit(0)
} else if omvpnpswfile == "" {
fmt.Println("OpenVPN用于验证用户的密码文件路径没有设置无法启动。请在启动命令后添加'-omvpnpswfile'参数进行设置")
os.Exit(0)
} }
statikFS, err := fs.New() statikFS, err := fs.New()
@@ -90,6 +105,7 @@ func main() {
}) })
authorizedRoute.StaticFS("/public", statikFS) authorizedRoute.StaticFS("/public", statikFS)
//authorizedRoute.StaticFS("/public",http.Dir("./public"))
authorizedRoute.StaticFile("/favicon.ico", "./public/favicon.ico") authorizedRoute.StaticFile("/favicon.ico", "./public/favicon.ico")
authorizedRoute.GET("/getOnlineClients", func(context *gin.Context) { authorizedRoute.GET("/getOnlineClients", func(context *gin.Context) {
@@ -125,6 +141,24 @@ func main() {
username := context.PostForm("username") username := context.PostForm("username")
_ = sendDataToSocket(omhost+":"+omport, "kill "+username) _ = sendDataToSocket(omhost+":"+omport, "kill "+username)
}) })
authorizedRoute.GET("/getAllUsers", func(context *gin.Context) {
filedata, err := ioutil.ReadFile(omvpnpswfile)
if err != nil {
fmt.Println("读取文件失败!")
}
r2 := csv.NewReader(strings.NewReader(string(filedata)))
ss, _ := r2.ReadAll()
sz := len(ss)
var euser *User
var eusers Users
// 循环取数据
for i := 0; i < sz; i++ {
euser = &User{Username: strings.Split(ss[i][0], " ")[0]}
eusers.Users = append(eusers.Users, *euser)
}
json, _ := json.Marshal(eusers)
context.JSON(http.StatusOK, string(json))
})
fmt.Println("OpenVPN Manager监听端口9090访问地址http://127.0.0.1:9090") fmt.Println("OpenVPN Manager监听端口9090访问地址http://127.0.0.1:9090")
router.Run(":9090") router.Run(":9090")
@@ -153,4 +187,3 @@ func sendDataToSocket(conf string, msg string) (resData []byte) {
return buf1 return buf1
} }

View File

@@ -8,7 +8,6 @@
<script type="text/javascript"> <script type="text/javascript">
let reg = /\\/g;
function getBytesSize(size) {//把字节转换成正常文件大小 function getBytesSize(size) {//把字节转换成正常文件大小
if (!size) return ""; if (!size) return "";
var num = 1024.00; //byte var num = 1024.00; //byte
@@ -22,7 +21,6 @@
return (size / Math.pow(num, 3)).toFixed(2) + "G"; //G return (size / Math.pow(num, 3)).toFixed(2) + "G"; //G
return (size / Math.pow(num, 4)).toFixed(2) + "T"; //T return (size / Math.pow(num, 4)).toFixed(2) + "T"; //T
} }
function formatDuring(mss){ function formatDuring(mss){
var days = parseInt(mss / ( 60 * 60 * 24)); var days = parseInt(mss / ( 60 * 60 * 24));
var hours = parseInt((mss % ( 60 * 60 * 24)) / ( 60 * 60)); var hours = parseInt((mss % ( 60 * 60 * 24)) / ( 60 * 60));
@@ -34,11 +32,11 @@
var ajaxObj = new XMLHttpRequest(); var ajaxObj = new XMLHttpRequest();
ajaxObj.open('get', '/getOnlineClients'); ajaxObj.open('get', '/getOnlineClients');
ajaxObj.send(); ajaxObj.send();
ajaxObj.responseType = 'json';
ajaxObj.onreadystatechange = function () { ajaxObj.onreadystatechange = function () {
if (ajaxObj.readyState == 4 && ajaxObj.status == 200) { if (ajaxObj.readyState == 4 && ajaxObj.status == 200) {
var originData = ajaxObj.responseText.replace(reg,'').substr(1) var res = this.response;
var originJsonData = originData.substring(0,originData.length - 1) var myObj = JSON.parse(res);
var myObj = JSON.parse(originJsonData);
var txt = ""; var txt = "";
for (let x=0;x < myObj.onlineclient.length;x++){ for (let x=0;x < myObj.onlineclient.length;x++){
var nowTs=parseInt(new Date().getTime()/1000) var nowTs=parseInt(new Date().getTime()/1000)
@@ -67,8 +65,30 @@
ajaxObj.open('post', "/kickOutClientByCN",false,); ajaxObj.open('post', "/kickOutClientByCN",false,);
ajaxObj.send(formData); ajaxObj.send(formData);
}; };
function getAllUsers(){
var xhr = new XMLHttpRequest();
xhr.open('get', "/getAllUsers");
xhr.send();
xhr.responseType = 'json';
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
var res = this.response;
var myObj = JSON.parse(res);
var usertxt = "";
for (let x=0;x < myObj.users.length;x++){
usertxt += "<tr>"
+ "<td>" + (x+1) + "</td>"
+ "<td>" + myObj.users[x].username + "</td>"
+ "</tr>";
console.log(myObj.users[x].username)
}
document.querySelector("#allusers").insertAdjacentHTML('afterBegin',usertxt)
}
}
}
window.onload=function() { window.onload=function() {
getOnlineClients(); getOnlineClients();
getAllUsers();
document.getElementById("onlineClientsTable").addEventListener('click', function (e) { document.getElementById("onlineClientsTable").addEventListener('click', function (e) {
if(e.target.className === 'kickOutBtn') { if(e.target.className === 'kickOutBtn') {
kickOutClientByCN(e.target.getAttribute('client_username')); kickOutClientByCN(e.target.getAttribute('client_username'));
@@ -76,11 +96,18 @@
}) })
} }
</script> </script>
<h1 align="center">OpenVPN在线用户列表</h1> <h1 align="center">OpenVPN在线用户列表</h1>
<table width="1300" height="100" border="2" cellpadding="15" cellspacing="0" align="center" > <table width="1300" height="100" border="2" cellpadding="15" cellspacing="0" align="center" >
<tr> <th>ID</th> <th>用户名</th> <th>客户端路由公网IP地址</th> <th>端口</th> <th>虚拟IP地址</th> <th>接收数据大小</th> <th>发送数据大小</th> <th>连接时间</th> <th>是否下线用户</th> </tr> <tr> <th>ID</th> <th>用户名</th> <th>客户端路由公网IP地址</th> <th>端口</th> <th>虚拟IP地址</th> <th>接收数据大小</th> <th>发送数据大小</th> <th>连接时间</th> <th>是否下线用户</th> </tr>
<tbody id="onlineClientsTable"> </tbody> <tbody id="onlineClientsTable"> </tbody>
</table> </table>
<h1 align="center">所有用户列表</h1>
<table width="100" height="100" border="2" cellpadding="15" cellspacing="0" align="center" >
<tr> <th></th> <th>用户名</th> </tr>
<tbody id="allusers"> </tbody>
</table>
</body> </body>
</html> </html>