mirror of
https://github.com/flant/ovpn-admin.git
synced 2026-02-04 09:12:13 -08:00
Compare commits
27 Commits
126fd38f9e
...
2f8af807c7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f8af807c7 | ||
|
|
f08fae7f14 | ||
|
|
b7d1b3cad3 | ||
|
|
76faf8db73 | ||
|
|
59a8a91bd7 | ||
|
|
69fdc6b094 | ||
|
|
75a7385d59 | ||
|
|
1191bf7f9d | ||
|
|
a2c41756a5 | ||
|
|
ac96942e1d | ||
|
|
39f95e3d2c | ||
|
|
0680b4ff05 | ||
|
|
a7aab7cb6a | ||
|
|
3674d003c9 | ||
|
|
8fc518dba8 | ||
|
|
7134815ce6 | ||
|
|
0c881c81e7 | ||
|
|
699cddc908 | ||
|
|
c83c581e21 | ||
|
|
35f76ec3b6 | ||
|
|
8a35f70364 | ||
|
|
4981dcb919 | ||
|
|
dbc48ef3f1 | ||
|
|
dfe2f3d756 | ||
|
|
0ee9be5744 | ||
|
|
f73626dd7b | ||
|
|
633ad79d6a |
25
.github/dependabot.yml
vendored
Normal file
25
.github/dependabot.yml
vendored
Normal 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
17
.github/release.yml
vendored
Normal 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
35
.github/workflows/chart-release.yml
vendored
Normal 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
107
.github/workflows/chart-test.yaml
vendored
Normal 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
|
||||
30
.github/workflows/publish-latest.yaml
vendored
30
.github/workflows/publish-latest.yaml
vendored
@ -1,30 +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: Push openvpn image to Docker Hub
|
||||
uses: docker/build-push-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USER }}
|
||||
password: ${{ secrets.DOCKER_PASS }}
|
||||
repository: flant/ovpn-admin
|
||||
tags: openvpn-latest
|
||||
dockerfile: Dockerfile.openvpn
|
||||
- name: Push ovpn-admin image to Docker Hub
|
||||
uses: docker/build-push-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USER }}
|
||||
password: ${{ secrets.DOCKER_PASS }}
|
||||
repository: flant/ovpn-admin
|
||||
tags: latest
|
||||
dockerfile: Dockerfile
|
||||
58
.github/workflows/publish-tag.yaml
vendored
58
.github/workflows/publish-tag.yaml
vendored
@ -1,34 +1,52 @@
|
||||
name: Build and publish tags to Docker Hub (tags only)
|
||||
name: Build and publish tags to ghcr.io
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
- v*
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
env:
|
||||
WERF_STAGED_DOCKERFILE_VERSION: v2
|
||||
# WERF_BUILDAH_MODE: auto
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: build images for tag
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Get the version
|
||||
id: get_version
|
||||
run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//}
|
||||
- name: Push openvpn image to Docker Hub
|
||||
uses: docker/build-push-action@v1
|
||||
|
||||
- uses: werf/actions/install@v1.2
|
||||
|
||||
- name: Login into ghcr.io
|
||||
shell: bash
|
||||
run: werf cr login -u ${{ github.actor }} -p ${{ github.token }} ghcr.io/${{ github.repository }}
|
||||
|
||||
- name: Extract Docker metadata
|
||||
uses: docker/metadata-action@v5.6.1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USER }}
|
||||
password: ${{ secrets.DOCKER_PASS }}
|
||||
repository: flant/ovpn-admin
|
||||
tags: openvpn-${{ steps.get_version.outputs.VERSION }}
|
||||
dockerfile: Dockerfile.openvpn
|
||||
- name: Push ovpn-admin image to Docker Hub
|
||||
uses: docker/build-push-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USER }}
|
||||
password: ${{ secrets.DOCKER_PASS }}
|
||||
repository: flant/ovpn-admin
|
||||
tags: ${{ steps.get_version.outputs.VERSION }}
|
||||
dockerfile: Dockerfile
|
||||
images: ghcr.io/${{ github.repository }}/${{ matrix.name }}
|
||||
|
||||
- name: Build Image
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
run: |
|
||||
source "$(werf ci-env github --as-file)"
|
||||
source <(jq -r '.labels | to_entries | to_entries[] | "export WERF_EXPORT_ADD_LABEL_\(.key)=\"\(.value.key)=\(.value.value)\""' <<< $DOCKER_METADATA_OUTPUT_JSON)
|
||||
|
||||
werf build --repo='' --final-repo='' --secondary-repo "$WERF_REPO"
|
||||
|
||||
- name: Build and Push Image
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
run: |
|
||||
source "$(werf ci-env github --as-file)"
|
||||
source <(jq -r '.labels | to_entries | to_entries[] | "export WERF_EXPORT_ADD_LABEL_\(.key)=\"\(.value.key)=\(.value.value)\""' <<< $DOCKER_METADATA_OUTPUT_JSON)
|
||||
|
||||
werf export --tag ghcr.io/${{ github.repository }}/%image%:${{ github.ref_name }}
|
||||
|
||||
4
.github/workflows/release.yaml
vendored
4
.github/workflows/release.yaml
vendored
@ -17,10 +17,10 @@ jobs:
|
||||
- name: checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: build binaries
|
||||
uses: wangyoucao577/go-release-action@v1.28
|
||||
uses: wangyoucao577/go-release-action@v1.53
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
goversion: 1.17
|
||||
goversion: 1.23
|
||||
goos: ${{ matrix.goos }}
|
||||
goarch: ${{ matrix.goarch }}
|
||||
build_command: bash -ex ./build.sh
|
||||
|
||||
4
.github/workflows/release_arm.yaml
vendored
4
.github/workflows/release_arm.yaml
vendored
@ -17,10 +17,10 @@ jobs:
|
||||
- name: checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: build binaries
|
||||
uses: wangyoucao577/go-release-action@v1.28
|
||||
uses: wangyoucao577/go-release-action@v1.53
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
goversion: 1.17
|
||||
goversion: 1.23
|
||||
goos: ${{ matrix.goos }}
|
||||
goarch: ${{ matrix.goarch }}
|
||||
build_command: bash -ex ./build_arm.sh
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@ -1,13 +1,11 @@
|
||||
bin/
|
||||
|
||||
easyrsa
|
||||
easyrsa_master
|
||||
easyrsa_slave
|
||||
ccd
|
||||
ccd_master
|
||||
ccd_slave
|
||||
openvpn-web-ui*
|
||||
openvpn-ui*
|
||||
openvpn-admin*
|
||||
ovpn-admin*
|
||||
frontend/node_modules
|
||||
|
||||
main-packr.go
|
||||
|
||||
17
Dockerfile
17
Dockerfile
@ -1,17 +0,0 @@
|
||||
FROM node:16-alpine3.15 AS frontend-builder
|
||||
COPY frontend/ /app
|
||||
RUN cd /app && npm install && npm run build
|
||||
|
||||
FROM golang:1.17.3-buster AS backend-builder
|
||||
RUN go install github.com/gobuffalo/packr/v2/packr2@latest
|
||||
COPY --from=frontend-builder /app/static /app/frontend/static
|
||||
COPY . /app
|
||||
RUN cd /app && packr2 && env CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -a -tags netgo -ldflags '-linkmode external -extldflags -static -s -w' -o ovpn-admin && packr2 clean
|
||||
|
||||
FROM alpine:3.16
|
||||
WORKDIR /app
|
||||
COPY --from=backend-builder /app/ovpn-admin /app
|
||||
RUN apk add --update bash easy-rsa openssl openvpn coreutils && \
|
||||
ln -s /usr/share/easy-rsa/easyrsa /usr/local/bin && \
|
||||
wget https://github.com/pashcovich/openvpn-user/releases/download/v1.0.4/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,7 +1,9 @@
|
||||
FROM alpine:3.16
|
||||
FROM alpine:3.21
|
||||
ARG TARGETARCH
|
||||
RUN apk add --update bash openvpn easy-rsa iptables && \
|
||||
ln -s /usr/share/easy-rsa/easyrsa /usr/local/bin && \
|
||||
wget https://github.com/pashcovich/openvpn-user/releases/download/v1.0.4/openvpn-user-linux-amd64.tar.gz -O - | tar xz -C /usr/local/bin && \
|
||||
wget https://github.com/pashcovich/openvpn-user/releases/download/v1.0.4/openvpn-user-linux-${TARGETARCH}.tar.gz -O - | tar xz -C /usr/local/bin && \
|
||||
rm -rf /tmp/* /var/tmp/* /var/cache/apk/* /var/cache/distfiles/*
|
||||
RUN if [ -f "/usr/local/bin/openvpn-user-${TARGETARCH}" ]; then ln -s /usr/local/bin/openvpn-user-${TARGETARCH} /usr/local/bin/openvpn-user; fi
|
||||
COPY setup/ /etc/openvpn/setup
|
||||
RUN chmod +x /etc/openvpn/setup/configure.sh
|
||||
20
Dockerfile.ovpn-admin
Normal file
20
Dockerfile.ovpn-admin
Normal file
@ -0,0 +1,20 @@
|
||||
FROM node:16-alpine3.15 AS frontend-builder
|
||||
COPY ../frontend /app
|
||||
RUN apk add --update python3 make g++ && cd /app && npm install && npm run build
|
||||
|
||||
FROM golang:1.23.6-bullseye AS backend-builder
|
||||
RUN go install github.com/gobuffalo/packr/v2/packr2@latest
|
||||
COPY --from=frontend-builder /app/static /app/frontend/static
|
||||
COPY .. /app
|
||||
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
|
||||
|
||||
FROM alpine:3.21
|
||||
WORKDIR /app
|
||||
COPY --from=backend-builder /app/ovpn-admin /app
|
||||
ARG TARGETARCH
|
||||
RUN apk add --update bash easy-rsa openssl openvpn coreutils && \
|
||||
ln -s /usr/share/easy-rsa/easyrsa /usr/local/bin && \
|
||||
wget https://github.com/pashcovich/openvpn-user/releases/download/v1.0.4/openvpn-user-linux-${TARGETARCH}.tar.gz -O - | tar xz -C /usr/local/bin && \
|
||||
rm -rf /tmp/* /var/tmp/* /var/cache/apk/* /var/cache/distfiles/*
|
||||
RUN if [ -f "/usr/local/bin/openvpn-user-${TARGETARCH}" ]; then ln -s /usr/local/bin/openvpn-user-${TARGETARCH} /usr/local/bin/openvpn-user; fi
|
||||
54
Makefile
Normal file
54
Makefile
Normal 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
|
||||
###########
|
||||
45
README.md
45
README.md
@ -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.
|
||||
|
||||
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.*
|
||||
|
||||
## Features
|
||||
@ -21,16 +19,16 @@ Originally created in [Flant](https://flant.com/) for internal needs & used for
|
||||
### Screenshots
|
||||
|
||||
Managing users in ovpn-admin:
|
||||

|
||||

|
||||
|
||||
An example of dashboard made using ovpn-admin metrics:
|
||||

|
||||

|
||||
|
||||
## Installation
|
||||
|
||||
### 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:
|
||||
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:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/flant/ovpn-admin.git
|
||||
git clone https://github.com/palark/ovpn-admin.git
|
||||
cd ovpn-admin
|
||||
./start.sh
|
||||
```
|
||||
@ -56,7 +54,7 @@ Requirements. You need Linux with the following components installed:
|
||||
Commands to execute:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/flant/ovpn-admin.git
|
||||
git clone https://github.com/palark/ovpn-admin.git
|
||||
cd ovpn-admin
|
||||
./bootstrap.sh
|
||||
./build.sh
|
||||
@ -67,18 +65,18 @@ cd ovpn-admin
|
||||
|
||||
### 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
|
||||
* 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`).
|
||||
* master-replica synchronization does not work with `--storage.backend=kubernetes.secrets` - **WIP**
|
||||
* additional password authentication does not work with `--storage.backend=kubernetes.secrets` - **WIP**
|
||||
* 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 only with Openvpn-server versions 2.4 and 2.5 with only tls-auth mode
|
||||
* not tested with EasyRsa version > 3.0.8
|
||||
* status of users connections update every 28 second(*no need to ask why =)*)
|
||||
* 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`).
|
||||
* 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.
|
||||
* 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`.
|
||||
* Tested with openvpn-server versions 2.4 and 2.5 and with tls-auth mode only.
|
||||
* Not tested with Easy-RSA version > 3.0.8.
|
||||
* Status of user connections update every 28 seconds.
|
||||
* Master-replica synchronization and additional password authentication do not work with `--storage.backend=kubernetes.secrets` - **WIP**
|
||||
|
||||
## Usage
|
||||
|
||||
@ -94,6 +92,9 @@ Flags:
|
||||
--listen.port="8080" port for ovpn-admin
|
||||
(or OVPN_LISTEN_PORT)
|
||||
|
||||
--listen.base-url="/" base URL for ovpn-admin web files
|
||||
(or $OVPN_LISTEN_BASE_URL)
|
||||
|
||||
--role="master" server role, master or slave
|
||||
(or OVPN_ROLE)
|
||||
|
||||
@ -157,6 +158,9 @@ Flags:
|
||||
--auth.db="./easyrsa/pki/users.db"
|
||||
(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)
|
||||
(or LOG_LEVEL)
|
||||
|
||||
@ -169,6 +173,11 @@ Flags:
|
||||
--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.
|
||||
|
||||
@ -8,7 +8,9 @@ import (
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -60,7 +62,6 @@ func genPrivKey() (privKeyPEM *bytes.Buffer, err error) {
|
||||
Bytes: privKeyPKCS8,
|
||||
})
|
||||
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@ -133,6 +134,17 @@ func genClientCert(privKey, caPrivKey *rsa.PrivateKey, ca *x509.Certificate, cn
|
||||
serialNumberRange := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
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{
|
||||
BasicConstraintsValid: true,
|
||||
DNSNames: []string{cn},
|
||||
@ -142,8 +154,8 @@ func genClientCert(privKey, caPrivKey *rsa.PrivateKey, ca *x509.Certificate, cn
|
||||
},
|
||||
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: ca.NotAfter,
|
||||
NotBefore: notBefore,
|
||||
NotAfter: notAfter,
|
||||
}
|
||||
|
||||
issuerBytes, err := x509.CreateCertificate(rand.Reader, &template, ca, &privKey.PublicKey, caPrivKey)
|
||||
|
||||
2
charts/cr.yaml
Normal file
2
charts/cr.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
owner: palark
|
||||
git-base-url: https://api.github.com/
|
||||
15
charts/openvpn-admin/Chart.yaml
Normal file
15
charts/openvpn-admin/Chart.yaml
Normal 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.3"
|
||||
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
|
||||
40
charts/openvpn-admin/README.md
Normal file
40
charts/openvpn-admin/README.md
Normal file
@ -0,0 +1,40 @@
|
||||
# openvpn-admin
|
||||
|
||||
 
|
||||
|
||||
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 |
|
||||
|-----|------|---------|-------------|
|
||||
| ovpnAdmin.repo | string | `"ghcr.io/palark/ovpn-admin/ovpn-admin"` | |
|
||||
| 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. |
|
||||
| ingress.domain | string | `"changeme"` | |
|
||||
| ingress.basicAuth.user | string | `"admin"` | |
|
||||
| ingress.basicAuth.password | string | `"changeme"` | |
|
||||
|
||||
----------------------------------------------
|
||||
Autogenerated from chart metadata using [helm-docs v1.11.0](https://github.com/norwoodj/helm-docs/releases/v1.11.0)
|
||||
@ -12,19 +12,19 @@ spec:
|
||||
labels:
|
||||
app: openvpn
|
||||
spec:
|
||||
{{- if .Values.openvpn.nodeSelector }}
|
||||
{{- if .Values.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- .Values.openvpn.nodeSelector | toYaml | indent 8 | printf "\n%s" }}
|
||||
{{- .Values.nodeSelector | toYaml | indent 8 | printf "\n%s" }}
|
||||
{{- end }}
|
||||
{{- if .Values.openvpn.tolerations }}
|
||||
{{- if .Values.tolerations }}
|
||||
tolerations:
|
||||
{{- .Values.openvpn.tolerations | toYaml | indent 8 | printf "\n%s" }}
|
||||
{{- .Values.tolerations | toYaml | indent 8 | printf "\n%s" }}
|
||||
{{- end }}
|
||||
terminationGracePeriodSeconds: 0
|
||||
serviceAccountName: openvpn
|
||||
containers:
|
||||
- name: ovpn-admin
|
||||
image: {{ .Values.ovpnAdmin.image }}
|
||||
image: {{ .Values.ovpnAdmin.repo }}:master
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
@ -60,7 +60,7 @@ spec:
|
||||
- name: ccd
|
||||
mountPath: /mnt/ccd
|
||||
- name: openvpn
|
||||
image: {{ .Values.openvpn.image }}
|
||||
image: {{ .Values.ovpnAdmin.repo }}:master
|
||||
command: [ '/entrypoint.sh' ]
|
||||
# imagePullPolicy: Always
|
||||
securityContext:
|
||||
@ -4,18 +4,23 @@ kind: Ingress
|
||||
metadata:
|
||||
name: ovpn-admin
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: nginx
|
||||
nginx.ingress.kubernetes.io/backend-protocol: HTTP
|
||||
nginx.ingress.kubernetes.io/auth-type: basic
|
||||
nginx.ingress.kubernetes.io/auth-realm: "Authentication Required"
|
||||
nginx.ingress.kubernetes.io/auth-secret: basic-auth
|
||||
{{- with .Values.ingress.annotations }}
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- with .Values.ingress.className }}
|
||||
ingressClassName: {{ . | quote }}
|
||||
{{- end }}
|
||||
tls:
|
||||
- hosts:
|
||||
- {{ .Values.domain }}
|
||||
- {{ .Values.ingress.domain }}
|
||||
secretName: ingress-tls
|
||||
rules:
|
||||
- host: {{ .Values.domain }}
|
||||
- host: {{ .Values.ingress.domain }}
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
@ -25,15 +30,3 @@ spec:
|
||||
name: ovpn-admin
|
||||
port:
|
||||
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
|
||||
8
charts/openvpn-admin/templates/secret.yaml
Normal file
8
charts/openvpn-admin/templates/secret.yaml
Normal file
@ -0,0 +1,8 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: basic-auth
|
||||
type: Opaque
|
||||
data:
|
||||
auth: {{ print .Values.ingress.basicAuth.user ":{PLAIN}" .Values.ingress.basicAuth.password | b64enc | quote }}
|
||||
42
charts/openvpn-admin/values.yaml
Normal file
42
charts/openvpn-admin/values.yaml
Normal file
@ -0,0 +1,42 @@
|
||||
ovpnAdmin:
|
||||
repo: ghcr.io/palark/ovpn-admin/ovpn-admin
|
||||
|
||||
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"
|
||||
|
||||
domain: changeme
|
||||
|
||||
basicAuth:
|
||||
user: admin
|
||||
password: changeme
|
||||
@ -31,7 +31,7 @@
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "P0D6E4079E36703EB"
|
||||
"uid": "$ds_prometheus"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
@ -90,7 +90,7 @@
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "P0D6E4079E36703EB"
|
||||
"uid": "$ds_prometheus"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
@ -149,7 +149,7 @@
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "P0D6E4079E36703EB"
|
||||
"uid": "$ds_prometheus"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
@ -206,7 +206,7 @@
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "P0D6E4079E36703EB"
|
||||
"uid": "$ds_prometheus"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
@ -263,7 +263,7 @@
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "P0D6E4079E36703EB"
|
||||
"uid": "$ds_prometheus"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
@ -305,7 +305,7 @@
|
||||
},
|
||||
"textMode": "auto"
|
||||
},
|
||||
"pluginVersion": "8.5.2",
|
||||
"pluginVersion": "8.5.13",
|
||||
"targets": [
|
||||
{
|
||||
"expr": "ovpn_clients_expired",
|
||||
@ -320,7 +320,7 @@
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "P0D6E4079E36703EB"
|
||||
"uid": "$ds_prometheus"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
@ -381,7 +381,7 @@
|
||||
"dashes": false,
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "P0D6E4079E36703EB"
|
||||
"uid": "$ds_prometheus"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
@ -469,7 +469,7 @@
|
||||
"dashes": false,
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "P0D6E4079E36703EB"
|
||||
"uid": "$ds_prometheus"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
@ -557,7 +557,7 @@
|
||||
"dashes": false,
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "P0D6E4079E36703EB"
|
||||
"uid": "$ds_prometheus"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
@ -647,7 +647,7 @@
|
||||
"dashes": false,
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "P0D6E4079E36703EB"
|
||||
"uid": "$ds_prometheus"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
@ -733,7 +733,7 @@
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "P0D6E4079E36703EB"
|
||||
"uid": "$ds_prometheus"
|
||||
},
|
||||
"description": "value show last connection check time",
|
||||
"fieldConfig": {
|
||||
@ -794,7 +794,7 @@
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "P0D6E4079E36703EB"
|
||||
"uid": "$ds_prometheus"
|
||||
},
|
||||
"description": "value shows when connection was started",
|
||||
"fieldConfig": {
|
||||
@ -855,7 +855,7 @@
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "P0D6E4079E36703EB"
|
||||
"uid": "$ds_prometheus"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
@ -928,7 +928,26 @@
|
||||
"style": "dark",
|
||||
"tags": [],
|
||||
"templating": {
|
||||
"list": []
|
||||
"list": [
|
||||
{
|
||||
"current": {
|
||||
"selected": false,
|
||||
"text": "default",
|
||||
"value": "default"
|
||||
},
|
||||
"hide": 0,
|
||||
"includeAll": false,
|
||||
"multi": false,
|
||||
"label": "Prometheus",
|
||||
"name": "ds_prometheus",
|
||||
"options": [],
|
||||
"query": "prometheus",
|
||||
"refresh": 1,
|
||||
"regex": "",
|
||||
"skipUrlSync": false,
|
||||
"type": "datasource"
|
||||
}
|
||||
]
|
||||
},
|
||||
"time": {
|
||||
"from": "now-15m",
|
||||
|
||||
12
go.mod
12
go.mod
@ -1,6 +1,6 @@
|
||||
module ovpn-admin
|
||||
|
||||
go 1.17
|
||||
go 1.23
|
||||
|
||||
require (
|
||||
github.com/gobuffalo/packr/v2 v2.8.3
|
||||
@ -38,17 +38,17 @@ require (
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.32.1 // indirect
|
||||
github.com/prometheus/procfs v0.7.3 // indirect
|
||||
golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d // indirect
|
||||
golang.org/x/net v0.33.0 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 // indirect
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
golang.org/x/term v0.27.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0 // indirect
|
||||
k8s.io/klog/v2 v2.40.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20220114203427-a0453230fd26 // indirect
|
||||
k8s.io/utils v0.0.0-20211208161948-7d6a63dca704 // indirect
|
||||
|
||||
20
go.sum
20
go.sum
@ -485,8 +485,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d h1:1n1fc535VhN8SYtD4cDUyNlfpAF2ROMM9+11equK3hs=
|
||||
golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@ -513,8 +513,9 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@ -573,12 +574,13 @@ golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
|
||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@ -587,8 +589,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@ -785,8 +788,9 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA=
|
||||
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
||||
@ -1,2 +0,0 @@
|
||||
name: ovpn-admin
|
||||
version: 1.0.0
|
||||
@ -1 +0,0 @@
|
||||
helm chart example
|
||||
@ -1,8 +0,0 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: basic-auth
|
||||
type: Opaque
|
||||
data:
|
||||
auth: {{ print .Values.ovpnAdmin.basicAuth.user ":{PLAIN}" .Values.ovpnAdmin.basicAuth.password | b64enc | quote }}
|
||||
@ -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
|
||||
94
main.go
94
main.go
@ -9,11 +9,7 @@ import (
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/google/uuid"
|
||||
"io/ioutil"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
@ -25,6 +21,11 @@ import (
|
||||
"time"
|
||||
"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/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
@ -34,14 +35,14 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
usernameRegexp = `^([a-zA-Z0-9_.-@])+$`
|
||||
usernameRegexp = `^([a-zA-Z0-9_.\-@])+$`
|
||||
passwordMinLength = 6
|
||||
downloadCertsApiUrl = "/api/data/certs/download"
|
||||
downloadCcdApiUrl = "/api/data/ccd/download"
|
||||
certsArchiveFileName = "certs.tar.gz"
|
||||
ccdArchiveFileName = "ccd.tar.gz"
|
||||
indexTxtDateLayout = "060102150405Z"
|
||||
stringDateFormat = "2006-01-02 15:04:05"
|
||||
downloadCertsApiUrl = "api/data/certs/download"
|
||||
downloadCcdApiUrl = "api/data/ccd/download"
|
||||
|
||||
kubeNamespaceFilePath = "/var/run/secrets/kubernetes.io/serviceaccount/namespace"
|
||||
)
|
||||
@ -49,6 +50,7 @@ const (
|
||||
var (
|
||||
listenHost = kingpin.Flag("listen.host", "host for ovpn-admin").Default("0.0.0.0").Envar("OVPN_LISTEN_HOST").String()
|
||||
listenPort = kingpin.Flag("listen.port", "port for ovpn-admin").Default("8080").Envar("OVPN_LISTEN_PORT").String()
|
||||
listenBaseUrl = kingpin.Flag("listen.base-url", "base url for ovpn-admin").Default("/").Envar("OVPN_LISTEN_BASE_URL").String()
|
||||
serverRole = kingpin.Flag("role", "server role, master or slave").Default("master").Envar("OVPN_ROLE").HintOptions("master", "slave").String()
|
||||
masterHost = kingpin.Flag("master.host", "URL for the master server").Default("http://127.0.0.1").Envar("OVPN_MASTER_HOST").String()
|
||||
masterBasicAuthUser = kingpin.Flag("master.basic-auth.user", "user for master server's Basic Auth").Default("").Envar("OVPN_MASTER_USER").String()
|
||||
@ -63,15 +65,18 @@ var (
|
||||
metricsPath = kingpin.Flag("metrics.path", "URL path for exposing collected metrics").Default("/metrics").Envar("OVPN_METRICS_PATH").String()
|
||||
easyrsaDirPath = kingpin.Flag("easyrsa.path", "path to easyrsa dir").Default("./easyrsa").Envar("EASYRSA_PATH").String()
|
||||
indexTxtPath = kingpin.Flag("easyrsa.index-path", "path to easyrsa index file").Default("").Envar("OVPN_INDEX_PATH").String()
|
||||
easyrsaBinPath = kingpin.Flag("easyrsa.bin-path", "path to easyrsa script").Default("easyrsa").Envar("EASYRSA_BIN_PATH").String()
|
||||
ccdEnabled = kingpin.Flag("ccd", "enable client-config-dir").Default("false").Envar("OVPN_CCD").Bool()
|
||||
ccdDir = kingpin.Flag("ccd.path", "path to client-config-dir").Default("./ccd").Envar("OVPN_CCD_PATH").String()
|
||||
clientConfigTemplatePath = kingpin.Flag("templates.clientconfig-path", "path to custom client.conf.tpl").Default("").Envar("OVPN_TEMPLATES_CC_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()
|
||||
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()
|
||||
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()
|
||||
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
|
||||
ccdArchivePath = "/tmp/" + ccdArchiveFileName
|
||||
@ -502,6 +507,10 @@ func main() {
|
||||
*indexTxtPath = *easyrsaDirPath + "/pki/index.txt"
|
||||
}
|
||||
|
||||
if *authDataBaseInit {
|
||||
ovpnUserInitDb()
|
||||
}
|
||||
|
||||
ovpnAdmin := new(OvpnAdmin)
|
||||
|
||||
ovpnAdmin.lastSyncTime = "unknown"
|
||||
@ -555,32 +564,32 @@ func main() {
|
||||
staticBox := packr.New("static", "./frontend/static")
|
||||
static := CacheControlWrapper(http.FileServer(staticBox))
|
||||
|
||||
http.Handle("/", static)
|
||||
http.HandleFunc("/api/server/settings", ovpnAdmin.serverSettingsHandler)
|
||||
http.HandleFunc("/api/users/list", ovpnAdmin.userListHandler)
|
||||
http.HandleFunc("/api/user/create", ovpnAdmin.userCreateHandler)
|
||||
http.HandleFunc("/api/user/change-password", ovpnAdmin.userChangePasswordHandler)
|
||||
http.HandleFunc("/api/user/rotate", ovpnAdmin.userRotateHandler)
|
||||
http.HandleFunc("/api/user/delete", ovpnAdmin.userDeleteHandler)
|
||||
http.HandleFunc("/api/user/revoke", ovpnAdmin.userRevokeHandler)
|
||||
http.HandleFunc("/api/user/unrevoke", ovpnAdmin.userUnrevokeHandler)
|
||||
http.HandleFunc("/api/user/config/show", ovpnAdmin.userShowConfigHandler)
|
||||
http.HandleFunc("/api/user/disconnect", ovpnAdmin.userDisconnectHandler)
|
||||
http.HandleFunc("/api/user/statistic", ovpnAdmin.userStatisticHandler)
|
||||
http.HandleFunc("/api/user/ccd", ovpnAdmin.userShowCcdHandler)
|
||||
http.HandleFunc("/api/user/ccd/apply", ovpnAdmin.userApplyCcdHandler)
|
||||
http.Handle(*listenBaseUrl, http.StripPrefix(strings.TrimRight(*listenBaseUrl, "/"), static))
|
||||
http.HandleFunc(*listenBaseUrl+"api/server/settings", ovpnAdmin.serverSettingsHandler)
|
||||
http.HandleFunc(*listenBaseUrl+"api/users/list", ovpnAdmin.userListHandler)
|
||||
http.HandleFunc(*listenBaseUrl+"api/user/create", ovpnAdmin.userCreateHandler)
|
||||
http.HandleFunc(*listenBaseUrl+"api/user/change-password", ovpnAdmin.userChangePasswordHandler)
|
||||
http.HandleFunc(*listenBaseUrl+"api/user/rotate", ovpnAdmin.userRotateHandler)
|
||||
http.HandleFunc(*listenBaseUrl+"api/user/delete", ovpnAdmin.userDeleteHandler)
|
||||
http.HandleFunc(*listenBaseUrl+"api/user/revoke", ovpnAdmin.userRevokeHandler)
|
||||
http.HandleFunc(*listenBaseUrl+"api/user/unrevoke", ovpnAdmin.userUnrevokeHandler)
|
||||
http.HandleFunc(*listenBaseUrl+"api/user/config/show", ovpnAdmin.userShowConfigHandler)
|
||||
http.HandleFunc(*listenBaseUrl+"api/user/disconnect", ovpnAdmin.userDisconnectHandler)
|
||||
http.HandleFunc(*listenBaseUrl+"api/user/statistic", ovpnAdmin.userStatisticHandler)
|
||||
http.HandleFunc(*listenBaseUrl+"api/user/ccd", ovpnAdmin.userShowCcdHandler)
|
||||
http.HandleFunc(*listenBaseUrl+"api/user/ccd/apply", ovpnAdmin.userApplyCcdHandler)
|
||||
|
||||
http.HandleFunc("/api/sync/last/try", ovpnAdmin.lastSyncTimeHandler)
|
||||
http.HandleFunc("/api/sync/last/successful", ovpnAdmin.lastSuccessfulSyncTimeHandler)
|
||||
http.HandleFunc(downloadCertsApiUrl, ovpnAdmin.downloadCertsHandler)
|
||||
http.HandleFunc(downloadCcdApiUrl, ovpnAdmin.downloadCcdHandler)
|
||||
http.HandleFunc(*listenBaseUrl+"api/sync/last/try", ovpnAdmin.lastSyncTimeHandler)
|
||||
http.HandleFunc(*listenBaseUrl+"api/sync/last/successful", ovpnAdmin.lastSuccessfulSyncTimeHandler)
|
||||
http.HandleFunc(*listenBaseUrl+downloadCertsApiUrl, ovpnAdmin.downloadCertsHandler)
|
||||
http.HandleFunc(*listenBaseUrl+downloadCcdApiUrl, ovpnAdmin.downloadCcdHandler)
|
||||
|
||||
http.Handle(*metricsPath, promhttp.HandlerFor(ovpnAdmin.promRegistry, promhttp.HandlerOpts{}))
|
||||
http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
|
||||
http.HandleFunc(*listenBaseUrl+"ping", func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "pong")
|
||||
})
|
||||
|
||||
log.Printf("Bind: http://%s:%s", *listenHost, *listenPort)
|
||||
log.Printf("Bind: http://%s:%s%s", *listenHost, *listenPort, *listenBaseUrl)
|
||||
log.Fatal(http.ListenAndServe(*listenHost+":"+*listenPort, nil))
|
||||
}
|
||||
|
||||
@ -852,7 +861,7 @@ func (oAdmin *OvpnAdmin) getCcd(username string) Ccd {
|
||||
}
|
||||
|
||||
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" {
|
||||
return true
|
||||
@ -983,7 +992,7 @@ func (oAdmin *OvpnAdmin) userCreate(username, password string) (bool, string) {
|
||||
log.Error(err)
|
||||
}
|
||||
} else {
|
||||
o := runBash(fmt.Sprintf("cd %s && easyrsa build-client-full %s nopass 1>/dev/null", *easyrsaDirPath, username))
|
||||
o := runBash(fmt.Sprintf("cd %s && %s build-client-full %s nopass 1>/dev/null", *easyrsaDirPath, *easyrsaBinPath, username))
|
||||
log.Debug(o)
|
||||
}
|
||||
|
||||
@ -1002,7 +1011,7 @@ func (oAdmin *OvpnAdmin) userCreate(username, password string) (bool, string) {
|
||||
func (oAdmin *OvpnAdmin) userChangePassword(username, password string) (error, string) {
|
||||
|
||||
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)
|
||||
|
||||
if err := validatePassword(password); err != nil {
|
||||
@ -1046,12 +1055,12 @@ func (oAdmin *OvpnAdmin) userRevoke(username string) (error, string) {
|
||||
log.Error(err)
|
||||
}
|
||||
} else {
|
||||
o := runBash(fmt.Sprintf("cd %s && echo yes | easyrsa revoke %s 1>/dev/null && easyrsa gen-crl 1>/dev/null", *easyrsaDirPath, 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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@ -1110,10 +1119,10 @@ func (oAdmin *OvpnAdmin) userUnrevoke(username string) (error, string) {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
_ = runBash(fmt.Sprintf("cd %s && easyrsa gen-crl 1>/dev/null", *easyrsaDirPath))
|
||||
_ = runBash(fmt.Sprintf("cd %s && %s gen-crl 1>/dev/null", *easyrsaDirPath, *easyrsaBinPath))
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@ -1201,7 +1210,7 @@ func (oAdmin *OvpnAdmin) userRotate(username, newPassword string) (error, string
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
_ = runBash(fmt.Sprintf("cd %s && easyrsa gen-crl 1>/dev/null", *easyrsaDirPath))
|
||||
_ = runBash(fmt.Sprintf("cd %s && %s gen-crl 1>/dev/null", *easyrsaDirPath, *easyrsaBinPath))
|
||||
}
|
||||
crlFix()
|
||||
oAdmin.clients = oAdmin.usersList()
|
||||
@ -1233,7 +1242,7 @@ func (oAdmin *OvpnAdmin) userDelete(username string) (error, string) {
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
_ = runBash(fmt.Sprintf("cd %s && easyrsa gen-crl 1>/dev/null ", *easyrsaDirPath))
|
||||
_ = runBash(fmt.Sprintf("cd %s && %s gen-crl 1>/dev/null ", *easyrsaDirPath, *easyrsaBinPath))
|
||||
}
|
||||
crlFix()
|
||||
oAdmin.clients = oAdmin.usersList()
|
||||
@ -1338,7 +1347,7 @@ func (oAdmin *OvpnAdmin) mgmtGetActiveClients() []clientStatus {
|
||||
break
|
||||
}
|
||||
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)...)
|
||||
conn.Close()
|
||||
}
|
||||
@ -1437,7 +1446,7 @@ func (oAdmin *OvpnAdmin) downloadCerts() bool {
|
||||
}
|
||||
}
|
||||
|
||||
err := fDownload(certsArchivePath, *masterHost+downloadCertsApiUrl+"?token="+oAdmin.masterSyncToken, oAdmin.masterHostBasicAuth)
|
||||
err := fDownload(certsArchivePath, *masterHost+*listenBaseUrl+downloadCertsApiUrl+"?token="+oAdmin.masterSyncToken, oAdmin.masterHostBasicAuth)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return false
|
||||
@ -1454,7 +1463,7 @@ func (oAdmin *OvpnAdmin) downloadCcd() bool {
|
||||
}
|
||||
}
|
||||
|
||||
err := fDownload(ccdArchivePath, *masterHost+downloadCcdApiUrl+"?token="+oAdmin.masterSyncToken, oAdmin.masterHostBasicAuth)
|
||||
err := fDownload(ccdArchivePath, *masterHost+*listenBaseUrl+downloadCcdApiUrl+"?token="+oAdmin.masterSyncToken, oAdmin.masterHostBasicAuth)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return false
|
||||
@ -1499,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() {
|
||||
retryCountMax := 3
|
||||
certsDownloadFailed := true
|
||||
|
||||
@ -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 "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
|
||||
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
|
||||
|
||||
[ -d $EASY_RSA_LOC/pki ] && chmod 755 $EASY_RSA_LOC/pki
|
||||
|
||||
2
start.sh
2
start.sh
@ -1,3 +1,3 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
docker-compose -p openvpn-master up -d --build
|
||||
docker compose -p openvpn-master up -d --build
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user