SSH密码、密钥从文件中读取

pull/21/merge
ouqiang 2017-05-01 13:59:52 +08:00
parent 741bee1392
commit 72499731f3
13 changed files with 160 additions and 144 deletions

View File

@ -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 '压缩文件'
# 压缩文件

View File

@ -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

View File

View File

View File

@ -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 {

View File

@ -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
}

View File

@ -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

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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)

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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