mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-01-06 04:09:40 -06:00
feat: add CSP and other security related headers in the oCIS proxy service (#8777)
* feat: add CSP and other security related headers in the oCIS proxy service * fix: consolidate security related headers - drop middleware.Secure * fix: use github.com/DeepDiver1975/secure * fix: acceptance tests * feat: support env var replacements in csp.yaml
This commit is contained in:
@@ -300,6 +300,11 @@ func loadMiddlewares(ctx context.Context, logger log.Logger, cfg *config.Config,
|
||||
Now: time.Now,
|
||||
})
|
||||
|
||||
cspConfig, err := middleware.LoadCSPConfig(cfg)
|
||||
if err != nil {
|
||||
logger.Fatal().Err(err).Msg("Failed to load CSP configuration.")
|
||||
}
|
||||
|
||||
return alice.New(
|
||||
// first make sure we log all requests and redirect to https if necessary
|
||||
otelhttp.NewMiddleware("proxy",
|
||||
@@ -315,6 +320,7 @@ func loadMiddlewares(ctx context.Context, logger log.Logger, cfg *config.Config,
|
||||
chimiddleware.RequestID,
|
||||
middleware.AccessLog(logger),
|
||||
middleware.HTTPSRedirect,
|
||||
middleware.Security(cspConfig),
|
||||
router.Middleware(cfg.PolicySelector, cfg.Policies, logger),
|
||||
middleware.Authentication(
|
||||
authenticators,
|
||||
|
||||
@@ -42,6 +42,7 @@ type Config struct {
|
||||
BackendHTTPSCACert string `yaml:"backend_https_cacert" env:"PROXY_HTTPS_CACERT" desc:"Path/File for the root CA certificate used to validate the server’s TLS certificate for https enabled backend services." introductionVersion:"pre5.0"`
|
||||
AuthMiddleware AuthMiddleware `yaml:"auth_middleware"`
|
||||
PoliciesMiddleware PoliciesMiddleware `yaml:"policies_middleware"`
|
||||
CSPConfigFileLocation string `yaml:"csp_config_file_location" env:"PROXY_CSP_CONFIG_FILE_LOCATION" desc:"The location of the CSP configuration file." introductionVersion:"6.0"`
|
||||
|
||||
Context context.Context `yaml:"-" json:"-"`
|
||||
}
|
||||
@@ -140,7 +141,7 @@ type RoleAssignment struct {
|
||||
OIDCRoleMapper OIDCRoleMapper `yaml:"oidc_role_mapper"`
|
||||
}
|
||||
|
||||
// OIDCRoleMapper contains the configuration for the "oidc" role assignment driber
|
||||
// OIDCRoleMapper contains the configuration for the "oidc" role assignment driver
|
||||
type OIDCRoleMapper struct {
|
||||
RoleClaim string `yaml:"role_claim" env:"PROXY_ROLE_ASSIGNMENT_OIDC_CLAIM" desc:"The OIDC claim used to create the users role assignment." introductionVersion:"pre5.0"`
|
||||
RolesMap []RoleMapping `yaml:"role_mapping" desc:"A list of mappings of ocis role names to PROXY_ROLE_ASSIGNMENT_OIDC_CLAIM claim values. This setting can only be configured in the configuration file and not via environment variables."`
|
||||
|
||||
13
services/proxy/pkg/config/csp.go
Normal file
13
services/proxy/pkg/config/csp.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
)
|
||||
|
||||
// CSP defines CSP header directives
|
||||
type CSP struct {
|
||||
Directives map[string][]string `yaml:"directives"`
|
||||
}
|
||||
|
||||
//go:embed csp.yaml
|
||||
var DefaultCSPConfig string
|
||||
31
services/proxy/pkg/config/csp.yaml
Normal file
31
services/proxy/pkg/config/csp.yaml
Normal file
@@ -0,0 +1,31 @@
|
||||
directives:
|
||||
child-src:
|
||||
- '''self'''
|
||||
connect-src:
|
||||
- '''self'''
|
||||
default-src:
|
||||
- '''none'''
|
||||
font-src:
|
||||
- '''self'''
|
||||
frame-ancestors:
|
||||
- '''none'''
|
||||
frame-src:
|
||||
- '''self'''
|
||||
- 'https://embed.diagrams.net/'
|
||||
img-src:
|
||||
- '''self'''
|
||||
- 'data:'
|
||||
- 'blob:'
|
||||
manifest-src:
|
||||
- '''self'''
|
||||
media-src:
|
||||
- '''self'''
|
||||
object-src:
|
||||
- '''self'''
|
||||
- 'blob:'
|
||||
script-src:
|
||||
- '''self'''
|
||||
- '''unsafe-inline'''
|
||||
style-src:
|
||||
- '''self'''
|
||||
- '''unsafe-inline'''
|
||||
@@ -86,6 +86,7 @@ func DefaultConfig() *config.Config {
|
||||
AutoprovisionAccounts: false,
|
||||
EnableBasicAuth: false,
|
||||
InsecureBackends: false,
|
||||
CSPConfigFileLocation: "",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
61
services/proxy/pkg/middleware/security.go
Normal file
61
services/proxy/pkg/middleware/security.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/a8m/envsubst"
|
||||
"github.com/owncloud/ocis/v2/services/proxy/pkg/config"
|
||||
"github.com/unrolled/secure"
|
||||
"github.com/unrolled/secure/cspbuilder"
|
||||
"gopkg.in/yaml.v2"
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
// LoadCSPConfig loads CSP header configuration from a yaml file.
|
||||
func LoadCSPConfig(proxyCfg *config.Config) (*config.CSP, error) {
|
||||
yamlContent, err := loadCSPYaml(proxyCfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// replace env vars ..
|
||||
yamlContent, err = envsubst.Bytes(yamlContent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// read yaml
|
||||
cspConfig := config.CSP{}
|
||||
err = yaml.Unmarshal(yamlContent, &cspConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &cspConfig, nil
|
||||
}
|
||||
|
||||
func loadCSPYaml(proxyCfg *config.Config) ([]byte, error) {
|
||||
if proxyCfg.CSPConfigFileLocation == "" {
|
||||
return []byte(config.DefaultCSPConfig), nil
|
||||
}
|
||||
return os.ReadFile(proxyCfg.CSPConfigFileLocation)
|
||||
}
|
||||
|
||||
// Security is a middleware to apply security relevant http headers like CSP.
|
||||
func Security(cspConfig *config.CSP) func(h http.Handler) http.Handler {
|
||||
cspBuilder := cspbuilder.Builder{
|
||||
Directives: cspConfig.Directives,
|
||||
}
|
||||
|
||||
secureMiddleware := secure.New(secure.Options{
|
||||
BrowserXssFilter: true,
|
||||
ContentSecurityPolicy: cspBuilder.MustBuild(),
|
||||
ContentTypeNosniff: true,
|
||||
CustomFrameOptionsValue: "SAMEORIGIN",
|
||||
FrameDeny: true,
|
||||
ReferrerPolicy: "strict-origin-when-cross-origin",
|
||||
STSSeconds: 315360000,
|
||||
STSPreload: true,
|
||||
})
|
||||
return func(next http.Handler) http.Handler {
|
||||
return secureMiddleware.Handler(next)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user