From c168f368623a952506edc87bc481575996a268c6 Mon Sep 17 00:00:00 2001 From: Sprait Date: Fri, 6 Oct 2023 18:47:28 +0000 Subject: [PATCH] fix passw auth; refactor type auth --- backend/errors.go | 4 +-- backend/flags.go | 3 +- backend/handlers.go | 4 +-- backend/methods.go | 66 +++++++++++++++++++++++++++----------- backend/models.go | 3 +- backend/utils.go | 18 +++++++++++ frontend/src/main.js | 15 +++++---- frontend/static/index.html | 19 ++++++----- main.go | 33 +++++++------------ setup/auth.sh | 2 +- setup/configure.sh | 2 +- 11 files changed, 106 insertions(+), 63 deletions(-) diff --git a/backend/errors.go b/backend/errors.go index 1118de6..5979e81 100644 --- a/backend/errors.go +++ b/backend/errors.go @@ -12,7 +12,7 @@ var ( userIsNotActiveError = errors.New("user is not active") passwordMismatchedError = errors.New("password mismatched") tokenMismatchedError = errors.New("token mismatched") - checkAppError = errors.New("failed to check 2FA app") - registerAppError = errors.New("failed to register 2FA app") + checkAppError = errors.New("failed to check 2FA TOTP app") + registerAppError = errors.New("failed to register 2FA TOTP app") authBackendDisabled = errors.New("auth backend not enabled yet") ) diff --git a/backend/flags.go b/backend/flags.go index 7edbd48..03d2287 100644 --- a/backend/flags.go +++ b/backend/flags.go @@ -36,8 +36,7 @@ var ( clientConfigTemplatePath = kingpin.Flag("templates.clientconfig-path", "path to custom client.conf.tpl").Default("").Envar("OVPN_TEMPLATES_CC_PATH").String() ccdTemplatePath = kingpin.Flag("templates.ccd-path", "path to custom ccd.tpl").Default("").Envar("OVPN_TEMPLATES_CCD_PATH").String() - AuthByPassword = kingpin.Flag("auth.password", "enable additional password authentication").Default("false").Envar("OVPN_AUTH").Bool() - AuthTFA = kingpin.Flag("auth.2fa", "auth type").Default("false").Envar("OVPN_AUTH_TFA").Bool() + AuthType = kingpin.Flag("auth.type", "auth type").Default("").Envar("OVPN_AUTH").HintOptions("TOTP", "PASSWORD", "").String() 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() diff --git a/backend/handlers.go b/backend/handlers.go index 9ed6cea..1aa0d9f 100644 --- a/backend/handlers.go +++ b/backend/handlers.go @@ -74,7 +74,7 @@ func (oAdmin *OvpnAdmin) UserResetTFAHandler(w http.ResponseWriter, r *http.Requ http.Error(w, err.Error(), http.StatusBadRequest) } else { w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, "2FA reseted") + fmt.Fprintf(w, "TOTP reseted") } } @@ -150,7 +150,7 @@ func (oAdmin *OvpnAdmin) UserUnrevokeHandler(w http.ResponseWriter, r *http.Requ func (oAdmin *OvpnAdmin) UserChangePasswordHandler(w http.ResponseWriter, r *http.Request) { log.Info(r.RemoteAddr, " ", r.RequestURI) _ = r.ParseForm() - if *AuthByPassword { + if oAdmin.ExtraAuth { err, msg := oAdmin.userChangePassword(r.FormValue("username"), r.FormValue("password")) if err != nil { w.WriteHeader(http.StatusInternalServerError) diff --git a/backend/methods.go b/backend/methods.go index 9434465..5dfabb8 100644 --- a/backend/methods.go +++ b/backend/methods.go @@ -152,7 +152,7 @@ func (oAdmin *OvpnAdmin) renderClientConfig(username string) string { conf.Key = fRead(*EasyrsaDirPath + "/pki/private/" + username + ".key") } - conf.PasswdAuth = *AuthByPassword + conf.PasswdAuth = oAdmin.ExtraAuth t := oAdmin.getTemplate("client.conf.tpl", "client-config", *clientConfigTemplatePath) @@ -313,8 +313,12 @@ func (oAdmin *OvpnAdmin) usersList() []OpenvpnClient { connectedUniqUsers += 1 } - if oAdmin.isSecondFactorConfigured(ovpnClient.Identity) { - ovpnClient.SecondFactor = true + if oAdmin.ExtraAuth{ + if oAdmin.isSecondFactorConfigured(ovpnClient.Identity) { + ovpnClient.SecondFactor = "enabled" + } else { + ovpnClient.SecondFactor = "disabled" + } } users = append(users, ovpnClient) @@ -354,7 +358,7 @@ func (oAdmin *OvpnAdmin) userCreate(username, password string) (string, error) { return err.Error(), err } - if *AuthByPassword { + if oAdmin.ExtraAuth { if err := validatePassword(password); err != nil { log.Debugf("userCreate: authByPassword(): %s", err.Error()) return err.Error(), err @@ -367,7 +371,7 @@ func (oAdmin *OvpnAdmin) userCreate(username, password string) (string, error) { log.Error(err) return err.Error(), err } - if *AuthByPassword { + if oAdmin.ExtraAuth { err = oAdmin.KubeClient.updatePasswordSecret(username, []byte(password)) if err != nil { return err.Error(), err @@ -376,7 +380,7 @@ func (oAdmin *OvpnAdmin) userCreate(username, password string) (string, error) { } else { o := runBash(fmt.Sprintf("cd %s && easyrsa build-client-full %s nopass 1>/dev/null", *EasyrsaDirPath, username)) log.Debug(o) - if *AuthByPassword { + if oAdmin.ExtraAuth { _, err := oAdmin.OUser.CreateUser(username, password) if err != nil { return err.Error(), err @@ -435,14 +439,21 @@ func (oAdmin *OvpnAdmin) isSecondFactorConfigured(username string) bool { } return sfe case "filesystem": - sfe, err := oAdmin.OUser.IsSecondFactorEnabled(username) - if err != nil { - return false + switch *AuthType { + case "TOTP": + sfe, err := oAdmin.OUser.IsSecondFactorEnabled(username) + if err != nil { + return false + } + return sfe + case "PASSWORD": + return true + //TODO: check if password is exist in db } - return sfe default: return false } + return false } func (oAdmin *OvpnAdmin) getUserSecret(username string) (string, error) { @@ -538,11 +549,11 @@ func (oAdmin *OvpnAdmin) registerUserAuthApp(username, totp string) error { for i, u := range oAdmin.clients { if u.Identity == username { - oAdmin.clients[i].SecondFactor = true + oAdmin.clients[i].SecondFactor = "enabled" } } - log.Infof("2FA configured for user %s", username) + log.Infof("TOTP configured for user %s", username) return nil } return fmt.Errorf("user \"%s\" not found", username) @@ -567,7 +578,7 @@ func (oAdmin *OvpnAdmin) resetUserAuthApp(username string) error { for i, u := range oAdmin.clients { if u.Identity == username { - oAdmin.clients[i].SecondFactor = false + oAdmin.clients[i].SecondFactor = "disabled" } } return nil @@ -587,7 +598,12 @@ func (oAdmin *OvpnAdmin) checkAuth(username, token string) error { return authErr } } else { - auth, authErr = oAdmin.OUser.AuthUser(username, "", token) + switch *AuthType { + case "TOTP": + auth, authErr = oAdmin.OUser.AuthUser(username, "", token) + case "PASSWORD": + auth, authErr = oAdmin.OUser.AuthUser(username, token, "") + } if authErr != nil { return authErr } @@ -625,7 +641,7 @@ func (oAdmin *OvpnAdmin) userRevoke(username string) (error, string) { log.Debugln(o) } - if *AuthByPassword { + if oAdmin.ExtraAuth { if oAdmin.OUser.CheckUserExistent(username) { revokeMsg, revokeErr := oAdmin.OUser.RevokedUser(username) log.Debug(revokeMsg) @@ -693,7 +709,7 @@ func (oAdmin *OvpnAdmin) userUnrevoke(username string) (error, string) { _ = runBash(fmt.Sprintf("cd %s && easyrsa gen-crl 1>/dev/null", *EasyrsaDirPath)) - if *AuthByPassword { + if oAdmin.ExtraAuth { if oAdmin.OUser.CheckUserExistent(username) { restoreMsg, restoreErr := oAdmin.OUser.RestoreUser(username) log.Debug(restoreMsg) @@ -750,7 +766,7 @@ func (oAdmin *OvpnAdmin) userRotate(username, newPassword string) (error, string log.Error(err) } - if *AuthByPassword { + if oAdmin.ExtraAuth { if oAdmin.OUser.CheckUserExistent(username) { deleteMsg, deleteErr := oAdmin.OUser.DeleteUser(username, true) log.Debug(deleteMsg) @@ -819,7 +835,7 @@ func (oAdmin *OvpnAdmin) userDelete(username string) (error, string) { break } } - if *AuthByPassword { + if oAdmin.ExtraAuth { if oAdmin.OUser.CheckUserExistent(username) { deleteMsg, deleteErr := oAdmin.OUser.DeleteUser(username, true) log.Debug(deleteMsg) @@ -1095,3 +1111,17 @@ func (oAdmin *OvpnAdmin) SyncWithMaster() { oAdmin.SyncDataFromMaster() } } + +func (oAdmin *OvpnAdmin) IsTotpAuth() bool { + if IsModuleEnabled("totpAuth", oAdmin.Modules) { + return true + } + return false +} + +func (oAdmin *OvpnAdmin) IsPasswdAuth() bool { + if IsModuleEnabled("passwdAuth", oAdmin.Modules) { + return true + } + return false +} \ No newline at end of file diff --git a/backend/models.go b/backend/models.go index 049b850..0e9d486 100644 --- a/backend/models.go +++ b/backend/models.go @@ -24,6 +24,7 @@ type OvpnAdmin struct { Modules []string mgmtStatusTimeFormat string CreateUserMutex *sync.Mutex + ExtraAuth bool } type OpenvpnServer struct { @@ -48,7 +49,7 @@ type OpenvpnClient struct { RevocationDate string `json:"RevocationDate"` ConnectionStatus string `json:"ConnectionStatus"` Connections int `json:"Connections"` - SecondFactor bool `json:"SecondFactor"` + SecondFactor string `json:"SecondFactor,omitempty"` } type ccdRoute struct { diff --git a/backend/utils.go b/backend/utils.go index be36465..0462124 100644 --- a/backend/utils.go +++ b/backend/utils.go @@ -24,6 +24,7 @@ import ( "strings" "time" "unicode/utf8" + "database/sql" ) var ( @@ -555,3 +556,20 @@ func randStr(strSize int, randType string) string { } return string(bytes) } + +func OpenDB(path string) *sql.DB { + db, err := sql.Open("sqlite3", path) + if err != nil { + log.Fatal(err) + } + return db +} + +func IsModuleEnabled(desiredModule string, listModules []string) bool { + for _, module := range listModules { + if module == desiredModule { + return true + } + } + return false +} \ No newline at end of file diff --git a/frontend/src/main.js b/frontend/src/main.js index 2c11f7a..6bb1497 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -160,6 +160,7 @@ new Vue({ label: 'Download config', class: 'btn-info', showWhenStatus: 'Active', + Require2FA: "true", showForServerRole: ['master', 'slave'], showForModule: ["core"], }, @@ -353,7 +354,7 @@ new Vue({ getUserTFAData: function(data) { let _this = this; - if (!_this.secondfactor) { + if (_this.secondfactor == 'disabled' ) { axios.request(axios_cfg('api/user/2fa/secret', data, 'form')) .then(function (response) { _this.u.secret = response.data; @@ -374,15 +375,15 @@ new Vue({ _this.u.modalActionStatus = 200; _this.u.modalRegister2faVisible = false; _this.getUserData(); - _this.secondfactor = true; + _this.secondfactor = "enabled"; _this.u.token = ""; _this.u.secret = ""; - _this.$notify({title: '2FA application registered for user ' + username, type: 'success'}); + _this.$notify({title: 'TOTP application registered for user ' + username, type: 'success'}); }) .catch(function(error) { _this.u.modalActionStatus = error.response.status; _this.u.modalActionMessage = error.response.data.message; - _this.$notify({title: 'Register 2FA application for user ' + username + ' failed!', type: 'error'}); + _this.$notify({title: 'Register TOTP application for user ' + username + ' failed!', type: 'error'}); }) }, @@ -396,15 +397,15 @@ new Vue({ axios.request(axios_cfg('api/user/2fa/reset', data, 'form')) .then(function(response) { _this.u.modalActionStatus = 200; - _this.secondfactor = false; + _this.secondfactor = "disabled"; _this.getUserTFAData(data); _this.getUserData(); - _this.$notify({title: '2FA application reset for user ' + username, type: 'success'}); + _this.$notify({title: 'TOTP application reset for user ' + username, type: 'success'}); }) .catch(function(error) { _this.u.modalActionStatus = error.response.status; _this.u.modalActionMessage = error.response.data.message; - _this.$notify({title: 'Reset 2FA application for user ' + username + ' failed!', type: 'error'}); + _this.$notify({title: 'Reset TOTP application for user ' + username + ' failed!', type: 'error'}); }) }, diff --git a/frontend/static/index.html b/frontend/static/index.html index 4e86bca..6f07a51 100644 --- a/frontend/static/index.html +++ b/frontend/static/index.html @@ -36,7 +36,10 @@ @click.left.stop="rowActionFn" v-for="action in actions" v-bind:class="action.class" - v-if="action.showWhenStatus == props.row.AccountStatus && action.showForServerRole.includes(serverRole) && action.showForModule.some(p=> modulesEnabled.includes(p))"> + v-if="action.showWhenStatus == props.row.AccountStatus && + ( props.row.SecondFactor != 'disabled' || !(action.Require2FA) ) && + action.showForServerRole.includes(serverRole) && + action.showForModule.some(p=> modulesEnabled.includes(p))"> {{ action.label }} @@ -51,7 +54,7 @@