A Friendly SSH Jumper Bastion Fortress Server

Felix 中文

Build Status Go Report Card

Thanks to 360 Total Security and 360-web-platform

For Whom

  • BackEnd Engineer
  • Golang SQL RESTful APIs engineer
  • DevOps Engineer
  • People Heavily engage with SSH

Do What

  • a slim jump SSH server with Web UI
  • manage massive SSH login configuration
  • ssh login speedily
  • generate a RESTful app from SQL database with gin-gonic/gin and GORM in GO
  • start TCP and SOCK proxy with ssh speedily
  • terminal todo task list
  • Pewdiepie's brofit command to subscribe the Youtube channel

Demo config.toml file

[spiderhn]
    cookie="user=neoxhau&SlKqTK32QSFSiWQu1vGgCr4aqvTx5NxT" #some cookie for hacknews spider
    userAgent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36"
    youdaoAppKey="xxx" # youdao tranlate app key
    youdaoAppHost="http://openapi.youdao.com/api"
    youdaoAppSecret="xx" # youdao tranlate app secret
    
[felix]
    slack="https://home.mojotv.cn/api/wslog/hook-api?_t=kiz9Wf-O6_lbY2pz2lGEeA" 
[sshw]
    addr=":2222"
    user="xxx" # init user
    password="xxx" # init password
    secret="9cAmRT3y&s6n$*3Lx*LaWZIi9I7yoNSK" # jwt secret 32 byte
    expire= 24 #jwt expire hours
[tech_mojotv_cn]
    srcDir="D:\\code\\tech.mojotv.cn" # my jekyll blog
[libragen_cn]
    srcDir="D:\\code\\libragen.cn" # my jekyll blog about libragen

Demo Nginx Config file

server {
        server_name home.mojotv.cn;
        charset utf-8;

        location /ws/
        {
                 proxy_pass http://127.0.0.1:2222;
                 proxy_http_version 1.1;
                 proxy_set_header Upgrade $http_upgrade;
                 proxy_set_header Connection "Upgrade";
                proxy_set_header X-Real-IP $remote_addr;
         }
        location / {
           proxy_set_header X-Forwarded-For $remote_addr;
           proxy_set_header Host $http_host;
           proxy_pass http://127.0.0.1:2222;
        }
        access_log  /data/wwwlogs/home.mojotv.cn.log;

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/captcha.mojotv.cn/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/captcha.mojotv.cn/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}
server {
    if ($host = home.mojotv.cn) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


        listen 80;
        server_name home.mojotv.cn;
    return 404; # managed by Certbot


}
        

Overview

commands:

$ felix -h
Usage:
  felix [flags]
  felix [command]

Available Commands:
  brofist     Pewdiepie needs your help.Do your part to subscribe Pewdiepie's Youtube Channel.
  clean       purge all felix configuration
  ginbin      Ginbin allows you to embed a directory of static files into your Go binary to be later served from github.com/gin-goin/gin
  ginbro      generate a RESTful codebase from SQL database
  godoc       golang.google.cn/pkg
  help        Help about any command
  json        open a tab in browser to convert json to golang struct
  scpd        scp download file or folder
  scpu        scp upload a file or a folder
  ssh         open a ssh terminal
  sshad       add a ssh connection configuration
  sshdu       duplicate a ssh connection
  sshedit     update a ssh connection
  sshexport   export all ssh connection configuration to a csv file
  sshimport   import massive ssh connection configuration from a csv file
  sshinfo     view a ssh connection
  sshls       list all ssh connection configuration or search by hostname
  sshproxy    ssh port proxy
  sshrm       delete a ssh connection
  sshsocks    start a socks4/5 proxy
  sshw        open a web UI for Felix http://localhost:2222
  task        list all rows in TaskList
  taskad      add a row into TaskList
  taskok      set a row done in TaskList
  taskrm      remove a row in TaskList

Flags:
  -h, --help      help for felix
      --verbose   verbose
  -V, --version   show binary build information

Use "felix [command] --help" for more information about a command.

Build and Run

or just go build run binary by ./felix

ScreenShot

command: felix sshw

$ felix sshw -h
the demo website is http://home.mojotv.cn:2222

Usage:
  felix sshw [flags]

Flags:
  -a, --addr string       listening addr (default ":2222")
  -x, --expire uint       token expire in * minute (default 1440)
  -h, --help              help for sshw
  -p, --password string   auth password (default "admin")
  -s, --secret string     jwt secret string
  -u, --user string       auth user (default "admin")

Global Flags:
      --verbose   verbose
$ felix sshw
use random string as jwt secret: @Ubr)Vrp~Zoo6Rvrk1PP1*ZXPYby_Z)s
login user: admin
login password: admin
login expire in 1440 minutes
[GIN-debug] [WARNING] Now Gin requires Go 1.6 or later and Go 1.7 will be required soon.

