main.go 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138
  1. package main
  2. import (
  3. "bufio"
  4. "bytes"
  5. "encoding/json"
  6. "fmt"
  7. "github.com/prometheus/client_golang/prometheus"
  8. "github.com/prometheus/client_golang/prometheus/promhttp"
  9. "gopkg.in/alecthomas/kingpin.v2"
  10. "log"
  11. "net"
  12. "net/http"
  13. "os"
  14. "regexp"
  15. "strconv"
  16. "strings"
  17. "text/template"
  18. "time"
  19. "github.com/gobuffalo/packr/v2"
  20. )
  21. const (
  22. usernameRegexp = `^([a-zA-Z0-9_.-@])+$`
  23. passwordRegexp = `^([a-zA-Z0-9_.-@])+$`
  24. passwordMinLength = 6
  25. downloadCertsApiUrl = "/api/data/certs/download"
  26. downloadCcdApiUrl = "/api/data/ccd/download"
  27. certsArchiveFileName = "certs.tar.gz"
  28. ccdArchiveFileName = "ccd.tar.gz"
  29. indexTxtDateLayout = "060102150405Z"
  30. stringDateFormat = "2006-01-02 15:04:05"
  31. ovpnStatusDateLayout = "2006-01-02 15:04:05"
  32. version = "1.6.0"
  33. )
  34. var (
  35. listenHost = kingpin.Flag("listen.host","host for openvpn-admin").Default("0.0.0.0").String()
  36. listenPort = kingpin.Flag("listen.port","port for openvpn-admin").Default("8080").String()
  37. serverRole = kingpin.Flag("role","server role master or slave").Default("master").HintOptions("master", "slave").String()
  38. masterHost = kingpin.Flag("master.host","url for master server").Default("http://127.0.0.1").String()
  39. masterBasicAuthUser = kingpin.Flag("master.basic-auth.user","user for basic auth on master server url").Default("").String()
  40. masterBasicAuthPassword = kingpin.Flag("master.basic-auth.password","password for basic auth on master server url").Default("").String()
  41. masterSyncFrequency = kingpin.Flag("master.sync-frequency", "master host data sync frequency in seconds.").Default("600").Int()
  42. masterSyncToken = kingpin.Flag("master.sync-token", "master host data sync security token").Default("VerySecureToken").PlaceHolder("TOKEN").String()
  43. openvpnNetwork = kingpin.Flag("ovpn.network","network for openvpn server").Default("172.16.100.0/24").String()
  44. openvpnServer = kingpin.Flag("ovpn.server","comma separated addresses for openvpn servers").Default("127.0.0.1:7777:tcp").PlaceHolder("HOST:PORT:PROTOCOL").Strings()
  45. mgmtAddress = kingpin.Flag("mgmt","comma separated (alias=address) for openvpn servers mgmt interfaces").Default("main=127.0.0.1:8989").Strings()
  46. metricsPath = kingpin.Flag("metrics.path", "URL path for surfacing collected metrics").Default("/metrics").String()
  47. easyrsaDirPath = kingpin.Flag("easyrsa.path", "path to easyrsa dir").Default("./easyrsa/").String()
  48. indexTxtPath = kingpin.Flag("easyrsa.index-path", "path to easyrsa index file.").Default("./easyrsa/pki/index.txt").String()
  49. ccdEnabled = kingpin.Flag("ccd", "Enable client-config-dir.").Default("false").Bool()
  50. ccdDir = kingpin.Flag("ccd.path", "path to client-config-dir").Default("./ccd").String()
  51. authByPassword = kingpin.Flag("auth.password", "Enable additional password authorization.").Default("false").Bool()
  52. authDatabase = kingpin.Flag("auth.db", "Database path fort password authorization.").Default("./easyrsa/pki/users.db").String()
  53. debug = kingpin.Flag("debug", "Enable debug mode.").Default("false").Bool()
  54. verbose = kingpin.Flag("verbose", "Enable verbose mode.").Default("false").Bool()
  55. certsArchivePath = "/tmp/" + certsArchiveFileName
  56. ccdArchivePath = "/tmp/" + ccdArchiveFileName
  57. )
  58. var (
  59. ovpnServerCertExpire = prometheus.NewGauge(prometheus.GaugeOpts{
  60. Name: "ovpn_server_cert_expire",
  61. Help: "openvpn server certificate expire time in days",
  62. },
  63. )
  64. ovpnServerCaCertExpire = prometheus.NewGauge(prometheus.GaugeOpts{
  65. Name: "ovpn_server_ca_cert_expire",
  66. Help: "openvpn server CA certificate expire time in days",
  67. },
  68. )
  69. ovpnClientsTotal = prometheus.NewGauge(prometheus.GaugeOpts{
  70. Name: "ovpn_clients_total",
  71. Help: "total openvpn users",
  72. },
  73. )
  74. ovpnClientsRevoked = prometheus.NewGauge(prometheus.GaugeOpts{
  75. Name: "ovpn_clients_revoked",
  76. Help: "revoked openvpn users",
  77. },
  78. )
  79. ovpnClientsExpired = prometheus.NewGauge(prometheus.GaugeOpts{
  80. Name: "ovpn_clients_expired",
  81. Help: "expired openvpn users",
  82. },
  83. )
  84. ovpnClientsConnected = prometheus.NewGauge(prometheus.GaugeOpts{
  85. Name: "ovpn_clients_connected",
  86. Help: "connected openvpn users",
  87. },
  88. )
  89. ovpnClientCertificateExpire = prometheus.NewGaugeVec(prometheus.GaugeOpts{
  90. Name: "ovpn_client_cert_expire",
  91. Help: "openvpn user certificate expire time in days",
  92. },
  93. []string{"client"},
  94. )
  95. ovpnClientConnectionInfo = prometheus.NewGaugeVec(prometheus.GaugeOpts{
  96. Name: "ovpn_client_connection_info",
  97. Help: "openvpn user connection info. ip - assigned address from ovpn network. value - last time when connection was refreshed in unix format",
  98. },
  99. []string{"client", "ip"},
  100. )
  101. ovpnClientConnectionFrom = prometheus.NewGaugeVec(prometheus.GaugeOpts{
  102. Name: "ovpn_client_connection_from",
  103. Help: "openvpn user connection info. ip - from which address connection was initialized. value - time when connection was initialized in unix format",
  104. },
  105. []string{"client", "ip"},
  106. )
  107. ovpnClientBytesReceived = prometheus.NewGaugeVec(prometheus.GaugeOpts{
  108. Name: "ovpn_client_bytes_received",
  109. Help: "openvpn user bytes received",
  110. },
  111. []string{"client"},
  112. )
  113. ovpnClientBytesSent = prometheus.NewGaugeVec(prometheus.GaugeOpts{
  114. Name: "ovpn_client_bytes_sent",
  115. Help: "openvpn user bytes sent",
  116. },
  117. []string{"client"},
  118. )
  119. )
  120. type OpenvpnAdmin struct {
  121. role string
  122. lastSyncTime string
  123. lastSuccessfulSyncTime string
  124. masterHostBasicAuth bool
  125. masterSyncToken string
  126. clients []OpenvpnClient
  127. activeClients []clientStatus
  128. promRegistry *prometheus.Registry
  129. mgmtInterfaces map[string]string
  130. templates *packr.Box
  131. modules []string
  132. }
  133. type OpenvpnServer struct {
  134. Host string
  135. Port string
  136. Protocol string
  137. }
  138. type openvpnClientConfig struct {
  139. Hosts []OpenvpnServer
  140. CA string
  141. Cert string
  142. Key string
  143. TLS string
  144. PasswdAuth bool
  145. }
  146. type OpenvpnClient struct {
  147. Identity string `json:"Identity"`
  148. AccountStatus string `json:"AccountStatus"`
  149. ExpirationDate string `json:"ExpirationDate"`
  150. RevocationDate string `json:"RevocationDate"`
  151. ConnectionStatus string `json:"ConnectionStatus"`
  152. ConnectionServer string `json:"ConnectionServer"`
  153. }
  154. type ccdRoute struct {
  155. Address string `json:"Address"`
  156. Mask string `json:"Mask"`
  157. Description string `json:"Description"`
  158. }
  159. type Ccd struct {
  160. User string `json:"User"`
  161. ClientAddress string `json:"ClientAddress"`
  162. CustomRoutes []ccdRoute `json:"CustomRoutes"`
  163. }
  164. type indexTxtLine struct {
  165. Flag string
  166. ExpirationDate string
  167. RevocationDate string
  168. SerialNumber string
  169. Filename string
  170. DistinguishedName string
  171. Identity string
  172. }
  173. type clientStatus struct {
  174. CommonName string
  175. RealAddress string
  176. BytesReceived string
  177. BytesSent string
  178. ConnectedSince string
  179. VirtualAddress string
  180. LastRef string
  181. ConnectedSinceFormatted string
  182. LastRefFormatted string
  183. ConnectedTo string
  184. }
  185. func (oAdmin *OpenvpnAdmin) userListHandler(w http.ResponseWriter, r *http.Request) {
  186. usersList, _ := json.Marshal(oAdmin.clients)
  187. fmt.Fprintf(w, "%s", usersList)
  188. }
  189. func (oAdmin *OpenvpnAdmin) userStatisticHandler(w http.ResponseWriter, r *http.Request) {
  190. r.ParseForm()
  191. userStatistic, _ := json.Marshal(oAdmin.getUserStatistic(r.FormValue("username")))
  192. fmt.Fprintf(w, "%s", userStatistic)
  193. }
  194. func (oAdmin *OpenvpnAdmin) userCreateHandler(w http.ResponseWriter, r *http.Request) {
  195. if oAdmin.role == "slave" {
  196. http.Error(w, `{"status":"error"}`, http.StatusLocked)
  197. return
  198. }
  199. r.ParseForm()
  200. userCreated, userCreateStatus := oAdmin.userCreate(r.FormValue("username"), r.FormValue("password"))
  201. if userCreated {
  202. w.WriteHeader(http.StatusOK)
  203. fmt.Fprintf(w, userCreateStatus)
  204. return
  205. } else {
  206. http.Error(w, userCreateStatus, http.StatusUnprocessableEntity)
  207. }
  208. }
  209. func (oAdmin *OpenvpnAdmin) userRevokeHandler(w http.ResponseWriter, r *http.Request) {
  210. if oAdmin.role == "slave" {
  211. http.Error(w, `{"status":"error"}`, http.StatusLocked)
  212. return
  213. }
  214. r.ParseForm()
  215. fmt.Fprintf(w, "%s", oAdmin.userRevoke(r.FormValue("username")))
  216. }
  217. func (oAdmin *OpenvpnAdmin) userUnrevokeHandler(w http.ResponseWriter, r *http.Request) {
  218. if oAdmin.role == "slave" {
  219. http.Error(w, `{"status":"error"}`, http.StatusLocked)
  220. return
  221. }
  222. r.ParseForm()
  223. fmt.Fprintf(w, "%s", oAdmin.userUnrevoke(r.FormValue("username")))
  224. }
  225. func (oAdmin *OpenvpnAdmin) userChangePasswordHandler(w http.ResponseWriter, r *http.Request) {
  226. r.ParseForm()
  227. if *authByPassword {
  228. passwordChanged, passwordChangeMessage := oAdmin.userChangePassword(r.FormValue("username"), r.FormValue("password"))
  229. if passwordChanged {
  230. w.WriteHeader(http.StatusOK)
  231. fmt.Fprintf(w, `{"status":"ok", "message": "%s"}`, passwordChangeMessage)
  232. return
  233. } else {
  234. w.WriteHeader(http.StatusInternalServerError)
  235. fmt.Fprintf(w, `{"status":"error", "message": "%s"}`, passwordChangeMessage)
  236. return
  237. }
  238. } else {
  239. http.Error(w, `{"status":"error"}`, http.StatusNotImplemented )
  240. }
  241. }
  242. func (oAdmin *OpenvpnAdmin) userShowConfigHandler(w http.ResponseWriter, r *http.Request) {
  243. r.ParseForm()
  244. fmt.Fprintf(w, "%s", oAdmin.renderClientConfig(r.FormValue("username")))
  245. }
  246. func (oAdmin *OpenvpnAdmin) userDisconnectHandler(w http.ResponseWriter, r *http.Request) {
  247. r.ParseForm()
  248. // fmt.Fprintf(w, "%s", userDisconnect(r.FormValue("username")))
  249. fmt.Fprintf(w, "%s", r.FormValue("username"))
  250. }
  251. func (oAdmin *OpenvpnAdmin) userShowCcdHandler(w http.ResponseWriter, r *http.Request) {
  252. r.ParseForm()
  253. ccd, _ := json.Marshal(oAdmin.getCcd(r.FormValue("username")))
  254. fmt.Fprintf(w, "%s", ccd)
  255. }
  256. func (oAdmin *OpenvpnAdmin) userApplyCcdHandler(w http.ResponseWriter, r *http.Request) {
  257. if oAdmin.role == "slave" {
  258. http.Error(w, `{"status":"error"}`, http.StatusLocked)
  259. return
  260. }
  261. var ccd Ccd
  262. if r.Body == nil {
  263. http.Error(w, "Please send a request body", http.StatusBadRequest)
  264. return
  265. }
  266. err := json.NewDecoder(r.Body).Decode(&ccd)
  267. if err != nil {
  268. log.Println(err)
  269. }
  270. ccdApplied, applyStatus := oAdmin.modifyCcd(ccd)
  271. if ccdApplied {
  272. w.WriteHeader(http.StatusOK)
  273. fmt.Fprintf(w, applyStatus)
  274. return
  275. } else {
  276. http.Error(w, applyStatus, http.StatusUnprocessableEntity)
  277. }
  278. }
  279. func (oAdmin *OpenvpnAdmin) serverSettingsHandler(w http.ResponseWriter, r *http.Request) {
  280. enabledModules, enabledModulesErr := json.Marshal(oAdmin.modules)
  281. if enabledModulesErr != nil {
  282. log.Printf("ERROR: %s\n",enabledModulesErr)
  283. }
  284. fmt.Fprintf(w, `{"status":"ok", "serverRole": "%s", "modules": %s }`, oAdmin.role, string(enabledModules))
  285. }
  286. func (oAdmin *OpenvpnAdmin) lastSyncTimeHandler(w http.ResponseWriter, r *http.Request) {
  287. fmt.Fprint(w, oAdmin.lastSyncTime)
  288. }
  289. func (oAdmin *OpenvpnAdmin) lastSuccessfulSyncTimeHandler(w http.ResponseWriter, r *http.Request) {
  290. fmt.Fprint(w, oAdmin.lastSuccessfulSyncTime)
  291. }
  292. func (oAdmin *OpenvpnAdmin) downloadCertsHandler(w http.ResponseWriter, r *http.Request) {
  293. if oAdmin.role == "slave" {
  294. http.Error(w, `{"status":"error"}`, http.StatusLocked)
  295. return
  296. }
  297. r.ParseForm()
  298. token := r.Form.Get("token")
  299. if token != oAdmin.masterSyncToken {
  300. http.Error(w, `{"status":"error"}`, http.StatusForbidden)
  301. return
  302. }
  303. archiveCerts()
  304. w.Header().Set("Content-Disposition", "attachment; filename=" + certsArchiveFileName)
  305. http.ServeFile(w,r, certsArchivePath)
  306. }
  307. func (oAdmin *OpenvpnAdmin) downloadCcdHandler(w http.ResponseWriter, r *http.Request) {
  308. if oAdmin.role == "slave" {
  309. http.Error(w, `{"status":"error"}`, http.StatusLocked)
  310. return
  311. }
  312. r.ParseForm()
  313. token := r.Form.Get("token")
  314. if token != oAdmin.masterSyncToken {
  315. http.Error(w, `{"status":"error"}`, http.StatusForbidden)
  316. return
  317. }
  318. archiveCcd()
  319. w.Header().Set("Content-Disposition", "attachment; filename=" + ccdArchiveFileName)
  320. http.ServeFile(w,r, ccdArchivePath)
  321. }
  322. func main() {
  323. kingpin.Version(version)
  324. kingpin.Parse()
  325. ovpnAdmin := new(OpenvpnAdmin)
  326. ovpnAdmin.lastSyncTime = "unknown"
  327. ovpnAdmin.role = *serverRole
  328. ovpnAdmin.lastSuccessfulSyncTime = "unknown"
  329. ovpnAdmin.masterSyncToken = *masterSyncToken
  330. ovpnAdmin.promRegistry = prometheus.NewRegistry()
  331. ovpnAdmin.modules = []string{}
  332. ovpnAdmin.mgmtInterfaces = make(map[string]string)
  333. for _, mgmtInterface := range *mgmtAddress {
  334. parts := strings.SplitN(mgmtInterface, "=",2)
  335. ovpnAdmin.mgmtInterfaces[parts[0]] = parts[len(parts)-1]
  336. }
  337. ovpnAdmin.registerMetrics()
  338. ovpnAdmin.setState()
  339. go ovpnAdmin.updateState()
  340. if *masterBasicAuthPassword != "" && *masterBasicAuthUser != "" {
  341. ovpnAdmin.masterHostBasicAuth = true
  342. } else {
  343. ovpnAdmin.masterHostBasicAuth = false
  344. }
  345. ovpnAdmin.modules = append(ovpnAdmin.modules, "core")
  346. if *authByPassword {
  347. ovpnAdmin.modules = append(ovpnAdmin.modules, "passwdAuth")
  348. }
  349. if *ccdEnabled {
  350. ovpnAdmin.modules = append(ovpnAdmin.modules, "ccd")
  351. }
  352. if ovpnAdmin.role == "slave" {
  353. ovpnAdmin.syncDataFromMaster()
  354. go ovpnAdmin.syncWithMaster()
  355. }
  356. ovpnAdmin.templates = packr.New("template", "./templates")
  357. staticBox := packr.New("static", "./frontend/static")
  358. static := CacheControlWrapper(http.FileServer(staticBox))
  359. http.Handle("/", static)
  360. http.HandleFunc("/api/server/settings", ovpnAdmin.serverSettingsHandler)
  361. http.HandleFunc("/api/users/list", ovpnAdmin.userListHandler)
  362. http.HandleFunc("/api/user/create", ovpnAdmin.userCreateHandler)
  363. http.HandleFunc("/api/user/change-password", ovpnAdmin.userChangePasswordHandler)
  364. http.HandleFunc("/api/user/revoke", ovpnAdmin.userRevokeHandler)
  365. http.HandleFunc("/api/user/unrevoke", ovpnAdmin.userUnrevokeHandler)
  366. http.HandleFunc("/api/user/config/show", ovpnAdmin.userShowConfigHandler)
  367. http.HandleFunc("/api/user/disconnect", ovpnAdmin.userDisconnectHandler)
  368. http.HandleFunc("/api/user/statistic", ovpnAdmin.userStatisticHandler)
  369. http.HandleFunc("/api/user/ccd", ovpnAdmin.userShowCcdHandler)
  370. http.HandleFunc("/api/user/ccd/apply", ovpnAdmin.userApplyCcdHandler)
  371. http.HandleFunc("/api/sync/last/try", ovpnAdmin.lastSyncTimeHandler)
  372. http.HandleFunc("/api/sync/last/successful", ovpnAdmin.lastSuccessfulSyncTimeHandler)
  373. http.HandleFunc(downloadCertsApiUrl, ovpnAdmin.downloadCertsHandler)
  374. http.HandleFunc(downloadCcdApiUrl, ovpnAdmin.downloadCcdHandler)
  375. http.Handle(*metricsPath, promhttp.HandlerFor(ovpnAdmin.promRegistry, promhttp.HandlerOpts{}))
  376. http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
  377. fmt.Fprintf(w, "pong")
  378. })
  379. fmt.Println("Bind: http://" + *listenHost + ":" + *listenPort)
  380. log.Fatal(http.ListenAndServe(*listenHost + ":" + *listenPort, nil))
  381. }
  382. func CacheControlWrapper(h http.Handler) http.Handler {
  383. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  384. w.Header().Set("Cache-Control", "max-age=2592000") // 30 days
  385. h.ServeHTTP(w, r)
  386. })
  387. }
  388. func (oAdmin *OpenvpnAdmin) registerMetrics() {
  389. oAdmin.promRegistry.MustRegister(ovpnServerCertExpire)
  390. oAdmin.promRegistry.MustRegister(ovpnServerCaCertExpire)
  391. oAdmin.promRegistry.MustRegister(ovpnClientsTotal)
  392. oAdmin.promRegistry.MustRegister(ovpnClientsRevoked)
  393. oAdmin.promRegistry.MustRegister(ovpnClientsConnected)
  394. oAdmin.promRegistry.MustRegister(ovpnClientsExpired)
  395. oAdmin.promRegistry.MustRegister(ovpnClientCertificateExpire)
  396. oAdmin.promRegistry.MustRegister(ovpnClientConnectionInfo)
  397. oAdmin.promRegistry.MustRegister(ovpnClientConnectionFrom)
  398. oAdmin.promRegistry.MustRegister(ovpnClientBytesReceived)
  399. oAdmin.promRegistry.MustRegister(ovpnClientBytesSent)
  400. }
  401. func (oAdmin *OpenvpnAdmin) setState() {
  402. oAdmin.activeClients = oAdmin.mgmtGetActiveClients()
  403. oAdmin.clients = oAdmin.usersList()
  404. ovpnServerCaCertExpire.Set(float64((getOvpnCaCertExpireDate().Unix() - time.Now().Unix()) / 3600 / 24))
  405. }
  406. func (oAdmin *OpenvpnAdmin) updateState() {
  407. for {
  408. time.Sleep(time.Duration(28) * time.Second)
  409. ovpnClientBytesSent.Reset()
  410. ovpnClientBytesReceived.Reset()
  411. ovpnClientConnectionFrom.Reset()
  412. ovpnClientConnectionInfo.Reset()
  413. go oAdmin.setState()
  414. }
  415. }
  416. func indexTxtParser(txt string) []indexTxtLine {
  417. var indexTxt []indexTxtLine
  418. txtLinesArray := strings.Split(txt, "\n")
  419. for _, v := range txtLinesArray {
  420. str := strings.Fields(v)
  421. if len(str) > 0 {
  422. switch {
  423. // case strings.HasPrefix(str[0], "E"):
  424. case strings.HasPrefix(str[0], "V"):
  425. 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:]})
  426. case strings.HasPrefix(str[0], "R"):
  427. 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:]})
  428. }
  429. }
  430. }
  431. return indexTxt
  432. }
  433. func renderIndexTxt(data []indexTxtLine) string {
  434. indexTxt := ""
  435. for _, line := range data {
  436. switch {
  437. case line.Flag == "V":
  438. indexTxt += fmt.Sprintf("%s\t%s\t\t%s\t%s\t%s\n", line.Flag, line.ExpirationDate, line.SerialNumber, line.Filename, line.DistinguishedName)
  439. case line.Flag == "R":
  440. 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)
  441. // case line.flag == "E":
  442. }
  443. }
  444. return indexTxt
  445. }
  446. func (oAdmin *OpenvpnAdmin) renderClientConfig(username string) string {
  447. if checkUserExist(username) {
  448. var hosts []OpenvpnServer
  449. for _, server := range *openvpnServer {
  450. parts := strings.SplitN(server, ":",3)
  451. hosts = append(hosts, OpenvpnServer{Host: parts[0], Port: parts[1], Protocol: parts[2]})
  452. }
  453. conf := openvpnClientConfig{}
  454. conf.Hosts = hosts
  455. conf.CA = fRead(*easyrsaDirPath + "/pki/ca.crt")
  456. conf.Cert = fRead(*easyrsaDirPath + "/pki/issued/" + username + ".crt")
  457. conf.Key = fRead(*easyrsaDirPath + "/pki/private/" + username + ".key")
  458. conf.TLS = fRead(*easyrsaDirPath + "/pki/ta.key")
  459. conf.PasswdAuth = *authByPassword
  460. clientConfigTpl, clientConfigTplErr := oAdmin.templates.FindString("client.conf.tpl")
  461. if clientConfigTplErr != nil {
  462. log.Println("ERROR: clientConfigTpl not found in templates box")
  463. }
  464. t := template.New(clientConfigTpl)
  465. var tmp bytes.Buffer
  466. err := t.Execute(&tmp, conf)
  467. if err != nil {
  468. log.Printf("WARNING: something goes wrong during rendering config for %s", username )
  469. }
  470. hosts = nil
  471. fmt.Printf("%+v\n", tmp.String())
  472. return fmt.Sprintf("%+v\n", tmp.String())
  473. }
  474. fmt.Printf("User \"%s\" not found", username)
  475. return fmt.Sprintf("User \"%s\" not found", username)
  476. }
  477. func (oAdmin *OpenvpnAdmin) parseCcd(username string) Ccd {
  478. ccd := Ccd{}
  479. ccd.User = username
  480. ccd.ClientAddress = "dynamic"
  481. ccd.CustomRoutes = []ccdRoute{}
  482. txtLinesArray := strings.Split(fRead(*ccdDir + "/" + username), "\n")
  483. for _, v := range txtLinesArray {
  484. str := strings.Fields(v)
  485. if len(str) > 0 {
  486. switch {
  487. case strings.HasPrefix(str[0], "ifconfig-push"):
  488. ccd.ClientAddress = str[1]
  489. case strings.HasPrefix(str[0], "push"):
  490. ccd.CustomRoutes = append(ccd.CustomRoutes, ccdRoute{Address: strings.Trim(str[2], "\""), Mask: strings.Trim(str[3], "\""), Description: strings.Trim(strings.Join(str[4:], ""), "#")})
  491. }
  492. }
  493. }
  494. return ccd
  495. }
  496. func (oAdmin *OpenvpnAdmin) modifyCcd(ccd Ccd) (bool, string) {
  497. ccdErr := "something goes wrong"
  498. if fCreate(*ccdDir + "/" + ccd.User) {
  499. ccdValid, ccdErr := validateCcd(ccd)
  500. if ccdErr != "" {
  501. return false, ccdErr
  502. }
  503. if ccdValid {
  504. ccdTpl, ccdTplErr := oAdmin.templates.FindString("ccd.tpl")
  505. if ccdTplErr != nil {
  506. ccdErr = "ccdTpl not found in templates box"
  507. log.Printf("ERROR: %s\n",ccdErr)
  508. return false, ccdErr
  509. }
  510. t := template.New(ccdTpl)
  511. var tmp bytes.Buffer
  512. tplErr := t.Execute(&tmp, ccd)
  513. if tplErr != nil {
  514. log.Println(tplErr)
  515. }
  516. fWrite(*ccdDir + "/" + ccd.User, tmp.String())
  517. return true, "ccd updated successfully"
  518. }
  519. }
  520. return false, ccdErr
  521. }
  522. func validateCcd(ccd Ccd) (bool, string) {
  523. ccdErr := ""
  524. if ccd.ClientAddress != "dynamic" {
  525. _, ovpnNet, err := net.ParseCIDR(*openvpnNetwork)
  526. if err != nil {
  527. log.Println(err)
  528. }
  529. if ! checkStaticAddressIsFree(ccd.ClientAddress, ccd.User) {
  530. ccdErr = fmt.Sprintf("ClientAddress \"%s\" already assigned to another user", ccd.ClientAddress)
  531. if *debug {
  532. log.Printf("ERROR: Modify ccd for user %s: %s", ccd.User, ccdErr)
  533. }
  534. return false, ccdErr
  535. }
  536. if net.ParseIP(ccd.ClientAddress) == nil {
  537. ccdErr = fmt.Sprintf("ClientAddress \"%s\" not a valid IP address", ccd.ClientAddress)
  538. if *debug {
  539. log.Printf("ERROR: Modify ccd for user %s: %s", ccd.User, ccdErr)
  540. }
  541. return false, ccdErr
  542. }
  543. if ! ovpnNet.Contains(net.ParseIP(ccd.ClientAddress)) {
  544. ccdErr = fmt.Sprintf("ClientAddress \"%s\" not belongs to openvpn server network", ccd.ClientAddress)
  545. if *debug {
  546. log.Printf("ERROR: Modify ccd for user %s: %s", ccd.User, ccdErr)
  547. }
  548. return false, ccdErr
  549. }
  550. }
  551. for _, route := range ccd.CustomRoutes {
  552. if net.ParseIP(route.Address) == nil {
  553. ccdErr = fmt.Sprintf("CustomRoute.Address \"%s\" must be a valid IP address", route.Address)
  554. if *debug {
  555. log.Printf("ERROR: Modify ccd for user %s: %s", ccd.User, ccdErr)
  556. }
  557. return false, ccdErr
  558. }
  559. if net.ParseIP(route.Mask) == nil {
  560. ccdErr = fmt.Sprintf("CustomRoute.Mask \"%s\" must be a valid IP address", route.Mask)
  561. if *debug {
  562. log.Printf("ERROR: Modify ccd for user %s: %s", ccd.User, ccdErr)
  563. }
  564. return false, ccdErr
  565. }
  566. }
  567. return true, ccdErr
  568. }
  569. func (oAdmin *OpenvpnAdmin) getCcd(username string) Ccd {
  570. ccd := Ccd{}
  571. ccd.User = username
  572. ccd.ClientAddress = "dynamic"
  573. ccd.CustomRoutes = []ccdRoute{}
  574. if fCreate(*ccdDir + "/" + username) {
  575. ccd = oAdmin.parseCcd(username)
  576. }
  577. return ccd
  578. }
  579. func checkStaticAddressIsFree(staticAddress string, username string) bool {
  580. o := runBash(fmt.Sprintf("grep -rl %s %s | grep -vx %s/%s | wc -l", staticAddress, *ccdDir, *ccdDir, username))
  581. if strings.TrimSpace(o) == "0" {
  582. return true
  583. }
  584. return false
  585. }
  586. func validateUsername(username string) bool {
  587. var validUsername = regexp.MustCompile(usernameRegexp)
  588. return validUsername.MatchString(username)
  589. }
  590. func validatePassword(password string) bool {
  591. if len(password) < passwordMinLength {
  592. return false
  593. } else {
  594. return true
  595. }
  596. }
  597. func checkUserExist(username string) bool {
  598. for _, u := range indexTxtParser(fRead(*indexTxtPath)) {
  599. if u.DistinguishedName == ("/CN=" + username) {
  600. return true
  601. }
  602. }
  603. return false
  604. }
  605. func (oAdmin *OpenvpnAdmin) usersList() []OpenvpnClient {
  606. var users []OpenvpnClient
  607. totalCerts := 0
  608. validCerts := 0
  609. revokedCerts := 0
  610. expiredCerts := 0
  611. connectedUsers := 0
  612. apochNow := time.Now().Unix()
  613. for _, line := range indexTxtParser(fRead(*indexTxtPath)) {
  614. if line.Identity != "server" {
  615. totalCerts += 1
  616. ovpnClient := OpenvpnClient{Identity: line.Identity, ExpirationDate: parseDateToString(indexTxtDateLayout, line.ExpirationDate, stringDateFormat)}
  617. switch {
  618. case line.Flag == "V":
  619. ovpnClient.AccountStatus = "Active"
  620. ovpnClientCertificateExpire.WithLabelValues(line.Identity).Set(float64((parseDateToUnix(indexTxtDateLayout, line.ExpirationDate) - apochNow) / 3600 / 24))
  621. validCerts += 1
  622. case line.Flag == "R":
  623. ovpnClient.AccountStatus = "Revoked"
  624. ovpnClient.RevocationDate = parseDateToString(indexTxtDateLayout, line.RevocationDate, stringDateFormat)
  625. ovpnClientCertificateExpire.WithLabelValues(line.Identity).Set(float64((parseDateToUnix(indexTxtDateLayout, line.ExpirationDate) - apochNow) / 3600 / 24))
  626. revokedCerts += 1
  627. case line.Flag == "E":
  628. ovpnClient.AccountStatus = "Expired"
  629. ovpnClientCertificateExpire.WithLabelValues(line.Identity).Set(float64((parseDateToUnix(indexTxtDateLayout, line.ExpirationDate) - apochNow) / 3600 / 24))
  630. expiredCerts += 1
  631. }
  632. ovpnClient.ConnectionServer = ""
  633. userConnected, userConnectedTo := isUserConnected(line.Identity, oAdmin.activeClients)
  634. if userConnected {
  635. ovpnClient.ConnectionStatus = "Connected"
  636. ovpnClient.ConnectionServer = userConnectedTo
  637. connectedUsers += 1
  638. }
  639. users = append(users, ovpnClient)
  640. } else {
  641. ovpnServerCertExpire.Set(float64((parseDateToUnix(indexTxtDateLayout, line.ExpirationDate) - apochNow) / 3600 / 24))
  642. }
  643. }
  644. otherCerts := totalCerts - validCerts - revokedCerts - expiredCerts
  645. if otherCerts != 0 {
  646. log.Printf("WARNING: there are %d otherCerts", otherCerts)
  647. }
  648. ovpnClientsTotal.Set(float64(totalCerts))
  649. ovpnClientsRevoked.Set(float64(revokedCerts))
  650. ovpnClientsExpired.Set(float64(expiredCerts))
  651. ovpnClientsConnected.Set(float64(connectedUsers))
  652. return users
  653. }
  654. func (oAdmin *OpenvpnAdmin) userCreate(username, password string) (bool, string) {
  655. ucErr := fmt.Sprintf("User \"%s\" created", username)
  656. if checkUserExist(username) {
  657. ucErr = fmt.Sprintf("User \"%s\" already exists\n", username)
  658. if *debug {
  659. log.Printf("ERROR: userCreate: %s", ucErr)
  660. }
  661. return false, ucErr
  662. }
  663. if ! validateUsername(username) {
  664. ucErr = fmt.Sprintf("Username \"%s\" incorrect, you can use only %s\n", username, usernameRegexp)
  665. if *debug {
  666. log.Printf("ERROR: userCreate: %s", ucErr)
  667. }
  668. return false, ucErr
  669. }
  670. if ! validatePassword(password) {
  671. ucErr = fmt.Sprintf("Password too short, password length must be greater or equal %d", passwordMinLength)
  672. if *debug {
  673. log.Printf("ERROR: userCreate: %s\n", ucErr)
  674. }
  675. return false, ucErr
  676. }
  677. o := runBash(fmt.Sprintf("date +%%Y-%%m-%%d\\ %%H:%%M:%%S && cd %s && easyrsa build-client-full %s nopass", *easyrsaDirPath, username))
  678. fmt.Println(o)
  679. if *authByPassword {
  680. o = runBash(fmt.Sprintf("openvpn-user create --db.path %s --user %s --password %s", *authDatabase, username, password))
  681. fmt.Println(o)
  682. }
  683. if *verbose {
  684. log.Printf("INFO: user created: %s", username)
  685. }
  686. oAdmin.clients = oAdmin.usersList()
  687. return true, ucErr
  688. }
  689. func (oAdmin *OpenvpnAdmin) userChangePassword(username, password string) (bool, string) {
  690. if checkUserExist(username) {
  691. o := runBash(fmt.Sprintf("openvpn-user check --db.path %s --user %s | grep %s | wc -l", *authDatabase, username, username))
  692. fmt.Println(o)
  693. if ! validatePassword(password) {
  694. ucpErr := fmt.Sprintf("Password for too short, password length must be greater or equal %d", passwordMinLength)
  695. if *debug {
  696. log.Printf("ERROR: userChangePassword: %s\n", ucpErr)
  697. }
  698. return false, ucpErr
  699. }
  700. if strings.TrimSpace(o) == "0" {
  701. fmt.Println("Creating user in users.db")
  702. o = runBash(fmt.Sprintf("openvpn-user create --db.path %s --user %s --password %s", *authDatabase, username, password))
  703. fmt.Println(o)
  704. }
  705. o = runBash(fmt.Sprintf("openvpn-user change-password --db.path %s --user %s --password %s", *authDatabase, username, password))
  706. fmt.Println(o)
  707. if *verbose {
  708. log.Printf("INFO: password for user %s was changed", username)
  709. }
  710. return true, "Password changed"
  711. }
  712. return false, "User does not exist"
  713. }
  714. func (oAdmin *OpenvpnAdmin) getUserStatistic(username string) clientStatus {
  715. for _, u := range oAdmin.activeClients {
  716. if u.CommonName == username {
  717. return u
  718. }
  719. }
  720. return clientStatus{}
  721. }
  722. func (oAdmin *OpenvpnAdmin) userRevoke(username string) string {
  723. if checkUserExist(username) {
  724. // check certificate valid flag 'V'
  725. o := runBash(fmt.Sprintf("date +%%Y-%%m-%%d\\ %%H:%%M:%%S && cd %s && echo yes | easyrsa revoke %s && easyrsa gen-crl", *easyrsaDirPath, username))
  726. if *authByPassword {
  727. o = runBash(fmt.Sprintf("openvpn-user revoke --db-path %s --user %s", *authDatabase, username))
  728. //fmt.Println(o)
  729. }
  730. crlFix()
  731. oAdmin.clients = oAdmin.usersList()
  732. return fmt.Sprintln(o)
  733. }
  734. fmt.Printf("User \"%s\" not found", username)
  735. return fmt.Sprintf("User \"%s\" not found", username)
  736. }
  737. func (oAdmin *OpenvpnAdmin) userUnrevoke(username string) string {
  738. if checkUserExist(username) {
  739. // check certificate revoked flag 'R'
  740. usersFromIndexTxt := indexTxtParser(fRead(*indexTxtPath))
  741. for i := range usersFromIndexTxt {
  742. if usersFromIndexTxt[i].DistinguishedName == ("/CN=" + username) {
  743. if usersFromIndexTxt[i].Flag == "R" {
  744. usersFromIndexTxt[i].Flag = "V"
  745. usersFromIndexTxt[i].RevocationDate = ""
  746. o := runBash(fmt.Sprintf("cd %s && cp pki/revoked/certs_by_serial/%s.crt pki/issued/%s.crt", *easyrsaDirPath, usersFromIndexTxt[i].SerialNumber, username))
  747. //fmt.Println(o)
  748. o = runBash(fmt.Sprintf("cd %s && cp pki/revoked/certs_by_serial/%s.crt pki/certs_by_serial/%s.pem", *easyrsaDirPath, usersFromIndexTxt[i].SerialNumber, usersFromIndexTxt[i].SerialNumber))
  749. //fmt.Println(o)
  750. o = runBash(fmt.Sprintf("cd %s && cp pki/revoked/private_by_serial/%s.key pki/private/%s.key", *easyrsaDirPath, usersFromIndexTxt[i].SerialNumber, username))
  751. //fmt.Println(o)
  752. o = runBash(fmt.Sprintf("cd %s && cp pki/revoked/reqs_by_serial/%s.req pki/reqs/%s.req", *easyrsaDirPath, usersFromIndexTxt[i].SerialNumber, username))
  753. //fmt.Println(o)
  754. fWrite(*indexTxtPath, renderIndexTxt(usersFromIndexTxt))
  755. //fmt.Print(renderIndexTxt(usersFromIndexTxt))
  756. o = runBash(fmt.Sprintf("cd %s && easyrsa gen-crl", *easyrsaDirPath))
  757. //fmt.Println(o)
  758. if *authByPassword {
  759. o = runBash(fmt.Sprintf("openvpn-user restore --db-path %s --user %s", *authDatabase, username))
  760. //fmt.Println(o)
  761. }
  762. crlFix()
  763. o = ""
  764. fmt.Println(o)
  765. break
  766. }
  767. }
  768. }
  769. fWrite(*indexTxtPath, renderIndexTxt(usersFromIndexTxt))
  770. fmt.Print(renderIndexTxt(usersFromIndexTxt))
  771. crlFix()
  772. oAdmin.clients = oAdmin.usersList()
  773. return fmt.Sprintf("{\"msg\":\"User %s successfully unrevoked\"}", username)
  774. }
  775. return fmt.Sprintf("{\"msg\":\"User \"%s\" not found\"}", username)
  776. }
  777. func (oAdmin *OpenvpnAdmin) mgmtRead(conn net.Conn) string {
  778. buf := make([]byte, 32768)
  779. bufLen, _ := conn.Read(buf)
  780. s := string(buf[:bufLen])
  781. return s
  782. }
  783. func (oAdmin *OpenvpnAdmin) mgmtConnectedUsersParser(text, serverName string) []clientStatus {
  784. var u []clientStatus
  785. isClientList := false
  786. isRouteTable := false
  787. scanner := bufio.NewScanner(strings.NewReader(text))
  788. for scanner.Scan() {
  789. txt := scanner.Text()
  790. if regexp.MustCompile(`^Common Name,Real Address,Bytes Received,Bytes Sent,Connected Since$`).MatchString(txt) {
  791. isClientList = true
  792. continue
  793. }
  794. if regexp.MustCompile(`^ROUTING TABLE$`).MatchString(txt) {
  795. isClientList = false
  796. continue
  797. }
  798. if regexp.MustCompile(`^Virtual Address,Common Name,Real Address,Last Ref$`).MatchString(txt) {
  799. isRouteTable = true
  800. continue
  801. }
  802. if regexp.MustCompile(`^GLOBAL STATS$`).MatchString(txt) {
  803. // isRouteTable = false // ineffectual assignment to isRouteTable (ineffassign)
  804. break
  805. }
  806. if isClientList {
  807. user := strings.Split(txt, ",")
  808. userName := user[0]
  809. userAddress := user[1]
  810. userBytesReceived:= user[2]
  811. userBytesSent:= user[3]
  812. userConnectedSince := user[4]
  813. userStatus := clientStatus{CommonName: userName, RealAddress: userAddress, BytesReceived: userBytesReceived, BytesSent: userBytesSent, ConnectedSince: userConnectedSince, ConnectedTo: serverName}
  814. u = append(u, userStatus)
  815. bytesSent, _ := strconv.Atoi(userBytesSent)
  816. bytesReceive, _ := strconv.Atoi(userBytesReceived)
  817. ovpnClientConnectionFrom.WithLabelValues(userName, userAddress).Set(float64(parseDateToUnix(ovpnStatusDateLayout, userConnectedSince)))
  818. ovpnClientBytesSent.WithLabelValues(userName).Set(float64(bytesSent))
  819. ovpnClientBytesReceived.WithLabelValues(userName).Set(float64(bytesReceive))
  820. }
  821. if isRouteTable {
  822. user := strings.Split(txt, ",")
  823. for i := range u {
  824. if u[i].CommonName == user[1] {
  825. u[i].VirtualAddress = user[0]
  826. u[i].LastRef = user[3]
  827. ovpnClientConnectionInfo.WithLabelValues(user[1], user[0]).Set(float64(parseDateToUnix(ovpnStatusDateLayout, user[3])))
  828. break
  829. }
  830. }
  831. }
  832. }
  833. return u
  834. }
  835. func (oAdmin *OpenvpnAdmin) mgmtKillUserConnection(username, serverName string) {
  836. conn, err := net.Dial("tcp", oAdmin.mgmtInterfaces[serverName])
  837. if err != nil {
  838. log.Printf("WARNING: openvpn mgmt interface for %s is not reachable by addr %s\n", serverName, oAdmin.mgmtInterfaces[serverName])
  839. return
  840. }
  841. oAdmin.mgmtRead(conn) // read welcome message
  842. conn.Write([]byte(fmt.Sprintf("kill %s\n", username)))
  843. fmt.Printf("%v", oAdmin.mgmtRead(conn))
  844. conn.Close()
  845. }
  846. func (oAdmin *OpenvpnAdmin) mgmtGetActiveClients() []clientStatus {
  847. var activeClients []clientStatus
  848. for srv, addr := range oAdmin.mgmtInterfaces {
  849. conn, err := net.Dial("tcp", addr)
  850. if err != nil {
  851. log.Printf("WARNING: openvpn mgmt interface for %s is not reachable by addr %s\n", srv, addr)
  852. break
  853. }
  854. oAdmin.mgmtRead(conn) // read welcome message
  855. conn.Write([]byte("status\n"))
  856. activeClients = append(activeClients, oAdmin.mgmtConnectedUsersParser(oAdmin.mgmtRead(conn), srv)...)
  857. conn.Close()
  858. }
  859. return activeClients
  860. }
  861. func isUserConnected(username string, connectedUsers []clientStatus) (bool, string) {
  862. for _, connectedUser := range connectedUsers {
  863. if connectedUser.CommonName == username {
  864. return true, connectedUser.ConnectedTo
  865. }
  866. }
  867. return false, ""
  868. }
  869. func (oAdmin *OpenvpnAdmin) downloadCerts() bool {
  870. if fExist(certsArchivePath) {
  871. fDelete(certsArchivePath)
  872. }
  873. err := fDownload(certsArchivePath, *masterHost + downloadCertsApiUrl + "?token=" + oAdmin.masterSyncToken, oAdmin.masterHostBasicAuth)
  874. if err != nil {
  875. log.Println(err)
  876. return false
  877. }
  878. return true
  879. }
  880. func (oAdmin *OpenvpnAdmin) downloadCcd() bool {
  881. if fExist(ccdArchivePath) {
  882. fDelete(ccdArchivePath)
  883. }
  884. err := fDownload(ccdArchivePath, *masterHost + downloadCcdApiUrl + "?token=" + oAdmin.masterSyncToken, oAdmin.masterHostBasicAuth)
  885. if err != nil {
  886. log.Println(err)
  887. return false
  888. }
  889. return true
  890. }
  891. func archiveCerts() {
  892. o := runBash(fmt.Sprintf("cd %s && tar -czf %s *", *easyrsaDirPath + "/pki", certsArchivePath ))
  893. fmt.Println(o)
  894. }
  895. func archiveCcd() {
  896. o := runBash(fmt.Sprintf("cd %s && tar -czf %s *", *ccdDir, ccdArchivePath ))
  897. fmt.Println(o)
  898. }
  899. func unArchiveCerts() {
  900. runBash(fmt.Sprintf("mkdir -p %s", *easyrsaDirPath + "/pki"))
  901. o := runBash(fmt.Sprintf("cd %s && tar -xzf %s", *easyrsaDirPath + "/pki", certsArchivePath ))
  902. fmt.Println(o)
  903. }
  904. func unArchiveCcd() {
  905. runBash(fmt.Sprintf("mkdir -p %s", *ccdDir))
  906. o := runBash(fmt.Sprintf("cd %s && tar -xzf %s", *ccdDir, ccdArchivePath ))
  907. fmt.Println(o)
  908. }
  909. func (oAdmin *OpenvpnAdmin) syncDataFromMaster() {
  910. retryCountMax := 3
  911. certsDownloadFailed := true
  912. ccdDownloadFailed := true
  913. certsDownloadRetries := 0
  914. ccdDownloadRetries := 0
  915. for certsDownloadFailed && certsDownloadRetries < retryCountMax {
  916. certsDownloadRetries += 1
  917. log.Printf("Downloading certs archive from master. Attempt %d", certsDownloadRetries)
  918. if oAdmin.downloadCerts() {
  919. certsDownloadFailed = false
  920. log.Println("Decompression certs archive from master")
  921. unArchiveCerts()
  922. } else {
  923. log.Printf("WARNING: something goes wrong during downloading certs from master. Attempt %d", certsDownloadRetries)
  924. }
  925. }
  926. for ccdDownloadFailed && ccdDownloadRetries < retryCountMax {
  927. ccdDownloadRetries += 1
  928. log.Printf("Downloading ccd archive from master. Attempt %d", ccdDownloadRetries)
  929. if oAdmin.downloadCcd() {
  930. ccdDownloadFailed = false
  931. log.Println("Decompression ccd archive from master")
  932. unArchiveCcd()
  933. } else {
  934. log.Printf("WARNING: something goes wrong during downloading certs from master. Attempt %d", ccdDownloadRetries)
  935. }
  936. }
  937. oAdmin.lastSyncTime = time.Now().Format("2006-01-02 15:04:05")
  938. if !ccdDownloadFailed && !certsDownloadFailed {
  939. oAdmin.lastSuccessfulSyncTime = time.Now().Format("2006-01-02 15:04:05")
  940. }
  941. }
  942. func (oAdmin *OpenvpnAdmin) syncWithMaster() {
  943. for {
  944. time.Sleep(time.Duration(*masterSyncFrequency) * time.Second)
  945. oAdmin.syncDataFromMaster()
  946. }
  947. }
  948. func getOvpnCaCertExpireDate() time.Time {
  949. caCertPath := *easyrsaDirPath + "/pki/ca.crt"
  950. caCertExpireDate := runBash(fmt.Sprintf("openssl x509 -in %s -noout -enddate | awk -F \"=\" {'print $2'}", caCertPath))
  951. dateLayout := "Jan 2 15:04:05 2006 MST"
  952. t, err := time.Parse(dateLayout, strings.TrimSpace(caCertExpireDate))
  953. if err != nil {
  954. log.Printf("WARNING: can`t parse expire date for CA cert: %v", err)
  955. return time.Now()
  956. }
  957. return t
  958. }
  959. // https://community.openvpn.net/openvpn/ticket/623
  960. func crlFix() {
  961. err1 := os.Chmod(*easyrsaDirPath + "/pki", 0755)
  962. if err1 != nil {
  963. log.Println(err1)
  964. }
  965. err2 := os.Chmod(*easyrsaDirPath + "/pki/crl.pem", 0644)
  966. if err2 != nil {
  967. log.Println(err2)
  968. }
  969. }