Additional password auth; Multiple mgmt interface usgae; Fixes; style changes;
This commit is contained in:
parent
bec0e738d1
commit
3614ab6ba5
14 changed files with 57 additions and 156 deletions
10
Dockerfile
10
Dockerfile
|
@ -1,22 +1,18 @@
|
|||
FROM golang:1.14.2-alpine3.11 AS backend-builder
|
||||
FROM golang:1.14.2-buster AS backend-builder
|
||||
COPY . /app
|
||||
#RUN apk --no-cache add build-base git gcc
|
||||
RUN cd /app && env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags='-extldflags "-static" -s -w' -o openvpn-admin
|
||||
RUN cd /app && env CGO_ENABLED=./1 GOOS=linux GOARCH=amd64 go build -ldflags='-linkmode external -extldflags "-static" -s -w' -o openvpn-admin
|
||||
|
||||
FROM node:14.2-alpine3.11 AS frontend-builder
|
||||
COPY frontend/ /app
|
||||
RUN cd /app && npm install && npm run build
|
||||
|
||||
FROM golang:1.14.2-buster AS user-builder
|
||||
RUN git clone https://github.com/pashcovich/openvpn-user /app && cd /app && env CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -ldflags='-linkmode external -extldflags "-static" -s -w' -o openvpn-user
|
||||
|
||||
FROM alpine:3.13
|
||||
WORKDIR /app
|
||||
COPY --from=backend-builder /app/openvpn-admin /app
|
||||
COPY --from=user-builder /app/openvpn-user /usr/local/bin
|
||||
COPY --from=frontend-builder /app/static /app/static
|
||||
COPY client.conf.tpl /app/client.conf.tpl
|
||||
COPY ccd.tpl /app/ccd.tpl
|
||||
RUN apk add --update bash easy-rsa && \
|
||||
ln -s /usr/share/easy-rsa/easyrsa /usr/local/bin && \
|
||||
wget https://github.com/pashcovich/openvpn-user/releases/download/v1.0.3-rc.1/openvpn-user-linux-amd64.tar.gz -O - | tar xz -C /usr/local/bin && \
|
||||
rm -rf /tmp/* /var/tmp/* /var/cache/apk/* /var/cache/distfiles/*
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
FROM golang:1.14.2-buster AS user-builder
|
||||
RUN git clone https://github.com/pashcovich/openvpn-user /app && cd /app && env CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -ldflags='-linkmode external -extldflags "-static" -s -w' -o openvpn-user
|
||||
|
||||
FROM alpine:3.13
|
||||
COPY --from=user-builder /app/openvpn-user /usr/local/bin
|
||||
RUN apk add --update bash openvpn easy-rsa && \
|
||||
ln -s /usr/share/easy-rsa/easyrsa /usr/local/bin && \
|
||||
wget https://github.com/pashcovich/openvpn-user/releases/download/v1.0.3-rc.1/openvpn-user-linux-amd64.tar.gz -O - | tar xz -C /usr/local/bin && \
|
||||
rm -rf /tmp/* /var/tmp/* /var/cache/apk/* /var/cache/distfiles/*
|
||||
COPY .werffiles /etc/openvpn/setup
|
||||
COPY setup/ /etc/openvpn/setup
|
||||
RUN chmod +x /etc/openvpn/setup/configure.sh
|
||||
|
|
2
build.sh
2
build.sh
|
@ -1,3 +1,3 @@
|
|||
#!/bin/bash
|
||||
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags='-extldflags "-static" -s -w' -o openvpn-admin
|
||||
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -ldflags "-linkmode external -extldflags -static -s -w" -o openvpn-admin
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{{- range $server := .Hosts }}
|
||||
remote {{ $server.Host }} {{ $server.Port }} tcp
|
||||
remote {{ $server.Host }} {{ $server.Port }} {{ $server.Protocol }}
|
||||
{{- end }}
|
||||
|
||||
verb 4
|
||||
|
|
|
@ -22,7 +22,7 @@ services:
|
|||
build:
|
||||
context: .
|
||||
image: openvpn-admin:local
|
||||
command: /app/openvpn-admin --debug --ovpn.network="172.16.100.0/22" --master.sync-token="TOKEN" --master.host="http://172.20.0.1:8080" --role="slave" --ovpn.host="127.0.0.1:7744" --ovpn.host="127.0.0.1:7778" --auth.password
|
||||
command: /app/openvpn-admin --debug --ovpn.network="172.16.100.0/22" --master.sync-token="TOKEN" --master.host="http://172.20.0.1:8080" --role="slave" --ovpn.server="127.0.0.1:7744" --ovpn.server="127.0.0.1:7778" --auth.password
|
||||
environment:
|
||||
- OPVN_SLAVE=1
|
||||
network_mode: service:openvpn
|
||||
|
|
5
frontend/package-lock.json
generated
5
frontend/package-lock.json
generated
|
@ -7838,6 +7838,11 @@
|
|||
"vue-style-loader": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"vue-notification": {
|
||||
"version": "1.3.20",
|
||||
"resolved": "https://registry.npmjs.org/vue-notification/-/vue-notification-1.3.20.tgz",
|
||||
"integrity": "sha512-vPj67Ah72p8xvtyVE8emfadqVWguOScAjt6OJDEUdcW5hW189NsqvfkOrctxHUUO9UYl9cTbIkzAEcPnHu+zBQ=="
|
||||
},
|
||||
"vue-style-loader": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.2.tgz",
|
||||
|
|
|
@ -14,7 +14,8 @@
|
|||
"vue": "^2.6.12",
|
||||
"vue-clipboard2": "^0.2.1",
|
||||
"vue-cookies": "^1.7.4",
|
||||
"vue-good-table": "^2.21.1"
|
||||
"vue-good-table": "^2.21.1",
|
||||
"vue-notification": "^1.3.20"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
|
|
|
@ -3,12 +3,14 @@ import axios from 'axios';
|
|||
import VueCookies from 'vue-cookies'
|
||||
import VueClipboard from 'vue-clipboard2'
|
||||
import VueGoodTablePlugin from 'vue-good-table'
|
||||
import Notifications from 'vue-notification'
|
||||
|
||||
import 'vue-good-table/dist/vue-good-table.css'
|
||||
|
||||
Vue.use(VueClipboard)
|
||||
Vue.use(VueGoodTablePlugin)
|
||||
Vue.use(VueCookies)
|
||||
Vue.use(Notifications)
|
||||
|
||||
var axios_cfg = function(url, data='', type='form') {
|
||||
if (data == '') {
|
||||
|
@ -182,6 +184,7 @@ new Vue({
|
|||
axios.request(axios_cfg('api/user/revoke', data, 'form'))
|
||||
.then(function(response) {
|
||||
_this.getUserData();
|
||||
_this.$notify({title: 'User ' + _this.username + ' revoked!', type: 'warn'})
|
||||
});
|
||||
})
|
||||
_this.$root.$on('u-unrevoke', function () {
|
||||
|
@ -190,6 +193,7 @@ new Vue({
|
|||
axios.request(axios_cfg('api/user/unrevoke', data, 'form'))
|
||||
.then(function(response) {
|
||||
_this.getUserData();
|
||||
_this.$notify({title: 'User ' + _this.username + ' unrevoked!', type: 'success'})
|
||||
});
|
||||
})
|
||||
_this.$root.$on('u-show-config', function () {
|
||||
|
@ -289,6 +293,15 @@ new Vue({
|
|||
_this.rows = response.data;
|
||||
});
|
||||
},
|
||||
|
||||
staticAddrCheckboxOnChange: function() {
|
||||
var staticAddrInput = document.getElementById('static-address');
|
||||
var staticAddrEnable = document.getElementById('enable-static');
|
||||
|
||||
staticAddrInput.disabled = !staticAddrEnable.checked;
|
||||
staticAddrInput.value == "dynamic" ? staticAddrInput.value = "" : staticAddrInput.value = "dynamic";
|
||||
},
|
||||
|
||||
getServerRole: function() {
|
||||
var _this = this;
|
||||
axios.request(axios_cfg('api/server/role'))
|
||||
|
@ -318,9 +331,12 @@ new Vue({
|
|||
_this.u.newUserName = '';
|
||||
_this.u.newUserPassword = '';
|
||||
_this.getUserData();
|
||||
_this.$notify({title: 'New user ' + _this.username + ' created', type: 'success'})
|
||||
})
|
||||
.catch(function(error) {
|
||||
_this.u.newUserCreateError = error.response.data;
|
||||
_this.$notify({title: 'New user ' + _this.username + ' creation failed.', type: 'error'})
|
||||
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -334,10 +350,12 @@ new Vue({
|
|||
.then(function(response) {
|
||||
_this.u.ccdApplyStatus = 200;
|
||||
_this.u.ccdApplyStatusMessage = response.data;
|
||||
_this.$notify({title: 'Ccd for user ' + _this.username + ' applied', type: 'success'})
|
||||
})
|
||||
.catch(function(error) {
|
||||
_this.u.ccdApplyStatus = error.response.status;
|
||||
_this.u.ccdApplyStatusMessage = error.response.data;
|
||||
_this.$notify({title: 'Ccd for user ' + _this.username + ' apply failed ', type: 'error'})
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -354,13 +372,14 @@ new Vue({
|
|||
.then(function(response) {
|
||||
_this.u.passwordChangeStatus = 200;
|
||||
_this.u.newPassword = '';
|
||||
_this.u.passwordChangeMessage = response.data.message;
|
||||
_this.getUserData();
|
||||
_this.u.modalChangePasswordVisible = false;
|
||||
_this.$notify({title: 'Password for user ' + _this.username + ' changed!', type: 'success'})
|
||||
})
|
||||
.catch(function(error) {
|
||||
_this.u.passwordChangeStatus = error.response.status;
|
||||
_this.u.passwordChangeMessage = error.response.data.message;
|
||||
_this.$notify({title: 'Changing password for user ' + _this.username + ' failed!', type: 'error'})
|
||||
});
|
||||
},
|
||||
}
|
||||
|
|
|
@ -89,7 +89,7 @@
|
|||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-success el-square modal-el-margin" v-on:click.stop="changeUserPassword(username)">Change password</button>
|
||||
<button type="button" class="btn btn-primary el-square d-flex justify-content-sm-end modal-el-margin" v-on:click.stop="u.newPassword='';u.modalChangePasswordVisible=false">Close</button>
|
||||
<button type="button" class="btn btn-primary el-square d-flex justify-content-sm-end modal-el-margin" v-on:click.stop="u.newPassword='';u.passwordChangeMessage='';u.modalChangePasswordVisible=false">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -127,10 +127,10 @@
|
|||
<h5 class="static-address-label ">Static address:</h5>
|
||||
<div class="input-group-prepend">
|
||||
<div class="input-group-text">
|
||||
<input id="enable-static" type="checkbox" onchange="document.getElementById('staticAddress').disabled=!this.checked;" v-if="serverRole == 'master'" v-bind:checked="!customAddressDisabled">
|
||||
<input id="enable-static" type="checkbox" @change="staticAddrCheckboxOnChange()" v-if="serverRole == 'master'" v-bind:checked="!customAddressDisabled">
|
||||
</div>
|
||||
</div>
|
||||
<input id="staticAddress" type="text" class="form-control" v-model="u.ccd.ClientAddress" placeholder="127.0.0.1" v-bind:disabled="customAddressDisabled">
|
||||
<input id="static-address" type="text" class="form-control" v-model="u.ccd.ClientAddress" placeholder="127.0.0.1" v-bind:disabled="customAddressDisabled">
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
@ -147,22 +147,22 @@
|
|||
<tbody>
|
||||
<tr v-for="(customRoute, index) in u.ccd.CustomRoutes">
|
||||
<td>
|
||||
<div v-if = "serverRole == 'slave'">
|
||||
<div v-if="serverRole == 'slave'">
|
||||
{{ customRoute.Address }}
|
||||
</div>
|
||||
<input v-if = "serverRole == 'master'" v-model = "customRoute.Address">
|
||||
<input v-if="serverRole == 'master'" v-model="customRoute.Address">
|
||||
</td>
|
||||
<td>
|
||||
<div v-if = "serverRole == 'slave'">
|
||||
<div v-if="serverRole == 'slave'">
|
||||
{{ customRoute.Mask }}
|
||||
</div>
|
||||
<input v-if = "serverRole == 'master'" v-model = "customRoute.Mask">
|
||||
<input v-if="serverRole == 'master'" v-model="customRoute.Mask">
|
||||
</td>
|
||||
<td>
|
||||
<div v-if = "serverRole == 'slave'">
|
||||
<div v-if="serverRole == 'slave'">
|
||||
{{ customRoute.Description }}
|
||||
</div>
|
||||
<input v-if = "serverRole == 'master'" v-model = "customRoute.Description">
|
||||
<input v-if="serverRole == 'master'" v-model="customRoute.Description">
|
||||
</td>
|
||||
<td class="text-right" v-if="serverRole == 'master'">
|
||||
<button type="button" class="btn btn-danger btn-sm el-square modal-el-margin" v-if="serverRole == 'master'" v-on:click.stop="u.ccd.CustomRoutes.splice(index, 1)">Delete</button>
|
||||
|
@ -193,6 +193,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<notifications position="bottom left" :speed="900" />
|
||||
</div>
|
||||
<script src="dist/build.js"></script>
|
||||
</body>
|
||||
|
|
22
main.go
22
main.go
|
@ -30,10 +30,10 @@ const (
|
|||
indexTxtDateLayout = "060102150405Z"
|
||||
stringDateFormat = "2006-01-02 15:04:05"
|
||||
ovpnStatusDateLayout = "Mon Jan 2 15:04:05 2006"
|
||||
version = "1.5.0"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
listenHost = kingpin.Flag("listen.host","host for openvpn-admin").Default("0.0.0.0").String()
|
||||
listenPort = kingpin.Flag("listen.port","port for openvpn-admin").Default("8080").String()
|
||||
serverRole = kingpin.Flag("role","server role master or slave").Default("master").HintOptions("master", "slave").String()
|
||||
|
@ -42,10 +42,9 @@ var (
|
|||
masterBasicAuthPassword = kingpin.Flag("master.basic-auth.password","password for basic auth on master server url").Default("").String()
|
||||
masterSyncFrequency = kingpin.Flag("master.sync-frequency", "master host data sync frequency in seconds.").Default("600").Int()
|
||||
masterSyncToken = kingpin.Flag("master.sync-token", "master host data sync security token").Default("justasimpleword").PlaceHolder("TOKEN").String()
|
||||
openvpnServer = kingpin.Flag("ovpn.host","host(s) for openvpn server").Default("127.0.0.1:7777").PlaceHolder("HOST:PORT").Strings()
|
||||
openvpnServer = kingpin.Flag("ovpn.server","comma separated addresses for openvpn servers").Default("127.0.0.1:7777").PlaceHolder("HOST:PORT").Strings()
|
||||
openvpnNetwork = kingpin.Flag("ovpn.network","network for openvpn server").Default("172.16.100.0/24").String()
|
||||
mgmtAddress = kingpin.Flag("mgmt","comma separated (alias=addresses) for openvpn servers mgmt interfaces").Default("main=127.0.0.1:8989").Strings()
|
||||
//mgmtListenPort = kingpin.Flag("mgmt.port","port for openvpn server mgmt interface").Default("8989").String()
|
||||
mgmtAddress = kingpin.Flag("mgmt","comma separated (alias=address) for openvpn servers mgmt interfaces").Default("main=127.0.0.1:8989").Strings()
|
||||
metricsPath = kingpin.Flag("metrics.path", "URL path for surfacing collected metrics").Default("/metrics").String()
|
||||
easyrsaDirPath = kingpin.Flag("easyrsa.path", "path to easyrsa dir").Default("/mnt/easyrsa").String()
|
||||
indexTxtPath = kingpin.Flag("easyrsa.index-path", "path to easyrsa index file.").Default("/mnt/easyrsa/pki/index.txt").String()
|
||||
|
@ -152,6 +151,7 @@ type OpenvpnAdmin struct {
|
|||
type OpenvpnServer struct {
|
||||
Host string
|
||||
Port string
|
||||
Protocol string
|
||||
}
|
||||
|
||||
type openvpnClientConfig struct {
|
||||
|
@ -366,7 +366,9 @@ func (oAdmin *OpenvpnAdmin) downloadCcdHandler(w http.ResponseWriter, r *http.Re
|
|||
}
|
||||
|
||||
func main() {
|
||||
kingpin.Parse()
|
||||
kingpin.Version(version)
|
||||
kingpin.Parse()
|
||||
|
||||
|
||||
ovpnAdmin := new(OpenvpnAdmin)
|
||||
ovpnAdmin.lastSyncTime = "unknown"
|
||||
|
@ -506,8 +508,8 @@ func (oAdmin *OpenvpnAdmin) renderClientConfig(username string) string {
|
|||
var hosts []OpenvpnServer
|
||||
|
||||
for _, server := range *openvpnServer {
|
||||
parts := strings.SplitN(server, ":",2)
|
||||
hosts = append(hosts, OpenvpnServer{Host: parts[0], Port: parts[1]})
|
||||
parts := strings.SplitN(server, ":",3)
|
||||
hosts = append(hosts, OpenvpnServer{Host: parts[0], Port: parts[1], Protocol: parts[2]})
|
||||
}
|
||||
|
||||
conf := openvpnClientConfig{}
|
||||
|
@ -947,7 +949,7 @@ func (oAdmin *OpenvpnAdmin) mgmtConnectedUsersParser(text, serverName string) []
|
|||
func (oAdmin *OpenvpnAdmin) mgmtKillUserConnection(username, serverName string) {
|
||||
conn, err := net.Dial("tcp", oAdmin.mgmtInterfaces[serverName])
|
||||
if err != nil {
|
||||
log.Println("ERROR: openvpn mgmt interface is not reachable")
|
||||
log.Printf("WARNING: openvpn mgmt interface for %s is not reachable by addr %s\n", serverName, oAdmin.mgmtInterfaces[serverName])
|
||||
return
|
||||
}
|
||||
oAdmin.mgmtRead(conn) // read welcome message
|
||||
|
@ -962,8 +964,8 @@ func (oAdmin *OpenvpnAdmin) mgmtGetActiveClients() []clientStatus {
|
|||
for srv, addr := range oAdmin.mgmtInterfaces {
|
||||
conn, err := net.Dial("tcp", addr)
|
||||
if err != nil {
|
||||
log.Printf("ERROR: openvpn mgmt interface for %s is not reachable by addr %s\n", srv, addr)
|
||||
//return []clientStatus{}
|
||||
log.Printf("WARNING: openvpn mgmt interface for %s is not reachable by addr %s\n", srv, addr)
|
||||
break
|
||||
}
|
||||
oAdmin.mgmtRead(conn) // read welcome message
|
||||
conn.Write([]byte("status\n"))
|
||||
|
|
120
werf.yaml
120
werf.yaml
|
@ -1,120 +0,0 @@
|
|||
project: openvpn-admin
|
||||
configVersion: 1
|
||||
deploy:
|
||||
helmRelease: "[[ project ]]-[[ env ]]"
|
||||
namespace: "[[ project ]]-[[ env ]]"
|
||||
|
||||
---
|
||||
artifact: backend-builder
|
||||
from: golang:1.14.2-alpine3.11
|
||||
git:
|
||||
- add: /
|
||||
to: /app
|
||||
stageDependencies:
|
||||
install:
|
||||
- "*.go"
|
||||
excludePaths:
|
||||
- .helm
|
||||
- .werf
|
||||
- frontend
|
||||
- werf.yaml
|
||||
- Dockerfile
|
||||
ansible:
|
||||
install:
|
||||
- name: Install packages
|
||||
apk:
|
||||
name:
|
||||
- build-base
|
||||
- gcc
|
||||
- name: Build backend
|
||||
command: go build -ldflags='-extldflags "-static" -s -w' -o openvpn-admin
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
GOOS: linux
|
||||
GOARCH: amd64
|
||||
args:
|
||||
chdir: /app
|
||||
|
||||
---
|
||||
artifact: frontend-builder
|
||||
from: node:14.2-alpine3.11
|
||||
git:
|
||||
- add: /frontend
|
||||
to: /app
|
||||
stageDependencies:
|
||||
install:
|
||||
- "**/*"
|
||||
excludePaths:
|
||||
- Dockerfile
|
||||
- build.sh
|
||||
- werf.yaml
|
||||
ansible:
|
||||
setup:
|
||||
- name: install deps
|
||||
command: npm install
|
||||
args:
|
||||
chdir: /app
|
||||
- name: Build app
|
||||
command: npm run build
|
||||
args:
|
||||
chdir: /app
|
||||
|
||||
---
|
||||
image: openvpn-admin
|
||||
from: alpine:3.11
|
||||
import:
|
||||
- artifact: backend-builder
|
||||
add: /app/openvpn-admin
|
||||
to: /usr/bin/openvpn-admin
|
||||
before: setup
|
||||
- artifact: frontend-builder
|
||||
add: /app/static
|
||||
to: /app/static
|
||||
before: setup
|
||||
git:
|
||||
- add: /client.conf.tpl
|
||||
to: /app/client.conf.tpl
|
||||
stageDependencies:
|
||||
setup:
|
||||
- "*"
|
||||
- add: /ccd.tpl
|
||||
to: /app/ccd.tpl
|
||||
stageDependencies:
|
||||
setup:
|
||||
- "*"
|
||||
ansible:
|
||||
install:
|
||||
- name: Install packages
|
||||
apk:
|
||||
name:
|
||||
- easy-rsa
|
||||
- bash
|
||||
- name: Create symbolic link for easy-rsa
|
||||
file:
|
||||
src: "/usr/share/easy-rsa/easyrsa"
|
||||
dest: "/usr/local/bin/easyrsa"
|
||||
state: link
|
||||
|
||||
---
|
||||
image: openvpn
|
||||
from: alpine:3.11
|
||||
git:
|
||||
- add: /.werffiles/
|
||||
to: /etc/openvpn/setup/
|
||||
stageDependencies:
|
||||
install:
|
||||
- "*"
|
||||
ansible:
|
||||
install:
|
||||
- name: Install packages
|
||||
apk:
|
||||
name:
|
||||
- openvpn
|
||||
- easy-rsa
|
||||
- name: Create symbolic link for easy-rsa
|
||||
file:
|
||||
src: "/usr/share/easy-rsa/easyrsa"
|
||||
dest: "/usr/local/bin/easyrsa"
|
||||
state: link
|
||||
|
||||
|
Loading…
Reference in a new issue