From 9f8098ab030c8fa9b797d6db72abe0354bf7887d Mon Sep 17 00:00:00 2001 From: Ilya Sosnovsky Date: Tue, 17 Nov 2020 20:48:26 +0300 Subject: [PATCH] Addded master-slave mode for openvpn-admin --- .dockerignore | 4 + .gitignore | 13 +- .werffiles/configure.sh | 26 ++- client.conf.tpl | 5 +- docker-compose-slave.yaml | 30 +++ docker-compose.yaml | 16 +- frontend/src/main.js | 69 ++++-- frontend/static/index.html | 20 +- get-easyrsa-end-gen-certs.sh | 6 +- go.mod | 3 + go.sum | 393 +++++++++++++++++++++++++++++++++++ helpers.go | 107 ++++++++++ main.go | 300 ++++++++++++++++++-------- start-with-slave.sh | 4 + start.sh | 3 + 15 files changed, 860 insertions(+), 139 deletions(-) create mode 100644 docker-compose-slave.yaml create mode 100644 helpers.go create mode 100755 start-with-slave.sh create mode 100755 start.sh diff --git a/.dockerignore b/.dockerignore index 2bde916..47adc59 100644 --- a/.dockerignore +++ b/.dockerignore @@ -6,7 +6,11 @@ gen easyrsa +easyrsa_master +easyrsa_slave ccd +ccd_master +ccd_slave werf.yaml frontend/node_modules openvpn-web-ui diff --git a/.gitignore b/.gitignore index 73bae87..58800da 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,17 @@ easyrsa +easyrsa_master +easyrsa_slave ccd +ccd_master +ccd_slave openvpn-web-ui openvpn-ui openvpn-admin -frontend/node_modules \ No newline at end of file +frontend/node_modules + +# Editor directories and files +.idea +*.suo +*.ntvs* +*.njsproj +*.sln \ No newline at end of file diff --git a/.werffiles/configure.sh b/.werffiles/configure.sh index 7291b26..d9ab8a5 100644 --- a/.werffiles/configure.sh +++ b/.werffiles/configure.sh @@ -1,17 +1,27 @@ -#!/bin/bash +#!/usr/bin/env bash set -x + EASY_RSA_LOC="/etc/openvpn/easyrsa" SERVER_CERT="${EASY_RSA_LOC}/pki/issued/server.crt" cd $EASY_RSA_LOC if [ -e "$SERVER_CERT" ]; then - echo "found existing certs - reusing" + echo "Found existing certs - reusing" else - easyrsa init-pki - cp -R /usr/share/easy-rsa/* $EASY_RSA_LOC/pki - echo "ca" | easyrsa build-ca nopass - easyrsa build-server-full server nopass - easyrsa gen-dh - openvpn --genkey --secret ./pki/ta.key + if [ ${OPVN_ROLE:-"master"} = "slave" ]; then + echo "Waiting for syncing data from master" + while [ $(wget -q localhost/api/sync/last -O - | wc -m) -lt 1 ] + do + sleep 5 + done + else + echo "Generating new certs" + easyrsa init-pki + cp -R /usr/share/easy-rsa/* $EASY_RSA_LOC/pki + echo "ca" | easyrsa build-ca nopass + easyrsa build-server-full server nopass + easyrsa gen-dh + openvpn --genkey --secret ./pki/ta.key + fi fi easyrsa gen-crl diff --git a/client.conf.tpl b/client.conf.tpl index 870ed14..4ee096e 100644 --- a/client.conf.tpl +++ b/client.conf.tpl @@ -1,4 +1,7 @@ -remote {{ .Host }} {{ .Port }} tcp +{{- range $server := .Hosts }} +remote {{ $server.Host }} {{ $server.Port }} tcp +{{- end }} + verb 4 client nobind diff --git a/docker-compose-slave.yaml b/docker-compose-slave.yaml new file mode 100644 index 0000000..8169a87 --- /dev/null +++ b/docker-compose-slave.yaml @@ -0,0 +1,30 @@ +version: '3' + +services: + openvpn: + build: + context: . + dockerfile: Dockerfile.openvpn + image: openvpn:local + command: /etc/openvpn/setup/configure.sh + environment: + - OPVN_ROLE=slave + cap_add: + - NET_ADMIN + ports: + - 7778:1194 # for openvpn + - 8081:8080 # for openvpn-admin because of network_mode + volumes: + - ./easyrsa_slave:/etc/openvpn/easyrsa + - ./ccd_slave:/etc/openvpn/ccd + openvpn-admin: + 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" + environment: + - OPVN_SLAVE=1 + network_mode: service:openvpn + volumes: + - ./easyrsa_slave:/mnt/easyrsa + - ./ccd_slave:/mnt/ccd diff --git a/docker-compose.yaml b/docker-compose.yaml index b2d107a..3cf4c36 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -10,17 +10,17 @@ services: cap_add: - NET_ADMIN ports: - - 7777:1194 + - 7777:1194 # for openvpn + - 8080:8080 # for openvpn-admin because of network_mode volumes: - - ./easyrsa:/etc/openvpn/easyrsa - - ./ccd:/etc/openvpn/ccd + - ./easyrsa_master:/etc/openvpn/easyrsa + - ./ccd_master:/etc/openvpn/ccd openvpn-admin: build: context: . image: openvpn-admin:local - command: /app/openvpn-admin --debug --ovpn.network="172.16.100.0/22" --mgmt.host="openvpn" - ports: - - 8080:8080 + command: /app/openvpn-admin --debug --ovpn.network="172.16.100.0/22" --master.sync-token="TOKEN" + network_mode: service:openvpn volumes: - - ./easyrsa:/mnt/easyrsa - - ./ccd:/mnt/ccd \ No newline at end of file + - ./easyrsa_master:/mnt/easyrsa + - ./ccd_master:/mnt/ccd diff --git a/frontend/src/main.js b/frontend/src/main.js index baa1696..aeca17c 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -87,32 +87,45 @@ new Vue({ { name: 'u-revoke', label: 'Revoke', - showWhenStatus: 'Active' + showWhenStatus: 'Active', + showForServerRole: ['master'] }, { name: 'u-unrevoke', label: 'Unrevoke', - showWhenStatus: 'Revoked' + showWhenStatus: 'Revoked', + showForServerRole: ['master'] }, { name: 'u-show-config', label: 'Show config', - showWhenStatus: 'Active' + showWhenStatus: 'Active', + showForServerRole: ['master', 'slave'] }, { name: 'u-download-config', label: 'Download config', - showWhenStatus: 'Active' + showWhenStatus: 'Active', + showForServerRole: ['master', 'slave'] }, { name: 'u-edit-ccd', label: 'Edit routes', - showWhenStatus: 'Active' + showWhenStatus: 'Active', + showForServerRole: ['master'] + }, + { + name: 'u-edit-ccd', + label: 'Show routes', + showWhenStatus: 'Active', + showForServerRole: ['slave'] } ], filters: { hideRevoked: true, }, + serverRole: "master", + lastSync: "unknown", u: { newUserName: '', // newUserPassword: 'nopass', @@ -134,22 +147,19 @@ new Vue({ watch: { }, mounted: function () { - this.u_get_data(); + this.getUserData(); + this.getServerRole(); this.filters.hideRevoked = this.$cookies.isKey('hideRevoked') ? (this.$cookies.get('hideRevoked') == "true") : false }, created() { var _this = this; -// if (!_this.$cookies.isKey('hideRevoked')) { -// _this.$cookies.set('hideRevoked', true, -1); -// } - _this.$root.$on('u-revoke', function (msg) { var data = new URLSearchParams(); data.append('username', _this.username); axios.request(axios_cfg('api/user/revoke', data, 'form')) .then(function(response) { - _this.u_get_data(); + _this.getUserData(); }); }) _this.$root.$on('u-unrevoke', function () { @@ -157,7 +167,7 @@ new Vue({ data.append('username', _this.username); axios.request(axios_cfg('api/user/unrevoke', data, 'form')) .then(function(response) { - _this.u_get_data(); + _this.getUserData(); }); }) _this.$root.$on('u-show-config', function () { @@ -191,7 +201,7 @@ new Vue({ _this.u.ccd = response.data; }); }) - _this.$root.$on('u-disconnect-use', function () { + _this.$root.$on('u-disconnect-user', function () { _this.u.modalShowCcdVisible = true; var data = new URLSearchParams(); data.append('username', _this.username); @@ -202,8 +212,8 @@ new Vue({ }) }, computed: { - customAddressEnabled: function () { - return this.u.ccd.ClientAddress == "dynamic" + customAddressDisabled: function () { + return this.serverRole == "master" ? this.u.ccd.ClientAddress == "dynamic" : true }, ccdApplyStatusCssClass: function () { return this.u.ccdApplyStatus == 200 ? "alert-success" : "alert-danger" @@ -223,30 +233,43 @@ new Vue({ filteredRows: function() { if (this.filters.hideRevoked) { return this.rows.filter(function(account) { - return account.AccountStatus === "Active"; + return account.AccountStatus == "Active" }); } else { - return this.rows; + return this.rows } } }, methods: { rowStyleClassFn: function(row) { - return row.ConnectionStatus == 'Connected' ? 'connected-user' : '' ; + return row.ConnectionStatus == 'Connected' ? 'connected-user' : '' }, rowActionFn: function(e) { this.username = e.target.dataset.username; this.$root.$emit(e.target.dataset.name); }, - u_get_data: function() { + getUserData: function() { var _this = this; axios.request(axios_cfg('api/users/list')) + .then(function(response) { + _this.rows = response.data; + }); + }, + getServerRole: function() { + var _this = this; + axios.request(axios_cfg('api/server/role')) .then(function(response) { - _this.rows = response.data; + _this.serverRole = response.data.serverRole; + if (_this.serverRole == "slave") { + axios.request(axios_cfg('api/sync/last')) + .then(function(response) { + _this.lastSync = response.data; + }); + } }); }, - create_user: function() { + createUser: function() { var _this = this; _this.u.newUserCreateError = ""; @@ -257,7 +280,7 @@ new Vue({ axios.request(axios_cfg('api/user/create', data, 'form')) .then(function(response) { - _this.u_get_data(); + _this.getUserData(); _this.u.modalNewUserVisible = false; _this.u.newUserName = ''; // _this.u.newUserPassword = 'nopass'; @@ -266,7 +289,7 @@ new Vue({ _this.u.newUserCreateError = error.response.data; }); }, - ccd_apply: function() { + ccdApply: function() { var _this = this; _this.u.ccdApplyStatus = ""; diff --git a/frontend/static/index.html b/frontend/static/index.html index 0685972..b34ab3a 100644 --- a/frontend/static/index.html +++ b/frontend/static/index.html @@ -16,12 +16,14 @@ :row-style-class="rowStyleClassFn" :search-options="{ enabled: true}" >
- + + Slave - last sync: {{ lastSync }}

No users have been created yet.

- +
+