[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "_release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] POST   /api/login                --> github.com/libragen/felix/ssh2ws/controllers.GetLoginHandler.func1 (4 handlers)
[GIN-debug] GET    /api/ws/:id               --> github.com/libragen/felix/ssh2ws/controllers.WsSsh (5 handlers)
[GIN-debug] GET    /api/ssh                  --> github.com/libragen/felix/ssh2ws/controllers.SshAll (5 handlers)
[GIN-debug] POST   /api/ssh                  --> github.com/libragen/felix/ssh2ws/controllers.SshCreate (5 handlers)
[GIN-debug] GET    /api/ssh/:id              --> github.com/libragen/felix/ssh2ws/controllers.SshOne (5 handlers)
[GIN-debug] PATCH  /api/ssh/:id              --> github.com/libragen/felix/ssh2ws/controllers.SshUpdate (5 handlers)
[GIN-debug] DELETE /api/ssh/:id              --> github.com/libragen/felix/ssh2ws/controllers.SshDelete (5 handlers)
[GIN-debug] GET    /api/sftp/:id             --> github.com/libragen/felix/ssh2ws/controllers.SftpLs (5 handlers)
[GIN-debug] GET    /api/sftp/:id/dl          --> github.com/libragen/felix/ssh2ws/controllers.SftpDl (5 handlers)
[GIN-debug] GET    /api/sftp/:id/cat         --> github.com/libragen/felix/ssh2ws/controllers.SftpCat (5 handlers)
[GIN-debug] GET    /api/sftp/:id/rm          --> github.com/libragen/felix/ssh2ws/controllers.SftpRm (5 handlers)
[GIN-debug] GET    /api/sftp/:id/rename      --> github.com/libragen/felix/ssh2ws/controllers.SftpRename (5 handlers)
[GIN-debug] GET    /api/sftp/:id/mkdir       --> github.com/libragen/felix/ssh2ws/controllers.SftpMkdir (5 handlers)
[GIN-debug] POST   /api/sftp/:id/up          --> github.com/libragen/felix/ssh2ws/controllers.SftpUp (5 handlers)
[GIN-debug] Listening and serving HTTP on :2222

command: felix ginbro ginbro doc

felix ginbro command is migrated from libragen/ginbro. Generated WordPress MySQL database running demo

Generated code base

$ felix ginbro
Error: required flag(s) "appDir", "dbAddr" not set
Usage:
  felix ginbro [flags]

Examples:
felix rest -u root -p password -a "127.0.0.1:3306" -d dbname -c utf8 --authTable=users --authColumn=pw_column -o=FelixRestOut"

Flags:
  -o, --appDir string       app's code output directory
  -l, --appListen string    app's listening addr (default "127.0.0.1:5555")
      --authColumn string   bcrypt password column (default "password")
      --authTable string    login user table (default "users")
  -a, --dbAddr string       database connection addr (default "127.0.0.1:3306")
  -c, --dbCharset string    database charset (default "utf8")
  -n, --dbName string       database name
  -p, --dbPassword string   database user password (default "password")
  -t, --dbType string       database type: mysql/postgres/mssql/sqlite (default "mysql")
  -u, --dbUser string       database username (default "root")
  -h, --help                help for ginbro

Global Flags:
      --verbose   verbose

required flag(s) "appDir", "dbAddr" not set

the output code base

ginbro output code base dir

command: felix sshls

felix sshls

command: felix ssh 3

felix sshls

command: felix ssh 2

felix sshls

command: felix sshsocks 34 -l 1080

felix sshls

command: felix taskad

felix sshls

Code Logic

  • save SSH configurations into a SQLite.db in $HOME/.felix.db
  • use spf13/cobra as command framework

felix ginbro Logic

  1. use SQL query get all tables and column schema from database
  2. transform SQL type into Golang type and Swagger Doc type
  3. use Golang Std Lib(text/template) to output Gin's handler and Route files and GORM model files
  4. os.exec call go fmt to format the output codebase

felix sshls Logic

  1. use GORM retrieve all SSH configuration from SQLite.db
  2. use olekukonko/tablewriter write table into terminal

felix ssh 9 Logic

  1. get a ssh configuration by ID
  2. use golang.org/x/crypto/ssh package start ssh session
  3. customize stdin and stdout to listen Sudo command for password message then input sudo password automatically
Comments
  • felix sshw 报错

    felix sshw 报错

    `felix sshw
    panic: interface conversion: interface {} is string, not []uint8 [recovered] panic: interface conversion: interface {} is string, not []uint8

    goroutine 1 [running]: github.com/jinzhu/gorm.(*Scope).callCallbacks.func1(0xc0006a3200) /Users/yxs/GolandProjects/src/github.com/jinzhu/gorm/scope.go:884 +0xb3 panic(0x48ac640, 0xc0006b4600) /usr/local/Cellar/go/1.11/libexec/src/runtime/panic.go:513 +0x1b9 github.com/dejavuzhou/felix/models.(*JsonArrayUint).Scan(0xc0001518c0, 0x485fd00, 0xc000069840, 0x6294200, 0xc0001518c0) /Users/yxs/GolandProjects/src/github.com/dejavuzhou/felix/models/j_array_uint.go:16 +0xa3 database/sql.convertAssign(0x4901cc0, 0xc0001518c0, 0x485fd00, 0xc000069840, 0xc0001518c0, 0x16) /usr/local/Cellar/go/1.11/libexec/src/database/sql/convert.go:341 +0x17c8 database/sql.convertAssign(0xc0001ccbc0, 0xc000444eb0, 0x485fd00, 0xc000069840, 0x0, 0x0) /usr/local/Cellar/go/1.11/libexec/src/database/sql/convert.go:384 +0xc14 database/sql.(*Rows).Scan(0xc0006a3300, 0xc000128400, 0x10, 0x10, 0xc0000ac0b0, 0x16) /usr/local/Cellar/go/1.11/libexec/src/database/sql/sql.go:2932 +0x1e7 github.com/jinzhu/gorm.(*Scope).scan(0xc0006a3200, 0xc0006a3300, 0xc00044df00, 0x10, 0x10, 0xc000128300, 0x11, 0x20) /Users/yxs/GolandProjects/src/github.com/jinzhu/gorm/scope.go:532 +0x499 github.com/jinzhu/gorm.queryCallback(0xc0006a3200) /Users/yxs/GolandProjects/src/github.com/jinzhu/gorm/callback_query.go:79 +0x523 github.com/jinzhu/gorm.(*Scope).callCallbacks(0xc0006a3200, 0xc0001c7600, 0x3, 0x4, 0x0) /Users/yxs/GolandProjects/src/github.com/jinzhu/gorm/scope.go:888 +0x88 github.com/jinzhu/gorm.(*DB).First(0xc0006aaea0, 0x4911ba0, 0xc000691c20, 0x0, 0x0, 0x0, 0x48909c0) /Users/yxs/GolandProjects/src/github.com/jinzhu/gorm/main.go:331 +0x10d github.com/jinzhu/gorm.(*DB).FirstOrCreate(0xc0006aaea0, 0x4911ba0, 0xc000691c20, 0x0, 0x0, 0x0, 0xc0006aaea0) /Users/yxs/GolandProjects/src/github.com/jinzhu/gorm/main.go:425 +0x9c github.com/dejavuzhou/felix/models.(*User).CreateInitUser(0xc000691c20, 0xc000691c20, 0xa) /Users/yxs/GolandProjects/src/github.com/dejavuzhou/felix/models/m_users.go:105 +0x105 github.com/dejavuzhou/felix/models.CreateSqliteDB(0x0) /Users/yxs/GolandProjects/src/github.com/dejavuzhou/felix/models/db.go:41 +0x532 github.com/dejavuzhou/felix/cmd.initFunc() /Users/yxs/GolandProjects/src/github.com/dejavuzhou/felix/cmd/root.go:53 +0x2c github.com/spf13/cobra.(*Command).preRun(0x51fd020) /Users/yxs/GolandProjects/src/github.com/spf13/cobra/command.go:856 +0x49 github.com/spf13/cobra.(*Command).execute(0x51fd020, 0x522d938, 0x0, 0x0, 0x51fd020, 0x522d938) /Users/yxs/GolandProjects/src/github.com/spf13/cobra/command.go:792 +0x148 github.com/spf13/cobra.(*Command).ExecuteC(0x51faaa0, 0xc0000e3f58, 0x46bfe81, 0xc00044d100) /Users/yxs/GolandProjects/src/github.com/spf13/cobra/command.go:914 +0x2f8 github.com/spf13/cobra.(*Command).Execute(0x51faaa0, 0xc0000e3f58, 0x46bff6e) /Users/yxs/GolandProjects/src/github.com/spf13/cobra/command.go:864 +0x2b github.com/dejavuzhou/felix/cmd.Execute(0x0, 0x0, 0x0, 0x0) /Users/yxs/GolandProjects/src/github.com/dejavuzhou/felix/cmd/root.go:37 +0x77 main.main() /Users/yxs/GolandProjects/src/github.com/dejavuzhou/felix/main.go:8 +0x51 `

    另外找到相同的问题 https://github.com/dejavuzhou/felix/issues/5 没有felix flush 这个指令 felix flush
    Error: unknown command "flush" for "felix" Run 'felix --help' for usage. unknown command "flush" for "felix"

  • Is the felixbin code available ?

    Is the felixbin code available ?

    Hey guys,

    Great job, with felix ! I was wondering if the felixbin code which is in the zipdata was available. And if not I wanted to know how you done the conversion to zipdata.

    Thanks a lot !

  • 找不到config

    找不到config

    看了一下readme,感觉像是要自己配个config ? 我用docker(golang:1.13)直接按照编译和运行的流程后,执行felix sshw出现:

    FATA[0000] Fatal error config file: Config File "config" Not Found in "[/go/src/github.com/libragen/felix /etc/felix /root/.felix]"

    是程序出错了还是需要自己配个config还是? 如果需要配置个config,能否直接提供一份default config在代码中,一开始要额外编辑,对docker玩家有点难受呀。

  • [install error]macOS 10.13 github.com/jinzhu/gorm/dialects/sqlite

    [install error]macOS 10.13 github.com/jinzhu/gorm/dialects/sqlite

    • env
    ProductName:	Mac OS X
    ProductVersion:	10.13.6
    BuildVersion:	17G702
    go version go1.11.4 darwin/amd64
    gcc -v                                                                                                                                                                                                    
    Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
    Apple LLVM version 10.0.0 (clang-1000.11.45.5)
    Target: x86_64-apple-darwin17.7.0
    Thread model: posix
    InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
    
    • error info
     $ go install -v -a
    github.com/dejavuzhou/felix/utils
    # github.com/mattn/go-sqlite3
    sqlite3-binding.c:18709:17: warning: 'OSAtomicCompareAndSwapPtrBarrier' is deprecated: first deprecated in macOS 10.12 - Use atomic_compare_exchange_strong() from <stdatomic.h> instead [-Wdeprecated-declarations]
    /usr/include/libkern/OSAtomicDeprecated.h:547:6: note: 'OSAtomicCompareAndSwapPtrBarrier' has been explicitly marked deprecated here
    # github.com/dejavuzhou/felix/utils
    utils/ssh_shell_conn.go:134:30: ssConn.Session.WindowChange undefined (type *ssh.Session has no field or method WindowChange)
    
  • 执行felix sshw启动报错

    执行felix sshw启动报错

    root@debian:/opt/goproject/src/github.com/dejavuzhou/felix# felix sshw panic: interface conversion: interface {} is string, not []uint8 [recovered] panic: interface conversion: interface {} is string, not []uint8

    goroutine 1 [running]: github.com/jinzhu/gorm.(*Scope).callCallbacks.func1(0xc00016d900) /opt/goproject/src/github.com/jinzhu/gorm/scope.go:884 +0xb0 panic(0xc85400, 0xc00018d080) /opt/soft/go/src/runtime/panic.go:522 +0x1b5 github.com/dejavuzhou/felix/models.(*JsonArrayUint).Scan(0xc00008c560, 0xc37ce0, 0xc0003f0d30, 0x7ff00783d238, 0xc00008c560) /opt/goproject/src/github.com/dejavuzhou/felix/models/j_array_uint.go:16 +0xa3 database/sql.convertAssignRows(0xcc2d20, 0xc00008c560, 0xc37ce0, 0xc0003f0d30, 0xc00016da00, 0xc00008c560, 0x0) /opt/soft/go/src/database/sql/convert.go:378 +0x189a database/sql.convertAssignRows(0xc000066a00, 0xc000010aa0, 0xc37ce0, 0xc0003f0d30, 0xc00016da00, 0x0, 0x0) /opt/soft/go/src/database/sql/convert.go:421 +0xf2f database/sql.(*Rows).Scan(0xc00016da00, 0xc0000c22a0, 0xe, 0xe, 0xc000074798, 0xc000010aa0) /opt/soft/go/src/database/sql/sql.go:2955 +0x212 github.com/jinzhu/gorm.(*Scope).scan(0xc00016d900, 0xc00016da00, 0xc0000c2000, 0xe, 0xe, 0xc00016db00, 0xf, 0x10) /opt/goproject/src/github.com/jinzhu/gorm/scope.go:532 +0x55b github.com/jinzhu/gorm.queryCallback(0xc00016d900) /opt/goproject/src/github.com/jinzhu/gorm/callback_query.go:79 +0x5f8 github.com/jinzhu/gorm.(*Scope).callCallbacks(0xc00016d900, 0xc0001a1520, 0x3, 0x4, 0x0) /opt/goproject/src/github.com/jinzhu/gorm/scope.go:888 +0x88 github.com/jinzhu/gorm.(*DB).First(0xc0003e7110, 0xcec7e0, 0xc0001aae10, 0x0, 0x0, 0x0, 0xc68ce0) /opt/goproject/src/github.com/jinzhu/gorm/main.go:331 +0x128 github.com/jinzhu/gorm.(*DB).FirstOrCreate(0xc0003e7110, 0xcec7e0, 0xc0001aae10, 0x0, 0x0, 0x0, 0xc0003e7110) /opt/goproject/src/github.com/jinzhu/gorm/main.go:425 +0x9c github.com/dejavuzhou/felix/models.(*User).CreateInitUser(0xc0001aae10, 0xc0001aae10, 0x9) /opt/goproject/src/github.com/dejavuzhou/felix/models/m_users.go:107 +0xfd github.com/dejavuzhou/felix/models.CreateSqliteDB(0x0) /opt/goproject/src/github.com/dejavuzhou/felix/models/db.go:41 +0x5a8 github.com/dejavuzhou/felix/cmd.initFunc() /opt/goproject/src/github.com/dejavuzhou/felix/cmd/root.go:53 +0x2c github.com/spf13/cobra.(*Command).preRun(0x1861240) /opt/goproject/src/github.com/spf13/cobra/command.go:856 +0x49 github.com/spf13/cobra.(*Command).execute(0x1861240, 0x18ca6c8, 0x0, 0x0, 0x1861240, 0x18ca6c8) /opt/goproject/src/github.com/spf13/cobra/command.go:792 +0x146 github.com/spf13/cobra.(*Command).ExecuteC(0x185ecc0, 0xc000021200, 0x18a9680, 0x0) /opt/goproject/src/github.com/spf13/cobra/command.go:914 +0x2fc github.com/spf13/cobra.(*Command).Execute(...) /opt/goproject/src/github.com/spf13/cobra/command.go:864 github.com/dejavuzhou/felix/cmd.Execute(0x0, 0x0, 0x0, 0x0) /opt/goproject/src/github.com/dejavuzhou/felix/cmd/root.go:37 +0x7c main.main() /opt/goproject/src/github.com/dejavuzhou/felix/main.go:8 +0x51

  • go get error

    go get error

    go get: github.com/libragen/[email protected]: parsing go.mod: module declares its path as: github.com/dejavuzhou/felix but was required as: github.com/libragen/felix

  • Fix tpl_core.go create directory permissions

    Fix tpl_core.go create directory permissions

    Changed created directory permissions from 644 (-w----r--) to 0644 (rw-r--r--) when calling MkdirAll(...).

    PoC:

    dc@jhtc:~/gogo/makedir$ cat main.go
    package main
    
    import "os"
    
    func main() {
        os.MkdirAll("644", 644)
        os.MkdirAll("0644", 0644)
    }
    dc@jhtc:~/gogo/makedir$ go run main.go
    ls dc@jhtc:~/gogo/makedir$ ls -la
    total 20
    drwxrwxr-x 4 dc dc 4096 Jun  9 17:10 .
    drwxrwxr-x 5 dc dc 4096 Jun  9 17:10 ..
    drw-r--r-- 2 dc dc 4096 Jun  9 17:10 0644
    d-w----r-- 2 dc dc 4096 Jun  9 17:10 644
    -rw-rw-r-- 1 dc dc  101 Jun  9 17:05 main.go
    
  • 命令的结果输出是原命令,而不是命令执行结果;

    命令的结果输出是原命令,而不是命令执行结果;

    `package main

    import ( "bufio" "bytes" "fmt" "github.com/gin-gonic/gin" "github.com/mitchellh/go-homedir" "io/ioutil" "log" "os" "strconv" "strings" "time"

    "encoding/base64"
    "encoding/json"
    "github.com/gorilla/websocket"
    "github.com/sirupsen/logrus"
    "golang.org/x/crypto/ssh"
    "io"
    "net/http"
    "sync"
    

    )

    const ( wsMsgCmd = "cmd" wsMsgResize = "resize" )

    type wsMsg struct { Type string json:"type" Cmd string json:"cmd" Cols int json:"cols" Rows int json:"rows" }

    // copy data from WebSocket to ssh server // and copy data from ssh server to WebSocket

    // write data to WebSocket // the data comes from ssh server. type wsBufferWriter struct { buffer bytes.Buffer mu sync.Mutex }

    // implement Write interface to write bytes from ssh server into bytes.Buffer. func (w *wsBufferWriter) Write(p []byte) (int, error) { w.mu.Lock() defer w.mu.Unlock() return w.buffer.Write(p) }

    func NewSshClient() (*ssh.Client, error) { config := &ssh.ClientConfig{ Timeout: time.Second * 50000, User: "root", HostKeyCallback: ssh.InsecureIgnoreHostKey(), //这个可以, 但是不够安全 //HostKeyCallback: hostKeyCallBackFunc("192.168.96.109"), } config.Auth = []ssh.AuthMethod{ssh.Password("cp123456")} //config.Auth = []ssh.AuthMethod{publicKeyAuthFunc(h.Key)} //addr := fmt.Sprintf("%s:%d", h.Host, h.Port) addr := "192.168.96.109:22" c, err := ssh.Dial("tcp", addr, config) if err != nil { return nil, err } return c, nil } func hostKeyCallBackFunc(host string) ssh.HostKeyCallback { hostPath, err := homedir.Expand("~/.ssh/known_hosts") if err != nil { log.Fatal("find known_hosts's home dir failed", err) } file, err := os.Open(hostPath) if err != nil { log.Fatal("can't find known_host file:", err) } defer file.Close() scanner := bufio.NewScanner(file) var hostKey ssh.PublicKey for scanner.Scan() { fields := strings.Split(scanner.Text(), " ") if len(fields) != 3 { continue } if strings.Contains(fields[0], host) { var err error hostKey, _, _, _, err = ssh.ParseAuthorizedKey(scanner.Bytes()) if err != nil { log.Fatalf("error parsing %q: %v", fields[2], err) } break } } if hostKey == nil { log.Fatalf("no hostkey for %s,%v", host, err) } return ssh.FixedHostKey(hostKey) }

    func publicKeyAuthFunc(kPath string) ssh.AuthMethod { keyPath, err := homedir.Expand(kPath) if err != nil { log.Fatal("find key's home dir failed", err) } key, err := ioutil.ReadFile(keyPath) if err != nil { log.Fatal("ssh key file read failed", err) } // Create the Signer for this private key. signer, err := ssh.ParsePrivateKey(key) if err != nil { log.Fatal("ssh key signer failed", err) } return ssh.PublicKeys(signer) } func runCommand(client *ssh.Client, command string) (stdout string, err error) { session, err := client.NewSession() if err != nil { //log.Print(err) return } defer session.Close()

    var buf bytes.Buffer
    session.Stdout = &buf
    err = session.Run(command)
    if err != nil {
        //log.Print(err)
        return
    }
    stdout = string(buf.Bytes())
    
    return
    

    }

    // connect to ssh server using ssh session. type SshConn struct { // calling Write() to write data into ssh server StdinPipe io.WriteCloser // Write() be called to receive data from ssh server ComboOutput *wsBufferWriter Session *ssh.Session }

    //flushComboOutput flush ssh.session combine output into websocket response func flushComboOutput(w *wsBufferWriter, wsConn *websocket.Conn) error { if w.buffer.Len() != 0 { fmt.Println(string(w.buffer.Bytes())) err := wsConn.WriteMessage(websocket.TextMessage, w.buffer.Bytes()) if err != nil { return err } w.buffer.Reset() } return nil } // setup ssh shell session // set Session and StdinPipe here, // and the Session.Stdout and Session.Sdterr are also set. func NewSshConn(cols, rows int, sshClient *ssh.Client) (*SshConn, error) { sshSession, err := sshClient.NewSession() if err != nil { fmt.Println("NewSession failed; msg:", err) return nil, err }

    // we set stdin, then we can write data to ssh server via this stdin.
    // but, as for reading data from ssh server, we can set Session.Stdout and Session.Stderr
    // to receive data from ssh server, and write back to somewhere.
    stdinP, err := sshSession.StdinPipe()
    if err != nil {
        return nil, err
    }
    
    comboWriter := new(wsBufferWriter)
    //ssh.stdout and stderr will write output into comboWriter
    sshSession.Stdout = comboWriter
    sshSession.Stderr = comboWriter
    
    modes := ssh.TerminalModes{
        ssh.ECHO:          1,     // disable echo
        ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
        ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
    }
    // Request pseudo terminal
    if err := sshSession.RequestPty("xterm", rows, cols, modes); err != nil {
        fmt.Println("RequestPty failed; msg: ", err.Error())
        return nil, err
    }
    // Start remote shell
    if err := sshSession.Shell(); err != nil {
        fmt.Println("ssh.Shell failed; msg:", err.Error())
        return nil, err
    }
    return &SshConn{StdinPipe: stdinP, ComboOutput: comboWriter, Session: sshSession}, nil
    

    } func (s *SshConn) Close() { if s.Session != nil { s.Session.Close() } } //ReceiveWsMsg receive websocket msg do some handling then write into ssh.session.stdin func (ssConn *SshConn) ReceiveWsMsg(wsConn *websocket.Conn, logBuff bytes.Buffer, exitCh chan bool) { //tells other go routine quit defer setQuit(exitCh) for { select { case <-exitCh: return default: //read websocket msg _, wsData, err := wsConn.ReadMessage() if err != nil { logrus.WithError(err).Error("reading webSocket message failed") return } //unmashal bytes into struct msgObj := wsMsg{} if err := json.Unmarshal(wsData, &msgObj); err != nil { logrus.WithError(err).WithField("wsData", string(wsData)).Error("unmarshal websocket message failed") } switch msgObj.Type { case wsMsgResize: //handle xterm.js size change if msgObj.Cols > 0 && msgObj.Rows > 0 { if err := ssConn.Session.WindowChange(msgObj.Rows, msgObj.Cols); err != nil { logrus.WithError(err).Error("ssh pty change windows size failed") } } case wsMsgCmd: //handle xterm.js stdin decodeBytes, err := base64.StdEncoding.DecodeString(msgObj.Cmd) if err != nil { logrus.WithError(err).Error("websock cmd string base64 decoding failed") } //if _, err := ssConn.StdinPipe.Write(decodeBytes); err != nil { if _, err := ssConn.StdinPipe.Write(decodeBytes); err != nil { logrus.WithError(err).Error("ws cmd bytes write to ssh.stdin pipe failed") } / //write input cmd to log buffer if _, err := logBuff.Write(decodeBytes); err != nil { logrus.WithError(err).Error("write received cmd into log buffer failed") } */ } } } }

    func (ssConn *SshConn) SendComboOutput(wsConn *websocket.Conn, exitCh chan bool) { //tells other go routine quit defer setQuit(exitCh)

    //every 120ms write combine output bytes into websocket response
    tick := time.NewTicker(time.Millisecond * time.Duration(120))
    //for range time.Tick(120 * time.Millisecond){}
    defer tick.Stop()
    for {
        select {
        case <-tick.C:
            //write combine output bytes into websocket response
            if err := flushComboOutput(ssConn.ComboOutput, wsConn); err != nil {
                logrus.WithError(err).Error("ssh sending combo output to webSocket failed")
                return
            }
        case <-exitCh:
            return
        }
    }
    

    }

    func (ssConn *SshConn) SessionWait(quitChan chan bool) { if err := ssConn.Session.Wait(); err != nil { logrus.WithError(err).Error("ssh session wait failed") setQuit(quitChan) } }

    func setQuit(ch chan bool) { ch <- true }

    func web(c *gin.Context) { //打印请求的方法 c.JSON(http.StatusOK, gin.H{"msg": "请求已提交", "status": 0}) //c.HTML(http.StatusOK, "websocket.html", gin.H{"title": "Posts"}) }

    var upGrader = websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024 * 1024 * 10, CheckOrigin: func(r *http.Request) bool { return true }, }

    // handle webSocket connection. // first,we establish a ssh connection to ssh server when a webSocket comes; // then we deliver ssh data via ssh connection between browser and ssh server. // That is, read webSocket data from browser (e.g. 'ls' command) and send data to ssh server via ssh connection; // the other hand, read returned ssh data from ssh server and write back to browser via webSocket API. func WsSsh(c *gin.Context) { fmt.Println("Wsssh...") wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil) if err != nil { fmt.Printf("upGrader failed; msg: %s", err.Error()) } defer wsConn.Close()

    cols, err := strconv.Atoi(c.DefaultQuery("cols", "120"))
    if err != nil {
        fmt.Printf("Atoi failed; msg: %s", err.Error())
    }
    rows, err := strconv.Atoi(c.DefaultQuery("rows", "32"))
    if err != nil {
        fmt.Printf("Atoi failed; msg: %s", err.Error())
    }
    client, err := NewSshClient()
    if err != nil {
        fmt.Printf("NewSshClient failed; msg: %s", err.Error())
    }
    defer client.Close()
    ssConn, err := NewSshConn(cols, rows, client)
    if err != nil {
        fmt.Printf("NewSshConn failed; msg: %s", err.Error())
    }
    defer ssConn.Close()
    
    quitChan := make(chan bool, 3)
    
    var logBuff = new(bytes.Buffer)
    
    // most messages are ssh output, not webSocket input
    go ssConn.ReceiveWsMsg(wsConn, logBuff, quitChan)
    go ssConn.SendComboOutput(wsConn, quitChan)
    go ssConn.SessionWait(quitChan)
    
    <-quitChan
    
    logrus.Info("websocket finished")
    

    }

    func InitRouter() *gin.Engine { r := gin.New() //http.Handle("/web/ssh", websocket.Handler(WsSsh)) r.GET("/web/ssh", WsSsh) r.GET("/web", web) return r }

    func main() { r := gin.New() r.GET("/web/ssh", WsSsh) r.GET("/web", web) gin.SetMode(gin.DebugMode)

    //接受websocket的路由地址
    s := &http.Server{
        Addr:           ":12345",
        Handler:        r,
        ReadTimeout:    60 * time.Second,
        WriteTimeout:   60 * time.Second,
        MaxHeaderBytes: 1 << 20,
    }
    
    if err := s.ListenAndServe(); err != nil {
        fmt.Printf("ListenAndServe failed; msg: %s", err.Error())
    }
    

    }
    ` websocket测试地址: http://www.bejson.com/httputil/websocket/

    测试命令: {"type": "cmd", "cmd": "L2Jpbi9scyUyMC9yb290JTBB", "cols": 20, "rows": 20}

    命令的结果输出是原命令,而不是命令执行结果; 麻烦大佬帮忙看下

  • Bump github.com/gin-gonic/gin from 1.4.0 to 1.7.0

    Bump github.com/gin-gonic/gin from 1.4.0 to 1.7.0

    Bumps github.com/gin-gonic/gin from 1.4.0 to 1.7.0.

    Release notes

    Sourced from github.com/gin-gonic/gin's releases.

    Release v1.7.0

    BUGFIXES

    • fix compile error from #2572 (#2600)
    • fix: print headers without Authorization header on broken pipe (#2528)
    • fix(tree): reassign fullpath when register new node (#2366)

    ENHANCEMENTS

    • Support params and exact routes without creating conflicts (#2663)
    • chore: improve render string performance (#2365)
    • Sync route tree to httprouter latest code (#2368)
    • chore: rename getQueryCache/getFormCache to initQueryCache/initFormCa (#2375)
    • chore(performance): improve countParams (#2378)
    • Remove some functions that have the same effect as the bytes package (#2387)
    • update:SetMode function (#2321)
    • remove a unused type SecureJSONPrefix (#2391)
    • Add a redirect sample for POST method (#2389)
    • Add CustomRecovery builtin middleware (#2322)
    • binding: avoid 2038 problem on 32-bit architectures (#2450)
    • Prevent panic in Context.GetQuery() when there is no Request (#2412)
    • Add GetUint and GetUint64 method on gin.context (#2487)
    • update content-disposition header to MIME-style (#2512)
    • reduce allocs and improve the render WriteString (#2508)
    • implement ".Unwrap() error" on Error type (#2525) (#2526)
    • Allow bind with a map[string]string (#2484)
    • chore: update tree (#2371)
    • Support binding for slice/array obj [Rewrite] (#2302)
    • basic auth: fix timing oracle (#2609)
    • Add mixed param and non-param paths (port of httprouter#329) (#2663)
    • feat(engine): add trustedproxies and remoteIP (#2632)

    Improve performance

    ENHANCEMENTS

    • Improve performance: Change *sync.RWMutex to sync.RWMutex in context. #2351

    release v1.6.2

    Release Notes

    • BUGFIXES
      • fix missing initial sync.RWMutex (#2305)
    • ENHANCEMENTS
      • Add set samesite in cookie. (#2306)

    Contributors

    release v1.6.1

    ... (truncated)

    Changelog

    Sourced from github.com/gin-gonic/gin's changelog.

    Gin v1.7.0

    BUGFIXES

    • fix compile error from #2572 (#2600)
    • fix: print headers without Authorization header on broken pipe (#2528)
    • fix(tree): reassign fullpath when register new node (#2366)

    ENHANCEMENTS

    • Support params and exact routes without creating conflicts (#2663)
    • chore: improve render string performance (#2365)
    • Sync route tree to httprouter latest code (#2368)
    • chore: rename getQueryCache/getFormCache to initQueryCache/initFormCa (#2375)
    • chore(performance): improve countParams (#2378)
    • Remove some functions that have the same effect as the bytes package (#2387)
    • update:SetMode function (#2321)
    • remove an unused type SecureJSONPrefix (#2391)
    • Add a redirect sample for POST method (#2389)
    • Add CustomRecovery builtin middleware (#2322)
    • binding: avoid 2038 problem on 32-bit architectures (#2450)
    • Prevent panic in Context.GetQuery() when there is no Request (#2412)
    • Add GetUint and GetUint64 method on gin.context (#2487)
    • update content-disposition header to MIME-style (#2512)
    • reduce allocs and improve the render WriteString (#2508)
    • implement ".Unwrap() error" on Error type (#2525) (#2526)
    • Allow bind with a map[string]string (#2484)
    • chore: update tree (#2371)
    • Support binding for slice/array obj [Rewrite] (#2302)
    • basic auth: fix timing oracle (#2609)
    • Add mixed param and non-param paths (port of httprouter#329) (#2663)
    • feat(engine): add trustedproxies and remoteIP (#2632)

    Gin v1.6.3

    ENHANCEMENTS

    • Improve performance: Change *sync.RWMutex to sync.RWMutex in context. #2351

    Gin v1.6.2

    BUGFIXES

    • fix missing initial sync.RWMutex #2305

    ENHANCEMENTS

    • Add set samesite in cookie. #2306

    Gin v1.6.1

    BUGFIXES

    • Revert "fix accept incoming network connections" #2294

    ... (truncated)

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

  • 启动提示找不到配置文件

    启动提示找不到配置文件

    环境win10;go版本 1.1.7

    time="2022-05-05T16:29:53+08:00" level=fatal msg="Fatal error config file: Config File "config" Not Found in "[D:\\go_project\\felix2 D:\\etc\\felix C:\\Users\\Administrator\\.felix]" \n"

  • ws_ssh_term.go文件中63-67行的代码功能是什么?我注释掉后正常能ssh到指定服务器

    ws_ssh_term.go文件中63-67行的代码功能是什么?我注释掉后正常能ssh到指定服务器

    	client, err := flx.NewSshClient(mc)
    	if wshandleError(wsConn, err) {
    		return
    	}
    	defer client.Close()
    	startTime := time.Now()
            //下面注释的这段代码功能是什么?我注释掉后还是正常能ssh到指定服务器
    	//ssConn, err := util.NewSshConn(cols, rows, client)
    	//if wshandleError(wsConn, err) {
    	//	return
    	//}
    	//defer ssConn.Close()
    	sws, err := model.NewLogicSshWsSession(cols, rows, true, client, wsConn)
    	if wshandleError(wsConn, err) {
    		return
    	}
    	defer sws.Close()
    
    	quitChan := make(chan bool, 3)
    	sws.Start(quitChan)
    	go sws.Wait(quitChan)
    
  • model db.CreateUserOfRole undefined

    model db.CreateUserOfRole undefined

    felix ginbro --dbAddr "X.X.X.X:YYYY" --dbName mydbname --dbPassword "mydbpass" --dbType mysql --dbUser mydbuser --dir myoutputdirectory --pkg mypackagename -V

    produces:

    2020/11/29 10:14:59 SQLite3 in: /home/ubuntu/.felix/sqlite.db cd mypackagename then run go run main.go test your codebase

    running: go run main.go model/m_info_flags.go:50:11: db.CreateUserOfRole undefined (type *gorm.DB has no field or method CreateUserOfRole) model/m_info_stats.go:62:11: db.CreateUserOfRole undefined (type *gorm.DB has no field or method CreateUserOfRole) model/m_time_stats.go:61:11: db.CreateUserOfRole undefined (type *gorm.DB has no field or method CreateUserOfRole) model/m_network_stats.go:61:11: db.CreateUserOfRole undefined (type *gorm.DB has no field or method CreateUserOfRole) model/m_network_stats.go:61:11: too many errors

  • 使用Postgresql生成的代码运行报错

    使用Postgresql生成的代码运行报错

    func createDatabase() (*gorm.DB, error) {
    	dbType := config.GetString("db.type")
    	dbAddr := config.GetString("db.addr")
    	dbName := config.GetString("db.database")
    	dbUser := config.GetString("db.user")
    	dbPassword := config.GetString("db.password")
    	dbCharset := config.GetString("db.charset")
    	conn := ""
    	switch dbType {
    	case "mysql":
    		conn = fmt.Sprintf("%s:%s@(%s)/%s?charset=%s&parseTime=True&loc=Local", dbUser, dbPassword, dbAddr, dbName, dbCharset)
    	case "sqlite":
    		conn = dbAddr
    	case "mssql":
    		return nil, errors.New("TODO:suport sqlServer")
    	case "postgres":
    		hostPort := strings.Split(dbAddr, ":")
    		if len(hostPort) == 2 {
    			return nil, errors.New("db.addr must be like this host:ip")
    		}
    		conn = fmt.Sprintf("host=%s port=%s user=%s dbname=%s password=%s sslmode=disable", hostPort[0], hostPort[1], dbUser, dbName, dbPassword)
    	default:
    		return nil, fmt.Errorf("database type %s is not supported by felix ginrbo", dbType)
    	}
    	return gorm.Open(dbType, conn)
    }
    

    其中的

    if len(hostPort) == 2 {
    			return nil, errors.New("db.addr must be like this host:ip")
    		}
    

    应该是len(hostPort) != 2

🏯 Monitor your (gitlab/github) CI/CD pipelines via command line interface with fortress
🏯 Monitor your (gitlab/github) CI/CD pipelines via command line interface with fortress

__ _ / _| | | | |_ ___ _ __| |_ _ __ ___ ___ ___ | _/ _ \| '__| __| '__/ _ \/ __/ _

Mar 31, 2022
This is a SSH CA that allows you to retrieve a signed SSH certificate by authenticating to Duo.

github-duo-ssh-ca Authenticate to GitHub Enterprise in a secure way by requiring users to go through a Duo flow to get a short-lived SSH certificate t

Jan 7, 2022
Integrated ssh-agent for windows. (pageant compatible. openSSH ssh-agent etc ..)
Integrated ssh-agent for windows. (pageant compatible. openSSH ssh-agent etc ..)

OmniSSHAgent About The chaotic windows ssh-agent has been integrated into one program. Chaos Map of SSH-Agent on Windows There are several different c

Dec 19, 2022
Run VS Code on any server over SSH.
Run VS Code on any server over SSH.

sshcode This project has been deprecated in favour of the code-server install script See the discussion in #185 sshcode is a CLI to automatically inst

Dec 25, 2022
A Go based deployment tool that allows the users to deploy the web application on the server using SSH information and pem file.

A Go based deployment tool that allows the users to deploy the web application on the server using SSH information and pem file. This application is intend for non tecnhincal users they can just open the GUI and given the server details just deploy.

Oct 16, 2021
A beginner friendly introduction to prometheus 🔥
A beginner friendly introduction to prometheus 🔥

Prometheus-Basics A beginner friendly introduction to prometheus. Table of Contents What is prometheus ? What are metrics and why is it important ? Ba

Dec 29, 2022
Testcontainers is a Golang library that providing a friendly API to run Docker container. It is designed to create runtime environment to use during your automatic tests.

When I was working on a Zipkin PR I discovered a nice Java library called Testcontainers. It provides an easy and clean API over the go docker sdk to

Jan 7, 2023
A lightweight Vault client module written in Go, with no dependencies, that is intuitive and user-friendly

libvault A lightweight Hashicorp Vault client written in Go, with no dependencies. It aims to provide an intuitive, simple API that is easy to use. Ju

Sep 18, 2022
Koyeb is a developer-friendly serverless platform to deploy apps globally.
Koyeb is a developer-friendly serverless platform to deploy apps globally.

Koyeb Serverless Platform Deploy a Go Gin application on Koyeb Learn more about Koyeb · Explore the documentation · Discover our tutorials About Koyeb

Nov 14, 2022
⚔ Personal Golang starter kit with an engineer research perspective, expressjs developer friendly, and aims for rapid app development.
⚔ Personal Golang starter kit with an engineer research perspective, expressjs developer friendly, and aims for rapid app development.

Goku (WIP; Author Only) ⚔ Personal Golang starter kit with an engineer research perspective, expressjs developer friendly, and aims for rapid app deve

Jan 6, 2022
Awesome-italia-remote - A list of remote-friendly or full-remote companies that targets Italian talents

Awesome Italia Remote A list of remote-friendly or full-remote companies that ta

Dec 29, 2022
Copy files and artifacts via SSH using a binary, docker or Drone CI.

drone-scp Copy files and artifacts via SSH using a binary, docker or Drone CI. Feature Support routines. Support wildcard pattern on source list. Supp

Dec 6, 2022
easyssh-proxy provides a simple implementation of some SSH protocol features in Go

easyssh-proxy easyssh-proxy provides a simple implementation of some SSH protocol features in Go. Feature This project is forked from easyssh but add

Dec 30, 2022
easy way to distribute commands over ssh.

grapes grapes is lightweight tool designed to distribute commands over ssh with ease. Update (25/04/2019) Handshake validation is now in place in orde

Dec 20, 2022
Manage your ssh alias configs easily.
Manage your ssh alias configs easily.

manssh manssh is a command line tool for managing your ssh alias config easily, inspired by storm project, powered by Go. Note: This project is actual

Nov 9, 2022
A simple and powerful SSH keys manager
A simple and powerful SSH keys manager

SKM is a simple and powerful SSH Keys Manager. It helps you to manage your multiple SSH keys easily! Features Create, List, Delete your SSH key(s) Man

Dec 17, 2022
⚡️ A dev tool for microservice developers to run local applications and/or forward others from/to Kubernetes SSH or TCP
⚡️ A dev tool for microservice developers to run local applications and/or forward others from/to Kubernetes SSH or TCP

Your new microservice development environment friend. This CLI tool allows you to define a configuration to work with both local applications (Go, Nod

Jan 4, 2023
Open URL in your local web browser from the SSH-connected remote environment.

opener Open URL in your local web browser from the SSH-connected remote environment. How does opener work? opener is a daemon process that runs locall

Oct 20, 2022
Google Compute Engine (GCE) VM takeover via DHCP flood - gain root access by getting SSH keys added by google_guest_agent

Abstract This is an advisory about an unpatched vulnerability (at time of publishing this repo, 2021-06-25) affecting virtual machines in Google's Com

Nov 9, 2022