diff --git a/cmd/common-main.go b/cmd/common-main.go
index 75f8d1487..71cf8d0e0 100644
--- a/cmd/common-main.go
+++ b/cmd/common-main.go
@@ -178,6 +178,23 @@ func minioConfigToConsoleFeatures() {
os.Setenv("CONSOLE_MINIO_REGION", globalSite.Region)
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()
}
diff --git a/cmd/config-current.go b/cmd/config-current.go
index e8f3a303f..9dc033a69 100644
--- a/cmd/config-current.go
+++ b/cmd/config-current.go
@@ -24,6 +24,8 @@ import (
"strings"
"sync"
+ "github.com/minio/minio/internal/config/browser"
+
"github.com/minio/madmin-go/v3"
"github.com/minio/minio/internal/config"
"github.com/minio/minio/internal/config/api"
@@ -74,6 +76,7 @@ func initHelp() {
config.DriveSubSys: drive.DefaultKVS,
config.CacheSubSys: cache.DefaultKVS,
config.BatchSubSys: batch.DefaultKVS,
+ config.BrowserSubSys: browser.DefaultKVS,
}
for k, v := range notify.DefaultNotificationKVS {
kvs[k] = v
@@ -226,6 +229,11 @@ func initHelp() {
Description: "enable cache plugin on MinIO for GET/HEAD requests",
Optional: true,
},
+ config.HelpKV{
+ Key: config.BrowserSubSys,
+ Description: "manage Browser HTTP specific features, such as Security headers, etc.",
+ Optional: true,
+ },
}
if globalIsErasure {
@@ -273,6 +281,7 @@ func initHelp() {
config.CallhomeSubSys: callhome.HelpCallhome,
config.DriveSubSys: drive.HelpDrive,
config.CacheSubSys: cache.Help,
+ config.BrowserSubSys: browser.Help,
}
config.RegisterHelpSubSys(helpMap)
@@ -407,6 +416,10 @@ func validateSubSysConfig(ctx context.Context, s config.Config, subSys string, o
return err
}
}
+ case config.BrowserSubSys:
+ if _, err := browser.LookupConfig(s[config.BrowserSubSys][config.Default]); err != nil {
+ return err
+ }
default:
if config.LoggerSubSystems.Contains(subSys) {
if err := logger.ValidateSubSysConfig(ctx, s, subSys); err != nil {
@@ -689,6 +702,12 @@ func applyDynamicConfigForSubSys(ctx context.Context, objAPI ObjectLayer, s conf
} else {
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()
defer globalServerConfigMu.Unlock()
diff --git a/cmd/globals.go b/cmd/globals.go
index b4ab43098..8f2b16617 100644
--- a/cmd/globals.go
+++ b/cmd/globals.go
@@ -33,6 +33,7 @@ import (
"github.com/minio/minio/internal/bpool"
"github.com/minio/minio/internal/bucket/bandwidth"
"github.com/minio/minio/internal/config"
+ "github.com/minio/minio/internal/config/browser"
"github.com/minio/minio/internal/handlers"
"github.com/minio/minio/internal/kms"
"go.uber.org/atomic"
@@ -191,6 +192,9 @@ var (
// Disable redirect, default is enabled.
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.
globalInplaceUpdateDisabled = false
diff --git a/internal/config/browser/browser.go b/internal/config/browser/browser.go
new file mode 100644
index 000000000..a67451a92
--- /dev/null
+++ b/internal/config/browser/browser.go
@@ -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 .
+
+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
+}
diff --git a/internal/config/browser/help.go b/internal/config/browser/help.go
new file mode 100644
index 000000000..8f8483e20
--- /dev/null
+++ b/internal/config/browser/help.go
@@ -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 .
+
+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",
+ },
+ }
+)
diff --git a/internal/config/config.go b/internal/config/config.go
index 63821894f..d329e0014 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -119,6 +119,7 @@ const (
CallhomeSubSys = madmin.CallhomeSubSys
DriveSubSys = madmin.DriveSubSys
BatchSubSys = madmin.BatchSubSys
+ BrowserSubSys = madmin.BrowserSubSys
// Add new constants here (similar to above) if you add new fields to config.
)
@@ -188,6 +189,7 @@ var SubSystemsDynamic = set.CreateStringSet(
StorageClassSubSys,
CacheSubSys,
BatchSubSys,
+ BrowserSubSys,
)
// SubSystemsSingleTargets - subsystems which only support single target.
@@ -209,8 +211,8 @@ var SubSystemsSingleTargets = set.CreateStringSet(
SubnetSubSys,
CallhomeSubSys,
DriveSubSys,
- CacheSubSys,
BatchSubSys,
+ BrowserSubSys,
)
// Constant separators
diff --git a/internal/config/constants.go b/internal/config/constants.go
index 52406586d..0dc0f8343 100644
--- a/internal/config/constants.go
+++ b/internal/config/constants.go
@@ -82,6 +82,7 @@ const (
EnvWorm = "MINIO_WORM" // legacy
EnvRegion = "MINIO_REGION" // legacy
EnvRegionName = "MINIO_REGION_NAME" // legacy
+
)
// Expiration Token durations