diff --git a/.github/workflows/publish-latest.yaml b/.github/workflows/publish-latest.yaml index 2976c31..ad1f8e4 100644 --- a/.github/workflows/publish-latest.yaml +++ b/.github/workflows/publish-latest.yaml @@ -12,11 +12,24 @@ jobs: uses: actions/checkout@v2 with: fetch-depth: 0 - - name: Push ovpn-admin image to Docker Hub - uses: docker/build-push-action@v1 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + - name: Login to Docker Hub + uses: docker/login-action@v2 with: username: ${{ secrets.DOCKER_USER }} password: ${{ secrets.DOCKER_PASS }} - repository: flant/ovpn-admin - tags: latest - dockerfile: Dockerfile + - name: Push openvpn image to Docker Hub + uses: docker/build-push-action@v4 + with: + tags: flant/ovpn-admin:openvpn-latest + platforms: linux/amd64,linux/arm64,linux/arm + file: Dockerfile.openvpn + push: true + - name: Push ovpn-admin image to Docker Hub + uses: docker/build-push-action@v4 + with: + tags: flant/ovpn-admin:latest + platforms: linux/amd64,linux/arm64,linux/arm + file: Dockerfile + push: true \ No newline at end of file diff --git a/.github/workflows/publish-tag.yaml b/.github/workflows/publish-tag.yaml index 17b7ea0..06bdaef 100644 --- a/.github/workflows/publish-tag.yaml +++ b/.github/workflows/publish-tag.yaml @@ -16,11 +16,24 @@ jobs: - name: Get the version id: get_version run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//} - - name: Push ovpn-admin image to Docker Hub - uses: docker/build-push-action@v1 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + - name: Login to Docker Hub + uses: docker/login-action@v2 with: username: ${{ secrets.DOCKER_USER }} password: ${{ secrets.DOCKER_PASS }} - repository: flant/ovpn-admin - tags: ${{ steps.get_version.outputs.VERSION }} - dockerfile: Dockerfile + - name: Push openvpn image to Docker Hub + uses: docker/build-push-action@v4 + with: + tags: flant/ovpn-admin:openvpn-${{ steps.get_version.outputs.VERSION }} + platforms: linux/amd64,linux/arm64,linux/arm + file: Dockerfile.openvpn + push: true + - name: Push ovpn-admin image to Docker Hub + uses: docker/build-push-action@v4 + with: + tags: flant/ovpn-admin:${{ steps.get_version.outputs.VERSION }} + platforms: linux/amd64,linux/arm64,linux/arm + file: Dockerfile + push: true \ No newline at end of file diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index d038490..482ba80 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -17,7 +17,7 @@ jobs: - name: checkout code uses: actions/checkout@v2 - name: build binaries - uses: wangyoucao577/go-release-action@v1.28 + uses: wangyoucao577/go-release-action@v1.40 with: github_token: ${{ secrets.GITHUB_TOKEN }} goversion: 1.17 diff --git a/.github/workflows/release_arm.yaml b/.github/workflows/release_arm.yaml index 3d52c12..d322571 100644 --- a/.github/workflows/release_arm.yaml +++ b/.github/workflows/release_arm.yaml @@ -17,7 +17,7 @@ jobs: - name: checkout code uses: actions/checkout@v2 - name: build binaries - uses: wangyoucao577/go-release-action@v1.28 + uses: wangyoucao577/go-release-action@v1.40 with: github_token: ${{ secrets.GITHUB_TOKEN }} goversion: 1.17 diff --git a/Dockerfile b/Dockerfile index 69ad22e..29d2cd6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,18 +1,21 @@ FROM node:16-alpine3.15 AS frontend-builder COPY frontend/ /app -RUN cd /app && npm install && npm run build +RUN apk add --update python3 make g++ && cd /app && npm install && npm run build FROM golang:1.17.3-buster AS backend-builder COPY --from=frontend-builder /app/static /app/frontend/static COPY . /app -RUN cd /app && env CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -a -tags netgo -ldflags '-linkmode external -extldflags -static -s -w' -o ovpn-admin +ARG TARGETARCH +RUN cd /app && env CGO_ENABLED=1 GOOS=linux GOARCH=${TARGETARCH} go build -a -tags netgo -ldflags '-linkmode external -extldflags -static -s -w' -o ovpn-admin FROM alpine:3.16 WORKDIR /app +ARG TARGETARCH RUN apk add --update bash easy-rsa openssl openvpn coreutils iptables curl&& \ ln -s /usr/share/easy-rsa/easyrsa /usr/local/bin && \ - wget https://github.com/pashcovich/openvpn-user/releases/download/v1.0.9/openvpn-user-linux-amd64.tar.gz -O - | tar xz -C /usr/local/bin && \ + wget https://github.com/pashcovich/openvpn-user/releases/download/v1.0.9/openvpn-user-linux-${TARGETARCH}.tar.gz -O - | tar xz -C /usr/local/bin && \ rm -rf /tmp/* /var/tmp/* /var/cache/apk/* /var/cache/distfiles/* +RUN if [ -f "/usr/local/bin/openvpn-user-${TARGETARCH}" ]; then ln -s /usr/local/bin/openvpn-user-${TARGETARCH} /usr/local/bin/openvpn-user; fi COPY --from=backend-builder /app/ovpn-admin /app COPY setup/ /etc/openvpn/setup RUN chmod +x /etc/openvpn/setup/configure.sh diff --git a/README.md b/README.md index 2d2701a..8ce2a6e 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,9 @@ Flags: --listen.port="8080" port for ovpn-admin (or OVPN_LISTEN_PORT) + --listen.base-url="/" base URL for ovpn-admin web files + (or $OVPN_LISTEN_BASE_URL) + --role="master" server role, master or slave (or OVPN_ROLE) diff --git a/backend/flags.go b/backend/flags.go index 03d2287..0f53874 100644 --- a/backend/flags.go +++ b/backend/flags.go @@ -3,9 +3,10 @@ package backend import "gopkg.in/alecthomas/kingpin.v2" var ( - ListenHost = kingpin.Flag("listen.host", "host for ovpn-admin").Default("0.0.0.0").Envar("OVPN_LISTEN_HOST").String() - ListenPort = kingpin.Flag("listen.port", "port for ovpn-admin").Default("8080").Envar("OVPN_LISTEN_PORT").String() - ServerRole = kingpin.Flag("role", "server role, master or slave").Default("master").Envar("OVPN_ROLE").HintOptions("master", "slave").String() + ListenHost = kingpin.Flag("listen.host", "host for ovpn-admin").Default("0.0.0.0").Envar("OVPN_LISTEN_HOST").String() + ListenPort = kingpin.Flag("listen.port", "port for ovpn-admin").Default("8080").Envar("OVPN_LISTEN_PORT").String() + ListenBaseUrl = kingpin.Flag("listen.base-url", "base url for ovpn-admin").Default("/").Envar("OVPN_LISTEN_BASE_URL").String() + ServerRole = kingpin.Flag("role", "server role, master or slave").Default("master").Envar("OVPN_ROLE").HintOptions("master", "slave").String() //PersonalAccess = kingpin.Flag("personalize", "personalize access for users").Default("false").Envar("OVPN_ADMIN_PERSONALIZE").Bool() //AdminUserPassword = kingpin.Flag("admin.password", "password fom admin user").Default("admin").Envar("OVPN_ADMIN_PASSWORD").String() @@ -29,6 +30,7 @@ var ( EasyrsaDirPath = kingpin.Flag("easyrsa.path", "path to easyrsa dir").Default("./easyrsa").Envar("EASYRSA_PATH").String() IndexTxtPath = kingpin.Flag("easyrsa.index-path", "path to easyrsa index file").Default("").Envar("OVPN_INDEX_PATH").String() + EasyrsaBinPath = kingpin.Flag("easyrsa.bin-path", "path to easyrsa script").Default("easyrsa").Envar("EASYRSA_BIN_PATH").String() CcdEnabled = kingpin.Flag("ccd", "enable client-config-dir").Default("false").Envar("OVPN_CCD").Bool() CcdDir = kingpin.Flag("ccd.path", "path to client-config-dir").Default("./ccd").Envar("OVPN_CCD_PATH").String() diff --git a/backend/methods.go b/backend/methods.go index 5dfabb8..5d5a002 100644 --- a/backend/methods.go +++ b/backend/methods.go @@ -378,7 +378,7 @@ func (oAdmin *OvpnAdmin) userCreate(username, password string) (string, error) { } } } else { - o := runBash(fmt.Sprintf("cd %s && easyrsa build-client-full %s nopass 1>/dev/null", *EasyrsaDirPath, username)) + o := runBash(fmt.Sprintf("cd %s && %s build-client-full %s nopass 1>/dev/null", *EasyrsaDirPath, *EasyrsaBinPath, username)) log.Debug(o) if oAdmin.ExtraAuth { _, err := oAdmin.OUser.CreateUser(username, password) @@ -637,7 +637,7 @@ func (oAdmin *OvpnAdmin) userRevoke(username string) (error, string) { log.Error(err) } } else { - o := runBash(fmt.Sprintf("cd %s && echo yes | easyrsa revoke %s 1>/dev/null && easyrsa gen-crl 1>/dev/null", *EasyrsaDirPath, username)) + o := runBash(fmt.Sprintf("cd %[1]s && echo yes | %[2]s revoke %[3]s 1>/dev/null && %[2]s gen-crl 1>/dev/null", *EasyrsaDirPath, *EasyrsaBinPath, username)) log.Debugln(o) } @@ -707,7 +707,7 @@ func (oAdmin *OvpnAdmin) userUnrevoke(username string) (error, string) { log.Error(err) } - _ = runBash(fmt.Sprintf("cd %s && easyrsa gen-crl 1>/dev/null", *EasyrsaDirPath)) + _ = runBash(fmt.Sprintf("cd %s && %s gen-crl 1>/dev/null", *EasyrsaDirPath, *EasyrsaBinPath)) if oAdmin.ExtraAuth { if oAdmin.OUser.CheckUserExistent(username) { @@ -810,7 +810,7 @@ func (oAdmin *OvpnAdmin) userRotate(username, newPassword string) (error, string log.Error(err) } - _ = runBash(fmt.Sprintf("cd %s && easyrsa gen-crl 1>/dev/null", *EasyrsaDirPath)) + _ = runBash(fmt.Sprintf("cd %s && %s gen-crl 1>/dev/null", *EasyrsaDirPath, *EasyrsaBinPath)) } crlFix() oAdmin.clients = oAdmin.usersList() @@ -850,7 +850,7 @@ func (oAdmin *OvpnAdmin) userDelete(username string) (error, string) { if err != nil { log.Error(err) } - _ = runBash(fmt.Sprintf("cd %s && easyrsa gen-crl 1>/dev/null ", *EasyrsaDirPath)) + _ = runBash(fmt.Sprintf("cd %s && %s gen-crl 1>/dev/null ", *EasyrsaDirPath, *EasyrsaBinPath)) } crlFix() oAdmin.clients = oAdmin.usersList() diff --git a/dashboard/ovpn-admin.json b/dashboard/ovpn-admin.json index bd01004..f7f83f7 100644 --- a/dashboard/ovpn-admin.json +++ b/dashboard/ovpn-admin.json @@ -31,7 +31,7 @@ { "datasource": { "type": "prometheus", - "uid": "P0D6E4079E36703EB" + "uid": "$ds_prometheus" }, "fieldConfig": { "defaults": { @@ -90,7 +90,7 @@ { "datasource": { "type": "prometheus", - "uid": "P0D6E4079E36703EB" + "uid": "$ds_prometheus" }, "fieldConfig": { "defaults": { @@ -149,7 +149,7 @@ { "datasource": { "type": "prometheus", - "uid": "P0D6E4079E36703EB" + "uid": "$ds_prometheus" }, "fieldConfig": { "defaults": { @@ -206,7 +206,7 @@ { "datasource": { "type": "prometheus", - "uid": "P0D6E4079E36703EB" + "uid": "$ds_prometheus" }, "fieldConfig": { "defaults": { @@ -263,7 +263,7 @@ { "datasource": { "type": "prometheus", - "uid": "P0D6E4079E36703EB" + "uid": "$ds_prometheus" }, "fieldConfig": { "defaults": { @@ -305,7 +305,7 @@ }, "textMode": "auto" }, - "pluginVersion": "8.5.2", + "pluginVersion": "8.5.13", "targets": [ { "expr": "ovpn_clients_expired", @@ -320,7 +320,7 @@ { "datasource": { "type": "prometheus", - "uid": "P0D6E4079E36703EB" + "uid": "$ds_prometheus" }, "fieldConfig": { "defaults": { @@ -381,7 +381,7 @@ "dashes": false, "datasource": { "type": "prometheus", - "uid": "P0D6E4079E36703EB" + "uid": "$ds_prometheus" }, "fieldConfig": { "defaults": { @@ -469,7 +469,7 @@ "dashes": false, "datasource": { "type": "prometheus", - "uid": "P0D6E4079E36703EB" + "uid": "$ds_prometheus" }, "fieldConfig": { "defaults": { @@ -557,7 +557,7 @@ "dashes": false, "datasource": { "type": "prometheus", - "uid": "P0D6E4079E36703EB" + "uid": "$ds_prometheus" }, "fieldConfig": { "defaults": { @@ -647,7 +647,7 @@ "dashes": false, "datasource": { "type": "prometheus", - "uid": "P0D6E4079E36703EB" + "uid": "$ds_prometheus" }, "fieldConfig": { "defaults": { @@ -733,7 +733,7 @@ { "datasource": { "type": "prometheus", - "uid": "P0D6E4079E36703EB" + "uid": "$ds_prometheus" }, "description": "value show last connection check time", "fieldConfig": { @@ -794,7 +794,7 @@ { "datasource": { "type": "prometheus", - "uid": "P0D6E4079E36703EB" + "uid": "$ds_prometheus" }, "description": "value shows when connection was started", "fieldConfig": { @@ -855,7 +855,7 @@ { "datasource": { "type": "prometheus", - "uid": "P0D6E4079E36703EB" + "uid": "$ds_prometheus" }, "fieldConfig": { "defaults": { @@ -928,7 +928,26 @@ "style": "dark", "tags": [], "templating": { - "list": [] + "list": [ + { + "current": { + "selected": false, + "text": "default", + "value": "default" + }, + "hide": 0, + "includeAll": false, + "multi": false, + "label": "Prometheus", + "name": "ds_prometheus", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + } + ] }, "time": { "from": "now-15m", @@ -952,4 +971,4 @@ "uid": "Z7qmFI0Gk", "version": 1, "weekStart": "" -} +} \ No newline at end of file diff --git a/main.go b/main.go index de857a7..66d22ee 100644 --- a/main.go +++ b/main.go @@ -126,41 +126,43 @@ func main() { go ovpnAdmin.UpdateState() - http.Handle("/", static) - http.HandleFunc("/api/server/settings", ovpnAdmin.ServerSettingsHandler) - http.HandleFunc("/api/users/list", ovpnAdmin.UserListHandler) - http.HandleFunc("/api/user/create", ovpnAdmin.UserCreateHandler) - http.HandleFunc("/api/user/rotate", ovpnAdmin.UserRotateHandler) - http.HandleFunc("/api/user/delete", ovpnAdmin.UserDeleteHandler) - http.HandleFunc("/api/user/revoke", ovpnAdmin.UserRevokeHandler) - http.HandleFunc("/api/user/unrevoke", ovpnAdmin.UserUnrevokeHandler) - http.HandleFunc("/api/user/config/show", ovpnAdmin.UserShowConfigHandler) + listenBaseUrl := *backend.ListenBaseUrl - http.HandleFunc("/api/user/disconnect", ovpnAdmin.UserDisconnectHandler) - http.HandleFunc("/api/user/statistic", ovpnAdmin.UserStatisticHandler) + http.Handle(listenBaseUrl, http.StripPrefix(strings.TrimRight(listenBaseUrl, "/"), static)) + http.HandleFunc(listenBaseUrl + "api/server/settings", ovpnAdmin.ServerSettingsHandler) + http.HandleFunc(listenBaseUrl + "api/users/list", ovpnAdmin.UserListHandler) + http.HandleFunc(listenBaseUrl + "api/user/create", ovpnAdmin.UserCreateHandler) + http.HandleFunc(listenBaseUrl + "api/user/rotate", ovpnAdmin.UserRotateHandler) + http.HandleFunc(listenBaseUrl + "api/user/delete", ovpnAdmin.UserDeleteHandler) + http.HandleFunc(listenBaseUrl + "api/user/revoke", ovpnAdmin.UserRevokeHandler) + http.HandleFunc(listenBaseUrl + "api/user/unrevoke", ovpnAdmin.UserUnrevokeHandler) + http.HandleFunc(listenBaseUrl + "api/user/config/show", ovpnAdmin.UserShowConfigHandler) + + http.HandleFunc(listenBaseUrl + "api/user/disconnect", ovpnAdmin.UserDisconnectHandler) + http.HandleFunc(listenBaseUrl + "api/user/statistic", ovpnAdmin.UserStatisticHandler) if *backend.CcdEnabled { - http.HandleFunc("/api/user/ccd", ovpnAdmin.UserShowCcdHandler) - http.HandleFunc("/api/user/ccd/apply", ovpnAdmin.UserApplyCcdHandler) + http.HandleFunc(listenBaseUrl + "api/user/ccd", ovpnAdmin.UserShowCcdHandler) + http.HandleFunc(listenBaseUrl + "api/user/ccd/apply", ovpnAdmin.UserApplyCcdHandler) } if ovpnAdmin.ExtraAuth { - http.HandleFunc("/api/user/change-password", ovpnAdmin.UserChangePasswordHandler) - http.HandleFunc("/api/auth/check", ovpnAdmin.AuthCheckHandler) + http.HandleFunc(listenBaseUrl + "api/user/change-password", ovpnAdmin.UserChangePasswordHandler) + http.HandleFunc(listenBaseUrl + "/api/auth/check", ovpnAdmin.AuthCheckHandler) if *backend.AuthType == "TOTP" { - http.HandleFunc("/api/user/2fa/secret", ovpnAdmin.UserGetSecretHandler) - http.HandleFunc("/api/user/2fa/register", ovpnAdmin.UserSetupTFAHandler) - http.HandleFunc("/api/user/2fa/reset", ovpnAdmin.UserResetTFAHandler) + http.HandleFunc(listenBaseUrl + "/api/user/2fa/secret", ovpnAdmin.UserGetSecretHandler) + http.HandleFunc(listenBaseUrl + "/api/user/2fa/register", ovpnAdmin.UserSetupTFAHandler) + http.HandleFunc(listenBaseUrl + "/api/user/2fa/reset", ovpnAdmin.UserResetTFAHandler) } } - http.HandleFunc("/api/sync/last/try", ovpnAdmin.LastSyncTimeHandler) - http.HandleFunc("/api/sync/last/successful", ovpnAdmin.LastSuccessfulSyncTimeHandler) - http.HandleFunc(backend.DownloadCertsApiUrl, ovpnAdmin.DownloadCertsHandler) - http.HandleFunc(backend.DownloadCcdApiUrl, ovpnAdmin.DownloadCcdHandler) + http.HandleFunc(listenBaseUrl + "/api/sync/last/try", ovpnAdmin.LastSyncTimeHandler) + http.HandleFunc(listenBaseUrl + "/api/sync/last/successful", ovpnAdmin.LastSuccessfulSyncTimeHandler) + http.HandleFunc(listenBaseUrl + backend.DownloadCertsApiUrl, ovpnAdmin.DownloadCertsHandler) + http.HandleFunc(listenBaseUrl + backend.DownloadCcdApiUrl, ovpnAdmin.DownloadCcdHandler) http.Handle(*backend.MetricsPath, promhttp.HandlerFor(ovpnAdmin.PromRegistry, promhttp.HandlerOpts{})) - http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) { + http.HandleFunc(listenBaseUrl + "/ping", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "pong") })