Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Totp auth #222

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ out
gen
.github

easyrsa
easyrsa_master
easyrsa_slave
ccd
ccd_master
ccd_slave
easyrsa/
easyrsa_master/
easyrsa_slave/
ccd/
ccd_master/
ccd_slave/
werf.yaml
frontend/node_modules
frontend/static/dist
Expand All @@ -24,3 +24,5 @@ docker-compose-slave.yaml
img
dashboard
.helm
.github

13 changes: 13 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
OVPN_SERVER_NET="192.168.100.0"
OVPN_SERVER_MASK="255.255.255.0"
OVPN_NETWORK="192.168.100.0/24"
OVPN_CCD="true"
OVPN_CCD_PATH="/mnt/ccd"
EASYRSA_PATH="/mnt/easyrsa"
OVPN_INDEX_PATH="/mnt/easyrsa/pki/index.txt"
OVPN_SERVER="127.0.0.1:7777:tcp"
OVPN_AUTH="true"
OVPN_AUTH_TFA="true"
OVPN_PASSWD_AUTH="true"
OVPN_AUTH_DB_PATH="/mnt/easyrsa/pki/users.db"
LOG_LEVEL="debug"
8 changes: 0 additions & 8 deletions .github/workflows/publish-latest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,6 @@ jobs:
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:
Expand Down
8 changes: 0 additions & 8 deletions .github/workflows/publish-tag.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,6 @@ jobs:
- name: Get the version
id: get_version
run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//}
- name: Push openvpn image to Docker Hub
uses: docker/build-push-action@v1
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASS }}
repository: flant/ovpn-admin
tags: openvpn-${{ steps.get_version.outputs.VERSION }}
dockerfile: Dockerfile.openvpn
- name: Push ovpn-admin image to Docker Hub
uses: docker/build-push-action@v1
with:
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,5 @@ packrd/
*.ntvs*
*.njsproj
*.sln

.env
11 changes: 6 additions & 5 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@ COPY frontend/ /app
RUN cd /app && npm install && npm run build

FROM golang:1.17.3-buster AS backend-builder
RUN go install github.com/gobuffalo/packr/v2/packr2@latest
COPY --from=frontend-builder /app/static /app/frontend/static
COPY . /app
RUN cd /app && packr2 && env CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -a -tags netgo -ldflags '-linkmode external -extldflags -static -s -w' -o ovpn-admin && packr2 clean
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

