Support to store browser config settings (#18631)

* csp_policy
* hsts_seconds
* hsts_include_subdomains
* hsts_preload
* referrer_policy
This commit is contained in:
Pedro Juarez 2024-01-01 08:36:33 -08:00 committed by GitHub
parent c1cae51fb5
commit 8f13c8c3bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 268 additions and 1 deletions

View File

@ -178,6 +178,23 @@ func minioConfigToConsoleFeatures() {
os.Setenv("CONSOLE_MINIO_REGION", globalSite.Region) os.Setenv("CONSOLE_MINIO_REGION", globalSite.Region)
os.Setenv("CONSOLE_CERT_PASSWD", env.Get("MINIO_CERT_PASSWD", "")) os.Setenv("CONSOLE_CERT_PASSWD", env.Get("MINIO_CERT_PASSWD", ""))
// This section sets Browser (console) stored config
if valueSCP := globalBrowserConfig.GetCSPolicy(); valueSCP != "" {
os.Setenv("CONSOLE_SECURE_CONTENT_SECURITY_POLICY", valueSCP)
}
if hstsSeconds := globalBrowserConfig.GetHSTSSeconds(); hstsSeconds > 0 {
isubdom := globalBrowserConfig.IsHSTSIncludeSubdomains()
isprel := globalBrowserConfig.IsHSTSPreload()
os.Setenv("CONSOLE_SECURE_STS_SECONDS", strconv.Itoa(hstsSeconds))
os.Setenv("CONSOLE_SECURE_STS_INCLUDE_SUB_DOMAINS", isubdom)
os.Setenv("CONSOLE_SECURE_STS_PRELOAD", isprel)
}
if valueRefer := globalBrowserConfig.GetReferPolicy(); valueRefer != "" {
os.Setenv("CONSOLE_SECURE_REFERRER_POLICY", valueRefer)
}
globalSubnetConfig.ApplyEnv() globalSubnetConfig.ApplyEnv()
} }

View File

