diff options
author | Alan Pearce | 2024-04-26 01:18:29 +0200 |
---|---|---|
committer | Alan Pearce | 2024-04-27 20:25:11 +0200 |
commit | fe2715d330402ad67fe866471bed89c7238ad2ec (patch) | |
tree | 815f5b631cec339ed010d9f0e72f42f6a0b0f047 | |
parent | d012553aaf78a436fa8871830b5d720a9e292d4b (diff) | |
download | website-fe2715d330402ad67fe866471bed89c7238ad2ec.tar.lz website-fe2715d330402ad67fe866471bed89c7238ad2ec.tar.zst website-fe2715d330402ad67fe866471bed89c7238ad2ec.zip |
config: use a table to configure CSP headers
-rw-r--r-- | cmd/cspgenerator/cspgenerator.go | 13 | ||||
-rw-r--r-- | config.toml | 26 | ||||
-rw-r--r-- | go.mod | 2 | ||||
-rw-r--r-- | go.sum | 4 | ||||
-rw-r--r-- | internal/config/config.go | 1 | ||||
-rw-r--r-- | internal/config/csp.go | 44 | ||||
-rw-r--r-- | internal/config/cspgenerator.go | 79 | ||||
-rw-r--r-- | internal/server/server.go | 1 |
8 files changed, 169 insertions, 1 deletions
diff --git a/cmd/cspgenerator/cspgenerator.go b/cmd/cspgenerator/cspgenerator.go new file mode 100644 index 0000000..f79a591 --- /dev/null +++ b/cmd/cspgenerator/cspgenerator.go @@ -0,0 +1,13 @@ +package main + +import ( + "log" + "website/internal/config" +) + +func main() { + err := config.GenerateCSP() + if err != nil { + log.Fatal(err) + } +} diff --git a/config.toml b/config.toml index 85ec950..55b2508 100644 --- a/config.toml +++ b/config.toml @@ -12,10 +12,34 @@ original_domain = "alanpearce.eu" name = "tags" feed = true +[content-security-policy] +default-src = [ + "'none'", +] +image-src = [ + "'self'", + "http://gc.zgo.at", +] +script-src = [ + "'self'", + "http://gc.zgo.at", +] +style-src = [ + "'unsafe-inline'", +] +frame-ancestors = [ + "https://kagi.com", +] +connect-src = [ + "https://alanpearce-eu.goatcounter.com/count", +] +require-trusted-types-for = [ + "'script'", +] + [extra.headers] cache-control = "max-age=14400" x-content-type-options = "nosniff" -content-security-policy = "default-src 'none'; img-src 'self' https://gc.zgo.at; script-src 'self https://gc.zgo.at'; style-src 'unsafe-inline'; frame-ancestors https://kagi.com; connect-src https://alanpearce-eu.goatcounter.com/count; require-trusted-types-for 'script'" [[menus.main]] name = "Home" diff --git a/go.mod b/go.mod index ca31318..a619ff3 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,9 @@ require ( github.com/antchfx/xmlquery v1.4.0 github.com/antchfx/xpath v1.3.0 github.com/ardanlabs/conf/v3 v3.1.7 + github.com/crewjam/csp v0.0.2 github.com/deckarep/golang-set/v2 v2.6.0 + github.com/fatih/structtag v1.2.0 github.com/getsentry/sentry-go v0.27.0 github.com/otiai10/copy v1.14.0 github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index 7792d9c..fb8f456 100644 --- a/go.sum +++ b/go.sum @@ -16,10 +16,14 @@ github.com/antchfx/xpath v1.3.0/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwq github.com/ardanlabs/conf/v3 v3.1.7 h1:p232cF68TafoA5U9ZlbxUIhGJtGNdKHBXF80Fdqb5t0= github.com/ardanlabs/conf/v3 v3.1.7/go.mod h1:zclexWKe0NVj6LHQ8NgDDZ7bQ1spE0KeKPFficdtAjU= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/crewjam/csp v0.0.2 h1:fIq6o0Z6bkABlvLT3kB0XgPnVX9iNXSAGMILs6AqHVw= +github.com/crewjam/csp v0.0.2/go.mod h1:0tirp4wHwMLZZtV+HXRqGFkUO7uD2ux+1ECvK+7/xFI= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= +github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= diff --git a/internal/config/config.go b/internal/config/config.go index ff213ac..56c0cbb 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -39,6 +39,7 @@ type Config struct { DomainStartDate string `toml:"domain_start_date"` OriginalDomain string `toml:"original_domain"` Taxonomies []Taxonomy + CSP *CSP `toml:"content-security-policy"` Extra struct { Headers map[string]string } diff --git a/internal/config/csp.go b/internal/config/csp.go new file mode 100644 index 0000000..536d9fc --- /dev/null +++ b/internal/config/csp.go @@ -0,0 +1,44 @@ +// Code generated DO NOT EDIT. +package config + +import ( + "github.com/crewjam/csp" +) + +type CSP struct { + BaseURI []string `csp:"base-uri" toml:"base-uri"` + BlockAllMixedContent bool `csp:"block-all-mixed-content" toml:"block-all-mixed-content"` + ChildSrc []string `csp:"child-src" toml:"child-src"` + ConnectSrc []string `csp:"connect-src" toml:"connect-src"` + DefaultSrc []string `csp:"default-src" toml:"default-src"` + FontSrc []string `csp:"font-src" toml:"font-src"` + FormAction []string `csp:"form-action" toml:"form-action"` + FrameAncestors []string `csp:"frame-ancestors" toml:"frame-ancestors"` + FrameSrc []string `csp:"frame-src" toml:"frame-src"` + ImgSrc []string `csp:"img-src" toml:"img-src"` + ManifestSrc []string `csp:"manifest-src" toml:"manifest-src"` + MediaSrc []string `csp:"media-src" toml:"media-src"` + NavigateTo []string `csp:"navigate-to" toml:"navigate-to"` + ObjectSrc []string `csp:"object-src" toml:"object-src"` + PluginTypes []string `csp:"plugin-types" toml:"plugin-types"` + PrefetchSrc []string `csp:"prefetch-src" toml:"prefetch-src"` + Referrer csp.ReferrerPolicy `csp:"referrer" toml:"referrer"` + ReportTo string `csp:"report-to" toml:"report-to"` + ReportURI string `csp:"report-uri" toml:"report-uri"` + RequireSRIFor []csp.RequireSRIFor `csp:"require-sri-for" toml:"require-sri-for"` + RequireTrustedTypesFor []csp.RequireTrustedTypesFor `csp:"require-trusted-types-for" toml:"require-trusted-types-for"` + Sandbox csp.Sandbox `csp:"sandbox" toml:"sandbox"` + ScriptSrc []string `csp:"script-src" toml:"script-src"` + ScriptSrcAttr []string `csp:"script-src-attr" toml:"script-src-attr"` + ScriptSrcElem []string `csp:"script-src-elem" toml:"script-src-elem"` + StyleSrc []string `csp:"style-src" toml:"style-src"` + StyleSrcAttr []string `csp:"style-src-attr" toml:"style-src-attr"` + StyleSrcElem []string `csp:"style-src-elem" toml:"style-src-elem"` + TrustedTypes []string `csp:"trusted-types" toml:"trusted-types"` + UpgradeInsecureRequests bool `csp:"upgrade-insecure-requests" toml:"upgrade-insecure-requests"` + WorkerSrc []string `csp:"worker-src" toml:"worker-src"` +} + +func (c *CSP) String() string { + return csp.Header(*c).String() +} diff --git a/internal/config/cspgenerator.go b/internal/config/cspgenerator.go new file mode 100644 index 0000000..4594d0d --- /dev/null +++ b/internal/config/cspgenerator.go @@ -0,0 +1,79 @@ +package config + +//go:generate go run ../../cmd/cspgenerator/ + +import ( + "fmt" + "os" + "reflect" + + "github.com/crewjam/csp" + "github.com/fatih/structtag" +) + +func GenerateCSP() error { + t := reflect.TypeFor[csp.Header]() + file, err := os.OpenFile("./csp.go", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0) + if err != nil { + return err + } + defer file.Close() + + _, err = fmt.Fprintf(file, `// Code generated DO NOT EDIT. +package config + +import ( + "github.com/crewjam/csp" +) + +`) + if err != nil { + return err + } + + _, err = fmt.Fprintf(file, "type CSP struct {\n") + if err != nil { + return err + } + + for i := 0; i < t.NumField(); i++ { + field := t.Field(i) + var t reflect.Type + if field.Type.Kind() == reflect.Slice { + t = field.Type + } else { + t = field.Type + } + tags, err := structtag.Parse(string(field.Tag)) + if err != nil { + return err + } + cspTag, err := tags.Get("csp") + if err != nil { + return err + } + tags.Set(&structtag.Tag{ + Key: "toml", + Name: cspTag.Name, + }) + + _, err = fmt.Fprintf(file, "\t%-23s %-28s `%s`\n", field.Name, t, tags.String()) + if err != nil { + return err + } + } + _, err = fmt.Fprintln(file, "}") + if err != nil { + return err + } + + _, err = fmt.Fprintln(file, ` +func (c *CSP) String() string { + return csp.Header(*c).String() +} + `) + if err != nil { + return err + } + return nil +} diff --git a/internal/server/server.go b/internal/server/server.go index e5c0dc7..d22ea62 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -63,6 +63,7 @@ func serveFile(w http.ResponseWriter, r *http.Request) *HTTPError { } w.Header().Add("ETag", file.etag) w.Header().Add("Vary", "Accept-Encoding") + w.Header().Add("Content-Security-Policy", config.CSP.String()) for k, v := range config.Extra.Headers { w.Header().Add(k, v) } |