FROM alpine:3.16
WORKDIR /app
COPY --from=backend-builder /app/ovpn-admin /app
RUN apk add --update bash easy-rsa openssl openvpn coreutils && \
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.4/openvpn-user-linux-amd64.tar.gz -O - | tar xz -C /usr/local/bin && \
wget https://github.com/pashcovich/openvpn-user/releases/download/v1.0.9/openvpn-user-linux-amd64.tar.gz -O - | tar xz -C /usr/local/bin && \
rm -rf /tmp/* /var/tmp/* /var/cache/apk/* /var/cache/distfiles/*
COPY --from=backend-builder /app/ovpn-admin /app
COPY setup/ /etc/openvpn/setup
RUN chmod +x /etc/openvpn/setup/configure.sh
7 changes: 0 additions & 7 deletions Dockerfile.openvpn

This file was deleted.

5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,12 @@ Ready docker images available on [Docker Hub](https://hub.docker.com/r/flant/ovp

Requirements. You need Linux with the following components installed:
- [golang](https://golang.org/doc/install)
- [packr2](https://github.com/gobuffalo/packr#installation)
- [nodejs/npm](https://nodejs.org/en/download/package-manager/)

before version 2.1.0 you need
- [packr2](https://github.com/gobuffalo/packr#installation)


Commands to execute:

```bash
Expand Down
31 changes: 15 additions & 16 deletions certificates.go → backend/certificates.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package main
package backend

import (
"bytes"
Expand All @@ -12,8 +12,8 @@ import (
"time"
)

// decode certificate from PEM to x509
func decodeCert(certPEMBytes []byte) (cert *x509.Certificate, err error) {
// DecodeCert decode certificate from PEM to x509
func DecodeCert(certPEMBytes []byte) (cert *x509.Certificate, err error) {
certPem, _ := pem.Decode(certPEMBytes)
certPemBytes := certPem.Bytes

Expand All @@ -25,8 +25,8 @@ func decodeCert(certPEMBytes []byte) (cert *x509.Certificate, err error) {
return
}

// decode private key from PEM to RSA format
func decodePrivKey(privKey []byte) (key *rsa.PrivateKey, err error) {
// DecodePrivKey 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 {
Expand All @@ -43,8 +43,8 @@ func decodePrivKey(privKey []byte) (key *rsa.PrivateKey, err error) {
return
}

// return PEM encoded private key
func genPrivKey() (privKeyPEM *bytes.Buffer, err error) {
// GenPrivKey return PEM encoded private key
func GenPrivKey() (privKeyPEM *bytes.Buffer, err error) {
privKey, err := rsa.GenerateKey(rand.Reader, 2048)

//privKeyPKCS1 := x509.MarshalPKCS1PrivateKey(privKey)
Expand All @@ -60,12 +60,11 @@ func genPrivKey() (privKeyPEM *bytes.Buffer, err error) {
Bytes: privKeyPKCS8,
})


return
}

// return PEM encoded certificate
func genCA(privKey *rsa.PrivateKey) (issuerPEM *bytes.Buffer, err error) {
// GenCA 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)
Expand Down Expand Up @@ -96,8 +95,8 @@ func genCA(privKey *rsa.PrivateKey) (issuerPEM *bytes.Buffer, err error) {
return
}

// return PEM encoded certificate
func genServerCert(privKey, caPrivKey *rsa.PrivateKey, ca *x509.Certificate, cn string) (issuerPEM *bytes.Buffer, err error) {
// GenServerCert 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)

Expand Down Expand Up @@ -128,8 +127,8 @@ func genServerCert(privKey, caPrivKey *rsa.PrivateKey, ca *x509.Certificate, cn
return
}

// return PEM encoded certificate
func genClientCert(privKey, caPrivKey *rsa.PrivateKey, ca *x509.Certificate, cn string) (issuerPEM *bytes.Buffer, err error) {
// GenClientCert 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)

Expand Down Expand Up @@ -160,8 +159,8 @@ func genClientCert(privKey, caPrivKey *rsa.PrivateKey, ca *x509.Certificate, cn
return
}

// return PEM encoded CRL
func genCRL(certs []*RevokedCert, ca *x509.Certificate, caKey *rsa.PrivateKey) (crlPEM *bytes.Buffer, err error) {
// GenCRL 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 {
Expand Down
14 changes: 14 additions & 0 deletions backend/consts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package backend

const (
usernameRegexp = `^([a-zA-Z0-9_.-@])+$`
passwordMinLength = 6
DownloadCertsApiUrl = "/api/data/certs/download"
DownloadCcdApiUrl = "/api/data/ccd/download"
certsArchiveFileName = "certs.tar.gz"
ccdArchiveFileName = "ccd.tar.gz"
indexTxtDateLayout = "060102150405Z"
stringDateFormat = "2006-01-02 15:04:05"

KubeNamespaceFilePath = "/var/run/secrets/kubernetes.io/serviceaccount/namespace"
)
18 changes: 18 additions & 0 deletions backend/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package backend

import "errors"

var (
userSecretDoesNotExistError = errors.New("user secret does not exist")
userAlreadyExistError = errors.New("user already exist")
userDeletedError = errors.New("user marked as deleted")
userRestoreError = errors.New("failed to restore user")
userRevokeError = errors.New("failed to revoke user")
userDeleteError = errors.New("failed to delete user")
userIsNotActiveError = errors.New("user is not active")
passwordMismatchedError = errors.New("password mismatched")
tokenMismatchedError = errors.New("token mismatched")
checkAppError = errors.New("failed to check 2FA app")
registerAppError = errors.New("failed to register 2FA app")
authBackendDisabled = errors.New("auth backend not enabled yet")
)
47 changes: 47 additions & 0 deletions backend/flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
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()

//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()
//AdditionalAdminUsers = kingpin.Flag("admin.users", "comma separated users for additional admin grants").Default("").Envar("OVPN_ADMIN_USERS").String()

masterHost = kingpin.Flag("master.host", "URL for the master server").Default("http://127.0.0.1").Envar("OVPN_MASTER_HOST").String()
MasterBasicAuthUser = kingpin.Flag("master.basic-auth.user", "user for master server's Basic Auth").Default("").Envar("OVPN_MASTER_USER").String()
MasterBasicAuthPassword = kingpin.Flag("master.basic-auth.password", "password for master server's Basic Auth").Default("").Envar("OVPN_MASTER_PASSWORD").String()
masterSyncFrequency = kingpin.Flag("master.sync-frequency", "master host data sync frequency in seconds").Default("600").Envar("OVPN_MASTER_SYNC_FREQUENCY").Int()
MasterSyncToken = kingpin.Flag("master.sync-token", "master host data sync security token").Default("VerySecureToken").Envar("OVPN_MASTER_TOKEN").PlaceHolder("TOKEN").String()

openvpnNetwork = kingpin.Flag("ovpn.network", "NETWORK/MASK_PREFIX for OpenVPN server").Default("172.16.100.0/24").Envar("OVPN_NETWORK").String()
openvpnServer = kingpin.Flag("ovpn.server", "HOST:PORT:PROTOCOL for OpenVPN server; can have multiple values").Default("127.0.0.1:7777:tcp").Envar("OVPN_SERVER").PlaceHolder("HOST:PORT:PROTOCOL").Strings()
//openvpnServerCryptoMode = kingpin.Flag("ovpn.server.crypto-mode", "OpenVPN server crypto mode").Default("tls-auth").Envar("OVPN_SERVER_CRYPTO_MODE").HintOptions("none", "tls-auth", "tls-crypt", "tls-cryptv2").Strings()
//openvpnServerCryptoFile = kingpin.Flag("ovpn.server.crypto-file", "OpenVPN server file name with cert for crypto ").Default("ta.key").Envar("OVPN_SERVER_CRYPTO_FILE").Strings()
openvpnServerBehindLB = kingpin.Flag("ovpn.server.behindLB", "enable if your OpenVPN server is behind Kubernetes Service having the LoadBalancer type").Default("false").Envar("OVPN_LB").Bool()
openvpnServiceName = kingpin.Flag("ovpn.service", "the name of Kubernetes Service having the LoadBalancer type if your OpenVPN server is behind it").Default("openvpn-external").Envar("OVPN_LB_SERVICE").Strings()

MgmtAddress = kingpin.Flag("mgmt", "ALIAS=HOST:PORT for OpenVPN server mgmt interface; can have multiple values").Default("main=127.0.0.1:8989").Envar("OVPN_MGMT").Strings()
MetricsPath = kingpin.Flag("metrics.path", "URL path for exposing collected metrics").Default("/metrics").Envar("OVPN_METRICS_PATH").String()

EasyrsaDirPath = kingpin.Flag("easyrsa.path", "path to easyrsa dir").Default("./easyrsa").Envar("EASYRSA_PATH").String()
IndexTxtPath = kingpin.Flag("easyrsa.index-path", "path to easyrsa index file").Default("").Envar("OVPN_INDEX_PATH").String()

CcdEnabled = kingpin.Flag("ccd", "enable client-config-dir").Default("false").Envar("OVPN_CCD").Bool()
CcdDir = kingpin.Flag("ccd.path", "path to client-config-dir").Default("./ccd").Envar("OVPN_CCD_PATH").String()

clientConfigTemplatePath = kingpin.Flag("templates.clientconfig-path", "path to custom client.conf.tpl").Default("").Envar("OVPN_TEMPLATES_CC_PATH").String()
ccdTemplatePath = kingpin.Flag("templates.ccd-path", "path to custom ccd.tpl").Default("").Envar("OVPN_TEMPLATES_CCD_PATH").String()

AuthByPassword = kingpin.Flag("auth.password", "enable additional password authentication").Default("false").Envar("OVPN_AUTH").Bool()
AuthTFA = kingpin.Flag("auth.2fa", "auth type").Default("false").Envar("OVPN_AUTH_TFA").Bool()
AuthDatabase = kingpin.Flag("auth.db", "database path for password authentication").Default("./easyrsa/pki/users.db").Envar("OVPN_AUTH_DB_PATH").String()

LogLevel = kingpin.Flag("log.level", "set log level: trace, debug, info, warn, error (default info)").Default("info").Envar("LOG_LEVEL").String()
LogFormat = kingpin.Flag("log.format", "set log format: text, json (default text)").Default("text").Envar("LOG_FORMAT").String()

StorageBackend = kingpin.Flag("storage.backend", "storage backend for user certs)").Default("filesystem").HintOptions("kubernetes.secrets", "filesystem").Envar("STORAGE_BACKEND").String()
)
Loading