@ -24,6 +24,8 @@ import (
"strings" "strings"
"sync" "sync"
"github.com/minio/minio/internal/config/browser"
"github.com/minio/madmin-go/v3" "github.com/minio/madmin-go/v3"
"github.com/minio/minio/internal/config" "github.com/minio/minio/internal/config"
"github.com/minio/minio/internal/config/api" "github.com/minio/minio/internal/config/api"
@ -74,6 +76,7 @@ func initHelp() {
config.DriveSubSys: drive.DefaultKVS, config.DriveSubSys: drive.DefaultKVS,
config.CacheSubSys: cache.DefaultKVS, config.CacheSubSys: cache.DefaultKVS,
config.BatchSubSys: batch.DefaultKVS, config.BatchSubSys: batch.DefaultKVS,
config.BrowserSubSys: browser.DefaultKVS,
} }
for k, v := range notify.DefaultNotificationKVS { for k, v := range notify.DefaultNotificationKVS {
kvs[k] = v kvs[k] = v
@ -226,6 +229,11 @@ func initHelp() {
Description: "enable cache plugin on MinIO for GET/HEAD requests", Description: "enable cache plugin on MinIO for GET/HEAD requests",
Optional: true, Optional: true,
}, },
config.HelpKV{
Key: config.BrowserSubSys,
Description: "manage Browser HTTP specific features, such as Security headers, etc.",
Optional: true,
},
} }
if globalIsErasure { if globalIsErasure {
@ -273,6 +281,7 @@ func initHelp() {
config.CallhomeSubSys: callhome.HelpCallhome, config.CallhomeSubSys: callhome.HelpCallhome,
config.DriveSubSys: drive.HelpDrive, config.DriveSubSys: drive.HelpDrive,
config.CacheSubSys: cache.Help, config.CacheSubSys: cache.Help,
config.BrowserSubSys: browser.Help,
} }
config.RegisterHelpSubSys(helpMap) config.RegisterHelpSubSys(helpMap)
@ -407,6 +416,10 @@ func validateSubSysConfig(ctx context.Context, s config.Config, subSys string, o
return err return err
} }
} }
case config.BrowserSubSys:
if _, err := browser.LookupConfig(s[config.BrowserSubSys][config.Default]); err != nil {
return err
}
default: default:
if config.LoggerSubSystems.Contains(subSys) { if config.LoggerSubSystems.Contains(subSys) {
if err := logger.ValidateSubSysConfig(ctx, s, subSys); err != nil { if err := logger.ValidateSubSysConfig(ctx, s, subSys); err != nil {
@ -689,6 +702,12 @@ func applyDynamicConfigForSubSys(ctx context.Context, objAPI ObjectLayer, s conf
} else { } else {
globalCacheConfig.Update(cacheCfg) globalCacheConfig.Update(cacheCfg)
} }
case config.BrowserSubSys:
browserCfg, err := browser.LookupConfig(s[config.BrowserSubSys][config.Default])
if err != nil {
return fmt.Errorf("Unable to apply browser config: %w", err)
}
globalBrowserConfig.Update(browserCfg)
} }
globalServerConfigMu.Lock() globalServerConfigMu.Lock()
defer globalServerConfigMu.Unlock() defer globalServerConfigMu.Unlock()

View File

@ -33,6 +33,7 @@ import (
"github.com/minio/minio/internal/bpool" "github.com/minio/minio/internal/bpool"
"github.com/minio/minio/internal/bucket/bandwidth" "github.com/minio/minio/internal/bucket/bandwidth"
"github.com/minio/minio/internal/config" "github.com/minio/minio/internal/config"
"github.com/minio/minio/internal/config/browser"
"github.com/minio/minio/internal/handlers" "github.com/minio/minio/internal/handlers"
"github.com/minio/minio/internal/kms" "github.com/minio/minio/internal/kms"
"go.uber.org/atomic" "go.uber.org/atomic"
@ -191,6 +192,9 @@ var (
// Disable redirect, default is enabled. // Disable redirect, default is enabled.
globalBrowserRedirect bool globalBrowserRedirect bool
// globalBrowserConfig Browser user configurable settings
globalBrowserConfig browser.Config
// This flag is set to 'true' when MINIO_UPDATE env is set to 'off'. Default is false. // This flag is set to 'true' when MINIO_UPDATE env is set to 'off'. Default is false.
globalInplaceUpdateDisabled = false globalInplaceUpdateDisabled = false

View File

@ -0,0 +1,164 @@
// Copyright (c) 2015-2023 MinIO, Inc.
//
// This file is part of MinIO Object Storage stack
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package browser
import (
"fmt"
"strconv"
"sync"
"github.com/minio/minio/internal/config"
"github.com/minio/pkg/v2/env"
)
// Browser sub-system constants
const (
// browserCSPPolicy setting name for Content-Security-Policy response header value
browserCSPPolicy = "csp_policy"
// browserHSTSSeconds setting name for Strict-Transport-Security response header, amount of seconds for 'max-age'
browserHSTSSeconds = "hsts_seconds"
// browserHSTSIncludeSubdomains setting name for Strict-Transport-Security response header 'includeSubDomains' flag (true or false)
browserHSTSIncludeSubdomains = "hsts_include_subdomains"
// browserHSTSPreload setting name for Strict-Transport-Security response header 'preload' flag (true or false)
browserHSTSPreload = "hsts_preload"
// browserReferrerPolicy setting name for Referrer-Policy response header
browserReferrerPolicy = "referrer_policy"
EnvBrowserCSPPolicy = "MINIO_BROWSER_CONTENT_SECURITY_POLICY"
EnvBrowserHSTSSeconds = "MINIO_BROWSER_HSTS_SECONDS"
EnvBrowserHSTSIncludeSubdomains = "MINIO_BROWSER_HSTS_INCLUDE_SUB_DOMAINS"
EnvBrowserHSTSPreload = "MINIO_BROWSER_HSTS_PRELOAD"
EnvBrowserReferrerPolicy = "MINIO_BROWSER_REFERRER_POLICY"
)
// DefaultKVS - default storage class config
var (
DefaultKVS = config.KVS{
config.KV{
Key: browserCSPPolicy,
Value: "default-src 'self' 'unsafe-eval' 'unsafe-inline';",
},
config.KV{
Key: browserHSTSSeconds,
Value: "0",
},
config.KV{
Key: browserHSTSIncludeSubdomains,
Value: config.EnableOff,
},
config.KV{
Key: browserHSTSPreload,
Value: config.EnableOff,
},
config.KV{
Key: browserReferrerPolicy,
Value: "strict-origin-when-cross-origin",
},
}
)
// configLock is a global lock for browser config
var configLock sync.RWMutex
// Config storage class configuration
type Config struct {
CSPPolicy string `json:"csp_policy"`
HSTSSeconds int `json:"hsts_seconds"`
HSTSIncludeSubdomains bool `json:"hsts_include_subdomains"`
HSTSPreload bool `json:"hsts_preload"`
ReferrerPolicy string `json:"referrer_policy"`
}
// Update Updates browser with new config
func (browseCfg *Config) Update(newCfg Config) {
configLock.Lock()
defer configLock.Unlock()
browseCfg.CSPPolicy = newCfg.CSPPolicy
browseCfg.HSTSSeconds = newCfg.HSTSSeconds
browseCfg.HSTSIncludeSubdomains = newCfg.HSTSIncludeSubdomains
browseCfg.HSTSPreload = newCfg.HSTSPreload
browseCfg.ReferrerPolicy = newCfg.ReferrerPolicy
}
// LookupConfig - lookup api config and override with valid environment settings if any.
func LookupConfig(kvs config.KVS) (cfg Config, err error) {
cspPolicy := env.Get(EnvBrowserCSPPolicy, kvs.GetWithDefault(browserCSPPolicy, DefaultKVS))
hstsSeconds, err := strconv.Atoi(env.Get(EnvBrowserHSTSSeconds, kvs.GetWithDefault(browserHSTSSeconds, DefaultKVS)))
if err != nil {
return cfg, err
}
hstsIncludeSubdomains := env.Get(EnvBrowserHSTSIncludeSubdomains, kvs.GetWithDefault(browserHSTSIncludeSubdomains, DefaultKVS)) == config.EnableOn
hstsPreload := env.Get(EnvBrowserHSTSPreload, kvs.Get(browserHSTSPreload)) == config.EnableOn
referrerPolicy := env.Get(EnvBrowserReferrerPolicy, kvs.GetWithDefault(browserReferrerPolicy, DefaultKVS))
switch referrerPolicy {
case "no-referrer", "no-referrer-when-downgrade", "origin", "origin-when-cross-origin", "same-origin", "strict-origin", "strict-origin-when-cross-origin", "unsafe-url":
cfg.ReferrerPolicy = referrerPolicy
default:
return cfg, fmt.Errorf("invalid value %v for %s", referrerPolicy, browserReferrerPolicy)
}
cfg.CSPPolicy = cspPolicy
cfg.HSTSSeconds = hstsSeconds
cfg.HSTSIncludeSubdomains = hstsIncludeSubdomains
cfg.HSTSPreload = hstsPreload
return cfg, nil
}
// GetCSPolicy - Get the Content security Policy
func (browseCfg *Config) GetCSPolicy() string {
configLock.RLock()
defer configLock.RUnlock()
return browseCfg.CSPPolicy
}
// GetHSTSSeconds - Get the Content security Policy
func (browseCfg *Config) GetHSTSSeconds() int {
configLock.RLock()
defer configLock.RUnlock()
return browseCfg.HSTSSeconds
}
// IsHSTSIncludeSubdomains - is HSTS 'includeSubdomains' directive enabled
func (browseCfg *Config) IsHSTSIncludeSubdomains() string {
configLock.RLock()
defer configLock.RUnlock()
if browseCfg.HSTSSeconds > 0 && browseCfg.HSTSIncludeSubdomains {
return config.EnableOn
}
return config.EnableOff
}
// IsHSTSPreload - is HSTS 'preload' directive enabled
func (browseCfg *Config) IsHSTSPreload() string {
configLock.RLock()
defer configLock.RUnlock()
if browseCfg.HSTSSeconds > 0 && browseCfg.HSTSPreload {
return config.EnableOn
}
return config.EnableOff
}
// GetReferPolicy - Get the ReferPolicy
func (browseCfg *Config) GetReferPolicy() string {
configLock.RLock()
defer configLock.RUnlock()
return browseCfg.ReferrerPolicy
}

View File

@ -0,0 +1,60 @@
// Copyright (c) 2015-2023 MinIO, Inc.
//
// # This file is part of MinIO Object Storage stack
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package browser
import "github.com/minio/minio/internal/config"
// Help template for browser feature.
var (
defaultHelpPostfix = func(key string) string {
return config.DefaultHelpPostfix(DefaultKVS, key)
}
Help = config.HelpKVS{
config.HelpKV{
Key: browserCSPPolicy,
Description: `set Content-Security-Policy response header value` + defaultHelpPostfix(browserCSPPolicy),
Optional: true,
Type: "string",
},
config.HelpKV{
Key: browserHSTSSeconds,
Description: `set Strict-Transport-Security 'max-age' amount of seconds value` + defaultHelpPostfix(browserHSTSSeconds),
Optional: true,
Type: "number",
},
config.HelpKV{
Key: browserHSTSIncludeSubdomains,
Description: `turn 'on' to set Strict-Transport-Security 'includeSubDomains' directive` + defaultHelpPostfix(browserHSTSIncludeSubdomains),
Optional: true,
Type: "boolean",
},
config.HelpKV{
Key: browserHSTSPreload,
Description: `turn 'on' to set Strict-Transport-Security 'preload' directive` + defaultHelpPostfix(browserHSTSPreload),
Optional: true,
Type: "boolean",
},
config.HelpKV{
Key: browserReferrerPolicy,
Description: `set Referrer-Policy response header value` + defaultHelpPostfix(browserReferrerPolicy),
Optional: true,
Type: "string",
},
}
)

View File

@ -119,6 +119,7 @@ const (
CallhomeSubSys = madmin.CallhomeSubSys CallhomeSubSys = madmin.CallhomeSubSys
DriveSubSys = madmin.DriveSubSys DriveSubSys = madmin.DriveSubSys
BatchSubSys = madmin.BatchSubSys BatchSubSys = madmin.BatchSubSys
BrowserSubSys = madmin.BrowserSubSys
// Add new constants here (similar to above) if you add new fields to config. // Add new constants here (similar to above) if you add new fields to config.
) )
@ -188,6 +189,7 @@ var SubSystemsDynamic = set.CreateStringSet(
StorageClassSubSys, StorageClassSubSys,
CacheSubSys, CacheSubSys,
BatchSubSys, BatchSubSys,
BrowserSubSys,
) )
// SubSystemsSingleTargets - subsystems which only support single target. // SubSystemsSingleTargets - subsystems which only support single target.
@ -209,8 +211,8 @@ var SubSystemsSingleTargets = set.CreateStringSet(
SubnetSubSys, SubnetSubSys,
CallhomeSubSys, CallhomeSubSys,
DriveSubSys, DriveSubSys,
CacheSubSys,
BatchSubSys, BatchSubSys,
BrowserSubSys,
) )
// Constant separators // Constant separators

View File

@ -82,6 +82,7 @@ const (
EnvWorm = "MINIO_WORM" // legacy EnvWorm = "MINIO_WORM" // legacy
EnvRegion = "MINIO_REGION" // legacy EnvRegion = "MINIO_REGION" // legacy
EnvRegionName = "MINIO_REGION_NAME" // legacy EnvRegionName = "MINIO_REGION_NAME" // legacy
) )
// Expiration Token durations // Expiration Token durations