blocky/server/server_test.go

798 lines
24 KiB
Go

package server
import (
"bytes"
"context"
"encoding/base64"
"fmt"
"io"
"net"
"net/http"
"strings"
"sync/atomic"
"time"
"github.com/0xERR0R/blocky/config"
"github.com/0xERR0R/blocky/docs"
. "github.com/0xERR0R/blocky/helpertest"
. "github.com/0xERR0R/blocky/log"
"github.com/0xERR0R/blocky/model"
"github.com/0xERR0R/blocky/resolver"
"github.com/0xERR0R/blocky/util"
"github.com/creasty/defaults"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/miekg/dns"
)
const (
httpBasePort = 4000
dnsBasePort = 5000
dnsBasePort2 = 55000
httpsBasePort = 6000
tlsBasePort = 8000
)
var (
mockClientName atomic.Value
sut *Server
err error
baseURL string
queryURL string
googleMockUpstream, fritzboxMockUpstream, clientMockUpstream *resolver.MockUDPUpstreamServer
)
var _ = BeforeSuite(func() {
baseURL = "http://localhost:" + GetStringPort(httpBasePort) + "/"
queryURL = baseURL + "dns-query"
var upstreamGoogle, upstreamFritzbox, upstreamClient config.Upstream
ctx, cancelFn := context.WithCancel(context.Background())
DeferCleanup(cancelFn)
googleMockUpstream = resolver.NewMockUDPUpstreamServer().WithAnswerFn(func(request *dns.Msg) (response *dns.Msg) {
if request.Question[0].Name == "error." {
return nil
}
response, err := util.NewMsgWithAnswer(
util.ExtractDomain(request.Question[0]), 123, A, "123.124.122.122",
)
Expect(err).Should(Succeed())
return response
})
fritzboxMockUpstream = resolver.NewMockUDPUpstreamServer().WithAnswerFn(func(request *dns.Msg) (response *dns.Msg) {
response, err := util.NewMsgWithAnswer(
util.ExtractDomain(request.Question[0]), 3600, A, "192.168.178.2",
)
Expect(err).Should(Succeed())
return response
})
clientMockUpstream = resolver.NewMockUDPUpstreamServer().WithAnswerFn(func(request *dns.Msg) (response *dns.Msg) {
var clientName string
if name, ok := mockClientName.Load().(string); ok {
clientName = name
}
response, err := util.NewMsgWithAnswer(
util.ExtractDomain(request.Question[0]), 3600, dns.Type(dns.TypePTR), clientName,
)
Expect(err).Should(Succeed())
return response
})
upstreamClient = clientMockUpstream.Start()
upstreamFritzbox = fritzboxMockUpstream.Start()
upstreamGoogle = googleMockUpstream.Start()
tmpDir := NewTmpFolder("server")
certPem := writeCertPem(tmpDir)
keyPem := writeKeyPem(tmpDir)
doubleclickFile := tmpDir.CreateStringFile("doubleclick.net.txt", "doubleclick.net", "doubleclick.net.cn")
bildFile := tmpDir.CreateStringFile("www.bild.de.txt", "www.bild.de")
heiseFile := tmpDir.CreateStringFile("heise.de.txt", "heise.de")
youtubeFile := tmpDir.CreateStringFile("youtube.com.txt", "youtube.com")
cfg := &config.Config{
CustomDNS: config.CustomDNS{
CustomTTL: config.Duration(3600 * time.Second),
Mapping: config.CustomDNSMapping{
HostIPs: map[string][]net.IP{
"custom.lan": {net.ParseIP("192.168.178.55")},
"lan.home": {net.ParseIP("192.168.178.56")},
},
},
},
Conditional: config.ConditionalUpstream{
Mapping: config.ConditionalUpstreamMapping{
Upstreams: map[string][]config.Upstream{
"net.cn": {upstreamClient},
"fritz.box": {upstreamFritzbox},
},
},
},
Blocking: config.Blocking{
BlackLists: map[string][]config.BytesSource{
"ads": config.NewBytesSources(
doubleclickFile.Path,
bildFile.Path,
heiseFile.Path,
),
"youtube": config.NewBytesSources(youtubeFile.Path),
},
WhiteLists: map[string][]config.BytesSource{
"ads": config.NewBytesSources(heiseFile.Path),
"whitelist": config.NewBytesSources(heiseFile.Path),
},
ClientGroupsBlock: map[string][]string{
"default": {"ads"},
"clWhitelistOnly": {"whitelist"},
"clAdsAndYoutube": {"ads", "youtube"},
"clYoutubeOnly": {"youtube"},
},
BlockType: "zeroIp",
BlockTTL: config.Duration(6 * time.Hour),
},
Upstreams: config.Upstreams{
Timeout: config.Duration(250 * time.Millisecond),
Groups: map[string][]config.Upstream{"default": {upstreamGoogle}},
},
ClientLookup: config.ClientLookup{
Upstream: upstreamClient,
},
Ports: config.Ports{
DNS: config.ListenConfig{GetStringPort(dnsBasePort)},
TLS: config.ListenConfig{GetStringPort(tlsBasePort)},
HTTP: config.ListenConfig{GetStringPort(httpBasePort)},
HTTPS: config.ListenConfig{GetStringPort(httpsBasePort)},
},
CertFile: certPem.Path,
KeyFile: keyPem.Path,
Prometheus: config.Metrics{
Enable: true,
Path: "/metrics",
},
}
// create server
sut, err = NewServer(ctx, cfg)
Expect(err).Should(Succeed())
errChan := make(chan error, 10)
// start server
go sut.Start(ctx, errChan)
DeferCleanup(func() { Expect(sut.Stop(ctx)).Should(Succeed()) })
Consistently(errChan, "1s").ShouldNot(Receive())
})
var _ = Describe("Running DNS server", func() {
var (
ctx context.Context
cancelFn context.CancelFunc
)
BeforeEach(func() {
ctx, cancelFn = context.WithCancel(context.Background())
DeferCleanup(cancelFn)
})
Describe("performing DNS request with running server", func() {
BeforeEach(func() {
mockClientName.Store("")
// reset client cache
clientNamesResolver, err := resolver.GetFromChainWithType[*resolver.ClientNamesResolver](sut.queryResolver)
Expect(err).Should(Succeed())
clientNamesResolver.FlushCache()
})
Context("DNS query is resolvable via external DNS", func() {
It("should return valid answer", func() {
Expect(requestServer(util.NewMsgWithQuestion("google.de.", A))).
Should(
SatisfyAll(
BeDNSRecord("google.de.", A, "123.124.122.122"),
HaveTTL(BeNumerically("==", 123)),
))
})
})
Context("Custom DNS entry with exact match", func() {
It("should return valid answer", func() {
Expect(requestServer(util.NewMsgWithQuestion("custom.lan.", A))).
Should(
SatisfyAll(
BeDNSRecord("custom.lan.", A, "192.168.178.55"),
HaveTTL(BeNumerically("==", 3600)),
))
})
})
Context("Custom DNS entry with sub domain", func() {
It("should return valid answer", func() {
Expect(requestServer(util.NewMsgWithQuestion("host.lan.home.", A))).
Should(
SatisfyAll(
BeDNSRecord("host.lan.home.", A, "192.168.178.56"),
HaveTTL(BeNumerically("==", 3600)),
))
})
})
Context("Conditional upstream", func() {
It("should resolve query via conditional upstream resolver", func() {
Expect(requestServer(util.NewMsgWithQuestion("host.fritz.box.", A))).
Should(
SatisfyAll(
BeDNSRecord("host.fritz.box.", A, "192.168.178.2"),
HaveTTL(BeNumerically("==", 3600)),
))
})
})
Context("Conditional upstream blocking", func() {
It("Query should be blocked, domain is in default group", func() {
Expect(requestServer(util.NewMsgWithQuestion("doubleclick.net.cn.", A))).
Should(
SatisfyAll(
BeDNSRecord("doubleclick.net.cn.", A, "0.0.0.0"),
HaveTTL(BeNumerically("==", 21600)),
))
})
})
Context("Blocking default group", func() {
It("Query should be blocked, domain is in default group", func() {
Expect(requestServer(util.NewMsgWithQuestion("doubleclick.net.", A))).
Should(
SatisfyAll(
BeDNSRecord("doubleclick.net.", A, "0.0.0.0"),
HaveTTL(BeNumerically("==", 21600)),
))
})
})
Context("Blocking default group with sub domain", func() {
It("Query with subdomain should be blocked, domain is in default group", func() {
Expect(requestServer(util.NewMsgWithQuestion("www.bild.de.", A))).
Should(
SatisfyAll(
BeDNSRecord("www.bild.de.", A, "0.0.0.0"),
HaveTTL(BeNumerically("==", 21600)),
))
})
})
Context("no blocking default group with sub domain", func() {
It("Query with should not be blocked, sub domain is not in blacklist", func() {
Expect(requestServer(util.NewMsgWithQuestion("bild.de.", A))).
Should(
SatisfyAll(
BeDNSRecord("bild.de.", A, "123.124.122.122"),
HaveTTL(BeNumerically("<=", 123)),
))
})
})
Context("domain is on white and blacklist default group", func() {
It("Query with should not be blocked, domain is on white and blacklist", func() {
Expect(requestServer(util.NewMsgWithQuestion("heise.de.", A))).
Should(
SatisfyAll(
BeDNSRecord("heise.de.", A, "123.124.122.122"),
HaveTTL(BeNumerically("<=", 123)),
))
})
})
Context("domain is on client specific white list", func() {
It("Query with should not be blocked, domain is on client's white list", func() {
mockClientName.Store("clWhitelistOnly")
Expect(requestServer(util.NewMsgWithQuestion("heise.de.", A))).
Should(
SatisfyAll(
BeDNSRecord("heise.de.", A, "123.124.122.122"),
HaveTTL(BeNumerically("<=", 123)),
))
})
})
Context("block client whitelist only", func() {
It("Query with should be blocked, client has only whitelist, domain is not on client's white list", func() {
mockClientName.Store("clWhitelistOnly")
Expect(requestServer(util.NewMsgWithQuestion("google.de.", A))).
Should(
SatisfyAll(
BeDNSRecord("google.de.", A, "0.0.0.0"),
HaveTTL(BeNumerically("==", 21600)),
))
})
})
Context("block client with 2 groups", func() {
It("Query with should be blocked, domain is on black list", func() {
mockClientName.Store("clAdsAndYoutube")
Expect(requestServer(util.NewMsgWithQuestion("www.bild.de.", A))).
Should(
SatisfyAll(
BeDNSRecord("www.bild.de.", A, "0.0.0.0"),
HaveTTL(BeNumerically("==", 21600)),
))
Expect(requestServer(util.NewMsgWithQuestion("youtube.com.", A))).
Should(
SatisfyAll(
BeDNSRecord("youtube.com.", A, "0.0.0.0"),
HaveTTL(BeNumerically("==", 21600)),
))
})
})
Context("client with 1 group: no block if domain in other group", func() {
It("Query with should not be blocked, domain is on black list in another group", func() {
mockClientName.Store("clYoutubeOnly")
Expect(requestServer(util.NewMsgWithQuestion("www.bild.de.", A))).
Should(
SatisfyAll(
BeDNSRecord("www.bild.de.", A, "123.124.122.122"),
HaveTTL(BeNumerically("<=", 123)),
))
})
})
Context("block client with 1 group", func() {
It("Query with should not blocked, domain is on black list in client's group", func() {
mockClientName.Store("clYoutubeOnly")
Expect(requestServer(util.NewMsgWithQuestion("youtube.com.", A))).
Should(
SatisfyAll(
BeDNSRecord("youtube.com.", A, "0.0.0.0"),
HaveTTL(BeNumerically("==", 21600)),
))
})
})
Context("health check", func() {
It("Should always return dummy response", func() {
resp := requestServer(util.NewMsgWithQuestion("healthcheck.blocky.", A))
Expect(resp.Answer).Should(BeEmpty())
})
})
})
Describe("Prometheus endpoint", func() {
When("Prometheus URL is called", func() {
It("should return prometheus data", func() {
resp, err := http.Get(baseURL + "metrics")
Expect(err).Should(Succeed())
Expect(resp).Should(HaveHTTPStatus(http.StatusOK))
})
})
})
Describe("Root endpoint", func() {
When("Root URL is called", func() {
It("should return root page", func() {
resp, err := http.Get(baseURL)
Expect(err).Should(Succeed())
Expect(resp).Should(
SatisfyAll(
HaveHTTPStatus(http.StatusOK),
HaveHTTPHeaderWithValue("Content-type", "text/html; charset=UTF-8"),
))
})
})
})
Describe("Docs endpoints", func() {
When("OpenApi URL is called", func() {
It("should return openAPI definition file", func() {
resp, err := http.Get(baseURL + "docs/openapi.yaml")
Expect(err).Should(Succeed())
Expect(resp).Should(
SatisfyAll(
HaveHTTPStatus(http.StatusOK),
HaveHTTPHeaderWithValue("Content-type", "text/yaml"),
HaveHTTPBody(docs.OpenAPI),
))
})
})
})
Describe("DOH endpoint", func() {
Context("DOH over GET (RFC 8484)", func() {
When("DOH get request with 'example.com' is performed", func() {
It("should get a valid response", func() {
resp, err := http.Get(queryURL + "?dns=AAABAAABAAAAAAAAA3d3dwdleGFtcGxlA2NvbQAAAQAB")
Expect(err).Should(Succeed())
DeferCleanup(resp.Body.Close)
Expect(resp).Should(HaveHTTPStatus(http.StatusOK))
Expect(resp).Should(HaveHTTPHeaderWithValue("Content-type", "application/dns-message"))
rawMsg, err := io.ReadAll(resp.Body)
Expect(err).Should(Succeed())
msg := new(dns.Msg)
err = msg.Unpack(rawMsg)
Expect(err).Should(Succeed())
Expect(msg.Answer).Should(BeDNSRecord("www.example.com.", A, "123.124.122.122"))
})
})
When("Request does not contain a valid DNS message", func() {
It("should return 'Bad Request'", func() {
resp, err := http.Get(queryURL + "?dns=xxxx")
Expect(err).Should(Succeed())
DeferCleanup(resp.Body.Close)
Expect(resp).Should(HaveHTTPStatus(http.StatusBadRequest))
})
})
When("Request's parameter does not contain a valid base64'", func() {
It("should return 'Bad Request'", func() {
resp, err := http.Get(queryURL + "?dns=äöä")
Expect(err).Should(Succeed())
DeferCleanup(resp.Body.Close)
Expect(resp).Should(HaveHTTPStatus(http.StatusBadRequest))
})
})
When("Request does not contain a dns parameter", func() {
It("should return 'Bad Request'", func() {
resp, err := http.Get(queryURL + "?test")
Expect(err).Should(Succeed())
DeferCleanup(resp.Body.Close)
Expect(resp).Should(HaveHTTPStatus(http.StatusBadRequest))
})
})
When("Request's dns parameter is too long'", func() {
It("should return 'URI Too Long'", func() {
longBase64msg := base64.StdEncoding.EncodeToString([]byte(strings.Repeat("t", 513)))
resp, err := http.Get(queryURL + "?dns=" + longBase64msg)
Expect(err).Should(Succeed())
DeferCleanup(resp.Body.Close)
Expect(resp).Should(HaveHTTPStatus(http.StatusRequestURITooLong))
})
})
})
Context("DOH over POST (RFC 8484)", func() {
var (
resp *http.Response
msg *dns.Msg
)
When("DOH post request with 'example.com' is performed", func() {
It("should get a valid response", func() {
msg = util.NewMsgWithQuestion("www.example.com.", A)
rawDNSMessage, err := msg.Pack()
Expect(err).Should(Succeed())
resp, err = http.Post(queryURL,
"application/dns-message", bytes.NewReader(rawDNSMessage))
Expect(err).Should(Succeed())
DeferCleanup(resp.Body.Close)
Expect(resp).Should(
SatisfyAll(
HaveHTTPStatus(http.StatusOK),
HaveHTTPHeaderWithValue("Content-type", "application/dns-message"),
))
rawMsg, err := io.ReadAll(resp.Body)
Expect(err).Should(Succeed())
msg = new(dns.Msg)
err = msg.Unpack(rawMsg)
Expect(err).Should(Succeed())
Expect(msg.Answer).Should(BeDNSRecord("www.example.com.", A, "123.124.122.122"))
})
It("should get a valid response, clientId is passed", func() {
msg = util.NewMsgWithQuestion("www.example.com.", A)
rawDNSMessage, err := msg.Pack()
Expect(err).Should(Succeed())
resp, err = http.Post(queryURL+"/client123",
"application/dns-message", bytes.NewReader(rawDNSMessage))
Expect(err).Should(Succeed())
DeferCleanup(resp.Body.Close)
Expect(resp).Should(
SatisfyAll(
HaveHTTPStatus(http.StatusOK),
HaveHTTPHeaderWithValue("Content-type", "application/dns-message"),
))
rawMsg, err := io.ReadAll(resp.Body)
Expect(err).Should(Succeed())
msg = new(dns.Msg)
err = msg.Unpack(rawMsg)
Expect(err).Should(Succeed())
Expect(msg.Answer).Should(BeDNSRecord("www.example.com.", A, "123.124.122.122"))
})
})
When("POST payload exceeds 512 bytes", func() {
It("should return 'Payload Too Large'", func() {
largeMessage := []byte(strings.Repeat("t", 513))
resp, err = http.Post(queryURL, "application/dns-message", bytes.NewReader(largeMessage))
Expect(err).Should(Succeed())
DeferCleanup(resp.Body.Close)
Expect(resp).Should(HaveHTTPStatus(http.StatusRequestEntityTooLarge))
})
})
When("Request has wrong type", func() {
It("should return 'Unsupported Media Type'", func() {
resp, err = http.Post(queryURL, "application/text", bytes.NewReader([]byte("a")))
Expect(err).Should(Succeed())
DeferCleanup(resp.Body.Close)
Expect(resp).Should(HaveHTTPStatus(http.StatusUnsupportedMediaType))
})
})
When("Internal error occurs", func() {
It("should return 'Internal server error'", func() {
msg = util.NewMsgWithQuestion("error.", A)
rawDNSMessage, err := msg.Pack()
Expect(err).Should(Succeed())
resp, err = http.Post(queryURL,
"application/dns-message", bytes.NewReader(rawDNSMessage))
Expect(err).Should(Succeed())
DeferCleanup(resp.Body.Close)
Expect(resp).Should(HaveHTTPStatus(http.StatusInternalServerError))
})
})
})
})
Describe("Server create", func() {
var (
cfg config.Config
cErr error
)
BeforeEach(func() {
cErr = defaults.Set(&cfg)
Expect(cErr).Should(Succeed())
cfg.Upstreams.Groups = map[string][]config.Upstream{
"default": {config.Upstream{Net: config.NetProtocolTcpUdp, Host: "1.1.1.1", Port: 53}},
}
cfg.Redis.Address = "test-fail"
})
When("Server is created", func() {
It("is created without redis connection", func() {
_, err = NewServer(ctx, &cfg)
Expect(err).Should(Succeed())
})
It("can't be created if redis server is unavailable", func() {
cfg.Redis.Required = true
_, err = NewServer(ctx, &cfg)
Expect(err).Should(HaveOccurred())
})
})
})
Describe("Server start", Label("XX"), func() {
When("Server start is called", func() {
var (
server *Server
errChan chan error
)
BeforeEach(func() {
// create server
server, err = NewServer(ctx, &config.Config{
Upstreams: config.Upstreams{
Groups: map[string][]config.Upstream{
"default": {config.Upstream{Net: config.NetProtocolTcpUdp, Host: "4.4.4.4", Port: 53}},
},
},
CustomDNS: config.CustomDNS{
Mapping: config.CustomDNSMapping{
HostIPs: map[string][]net.IP{
"custom.lan": {net.ParseIP("192.168.178.55")},
"lan.home": {net.ParseIP("192.168.178.56")},
},
},
},
Blocking: config.Blocking{BlockType: "zeroIp"},
Ports: config.Ports{
DNS: config.ListenConfig{"127.0.0.1:" + GetStringPort(dnsBasePort2)},
},
})
Expect(err).Should(Succeed())
errChan = make(chan error, 10)
// start server
go server.Start(ctx, errChan)
DeferCleanup(server.Stop)
})
It("start was called 2 times, start should fail", func() {
Consistently(errChan, "1s").ShouldNot(Receive())
// start again -> should fail
server.Start(ctx, errChan)
Eventually(errChan).Should(Receive())
})
})
})
Describe("Server stop", func() {
When("Stop is called", func() {
var (
server *Server
errChan chan error
)
BeforeEach(func() {
// create server
server, err = NewServer(ctx, &config.Config{
Upstreams: config.Upstreams{
Groups: map[string][]config.Upstream{
"default": {config.Upstream{Net: config.NetProtocolTcpUdp, Host: "4.4.4.4", Port: 53}},
},
},
CustomDNS: config.CustomDNS{
Mapping: config.CustomDNSMapping{
HostIPs: map[string][]net.IP{
"custom.lan": {net.ParseIP("192.168.178.55")},
"lan.home": {net.ParseIP("192.168.178.56")},
},
},
},
Blocking: config.Blocking{BlockType: "zeroIp"},
Ports: config.Ports{
DNS: config.ListenConfig{"127.0.0.1:" + GetStringPort(dnsBasePort2)},
},
})
Expect(err).Should(Succeed())
errChan = make(chan error, 10)
})
It("stop was called 2 times, start should fail", func() {
// start server
go server.Start(ctx, errChan)
time.Sleep(100 * time.Millisecond)
err = server.Stop(ctx)
// stop server, should be ok
Expect(err).Should(Succeed())
// stop again, should raise error
err = server.Stop(ctx)
Expect(err).Should(HaveOccurred())
})
})
})
Describe("resolve client IP", func() {
Context("UDP address", func() {
It("should correct resolve client IP", func() {
ip, protocol := resolveClientIPAndProtocol(&net.UDPAddr{IP: net.ParseIP("192.168.178.88")})
Expect(ip).Should(Equal(net.ParseIP("192.168.178.88")))
Expect(protocol).Should(Equal(model.RequestProtocolUDP))
})
})
Context("TCP address", func() {
It("should correct resolve client IP", func() {
ip, protocol := resolveClientIPAndProtocol(&net.TCPAddr{IP: net.ParseIP("192.168.178.88")})
Expect(ip).Should(Equal(net.ParseIP("192.168.178.88")))
Expect(protocol).Should(Equal(model.RequestProtocolTCP))
})
})
})
Describe("self-signed certificate creation", func() {
var (
cfg config.Config
cErr error
)
BeforeEach(func() {
cErr = defaults.Set(&cfg)
Expect(cErr).Should(Succeed())
cfg.Upstreams.Groups = map[string][]config.Upstream{
"default": {config.Upstream{Net: config.NetProtocolTcpUdp, Host: "1.1.1.1", Port: 53}},
}
})
It("should create self-signed certificate if key/cert files are not provided", func() {
cfg.KeyFile = ""
cfg.CertFile = ""
cfg.Ports = config.Ports{
HTTPS: []string{fmt.Sprintf(":%d", GetIntPort(httpsBasePort)+100)},
}
sut, err := NewServer(ctx, &cfg)
Expect(err).Should(Succeed())
Expect(sut.cert.Certificate).ShouldNot(BeNil())
})
})
})
func requestServer(request *dns.Msg) *dns.Msg {
conn, err := net.Dial("udp", ":"+GetStringPort(dnsBasePort))
if err != nil {
Log().Fatal("could not connect to server: ", err)
}
defer conn.Close()
msg, err := request.Pack()
if err != nil {
Log().Fatal("can't pack request: ", err)
}
_, err = conn.Write(msg)
if err != nil {
Log().Fatal("can't send request to server: ", err)
}
out := make([]byte, 1024)
if _, err := conn.Read(out); err == nil {
response := new(dns.Msg)
err = response.Unpack(out)
if err != nil {
Log().Fatal("can't unpack response: ", err)
}
return response
}
Log().Fatal("could not read from connection", err)
return nil
}
func writeCertPem(tmpDir *TmpFolder) *TmpFile {
return tmpDir.CreateStringFile("cert.pem",
"-----BEGIN CERTIFICATE-----",
"MIICMzCCAZygAwIBAgIRAJCCrDTGEtZfRpxDY1KAoswwDQYJKoZIhvcNAQELBQAw",
"EjEQMA4GA1UEChMHQWNtZSBDbzAgFw03MDAxMDEwMDAwMDBaGA8yMDg0MDEyOTE2",
"MDAwMFowEjEQMA4GA1UEChMHQWNtZSBDbzCBnzANBgkqhkiG9w0BAQEFAAOBjQAw",
"gYkCgYEA4mEaF5yWYYrTfMgRXdBpgGnqsHIADQWlw7BIJWD/gNp+fgp4TUZ/7ggV",
"rrvRORvRFjw14avd9L9EFP7XLi8ViU3uoE1UWI32MlrKqLbGNCXyUIApIoqlbRg6",
"iErxIk5+ChzFuysQOx01S2yv/ML6dx7NOGHs1S38MUzRZtcXBH8CAwEAAaOBhjCB",
"gzAOBgNVHQ8BAf8EBAMCAqQwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0TAQH/",
"BAUwAwEB/zAdBgNVHQ4EFgQUslNI6tYIv909RttHaZVMS/u/VYYwLAYDVR0RBCUw",
"I4IJbG9jYWxob3N0hwR/AAABhxAAAAAAAAAAAAAAAAAAAAABMA0GCSqGSIb3DQEB",
"CwUAA4GBAJ2gRpQHr5Qj7dt26bYVMdN4JGXTsvjbVrJfKI0VfPGJ+SUY/uTVBUeX",
"+Cwv4DFEPBlNx/lzuUkwmRaExC4/w81LWwxe5KltYsjyJuYowiUbLZ6tzLaQ9Bcx",
"jxClAVvgj90TGYOwsv6ESOX7GWteN1FlD3+jk7vefjFagaKKFYR9",
"-----END CERTIFICATE-----")
}
func writeKeyPem(tmpDir *TmpFolder) *TmpFile {
return tmpDir.CreateStringFile("key.pem",
"-----BEGIN PRIVATE KEY-----",
"MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAOJhGheclmGK03zI",
"EV3QaYBp6rByAA0FpcOwSCVg/4Dafn4KeE1Gf+4IFa670Tkb0RY8NeGr3fS/RBT+",
"1y4vFYlN7qBNVFiN9jJayqi2xjQl8lCAKSKKpW0YOohK8SJOfgocxbsrEDsdNUts",
"r/zC+ncezThh7NUt/DFM0WbXFwR/AgMBAAECgYEA1exixstPhI+2+OTrHFc1S4dL",
"oz+ncqbSlZEBLGl0KWTQQfVM5+FmRR7Yto1/0lLKDBQL6t0J2x3fjWOhHmCaHKZA",
"VAvZ8+OKxwofih3hlO0tGCB8szUJygp2FAmd0rOUqvPQ+PTohZEUXyDaB8MOIbX+",
"qoo7g19+VlbyKqmM8HkCQQDs4GQJwEn7GXKllSMyOfiYnjQM2pwsqO0GivXkH+p3",
"+h5KDp4g3O4EbmbrvZyZB2euVsBjW3pFMu+xPXuOXf91AkEA9KfC7LGLD2OtLmrM",
"iCZAqHlame+uEEDduDmqjTPnNKUWVeRtYKMF5Hltbeo1jMXMSbVZ+fRWKfQ+HAhQ",
"xjFJowJAV6U7PqRoe0FSO1QwXrA2fHnk9nCY4qlqckZObyckAVqJhIteFPjKFNeo",
"u0dAPxsPUOGGc/zwA9Sx/ZmrMuUy1QJBALl7bqawO/Ng6G0mfwZBqgeQaYYHVnnw",
"E6iV353J2eHpvzNDSUFYlyEOhk4soIindSf0m9CK08Be8a+jBkocF+0CQQC+Hi7L",
"kZV1slpW82BxYIhs9Gb0OQgK8SsI4aQPTFGUarQXXAm4eRqBO0kaG+jGX6TtW353",
"EHK784GIxwVXKej/",
"-----END PRIVATE KEY-----")
}