From 6016074b14eb1e7d409f84f9c5a1994bbf964c8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Poniedzia=C5=82ek?= Date: Fri, 26 Jan 2024 11:51:41 +0100 Subject: [PATCH 1/4] Add HSTS to caddy configuration --- .../control-plane/add_hsts_header.go | 39 +++++++++++++++++++ provisioning/resources/control-plane/main.go | 16 ++++++++ 2 files changed, 55 insertions(+) create mode 100644 provisioning/resources/control-plane/add_hsts_header.go diff --git a/provisioning/resources/control-plane/add_hsts_header.go b/provisioning/resources/control-plane/add_hsts_header.go new file mode 100644 index 00000000..2564b7c3 --- /dev/null +++ b/provisioning/resources/control-plane/add_hsts_header.go @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2024-present Snowplow Analytics Ltd. All rights reserved. + * + * This software is made available by Snowplow Analytics, Ltd., + * under the terms of the Snowplow Limited Use License Agreement, Version 1.0 + * located at https://docs.snowplow.io/limited-use-license-1.0 + * BY INSTALLING, DOWNLOADING, ACCESSING, USING OR DISTRIBUTING ANY PORTION + * OF THE SOFTWARE, YOU AGREE TO THE TERMS OF SUCH LICENSE AGREEMENT. + */ + +package main + +import ( + "io/ioutil" + "strings" +) + +func addHstsHeader(configPath string) error { + currentConfig, err := ioutil.ReadFile(configPath) + + if err != nil { + return err + } + toReplacePattern := +` + handle @isHttps { + import handleProtectedPaths + } +` + replaceWithHsts := +` + handle @isHttps { + import handleProtectedPaths + header Strict-Transport-Security max-age=31536000; includeSubDomains + } +` + newCaddyConfig := strings.Replace(string(currentConfig), toReplacePattern, replaceWithHsts, 1) + return ioutil.WriteFile(configPath, []byte(newCaddyConfig), 0644) +} diff --git a/provisioning/resources/control-plane/main.go b/provisioning/resources/control-plane/main.go index dd1a3e04..e26fa399 100644 --- a/provisioning/resources/control-plane/main.go +++ b/provisioning/resources/control-plane/main.go @@ -47,6 +47,7 @@ func main() { http.HandleFunc("/version", getSpminiVersion) http.HandleFunc("/telemetry", manageTelemetry) http.HandleFunc("/reset-service", resetService) + http.HandleFunc("/add-hsts", addHsts) log.Fatal(http.ListenAndServe(":10000", nil)) } @@ -131,6 +132,21 @@ func resetService(resp http.ResponseWriter, req *http.Request) { } } +func addHsts (resp http.ResponseWriter, req *http.Request) { + if req.Method == "PUT" { + err := addHstsHeader(config.Dirs.Config+"/"+config.ConfigNames.Caddy) + if err != nil { + http.Error(resp, err.Error(), 500) + } else { + resp.WriteHeader(http.StatusOK) + io.WriteString(resp, "OK") + } + } else { + // Return 404 for other methods + http.Error(resp, "", 404) + } +} + func uploadEnrichments(resp http.ResponseWriter, req *http.Request) { if req.Method == "POST" { // maxMemory bytes of body's file parts are stored in memory, From 86ada3350ea0c412968cb40ca8666e8849864890 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Poniedzia=C5=82ek?= Date: Mon, 29 Jan 2024 11:32:56 +0100 Subject: [PATCH 2/4] Formatting + caddyfile path fix --- .../resources/control-plane/add_hsts_header.go | 16 ++++++++-------- provisioning/resources/control-plane/main.go | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/provisioning/resources/control-plane/add_hsts_header.go b/provisioning/resources/control-plane/add_hsts_header.go index 2564b7c3..e6956b20 100644 --- a/provisioning/resources/control-plane/add_hsts_header.go +++ b/provisioning/resources/control-plane/add_hsts_header.go @@ -12,28 +12,28 @@ package main import ( "io/ioutil" - "strings" + "strings" ) func addHstsHeader(configPath string) error { - currentConfig, err := ioutil.ReadFile(configPath) + currentConfig, err := ioutil.ReadFile(configPath) - if err != nil { + if err != nil { return err } - toReplacePattern := -` + toReplacePattern := + ` handle @isHttps { import handleProtectedPaths } ` - replaceWithHsts := -` + replaceWithHsts := + ` handle @isHttps { import handleProtectedPaths header Strict-Transport-Security max-age=31536000; includeSubDomains } ` - newCaddyConfig := strings.Replace(string(currentConfig), toReplacePattern, replaceWithHsts, 1) + newCaddyConfig := strings.Replace(string(currentConfig), toReplacePattern, replaceWithHsts, 1) return ioutil.WriteFile(configPath, []byte(newCaddyConfig), 0644) } diff --git a/provisioning/resources/control-plane/main.go b/provisioning/resources/control-plane/main.go index e26fa399..5b0e5ef8 100644 --- a/provisioning/resources/control-plane/main.go +++ b/provisioning/resources/control-plane/main.go @@ -132,9 +132,9 @@ func resetService(resp http.ResponseWriter, req *http.Request) { } } -func addHsts (resp http.ResponseWriter, req *http.Request) { - if req.Method == "PUT" { - err := addHstsHeader(config.Dirs.Config+"/"+config.ConfigNames.Caddy) +func addHsts(resp http.ResponseWriter, req *http.Request) { + if req.Method == "PUT" { + err := addHstsHeader(config.ConfigNames.Caddy) if err != nil { http.Error(resp, err.Error(), 500) } else { From f55c81e80dbdc067461860a51942f9f544e5648b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Poniedzia=C5=82ek?= Date: Mon, 29 Jan 2024 11:41:33 +0100 Subject: [PATCH 3/4] Restart caddy after adding HSTS --- provisioning/resources/control-plane/main.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/provisioning/resources/control-plane/main.go b/provisioning/resources/control-plane/main.go index 5b0e5ef8..f8475e3f 100644 --- a/provisioning/resources/control-plane/main.go +++ b/provisioning/resources/control-plane/main.go @@ -137,10 +137,15 @@ func addHsts(resp http.ResponseWriter, req *http.Request) { err := addHstsHeader(config.ConfigNames.Caddy) if err != nil { http.Error(resp, err.Error(), 500) - } else { - resp.WriteHeader(http.StatusOK) - io.WriteString(resp, "OK") + return + } + err, status := restartSPService("caddy") + if err != nil { + http.Error(resp, err.Error(), status) + return } + resp.WriteHeader(http.StatusOK) + io.WriteString(resp, "OK") } else { // Return 404 for other methods http.Error(resp, "", 404) From f5e971819f9643b8328fd09f23b234d631594614 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Poniedzia=C5=82ek?= Date: Mon, 29 Jan 2024 13:33:36 +0100 Subject: [PATCH 4/4] Surround HSTS header value with `""` + add endpoint to openapi.yaml --- provisioning/resources/configs/openapi.yaml | 15 ++++++++++++--- .../resources/control-plane/add_hsts_header.go | 2 +- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/provisioning/resources/configs/openapi.yaml b/provisioning/resources/configs/openapi.yaml index f94b0a61..ab305ccf 100644 --- a/provisioning/resources/configs/openapi.yaml +++ b/provisioning/resources/configs/openapi.yaml @@ -221,9 +221,18 @@ paths: description: "Telemetry has been reconfigured" "401": description: "Unauthorized" - - - + /add-hsts: + put: + tags: + - "configuration" + summary: "Add HSTS header" + description: "Adds HSTS header to underlying caddy configuration. When added, 'Strict-Transport-Security' header is returned for each HTTPS response" + operationId: "addHsts" + responses: + "200": + description: "HSTS header has been added" + "401": + description: "Unauthorized" /restart-services: put: diff --git a/provisioning/resources/control-plane/add_hsts_header.go b/provisioning/resources/control-plane/add_hsts_header.go index e6956b20..67849fed 100644 --- a/provisioning/resources/control-plane/add_hsts_header.go +++ b/provisioning/resources/control-plane/add_hsts_header.go @@ -31,7 +31,7 @@ func addHstsHeader(configPath string) error { ` handle @isHttps { import handleProtectedPaths - header Strict-Transport-Security max-age=31536000; includeSubDomains + header Strict-Transport-Security "max-age=31536000; includeSubDomains" } ` newCaddyConfig := strings.Replace(string(currentConfig), toReplacePattern, replaceWithHsts, 1)