conf: overhaul security settings

This commit is contained in:
ᴜɴᴋɴᴡᴏɴ 2020-02-22 20:46:16 +08:00
parent a7e53b8134
commit 286fbc07e9
No known key found for this signature in database
GPG Key ID: B43718D76E30A238
19 changed files with 151 additions and 126 deletions

View File

@ -146,6 +146,27 @@ SSL_MODE = disable
; For "sqlite3" only, make sure to use absolute path.
PATH = data/gogs.db
[security]
; Whether to show the install page, set this to "true" to bypass it.
INSTALL_LOCK = false
; The secret to encrypt cookie values, 2FA code, etc.
; !!CHANGE THIS TO KEEP YOUR USER DATA SAFE!!
SECRET_KEY = !#@FDEWREWR&*(
; The days remembered for auto-login.
LOGIN_REMEMBER_DAYS = 7
; The cookie name to stoed auto-login information.
COOKIE_REMEMBER_NAME = gogs_incredible
; The cookie name to stored logged in username.
COOKIE_USERNAME = gogs_awesome
; Whether to set secure cookie.
COOKIE_SECURE = false
; The HTTP header for reverse proxy authentication via username.
REVERSE_PROXY_AUTHENTICATION_USER = X-WEBAUTH-USER
; Whether to set cookie to indicate user login status.
ENABLE_LOGIN_STATUS_COOKIE = false
; The cookie name to store user login status.
LOGIN_STATUS_COOKIE_NAME = login_status
; Attachment settings for releases
[release.attachment]
; Whether attachments are enabled. Defaults to `true`
@ -184,21 +205,6 @@ ACCESS_CONTROL_ALLOW_ORIGIN =
; Disable regular (non-admin) users to create organizations
DISABLE_REGULAR_ORG_CREATION = false
[security]
INSTALL_LOCK = false
; !!CHANGE THIS TO KEEP YOUR USER DATA SAFE!!
SECRET_KEY = !#@FDEWREWR&*(
; Auto-login remember days
LOGIN_REMEMBER_DAYS = 7
COOKIE_USERNAME = gogs_awesome
COOKIE_REMEMBER_NAME = gogs_incredible
COOKIE_SECURE = false
; Reverse proxy authentication header name of user name
REVERSE_PROXY_AUTHENTICATION_USER = X-WEBAUTH-USER
; Enable to set cookie to indicate user login status
ENABLE_LOGIN_STATUS_COOKIE = false
LOGIN_STATUS_COOKIE_NAME = login_status
[service]
ACTIVE_CODE_LIVE_MINUTES = 180
RESET_PASSWD_CODE_LIVE_MINUTES = 180

View File

