Compare commits

..

16 Commits

Author SHA1 Message Date
dependabot[bot]
6389cd860a
Bump gopkg.in/yaml.v3 from 3.0.0-20210107192922-496545a6307b to 3.0.0
Bumps gopkg.in/yaml.v3 from 3.0.0-20210107192922-496545a6307b to 3.0.0.

---
updated-dependencies:
- dependency-name: gopkg.in/yaml.v3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-04 07:25:48 +00:00
dependabot[bot]
59a8a91bd7
Bump wangyoucao577/go-release-action from 1.40 to 1.53 (#305)
Bumps [wangyoucao577/go-release-action](https://github.com/wangyoucao577/go-release-action) from 1.40 to 1.53.
- [Release notes](https://github.com/wangyoucao577/go-release-action/releases)
- [Commits](https://github.com/wangyoucao577/go-release-action/compare/v1.40...v1.53)

---
updated-dependencies:
- dependency-name: wangyoucao577/go-release-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-04 08:18:58 +01:00
dependabot[bot]
69fdc6b094
Bump alpine from 3.16 to 3.21 (#310)
Bumps alpine from 3.16 to 3.21.

---
updated-dependencies:
- dependency-name: alpine
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-04 08:10:30 +01:00
Maksim Nabokikh
75a7385d59
More CI fixes (#311)
Signed-off-by: maksim.nabokikh <max.nabokih@gmail.com>
2025-03-04 08:00:52 +01:00
Maksim Nabokikh
1191bf7f9d
Add workflows for the chart to CI (#302)
Signed-off-by: maksim.nabokikh <max.nabokih@gmail.com>
2025-03-04 07:24:24 +01:00
Paramoshka
a2c41756a5
Added the ability to set the validity period of the client certificate. (#301)
Signed-off-by: Paramoshka <parfenov_ivan_42a@mail.ru>
2025-03-03 13:45:33 +01:00
Izhikov Matvey
ac96942e1d
Ovpn user call and mgmt fixes + added new flag for init users db (#296) 2025-03-03 12:19:53 +01:00
Dmitry Shurupov
39f95e3d2c
Small fixes in the README.md Notes 2025-02-17 12:04:43 +07:00
Dmitry Shurupov
0680b4ff05
Merge pull request #293 from ogumemura/patch-1
Fix username validation regex to correctly recognize hyphen (-)
2025-01-10 11:31:42 +07:00
Dmitry Shurupov
a7aab7cb6a
docs: Fixing authors' links in README
Signed-off-by: Dmitry Shurupov <dmitry.shurupov@palark.com>
2024-11-14 15:48:12 +07:00
Dmitry Shurupov
3674d003c9
Moving repo to Palark
Signed-off-by: Dmitry Shurupov <dmitry.shurupov@palark.com>
2024-11-14 15:16:17 +07:00
Shobu UMEMURA
8fc518dba8
Fix username validation regex to correctly recognize hyphen (-)
This pull request corrects the regular expression used for username validation to correctly recognize hyphens (-).

Changes Made:
Changed the regex pattern from ^([a-zA-Z0-9_.-@])+$ to ^([a-zA-Z0-9_.\-@])+$.

Reason for Change:
In the previous regex, the hyphen (-) within the character class was interpreted as a range operator, not as a literal character. This caused usernames with hyphens to be incorrectly marked as invalid.
By escaping the hyphen (\-), the regex now correctly recognizes it as a literal character. This ensures that usernames containing hyphens are validated properly.

Points of Verification:
Confirmed that usernames containing hyphens are now correctly recognized and pass the validation.
Verified that other characters (letters, numbers, underscores, dots, and at signs) are still being properly validated.
2024-09-28 17:57:02 +09:00
Mike Klyuev
7134815ce6
Update README.md (#291)
Update current project status
2024-09-24 11:54:23 +03:00
Sprait
0c881c81e7
update start.sh
using compose v2
https://docs.docker.com/compose/migrate/#docker-compose-vs-docker-compose
2023-12-12 09:58:55 +03:00
Dmitry Shurupov
699cddc908
Fix the announcement blog link in README.md 2023-10-25 11:57:38 +07:00
Sprait
c83c581e21
fix revoke user (#243)
* fix reuse argument multiple times

* replace whitespace with tab
2023-09-12 11:05:28 +03:00
31 changed files with 483 additions and 177 deletions

25
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,25 @@
version: 2
updates:
# Dependencies listed in go.mod
- package-ecosystem: "gomod"
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
# Dependencies listed in .github/workflows/*.yml
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
# Dependencies listed in Dockerfile
- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "weekly"
# Dependencies listed in frontend/package.json
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"

17
.github/release.yml vendored Normal file
View File

@ -0,0 +1,17 @@
changelog:
exclude:
labels:
- ignore
categories:
- title: Enhancements 🚀
labels:
- enhancement
- title: Bug Fixes 🐛
labels:
- bug
- title: Dependency Updates ⬆️
labels:
- dependencies
- title: Other Changes
labels:
- "*"

35
.github/workflows/chart-release.yml vendored Normal file
View File

@ -0,0 +1,35 @@
name: Release Charts
on:
push:
branches:
- master
paths:
- 'charts/**'
jobs:
chart-release:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Configure Git
run: |
git config user.name "$GITHUB_ACTOR"
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
- name: Install Helm
uses: azure/setup-helm@v4
with:
version: v3.7.1
- name: Run chart-releaser
uses: helm/chart-releaser-action@v1.6.0
with:
charts_dir: charts
config: charts/cr.yaml
env:
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"

107
.github/workflows/chart-test.yaml vendored Normal file
View File

@ -0,0 +1,107 @@
name: Chart Test
on:
push:
tags:
- v*
branches:
- master
pull_request:
branches:
- master
jobs:
chart:
name: Chart
runs-on: ubuntu-latest
outputs:
changed: ${{ steps.changes.outputs.changed }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Helm
uses: azure/setup-helm@v4
with:
version: v3.10.3
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Set up chart-testing
uses: helm/chart-testing-action@v2.6.1
- name: Lint
run: ct lint
- name: Check generated docs
run: |
make docs
test "$(git diff --name-only)" == "" \
|| ( printf >&2 "\nREADME files are not up to date (run 'make docs'), differences:\n\n%s\n\n" "$(git diff)" ; exit 1 ; )
- name: Detect changes
id: changes
run: |
changed=$(ct list-changed)
if [[ -n "$changed" ]]; then
echo "changed=true" >> $GITHUB_OUTPUT
fi
chart-test:
name: Chart Test
runs-on: ubuntu-latest
needs: chart
if: needs.chart.outputs.changed == 'true'
strategy:
fail-fast: false
matrix:
kube: ["1.25", "1.29", "1.31"]
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Helm
uses: azure/setup-helm@v4
with:
version: v3.10.3
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Set up chart-testing
uses: helm/chart-testing-action@v2.6.1
# See https://github.com/kubernetes-sigs/kind/releases/tag/v0.17.0
- name: Determine KinD node image version
id: node_image
run: |
case ${{ matrix.kube }} in
1.25)
NODE_IMAGE=kindest/node:v1.25.3@sha256:f52781bc0d7a19fb6c405c2af83abfeb311f130707a0e219175677e366cc45d1 ;;
1.29)
NODE_IMAGE=kindest/node:v1.29.12@sha256:62c0672ba99a4afd7396512848d6fc382906b8f33349ae68fb1dbfe549f70dec ;;
1.31)
NODE_IMAGE=kindest/node:v1.31.2@sha256:0526eb5cd8d892ed79b56feb48d17eeee1f719f55d5c35cef468f053caffad32 ;;
esac
echo "image=$NODE_IMAGE" >> $GITHUB_OUTPUT
- name: Create KinD cluster
uses: helm/kind-action@v1.12.0
with:
version: v0.17.0
node_image: ${{ steps.node_image.outputs.image }}
- name: Test
run: ct install

View File

@ -1,35 +0,0 @@
name: Build and publish latest tag to Docker Hub (releases only)
on:
release:
types: [created]
jobs:
build:
name: build latest images for release
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASS }}
- name: Push openvpn image to Docker Hub
uses: docker/build-push-action@v4
with:
tags: flant/ovpn-admin:openvpn-latest
platforms: linux/amd64,linux/arm64,linux/arm
file: Dockerfile.openvpn
push: true
- name: Push ovpn-admin image to Docker Hub
uses: docker/build-push-action@v4
with:
tags: flant/ovpn-admin:latest
platforms: linux/amd64,linux/arm64,linux/arm
file: Dockerfile
push: true

View File

@ -1,39 +1,52 @@
name: Build and publish tags to Docker Hub (tags only) name: Build and publish tags to ghcr.io
on: on:
push: push:
tags: tags:
- '*' - v*
branches:
- master
pull_request:
branches:
- master
env:
WERF_STAGED_DOCKERFILE_VERSION: v2
# WERF_BUILDAH_MODE: auto
jobs: jobs:
build: build:
name: build images for tag name: build images for tag
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v2
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Get the version
id: get_version - uses: werf/actions/install@v1.2
run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//}
- name: Set up Docker Buildx - name: Login into ghcr.io
uses: docker/setup-buildx-action@v2 shell: bash
- name: Login to Docker Hub run: werf cr login -u ${{ github.actor }} -p ${{ github.token }} ghcr.io/${{ github.repository }}
uses: docker/login-action@v2
- name: Extract Docker metadata
uses: docker/metadata-action@v5.6.1
with: with:
username: ${{ secrets.DOCKER_USER }} images: ghcr.io/${{ github.repository }}/${{ matrix.name }}
password: ${{ secrets.DOCKER_PASS }}
- name: Push openvpn image to Docker Hub - name: Build Image
uses: docker/build-push-action@v4 if: ${{ github.event_name == 'pull_request' }}
with: run: |
tags: flant/ovpn-admin:openvpn-${{ steps.get_version.outputs.VERSION }} source "$(werf ci-env github --as-file)"
platforms: linux/amd64,linux/arm64,linux/arm source <(jq -r '.labels | to_entries | to_entries[] | "export WERF_EXPORT_ADD_LABEL_\(.key)=\"\(.value.key)=\(.value.value)\""' <<< $DOCKER_METADATA_OUTPUT_JSON)
file: Dockerfile.openvpn
push: true werf build --repo='' --final-repo=''
- name: Push ovpn-admin image to Docker Hub
uses: docker/build-push-action@v4 - name: Build and Push Image
with: if: ${{ github.event_name != 'pull_request' }}
tags: flant/ovpn-admin:${{ steps.get_version.outputs.VERSION }} run: |
platforms: linux/amd64,linux/arm64,linux/arm source "$(werf ci-env github --as-file)"
file: Dockerfile source <(jq -r '.labels | to_entries | to_entries[] | "export WERF_EXPORT_ADD_LABEL_\(.key)=\"\(.value.key)=\(.value.value)\""' <<< $DOCKER_METADATA_OUTPUT_JSON)
push: true
werf export --tag ghcr.io/${{ github.repository }}/%image%:${{ github.ref_name }}

View File

@ -17,7 +17,7 @@ jobs:
- name: checkout code - name: checkout code
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: build binaries - name: build binaries
uses: wangyoucao577/go-release-action@v1.40 uses: wangyoucao577/go-release-action@v1.53
with: with:
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
goversion: 1.17 goversion: 1.17

View File

@ -17,7 +17,7 @@ jobs:
- name: checkout code - name: checkout code
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: build binaries - name: build binaries
uses: wangyoucao577/go-release-action@v1.40 uses: wangyoucao577/go-release-action@v1.53
with: with:
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
goversion: 1.17 goversion: 1.17

6
.gitignore vendored
View File

@ -1,13 +1,11 @@
bin/
easyrsa easyrsa
easyrsa_master easyrsa_master
easyrsa_slave easyrsa_slave
ccd ccd
ccd_master ccd_master
ccd_slave ccd_slave
openvpn-web-ui*
openvpn-ui*
openvpn-admin*
ovpn-admin*
frontend/node_modules frontend/node_modules
main-packr.go main-packr.go

View File

@ -1,4 +1,4 @@
FROM alpine:3.16 FROM alpine:3.21
ARG TARGETARCH ARG TARGETARCH
RUN apk add --update bash openvpn easy-rsa iptables && \ RUN apk add --update bash openvpn easy-rsa iptables && \
ln -s /usr/share/easy-rsa/easyrsa /usr/local/bin && \ ln -s /usr/share/easy-rsa/easyrsa /usr/local/bin && \

View File

@ -1,15 +1,15 @@
FROM node:16-alpine3.15 AS frontend-builder FROM node:16-alpine3.15 AS frontend-builder
COPY frontend/ /app COPY ../frontend /app
RUN apk add --update python3 make g++ && cd /app && npm install && npm run build RUN apk add --update python3 make g++ && cd /app && npm install && npm run build
FROM golang:1.17.3-buster AS backend-builder FROM golang:1.17.3-buster AS backend-builder
RUN go install github.com/gobuffalo/packr/v2/packr2@latest RUN go install github.com/gobuffalo/packr/v2/packr2@latest
COPY --from=frontend-builder /app/static /app/frontend/static COPY --from=frontend-builder /app/static /app/frontend/static
COPY . /app COPY .. /app
ARG TARGETARCH ARG TARGETARCH
RUN cd /app && packr2 && env CGO_ENABLED=1 GOOS=linux GOARCH=${TARGETARCH} go build -a -tags netgo -ldflags '-linkmode external -extldflags -static -s -w' -o ovpn-admin && packr2 clean RUN cd /app && packr2 && env CGO_ENABLED=1 GOOS=linux GOARCH=${TARGETARCH} go build -a -tags netgo -ldflags '-linkmode external -extldflags -static -s -w' -o ovpn-admin && packr2 clean
FROM alpine:3.16 FROM alpine:3.21
WORKDIR /app WORKDIR /app
COPY --from=backend-builder /app/ovpn-admin /app COPY --from=backend-builder /app/ovpn-admin /app
ARG TARGETARCH ARG TARGETARCH

54
Makefile Normal file
View File

@ -0,0 +1,54 @@
export PATH := $(abspath bin/protoc/bin/):$(abspath bin/):${PATH}
export SHELL := env PATH=$(PATH) /bin/sh
GOOS?=$(shell go env GOOS)
GOARCH?=$(shell go env GOARCH)
GOLANGCI_VERSION = 1.55.2
HELM_DOCS_VERSION = 1.11.0
ifeq ($(GOARCH),arm)
ARCH=armv7
else
ARCH=$(GOARCH)
endif
COMMIT=$(shell git rev-parse --verify HEAD)
###########
# BUILDING
###########
###########
# LINTING
###########
bin/golangci-lint: bin/golangci-lint-${GOLANGCI_VERSION}
@ln -sf golangci-lint-${GOLANGCI_VERSION} bin/golangci-lint
bin/golangci-lint-${GOLANGCI_VERSION}:
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | BINARY=golangci-lint bash -s -- v${GOLANGCI_VERSION}
@mv bin/golangci-lint $@
###########
# HELM
###########
bin/helm-docs: bin/helm-docs-${HELM_DOCS_VERSION}
@ln -sf helm-docs-${HELM_DOCS_VERSION} bin/helm-docs
bin/helm-docs-${HELM_DOCS_VERSION}:
@mkdir -p bin
curl -L https://github.com/norwoodj/helm-docs/releases/download/v${HELM_DOCS_VERSION}/helm-docs_${HELM_DOCS_VERSION}_$(shell uname)_x86_64.tar.gz | tar -zOxf - helm-docs > ./bin/helm-docs-${HELM_DOCS_VERSION} && chmod +x ./bin/helm-docs-${HELM_DOCS_VERSION}
.PHONY: lint fix
lint: bin/golangci-lint
bin/golangci-lint run
fix: bin/golangci-lint
bin/golangci-lint run --fix
.PHONY: docs
docs: bin/helm-docs
bin/helm-docs -s file -c charts/ -t README.md.gotmpl
###########
# TESTING
###########

View File

@ -2,8 +2,6 @@
Simple web UI to manage OpenVPN users, their certificates & routes in Linux. While backend is written in Go, frontend is based on Vue.js. Simple web UI to manage OpenVPN users, their certificates & routes in Linux. While backend is written in Go, frontend is based on Vue.js.
Originally created in [Flant](https://flant.com/) for internal needs & used for years, then updated to be more modern and [publicly released](https://blog.flant.com/introducing-ovpn-admin-web-interface-for-openvpn/) in March'21. Your contributions are welcome!
***DISCLAIMER!** This project was created for experienced users (system administrators) and private (e.g., protected by network policies) environments only. Thus, it is not implemented with security in mind (e.g., it doesn't strictly check all parameters passed by users, etc.). It also relies heavily on files and fails if required files aren't available.* ***DISCLAIMER!** This project was created for experienced users (system administrators) and private (e.g., protected by network policies) environments only. Thus, it is not implemented with security in mind (e.g., it doesn't strictly check all parameters passed by users, etc.). It also relies heavily on files and fails if required files aren't available.*
## Features ## Features
@ -21,16 +19,16 @@ Originally created in [Flant](https://flant.com/) for internal needs & used for
### Screenshots ### Screenshots
Managing users in ovpn-admin: Managing users in ovpn-admin:
![ovpn-admin UI](https://raw.githubusercontent.com/flant/ovpn-admin/master/img/ovpn-admin-users.png) ![ovpn-admin UI](https://raw.githubusercontent.com/palark/ovpn-admin/master/img/ovpn-admin-users.png)
An example of dashboard made using ovpn-admin metrics: An example of dashboard made using ovpn-admin metrics:
![ovpn-admin metrics](https://raw.githubusercontent.com/flant/ovpn-admin/master/img/ovpn-admin-metrics.png) ![ovpn-admin metrics](https://raw.githubusercontent.com/palark/ovpn-admin/master/img/ovpn-admin-metrics.png)
## Installation ## Installation
### 1. Docker ### 1. Docker
There is a ready-to-use [docker-compose.yaml](https://github.com/flant/ovpn-admin/blob/master/docker-compose.yaml), so you can just change/add values you need and start it with [start.sh](https://github.com/flant/ovpn-admin/blob/master/start.sh). There is a ready-to-use [docker-compose.yaml](https://github.com/palark/ovpn-admin/blob/master/docker-compose.yaml), so you can just change/add values you need and start it with [start.sh](https://github.com/palark/ovpn-admin/blob/master/start.sh).
Requirements: Requirements:
You need [Docker](https://docs.docker.com/get-docker/) and [docker-compose](https://docs.docker.com/compose/install/) installed. You need [Docker](https://docs.docker.com/get-docker/) and [docker-compose](https://docs.docker.com/compose/install/) installed.
@ -38,7 +36,7 @@ You need [Docker](https://docs.docker.com/get-docker/) and [docker-compose](http
Commands to execute: Commands to execute:
```bash ```bash
git clone https://github.com/flant/ovpn-admin.git git clone https://github.com/palark/ovpn-admin.git
cd ovpn-admin cd ovpn-admin
./start.sh ./start.sh
``` ```
@ -56,7 +54,7 @@ Requirements. You need Linux with the following components installed:
Commands to execute: Commands to execute:
```bash ```bash
git clone https://github.com/flant/ovpn-admin.git git clone https://github.com/palark/ovpn-admin.git
cd ovpn-admin cd ovpn-admin
./bootstrap.sh ./bootstrap.sh
./build.sh ./build.sh
@ -67,18 +65,18 @@ cd ovpn-admin
### 3. Prebuilt binary ### 3. Prebuilt binary
You can also download and use prebuilt binaries from the [releases](https://github.com/flant/ovpn-admin/releases/latest) page — just choose a relevant tar.gz file. You can also download and use prebuilt binaries from the [releases](https://github.com/palark/ovpn-admin/releases/latest) page — just choose a relevant tar.gz file.
## Notes ## Notes
* this tool uses external calls for `bash`, `coreutils` and `easy-rsa`, thus **Linux systems only are supported** at the moment. * This tool uses external calls for `bash`, `coreutils` and `easy-rsa`, thus **Linux systems only are supported** at the moment.
* to enable additional password authentication provide `--auth` and `--auth.db="/etc/easyrsa/pki/users.db`" flags and install [openvpn-user](https://github.com/pashcovich/openvpn-user/releases/latest). This tool should be available in your `$PATH` and its binary should be executable (`+x`). * To enable additional password authentication, provide `--auth` and `--auth.db="/etc/easyrsa/pki/users.db`" flags and install [openvpn-user](https://github.com/pashcovich/openvpn-user/releases/latest). This tool should be available in your `$PATH` and its binary should be executable (`+x`).
* master-replica synchronization does not work with `--storage.backend=kubernetes.secrets` - **WIP** * If you use `--ccd` and `--ccd.path="/etc/openvpn/ccd"` and plan to use static address setup for users, do not forget to provide `--ovpn.network="172.16.100.0/24"` with valid openvpn-server network.
* additional password authentication does not work with `--storage.backend=kubernetes.secrets` - **WIP** * If you want to pass all the traffic generated by the user, you need to edit `ovpn-admin/templates/client.conf.tpl` and uncomment `redirect-gateway def1`.
* if you use `--ccd` and `--ccd.path="/etc/openvpn/ccd"` abd plan to use static address setup for users do not forget to provide `--ovpn.network="172.16.100.0/24"` with valid openvpn-server network * Tested with openvpn-server versions 2.4 and 2.5 and with tls-auth mode only.
* tested only with Openvpn-server versions 2.4 and 2.5 with only tls-auth mode * Not tested with Easy-RSA version > 3.0.8.
* not tested with EasyRsa version > 3.0.8 * Status of user connections update every 28 seconds.
* status of users connections update every 28 second(*no need to ask why =)*) * Master-replica synchronization and additional password authentication do not work with `--storage.backend=kubernetes.secrets` - **WIP**
## Usage ## Usage
@ -160,6 +158,9 @@ Flags:
--auth.db="./easyrsa/pki/users.db" --auth.db="./easyrsa/pki/users.db"
(or OVPN_AUTH_DB_PATH) database path for password authorization (or OVPN_AUTH_DB_PATH) database path for password authorization
--auth.db-init
(or OVPN_AUTH_DB_INIT) enable database init if user db not exists or size is 0
--log.level set log level: trace, debug, info, warn, error (default info) --log.level set log level: trace, debug, info, warn, error (default info)
(or LOG_LEVEL) (or LOG_LEVEL)
@ -172,6 +173,11 @@ Flags:
--version show application version --version show application version
``` ```
## Further information ## Authors
Please feel free to use [issues](https://github.com/flant/ovpn-admin/issues) and [discussions](https://github.com/flant/ovpn-admin/discussions) to get help from maintainers & community. ovpn-admin was originally created in [Flant](https://github.com/flant/) and used internally for years.
In March 2021, it [went public](https://medium.com/flant-com/introducing-ovpn-admin-a-web-interface-to-manage-openvpn-users-d81705ad8f23) and was still developed in Flant.
Namely, [@vitaliy-sn](https://github.com/vitaliy-sn) created its first version in Python, and [@pashcovich](https://github.com/pashcovich) rewrote it in Go.
In November 2024, this project was moved to [Palark](https://github.com/palark/), which is currently responsible for its maintenance and development.

View File

@ -8,7 +8,9 @@ import (
"crypto/x509/pkix" "crypto/x509/pkix"
"encoding/pem" "encoding/pem"
"errors" "errors"
"fmt"
"math/big" "math/big"
"strconv"
"time" "time"
) )
@ -60,7 +62,6 @@ func genPrivKey() (privKeyPEM *bytes.Buffer, err error) {
Bytes: privKeyPKCS8, Bytes: privKeyPKCS8,
}) })
return return
} }
@ -133,6 +134,17 @@ func genClientCert(privKey, caPrivKey *rsa.PrivateKey, ca *x509.Certificate, cn
serialNumberRange := new(big.Int).Lsh(big.NewInt(1), 128) serialNumberRange := new(big.Int).Lsh(big.NewInt(1), 128)
serial, err := rand.Int(rand.Reader, serialNumberRange) serial, err := rand.Int(rand.Reader, serialNumberRange)
certLifetimeDays, err := strconv.Atoi(*clientCertExpirationDays)
if err != nil {
return nil, fmt.Errorf("can't get client certificate expiration value: %w", err)
}
notBefore := time.Now()
notAfter := notBefore.Add(time.Duration(certLifetimeDays) * 24 * time.Hour)
if notAfter.After(ca.NotAfter) {
notAfter = ca.NotAfter
}
template := x509.Certificate{ template := x509.Certificate{
BasicConstraintsValid: true, BasicConstraintsValid: true,
DNSNames: []string{cn}, DNSNames: []string{cn},
@ -142,8 +154,8 @@ func genClientCert(privKey, caPrivKey *rsa.PrivateKey, ca *x509.Certificate, cn
}, },
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment, KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
NotBefore: time.Now(), NotBefore: notBefore,
NotAfter: ca.NotAfter, NotAfter: notAfter,
} }
issuerBytes, err := x509.CreateCertificate(rand.Reader, &template, ca, &privKey.PublicKey, caPrivKey) issuerBytes, err := x509.CreateCertificate(rand.Reader, &template, ca, &privKey.PublicKey, caPrivKey)

2
charts/cr.yaml Normal file
View File

@ -0,0 +1,2 @@
owner: palark
git-base-url: https://api.github.com/

View File

@ -0,0 +1,15 @@
apiVersion: v1
appVersion: "2.0.2"
description: Simple web UI to manage OpenVPN users, their certificates & routes in Linux. While backend is written in Go, frontend is based on Vue.js.
name: openvpn-admin
version: "0.0.2"
kubeVersion: ">=1.14.0-0"
maintainers:
- name: nabokihms
email: max.nabokih@gmail.com
url: github.com/nabokihms
sources:
- https://github.com/palark/openvpn-admin
keywords:
- kubernetes
- openvpn

View File

@ -0,0 +1,40 @@
# openvpn-admin
![Version: 0.0.2](https://img.shields.io/badge/Version-0.0.2-informational?style=flat-square) ![AppVersion: 2.0.2](https://img.shields.io/badge/AppVersion-2.0.2-informational?style=flat-square)
Simple web UI to manage OpenVPN users, their certificates & routes in Linux. While backend is written in Go, frontend is based on Vue.js.
## Maintainers
| Name | Email | Url |
| ---- | ------ | --- |
| nabokihms | <max.nabokih@gmail.com> | <github.com/nabokihms> |
## Source Code
* <https://github.com/palark/openvpn-admin>
## Requirements
Kubernetes: `>=1.14.0-0`
## Values
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| domain | string | `"changeme"` | |
| ovpnAdmin.repo | string | `"ghcr.io/palark/ovpn-admin/ovpn-admin"` | |
| ovpnAdmin.basicAuth.user | string | `"admin"` | |
| ovpnAdmin.basicAuth.password | string | `"changeme"` | |
| openvpn.repo | string | `"ghcr.io/palark/ovpn-admin/openvpn"` | |
| openvpn.subnet | string | `"172.16.200.0/255.255.255.0"` | |
| openvpn.inlet | string | `"HostPort"` | |
| openvpn.hostPort | int | `1194` | |
| nodeSelector | object | `{}` | [Node selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) configuration. |
| tolerations | list | `[]` | [Tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) for node taints. See the [API reference](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#scheduling) for details. |
| ingress.enabled | bool | `false` | Enable [ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/). |
| ingress.className | string | `""` | Ingress [class name](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class). |
| ingress.annotations | object | `{}` | Annotations to be added to the ingress. |
----------------------------------------------
Autogenerated from chart metadata using [helm-docs v1.11.0](https://github.com/norwoodj/helm-docs/releases/v1.11.0)

View File

@ -12,19 +12,19 @@ spec:
labels: labels:
app: openvpn app: openvpn
spec: spec:
{{- if .Values.openvpn.nodeSelector }} {{- if .Values.nodeSelector }}
nodeSelector: nodeSelector:
{{- .Values.openvpn.nodeSelector | toYaml | indent 8 | printf "\n%s" }} {{- .Values.nodeSelector | toYaml | indent 8 | printf "\n%s" }}
{{- end }} {{- end }}
{{- if .Values.openvpn.tolerations }} {{- if .Values.tolerations }}
tolerations: tolerations:
{{- .Values.openvpn.tolerations | toYaml | indent 8 | printf "\n%s" }} {{- .Values.tolerations | toYaml | indent 8 | printf "\n%s" }}
{{- end }} {{- end }}
terminationGracePeriodSeconds: 0 terminationGracePeriodSeconds: 0
serviceAccountName: openvpn serviceAccountName: openvpn
containers: containers:
- name: ovpn-admin - name: ovpn-admin
image: {{ .Values.ovpnAdmin.image }} image: {{ .Values.ovpnAdmin.repo }}:master
command: command:
- /bin/sh - /bin/sh
- -c - -c
@ -60,7 +60,7 @@ spec:
- name: ccd - name: ccd
mountPath: /mnt/ccd mountPath: /mnt/ccd
- name: openvpn - name: openvpn
image: {{ .Values.openvpn.image }} image: {{ .Values.ovpnAdmin.repo }}:master
command: [ '/entrypoint.sh' ] command: [ '/entrypoint.sh' ]
# imagePullPolicy: Always # imagePullPolicy: Always
securityContext: securityContext:

View File

@ -4,12 +4,17 @@ kind: Ingress
metadata: metadata:
name: ovpn-admin name: ovpn-admin
annotations: annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/backend-protocol: HTTP nginx.ingress.kubernetes.io/backend-protocol: HTTP
nginx.ingress.kubernetes.io/auth-type: basic nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-realm: "Authentication Required" nginx.ingress.kubernetes.io/auth-realm: "Authentication Required"
nginx.ingress.kubernetes.io/auth-secret: basic-auth nginx.ingress.kubernetes.io/auth-secret: basic-auth
{{- with .Values.ingress.annotations }}
{{- toYaml . | nindent 4 }}
{{- end }}
spec: spec:
{{- with .Values.ingress.className }}
ingressClassName: {{ . | quote }}
{{- end }}
tls: tls:
- hosts: - hosts:
- {{ .Values.domain }} - {{ .Values.domain }}
@ -25,15 +30,3 @@ spec:
name: ovpn-admin name: ovpn-admin
port: port:
name: http name: http
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: ovpn-admin
spec:
secretName: ingress-tls
dnsNames:
- {{ .Values.domain }}
issuerRef:
name: letsencrypt
kind: ClusterIssuer

View File

@ -0,0 +1,39 @@
domain: changeme
ovpnAdmin:
repo: ghcr.io/palark/ovpn-admin/ovpn-admin
basicAuth:
user: admin
password: changeme
openvpn:
repo: ghcr.io/palark/ovpn-admin/openvpn
subnet: 172.16.200.0/255.255.255.0
# LoadBalancer or ExternalIP or HostPort
inlet: HostPort
#
# If inlet: ExternalIP
# externalIP: 1.2.3.4
# externalPort: 1194
#
# If inlet: HostPort
hostPort: 1194
# Domain or ip for connect to OpenVPN server
# externalHost: 1.2.3.4
# -- [Node selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) configuration.
nodeSelector: {}
# -- [Tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) for node taints.
# See the [API reference](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#scheduling) for details.
tolerations: []
ingress:
# -- Enable [ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/).
enabled: false
# -- Ingress [class name](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class).
className: ""
# -- Annotations to be added to the ingress.
annotations: {}
# kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: "true"

View File

@ -1,2 +0,0 @@
name: ovpn-admin
version: 1.0.0

View File

@ -1 +0,0 @@
helm chart example

View File

@ -1,26 +0,0 @@
domain: changeme
ovpnAdmin:
image: changeme
basicAuth:
user: admin
password: changeme
openvpn:
image: changeme
subnet: 172.16.200.0/255.255.255.0
# nodeSelector:
# node-role.kubernetes.io/master: ""
# tolerations:
# - effect: NoSchedule
# key: node-role.kubernetes.io/master
#
# // LoadBalancer or ExternalIP or HostPort
inlet: HostPort
#
# If inlet: ExternalIP
# externalIP: 1.2.3.4
# externalPort: 1194
#
# If inlet: HostPort
hostPort: 1194
# Domain or ip for connect to OpenVPN server
# externalHost: 1.2.3.4

72
main.go
View File

@ -9,11 +9,7 @@ import (
"encoding/pem" "encoding/pem"
"errors" "errors"
"fmt" "fmt"
"github.com/google/uuid"
"io/ioutil" "io/ioutil"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"net" "net"
"net/http" "net/http"
"os" "os"
@ -25,6 +21,11 @@ import (
"time" "time"
"unicode/utf8" "unicode/utf8"
"github.com/google/uuid"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"github.com/gobuffalo/packr/v2" "github.com/gobuffalo/packr/v2"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
@ -34,7 +35,7 @@ import (
) )
const ( const (
usernameRegexp = `^([a-zA-Z0-9_.-@])+$` usernameRegexp = `^([a-zA-Z0-9_.\-@])+$`
passwordMinLength = 6 passwordMinLength = 6
certsArchiveFileName = "certs.tar.gz" certsArchiveFileName = "certs.tar.gz"
ccdArchiveFileName = "ccd.tar.gz" ccdArchiveFileName = "ccd.tar.gz"
@ -71,9 +72,11 @@ var (
ccdTemplatePath = kingpin.Flag("templates.ccd-path", "path to custom ccd.tpl").Default("").Envar("OVPN_TEMPLATES_CCD_PATH").String() ccdTemplatePath = kingpin.Flag("templates.ccd-path", "path to custom ccd.tpl").Default("").Envar("OVPN_TEMPLATES_CCD_PATH").String()
authByPassword = kingpin.Flag("auth.password", "enable additional password authentication").Default("false").Envar("OVPN_AUTH").Bool() authByPassword = kingpin.Flag("auth.password", "enable additional password authentication").Default("false").Envar("OVPN_AUTH").Bool()
authDatabase = kingpin.Flag("auth.db", "database path for password authentication").Default("./easyrsa/pki/users.db").Envar("OVPN_AUTH_DB_PATH").String() authDatabase = kingpin.Flag("auth.db", "database path for password authentication").Default("./easyrsa/pki/users.db").Envar("OVPN_AUTH_DB_PATH").String()
authDataBaseInit = kingpin.Flag("auth.db-init", "enable database initialization if db user not exists or size is 0").Default("false").Envar("OVPN_AUTH_DB_INIT").Bool()
logLevel = kingpin.Flag("log.level", "set log level: trace, debug, info, warn, error (default info)").Default("info").Envar("LOG_LEVEL").String() logLevel = kingpin.Flag("log.level", "set log level: trace, debug, info, warn, error (default info)").Default("info").Envar("LOG_LEVEL").String()
logFormat = kingpin.Flag("log.format", "set log format: text, json (default text)").Default("text").Envar("LOG_FORMAT").String() logFormat = kingpin.Flag("log.format", "set log format: text, json (default text)").Default("text").Envar("LOG_FORMAT").String()
storageBackend = kingpin.Flag("storage.backend", "storage backend: filesystem, kubernetes.secrets (default filesystem)").Default("filesystem").Envar("STORAGE_BACKEND").String() storageBackend = kingpin.Flag("storage.backend", "storage backend: filesystem, kubernetes.secrets (default filesystem)").Default("filesystem").Envar("STORAGE_BACKEND").String()
clientCertExpirationDays = kingpin.Flag("client-cert.expiration-days", "Expiration period of OpenVPN client certificates in days, the period will shrink automatically to the CA expiration period").Default("3650").Envar("CLIENT_CERT_EXPIRATION_DAYS").String()
certsArchivePath = "/tmp/" + certsArchiveFileName certsArchivePath = "/tmp/" + certsArchiveFileName
ccdArchivePath = "/tmp/" + ccdArchiveFileName ccdArchivePath = "/tmp/" + ccdArchiveFileName
@ -504,6 +507,10 @@ func main() {
*indexTxtPath = *easyrsaDirPath + "/pki/index.txt" *indexTxtPath = *easyrsaDirPath + "/pki/index.txt"
} }
if *authDataBaseInit {
ovpnUserInitDb()
}
ovpnAdmin := new(OvpnAdmin) ovpnAdmin := new(OvpnAdmin)
ovpnAdmin.lastSyncTime = "unknown" ovpnAdmin.lastSyncTime = "unknown"
@ -558,27 +565,27 @@ func main() {
static := CacheControlWrapper(http.FileServer(staticBox)) static := CacheControlWrapper(http.FileServer(staticBox))
http.Handle(*listenBaseUrl, http.StripPrefix(strings.TrimRight(*listenBaseUrl, "/"), static)) http.Handle(*listenBaseUrl, http.StripPrefix(strings.TrimRight(*listenBaseUrl, "/"), static))
http.HandleFunc(*listenBaseUrl + "api/server/settings", ovpnAdmin.serverSettingsHandler) http.HandleFunc(*listenBaseUrl+"api/server/settings", ovpnAdmin.serverSettingsHandler)
http.HandleFunc(*listenBaseUrl + "api/users/list", ovpnAdmin.userListHandler) http.HandleFunc(*listenBaseUrl+"api/users/list", ovpnAdmin.userListHandler)
http.HandleFunc(*listenBaseUrl + "api/user/create", ovpnAdmin.userCreateHandler) http.HandleFunc(*listenBaseUrl+"api/user/create", ovpnAdmin.userCreateHandler)
http.HandleFunc(*listenBaseUrl + "api/user/change-password", ovpnAdmin.userChangePasswordHandler) http.HandleFunc(*listenBaseUrl+"api/user/change-password", ovpnAdmin.userChangePasswordHandler)
http.HandleFunc(*listenBaseUrl + "api/user/rotate", ovpnAdmin.userRotateHandler) http.HandleFunc(*listenBaseUrl+"api/user/rotate", ovpnAdmin.userRotateHandler)
http.HandleFunc(*listenBaseUrl + "api/user/delete", ovpnAdmin.userDeleteHandler) http.HandleFunc(*listenBaseUrl+"api/user/delete", ovpnAdmin.userDeleteHandler)
http.HandleFunc(*listenBaseUrl + "api/user/revoke", ovpnAdmin.userRevokeHandler) http.HandleFunc(*listenBaseUrl+"api/user/revoke", ovpnAdmin.userRevokeHandler)
http.HandleFunc(*listenBaseUrl + "api/user/unrevoke", ovpnAdmin.userUnrevokeHandler) http.HandleFunc(*listenBaseUrl+"api/user/unrevoke", ovpnAdmin.userUnrevokeHandler)
http.HandleFunc(*listenBaseUrl + "api/user/config/show", ovpnAdmin.userShowConfigHandler) http.HandleFunc(*listenBaseUrl+"api/user/config/show", ovpnAdmin.userShowConfigHandler)
http.HandleFunc(*listenBaseUrl + "api/user/disconnect", ovpnAdmin.userDisconnectHandler) http.HandleFunc(*listenBaseUrl+"api/user/disconnect", ovpnAdmin.userDisconnectHandler)
http.HandleFunc(*listenBaseUrl + "api/user/statistic", ovpnAdmin.userStatisticHandler) http.HandleFunc(*listenBaseUrl+"api/user/statistic", ovpnAdmin.userStatisticHandler)
http.HandleFunc(*listenBaseUrl + "api/user/ccd", ovpnAdmin.userShowCcdHandler) http.HandleFunc(*listenBaseUrl+"api/user/ccd", ovpnAdmin.userShowCcdHandler)
http.HandleFunc(*listenBaseUrl + "api/user/ccd/apply", ovpnAdmin.userApplyCcdHandler) http.HandleFunc(*listenBaseUrl+"api/user/ccd/apply", ovpnAdmin.userApplyCcdHandler)
http.HandleFunc(*listenBaseUrl + "api/sync/last/try", ovpnAdmin.lastSyncTimeHandler) http.HandleFunc(*listenBaseUrl+"api/sync/last/try", ovpnAdmin.lastSyncTimeHandler)
http.HandleFunc(*listenBaseUrl + "api/sync/last/successful", ovpnAdmin.lastSuccessfulSyncTimeHandler) http.HandleFunc(*listenBaseUrl+"api/sync/last/successful", ovpnAdmin.lastSuccessfulSyncTimeHandler)
http.HandleFunc(*listenBaseUrl + downloadCertsApiUrl, ovpnAdmin.downloadCertsHandler) http.HandleFunc(*listenBaseUrl+downloadCertsApiUrl, ovpnAdmin.downloadCertsHandler)
http.HandleFunc(*listenBaseUrl + downloadCcdApiUrl, ovpnAdmin.downloadCcdHandler) http.HandleFunc(*listenBaseUrl+downloadCcdApiUrl, ovpnAdmin.downloadCcdHandler)
http.Handle(*metricsPath, promhttp.HandlerFor(ovpnAdmin.promRegistry, promhttp.HandlerOpts{})) http.Handle(*metricsPath, promhttp.HandlerFor(ovpnAdmin.promRegistry, promhttp.HandlerOpts{}))
http.HandleFunc(*listenBaseUrl + "ping", func(w http.ResponseWriter, r *http.Request) { http.HandleFunc(*listenBaseUrl+"ping", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "pong") fmt.Fprintf(w, "pong")
}) })
@ -854,7 +861,7 @@ func (oAdmin *OvpnAdmin) getCcd(username string) Ccd {
} }
func checkStaticAddressIsFree(staticAddress string, username string) bool { func checkStaticAddressIsFree(staticAddress string, username string) bool {
o := runBash(fmt.Sprintf("grep -rl ' %s ' %s | grep -vx %s/%s | wc -l", staticAddress, *ccdDir, *ccdDir, username)) o := runBash(fmt.Sprintf("grep -rl ' %[1]s ' %[2]s | grep -vx %[2]s/%[3]s | wc -l", staticAddress, *ccdDir, username))
if strings.TrimSpace(o) == "0" { if strings.TrimSpace(o) == "0" {
return true return true
@ -1004,7 +1011,7 @@ func (oAdmin *OvpnAdmin) userCreate(username, password string) (bool, string) {
func (oAdmin *OvpnAdmin) userChangePassword(username, password string) (error, string) { func (oAdmin *OvpnAdmin) userChangePassword(username, password string) (error, string) {
if checkUserExist(username) { if checkUserExist(username) {
o := runBash(fmt.Sprintf("openvpn-user check --db.path %s --user %s | grep %s | wc -l", *authDatabase, username, username)) o := runBash(fmt.Sprintf("openvpn-user check --db.path %[1]s --user %[2]s | grep %[2]s | wc -l", *authDatabase, username))
log.Debug(o) log.Debug(o)
if err := validatePassword(password); err != nil { if err := validatePassword(password); err != nil {
@ -1048,12 +1055,12 @@ func (oAdmin *OvpnAdmin) userRevoke(username string) (error, string) {
log.Error(err) log.Error(err)
} }
} else { } else {
o := runBash(fmt.Sprintf("cd %s && echo yes | easyrsa revoke %s 1>/dev/null && %s gen-crl 1>/dev/null", *easyrsaDirPath, *easyrsaBinPath, username)) o := runBash(fmt.Sprintf("cd %[1]s && echo yes | %[2]s revoke %[3]s 1>/dev/null && %[2]s gen-crl 1>/dev/null", *easyrsaDirPath, *easyrsaBinPath, username))
log.Debugln(o) log.Debugln(o)
} }
if *authByPassword { if *authByPassword {
o := runBash(fmt.Sprintf("openvpn-user revoke --db-path %s --user %s", *authDatabase, username)) o := runBash(fmt.Sprintf("openvpn-user revoke --db.path %s --user %s", *authDatabase, username))
log.Debug(o) log.Debug(o)
} }
@ -1115,7 +1122,7 @@ func (oAdmin *OvpnAdmin) userUnrevoke(username string) (error, string) {
_ = runBash(fmt.Sprintf("cd %s && %s gen-crl 1>/dev/null", *easyrsaDirPath, *easyrsaBinPath)) _ = runBash(fmt.Sprintf("cd %s && %s gen-crl 1>/dev/null", *easyrsaDirPath, *easyrsaBinPath))
if *authByPassword { if *authByPassword {
o := runBash(fmt.Sprintf("openvpn-user restore --db-path %s --user %s", *authDatabase, username)) o := runBash(fmt.Sprintf("openvpn-user restore --db.path %s --user %s", *authDatabase, username))
log.Debug(o) log.Debug(o)
} }
@ -1340,7 +1347,7 @@ func (oAdmin *OvpnAdmin) mgmtGetActiveClients() []clientStatus {
break break
} }
oAdmin.mgmtRead(conn) // read welcome message oAdmin.mgmtRead(conn) // read welcome message
conn.Write([]byte("status\n")) conn.Write([]byte("status 1\n"))
activeClients = append(activeClients, oAdmin.mgmtConnectedUsersParser(oAdmin.mgmtRead(conn), srv)...) activeClients = append(activeClients, oAdmin.mgmtConnectedUsersParser(oAdmin.mgmtRead(conn), srv)...)
conn.Close() conn.Close()
} }
@ -1501,6 +1508,13 @@ func unArchiveCcd() {
} }
} }
func ovpnUserInitDb() {
if fi, err := os.Stat(*authDatabase); errors.Is(err, os.ErrNotExist) || fi.Size() == 0 {
i := runBash(fmt.Sprintf("openvpn-user --db.path %[1]s db-init && openvpn-user --db.path %[1]s db-migrate", *authDatabase))
log.Debug(i)
}
}
func (oAdmin *OvpnAdmin) syncDataFromMaster() { func (oAdmin *OvpnAdmin) syncDataFromMaster() {
retryCountMax := 3 retryCountMax := 3
certsDownloadFailed := true certsDownloadFailed := true

View File

@ -48,7 +48,7 @@ if [ ${OVPN_PASSWD_AUTH} = "true" ]; then
echo "auth-user-pass-verify /etc/openvpn/scripts/auth.sh via-file" | tee -a /etc/openvpn/openvpn.conf 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 "script-security 2" | tee -a /etc/openvpn/openvpn.conf
echo "verify-client-cert require" | 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 openvpn-user db-init --db.path=$EASY_RSA_LOC/pki/users.db && openvpn-user db-migrate --db.path=$EASY_RSA_LOC/pki/users.db
fi fi
[ -d $EASY_RSA_LOC/pki ] && chmod 755 $EASY_RSA_LOC/pki [ -d $EASY_RSA_LOC/pki ] && chmod 755 $EASY_RSA_LOC/pki

View File

@ -1,3 +1,3 @@
#!/usr/bin/env bash #!/usr/bin/env bash
docker-compose -p openvpn-master up -d --build docker compose -p openvpn-master up -d --build

View File

@ -1,10 +1,10 @@
project: ovpn-admin project: ovpn-admin
configVersion: 1 configVersion: 1
--- ---
image: ovpn-admin image: ovpn-admin
dockerfile: Dockerfile dockerfile: Dockerfile.ovpn-admin
context: .
--- ---
image: openvpn image: openvpn
dockerfile: Dockerfile.openvpn dockerfile: Dockerfile.openvpn
context: .