// 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) { cfg = Config{ CSPPolicy: env.Get(EnvBrowserCSPPolicy, kvs.GetWithDefault(browserCSPPolicy, DefaultKVS)), HSTSSeconds: 0, HSTSIncludeSubdomains: true, HSTSPreload: true, ReferrerPolicy: "strict-origin-when-cross-origin", } if err = config.CheckValidKeys(config.BrowserSubSys, kvs, DefaultKVS); err != nil { return cfg, err } hstsIncludeSubdomains := env.Get(EnvBrowserHSTSIncludeSubdomains, kvs.GetWithDefault(browserHSTSIncludeSubdomains, DefaultKVS)) == config.EnableOn hstsPreload := env.Get(EnvBrowserHSTSPreload, kvs.Get(browserHSTSPreload)) == config.EnableOn hstsSeconds, err := strconv.Atoi(env.Get(EnvBrowserHSTSSeconds, kvs.GetWithDefault(browserHSTSSeconds, DefaultKVS))) if err != nil { return cfg, err } cfg.HSTSSeconds = hstsSeconds cfg.HSTSIncludeSubdomains = hstsIncludeSubdomains cfg.HSTSPreload = hstsPreload 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) } 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 }