2020-05-14 19:13:33 -04:00
package main
import (
2020-11-17 12:48:26 -05:00
"bufio"
2020-05-14 19:13:33 -04:00
"bytes"
2021-10-05 11:09:29 -04:00
"context"
2022-01-20 09:49:03 -05:00
"crypto/x509"
2020-11-17 12:48:26 -05:00
"encoding/json"
2022-01-20 09:49:03 -05:00
"encoding/pem"
2020-05-14 19:13:33 -04:00
"fmt"
2022-01-20 09:49:03 -05:00
"io/ioutil"
2020-11-17 12:48:26 -05:00
"net"
"net/http"
2020-05-14 19:13:33 -04:00
"os"
"regexp"
2020-11-27 02:23:59 -05:00
"strconv"
2020-05-14 19:13:33 -04:00
"strings"
"text/template"
2020-11-17 12:48:26 -05:00
"time"
2021-02-26 07:11:13 -05:00
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"
2021-02-26 07:11:13 -05:00
"github.com/gobuffalo/packr/v2"
2021-07-21 18:06:34 -04:00
"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"
2021-07-21 18:06:34 -04:00
"gopkg.in/alecthomas/kingpin.v2"
2020-11-17 12:48:26 -05:00
)
const (
2021-07-21 18:06:34 -04:00
usernameRegexp = ` ^([a-zA-Z0-9_.-@])+$ `
passwordMinLength = 6
downloadCertsApiUrl = "/api/data/certs/download"
downloadCcdApiUrl = "/api/data/ccd/download"
2020-11-17 12:48:26 -05:00
certsArchiveFileName = "certs.tar.gz"
2021-07-21 18:06:34 -04:00
ccdArchiveFileName = "ccd.tar.gz"
indexTxtDateLayout = "060102150405Z"
stringDateFormat = "2006-01-02 15:04:05"
2021-10-21 06:01:03 -04:00
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 ( )
2021-10-21 06:01:03 -04:00
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 ( )
2022-04-27 06:42:59 -04:00
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 ( )
2021-10-21 06:01:03 -04:00
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 ( )
2022-01-20 11:42:36 -05:00
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"
2020-11-27 02:23:59 -05:00
)
2021-12-30 03:24:44 -05:00
var logLevels = map [ string ] log . Level {
2022-01-20 11:42:36 -05:00
"trace" : log . TraceLevel ,
2021-12-30 03:24:44 -05:00
"debug" : log . DebugLevel ,
"info" : log . InfoLevel ,
"warn" : log . WarnLevel ,
"error" : log . ErrorLevel ,
}
2022-01-20 11:42:36 -05:00
var logFormats = map [ string ] log . Formatter {
"text" : & log . TextFormatter { } ,
"json" : & log . JSONFormatter { } ,
}
2020-11-27 02:23:59 -05:00
var (
ovpnServerCertExpire = prometheus . NewGauge ( prometheus . GaugeOpts {
2021-07-21 18:06:34 -04:00
Name : "ovpn_server_cert_expire" ,
Help : "openvpn server certificate expire time in days" ,
} ,
2020-11-27 02:23:59 -05:00
)
ovpnServerCaCertExpire = prometheus . NewGauge ( prometheus . GaugeOpts {
2021-07-21 18:06:34 -04:00
Name : "ovpn_server_ca_cert_expire" ,
Help : "openvpn server CA certificate expire time in days" ,
} ,
2020-11-27 02:23:59 -05:00
)
ovpnClientsTotal = prometheus . NewGauge ( prometheus . GaugeOpts {
2021-07-21 18:06:34 -04:00
Name : "ovpn_clients_total" ,
Help : "total openvpn users" ,
} ,
2020-11-27 02:23:59 -05:00
)
ovpnClientsRevoked = prometheus . NewGauge ( prometheus . GaugeOpts {
2021-07-21 18:06:34 -04:00
Name : "ovpn_clients_revoked" ,
Help : "revoked openvpn users" ,
} ,
2020-11-27 02:23:59 -05:00
)
ovpnClientsExpired = prometheus . NewGauge ( prometheus . GaugeOpts {
Name : "ovpn_clients_expired" ,
Help : "expired openvpn users" ,
} ,
)
ovpnClientsConnected = prometheus . NewGauge ( prometheus . GaugeOpts {
2021-07-21 18:06:34 -04:00
Name : "ovpn_clients_connected" ,
Help : "connected openvpn users" ,
} ,
2020-11-27 02:23:59 -05:00
)
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 {
2021-07-21 18:06:34 -04:00
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" ,
} ,
2020-11-27 02:23:59 -05:00
[ ] 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 {
2021-07-21 18:06:34 -04:00
Name : "ovpn_client_bytes_received" ,
Help : "openvpn user bytes received" ,
} ,
2020-11-27 02:23:59 -05:00
[ ] string { "client" } ,
)
ovpnClientBytesSent = prometheus . NewGaugeVec ( prometheus . GaugeOpts {
2021-07-21 18:06:34 -04:00
Name : "ovpn_client_bytes_sent" ,
Help : "openvpn user bytes sent" ,
} ,
2020-11-27 02:23:59 -05:00
[ ] string { "client" } ,
)
2020-05-14 19:13:33 -04:00
)
2021-03-17 06:44:12 -04:00
type OvpnAdmin struct {
2021-07-21 18:06:34 -04:00
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
2022-01-20 11:42:36 -05:00
mgmtStatusTimeFormat string
2020-11-27 02:23:59 -05:00
}
2020-11-17 12:48:26 -05:00
type OpenvpnServer struct {
2021-07-21 18:06:34 -04:00
Host string
Port string
2021-02-20 07:48:41 -05:00
Protocol string
2020-11-17 12:48:26 -05:00
}
type openvpnClientConfig struct {
2021-07-21 18:06:34 -04:00
Hosts [ ] OpenvpnServer
CA string
Cert string
Key string
TLS string
2021-02-15 01:03:38 -05:00
PasswdAuth bool
2020-05-14 19:13:33 -04:00
}
2020-11-17 12:48:26 -05: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 {
2021-07-21 18:06:34 -04:00
Address string ` json:"Address" `
Mask string ` json:"Mask" `
Description string ` json:"Description" `
2020-10-29 06:50:19 -04:00
}
type Ccd struct {
2021-07-21 18:06:34 -04:00
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 {
2021-02-15 01:03:38 -05:00
CommonName string
RealAddress string
BytesReceived string
BytesSent string
ConnectedSince string
VirtualAddress string
LastRef string
2020-11-27 02:23:59 -05:00
ConnectedSinceFormatted string
LastRefFormatted string
2021-02-15 01:03:38 -05:00
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 ) {
2022-01-20 11:42:36 -05:00
log . Info ( r . RemoteAddr , r . RequestURI )
2020-11-27 02:23:59 -05:00
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 ) {
2022-01-20 11:42:36 -05:00
log . Info ( r . RemoteAddr , r . RequestURI )
2021-12-30 03:24:44 -05:00
_ = r . ParseForm ( )
2020-11-27 02:23:59 -05:00
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 ) {
2022-01-20 11:42:36 -05:00
log . Info ( r . RemoteAddr , r . RequestURI )
2020-11-27 02:23:59 -05:00
if oAdmin . role == "slave" {
2020-11-17 12:48:26 -05:00
http . Error ( w , ` { "status":"error"} ` , http . StatusLocked )
return
}
2021-12-30 03:24:44 -05:00
_ = r . ParseForm ( )
2021-02-15 01:03:38 -05:00
userCreated , userCreateStatus := oAdmin . userCreate ( r . FormValue ( "username" ) , r . FormValue ( "password" ) )
2020-10-29 06:50:19 -04:00
2021-07-21 18:06:34 -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 ) {
2022-01-20 11:42:36 -05:00
log . Info ( r . RemoteAddr , r . RequestURI )
2020-11-27 02:23:59 -05:00
if oAdmin . role == "slave" {
2020-11-17 12:48:26 -05:00
http . Error ( w , ` { "status":"error"} ` , http . StatusLocked )
return
}
2021-12-30 03:24:44 -05:00
_ = r . ParseForm ( )
2020-11-27 02:23:59 -05:00
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 ) {
2022-01-20 11:42:36 -05:00
log . Info ( r . RemoteAddr , r . RequestURI )
2020-11-27 02:23:59 -05:00
if oAdmin . role == "slave" {
2020-11-17 12:48:26 -05:00
http . Error ( w , ` { "status":"error"} ` , http . StatusLocked )
return
}
2021-12-30 03:24:44 -05:00
_ = r . ParseForm ( )
2020-11-27 02:23:59 -05:00
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 ) {
2022-01-20 11:42:36 -05:00
log . Info ( r . RemoteAddr , r . RequestURI )
2021-12-30 03:24:44 -05:00
_ = r . ParseForm ( )
2021-02-15 01:03:38 -05:00
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 {
2021-07-21 18:06:34 -04:00
http . Error ( w , ` { "status":"error"} ` , http . StatusNotImplemented )
2021-02-15 01:03:38 -05:00
}
}
2021-03-17 06:44:12 -04:00
func ( oAdmin * OvpnAdmin ) userShowConfigHandler ( w http . ResponseWriter , r * http . Request ) {
2022-01-20 11:42:36 -05:00
log . Info ( r . RemoteAddr , r . RequestURI )
2021-12-30 03:24:44 -05:00
_ = r . ParseForm ( )
2020-11-27 02:23:59 -05:00
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 ) {
2022-01-20 11:42:36 -05:00
log . Info ( r . RemoteAddr , r . RequestURI )
2021-12-30 03:24:44 -05:00
_ = r . ParseForm ( )
2021-07-21 18:06:34 -04:00
// 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 ) {
2022-01-20 11:42:36 -05:00
log . Info ( r . RemoteAddr , r . RequestURI )
2021-12-30 03:24:44 -05:00
_ = r . ParseForm ( )
2020-11-27 02:23:59 -05:00
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 ) {
2022-01-20 11:42:36 -05:00
log . Info ( r . RemoteAddr , r . RequestURI )
2020-11-27 02:23:59 -05:00
if oAdmin . role == "slave" {
2020-11-17 12:48:26 -05:00
http . Error ( w , ` { "status":"error"} ` , http . StatusLocked )
return
}
2021-07-21 18:06:34 -04:00
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 )
2021-07-21 18:06:34 -04:00
}
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 ) {
2022-01-20 11:42:36 -05:00
log . Info ( r . RemoteAddr , r . RequestURI )
2021-02-26 07:11:13 -05:00
enabledModules , enabledModulesErr := json . Marshal ( oAdmin . modules )
if enabledModulesErr != nil {
2021-12-30 03:24:44 -05:00
log . Errorln ( enabledModulesErr )
2021-02-26 07:11:13 -05:00
}
fmt . Fprintf ( w , ` { "status":"ok", "serverRole": "%s", "modules": %s } ` , oAdmin . role , string ( enabledModules ) )
2020-11-17 12:48:26 -05:00
}
2021-03-17 06:44:12 -04:00
func ( oAdmin * OvpnAdmin ) lastSyncTimeHandler ( w http . ResponseWriter , r * http . Request ) {
2022-01-20 11:42:36 -05:00
log . Debug ( r . RemoteAddr , r . RequestURI )
2020-11-27 02:23:59 -05:00
fmt . Fprint ( w , oAdmin . lastSyncTime )
2020-11-17 12:48:26 -05:00
}
2021-03-17 06:44:12 -04:00
func ( oAdmin * OvpnAdmin ) lastSuccessfulSyncTimeHandler ( w http . ResponseWriter , r * http . Request ) {
2022-01-20 11:42:36 -05:00
log . Debug ( r . RemoteAddr , r . RequestURI )
2020-11-27 02:23:59 -05:00
fmt . Fprint ( w , oAdmin . lastSuccessfulSyncTime )
2020-11-19 12:08:55 -05:00
}
2021-03-17 06:44:12 -04:00
func ( oAdmin * OvpnAdmin ) downloadCertsHandler ( w http . ResponseWriter , r * http . Request ) {
2022-01-20 11:42:36 -05:00
log . Info ( r . RemoteAddr , r . RequestURI )
2020-11-27 02:23:59 -05:00
if oAdmin . role == "slave" {
2020-11-17 12:48:26 -05:00
http . Error ( w , ` { "status":"error"} ` , http . StatusLocked )
return
}
2021-12-30 03:24:44 -05:00
_ = r . ParseForm ( )
2020-11-17 12:48:26 -05:00
token := r . Form . Get ( "token" )
2020-11-27 02:23:59 -05:00
if token != oAdmin . masterSyncToken {
2020-11-17 12:48:26 -05:00
http . Error ( w , ` { "status":"error"} ` , http . StatusForbidden )
return
}
archiveCerts ( )
2021-07-21 18:06:34 -04:00
w . Header ( ) . Set ( "Content-Disposition" , "attachment; filename=" + certsArchiveFileName )
http . ServeFile ( w , r , certsArchivePath )
2020-11-17 12:48:26 -05:00
}
2021-03-17 06:44:12 -04:00
func ( oAdmin * OvpnAdmin ) downloadCcdHandler ( w http . ResponseWriter , r * http . Request ) {
2022-01-20 11:42:36 -05:00
log . Info ( r . RemoteAddr , r . RequestURI )
2020-11-27 02:23:59 -05:00
if oAdmin . role == "slave" {
2020-11-17 12:48:26 -05:00
http . Error ( w , ` { "status":"error"} ` , http . StatusLocked )
return
}
2021-12-30 03:24:44 -05:00
_ = r . ParseForm ( )
2020-11-17 12:48:26 -05:00
token := r . Form . Get ( "token" )
2020-11-27 02:23:59 -05:00
if token != oAdmin . masterSyncToken {
2020-11-17 12:48:26 -05:00
http . Error ( w , ` { "status":"error"} ` , http . StatusForbidden )
return
}
archiveCcd ( )
2021-07-21 18:06:34 -04:00
w . Header ( ) . Set ( "Content-Disposition" , "attachment; filename=" + ccdArchiveFileName )
http . ServeFile ( w , r , ccdArchivePath )
2020-11-17 12:48:26 -05:00
}
2021-12-30 03:24:44 -05:00
var app OpenVPNPKI
2020-05-14 19:13:33 -04:00
func main ( ) {
2021-02-20 07:48:41 -05:00
kingpin . Version ( version )
kingpin . Parse ( )
2021-12-30 03:24:44 -05:00
log . SetLevel ( logLevels [ * logLevel ] )
2022-01-20 11:42:36 -05:00
log . SetFormatter ( logFormats [ * logFormat ] )
2021-12-30 03:24:44 -05:00
2022-01-20 11:42:36 -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 )
2022-01-20 11:42:36 -05:00
2020-11-27 02:23:59 -05:00
ovpnAdmin . lastSyncTime = "unknown"
ovpnAdmin . role = * serverRole
ovpnAdmin . lastSuccessfulSyncTime = "unknown"
ovpnAdmin . masterSyncToken = * masterSyncToken
ovpnAdmin . promRegistry = prometheus . NewRegistry ( )
2021-02-26 07:11:13 -05:00
ovpnAdmin . modules = [ ] string { }
2020-11-27 02:23:59 -05:00
2021-02-15 01:03:38 -05:00
ovpnAdmin . mgmtInterfaces = make ( map [ string ] string )
for _ , mgmtInterface := range * mgmtAddress {
2021-07-21 18:06:34 -04:00
parts := strings . SplitN ( mgmtInterface , "=" , 2 )
2021-02-15 01:03:38 -05:00
ovpnAdmin . mgmtInterfaces [ parts [ 0 ] ] = parts [ len ( parts ) - 1 ]
}
2022-01-20 11:42:36 -05:00
ovpnAdmin . mgmtSetTimeFormat ( )
2020-11-27 02:23:59 -05:00
ovpnAdmin . registerMetrics ( )
ovpnAdmin . setState ( )
go ovpnAdmin . updateState ( )
2020-11-17 12:48:26 -05:00
if * masterBasicAuthPassword != "" && * masterBasicAuthUser != "" {
2020-11-27 02:23:59 -05:00
ovpnAdmin . masterHostBasicAuth = true
} else {
ovpnAdmin . masterHostBasicAuth = false
2020-11-17 12:48:26 -05:00
}
2021-02-26 07:11:13 -05:00
ovpnAdmin . modules = append ( ovpnAdmin . modules , "core" )
if * authByPassword {
ovpnAdmin . modules = append ( ovpnAdmin . modules , "passwdAuth" )
}
if * ccdEnabled {
ovpnAdmin . modules = append ( ovpnAdmin . modules , "ccd" )
}
2020-11-27 02:23:59 -05:00
if ovpnAdmin . role == "slave" {
ovpnAdmin . syncDataFromMaster ( )
2021-07-21 18:06:34 -04:00
go ovpnAdmin . syncWithMaster ( )
2020-11-17 12:48:26 -05:00
}
2021-02-26 07:11:13 -05:00
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
2021-02-26 07:11:13 -05:00
http . Handle ( "/" , static )
http . HandleFunc ( "/api/server/settings" , ovpnAdmin . serverSettingsHandler )
2020-11-27 02:23:59 -05:00
http . HandleFunc ( "/api/users/list" , ovpnAdmin . userListHandler )
http . HandleFunc ( "/api/user/create" , ovpnAdmin . userCreateHandler )
2021-02-15 01:03:38 -05:00
http . HandleFunc ( "/api/user/change-password" , ovpnAdmin . userChangePasswordHandler )
2020-11-27 02:23:59 -05:00
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 )
2021-02-15 01:03:38 -05:00
http . HandleFunc ( downloadCcdApiUrl , ovpnAdmin . downloadCcdHandler )
2020-11-27 02:23:59 -05:00
http . Handle ( * metricsPath , promhttp . HandlerFor ( ovpnAdmin . promRegistry , promhttp . HandlerOpts { } ) )
http . HandleFunc ( "/ping" , func ( w http . ResponseWriter , r * http . Request ) {
fmt . Fprintf ( w , "pong" )
} )
2020-11-17 12:48:26 -05:00
2022-01-20 11:42:36 -05:00
log . Printf ( "Bind: http://%s:%s" , * listenHost , * listenPort )
2021-07-21 18:06:34 -04:00
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 {
2020-11-17 12:48:26 -05:00
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 ( ) {
2020-11-27 02:23:59 -05:00
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 ( ) {
2020-11-27 02:23:59 -05:00
oAdmin . activeClients = oAdmin . mgmtGetActiveClients ( )
oAdmin . clients = oAdmin . usersList ( )
2021-02-15 01:03:38 -05:00
ovpnServerCaCertExpire . Set ( float64 ( ( getOvpnCaCertExpireDate ( ) . Unix ( ) - time . Now ( ) . Unix ( ) ) / 3600 / 24 ) )
2020-11-27 02:23:59 -05:00
}
2021-03-17 06:44:12 -04:00
func ( oAdmin * OvpnAdmin ) updateState ( ) {
2020-11-27 02:23:59 -05:00
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 {
2020-11-17 12:48:26 -05:00
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" ) :
2021-07-21 18:06:34 -04:00
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" ) :
2021-07-21 18:06:34 -04:00
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-11-27 02:23:59 -05: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" :
2021-07-21 18:06:34 -04:00
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" :
2021-07-21 18:06:34 -04:00
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
}
2021-07-21 18:06:34 -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 {
2022-01-20 11:42:36 -05:00
log . Error ( "clientConfigTpl not found in templates box" )
2021-07-21 18:06:34 -04:00
}
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 ) {
2020-11-17 12:48:26 -05:00
var hosts [ ] OpenvpnServer
for _ , server := range * openvpnServer {
2021-07-21 18:06:34 -04:00
parts := strings . SplitN ( server , ":" , 3 )
2021-02-20 07:48:41 -05:00
hosts = append ( hosts , OpenvpnServer { Host : parts [ 0 ] , Port : parts [ 1 ] , Protocol : parts [ 2 ] } )
2020-11-17 12:48:26 -05:00
}
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
}
2022-01-20 11:42:36 -05: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 { }
2020-11-17 12:48:26 -05:00
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
2022-01-20 11:42:36 -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" )
}
2021-02-15 01:03:38 -05:00
conf . PasswdAuth = * authByPassword
2020-10-29 06:50:19 -04:00
2021-07-21 18:06:34 -04:00
t := oAdmin . getClientConfigTemplate ( )
2021-02-26 07:11:13 -05:00
2020-05-14 19:13:33 -04:00
var tmp bytes . Buffer
2020-11-27 02:23:59 -05:00
err := t . Execute ( & tmp , conf )
if err != nil {
2022-01-20 11:42:36 -05:00
log . Errorf ( "something goes wrong during rendering config for %s" , username )
log . Debugf ( "rendering config for %s failed with error %v" , username , err )
2020-11-27 02:23:59 -05:00
}
2020-10-29 06:50:19 -04:00
2020-11-17 12:48:26 -05:00
hosts = nil
2021-12-30 03:24:44 -05:00
2022-01-20 11:42:36 -05:00
log . Tracef ( "Rendered config for user %s: %+v" , username , tmp . String ( ) )
2021-12-30 03:24:44 -05:00
2022-01-20 11:42:36 -05:00
return fmt . Sprintf ( "%+v" , tmp . String ( ) )
2020-05-14 19:13:33 -04:00
}
2022-01-20 11:42:36 -05:00
log . Warnf ( "user \"%s\" not found" , username )
return fmt . Sprintf ( "user \"%s\" not found" , username )
2020-05-14 19:13:33 -04:00
}
2021-07-21 18:06:34 -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" )
2021-07-21 18:06:34 -04:00
}
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
2022-01-20 11:42:36 -05: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" ) :
2021-07-21 18:06:34 -04:00
ccd . ClientAddress = str [ 1 ]
2020-10-15 12:12:31 -04:00
case strings . HasPrefix ( str [ 0 ] , "push" ) :
2021-07-21 18:06:34 -04:00
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