Commit 77ac1fce authored by ibuler's avatar ibuler

[Update] 修改结构

parent 89a10490
...@@ -15,4 +15,5 @@ log/ ...@@ -15,4 +15,5 @@ log/
go.sum go.sum
.idea/ .idea/
vendor/ vendor/
config.yml config.yml
\ No newline at end of file host_key
...@@ -4,9 +4,6 @@ import ( ...@@ -4,9 +4,6 @@ import (
gossh "golang.org/x/crypto/ssh" gossh "golang.org/x/crypto/ssh"
"github.com/gliderlabs/ssh" "github.com/gliderlabs/ssh"
"cocogo/pkg/common"
"cocogo/pkg/service"
) )
func CheckUserPassword(ctx ssh.Context, password string) bool { func CheckUserPassword(ctx ssh.Context, password string) bool {
...@@ -14,15 +11,15 @@ func CheckUserPassword(ctx ssh.Context, password string) bool { ...@@ -14,15 +11,15 @@ func CheckUserPassword(ctx ssh.Context, password string) bool {
} }
func CheckUserPublicKey(ctx ssh.Context, key ssh.PublicKey) bool { func CheckUserPublicKey(ctx ssh.Context, key ssh.PublicKey) bool {
username := ctx.User() //username := ctx.User()
b := key.Marshal() //b := key.Marshal()
publicKeyBase64 := common.Base64Encode(string(b)) //publicKeyBase64 := common.Base64Encode(string(b))
remoteAddr := ctx.RemoteAddr().String() //remoteAddr := ctx.RemoteAddr().String()
authUser, err := service.CheckAuth(username, "", publicKeyBase64, remoteAddr, "T") //authUser, err := service.CheckAuth(username, "", publicKeyBase64, remoteAddr, "T")
if err != nil { //if err != nil {
return false // return false
} //}
ctx.SetValue("LoginUser", authUser) //ctx.SetValue("LoginUser", authUser)
return true return true
} }
......
package common package common
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"io/ioutil"
)
func GeneratePrivateKey(bitSize int) (*rsa.PrivateKey, error) {
// Private Key generation
privateKey, err := rsa.GenerateKey(rand.Reader, bitSize)
if err != nil {
return nil, err
}
// Validate Private Key
err = privateKey.Validate()
if err != nil {
return nil, err
}
return privateKey, nil
}
func EncodePrivateKeyToPEM(privateKey *rsa.PrivateKey) []byte {
// Get ASN.1 DER format
privDER := x509.MarshalPKCS1PrivateKey(privateKey)
// pem.Block
privBlock := pem.Block{
Type: "RSA PRIVATE KEY",
Headers: nil,
Bytes: privDER,
}
// Private key in PEM format
privatePEM := pem.EncodeToMemory(&privBlock)
return privatePEM
}
func WriteKeyToFile(keyBytes []byte, saveFileTo string) error {
err := ioutil.WriteFile(saveFileTo, keyBytes, 0600)
if err != nil {
return err
}
return nil
}
...@@ -110,6 +110,8 @@ var Conf = &Config{ ...@@ -110,6 +110,8 @@ var Conf = &Config{
AccessKey: "", AccessKey: "",
AccessKeyFile: "access_key", AccessKeyFile: "access_key",
LogLevel: "DEBUG", LogLevel: "DEBUG",
HostKeyFile: "host_key",
HostKey: "",
RootPath: rootPath, RootPath: rootPath,
Comment: "Coco", Comment: "Coco",
ReplayStorage: map[string]string{}, ReplayStorage: map[string]string{},
......
package context
import (
"context"
"github.com/gliderlabs/ssh"
"cocogo/pkg/model"
)
type UserContext struct {
context.Context
SessionCtx ssh.Context
User model.User
Asset sdk.Asset
SystemUser model.SystemUser
}
package handlers package handler
import ( import (
"text/template" "text/template"
......
package handlers package handler
const ( const (
GreenColorCode = "\033[32m" GreenColorCode = "\033[32m"
......
package handlers package handler
import ( import (
//"context"
//"strconv"
"cocogo/pkg/sdk"
"context" "context"
"encoding/json"
"fmt" "fmt"
"github.com/olekukonko/tablewriter"
"github.com/xlab/treeprint"
//"encoding/json"
//"fmt"
"io" "io"
"strconv" "strconv"
"strings" "strings"
//"strings"
//"strconv"
//"strings"
"sync" "sync"
"time" //"time"
"github.com/gliderlabs/ssh" "github.com/gliderlabs/ssh"
"github.com/olekukonko/tablewriter" //"github.com/olekukonko/tablewriter"
"github.com/satori/go.uuid" //"github.com/satori/go.uuid"
"github.com/xlab/treeprint" //"github.com/xlab/treeprint"
"golang.org/x/crypto/ssh/terminal" "golang.org/x/crypto/ssh/terminal"
"cocogo/pkg/logger" "cocogo/pkg/logger"
"cocogo/pkg/model" //"cocogo/pkg/proxy"
"cocogo/pkg/proxy" //"cocogo/pkg/service"
"cocogo/pkg/service" //"cocogo/pkg/transport"
"cocogo/pkg/transport" //"cocogo/pkg/userhome"
"cocogo/pkg/userhome"
) )
type InteractiveHandler struct { type InteractiveHandler struct {
sess ssh.Session sess ssh.Session
term *terminal.Terminal term *terminal.Terminal
assetData *sync.Map user sdk.User
user model.User
banner *Banner banner *Banner
currentSearchAssets []model.Asset currentSearchAssets []sdk.Asset
onceLoad sync.Once onceLoad sync.Once
sync.RWMutex sync.RWMutex
} }
...@@ -39,218 +50,194 @@ func (i *InteractiveHandler) displayBanner() { ...@@ -39,218 +50,194 @@ func (i *InteractiveHandler) displayBanner() {
i.banner.display(i.sess) i.banner.display(i.sess)
} }
//func (i *InteractiveHandler) Dispatch() { func (i *InteractiveHandler) Dispatch() {
// _, winCh, _ := i.sess.Pty() i.displayBanner()
// for { _, winCh, _ := i.sess.Pty()
// ctx, cancelFunc := context.WithCancel(i.sess.Context()) for {
// go func() { ctx, cancelFunc := context.WithCancel(i.sess.Context())
// for { go func() {
// select { for {
// case <-ctx.Done(): select {
// logger.Info("ctx done") case <-ctx.Done():
// return logger.Info("ctx done")
// case win, ok := <-winCh: return
// if !ok { case win, ok := <-winCh:
// return if !ok {
// } return
// logger.Info("SessionHandler term change:", win) }
// _ = i.term.SetSize(win.Width, win.Height) logger.Info("Term change:", win)
// } _ = i.term.SetSize(win.Width, win.Height)
// } }
// }() }
// line, err := i.term.ReadLine() }()
// cancelFunc() line, err := i.term.ReadLine()
// if err != nil { cancelFunc()
// logger.Error("ReadLine done", err) if err != nil {
// break logger.Error("ReadLine done", err)
// } break
// if line == "" { }
// continue if line == "" {
// } continue
// }
// i.onceLoad.Do(func() {
// if _, ok := Cached.Load(i.user.Id); !ok { if len(line) == 1 {
// logger.Info("first load this user asset data ") switch line {
// i.loadUserAssets() case "p", "P":
// i.loadUserAssetNodes() i.displayAssets([]sdk.Asset{})
//
// } else { case "g", "G":
// go func() { i.displayNodes([]sdk.Node{})
// i.loadUserAssets() case "s", "S":
// i.loadUserAssetNodes() i.changeLanguage()
// }() case "h", "H":
// } i.displayBanner()
// }) case "r", "R":
// i.refreshAssetsAndNodesData()
// if len(line) == 1 { case "q", "Q":
// switch line { logger.Info("exit session")
// case "p", "P": return
// if assets, ok := i.assetData.Load(AssetsMapKey); ok { default:
// i.displayAssets(assets.([]model.Asset)) assets := i.searchAsset(line)
// i.currentSearchAssets = assets.([]model.Asset) i.currentSearchAssets = assets
// } else if assets, ok := Cached.Load(i.user.Id); ok { i.displayAssetsOrProxy(assets)
// i.displayAssets(assets.([]model.Asset)) }
// i.currentSearchAssets = assets.([]model.Asset) continue
// } }
// if strings.Index(line, "/") == 0 {
// case "g", "G": searchWord := strings.TrimSpace(strings.TrimPrefix(line, "/"))
// if assetNodes, ok := i.assetData.Load(AssetNodesMapKey); ok { assets := i.searchAsset(searchWord)
// i.displayAssetNodes(assetNodes.([]model.AssetNode)) i.currentSearchAssets = assets
// } else { i.displayAssets(assets)
// i.displayAssetNodes([]model.AssetNode{}) continue
// } }
// case "s", "S":
// i.changeLanguage() if strings.Index(line, "g") == 0 {
// case "h", "H": searchWord := strings.TrimSpace(strings.TrimPrefix(line, "g"))
// i.displayBanner() if num, err := strconv.Atoi(searchWord); err == nil {
// case "r", "R": if num >= 0 {
// i.refreshAssetsAndNodesData() assets := i.searchNodeAssets(num)
// case "q", "Q": i.displayAssets(assets)
// logger.Info("exit session") i.currentSearchAssets = assets
// return continue
// default: }
// assets := i.searchAsset(line) }
// i.currentSearchAssets = assets }
// i.displayAssetsOrProxy(assets)
// } if strings.Index(line, "join") == 0 {
// continue roomID := strings.TrimSpace(strings.TrimPrefix(line, "join"))
// } i.JoinShareRoom(roomID)
// if strings.Index(line, "/") == 0 { continue
// searchWord := strings.TrimSpace(strings.TrimPrefix(line, "/")) }
// assets := i.searchAsset(searchWord)
// i.currentSearchAssets = assets assets := i.searchAsset(line)
// i.displayAssets(assets) i.currentSearchAssets = assets
// continue i.displayAssetsOrProxy(assets)
// }
// }
// if strings.Index(line, "g") == 0 { }
// searchWord := strings.TrimSpace(strings.TrimPrefix(line, "g"))
// if num, err := strconv.Atoi(searchWord); err == nil { func (i *InteractiveHandler) chooseSystemUser(systemUsers []sdk.SystemUser) sdk.SystemUser {
// if num >= 0 { table := tablewriter.NewWriter(i.sess)
// assets := i.searchNodeAssets(num) table.SetHeader([]string{"ID", "UserName"})
// i.displayAssets(assets) for i := 0; i < len(systemUsers); i++ {
// i.currentSearchAssets = assets table.Append([]string{strconv.Itoa(i + 1), systemUsers[i].UserName})
// continue }
// } table.SetBorder(false)
// } count := 0
// } term := terminal.NewTerminal(i.sess, "num:")
// for count < 3 {
// if strings.Index(line, "join") == 0 { table.Render()
// roomID := strings.TrimSpace(strings.TrimPrefix(line, "join")) line, err := term.ReadLine()
// i.JoinShareRoom(roomID) if err != nil {
// continue continue
// } }
// if num, err := strconv.Atoi(line); err == nil {
// assets := i.searchAsset(line) if num > 0 && num <= len(systemUsers) {
// i.currentSearchAssets = assets return systemUsers[num-1]
// i.displayAssetsOrProxy(assets) }
// }
// } count++
//} }
// return systemUsers[0]
//func (i *InteractiveHandler) chooseSystemUser(systemUsers []model.SystemUser) model.SystemUser { }
// table := tablewriter.NewWriter(i.sess)
// table.SetHeader([]string{"ID", "UserName"}) // 当资产的数量为1的时候,就进行代理转化
// for i := 0; i < len(systemUsers); i++ { func (i *InteractiveHandler) displayAssetsOrProxy(assets []sdk.Asset) {
// table.Append([]string{strconv.Itoa(i + 1), systemUsers[i].UserName}) if len(assets) == 1 {
// } var systemUser sdk.SystemUser
// table.SetBorder(false) switch len(assets[0].SystemUsers) {
// count := 0 case 0:
// term := terminal.NewTerminal(i.sess, "num:") // 有授权的资产,但是资产用户信息,无法登陆
// for count < 3 { i.displayAssets(assets)
// table.Render() return
// line, err := term.ReadLine() case 1:
// if err != nil { systemUser = assets[0].SystemUsers[0]
// continue default:
// } systemUser = i.chooseSystemUser(assets[0].SystemUsers)
// if num, err := strconv.Atoi(line); err == nil { }
// if num > 0 && num <= len(systemUsers) {
// return systemUsers[num-1] authInfo, err := sdk.GetSystemUserAssetAuthInfo(systemUser.Id, assets[0].Id)
// } if err != nil {
// } return
// count++ }
// } if ok := sdk.ValidateUserAssetPermission(i.user.Id, systemUser.Id, assets[0].Id); !ok {
// return systemUsers[0] // 检查user 是否对该资产有权限
//} return
// }
//// 当资产的数量为1的时候,就进行代理转化
//func (i *InteractiveHandler) displayAssetsOrProxy(assets []model.Asset) { err = i.Proxy(assets[0], authInfo)
// if len(assets) == 1 { if err != nil {
// var systemUser model.SystemUser logger.Info(err)
// switch len(assets[0].SystemUsers) { }
// case 0: return
// // 有授权的资产,但是资产用户信息,无法登陆 } else {
// i.displayAssets(assets) i.displayAssets(assets)
// return }
// case 1: }
// systemUser = assets[0].SystemUsers[0]
// default: func (i *InteractiveHandler) displayAssets(assets []sdk.Asset) {
// systemUser = i.chooseSystemUser(assets[0].SystemUsers) if len(assets) == 0 {
// } _, _ = io.WriteString(i.sess, "\r\n No Assets\r\n\r")
// } else {
// authInfo, err := service.GetSystemUserAssetAuthInfo(systemUser.Id, assets[0].Id) table := tablewriter.NewWriter(i.sess)
// if err != nil { table.SetHeader([]string{"ID", "Hostname", "IP", "LoginAs", "Comment"})
// return for index, assetItem := range assets {
// } sysUserArray := make([]string, len(assetItem.SystemUsers))
// if ok := appService.ValidateUserAssetPermission(i.user.Id, systemUser.Id, assets[0].Id); !ok { for index, sysUser := range assetItem.SystemUsers {
// // 检查user 是否对该资产有权限 sysUserArray[index] = sysUser.Name
// return }
// } sysUsers := "[" + strings.Join(sysUserArray, " ") + "]"
// table.Append([]string{strconv.Itoa(index + 1), assetItem.Hostname, assetItem.Ip, sysUsers, assetItem.Comment})
// err = i.Proxy(assets[0], authInfo) }
// if err != nil {
// logger.Info(err) table.SetBorder(false)
// } table.Render()
// return }
// } else {
// i.displayAssets(assets) }
// }
//} func (i *InteractiveHandler) displayNodes(nodes []sdk.Node) {
// tree := ConstructAssetNodeTree(nodes)
//func (i *InteractiveHandler) displayAssets(assets []model.Asset) { tipHeaderMsg := "\r\nNode: [ ID.Name(Asset amount) ]"
// if len(assets) == 0 { tipEndMsg := "Tips: Enter g+NodeID to display the host under the node, such as g1\r\n\r"
// _, _ = io.WriteString(i.sess, "\r\n No Assets\r\n\r")
// } else { _, err := io.WriteString(i.sess, tipHeaderMsg)
// table := tablewriter.NewWriter(i.sess) _, err = io.WriteString(i.sess, tree.String())
// table.SetHeader([]string{"ID", "Hostname", "IP", "LoginAs", "Comment"}) _, err = io.WriteString(i.sess, tipEndMsg)
// for index, assetItem := range assets { if err != nil {
// sysUserArray := make([]string, len(assetItem.SystemUsers)) logger.Info("displayAssetNodes err:", err)
// for index, sysUser := range assetItem.SystemUsers { }
// sysUserArray[index] = sysUser.Name
// } }
// sysUsers := "[" + strings.Join(sysUserArray, " ") + "]"
// table.Append([]string{strconv.Itoa(index + 1), assetItem.Hostname, assetItem.Ip, sysUsers, assetItem.Comment}) func (i *InteractiveHandler) refreshAssetsAndNodesData() {
// } _, err := io.WriteString(i.sess, "Refresh done\r\n")
// if err != nil {
// table.SetBorder(false) logger.Error("refresh Assets Nodes err:", err)
// table.Render() }
// }
// }
//}
//
//func (i *InteractiveHandler) displayAssetNodes(nodes []model.AssetNode) {
// tree := ConstructAssetNodeTree(nodes)
// tipHeaderMsg := "\r\nNode: [ ID.Name(Asset amount) ]"
// tipEndMsg := "Tips: Enter g+NodeID to display the host under the node, such as g1\r\n\r"
//
// _, err := io.WriteString(i.sess, tipHeaderMsg)
// _, err = io.WriteString(i.sess, tree.String())
// _, err = io.WriteString(i.sess, tipEndMsg)
// if err != nil {
// logger.Info("displayAssetNodes err:", err)
// }
//
//}
//
//func (i *InteractiveHandler) refreshAssetsAndNodesData() {
// i.loadUserAssets()
// i.loadUserAssetNodes()
// _, err := io.WriteString(i.sess, "Refresh done\r\n")
// if err != nil {
// logger.Error("refresh Assets Nodes err:", err)
// }
//
//}
// //
//func (i *InteractiveHandler) loadUserAssets() { //func (i *InteractiveHandler) loadUserAssets() {
// assets, err := appService.GetUserAssets(i.user.Id) // assets, err := appService.GetUserAssets(i.user.Id)
...@@ -273,9 +260,10 @@ func (i *InteractiveHandler) displayBanner() { ...@@ -273,9 +260,10 @@ func (i *InteractiveHandler) displayBanner() {
// i.assetData.Store(AssetNodesMapKey, assetNodes) // i.assetData.Store(AssetNodesMapKey, assetNodes)
//} //}
// //
//func (i *InteractiveHandler) changeLanguage() { func (i *InteractiveHandler) changeLanguage() {
//
//} }
// //
//func (i *InteractiveHandler) JoinShareRoom(roomID string) { //func (i *InteractiveHandler) JoinShareRoom(roomID string) {
// sshConn := userhome.NewSSHConn(i.sess) // sshConn := userhome.NewSSHConn(i.sess)
...@@ -301,22 +289,22 @@ func (i *InteractiveHandler) displayBanner() { ...@@ -301,22 +289,22 @@ func (i *InteractiveHandler) displayBanner() {
// //
//} //}
// //
//func (i *InteractiveHandler) searchAsset(key string) (assets []model.Asset) { //func (i *InteractiveHandler) searchAsset(key string) (assets []sdk.Asset) {
// if indexNum, err := strconv.Atoi(key); err == nil { // if indexNum, err := strconv.Atoi(key); err == nil {
// if indexNum > 0 && indexNum <= len(i.currentSearchAssets) { // if indexNum > 0 && indexNum <= len(i.currentSearchAssets) {
// return []model.Asset{i.currentSearchAssets[indexNum-1]} // return []sdk.Asset{i.currentSearchAssets[indexNum-1]}
// } // }
// } // }
// //
// if assetsData, ok := i.assetData.Load(AssetsMapKey); ok { // if assetsData, ok := i.assetData.Load(AssetsMapKey); ok {
// for _, assetValue := range assetsData.([]model.Asset) { // for _, assetValue := range assetsData.([]sdk.Asset) {
// if isSubstring([]string{assetValue.Ip, assetValue.Hostname, assetValue.Comment}, key) { // if isSubstring([]string{assetValue.Ip, assetValue.Hostname, assetValue.Comment}, key) {
// assets = append(assets, assetValue) // assets = append(assets, assetValue)
// } // }
// } // }
// } else { // } else {
// assetsData, _ := Cached.Load(i.user.Id) // assetsData, _ := Cached.Load(i.user.Id)
// for _, assetValue := range assetsData.([]model.Asset) { // for _, assetValue := range assetsData.([]sdk.Asset) {
// if isSubstring([]string{assetValue.Ip, assetValue.Hostname, assetValue.Comment}, key) { // if isSubstring([]string{assetValue.Ip, assetValue.Hostname, assetValue.Comment}, key) {
// assets = append(assets, assetValue) // assets = append(assets, assetValue)
// } // }
...@@ -326,10 +314,10 @@ func (i *InteractiveHandler) displayBanner() { ...@@ -326,10 +314,10 @@ func (i *InteractiveHandler) displayBanner() {
// return assets // return assets
//} //}
// //
//func (i *InteractiveHandler) searchNodeAssets(num int) (assets []model.Asset) { //func (i *InteractiveHandler) searchNodeAssets(num int) (assets []sdk.Asset) {
// var assetNodesData []model.AssetNode // var assetNodesData []sdk.Node
// if assetNodes, ok := i.assetData.Load(AssetNodesMapKey); ok { // if assetNodes, ok := i.assetData.Load(AssetNodesMapKey); ok {
// assetNodesData = assetNodes.([]model.AssetNode) // assetNodesData = assetNodes.([]sdk.Node)
// if num > len(assetNodesData) || num == 0 { // if num > len(assetNodesData) || num == 0 {
// return assets // return assets
// } // }
...@@ -339,7 +327,7 @@ func (i *InteractiveHandler) displayBanner() { ...@@ -339,7 +327,7 @@ func (i *InteractiveHandler) displayBanner() {
// //
//} //}
// //
//func (i *InteractiveHandler) Proxy(asset model.Asset, systemUser model.SystemUserAuthInfo) error { //func (i *InteractiveHandler) Proxy(asset sdk.Asset, systemUser sdk.SystemUserAuthInfo) error {
// /* // /*
// 1. 创建SSHConn,符合core.Conn接口 // 1. 创建SSHConn,符合core.Conn接口
// 2. 创建一个session Home // 2. 创建一个session Home
...@@ -418,52 +406,43 @@ func (i *InteractiveHandler) displayBanner() { ...@@ -418,52 +406,43 @@ func (i *InteractiveHandler) displayBanner() {
// return false // return false
//} //}
// //
//func ConstructAssetNodeTree(assetNodes []model.AssetNode) treeprint.Tree { func ConstructAssetNodeTree(assetNodes []sdk.Node) treeprint.Tree {
// model.SortAssetNodesByKey(assetNodes) sdk.SortAssetNodesByKey(assetNodes)
// var treeMap = map[string]treeprint.Tree{} var treeMap = map[string]treeprint.Tree{}
// tree := treeprint.New() tree := treeprint.New()
// for i := 0; i < len(assetNodes); i++ { for i := 0; i < len(assetNodes); i++ {
// r := strings.LastIndex(assetNodes[i].Key, ":") r := strings.LastIndex(assetNodes[i].Key, ":")
// if r < 0 { if r < 0 {
// subtree := tree.AddBranch(fmt.Sprintf("%s.%s(%s)", subtree := tree.AddBranch(fmt.Sprintf("%s.%s(%s)",
// strconv.Itoa(i+1), assetNodes[i].Name, strconv.Itoa(i+1), assetNodes[i].Name,
// strconv.Itoa(assetNodes[i].AssetsAmount))) strconv.Itoa(assetNodes[i].AssetsAmount)))
// treeMap[assetNodes[i].Key] = subtree treeMap[assetNodes[i].Key] = subtree
// continue continue
// } }
// if subtree, ok := treeMap[assetNodes[i].Key[:r]]; ok { if subtree, ok := treeMap[assetNodes[i].Key[:r]]; ok {
// nodeTree := subtree.AddBranch(fmt.Sprintf("%s.%s(%s)", nodeTree := subtree.AddBranch(fmt.Sprintf("%s.%s(%s)",
// strconv.Itoa(i+1), assetNodes[i].Name, strconv.Itoa(i+1), assetNodes[i].Name,
// strconv.Itoa(assetNodes[i].AssetsAmount))) strconv.Itoa(assetNodes[i].AssetsAmount)))
// treeMap[assetNodes[i].Key] = nodeTree treeMap[assetNodes[i].Key] = nodeTree
// } }
//
// } }
// return tree return tree
//} }
func SessionHandler(sess ssh.Session) { func SessionHandler(sess ssh.Session) {
_, _, ptyOk := sess.Pty() _, _, ptyOk := sess.Pty()
if ptyOk { if ptyOk {
user, ok := sess.Context().Value("LoginUser").(model.User)
if !ok {
logger.Info("Get current User failed")
return
}
banner := NewBanner(sess.User()) banner := NewBanner(sess.User())
handler := &InteractiveHandler{ handler := &InteractiveHandler{
sess: sess, sess: sess,
term: terminal.NewTerminal(sess, "Opt> "), term: terminal.NewTerminal(sess, "Opt> "),
user: user, banner: banner,
assetData: new(sync.Map),
banner: banner,
} }
logger.Info("accept one session") logger.Info("Accept one session")
handler.displayBanner() handler.displayBanner()
handler.Dispatch() handler.Dispatch()
} else { } else {
_, err := io.WriteString(sess, "No PTY requested.\n") _, err := io.WriteString(sess, "No PTY requested.\n")
if err != nil { if err != nil {
......
package handler
import (
"fmt"
"io"
"os"
"os/exec"
"syscall"
"unsafe"
"github.com/gliderlabs/ssh"
"github.com/kr/pty"
)
func setWinsize(f *os.File, w, h int) {
syscall.Syscall(syscall.SYS_IOCTL, f.Fd(), uintptr(syscall.TIOCSWINSZ),
uintptr(unsafe.Pointer(&struct{ h, w, x, y uint16 }{uint16(h), uint16(w), 0, 0})))
}
func TestHandler(s ssh.Session) {
cmd := exec.Command("top")
ptyReq, winCh, isPty := s.Pty()
if isPty {
cmd.Env = append(cmd.Env, fmt.Sprintf("TERM=%s", ptyReq.Term))
f, err := pty.Start(cmd)
if err != nil {
panic(err)
}
go func() {
for win := range winCh {
setWinsize(f, win.Width, win.Height)
}
}()
go func() {
io.Copy(f, s) // stdin
}()
io.Copy(s, f) // stdout
} else {
io.WriteString(s, "No PTY requested.\n")
s.Exit(1)
}
}
...@@ -9,9 +9,13 @@ import ( ...@@ -9,9 +9,13 @@ import (
var logger = logrus.New() var logger = logrus.New()
func init() { func init() {
customFormatter := new(logrus.TextFormatter) customFormatter := &logrus.TextFormatter{
DisableColors: false,
FullTimestamp: true,
DisableLevelTruncation: true,
}
customFormatter.TimestampFormat = "2006-01-02 15:04:05" customFormatter.TimestampFormat = "2006-01-02 15:04:05"
logger.SetFormatter(&logrus.TextFormatter{}) logger.SetFormatter(customFormatter)
// Output to stdout instead of the default stderr // Output to stdout instead of the default stderr
// Can be any io.Writer, see below for File example // Can be any io.Writer, see below for File example
......
package model
type Asset struct {
Id string `json:"id"`
Hostname string `json:"hostname"`
Ip string `json:"ip"`
Port int `json:"port"`
SystemUsers []SystemUser `json:"system_users_granted"`
IsActive bool `json:"is_active"`
SystemUsersJoin string `json:"system_users_join"`
Os string `json:"os"`
Domain string `json:"domain"`
Platform string `json:"platform"`
Comment string `json:"comment"`
Protocol string `json:"protocol"`
OrgID string `json:"org_id"`
OrgName string `json:"org_name"`
}
package model
import (
"sort"
"strconv"
"strings"
)
type AssetNode struct {
Id string `json:"id"`
Key string `json:"key"`
Name string `json:"name"`
Value string `json:"value"`
Parent string `json:"parent"`
AssetsGranted []Asset `json:"assets_granted"`
AssetsAmount int `json:"assets_amount"`
OrgId string `json:"org_id"`
}
type nodeSortBy func(node1, node2 *AssetNode) bool
func (by nodeSortBy) Sort(assetNodes []AssetNode) {
nodeSorter := &AssetNodeSorter{
assetNodes: assetNodes,
sortBy: by,
}
sort.Sort(nodeSorter)
}
type AssetNodeSorter struct {
assetNodes []AssetNode
sortBy func(node1, node2 *AssetNode) bool
}
func (a *AssetNodeSorter) Len() int {
return len(a.assetNodes)
}
func (a *AssetNodeSorter) Swap(i, j int) {
a.assetNodes[i], a.assetNodes[j] = a.assetNodes[j], a.assetNodes[i]
}
func (a *AssetNodeSorter) Less(i, j int) bool {
return a.sortBy(&a.assetNodes[i], &a.assetNodes[j])
}
/*
key的排列顺序:
1 1:3 1:3:0 1:4 1:5 1:8
*/
func keySort(node1, node2 *AssetNode) bool {
node1Keys := strings.Split(node1.Key, ":")
node2Keys := strings.Split(node2.Key, ":")
for i := 0; i < len(node1Keys); i++ {
if i >= len(node2Keys) {
return false
}
node1num, _ := strconv.Atoi(node1Keys[i])
node2num, _ := strconv.Atoi(node2Keys[i])
if node1num == node2num {
continue
} else if node1num-node2num > 0 {
return false
} else {
return true
}
}
return true
}
func SortAssetNodesByKey(assetNodes []AssetNode) {
nodeSortBy(keySort).Sort(assetNodes)
}
package service package sdk
import ( import (
"errors" "errors"
......
package service package sdk
import ( import (
"cocogo/pkg/logger" "cocogo/pkg/logger"
......
package model package sdk
import ( import (
"sort" "sort"
"strconv"
"strings"
) )
type Asset struct {
Id string `json:"id"`
Hostname string `json:"hostname"`
Ip string `json:"ip"`
Port int `json:"port"`
SystemUsers []SystemUser `json:"system_users_granted"`
IsActive bool `json:"is_active"`
SystemUsersJoin string `json:"system_users_join"`
Os string `json:"os"`
Domain string `json:"domain"`
Platform string `json:"platform"`
Comment string `json:"comment"`
Protocol string `json:"protocol"`
OrgID string `json:"org_id"`
OrgName string `json:"org_name"`
}
type Node struct {
Id string `json:"id"`
Key string `json:"key"`
Name string `json:"name"`
Value string `json:"value"`
Parent string `json:"parent"`
AssetsGranted []Asset `json:"assets_granted"`
AssetsAmount int `json:"assets_amount"`
OrgId string `json:"org_id"`
}
type nodeSortBy func(node1, node2 *Node) bool
func (by nodeSortBy) Sort(assetNodes []Node) {
nodeSorter := &AssetNodeSorter{
assetNodes: assetNodes,
sortBy: by,
}
sort.Sort(nodeSorter)
}
type AssetNodeSorter struct {
assetNodes []Node
sortBy func(node1, node2 *Node) bool
}
func (a *AssetNodeSorter) Len() int {
return len(a.assetNodes)
}
func (a *AssetNodeSorter) Swap(i, j int) {
a.assetNodes[i], a.assetNodes[j] = a.assetNodes[j], a.assetNodes[i]
}
func (a *AssetNodeSorter) Less(i, j int) bool {
return a.sortBy(&a.assetNodes[i], &a.assetNodes[j])
}
/* /*
{"id": "fbd39f8c-fa3e-4c2b-948e-ce1e0380b4f9", key的排列顺序:
"name": "docker_root", 1 1:3 1:3:0 1:4 1:5 1:8
"username": "root",
"priority": 19,
"protocol": "ssh",
"comment": "screencast",
"login_mode": "auto"}
*/ */
func keySort(node1, node2 *Node) bool {
node1Keys := strings.Split(node1.Key, ":")
node2Keys := strings.Split(node2.Key, ":")
for i := 0; i < len(node1Keys); i++ {
if i >= len(node2Keys) {
return false
}
node1num, _ := strconv.Atoi(node1Keys[i])
node2num, _ := strconv.Atoi(node2Keys[i])
if node1num == node2num {
continue
} else if node1num-node2num > 0 {
return false
} else {
return true
}
}
return true
}
func SortAssetNodesByKey(assetNodes []Node) {
nodeSortBy(keySort).Sort(assetNodes)
}
type SystemUser struct { type SystemUser struct {
Id string `json:"id"` Id string `json:"id"`
......
package service package sdk
import ( import (
"path" "path"
......
package sdk
var Client = WrapperClient{}
func init() {
//err := Client.LoadAuth()
//if err != nil {
// logger.Error("Load client access key error: %s", err)
// os.Exit(10)
//}
//err = Client.CheckAuth()
//if err != nil {
// logger.Error("Check client auth error: %s", err)
// os.Exit(11)
//}
}
package sdk
//
//func (s *Service) GetUserAssets(uid string) (resp []sdk.Asset, err error) {
//
// url := fmt.Sprintf("%s%s", s.Conf.CoreHost, fmt.Sprintf(UserAssetsUrl, uid))
//
// buf, err := s.SendHTTPRequest("GET", url, nil)
// if err != nil {
// log.Info("get User Assets err:", err)
// return resp, err
// }
// err = json.Unmarshal(buf, &resp)
// if err != nil {
// log.Info(err)
// return resp, err
// }
// return resp, nil
//
//}
//
//func (s *Service) GetUserAssetNodes(uid string) ([]model.Node, error) {
//
// var resp []model.Node
//
// url := fmt.Sprintf("%s%s", s.Conf.CoreHost, fmt.Sprintf(UserNodesAssetsUrl, uid))
//
// buf, err := s.SendHTTPRequest("GET", url, nil)
// if err != nil {
// log.Info("get User Assets Groups err:", err)
// return resp, err
// }
// err = json.Unmarshal(buf, &resp)
// if err != nil {
// log.Info(err)
// return resp, err
// }
// return resp, err
//}
//
//func (s *Service) ValidateUserAssetPermission(userID, systemUserID, AssetID string) bool {
// // cache_policy 0:不使用缓存 1:使用缓存 2: 刷新缓存
//
// baseUrl, _ := neturl.Parse(fmt.Sprintf("%s%s", s.Conf.CoreHost, ValidateUserAssetPermission))
// params := neturl.Values{}
// params.Add("user_id", userID)
// params.Add("asset_id", AssetID)
// params.Add("system_user_id", systemUserID)
// params.Add("cache_policy", "1")
//
// baseUrl.RawQuery = params.Encode()
// buf, err := s.SendHTTPRequest("GET", baseUrl.String(), nil)
// if err != nil {
// log.Error("Check User Asset Permission err:", err)
// return false
// }
// var res struct {
// Msg bool `json:"msg"'`
// }
// if err = json.Unmarshal(buf, &res); err != nil {
// return false
// }
// return res.Msg
//}
package service package sdk
// //
//func (s *Service) PushSessionReplay(gZipFile, sessionID string) error { //func (s *Service) PushSessionReplay(gZipFile, sessionID string) error {
......
package service package sdk
var urls = map[string]string{ var urls = map[string]string{
"UserAuthUrl": "/api/users/v1/auth/", // post 验证用户登陆 "UserAuthUrl": "/api/users/v1/auth/", // post 验证用户登陆
......
package sdk
import "cocogo/pkg/model"
func CheckAuth(username, password, publicKey, remoteAddr, loginType string) (user model.User, err error) {
return user, nil
}
//
//func (s *Service) CheckAuth(username, password, publicKey, remoteAddr, loginType string) (model.User, error) {
// /*
// {
// 'token': '0191970b1f5b414bbae42ec8fbb2a2ad',
// 'user':{'id': '34987591-bf75-4e5f-a102-6d59a1103431',
// 'name': 'softwareuser1', 'username': 'softwareuser1',
// 'email': 'xplz@hotmail.com',
// 'groups': ['bdc861f9-f476-4554-9bd4-13c3112e469d'],
// 'groups_display': '研发组', 'role': 'User',
// 'role_display': '用户', 'avatar_url': '/static/img/avatar/user.png',
// 'wechat': '', 'phone': None, 'otp_level': 0, 'comment': '',
// 'source': 'local', 'source_display': 'Local', 'is_valid': True,
// 'is_expired': False, 'is_active': True, 'created_by': 'admin',
// 'is_first_login': True, 'date_password_last_updated': '2019-03-08 11:47:04 +0800',
// 'date_expired': '2089-02-18 09:37:00 +0800'}}
// */
//
// postMap := map[string]string{
// "username": username,
// "password": password,
// "public_key": publicKey,
// "remote_addr": remoteAddr,
// "login_type": loginType,
// }
//
// data, err := json.Marshal(postMap)
// if err != nil {
// log.Info(err)
// return model.User{}, err
// }
//
// url := fmt.Sprintf("%s%s", s.Conf.CoreHost, UserAuthUrl)
// body, err := s.SendHTTPRequest(http.MethodPost, url, data)
//
// if err != nil {
// log.Info("read body failed:", err)
// return model.User{}, err
// }
// var result struct {
// Token string `json:"token"`
// User model.User `json:"user"`
// }
//
// err = json.Unmarshal(body, &result)
// if err != nil {
// log.Info("json decode failed:", err)
// return model.User{}, err
// }
//
// return result.User, nil
//}
//
//func (s *Service) CheckSSHPassword(ctx ssh.Value, password string) bool {
//
// username := ctx.User()
// remoteAddr := ctx.RemoteAddr().String()
// authUser, err := s.CheckAuth(username, password, "", remoteAddr, "T")
// if err != nil {
// return false
// }
// ctx.SetValue("LoginUser", authUser)
// return true
//}
package model package sdk
/* /*
{'id': '1f8e54a8-d99d-4074-b35d-45264adb4e34', {'id': '1f8e54a8-d99d-4074-b35d-45264adb4e34',
......
package service
import (
"os"
"cocogo/pkg/logger"
)
var Client = WrapperClient{}
func init() {
err := Client.LoadAuth()
if err != nil {
logger.Error("Load client access key error: %s", err)
os.Exit(10)
}
err = Client.CheckAuth()
if err != nil {
logger.Error("Check client auth error: %s", err)
os.Exit(11)
}
}
package service package service
// import "cocogo/pkg/sdk"
//func (s *Service) GetUserAssets(uid string) (resp []model.Asset, err error) {
// func GetUserAssets(userId string) (assets []sdk.Asset) {
// url := fmt.Sprintf("%s%s", s.Conf.CoreHost, fmt.Sprintf(UserAssetsUrl, uid)) return
// }
// buf, err := s.SendHTTPRequest("GET", url, nil)
// if err != nil { func GetUserNodes(userId string) (nodes []sdk.Node) {
// log.Info("get User Assets err:", err) return
// return resp, err }
// }
// err = json.Unmarshal(buf, &resp)
// if err != nil {
// log.Info(err)
// return resp, err
// }
// return resp, nil
//
//}
//
//func (s *Service) GetUserAssetNodes(uid string) ([]model.AssetNode, error) {
//
// var resp []model.AssetNode
//
// url := fmt.Sprintf("%s%s", s.Conf.CoreHost, fmt.Sprintf(UserNodesAssetsUrl, uid))
//
// buf, err := s.SendHTTPRequest("GET", url, nil)
// if err != nil {
// log.Info("get User Assets Groups err:", err)
// return resp, err
// }
// err = json.Unmarshal(buf, &resp)
// if err != nil {
// log.Info(err)
// return resp, err
// }
// return resp, err
//}
//
//func (s *Service) ValidateUserAssetPermission(userID, systemUserID, AssetID string) bool {
// // cache_policy 0:不使用缓存 1:使用缓存 2: 刷新缓存
//
// baseUrl, _ := neturl.Parse(fmt.Sprintf("%s%s", s.Conf.CoreHost, ValidateUserAssetPermission))
// params := neturl.Values{}
// params.Add("user_id", userID)
// params.Add("asset_id", AssetID)
// params.Add("system_user_id", systemUserID)
// params.Add("cache_policy", "1")
//
// baseUrl.RawQuery = params.Encode()
// buf, err := s.SendHTTPRequest("GET", baseUrl.String(), nil)
// if err != nil {
// log.Error("Check User Asset Permission err:", err)
// return false
// }
// var res struct {
// Msg bool `json:"msg"'`
// }
// if err = json.Unmarshal(buf, &res); err != nil {
// return false
// }
// return res.Msg
//}
package service package service
import "cocogo/pkg/model"
func CheckAuth(username, password, publicKey, remoteAddr, loginType string) (user model.User, err error) {
return user, nil
}
//
//func (s *Service) CheckAuth(username, password, publicKey, remoteAddr, loginType string) (model.User, error) {
// /*
// {
// 'token': '0191970b1f5b414bbae42ec8fbb2a2ad',
// 'user':{'id': '34987591-bf75-4e5f-a102-6d59a1103431',
// 'name': 'softwareuser1', 'username': 'softwareuser1',
// 'email': 'xplz@hotmail.com',
// 'groups': ['bdc861f9-f476-4554-9bd4-13c3112e469d'],
// 'groups_display': '研发组', 'role': 'User',
// 'role_display': '用户', 'avatar_url': '/static/img/avatar/user.png',
// 'wechat': '', 'phone': None, 'otp_level': 0, 'comment': '',
// 'source': 'local', 'source_display': 'Local', 'is_valid': True,
// 'is_expired': False, 'is_active': True, 'created_by': 'admin',
// 'is_first_login': True, 'date_password_last_updated': '2019-03-08 11:47:04 +0800',
// 'date_expired': '2089-02-18 09:37:00 +0800'}}
// */
//
// postMap := map[string]string{
// "username": username,
// "password": password,
// "public_key": publicKey,
// "remote_addr": remoteAddr,
// "login_type": loginType,
// }
//
// data, err := json.Marshal(postMap)
// if err != nil {
// log.Info(err)
// return model.User{}, err
// }
//
// url := fmt.Sprintf("%s%s", s.Conf.CoreHost, UserAuthUrl)
// body, err := s.SendHTTPRequest(http.MethodPost, url, data)
//
// if err != nil {
// log.Info("read body failed:", err)
// return model.User{}, err
// }
// var result struct {
// Token string `json:"token"`
// User model.User `json:"user"`
// }
//
// err = json.Unmarshal(body, &result)
// if err != nil {
// log.Info("json decode failed:", err)
// return model.User{}, err
// }
//
// return result.User, nil
//}
//
//func (s *Service) CheckSSHPassword(ctx ssh.Value, password string) bool {
//
// username := ctx.User()
// remoteAddr := ctx.RemoteAddr().String()
// authUser, err := s.CheckAuth(username, password, "", remoteAddr, "T")
// if err != nil {
// return false
// }
// ctx.SetValue("LoginUser", authUser)
// return true
//}
package sshd package sshd
import ( import (
"golang.org/x/crypto/ssh"
"io/ioutil" "io/ioutil"
"os" "os"
"golang.org/x/crypto/ssh"
"cocogo/pkg/common"
) )
type HostKey struct { type HostKey struct {
...@@ -29,11 +32,16 @@ func (hk *HostKey) loadHostKeyFromString(value string) (signer ssh.Signer, err e ...@@ -29,11 +32,16 @@ func (hk *HostKey) loadHostKeyFromString(value string) (signer ssh.Signer, err e
} }
func (hk *HostKey) Gen() (signer ssh.Signer, err error) { func (hk *HostKey) Gen() (signer ssh.Signer, err error) {
return key, err := common.GeneratePrivateKey(2048)
} if err != nil {
return
func (hk *HostKey) SaveToFile(signer ssh.Signer) (err error) { }
return keyBytes := common.EncodePrivateKeyToPEM(key)
err = common.WriteKeyToFile(keyBytes, hk.Path)
if err != nil {
return
}
return ssh.NewSignerFromKey(key)
} }
func (hk *HostKey) Load() (signer ssh.Signer, err error) { func (hk *HostKey) Load() (signer ssh.Signer, err error) {
...@@ -50,12 +58,5 @@ func (hk *HostKey) Load() (signer ssh.Signer, err error) { ...@@ -50,12 +58,5 @@ func (hk *HostKey) Load() (signer ssh.Signer, err error) {
} }
} }
signer, err = hk.Gen() signer, err = hk.Gen()
if err != nil {
return
}
err = hk.SaveToFile(signer)
if err != nil {
return
}
return return
} }
...@@ -7,8 +7,8 @@ import ( ...@@ -7,8 +7,8 @@ import (
"cocogo/pkg/auth" "cocogo/pkg/auth"
"cocogo/pkg/config" "cocogo/pkg/config"
"cocogo/pkg/handler"
"cocogo/pkg/logger" "cocogo/pkg/logger"
"cocogo/pkg/sshd/handlers"
) )
var ( var (
...@@ -16,6 +16,7 @@ var ( ...@@ -16,6 +16,7 @@ var (
) )
func StartServer() { func StartServer() {
logger.Debug("Load host access key")
hostKey := HostKey{Value: conf.HostKey, Path: conf.HostKeyFile} hostKey := HostKey{Value: conf.HostKey, Path: conf.HostKeyFile}
signer, err := hostKey.Load() signer, err := hostKey.Load()
if err != nil { if err != nil {
...@@ -29,7 +30,7 @@ func StartServer() { ...@@ -29,7 +30,7 @@ func StartServer() {
KeyboardInteractiveHandler: auth.CheckMFA, KeyboardInteractiveHandler: auth.CheckMFA,
HostSigners: []ssh.Signer{signer}, HostSigners: []ssh.Signer{signer},
Version: "coco-v1.4", Version: "coco-v1.4",
Handler: handlers.SessionHandler, Handler: handler.TestHandler,
} }
logger.Fatal(srv.ListenAndServe()) logger.Fatal(srv.ListenAndServe())
} }
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