Compare commits

..

178 Commits

Author SHA1 Message Date
dependabot[bot]
52f787d41b
Bump lodash from 4.17.21 to 4.17.23 in /frontend (#424)
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.21 to 4.17.23.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.21...4.17.23)

---
updated-dependencies:
- dependency-name: lodash
  dependency-version: 4.17.23
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-22 12:55:42 +00:00
dependabot[bot]
fe8767bc13
Bump github.com/sirupsen/logrus from 1.9.3 to 1.9.4 (#423)
Bumps [github.com/sirupsen/logrus](https://github.com/sirupsen/logrus) from 1.9.3 to 1.9.4.
- [Release notes](https://github.com/sirupsen/logrus/releases)
- [Changelog](https://github.com/sirupsen/logrus/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sirupsen/logrus/compare/v1.9.3...v1.9.4)

---
updated-dependencies:
- dependency-name: github.com/sirupsen/logrus
  dependency-version: 1.9.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-19 11:33:02 +00:00
dependabot[bot]
66bdb0bb3e
Bump qs and express in /frontend (#422)
Bumps [qs](https://github.com/ljharb/qs) and [express](https://github.com/expressjs/express). These dependencies needed to be updated together.

Updates `qs` from 6.13.0 to 6.14.1
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/qs/compare/v6.13.0...v6.14.1)

Updates `express` from 4.21.2 to 4.22.1
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/v4.22.1/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.21.2...v4.22.1)

---
updated-dependencies:
- dependency-name: qs
  dependency-version: 6.14.1
  dependency-type: indirect
- dependency-name: express
  dependency-version: 4.22.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-05 11:21:34 +00:00
dependabot[bot]
a781600f13
Bump k8s.io/client-go from 0.34.2 to 0.34.3 (#416)
Bumps [k8s.io/client-go](https://github.com/kubernetes/client-go) from 0.34.2 to 0.34.3.
- [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kubernetes/client-go/compare/v0.34.2...v0.34.3)

---
updated-dependencies:
- dependency-name: k8s.io/client-go
  dependency-version: 0.34.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-15 16:46:03 +00:00
dependabot[bot]
6dac5622c6
Bump k8s.io/api from 0.34.2 to 0.34.3 (#417)
Bumps [k8s.io/api](https://github.com/kubernetes/api) from 0.34.2 to 0.34.3.
- [Commits](https://github.com/kubernetes/api/compare/v0.34.2...v0.34.3)

---
updated-dependencies:
- dependency-name: k8s.io/api
  dependency-version: 0.34.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-15 16:39:28 +00:00
dependabot[bot]
0bffce2dc0
Bump wangyoucao577/go-release-action from 1.54 to 1.55 (#419)
Bumps [wangyoucao577/go-release-action](https://github.com/wangyoucao577/go-release-action) from 1.54 to 1.55.
- [Release notes](https://github.com/wangyoucao577/go-release-action/releases)
- [Commits](https://github.com/wangyoucao577/go-release-action/compare/v1.54...v1.55)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-15 16:39:11 +00:00
dependabot[bot]
3ebb31c65b
Bump wangyoucao577/go-release-action from 1.53 to 1.54 (#415)
Bumps [wangyoucao577/go-release-action](https://github.com/wangyoucao577/go-release-action) from 1.53 to 1.54.
- [Release notes](https://github.com/wangyoucao577/go-release-action/releases)
- [Commits](https://github.com/wangyoucao577/go-release-action/compare/v1.53...v1.54)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-08 12:10:53 +00:00
dependabot[bot]
d22a9ed5c9
Bump alpine from 3.22 to 3.23 (#414)
Bumps alpine from 3.22 to 3.23.

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-08 12:10:22 +00:00
dependabot[bot]
80f8752c93
Bump docker/metadata-action from 5.9.0 to 5.10.0 (#413)
Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 5.9.0 to 5.10.0.
- [Release notes](https://github.com/docker/metadata-action/releases)
- [Commits](https://github.com/docker/metadata-action/compare/v5.9.0...v5.10.0)

---
updated-dependencies:
- dependency-name: docker/metadata-action
  dependency-version: 5.10.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-01 02:55:45 +00:00
dependabot[bot]
a311ec30da
Bump k8s.io/client-go from 0.34.0 to 0.34.2 (#410)
Bumps [k8s.io/client-go](https://github.com/kubernetes/client-go) from 0.34.0 to 0.34.2.
- [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kubernetes/client-go/compare/v0.34.0...v0.34.2)

---
updated-dependencies:
- dependency-name: k8s.io/client-go
  dependency-version: 0.34.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-27 13:43:43 +00:00
dependabot[bot]
74693eaefc
Bump docker/metadata-action from 5.8.0 to 5.9.0 (#407)
Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 5.8.0 to 5.9.0.
- [Release notes](https://github.com/docker/metadata-action/releases)
- [Commits](https://github.com/docker/metadata-action/compare/v5.8.0...v5.9.0)

---
updated-dependencies:
- dependency-name: docker/metadata-action
  dependency-version: 5.9.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-27 13:40:43 +00:00
dependabot[bot]
e7fcb76a9b
Bump actions/checkout from 5 to 6 (#411)
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-27 13:39:56 +00:00
dependabot[bot]
4b6d2e2eee
Bump node-forge from 1.3.1 to 1.3.2 in /frontend (#412)
Bumps [node-forge](https://github.com/digitalbazaar/forge) from 1.3.1 to 1.3.2.
- [Changelog](https://github.com/digitalbazaar/forge/blob/main/CHANGELOG.md)
- [Commits](https://github.com/digitalbazaar/forge/compare/v1.3.1...v1.3.2)

---
updated-dependencies:
- dependency-name: node-forge
  dependency-version: 1.3.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-27 13:39:43 +00:00
dependabot[bot]
2baac52ce3
Bump k8s.io/api from 0.34.1 to 0.34.2 (#409)
Bumps [k8s.io/api](https://github.com/kubernetes/api) from 0.34.1 to 0.34.2.
- [Commits](https://github.com/kubernetes/api/compare/v0.34.1...v0.34.2)

---
updated-dependencies:
- dependency-name: k8s.io/api
  dependency-version: 0.34.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-27 13:39:14 +00:00
dependabot[bot]
1f29aec3b8
Bump helm/kind-action from 1.12.0 to 1.13.0 (#405)
Bumps [helm/kind-action](https://github.com/helm/kind-action) from 1.12.0 to 1.13.0.
- [Release notes](https://github.com/helm/kind-action/releases)
- [Commits](https://github.com/helm/kind-action/compare/v1.12.0...v1.13.0)

---
updated-dependencies:
- dependency-name: helm/kind-action
  dependency-version: 1.13.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-27 13:38:30 +00:00
dependabot[bot]
c9c4c28253
Bump actions/setup-python from 5 to 6 (#398)
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5 to 6.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-15 10:20:01 +07:00
dependabot[bot]
ff8c281c91
Bump github.com/prometheus/client_golang from 1.23.0 to 1.23.2 (#397)
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.23.0 to 1.23.2.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.23.0...v1.23.2)

---
updated-dependencies:
- dependency-name: github.com/prometheus/client_golang
  dependency-version: 1.23.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-15 10:19:35 +07:00
dependabot[bot]
501451d41b
Bump k8s.io/api from 0.34.0 to 0.34.1 (#400)
Bumps [k8s.io/api](https://github.com/kubernetes/api) from 0.34.0 to 0.34.1.
- [Commits](https://github.com/kubernetes/api/compare/v0.34.0...v0.34.1)

---
updated-dependencies:
- dependency-name: k8s.io/api
  dependency-version: 0.34.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-15 10:19:21 +07:00
dependabot[bot]
0baf464e8c
Bump axios from 1.8.2 to 1.12.0 in /frontend (#399)
Bumps [axios](https://github.com/axios/axios) from 1.8.2 to 1.12.0.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.8.2...v1.12.0)

---
updated-dependencies:
- dependency-name: axios
  dependency-version: 1.12.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-15 10:19:04 +07:00
dependabot[bot]
d5fb51645a
Bump k8s.io/client-go from 0.33.4 to 0.34.0 (#395)
Bumps [k8s.io/client-go](https://github.com/kubernetes/client-go) from 0.33.4 to 0.34.0.
- [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kubernetes/client-go/compare/v0.33.4...v0.34.0)

---
updated-dependencies:
- dependency-name: k8s.io/client-go
  dependency-version: 0.34.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-02 13:42:19 +07:00
dependabot[bot]
6bc113f4f9
Bump actions/checkout from 4 to 5 (#390)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-20 11:36:12 +07:00
dependabot[bot]
1db41fd083
Bump k8s.io/client-go from 0.33.3 to 0.33.4 (#392)
Bumps [k8s.io/client-go](https://github.com/kubernetes/client-go) from 0.33.3 to 0.33.4.
- [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kubernetes/client-go/compare/v0.33.3...v0.33.4)

---
updated-dependencies:
- dependency-name: k8s.io/client-go
  dependency-version: 0.33.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-20 11:35:41 +07:00
dependabot[bot]
2da3acf84c
Bump k8s.io/api from 0.33.3 to 0.33.4 (#389)
Bumps [k8s.io/api](https://github.com/kubernetes/api) from 0.33.3 to 0.33.4.
- [Commits](https://github.com/kubernetes/api/compare/v0.33.3...v0.33.4)

---
updated-dependencies:
- dependency-name: k8s.io/api
  dependency-version: 0.33.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-18 10:48:52 +07:00
dependabot[bot]
cccdabf0b0
Bump golang from 1.24.5-bullseye to 1.24.6-bullseye (#388)
Bumps golang from 1.24.5-bullseye to 1.24.6-bullseye.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.24.6-bullseye
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-11 11:51:57 +07:00
dependabot[bot]
1e65366668
Bump github.com/prometheus/client_golang from 1.22.0 to 1.23.0 (#386)
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.22.0 to 1.23.0.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.22.0...v1.23.0)

---
updated-dependencies:
- dependency-name: github.com/prometheus/client_golang
  dependency-version: 1.23.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-04 12:18:53 +07:00
dependabot[bot]
9205f424cc
Bump docker/metadata-action from 5.7.0 to 5.8.0 (#385)
Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 5.7.0 to 5.8.0.
- [Release notes](https://github.com/docker/metadata-action/releases)
- [Commits](https://github.com/docker/metadata-action/compare/v5.7.0...v5.8.0)

---
updated-dependencies:
- dependency-name: docker/metadata-action
  dependency-version: 5.8.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-04 12:18:38 +07:00
Parfenov Ivan
e5bf819db3
feat(ccd): support IP conflict validation in Kubernetes Secret mode (#384)
Signed-off-by: Paramoshka <parfenov_ivan_42a@mail.ru>
2025-07-23 13:21:07 +02:00
Parfenov Ivan
fbee2c07dc
[openvpn] Transferring routes from rotated certs (#382)
Signed-off-by: Paramoshka <parfenov_ivan_42a@mail.ru>
2025-07-22 13:04:15 +07:00
dependabot[bot]
10e961ca0f
Bump form-data, node-sass and sass-loader in /frontend (#383)
---
updated-dependencies:
- dependency-name: form-data
  dependency-version: 4.0.4
  dependency-type: indirect
- dependency-name: node-sass
  dependency-version: 9.0.0
  dependency-type: direct:development
- dependency-name: sass-loader
  dependency-version: 16.0.5
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-22 13:01:32 +07:00
dependabot[bot]
a6fc3294b6
Bump k8s.io/client-go from 0.33.2 to 0.33.3 (#380)
---
updated-dependencies:
- dependency-name: k8s.io/client-go
  dependency-version: 0.33.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-21 11:39:26 +07:00
dependabot[bot]
c926fba345
Bump on-headers and compression in /frontend (#378)
Bumps [on-headers](https://github.com/jshttp/on-headers) and [compression](https://github.com/expressjs/compression). These dependencies needed to be updated together.

Updates `on-headers` from 1.0.2 to 1.1.0
- [Release notes](https://github.com/jshttp/on-headers/releases)
- [Changelog](https://github.com/jshttp/on-headers/blob/master/HISTORY.md)
- [Commits](https://github.com/jshttp/on-headers/compare/v1.0.2...v1.1.0)

Updates `compression` from 1.7.4 to 1.8.1
- [Release notes](https://github.com/expressjs/compression/releases)
- [Changelog](https://github.com/expressjs/compression/blob/master/HISTORY.md)
- [Commits](https://github.com/expressjs/compression/compare/1.7.4...v1.8.1)

---
updated-dependencies:
- dependency-name: on-headers
  dependency-version: 1.1.0
  dependency-type: indirect
- dependency-name: compression
  dependency-version: 1.8.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-21 11:33:06 +07:00
dependabot[bot]
f3802a153a
Bump k8s.io/api from 0.33.2 to 0.33.3 (#381)
---
updated-dependencies:
- dependency-name: k8s.io/api
  dependency-version: 0.33.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-21 11:32:38 +07:00
Dmitry Shurupov
2edd30798d
Merge pull request #377 from palark/dependabot/docker/golang-1.24.5-bullseye
Bump golang from 1.24.4-bullseye to 1.24.5-bullseye
2025-07-14 11:10:39 +07:00
dependabot[bot]
6d679b3ec1
Bump golang from 1.24.4-bullseye to 1.24.5-bullseye
Bumps golang from 1.24.4-bullseye to 1.24.5-bullseye.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.24.5-bullseye
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-14 02:36:08 +00:00
Dmitry Shurupov
efca43410c
Merge pull request #374 from palark/dependabot/go_modules/k8s.io/client-go-0.33.2
Bump k8s.io/client-go from 0.33.1 to 0.33.2
2025-07-08 16:48:40 +07:00
dependabot[bot]
cb864f5420
Bump k8s.io/client-go from 0.33.1 to 0.33.2
Bumps [k8s.io/client-go](https://github.com/kubernetes/client-go) from 0.33.1 to 0.33.2.
- [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kubernetes/client-go/compare/v0.33.1...v0.33.2)

---
updated-dependencies:
- dependency-name: k8s.io/client-go
  dependency-version: 0.33.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-23 02:39:52 +00:00
Dmitry Shurupov
d74e7f8313
Merge pull request #372 from palark/dependabot/docker/golang-1.24.4-bullseye
Bump golang from 1.24.3-bullseye to 1.24.4-bullseye
2025-06-10 11:40:51 +07:00
dependabot[bot]
0239b19d7f
Bump golang from 1.24.3-bullseye to 1.24.4-bullseye
Bumps golang from 1.24.3-bullseye to 1.24.4-bullseye.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.24.4-bullseye
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-09 03:28:26 +00:00
Dmitry Shurupov
3ebde2e547
Merge pull request #370 from palark/dependabot/docker/alpine-3.22
Bump alpine from 3.21 to 3.22
2025-06-06 11:56:26 +07:00
Dmitry Shurupov
dd9f2b86a8
Merge pull request #367 from palark/dependabot/go_modules/k8s.io/client-go-0.33.1
Bump k8s.io/client-go from 0.23.1 to 0.33.1
2025-06-06 11:45:47 +07:00
Dmitry Shurupov
be3e0ab26d
Merge pull request #371 from palark/dependabot/npm_and_yarn/frontend/webpack-dev-server-5.2.1
Bump webpack-dev-server from 4.8.1 to 5.2.1 in /frontend
2025-06-06 11:44:58 +07:00
dependabot[bot]
16f55bb9cb
Bump webpack-dev-server from 4.8.1 to 5.2.1 in /frontend
Bumps [webpack-dev-server](https://github.com/webpack/webpack-dev-server) from 4.8.1 to 5.2.1.
- [Release notes](https://github.com/webpack/webpack-dev-server/releases)
- [Changelog](https://github.com/webpack/webpack-dev-server/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-dev-server/compare/v4.8.1...v5.2.1)

---
updated-dependencies:
- dependency-name: webpack-dev-server
  dependency-version: 5.2.1
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-06 03:43:09 +00:00
dependabot[bot]
6929d45686
Bump alpine from 3.21 to 3.22
Bumps alpine from 3.21 to 3.22.

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-02 02:45:14 +00:00
Dmitry Shurupov
3f17e73dd1
Merge pull request #369 from palark/fix-easyrsa-build-client
Fix easyrsa build-client-full command
2025-05-30 20:13:58 +07:00
Dmitry Shurupov
d76966a39e
Fix easyrsa build-client-full command
Signed-off-by: Dmitry Shurupov <dmitry.shurupov@palark.com>
2025-05-30 20:07:40 +07:00
Dmitry Shurupov
6cf586b419
Merge pull request #368 from palark/dependabot/npm_and_yarn/frontend/multi-8a59521ea1
Bump ip and socks in /frontend
2025-05-30 18:36:12 +07:00
dependabot[bot]
a308adaf52
Bump k8s.io/client-go from 0.23.1 to 0.33.1
Bumps [k8s.io/client-go](https://github.com/kubernetes/client-go) from 0.23.1 to 0.33.1.
- [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kubernetes/client-go/compare/v0.23.1...v0.33.1)

---
updated-dependencies:
- dependency-name: k8s.io/client-go
  dependency-version: 0.33.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-30 11:34:06 +00:00
dependabot[bot]
cba9040957
Bump ip and socks in /frontend
Removes [ip](https://github.com/indutny/node-ip). It's no longer used after updating ancestor dependency [socks](https://github.com/JoshGlazebrook/socks). These dependencies need to be updated together.


Removes `ip`

Updates `socks` from 2.6.2 to 2.8.4
- [Release notes](https://github.com/JoshGlazebrook/socks/releases)
- [Commits](https://github.com/JoshGlazebrook/socks/commits/2.8.4)

---
updated-dependencies:
- dependency-name: ip
  dependency-version: 
  dependency-type: indirect
- dependency-name: socks
  dependency-version: 2.8.4
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-30 11:33:20 +00:00
Dmitry Shurupov
7ddb49bfbc
Merge pull request #360 from palark/dependabot/go_modules/github.com/prometheus/client_golang-1.22.0
Bump github.com/prometheus/client_golang from 1.21.1 to 1.22.0
2025-05-30 18:32:38 +07:00
Dmitry Shurupov
02fadf9058
Merge pull request #361 from palark/dependabot/npm_and_yarn/frontend/http-proxy-middleware-2.0.9
Bump http-proxy-middleware from 2.0.6 to 2.0.9 in /frontend
2025-05-30 18:32:24 +07:00
Dmitry Shurupov
c7f4e092eb
Merge pull request #365 from palark/dependabot/docker/golang-1.24.3-bullseye
Bump golang from 1.24.2-bullseye to 1.24.3-bullseye
2025-05-12 09:53:38 +05:00
dependabot[bot]
e247846250
Bump golang from 1.24.2-bullseye to 1.24.3-bullseye
Bumps golang from 1.24.2-bullseye to 1.24.3-bullseye.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.24.3-bullseye
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-12 02:30:51 +00:00
dependabot[bot]
7a3249bf0d
Bump http-proxy-middleware from 2.0.6 to 2.0.9 in /frontend
Bumps [http-proxy-middleware](https://github.com/chimurai/http-proxy-middleware) from 2.0.6 to 2.0.9.
- [Release notes](https://github.com/chimurai/http-proxy-middleware/releases)
- [Changelog](https://github.com/chimurai/http-proxy-middleware/blob/v2.0.9/CHANGELOG.md)
- [Commits](https://github.com/chimurai/http-proxy-middleware/compare/v2.0.6...v2.0.9)

---
updated-dependencies:
- dependency-name: http-proxy-middleware
  dependency-version: 2.0.9
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-16 15:34:03 +00:00
dependabot[bot]
34ed41efec
Bump github.com/prometheus/client_golang from 1.21.1 to 1.22.0
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.21.1 to 1.22.0.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.21.1...v1.22.0)

---
updated-dependencies:
- dependency-name: github.com/prometheus/client_golang
  dependency-version: 1.22.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-14 02:45:07 +00:00
Dmitry Shurupov
91dfa75ccd
Merge pull request #359 from palark/dependabot/docker/golang-1.24.2-bullseye
Bump golang from 1.24.1-bullseye to 1.24.2-bullseye
2025-04-09 17:02:14 +07:00
dependabot[bot]
3d558566ea
Bump golang from 1.24.1-bullseye to 1.24.2-bullseye
Bumps golang from 1.24.1-bullseye to 1.24.2-bullseye.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.24.2-bullseye
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-07 02:56:13 +00:00
Dmitry Shurupov
a591f3bec3
Merge pull request #356 from palark/fix-easyrsa-prompt-2
Fix easyrsa build-server-full prompt
2025-03-28 21:29:42 +07:00
Dmitry Shurupov
36b6964f8c
Fix easyrsa build-server-full prompt
Signed-off-by: Dmitry Shurupov <dmitry.shurupov@palark.com>
2025-03-28 21:16:14 +07:00
Dmitry Shurupov
2fc8f729b9
Merge pull request #355 from palark/fix-easyrsa-prompt
Fix easyrsa init-pki prompt confirmation
2025-03-28 20:45:38 +07:00
Dmitry Shurupov
f5fc3e988f
Fix easyrsa init-pki prompt confirmation
Signed-off-by: Dmitry Shurupov <dmitry.shurupov@palark.com>
2025-03-28 20:40:40 +07:00
Dmitry Shurupov
0fc701f2eb
Merge pull request #338 from naidu/patch-1
fixing docker-compose.yaml to point to right dockerfile
2025-03-28 16:19:26 +07:00
dependabot[bot]
f25d6e30cb
Bump axios from 0.28.0 to 1.8.2 in /frontend (#339)
Bumps [axios](https://github.com/axios/axios) from 0.28.0 to 1.8.2.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v0.28.0...v1.8.2)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-23 08:35:12 +01:00
dependabot[bot]
83e74ec351
Bump golang from 1.23.6-bullseye to 1.24.1-bullseye (#340)
Bumps golang from 1.23.6-bullseye to 1.24.1-bullseye.

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-23 08:34:21 +01:00
xzxiaoshan
711bd120b9
Comment the docker-compose plugin (#349)
Signed-off-by: xzxiaoshan <365384722@qq.com>
2025-03-23 08:33:30 +01:00
dependabot[bot]
29c68e74fe
Bump actions/checkout from 2 to 4 (#346)
Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v2...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-23 08:31:31 +01:00
dependabot[bot]
ee618772c3
Bump github.com/google/uuid from 1.3.0 to 1.6.0 (#343)
Bumps [github.com/google/uuid](https://github.com/google/uuid) from 1.3.0 to 1.6.0.
- [Release notes](https://github.com/google/uuid/releases)
- [Changelog](https://github.com/google/uuid/blob/master/CHANGELOG.md)
- [Commits](https://github.com/google/uuid/compare/v1.3.0...v1.6.0)

---
updated-dependencies:
- dependency-name: github.com/google/uuid
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-16 22:20:30 +01:00
dependabot[bot]
643fe58a58
Bump github.com/sirupsen/logrus from 1.8.1 to 1.9.3 (#345)
Bumps [github.com/sirupsen/logrus](https://github.com/sirupsen/logrus) from 1.8.1 to 1.9.3.
- [Release notes](https://github.com/sirupsen/logrus/releases)
- [Changelog](https://github.com/sirupsen/logrus/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sirupsen/logrus/compare/v1.8.1...v1.9.3)

---
updated-dependencies:
- dependency-name: github.com/sirupsen/logrus
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-16 22:20:19 +01:00
dependabot[bot]
ae732f2ce1
Bump helm/chart-releaser-action from 1.6.0 to 1.7.0 (#347)
Bumps [helm/chart-releaser-action](https://github.com/helm/chart-releaser-action) from 1.6.0 to 1.7.0.
- [Release notes](https://github.com/helm/chart-releaser-action/releases)
- [Commits](https://github.com/helm/chart-releaser-action/compare/v1.6.0...v1.7.0)

---
updated-dependencies:
- dependency-name: helm/chart-releaser-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-11 12:43:36 +01:00
dependabot[bot]
4f4998f249
Bump github.com/prometheus/client_golang from 1.11.1 to 1.21.1 (#344)
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.11.1 to 1.21.1.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.11.1...v1.21.1)

---
updated-dependencies:
- dependency-name: github.com/prometheus/client_golang
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-11 12:43:08 +01:00
dependabot[bot]
a13e4055b8
Bump docker/metadata-action from 5.6.1 to 5.7.0 (#348)
Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 5.6.1 to 5.7.0.
- [Release notes](https://github.com/docker/metadata-action/releases)
- [Commits](https://github.com/docker/metadata-action/compare/v5.6.1...v5.7.0)

---
updated-dependencies:
- dependency-name: docker/metadata-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-11 12:43:00 +01:00
BTR Naidu
eacd266124
fixing docker-compose.yaml to point to right dockerfile 2025-03-05 14:59:21 +01:00
Maksim Nabokikh
3b7b53ff24
Setup QEMU for more archs 2025-03-04 21:31:20 +01:00
Maksim Nabokikh
0c0265e31d
Use .Env in werf.yaml 2025-03-04 21:29:02 +01:00
Maksim Nabokikh
671b408b11
Fix werf env 2025-03-04 21:26:09 +01:00
Maksim Nabokikh
26372556b8
Release multiarch build fix (#335)
Signed-off-by: maksim.nabokikh <max.nabokih@gmail.com>
2025-03-04 21:22:30 +01:00
Maksim Nabokikh
63b219d3e3
Release multiarch build (#334)
Signed-off-by: maksim.nabokikh <max.nabokih@gmail.com>
2025-03-04 21:15:03 +01:00
dependabot[bot]
a067eab80e
Bump postcss from 8.4.12 to 8.5.3 in /frontend (#329)
Bumps [postcss](https://github.com/postcss/postcss) from 8.4.12 to 8.5.3.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.4.12...8.5.3)

---
updated-dependencies:
- dependency-name: postcss
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-04 21:09:00 +01:00
dependabot[bot]
0df7679239
Bump braces from 3.0.2 to 3.0.3 in /frontend (#331)
Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3.
- [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3)

---
updated-dependencies:
- dependency-name: braces
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-04 21:08:51 +01:00
dependabot[bot]
7331d87ab0
Bump ws from 8.5.0 to 8.18.1 in /frontend (#332)
Bumps [ws](https://github.com/websockets/ws) from 8.5.0 to 8.18.1.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/8.5.0...8.18.1)

---
updated-dependencies:
- dependency-name: ws
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-04 21:08:40 +01:00
dependabot[bot]
08925cf9b1
Bump webpack-dev-middleware from 5.3.1 to 5.3.4 in /frontend (#330)
Bumps [webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware) from 5.3.1 to 5.3.4.
- [Release notes](https://github.com/webpack/webpack-dev-middleware/releases)
- [Changelog](https://github.com/webpack/webpack-dev-middleware/blob/v5.3.4/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-dev-middleware/compare/v5.3.1...v5.3.4)

---
updated-dependencies:
- dependency-name: webpack-dev-middleware
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-04 21:08:30 +01:00
dependabot[bot]
e24d02ac12
Bump @babel/traverse from 7.17.9 to 7.26.9 in /frontend (#333)
Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.17.9 to 7.26.9.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.26.9/packages/babel-traverse)

---
updated-dependencies:
- dependency-name: "@babel/traverse"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-04 21:08:17 +01:00
dependabot[bot]
ca962491b6
Bump json5 in /frontend (#328)
Bumps  and [json5](https://github.com/json5/json5). These dependencies needed to be updated together.

Updates `json5` from 1.0.1 to 2.2.3
- [Release notes](https://github.com/json5/json5/releases)
- [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md)
- [Commits](https://github.com/json5/json5/compare/v1.0.1...v2.2.3)

Updates `json5` from 2.2.1 to 2.2.3
- [Release notes](https://github.com/json5/json5/releases)
- [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md)
- [Commits](https://github.com/json5/json5/compare/v1.0.1...v2.2.3)

---
updated-dependencies:
- dependency-name: json5
  dependency-type: indirect
- dependency-name: json5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-04 20:58:02 +01:00
dependabot[bot]
f2675036f3
Bump axios from 0.27.1 to 0.28.0 in /frontend (#327)
Bumps [axios](https://github.com/axios/axios) from 0.27.1 to 0.28.0.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v0.28.0/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v0.27.1...v0.28.0)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-04 20:57:14 +01:00
dependabot[bot]
4412836db6
Bump webpack from 5.72.0 to 5.98.0 in /frontend (#319)
Bumps [webpack](https://github.com/webpack/webpack) from 5.72.0 to 5.98.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.72.0...v5.98.0)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-04 20:53:38 +01:00
dependabot[bot]
8bab06fa37
Bump serialize-javascript from 6.0.0 to 6.0.2 in /frontend (#314)
Bumps [serialize-javascript](https://github.com/yahoo/serialize-javascript) from 6.0.0 to 6.0.2.
- [Release notes](https://github.com/yahoo/serialize-javascript/releases)
- [Commits](https://github.com/yahoo/serialize-javascript/compare/v6.0.0...v6.0.2)

---
updated-dependencies:
- dependency-name: serialize-javascript
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-04 20:53:30 +01:00
dependabot[bot]
3f50da8036
Bump express from 4.18.0 to 4.21.2 in /frontend (#320)
Bumps [express](https://github.com/expressjs/express) from 4.18.0 to 4.21.2.
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/4.21.2/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.18.0...4.21.2)

---
updated-dependencies:
- dependency-name: express
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-04 20:53:20 +01:00
dependabot[bot]
9f746228b1
Bump loader-utils in /frontend (#325)
Bumps  and [loader-utils](https://github.com/webpack/loader-utils). These dependencies needed to be updated together.

Updates `loader-utils` from 1.4.0 to 2.0.4
- [Release notes](https://github.com/webpack/loader-utils/releases)
- [Changelog](https://github.com/webpack/loader-utils/blob/v2.0.4/CHANGELOG.md)
- [Commits](https://github.com/webpack/loader-utils/compare/v1.4.0...v2.0.4)

Updates `loader-utils` from 2.0.2 to 2.0.4
- [Release notes](https://github.com/webpack/loader-utils/releases)
- [Changelog](https://github.com/webpack/loader-utils/blob/v2.0.4/CHANGELOG.md)
- [Commits](https://github.com/webpack/loader-utils/compare/v1.4.0...v2.0.4)

---
updated-dependencies:
- dependency-name: loader-utils
  dependency-type: indirect
- dependency-name: loader-utils
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-04 20:44:00 +01:00
Maksim Nabokikh
f215e6f157
Multiplatform and staged builds (#323)
Signed-off-by: maksim.nabokikh <max.nabokih@gmail.com>
2025-03-04 20:43:35 +01:00
dependabot[bot]
540b5836b2
Bump google.golang.org/protobuf from 1.27.1 to 1.33.0 (#324)
Bumps google.golang.org/protobuf from 1.27.1 to 1.33.0.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-04 10:03:43 +01:00
dependabot[bot]
9317725352
Bump github.com/prometheus/client_golang from 1.11.0 to 1.11.1 (#219)
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.11.0 to 1.11.1.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.11.0...v1.11.1)

---
updated-dependencies:
- dependency-name: github.com/prometheus/client_golang
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-04 09:54:56 +01:00
dependabot[bot]
f08fae7f14
Bump golang.org/x/net from 0.0.0-20220114011407-0dd24b26b47d to 0.33.0 (#313)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.0.0-20220114011407-0dd24b26b47d to 0.33.0.
- [Commits](https://github.com/golang/net/commits/v0.33.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-04 09:51:23 +01:00
Maksim Nabokikh
b7d1b3cad3
More CI fixes (#321)
Signed-off-by: maksim.nabokikh <max.nabokih@gmail.com>
2025-03-04 09:41:47 +01:00
dependabot[bot]
76faf8db73
Bump gopkg.in/yaml.v3 from 3.0.0-20210107192922-496545a6307b to 3.0.0 (#241)
Bumps gopkg.in/yaml.v3 from 3.0.0-20210107192922-496545a6307b to 3.0.0.

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

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

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

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

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

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

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

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

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

* replace whitespace with tab
2023-09-12 11:05:28 +03:00
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
Ilya Sosnovsky
5d41f6d91e
Fix multiarch build 2022-09-05 11:48:34 +03:00
Ilya Sosnovsky
7db35753ad
Merge pull request #137 from miklezzzz/master
fix absent labels for secrets
2022-09-01 14:42:26 +03:00
Mikhail Scherba
10f7441c49 fix absent labels for secrets 2022-09-01 13:15:22 +03:00
Ilya Sosnovsky
ca53605554
Merge pull request #132 from flant/rc/2.0.0
Release 2.0.0
2022-08-29 12:20:48 +03:00
Ilya Sosnovsky
d012141b51 refactoring 2022-08-12 13:52:45 +03:00
Ilya Sosnovsky
0dfe9f9494 Add example dashboard 2022-08-04 15:13:41 +03:00
Ilya Sosnovsky
3e599eb989 Merge branch 'rc/2.0.0' of github.com:flant/ovpn-admin into rc/2.0.0 2022-08-02 17:19:58 +03:00
Ilya Sosnovsky
67dd4538ad small refactoring to reduce os.exec calls 2022-08-02 17:19:27 +03:00
Ilya Sosnovsky
5146d04a0d
Merge branch 'master' into rc/2.0.0 2022-07-21 18:40:57 +03:00
Ilya Sosnovsky
a0daf5b4d7 add feature for rotate and delete users; fixes; refactoring 2022-07-21 18:37:34 +03:00
Ilya Sosnovsky
f369639a2a
Merge pull request #126 from sabuhish/master
Fix typo error
2022-06-08 13:17:21 +03:00
sabuhish
5a6724bf6a Fix typo error 2022-05-18 11:37:02 +04:00
vitaliy-sn
53119e17b2
Merge pull request #122 from flant/fix-crl-file-permissions
Fix crl.pem file permissions
2022-04-27 23:03:57 +03:00
Vitaliy Snurnitsin
5705d2f60b Fix crl file permissions 2022-04-27 21:51:43 +03:00
vitaliy-sn
99569daf31
Merge pull request #121 from flant/multiple-lb
Add ability to specify multiple --ovpn.service
2022-04-27 15:29:27 +03:00
Vitaliy Snurnitsin
47abe3bc1a Added ability to specify multiple --ovpn.service 2022-04-27 13:45:43 +03:00
vitaliy-sn
cd746c20b5
Merge pull request #99 from vitaliy-sn/k8s-secrets-storage
K8s secrets storage
2022-03-18 17:44:02 +03:00
Vitaliy Snurnitsin
b26d0968e1 add helm chart example 2022-03-18 17:36:57 +03:00
Vitaliy Snurnitsin
7f1da70b9d kubernetes.secrets storage.backend implementation 2022-03-18 17:36:57 +03:00
Vitaliy Snurnitsin
4b7ef65a66 add storage.backend selection (filesystem or kubernetes.secrets) 2022-03-18 17:36:57 +03:00
Vitaliy Snurnitsin
77adc1108c openssl binary replaced with golang crypto/x509 library 2022-01-29 03:07:45 +03:00
Vitaliy Snurnitsin
9b1b34d4c4 logs, gofmt 2022-01-29 03:06:35 +03:00
Vitaliy Snurnitsin
5af16605d2 go 1.17 2022-01-29 03:04:26 +03:00
Vitaliy Snurnitsin
f180a9cc5a node 16.13 2022-01-29 02:22:08 +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
Ilya Sosnovsky
d3b5a77efb fixes for build scripts 2021-11-29 13:40:05 +03:00
Ilya Sosnovsky
af65b36d2b fix 2021-11-29 13:10:55 +03:00
Ilya Sosnovsky
2d75fb1f4b bump go version 2021-11-29 12:57:45 +03:00
Ilya Sosnovsky
5ddbfe81ed
Merge pull request #88 from flant/rc/1.7.5
fixed a broken release
2021-11-29 11:37:28 +03:00
Ilya Sosnovsky
9873c2cb76 fixed a broken release 2021-11-29 11:34:34 +03:00
Ilya Sosnovsky
4bdb74411c
Merge pull request #86 from smalot/fix-packr-path
Fix packr path dependency
2021-11-25 20:30:00 +03:00
Sebastien Malot
b378ae17dd Fix packr path dependency 2021-11-23 08:57:11 +01:00
Sebastien Malot
1b421070cb Fix packr path dependency 2021-11-23 08:55:59 +01:00
Dmitry Shurupov
3c77273990
Security disclaimer for README 2021-11-11 11:12:10 +07:00
Dmitry Shurupov
53e9cb7835 Fixing env vars descriptions and names 2021-10-22 11:35:16 +07:00
Dmitry Shurupov
f5507be24d
Adding env vars and other fixes for README 2021-10-22 11:26:09 +07:00
Ilya Sosnovsky
5543829717 fix arm workflow 2021-10-21 14:26:50 +03:00
Ilya Sosnovsky
2daadf30ae
Merge pull request #63 from flant/rc/1.7.4
Release 1.7.4
2021-10-21 14:15:46 +03:00
Ilya Sosnovsky
ed71ed1537 envvar configuraion; arm build; docs fixes 2021-10-21 13:01:03 +03:00
Dmitry Shurupov
a5d8f3dd96
Merge pull request #61 from MinimaJack/patch-1
Fix a typo in client.conf.tpl
2021-10-20 13:51:14 +07:00
Dmitry Shurupov
61905a911a
Merge pull request #62 from olegznv/oleg-readme
README text typo correction
2021-10-20 13:47:46 +07:00
olegznv
214d7b30c5 forgot fix 2021-10-20 09:37:10 +03:00
Evgeniy Vanzhula
e07c1b6dfe
Update client.conf.tpl
typo fix
2021-10-19 16:47:27 +03:00
Ilya Sosnovsky
c27229e920 build changes 2021-10-15 08:05:30 +03:00
Ilya Sosnovsky
8db6d93bcb show client config in log only in verbose mode; actions changes 2021-10-15 07:54:24 +03:00
Ilya Sosnovsky
c1970c26e4
Lowercase protocol for remote from k8s api 2021-10-14 08:11:07 +03:00
Ilya Sosnovsky
924230c6ba
Merge pull request #50 from noamkush/iptables-fix
Allow iptables delete command to fail
2021-10-13 11:50:45 +03:00
Noam
d17ea9aee9 Allowed iptables delete command to fail. 2021-10-07 15:18:58 +03:00
Ilya Sosnovsky
9fa3c44f9a
Merge pull request #47 from flant/rc-1.7.0
Release 1.7.0
2021-10-05 18:34:45 +03:00
Ilya Sosnovsky
77c0fbb778 Prepare for release 1.7.0 2021-10-05 18:09:29 +03:00
Ilya Sosnovsky
ace42f729e
Merge pull request #37 from wzooff/feat/add-custom-client-config-template
add option to specify custom user template path
2021-10-05 15:08:17 +03:00
Ilya Sosnovsky
2012f07135
Merge pull request #34 from CheyenneForbes/patch-1
Ensure `rows` is set to an array
2021-10-05 15:07:15 +03:00
Ilya Sosnovsky
63d76b5991
Merge pull request #10 from vabrn/patch-1
Update ccd.tpl
2021-10-05 15:05:22 +03:00
Andrii Veklychev
fa9022ee1b add option to specify custom user template path 2021-07-22 01:44:04 +03:00
CheyenneForbes
44c2b34f6d
Ensure rows is set to an array 2021-07-16 03:03:12 -05:00
Dmitry Shurupov
05d7462a79
Merge pull request #21 from flant/boris/readme_fix
Update README.md
2021-04-22 15:45:13 +07:00
Uzhinskiy Boris
f4d8a4ab01
Update README.md
correct names for coreutils & easy-rsa packages
2021-04-22 11:42:57 +03:00
Dmitry Shurupov
c954d05f26
Announcement link & story for README 2021-03-26 15:04:34 +07:00
vabrn
8c36329b0c
Update ccd.tpl 2021-03-22 13:37:26 +03:00
53 changed files with 12485 additions and 8623 deletions

View File

@ -3,7 +3,7 @@
*.iml *.iml
out out
gen gen
.github
easyrsa easyrsa
easyrsa_master easyrsa_master
@ -13,6 +13,7 @@ ccd_master
ccd_slave ccd_slave
werf.yaml werf.yaml
frontend/node_modules frontend/node_modules
frontend/static/dist
openvpn-web-ui openvpn-web-ui
openvpn-ui openvpn-ui
openvpn-admin openvpn-admin
@ -21,3 +22,5 @@ ovpn-admin
docker-compose.yaml docker-compose.yaml
docker-compose-slave.yaml docker-compose-slave.yaml
img img
dashboard
.helm

24
.editorconfig Normal file
View File

@ -0,0 +1,24 @@
; https://editorconfig.org/
root = true
[*]
insert_final_newline = true
charset = utf-8
trim_trailing_whitespace = true
indent_style = space
indent_size = 2
[{Makefile,go.mod,go.sum,*.go,.gitmodules}]
indent_style = tab
indent_size = 4
[*.md]
indent_size = 4
trim_trailing_whitespace = false
eclint_indent_style = unset
[Dockerfile]
indent_size = 4

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

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

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

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

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

@ -0,0 +1,35 @@
name: Release Charts
on:
push:
branches:
- master
paths:
- 'charts/**'
jobs:
chart-release:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
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.7.0
with:
charts_dir: charts
config: charts/cr.yaml
env:
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"

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

@ -0,0 +1,107 @@
name: Chart Test
on:
push:
tags:
- v*
branches:
- master
pull_request:
branches:
- master
jobs:
chart:
name: Chart
runs-on: ubuntu-latest
outputs:
changed: ${{ steps.changes.outputs.changed }}
steps:
- name: Checkout
uses: actions/checkout@v6
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@v6
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@v6
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@v6
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.13.0
with:
version: v0.17.0
node_image: ${{ steps.node_image.outputs.image }}
- name: Test
run: ct install

View File

@ -1,30 +0,0 @@
name: Build and publish to Docker Hub (releases only)
on:
release:
types: [created]
jobs:
build:
name: build latest images for relase
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

View File

@ -1,34 +1,59 @@
name: Build and publish to Docker Hub (tags only) name: Build and publish tags to ghcr.io
on: on:
push: push:
tags: tags:
- '*' - v*
branches:
- master
pull_request:
branches:
- master
env:
WERF_STAGED_DOCKERFILE_VERSION: v2
WERF_BUILDAH_MODE: auto
WERF_ENV: ${{ (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/')) && 'release' || 'pr' }}
jobs: jobs:
build: build:
name: build images for tag name: build images for tag
runs-on: ubuntu-latest runs-on: ubuntu-22.04
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v6
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Get the version
id: get_version - name: Install werf
run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//} uses: werf/actions/install@v2
- name: Push openvpn image to Docker Hub
uses: docker/build-push-action@v1 - name: Set up QEMU
uses: docker/setup-qemu-action@v3
with: with:
username: ${{ secrets.DOCKER_USER }} platforms: linux/amd64, linux/arm64, linux/arm/v7, linux/arm/v8
password: ${{ secrets.DOCKER_PASS }}
repository: flant/ovpn-admin - name: Login into ghcr.io
tags: openvpn-${{ steps.get_version.outputs.VERSION }} shell: bash
dockerfile: Dockerfile.openvpn run: werf cr login -u ${{ github.actor }} -p ${{ github.token }} ghcr.io/${{ github.repository }}
- name: Push ovpn-admin image to Docker Hub
uses: docker/build-push-action@v1 - name: Extract Docker metadata
uses: docker/metadata-action@v5.10.0
with: with:
username: ${{ secrets.DOCKER_USER }} images: ghcr.io/${{ github.repository }}/${{ matrix.name }}
password: ${{ secrets.DOCKER_PASS }}
repository: flant/ovpn-admin - name: Build Image
tags: ${{ steps.get_version.outputs.VERSION }} if: ${{ github.event_name == 'pull_request' }}
dockerfile: Dockerfile 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" --env "$WERF_ENV"
- 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 }} --env "$WERF_ENV"

29
.github/workflows/release.yaml vendored Normal file
View File

@ -0,0 +1,29 @@
name: Build and publish binaries (releases only)
on:
release:
types: [created]
jobs:
releases-matrix:
name: Release Go Binary
runs-on: ubuntu-latest
env:
CGO_ENABLED: 1
strategy:
matrix:
goos: [linux]
goarch: ["386", "amd64"]
steps:
- name: checkout code
uses: actions/checkout@v6
- name: build binaries
uses: wangyoucao577/go-release-action@v1.55
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
goversion: 1.23
goos: ${{ matrix.goos }}
goarch: ${{ matrix.goarch }}
build_command: bash -ex ./build.sh
pre_command: bash -ex ./install-deps.sh
binary_name: "ovpn-admin"
asset_name: ovpn-admin-${{ matrix.goos }}-${{ matrix.goarch }}

29
.github/workflows/release_arm.yaml vendored Normal file
View File

@ -0,0 +1,29 @@
name: Build and publish arm binaries (releases only)
on:
release:
types: [created]
jobs:
releases-matrix:
name: Release Go Binary
runs-on: ubuntu-latest
env:
CGO_ENABLED: 1
strategy:
matrix:
goos: [linux]
goarch: ["arm", "arm64"]
steps:
- name: checkout code
uses: actions/checkout@v6
- name: build binaries
uses: wangyoucao577/go-release-action@v1.55
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
goversion: 1.23
goos: ${{ matrix.goos }}
goarch: ${{ matrix.goarch }}
build_command: bash -ex ./build_arm.sh
pre_command: bash -ex ./install-deps-arm.sh
binary_name: "ovpn-admin"
asset_name: ovpn-admin-${{ matrix.goos }}-${{ matrix.goarch }}

6
.gitignore vendored
View File

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

View File

@ -1,16 +0,0 @@
FROM node:14.2-alpine3.11 AS frontend-builder
COPY frontend/ /app
RUN cd /app && npm install && npm run build
FROM golang:1.14.2-buster AS backend-builder
COPY --from=frontend-builder /app/static /app/frontend/static
COPY . /app
RUN go get -u github.com/gobuffalo/packr/v2/packr2 && cd /app && packr2 && env CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -ldflags='-linkmode external -extldflags "-static" -s -w' -o ovpn-admin && packr2 clean
FROM alpine:3.13
WORKDIR /app
COPY --from=backend-builder /app/ovpn-admin /app
RUN apk add --update bash easy-rsa && \
ln -s /usr/share/easy-rsa/easyrsa /usr/local/bin && \
wget https://github.com/pashcovich/openvpn-user/releases/download/v1.0.3-rc.1/openvpn-user-linux-amd64.tar.gz -O - | tar xz -C /usr/local/bin && \
rm -rf /tmp/* /var/tmp/* /var/cache/apk/* /var/cache/distfiles/*

View File

@ -1,7 +1,9 @@
FROM alpine:3.13 FROM alpine:3.23
RUN apk add --update bash openvpn easy-rsa && \ ARG TARGETARCH
RUN apk add --update bash openvpn easy-rsa iptables && \
ln -s /usr/share/easy-rsa/easyrsa /usr/local/bin && \ ln -s /usr/share/easy-rsa/easyrsa /usr/local/bin && \
wget https://github.com/pashcovich/openvpn-user/releases/download/v1.0.3-rc.1/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/* 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 COPY setup/ /etc/openvpn/setup
RUN chmod +x /etc/openvpn/setup/configure.sh RUN chmod +x /etc/openvpn/setup/configure.sh

20
Dockerfile.ovpn-admin Normal file
View 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.24.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.23
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
View File

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

171
README.md
View File

@ -2,43 +2,47 @@
Simple web UI to manage OpenVPN users, their certificates & routes in Linux. While backend is written in Go, frontend is based on Vue.js. Simple web UI to manage OpenVPN users, their certificates & routes in Linux. While backend is written in Go, frontend is based on Vue.js.
***DISCLAIMER!** This project was created for experienced users (system administrators) and private (e.g., protected by network policies) environments only. Thus, it is not implemented with security in mind (e.g., it doesn't strictly check all parameters passed by users, etc.). It also relies heavily on files and fails if required files aren't available.*
## Features ## Features
* Adding OpenVPN users (generating certificates for them); * Adding, deleting OpenVPN users (generating certificates for them);
* Revoking/restoring users certificates; * Revoking/restoring/rotating users certificates;
* Generating ready-to-user config files; * Generating ready-to-user config files;
* Providing metrics for Prometheus, including certificates expiration date, number of (connected/total) users, information about connected users; * Providing metrics for Prometheus, including certificates expiration date, number of (connected/total) users, information about connected users;
* (optionally) Specifying CCD (`client-config-dir`) for each user; * (optionally) Specifying CCD (`client-config-dir`) for each user;
* (optionally) Operating in a master/slave mode (syncing certs & CCD with other server); * (optionally) Operating in a master/slave mode (syncing certs & CCD with other server);
* (optionally) Specifying/changing password for additional authorization in OpenVPN. * (optionally) Specifying/changing password for additional authorization in OpenVPN;
* (optionally) Specifying the Kubernetes LoadBalancer if it's used in front of the OpenVPN server (to get an automatically defined `remote` in the `client.conf.tpl` template).
* (optionally) Storing certificates and other files in Kubernetes Secrets (**Attention, this feature is experimental!**).
### Screenshots ### Screenshots
Managing users in ovpn-admin: Managing users in ovpn-admin:
![ovpn-admin UI](https://raw.githubusercontent.com/flant/ovpn-admin/master/img/ovpn-admin-users.png) ![ovpn-admin UI](https://raw.githubusercontent.com/palark/ovpn-admin/master/img/ovpn-admin-users.png)
An example of dashboard made using ovpn-admin metrics: An example of dashboard made using ovpn-admin metrics:
![ovpn-admin metrics](https://raw.githubusercontent.com/flant/ovpn-admin/master/img/ovpn-admin-metrics.png) ![ovpn-admin metrics](https://raw.githubusercontent.com/palark/ovpn-admin/master/img/ovpn-admin-metrics.png)
## Installation ## Installation
### Disclaimer
This tool uses external calls for `bash`, `core-utils` and `easyrsa`, thus **Linux systems only are supported** at the moment.
### 1. Docker ### 1. Docker
There is a ready-to-use [docker-compose.yaml](https://github.com/flant/ovpn-admin/blob/master/docker-compose.yaml), so you can just change/add values you need and start it with [start.sh](https://github.com/flant/ovpn-admin/blob/master/start.sh). There is a ready-to-use [docker-compose.yaml](https://github.com/palark/ovpn-admin/blob/master/docker-compose.yaml), so you can just change/add values you need and start it with [start.sh](https://github.com/palark/ovpn-admin/blob/master/start.sh).
Requirements. You need [Docker](https://docs.docker.com/get-docker/) and [docker-compose](https://docs.docker.com/compose/install/) installed. Requirements:
You need [Docker](https://docs.docker.com/get-docker/) and [docker-compose](https://docs.docker.com/compose/install/) installed.
Commands to execute: Commands to execute:
```bash ```bash
git clone https://github.com/flant/ovpn-admin.git git clone https://github.com/palark/ovpn-admin.git
cd ovpn-admin cd ovpn-admin
./start.sh ./start.sh
``` ```
#### 1.1
Ready docker images available on [Docker Hub](https://hub.docker.com/r/flant/ovpn-admin/tags)
. Tags are simple: `$VERSION` or `latest` for ovpn-admin and `openvpn-$VERSION` or `openvpn-latest` for openvpn-server
### 2. Building from source ### 2. Building from source
@ -50,18 +54,29 @@ Requirements. You need Linux with the following components installed:
Commands to execute: Commands to execute:
```bash ```bash
git clone https://github.com/flant/ovpn-admin.git git clone https://github.com/palark/ovpn-admin.git
cd ovpn-admin cd ovpn-admin
./bootstrap.sh ./bootstrap.sh
./build.sh ./build.sh
./ovpn-admin ./ovpn-admin
``` ```
(Please don't forgot to configure all needed params in advance.) (Please don't forget to configure all needed params in advance.)
### 3. Prebuilt binary (WIP) ### 3. Prebuilt binary
You can also download & use prebuilt binaries from the [releases](https://github.com/flant/ovpn-admin/releases) 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`).
* 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 ## Usage
@ -69,38 +84,100 @@ You can also download & use prebuilt binaries from the [releases](https://github
usage: ovpn-admin [<flags>] usage: ovpn-admin [<flags>]
Flags: Flags:
--help Show context-sensitive help (also try --help-long and --help-man). --help show context-sensitive help (try also --help-long and --help-man)
--listen.host="0.0.0.0" host for ovpn-admin
--listen.port="8080" port for ovpn-admin
--role="master" server role master or slave
--master.host="http://127.0.0.1"
url for master server
--master.basic-auth.user="" user for basic auth on master server url
--master.basic-auth.password=""
password for basic auth on master server url
--master.sync-frequency=600 master host data sync frequency in seconds.
--master.sync-token=TOKEN master host data sync security token
--ovpn.network="172.16.100.0/24"
network for openvpn server
--ovpn.server=HOST:PORT:PROTOCOL ...
comma separated addresses for openvpn servers
--mgmt=main=127.0.0.1:8989 ...
comma separated (alias=address) for openvpn servers mgmt interfaces
--metrics.path="/metrics" URL path for surfacing collected metrics
--easyrsa.path="./easyrsa/" path to easyrsa dir
--easyrsa.index-path="./easyrsa/pki/index.txt"
path to easyrsa index file.
--ccd Enable client-config-dir.
--ccd.path="./ccd" path to client-config-dir
--auth.password Enable additional password authorization.
--auth.db="./easyrsa/pki/users.db"
Database path fort password authorization.
--debug Enable debug mode.
--verbose Enable verbose mode.
--version Show application version.
--listen.host="0.0.0.0" host for ovpn-admin
(or OVPN_LISTEN_HOST)
--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)
--master.host="http://127.0.0.1"
(or OVPN_MASTER_HOST) URL for the master server
--master.basic-auth.user="" user for master server's Basic Auth
(or OVPN_MASTER_USER)
--master.basic-auth.password=""
(or OVPN_MASTER_PASSWORD) password for master server's Basic Auth
--master.sync-frequency=600 master host data sync frequency in seconds
(or OVPN_MASTER_SYNC_FREQUENCY)
--master.sync-token=TOKEN master host data sync security token
(or OVPN_MASTER_TOKEN)
--ovpn.network="172.16.100.0/24"
(or OVPN_NETWORK) NETWORK/MASK_PREFIX for OpenVPN server
--ovpn.server=HOST:PORT:PROTOCOL ...
(or OVPN_SERVER) HOST:PORT:PROTOCOL for OpenVPN server
can have multiple values
--ovpn.server.behindLB enable if your OpenVPN server is behind Kubernetes
(or OVPN_LB) Service having the LoadBalancer type
--ovpn.service="openvpn-external"
(or OVPN_LB_SERVICE) the name of Kubernetes Service having the LoadBalancer
type if your OpenVPN server is behind it
--mgmt=main=127.0.0.1:8989 ...
(or OVPN_MGMT) ALIAS=HOST:PORT for OpenVPN server mgmt interface;
can have multiple values
--metrics.path="/metrics" URL path for exposing collected metrics
(or OVPN_METRICS_PATH)
--easyrsa.path="./easyrsa/" path to easyrsa dir
(or EASYRSA_PATH)
--easyrsa.index-path="./easyrsa/pki/index.txt"
(or OVPN_INDEX_PATH) path to easyrsa index file
--ccd enable client-config-dir
(or OVPN_CCD)
--ccd.path="./ccd" path to client-config-dir
(or OVPN_CCD_PATH)
--templates.clientconfig-path=""
(or OVPN_TEMPLATES_CC_PATH) path to custom client.conf.tpl
--templates.ccd-path="" path to custom ccd.tpl
(or OVPN_TEMPLATES_CCD_PATH)
--auth.password enable additional password authorization
(or OVPN_AUTH)
--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)
--log.format set log format: text, json (default text)
(or LOG_FORMAT)
--storage.backend storage backend: filesystem, kubernetes.secrets (default filesystem)
(or STORAGE_BACKEND)
--version show application version
``` ```
## Further information ## Authors
Please feel free to use [issues](https://github.com/flant/ovpn-admin/issues) and [discussions](https://github.com/flant/ovpn-admin/discussions) to get help from maintainers & community. ovpn-admin was originally created in [Flant](https://github.com/flant/) and used internally for years.
In March 2021, it [went public](https://medium.com/flant-com/introducing-ovpn-admin-a-web-interface-to-manage-openvpn-users-d81705ad8f23) and was still developed in Flant.
Namely, [@vitaliy-sn](https://github.com/vitaliy-sn) created its first version in Python, and [@pashcovich](https://github.com/pashcovich) rewrote it in Go.
In November 2024, this project was moved to [Palark](https://github.com/palark/), which is currently responsible for its maintenance and development.

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
mkdir -p {easyrsa,ccd} mkdir -p {easyrsa,ccd}

View File

@ -1,12 +1,11 @@
#!/bin/bash #!/usr/bin/env bash
PATH=$PATH:~/go/bin PATH=$PATH:~/go/bin
# go get -u github.com/gobuffalo/packr/v2/packr2
cd frontend && npm install && npm run build && cd .. cd frontend && npm install && npm run build && cd ..
packr2 packr2
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -ldflags "-linkmode external -extldflags -static -s -w" -o ovpn-admin CGO_ENABLED=1 GOOS=linux GOARCH=${GOARCH:-amd64} go build -a -tags netgo -ldflags "-linkmode external -extldflags -static -s -w" $@
packr2 clean packr2 clean

18
build_arm.sh Executable file
View File

@ -0,0 +1,18 @@
#!/usr/bin/env bash
PATH=$PATH:~/go/bin
cd frontend && npm install && npm run build && cd ..
packr2
if [[ "$GOOS" == "linux" ]]; then
if [[ "$GOARCH" == "arm" ]]; then
CC=arm-linux-gnueabi-gcc CGO_ENABLED=1 GOOS=linux GOARCH=arm go build -a -tags netgo -ldflags "-linkmode external -extldflags -static -s -w" $@
fi
if [[ "$GOARCH" == "arm64" ]]; then
CC=aarch64-linux-gnu-gcc CGO_ENABLED=1 GOOS=linux GOARCH=arm64 go build -a -tags netgo -ldflags "-linkmode external -extldflags -static -s -w" $@
fi
fi
packr2 clean

207
certificates.go Normal file
View File

@ -0,0 +1,207 @@
package main
import (
"bytes"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"errors"
"fmt"
"math/big"
"strconv"
"time"
)
// decode certificate from PEM to x509
func decodeCert(certPEMBytes []byte) (cert *x509.Certificate, err error) {
certPem, _ := pem.Decode(certPEMBytes)
certPemBytes := certPem.Bytes
cert, err = x509.ParseCertificate(certPemBytes)
if err != nil {
return
}
return
}
// decode private key from PEM to RSA format
func decodePrivKey(privKey []byte) (key *rsa.PrivateKey, err error) {
privKeyPem, _ := pem.Decode(privKey)
key, err = x509.ParsePKCS1PrivateKey(privKeyPem.Bytes)
if err == nil {
return
}
tmp, err := x509.ParsePKCS8PrivateKey(privKeyPem.Bytes)
if err != nil {
err = errors.New("error parse private key")
return
}
key, _ = tmp.(*rsa.PrivateKey)
return
}
// return PEM encoded private key
func genPrivKey() (privKeyPEM *bytes.Buffer, err error) {
privKey, err := rsa.GenerateKey(rand.Reader, 2048)
//privKeyPKCS1 := x509.MarshalPKCS1PrivateKey(privKey)
privKeyPKCS8, err := x509.MarshalPKCS8PrivateKey(privKey)
if err != nil {
return
}
privKeyPEM = new(bytes.Buffer)
err = pem.Encode(privKeyPEM, &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: privKeyPKCS8,
})
return
}
// return PEM encoded certificate
func genCA(privKey *rsa.PrivateKey) (issuerPEM *bytes.Buffer, err error) {
serialNumberRange := new(big.Int).Lsh(big.NewInt(1), 128)
issuerSerial, err := rand.Int(rand.Reader, serialNumberRange)
issuerTemplate := x509.Certificate{
BasicConstraintsValid: true,
IsCA: true,
SerialNumber: issuerSerial,
Subject: pkix.Name{
CommonName: "ca",
},
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(10, 0, 0),
}
issuerBytes, err := x509.CreateCertificate(rand.Reader, &issuerTemplate, &issuerTemplate, &privKey.PublicKey, privKey)
if err != nil {
return
}
issuerPEM = new(bytes.Buffer)
_ = pem.Encode(issuerPEM, &pem.Block{
Type: "CERTIFICATE",
Bytes: issuerBytes,
})
return
}
// return PEM encoded certificate
func genServerCert(privKey, caPrivKey *rsa.PrivateKey, ca *x509.Certificate, cn string) (issuerPEM *bytes.Buffer, err error) {
serialNumberRange := new(big.Int).Lsh(big.NewInt(1), 128)
serial, err := rand.Int(rand.Reader, serialNumberRange)
template := x509.Certificate{
BasicConstraintsValid: true,
DNSNames: []string{cn},
SerialNumber: serial,
Subject: pkix.Name{
CommonName: cn,
},
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
NotBefore: time.Now(),
NotAfter: ca.NotAfter,
}
issuerBytes, err := x509.CreateCertificate(rand.Reader, &template, ca, &privKey.PublicKey, caPrivKey)
if err != nil {
return
}
issuerPEM = new(bytes.Buffer)
_ = pem.Encode(issuerPEM, &pem.Block{
Type: "CERTIFICATE",
Bytes: issuerBytes,
})
return
}
// return PEM encoded certificate
func genClientCert(privKey, caPrivKey *rsa.PrivateKey, ca *x509.Certificate, cn string) (issuerPEM *bytes.Buffer, err error) {
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},
SerialNumber: serial,
Subject: pkix.Name{
CommonName: cn,
},
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
NotBefore: notBefore,
NotAfter: notAfter,
}
issuerBytes, err := x509.CreateCertificate(rand.Reader, &template, ca, &privKey.PublicKey, caPrivKey)
if err != nil {
return
}
issuerPEM = new(bytes.Buffer)
_ = pem.Encode(issuerPEM, &pem.Block{
Type: "CERTIFICATE",
Bytes: issuerBytes,
})
return
}
// return PEM encoded CRL
func genCRL(certs []*RevokedCert, ca *x509.Certificate, caKey *rsa.PrivateKey) (crlPEM *bytes.Buffer, err error) {
var revokedCertificates []pkix.RevokedCertificate
for _, cert := range certs {
revokedCertificates = append(revokedCertificates, pkix.RevokedCertificate{SerialNumber: cert.Cert.SerialNumber, RevocationTime: cert.RevokedTime})
}
revocationList := &x509.RevocationList{
//SignatureAlgorithm: x509.SHA256WithRSA,
RevokedCertificates: revokedCertificates,
Number: big.NewInt(1),
ThisUpdate: time.Now(),
NextUpdate: time.Now().Add(180 * time.Hour * 24),
//ExtraExtensions: []pkix.Extension{},
}
crl, err := x509.CreateRevocationList(rand.Reader, revocationList, ca, caKey)
if err != nil {
return nil, err
}
crlPEM = new(bytes.Buffer)
err = pem.Encode(crlPEM, &pem.Block{
Type: "X509 CRL",
Bytes: crl,
})
if err != nil {
return
}
return
}

2
charts/cr.yaml Normal file
View File

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

View File

@ -0,0 +1,15 @@
apiVersion: v1
appVersion: "2.0.2"
description: Simple web UI to manage OpenVPN users, their certificates & routes in Linux. While backend is written in Go, frontend is based on Vue.js.
name: openvpn-admin
version: "0.0.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

View File

@ -0,0 +1,40 @@
# openvpn-admin
![Version: 0.0.3](https://img.shields.io/badge/Version-0.0.3-informational?style=flat-square) ![AppVersion: 2.0.2](https://img.shields.io/badge/AppVersion-2.0.2-informational?style=flat-square)
Simple web UI to manage OpenVPN users, their certificates & routes in Linux. While backend is written in Go, frontend is based on Vue.js.
## Maintainers
| Name | Email | Url |
| ---- | ------ | --- |
| nabokihms | <max.nabokih@gmail.com> | <github.com/nabokihms> |
## Source Code
* <https://github.com/palark/openvpn-admin>
## Requirements
Kubernetes: `>=1.14.0-0`
## Values
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| 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)

View File

@ -0,0 +1,88 @@
{{ $openvpnNetwork := required "A valid .Values.openvpn.subnet entry required!" .Values.openvpn.subnet }}
{{ $openvpnNetworkAddress := index (splitList "/" $openvpnNetwork) 0 }}
{{ $openvpnNetworkNetmask := index (splitList "/" $openvpnNetwork) 1 }}
---
apiVersion: v1
kind: ConfigMap
metadata:
name: openvpn
data:
openvpn.conf: |-
user nobody
group nogroup
mode server
tls-server
# dev-type tun
dev tun
proto tcp-server
port 1194
# local 127.0.0.1
management 127.0.0.1 8989
tun-mtu 1500
mssfix
# only udp
#fragment 1300
keepalive 10 60
client-to-client
persist-key
persist-tun
cipher AES-128-CBC
duplicate-cn
server {{ $openvpnNetworkAddress }} {{ $openvpnNetworkNetmask }}
topology subnet
push "topology subnet"
push "route-metric 9999"
verb 4
ifconfig-pool-persist /tmp/openvpn.ipp
status /tmp/openvpn.status
key-direction 0
ca /etc/openvpn/certs/pki/ca.crt
key /etc/openvpn/certs/pki/private/server.key
cert /etc/openvpn/certs/pki/issued/server.crt
dh /etc/openvpn/certs/pki/dh.pem
crl-verify /etc/openvpn/certs/pki/crl.pem
tls-auth /etc/openvpn/certs/pki/ta.key
client-config-dir /etc/openvpn/ccd
entrypoint.sh: |-
#!/bin/sh
set -x
iptables -t nat -A POSTROUTING -s {{ $openvpnNetworkAddress }}/{{ $openvpnNetworkNetmask }} ! -d {{ $openvpnNetworkAddress }}/{{ $openvpnNetworkNetmask }} -j MASQUERADE
mkdir -p /dev/net
if [ ! -c /dev/net/tun ]; then
mknod /dev/net/tun c 10 200
fi
wait_file() {
file_path="$1"
while true; do
if [ -f $file_path ]; then
break
fi
echo "wait $file_path"
sleep 2
done
}
easyrsa_path="/etc/openvpn/certs"
wait_file "$easyrsa_path/pki/ca.crt"
wait_file "$easyrsa_path/pki/private/server.key"
wait_file "$easyrsa_path/pki/issued/server.crt"
wait_file "$easyrsa_path/pki/ta.key"
wait_file "$easyrsa_path/pki/dh.pem"
wait_file "$easyrsa_path/pki/crl.pem"
openvpn --config /etc/openvpn/openvpn.conf

View File

@ -0,0 +1,117 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: openvpn
spec:
selector:
matchLabels:
app: openvpn
template:
metadata:
labels:
app: openvpn
spec:
{{- if .Values.nodeSelector }}
nodeSelector:
{{- .Values.nodeSelector | toYaml | indent 8 | printf "\n%s" }}
{{- end }}
{{- if .Values.tolerations }}
tolerations:
{{- .Values.tolerations | toYaml | indent 8 | printf "\n%s" }}
{{- end }}
terminationGracePeriodSeconds: 0
serviceAccountName: openvpn
containers:
- name: ovpn-admin
image: {{ .Values.ovpnAdmin.repo }}:master
command:
- /bin/sh
- -c
- /app/ovpn-admin
--storage.backend="kubernetes.secrets"
--listen.host="0.0.0.0"
--listen.port="8000"
--role="master"
{{- if hasKey .Values.openvpn "inlet" }}
{{- if eq .Values.openvpn.inlet "LoadBalancer" }}
--ovpn.server.behindLB
--ovpn.service="openvpn-external"
{{- end }}
{{- end }}
--mgmt=main="127.0.0.1:8989"
--ccd --ccd.path="/mnt/ccd"
--easyrsa.path="/mnt/certs"
{{- $externalHost := "" }}
{{- if hasKey .Values.openvpn "inlet" }}
{{- if eq .Values.openvpn.inlet "ExternalIP" }}{{ $externalHost = .Values.openvpn.externalIP }}{{- end }}
{{- end }}
{{- if hasKey .Values.openvpn "externalHost" }}{{ $externalHost = .Values.openvpn.externalHost }}{{- end }}
{{- if ne $externalHost "" }}
--ovpn.server="{{ $externalHost }}:{{ .Values.openvpn.externalPort | default 5416 | quote }}:tcp"
{{- end }}
ports:
- name: ovpn-admin
protocol: TCP
containerPort: 8000
volumeMounts:
- name: certs
mountPath: /mnt/certs
- name: ccd
mountPath: /mnt/ccd
- name: openvpn
image: {{ .Values.ovpnAdmin.repo }}:master
command: [ '/entrypoint.sh' ]
# imagePullPolicy: Always
securityContext:
allowPrivilegeEscalation: false
capabilities:
add:
- NET_ADMIN
- NET_RAW
- MKNOD
- SETGID
- SETUID
drop:
- ALL
ports:
- name: openvpn-tcp
protocol: TCP
containerPort: 1194
{{- if eq .Values.openvpn.inlet "HostPort" }}
hostPort: {{ .Values.openvpn.hostPort }}
{{- end }}
volumeMounts:
- name: tmp
mountPath: /tmp
- name: dev-net
mountPath: /dev/net
- name: certs
mountPath: /etc/openvpn/certs
- name: ccd
mountPath: /etc/openvpn/ccd
- name: config
mountPath: /etc/openvpn/openvpn.conf
subPath: openvpn.conf
readOnly: true
- name: entrypoint
mountPath: /entrypoint.sh
subPath: entrypoint.sh
readOnly: true
volumes:
- name: tmp
emptyDir: {}
- name: dev-net
emptyDir: {}
- name: certs
emptyDir: {}
- name: ccd
emptyDir: {}
- name: config
configMap:
name: openvpn
defaultMode: 0644
- name: entrypoint
configMap:
name: openvpn
defaultMode: 0755

View File

@ -0,0 +1,32 @@
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ovpn-admin
annotations:
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.ingress.domain }}
secretName: ingress-tls
rules:
- host: {{ .Values.ingress.domain }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: ovpn-admin
port:
name: http

View File

@ -0,0 +1,36 @@
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: openvpn
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: openvpn
rules:
- apiGroups:
- ""
resources:
- services
verbs:
- get
- list
- apiGroups:
- ""
resources:
- secrets
verbs:
- "*"
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: openvpn
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: openvpn
subjects:
- kind: ServiceAccount
name: openvpn

View 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 }}

View File

@ -0,0 +1,57 @@
---
apiVersion: v1
kind: Service
metadata:
name: ovpn-admin
spec:
clusterIP: None
ports:
- name: http
port: 8000
protocol: TCP
targetPort: 8000
selector:
app: openvpn
---
{{- if hasKey .Values.openvpn "inlet" }}
{{- if eq .Values.openvpn.inlet "LoadBalancer" }}
---
apiVersion: v1
kind: Service
metadata:
name: openvpn-external
spec:
externalTrafficPolicy: Local
type: LoadBalancer
ports:
- name: openvpn-tcp
protocol: TCP
port: {{ .Values.openvpn.externalPort | default 1194 }}
targetPort: openvpn-tcp
selector:
app: openvpn
{{- else if eq .Values.openvpn.inlet "ExternalIP" }}
---
apiVersion: v1
kind: Service
metadata:
name: openvpn-external
spec:
type: ClusterIP
externalIPs:
- {{ .Values.openvpn.externalIP }}
ports:
- name: openvpn-tcp
port: {{ .Values.openvpn.externalPort | default 1194 }}
protocol: TCP
targetPort: openvpn-tcp
selector:
app: openvpn
{{- else if eq .Values.openvpn.inlet "HostPort" }}
---
{{- else }}
{{- cat "Unsupported inlet type" .inlet | fail }}
{{- end }}
{{- end }}

View 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

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": ""
}

View File

@ -7,6 +7,10 @@ services:
dockerfile: Dockerfile.openvpn dockerfile: Dockerfile.openvpn
image: openvpn:local image: openvpn:local
command: /etc/openvpn/setup/configure.sh command: /etc/openvpn/setup/configure.sh
environment:
OVPN_SERVER_NET: "192.168.100.0"
OVPN_SERVER_MASK: "255.255.255.0"
OVPN_PASSWD_AUTH: "true"
cap_add: cap_add:
- NET_ADMIN - NET_ADMIN
ports: ports:
@ -18,8 +22,21 @@ services:
ovpn-admin: ovpn-admin:
build: build:
context: . context: .
dockerfile: Dockerfile.ovpn-admin
image: ovpn-admin:local image: ovpn-admin:local
command: /app/ovpn-admin --debug --ovpn.network="172.16.100.0/22" --master.sync-token="TOKEN" --easyrsa.path="/mnt/easyrsa" --easyrsa.index-path="/mnt/easyrsa/pki/index.txt" --ovpn.server="127.0.0.1:7777:tcp" command: /app/ovpn-admin
environment:
OVPN_DEBUG: "true"
OVPN_VERBOSE: "true"
OVPN_NETWORK: "192.168.100.0/24"
OVPN_CCD: "true"
OVPN_CCD_PATH: "/mnt/ccd"
EASYRSA_PATH: "/mnt/easyrsa"
OVPN_SERVER: "127.0.0.1:7777:tcp"
OVPN_INDEX_PATH: "/mnt/easyrsa/pki/index.txt"
OVPN_AUTH: "true"
OVPN_AUTH_DB_PATH: "/mnt/easyrsa/pki/users.db"
LOG_LEVEL: "debug"
network_mode: service:openvpn network_mode: service:openvpn
volumes: volumes:
- ./easyrsa_master:/mnt/easyrsa - ./easyrsa_master:/mnt/easyrsa

View File

@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
image="node:14.2-alpine3.11" image="node:16.13.0-alpine3.12"
uid="$(id -u $USER)" uid="$(id -u $USER)"
docker run -u $uid -w /app -v $(pwd):/app $image npm i && \ docker run -u $uid -w /app -v $(pwd):/app $image npm i && \

14731
frontend/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -7,17 +7,18 @@
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "cross-env NODE_ENV=development webpack-dev-server --hot", "dev": "cross-env NODE_ENV=development webpack-dev-server --hot",
"build": "cross-env NODE_ENV=production webpack --progress --hide-modules" "build": "cross-env NODE_ENV=production webpack --progress"
}, },
"dependencies": { "dependencies": {
"axios": "^0.19.2", "axios": "^1.12.0",
"bootstrap-vue": "^2.21.2", "bootstrap-vue": "^2.22.0",
"normalize.css": "^8.0.1", "normalize.css": "^8.0.1",
"vue": "^2.6.12", "vue": "^2.6.14",
"vue-clipboard2": "^0.2.1", "vue-clipboard2": "^0.3.3",
"vue-cookies": "^1.7.4", "vue-cookies": "^1.7.4",
"vue-good-table": "^2.21.1", "vue-good-table": "^2.21.11",
"vue-notification": "^1.3.20" "vue-notification": "^1.3.20",
"vue-style-loader": "^4.1.3"
}, },
"browserslist": [ "browserslist": [
"> 1%", "> 1%",
@ -25,23 +26,23 @@
"not ie <= 8" "not ie <= 8"
], ],
"devDependencies": { "devDependencies": {
"@babel/core": "^7.8.6", "@babel/core": "^7.16.5",
"@babel/plugin-proposal-class-properties": "^7.0.0", "@babel/plugin-proposal-class-properties": "^7.16.7",
"@babel/plugin-proposal-json-strings": "^7.0.0", "@babel/plugin-proposal-json-strings": "^7.16.7",
"@babel/plugin-syntax-dynamic-import": "^7.0.0", "@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/plugin-syntax-import-meta": "^7.0.0", "@babel/plugin-syntax-import-meta": "^7.10.4",
"@babel/preset-env": "^7.0.0", "@babel/preset-env": "^7.16.5",
"babel-loader": "^8.0.0", "babel-loader": "^8.2.3",
"cross-env": "^7.0.0", "cross-env": "^7.0.3",
"css-loader": "^3.4.2", "css-loader": "^6.5.1",
"file-loader": "^5.1.0", "file-loader": "^6.2.0",
"node-sass": "^4.13.1", "node-sass": "^9.0.0",
"sass-loader": "^8.0.2", "sass-loader": "^16.0.5",
"terser-webpack-plugin": "^2.3.5", "terser-webpack-plugin": "^5.3.0",
"vue-loader": "^15.9.0", "vue-loader": "^17.0.0",
"vue-template-compiler": "^2.6.11", "vue-template-compiler": "^2.6.14",
"webpack": "^4.42.0", "webpack": "^5.98.0",
"webpack-cli": "^3.3.11", "webpack-cli": "^4.9.1",
"webpack-dev-server": "^3.10.3" "webpack-dev-server": "^5.2.1"
} }
} }

View File

@ -57,8 +57,8 @@ new Vue({
filterable: true, filterable: true,
}, },
{ {
label: 'Connection Server', label: 'Active Connections',
field: 'ConnectionServer', field: 'Connections',
filterable: true, filterable: true,
}, },
{ {
@ -107,6 +107,38 @@ new Vue({
showForServerRole: ['master'], showForServerRole: ['master'],
showForModule: ["core"], showForModule: ["core"],
}, },
{
name: 'u-delete',
label: 'Delete',
class: 'btn-danger',
showWhenStatus: 'Revoked',
showForServerRole: ['master'],
showForModule: ["core"],
},
{
name: 'u-delete',
label: 'Delete',
class: 'btn-danger',
showWhenStatus: 'Expired',
showForServerRole: ['master'],
showForModule: ["core"],
},
{
name: 'u-rotate',
label: 'Rotate',
class: 'btn-warning',
showWhenStatus: 'Revoked',
showForServerRole: ['master'],
showForModule: ["core"],
},
{
name: 'u-rotate',
label: 'Rotate',
class: 'btn-warning',
showWhenStatus: 'Expired',
showForServerRole: ['master'],
showForModule: ["core"],
},
{ {
name: 'u-unrevoke', name: 'u-unrevoke',
label: 'Unrevoke', label: 'Unrevoke',
@ -161,10 +193,14 @@ new Vue({
newPassword: '', newPassword: '',
passwordChangeStatus: '', passwordChangeStatus: '',
passwordChangeMessage: '', passwordChangeMessage: '',
rotateUserMessage: '',
deleteUserMessage: '',
modalNewUserVisible: false, modalNewUserVisible: false,
modalShowConfigVisible: false, modalShowConfigVisible: false,
modalShowCcdVisible: false, modalShowCcdVisible: false,
modalChangePasswordVisible: false, modalChangePasswordVisible: false,
modalRotateUserVisible: false,
modalDeleteUserVisible: false,
openvpnConfig: '', openvpnConfig: '',
ccd: { ccd: {
Name: '', Name: '',
@ -204,6 +240,16 @@ new Vue({
_this.$notify({title: 'User ' + _this.username + ' unrevoked!', type: 'success'}) _this.$notify({title: 'User ' + _this.username + ' unrevoked!', type: 'success'})
}); });
}) })
_this.$root.$on('u-rotate', function () {
_this.u.modalRotateUserVisible = true;
var data = new URLSearchParams();
data.append('username', _this.username);
})
_this.$root.$on('u-delete', function () {
_this.u.modalDeleteUserVisible = true;
var data = new URLSearchParams();
data.append('username', _this.username);
})
_this.$root.$on('u-show-config', function () { _this.$root.$on('u-show-config', function () {
_this.u.modalShowConfigVisible = true; _this.u.modalShowConfigVisible = true;
var data = new URLSearchParams(); var data = new URLSearchParams();
@ -251,8 +297,8 @@ new Vue({
}) })
}, },
computed: { computed: {
customAddressDisabled: function () { customAddressDynamic: function () {
return this.serverRole == "master" ? this.u.ccd.ClientAddress == "dynamic" : true return this.u.ccd.ClientAddress == "dynamic"
}, },
ccdApplyStatusCssClass: function () { ccdApplyStatusCssClass: function () {
return this.u.ccdApplyStatus == 200 ? "alert-success" : "alert-danger" return this.u.ccdApplyStatus == 200 ? "alert-success" : "alert-danger"
@ -260,6 +306,12 @@ new Vue({
passwordChangeStatusCssClass: function () { passwordChangeStatusCssClass: function () {
return this.u.passwordChangeStatus == 200 ? "alert-success" : "alert-danger" return this.u.passwordChangeStatus == 200 ? "alert-success" : "alert-danger"
}, },
userRotateStatusCssClass: function () {
return this.u.roatateUserStatus == 200 ? "alert-success" : "alert-danger"
},
deleteUserStatusCssClass: function () {
return this.u.deleteUserStatus == 200 ? "alert-success" : "alert-danger"
},
modalNewUserDisplay: function () { modalNewUserDisplay: function () {
return this.u.modalNewUserVisible ? {display: 'flex'} : {} return this.u.modalNewUserVisible ? {display: 'flex'} : {}
}, },
@ -272,6 +324,12 @@ new Vue({
modalChangePasswordDisplay: function () { modalChangePasswordDisplay: function () {
return this.u.modalChangePasswordVisible ? {display: 'flex'} : {} return this.u.modalChangePasswordVisible ? {display: 'flex'} : {}
}, },
modalRotateUserDisplay: function () {
return this.u.modalRotateUserVisible ? {display: 'flex'} : {}
},
modalDeleteUserDisplay: function () {
return this.u.modalDeleteUserVisible ? {display: 'flex'} : {}
},
revokeFilterText: function() { revokeFilterText: function() {
return this.filters.hideRevoked ? "Show revoked" : "Hide revoked" return this.filters.hideRevoked ? "Show revoked" : "Hide revoked"
}, },
@ -288,7 +346,16 @@ new Vue({
}, },
methods: { methods: {
rowStyleClassFn: function(row) { rowStyleClassFn: function(row) {
return row.ConnectionStatus == 'Connected' ? 'connected-user' : '' if (row.ConnectionStatus == 'Connected') {
return 'connected-user'
}
if (row.AccountStatus == 'Revoked') {
return 'revoked-user'
}
if (row.AccountStatus == 'Expired') {
return 'expired-user'
}
return ''
}, },
rowActionFn: function(e) { rowActionFn: function(e) {
this.username = e.target.dataset.username; this.username = e.target.dataset.username;
@ -298,18 +365,10 @@ new Vue({
var _this = this; var _this = this;
axios.request(axios_cfg('api/users/list')) axios.request(axios_cfg('api/users/list'))
.then(function(response) { .then(function(response) {
_this.rows = response.data; _this.rows = Array.isArray(response.data) ? response.data : [];
}); });
}, },
staticAddrCheckboxOnChange: function() {
var staticAddrInput = document.getElementById('static-address');
var staticAddrEnable = document.getElementById('enable-static');
staticAddrInput.disabled = !staticAddrEnable.checked;
staticAddrInput.value == "dynamic" ? staticAddrInput.value = "" : staticAddrInput.value = "dynamic";
},
getServerSetting: function() { getServerSetting: function() {
var _this = this; var _this = this;
axios.request(axios_cfg('api/server/settings')) axios.request(axios_cfg('api/server/settings'))
@ -335,13 +394,15 @@ new Vue({
data.append('username', _this.u.newUserName); data.append('username', _this.u.newUserName);
data.append('password', _this.u.newUserPassword); data.append('password', _this.u.newUserPassword);
_this.username = _this.u.newUserName;
axios.request(axios_cfg('api/user/create', data, 'form')) axios.request(axios_cfg('api/user/create', data, 'form'))
.then(function(response) { .then(function(response) {
_this.$notify({title: 'New user ' + _this.username + ' created', type: 'success'})
_this.u.modalNewUserVisible = false; _this.u.modalNewUserVisible = false;
_this.u.newUserName = ''; _this.u.newUserName = '';
_this.u.newUserPassword = ''; _this.u.newUserPassword = '';
_this.getUserData(); _this.getUserData();
_this.$notify({title: 'New user ' + _this.username + ' created', type: 'success'})
}) })
.catch(function(error) { .catch(function(error) {
_this.u.newUserCreateError = error.response.data; _this.u.newUserCreateError = error.response.data;
@ -392,6 +453,52 @@ new Vue({
_this.$notify({title: 'Changing password for user ' + _this.username + ' failed!', type: 'error'}) _this.$notify({title: 'Changing password for user ' + _this.username + ' failed!', type: 'error'})
}); });
}, },
rotateUser: function(user) {
var _this = this;
_this.u.rotateUserMessage = "";
var data = new URLSearchParams();
data.append('username', user);
data.append('password', _this.u.newPassword);
axios.request(axios_cfg('api/user/rotate', data, 'form'))
.then(function(response) {
_this.u.roatateUserStatus = 200;
_this.u.newPassword = '';
_this.getUserData();
_this.u.modalRotateUserVisible = false;
_this.$notify({title: 'Certificates for user ' + _this.username + ' rotated!', type: 'success'})
})
.catch(function(error) {
_this.u.roatateUserStatus = error.response.status;
_this.u.rotateUserMessage = error.response.data.message;
_this.$notify({title: 'Rotate certificates for user ' + _this.username + ' failed!', type: 'error'})
})
},
deleteUser: function(user) {
var _this = this;
_this.u.deleteUserMessage = "";
var data = new URLSearchParams();
data.append('username', user);
axios.request(axios_cfg('api/user/delete', data, 'form'))
.then(function(response) {
_this.u.deleteUserStatus = 200;
_this.u.newPassword = '';
_this.getUserData();
_this.u.modalDeleteUserVisible = false;
_this.$notify({title: 'User ' + _this.username + ' deleted!', type: 'success'})
})
.catch(function(error) {
_this.u.deleteUserStatus = error.response.status;
_this.u.deleteUserMessage = error.response.data.message;
_this.$notify({title: 'Deleting user ' + _this.username + ' failed!', type: 'error'})
})
},
} }
}) })

View File

@ -48,6 +48,14 @@ body {
background-color: rgba(162, 245, 169, 0.5); background-color: rgba(162, 245, 169, 0.5);
} }
.revoked-user {
background-color: rgba(198, 186, 186, 0.5);
}
.expired-user {
background-color: rgba(255, 220, 127, 0.5);
}
.new-user-btn { .new-user-btn {
margin-right: 2rem; margin-right: 2rem;
} }

View File

@ -42,10 +42,6 @@
</template> </template>
</vue-good-table> </vue-good-table>
<!-- <div class="d-flex justify-content-md-end">-->
<!-- <button type="button" class="btn btn-sm btn-success el-square new-user-btn" v-on:click.stop="u.ctxVisible=false;u.modalNewUserVisible=true">Add user</button>-->
<!-- </div>-->
<div class="modal-wrapper" v-if="u.modalNewUserVisible" v-bind:style="modalNewUserDisplay"> <div class="modal-wrapper" v-if="u.modalNewUserVisible" v-bind:style="modalNewUserDisplay">
<div class="modal-dialog modal-lg"> <div class="modal-dialog modal-lg">
<div class="modal-content"> <div class="modal-content">
@ -123,13 +119,11 @@
<div class="modal-body"> <div class="modal-body">
<div class="input-group"> <div class="input-group">
<h5 class="static-address-label ">Static address:</h5> <h5 class="static-address-label ">Static address:</h5>
<div class="input-group-prepend"> <input id="static-address" type="text" class="form-control" v-model="u.ccd.ClientAddress" placeholder="127.0.0.1">
<div class="input-group-text"> <div class="input-group-append">
<input id="enable-static" type="checkbox" @change="staticAddrCheckboxOnChange()" v-if="serverRole == 'master'" v-bind:checked="!customAddressDisabled"> <button id="static-address-clear" class="btn btn-warning" type="button" v-on:click="u.ccd.ClientAddress = 'dynamic'" v-if="serverRole == 'master'" v-bind:disabled="customAddressDynamic">Clear</button>
</div> </div>
</div> </div>
<input id="static-address" type="text" class="form-control" v-model="u.ccd.ClientAddress" placeholder="127.0.0.1" v-bind:disabled="customAddressDisabled">
</div>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div class="d-flex "> <div class="d-flex ">
@ -191,6 +185,50 @@
</div> </div>
</div> </div>
<div class="modal-wrapper" v-if="u.modalRotateUserVisible" v-bind:style="modalRotateUserDisplay">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h4>Confirm rotating certificates for user: <strong>{{ username }}</strong></h4>
</div>
<div class="modal-body" v-if="modulesEnabled.includes('passwdAuth')">
<h4>Enter new password:</h4>
<input type="password" class="form-control el-square modal-el-margin" minlength="6" autocomplete="off" placeholder="Password [_a-zA-Z0-9\.-]" v-model="u.newPassword">
</div>
<div class="modal-footer justify-content-center" v-if="u.rotateUserMessage.length > 0">
<div class="alert" v-bind:class="userRotateStatusCssClass" role="alert" >
{{ u.rotateUserMessage }}
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger el-square modal-el-margin" v-on:click.stop="rotateUser(username)">Rotate</button>
<button type="button" class="btn btn-primary el-square d-flex justify-content-sm-end modal-el-margin" v-on:click.stop="u.newPassword='';u.rotateUserMessage='';u.modalRotateUserVisible=false">Close</button>
</div>
</div>
</div>
</div>
<div class="modal-wrapper" v-if="u.modalDeleteUserVisible" v-bind:style="modalDeleteUserDisplay">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h4>Confirm deleting user: <strong>{{ username }}</strong></h4>
</div>
<div class="modal-footer justify-content-center" v-if="u.deleteUserMessage.length > 0">
<div class="alert" v-bind:class="deleteUserStatusCssClass" role="alert" >
{{ u.deleteUserMessage }}
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger el-square modal-el-margin" v-on:click.stop="deleteUser(username)">Delete</button>
<button type="button" class="btn btn-primary el-square d-flex justify-content-sm-end modal-el-margin" v-on:click.stop="u.deleteUserMessage='';u.modalDeleteUserVisible=false">Close</button>
</div>
</div>
</div>
</div>
<notifications position="bottom left" :speed="900" /> <notifications position="bottom left" :speed="900" />
</div> </div>
<script src="dist/bundle.min.js"></script> <script src="dist/bundle.min.js"></script>

View File

@ -1,6 +1,5 @@
var path = require('path') const path = require('path');
var webpack = require('webpack') //const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const TerserPlugin = require('terser-webpack-plugin');
module.exports = { module.exports = {
mode: 'production', mode: 'production',
@ -17,6 +16,9 @@ module.exports = {
publicPath: '/dist/', publicPath: '/dist/',
filename: '[name].min.js' filename: '[name].min.js'
}, },
plugins: [
//new BundleAnalyzerPlugin(),
],
module: { module: {
rules: [ rules: [
{ {
@ -26,45 +28,23 @@ module.exports = {
'css-loader' 'css-loader'
], ],
}, },
{ {
test: /\.js$/, test: /\.js$/,
//exclude: /node_modules\/(?!bootstrap-vue\/src\/)/,
exclude: /node_modules/,
loader: 'babel-loader', loader: 'babel-loader',
exclude: /node_modules/ options: {
presets: ['@babel/preset-env']
} }
},
] ],
}, },
resolve: { resolve: {
alias: { alias: {
'vue$': 'vue/dist/vue.esm.js' 'vue$': 'vue/dist/vue.esm.js',
//'bootstrap-vue$': 'bootstrap-vue/src/index.js'
}, },
extensions: ['*', '.js', '.vue', '.json'] extensions: ['*', '.js', '.vue', '.json']
}, },
devServer: {
historyApiFallback: true,
noInfo: true,
overlay: true
},
performance: {
hints: false
},
}
if (process.env.NODE_ENV === 'production') {
module.exports.devtool = 'false'
// http://vue-loader.vuejs.org/en/workflow/production.html
module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
} }
}),
new TerserPlugin({
sourceMap: false
}),
new webpack.LoaderOptionsPlugin({
minimize: true
})
])
}

74
go.mod
View File

@ -1,20 +1,66 @@
module ovpn-admin module ovpn-admin
go 1.14 go 1.24.0
require ( require (
github.com/gobuffalo/packr/v2 v2.8.1 github.com/gobuffalo/packr/v2 v2.8.3
github.com/karrick/godirwalk v1.16.1 // indirect github.com/google/uuid v1.6.0
github.com/magefile/mage v1.11.0 // indirect github.com/prometheus/client_golang v1.23.2
github.com/prometheus/client_golang v1.8.0 github.com/sirupsen/logrus v1.9.4
github.com/prometheus/common v0.15.0 // indirect
github.com/rogpeppe/go-internal v1.7.0 // indirect
github.com/sirupsen/logrus v1.8.0 // indirect
github.com/spf13/cobra v1.1.3 // indirect
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sys v0.0.0-20210225080010-8e9945a5478f // indirect
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect
golang.org/x/tools v0.1.0 // indirect
gopkg.in/alecthomas/kingpin.v2 v2.2.6 gopkg.in/alecthomas/kingpin.v2 v2.2.6
k8s.io/api v0.34.3
k8s.io/apimachinery v0.34.3
k8s.io/client-go v0.34.3
)
require (
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/gobuffalo/logger v1.0.6 // indirect
github.com/gobuffalo/packd v1.0.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/gnostic-models v0.7.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/karrick/godirwalk v1.16.1 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/markbates/errx v1.1.0 // indirect
github.com/markbates/oncer v1.0.0 // indirect
github.com/markbates/safe v1.0.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.66.1 // indirect
github.com/prometheus/procfs v0.16.1 // indirect
github.com/x448/float16 v0.8.4 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/net v0.43.0 // indirect
golang.org/x/oauth2 v0.30.0 // indirect
golang.org/x/sys v0.35.0 // indirect
golang.org/x/term v0.34.0 // indirect
golang.org/x/text v0.28.0 // indirect
golang.org/x/time v0.9.0 // indirect
google.golang.org/protobuf v1.36.8 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
sigs.k8s.io/yaml v1.6.0 // indirect
) )

774
go.sum

File diff suppressed because it is too large Load Diff

View File

@ -1,37 +1,43 @@
package main package main
import ( import (
"archive/tar"
"compress/gzip"
"fmt" "fmt"
"io"
"io/ioutil" "io/ioutil"
"log"
"net/http" "net/http"
"os" "os"
"os/exec" "os/exec"
"path/filepath"
"strings"
"time" "time"
log "github.com/sirupsen/logrus"
) )
func parseDate(layout,datetime string) time.Time { func parseDate(layout, datetime string) time.Time {
t, err := time.Parse(layout, datetime) t, err := time.Parse(layout, datetime)
if err != nil { if err != nil {
log.Println(err) log.Errorln(err)
} }
return t return t
} }
func parseDateToString(layout,datetime,format string) string { func parseDateToString(layout, datetime, format string) string {
return parseDate(layout, datetime).Format(format) return parseDate(layout, datetime).Format(format)
} }
func parseDateToUnix(layout,datetime string) int64 { func parseDateToUnix(layout, datetime string) int64 {
return parseDate(layout, datetime).Unix() return parseDate(layout, datetime).Unix()
} }
func runBash(script string) string { func runBash(script string) string {
fmt.Println(script) log.Debugln(script)
cmd := exec.Command("bash", "-c", script) cmd := exec.Command("bash", "-c", script)
stdout, err := cmd.CombinedOutput() stdout, err := cmd.CombinedOutput()
if err != nil { if err != nil {
return (fmt.Sprint(err) + " : " + string(stdout)) return fmt.Sprint(err) + " : " + string(stdout)
} }
return string(stdout) return string(stdout)
} }
@ -42,7 +48,7 @@ func fExist(path string) bool {
if os.IsNotExist(err) { if os.IsNotExist(err) {
return false return false
} else if err != nil { } else if err != nil {
log.Fatal(err) log.Fatalf("fExist: %s", err)
return false return false
} }
@ -52,37 +58,102 @@ func fExist(path string) bool {
func fRead(path string) string { func fRead(path string) string {
content, err := ioutil.ReadFile(path) content, err := ioutil.ReadFile(path)
if err != nil { if err != nil {
log.Fatal(err) log.Warning(err)
return ""
} }
return string(content) return string(content)
} }
func fCreate(path string) bool { func fCreate(path string) error {
var _, err = os.Stat(path) var _, err = os.Stat(path)
if os.IsNotExist(err) { if os.IsNotExist(err) {
var file, err = os.Create(path) var file, err = os.Create(path)
if err != nil { if err != nil {
log.Println(err) log.Errorln(err)
return false return err
} }
defer file.Close() defer file.Close()
} }
return true return nil
} }
func fWrite(path, content string) { func fWrite(path, content string) error {
err := ioutil.WriteFile(path, []byte(content), 0644) err := ioutil.WriteFile(path, []byte(content), 0644)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
return nil
} }
func fDelete(path string) { func fDelete(path string) error {
err := os.Remove(path) err := os.Remove(path)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
return nil
}
func fCopy(src, dst string) error {
sfi, err := os.Stat(src)
if err != nil {
return err
}
if !sfi.Mode().IsRegular() {
// cannot copy non-regular files (e.g., directories, symlinks, devices, etc.)
return fmt.Errorf("fCopy: non-regular source file %s (%q)", sfi.Name(), sfi.Mode().String())
}
dfi, err := os.Stat(dst)
if err != nil {
if !os.IsNotExist(err) {
return err
}
} else {
if !(dfi.Mode().IsRegular()) {
return fmt.Errorf("fCopy: non-regular destination file %s (%q)", dfi.Name(), dfi.Mode().String())
}
if os.SameFile(sfi, dfi) {
return err
}
}
if err = os.Link(src, dst); err == nil {
return err
}
in, err := os.Open(src)
if err != nil {
return err
}
defer in.Close()
out, err := os.Create(dst)
if err != nil {
return err
}
defer func() {
cerr := out.Close()
if err == nil {
err = cerr
}
}()
if _, err = io.Copy(out, in); err != nil {
return err
}
err = out.Sync()
return err
}
func fMove(src, dst string) error {
err := fCopy(src, dst)
if err != nil {
log.Warn(err)
return err
}
err = fDelete(src)
if err != nil {
log.Warn(err)
return err
}
return nil
} }
func fDownload(path, url string, basicAuth bool) error { func fDownload(path, url string, basicAuth bool) error {
@ -98,7 +169,7 @@ func fDownload(path, url string, basicAuth bool) error {
} }
if resp.StatusCode != 200 { if resp.StatusCode != 200 {
log.Printf("WARNING: Download file operation for url %s finished with status code %d", url, resp.StatusCode ) log.Warnf("WARNING: Download file operation for url %s finished with status code %d\n", url, resp.StatusCode)
} }
defer resp.Body.Close() defer resp.Body.Close()
@ -112,3 +183,124 @@ func fDownload(path, url string, basicAuth bool) error {
return nil return nil
} }
func createArchiveFromDir(dir, path string) error {
var files []string
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
log.Warn(err)
return err
}
if !info.IsDir() {
files = append(files, path)
}
return nil
})
if err != nil {
log.Warn(err)
}
out, err := os.Create(path)
if err != nil {
log.Errorf("Error writing archive %s: %s", path, err)
return err
}
defer out.Close()
gw := gzip.NewWriter(out)
defer gw.Close()
tw := tar.NewWriter(gw)
defer tw.Close()
// Iterate over files and add them to the tar archive
for _, filePath := range files {
file, err := os.Open(filePath)
if err != nil {
log.Warnf("Error writing archive %s: %s", path, err)
return err
}
// Get FileInfo about our file providing file size, mode, etc.
info, err := file.Stat()
if err != nil {
file.Close()
return err
}
// Create a tar Header from the FileInfo data
header, err := tar.FileInfoHeader(info, info.Name())
if err != nil {
file.Close()
return err
}
header.Name = strings.Replace(filePath, dir+"/", "", 1)
// Write file header to the tar archive
err = tw.WriteHeader(header)
if err != nil {
file.Close()
return err
}
// Copy file content to tar archive
_, err = io.Copy(tw, file)
if err != nil {
file.Close()
return err
}
file.Close()
}
return nil
}
func extractFromArchive(archive, path string) error {
// Open the file which will be written into the archive
file, err := os.Open(archive)
if err != nil {
return err
}
defer file.Close()
// Write file header to the tar archive
uncompressedStream, err := gzip.NewReader(file)
if err != nil {
log.Fatal("extractFromArchive(): NewReader failed")
}
tarReader := tar.NewReader(uncompressedStream)
for true {
header, err := tarReader.Next()
if err == io.EOF {
break
}
if err != nil {
log.Fatalf("extractFromArchive: Next() failed: %s", err.Error())
}
switch header.Typeflag {
case tar.TypeDir:
if err := os.Mkdir(path+"/"+header.Name, 0755); err != nil {
log.Fatalf("extractFromArchive: Mkdir() failed: %s", err.Error())
}
case tar.TypeReg:
outFile, err := os.Create(path + "/" + header.Name)
if err != nil {
log.Fatalf("extractFromArchive: Create() failed: %s", err.Error())
}
if _, err := io.Copy(outFile, tarReader); err != nil {
log.Fatalf("extractFromArchive: Copy() failed: %s", err.Error())
}
outFile.Close()
default:
log.Fatalf(
"extractFromArchive: uknown type: %s in %s", header.Typeflag, header.Name)
}
}
return nil
}

12
install-deps-arm.sh Executable file
View File

@ -0,0 +1,12 @@
#!/usr/bin/env bash
apt-get update
apt-get install -y curl
apt-get install -y libc6 libc6-dev gcc-arm-linux-gnueabi gcc-aarch64-linux-gnu
curl -sL https://deb.nodesource.com/setup_16.x | bash -
apt-get install -y nodejs
PATH=$PATH:~/go/bin
go install github.com/gobuffalo/packr/v2/packr2@latest

12
install-deps.sh Executable file
View File

@ -0,0 +1,12 @@
#!/usr/bin/env bash
apt-get update
apt-get install -y curl
apt-get install -y libc6 libc6-dev libc6-dev-i386
curl -sL https://deb.nodesource.com/setup_16.x | bash -
apt-get install -y nodejs
PATH=$PATH:~/go/bin
go install github.com/gobuffalo/packr/v2/packr2@latest

794
kubernetes.go Normal file
View File

@ -0,0 +1,794 @@
package main
import (
"bytes"
"context"
"crypto/rsa"
"crypto/x509"
"errors"
"fmt"
"github.com/google/uuid"
"io/ioutil"
"os"
"os/exec"
"strings"
"time"
log "github.com/sirupsen/logrus"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
)
const (
secretCA = "openvpn-pki-ca"
secretServer = "openvpn-pki-server"
secretClientTmpl = "openvpn-pki-%d"
secretCRL = "openvpn-pki-crl"
secretIndexTxt = "openvpn-pki-index-txt"
secretDHandTA = "openvpn-pki-dh-and-ta"
certFileName = "tls.crt"
privKeyFileName = "tls.key"
)
// <year><month><day><hour><minute><second>Z
const indexTxtDateFormat = "060102150405Z"
var namespace = "default"
type OpenVPNPKI struct {
CAPrivKeyRSA *rsa.PrivateKey
CAPrivKeyPEM *bytes.Buffer
CACert *x509.Certificate
CACertPEM *bytes.Buffer
ServerPrivKeyRSA *rsa.PrivateKey
ServerPrivKeyPEM *bytes.Buffer
ServerCert *x509.Certificate
ServerCertPEM *bytes.Buffer
ClientCerts []ClientCert
RevokedCerts []RevokedCert
KubeClient *kubernetes.Clientset
}
type ClientCert struct {
PrivKeyRSA *rsa.PrivateKey
PrivKeyPEM *bytes.Buffer
Cert *x509.Certificate
CertPEM *bytes.Buffer
}
type RevokedCert struct {
RevokedTime time.Time `json:"revokedTime"`
CommonName string `json:"commonName"`
Cert *x509.Certificate `json:"cert"`
}
func (openVPNPKI *OpenVPNPKI) run() (err error) {
if _, err := os.Stat(kubeNamespaceFilePath); err == nil {
file, err := ioutil.ReadFile(kubeNamespaceFilePath)
if err != nil {
return err
}
namespace = string(file)
}
err = openVPNPKI.initKubeClient()
if err != nil {
return
}
err = openVPNPKI.initPKI()
if err != nil {
return
}
err = openVPNPKI.indexTxtUpdate()
if err != nil {
log.Error(err)
}
err = openVPNPKI.easyrsaGenCRL()
if err != nil {
log.Error(err)
}
if res, _ := openVPNPKI.secretCheckExists(secretDHandTA); !res {
err := openVPNPKI.secretGenTaKeyAndDHParam()
if err != nil {
log.Error(err)
}
}
err = openVPNPKI.updateFilesFromSecrets()
if err != nil {
log.Error(err)
}
err = openVPNPKI.updateCRLOnDisk()
if err != nil {
log.Error(err)
}
err = openVPNPKI.updateIndexTxtOnDisk()
if err != nil {
log.Error(err)
}
err = openVPNPKI.updateCcdOnDisk()
if err != nil {
log.Error(err)
}
return
}
func (openVPNPKI *OpenVPNPKI) initKubeClient() (err error) {
config, _ := rest.InClusterConfig()
openVPNPKI.KubeClient, err = kubernetes.NewForConfig(config)
return
}
func (openVPNPKI *OpenVPNPKI) initPKI() (err error) {
if res, _ := openVPNPKI.secretCheckExists(secretCA); res {
cert, err := openVPNPKI.secretGetClientCert(secretCA)
if err != nil {
return err
}
openVPNPKI.CAPrivKeyPEM = cert.PrivKeyPEM
openVPNPKI.CAPrivKeyRSA = cert.PrivKeyRSA
openVPNPKI.CACertPEM = cert.CertPEM
openVPNPKI.CACert = cert.Cert
} else {
openVPNPKI.CAPrivKeyPEM, err = genPrivKey()
if err != nil {
return
}
openVPNPKI.CAPrivKeyRSA, err = decodePrivKey(openVPNPKI.CAPrivKeyPEM.Bytes())
openVPNPKI.CACertPEM, _ = genCA(openVPNPKI.CAPrivKeyRSA)
openVPNPKI.CACert, err = decodeCert(openVPNPKI.CACertPEM.Bytes())
if err != nil {
return
}
secretMetaData := metav1.ObjectMeta{Name: secretCA}
secretData := map[string][]byte{
certFileName: openVPNPKI.CACertPEM.Bytes(),
privKeyFileName: openVPNPKI.CAPrivKeyPEM.Bytes(),
}
err = openVPNPKI.secretCreate(secretMetaData, secretData, v1.SecretTypeTLS)
if err != nil {
return
}
}
if res, _ := openVPNPKI.secretCheckExists(secretServer); res {
cert, err := openVPNPKI.secretGetClientCert(secretServer)
if err != nil {
return err
}
openVPNPKI.ServerPrivKeyPEM = cert.PrivKeyPEM
openVPNPKI.ServerPrivKeyRSA = cert.PrivKeyRSA
openVPNPKI.ServerCertPEM = cert.CertPEM
openVPNPKI.ServerCert = cert.Cert
} else {
openVPNPKI.ServerPrivKeyPEM, err = genPrivKey()
if err != nil {
return
}
openVPNPKI.ServerPrivKeyRSA, err = decodePrivKey(openVPNPKI.ServerPrivKeyPEM.Bytes())
if err != nil {
return
}
openVPNPKI.ServerCertPEM, _ = genServerCert(openVPNPKI.ServerPrivKeyRSA, openVPNPKI.CAPrivKeyRSA, openVPNPKI.CACert, "server")
openVPNPKI.ServerCert, err = decodeCert(openVPNPKI.ServerCertPEM.Bytes())
secretMetaData := metav1.ObjectMeta{
Name: secretServer,
Labels: map[string]string{
"index.txt": "",
"name": "server",
"type": "serverAuth",
},
}
secretData := map[string][]byte{
certFileName: openVPNPKI.ServerCertPEM.Bytes(),
privKeyFileName: openVPNPKI.ServerPrivKeyPEM.Bytes(),
}
err = openVPNPKI.secretCreate(secretMetaData, secretData, v1.SecretTypeTLS)
if err != nil {
return
}
}
return
}
func (openVPNPKI *OpenVPNPKI) indexTxtUpdate() (err error) {
secrets, err := openVPNPKI.secretsGetByLabels("index.txt=")
if err != nil {
return
}
var indexTxt string
for _, secret := range secrets.Items {
certPEM := bytes.NewBuffer(secret.Data[certFileName])
log.Trace("indexTxtUpdate:" + secret.Name)
cert, err := decodeCert(certPEM.Bytes())
if err != nil {
return nil
}
log.Trace(cert.Subject.CommonName)
if secret.Annotations["revokedAt"] == "" {
indexTxt += fmt.Sprintf("%s\t%s\t\t%s\t%s\t%s\n", "V", cert.NotAfter.Format(indexTxtDateFormat), fmt.Sprintf("%d", cert.SerialNumber), "unknown", "/CN="+secret.Labels["name"])
} else if cert.NotAfter.Before(time.Now()) {
indexTxt += fmt.Sprintf("%s\t%s\t\t%s\t%s\t%s\n", "E", cert.NotAfter.Format(indexTxtDateFormat), fmt.Sprintf("%d", cert.SerialNumber), "unknown", "/CN="+secret.Labels["name"])
} else {
indexTxt += fmt.Sprintf("%s\t%s\t%s\t%s\t%s\t%s\n", "R", cert.NotAfter.Format(indexTxtDateFormat), secret.Annotations["revokedAt"], fmt.Sprintf("%d", cert.SerialNumber), "unknown", "/CN="+secret.Labels["name"])
}
}
secretMetaData := metav1.ObjectMeta{Name: secretIndexTxt}
secretData := map[string][]byte{"index.txt": []byte(indexTxt)}
if res, _ := openVPNPKI.secretCheckExists(secretIndexTxt); !res {
err = openVPNPKI.secretCreate(secretMetaData, secretData, v1.SecretTypeOpaque)
} else {
err = openVPNPKI.secretUpdate(secretMetaData, secretData, v1.SecretTypeOpaque)
}
return
}
func (openVPNPKI *OpenVPNPKI) updateIndexTxtOnDisk() (err error) {
secret, err := openVPNPKI.secretGetByName(secretIndexTxt)
indexTxt := secret.Data["index.txt"]
err = ioutil.WriteFile(fmt.Sprintf("%s/pki/index.txt", *easyrsaDirPath), indexTxt, 0600)
return
}
func (openVPNPKI *OpenVPNPKI) easyrsaGenCRL() (err error) {
err = openVPNPKI.indexTxtUpdate()
if err != nil {
return
}
secrets, err := openVPNPKI.secretsGetByLabels("index.txt=,type=clientAuth")
if err != nil {
return
}
var revoked []*RevokedCert
for _, secret := range secrets.Items {
if secret.Annotations["revokedAt"] != "" {
revokedAt, err := time.Parse(indexTxtDateFormat, secret.Annotations["revokedAt"])
if err != nil {
log.Warning(err)
}
cert, err := decodeCert(secret.Data[certFileName])
revoked = append(revoked, &RevokedCert{RevokedTime: revokedAt, Cert: cert})
}
}
crl, err := genCRL(revoked, openVPNPKI.CACert, openVPNPKI.CAPrivKeyRSA)
if err != nil {
return
}
secretMetaData := metav1.ObjectMeta{Name: secretCRL}
secretData := map[string][]byte{
"crl.pem": crl.Bytes(),
}
//err = openVPNPKI.secretCreate(secretMetaData, secretData)
if res, _ := openVPNPKI.secretCheckExists(secretCRL); !res {
err = openVPNPKI.secretCreate(secretMetaData, secretData, v1.SecretTypeOpaque)
} else {
err = openVPNPKI.secretUpdate(secretMetaData, secretData, v1.SecretTypeOpaque)
}
return
}
func (openVPNPKI *OpenVPNPKI) easyrsaBuildClient(commonName string) (err error) {
// check certificate exists
_, err = openVPNPKI.secretGetByLabels("name=" + commonName)
if err == nil {
return errors.New(fmt.Sprintf("certificate for user (%s) already exists", commonName))
}
clientPrivKeyPEM, err := genPrivKey()
if err != nil {
return
}
clientPrivKeyRSA, err := decodePrivKey(clientPrivKeyPEM.Bytes())
if err != nil {
return
}
clientCertPEM, _ := genClientCert(clientPrivKeyRSA, openVPNPKI.CAPrivKeyRSA, openVPNPKI.CACert, commonName)
clientCert, err := decodeCert(clientCertPEM.Bytes())
secretMetaData := metav1.ObjectMeta{
Name: fmt.Sprintf(secretClientTmpl, clientCert.SerialNumber),
Labels: map[string]string{
labelKeyIndexTxt: "",
labelKeyType: labelValueClientAuth,
labelKeyName: commonName,
labelKeyManagedBy: labelValueManagedByApp,
},
Annotations: map[string]string{
"commonName": commonName,
"notBefore": clientCert.NotBefore.Format(indexTxtDateFormat),
"notAfter": clientCert.NotAfter.Format(indexTxtDateFormat),
"revokedAt": "",
"serialNumber": fmt.Sprintf("%d", clientCert.SerialNumber),
},
}
secretData := map[string][]byte{
certFileName: clientCertPEM.Bytes(),
privKeyFileName: clientPrivKeyPEM.Bytes(),
}
err = openVPNPKI.secretCreate(secretMetaData, secretData, v1.SecretTypeTLS)
if err != nil {
return
}
err = openVPNPKI.indexTxtUpdate()
if err != nil {
return
}
err = openVPNPKI.updateIndexTxtOnDisk()
return
}
func (openVPNPKI *OpenVPNPKI) easyrsaGetCACert() string {
return openVPNPKI.CACertPEM.String()
}
func (openVPNPKI *OpenVPNPKI) easyrsaGetClientCert(commonName string) (cert, key string) {
secret, err := openVPNPKI.secretGetByLabels("name=" + commonName)
if err != nil {
log.Error(err)
}
cert = string(secret.Data[certFileName])
key = string(secret.Data[privKeyFileName])
return
}
func (openVPNPKI *OpenVPNPKI) easyrsaRevoke(commonName string) (err error) {
secret, err := openVPNPKI.secretGetByLabels("name=" + commonName)
if err != nil {
log.Error(err)
}
if secret.Annotations["revokedAt"] != "" {
log.Warnf("user (%s) already revoked", commonName)
return
}
secret.Annotations["revokedAt"] = time.Now().Format(indexTxtDateFormat)
_, err = openVPNPKI.KubeClient.CoreV1().Secrets(namespace).Update(context.TODO(), secret, metav1.UpdateOptions{})
if err != nil {
return
}
err = openVPNPKI.indexTxtUpdate()
if err != nil {
return
}
err = openVPNPKI.updateIndexTxtOnDisk()
if err != nil {
return
}
err = openVPNPKI.easyrsaGenCRL()
if err != nil {
log.Error(err)
}
err = openVPNPKI.updateCRLOnDisk()
return
}
func (openVPNPKI *OpenVPNPKI) easyrsaUnrevoke(commonName string) (err error) {
secret, err := openVPNPKI.secretGetByLabels("name=" + commonName)
if err != nil {
log.Error(err)
}
secret.Annotations["revokedAt"] = ""
_, err = openVPNPKI.KubeClient.CoreV1().Secrets(namespace).Update(context.TODO(), secret, metav1.UpdateOptions{})
if err != nil {
return
}
err = openVPNPKI.indexTxtUpdate()
if err != nil {
return
}
err = openVPNPKI.updateIndexTxtOnDisk()
if err != nil {
return
}
err = openVPNPKI.easyrsaGenCRL()
if err != nil {
log.Error(err)
}
err = openVPNPKI.updateCRLOnDisk()
return
}
func (openVPNPKI *OpenVPNPKI) easyrsaRotate(commonName, newPassword string) (err error) {
secret, err := openVPNPKI.secretGetByLabels("name=" + commonName)
if err != nil {
log.Error(err)
}
uniqHash := strings.Replace(uuid.New().String(), "-", "", -1)
secret.Annotations["commonName"] = "REVOKED-" + commonName + "-" + uniqHash
secret.Labels["name"] = "REVOKED" + commonName
secret.Labels["revokedForever"] = "true"
_, err = openVPNPKI.KubeClient.CoreV1().Secrets(namespace).Update(context.TODO(), secret, metav1.UpdateOptions{})
if err != nil {
return
}
err = openVPNPKI.easyrsaBuildClient(commonName)
if err != nil {
return
}
err = openVPNPKI.transferRoutes(secret, commonName)
if err != nil {
return
}
err = openVPNPKI.indexTxtUpdate()
if err != nil {
return
}
err = openVPNPKI.updateIndexTxtOnDisk()
if err != nil {
return
}
err = openVPNPKI.easyrsaGenCRL()
if err != nil {
log.Error(err)
}
err = openVPNPKI.updateCRLOnDisk()
return
}
func (openVPNPKI *OpenVPNPKI) easyrsaDelete(commonName string) (err error) {
secret, err := openVPNPKI.secretGetByLabels("name=" + commonName)
if err != nil {
log.Error(err)
}
uniqHash := strings.Replace(uuid.New().String(), "-", "", -1)
secret.Annotations["commonName"] = "REVOKED-" + commonName + "-" + uniqHash
secret.Labels["name"] = "REVOKED-" + commonName + "-" + uniqHash
secret.Labels["revokedForever"] = "true"
_, err = openVPNPKI.KubeClient.CoreV1().Secrets(namespace).Update(context.TODO(), secret, metav1.UpdateOptions{})
if err != nil {
return
}
err = openVPNPKI.indexTxtUpdate()
if err != nil {
return
}
err = openVPNPKI.updateIndexTxtOnDisk()
if err != nil {
return
}
err = openVPNPKI.easyrsaGenCRL()
if err != nil {
log.Error(err)
}
err = openVPNPKI.updateCRLOnDisk()
return
}
func (openVPNPKI *OpenVPNPKI) secretGetClientCert(name string) (cert ClientCert, err error) {
secret, err := openVPNPKI.secretGetByName(name)
if err != nil {
return
}
cert.CertPEM = bytes.NewBuffer(secret.Data[certFileName])
cert.Cert, err = decodeCert(cert.CertPEM.Bytes())
if err != nil {
return
}
cert.PrivKeyPEM = bytes.NewBuffer(secret.Data[privKeyFileName])
cert.PrivKeyRSA, err = decodePrivKey(cert.PrivKeyPEM.Bytes())
if err != nil {
return
}
return
}
func (openVPNPKI *OpenVPNPKI) updateFilesFromSecrets() (err error) {
ca, err := openVPNPKI.secretGetClientCert(secretCA)
if err != nil {
return
}
server, err := openVPNPKI.secretGetClientCert(secretServer)
if err != nil {
return
}
secret, err := openVPNPKI.secretGetByName(secretDHandTA)
takey := secret.Data["ta.key"]
dhparam := secret.Data["dh.pem"]
if _, err := os.Stat(fmt.Sprintf("%s/pki/issued", *easyrsaDirPath)); os.IsNotExist(err) {
err = os.MkdirAll(fmt.Sprintf("%s/pki/issued", *easyrsaDirPath), 0755)
}
if _, err := os.Stat(fmt.Sprintf("%s/pki/private", *easyrsaDirPath)); os.IsNotExist(err) {
err = os.MkdirAll(fmt.Sprintf("%s/pki/private", *easyrsaDirPath), 0755)
}
err = ioutil.WriteFile(fmt.Sprintf("%s/pki/ca.crt", *easyrsaDirPath), ca.CertPEM.Bytes(), 0600)
if err != nil {
return
}
err = ioutil.WriteFile(fmt.Sprintf("%s/pki/issued/server.crt", *easyrsaDirPath), server.CertPEM.Bytes(), 0600)
if err != nil {
return
}
err = ioutil.WriteFile(fmt.Sprintf("%s/pki/private/server.key", *easyrsaDirPath), server.PrivKeyPEM.Bytes(), 0600)
if err != nil {
return
}
err = ioutil.WriteFile(fmt.Sprintf("%s/pki/ta.key", *easyrsaDirPath), takey, 0600)
if err != nil {
return
}
err = ioutil.WriteFile(fmt.Sprintf("%s/pki/dh.pem", *easyrsaDirPath), dhparam, 0600)
if err != nil {
return
}
err = openVPNPKI.updateCRLOnDisk()
return
}
func (openVPNPKI *OpenVPNPKI) updateCRLOnDisk() (err error) {
secret, err := openVPNPKI.secretGetByName(secretCRL)
crl := secret.Data["crl.pem"]
err = ioutil.WriteFile(fmt.Sprintf("%s/pki/crl.pem", *easyrsaDirPath), crl, 0644)
if err != nil {
log.Errorf("error write crl.pem:%s", err.Error())
}
return
}
func (openVPNPKI *OpenVPNPKI) secretGenTaKeyAndDHParam() (err error) {
taKeyPath := "/tmp/ta.key"
cmd := exec.Command("bash", "-c", fmt.Sprintf("/usr/sbin/openvpn --genkey --secret %s", taKeyPath))
stdout, err := cmd.CombinedOutput()
log.Info(fmt.Sprintf("/usr/sbin/openvpn --genkey --secret %s: %s", taKeyPath, string(stdout)))
if err != nil {
return
}
taKey, err := ioutil.ReadFile(taKeyPath)
dhparamPath := "/tmp/dh.pem"
cmd = exec.Command("bash", "-c", fmt.Sprintf("openssl dhparam -out %s 2048", dhparamPath))
_, err = cmd.CombinedOutput()
if err != nil {
return
}
dhparam, err := ioutil.ReadFile(dhparamPath)
secretMetaData := metav1.ObjectMeta{Name: secretDHandTA}
secretData := map[string][]byte{
"ta.key": taKey,
"dh.pem": dhparam,
}
err = openVPNPKI.secretCreate(secretMetaData, secretData, v1.SecretTypeOpaque)
if err != nil {
return
}
return
}
// ccd
func (openVPNPKI *OpenVPNPKI) secretGetCcd(commonName string) (ccd string) {
secret, err := openVPNPKI.secretGetByLabels("name=" + commonName)
if err != nil {
log.Error(err)
return
}
for k, _ := range secret.Data {
if k == "ccd" {
ccd = string(secret.Data["ccd"])
return
}
}
return
}
func (openVPNPKI *OpenVPNPKI) secretUpdateCcd(commonName string, ccd []byte) {
secret, err := openVPNPKI.secretGetByLabels("name=" + commonName)
if err != nil {
log.Error(err)
return
}
secret.Data["ccd"] = ccd
err = openVPNPKI.secretUpdate(secret.ObjectMeta, secret.Data, v1.SecretTypeTLS)
if err != nil {
log.Errorf("secret (%s) update error: %s", secret.Name, err.Error())
}
err = openVPNPKI.updateCcdOnDisk()
if err != nil {
log.Error(err)
}
}
func (openVPNPKI *OpenVPNPKI) updateCcdOnDisk() (err error) {
secrets, err := openVPNPKI.secretsGetByLabels("index.txt=,type=clientAuth")
if err != nil {
return
}
if _, err := os.Stat(*ccdDir); os.IsNotExist(err) {
err = os.MkdirAll(*ccdDir, 0755)
}
for _, secret := range secrets.Items {
ccd := secret.Data["ccd"]
if len(ccd) > 0 {
err = ioutil.WriteFile(fmt.Sprintf("%s/%s", *ccdDir, secret.Labels["name"]), ccd, 0644)
if err != nil {
log.Error(err)
}
}
}
return
}
//
func (openVPNPKI *OpenVPNPKI) secretCreate(objectMeta metav1.ObjectMeta, data map[string][]byte, secretType v1.SecretType) (err error) {
if objectMeta.Name == "nil" {
err = errors.New("secret name not defined")
return
}
secret := &v1.Secret{
TypeMeta: metav1.TypeMeta{},
ObjectMeta: objectMeta,
Data: data,
Type: secretType,
}
_, err = openVPNPKI.KubeClient.CoreV1().Secrets(namespace).Create(context.TODO(), secret, metav1.CreateOptions{})
return
}
func (openVPNPKI *OpenVPNPKI) secretUpdate(objectMeta metav1.ObjectMeta, data map[string][]byte, secretType v1.SecretType) (err error) {
secret := &v1.Secret{
TypeMeta: metav1.TypeMeta{},
ObjectMeta: objectMeta,
Data: data,
Type: secretType,
}
_, err = openVPNPKI.KubeClient.CoreV1().Secrets(namespace).Update(context.TODO(), secret, metav1.UpdateOptions{})
return
}
func (openVPNPKI *OpenVPNPKI) secretGetByName(name string) (secret *v1.Secret, err error) {
secret, err = openVPNPKI.KubeClient.CoreV1().Secrets(namespace).Get(context.TODO(), name, metav1.GetOptions{})
return
}
func (openVPNPKI *OpenVPNPKI) secretsGetByLabels(labels string) (secrets *v1.SecretList, err error) {
secrets, err = openVPNPKI.KubeClient.CoreV1().Secrets(namespace).List(context.TODO(), metav1.ListOptions{LabelSelector: labels})
if err != nil {
return
}
if len(secrets.Items) == 0 {
log.Debugf("secrets with labels %s not found", labels)
}
return
}
func (openVPNPKI *OpenVPNPKI) secretGetByLabels(labels string) (secret *v1.Secret, err error) {
secrets, err := openVPNPKI.secretsGetByLabels(labels)
if err != nil {
return
}
if len(secrets.Items) > 1 {
err = errors.New(fmt.Sprintf("found more than one secret with labels %s", labels))
return
}
if len(secrets.Items) == 0 {
err = errors.New(fmt.Sprintf("secret not found"))
return
}
secret = &secrets.Items[0]
return
}
func (openVPNPKI *OpenVPNPKI) secretCheckExists(name string) (bool, string) {
secret, err := openVPNPKI.KubeClient.CoreV1().Secrets(namespace).Get(context.TODO(), name, metav1.GetOptions{})
if err != nil {
log.Debug(err)
return false, ""
}
return true, secret.ResourceVersion
}
// transferRoutes transfers configured routes from revoked certs to a new one
func (openVPNPKI *OpenVPNPKI) transferRoutes(revokedSecret *v1.Secret, newNameCert string) error {
ccd, ok := revokedSecret.Data["ccd"]
if !ok || len(ccd) == 0 {
log.Infof("No CCD data found in secret %s", revokedSecret.Name)
return nil
}
openVPNPKI.secretUpdateCcd(newNameCert, ccd)
return nil
}

1130
main.go

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,7 @@ env
auth_usr=$(head -1 $1) auth_usr=$(head -1 $1)
auth_passwd=$(tail -1 $1) auth_passwd=$(tail -1 $1)
if [ $common_name = $username ]; then if [ $common_name = $auth_usr ]; then
openvpn-user auth --db.path /etc/openvpn/easyrsa/pki/users.db --user ${auth_usr} --password ${auth_passwd} openvpn-user auth --db.path /etc/openvpn/easyrsa/pki/users.db --user ${auth_usr} --password ${auth_passwd}
else else
echo "Authorization failed" echo "Authorization failed"

View File

@ -3,7 +3,13 @@ set -ex
EASY_RSA_LOC="/etc/openvpn/easyrsa" EASY_RSA_LOC="/etc/openvpn/easyrsa"
SERVER_CERT="${EASY_RSA_LOC}/pki/issued/server.crt" SERVER_CERT="${EASY_RSA_LOC}/pki/issued/server.crt"
OVPN_SRV_NET=${OVPN_SERVER_NET:-172.16.100.0}
OVPN_SRV_MASK=${OVPN_SERVER_MASK:-255.255.255.0}
cd $EASY_RSA_LOC cd $EASY_RSA_LOC
if [ -e "$SERVER_CERT" ]; then if [ -e "$SERVER_CERT" ]; then
echo "Found existing certs - reusing" echo "Found existing certs - reusing"
else else
@ -15,17 +21,18 @@ else
done done
else else
echo "Generating new certs" echo "Generating new certs"
easyrsa init-pki easyrsa --batch init-pki
cp -R /usr/share/easy-rsa/* $EASY_RSA_LOC/pki cp -R /usr/share/easy-rsa/* $EASY_RSA_LOC/pki
echo "ca" | easyrsa build-ca nopass echo "ca" | easyrsa build-ca nopass
easyrsa build-server-full server nopass easyrsa --batch build-server-full server nopass
easyrsa gen-dh easyrsa gen-dh
openvpn --genkey --secret ./pki/ta.key openvpn --genkey --secret ./pki/ta.key
fi fi
fi fi
easyrsa gen-crl easyrsa gen-crl
iptables -t nat -A POSTROUTING -s 172.16.100.0/255.255.255.0 ! -d 172.16.100.0/255.255.255.0 -j MASQUERADE iptables -t nat -D POSTROUTING -s ${OVPN_SRV_NET}/${OVPN_SRV_MASK} ! -d ${OVPN_SRV_NET}/${OVPN_SRV_MASK} -j MASQUERADE || true
iptables -t nat -A POSTROUTING -s ${OVPN_SRV_NET}/${OVPN_SRV_MASK} ! -d ${OVPN_SRV_NET}/${OVPN_SRV_MASK} -j MASQUERADE
mkdir -p /dev/net mkdir -p /dev/net
if [ ! -c /dev/net/tun ]; then if [ ! -c /dev/net/tun ]; then
@ -41,7 +48,7 @@ if [ ${OVPN_PASSWD_AUTH} = "true" ]; then
echo "auth-user-pass-verify /etc/openvpn/scripts/auth.sh via-file" | tee -a /etc/openvpn/openvpn.conf echo "auth-user-pass-verify /etc/openvpn/scripts/auth.sh via-file" | tee -a /etc/openvpn/openvpn.conf
echo "script-security 2" | tee -a /etc/openvpn/openvpn.conf echo "script-security 2" | tee -a /etc/openvpn/openvpn.conf
echo "verify-client-cert require" | tee -a /etc/openvpn/openvpn.conf echo "verify-client-cert require" | tee -a /etc/openvpn/openvpn.conf
openvpn-user db-init --db.path=$EASY_RSA_LOC/pki/users.db openvpn-user db-init --db.path=$EASY_RSA_LOC/pki/users.db && openvpn-user db-migrate --db.path=$EASY_RSA_LOC/pki/users.db
fi fi
[ -d $EASY_RSA_LOC/pki ] && chmod 755 $EASY_RSA_LOC/pki [ -d $EASY_RSA_LOC/pki ] && chmod 755 $EASY_RSA_LOC/pki
@ -49,5 +56,4 @@ fi
mkdir -p /etc/openvpn/ccd mkdir -p /etc/openvpn/ccd
openvpn --config /etc/openvpn/openvpn.conf --client-config-dir /etc/openvpn/ccd --port 1194 --proto tcp --management 127.0.0.1 8989 --dev tun0 openvpn --config /etc/openvpn/openvpn.conf --client-config-dir /etc/openvpn/ccd --port 1194 --proto tcp --management 127.0.0.1 8989 --dev tun0 --server ${OVPN_SRV_NET} ${OVPN_SRV_MASK}

View File

@ -1,4 +1,4 @@
server 172.16.100.0 255.255.255.0 # server 172.16.100.0 255.255.255.0
verb 3 verb 3
tls-server tls-server
ca /etc/openvpn/easyrsa/pki/ca.crt ca /etc/openvpn/easyrsa/pki/ca.crt
@ -14,6 +14,7 @@ keepalive 10 60
persist-key persist-key
persist-tun persist-tun
topology subnet topology subnet
#duplicate-cn
#proto tcp #proto tcp
#port 1194 #port 1194
#dev tun0 #dev tun0

View File

@ -1,3 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
docker-compose -p openvpn-master up -d --build # About 'docker compose' and 'docker-compose'
# We are using Docker Compose in plugin mode with Docker. For more details, see: https://docs.docker.com/compose/install/linux/. If you need to use the standalone Docker Compose, you can modify the command `docker compose` to `docker-compose` accordingly.
docker compose -p openvpn-master up -d --build

View File

@ -1,5 +1,5 @@
{{- if (ne .ClientAddress "dynamic") }} {{- if (ne .ClientAddress "dynamic") }}
ifconfig-push {{ .ClientAddress }} 255.255.255.255 ifconfig-push {{ .ClientAddress }} 255.255.255.0
{{- end }} {{- end }}
{{- range $route := .CustomRoutes }} {{- range $route := .CustomRoutes }}
push "route {{ $route.Address }} {{ $route.Mask }}" # {{ $route.Description }} push "route {{ $route.Address }} {{ $route.Mask }}" # {{ $route.Description }}

View File

@ -13,7 +13,7 @@ tls-client
remote-cert-tls server remote-cert-tls server
# uncomment below lines for use with linux # uncomment below lines for use with linux
#script-security 2 #script-security 2
# if use use resolved # if you use resolved
#up /etc/openvpn/update-resolv-conf #up /etc/openvpn/update-resolv-conf
#down /etc/openvpn/update-resolv-conf #down /etc/openvpn/update-resolv-conf
# if you use systemd-resolved first install openvpn-systemd-resolved package # if you use systemd-resolved first install openvpn-systemd-resolved package

View File

@ -1,10 +1,19 @@
project: ovpn-admin project: ovpn-admin
configVersion: 1 configVersion: 1
build:
platform:
- linux/amd64
{{- if eq .Env "release" }}
- linux/arm64
- linux/arm/v7
- linux/arm/v8
{{- end }}
staged: true
--- ---
image: ovpn-admin image: ovpn-admin
dockerfile: Dockerfile dockerfile: Dockerfile.ovpn-admin
context: .
--- ---
image: openvpn image: openvpn
dockerfile: Dockerfile.openvpn dockerfile: Dockerfile.openvpn
context: .