✨ Add Helmet & Watermark
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				release-nightly / build-docker (push) Successful in 2m37s
				
					
					
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	release-nightly / build-docker (push) Successful in 2m37s
				This commit is contained in:
		| @@ -28,4 +28,5 @@ host = ["localhost:8000"] | ||||
| path = ["/"] | ||||
| [[locations.destinations]] | ||||
| id = "example-destination" | ||||
| uri = "http://protocol.smartsheep.studio:20004" | ||||
| uri = "https://example.com" | ||||
| helmet = { x_frame_options = "SAMEORIGIN" } | ||||
| @@ -1,7 +1,6 @@ | ||||
| package hypertext | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"github.com/spf13/viper" | ||||
| 	"math/rand" | ||||
| 	"regexp" | ||||
| @@ -105,22 +104,6 @@ func makeResponse(c *fiber.Ctx, region *navi.Region, location *navi.Location, de | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Add reserve proxy headers | ||||
| 	ip := c.IP() | ||||
| 	scheme := c.Protocol() | ||||
| 	protocol := string(c.Request().Header.Protocol()) | ||||
| 	c.Request().Header.Set(fiber.HeaderXForwardedFor, ip) | ||||
| 	c.Request().Header.Set(fiber.HeaderXForwardedHost, ip) | ||||
| 	c.Request().Header.Set(fiber.HeaderXForwardedProto, scheme) | ||||
| 	c.Request().Header.Set( | ||||
| 		fiber.HeaderVia, | ||||
| 		fmt.Sprintf("%s %s", protocol, viper.GetString("central")), | ||||
| 	) | ||||
| 	c.Request().Header.Set( | ||||
| 		fiber.HeaderForwarded, | ||||
| 		fmt.Sprintf("by=%s; for=%s; host=%s; proto=%s", c.IP(), c.IP(), c.Get(fiber.HeaderHost), scheme), | ||||
| 	) | ||||
|  | ||||
| 	// Forward | ||||
| 	err := navi.R.Forward(c, dest) | ||||
|  | ||||
|   | ||||
							
								
								
									
										89
									
								
								pkg/navi/helmet.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								pkg/navi/helmet.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,89 @@ | ||||
| package navi | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| ) | ||||
|  | ||||
| type HelmetConfig struct { | ||||
| 	XSSProtection             string `json:"xss_protection" toml:"xss_protection"` | ||||
| 	ContentTypeNosniff        string `json:"content_type_nosniff" toml:"content_type_nosniff"` | ||||
| 	XFrameOptions             string `json:"x_frame_options" toml:"x_frame_options"` | ||||
| 	HSTSMaxAge                int    `json:"hsts_max_age" toml:"hsts_max_age"` | ||||
| 	HSTSExcludeSubdomains     bool   `json:"hsts_exclude_subdomains" toml:"hsts_exclude_subdomains"` | ||||
| 	ContentSecurityPolicy     string `json:"content_security_policy" toml:"content_security_policy"` | ||||
| 	CSPReportOnly             bool   `json:"csp_report_only" toml:"csp_report_only"` | ||||
| 	HSTSPreloadEnabled        bool   `json:"hsts_preload_enabled" toml:"hsts_preload_enabled"` | ||||
| 	ReferrerPolicy            string `json:"referrer_policy" toml:"referrer_policy"` | ||||
| 	PermissionPolicy          string `json:"permission_policy" toml:"permission_policy"` | ||||
| 	CrossOriginEmbedderPolicy string `json:"cross_origin_embedder_policy" toml:"cross_origin_embedder_policy"` | ||||
| 	CrossOriginOpenerPolicy   string `json:"cross_origin_opener_policy" toml:"cross_origin_opener_policy"` | ||||
| 	CrossOriginResourcePolicy string `json:"cross_origin_resource_policy" toml:"cross_origin_resource_policy"` | ||||
| 	OriginAgentCluster        string `json:"origin_agent_cluster" toml:"origin_agent_cluster"` | ||||
| 	XDNSPrefetchControl       string `json:"xdns_prefetch_control" toml:"xdns_prefetch_control"` | ||||
| 	XDownloadOptions          string `json:"x_download_options" toml:"x_download_options"` | ||||
| 	XPermittedCrossDomain     string `json:"x_permitted_cross_domain" toml:"x_permitted_cross_domain"` | ||||
| } | ||||
|  | ||||
| func (cfg HelmetConfig) Apply(c *fiber.Ctx) { | ||||
| 	// Apply other headers | ||||
| 	if cfg.XSSProtection != "" { | ||||
| 		c.Set(fiber.HeaderXXSSProtection, cfg.XSSProtection) | ||||
| 	} | ||||
| 	if cfg.ContentTypeNosniff != "" { | ||||
| 		c.Set(fiber.HeaderXContentTypeOptions, cfg.ContentTypeNosniff) | ||||
| 	} | ||||
| 	if cfg.XFrameOptions != "" { | ||||
| 		c.Set(fiber.HeaderXFrameOptions, cfg.XFrameOptions) | ||||
| 	} | ||||
| 	if cfg.CrossOriginEmbedderPolicy != "" { | ||||
| 		c.Set("Cross-Origin-Embedder-Policy", cfg.CrossOriginEmbedderPolicy) | ||||
| 	} | ||||
| 	if cfg.CrossOriginOpenerPolicy != "" { | ||||
| 		c.Set("Cross-Origin-Opener-Policy", cfg.CrossOriginOpenerPolicy) | ||||
| 	} | ||||
| 	if cfg.CrossOriginResourcePolicy != "" { | ||||
| 		c.Set("Cross-Origin-Resource-Policy", cfg.CrossOriginResourcePolicy) | ||||
| 	} | ||||
| 	if cfg.OriginAgentCluster != "" { | ||||
| 		c.Set("Origin-Agent-Cluster", cfg.OriginAgentCluster) | ||||
| 	} | ||||
| 	if cfg.ReferrerPolicy != "" { | ||||
| 		c.Set("Referrer-Policy", cfg.ReferrerPolicy) | ||||
| 	} | ||||
| 	if cfg.XDNSPrefetchControl != "" { | ||||
| 		c.Set("X-DNS-Prefetch-Control", cfg.XDNSPrefetchControl) | ||||
| 	} | ||||
| 	if cfg.XDownloadOptions != "" { | ||||
| 		c.Set("X-Download-Options", cfg.XDownloadOptions) | ||||
| 	} | ||||
| 	if cfg.XPermittedCrossDomain != "" { | ||||
| 		c.Set("X-Permitted-Cross-Domain-Policies", cfg.XPermittedCrossDomain) | ||||
| 	} | ||||
|  | ||||
| 	// Handle HSTS headers | ||||
| 	if c.Protocol() == "https" && cfg.HSTSMaxAge != 0 { | ||||
| 		subdomains := "" | ||||
| 		if !cfg.HSTSExcludeSubdomains { | ||||
| 			subdomains = "; includeSubDomains" | ||||
| 		} | ||||
| 		if cfg.HSTSPreloadEnabled { | ||||
| 			subdomains = fmt.Sprintf("%s; preload", subdomains) | ||||
| 		} | ||||
| 		c.Set(fiber.HeaderStrictTransportSecurity, fmt.Sprintf("max-age=%d%s", cfg.HSTSMaxAge, subdomains)) | ||||
| 	} | ||||
|  | ||||
| 	// Handle Content-Security-Policy headers | ||||
| 	if cfg.ContentSecurityPolicy != "" { | ||||
| 		if cfg.CSPReportOnly { | ||||
| 			c.Set(fiber.HeaderContentSecurityPolicyReportOnly, cfg.ContentSecurityPolicy) | ||||
| 		} else { | ||||
| 			c.Set(fiber.HeaderContentSecurityPolicy, cfg.ContentSecurityPolicy) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Handle Permissions-Policy headers | ||||
| 	if cfg.PermissionPolicy != "" { | ||||
| 		c.Set(fiber.HeaderPermissionsPolicy, cfg.PermissionPolicy) | ||||
| 	} | ||||
| } | ||||
| @@ -1,7 +1,10 @@ | ||||
| package navi | ||||
|  | ||||
| import ( | ||||
| 	roadsign "code.smartsheep.studio/goatworks/roadsign/pkg" | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/navi/transformers" | ||||
| 	"fmt" | ||||
| 	"github.com/spf13/viper" | ||||
|  | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| ) | ||||
| @@ -11,15 +14,44 @@ type RoadApp struct { | ||||
| 	Traces  []RoadTrace `json:"traces"` | ||||
| } | ||||
|  | ||||
| func (v *RoadApp) Forward(ctx *fiber.Ctx, dest *Destination) error { | ||||
| func (v *RoadApp) Forward(c *fiber.Ctx, dest *Destination) error { | ||||
| 	// Add reserve proxy headers | ||||
| 	ip := c.IP() | ||||
| 	scheme := c.Protocol() | ||||
| 	protocol := string(c.Request().Header.Protocol()) | ||||
| 	c.Request().Header.Set(fiber.HeaderXForwardedFor, ip) | ||||
| 	c.Request().Header.Set(fiber.HeaderXForwardedHost, ip) | ||||
| 	c.Request().Header.Set(fiber.HeaderXForwardedProto, scheme) | ||||
| 	c.Request().Header.Set( | ||||
| 		fiber.HeaderVia, | ||||
| 		fmt.Sprintf("%s %s", protocol, viper.GetString("central")), | ||||
| 	) | ||||
| 	c.Request().Header.Set( | ||||
| 		fiber.HeaderForwarded, | ||||
| 		fmt.Sprintf("by=%s; for=%s; host=%s; proto=%s", c.IP(), c.IP(), c.Get(fiber.HeaderHost), scheme), | ||||
| 	) | ||||
|  | ||||
| 	// Response body | ||||
| 	var err error | ||||
| 	switch dest.GetType() { | ||||
| 	case DestinationHypertext: | ||||
| 		return makeUnifiedResponse(ctx, dest) | ||||
| 		err = makeUnifiedResponse(c, dest) | ||||
| 	case DestinationStaticFile: | ||||
| 		return makeFileResponse(ctx, dest) | ||||
| 		err = makeFileResponse(c, dest) | ||||
| 	default: | ||||
| 		return fiber.ErrBadGateway | ||||
| 		err = fiber.ErrBadGateway | ||||
| 	} | ||||
|  | ||||
| 	// Apply helmet | ||||
| 	if dest.Helmet != nil { | ||||
| 		dest.Helmet.Apply(c) | ||||
| 	} | ||||
|  | ||||
| 	// Apply watermark | ||||
| 	c.Response().Header.Set(fiber.HeaderServer, "RoadSign") | ||||
| 	c.Response().Header.Set(fiber.HeaderXPoweredBy, fmt.Sprintf("RoadSign %s", roadsign.AppVersion)) | ||||
|  | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| type RequestTransformerConfig = transformers.TransformerConfig | ||||
|   | ||||
| @@ -38,6 +38,7 @@ const ( | ||||
| type Destination struct { | ||||
| 	ID           string                           `json:"id" toml:"id"` | ||||
| 	Uri          string                           `json:"uri" toml:"uri"` | ||||
| 	Helmet       *HelmetConfig                    `json:"helmet" toml:"helmet"` | ||||
| 	Transformers []transformers.TransformerConfig `json:"transformers" toml:"transformers"` | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user