mirror of
https://github.com/flant/ovpn-admin.git
synced 2026-02-04 09:12:13 -08:00
Compare commits
1 Commits
6389cd860a
...
bde25f690e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bde25f690e |
25
.github/dependabot.yml
vendored
25
.github/dependabot.yml
vendored
@ -1,25 +0,0 @@
|
||||
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
17
.github/release.yml
vendored
@ -1,17 +0,0 @@
|
||||
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
35
.github/workflows/chart-release.yml
vendored
@ -1,35 +0,0 @@
|
||||
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
107
.github/workflows/chart-test.yaml
vendored
@ -1,107 +0,0 @@
|
||||
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
|
||||
35
.github/workflows/publish-latest.yaml
vendored
Normal file
35
.github/workflows/publish-latest.yaml
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
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
|
||||
63
.github/workflows/publish-tag.yaml
vendored
63
.github/workflows/publish-tag.yaml
vendored
@ -1,52 +1,39 @@
|
||||
name: Build and publish tags to ghcr.io
|
||||
name: Build and publish tags to Docker Hub (tags only)
|
||||
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
|
||||
|
||||
- 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
|
||||
- name: Get the version
|
||||
id: get_version
|
||||
run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//}
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
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=''
|
||||
|
||||
- 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 }}
|
||||
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-${{ steps.get_version.outputs.VERSION }}
|
||||
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:${{ steps.get_version.outputs.VERSION }}
|
||||
platforms: linux/amd64,linux/arm64,linux/arm
|
||||
file: Dockerfile
|
||||
push: true
|
||||
2
.github/workflows/release.yaml
vendored
2
.github/workflows/release.yaml
vendored
@ -17,7 +17,7 @@ jobs:
|
||||
- name: checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: build binaries
|
||||
uses: wangyoucao577/go-release-action@v1.53
|
||||
uses: wangyoucao577/go-release-action@v1.40
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
goversion: 1.17
|
||||
|
||||
2
.github/workflows/release_arm.yaml
vendored
2
.github/workflows/release_arm.yaml
vendored
@ -17,7 +17,7 @@ jobs:
|
||||
- name: checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: build binaries
|
||||
uses: wangyoucao577/go-release-action@v1.53
|
||||
uses: wangyoucao577/go-release-action@v1.40
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
goversion: 1.17
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@ -1,11 +1,13 @@
|
||||
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
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
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
|
||||
|
||||
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
|
||||
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
|
||||
FROM alpine:3.16
|
||||
WORKDIR /app
|
||||
COPY --from=backend-builder /app/ovpn-admin /app
|
||||
ARG TARGETARCH
|
||||
@ -1,4 +1,4 @@
|
||||
FROM alpine:3.21
|
||||
FROM alpine:3.16
|
||||
ARG TARGETARCH
|
||||
RUN apk add --update bash openvpn easy-rsa iptables && \
|
||||
ln -s /usr/share/easy-rsa/easyrsa /usr/local/bin && \
|
||||
|
||||
54
Makefile
54
Makefile
@ -1,54 +0,0 @@
|
||||
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
|
||||
###########
|
||||
42
README.md
42
README.md
@ -2,6 +2,8 @@
|
||||
|
||||
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
|
||||
@ -19,16 +21,16 @@ Simple web UI to manage OpenVPN users, their certificates & routes in Linux. Whi
|
||||
### 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/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).
|
||||
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).
|
||||
|
||||
Requirements:
|
||||
You need [Docker](https://docs.docker.com/get-docker/) and [docker-compose](https://docs.docker.com/compose/install/) installed.
|
||||
@ -36,7 +38,7 @@ You need [Docker](https://docs.docker.com/get-docker/) and [docker-compose](http
|
||||
Commands to execute:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/palark/ovpn-admin.git
|
||||
git clone https://github.com/flant/ovpn-admin.git
|
||||
cd ovpn-admin
|
||||
./start.sh
|
||||
```
|
||||
@ -54,7 +56,7 @@ Requirements. You need Linux with the following components installed:
|
||||
Commands to execute:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/palark/ovpn-admin.git
|
||||
git clone https://github.com/flant/ovpn-admin.git
|
||||
cd ovpn-admin
|
||||
./bootstrap.sh
|
||||
./build.sh
|
||||
@ -65,18 +67,18 @@ cd ovpn-admin
|
||||
|
||||
### 3. Prebuilt binary
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
|
||||
## 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`).
|
||||
* 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**
|
||||
* 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 =)*)
|
||||
|
||||
## Usage
|
||||
|
||||
@ -158,9 +160,6 @@ 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)
|
||||
|
||||
@ -173,11 +172,6 @@ Flags:
|
||||
--version show application version
|
||||
```
|
||||
|
||||
## Authors
|
||||
## Further information
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
@ -8,9 +8,7 @@ import (
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -62,6 +60,7 @@ func genPrivKey() (privKeyPEM *bytes.Buffer, err error) {
|
||||
Bytes: privKeyPKCS8,
|
||||
})
|
||||
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@ -134,17 +133,6 @@ 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},
|
||||
@ -154,8 +142,8 @@ func genClientCert(privKey, caPrivKey *rsa.PrivateKey, ca *x509.Certificate, cn
|
||||
},
|
||||
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
||||
NotBefore: notBefore,
|
||||
NotAfter: notAfter,
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: ca.NotAfter,
|
||||
}
|
||||
|
||||
issuerBytes, err := x509.CreateCertificate(rand.Reader, &template, ca, &privKey.PublicKey, caPrivKey)
|
||||
|
||||
@ -1,2 +0,0 @@
|
||||
owner: palark
|
||||
git-base-url: https://api.github.com/
|
||||
@ -1,15 +0,0 @@
|
||||
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
|
||||
@ -1,40 +0,0 @@
|
||||
# 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 |
|
||||
|-----|------|---------|-------------|
|
||||
| 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)
|
||||
@ -1,39 +0,0 @@
|
||||
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"
|
||||
2
helm/Chart.yaml
Normal file
2
helm/Chart.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
name: ovpn-admin
|
||||
version: 1.0.0
|
||||
1
helm/README.md
Normal file
1
helm/README.md
Normal file
@ -0,0 +1 @@
|
||||
helm chart example
|
||||
@ -12,19 +12,19 @@ spec:
|
||||
labels:
|
||||
app: openvpn
|
||||
spec:
|
||||
{{- if .Values.nodeSelector }}
|
||||
{{- if .Values.openvpn.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- .Values.nodeSelector | toYaml | indent 8 | printf "\n%s" }}
|
||||
{{- .Values.openvpn.nodeSelector | toYaml | indent 8 | printf "\n%s" }}
|
||||
{{- end }}
|
||||
{{- if .Values.tolerations }}
|
||||
{{- if .Values.openvpn.tolerations }}
|
||||
tolerations:
|
||||
{{- .Values.tolerations | toYaml | indent 8 | printf "\n%s" }}
|
||||
{{- .Values.openvpn.tolerations | toYaml | indent 8 | printf "\n%s" }}
|
||||
{{- end }}
|
||||
terminationGracePeriodSeconds: 0
|
||||
serviceAccountName: openvpn
|
||||
containers:
|
||||
- name: ovpn-admin
|
||||
image: {{ .Values.ovpnAdmin.repo }}:master
|
||||
image: {{ .Values.ovpnAdmin.image }}
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
@ -60,7 +60,7 @@ spec:
|
||||
- name: ccd
|
||||
mountPath: /mnt/ccd
|
||||
- name: openvpn
|
||||
image: {{ .Values.ovpnAdmin.repo }}:master
|
||||
image: {{ .Values.openvpn.image }}
|
||||
command: [ '/entrypoint.sh' ]
|
||||
# imagePullPolicy: Always
|
||||
securityContext:
|
||||
@ -4,17 +4,12 @@ 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 }}
|
||||
@ -30,3 +25,15 @@ 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
|
||||
26
helm/values.yaml
Normal file
26
helm/values.yaml
Normal file
@ -0,0 +1,26 @@
|
||||
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
|
||||
36
main.go
36
main.go
@ -9,7 +9,11 @@ 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"
|
||||
@ -21,11 +25,6 @@ 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"
|
||||
@ -35,7 +34,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
usernameRegexp = `^([a-zA-Z0-9_.\-@])+$`
|
||||
usernameRegexp = `^([a-zA-Z0-9_.-@])+$`
|
||||
passwordMinLength = 6
|
||||
certsArchiveFileName = "certs.tar.gz"
|
||||
ccdArchiveFileName = "ccd.tar.gz"
|
||||
@ -72,11 +71,9 @@ var (
|
||||
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
|
||||
@ -507,10 +504,6 @@ func main() {
|
||||
*indexTxtPath = *easyrsaDirPath + "/pki/index.txt"
|
||||
}
|
||||
|
||||
if *authDataBaseInit {
|
||||
ovpnUserInitDb()
|
||||
}
|
||||
|
||||
ovpnAdmin := new(OvpnAdmin)
|
||||
|
||||
ovpnAdmin.lastSyncTime = "unknown"
|
||||
@ -861,7 +854,7 @@ func (oAdmin *OvpnAdmin) getCcd(username string) Ccd {
|
||||
}
|
||||
|
||||
func checkStaticAddressIsFree(staticAddress string, username string) bool {
|
||||
o := runBash(fmt.Sprintf("grep -rl ' %[1]s ' %[2]s | grep -vx %[2]s/%[3]s | wc -l", staticAddress, *ccdDir, username))
|
||||
o := runBash(fmt.Sprintf("grep -rl ' %s ' %s | grep -vx %s/%s | wc -l", staticAddress, *ccdDir, *ccdDir, username))
|
||||
|
||||
if strings.TrimSpace(o) == "0" {
|
||||
return true
|
||||
@ -1011,7 +1004,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 %[1]s --user %[2]s | grep %[2]s | wc -l", *authDatabase, username))
|
||||
o := runBash(fmt.Sprintf("openvpn-user check --db.path %s --user %s | grep %s | wc -l", *authDatabase, username, username))
|
||||
log.Debug(o)
|
||||
|
||||
if err := validatePassword(password); err != nil {
|
||||
@ -1055,12 +1048,12 @@ func (oAdmin *OvpnAdmin) userRevoke(username string) (error, string) {
|
||||
log.Error(err)
|
||||
}
|
||||
} else {
|
||||
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))
|
||||
o := runBash(fmt.Sprintf("cd %s && echo yes | easyrsa revoke %s 1>/dev/null && %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)
|
||||
}
|
||||
|
||||
@ -1122,7 +1115,7 @@ func (oAdmin *OvpnAdmin) userUnrevoke(username string) (error, string) {
|
||||
_ = 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)
|
||||
}
|
||||
|
||||
@ -1347,7 +1340,7 @@ func (oAdmin *OvpnAdmin) mgmtGetActiveClients() []clientStatus {
|
||||
break
|
||||
}
|
||||
oAdmin.mgmtRead(conn) // read welcome message
|
||||
conn.Write([]byte("status 1\n"))
|
||||
conn.Write([]byte("status\n"))
|
||||
activeClients = append(activeClients, oAdmin.mgmtConnectedUsersParser(oAdmin.mgmtRead(conn), srv)...)
|
||||
conn.Close()
|
||||
}
|
||||
@ -1508,13 +1501,6 @@ 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-migrate --db.path=$EASY_RSA_LOC/pki/users.db
|
||||
openvpn-user db-init --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