Commit c489f6c4 authored by Eric's avatar Eric

[Update] MFA相关

parent 9aa34a44
...@@ -154,7 +154,7 @@ ...@@ -154,7 +154,7 @@
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:f8fa0c03fa1a9b220cd6e7984cd5ab923a83065f338a5fe2b2f61020d62f0991" digest = "1:d275874accb1537f7bd7ac28985aeb1ddc32b22884e5f57b68c32591437f822b"
name = "golang.org/x/crypto" name = "golang.org/x/crypto"
packages = [ packages = [
"curve25519", "curve25519",
...@@ -167,7 +167,7 @@ ...@@ -167,7 +167,7 @@
"ssh/terminal", "ssh/terminal",
] ]
pruneopts = "UT" pruneopts = "UT"
revision = "40738d426814df40c4d54d5e7017f7af7725da47" revision = "ef0d1a6f5b87067803518089d4cbc349777a56bd"
source = "github.com/ibuler/crypto" source = "github.com/ibuler/crypto"
[[projects]] [[projects]]
......
package auth package auth
import ( import (
"cocogo/pkg/model"
"fmt"
"strings" "strings"
"github.com/gliderlabs/ssh" "github.com/gliderlabs/ssh"
...@@ -17,7 +15,7 @@ import ( ...@@ -17,7 +15,7 @@ import (
func checkAuth(ctx ssh.Context, password, publicKey string) (res ssh.AuthResult) { func checkAuth(ctx ssh.Context, password, publicKey string) (res ssh.AuthResult) {
username := ctx.User() username := ctx.User()
remoteAddr := strings.Split(ctx.RemoteAddr().String(), ":")[0] remoteAddr := strings.Split(ctx.RemoteAddr().String(), ":")[0]
user, err := service.Authenticate(username, password, publicKey, remoteAddr, "T") resp, err := service.Authenticate(username, password, publicKey, remoteAddr, "T")
authMethod := "publickey" authMethod := "publickey"
action := "Accepted" action := "Accepted"
res = ssh.AuthFailed res = ssh.AuthFailed
...@@ -26,10 +24,23 @@ func checkAuth(ctx ssh.Context, password, publicKey string) (res ssh.AuthResult) ...@@ -26,10 +24,23 @@ func checkAuth(ctx ssh.Context, password, publicKey string) (res ssh.AuthResult)
} }
if err != nil { if err != nil {
action = "Failed" action = "Failed"
} else { res = ssh.AuthFailed
ctx.SetValue(cctx.ContextKeyUser, user)
res = ssh.AuthPartiallySuccessful
} }
if resp != nil {
switch resp.User.IsMFA {
case 0:
res = ssh.AuthSuccessful
case 1:
res = ssh.AuthPartiallySuccessful
case 2:
res = ssh.AuthPartiallySuccessful
default:
}
ctx.SetValue(cctx.ContextKeyUser, resp.User)
ctx.SetValue(cctx.ContextKeySeed, resp.Seed)
ctx.SetValue(cctx.ContextKeyToken, resp.Token)
}
logger.Infof("%s %s for %s from %s", action, authMethod, username, remoteAddr) logger.Infof("%s %s for %s from %s", action, authMethod, username, remoteAddr)
return res return res
} }
...@@ -46,13 +57,17 @@ func CheckUserPublicKey(ctx ssh.Context, key ssh.PublicKey) ssh.AuthResult { ...@@ -46,13 +57,17 @@ func CheckUserPublicKey(ctx ssh.Context, key ssh.PublicKey) ssh.AuthResult {
} }
func CheckMFA(ctx ssh.Context, challenger gossh.KeyboardInteractiveChallenge) ssh.AuthResult { func CheckMFA(ctx ssh.Context, challenger gossh.KeyboardInteractiveChallenge) ssh.AuthResult {
answers, err := challenger("admin", "> ", []string{"MFA"}, []bool{true}) answers, err := challenger(ctx.User(), "Please enter 6 digits.", []string{"[MFA auth]: "}, []bool{true})
if err != nil { if err != nil {
return ssh.AuthFailed return ssh.AuthFailed
} }
fmt.Println(answers) seed := ctx.Value(cctx.ContextKeySeed).(string)
code := answers[0]
res, err := service.AuthenticateMFA(seed, code, "T")
if err != nil || res != nil {
return ssh.AuthFailed
}
//ok := checkAuth(ctx, "admin", "") //ok := checkAuth(ctx, "admin", "")
ctx.SetValue(cctx.ContextKeyUser, &model.User{Username: "admin", Name: "admin"})
return ssh.AuthSuccessful return ssh.AuthSuccessful
} }
...@@ -19,6 +19,8 @@ var ( ...@@ -19,6 +19,8 @@ var (
ContextKeySSHSession = &contextKey{"sshSession"} ContextKeySSHSession = &contextKey{"sshSession"}
ContextKeyLocalAddr = &contextKey{"localAddr"} ContextKeyLocalAddr = &contextKey{"localAddr"}
ContextKeySSHCtx = &contextKey{"sshCtx"} ContextKeySSHCtx = &contextKey{"sshCtx"}
ContextKeySeed = &contextKey{"seed"}
ContextKeyToken = &contextKey{"token"}
) )
type Context interface { type Context interface {
......
...@@ -29,6 +29,7 @@ func SessionHandler(sess ssh.Session) { ...@@ -29,6 +29,7 @@ func SessionHandler(sess ssh.Session) {
_, _, ptyOk := sess.Pty() _, _, ptyOk := sess.Pty()
if ptyOk { if ptyOk {
ctx, cancel := cctx.NewContext(sess) ctx, cancel := cctx.NewContext(sess)
fmt.Println(ctx.User())
handler := &InteractiveHandler{ handler := &InteractiveHandler{
sess: sess, sess: sess,
user: ctx.User(), user: ctx.User(),
...@@ -88,6 +89,7 @@ func (i *InteractiveHandler) watchWinSizeChange(winCh <-chan ssh.Window, done <- ...@@ -88,6 +89,7 @@ func (i *InteractiveHandler) watchWinSizeChange(winCh <-chan ssh.Window, done <-
func (i *InteractiveHandler) Dispatch(ctx cctx.Context) { func (i *InteractiveHandler) Dispatch(ctx cctx.Context) {
i.preDispatch() i.preDispatch()
fmt.Println(i.user)
_, winCh, _ := i.sess.Pty() _, winCh, _ := i.sess.Pty()
for { for {
doneChan := make(chan struct{}) doneChan := make(chan struct{})
...@@ -323,8 +325,8 @@ func (i *InteractiveHandler) searchNodeAssets(num int) (assets []model.Asset) { ...@@ -323,8 +325,8 @@ func (i *InteractiveHandler) searchNodeAssets(num int) (assets []model.Asset) {
} }
func (i *InteractiveHandler) Proxy(ctx context.Context) { func (i *InteractiveHandler) Proxy(ctx context.Context) {
i.assetSelect = &model.Asset{Hostname: "centos", Port: 22, Ip: "192.168.244.185"} i.assetSelect = &model.Asset{Hostname: "centos", Port: 32768, Ip: "127.0.0.1"}
i.systemUserSelect = &model.SystemUser{Name: "web", UserName: "web", Password: "redhat"} i.systemUserSelect = &model.SystemUser{Name: "web", UserName: "root", Password: "screencast"}
p := proxy.ProxyServer{ p := proxy.ProxyServer{
Session: i.sess, Session: i.sess,
User: i.user, User: i.user,
......
...@@ -18,6 +18,12 @@ package model ...@@ -18,6 +18,12 @@ package model
'date_expired': '2089-03-21 18:18:24 +0800'} 'date_expired': '2089-03-21 18:18:24 +0800'}
*/ */
type AuthResponse struct {
Token string `json:"token"`
Seed string `json:"seed"`
User *User `json:"user"`
}
type User struct { type User struct {
Id string `json:"id"` Id string `json:"id"`
Username string `json:"username"` Username string `json:"username"`
...@@ -27,6 +33,7 @@ type User struct { ...@@ -27,6 +33,7 @@ type User struct {
Role string `json:"role"` Role string `json:"role"`
IsValid bool `json:"is_valid"` IsValid bool `json:"is_valid"`
IsActive bool `json:"is_active"` IsActive bool `json:"is_active"`
IsMFA int `json:"otp_level"`
} }
type TokenUser struct { type TokenUser struct {
......
...@@ -3,6 +3,7 @@ package proxy ...@@ -3,6 +3,7 @@ package proxy
import ( import (
"fmt" "fmt"
"io" "io"
"strconv"
"strings" "strings"
"time" "time"
...@@ -13,7 +14,6 @@ import ( ...@@ -13,7 +14,6 @@ import (
"cocogo/pkg/logger" "cocogo/pkg/logger"
"cocogo/pkg/model" "cocogo/pkg/model"
"cocogo/pkg/service" "cocogo/pkg/service"
"strconv"
) )
type ProxyServer struct { type ProxyServer struct {
......
...@@ -15,7 +15,7 @@ func GetUserAssets(userId, cachePolicy string) (assets model.AssetList) { ...@@ -15,7 +15,7 @@ func GetUserAssets(userId, cachePolicy string) (assets model.AssetList) {
Url := authClient.ParseUrlQuery(fmt.Sprintf(UserAssetsURL, userId), params) Url := authClient.ParseUrlQuery(fmt.Sprintf(UserAssetsURL, userId), params)
err := authClient.Get(Url, &assets) err := authClient.Get(Url, &assets)
if err != nil { if err != nil {
logger.Error(err) logger.Error("GetUserAssets---err")
} }
return return
} }
...@@ -28,7 +28,7 @@ func GetUserNodes(userId, cachePolicy string) (nodes model.NodeList) { ...@@ -28,7 +28,7 @@ func GetUserNodes(userId, cachePolicy string) (nodes model.NodeList) {
Url := authClient.ParseUrlQuery(fmt.Sprintf(UserNodesAssetsURL, userId), params) Url := authClient.ParseUrlQuery(fmt.Sprintf(UserNodesAssetsURL, userId), params)
err := authClient.Get(Url, &nodes) err := authClient.Get(Url, &nodes)
if err != nil { if err != nil {
logger.Error(err) logger.Error("GetUserNodes err")
} }
return return
} }
......
...@@ -5,6 +5,8 @@ const ( ...@@ -5,6 +5,8 @@ const (
UserProfileURL = "/api/users/v1/profile/" // 获取当前用户的基本信息 UserProfileURL = "/api/users/v1/profile/" // 获取当前用户的基本信息
UserUserURL = "/api/users/v1/users/%s/" // 获取用户信息 UserUserURL = "/api/users/v1/users/%s/" // 获取用户信息
AuthMFAURL = "/api/authentication/v1/otp/auth/" // MFA 验证用户信息
SystemUserAssetAuthURL = "/api/assets/v1/system-user/%s/asset/%s/auth-info/" // 该系统用户对某资产的授权 SystemUserAssetAuthURL = "/api/assets/v1/system-user/%s/asset/%s/auth-info/" // 该系统用户对某资产的授权
SystemUserAuthInfoURL = "/api/assets/v1/system-user/%s/auth-info/" // 该系统用户的授权 SystemUserAuthInfoURL = "/api/assets/v1/system-user/%s/auth-info/" // 该系统用户的授权
SystemUserCmdFilterRules = "/api/assets/v1/system-user/%s/cmd-filter-rules/" // 过滤规则url SystemUserCmdFilterRules = "/api/assets/v1/system-user/%s/cmd-filter-rules/" // 过滤规则url
......
...@@ -7,7 +7,7 @@ import ( ...@@ -7,7 +7,7 @@ import (
"cocogo/pkg/model" "cocogo/pkg/model"
) )
func Authenticate(username, password, publicKey, remoteAddr, loginType string) (user *model.User, err error) { func Authenticate(username, password, publicKey, remoteAddr, loginType string) (resp *model.AuthResponse, err error) {
data := map[string]string{ data := map[string]string{
"username": username, "username": username,
"password": password, "password": password,
...@@ -15,20 +15,39 @@ func Authenticate(username, password, publicKey, remoteAddr, loginType string) ( ...@@ -15,20 +15,39 @@ func Authenticate(username, password, publicKey, remoteAddr, loginType string) (
"remote_addr": remoteAddr, "remote_addr": remoteAddr,
"login_type": loginType, "login_type": loginType,
} }
var resp struct {
Token string `json:"token"`
User *model.User `json:"user"`
}
Url := client.ParseUrlQuery(UserAuthURL, nil) Url := client.ParseUrlQuery(UserAuthURL, nil)
err = client.Post(Url, data, &resp) err = client.Post(Url, data, resp)
if err != nil { if err != nil {
logger.Error(err) logger.Error(err)
return
} }
user = resp.User
return return
} }
func AuthenticateMFA(seed, code, loginType string) (resp *model.AuthResponse, err error) {
/*
data = {
'seed': seed,
'otp_code': otp_code,
'login_type': login_type,
}
*/
data := map[string]string{
"seed": seed,
"otp_code": code,
"login_type": loginType,
}
Url := client.ParseUrlQuery(AuthMFAURL, nil)
err = client.Post(Url, data, resp)
if err != nil {
logger.Error(err)
}
return
}
func GetUserProfile(userId string) (user *model.User) { func GetUserProfile(userId string) (user *model.User) {
Url := authClient.ParseUrlQuery(fmt.Sprintf(UserUserURL, userId), nil) Url := authClient.ParseUrlQuery(fmt.Sprintf(UserUserURL, userId), nil)
err := authClient.Get(Url, &user) err := authClient.Get(Url, &user)
......
...@@ -18,12 +18,18 @@ const version = "v1.4.0" ...@@ -18,12 +18,18 @@ const version = "v1.4.0"
func defaultConfig(ctx ssh.Context) (conf *gossh.ServerConfig) { func defaultConfig(ctx ssh.Context) (conf *gossh.ServerConfig) {
conf = new(gossh.ServerConfig) conf = new(gossh.ServerConfig)
conf.AuthLogCallback = func(conn gossh.ConnMetadata, method string, err error) {
fmt.Println(err)
fmt.Println(method)
result := "failed"
if err == nil {
result = "success"
}
logger.Debugf("%s use AuthMethod %s %s\n", conn.User(), method, result)
}
conf.NextAuthMethodsCallback = func(conn gossh.ConnMetadata) (methods []string) { conf.NextAuthMethodsCallback = func(conn gossh.ConnMetadata) (methods []string) {
fmt.Println("Username: ", conn.User()) fmt.Println("Username: ", conn.User())
if conn.User() == "ibuler" { return []string{"keyboard-interactive"}
return []string{"keyboard-interactive"}
}
return
} }
return conf return conf
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment