新增HTTP中间件-限制客户端IP访问

pull/21/merge
ouqiang 2017-05-04 14:02:50 +08:00
parent 90322c1230
commit d3d7350fc7
12 changed files with 81 additions and 84 deletions

View File

@ -11,6 +11,7 @@ import (
"github.com/ouqiang/gocron/modules/logger" "github.com/ouqiang/gocron/modules/logger"
"github.com/ouqiang/gocron/service" "github.com/ouqiang/gocron/service"
"github.com/ouqiang/gocron/models" "github.com/ouqiang/gocron/models"
"github.com/ouqiang/gocron/modules/setting"
) )
// web服务器默认端口 // web服务器默认端口
@ -56,6 +57,13 @@ func initModule() {
if !app.Installed { if !app.Installed {
return return
} }
config, err := setting.Read(app.AppConfig)
if err != nil {
logger.Fatal("读取应用配置失败", err)
}
app.Setting = config
models.Db = models.CreateDb() models.Db = models.CreateDb()
// 初始化定时任务 // 初始化定时任务

View File

@ -9,7 +9,6 @@ import (
"strings" "strings"
"time" "time"
"github.com/ouqiang/gocron/modules/logger" "github.com/ouqiang/gocron/modules/logger"
"github.com/ouqiang/gocron/modules/setting"
"github.com/ouqiang/gocron/modules/app" "github.com/ouqiang/gocron/modules/app"
) )
@ -124,23 +123,15 @@ func keepDbAlived(engine *xorm.Engine) {
// 获取数据库配置 // 获取数据库配置
func getDbConfig() map[string]string { 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("获取DB配置失败", err)
}
var db map[string]string = make(map[string]string) var db map[string]string = make(map[string]string)
db["user"] = section.Key("user").String() db["user"] = app.Setting.Key("db.user").String()
db["password"] = section.Key("password").String() db["password"] = app.Setting.Key("db.password").String()
db["host"] = section.Key("host").String() db["host"] = app.Setting.Key("db.host").String()
db["port"] = section.Key("port").String() db["port"] = app.Setting.Key("db.port").String()
db["database"] = section.Key("database").String() db["database"] = app.Setting.Key("db.database").String()
db["charset"] = section.Key("charset").String() db["charset"] = app.Setting.Key("db.charset").String()
db["prefix"] = section.Key("prefix").String() db["prefix"] = app.Setting.Key("db.prefix").String()
db["engine"] = section.Key("engine").String() db["engine"] = app.Setting.Key("db.engine").String()
return db return db
} }

View File

@ -6,6 +6,7 @@ import (
"github.com/ouqiang/gocron/modules/logger" "github.com/ouqiang/gocron/modules/logger"
"runtime" "runtime"
"github.com/ouqiang/gocron/modules/utils" "github.com/ouqiang/gocron/modules/utils"
"gopkg.in/ini.v1"
) )
var ( var (
@ -15,6 +16,7 @@ var (
DataDir string // 存放session等 DataDir string // 存放session等
AppConfig string // 应用配置文件 AppConfig string // 应用配置文件
Installed bool // 应用是否安装过 Installed bool // 应用是否安装过
Setting *ini.Section // 应用配置
) )
func InitEnv() { func InitEnv() {
@ -47,7 +49,7 @@ func IsInstalled() bool {
func CreateInstallLock() error { func CreateInstallLock() error {
_, err := os.Create(ConfDir + "/install.lock") _, err := os.Create(ConfDir + "/install.lock")
if err != nil { if err != nil {
logger.Error("创建安装锁文件失败") logger.Error("创建安装锁文件conf/install.lock失败")
} }
return err return err

View File

@ -62,7 +62,6 @@ func request(req *http.Request, timeout int) ResponseWrapper {
} }
func setRequestHeader(req *http.Request) { func setRequestHeader(req *http.Request) {
req.Header.Set("Connection", "keep-alive")
req.Header.Set("Accept-Language", "zh-CN,zh;q=0.8,en;q=0.6") req.Header.Set("Accept-Language", "zh-CN,zh;q=0.8,en;q=0.6")
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36 golang/gocron") req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36 golang/gocron")
} }

View File

@ -5,39 +5,41 @@ import (
"gopkg.in/ini.v1" "gopkg.in/ini.v1"
) )
// 读取配置 const DefaultSection = "default"
func Read(filename string) (config *ini.File, err error) {
config, err = ini.Load(filename)
if err != nil {
return
}
return // 读取配置
func Read(filename string) (*ini.Section,error) {
config, err := ini.Load(filename)
if err != nil {
return nil, err
}
section := config.Section(DefaultSection)
return section, nil
} }
// 写入配置 // 写入配置
func Write(config map[string]map[string]string, filename string) error { func Write(config map[string]string, filename string) error {
if len(config) == 0 { if len(config) == 0 {
return errors.New("参数不能为空") return errors.New("参数不能为空")
} }
file := ini.Empty() file := ini.Empty()
for sectionName, items := range config {
if sectionName == "" { section, err := file.NewSection(DefaultSection)
return errors.New("节名称不能为空") if err != nil {
return err
}
for key, value := range config {
if key == "" {
continue
} }
section, err := file.NewSection(sectionName) _, err = section.NewKey(key, value)
if err != nil { if err != nil {
return err return err
} }
for key, value := range items {
_, err = section.NewKey(key, value)
if err != nil {
return err
}
}
} }
err := file.SaveTo(filename) err = file.SaveTo(filename)
return err return err
} }

View File

@ -89,7 +89,7 @@ func Exec(sshConfig SSHConfig, cmd string) (output string, err error) {
// 后台运行 // 后台运行
if sshConfig.ExecTimeout < 0 { if sshConfig.ExecTimeout < 0 {
go session.CombinedOutput(cmd) go session.CombinedOutput(cmd)
time.Sleep(5 * time.Second) time.Sleep(3 * time.Second)
return "", nil return "", nil
} }
// 不限制超时 // 不限制超时

View File

@ -91,8 +91,9 @@ func ReplaceStrings(s string, old []string, replace []string) string {
} }
func InStringSlice(slice []string, element string) bool { func InStringSlice(slice []string, element string) bool {
element = strings.TrimSpace(element)
for _, v := range slice { for _, v := range slice {
if v == element { if strings.TrimSpace(v) == element{
return true return true
} }
} }

View File

@ -7,8 +7,6 @@ import (
"github.com/ouqiang/gocron/modules/utils" "github.com/ouqiang/gocron/modules/utils"
"gopkg.in/macaron.v1" "gopkg.in/macaron.v1"
"strconv" "strconv"
"github.com/ouqiang/gocron/modules/logger"
"github.com/go-macaron/binding"
"fmt" "fmt"
"github.com/ouqiang/gocron/service" "github.com/ouqiang/gocron/service"
) )
@ -29,10 +27,6 @@ type InstallForm struct {
AdminEmail string `binding:"Required;Email;MaxSize(50)"` AdminEmail string `binding:"Required;Email;MaxSize(50)"`
} }
func(f InstallForm) Error(ctx *macaron.Context, errs binding.Errors) {
logger.Error(errs)
}
func Create(ctx *macaron.Context) { func Create(ctx *macaron.Context) {
if app.Installed { if app.Installed {
ctx.Redirect("/") ctx.Redirect("/")
@ -61,6 +55,12 @@ func Store(ctx *macaron.Context, form InstallForm) string {
return json.CommonFailure("数据库配置写入文件失败", err) return json.CommonFailure("数据库配置写入文件失败", err)
} }
appConfig, err := setting.Read(app.AppConfig)
if err != nil {
return json.CommonFailure("读取应用配置失败", err)
}
app.Setting = appConfig
models.Db = models.CreateDb() models.Db = models.CreateDb()
// 创建数据库表 // 创建数据库表
migration := new(models.Migration) migration := new(models.Migration)
@ -89,19 +89,18 @@ func Store(ctx *macaron.Context, form InstallForm) string {
return json.Success("安装成功", nil) return json.Success("安装成功", nil)
} }
// 数据库配置写入文件 // 配置写入文件
func writeConfig(form InstallForm) error { func writeConfig(form InstallForm) error {
dbConfig := map[string]map[string]string{ dbConfig := map[string]string{
"db": map[string]string{ "db.engine": form.DbType,
"engine": form.DbType, "db.host": form.DbHost,
"host": form.DbHost, "db.port": strconv.Itoa(form.DbPort),
"port": strconv.Itoa(form.DbPort), "db.user": form.DbUsername,
"user": form.DbUsername, "db.password": form.DbPassword,
"password": form.DbPassword, "db.database": form.DbName,
"database": form.DbName, "db.prefix": form.DbTablePrefix,
"prefix": form.DbTablePrefix, "db.charset": "utf8",
"charset": "utf8", "allow_ips" : "",
},
} }
return setting.Write(dbConfig, app.AppConfig) return setting.Write(dbConfig, app.AppConfig)

View File

@ -142,6 +142,7 @@ func RegisterMiddleware(m *macaron.Macaron) {
m.Use(csrf.Csrfer()) m.Use(csrf.Csrfer())
m.Use(toolbox.Toolboxer(m)) m.Use(toolbox.Toolboxer(m))
checkAppInstall(m) checkAppInstall(m)
ipAuth(m)
userAuth(m) userAuth(m)
setShareData(m) setShareData(m)
} }
@ -159,6 +160,20 @@ func checkAppInstall(m *macaron.Macaron) {
}) })
} }
// IP验证, 通过反向代理访问gocron需设置Header X-Real-IP才能获取到客户端真实IP
func ipAuth(m *macaron.Macaron) {
m.Use(func(ctx *macaron.Context) {
allowIpsStr := app.Setting.Key("allow_ips").String()
if allowIpsStr == "" {
return
}
clientIp := ctx.RemoteAddr()
if !utils.InStringSlice(allowIps, clientIp) {
ctx.Status(403)
}
})
}
// 用户认证 // 用户认证
func userAuth(m *macaron.Macaron) { func userAuth(m *macaron.Macaron) {
m.Use(func(ctx *macaron.Context, sess session.Store) { m.Use(func(ctx *macaron.Context, sess session.Store) {
@ -167,7 +182,7 @@ func userAuth(m *macaron.Macaron) {
} }
uri := ctx.Req.URL.Path uri := ctx.Req.URL.Path
found := false found := false
excludePaths := []string{"/install", "/user/login", "/"} excludePaths := []string{"/install", "/user/login", "/api"}
for _, path := range excludePaths { for _, path := range excludePaths {
if strings.HasPrefix(uri, path) { if strings.HasPrefix(uri, path) {
found = true found = true
@ -199,14 +214,6 @@ func setShareData(m *macaron.Macaron) {
}) })
} }
// 管理员认证
func adminAuth(ctx *macaron.Context, sess session.Store) {
if !user.IsAdmin(sess) {
ctx.Data["Title"] = "无权限访问此页面"
ctx.HTML(403, "error/no_permission")
}
}
func isAjaxRequest(ctx *macaron.Context) bool { func isAjaxRequest(ctx *macaron.Context) bool {
req := ctx.Req.Header.Get("X-Requested-With") req := ctx.Req.Header.Get("X-Requested-With")
if req == "XMLHttpRequest" { if req == "XMLHttpRequest" {

View File

@ -57,7 +57,7 @@ func (task *Task) Initialize() {
taskModel := new(models.Task) taskModel := new(models.Task)
taskList, err := taskModel.ActiveList() taskList, err := taskModel.ActiveList()
if err != nil { if err != nil {
logger.Error("获取任务列表错误-", err.Error()) logger.Error("定时任务初始化#获取任务列表错误-", err.Error())
return return
} }
if len(taskList) == 0 { if len(taskList) == 0 {

View File

@ -1,12 +0,0 @@
{{{ template "common/header" . }}}
<script>
swal({
title: "403 - FORBIDDEN",
text: "您无权限访问此页面",
type: "warning"
},
function(){
location.href = "/"
});
</script>
{{{ template "common/footer" . }}}

View File

@ -66,13 +66,13 @@
<textarea rows="5" name="command">{{{.Task.Command}}}</textarea> <textarea rows="5" name="command">{{{.Task.Command}}}</textarea>
</div> </div>
</div> </div>
<div class="three fields"> <div class="six fields">
<div class="field"> <div class="field">
<label>()</label> <label>()</label>
<input type="text" name="timeout" value="{{{.Task.Timeout}}}"> <input type="text" name="timeout" value="{{{.Task.Timeout}}}">
</div> </div>
</div> </div>
<div class="two fields"> <div class="six fields">
<div class="field"> <div class="field">
<label></label> <label></label>
<input type="text" name="retry_times" value="{{{.Task.RetryTimes}}}"> <input type="text" name="retry_times" value="{{{.Task.RetryTimes}}}">
@ -143,7 +143,7 @@
</div> </div>
</div> </div>
{{else}} {{else}}
<a class="ui blue button" href="/manage/mail/edit"></a><br><br> <a class="ui blue button" href="/manage/mail/edit" target="_blank"></a><br><br>
{{/each}} {{/each}}
</script> </script>
@ -156,7 +156,7 @@
</div> </div>
</div> </div>
{{else}} {{else}}
<a class="ui blue button" href="/manage/slack/edit">Slack</a> <a class="ui blue button" href="/manage/slack/edit" target="_blank">Slack</a>
{{/each}} {{/each}}
</script> </script>