Commit e70545d3 authored by ibuler's avatar ibuler

{update} tij

parent 774c4ac4
BRANCH := $(shell git symbolic-ref HEAD 2>/dev/null | cut -d"/" -f 3)
BUILD := $(shell git rev-parse --short HEAD)
VERSION=$(BRANCH)-$(BUILD)
VERSION = $(BRANCH)-$(BUILD)
NAME := coco
BASEPATH := $(shell pwd)
CGO_ENABLED = 0
GOCMD=go
GOBUILD=$(GOCMD) build
GOCMD = go
GOBUILD = $(GOCMD) build
ASSETS = $(shell echo "locale static templates coco config_example.yml")
SOFTWARENAME = $(NAME)-$(VERSION)
COCOSRCFILE = coco.go
SOFTWARENAME=$(NAME)-$(VERSION)
COCOSRCFILE= coco.go
.PHONY: windows
windows:
@echo "编译windows"
mkdir -p $(BASEPATH)/../build
GOOS=windows GOARCH=amd64 go build -o $(BASEPATH)/$(SOFTWARENAME)-windows-amd64 $(COCOSRCFILE)
GOOS=windows GOARCH=amd64 go build -o $(BASEPATH)/$(NAME) $(COCOSRCFILE)
tar czvf $(BASEPATH)/../build/$(SOFTWARENAME)-windows-amd64.tar.gz $(SOFTWARENAME)-windows-amd64 locale/ config_example.yml
.PHONY: linux
linux:
@echo "编译linux"
mkdir -p $(BASEPATH)/../build
GOOS=linux GOARCH=amd64 go build -o $(BASEPATH)/$(SOFTWARENAME)-linux-amd64 $(COCOSRCFILE)
tar czvf $(BASEPATH)/../build/$(SOFTWARENAME)-linux-amd64.tar.gz $(SOFTWARENAME)-linux-amd64 locale/ config_example.yml
GOOS=linux GOARCH=amd64 go build -o $(BASEPATH)/$(NAME) $(COCOSRCFILE)
tar czvf $(BASEPATH)/../build/$(SOFTWARENAME)-linux-amd64.tar.gz $(ASSETS)
.PHONY: darwin
darwin:
@echo "编译darwin"
mkdir -p $(BASEPATH)/../build
GOOS=darwin GOARCH=amd64 go build -o $(BASEPATH)/$(SOFTWARENAME)-darwin-amd64 $(COCOSRCFILE)
GOOS=darwin GOARCH=amd64 go build -o $(BASEPATH)/$(NAME) $(COCOSRCFILE)
tar czvf $(BASEPATH)/../build/$(SOFTWARENAME)-darwin-amd64.tar.gz $(SOFTWARENAME)-darwin-amd64 locale/ config_example.yml
.PHONY: docker
......
......@@ -38,7 +38,7 @@ type UrlParser interface {
func NewClient(timeout time.Duration, baseHost string) *Client {
headers := make(map[string]string, 0)
client := http.DefaultClient
client := new(http.Client)
client.Timeout = timeout * time.Second
return &Client{
BaseHost: baseHost,
......
......@@ -3,9 +3,7 @@ package handler
import (
"fmt"
"io"
"net"
"os"
"strconv"
"strings"
"sync"
"syscall"
......@@ -13,7 +11,6 @@ import (
"github.com/gliderlabs/ssh"
"github.com/pkg/sftp"
gossh "golang.org/x/crypto/ssh"
"cocogo/pkg/cctx"
......@@ -21,38 +18,47 @@ import (
"cocogo/pkg/logger"
"cocogo/pkg/model"
"cocogo/pkg/service"
"cocogo/pkg/srvconn"
)
func SftpHandler(sess ssh.Session) {
ctx, cancel := cctx.NewContext(sess)
defer cancel()
userhandler := &userSftpRequests{user: ctx.User()}
userhandler.initial()
hs := sftp.Handlers{
FileGet: userhandler,
FilePut: userhandler,
FileCmd: userhandler,
FileList: userhandler}
handler := &sftpHandler{user: ctx.User()}
handler.initial()
handlers := sftp.Handlers{
FileGet: handler,
FilePut: handler,
FileCmd: handler,
FileList: handler,
}
req := sftp.NewRequestServer(sess, hs)
req := sftp.NewRequestServer(sess, handlers)
if err := req.Serve(); err == io.EOF {
_ = req.Close()
hosts := handler.hosts
for hostname, dir := range hosts {
for name, d := range dir.suMaps {
srvconn.RecycleClient(d.conn)
delete(dir.suMaps, name)
}
delete(hosts, hostname)
}
logger.Info("sftp client exited session.")
} else if err != nil {
logger.Error("sftp server completed with error:", err)
}
}
type userSftpRequests struct {
type sftpHandler struct {
user *model.User
assets model.AssetList
rootPath string // tmp || home || ~
hosts map[string]*HostNameDir
}
func (fs *userSftpRequests) initial() {
func (fs *sftpHandler) initial() {
fs.loadAssets()
fs.hosts = make(map[string]*HostNameDir)
fs.rootPath = config.GetConf().SftpRoot
......@@ -67,11 +73,11 @@ func (fs *userSftpRequests) initial() {
}
}
func (fs *userSftpRequests) loadAssets() {
func (fs *sftpHandler) loadAssets() {
fs.assets = service.GetUserAssets(fs.user.ID, "1")
}
func (fs *userSftpRequests) Filelist(r *sftp.Request) (sftp.ListerAt, error) {
func (fs *sftpHandler) Filelist(r *sftp.Request) (sftp.ListerAt, error) {
var fileInfos = listerat{}
var err error
logger.Debug("list path: ", r.Filepath)
......@@ -116,10 +122,12 @@ func (fs *userSftpRequests) Filelist(r *sftp.Request) (sftp.ListerAt, error) {
}
realPath = sysUserDir.ParsePath(r.Filepath)
if sysUserDir.client == nil {
sysUserDir.client, err = fs.GetSftpClient(hostDir.asset, sysUserDir.systemUser)
client, conn, err := fs.GetSftpClient(hostDir.asset, sysUserDir.systemUser)
if err != nil {
return nil, sftp.ErrSshFxPermissionDenied
}
sysUserDir.client = client
sysUserDir.conn = conn
}
switch r.Method {
......@@ -144,7 +152,7 @@ func (fs *userSftpRequests) Filelist(r *sftp.Request) (sftp.ListerAt, error) {
return fileInfos, err
}
func (fs *userSftpRequests) Filecmd(r *sftp.Request) error {
func (fs *sftpHandler) Filecmd(r *sftp.Request) error {
logger.Debug("File cmd: ", r.Filepath)
pathNames := strings.Split(strings.TrimPrefix(r.Filepath, "/"), "/")
if len(pathNames) <= 2 {
......@@ -153,11 +161,12 @@ func (fs *userSftpRequests) Filecmd(r *sftp.Request) error {
hostDir := fs.hosts[pathNames[0]]
suDir := hostDir.suMaps[pathNames[1]]
if suDir.client == nil {
client, err := fs.GetSftpClient(hostDir.asset, suDir.systemUser)
client, conn, err := fs.GetSftpClient(hostDir.asset, suDir.systemUser)
if err != nil {
return sftp.ErrSshFxPermissionDenied
}
suDir.client = client
suDir.conn = conn
}
realPathName := suDir.ParsePath(r.Filepath)
switch r.Method {
......@@ -179,7 +188,7 @@ func (fs *userSftpRequests) Filecmd(r *sftp.Request) error {
return nil
}
func (fs *userSftpRequests) Filewrite(r *sftp.Request) (io.WriterAt, error) {
func (fs *sftpHandler) Filewrite(r *sftp.Request) (io.WriterAt, error) {
logger.Debug("File write: ", r.Filepath)
pathNames := strings.Split(strings.TrimPrefix(r.Filepath, "/"), "/")
if len(pathNames) <= 2 {
......@@ -188,18 +197,19 @@ func (fs *userSftpRequests) Filewrite(r *sftp.Request) (io.WriterAt, error) {
hostDir := fs.hosts[pathNames[0]]
suDir := hostDir.suMaps[pathNames[1]]
if suDir.client == nil {
client, err := fs.GetSftpClient(hostDir.asset, suDir.systemUser)
client, conn, err := fs.GetSftpClient(hostDir.asset, suDir.systemUser)
if err != nil {
return nil, sftp.ErrSshFxPermissionDenied
}
suDir.client = client
suDir.conn = conn
}
realPathName := suDir.ParsePath(r.Filepath)
f, err := suDir.client.Create(realPathName)
return NewWriterAt(f), err
}
func (fs *userSftpRequests) Fileread(r *sftp.Request) (io.ReaderAt, error) {
func (fs *sftpHandler) Fileread(r *sftp.Request) (io.ReaderAt, error) {
logger.Debug("File read: ", r.Filepath)
pathNames := strings.Split(strings.TrimPrefix(r.Filepath, "/"), "/")
if len(pathNames) <= 2 {
......@@ -208,11 +218,12 @@ func (fs *userSftpRequests) Fileread(r *sftp.Request) (io.ReaderAt, error) {
hostDir := fs.hosts[pathNames[0]]
suDir := hostDir.suMaps[pathNames[1]]
if suDir.client == nil {
client, err := fs.GetSftpClient(hostDir.asset, suDir.systemUser)
ftpClient, client, err := fs.GetSftpClient(hostDir.asset, suDir.systemUser)
if err != nil {
return nil, sftp.ErrSshFxPermissionDenied
}
suDir.client = client
suDir.client = ftpClient
suDir.conn = client
}
realPathName := suDir.ParsePath(r.Filepath)
f, err := suDir.client.Open(realPathName)
......@@ -222,10 +233,16 @@ func (fs *userSftpRequests) Fileread(r *sftp.Request) (io.ReaderAt, error) {
return NewReaderAt(f), err
}
func (fs *userSftpRequests) GetSftpClient(asset *model.Asset, sysUser *model.SystemUser) (*sftp.Client, error) {
logger.Debug("Get Sftp Client")
info := service.GetSystemUserAssetAuthInfo(sysUser.ID, asset.ID)
return CreateSFTPConn(sysUser.Username, info.Password, info.PrivateKey, asset.IP, strconv.Itoa(asset.Port))
func (fs *sftpHandler) GetSftpClient(asset *model.Asset, sysUser *model.SystemUser) (sftpClient *sftp.Client, sshClient *gossh.Client, err error) {
sshClient, err = srvconn.NewClient(fs.user, asset, sysUser, config.GetConf().SSHTimeout*time.Second)
if err != nil {
return
}
sftpClient, err = sftp.NewClient(sshClient)
if err != nil {
return
}
return sftpClient, sshClient, nil
}
type HostNameDir struct {
......@@ -259,6 +276,7 @@ type SysUserDir struct {
systemUser *model.SystemUser
time time.Time
client *sftp.Client
conn *gossh.Client
}
func (su *SysUserDir) Name() string { return su.systemUser.Name }
......@@ -388,29 +406,3 @@ func (c *clientReadWritAt) ReadAt(p []byte, off int64) (n int, err error) {
return nr, err
}
func CreateSFTPConn(user, password, privateKey, host, port string) (*sftp.Client, error) {
authMethods := make([]gossh.AuthMethod, 0)
if password != "" {
authMethods = append(authMethods, gossh.Password(password))
}
if privateKey != "" {
if signer, err := gossh.ParsePrivateKey([]byte(privateKey)); err != nil {
err = fmt.Errorf("parse private key error: %sc", err)
} else {
authMethods = append(authMethods, gossh.PublicKeys(signer))
}
}
config := &gossh.ClientConfig{
User: user,
Auth: authMethods,
HostKeyCallback: gossh.InsecureIgnoreHostKey(),
Timeout: 300 * time.Second,
}
client, err := gossh.Dial("tcp", net.JoinHostPort(host, port), config)
if err != nil {
return nil, err
}
return sftp.NewClient(client)
}
......@@ -23,7 +23,7 @@ type DataMsg struct {
Room string `json:"room"`
}
type EmitRoomMsg struct {
type RoomMsg struct {
Room string `json:"room"`
Secret string `json:"secret"`
}
......
......@@ -10,7 +10,7 @@ import (
"github.com/gorilla/mux"
)
func OnElfinderConnect(s socketio.Conn) error {
func OnELFinderConnect(s socketio.Conn) error {
u := s.URL()
sid := u.Query().Get("sid")
data := EmitSidMsg{Sid: sid}
......@@ -18,7 +18,7 @@ func OnElfinderConnect(s socketio.Conn) error {
return nil
}
func OnElfinderDisconnect(s socketio.Conn, msg string) {
func OnELFinderDisconnect(s socketio.Conn, msg string) {
u := s.URL()
sid := u.Query().Get("sid")
log.Println("disconnect: ", sid)
......
......@@ -39,7 +39,7 @@ func AuthDecorator(handler http.HandlerFunc) http.HandlerFunc {
// OnConnectHandler 当websocket连接后触发
func OnConnectHandler(s socketio.Conn) error {
// 首次连接 1.获取当前用户的信息
logger.Debug("On connect trigger")
logger.Debug("Web terminal on connect event trigger")
cookies := strings.Split(s.RemoteHeader().Get("Cookie"), ";")
var csrfToken, sessionID, remoteIP string
for _, line := range cookies {
......@@ -62,25 +62,22 @@ func OnConnectHandler(s socketio.Conn) error {
} else {
remoteIP = strings.Split(remoteAddr, ",")[0]
}
logger.Infof("%s connect websocket from %s\n", user.Username, remoteIP)
logger.Infof("Accepted %s connect websocket from %s", user.Username, remoteIP)
conn := newWebConn(s.ID(), s, remoteIP, user)
ctx := WebContext{User: user, Connection: conn}
s.SetContext(ctx)
conns.AddWebConn(s.ID(), conn)
logger.Info("On Connect handler end")
return nil
}
// OnErrorHandler 当出现错误时触发
func OnErrorHandler(e error) {
logger.Debug("OnError trigger")
logger.Debug(e)
logger.Debug("Web terminal on error trigger: ", e)
}
// OnHostHandler 当用户连接Host时触发
func OnHostHandler(s socketio.Conn, message HostMsg) {
// secret uuid string
logger.Debug("On host event trigger")
logger.Debug("Web terminal on host event trigger")
win := ssh.Window{Height: 24, Width: 80}
assetID := message.Uuid
systemUserID := message.UserID
......@@ -93,15 +90,15 @@ func OnHostHandler(s socketio.Conn, message HostMsg) {
win.Height = height
}
clientID := uuid.NewV4().String()
emitMsg := EmitRoomMsg{clientID, secret}
emitMsg := RoomMsg{clientID, secret}
s.Emit("room", emitMsg)
logger.Debug("Asset id: ", assetID)
asset := service.GetAsset(assetID)
systemUser := service.GetSystemUser(systemUserID)
if asset.ID == "" || systemUser.ID == "" {
return
}
logger.Debug("Web terminal want to connect host: ", asset.Hostname)
ctx := s.Context().(WebContext)
userR, userW := io.Pipe()
......@@ -118,12 +115,15 @@ func OnHostHandler(s socketio.Conn, message HostMsg) {
UserConn: client, User: ctx.User,
Asset: &asset, SystemUser: &systemUser,
}
go proxySrv.Proxy()
go func() {
proxySrv.Proxy()
s.Emit("logout", RoomMsg{Room: clientID})
}()
}
// OnTokenHandler 当使用token连接时触发
func OnTokenHandler(s socketio.Conn, message TokenMsg) {
logger.Debug("On token event trigger")
logger.Debug("Web terminal on token event trigger")
win := ssh.Window{Height: 24, Width: 80}
token := message.Token
secret := message.Secret
......@@ -135,7 +135,7 @@ func OnTokenHandler(s socketio.Conn, message TokenMsg) {
win.Height = height
}
clientID := uuid.NewV4().String()
emitMs := EmitRoomMsg{clientID, secret}
emitMs := RoomMsg{clientID, secret}
s.Emit("room", emitMs)
// check token
......@@ -177,7 +177,10 @@ func OnTokenHandler(s socketio.Conn, message TokenMsg) {
UserConn: &client, User: currentUser,
Asset: &asset, SystemUser: &systemUser,
}
go proxySrv.Proxy()
go func() {
proxySrv.Proxy()
s.Emit("logout", RoomMsg{Room: clientID})
}()
}
// OnDataHandler 收发数据时触发
......@@ -193,15 +196,15 @@ func OnDataHandler(s socketio.Conn, message DataMsg) {
// OnResizeHandler 用户窗口改变时触发
func OnResizeHandler(s socketio.Conn, message ResizeMsg) {
logger.Debugf("Web terminal on resize event trigger: %d*%d", message.Width, message.Height)
winSize := ssh.Window{Height: message.Height, Width: message.Width}
logger.Debugf("On resize event trigger: %d*%d", message.Width, message.Height)
conn := conns.GetWebConn(s.ID())
conn.SetWinSize(winSize)
}
// OnLogoutHandler 用户登出一个会话时触发
func OnLogoutHandler(s socketio.Conn, message string) {
logger.Debug("On logout event trigger")
logger.Debug("Web terminal on logout event trigger")
conn := conns.GetWebConn(s.ID())
if conn == nil {
logger.Error("No conn found")
......
......@@ -31,8 +31,8 @@ func StartHTTPServer() {
server.OnEvent("/ssh", "resize", OnResizeHandler)
server.OnEvent("/ssh", "logout", OnLogoutHandler)
server.OnConnect("/elfinder", OnElfinderConnect)
server.OnDisconnect("/elfinder", OnElfinderDisconnect)
server.OnConnect("/elfinder", OnELFinderConnect)
server.OnDisconnect("/elfinder", OnELFinderDisconnect)
go server.Serve()
defer server.Close()
......@@ -44,8 +44,7 @@ func StartHTTPServer() {
router.Handle("/socket.io/", server)
router.HandleFunc("/coco/elfinder/sftp/{host}/", sftpHostFinder)
router.HandleFunc("/coco/elfinder/sftp/", sftpFinder)
router.HandleFunc("/coco/elfinder/sftp/connector/{host}/",
sftpHostConnectorView).Methods("GET", "POST")
router.HandleFunc("/coco/elfinder/sftp/connector/{host}/", sftpHostConnectorView).Methods("GET", "POST")
addr := net.JoinHostPort(conf.BindHost, conf.HTTPPort)
logger.Debug("Start HTTP server at ", addr)
......
......@@ -34,6 +34,13 @@ func Initial() {
go KeepSyncConfigWithServer()
}
func newClient() *common.Client {
cf := config.GetConf()
cli := common.NewClient(30, "")
cli.BaseHost = cf.CoreHost
return cli
}
func validateAccessAuth() {
cf := config.GetConf()
maxTry := 30
......
package service
const (
UserAuthURL = "/api/users/v1/auth/" // post 验证用户登陆
UserAuthURL = "/api/authentication/v1/auth/" // post 验证用户登陆
UserProfileURL = "/api/users/v1/profile/" // 获取当前用户的基本信息
UserListUrl = "/api/users/v1/users/" // 用户列表地址
UserDetailURL = "/api/users/v1/users/%s/" // 获取用户信息
......
......@@ -69,8 +69,9 @@ func CheckUserOTP(seed, code string) (resp *AuthResp, err error) {
}
func CheckUserCookie(sessionID, csrfToken string) (user *model.User, err error) {
client.SetCookie("csrftoken", csrfToken)
client.SetCookie("sessionid", sessionID)
err = client.Get(UserProfileURL, &user)
cli := newClient()
cli.SetCookie("csrftoken", csrfToken)
cli.SetCookie("sessionid", sessionID)
err = cli.Get(UserProfileURL, &user)
return
}
......@@ -2,6 +2,7 @@ package srvconn
import (
"cocogo/pkg/service"
"encoding/json"
"errors"
"fmt"
"net"
......@@ -22,13 +23,13 @@ var (
)
type SSHClientConfig struct {
Host string
Port string
User string
Password string
PrivateKey string
PrivateKeyPath string
Timeout time.Duration
Host string `json:"host"`
Port string `json:"port"`
User string `json:"user"`
Password string `json:"password"`
PrivateKey string `json:"private_key"`
PrivateKeyPath string `json:"private_key_path"`
Timeout time.Duration `json:"timeout"`
Proxy []*SSHClientConfig
proxyConn gossh.Conn
......@@ -66,13 +67,14 @@ func (sc *SSHClientConfig) Config() (config *gossh.ClientConfig, err error) {
func (sc *SSHClientConfig) DialProxy() (client *gossh.Client, err error) {
for _, p := range sc.Proxy {
logger.Debug("Connect proxy: .......")
data, _ := json.Marshal(p)
fmt.Println(string(data))
client, err = p.Dial()
if err == nil {
logger.Debug("Connect proxy host %s:%s success", p.Host, p.Port)
logger.Debugf("Connect proxy host %s:%s success", p.Host, p.Port)
return
} else {
logger.Errorf("Connect proxy host %s:%s error: ", p.Host, p.Port, err)
logger.Errorf("Connect proxy host %s:%s error: %s", p.Host, p.Port, err)
}
}
return
......@@ -135,6 +137,11 @@ func MakeConfig(asset *model.Asset, systemUser *model.SystemUser, timeout time.D
}
}
}
if systemUser.Password == "" && systemUser.PrivateKey == "" {
info := service.GetSystemUserAssetAuthInfo(systemUser.ID, asset.ID)
systemUser.Password = info.Password
systemUser.PrivateKey = info.PrivateKey
}
conf = &SSHClientConfig{
Host: asset.IP,
Port: strconv.Itoa(asset.Port),
......@@ -168,7 +175,7 @@ func NewClient(user *model.User, asset *model.Asset, systemUser *model.SystemUse
clientsRefCounter[client]++
var counter = clientsRefCounter[client]
logger.Infof("Reuse connection: %s->%s@%s\n ref: %d", u, sysName, ip, counter)
logger.Infof("Reuse connection: %s->%s@%s ref: %d", u, sysName, ip, counter)
clientLock.Unlock()
return client, nil
}
......@@ -215,8 +222,8 @@ func RecycleClient(client *gossh.Client) {
delete(sshClients, key)
}
} else {
logger.Debug("Recycle client: ref -1")
clientsRefCounter[client]--
logger.Debugf("Recycle client: ref -1: %d", clientsRefCounter[client])
}
}
}
package srvconn
import (
"github.com/pkg/errors"
"errors"
"io"
"time"
......
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