1
0
Fork 0
mirror of synced 2024-06-16 06:11:09 -04:00

Compare commits

...

15 commits

Author SHA1 Message Date
Sprait 0c881c81e7
update start.sh
using compose v2
https://docs.docker.com/compose/migrate/#docker-compose-vs-docker-compose
2023-12-12 09:58:55 +03:00
Dmitry Shurupov 699cddc908
Fix the announcement blog link in README.md 2023-10-25 11:57:38 +07:00
Sprait c83c581e21
fix revoke user (#243)
* fix reuse argument multiple times

* replace whitespace with tab
2023-09-12 11:05:28 +03:00
Sprait 35f76ec3b6
Update actions build binary (#240)
* disable-386-build

* update versison go-release-action
2023-09-11 09:18:48 +03:00
Sprait 8a35f70364
add variable for prometheus datasource to dashboard (#239) 2023-09-08 17:45:34 +03:00
Sprait 4981dcb919
Multi-platform build (#234)
* add multi-platform build
2023-09-04 19:24:13 +03:00
Sprait dbc48ef3f1
Merge pull request #92 from strnk/master
listen.base-url parameter to support reverse-proxy setups
2023-08-22 13:40:46 +03:00
vitaliy-sn f4d0212bfc
Merge pull request #199 from flant/update-user-list-in-ha-mode
Fix update user list for kubernetes.secrets backend
2023-04-27 16:04:00 +03:00
Vitaliy Snurnitsin 9024405232 Force update user list for kubernetes.secrets backend
Signed-off-by: Vitaliy Snurnitsin <vitaliy.snurnitsin@flant.com>
2023-04-27 15:18:29 +03:00
Christophe Huriaux dfe2f3d756
Merge branch 'master' into master 2022-11-02 14:29:22 +01:00
Ilya Sosnovsky a973b88463
Update README.md
Fix notes due #165 #166
2022-11-02 14:41:30 +03:00
Ilya Sosnovsky 3f3976ff7a Add dashboard example 2022-09-06 09:55:02 +03:00
strnk 0ee9be5744 Fix certs and ccd slave download API endpoints 2021-12-07 16:12:57 +01:00
strnk f73626dd7b Add configuration parameter for the easyrsa script path 2021-12-07 15:57:31 +01:00
strnk 633ad79d6a Add base URL configuration to the webserver to support reverse-proxy setups 2021-12-07 15:44:52 +01:00
10 changed files with 1069 additions and 65 deletions

View file

@ -12,19 +12,24 @@ jobs:
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@v1
uses: docker/build-push-action@v4
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASS }}
repository: flant/ovpn-admin
tags: openvpn-latest
dockerfile: Dockerfile.openvpn
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@v1
uses: docker/build-push-action@v4
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASS }}
repository: flant/ovpn-admin
tags: latest
dockerfile: Dockerfile
tags: flant/ovpn-admin:latest
platforms: linux/amd64,linux/arm64,linux/arm
file: Dockerfile
push: true

View file

@ -16,19 +16,24 @@ jobs:
- 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:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASS }}
- name: Push openvpn image to Docker Hub
uses: docker/build-push-action@v1
uses: docker/build-push-action@v4
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASS }}
repository: flant/ovpn-admin
tags: openvpn-${{ steps.get_version.outputs.VERSION }}
dockerfile: Dockerfile.openvpn
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@v1
uses: docker/build-push-action@v4
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASS }}
repository: flant/ovpn-admin
tags: ${{ steps.get_version.outputs.VERSION }}
dockerfile: Dockerfile
tags: flant/ovpn-admin:${{ steps.get_version.outputs.VERSION }}
platforms: linux/amd64,linux/arm64,linux/arm
file: Dockerfile
push: true

View file

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

View file

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

View file

@ -1,17 +1,20 @@
FROM node:16-alpine3.15 AS frontend-builder
COPY frontend/ /app
RUN cd /app && npm install && npm run build
RUN apk add --update python3 make g++ && cd /app && npm install && npm run build
FROM golang:1.17.3-buster AS backend-builder
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
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.16
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-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

View file

@ -1,7 +1,9 @@
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 && \
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
RUN chmod +x /etc/openvpn/setup/configure.sh

View file

@ -2,7 +2,7 @@
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!
Originally created in [Flant](https://flant.com/) for internal needs & used for years, then updated to be more modern and [publicly released](https://medium.com/flant-com/introducing-ovpn-admin-a-web-interface-to-manage-openvpn-users-d81705ad8f23) 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.*
@ -76,7 +76,8 @@ You can also download and use prebuilt binaries from the [releases](https://gith
* 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.
* 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
@ -93,6 +94,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)

974
dashboard/ovpn-admin.json Normal file
View file

@ -0,0 +1,974 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "datasource",
"uid": "grafana"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"target": {
"limit": 100,
"matchAny": false,
"tags": [],
"type": "dashboard"
},
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 54,
"links": [],
"liveNow": false,
"panels": [
{
"datasource": {
"type": "prometheus",
"uid": "$ds_prometheus"
},
"fieldConfig": {
"defaults": {
"decimals": 1,
"mappings": [],
"thresholds": {
"mode": "percentage",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 10
}
]
},
"unit": "d"
},
"overrides": []
},
"gridPos": {
"h": 5,
"w": 7,
"x": 5,
"y": 0
},
"id": 2,
"options": {
"colorMode": "value",
"graphMode": "none",
"justifyMode": "center",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"last"
],
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "8.5.2",
"targets": [
{
"expr": "ovpn_server_cert_expire",
"interval": "",
"legendFormat": "",
"refId": "A"
}
],
"title": "Server cert valid time",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "$ds_prometheus"
},
"fieldConfig": {
"defaults": {
"decimals": 1,
"mappings": [],
"thresholds": {
"mode": "percentage",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 10
}
]
},
"unit": "d"
},
"overrides": []
},
"gridPos": {
"h": 5,
"w": 7,
"x": 12,
"y": 0
},
"id": 3,
"options": {
"colorMode": "value",
"graphMode": "none",
"justifyMode": "center",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"last"
],
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "8.5.2",
"targets": [
{
"expr": "ovpn_server_ca_cert_expire",
"interval": "",
"legendFormat": "",
"refId": "A"
}
],
"title": "Server CA cert valid time",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "$ds_prometheus"
},
"fieldConfig": {
"defaults": {
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "semi-dark-orange",
"value": 200
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 5,
"w": 6,
"x": 0,
"y": 5
},
"id": 4,
"options": {
"colorMode": "value",
"graphMode": "none",
"justifyMode": "center",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"last"
],
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "8.5.2",
"targets": [
{
"expr": "ovpn_clients_total",
"interval": "",
"legendFormat": "",
"refId": "A"
}
],
"title": "Total clients",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "$ds_prometheus"
},
"fieldConfig": {
"defaults": {
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 5,
"w": 6,
"x": 6,
"y": 5
},
"id": 5,
"options": {
"colorMode": "value",
"graphMode": "none",
"justifyMode": "center",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"last"
],
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "8.5.2",
"targets": [
{
"expr": "ovpn_clients_connected",
"interval": "",
"legendFormat": "",
"refId": "A"
}
],
"title": "Connected clients",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "$ds_prometheus"
},
"fieldConfig": {
"defaults": {
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "semi-dark-orange",
"value": 10
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 5,
"w": 6,
"x": 12,
"y": 5
},
"id": 7,
"options": {
"colorMode": "value",
"graphMode": "none",
"justifyMode": "center",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"last"
],
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "8.5.13",
"targets": [
{
"expr": "ovpn_clients_expired",
"interval": "",
"legendFormat": "",
"refId": "A"
}
],
"title": "Revoked clients",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "$ds_prometheus"
},
"fieldConfig": {
"defaults": {
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 1
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 5,
"w": 6,
"x": 18,
"y": 5
},
"id": 6,
"options": {
"colorMode": "value",
"graphMode": "none",
"justifyMode": "center",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"last"
],
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "8.5.2",
"targets": [
{
"expr": "ovpn_clients_expired",
"interval": "",
"legendFormat": "",
"refId": "A"
}
],
"title": "Expired clients",
"type": "stat"
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": {
"type": "prometheus",
"uid": "$ds_prometheus"
},
"fieldConfig": {
"defaults": {
"links": []
},
"overrides": []
},
"fill": 2,
"fillGradient": 0,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 10
},
"hiddenSeries": false,
"id": 9,
"legend": {
"avg": false,
"current": false,
"hideEmpty": true,
"max": false,
"min": false,
"show": false,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null as zero",
"options": {
"alertThreshold": true
},
"percentage": false,
"pluginVersion": "8.5.2",
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "ovpn_client_bytes_received",
"interval": "",
"legendFormat": "{{ client }}",
"refId": "A"
}
],
"thresholds": [],
"timeRegions": [],
"title": "Сlient bytes received",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"mode": "time",
"show": true,
"values": []
},
"yaxes": [
{
"format": "decbytes",
"logBase": 1,
"show": true
},
{
"format": "short",
"logBase": 1,
"show": false
}
],
"yaxis": {
"align": false
}
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": {
"type": "prometheus",
"uid": "$ds_prometheus"
},
"fieldConfig": {
"defaults": {
"links": []
},
"overrides": []
},
"fill": 2,
"fillGradient": 0,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 10
},
"hiddenSeries": false,
"id": 10,
"legend": {
"avg": false,
"current": false,
"hideEmpty": true,
"max": false,
"min": false,
"show": false,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null as zero",
"options": {
"alertThreshold": true
},
"percentage": false,
"pluginVersion": "8.5.2",
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "ovpn_client_bytes_sent",
"interval": "",
"legendFormat": "{{ client }}",
"refId": "A"
}
],
"thresholds": [],
"timeRegions": [],
"title": "Сlient bytes sent",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"mode": "time",
"show": true,
"values": []
},
"yaxes": [
{
"format": "decbytes",
"logBase": 1,
"show": true
},
{
"format": "short",
"logBase": 1,
"show": false
}
],
"yaxis": {
"align": false
}
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": {
"type": "prometheus",
"uid": "$ds_prometheus"
},
"fieldConfig": {
"defaults": {
"links": []
},
"overrides": []
},
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 18
},
"hiddenSeries": false,
"id": 16,
"legend": {
"avg": false,
"current": false,
"hideEmpty": true,
"max": false,
"min": false,
"show": false,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null as zero",
"options": {
"alertThreshold": true
},
"percentage": false,
"pluginVersion": "8.5.2",
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "rate(ovpn_client_bytes_received[1m])",
"interval": "",
"legendFormat": "{{ client }}",
"refId": "A"
}
],
"thresholds": [],
"timeRegions": [],
"title": "Clients bytes received rate",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"mode": "time",
"show": true,
"values": []
},
"yaxes": [
{
"$$hashKey": "object:93",
"format": "Bps",
"logBase": 1,
"show": true
},
{
"$$hashKey": "object:94",
"format": "short",
"logBase": 1,
"show": false
}
],
"yaxis": {
"align": false
}
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": {
"type": "prometheus",
"uid": "$ds_prometheus"
},
"fieldConfig": {
"defaults": {
"links": []
},
"overrides": []
},
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 18
},
"hiddenSeries": false,
"id": 17,
"legend": {
"avg": false,
"current": false,
"hideEmpty": true,
"max": false,
"min": false,
"show": false,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null as zero",
"options": {
"alertThreshold": true
},
"percentage": false,
"pluginVersion": "8.5.2",
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "rate(ovpn_client_bytes_sent[1m])",
"interval": "",
"legendFormat": "{{ client }}",
"refId": "A"
}
],
"thresholds": [],
"timeRegions": [],
"title": "Client bytes sent rate ",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"mode": "time",
"show": true,
"values": []
},
"yaxes": [
{
"$$hashKey": "object:174",
"format": "Bps",
"logBase": 1,
"show": true
},
{
"$$hashKey": "object:175",
"format": "short",
"logBase": 1,
"show": false
}
],
"yaxis": {
"align": false
}
},
{
"datasource": {
"type": "prometheus",
"uid": "$ds_prometheus"
},
"description": "value show last connection check time",
"fieldConfig": {
"defaults": {
"custom": {
"align": "center",
"displayMode": "auto",
"width": 20
},
"mappings": [],
"noValue": "Currently there are no connections",
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green"
}
]
},
"unit": "dateTimeAsIso"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 26
},
"id": 12,
"maxDataPoints": 1,
"options": {
"colorMode": "value",
"graphMode": "none",
"justifyMode": "auto",
"orientation": "horizontal",
"reduceOptions": {
"calcs": [
"last"
],
"fields": "",
"values": false
}
},
"pluginVersion": "7.0.6",
"targets": [
{
"expr": "ovpn_client_connection_info * 1000",
"format": "time_series",
"interval": "",
"legendFormat": "{{ client }}-{{ip}}",
"refId": "A"
}
],
"title": "Connection info",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "$ds_prometheus"
},
"description": "value shows when connection was started",
"fieldConfig": {
"defaults": {
"custom": {
"align": "center",
"displayMode": "auto",
"width": 20
},
"mappings": [],
"noValue": "Currently there are no connections",
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green"
}
]
},
"unit": "dateTimeAsIso"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 26
},
"id": 13,
"maxDataPoints": 1,
"options": {
"colorMode": "value",
"graphMode": "none",
"justifyMode": "auto",
"orientation": "horizontal",
"reduceOptions": {
"calcs": [
"last"
],
"fields": "",
"values": false
}
},
"pluginVersion": "7.0.6",
"targets": [
{
"expr": "ovpn_client_connection_from * 1000",
"format": "time_series",
"interval": "",
"legendFormat": "{{ client }}-{{ip}}",
"refId": "A"
}
],
"title": "Connection from",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "$ds_prometheus"
},
"fieldConfig": {
"defaults": {
"custom": {},
"mappings": [],
"min": 0,
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green"
},
{
"color": "red",
"value": 7
},
{
"color": "dark-orange",
"value": 14
},
{
"color": "#EAB839",
"value": 30
},
{
"color": "green",
"value": 31
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 14,
"w": 24,
"x": 0,
"y": 34
},
"id": 19,
"options": {
"colorMode": "value",
"graphMode": "none",
"justifyMode": "center",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"last"
],
"fields": "",
"values": false
}
},
"pluginVersion": "7.0.6",
"targets": [
{
"expr": "ovpn_client_cert_expire ",
"format": "time_series",
"interval": "",
"legendFormat": "{{ client }}",
"refId": "A"
}
],
"title": "Client cert valid days",
"type": "stat"
}
],
"refresh": false,
"schemaVersion": 36,
"style": "dark",
"tags": [],
"templating": {
"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",
"to": "now"
},
"timepicker": {
"refresh_intervals": [
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
]
},
"timezone": "",
"title": "Ovpn-Admin",
"uid": "Z7qmFI0Gk",
"version": 1,
"weekStart": ""
}

73
main.go
View file

@ -36,12 +36,12 @@ import (
const (
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 +49,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,6 +64,7 @@ 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()
@ -248,6 +250,15 @@ type clientStatus struct {
func (oAdmin *OvpnAdmin) userListHandler(w http.ResponseWriter, r *http.Request) {
log.Info(r.RemoteAddr, " ", r.RequestURI)
if *storageBackend == "kubernetes.secrets" {
err := app.updateIndexTxtOnDisk()
if err != nil {
log.Errorln(err)
}
oAdmin.clients = oAdmin.usersList()
}
usersList, _ := json.Marshal(oAdmin.clients)
fmt.Fprintf(w, "%s", usersList)
}
@ -546,32 +557,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))
}
@ -843,7 +854,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
@ -974,7 +985,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)
}
@ -993,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 %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 {
@ -1037,7 +1048,7 @@ 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)
}
@ -1101,7 +1112,7 @@ 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))
@ -1192,7 +1203,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()
@ -1224,7 +1235,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()
@ -1428,7 +1439,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
@ -1445,7 +1456,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

View file

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