@ -1226,8 +1226,16 @@ config.db.ssl_mode_helper = (for "postgres" only)
config.db.path = Path
config.db.path_helper = (for "sqlite3"only)
config.security_config = Security configuration
config.security.login_remember_days = Login remember days
config.security.cookie_remember_name = Remember cookie
config.security.cookie_username = Username cookie
config.security.cookie_secure = Enable secure cookie
config.security.reverse_proxy_auth_user = Reverse proxy authentication header
config.security.enable_login_status_cookie = Enable login status cookie
config.security.login_status_cookie_name = Login status cookie
config.log_file_root_path = Log File Root Path
config.reverse_auth_user = Reverse Authentication User
config.http_config = HTTP Configuration
config.http_access_control_allow_origin = Access Control Allow Origin

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -13,9 +13,9 @@ import (
"gopkg.in/macaron.v1"
log "unknwon.dev/clog/v2"
"gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/db"
"gogs.io/gogs/internal/db/errors"
"gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/tool"
)
@ -91,7 +91,7 @@ func SignedInUser(ctx *macaron.Context, sess session.Store) (_ *db.User, isBasic
if uid <= 0 {
if conf.Service.EnableReverseProxyAuth {
webAuthUser := ctx.Req.Header.Get(conf.ReverseProxyAuthUser)
webAuthUser := ctx.Req.Header.Get(conf.Security.ReverseProxyAuthenticationUser)
if len(webAuthUser) > 0 {
u, err := db.GetUserByName(webAuthUser)
if err != nil {

View File

@ -143,7 +143,7 @@ func newMacaron() *macaron.Macaron {
}))
m.Use(session.Sessioner(conf.SessionConfig))
m.Use(csrf.Csrfer(csrf.Options{
Secret: conf.SecretKey,
Secret: conf.Security.SecretKey,
Cookie: conf.CSRFCookieName,
SetCookie: true,
Header: "X-Csrf-Token",

View File

@ -5,6 +5,7 @@
package conf
import (
"fmt"
"net/mail"
"net/url"
"os"
@ -27,7 +28,6 @@ import (
"gogs.io/gogs/internal/assets/conf"
"gogs.io/gogs/internal/osutil"
"gogs.io/gogs/internal/user"
)
func init() {
@ -192,30 +192,27 @@ func Init(customConf string) error {
}
Database.Path = ensureAbs(Database.Path)
// *******************************
// ----- Security settings -----
// *******************************
if err = File.Section("security").MapTo(&Security); err != nil {
return errors.Wrap(err, "mapping [security] section")
}
// Check run user when the install is locked.
if Security.InstallLock {
currentUser, match := CheckRunUser(App.RunUser)
if !match {
return fmt.Errorf("user configured to run Gogs is %q, but the current user is %q", App.RunUser, currentUser)
}
}
handleDeprecated()
// TODO
sec := File.Section("security")
InstallLock = sec.Key("INSTALL_LOCK").MustBool()
SecretKey = sec.Key("SECRET_KEY").String()
LoginRememberDays = sec.Key("LOGIN_REMEMBER_DAYS").MustInt()
CookieUserName = sec.Key("COOKIE_USERNAME").String()
CookieRememberName = sec.Key("COOKIE_REMEMBER_NAME").String()
CookieSecure = sec.Key("COOKIE_SECURE").MustBool(false)
ReverseProxyAuthUser = sec.Key("REVERSE_PROXY_AUTHENTICATION_USER").MustString("X-WEBAUTH-USER")
EnableLoginStatusCookie = sec.Key("ENABLE_LOGIN_STATUS_COOKIE").MustBool(false)
LoginStatusCookieName = sec.Key("LOGIN_STATUS_COOKIE_NAME").MustString("login_status")
// Does not check run user when the install lock is off.
if InstallLock {
currentUser, match := IsRunUserMatchCurrentUser(App.RunUser)
if !match {
log.Fatal("The user configured to run Gogs is %q, but the current user is %q", App.RunUser, currentUser)
}
}
sec = File.Section("attachment")
sec := File.Section("attachment")
AttachmentPath = sec.Key("PATH").MustString(filepath.Join(Server.AppDataPath, "attachments"))
if !filepath.IsAbs(AttachmentPath) {
AttachmentPath = path.Join(workDir, AttachmentPath)
@ -342,17 +339,6 @@ var (
AccessControlAllowOrigin string
}
// Security settings
InstallLock bool
SecretKey string
LoginRememberDays int
CookieUserName string
CookieRememberName string
CookieSecure bool
ReverseProxyAuthUser string
EnableLoginStatusCookie bool
LoginStatusCookieName string
// Database settings
UseSQLite3 bool
UseMySQL bool
@ -539,19 +525,6 @@ func DateLang(lang string) string {
return "en"
}
// IsRunUserMatchCurrentUser returns false if configured run user does not match
// actual user that runs the app. The first return value is the actual user name.
// This check is ignored under Windows since SSH remote login is not the main
// method to login on Windows.
func IsRunUserMatchCurrentUser(runUser string) (string, bool) {
if IsWindowsRuntime() {
return "", true
}
currentUser := user.CurrentUsername()
return currentUser, runUser == currentUser
}
// InitLogging initializes the logging service of the application.
func InitLogging() {
LogRootPath = File.Section("log").Key("ROOT_PATH").MustString(filepath.Join(WorkDir(), "log"))
@ -585,7 +558,7 @@ func InitLogging() {
return
}
level := levelMappings[sec.Key("LEVEL").MustString("trace")]
level := levelMappings[strings.ToLower(sec.Key("LEVEL").MustString("trace"))]
buffer := sec.Key("BUFFER_LEN").MustInt64(100)
c := new(config)
switch mode {

View File

@ -139,6 +139,19 @@ var (
// Deprecated: Use Password instead, will be removed in 0.13.
Passwd string
}
// Security settings
Security struct {
InstallLock bool
SecretKey string
LoginRememberDays int
CookieRememberName string
CookieUsername string
CookieSecure bool
ReverseProxyAuthenticationUser string
EnableLoginStatusCookie bool
LoginStatusCookieName string
}
)
// handleDeprecated transfers deprecated values to the new ones when set.

View File

@ -10,6 +10,7 @@ import (
"github.com/pkg/errors"
"gogs.io/gogs/internal/osutil"
"gogs.io/gogs/internal/process"
)
@ -34,3 +35,15 @@ func ensureAbs(path string) string {
}
return filepath.Join(WorkDir(), path)
}
// CheckRunUser returns false if configured run user does not match actual user that
// runs the app. The first return value is the actual user name. This check is ignored
// under Windows since SSH remote login is not the main method to login on Windows.
func CheckRunUser(runUser string) (string, bool) {
if IsWindowsRuntime() {
return "", true
}
currentUser := osutil.CurrentUsername()
return currentUser, runUser == currentUser
}

View File

@ -27,7 +27,7 @@ type ToggleOptions struct {
func Toggle(options *ToggleOptions) macaron.Handler {
return func(c *Context) {
// Cannot view any page before installation.
if !conf.InstallLock {
if !conf.Security.InstallLock {
c.Redirect(conf.Server.Subpath + "/install")
return
}
@ -80,7 +80,7 @@ func Toggle(options *ToggleOptions) macaron.Handler {
// Redirect to log in page if auto-signin info is provided and has not signed in.
if !options.SignOutRequired && !c.IsLogged && !auth.IsAPIPath(c.Req.URL.Path) &&
len(c.GetCookie(conf.CookieUserName)) > 0 {
len(c.GetCookie(conf.Security.CookieUsername)) > 0 {
c.SetCookie("redirect_to", url.QueryEscape(conf.Server.Subpath+c.Req.RequestURI), 0, conf.Server.Subpath)
c.Redirect(conf.Server.Subpath + "/user/login")
return

View File

@ -15,8 +15,8 @@ import (
log "unknwon.dev/clog/v2"
"xorm.io/xorm"
"gogs.io/gogs/internal/db/errors"
"gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/db/errors"
"gogs.io/gogs/internal/tool"
)
@ -47,7 +47,7 @@ func (t *TwoFactor) ValidateTOTP(passcode string) (bool, error) {
if err != nil {
return false, fmt.Errorf("DecodeString: %v", err)
}
decryptSecret, err := com.AESGCMDecrypt(tool.MD5Bytes(conf.SecretKey), secret)
decryptSecret, err := com.AESGCMDecrypt(tool.MD5Bytes(conf.Security.SecretKey), secret)
if err != nil {
return false, fmt.Errorf("AESGCMDecrypt: %v", err)
}
@ -85,7 +85,7 @@ func NewTwoFactor(userID int64, secret string) error {
}
// Encrypt secret
encryptSecret, err := com.AESGCMEncrypt(tool.MD5Bytes(conf.SecretKey), []byte(secret))
encryptSecret, err := com.AESGCMEncrypt(tool.MD5Bytes(conf.Security.SecretKey), []byte(secret))
if err != nil {
return fmt.Errorf("AESGCMEncrypt: %v", err)
}

View File

@ -16,3 +16,13 @@ func IsFile(path string) bool {
}
return !f.IsDir()
}
// CurrentUsername returns the current system user via environment variables.
func CurrentUsername() string {
curUserName := os.Getenv("USER")
if len(curUserName) > 0 {
return curUserName
}
return os.Getenv("USERNAME")
}

View File

@ -201,9 +201,9 @@ func Config(c *context.Context) {
c.Data["SSH"] = conf.SSH
c.Data["Repository"] = conf.Repository
c.Data["Database"] = conf.Database
c.Data["Security"] = conf.Security
c.Data["LogRootPath"] = conf.LogRootPath
c.Data["ReverseProxyAuthUser"] = conf.ReverseProxyAuthUser
c.Data["HTTP"] = conf.HTTP

View File

@ -32,7 +32,7 @@ func Home(c *context.Context) {
}
// Check auto-login.
uname := c.GetCookie(conf.CookieUserName)
uname := c.GetCookie(conf.Security.CookieUsername)
if len(uname) != 0 {
c.Redirect(conf.Server.Subpath + "/user/login")
return

View File

@ -31,7 +31,6 @@ import (
"gogs.io/gogs/internal/ssh"
"gogs.io/gogs/internal/template/highlight"
"gogs.io/gogs/internal/tool"
"gogs.io/gogs/internal/user"
)
const (
@ -67,7 +66,7 @@ func GlobalInit(customConf string) error {
conf.NewServices()
mailer.NewContext()
if conf.InstallLock {
if conf.Security.InstallLock {
highlight.NewContext()
markup.NewSanitizer()
if err := db.NewEngine(); err != nil {
@ -96,7 +95,7 @@ func GlobalInit(customConf string) error {
}
checkRunMode()
if !conf.InstallLock {
if !conf.Security.InstallLock {
return nil
}
@ -116,7 +115,7 @@ func GlobalInit(customConf string) error {
}
func InstallInit(c *context.Context) {
if conf.InstallLock {
if conf.Security.InstallLock {
c.NotFound()
return
}
@ -159,7 +158,7 @@ func Install(c *context.Context) {
// Note(unknwon): it's hard for Windows users change a running user,
// so just use current one if config says default.
if conf.IsWindowsRuntime() && conf.App.RunUser == "git" {
f.RunUser = user.CurrentUsername()
f.RunUser = osutil.CurrentUsername()
} else {
f.RunUser = conf.App.RunUser
}
@ -265,7 +264,7 @@ func InstallPost(c *context.Context, f form.Install) {
return
}
currentUser, match := conf.IsRunUserMatchCurrentUser(f.RunUser)
currentUser, match := conf.CheckRunUser(f.RunUser)
if !match {
c.FormErr("RunUser")
c.RenderWithErr(c.Tr("install.run_user_not_match", f.RunUser, currentUser), INSTALL, &f)
@ -406,7 +405,7 @@ func InstallPost(c *context.Context, f form.Install) {
}
if err := db.CreateUser(u); err != nil {
if !db.IsErrUserAlreadyExist(err) {
conf.InstallLock = false
conf.Security.InstallLock = false
c.FormErr("AdminName", "AdminEmail")
c.RenderWithErr(c.Tr("install.invalid_admin_setting", err), INSTALL, &f)
return

View File

@ -36,7 +36,7 @@ func AutoLogin(c *context.Context) (bool, error) {
return false, nil
}
uname := c.GetCookie(conf.CookieUserName)
uname := c.GetCookie(conf.Security.CookieUsername)
if len(uname) == 0 {
return false, nil
}
@ -45,9 +45,9 @@ func AutoLogin(c *context.Context) (bool, error) {
defer func() {
if !isSucceed {
log.Trace("auto-login cookie cleared: %s", uname)
c.SetCookie(conf.CookieUserName, "", -1, conf.Server.Subpath)
c.SetCookie(conf.CookieRememberName, "", -1, conf.Server.Subpath)
c.SetCookie(conf.LoginStatusCookieName, "", -1, conf.Server.Subpath)
c.SetCookie(conf.Security.CookieUsername, "", -1, conf.Server.Subpath)
c.SetCookie(conf.Security.CookieRememberName, "", -1, conf.Server.Subpath)
c.SetCookie(conf.Security.LoginStatusCookieName, "", -1, conf.Server.Subpath)
}
}()
@ -59,7 +59,7 @@ func AutoLogin(c *context.Context) (bool, error) {
return false, nil
}
if val, ok := c.GetSuperSecureCookie(u.Rands+u.Passwd, conf.CookieRememberName); !ok || val != u.Name {
if val, ok := c.GetSuperSecureCookie(u.Rands+u.Passwd, conf.Security.CookieRememberName); !ok || val != u.Name {
return false, nil
}
@ -67,8 +67,8 @@ func AutoLogin(c *context.Context) (bool, error) {
c.Session.Set("uid", u.ID)
c.Session.Set("uname", u.Name)
c.SetCookie(conf.CSRFCookieName, "", -1, conf.Server.Subpath)
if conf.EnableLoginStatusCookie {
c.SetCookie(conf.LoginStatusCookieName, "true", 0, conf.Server.Subpath)
if conf.Security.EnableLoginStatusCookie {
c.SetCookie(conf.Security.LoginStatusCookieName, "true", 0, conf.Server.Subpath)
}
return true, nil
}
@ -119,9 +119,9 @@ func Login(c *context.Context) {
func afterLogin(c *context.Context, u *db.User, remember bool) {
if remember {
days := 86400 * conf.LoginRememberDays
c.SetCookie(conf.CookieUserName, u.Name, days, conf.Server.Subpath, "", conf.CookieSecure, true)
c.SetSuperSecureCookie(u.Rands+u.Passwd, conf.CookieRememberName, u.Name, days, conf.Server.Subpath, "", conf.CookieSecure, true)
days := 86400 * conf.Security.LoginRememberDays
c.SetCookie(conf.Security.CookieUsername, u.Name, days, conf.Server.Subpath, "", conf.Security.CookieSecure, true)
c.SetSuperSecureCookie(u.Rands+u.Passwd, conf.Security.CookieRememberName, u.Name, days, conf.Server.Subpath, "", conf.Security.CookieSecure, true)
}
c.Session.Set("uid", u.ID)
@ -131,8 +131,8 @@ func afterLogin(c *context.Context, u *db.User, remember bool) {
// Clear whatever CSRF has right now, force to generate a new one
c.SetCookie(conf.CSRFCookieName, "", -1, conf.Server.Subpath)
if conf.EnableLoginStatusCookie {
c.SetCookie(conf.LoginStatusCookieName, "true", 0, conf.Server.Subpath)
if conf.Security.EnableLoginStatusCookie {
c.SetCookie(conf.Security.LoginStatusCookieName, "true", 0, conf.Server.Subpath)
}
redirectTo, _ := url.QueryUnescape(c.GetCookie("redirect_to"))
@ -283,8 +283,8 @@ func LoginTwoFactorRecoveryCodePost(c *context.Context) {
func SignOut(c *context.Context) {
c.Session.Flush()
c.Session.Destory(c.Context)
c.SetCookie(conf.CookieUserName, "", -1, conf.Server.Subpath)
c.SetCookie(conf.CookieRememberName, "", -1, conf.Server.Subpath)
c.SetCookie(conf.Security.CookieUsername, "", -1, conf.Server.Subpath)
c.SetCookie(conf.Security.CookieRememberName, "", -1, conf.Server.Subpath)
c.SetCookie(conf.CSRFCookieName, "", -1, conf.Server.Subpath)
c.SubURLRedirect("/")
}

View File

@ -170,7 +170,7 @@ func CreateTimeLimitCode(data string, minutes int, startInf interface{}) string
// create sha1 encode string
sh := sha1.New()
_, _ = sh.Write([]byte(data + conf.SecretKey + startStr + endStr + com.ToStr(minutes)))
_, _ = sh.Write([]byte(data + conf.Security.SecretKey + startStr + endStr + com.ToStr(minutes)))
encoded := hex.EncodeToString(sh.Sum(nil))
code := fmt.Sprintf("%s%06d%s", startStr, minutes, encoded)

View File

@ -1,18 +0,0 @@
// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package user
import (
"os"
)
func CurrentUsername() string {
curUserName := os.Getenv("USER")
if len(curUserName) > 0 {
return curUserName
}
return os.Getenv("USERNAME")
}

View File

@ -67,8 +67,6 @@
<dt>{{.i18n.Tr "admin.config.log_file_root_path"}}</dt>
<dd><code>{{.LogRootPath}}</code></dd>
<dt>{{.i18n.Tr "admin.config.reverse_auth_user"}}</dt>
<dd><code>{{.ReverseProxyAuthUser}}</code></dd>
</dl>
</div>
@ -182,6 +180,29 @@
</dl>
</div>
{{/* Security settings */}}
<h4 class="ui top attached header">
{{.i18n.Tr "admin.config.security_config"}}
</h4>
<div class="ui attached table segment">
<dl class="dl-horizontal admin-dl-horizontal">
<dt>{{.i18n.Tr "admin.config.security.login_remember_days"}}</dt>
<dd>{{.Security.LoginRememberDays}}</dd>
<dt>{{.i18n.Tr "admin.config.security.cookie_remember_name"}}</dt>
<dd>{{.Security.CookieRememberName}}</dd>
<dt>{{.i18n.Tr "admin.config.security.cookie_username"}}</dt>
<dd>{{.Security.CookieUsername}}</dd>
<dt>{{.i18n.Tr "admin.config.security.cookie_secure"}}</dt>
<dd><i class="fa fa{{if .Security.CookieSecure}}-check{{end}}-square-o"></i></dd>
<dt>{{.i18n.Tr "admin.config.security.reverse_proxy_auth_user"}}</dt>
<dd>{{.Security.ReverseProxyAuthenticationUser}}</dd>
<dt>{{.i18n.Tr "admin.config.security.enable_login_status_cookie"}}</dt>
<dd><i class="fa fa{{if .Security.EnableLoginStatusCookie}}-check{{end}}-square-o"></i></dd>
<dt>{{.i18n.Tr "admin.config.security.login_status_cookie_name"}}</dt>
<dd>{{.Security.LoginStatusCookieName}}</dd>
</dl>
</div>
<!-- HTTP Configuration -->
<h4 class="ui top attached header">
{{.i18n.Tr "admin.config.http_config"}}