mirror of https://github.com/minio/minio.git
				
				
				
			Support to store browser config settings (#18631)
* csp_policy * hsts_seconds * hsts_include_subdomains * hsts_preload * referrer_policy
This commit is contained in:
		
							parent
							
								
									c1cae51fb5
								
							
						
					
					
						commit
						8f13c8c3bf
					
				|  | @ -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() | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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() | ||||
|  |  | |||
|  | @ -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 | ||||
| 
 | ||||
|  |  | |||
|  | @ -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 | ||||
| } | ||||
|  | @ -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", | ||||
| 		}, | ||||
| 	} | ||||
| ) | ||||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -82,6 +82,7 @@ const ( | |||
| 	EnvWorm       = "MINIO_WORM"        // legacy
 | ||||
| 	EnvRegion     = "MINIO_REGION"      // legacy
 | ||||
| 	EnvRegionName = "MINIO_REGION_NAME" // legacy
 | ||||
| 
 | ||||
| ) | ||||
| 
 | ||||
| // Expiration Token durations
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue