398 lines
18 KiB
Diff
398 lines
18 KiB
Diff
|
|
From 6b605505047416bbbf513bba1540220a8897f3f6 Mon Sep 17 00:00:00 2001
|
||
|
|
From: Damien Neil <dneil@google.com>
|
||
|
|
Date: Fri, 22 Nov 2024 12:34:11 -0800
|
||
|
|
Subject: [PATCH] [release-branch.go1.24] net/http: persist header stripping across repeated redirects
|
||
|
|
|
||
|
|
CVE: CVE-2024-45336
|
||
|
|
Reference: https://go-review.googlesource.com/c/go/+/643106
|
||
|
|
|
||
|
|
When an HTTP redirect changes the host of a request, we drop
|
||
|
|
sensitive headers such as Authorization from the redirected request.
|
||
|
|
Fix a bug where a chain of redirects could result in sensitive
|
||
|
|
headers being sent to the wrong host:
|
||
|
|
|
||
|
|
1. request to a.tld with Authorization header
|
||
|
|
2. a.tld redirects to b.tld
|
||
|
|
3. request to b.tld with no Authorization header
|
||
|
|
4. b.tld redirects to b.tld
|
||
|
|
3. request to b.tld with Authorization header restored
|
||
|
|
|
||
|
|
Thanks to Kyle Seely for reporting this issue.
|
||
|
|
|
||
|
|
For #70530
|
||
|
|
Fixes #71212
|
||
|
|
Fixes CVE-2024-45336
|
||
|
|
|
||
|
|
Change-Id: Ia58a2e10d33d6b0cc7220935e771450e5c34de72
|
||
|
|
Reviewed-on: https://go-internal-review.googlesource.com/c/go/+/1641
|
||
|
|
Reviewed-by: Roland Shoemaker <bracewell@google.com>
|
||
|
|
Reviewed-by: Tatiana Bradley <tatianabradley@google.com>
|
||
|
|
Commit-Queue: Roland Shoemaker <bracewell@google.com>
|
||
|
|
(cherry picked from commit 2889169b87a61f1218a02994feb80fd3d8bfa87c)
|
||
|
|
Reviewed-on: https://go-internal-review.googlesource.com/c/go/+/1766
|
||
|
|
Reviewed-on: https://go-review.googlesource.com/c/go/+/643100
|
||
|
|
Auto-Submit: Michael Knyszek <mknyszek@google.com>
|
||
|
|
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
|
||
|
|
Reviewed-by: Michael Pratt <mpratt@google.com>
|
||
|
|
---
|
||
|
|
|
||
|
|
diff --git a/src/net/http/client.go b/src/net/http/client.go
|
||
|
|
index fda7815..9231f63 100644
|
||
|
|
--- a/src/net/http/client.go
|
||
|
|
+++ b/src/net/http/client.go
|
||
|
|
@@ -610,8 +610,9 @@
|
||
|
|
reqBodyClosed = false // have we closed the current req.Body?
|
||
|
|
|
||
|
|
// Redirect behavior:
|
||
|
|
- redirectMethod string
|
||
|
|
- includeBody bool
|
||
|
|
+ redirectMethod string
|
||
|
|
+ includeBody = true
|
||
|
|
+ stripSensitiveHeaders = false
|
||
|
|
)
|
||
|
|
uerr := func(err error) error {
|
||
|
|
// the body may have been closed already by c.send()
|
||
|
|
@@ -678,7 +679,12 @@
|
||
|
|
// in case the user set Referer on their first request.
|
||
|
|
// If they really want to override, they can do it in
|
||
|
|
// their CheckRedirect func.
|
||
|
|
- copyHeaders(req)
|
||
|
|
+ if !stripSensitiveHeaders && reqs[0].URL.Host != req.URL.Host {
|
||
|
|
+ if !shouldCopyHeaderOnRedirect(reqs[0].URL, req.URL) {
|
||
|
|
+ stripSensitiveHeaders = true
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+ copyHeaders(req, stripSensitiveHeaders)
|
||
|
|
|
||
|
|
// Add the Referer header from the most recent
|
||
|
|
// request URL to the new one, if it's not https->http:
|
||
|
|
@@ -746,7 +752,7 @@
|
||
|
|
// makeHeadersCopier makes a function that copies headers from the
|
||
|
|
// initial Request, ireq. For every redirect, this function must be called
|
||
|
|
// so that it can copy headers into the upcoming Request.
|
||
|
|
-func (c *Client) makeHeadersCopier(ireq *Request) func(*Request) {
|
||
|
|
+func (c *Client) makeHeadersCopier(ireq *Request) func(req *Request, stripSensitiveHeaders bool) {
|
||
|
|
// The headers to copy are from the very initial request.
|
||
|
|
// We use a closured callback to keep a reference to these original headers.
|
||
|
|
var (
|
||
|
|
@@ -760,8 +766,7 @@
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
- preq := ireq // The previous request
|
||
|
|
- return func(req *Request) {
|
||
|
|
+ return func(req *Request, stripSensitiveHeaders bool) {
|
||
|
|
// If Jar is present and there was some initial cookies provided
|
||
|
|
// via the request header, then we may need to alter the initial
|
||
|
|
// cookies as we follow redirects since each redirect may end up
|
||
|
|
@@ -798,12 +803,15 @@
|
||
|
|
// Copy the initial request's Header values
|
||
|
|
// (at least the safe ones).
|
||
|
|
for k, vv := range ireqhdr {
|
||
|
|
- if shouldCopyHeaderOnRedirect(k, preq.URL, req.URL) {
|
||
|
|
+ sensitive := false
|
||
|
|
+ switch CanonicalHeaderKey(k) {
|
||
|
|
+ case "Authorization", "Www-Authenticate", "Cookie", "Cookie2":
|
||
|
|
+ sensitive = true
|
||
|
|
+ }
|
||
|
|
+ if !(sensitive && stripSensitiveHeaders) {
|
||
|
|
req.Header[k] = vv
|
||
|
|
}
|
||
|
|
}
|
||
|
|
-
|
||
|
|
- preq = req // Update previous Request with the current request
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
@@ -979,28 +987,23 @@
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
-func shouldCopyHeaderOnRedirect(headerKey string, initial, dest *url.URL) bool {
|
||
|
|
- switch CanonicalHeaderKey(headerKey) {
|
||
|
|
- case "Authorization", "Www-Authenticate", "Cookie", "Cookie2":
|
||
|
|
- // Permit sending auth/cookie headers from "foo.com"
|
||
|
|
- // to "sub.foo.com".
|
||
|
|
+func shouldCopyHeaderOnRedirect(initial, dest *url.URL) bool {
|
||
|
|
+ // Permit sending auth/cookie headers from "foo.com"
|
||
|
|
+ // to "sub.foo.com".
|
||
|
|
|
||
|
|
- // Note that we don't send all cookies to subdomains
|
||
|
|
- // automatically. This function is only used for
|
||
|
|
- // Cookies set explicitly on the initial outgoing
|
||
|
|
- // client request. Cookies automatically added via the
|
||
|
|
- // CookieJar mechanism continue to follow each
|
||
|
|
- // cookie's scope as set by Set-Cookie. But for
|
||
|
|
- // outgoing requests with the Cookie header set
|
||
|
|
- // directly, we don't know their scope, so we assume
|
||
|
|
- // it's for *.domain.com.
|
||
|
|
+ // Note that we don't send all cookies to subdomains
|
||
|
|
+ // automatically. This function is only used for
|
||
|
|
+ // Cookies set explicitly on the initial outgoing
|
||
|
|
+ // client request. Cookies automatically added via the
|
||
|
|
+ // CookieJar mechanism continue to follow each
|
||
|
|
+ // cookie's scope as set by Set-Cookie. But for
|
||
|
|
+ // outgoing requests with the Cookie header set
|
||
|
|
+ // directly, we don't know their scope, so we assume
|
||
|
|
+ // it's for *.domain.com.
|
||
|
|
|
||
|
|
- ihost := idnaASCIIFromURL(initial)
|
||
|
|
- dhost := idnaASCIIFromURL(dest)
|
||
|
|
- return isDomainOrSubdomain(dhost, ihost)
|
||
|
|
- }
|
||
|
|
- // All other headers are copied:
|
||
|
|
- return true
|
||
|
|
+ ihost := idnaASCIIFromURL(initial)
|
||
|
|
+ dhost := idnaASCIIFromURL(dest)
|
||
|
|
+ return isDomainOrSubdomain(dhost, ihost)
|
||
|
|
}
|
||
|
|
|
||
|
|
// isDomainOrSubdomain reports whether sub is a subdomain (or exact
|
||
|
|
diff --git a/src/net/http/client_test.go b/src/net/http/client_test.go
|
||
|
|
index 429b8f1..1ce9539 100644
|
||
|
|
--- a/src/net/http/client_test.go
|
||
|
|
+++ b/src/net/http/client_test.go
|
||
|
|
@@ -1536,6 +1536,55 @@
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
+// Issue #70530: Once we strip a header on a redirect to a different host,
|
||
|
|
+// the header should stay stripped across any further redirects.
|
||
|
|
+func TestClientStripHeadersOnRepeatedRedirect(t *testing.T) {
|
||
|
|
+ run(t, testClientStripHeadersOnRepeatedRedirect)
|
||
|
|
+}
|
||
|
|
+func testClientStripHeadersOnRepeatedRedirect(t *testing.T, mode testMode) {
|
||
|
|
+ var proto string
|
||
|
|
+ ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
|
||
|
|
+ if r.Host+r.URL.Path != "a.example.com/" {
|
||
|
|
+ if h := r.Header.Get("Authorization"); h != "" {
|
||
|
|
+ t.Errorf("on request to %v%v, Authorization=%q, want no header", r.Host, r.URL.Path, h)
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+ // Follow a chain of redirects from a to b and back to a.
|
||
|
|
+ // The Authorization header is stripped on the first redirect to b,
|
||
|
|
+ // and stays stripped even if we're sent back to a.
|
||
|
|
+ switch r.Host + r.URL.Path {
|
||
|
|
+ case "a.example.com/":
|
||
|
|
+ Redirect(w, r, proto+"://b.example.com/", StatusFound)
|
||
|
|
+ case "b.example.com/":
|
||
|
|
+ Redirect(w, r, proto+"://b.example.com/redirect", StatusFound)
|
||
|
|
+ case "b.example.com/redirect":
|
||
|
|
+ Redirect(w, r, proto+"://a.example.com/redirect", StatusFound)
|
||
|
|
+ case "a.example.com/redirect":
|
||
|
|
+ w.Header().Set("X-Done", "true")
|
||
|
|
+ default:
|
||
|
|
+ t.Errorf("unexpected request to %v", r.URL)
|
||
|
|
+ }
|
||
|
|
+ })).ts
|
||
|
|
+ proto, _, _ = strings.Cut(ts.URL, ":")
|
||
|
|
+
|
||
|
|
+ c := ts.Client()
|
||
|
|
+ c.Transport.(*Transport).Dial = func(_ string, _ string) (net.Conn, error) {
|
||
|
|
+ return net.Dial("tcp", ts.Listener.Addr().String())
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ req, _ := NewRequest("GET", proto+"://a.example.com/", nil)
|
||
|
|
+ req.Header.Add("Cookie", "foo=bar")
|
||
|
|
+ req.Header.Add("Authorization", "secretpassword")
|
||
|
|
+ res, err := c.Do(req)
|
||
|
|
+ if err != nil {
|
||
|
|
+ t.Fatal(err)
|
||
|
|
+ }
|
||
|
|
+ defer res.Body.Close()
|
||
|
|
+ if res.Header.Get("X-Done") != "true" {
|
||
|
|
+ t.Fatalf("response missing expected header: X-Done=true")
|
||
|
|
+ }
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
// Issue 22233: copy host when Client follows a relative redirect.
|
||
|
|
func TestClientCopyHostOnRedirect(t *testing.T) { run(t, testClientCopyHostOnRedirect) }
|
||
|
|
func testClientCopyHostOnRedirect(t *testing.T, mode testMode) {
|
||
|
|
@@ -1702,43 +1751,39 @@
|
||
|
|
// Part of Issue 4800
|
||
|
|
func TestShouldCopyHeaderOnRedirect(t *testing.T) {
|
||
|
|
tests := []struct {
|
||
|
|
- header string
|
||
|
|
initialURL string
|
||
|
|
destURL string
|
||
|
|
want bool
|
||
|
|
}{
|
||
|
|
- {"User-Agent", "http://foo.com/", "http://bar.com/", true},
|
||
|
|
- {"X-Foo", "http://foo.com/", "http://bar.com/", true},
|
||
|
|
-
|
||
|
|
// Sensitive headers:
|
||
|
|
- {"cookie", "http://foo.com/", "http://bar.com/", false},
|
||
|
|
- {"cookie2", "http://foo.com/", "http://bar.com/", false},
|
||
|
|
- {"authorization", "http://foo.com/", "http://bar.com/", false},
|
||
|
|
- {"authorization", "http://foo.com/", "https://foo.com/", true},
|
||
|
|
- {"authorization", "http://foo.com:1234/", "http://foo.com:4321/", true},
|
||
|
|
- {"www-authenticate", "http://foo.com/", "http://bar.com/", false},
|
||
|
|
- {"authorization", "http://foo.com/", "http://[::1%25.foo.com]/", false},
|
||
|
|
+ {"http://foo.com/", "http://bar.com/", false},
|
||
|
|
+ {"http://foo.com/", "http://bar.com/", false},
|
||
|
|
+ {"http://foo.com/", "http://bar.com/", false},
|
||
|
|
+ {"http://foo.com/", "https://foo.com/", true},
|
||
|
|
+ {"http://foo.com:1234/", "http://foo.com:4321/", true},
|
||
|
|
+ {"http://foo.com/", "http://bar.com/", false},
|
||
|
|
+ {"http://foo.com/", "http://[::1%25.foo.com]/", false},
|
||
|
|
|
||
|
|
// But subdomains should work:
|
||
|
|
- {"www-authenticate", "http://foo.com/", "http://foo.com/", true},
|
||
|
|
- {"www-authenticate", "http://foo.com/", "http://sub.foo.com/", true},
|
||
|
|
- {"www-authenticate", "http://foo.com/", "http://notfoo.com/", false},
|
||
|
|
- {"www-authenticate", "http://foo.com/", "https://foo.com/", true},
|
||
|
|
- {"www-authenticate", "http://foo.com:80/", "http://foo.com/", true},
|
||
|
|
- {"www-authenticate", "http://foo.com:80/", "http://sub.foo.com/", true},
|
||
|
|
- {"www-authenticate", "http://foo.com:443/", "https://foo.com/", true},
|
||
|
|
- {"www-authenticate", "http://foo.com:443/", "https://sub.foo.com/", true},
|
||
|
|
- {"www-authenticate", "http://foo.com:1234/", "http://foo.com/", true},
|
||
|
|
+ {"http://foo.com/", "http://foo.com/", true},
|
||
|
|
+ {"http://foo.com/", "http://sub.foo.com/", true},
|
||
|
|
+ {"http://foo.com/", "http://notfoo.com/", false},
|
||
|
|
+ {"http://foo.com/", "https://foo.com/", true},
|
||
|
|
+ {"http://foo.com:80/", "http://foo.com/", true},
|
||
|
|
+ {"http://foo.com:80/", "http://sub.foo.com/", true},
|
||
|
|
+ {"http://foo.com:443/", "https://foo.com/", true},
|
||
|
|
+ {"http://foo.com:443/", "https://sub.foo.com/", true},
|
||
|
|
+ {"http://foo.com:1234/", "http://foo.com/", true},
|
||
|
|
|
||
|
|
- {"authorization", "http://foo.com/", "http://foo.com/", true},
|
||
|
|
- {"authorization", "http://foo.com/", "http://sub.foo.com/", true},
|
||
|
|
- {"authorization", "http://foo.com/", "http://notfoo.com/", false},
|
||
|
|
- {"authorization", "http://foo.com/", "https://foo.com/", true},
|
||
|
|
- {"authorization", "http://foo.com:80/", "http://foo.com/", true},
|
||
|
|
- {"authorization", "http://foo.com:80/", "http://sub.foo.com/", true},
|
||
|
|
- {"authorization", "http://foo.com:443/", "https://foo.com/", true},
|
||
|
|
- {"authorization", "http://foo.com:443/", "https://sub.foo.com/", true},
|
||
|
|
- {"authorization", "http://foo.com:1234/", "http://foo.com/", true},
|
||
|
|
+ {"http://foo.com/", "http://foo.com/", true},
|
||
|
|
+ {"http://foo.com/", "http://sub.foo.com/", true},
|
||
|
|
+ {"http://foo.com/", "http://notfoo.com/", false},
|
||
|
|
+ {"http://foo.com/", "https://foo.com/", true},
|
||
|
|
+ {"http://foo.com:80/", "http://foo.com/", true},
|
||
|
|
+ {"http://foo.com:80/", "http://sub.foo.com/", true},
|
||
|
|
+ {"http://foo.com:443/", "https://foo.com/", true},
|
||
|
|
+ {"http://foo.com:443/", "https://sub.foo.com/", true},
|
||
|
|
+ {"http://foo.com:1234/", "http://foo.com/", true},
|
||
|
|
}
|
||
|
|
for i, tt := range tests {
|
||
|
|
u0, err := url.Parse(tt.initialURL)
|
||
|
|
@@ -1751,10 +1796,10 @@
|
||
|
|
t.Errorf("%d. dest URL %q parse error: %v", i, tt.destURL, err)
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
- got := Export_shouldCopyHeaderOnRedirect(tt.header, u0, u1)
|
||
|
|
+ got := Export_shouldCopyHeaderOnRedirect(u0, u1)
|
||
|
|
if got != tt.want {
|
||
|
|
- t.Errorf("%d. shouldCopyHeaderOnRedirect(%q, %q => %q) = %v; want %v",
|
||
|
|
- i, tt.header, tt.initialURL, tt.destURL, got, tt.want)
|
||
|
|
+ t.Errorf("%d. shouldCopyHeaderOnRedirect(%q => %q) = %v; want %v",
|
||
|
|
+ i, tt.initialURL, tt.destURL, got, tt.want)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
diff --git a/src/net/http/internal/testcert/testcert.go b/src/net/http/internal/testcert/testcert.go
|
||
|
|
index d510e79..78ce42e 100644
|
||
|
|
--- a/src/net/http/internal/testcert/testcert.go
|
||
|
|
+++ b/src/net/http/internal/testcert/testcert.go
|
||
|
|
@@ -10,56 +10,56 @@
|
||
|
|
// LocalhostCert is a PEM-encoded TLS cert with SAN IPs
|
||
|
|
// "127.0.0.1" and "[::1]", expiring at Jan 29 16:00:00 2084 GMT.
|
||
|
|
// generated from src/crypto/tls:
|
||
|
|
-// go run generate_cert.go --rsa-bits 2048 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
|
||
|
|
+// go run generate_cert.go --rsa-bits 2048 --host 127.0.0.1,::1,example.com,*.example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
|
||
|
|
var LocalhostCert = []byte(`-----BEGIN CERTIFICATE-----
|
||
|
|
-MIIDOTCCAiGgAwIBAgIQSRJrEpBGFc7tNb1fb5pKFzANBgkqhkiG9w0BAQsFADAS
|
||
|
|
+MIIDSDCCAjCgAwIBAgIQccHlx0t8YUF0slXOMl05bzANBgkqhkiG9w0BAQsFADAS
|
||
|
|
MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw
|
||
|
|
MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
||
|
|
-MIIBCgKCAQEA6Gba5tHV1dAKouAaXO3/ebDUU4rvwCUg/CNaJ2PT5xLD4N1Vcb8r
|
||
|
|
-bFSW2HXKq+MPfVdwIKR/1DczEoAGf/JWQTW7EgzlXrCd3rlajEX2D73faWJekD0U
|
||
|
|
-aUgz5vtrTXZ90BQL7WvRICd7FlEZ6FPOcPlumiyNmzUqtwGhO+9ad1W5BqJaRI6P
|
||
|
|
-YfouNkwR6Na4TzSj5BrqUfP0FwDizKSJ0XXmh8g8G9mtwxOSN3Ru1QFc61Xyeluk
|
||
|
|
-POGKBV/q6RBNklTNe0gI8usUMlYyoC7ytppNMW7X2vodAelSu25jgx2anj9fDVZu
|
||
|
|
-h7AXF5+4nJS4AAt0n1lNY7nGSsdZas8PbQIDAQABo4GIMIGFMA4GA1UdDwEB/wQE
|
||
|
|
+MIIBCgKCAQEAzZyG7/IYBvSYBqhGEr0RwBeGqAlzDj8e6v35ZR4wjIc31r6TV2wc
|
||
|
|
+FGjZ9jztHeDJkSe4eLRjYlnL1LoNDJ5QZ8lvipFQH0CMoB5oUsbnE5a/7/VkC9vI
|
||
|
|
+y3TvFktq1nCCQJ+jgeAQT+X04R98Bl8Ci4SAnVKQVSNue9GfG0jBvT59vIkBa7xt
|
||
|
|
+dBegtcbDjtfb9wtOgXceQONtYL+YrGVTOt56Y2aA42zDYIJw25u1dgufkZL6pCvA
|
||
|
|
+u4P1MhLwEiRXP2MYhwrnsQDuC7RWvcHoq03XbGXi5SNyxQExerrIyYGVhn1wXKrD
|
||
|
|
+mJB8rc0+I8tP/UOaYmV9B3x44YQZI8ekBQIDAQABo4GXMIGUMA4GA1UdDwEB/wQE
|
||
|
|
AwICpDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
|
||
|
|
-DgQWBBStsdjh3/JCXXYlQryOrL4Sh7BW5TAuBgNVHREEJzAlggtleGFtcGxlLmNv
|
||
|
|
-bYcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG9w0BAQsFAAOCAQEAxWGI
|
||
|
|
-5NhpF3nwwy/4yB4i/CwwSpLrWUa70NyhvprUBC50PxiXav1TeDzwzLx/o5HyNwsv
|
||
|
|
-cxv3HdkLW59i/0SlJSrNnWdfZ19oTcS+6PtLoVyISgtyN6DpkKpdG1cOkW3Cy2P2
|
||
|
|
-+tK/tKHRP1Y/Ra0RiDpOAmqn0gCOFGz8+lqDIor/T7MTpibL3IxqWfPrvfVRHL3B
|
||
|
|
-grw/ZQTTIVjjh4JBSW3WyWgNo/ikC1lrVxzl4iPUGptxT36Cr7Zk2Bsg0XqwbOvK
|
||
|
|
-5d+NTDREkSnUbie4GeutujmX3Dsx88UiV6UY/4lHJa6I5leHUNOHahRbpbWeOfs/
|
||
|
|
-WkBKOclmOV2xlTVuPw==
|
||
|
|
+DgQWBBT8AKB32mDTtnvHs6woiw9g00AfSDA9BgNVHREENjA0ggtleGFtcGxlLmNv
|
||
|
|
+bYINKi5leGFtcGxlLmNvbYcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG
|
||
|
|
+9w0BAQsFAAOCAQEAzCnM+T8BrnBm7X6thsh/uOWHB+8NOeUiBf2Q3V8/D/k/ehlS
|
||
|
|
+N4SnQNa8QIq1nRtx8w2/w+QoJFK9TKxTED+abbfTTImqOXeyTAyRUTtbSAO9XQyc
|
||
|
|
+ydxpOUYvYX9WO+EHVcKi0i+gkwuacAkDYr/lOsYojwrUEs8t4VAwTjVUzHvYvbc3
|
||
|
|
++dYZbY0xWa9C9JmO4Y6WTbPFc0zjLMweOhY3hAugWjspEXqsXPynlIddqhO5m0FC
|
||
|
|
+lEIRSNwsSg/V9wIxOr/ybEAwB+opQdLxESw4w1hpZePNznnEfbnbpnytytmy7RMS
|
||
|
|
+H35nDZdqF17KMJ86ZCrESmeR/JSpO0CqoOL51w==
|
||
|
|
-----END CERTIFICATE-----`)
|
||
|
|
|
||
|
|
// LocalhostKey is the private key for LocalhostCert.
|
||
|
|
var LocalhostKey = []byte(testingKey(`-----BEGIN RSA TESTING KEY-----
|
||
|
|
-MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDoZtrm0dXV0Aqi
|
||
|
|
-4Bpc7f95sNRTiu/AJSD8I1onY9PnEsPg3VVxvytsVJbYdcqr4w99V3AgpH/UNzMS
|
||
|
|
-gAZ/8lZBNbsSDOVesJ3euVqMRfYPvd9pYl6QPRRpSDPm+2tNdn3QFAvta9EgJ3sW
|
||
|
|
-URnoU85w+W6aLI2bNSq3AaE771p3VbkGolpEjo9h+i42TBHo1rhPNKPkGupR8/QX
|
||
|
|
-AOLMpInRdeaHyDwb2a3DE5I3dG7VAVzrVfJ6W6Q84YoFX+rpEE2SVM17SAjy6xQy
|
||
|
|
-VjKgLvK2mk0xbtfa+h0B6VK7bmODHZqeP18NVm6HsBcXn7iclLgAC3SfWU1jucZK
|
||
|
|
-x1lqzw9tAgMBAAECggEABWzxS1Y2wckblnXY57Z+sl6YdmLV+gxj2r8Qib7g4ZIk
|
||
|
|
-lIlWR1OJNfw7kU4eryib4fc6nOh6O4AWZyYqAK6tqNQSS/eVG0LQTLTTEldHyVJL
|
||
|
|
-dvBe+MsUQOj4nTndZW+QvFzbcm2D8lY5n2nBSxU5ypVoKZ1EqQzytFcLZpTN7d89
|
||
|
|
-EPj0qDyrV4NZlWAwL1AygCwnlwhMQjXEalVF1ylXwU3QzyZ/6MgvF6d3SSUlh+sq
|
||
|
|
-XefuyigXw484cQQgbzopv6niMOmGP3of+yV4JQqUSb3IDmmT68XjGd2Dkxl4iPki
|
||
|
|
-6ZwXf3CCi+c+i/zVEcufgZ3SLf8D99kUGE7v7fZ6AQKBgQD1ZX3RAla9hIhxCf+O
|
||
|
|
-3D+I1j2LMrdjAh0ZKKqwMR4JnHX3mjQI6LwqIctPWTU8wYFECSh9klEclSdCa64s
|
||
|
|
-uI/GNpcqPXejd0cAAdqHEEeG5sHMDt0oFSurL4lyud0GtZvwlzLuwEweuDtvT9cJ
|
||
|
|
-Wfvl86uyO36IW8JdvUprYDctrQKBgQDycZ697qutBieZlGkHpnYWUAeImVA878sJ
|
||
|
|
-w44NuXHvMxBPz+lbJGAg8Cn8fcxNAPqHIraK+kx3po8cZGQywKHUWsxi23ozHoxo
|
||
|
|
-+bGqeQb9U661TnfdDspIXia+xilZt3mm5BPzOUuRqlh4Y9SOBpSWRmEhyw76w4ZP
|
||
|
|
-OPxjWYAgwQKBgA/FehSYxeJgRjSdo+MWnK66tjHgDJE8bYpUZsP0JC4R9DL5oiaA
|
||
|
|
-brd2fI6Y+SbyeNBallObt8LSgzdtnEAbjIH8uDJqyOmknNePRvAvR6mP4xyuR+Bv
|
||
|
|
-m+Lgp0DMWTw5J9CKpydZDItc49T/mJ5tPhdFVd+am0NAQnmr1MCZ6nHxAoGABS3Y
|
||
|
|
-LkaC9FdFUUqSU8+Chkd/YbOkuyiENdkvl6t2e52jo5DVc1T7mLiIrRQi4SI8N9bN
|
||
|
|
-/3oJWCT+uaSLX2ouCtNFunblzWHBrhxnZzTeqVq4SLc8aESAnbslKL4i8/+vYZlN
|
||
|
|
-s8xtiNcSvL+lMsOBORSXzpj/4Ot8WwTkn1qyGgECgYBKNTypzAHeLE6yVadFp3nQ
|
||
|
|
-Ckq9yzvP/ib05rvgbvrne00YeOxqJ9gtTrzgh7koqJyX1L4NwdkEza4ilDWpucn0
|
||
|
|
-xiUZS4SoaJq6ZvcBYS62Yr1t8n09iG47YL8ibgtmH3L+svaotvpVxVK+d7BLevA/
|
||
|
|
-ZboOWVe3icTy64BT3OQhmg==
|
||
|
|
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDNnIbv8hgG9JgG
|
||
|
|
+qEYSvRHAF4aoCXMOPx7q/fllHjCMhzfWvpNXbBwUaNn2PO0d4MmRJ7h4tGNiWcvU
|
||
|
|
+ug0MnlBnyW+KkVAfQIygHmhSxucTlr/v9WQL28jLdO8WS2rWcIJAn6OB4BBP5fTh
|
||
|
|
+H3wGXwKLhICdUpBVI2570Z8bSMG9Pn28iQFrvG10F6C1xsOO19v3C06Bdx5A421g
|
||
|
|
+v5isZVM63npjZoDjbMNggnDbm7V2C5+RkvqkK8C7g/UyEvASJFc/YxiHCuexAO4L
|
||
|
|
+tFa9weirTddsZeLlI3LFATF6usjJgZWGfXBcqsOYkHytzT4jy0/9Q5piZX0HfHjh
|
||
|
|
+hBkjx6QFAgMBAAECggEAGpX0tK3fAXc6+RFlDiM4WlgwZyXhpKPwwX3Zc4O1z1Xk
|
||
|
|
+mHXH43PtJY5xMG+sUUKxjX1PUvwwJZVqz7TG3yzRRLo9G/OyInhcNo/3+UwSsOu7
|
||
|
|
+IbHfH9FpYzVZfM5/n/6oOObrY22vbkVTppBFnyVF1PhrEOyOERQlwwjVw9diha+F
|
||
|
|
+icv32fCm2KLH1KKn8sn9Gl/Ru/ja0eI8vFZ2pjpA/vRr9peSqyZ9XSo3VkJ1AZ7B
|
||
|
|
+uqkiyYBn+w35HqHtqgqGIrJO4i1WUXAuhTc0Z4P6FUukbzrnWww48JjQoBXIRTI9
|
||
|
|
+/7FNGToTThTz6XxIaYvpvKvg8uGHt9BKmZAH1NBqgQKBgQDlSoUSh8Ftl420yPWO
|
||
|
|
+p8pyzkLwAg9cqtUJZKT6US6p0WXfTAa6GpIwxuYYCfG4yWMTUTeeN9Jxb4KLRHTr
|
||
|
|
+wLMJZXmFVtm0CWWl/KsL0pRwdAziGh7Zq3jEDOUHWr2CzqCPuIIA0CfY5t/uSwLV
|
||
|
|
+TQyn42lqWLtB9wrVAj9LgL5d0QKBgQDlj+C4CCgrgZ4l8i4OAj5LPYoSncdglu46
|
||
|
|
+dXOl41APyZ36Z1Lt6jsrO07AmztRzdYQWOv2iSIZ9lTJy+58y/gJHd8ijO4uUHc2
|
||
|
|
+QMU17l5NGQ1OqAiat+WnJFRwbUEdSaa92KMjV9magwmdmTkppQJZS6ZrSXRB6VoQ
|
||
|
|
+VKVfdynr9QKBgQCka0p/Xi/bOWkZMV28nR90MeoYFzIS3kGDydLv2NUgWxK1C9xr
|
||
|
|
+CXC5X3dR15epTWgpSv0aDKdwRmkTGtTI1VbNLyHz9rKIApEMdOHmyWs+NEmkvNxQ
|
||
|
|
+dEBWPXTiUDRDH45NYR6AHMPmKrB7PPjcIbMolM9bviMRi2gOFN10c+6OcQKBgHxN
|
||
|
|
+pzIfgJjmS9tiQtvlDRQy03P3KYG82GyhMqkN6ElUNA3mKvqXDckACUm0BK/sFFCv
|
||
|
|
+xb2uTd/fCdRnb9D1pW5SVYPg6gv8GEExW1gzpa57tT/1LwuQLnON8YcbMdoJCpfc
|
||
|
|
+GaJGrJbFA8zprhFFv3rYwtlvYdta3yDepNmHrhSNAoGBAJeNGksK7Zj4J10Lx+0W
|
||
|
|
+O++BysvZX8h4VbH/vZUoNrc/vadGg2Z0jhz3XVtTigR4gr77+q5WrbovVraPamK5
|
||
|
|
+TLS+4PJfBPT0DC9jyUf25C5CFYj3Z9+PH+DC6LI/ZVD8pf9JJ2JKOxfT4gKec/Hs
|
||
|
|
+YuMxrufDlB4Ac6Z95KfoT1lz
|
||
|
|
-----END RSA TESTING KEY-----`))
|
||
|
|
|
||
|
|
func testingKey(s string) string { return strings.ReplaceAll(s, "TESTING KEY", "PRIVATE KEY") }
|
||
|
|
|