diff --git a/.dockerignore b/.dockerignore index 47adc59..a80bc64 100644 --- a/.dockerignore +++ b/.dockerignore @@ -16,3 +16,6 @@ frontend/node_modules openvpn-web-ui openvpn-ui openvpn-admin + +docker-compose.yaml +docker-compose-slave.yaml \ No newline at end of file diff --git a/.werffiles/auth.sh b/.werffiles/auth.sh new file mode 100644 index 0000000..66bb5a5 --- /dev/null +++ b/.werffiles/auth.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env sh + +PATH=$PATH:/usr/local/bin +set -e + +env + +auth_usr=$(head -1 $1) +auth_passwd=$(tail -1 $1) + +if [ $common_name = $username ]; then + openvpn-user auth --db.path /etc/openvpn/easyrsa/pki/users.db --user ${auth_usr} --password ${auth_passwd} +else + echo "Authorization failed" + exit 1 +fi \ No newline at end of file diff --git a/.werffiles/configure.sh b/.werffiles/configure.sh index f439248..c12eef8 100644 --- a/.werffiles/configure.sh +++ b/.werffiles/configure.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -set -x +set -ex EASY_RSA_LOC="/etc/openvpn/easyrsa" SERVER_CERT="${EASY_RSA_LOC}/pki/issued/server.crt" @@ -34,10 +34,20 @@ fi cp -f /etc/openvpn/setup/openvpn.conf /etc/openvpn/openvpn.conf +if [ ${OPVN_PASSWD_AUTH} = "true" ]; then + mkdir -p /etc/openvpn/scripts/ + cp -f /etc/openvpn/setup/auth.sh /etc/openvpn/scripts/auth.sh + chmod +x /etc/openvpn/scripts/auth.sh + echo "auth-user-pass-verify /etc/openvpn/scripts/auth.sh via-file" | tee -a /etc/openvpn/openvpn.conf + echo "script-security 2" | tee -a /etc/openvpn/openvpn.conf + echo "verify-client-cert require" | tee -a /etc/openvpn/openvpn.conf + openvpn-user db-init --db.path=$EASY_RSA_LOC/pki/users.db +fi + [ -d $EASY_RSA_LOC/pki ] && chmod 755 $EASY_RSA_LOC/pki [ -f $EASY_RSA_LOC/pki/crl.pem ] && chmod 644 $EASY_RSA_LOC/pki/crl.pem mkdir -p /etc/openvpn/ccd -openvpn --config /etc/openvpn/openvpn.conf --client-config-dir /etc/openvpn/ccd +openvpn --config /etc/openvpn/openvpn.conf --client-config-dir /etc/openvpn/ccd --port 1194 --proto tcp --management 127.0.0.1 8989 diff --git a/.werffiles/openvpn.conf b/.werffiles/openvpn.conf index 10d1c4f..785dcef 100644 --- a/.werffiles/openvpn.conf +++ b/.werffiles/openvpn.conf @@ -8,19 +8,18 @@ dh /etc/openvpn/easyrsa/pki/dh.pem crl-verify /etc/openvpn/easyrsa/pki/crl.pem tls-auth /etc/openvpn/easyrsa/pki/ta.key key-direction 0 -duplicate-cn cipher AES-128-CBC -management 127.0.0.1 8989 +#management 127.0.0.1 8989 keepalive 10 60 persist-key persist-tun topology subnet -proto tcp -port 1194 +#proto tcp +#port 1194 dev tun0 status /tmp/openvpn-status.log user nobody group nogroup push "topology subnet" push "route-metric 9999" -push "dhcp-option DNS 1.1.1.1" \ No newline at end of file +push "dhcp-option DNS 1.1.1.1" diff --git a/Dockerfile b/Dockerfile index cd785fc..29a8405 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,13 +7,16 @@ FROM node:14.2-alpine3.11 AS frontend-builder COPY frontend/ /app RUN cd /app && npm install && npm run build -FROM alpine:3.11 +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 echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing/" >> /etc/apk/repositories && \ - apk add --update bash easy-rsa && \ +RUN apk add --update bash easy-rsa && \ ln -s /usr/share/easy-rsa/easyrsa /usr/local/bin && \ rm -rf /tmp/* /var/tmp/* /var/cache/apk/* /var/cache/distfiles/* diff --git a/Dockerfile.openvpn b/Dockerfile.openvpn index 94a783b..f0c5072 100644 --- a/Dockerfile.openvpn +++ b/Dockerfile.openvpn @@ -1,6 +1,9 @@ -FROM alpine:3.11 -RUN echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing/" >> /etc/apk/repositories && \ - apk add --update bash openvpn easy-rsa && \ +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 && \ rm -rf /tmp/* /var/tmp/* /var/cache/apk/* /var/cache/distfiles/* COPY .werffiles /etc/openvpn/setup diff --git a/client.conf.tpl b/client.conf.tpl index 4ee096e..b5a8c6d 100644 --- a/client.conf.tpl +++ b/client.conf.tpl @@ -11,10 +11,19 @@ key-direction 1 #redirect-gateway def1 tls-client remote-cert-tls server -# for update resolv.conf on ubuntu -#script-security 2 system +# uncomment needed below lines for use with linux +#script-security 2 +# if use use resolved #up /etc/openvpn/update-resolv-conf #down /etc/openvpn/update-resolv-conf +# if you use systemd-resolved first install and openvpn-systemd-resolved package +#up /etc/openvpn/update-systemd-resolved +#down /etc/openvpn/update-systemd-resolved + +{{- if .PasswdAuth }} +auth-user-pass +{{- end }} + {{ .Cert -}} diff --git a/docker-compose-slave.yaml b/docker-compose-slave.yaml index 8169a87..e55a5f2 100644 --- a/docker-compose-slave.yaml +++ b/docker-compose-slave.yaml @@ -8,6 +8,7 @@ services: image: openvpn:local command: /etc/openvpn/setup/configure.sh environment: + - OPVN_PASSWD_AUTH=true - OPVN_ROLE=slave cap_add: - NET_ADMIN @@ -21,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" + 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 environment: - OPVN_SLAVE=1 network_mode: service:openvpn diff --git a/docker-compose.yaml b/docker-compose.yaml index 3cf4c36..3cb6fef 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -7,6 +7,8 @@ services: dockerfile: Dockerfile.openvpn image: openvpn:local command: /etc/openvpn/setup/configure.sh + environment: + - OPVN_PASSWD_AUTH=true cap_add: - NET_ADMIN ports: @@ -19,7 +21,7 @@ services: build: context: . image: openvpn-admin:local - command: /app/openvpn-admin --debug --ovpn.network="172.16.100.0/22" --master.sync-token="TOKEN" + command: /app/openvpn-admin --debug --ovpn.network="172.16.100.0/22" --master.sync-token="TOKEN" --auth.password network_mode: service:openvpn volumes: - ./easyrsa_master:/mnt/easyrsa diff --git a/frontend/src/main.js b/frontend/src/main.js index 1d8a995..15e9868 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -54,6 +54,11 @@ new Vue({ field: 'AccountStatus', filterable: true, }, + { + label: 'Connection Server', + field: 'ConnectionServer', + filterable: true, + }, { label: 'Expiration Date', field: 'ExpirationDate', @@ -84,39 +89,52 @@ new Vue({ ], rows: [], actions: [ + { + name: 'u-change-password', + label: 'Change password', + class: 'btn-warning', + showWhenStatus: 'Active', + showForServerRole: ['master'] + }, { name: 'u-revoke', label: 'Revoke', + class: 'btn-warning', showWhenStatus: 'Active', showForServerRole: ['master'] }, { name: 'u-unrevoke', label: 'Unrevoke', + class: 'btn-primary', showWhenStatus: 'Revoked', showForServerRole: ['master'] }, - { - name: 'u-show-config', - label: 'Show config', - showWhenStatus: 'Active', - showForServerRole: ['master', 'slave'] - }, + // { + // name: 'u-show-config', + // label: 'Show config', + // class: 'btn-primary', + // showWhenStatus: 'Active', + // showForServerRole: ['master', 'slave'] + // }, { name: 'u-download-config', label: 'Download config', + class: 'btn-info', showWhenStatus: 'Active', showForServerRole: ['master', 'slave'] }, { name: 'u-edit-ccd', label: 'Edit routes', + class: 'btn-primary', showWhenStatus: 'Active', showForServerRole: ['master'] }, { name: 'u-edit-ccd', label: 'Show routes', + class: 'btn-primary', showWhenStatus: 'Active', showForServerRole: ['slave'] } @@ -128,11 +146,15 @@ new Vue({ lastSync: "unknown", u: { newUserName: '', -// newUserPassword: 'nopass', + newUserPassword: '', newUserCreateError: '', + newPassword: '', + passwordChangeStatus: '', + passwordChangeMessage: '', modalNewUserVisible: false, modalShowConfigVisible: false, modalShowCcdVisible: false, + modalChangePasswordVisible: false, openvpnConfig: '', ccd: { Name: '', @@ -210,6 +232,11 @@ new Vue({ console.log(response.data); }); }) + _this.$root.$on('u-change-password', function () { + _this.u.modalChangePasswordVisible = true; + var data = new URLSearchParams(); + data.append('username', _this.username); + }) }, computed: { customAddressDisabled: function () { @@ -218,6 +245,9 @@ new Vue({ ccdApplyStatusCssClass: function () { return this.u.ccdApplyStatus == 200 ? "alert-success" : "alert-danger" }, + passwordChangeStatusCssClass: function () { + return this.u.passwordChangeStatus == 200 ? "alert-success" : "alert-danger" + }, modalNewUserDisplay: function () { return this.u.modalNewUserVisible ? {display: 'flex'} : {} }, @@ -227,6 +257,9 @@ new Vue({ modalShowCcdDisplay: function () { return this.u.modalShowCcdVisible ? {display: 'flex'} : {} }, + modalChangePasswordDisplay: function () { + return this.u.modalChangePasswordVisible ? {display: 'flex'} : {} + }, revokeFilterText: function() { return this.filters.hideRevoked ? "Show revoked" : "Hide revoked" }, @@ -269,6 +302,7 @@ new Vue({ } }); }, + createUser: function() { var _this = this; @@ -276,19 +310,20 @@ new Vue({ var data = new URLSearchParams(); data.append('username', _this.u.newUserName); -// data.append('password', this.u.newUserPassword); + data.append('password', _this.u.newUserPassword); axios.request(axios_cfg('api/user/create', data, 'form')) .then(function(response) { - _this.getUserData(); _this.u.modalNewUserVisible = false; _this.u.newUserName = ''; -// _this.u.newUserPassword = 'nopass'; + _this.u.newUserPassword = ''; + _this.getUserData(); }) .catch(function(error) { _this.u.newUserCreateError = error.response.data; }); }, + ccdApply: function() { var _this = this; @@ -304,6 +339,30 @@ new Vue({ _this.u.ccdApplyStatus = error.response.status; _this.u.ccdApplyStatusMessage = error.response.data; }); - } + }, + + changeUserPassword: function(user) { + var _this = this; + + _this.u.passwordChangeMessage = ""; + + var data = new URLSearchParams(); + data.append('username', user); + data.append('password', _this.u.newPassword); + + axios.request(axios_cfg('api/user/change-password', data, 'form')) + .then(function(response) { + _this.u.passwordChangeStatus = 200; + _this.u.newPassword = ''; + _this.u.passwordChangeMessage = response.data.message; + _this.getUserData(); + _this.u.modalChangePasswordVisible = false; + }) + .catch(function(error) { + _this.u.passwordChangeStatus = error.response.status; + _this.u.passwordChangeMessage = error.response.data.message; + }); + }, } + }) diff --git a/frontend/static/index.html b/frontend/static/index.html index 48dff1d..485469e 100644 --- a/frontend/static/index.html +++ b/frontend/static/index.html @@ -28,7 +28,7 @@