SSH密码、密钥从文件中读取
parent
741bee1392
commit
72499731f3
2
build.sh
2
build.sh
|
@ -86,6 +86,8 @@ done
|
|||
rm -rf $TEMP_DIR/$APP_NAME/conf/*
|
||||
rm -rf $TEMP_DIR/$APP_NAME/log/*
|
||||
rm -rf $TEMP_DIR/$APP_NAME/data/sessions/*
|
||||
rm -rf $TEMP_DIR/$APP_NAME/data/password/*
|
||||
rm -rf $TEMP_DIR/$APP_NAME/data/private_key/*
|
||||
|
||||
echo '压缩文件'
|
||||
# 压缩文件
|
||||
|
|
12
cmd/web.go
12
cmd/web.go
|
@ -9,6 +9,8 @@ import (
|
|||
"os/signal"
|
||||
"syscall"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"github.com/ouqiang/gocron/service"
|
||||
"github.com/ouqiang/gocron/models"
|
||||
)
|
||||
|
||||
// web服务器默认端口
|
||||
|
@ -37,6 +39,8 @@ func run(ctx *cli.Context) {
|
|||
setEnvironment(ctx)
|
||||
// 初始化应用
|
||||
app.InitEnv()
|
||||
// 初始化模块 DB、定时任务等
|
||||
initModule()
|
||||
// 捕捉信号,配置热更新等
|
||||
go catchSignal()
|
||||
m := macaron.Classic()
|
||||
|
@ -48,6 +52,14 @@ func run(ctx *cli.Context) {
|
|||
m.Run(port)
|
||||
}
|
||||
|
||||
func initModule() {
|
||||
models.Db = models.CreateDb()
|
||||
|
||||
// 初始化定时任务
|
||||
serviceTask := new(service.Task)
|
||||
serviceTask.Initialize()
|
||||
}
|
||||
|
||||
// 解析端口
|
||||
func parsePort(ctx *cli.Context) int {
|
||||
var port int = DefaultPort
|
||||
|
|
|
@ -3,6 +3,12 @@ package models
|
|||
import (
|
||||
"github.com/ouqiang/gocron/modules/ssh"
|
||||
"github.com/go-xorm/xorm"
|
||||
"github.com/ouqiang/gocron/modules/app"
|
||||
"github.com/ouqiang/gocron/modules/utils"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
)
|
||||
|
||||
// 主机
|
||||
|
@ -11,11 +17,9 @@ type Host struct {
|
|||
Name string `xorm:"varchar(64) notnull"` // 主机名称
|
||||
Alias string `xorm:"varchar(32) notnull default '' "` // 主机别名
|
||||
Username string `xorm:"varchar(32) notnull default '' "` // ssh 用户名
|
||||
Password string `xorm:"varchar(64) notnull default ''"` // ssh 密码
|
||||
Port int `xorm:"notnull default 22"` // 主机端口
|
||||
Remark string `xorm:"varchar(100) notnull default '' "` // 备注
|
||||
AuthType ssh.HostAuthType `xorm:"tinyint notnull default 1"` // 认证方式 1: 密码 2: 公钥
|
||||
PrivateKey string `xorm:"varchar(4096) notnull default '' "` // 私钥
|
||||
BaseModel `xorm:"-"`
|
||||
}
|
||||
|
||||
|
@ -30,7 +34,7 @@ func (host *Host) Create() (insertId int16, err error) {
|
|||
}
|
||||
|
||||
func (host *Host) UpdateBean(id int16) (int64, error) {
|
||||
return Db.ID(id).Cols("name,alias,username,password,port,remark,auth_type,private_key").Update(host)
|
||||
return Db.ID(id).Cols("name,alias,username,port,remark,auth_type").Update(host)
|
||||
}
|
||||
|
||||
|
||||
|
@ -83,6 +87,38 @@ func (host *Host) Total(params CommonMap) (int64, error) {
|
|||
return session.Count(host)
|
||||
}
|
||||
|
||||
func (h *Host) GetPasswordByHost(host string) (string, error) {
|
||||
path := app.DataDir + "/ssh/password/" + host
|
||||
|
||||
|
||||
return h.readFile(path)
|
||||
}
|
||||
|
||||
func (h *Host) GetPrivateKeyByHost(host string) (string,error) {
|
||||
path := app.DataDir + "/ssh/private_key/" + host
|
||||
|
||||
return h.readFile(path)
|
||||
}
|
||||
|
||||
func (host *Host) readFile(file string) (string, error) {
|
||||
logger.Debug("认证文件路径: ", file)
|
||||
if !utils.FileExist(file) {
|
||||
return "", errors.New(file + "-认证文件不存在或无权限访问")
|
||||
}
|
||||
|
||||
contentByte, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
content := string(contentByte)
|
||||
content = strings.TrimSpace(content)
|
||||
if content == "" {
|
||||
return "", errors.New("密码为空")
|
||||
}
|
||||
|
||||
return content, nil
|
||||
}
|
||||
|
||||
// 解析where
|
||||
func (host *Host) parseWhere(session *xorm.Session, params CommonMap) {
|
||||
if len(params) == 0 {
|
||||
|
|
|
@ -8,6 +8,9 @@ import (
|
|||
"gopkg.in/macaron.v1"
|
||||
"strings"
|
||||
"time"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"github.com/ouqiang/gocron/modules/setting"
|
||||
"github.com/ouqiang/gocron/modules/app"
|
||||
)
|
||||
|
||||
type Status int8
|
||||
|
@ -60,7 +63,8 @@ func (model *BaseModel) pageLimitOffset() int {
|
|||
}
|
||||
|
||||
// 创建Db
|
||||
func CreateDb(config map[string]string) *xorm.Engine {
|
||||
func CreateDb() *xorm.Engine {
|
||||
config := getDbConfig()
|
||||
dsn := getDbEngineDSN(config["engine"], config)
|
||||
engine, err := xorm.NewEngine(config["engine"], dsn)
|
||||
if err != nil {
|
||||
|
@ -115,4 +119,27 @@ func keepDbAlived(engine *xorm.Engine) {
|
|||
<- t
|
||||
engine.Ping()
|
||||
}
|
||||
}
|
||||
|
||||
// 获取数据库配置
|
||||
func getDbConfig() map[string]string {
|
||||
config, err := setting.Read(app.AppConfig)
|
||||
if err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
section := config.Section("db")
|
||||
if err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
var db map[string]string = make(map[string]string)
|
||||
db["user"] = section.Key("user").String()
|
||||
db["password"] = section.Key("password").String()
|
||||
db["host"] = section.Key("host").String()
|
||||
db["port"] = section.Key("port").String()
|
||||
db["database"] = section.Key("database").String()
|
||||
db["charset"] = section.Key("charset").String()
|
||||
db["prefix"] = section.Key("prefix").String()
|
||||
db["engine"] = section.Key("engine").String()
|
||||
|
||||
return db
|
||||
}
|
|
@ -40,10 +40,8 @@ type TaskHost struct {
|
|||
Name string
|
||||
Port int
|
||||
Username string
|
||||
Password string
|
||||
Alias string
|
||||
AuthType ssh.HostAuthType
|
||||
PrivateKey string
|
||||
}
|
||||
|
||||
func (TaskHost) TableName() string {
|
||||
|
@ -87,7 +85,7 @@ func (task *Task) Enable(id int) (int64, error) {
|
|||
// 获取所有激活任务
|
||||
func (task *Task) ActiveList() ([]TaskHost, error) {
|
||||
list := make([]TaskHost, 0)
|
||||
fields := "t.*, host.alias,host.name,host.username,host.password,host.port,host.auth_type,host.private_key"
|
||||
fields := "t.*, host.alias,host.name,host.username,host.port,host.auth_type"
|
||||
err := Db.Alias("t").Join("LEFT", hostTableName(), "t.host_id=host.id").Where("t.status = ?", Enabled).Cols(fields).Find(&list)
|
||||
|
||||
return list, err
|
||||
|
@ -96,7 +94,7 @@ func (task *Task) ActiveList() ([]TaskHost, error) {
|
|||
// 获取某个主机下的所有激活任务
|
||||
func (task *Task) ActiveListByHostId(hostId int16) ([]TaskHost, error) {
|
||||
list := make([]TaskHost, 0)
|
||||
fields := "t.*, host.alias,host.name,host.username,host.password,host.port,host.auth_type,host.private_key"
|
||||
fields := "t.*, host.alias,host.name,host.username,host.port,host.auth_type"
|
||||
err := Db.Alias("t").Join("LEFT", hostTableName(), "t.host_id=host.id").Where("t.status = ? AND t.host_id = ?", Enabled, hostId).Cols(fields).Find(&list)
|
||||
|
||||
return list, err
|
||||
|
@ -122,7 +120,7 @@ func (task *Task) NameExist(name string, id int) (bool, error) {
|
|||
|
||||
func(task *Task) Detail(id int) (TaskHost, error) {
|
||||
taskHost := TaskHost{}
|
||||
fields := "t.*, host.alias,host.name,host.username,host.password,host.port,host.auth_type,host.private_key"
|
||||
fields := "t.*, host.alias,host.name,host.username,host.port,host.auth_type"
|
||||
_, err := Db.Alias("t").Join("LEFT", hostTableName(), "t.host_id=host.id").Where("t.id=?", id).Cols(fields).Get(&taskHost)
|
||||
|
||||
return taskHost, err
|
||||
|
|
|
@ -3,11 +3,9 @@ package app
|
|||
import (
|
||||
"os"
|
||||
|
||||
"github.com/ouqiang/gocron/models"
|
||||
"github.com/ouqiang/gocron/service"
|
||||
"github.com/ouqiang/gocron/modules/setting"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"runtime"
|
||||
"github.com/ouqiang/gocron/modules/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -33,10 +31,6 @@ func InitEnv() {
|
|||
AppConfig = ConfDir + "/app.ini"
|
||||
checkDirExists(ConfDir, LogDir, DataDir)
|
||||
Installed = IsInstalled()
|
||||
if Installed {
|
||||
InitDb()
|
||||
InitResource()
|
||||
}
|
||||
}
|
||||
|
||||
// 判断应用是否安装过
|
||||
|
@ -59,51 +53,11 @@ func CreateInstallLock() error {
|
|||
return err
|
||||
}
|
||||
|
||||
// 初始化资源
|
||||
func InitResource() {
|
||||
// 初始化定时任务
|
||||
serviceTask := new(service.Task)
|
||||
serviceTask.Initialize()
|
||||
}
|
||||
|
||||
// 初始化DB
|
||||
func InitDb() {
|
||||
dbConfig := getDbConfig(AppConfig)
|
||||
models.Db = models.CreateDb(dbConfig)
|
||||
}
|
||||
|
||||
// 检测目录是否存在
|
||||
func checkDirExists(path ...string) {
|
||||
for _, value := range path {
|
||||
_, err := os.Stat(value)
|
||||
if os.IsNotExist(err) {
|
||||
logger.Fatal(value + "目录不存在")
|
||||
}
|
||||
if os.IsPermission(err) {
|
||||
logger.Fatal(value + "目录无权限操作")
|
||||
if !utils.FileExist(value) {
|
||||
logger.Fatal(value + "目录不存在或无权限访问")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取数据库配置
|
||||
func getDbConfig(configFile string) map[string]string {
|
||||
config, err := setting.Read(configFile)
|
||||
if err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
section := config.Section("db")
|
||||
if err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
var db map[string]string = make(map[string]string)
|
||||
db["user"] = section.Key("user").String()
|
||||
db["password"] = section.Key("password").String()
|
||||
db["host"] = section.Key("host").String()
|
||||
db["port"] = section.Key("port").String()
|
||||
db["database"] = section.Key("database").String()
|
||||
db["charset"] = section.Key("charset").String()
|
||||
db["prefix"] = section.Key("prefix").String()
|
||||
db["engine"] = section.Key("engine").String()
|
||||
|
||||
return db
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ import (
|
|||
"runtime"
|
||||
"github.com/Tang-RoseChild/mahonia"
|
||||
"strings"
|
||||
"os"
|
||||
)
|
||||
|
||||
|
||||
|
@ -105,4 +106,16 @@ func EscapeJson(s string) string {
|
|||
replaceChars := []string{ "\\\\", "\\b", "\\f", "\\n", "\\r", "\\t", "\\\"",}
|
||||
|
||||
return ReplaceStrings(s, specialChars, replaceChars)
|
||||
}
|
||||
|
||||
func FileExist(file string) bool {
|
||||
_, err := os.Stat(file)
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
if os.IsPermission(err) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
|
@ -64,15 +64,28 @@ func Ping(ctx *macaron.Context) string {
|
|||
return json.CommonFailure("主机不存在", err)
|
||||
}
|
||||
|
||||
sshConfig := ssh.SSHConfig{
|
||||
User: hostModel.Username,
|
||||
Password: hostModel.Password,
|
||||
Host: hostModel.Name,
|
||||
Port: hostModel.Port,
|
||||
ExecTimeout: 5,
|
||||
AuthType: hostModel.AuthType,
|
||||
PrivateKey: hostModel.PrivateKey,
|
||||
sshConfig := ssh.SSHConfig{}
|
||||
sshConfig.User = hostModel.Username
|
||||
sshConfig.Host = hostModel.Name
|
||||
sshConfig.Port = hostModel.Port
|
||||
sshConfig.ExecTimeout = 5
|
||||
sshConfig.AuthType = hostModel.AuthType
|
||||
var password string
|
||||
var privateKey string
|
||||
if hostModel.AuthType == ssh.HostPassword {
|
||||
password, err = hostModel.GetPasswordByHost(hostModel.Name)
|
||||
if err != nil {
|
||||
return json.CommonFailure(err.Error(), err)
|
||||
}
|
||||
sshConfig.Password = password
|
||||
} else {
|
||||
privateKey, err = hostModel.GetPrivateKeyByHost(hostModel.Name)
|
||||
if err != nil {
|
||||
return json.CommonFailure(err.Error(), err)
|
||||
}
|
||||
sshConfig.PrivateKey = privateKey
|
||||
}
|
||||
|
||||
_, err = ssh.Exec(sshConfig, "pwd")
|
||||
if err != nil {
|
||||
return json.CommonFailure("连接失败-" + err.Error(), err)
|
||||
|
@ -86,10 +99,8 @@ type HostForm struct {
|
|||
Name string `binding:"Required;MaxSize(100)"`
|
||||
Alias string `binding:"Required;MaxSize(32)"`
|
||||
Username string `binding:"Required;MaxSize(32)"`
|
||||
Password string
|
||||
Port int `binding:"Required;Range(1-65535)"`
|
||||
AuthType ssh.HostAuthType `binding:"Required:Range(1,2)"`
|
||||
PrivateKey string
|
||||
Remark string
|
||||
}
|
||||
|
||||
|
@ -108,16 +119,9 @@ func Store(ctx *macaron.Context, form HostForm) string {
|
|||
hostModel.Name = form.Name
|
||||
hostModel.Alias = form.Alias
|
||||
hostModel.Username = form.Username
|
||||
hostModel.Password = form.Password
|
||||
hostModel.Port = form.Port
|
||||
hostModel.Remark = form.Remark
|
||||
hostModel.PrivateKey = form.PrivateKey
|
||||
hostModel.AuthType = form.AuthType
|
||||
if hostModel.AuthType == ssh.HostPublicKey {
|
||||
hostModel.Password = ""
|
||||
} else {
|
||||
hostModel.PrivateKey = ""
|
||||
}
|
||||
isCreate := false
|
||||
if id > 0 {
|
||||
_, err = hostModel.UpdateBean(id)
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"github.com/go-macaron/binding"
|
||||
"fmt"
|
||||
"github.com/ouqiang/gocron/service"
|
||||
)
|
||||
|
||||
// 系统安装
|
||||
|
@ -60,7 +61,7 @@ func Store(ctx *macaron.Context, form InstallForm) string {
|
|||
return json.CommonFailure("数据库配置写入文件失败", err)
|
||||
}
|
||||
|
||||
app.InitDb()
|
||||
models.Db = models.CreateDb()
|
||||
// 创建数据库表
|
||||
migration := new(models.Migration)
|
||||
err = migration.Exec(form.DbName)
|
||||
|
@ -81,8 +82,9 @@ func Store(ctx *macaron.Context, form InstallForm) string {
|
|||
}
|
||||
|
||||
app.Installed = true
|
||||
// 初始化定时任务等
|
||||
app.InitResource()
|
||||
// 初始化定时任务
|
||||
serviceTask := new(service.Task)
|
||||
serviceTask.Initialize()
|
||||
|
||||
return json.Success("安装成功", nil)
|
||||
}
|
||||
|
|
|
@ -149,15 +149,33 @@ func (h *HTTPHandler) Run(taskModel models.TaskHost) (result string, err error)
|
|||
type SSHCommandHandler struct{}
|
||||
|
||||
func (h *SSHCommandHandler) Run(taskModel models.TaskHost) (string, error) {
|
||||
sshConfig := ssh.SSHConfig{
|
||||
User: taskModel.Username,
|
||||
Password: taskModel.Password,
|
||||
Host: taskModel.Name,
|
||||
Port: taskModel.Port,
|
||||
ExecTimeout: taskModel.Timeout,
|
||||
AuthType: taskModel.AuthType,
|
||||
PrivateKey: taskModel.PrivateKey,
|
||||
hostModel := new(models.Host)
|
||||
err := hostModel.Find(int(taskModel.HostId))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
sshConfig := ssh.SSHConfig{}
|
||||
sshConfig.User = hostModel.Username
|
||||
sshConfig.Host = hostModel.Name
|
||||
sshConfig.Port = hostModel.Port
|
||||
sshConfig.ExecTimeout = 5
|
||||
sshConfig.AuthType = hostModel.AuthType
|
||||
var password string
|
||||
var privateKey string
|
||||
if hostModel.AuthType == ssh.HostPassword {
|
||||
password, err = hostModel.GetPasswordByHost(hostModel.Name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
sshConfig.Password = password
|
||||
} else {
|
||||
privateKey, err = hostModel.GetPrivateKeyByHost(hostModel.Name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
sshConfig.PrivateKey = privateKey
|
||||
}
|
||||
|
||||
return ssh.Exec(sshConfig, taskModel.Command)
|
||||
}
|
||||
|
||||
|
|
|
@ -43,8 +43,14 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="four fields">
|
||||
<div class="fields">
|
||||
<div class="field">
|
||||
<div class="ui blue message">
|
||||
密码认证: 把密码写入文件 gocron根目录/data/ssh/password/主机名<br>
|
||||
例: echo '12345678' > data/ssh/password/127.0.0.1<br><br>
|
||||
公钥认证: 把私钥写入文件 gocron根目录/data/ssh/private_key/主机名<br>
|
||||
例: cp ~/.ssh/id_rsa data/ssh/private_key/127.0.0.1
|
||||
</div>
|
||||
<label>认证方式</label>
|
||||
<select name="auth_type" id="authType">
|
||||
<option value="2" {{{if .Host}}} {{{if eq .Host.AuthType 2}}}selected {{{end}}} {{{end}}} data-validate-type="selectPrivateKey" data-match="private_key">公钥</option>
|
||||
|
@ -52,22 +58,6 @@
|
|||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="two fields" id="private_key">
|
||||
<div class="field">
|
||||
<label>私钥 (~/.ssh/id_rsa)</label>
|
||||
<div class="ui small input">
|
||||
<textarea rows="7" name="private_key">{{{.Host.PrivateKey}}}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="four fields" id="password">
|
||||
<div class="field">
|
||||
<label>SSH密码</label>
|
||||
<div class="ui small input">
|
||||
<input type="text" placeholder="" name="password" value="{{{.Host.Password}}}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="two fields">
|
||||
<div class="field">
|
||||
<label>备注</label>
|
||||
|
@ -85,28 +75,6 @@
|
|||
|
||||
<script type="text/javascript">
|
||||
var $uiForm = $('.ui.form');
|
||||
$(function() {
|
||||
changeAuthType($('#authType').val());
|
||||
});
|
||||
|
||||
function changeAuthType(type) {
|
||||
// 私钥
|
||||
if (type == 2) {
|
||||
$('#password').hide();
|
||||
$('#private_key').show();
|
||||
return;
|
||||
}
|
||||
|
||||
// 密码
|
||||
$('#private_key').hide();
|
||||
$('#password').show();
|
||||
}
|
||||
|
||||
$('#authType').change(function() {
|
||||
changeAuthType($(this).val());
|
||||
});
|
||||
registerSelectFormValidation("selectPrivateKey", $uiForm, $('#authType'), 'auth_type');
|
||||
registerSelectFormValidation("selectPassword", $uiForm, $('#authType'), 'auth_type');
|
||||
$($uiForm).form(
|
||||
{
|
||||
onSuccess: function(event, fields) {
|
||||
|
@ -152,24 +120,6 @@
|
|||
prompt : '请输入有效的端口号'
|
||||
}
|
||||
]
|
||||
},
|
||||
PrivateKey: {
|
||||
identifier : 'private_key',
|
||||
rules: [
|
||||
{
|
||||
type : 'selectPrivateKey',
|
||||
prompt : '请输入私钥'
|
||||
}
|
||||
]
|
||||
},
|
||||
Password: {
|
||||
identifier : 'password',
|
||||
rules: [
|
||||
{
|
||||
type : 'selectPassword',
|
||||
prompt : '请输入密码'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
inline : true
|
||||
|
|
Loading…
Reference in New Issue