1
0
Fork 0
mirror of synced 2025-02-12 20:17:46 -05:00
ovpn-admin/main.go

1368 lines
43 KiB
Go
Raw Normal View History

2020-05-14 19:13:33 -04:00
package main
import (
"bufio"
2020-05-14 19:13:33 -04:00
"bytes"
2021-10-05 11:09:29 -04:00
"context"
"crypto/x509"
"encoding/json"
"encoding/pem"
2020-05-14 19:13:33 -04:00
"fmt"
"io/ioutil"
"net"
"net/http"
2020-05-14 19:13:33 -04:00
"os"
"regexp"
"strconv"
2020-05-14 19:13:33 -04:00
"strings"
"text/template"
"time"
2021-10-05 11:09:29 -04:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"github.com/gobuffalo/packr/v2"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
2021-12-30 03:24:44 -05:00
log "github.com/sirupsen/logrus"
"gopkg.in/alecthomas/kingpin.v2"
)
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"
2020-10-15 12:12:31 -04:00
)
var (
2021-12-30 03:24:44 -05:00
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()
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()
2021-10-22 00:35:16 -04:00
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()
2021-12-30 03:24:44 -05:00
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()
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()
2021-12-30 03:24:44 -05:00
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()
2021-10-22 00:35:16 -04:00
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()
2021-12-30 03:24:44 -05:00
authByPassword = kingpin.Flag("auth.password", "enable additional password authentication").Default("false").Envar("OVPN_AUTH").Bool()
authDatabase = kingpin.Flag("auth.db", "database path for password authentication").Default("./easyrsa/pki/users.db").Envar("OVPN_AUTH_DB_PATH").String()
logLevel = kingpin.Flag("log.level", "set log level: trace, debug, info, warn, error (default info)").Default("info").Envar("LOG_LEVEL").String()
logFormat = kingpin.Flag("log.format", "set log format: text, json (default text)").Default("text").Envar("LOG_FORMAT").String()
storageBackend = kingpin.Flag("storage.backend", "storage backend: filesystem, kubernetes.secrets (default filesystem)").Default("filesystem").Envar("STORAGE_BACKEND").String()
2021-10-05 11:09:29 -04:00
2021-12-30 03:24:44 -05:00
certsArchivePath = "/tmp/" + certsArchiveFileName
ccdArchivePath = "/tmp/" + ccdArchiveFileName
2021-10-05 11:09:29 -04:00
2021-11-29 03:34:34 -05:00
version = "1.7.5"
)
2021-12-30 03:24:44 -05:00
var logLevels = map[string]log.Level{
"trace": log.TraceLevel,
2021-12-30 03:24:44 -05:00
"debug": log.DebugLevel,
"info": log.InfoLevel,
"warn": log.WarnLevel,
"error": log.ErrorLevel,
}
var logFormats = map[string]log.Formatter{
"text": &log.TextFormatter{},
"json": &log.JSONFormatter{},
}
var (
ovpnServerCertExpire = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "ovpn_server_cert_expire",
Help: "openvpn server certificate expire time in days",
},
)
ovpnServerCaCertExpire = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "ovpn_server_ca_cert_expire",
Help: "openvpn server CA certificate expire time in days",
},
)
ovpnClientsTotal = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "ovpn_clients_total",
Help: "total openvpn users",
},
)
ovpnClientsRevoked = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "ovpn_clients_revoked",
Help: "revoked openvpn users",
},
)
ovpnClientsExpired = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "ovpn_clients_expired",
Help: "expired openvpn users",
},
)
ovpnClientsConnected = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "ovpn_clients_connected",
Help: "connected openvpn users",
},
)
ovpnClientCertificateExpire = prometheus.NewGaugeVec(prometheus.GaugeOpts{
Name: "ovpn_client_cert_expire",
Help: "openvpn user certificate expire time in days",
},
[]string{"client"},
)
ovpnClientConnectionInfo = prometheus.NewGaugeVec(prometheus.GaugeOpts{
Name: "ovpn_client_connection_info",
Help: "openvpn user connection info. ip - assigned address from ovpn network. value - last time when connection was refreshed in unix format",
},
[]string{"client", "ip"},
)
ovpnClientConnectionFrom = prometheus.NewGaugeVec(prometheus.GaugeOpts{
Name: "ovpn_client_connection_from",
Help: "openvpn user connection info. ip - from which address connection was initialized. value - time when connection was initialized in unix format",
},
[]string{"client", "ip"},
)
ovpnClientBytesReceived = prometheus.NewGaugeVec(prometheus.GaugeOpts{
Name: "ovpn_client_bytes_received",
Help: "openvpn user bytes received",
},
[]string{"client"},
)
ovpnClientBytesSent = prometheus.NewGaugeVec(prometheus.GaugeOpts{
Name: "ovpn_client_bytes_sent",
Help: "openvpn user bytes sent",
},
[]string{"client"},
)
2020-05-14 19:13:33 -04:00
)
2021-03-17 06:44:12 -04:00
type OvpnAdmin struct {
role string
lastSyncTime string
lastSuccessfulSyncTime string
masterHostBasicAuth bool
masterSyncToken string
clients []OpenvpnClient
activeClients []clientStatus
promRegistry *prometheus.Registry
mgmtInterfaces map[string]string
templates *packr.Box
modules []string
mgmtStatusTimeFormat string
}
type OpenvpnServer struct {
Host string
Port string
Protocol string
}
type openvpnClientConfig struct {
Hosts []OpenvpnServer
CA string
Cert string
Key string
TLS string
PasswdAuth bool
2020-05-14 19:13:33 -04:00
}
type OpenvpnClient struct {
2021-12-30 03:24:44 -05:00
Identity string `json:"Identity"`
AccountStatus string `json:"AccountStatus"`
ExpirationDate string `json:"ExpirationDate"`
RevocationDate string `json:"RevocationDate"`
ConnectionStatus string `json:"ConnectionStatus"`
ConnectionServer string `json:"ConnectionServer"`
2020-10-15 12:12:31 -04:00
}
2020-10-29 06:50:19 -04:00
type ccdRoute struct {
Address string `json:"Address"`
Mask string `json:"Mask"`
Description string `json:"Description"`
2020-10-29 06:50:19 -04:00
}
type Ccd struct {
User string `json:"User"`
ClientAddress string `json:"ClientAddress"`
CustomRoutes []ccdRoute `json:"CustomRoutes"`
2020-10-15 12:12:31 -04:00
}
2020-05-14 19:13:33 -04:00
type indexTxtLine struct {
Flag string
ExpirationDate string
RevocationDate string
SerialNumber string
Filename string
DistinguishedName string
Identity string
}
type clientStatus struct {
CommonName string
RealAddress string
BytesReceived string
BytesSent string
ConnectedSince string
VirtualAddress string
LastRef string
ConnectedSinceFormatted string
LastRefFormatted string
ConnectedTo string
2020-05-14 19:13:33 -04:00
}
2021-03-17 06:44:12 -04:00
func (oAdmin *OvpnAdmin) userListHandler(w http.ResponseWriter, r *http.Request) {
log.Info(r.RemoteAddr, r.RequestURI)
usersList, _ := json.Marshal(oAdmin.clients)
2020-10-29 06:50:19 -04:00
fmt.Fprintf(w, "%s", usersList)
2020-05-14 19:13:33 -04:00
}
2021-03-17 06:44:12 -04:00
func (oAdmin *OvpnAdmin) userStatisticHandler(w http.ResponseWriter, r *http.Request) {
log.Info(r.RemoteAddr, r.RequestURI)
2021-12-30 03:24:44 -05:00
_ = r.ParseForm()
userStatistic, _ := json.Marshal(oAdmin.getUserStatistic(r.FormValue("username")))
fmt.Fprintf(w, "%s", userStatistic)
}
2021-03-17 06:44:12 -04:00
func (oAdmin *OvpnAdmin) userCreateHandler(w http.ResponseWriter, r *http.Request) {
log.Info(r.RemoteAddr, r.RequestURI)
if oAdmin.role == "slave" {
http.Error(w, `{"status":"error"}`, http.StatusLocked)
return
}
2021-12-30 03:24:44 -05:00
_ = r.ParseForm()
userCreated, userCreateStatus := oAdmin.userCreate(r.FormValue("username"), r.FormValue("password"))
2020-10-29 06:50:19 -04:00
if userCreated {
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, userCreateStatus)
return
} else {
http.Error(w, userCreateStatus, http.StatusUnprocessableEntity)
}
2020-05-14 19:13:33 -04:00
}
2021-03-17 06:44:12 -04:00
func (oAdmin *OvpnAdmin) userRevokeHandler(w http.ResponseWriter, r *http.Request) {
log.Info(r.RemoteAddr, r.RequestURI)
if oAdmin.role == "slave" {
http.Error(w, `{"status":"error"}`, http.StatusLocked)
return
}
2021-12-30 03:24:44 -05:00
_ = r.ParseForm()
fmt.Fprintf(w, "%s", oAdmin.userRevoke(r.FormValue("username")))
2020-05-14 19:13:33 -04:00
}
2021-03-17 06:44:12 -04:00
func (oAdmin *OvpnAdmin) userUnrevokeHandler(w http.ResponseWriter, r *http.Request) {
log.Info(r.RemoteAddr, r.RequestURI)
if oAdmin.role == "slave" {
http.Error(w, `{"status":"error"}`, http.StatusLocked)
return
}
2021-12-30 03:24:44 -05:00
_ = r.ParseForm()
fmt.Fprintf(w, "%s", oAdmin.userUnrevoke(r.FormValue("username")))
2020-05-14 19:13:33 -04:00
}
2021-03-17 06:44:12 -04:00
func (oAdmin *OvpnAdmin) userChangePasswordHandler(w http.ResponseWriter, r *http.Request) {
log.Info(r.RemoteAddr, r.RequestURI)
2021-12-30 03:24:44 -05:00
_ = r.ParseForm()
if *authByPassword {
passwordChanged, passwordChangeMessage := oAdmin.userChangePassword(r.FormValue("username"), r.FormValue("password"))
if passwordChanged {
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `{"status":"ok", "message": "%s"}`, passwordChangeMessage)
return
} else {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, `{"status":"error", "message": "%s"}`, passwordChangeMessage)
return
}
} else {
http.Error(w, `{"status":"error"}`, http.StatusNotImplemented)
}
}
2021-03-17 06:44:12 -04:00
func (oAdmin *OvpnAdmin) userShowConfigHandler(w http.ResponseWriter, r *http.Request) {
log.Info(r.RemoteAddr, r.RequestURI)
2021-12-30 03:24:44 -05:00
_ = r.ParseForm()
fmt.Fprintf(w, "%s", oAdmin.renderClientConfig(r.FormValue("username")))
2020-05-14 19:13:33 -04:00
}
2021-03-17 06:44:12 -04:00
func (oAdmin *OvpnAdmin) userDisconnectHandler(w http.ResponseWriter, r *http.Request) {
log.Info(r.RemoteAddr, r.RequestURI)
2021-12-30 03:24:44 -05:00
_ = r.ParseForm()
// fmt.Fprintf(w, "%s", userDisconnect(r.FormValue("username")))
2020-10-29 06:50:19 -04:00
fmt.Fprintf(w, "%s", r.FormValue("username"))
}
2021-03-17 06:44:12 -04:00
func (oAdmin *OvpnAdmin) userShowCcdHandler(w http.ResponseWriter, r *http.Request) {
log.Info(r.RemoteAddr, r.RequestURI)
2021-12-30 03:24:44 -05:00
_ = r.ParseForm()
ccd, _ := json.Marshal(oAdmin.getCcd(r.FormValue("username")))
2020-10-29 06:50:19 -04:00
fmt.Fprintf(w, "%s", ccd)
2020-10-15 12:12:31 -04:00
}
2021-03-17 06:44:12 -04:00
func (oAdmin *OvpnAdmin) userApplyCcdHandler(w http.ResponseWriter, r *http.Request) {
log.Info(r.RemoteAddr, r.RequestURI)
if oAdmin.role == "slave" {
http.Error(w, `{"status":"error"}`, http.StatusLocked)
return
}
var ccd Ccd
if r.Body == nil {
http.Error(w, "Please send a request body", http.StatusBadRequest)
return
}
err := json.NewDecoder(r.Body).Decode(&ccd)
if err != nil {
2021-12-30 03:24:44 -05:00
log.Errorln(err)
}
ccdApplied, applyStatus := oAdmin.modifyCcd(ccd)
if ccdApplied {
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, applyStatus)
return
} else {
http.Error(w, applyStatus, http.StatusUnprocessableEntity)
}
2020-10-15 12:12:31 -04:00
}
2021-03-17 06:44:12 -04:00
func (oAdmin *OvpnAdmin) serverSettingsHandler(w http.ResponseWriter, r *http.Request) {
log.Info(r.RemoteAddr, r.RequestURI)
enabledModules, enabledModulesErr := json.Marshal(oAdmin.modules)
if enabledModulesErr != nil {
2021-12-30 03:24:44 -05:00
log.Errorln(enabledModulesErr)
}
fmt.Fprintf(w, `{"status":"ok", "serverRole": "%s", "modules": %s }`, oAdmin.role, string(enabledModules))
}
2021-03-17 06:44:12 -04:00
func (oAdmin *OvpnAdmin) lastSyncTimeHandler(w http.ResponseWriter, r *http.Request) {
log.Debug(r.RemoteAddr, r.RequestURI)
fmt.Fprint(w, oAdmin.lastSyncTime)
}
2021-03-17 06:44:12 -04:00
func (oAdmin *OvpnAdmin) lastSuccessfulSyncTimeHandler(w http.ResponseWriter, r *http.Request) {
log.Debug(r.RemoteAddr, r.RequestURI)
fmt.Fprint(w, oAdmin.lastSuccessfulSyncTime)
}
2021-03-17 06:44:12 -04:00
func (oAdmin *OvpnAdmin) downloadCertsHandler(w http.ResponseWriter, r *http.Request) {
log.Info(r.RemoteAddr, r.RequestURI)
if oAdmin.role == "slave" {
http.Error(w, `{"status":"error"}`, http.StatusLocked)
return
}
2021-12-30 03:24:44 -05:00
_ = r.ParseForm()
token := r.Form.Get("token")
if token != oAdmin.masterSyncToken {
http.Error(w, `{"status":"error"}`, http.StatusForbidden)
return
}
archiveCerts()
w.Header().Set("Content-Disposition", "attachment; filename="+certsArchiveFileName)
http.ServeFile(w, r, certsArchivePath)
}
2021-03-17 06:44:12 -04:00
func (oAdmin *OvpnAdmin) downloadCcdHandler(w http.ResponseWriter, r *http.Request) {
log.Info(r.RemoteAddr, r.RequestURI)
if oAdmin.role == "slave" {
http.Error(w, `{"status":"error"}`, http.StatusLocked)
return
}
2021-12-30 03:24:44 -05:00
_ = r.ParseForm()
token := r.Form.Get("token")
if token != oAdmin.masterSyncToken {
http.Error(w, `{"status":"error"}`, http.StatusForbidden)
return
}
archiveCcd()
w.Header().Set("Content-Disposition", "attachment; filename="+ccdArchiveFileName)
http.ServeFile(w, r, ccdArchivePath)
}
2021-12-30 03:24:44 -05:00
var app OpenVPNPKI
2020-05-14 19:13:33 -04:00
func main() {
kingpin.Version(version)
kingpin.Parse()
2021-12-30 03:24:44 -05:00
log.SetLevel(logLevels[*logLevel])
log.SetFormatter(logFormats[*logFormat])
2021-12-30 03:24:44 -05:00
if *storageBackend == "kubernetes.secrets" {
2021-12-30 03:24:44 -05:00
err := app.run()
if err != nil {
log.Error(err)
}
}
if *indexTxtPath == "" {
*indexTxtPath = *easyrsaDirPath + "/pki/index.txt"
}
2021-03-17 06:44:12 -04:00
ovpnAdmin := new(OvpnAdmin)
ovpnAdmin.lastSyncTime = "unknown"
ovpnAdmin.role = *serverRole
ovpnAdmin.lastSuccessfulSyncTime = "unknown"
ovpnAdmin.masterSyncToken = *masterSyncToken
ovpnAdmin.promRegistry = prometheus.NewRegistry()
ovpnAdmin.modules = []string{}
ovpnAdmin.mgmtInterfaces = make(map[string]string)
for _, mgmtInterface := range *mgmtAddress {
parts := strings.SplitN(mgmtInterface, "=", 2)
ovpnAdmin.mgmtInterfaces[parts[0]] = parts[len(parts)-1]
}
ovpnAdmin.mgmtSetTimeFormat()
ovpnAdmin.registerMetrics()
ovpnAdmin.setState()
go ovpnAdmin.updateState()
if *masterBasicAuthPassword != "" && *masterBasicAuthUser != "" {
ovpnAdmin.masterHostBasicAuth = true
} else {
ovpnAdmin.masterHostBasicAuth = false
}
ovpnAdmin.modules = append(ovpnAdmin.modules, "core")
if *authByPassword {
ovpnAdmin.modules = append(ovpnAdmin.modules, "passwdAuth")
}
if *ccdEnabled {
ovpnAdmin.modules = append(ovpnAdmin.modules, "ccd")
}
if ovpnAdmin.role == "slave" {
ovpnAdmin.syncDataFromMaster()
go ovpnAdmin.syncWithMaster()
}
ovpnAdmin.templates = packr.New("template", "./templates")
staticBox := packr.New("static", "./frontend/static")
static := CacheControlWrapper(http.FileServer(staticBox))
2020-05-14 19:13:33 -04:00
http.Handle("/", static)
http.HandleFunc("/api/server/settings", ovpnAdmin.serverSettingsHandler)
http.HandleFunc("/api/users/list", ovpnAdmin.userListHandler)
http.HandleFunc("/api/user/create", ovpnAdmin.userCreateHandler)
http.HandleFunc("/api/user/change-password", ovpnAdmin.userChangePasswordHandler)
http.HandleFunc("/api/user/revoke", ovpnAdmin.userRevokeHandler)
http.HandleFunc("/api/user/unrevoke", ovpnAdmin.userUnrevokeHandler)
http.HandleFunc("/api/user/config/show", ovpnAdmin.userShowConfigHandler)
http.HandleFunc("/api/user/disconnect", ovpnAdmin.userDisconnectHandler)
http.HandleFunc("/api/user/statistic", ovpnAdmin.userStatisticHandler)
http.HandleFunc("/api/user/ccd", ovpnAdmin.userShowCcdHandler)
http.HandleFunc("/api/user/ccd/apply", ovpnAdmin.userApplyCcdHandler)
http.HandleFunc("/api/sync/last/try", ovpnAdmin.lastSyncTimeHandler)
http.HandleFunc("/api/sync/last/successful", ovpnAdmin.lastSuccessfulSyncTimeHandler)
http.HandleFunc(downloadCertsApiUrl, ovpnAdmin.downloadCertsHandler)
http.HandleFunc(downloadCcdApiUrl, ovpnAdmin.downloadCcdHandler)
http.Handle(*metricsPath, promhttp.HandlerFor(ovpnAdmin.promRegistry, promhttp.HandlerOpts{}))
http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "pong")
})
log.Printf("Bind: http://%s:%s", *listenHost, *listenPort)
log.Fatal(http.ListenAndServe(*listenHost+":"+*listenPort, nil))
2020-05-14 19:13:33 -04:00
}
2020-10-29 06:50:19 -04:00
func CacheControlWrapper(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Cache-Control", "max-age=2592000") // 30 days
h.ServeHTTP(w, r)
})
2020-05-14 19:13:33 -04:00
}
2021-03-17 06:44:12 -04:00
func (oAdmin *OvpnAdmin) registerMetrics() {
oAdmin.promRegistry.MustRegister(ovpnServerCertExpire)
oAdmin.promRegistry.MustRegister(ovpnServerCaCertExpire)
oAdmin.promRegistry.MustRegister(ovpnClientsTotal)
oAdmin.promRegistry.MustRegister(ovpnClientsRevoked)
oAdmin.promRegistry.MustRegister(ovpnClientsConnected)
oAdmin.promRegistry.MustRegister(ovpnClientsExpired)
oAdmin.promRegistry.MustRegister(ovpnClientCertificateExpire)
oAdmin.promRegistry.MustRegister(ovpnClientConnectionInfo)
oAdmin.promRegistry.MustRegister(ovpnClientConnectionFrom)
oAdmin.promRegistry.MustRegister(ovpnClientBytesReceived)
oAdmin.promRegistry.MustRegister(ovpnClientBytesSent)
}
2021-03-17 06:44:12 -04:00
func (oAdmin *OvpnAdmin) setState() {
oAdmin.activeClients = oAdmin.mgmtGetActiveClients()
oAdmin.clients = oAdmin.usersList()
ovpnServerCaCertExpire.Set(float64((getOvpnCaCertExpireDate().Unix() - time.Now().Unix()) / 3600 / 24))
}
2021-03-17 06:44:12 -04:00
func (oAdmin *OvpnAdmin) updateState() {
for {
time.Sleep(time.Duration(28) * time.Second)
ovpnClientBytesSent.Reset()
ovpnClientBytesReceived.Reset()
ovpnClientConnectionFrom.Reset()
ovpnClientConnectionInfo.Reset()
go oAdmin.setState()
}
}
2020-05-14 19:13:33 -04:00
func indexTxtParser(txt string) []indexTxtLine {
var indexTxt []indexTxtLine
2020-05-14 19:13:33 -04:00
txtLinesArray := strings.Split(txt, "\n")
for _, v := range txtLinesArray {
str := strings.Fields(v)
if len(str) > 0 {
switch {
// case strings.HasPrefix(str[0], "E"):
case strings.HasPrefix(str[0], "V"):
indexTxt = append(indexTxt, indexTxtLine{Flag: str[0], ExpirationDate: str[1], SerialNumber: str[2], Filename: str[3], DistinguishedName: str[4], Identity: str[4][strings.Index(str[4], "=")+1:]})
2020-05-14 19:13:33 -04:00
case strings.HasPrefix(str[0], "R"):
indexTxt = append(indexTxt, indexTxtLine{Flag: str[0], ExpirationDate: str[1], RevocationDate: str[2], SerialNumber: str[3], Filename: str[4], DistinguishedName: str[5], Identity: str[5][strings.Index(str[5], "=")+1:]})
2020-05-14 19:13:33 -04:00
}
}
}
2020-05-14 19:13:33 -04:00
return indexTxt
}
func renderIndexTxt(data []indexTxtLine) string {
indexTxt := ""
for _, line := range data {
switch {
case line.Flag == "V":
indexTxt += fmt.Sprintf("%s\t%s\t\t%s\t%s\t%s\n", line.Flag, line.ExpirationDate, line.SerialNumber, line.Filename, line.DistinguishedName)
2020-05-14 19:13:33 -04:00
case line.Flag == "R":
indexTxt += fmt.Sprintf("%s\t%s\t%s\t%s\t%s\t%s\n", line.Flag, line.ExpirationDate, line.RevocationDate, line.SerialNumber, line.Filename, line.DistinguishedName)
// case line.flag == "E":
2020-05-14 19:13:33 -04:00
}
}
2020-10-29 06:50:19 -04:00
return indexTxt
2020-05-14 19:13:33 -04:00
}
func (oAdmin *OvpnAdmin) getClientConfigTemplate() *template.Template {
if *clientConfigTemplatePath != "" {
return template.Must(template.ParseFiles(*clientConfigTemplatePath))
} else {
clientConfigTpl, clientConfigTplErr := oAdmin.templates.FindString("client.conf.tpl")
if clientConfigTplErr != nil {
log.Error("clientConfigTpl not found in templates box")
}
return template.Must(template.New("client-config").Parse(clientConfigTpl))
}
}
2021-03-17 06:44:12 -04:00
func (oAdmin *OvpnAdmin) renderClientConfig(username string) string {
2020-05-14 19:13:33 -04:00
if checkUserExist(username) {
var hosts []OpenvpnServer
for _, server := range *openvpnServer {
parts := strings.SplitN(server, ":", 3)
hosts = append(hosts, OpenvpnServer{Host: parts[0], Port: parts[1], Protocol: parts[2]})
}
2021-12-30 03:24:44 -05:00
2021-10-05 11:09:29 -04:00
if *openvpnServerBehindLB {
2021-12-30 03:24:44 -05:00
var err error
hosts, err = getOvpnServerHostsFromKubeApi()
if err != nil {
log.Error(err)
}
2021-10-05 11:09:29 -04:00
}
log.Tracef("hosts for %s\n %v", username, hosts)
2021-12-30 03:24:44 -05:00
2020-05-14 19:13:33 -04:00
conf := openvpnClientConfig{}
conf.Hosts = hosts
2020-10-29 06:50:19 -04:00
conf.CA = fRead(*easyrsaDirPath + "/pki/ca.crt")
conf.TLS = fRead(*easyrsaDirPath + "/pki/ta.key")
2021-12-30 03:24:44 -05:00
if *storageBackend == "kubernetes.secrets" {
conf.Cert, conf.Key = app.easyrsaGetClientCert(username)
2021-12-30 03:24:44 -05:00
} else {
conf.Cert = fRead(*easyrsaDirPath + "/pki/issued/" + username + ".crt")
conf.Key = fRead(*easyrsaDirPath + "/pki/private/" + username + ".key")
}
conf.PasswdAuth = *authByPassword
2020-10-29 06:50:19 -04:00
t := oAdmin.getClientConfigTemplate()
2020-05-14 19:13:33 -04:00
var tmp bytes.Buffer
err := t.Execute(&tmp, conf)
if err != nil {
log.Errorf("something goes wrong during rendering config for %s", username)
log.Debugf("rendering config for %s failed with error %v", username, err)
}
2020-10-29 06:50:19 -04:00
hosts = nil
2021-12-30 03:24:44 -05:00
log.Tracef("Rendered config for user %s: %+v", username, tmp.String())
2021-12-30 03:24:44 -05:00
return fmt.Sprintf("%+v", tmp.String())
2020-05-14 19:13:33 -04:00
}
log.Warnf("user \"%s\" not found", username)
return fmt.Sprintf("user \"%s\" not found", username)
2020-05-14 19:13:33 -04:00
}
func (oAdmin *OvpnAdmin) getCcdTemplate() *template.Template {
if *ccdTemplatePath != "" {
return template.Must(template.ParseFiles(*ccdTemplatePath))
} else {
ccdTpl, ccdTplErr := oAdmin.templates.FindString("ccd.tpl")
if ccdTplErr != nil {
2021-12-30 03:24:44 -05:00
log.Errorf("ccdTpl not found in templates box")
}
return template.Must(template.New("ccd").Parse(ccdTpl))
}
}
2021-03-17 06:44:12 -04:00
func (oAdmin *OvpnAdmin) parseCcd(username string) Ccd {
2020-10-29 06:50:19 -04:00
ccd := Ccd{}
ccd.User = username
ccd.ClientAddress = "dynamic"
ccd.CustomRoutes = []ccdRoute{}
2020-10-15 12:12:31 -04:00
var txtLinesArray []string
if *storageBackend == "kubernetes.secrets" {
txtLinesArray = strings.Split(app.secretGetCcd(ccd.User), "\n")
} else {
txtLinesArray = strings.Split(fRead(*ccdDir+"/"+username), "\n")
}
2020-10-15 12:12:31 -04:00
for _, v := range txtLinesArray {
str := strings.Fields(v)
if len(str) > 0 {
switch {
case strings.HasPrefix(str[0], "ifconfig-push"):
ccd.ClientAddress = str[1]
2020-10-15 12:12:31 -04:00
case strings.HasPrefix(str[0], "push"):
ccd.CustomRoutes = append(ccd.CustomRoutes, ccdRoute{Address: strings.Trim(str[2], "\""), Mask: strings.Trim(str[3], "\""), Description: strings.Trim(strings.Join(str[4:], ""), "#")})
2020-10-15 12:12:31 -04:00
}
}
}
2020-10-29 06:50:19 -04:00
return ccd
2020-10-15 12:12:31 -04:00