├── .gitignore ├── download_cacert.sh ├── Dockerfile ├── go.mod ├── banner.txt ├── .github └── workflows │ ├── release.yml │ ├── go.yml │ ├── docker-build-latest.yml │ └── docker-build-release.yml ├── .goreleaser.yml ├── proxy ├── config_test.go ├── proxy_test.go ├── mitm.go ├── config.go └── proxy.go ├── certutil └── certutil.go ├── webhook_sentry.go ├── README.md ├── LICENSE ├── integration_test.go └── go.sum /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | certs/ 3 | .idea/ 4 | .tool-versions 5 | config.yaml 6 | -------------------------------------------------------------------------------- /download_cacert.sh: -------------------------------------------------------------------------------- 1 | wget -O proxy/cacert.pem https://curl.haxx.se/ca/cacert.pem 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.16-alpine 2 | 3 | WORKDIR /app 4 | 5 | COPY go.mod ./ 6 | COPY go.sum ./ 7 | RUN go mod download 8 | 9 | COPY . ./ 10 | 11 | RUN go build -o /webhook-sentry 12 | EXPOSE 9090 13 | EXPOSE 2112 14 | CMD /webhook-sentry 15 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/juggernaut/webhook-sentry 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/google/uuid v1.3.0 7 | github.com/prometheus/client_golang v1.11.1 8 | github.com/sirupsen/logrus v1.6.0 9 | github.com/spf13/cobra v1.6.1 10 | github.com/spf13/viper v1.14.0 11 | gopkg.in/yaml.v2 v2.4.0 12 | ) 13 | -------------------------------------------------------------------------------- /banner.txt: -------------------------------------------------------------------------------- 1 | __ __ _ _ _ ____ _ 2 | \ \ / /__| |__ | |__ ___ ___ | | __ / ___| ___ _ __ | |_ _ __ _ _ 3 | \ \ /\ / / _ \ '_ \| '_ \ / _ \ / _ \| |/ / \___ \ / _ \ '_ \| __| '__| | | | 4 | \ V V / __/ |_) | | | | (_) | (_) | < ___) | __/ | | | |_| | | |_| | 5 | \_/\_/ \___|_.__/|_| |_|\___/ \___/|_|\_\ |____/ \___|_| |_|\__|_| \__, | 6 | |___/ 7 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: goreleaser 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | goreleaser: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - 13 | name: Checkout 14 | uses: actions/checkout@v2 15 | with: 16 | fetch-depth: 0 17 | - 18 | name: Set up Go 19 | uses: actions/setup-go@v2 20 | with: 21 | go-version: 1.16 22 | - 23 | name: Run GoReleaser 24 | uses: goreleaser/goreleaser-action@v2 25 | with: 26 | version: latest 27 | args: release --rm-dist 28 | env: 29 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 30 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | # This is an example goreleaser.yaml file with some sane defaults. 2 | # Make sure to check the documentation at http://goreleaser.com 3 | before: 4 | hooks: 5 | # You may remove this if you don't use go modules. 6 | - go mod download 7 | # you may remove this if you don't need go generate 8 | - go generate ./... 9 | builds: 10 | - env: 11 | - CGO_ENABLED=0 12 | goos: 13 | - linux 14 | - darwin 15 | binary: whsentry 16 | archives: 17 | - replacements: 18 | darwin: Darwin 19 | linux: Linux 20 | windows: Windows 21 | 386: i386 22 | amd64: x86_64 23 | checksum: 24 | name_template: 'checksums.txt' 25 | snapshot: 26 | name_template: "{{ .Tag }}-next" 27 | changelog: 28 | sort: asc 29 | filters: 30 | exclude: 31 | - '^docs:' 32 | - '^test:' 33 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | 11 | build: 12 | name: Build 13 | runs-on: ubuntu-latest 14 | steps: 15 | 16 | - name: Set up Go 1.16 17 | uses: actions/setup-go@v2 18 | with: 19 | go-version: 1.16.5 20 | id: go 21 | 22 | - name: Check out code into the Go module directory 23 | uses: actions/checkout@v2 24 | 25 | - name: Get dependencies 26 | run: | 27 | go get -v -t -d ./... 28 | if [ -f Gopkg.toml ]; then 29 | curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh 30 | dep ensure 31 | fi 32 | 33 | - name: Build 34 | run: go build -v . 35 | 36 | - name: Test 37 | run: go test -v . 38 | -------------------------------------------------------------------------------- /.github/workflows/docker-build-latest.yml: -------------------------------------------------------------------------------- 1 | name: Build and publish latest to Docker Hub 2 | on: 3 | push: 4 | branches: 5 | - 'master' 6 | 7 | jobs: 8 | docker: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - 12 | name: Set up QEMU 13 | uses: docker/setup-qemu-action@v2 14 | - 15 | name: Set up Docker Buildx 16 | uses: docker/setup-buildx-action@v2 17 | - 18 | name: Login to Docker Hub 19 | uses: docker/login-action@v2 20 | with: 21 | username: ${{ secrets.DOCKERHUB_USERNAME }} 22 | password: ${{ secrets.DOCKERHUB_TOKEN }} 23 | - 24 | name: Build and push 25 | uses: docker/build-push-action@v3 26 | with: 27 | push: true 28 | tags: juggernaut/webhook-sentry:latest 29 | platforms: linux/amd64,linux/arm64 30 | -------------------------------------------------------------------------------- /.github/workflows/docker-build-release.yml: -------------------------------------------------------------------------------- 1 | name: Build and publish release to Docker Hub 2 | on: 3 | push: 4 | tags: 5 | - '*' 6 | 7 | jobs: 8 | docker: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - 12 | name: Set up QEMU 13 | uses: docker/setup-qemu-action@v2 14 | - 15 | name: Set up Docker Buildx 16 | uses: docker/setup-buildx-action@v2 17 | - 18 | name: Login to Docker Hub 19 | uses: docker/login-action@v2 20 | with: 21 | username: ${{ secrets.DOCKERHUB_USERNAME }} 22 | password: ${{ secrets.DOCKERHUB_TOKEN }} 23 | - 24 | name: Extract metadata (tags, labels) for Docker 25 | id: meta 26 | uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 27 | with: 28 | images: juggernaut/webhook-sentry 29 | - 30 | name: Build and push 31 | uses: docker/build-push-action@v3 32 | with: 33 | push: true 34 | tags: ${{ steps.meta.outputs.tags }} 35 | labels: ${{ steps.meta.outputs.labels }} 36 | platforms: linux/amd64,linux/arm64 37 | -------------------------------------------------------------------------------- /proxy/config_test.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 Ameya Lokare 3 | */ 4 | package proxy 5 | 6 | import ( 7 | "strings" 8 | "testing" 9 | "time" 10 | ) 11 | 12 | func checkNoError(t *testing.T, e error) { 13 | if e != nil { 14 | t.Fatalf("Expected no error, but found error %s\n", e) 15 | } 16 | } 17 | 18 | func assertError(t *testing.T, msg string, e error) { 19 | if !strings.Contains(e.Error(), msg) { 20 | t.Fatalf("Expected error message '%s' to contained string '%s'", e.Error(), msg) 21 | } 22 | } 23 | 24 | func assertEqual(t *testing.T, a interface{}, b interface{}) { 25 | if a != b { 26 | t.Fatalf("Expected %s to be equal to %s\n", a, b) 27 | } 28 | } 29 | 30 | func assertNotNil(t *testing.T, a interface{}, name string) { 31 | if a == nil { 32 | t.Fatalf("Expected %s to be non-nil, but was nil\n", name) 33 | } 34 | } 35 | 36 | func TestListenerValidation(t *testing.T) { 37 | 38 | t.Run("Port only is valid", func(t *testing.T) { 39 | checkNoError(t, validateAddress(":9090")) 40 | }) 41 | 42 | t.Run("IP port is valid", func(t *testing.T) { 43 | checkNoError(t, validateAddress("127.0.0.1:9090")) 44 | }) 45 | 46 | t.Run("Hostname is not valid", func(t *testing.T) { 47 | assertError(t, "should be in the format IP:Port", validateAddress("foohost:9090")) 48 | }) 49 | 50 | t.Run("IPv6 is not valid", func(t *testing.T) { 51 | assertError(t, "only IPv4 addresses are supported", validateAddress("[2001:db8::68]:11090")) 52 | }) 53 | 54 | t.Run("HTTPS needs both certFile and keyFile", func(t *testing.T) { 55 | listener := ListenerConfig{ 56 | Type: HTTPS, 57 | Address: ":9091", 58 | CertFile: "/etc/pki/cert", 59 | } 60 | err := validateListeners([]ListenerConfig{listener}) 61 | assertError(t, "Both certificate file and private key file", err) 62 | }) 63 | } 64 | 65 | func TestYaml(t *testing.T) { 66 | 67 | t.Run("Defaults", func(t *testing.T) { 68 | config := NewDefaultConfig() 69 | assertEqual(t, 1, len(config.Listeners)) 70 | listener := config.Listeners[0] 71 | assertEqual(t, HTTP, listener.Type) 72 | assertEqual(t, ":9090", listener.Address) 73 | assertEqual(t, time.Duration(10)*time.Second, config.ConnectTimeout) 74 | assertEqual(t, false, config.InsecureSkipCertVerification) 75 | assertEqual(t, false, config.InsecureSkipCidrDenyList) 76 | }) 77 | 78 | t.Run("Override config", func(t *testing.T) { 79 | var data = ` 80 | cidrDenyList: ["9.9.9.9/32", "172.0.0.1/24"] 81 | listeners: 82 | - type: http 83 | address: ":12090" 84 | ` 85 | config, err := UnmarshalConfig([]byte(data)) 86 | checkNoError(t, err) 87 | assertNotNil(t, config.CidrDenyList, "CidrDenyList") 88 | assertEqual(t, 2, len(config.CidrDenyList)) 89 | assertEqual(t, 1, len(config.Listeners)) 90 | listener := config.Listeners[0] 91 | assertEqual(t, HTTP, listener.Type) 92 | assertEqual(t, ":12090", listener.Address) 93 | 94 | // test defaults set for parameters that aren't overridden 95 | assertEqual(t, time.Duration(10)*time.Second, config.ConnectTimeout) 96 | }) 97 | } 98 | -------------------------------------------------------------------------------- /proxy/proxy_test.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 Ameya Lokare 3 | */ 4 | package proxy 5 | 6 | import ( 7 | "net/http" 8 | "testing" 9 | ) 10 | 11 | func TestCopyHeadersSkipProxyConnection(t *testing.T) { 12 | inHeaders := make(map[string][]string) 13 | inHeaders["Proxy-Connection"] = []string{"Keep-Alive"} 14 | inHeaders["Content-Length"] = []string{"100"} 15 | outHeaders := make(map[string][]string) 16 | outHeaders["Connection"] = []string{"Close"} 17 | copyHeaders(inHeaders, outHeaders) 18 | _, ok := outHeaders["Proxy-Connection"] 19 | if ok { 20 | t.Error("Proxy-Connection erroneously copied") 21 | } 22 | cl, ok := outHeaders["Content-Length"] 23 | if !ok { 24 | t.Error("Content-Length not copied over") 25 | } 26 | if len(cl) != 1 { 27 | t.Errorf("Expected number of Content-Length header values was 1, got %d", len(cl)) 28 | } 29 | clVal := cl[0] 30 | if clVal != "100" { 31 | t.Errorf("Expected Content-Length value of 100, got %s", clVal) 32 | } 33 | _, ok = outHeaders["Connection"] 34 | if !ok { 35 | t.Error("Connection header erroneously removed") 36 | } 37 | } 38 | 39 | func TestCopyHeadersSkipWHSentryHeaders(t *testing.T) { 40 | inHeaders := make(map[string][]string) 41 | inHeaders[http.CanonicalHeaderKey("X-WhSentry-TLS")] = []string{"true"} 42 | inHeaders[http.CanonicalHeaderKey("X-WhSentry-Foo")] = []string{"bar"} 43 | outHeaders := make(map[string][]string) 44 | outHeaders["Connection"] = []string{"Close"} 45 | copyHeaders(inHeaders, outHeaders) 46 | if len(outHeaders) != 1 { 47 | t.Errorf("Expected to skip X-Wh headers but weren't skipped") 48 | } 49 | _, ok := outHeaders["Connection"] 50 | if !ok { 51 | t.Error("Connection header erroneously removed") 52 | } 53 | } 54 | 55 | func TestIsTLS(t *testing.T) { 56 | t.Run("Header absent", func(t *testing.T) { 57 | headers := make(map[string][]string) 58 | headers["Connection"] = []string{"Close"} 59 | if isTLS(headers) { 60 | t.Error("isTLS should be false when X-WhSentry-TLS header is absent, but it was true") 61 | } 62 | }) 63 | 64 | t.Run("Header present", func(t *testing.T) { 65 | headers := make(map[string][]string) 66 | headers["Connection"] = []string{"Close"} 67 | headers[http.CanonicalHeaderKey("X-WhSentry-TLS")] = []string{"true"} 68 | if !isTLS(headers) { 69 | t.Error("isTLS should be true when X-WhSentry-TLS header is present, but it was false") 70 | } 71 | }) 72 | 73 | t.Run("Header present but false", func(t *testing.T) { 74 | headers := make(map[string][]string) 75 | headers["Connection"] = []string{"Close"} 76 | headers[http.CanonicalHeaderKey("X-WhSentry-TLS")] = []string{"false"} 77 | if isTLS(headers) { 78 | t.Error("isTLS should be false when X-WhSentry-TLS header is present but 'false', but it was true") 79 | } 80 | }) 81 | 82 | t.Run("Header present but 0", func(t *testing.T) { 83 | headers := make(map[string][]string) 84 | headers["Connection"] = []string{"Close"} 85 | headers[http.CanonicalHeaderKey("X-WhSentry-TLS")] = []string{"0"} 86 | if isTLS(headers) { 87 | t.Error("isTLS should be false when X-WhSentry-TLS header is present but '0', but it was true") 88 | } 89 | }) 90 | } 91 | -------------------------------------------------------------------------------- /proxy/mitm.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 Ameya Lokare 3 | */ 4 | package proxy 5 | 6 | import ( 7 | "context" 8 | "crypto" 9 | "crypto/ecdsa" 10 | "crypto/ed25519" 11 | "crypto/rand" 12 | "crypto/rsa" 13 | "crypto/tls" 14 | "crypto/x509" 15 | "crypto/x509/pkix" 16 | "encoding/pem" 17 | "io" 18 | "math/big" 19 | "net" 20 | "net/http" 21 | "sync" 22 | "time" 23 | ) 24 | 25 | type Mitmer struct { 26 | dialContext func(ctx context.Context, network, addr string) (net.Conn, error) 27 | issuerCertificate *x509.Certificate 28 | issuerPrivateKey crypto.PrivateKey 29 | generatedCertKeyPair *rsa.PrivateKey 30 | doTLSHandshake func(conn net.Conn, hostname string, certAlias string) (net.Conn, error) 31 | } 32 | 33 | func NewMitmer() (*Mitmer, error) { 34 | keyPair, err := rsa.GenerateKey(rand.Reader, 2048) 35 | if err != nil { 36 | return nil, err 37 | } 38 | return &Mitmer{generatedCertKeyPair: keyPair}, nil 39 | } 40 | 41 | func (m *Mitmer) HandleHttpConnect(requestID string, w http.ResponseWriter, r *http.Request) { 42 | // TODO: think about what context deadlines to set etc 43 | outboundConn, err := m.dialContext(context.Background(), "tcp4", r.RequestURI) 44 | if err != nil { 45 | responseCode, errorCode, errorMsg := mapError(requestID, err) 46 | sendHTTPError(w, responseCode, errorCode, errorMsg) 47 | return 48 | } 49 | defer outboundConn.Close() 50 | hj, ok := w.(http.Hijacker) 51 | if !ok { 52 | http.Error(w, "Connection hijacking not supported", http.StatusInternalServerError) 53 | return 54 | } 55 | inboundConn, bufrw, err := hj.Hijack() 56 | if err != nil { 57 | http.Error(w, err.Error(), http.StatusInternalServerError) 58 | return 59 | } 60 | defer inboundConn.Close() 61 | bufrw.WriteString("HTTP/1.1 200 Connection Established\r\n") 62 | bufrw.WriteString("Connection: Close\r\n") 63 | bufrw.WriteString("\r\n") 64 | bufrw.Flush() 65 | 66 | m.doMitm(inboundConn, outboundConn, r.URL.Hostname()) 67 | } 68 | 69 | func (m *Mitmer) doMitm(inboundConn net.Conn, outboundConn net.Conn, hostnameInRequest string) { 70 | var remoteHostname string 71 | config := &tls.Config{ 72 | GetCertificate: func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) { 73 | sni := clientHello.ServerName 74 | if sni == "" { 75 | remoteHostname = hostnameInRequest 76 | } else { 77 | if sni != hostnameInRequest { 78 | log.Warnf("SNI name %s in TLS ClientHello is not the same as hostname %s indicated in HTTP CONNECT, proceeding anyway", sni, hostnameInRequest) 79 | } 80 | remoteHostname = sni 81 | } 82 | return m.generateCert(remoteHostname) 83 | }, 84 | } 85 | inboundTLSConn := tls.Server(inboundConn, config) 86 | defer inboundTLSConn.Close() 87 | err := inboundTLSConn.Handshake() 88 | if err != nil { 89 | log.Errorf("Inbound (MITM) handshake failed with error: %s\n", err) 90 | return 91 | } 92 | // NOTE: remoteHostname will only be set after the inbound handshake is done, so we can't do 93 | // inbound and outbound handshakes in parallel 94 | handshakeConn, err := m.doTLSHandshake(outboundConn, remoteHostname, "default") 95 | if err != nil { 96 | log.Errorf("TLS Handshake failed on outbound connection: %s\n", err) 97 | return 98 | } 99 | outboundTLSConn := handshakeConn.(*tls.Conn) 100 | var wg sync.WaitGroup 101 | wg.Add(2) 102 | go func() { 103 | rawProxy(inboundTLSConn, outboundTLSConn) 104 | wg.Done() 105 | }() 106 | go func() { 107 | rawProxy(outboundTLSConn, inboundTLSConn) 108 | wg.Done() 109 | }() 110 | wg.Wait() 111 | } 112 | 113 | // Heavily inspired by generate_cert.go 114 | func (m *Mitmer) generateCert(hostname string) (*tls.Certificate, error) { 115 | serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) 116 | serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) 117 | if err != nil { 118 | return nil, err 119 | } 120 | 121 | notBefore := time.Now().Add(time.Duration(-1) * time.Hour) 122 | notAfter := time.Now().Add(time.Duration(1) * time.Hour) 123 | 124 | template := x509.Certificate{ 125 | SerialNumber: serialNumber, 126 | Subject: pkix.Name{ 127 | Organization: []string{"WHSentry Co"}, 128 | }, 129 | NotBefore: notBefore, 130 | NotAfter: notAfter, 131 | 132 | KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, 133 | ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, 134 | BasicConstraintsValid: true, 135 | IsCA: false, 136 | } 137 | 138 | if ip := net.ParseIP(hostname); ip != nil { 139 | template.IPAddresses = append(template.IPAddresses, ip) 140 | } else { 141 | template.DNSNames = append(template.DNSNames, hostname) 142 | } 143 | 144 | derBytes, err := x509.CreateCertificate(rand.Reader, &template, m.issuerCertificate, PublicKey(m.generatedCertKeyPair), m.issuerPrivateKey) 145 | if err != nil { 146 | return nil, err 147 | } 148 | 149 | certPemBytes := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) 150 | // TODO: this can be done during initialization 151 | privKeyBytes, err := x509.MarshalPKCS8PrivateKey(m.generatedCertKeyPair) 152 | if err != nil { 153 | return nil, err 154 | } 155 | privKeyPemBytes := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: privKeyBytes}) 156 | cert, err := tls.X509KeyPair(certPemBytes, privKeyPemBytes) 157 | return &cert, err 158 | } 159 | 160 | func PublicKey(priv interface{}) interface{} { 161 | switch k := priv.(type) { 162 | case *rsa.PrivateKey: 163 | return &k.PublicKey 164 | case *ecdsa.PrivateKey: 165 | return &k.PublicKey 166 | case ed25519.PrivateKey: 167 | return k.Public().(ed25519.PublicKey) 168 | default: 169 | return nil 170 | } 171 | } 172 | 173 | func rawProxy(inConn *tls.Conn, outConn *tls.Conn) { 174 | buf := make([]byte, 2048) 175 | for { 176 | numRead, err := inConn.Read(buf) 177 | if numRead > 0 { 178 | _, writeErr := outConn.Write(buf[:numRead]) 179 | // Write must return a non-nil error if it returns n < len(p) 180 | if writeErr != nil { 181 | log.Warnf("Error writing to outbound connection: %s\n", writeErr) 182 | inConn.Close() 183 | outConn.Close() 184 | return 185 | } 186 | } 187 | if err == io.EOF { 188 | outConn.CloseWrite() 189 | return 190 | } 191 | if err != nil { 192 | log.Warnf("Error reading from inbound connection: %s\n", err) 193 | inConn.Close() 194 | outConn.Close() 195 | return 196 | } 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /certutil/certutil.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 Ameya Lokare 3 | */ 4 | package certutil 5 | 6 | import ( 7 | "crypto" 8 | "crypto/ecdsa" 9 | "crypto/elliptic" 10 | "crypto/rand" 11 | "crypto/rsa" 12 | "crypto/tls" 13 | "crypto/x509" 14 | "crypto/x509/pkix" 15 | "encoding/pem" 16 | "github.com/juggernaut/webhook-sentry/proxy" 17 | "math/big" 18 | "net" 19 | "testing" 20 | "time" 21 | ) 22 | 23 | func GenerateKeyPair() (crypto.PrivateKey, error) { 24 | return ecdsa.GenerateKey(elliptic.P256(), rand.Reader) 25 | } 26 | 27 | func GenerateCertificate(hostname string, organizationName string, key crypto.PrivateKey, notBefore time.Time, notAfter time.Time, issuerCertificate *x509.Certificate, issuerPrivateKey crypto.PrivateKey, isClientCert bool, isCA bool) ([]byte, error) { 28 | 29 | serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) 30 | serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) 31 | if err != nil { 32 | return nil, err 33 | } 34 | 35 | // ECDSA, ED25519 and RSA subject keys should have the DigitalSignature 36 | // KeyUsage bits set in the x509.Certificate template 37 | keyUsage := x509.KeyUsageDigitalSignature 38 | // Only RSA subject keys should have the KeyEncipherment KeyUsage bits set. In 39 | // the context of TLS this KeyUsage is particular to RSA key exchange and 40 | // authentication. 41 | if _, isRSA := key.(*rsa.PrivateKey); isRSA { 42 | keyUsage |= x509.KeyUsageKeyEncipherment 43 | } 44 | 45 | var extKeyUsage x509.ExtKeyUsage 46 | if isCA { 47 | extKeyUsage = x509.ExtKeyUsageAny 48 | } else if isClientCert { 49 | extKeyUsage = x509.ExtKeyUsageClientAuth 50 | } else { 51 | extKeyUsage = x509.ExtKeyUsageServerAuth 52 | } 53 | 54 | template := x509.Certificate{ 55 | SerialNumber: serialNumber, 56 | Subject: pkix.Name{ 57 | Organization: []string{organizationName}, 58 | }, 59 | NotBefore: notBefore, 60 | NotAfter: notAfter, 61 | 62 | KeyUsage: keyUsage, 63 | ExtKeyUsage: []x509.ExtKeyUsage{extKeyUsage}, 64 | BasicConstraintsValid: true, 65 | } 66 | 67 | if ip := net.ParseIP(hostname); ip != nil { 68 | template.IPAddresses = append(template.IPAddresses, ip) 69 | } else { 70 | template.DNSNames = append(template.DNSNames, hostname) 71 | } 72 | 73 | if isCA { 74 | template.IsCA = true 75 | template.KeyUsage |= x509.KeyUsageCertSign 76 | } 77 | 78 | var issuerTemplate *x509.Certificate = issuerCertificate 79 | if issuerTemplate == nil { 80 | issuerTemplate = &template 81 | } 82 | 83 | if issuerPrivateKey == nil { 84 | issuerPrivateKey = key 85 | } 86 | 87 | return x509.CreateCertificate(rand.Reader, &template, issuerTemplate, proxy.PublicKey(key), issuerPrivateKey) 88 | } 89 | 90 | func GenerateRootCACert() (crypto.PrivateKey, *x509.Certificate, error) { 91 | key, err := GenerateKeyPair() 92 | if err != nil { 93 | return nil, nil, err 94 | } 95 | notBefore := time.Now().Add(time.Duration(-1) * time.Hour) 96 | notAfter := time.Now().Add(time.Duration(1) * time.Hour) 97 | certBytes, err := GenerateCertificate("wh-sentry-root.com", "WH Sentry Root", key, notBefore, notAfter, nil, nil, true, true) 98 | if err != nil { 99 | return nil, nil, err 100 | } 101 | cert, err := x509.ParseCertificate(certBytes) 102 | if err != nil { 103 | return nil, nil, err 104 | } 105 | return key, cert, nil 106 | } 107 | 108 | func GenerateLeafCert(hostname string, organizationName string, issuerCert *x509.Certificate, issuerKey crypto.PrivateKey, isClient bool) (*tls.Certificate, error) { 109 | key, err := GenerateKeyPair() 110 | if err != nil { 111 | return nil, err 112 | } 113 | notBefore := time.Now().Add(time.Duration(-1) * time.Minute) 114 | notAfter := time.Now().Add(time.Duration(30) * time.Minute) 115 | 116 | derBytes, err := GenerateCertificate(hostname, organizationName, key, notBefore, notAfter, issuerCert, issuerKey, isClient, false) 117 | if err != nil { 118 | return nil, err 119 | } 120 | 121 | certPemBytes := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) 122 | 123 | privKeyBytes, err := x509.MarshalPKCS8PrivateKey(key) 124 | if err != nil { 125 | return nil, err 126 | } 127 | privKeyPemBytes := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: privKeyBytes}) 128 | cert, err := tls.X509KeyPair(certPemBytes, privKeyPemBytes) 129 | return &cert, err 130 | } 131 | 132 | func X509ToTLSCertificate(x509Cert *x509.Certificate, privateKey crypto.PrivateKey) (*tls.Certificate, error) { 133 | certPemBytes := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: x509Cert.Raw}) 134 | 135 | privKeyBytes, err := x509.MarshalPKCS8PrivateKey(privateKey) 136 | if err != nil { 137 | return nil, err 138 | } 139 | privKeyPemBytes := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: privKeyBytes}) 140 | cert, err := tls.X509KeyPair(certPemBytes, privKeyPemBytes) 141 | return &cert, err 142 | } 143 | 144 | type CertificateFixtures struct { 145 | RootCAs *x509.CertPool 146 | RootCAPrivateKey crypto.PrivateKey 147 | RootCACert *tls.Certificate 148 | ProxyCert *tls.Certificate 149 | ServerCert *tls.Certificate 150 | InvalidHostnameServerCert *tls.Certificate 151 | ClientCert *tls.Certificate 152 | } 153 | 154 | func NewCertificateFixtures(t *testing.T) *CertificateFixtures { 155 | rootCertKey, rootCert, err := GenerateRootCACert() 156 | if err != nil { 157 | t.Fatalf("Error generating root CA cert: %s", err) 158 | } 159 | certPool := x509.NewCertPool() 160 | certPool.AddCert(rootCert) 161 | 162 | rootCACert, err := X509ToTLSCertificate(rootCert, rootCertKey) 163 | if err != nil { 164 | t.Fatalf("Error converting x509 to TLS certificate: %s", err) 165 | } 166 | 167 | serverCert, err := GenerateLeafCert("localhost", "WH Sentry Test Server", rootCert, rootCertKey, false) 168 | if err != nil { 169 | t.Fatalf("Error generating server cert: %s", err) 170 | } 171 | 172 | invalidHostnameServerCert, err := GenerateLeafCert("wh-target-server.com", "WH Sentry Test Server", rootCert, rootCertKey, false) 173 | if err != nil { 174 | t.Fatalf("Error generating server cert: %s", err) 175 | } 176 | 177 | proxyCert, err := GenerateLeafCert("127.0.0.1", "WH Sentry Proxy", rootCert, rootCertKey, false) 178 | if err != nil { 179 | t.Fatalf("Error generating server cert: %s", err) 180 | } 181 | 182 | clientCert, err := GenerateLeafCert("wh-client.com", "WH Sentry Client", rootCert, rootCertKey, true) 183 | if err != nil { 184 | t.Fatalf("Error generating client cert: %s", err) 185 | } 186 | return &CertificateFixtures{ 187 | RootCAs: certPool, 188 | RootCAPrivateKey: rootCertKey, 189 | RootCACert: rootCACert, 190 | ServerCert: serverCert, 191 | InvalidHostnameServerCert: invalidHostnameServerCert, 192 | ProxyCert: proxyCert, 193 | ClientCert: clientCert, 194 | } 195 | } 196 | 197 | -------------------------------------------------------------------------------- /proxy/config.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 Ameya Lokare 3 | */ 4 | package proxy 5 | 6 | import ( 7 | "crypto/tls" 8 | "crypto/x509" 9 | _ "embed" 10 | "fmt" 11 | "io/ioutil" 12 | "net" 13 | "strings" 14 | "time" 15 | 16 | "gopkg.in/yaml.v2" 17 | ) 18 | 19 | //go:embed cacert.pem 20 | var cacert []byte 21 | 22 | const defaultConfig = ` 23 | cidrDenyList: [ 24 | "127.0.0.0/8", 25 | "10.0.0.0/8", 26 | "0.0.0.0/8", 27 | "100.64.0.0/10", 28 | "169.254.0.0/16", 29 | "172.16.0.0/12", 30 | "192.0.0.0/24", 31 | "192.168.0.0/16", 32 | "224.0.0.0/4", 33 | "240.0.0.0/4" 34 | ] 35 | listeners: 36 | - type: http 37 | address: ":9090" 38 | connectTimeout: 10s 39 | connectionLifetime: 60s 40 | readTimeout: 10s 41 | insecureSkipCertVerification: false 42 | insecureSkipCidrDenyList: false 43 | maxResponseBodySize: 1048576 44 | accessLog: 45 | type: text 46 | proxyLog: 47 | type: text 48 | metricsAddress: ":2112" 49 | requestIDHeader: Request-ID 50 | ` 51 | 52 | type Cidr net.IPNet 53 | 54 | type ProxyConfig struct { 55 | CidrDenyList []Cidr `yaml:"cidrDenyList"` 56 | Listeners []ListenerConfig `yaml:"listeners"` 57 | ConnectTimeout time.Duration `yaml:"connectTimeout"` 58 | ConnectionLifetime time.Duration `yaml:"connectionLifetime"` 59 | ReadTimeout time.Duration `yaml:"readTimeout"` 60 | MaxResponseBodySize uint32 `yaml:"maxResponseBodySize"` 61 | InsecureSkipCertVerification bool `yaml:"insecureSkipCertVerification"` 62 | InsecureSkipCidrDenyList bool `yaml:"insecureSkipCidrDenyList"` 63 | ClientCertFile string `yaml:"clientCertFile"` 64 | ClientKeyFile string `yaml:"clientKeyFile"` 65 | ClientCerts map[string]tls.Certificate `yaml:"-"` 66 | RootCACerts *x509.CertPool `yaml:"-"` // TODO: not taking a file override yet 67 | MitmIssuerCertFile string `yaml:"mitmIssuerCertFile"` 68 | MitmIssuerKeyFile string `yaml:"mitmIssuerKeyFile"` 69 | MitmIssuerCert *tls.Certificate `yaml:"-"` 70 | AccessLog LogConfig `yaml:"accessLog"` 71 | ProxyLog LogConfig `yaml:"proxyLog"` 72 | MetricsAddress string `yaml:"metricsAddress"` 73 | RequestIDHeader string `yaml:"requestIDHeader"` 74 | } 75 | 76 | type Protocol string 77 | 78 | const ( 79 | HTTP Protocol = "http" 80 | HTTPS Protocol = "https" 81 | ) 82 | 83 | type ListenerConfig struct { 84 | Address string 85 | Type Protocol 86 | CertFile string `yaml:"certFile"` 87 | KeyFile string `yaml:"keyFile"` 88 | } 89 | 90 | type LogType string 91 | 92 | const ( 93 | JSON LogType = "json" 94 | Text LogType = "text" 95 | ) 96 | 97 | type LogConfig struct { 98 | File string 99 | Type LogType 100 | } 101 | 102 | func (cidr *Cidr) UnmarshalYAML(unmarshal func(interface{}) error) error { 103 | var cidrStr string 104 | if err := unmarshal(&cidrStr); err != nil { 105 | return err 106 | } 107 | _, ipNet, err := net.ParseCIDR(cidrStr) 108 | if err != nil { 109 | return err 110 | } 111 | *cidr = (Cidr)(*ipNet) 112 | return nil 113 | } 114 | 115 | func (config *ProxyConfig) Validate() error { 116 | if err := validateListeners(config.Listeners); err != nil { 117 | return err 118 | } 119 | return nil 120 | } 121 | 122 | func validateListeners(listeners []ListenerConfig) error { 123 | for _, l := range listeners { 124 | if l.Type != HTTP && l.Type != HTTPS { 125 | return fmt.Errorf("Invalid listener type %s; must be one of 'http' or 'https'", l.Type) 126 | } 127 | if err := validateAddress(l.Address); err != nil { 128 | return err 129 | } 130 | if l.Type == HTTPS && (l.CertFile == "" || l.KeyFile == "") { 131 | return fmt.Errorf("Both certificate file and private key file must be specified for listener %s", l.Address) 132 | } 133 | } 134 | return nil 135 | } 136 | 137 | func validateAddress(address string) error { 138 | host, _, err := net.SplitHostPort(address) 139 | if err != nil { 140 | return fmt.Errorf("Invalid listener address %s; %s", address, err) 141 | } 142 | if host != "" { 143 | ip := net.ParseIP(host) 144 | if ip == nil { 145 | return fmt.Errorf("Invalid listener address %s; it should be in the format IP:Port", address) 146 | } 147 | if strings.Count(ip.String(), ":") > 0 { 148 | return fmt.Errorf("Invalid listener address %s; only IPv4 addresses are supported", address) 149 | } 150 | } 151 | return nil 152 | } 153 | 154 | func (p *ProxyConfig) loadClientCert() error { 155 | p.ClientCerts = make(map[string]tls.Certificate) 156 | cert, err := loadCert(p.ClientCertFile, p.ClientKeyFile, "client") 157 | if err != nil { 158 | return err 159 | } 160 | if cert != nil { 161 | p.ClientCerts["default"] = *cert 162 | } 163 | return nil 164 | } 165 | 166 | func (p *ProxyConfig) loadMitmIssuerCert() error { 167 | cert, err := loadCert(p.MitmIssuerCertFile, p.MitmIssuerKeyFile, "mitmIssuer") 168 | if err != nil { 169 | return err 170 | } 171 | if cert != nil { 172 | p.MitmIssuerCert = cert 173 | } 174 | return nil 175 | } 176 | 177 | func loadCert(certFile string, keyFile string, certName string) (*tls.Certificate, error) { 178 | if certFile == "" && keyFile == "" { 179 | return nil, nil 180 | } else if certFile != "" && keyFile == "" { 181 | return nil, fmt.Errorf("%sKeyFile must also be specified if %sCertFile is", certName, certName) 182 | } else if certFile == "" && keyFile != "" { 183 | return nil, fmt.Errorf("%sCertFile must also be specified if %sKeyFile is", certName, certName) 184 | } else { 185 | cert, err := tls.LoadX509KeyPair(certFile, keyFile) 186 | if err != nil { 187 | return nil, fmt.Errorf("Error loading %s certificate: %s", certName, err) 188 | } 189 | return &cert, err 190 | } 191 | } 192 | 193 | func loadRootCABundle() *x509.CertPool { 194 | rootCerts := x509.NewCertPool() 195 | if !rootCerts.AppendCertsFromPEM(cacert) { 196 | panic("Failed to load embedded CA certs!") 197 | } 198 | return rootCerts 199 | } 200 | 201 | func UnmarshalConfigFromFile(configFile string) (*ProxyConfig, error) { 202 | configData, err := ioutil.ReadFile(configFile) 203 | if err != nil { 204 | return nil, fmt.Errorf("Error reading file %s: %s", configFile, err) 205 | } 206 | return UnmarshalConfig(configData) 207 | } 208 | 209 | func UnmarshalConfig(configData []byte) (*ProxyConfig, error) { 210 | config := NewDefaultConfig() 211 | if err := yaml.UnmarshalStrict(configData, config); err != nil { 212 | return nil, fmt.Errorf("Malformed yaml: %s", err) 213 | } 214 | if err := config.Validate(); err != nil { 215 | return nil, fmt.Errorf("Invalid configuration: %s", err) 216 | } 217 | if err := InitConfig(config); err != nil { 218 | return nil, err 219 | } 220 | return config, nil 221 | } 222 | 223 | func InitConfig(config *ProxyConfig) error { 224 | if err := config.loadClientCert(); err != nil { 225 | return err 226 | } 227 | if err := config.loadMitmIssuerCert(); err != nil { 228 | return err 229 | } 230 | rootCerts := loadRootCABundle() 231 | config.RootCACerts = rootCerts 232 | return nil 233 | } 234 | 235 | func InitDefaultConfig() (*ProxyConfig, error) { 236 | config := NewDefaultConfig() 237 | if err := InitConfig(config); err != nil { 238 | return nil, err 239 | } 240 | return config, nil 241 | } 242 | 243 | func NewDefaultConfig() *ProxyConfig { 244 | var config ProxyConfig 245 | if err := yaml.UnmarshalStrict([]byte(defaultConfig), &config); err != nil { 246 | panic(err) 247 | } 248 | return &config 249 | } 250 | -------------------------------------------------------------------------------- /webhook_sentry.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | _ "embed" 5 | "fmt" 6 | "github.com/juggernaut/webhook-sentry/proxy" 7 | "github.com/spf13/cobra" 8 | "github.com/spf13/viper" 9 | "log" 10 | "net" 11 | "strings" 12 | "sync" 13 | "time" 14 | ) 15 | 16 | //go:embed banner.txt 17 | var banner string 18 | 19 | var ( 20 | cfgFile string 21 | rootCmd = &cobra.Command{ 22 | Use: "webhook-sentry", 23 | Short: "An egress proxy for sending webhooks securely", 24 | Run: func(cmd *cobra.Command, args []string) { 25 | execute() 26 | }, 27 | } 28 | ) 29 | 30 | func main() { 31 | rootCmd.Execute() 32 | } 33 | 34 | func init() { 35 | cobra.OnInitialize(initConfig) 36 | rootCmd.Flags().StringVar(&cfgFile, "config", "", "Path to config file") 37 | rootCmd.Flags().String("listener-address", ":9090", "Address to listen on") 38 | rootCmd.Flags().String("listener-type", "http", "Type of listener (http or https)") 39 | rootCmd.Flags().String("listener-cert-file", "", "Path to certificate file (required only if listener is https)") 40 | rootCmd.Flags().String("listener-key-file", "", "Path to key file (required only if listener is https)") 41 | rootCmd.Flags().Duration("connect-timeout", time.Second*10, "TCP connect timeout") 42 | rootCmd.Flags().Duration("connection-lifetime", time.Second*60, "TCP connection lifetime") 43 | rootCmd.Flags().Duration("read-timeout", time.Second*10, "TCP connection read timeout") 44 | rootCmd.Flags().Bool("insecure-skip-cert-verification", false, "Skip target certificate verification (WARNING: not for production use!)") 45 | rootCmd.Flags().Bool("insecure-skip-cidr-deny-list", false, "Skip checking CIDR deny list (WARNING: not for production use!)") 46 | rootCmd.Flags().Uint32("max-response-body-size", 1048576, "Maximum response body size (in bytes) over which the connection is automatically shut down") 47 | rootCmd.Flags().String("access-log-type", "text", "Type of access log (text or json)") 48 | rootCmd.Flags().String("access-log-file", "", "Path to access log file (default goes to stdout)") 49 | rootCmd.Flags().String("proxy-log-type", "text", "Type of proxy log (text or json)") 50 | rootCmd.Flags().String("proxy-log-file", "", "Path to proxy log file (default goes to stdout)") 51 | rootCmd.Flags().String("metrics-address", ":2112", "Address to expose prometheus metrics on") 52 | rootCmd.Flags().StringSlice("cidr-deny-list", nil, "List of CIDRs to be blocked (see docs for default)") 53 | 54 | viper.BindPFlag("listener.address", rootCmd.Flags().Lookup("listener-address")) 55 | viper.BindPFlag("listener.type", rootCmd.Flags().Lookup("listener-type")) 56 | viper.BindPFlag("listener.certFile", rootCmd.Flags().Lookup("listener-cert-file")) 57 | viper.BindPFlag("listener.keyFile", rootCmd.Flags().Lookup("listener-key-file")) 58 | viper.BindPFlag("connectTimeout", rootCmd.Flags().Lookup("connect-timeout")) 59 | viper.BindPFlag("connectionLifetime", rootCmd.Flags().Lookup("connection-lifetime")) 60 | viper.BindPFlag("readTimeout", rootCmd.Flags().Lookup("read-timeout")) 61 | viper.BindPFlag("insecureSkipCertVerification", rootCmd.Flags().Lookup("insecure-skip-cert-verification")) 62 | viper.BindPFlag("insecureSkipCidrDenyList", rootCmd.Flags().Lookup("insecure-skip-cidr-deny-list")) 63 | viper.BindPFlag("maxResponseBodySize", rootCmd.Flags().Lookup("max-response-body-size")) 64 | viper.BindPFlag("accessLog.type", rootCmd.Flags().Lookup("access-log-type")) 65 | viper.BindPFlag("accessLog.file", rootCmd.Flags().Lookup("access-log-file")) 66 | viper.BindPFlag("proxyLog.type", rootCmd.Flags().Lookup("proxy-log-type")) 67 | viper.BindPFlag("proxyLog.file", rootCmd.Flags().Lookup("proxy-log-file")) 68 | viper.BindPFlag("metrics.address", rootCmd.Flags().Lookup("metrics-address")) 69 | viper.BindPFlag("cidrDenyList", rootCmd.Flags().Lookup("cidr-deny-list")) 70 | 71 | viper.SetDefault("cidrDenyList", []string{ 72 | "127.0.0.0/8", 73 | "10.0.0.0/8", 74 | "0.0.0.0/8", 75 | "100.64.0.0/10", 76 | "169.254.0.0/16", 77 | "172.16.0.0/12", 78 | "192.0.0.0/24", 79 | "192.168.0.0/16", 80 | "224.0.0.0/4", 81 | "240.0.0.0/4", 82 | }) 83 | 84 | viper.SetDefault("listener.address", ":9090") 85 | viper.SetDefault("listener.type", "http") 86 | viper.SetDefault("connectTimeout", "10s") 87 | viper.SetDefault("connectionLifetime", "60s") 88 | viper.SetDefault("readTimeout", "10s") 89 | viper.SetDefault("insecureSkipCertVerification", false) 90 | viper.SetDefault("insecureSkipCidrDenyList", false) 91 | viper.SetDefault("maxResponseBodySize", 1048576) 92 | viper.SetDefault("accessLog.type", "text") 93 | viper.SetDefault("proxyLog.type", "text") 94 | viper.SetDefault("metrics.address", ":2112") 95 | viper.SetDefault("requestIDHeader", "Request-ID") 96 | 97 | viper.AutomaticEnv() 98 | viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) 99 | } 100 | 101 | func initConfig() { 102 | 103 | if cfgFile != "" { 104 | viper.SetConfigFile(cfgFile) 105 | } else { 106 | viper.SetConfigName("config") 107 | viper.SetConfigType("yaml") 108 | viper.AddConfigPath(".") 109 | } 110 | 111 | if err := viper.ReadInConfig(); err != nil { 112 | if _, ok := err.(viper.ConfigFileNotFoundError); ok { 113 | // Config file not found; rely on defaults only 114 | } else { 115 | // Config file was found but another error was produced 116 | panic(err) 117 | } 118 | } 119 | } 120 | 121 | func execute() { 122 | var cidrs []proxy.Cidr 123 | for _, c := range viper.GetStringSlice("cidrDenyList") { 124 | cidrs = append(cidrs, validCidr(c)) 125 | } 126 | 127 | config := &proxy.ProxyConfig{ 128 | CidrDenyList: cidrs, 129 | Listeners: []proxy.ListenerConfig{{ 130 | Address: viper.GetString("listener.address"), 131 | Type: validProtocol(viper.GetString("listener.type")), 132 | CertFile: viper.GetString("listener.certFile"), 133 | KeyFile: viper.GetString("listener.keyFile"), 134 | }}, 135 | ConnectTimeout: viper.GetDuration("connectTimeout"), 136 | ConnectionLifetime: viper.GetDuration("connectionLifetime"), 137 | ReadTimeout: viper.GetDuration("readTimeout"), 138 | MaxResponseBodySize: viper.GetUint32("maxResponseBodySize"), 139 | InsecureSkipCertVerification: viper.GetBool("insecureSkipCertVerification"), 140 | InsecureSkipCidrDenyList: viper.GetBool("insecureSkipCidrDenyList"), 141 | ClientCertFile: viper.GetString("clientCertFile"), 142 | ClientKeyFile: viper.GetString("clientKeyFile"), 143 | MitmIssuerCertFile: viper.GetString("mitmIssuerCertFile"), 144 | MitmIssuerKeyFile: viper.GetString("mitmIssuerKeyFile"), 145 | AccessLog: logConfig("accessLog"), 146 | ProxyLog: logConfig("proxyLog"), 147 | MetricsAddress: viper.GetString("metrics.address"), 148 | RequestIDHeader: viper.GetString("requestIDHeader"), 149 | } 150 | 151 | if err := config.Validate(); err != nil { 152 | panic(err) 153 | } 154 | 155 | if err := proxy.InitConfig(config); err != nil { 156 | panic(err) 157 | } 158 | if err := proxy.SetupLogging(config); err != nil { 159 | log.Fatalf("Failed to configure logging: %s\n", err) 160 | } 161 | 162 | proxy.SetupMetrics(config.MetricsAddress) 163 | 164 | fmt.Print(banner) 165 | 166 | proxyServers := proxy.CreateProxyServers(config) 167 | wg := &sync.WaitGroup{} 168 | for i, proxyServer := range proxyServers { 169 | wg.Add(1) 170 | listenerConfig := config.Listeners[i] 171 | if listenerConfig.Type == proxy.HTTP { 172 | proxy.StartHTTPServer(listenerConfig.Address, proxyServer, wg) 173 | } else { 174 | proxy.StartTLSServer(listenerConfig.Address, listenerConfig.CertFile, listenerConfig.KeyFile, proxyServer, wg) 175 | } 176 | } 177 | wg.Wait() 178 | 179 | } 180 | 181 | func validProtocol(proto string) proxy.Protocol { 182 | p := proxy.Protocol(proto) 183 | if p != proxy.HTTP && p != proxy.HTTPS { 184 | panic("Invalid protocol " + proto) 185 | } 186 | return p 187 | } 188 | 189 | func validLogType(logType string) proxy.LogType { 190 | l := proxy.LogType(logType) 191 | if l != proxy.Text && l != proxy.JSON { 192 | panic("Invalid log type " + logType) 193 | } 194 | return l 195 | } 196 | 197 | func logConfig(key string) proxy.LogConfig { 198 | return proxy.LogConfig{ 199 | File: viper.GetString(key + ".file"), 200 | Type: validLogType(viper.GetString(key + ".type")), 201 | } 202 | } 203 | 204 | func validCidr(cidr string) proxy.Cidr { 205 | _, ipNet, err := net.ParseCIDR(cidr) 206 | if err != nil { 207 | panic("Invalid CIDR " + cidr) 208 | } 209 | return proxy.Cidr(*ipNet) 210 | } 211 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Webhook Sentry [![Actions Status](https://github.com/juggernaut/egress-proxy/workflows/Go/badge.svg)](https://github.com/juggernaut/egress-proxy/actions) ![release](https://img.shields.io/github/v/release/juggernaut/webhook-sentry?sort=semver) 2 | Webhook Sentry is a proxy that helps you send [webhooks](https://en.wikipedia.org/wiki/Webhook) to your customers securely. 3 | 4 | ## Why? 5 | ### Security 6 | Sending webhooks appears simple on the surface -- they're just HTTP requests after all. But sending them _securely_ is hard. If your application sends webhooks, does your implementation 7 | 1. Prevent [SSRF](https://portswigger.net/web-security/ssrf) attacks? 8 | 2. Protect against [DNS rebinding](https://en.wikipedia.org/wiki/DNS_rebinding) attacks? 9 | 3. Support mutual TLS? 10 | 4. Validate SSL certificate chains correctly? 11 | 5. Use an updated CA certificate bundle? 12 | 6. Specify reasonable idle socket and connection timeouts? 13 | 14 | By proxying webhooks through Webhook Sentry, you get all of these for free. 15 | 16 | ### Auditability 17 | Sending webhooks involves making connections to untrusted and possibly malicious servers on the public internet. Maintaining an audit trail is essential for forensics and compliance. 18 | Limiting the set of instances that send such requests to a single proxy layer makes auditing simpler and more manageable. 19 | 20 | ### Static Egress IPs 21 | Many customers require webhook requests to be sent from a list or range of static IPs in order to configure their firewalls. In a cloud environment with autoscaling, you 22 | may not want to allocate static IPs to your application instances. In other situations, like serverless applications, it may be impossible to assign static IPs. With a centralized 23 | egress proxy layer, you only need to assign static IPs to your proxy instances. 24 | 25 | ## Getting Started 26 | 27 | Webhook Sentry runs on port 9090 by default. You can configure the address and port in the `listeners` section of the [config](#Configuration). 28 | 29 | The simplest way to run Webhook Sentry is to use the latest binary: 30 | 31 | 1. Download the [latest release](https://github.com/juggernaut/webhook-sentry/releases/latest) for your platform 32 | 2. Run the downloaded binary: 33 | ``` 34 | whsentry 35 | ``` 36 | 37 | We also have a docker image: 38 | 39 | ``` 40 | docker run -p 9090:9090 juggernaut/webhook-sentry:latest 41 | ``` 42 | 43 | You can also pin a [tagged release](https://github.com/juggernaut/webhook-sentry/releases): 44 | 45 | ``` 46 | docker run -p 9090:9090 juggernaut/webhook-sentry:v1.0.8 47 | ``` 48 | 49 | If you need to override settings, you can mount a configuration file, pass in command line flags or set environment variables. See [configuration](#Configuration) for details. 50 | 51 | If you need prometheus metrics for the service, allow access on port 2112 with something like `-p 2112:2112`. 52 | 53 | 54 | ## Usage 55 | ### HTTP target 56 | 57 | ``` 58 | curl -x http://localhost:9090 http://www.google.com 59 | ``` 60 | 61 | ### HTTPS target 62 | HTTP clients create a `CONNECT` tunnel when a proxy is configured and the target is a `https` URL. This does not give us the benefits of initiating TLS from the proxy. To get around this behavior, Webhook Sentry supports a unique way of proxying to HTTPS targets. Pass a `X-WhSentry-TLS` header and change the protocol to `http`: 63 | 64 | ``` 65 | curl -v -x http://localhost:9090 --header 'X-WhSentry-TLS: true' http://www.google.com 66 | ``` 67 | 68 | Although `CONNECT` is supported, I strongly recommend using the header approach to take advantage of the TLS capabilities of Webhook Sentry, like mutual TLS and robust certificate validation. 69 | 70 | ### Mutual TLS 71 | Specify `clientCertFile` and `clientKeyFile` in the configuration to enable mutual TLS: 72 | ``` 73 | clientCertFile: /path/to/client.pem 74 | clientKeyFile: /path/to/key.pem 75 | ``` 76 | 77 | ### Prometheus Metrics 78 | 79 | Point your collector to :2112 for metrics. 80 | 81 | E.g if the proxy is running on localhost, to verify metrics are correctly exposed: 82 | 83 | ``` 84 | curl http://localhost:2112/metrics 85 | ``` 86 | 87 | ## AWS EKS Configuration for Static IP egress 88 | To deploy Webhook Sentry with a static egress IP addresses in AWS EKS, you'll need a node group with an Elastic IP address: 89 | 90 | - Create a new NAT Gateway. 91 | - Create an Elastic IP address and assign it to your NAT Gateway. This will be your egress IP. 92 | - Create a subnet dividing your network space. 93 | - Create a route table linking the subnet to the NAT Gateway. 94 | - Create a custom node group for the Webhook Sentry pods and assign it to the new subnet. 95 | - Finally, [assign](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/) your Webhook Sentry pods to that node group, and ensure your existing pods do not get assigned to it. 96 | 97 | For redundancy, you may want to create multiple NAT Gatways, subnets, and egress IP addressses. 98 | 99 | 100 | ## Protections 101 | ### SSRF attack protection 102 | Webhook Sentry blocks access to private/internal IPs to prevent SSRF attacks: 103 | ``` 104 | $ curl -i -x http://localhost:9090 http://127.0.0.1:3000 105 | 106 | HTTP/1.1 403 Forbidden 107 | Content-Type: text/plain; charset=utf-8 108 | X-Content-Type-Options: nosniff 109 | X-Whsentry-Reason: IP 127.0.0.1 is blocked 110 | X-Whsentry-Reasoncode: 1000 111 | Date: Fri, 18 Sep 2020 07:15:20 GMT 112 | Content-Length: 24 113 | 114 | IP 127.0.0.1 is blocked 115 | ``` 116 | 117 | Unlike naive implementations, it also correctly checks the IP after DNS resolution. This example makes use of the [1u.ms](http://1u.ms/) service which can serve up DNS records using any IP we want: 118 | ``` 119 | $ curl -i -x http://localhost:9090 http://make-127-0-0-1-rr.1u.ms 120 | 121 | HTTP/1.1 403 Forbidden 122 | Content-Type: text/plain; charset=utf-8 123 | X-Content-Type-Options: nosniff 124 | X-Whsentry-Reason: IP 127.0.0.1 is blocked 125 | X-Whsentry-Reasoncode: 1000 126 | Date: Fri, 18 Sep 2020 07:21:58 GMT 127 | Content-Length: 24 128 | 129 | IP 127.0.0.1 is blocked 130 | ``` 131 | 132 | ### DNS rebinding attack prevention 133 | A malicious attacker can set up their DNS such that it first resolves to a valid public IP adddress, but subsequent resolutions point to private/internal IP addresses. This can be used to exploit webhook implementations that validate the resolved IP using `getaddrinfo()` or equivalent, then pass the original URL to a HTTP client library which resolves the host a second time. Again, let's use 1u.ms to first return a valid public IP and then the loopback IP: 134 | 135 | ``` 136 | $ curl -i -x http://localhost:9090 http://make-3-221-81-55-rebind-127-0-0-1-rr.1u.ms/get 137 | 138 | HTTP/1.1 200 OK 139 | Access-Control-Allow-Credentials: true 140 | Access-Control-Allow-Origin: * 141 | Content-Length: 324 142 | Content-Type: application/json 143 | Date: Wed, 30 Sep 2020 07:38:47 GMT 144 | Server: gunicorn/19.9.0 145 | 146 | { 147 | "args": {}, 148 | "headers": { 149 | "Accept": "*/*", 150 | "Host": "make-3-221-81-55-rebind-127-0-0-1-rr.1u.ms", 151 | "User-Agent": "Webhook Sentry/0.1", 152 | "X-Amzn-Trace-Id": "Root=1-5f743607-afdf257ca619f90a14fc92b8" 153 | }, 154 | "origin": "73.189.176.226", 155 | "url": "http://make-3-221-81-55-rebind-127-0-0-1-rr.1u.ms/get" 156 | } 157 | ``` 158 | 159 | ### Mozilla CA certificate bundle 160 | Webhook Sentry uses the latest [Mozilla CA certificate bundle](https://www.mozilla.org/en-US/about/governance/policies/security-group/certs/) instead of relying on CA certificates bundled with the OS. This avoids the problem of out-of-date root CA certificates on older OS versions. See [this blog post](https://www.agwa.name/blog/post/fixing_the_addtrust_root_expiration) for why this is important. Notably, Stripe's webhooks were affected by this issue and took hours to fix. 161 | 162 | On startup, Webhook Sentry checks if there is a newer version of the Mozilla CA certificate bundle than on disk, and if so, downloads it. 163 | 164 | Additionally, by virtue of being written in Go, Webhook Sentry does not rely on OpenSSL or GnuTLS for certificate validation. 165 | 166 | ## Configuration 167 | webhook-sentry uses [viper](https://github.com/spf13/viper) for configuration. You can use a yaml file, environment variables or command line flags to provide configuration parameters. 168 | By default, webhook-sentry looks for a file named `config.yaml` in the current working directory. You can specify a different file using the `--config 0 && uint32(resp.ContentLength) > p.maxContentLength { 221 | responseCode = http.StatusBadGateway 222 | errorCode = ResponseTooLarge 223 | errorMessage = "Response exceeds max content length" 224 | } else { 225 | responseCode = resp.StatusCode 226 | writeResponseHeaders(w, resp) 227 | p.writeResponseBody(requestID, w, resp, cancel) 228 | } 229 | 230 | if errorCode != 0 { 231 | sendHTTPError(w, responseCode, errorCode, errorMessage) 232 | } 233 | 234 | duration := time.Now().Sub(start) 235 | if errorCode == InternalServerError { 236 | logError(requestID, "Unexpected error while proxying request", err) 237 | } 238 | logRequest(r, requestID, responseCode, duration) 239 | updateMetrics(duration, errorCode) 240 | } 241 | } 242 | 243 | func (p *ProxyHTTPHandler) connStateCallback(conn net.Conn, connState http.ConnState) { 244 | // NOTE: Hijacked connections do not transition to closed 245 | if connState == http.StateNew { 246 | p.incrementInboundConns() 247 | } else if connState == http.StateClosed { 248 | p.decrementInboundConns() 249 | } 250 | } 251 | 252 | func (p *ProxyHTTPHandler) incrementInboundConns() { 253 | p.currentInboundConnsGauge.Inc() 254 | } 255 | 256 | func (p *ProxyHTTPHandler) decrementInboundConns() { 257 | p.currentInboundConnsGauge.Dec() 258 | } 259 | 260 | func writeResponseHeaders(w http.ResponseWriter, resp *http.Response) { 261 | for k, values := range resp.Header { 262 | w.Header().Set(k, values[0]) 263 | for _, v := range values[1:] { 264 | w.Header().Add(k, v) 265 | } 266 | } 267 | if resp.TransferEncoding != nil { 268 | for _, t := range resp.TransferEncoding { 269 | w.Header().Add("Transfer-Encoding", t) 270 | } 271 | } 272 | w.WriteHeader(resp.StatusCode) 273 | } 274 | 275 | func (p *ProxyHTTPHandler) writeResponseBody(requestID string, w http.ResponseWriter, resp *http.Response, cancel context.CancelFunc) { 276 | defer resp.Body.Close() 277 | // XXX: pick optimal buffer size 278 | buf := make([]byte, 512) 279 | timer := time.AfterFunc(p.idleReadTimeout, func() { 280 | cancel() 281 | }) 282 | var bytesReadSoFar uint32 = 0 283 | for { 284 | n, err := resp.Body.Read(buf) 285 | if n > 0 { 286 | bytesReadSoFar += uint32(n) 287 | if bytesReadSoFar > p.maxContentLength { 288 | logWarn(requestID, "Response body exceeded maximum allowed length", nil) 289 | break 290 | } 291 | _, writeErr := w.Write(buf[:n]) 292 | if writeErr != nil { 293 | logError(requestID, "Error writing to inbound socket", writeErr) 294 | break 295 | } 296 | } 297 | if err == io.EOF { 298 | break 299 | } else if err == context.Canceled { 300 | logWarn(requestID, "Socket idle read time out reached", nil) 301 | break 302 | } else if err != nil { 303 | logWarn(requestID, "Error occured reading response from target", err) 304 | break 305 | } 306 | timer.Reset(p.idleReadTimeout) 307 | } 308 | } 309 | 310 | type key int 311 | 312 | const clientCertKey key = 0 313 | 314 | func (p ProxyHTTPHandler) doProxy(ctx context.Context, r *http.Request) (*http.Response, error) { 315 | if !r.URL.IsAbs() { 316 | return nil, &proxyError{statusCode: http.StatusBadRequest, message: "Request URI must be absolute", errorCode: InvalidRequestURI} 317 | } 318 | if r.URL.Scheme != "http" { 319 | return nil, &proxyError{statusCode: http.StatusBadRequest, message: "URL scheme must be HTTP", errorCode: InvalidUrlScheme} 320 | } 321 | //fmt.Fprintf(w, "Hello Go HTTP") 322 | var outboundUri = r.RequestURI 323 | if isTLS(r.Header) { 324 | outboundUri = strings.Replace(outboundUri, "http", "https", 1) 325 | } 326 | clientCert, ok := r.Header["X-Whsentry-Clientcert"] 327 | if ok && len(clientCert) > 0 { 328 | ctx = context.WithValue(ctx, clientCertKey, clientCert[0]) 329 | } 330 | outboundRequest, err := http.NewRequestWithContext(ctx, r.Method, outboundUri, r.Body) 331 | if err != nil { 332 | return nil, err 333 | } 334 | copyHeaders(r.Header, outboundRequest.Header) 335 | outboundRequest.Header["User-Agent"] = []string{"Webhook Sentry/0.1"} 336 | outboundRequest.ContentLength = r.ContentLength 337 | return p.roundTripper.RoundTrip(outboundRequest) 338 | } 339 | 340 | func sendHTTPError(w http.ResponseWriter, statusCode int, errorCode uint16, errorMessage string) { 341 | w.Header().Add(ReasonCodeHeader, strconv.Itoa(int(errorCode))) 342 | w.Header().Add(ReasonHeader, errorMessage) 343 | http.Error(w, errorMessage, statusCode) 344 | } 345 | 346 | func mapError(requestID string, err error) (int, uint16, string) { 347 | switch v := err.(type) { 348 | case *proxyError: 349 | return int(v.statusCode), v.errorCode, v.message 350 | case *net.DNSError: 351 | return http.StatusBadGateway, UnableToResolveIP, err.Error() 352 | case net.Error: 353 | if v.Timeout() { 354 | return http.StatusBadGateway, RequestTimedOut, "Request to target timed out" 355 | } 356 | if opErr, ok := v.(*net.OpError); ok { 357 | return mapNetOpError(requestID, *opErr) 358 | } 359 | case x509.CertificateInvalidError, x509.HostnameError, x509.UnknownAuthorityError: 360 | logWarn(requestID, "Certificate validation error", err) 361 | return http.StatusBadGateway, CertificateValidationError, v.Error() 362 | } 363 | return http.StatusInternalServerError, InternalServerError, "Internal Server Error" 364 | } 365 | 366 | func mapNetOpError(requestID string, err net.OpError) (int, uint16, string) { 367 | wrapped := err.Unwrap() 368 | // This is hacky, but the TLS alert errors aren't exported 369 | if strings.Contains(wrapped.Error(), "tls:") { 370 | logWarn(requestID, "TLS handshake error", wrapped) 371 | message := fmt.Sprintf("TLS handshake error: %s", wrapped) 372 | return http.StatusBadGateway, TLSHandshakeError, message 373 | } 374 | if strings.Contains(wrapped.Error(), "connect:") { 375 | logWarn(requestID, "TCP connection error", wrapped) 376 | message := fmt.Sprintf("TCP connection error: %s", wrapped) 377 | return http.StatusBadGateway, TCPConnectionError, message 378 | } 379 | return http.StatusInternalServerError, InternalServerError, "Internal Server Error" 380 | } 381 | 382 | func logRequest(r *http.Request, requestID string, responseCode int, responseTime time.Duration) { 383 | url := r.RequestURI 384 | if isTLS(r.Header) { 385 | url = strings.Replace(url, "http:", "https:", 1) 386 | } 387 | requestLogger := accessLog.WithFields(logrus.Fields{"rq_id": requestID, "client_addr": r.RemoteAddr, "method": r.Method, "url": url, "response_code": responseCode, 388 | "response_time": responseTime}) 389 | requestLogger.Info() 390 | } 391 | 392 | func logWarn(requestID string, message string, err error) { 393 | doLog(requestID, message, err, logrus.WarnLevel) 394 | } 395 | 396 | func logError(requestID string, message string, err error) { 397 | doLog(requestID, message, err, logrus.ErrorLevel) 398 | } 399 | 400 | func doLog(requestID string, message string, err error, level logrus.Level) { 401 | var errorStr string 402 | if err != nil { 403 | errorStr = err.Error() 404 | } 405 | logger := log.WithFields(logrus.Fields{"rq_id": requestID, "error": errorStr}) 406 | logger.Log(level, message) 407 | } 408 | 409 | func updateMetrics(duration time.Duration, errorCode uint16) { 410 | responseHistogram.With(prometheus.Labels{"error_code": strconv.Itoa(int(errorCode))}).Observe(float64(duration.Milliseconds())) 411 | } 412 | 413 | func isTLS(h http.Header) bool { 414 | tlsHeader, ok := h["X-Whsentry-Tls"] 415 | if ok { 416 | for _, val := range tlsHeader { 417 | if val == "0" || strings.EqualFold(val, "false") { 418 | return false 419 | } 420 | } 421 | return true 422 | } 423 | return false 424 | } 425 | 426 | func copyHeaders(inHeader http.Header, outHeader http.Header) { 427 | for name, values := range inHeader { 428 | var skipHeader = false 429 | for _, skipHeaderName := range skipHeaders { 430 | if name == skipHeaderName { 431 | skipHeader = true 432 | break 433 | } 434 | } 435 | if strings.HasPrefix(name, "X-Whsentry") { 436 | skipHeader = true 437 | } 438 | if !skipHeader { 439 | for _, value := range values { 440 | outHeader.Add(name, value) 441 | } 442 | } 443 | } 444 | } 445 | 446 | type safeDialer struct { 447 | dialer *net.Dialer 448 | cidrBlacklist []net.IPNet 449 | clientCerts map[string]tls.Certificate 450 | skipServerCertVerification bool 451 | rootCerts *x509.CertPool 452 | } 453 | 454 | func newSafeDialer(config *ProxyConfig) *safeDialer { 455 | dialer := &net.Dialer{ 456 | Timeout: config.ConnectTimeout, 457 | DualStack: false, 458 | KeepAlive: -1, 459 | } 460 | var cidrDenyList []net.IPNet 461 | if !config.InsecureSkipCidrDenyList { 462 | for _, cidr := range config.CidrDenyList { 463 | cidrDenyList = append(cidrDenyList, net.IPNet(cidr)) 464 | } 465 | } 466 | return &safeDialer{ 467 | dialer: dialer, 468 | cidrBlacklist: cidrDenyList, 469 | skipServerCertVerification: config.InsecureSkipCertVerification, 470 | clientCerts: config.ClientCerts, 471 | rootCerts: config.RootCACerts, 472 | } 473 | } 474 | 475 | func (s *safeDialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) { 476 | ipPort, err := s.resolveIPPort(ctx, addr) 477 | if err != nil { 478 | return nil, err 479 | } 480 | return s.dialer.DialContext(ctx, "tcp4", ipPort) 481 | } 482 | 483 | func (s *safeDialer) resolveIPPort(ctx context.Context, addr string) (string, error) { 484 | host, port, err := net.SplitHostPort(addr) 485 | if err != nil { 486 | return "", err 487 | } 488 | ips, err := s.dialer.Resolver.LookupIPAddr(ctx, host) 489 | if err != nil { 490 | return "", err 491 | } 492 | var chosenIP net.IP = nil 493 | for _, ip := range ips { 494 | if strings.Count(ip.IP.String(), ":") < 2 { 495 | chosenIP = ip.IP 496 | break 497 | } 498 | } 499 | if chosenIP == nil { 500 | return "", &proxyError{statusCode: http.StatusBadRequest, message: fmt.Sprintf("Target %s did not resolve to a valid IPv4 address", addr), errorCode: UnableToResolveIP} 501 | } 502 | if isBlacklisted(s.cidrBlacklist, chosenIP) { 503 | return "", &proxyError{statusCode: http.StatusForbidden, message: fmt.Sprintf("IP %s is blocked", chosenIP.String()), errorCode: BlockedIPAddress} 504 | } 505 | 506 | return net.JoinHostPort(chosenIP.String(), port), nil 507 | } 508 | 509 | func (s *safeDialer) DialTLSContext(ctx context.Context, network, addr string) (net.Conn, error) { 510 | // We need the host here to set the SNI hostname, otherwise it incorrectly uses the IP address as the SNI 511 | host, _, err := net.SplitHostPort(addr) 512 | if err != nil { 513 | return nil, err 514 | } 515 | 516 | ipPort, err := s.resolveIPPort(ctx, addr) 517 | if err != nil { 518 | return nil, err 519 | } 520 | certAlias, ok := ctx.Value(clientCertKey).(string) 521 | if ok { 522 | if _, found := s.clientCerts[certAlias]; !found { 523 | return nil, &proxyError{statusCode: http.StatusBadRequest, message: fmt.Sprintf("Cert with alias %s not found in certificate store", certAlias), errorCode: ClientCertNotFoundError} 524 | } 525 | } 526 | conn, err := s.dialer.DialContext(ctx, "tcp4", ipPort) 527 | if err != nil { 528 | return nil, err 529 | } 530 | return s.doTLSHandshake(conn, host, certAlias) 531 | } 532 | 533 | func (s *safeDialer) doTLSHandshake(conn net.Conn, hostname string, certAlias string) (net.Conn, error) { 534 | var clientCert tls.Certificate 535 | if certAlias == "" { 536 | certAlias = "default" 537 | } 538 | 539 | if cert, ok := s.clientCerts[certAlias]; ok { 540 | clientCert = cert 541 | } 542 | 543 | tlsConfig := &tls.Config{ 544 | ServerName: hostname, 545 | InsecureSkipVerify: s.skipServerCertVerification, 546 | GetClientCertificate: func(requestInfo *tls.CertificateRequestInfo) (*tls.Certificate, error) { 547 | if len(clientCert.Certificate) == 0 { 548 | //logWarn("Client certificate requested by server, but we don't have one") 549 | } 550 | return &clientCert, nil 551 | }, 552 | RootCAs: s.rootCerts, 553 | } 554 | tlsConn := tls.Client(conn, tlsConfig) 555 | // NOTE: this effectively makes the total timeout for a TLS conn (2 * Config.Timeout) 556 | tlsConn.SetDeadline(time.Now().Add(s.dialer.Timeout)) 557 | if err := tlsConn.Handshake(); err != nil { 558 | return nil, err 559 | } 560 | tlsConn.SetDeadline(time.Time{}) 561 | return tlsConn, nil 562 | } 563 | 564 | func isBlacklisted(cidrBlacklist []net.IPNet, ip net.IP) bool { 565 | if cidrBlacklist == nil { 566 | return false 567 | } 568 | for _, cidr := range cidrBlacklist { 569 | if cidr.Contains(ip) { 570 | return true 571 | } 572 | } 573 | return false 574 | } 575 | 576 | type proxyError struct { 577 | statusCode uint 578 | message string 579 | errorCode uint16 580 | } 581 | 582 | func (p *proxyError) Error() string { 583 | return fmt.Sprintf("%s, Status code: %d", p.message, p.statusCode) 584 | } 585 | 586 | func isTruish(val string) bool { 587 | if val == "" { 588 | return false 589 | } 590 | return val == "1" || strings.EqualFold(val, "true") 591 | } 592 | 593 | type AccessLogTextFormatter struct { 594 | } 595 | 596 | func (f *AccessLogTextFormatter) Format(entry *logrus.Entry) ([]byte, error) { 597 | fields := entry.Data 598 | ts := entry.Time.Format(time.RFC3339) 599 | responseTime := fields["response_time"].(time.Duration) 600 | logLine := fmt.Sprintf("[%s] %s %s %s %s %d %dms\n", ts, fields["rq_id"], fields["client_addr"], fields["method"], fields["url"], fields["response_code"], responseTime.Milliseconds()) 601 | return []byte(logLine), nil 602 | } 603 | 604 | type ProxyLogTextFormatter struct { 605 | } 606 | 607 | func (f *ProxyLogTextFormatter) Format(entry *logrus.Entry) ([]byte, error) { 608 | fields := entry.Data 609 | ts := entry.Time.Format(time.RFC3339) 610 | var errorStr string 611 | if err, ok := fields["error"]; ok { 612 | errorStr = err.(string) 613 | errorStr = ": " + errorStr 614 | } 615 | logLine := fmt.Sprintf("[%s] %s %s %s%s\n", ts, fields["rq_id"], strings.ToUpper(entry.Level.String()), entry.Message, errorStr) 616 | return []byte(logLine), nil 617 | } 618 | -------------------------------------------------------------------------------- /integration_test.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 Ameya Lokare 3 | */ 4 | package main 5 | 6 | import ( 7 | "bufio" 8 | "context" 9 | "crypto/tls" 10 | "crypto/x509" 11 | "fmt" 12 | "github.com/juggernaut/webhook-sentry/certutil" 13 | "github.com/juggernaut/webhook-sentry/proxy" 14 | "io" 15 | "io/ioutil" 16 | "net" 17 | "net/http" 18 | "net/url" 19 | "strconv" 20 | "strings" 21 | "testing" 22 | "time" 23 | ) 24 | 25 | const ( 26 | proxyHttpAddress = "127.0.0.1:11090" 27 | proxyHttpsAddress = "127.0.0.1:11091" 28 | httpTargetServerPort = "12080" 29 | httpsTargetServerPort = "12081" 30 | httpsTargetServerWithClientCertCheckPort = "12089" 31 | ) 32 | 33 | type testFixture struct { 34 | certificates *certutil.CertificateFixtures 35 | configSetup func(*proxy.ProxyConfig, *certutil.CertificateFixtures) 36 | serversSetup func(*certutil.CertificateFixtures) []*http.Server 37 | transportSetup func(*http.Transport, *certutil.CertificateFixtures) 38 | proxy *http.Server 39 | proxyType proxy.Protocol 40 | servers []*http.Server 41 | } 42 | 43 | func (f *testFixture) setUp(t *testing.T) *http.Client { 44 | if f.certificates == nil { 45 | f.certificates = certutil.NewCertificateFixtures(t) 46 | } 47 | proxyConfig := proxy.NewDefaultConfig() 48 | if f.configSetup != nil { 49 | f.configSetup(proxyConfig, f.certificates) 50 | } 51 | if f.proxyType == "" { 52 | f.proxyType = proxy.HTTP 53 | } 54 | switch f.proxyType { 55 | case proxy.HTTP: 56 | f.proxy = startProxy(t, proxyConfig) 57 | case proxy.HTTPS: 58 | f.proxy = startTLSProxyWithCert(t, proxyConfig, f.certificates.ProxyCert) 59 | } 60 | 61 | if f.serversSetup == nil { 62 | t.Fatal("Target servers must be setup in test fixture!") 63 | } 64 | 65 | f.servers = f.serversSetup(f.certificates) 66 | waitForStartup(t, f.proxy.Addr) 67 | 68 | tr := &http.Transport{ 69 | Proxy: func(r *http.Request) (*url.URL, error) { 70 | if f.proxyType == proxy.HTTP { 71 | return url.Parse("http://" + proxyHttpAddress) 72 | } else { 73 | return url.Parse("https://" + proxyHttpsAddress) 74 | } 75 | }, 76 | } 77 | if f.transportSetup != nil { 78 | f.transportSetup(tr, f.certificates) 79 | } 80 | return &http.Client{Transport: tr} 81 | } 82 | 83 | func (f *testFixture) tearDown(t *testing.T) { 84 | if f.proxy != nil { 85 | f.proxy.Shutdown(context.TODO()) 86 | } 87 | for _, s := range f.servers { 88 | s.Shutdown(context.TODO()) 89 | } 90 | } 91 | 92 | func assertForbiddenIP(client *http.Client, host string, t *testing.T) { 93 | resp, err := client.Get(fmt.Sprintf("http://%s:%s", host, httpTargetServerPort)) 94 | if err != nil { 95 | t.Errorf("Error in GET request to target server via proxy: %s\n", err) 96 | } 97 | if resp.StatusCode != 403 { 98 | t.Errorf("Expected status code 403, got %d\n", resp.StatusCode) 99 | } 100 | errorCode, ok := resp.Header[http.CanonicalHeaderKey(proxy.ReasonCodeHeader)] 101 | if !ok { 102 | t.Errorf("Error code header not present") 103 | } 104 | errorCodeI, err := strconv.Atoi(errorCode[0]) 105 | if err != nil { 106 | t.Fatal(err) 107 | } 108 | if uint16(errorCodeI) != proxy.BlockedIPAddress { 109 | t.Errorf("Expected errorCode %d, but found %s", proxy.BlockedIPAddress, errorCode[0]) 110 | } 111 | } 112 | 113 | func TestPrivateNetworkForbidden(t *testing.T) { 114 | fixture := &testFixture{ 115 | serversSetup: func(c *certutil.CertificateFixtures) []*http.Server { 116 | server := startTargetServer(t, nil) 117 | return []*http.Server{server} 118 | }, 119 | } 120 | 121 | client := fixture.setUp(t) 122 | 123 | t.Run("Localhost forbidden", func(t *testing.T) { 124 | assertForbiddenIP(client, "localhost", t) 125 | }) 126 | 127 | t.Run("RFC 1918 addresses forbidden", func(t *testing.T) { 128 | assertForbiddenIP(client, "10.1.1.1", t) 129 | assertForbiddenIP(client, "172.16.1.1", t) 130 | }) 131 | 132 | fixture.tearDown(t) 133 | } 134 | 135 | func TestProxy(t *testing.T) { 136 | fixture := &testFixture{ 137 | configSetup: func(config *proxy.ProxyConfig, c *certutil.CertificateFixtures) { 138 | config.InsecureSkipCidrDenyList = true 139 | }, 140 | serversSetup: func(certificates *certutil.CertificateFixtures) []*http.Server { 141 | var servers []*http.Server 142 | httpServer := startTargetServer(t, nil) 143 | httpsServer := startTargetHTTPSServerWithInMemoryCert(t, certificates.InvalidHostnameServerCert) 144 | return append(servers, httpServer, httpsServer) 145 | }, 146 | } 147 | 148 | client := fixture.setUp(t) 149 | 150 | t.Run("Proxy 200 OK", func(t *testing.T) { 151 | resp, err := client.Get(fmt.Sprintf("http://localhost:%s/target", httpTargetServerPort)) 152 | if err != nil { 153 | t.Errorf("Error in GET request to target server via proxy: %s\n", err) 154 | } 155 | if resp.StatusCode != 200 { 156 | t.Errorf("Expected status code 200, got %d\n", resp.StatusCode) 157 | } 158 | customHeader := resp.Header.Get("X-Custom-Header") 159 | if customHeader != "custom" { 160 | t.Fatalf("Expected custom header to be present, but it is not") 161 | } 162 | }) 163 | 164 | t.Run("Proxy 404 Not Found", func(t *testing.T) { 165 | resp, err := client.Get(fmt.Sprintf("http://localhost:%s/someRandomPath", httpTargetServerPort)) 166 | if err != nil { 167 | t.Errorf("Error in GET request to target server via proxy: %s\n", err) 168 | } 169 | if resp.StatusCode != 404 { 170 | t.Errorf("Expected status code 404, got %d\n", resp.StatusCode) 171 | } 172 | }) 173 | 174 | t.Run("HTTPS target using header fails due to invalid hostname in cert", func(t *testing.T) { 175 | req, err := http.NewRequest("GET", fmt.Sprintf("http://localhost:%s", httpsTargetServerPort), nil) 176 | if err != nil { 177 | t.Fatalf("Failed to create new request: %s\n", err) 178 | } 179 | req.Header.Add("X-WHSentry-TLS", "true") 180 | resp, err := client.Do(req) 181 | if err != nil { 182 | t.Errorf("Error in GET request to target server via proxy: %s\n", err) 183 | } 184 | if resp.StatusCode != 502 { 185 | t.Errorf("Expected status code 502, got %d\n", resp.StatusCode) 186 | } 187 | }) 188 | 189 | fixture.tearDown(t) 190 | } 191 | 192 | func TestHTTPSTargets(t *testing.T) { 193 | fixture := &testFixture{ 194 | configSetup: func(config *proxy.ProxyConfig, certificates *certutil.CertificateFixtures) { 195 | config.InsecureSkipCidrDenyList = true 196 | config.InsecureSkipCertVerification = true 197 | config.ClientCerts = make(map[string]tls.Certificate) 198 | config.ClientCerts["default"] = *certificates.ClientCert 199 | }, 200 | serversSetup: func(certificates *certutil.CertificateFixtures) []*http.Server { 201 | var servers []*http.Server 202 | httpsServer := startTargetHTTPSServerWithInMemoryCert(t, certificates.ServerCert) 203 | httpsServerWithClientCertCheck := startTargetHTTPSServerWithClientCertCheck(t, certificates.ServerCert, certificates.RootCAs) 204 | return append(servers, httpsServer, httpsServerWithClientCertCheck) 205 | }, 206 | } 207 | 208 | client := fixture.setUp(t) 209 | 210 | t.Run("Successful proxy to HTTPS target", func(t *testing.T) { 211 | req, err := http.NewRequest("GET", fmt.Sprintf("http://localhost:%s/target", httpsTargetServerPort), nil) 212 | if err != nil { 213 | t.Fatalf("Failed to create new request: %s\n", err) 214 | } 215 | req.Header.Add("X-WHSentry-TLS", "true") 216 | resp, err := client.Do(req) 217 | if err != nil { 218 | t.Errorf("Error in GET request to target server via proxy: %s\n", err) 219 | } 220 | if resp.StatusCode != 200 { 221 | t.Errorf("Expected status code 200, got %d\n", resp.StatusCode) 222 | } 223 | buf := new(strings.Builder) 224 | _, err = io.Copy(buf, resp.Body) 225 | if err != nil { 226 | t.Errorf("Error while reading body: %s\n", err) 227 | } 228 | if buf.String() != "Hello from target HTTPS" { 229 | t.Errorf("Expected string 'Hello from target HTTPS' in response, but was %s\n", buf.String()) 230 | } 231 | }) 232 | 233 | t.Run("Unknown client cert", func(t *testing.T) { 234 | req, err := http.NewRequest("GET", fmt.Sprintf("http://localhost:%s/target", httpsTargetServerWithClientCertCheckPort), nil) 235 | if err != nil { 236 | t.Fatalf("Failed to create new request: %s\n", err) 237 | } 238 | req.Header.Add("X-WHSentry-TLS", "true") 239 | req.Header.Add("X-WHSentry-ClientCert", "foobar") 240 | resp, err := client.Do(req) 241 | if err != nil { 242 | t.Errorf("Error in GET request to target server via proxy: %s\n", err) 243 | } 244 | if resp.StatusCode != 400 { 245 | t.Errorf("Expected status code 400, got %d\n", resp.StatusCode) 246 | } 247 | errorCode, ok := resp.Header[http.CanonicalHeaderKey(proxy.ReasonCodeHeader)] 248 | if !ok { 249 | t.Errorf("Error Code header not present") 250 | } 251 | errorCodeI, err := strconv.Atoi(errorCode[0]) 252 | if err != nil { 253 | t.Fatal(err) 254 | } 255 | if uint16(errorCodeI) != proxy.ClientCertNotFoundError { 256 | t.Errorf("Expected %d errorCode, got %s", proxy.ClientCertNotFoundError, errorCode[0]) 257 | } 258 | }) 259 | 260 | t.Run("Successful proxy to HTTPS target that checks client cert using implicit default client cert", func(t *testing.T) { 261 | req, err := http.NewRequest("GET", fmt.Sprintf("http://localhost:%s/target", httpsTargetServerWithClientCertCheckPort), nil) 262 | if err != nil { 263 | t.Fatalf("Failed to create new request: %s\n", err) 264 | } 265 | req.Header.Add("X-WHSentry-TLS", "true") 266 | resp, err := client.Do(req) 267 | if err != nil { 268 | t.Errorf("Error in GET request to target server via proxy: %s\n", err) 269 | } 270 | if resp.StatusCode != 200 { 271 | t.Errorf("Expected status code 200, got %d\n", resp.StatusCode) 272 | } 273 | buf := new(strings.Builder) 274 | _, err = io.Copy(buf, resp.Body) 275 | if err != nil { 276 | t.Errorf("Error while reading body: %s\n", err) 277 | } 278 | if buf.String() != "Hello from target HTTPS with client cert check" { 279 | t.Errorf("Expected string 'Hello from target HTTPS with client cert check' in response, but was %s\n", buf.String()) 280 | } 281 | }) 282 | 283 | t.Run("Successful proxy to HTTPS target that checks client cert with explicit cert specified", func(t *testing.T) { 284 | req, err := http.NewRequest("GET", fmt.Sprintf("http://localhost:%s/target", httpsTargetServerWithClientCertCheckPort), nil) 285 | if err != nil { 286 | t.Fatalf("Failed to create new request: %s\n", err) 287 | } 288 | req.Header.Add("X-WHSentry-TLS", "true") 289 | req.Header.Add("X-WHSentry-ClientCert", "default") 290 | resp, err := client.Do(req) 291 | if err != nil { 292 | t.Errorf("Error in GET request to target server via proxy: %s\n", err) 293 | } 294 | if resp.StatusCode != 200 { 295 | t.Errorf("Expected status code 200, got %d\n", resp.StatusCode) 296 | } 297 | buf := new(strings.Builder) 298 | _, err = io.Copy(buf, resp.Body) 299 | if err != nil { 300 | t.Errorf("Error while reading body: %s\n", err) 301 | } 302 | if buf.String() != "Hello from target HTTPS with client cert check" { 303 | t.Errorf("Expected string 'Hello from target HTTPS with client cert check' in response, but was %s\n", buf.String()) 304 | } 305 | }) 306 | 307 | fixture.tearDown(t) 308 | 309 | } 310 | 311 | func TestHttpConnectNotAllowedByDefault(t *testing.T) { 312 | fixture := &testFixture{ 313 | configSetup: func(config *proxy.ProxyConfig, c *certutil.CertificateFixtures) { 314 | config.InsecureSkipCidrDenyList = true 315 | config.InsecureSkipCertVerification = true 316 | }, 317 | serversSetup: func(certificates *certutil.CertificateFixtures) []*http.Server { 318 | target := startTargetHTTPSServerWithInMemoryCert(t, certificates.ServerCert) 319 | return []*http.Server{target} 320 | }, 321 | } 322 | client := fixture.setUp(t) 323 | _, err := client.Get(fmt.Sprintf("https://localhost:%s/target", httpsTargetServerPort)) 324 | if err == nil { 325 | t.Error("Expected to get error because CONNECT is disallowed, instead got no error") 326 | } 327 | if !strings.Contains(err.Error(), "Method Not Allowed") { 328 | t.Errorf("Expected error '%s' to contain string 'Method Not Allowed'", err.Error()) 329 | } 330 | fixture.tearDown(t) 331 | } 332 | 333 | func TestMitmHttpConnect(t *testing.T) { 334 | fixture := &testFixture{ 335 | configSetup: func(config *proxy.ProxyConfig, c *certutil.CertificateFixtures) { 336 | config.InsecureSkipCidrDenyList = true 337 | // This only disables the cert verification for the target server from the proxy, not from client to (MITM) proxy 338 | config.InsecureSkipCertVerification = true 339 | config.MitmIssuerCert = c.RootCACert 340 | }, 341 | serversSetup: func(c *certutil.CertificateFixtures) []*http.Server { 342 | server := startTargetHTTPSServerWithInMemoryCert(t, c.ServerCert) 343 | return []*http.Server{server} 344 | }, 345 | transportSetup: func(tr *http.Transport, c *certutil.CertificateFixtures) { 346 | tr.TLSClientConfig = &tls.Config{ 347 | RootCAs: c.RootCAs, 348 | } 349 | }, 350 | } 351 | 352 | client := fixture.setUp(t) 353 | 354 | resp, err := client.Get(fmt.Sprintf("https://localhost:%s/target", httpsTargetServerPort)) 355 | if err != nil { 356 | t.Fatalf("Got error requesting CONNECT to HTTPS target: %s", err) 357 | } 358 | if resp.StatusCode != 200 { 359 | t.Fatalf("Expected status code 200, got status code %d", resp.StatusCode) 360 | } 361 | 362 | fixture.tearDown(t) 363 | } 364 | 365 | func TestOutboundConnectionLifetime(t *testing.T) { 366 | 367 | fixture := &testFixture{ 368 | configSetup: func(config *proxy.ProxyConfig, c *certutil.CertificateFixtures) { 369 | config.InsecureSkipCidrDenyList = true 370 | config.ConnectionLifetime = time.Second * 5 371 | config.ReadTimeout = time.Second * 2 372 | }, 373 | serversSetup: func(c *certutil.CertificateFixtures) []*http.Server { 374 | // These are not http.Server unfortunately 375 | go startSlowToRespondServer(t) 376 | go startNeverSendsBodyServer(t) 377 | return []*http.Server{} 378 | }, 379 | } 380 | 381 | client := fixture.setUp(t) 382 | 383 | t.Run("test connection lifetime", func(t *testing.T) { 384 | resp, err := client.Get("http://localhost:14400/") 385 | if err != nil { 386 | t.Errorf("Error in GET request to target server via proxy: %s\n", err) 387 | } 388 | if resp.StatusCode != 502 { 389 | t.Errorf("Expected status code 502, got %d\n", resp.StatusCode) 390 | } 391 | errorCode, ok := resp.Header[http.CanonicalHeaderKey(proxy.ReasonCodeHeader)] 392 | if !ok { 393 | t.Errorf("Error Code header not present") 394 | } 395 | errorCodeI, err := strconv.Atoi(errorCode[0]) 396 | if err != nil { 397 | t.Fatal(err) 398 | } 399 | if uint16(errorCodeI) != proxy.RequestTimedOut { 400 | t.Errorf("Expected %d errorCode, got %s", proxy.RequestTimedOut, errorCode[0]) 401 | } 402 | }) 403 | 404 | t.Run("test socket read timeout", func(t *testing.T) { 405 | ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) 406 | defer cancel() 407 | req, _ := http.NewRequestWithContext(ctx, "GET", "http://localhost:14402/", nil) 408 | start := time.Now() 409 | resp, err := client.Do(req) 410 | if err != nil { 411 | t.Errorf("Error in GET request to target server via proxy: %s\n", err) 412 | } 413 | buf := make([]byte, resp.ContentLength, resp.ContentLength) 414 | _, err = resp.Body.Read(buf) 415 | if err != io.ErrUnexpectedEOF { 416 | t.Errorf("Expected a 'UnexpectedEOF' error, instead got: %s\n", err) 417 | } 418 | duration := time.Now().Sub(start) 419 | if !(duration.Seconds() >= 1.9 && duration.Seconds() <= 2.2) { 420 | t.Errorf("Expected read timeout (and hence connection close) at ~2 seconds, instead it took %f seconds", duration.Seconds()) 421 | } 422 | 423 | }) 424 | 425 | fixture.tearDown(t) 426 | 427 | } 428 | 429 | func TestProxyTrustsTargetSignedWithCustomRootCA(t *testing.T) { 430 | fixture := &testFixture{ 431 | configSetup: func(config *proxy.ProxyConfig, c *certutil.CertificateFixtures) { 432 | config.InsecureSkipCidrDenyList = true 433 | // This is false by default, but make it explicit for clarity 434 | config.InsecureSkipCertVerification = false 435 | config.RootCACerts = c.RootCAs 436 | }, 437 | serversSetup: func(c *certutil.CertificateFixtures) []*http.Server { 438 | server := startTargetHTTPSServerWithInMemoryCert(t, c.ServerCert) 439 | return []*http.Server{server} 440 | }, 441 | } 442 | 443 | client := fixture.setUp(t) 444 | 445 | t.Run("Successful proxy to HTTPS target with custom root CA", func(t *testing.T) { 446 | req, err := http.NewRequest("GET", "http://localhost:12081/target", nil) 447 | if err != nil { 448 | t.Fatalf("Failed to create new request: %s\n", err) 449 | } 450 | req.Header.Add("X-WHSentry-TLS", "true") 451 | resp, err := client.Do(req) 452 | if err != nil { 453 | t.Errorf("Error in GET request to target server via proxy: %s\n", err) 454 | } 455 | if resp.StatusCode != 200 { 456 | t.Errorf("Expected status code 200, got %d\n", resp.StatusCode) 457 | } 458 | buf := new(strings.Builder) 459 | _, err = io.Copy(buf, resp.Body) 460 | if err != nil { 461 | t.Errorf("Error while reading body: %s\n", err) 462 | } 463 | if buf.String() != "Hello from target HTTPS" { 464 | t.Errorf("Expected string 'Hello from target HTTPS' in response, but was %s\n", buf.String()) 465 | } 466 | }) 467 | 468 | fixture.tearDown(t) 469 | 470 | } 471 | 472 | func TestHTTPSProxyListener(t *testing.T) { 473 | fixture := &testFixture{ 474 | configSetup: func(config *proxy.ProxyConfig, c *certutil.CertificateFixtures) { 475 | config.InsecureSkipCidrDenyList = true 476 | config.InsecureSkipCertVerification = false 477 | config.RootCACerts = c.RootCAs 478 | }, 479 | serversSetup: func(c *certutil.CertificateFixtures) []*http.Server { 480 | httpServer := startTargetServer(t, nil) 481 | httpsServer := startTargetHTTPSServerWithInMemoryCert(t, c.ServerCert) 482 | return []*http.Server{httpServer, httpsServer} 483 | }, 484 | proxyType: proxy.HTTPS, 485 | transportSetup: func(tr *http.Transport, c *certutil.CertificateFixtures) { 486 | tr.TLSClientConfig = &tls.Config{ 487 | RootCAs: c.RootCAs, 488 | } 489 | }, 490 | } 491 | 492 | client := fixture.setUp(t) 493 | 494 | t.Run("Test HTTPS proxy -> HTTP target", func(t *testing.T) { 495 | req, err := http.NewRequest("GET", fmt.Sprintf("http://localhost:%s/target", httpTargetServerPort), nil) 496 | if err != nil { 497 | t.Fatalf("Failed to create new request: %s\n", err) 498 | } 499 | resp, err := client.Do(req) 500 | if err != nil { 501 | t.Errorf("Error in GET request to target server via proxy: %s\n", err) 502 | } 503 | if resp.StatusCode != 200 { 504 | t.Errorf("Expected status code 200, got %d\n", resp.StatusCode) 505 | } 506 | }) 507 | 508 | t.Run("Test HTTPS proxy -> HTTPS target", func(t *testing.T) { 509 | req, err := http.NewRequest("GET", fmt.Sprintf("http://localhost:%s/target", httpsTargetServerPort), nil) 510 | if err != nil { 511 | t.Fatalf("Failed to create new request: %s\n", err) 512 | } 513 | req.Header.Add("X-WHSentry-TLS", "true") 514 | resp, err := client.Do(req) 515 | if err != nil { 516 | t.Errorf("Error in GET request to target server via proxy: %s\n", err) 517 | } 518 | if resp.StatusCode != 200 { 519 | t.Errorf("Expected status code 200, got %d\n", resp.StatusCode) 520 | } 521 | buf := new(strings.Builder) 522 | _, err = io.Copy(buf, resp.Body) 523 | if err != nil { 524 | t.Errorf("Error while reading body: %s\n", err) 525 | } 526 | if buf.String() != "Hello from target HTTPS" { 527 | t.Errorf("Expected string 'Hello from target HTTPS' in response, but was %s\n", buf.String()) 528 | } 529 | }) 530 | 531 | fixture.tearDown(t) 532 | } 533 | 534 | func TestContentLengthLimit(t *testing.T) { 535 | maxContentLength := 8 536 | fixture := &testFixture{ 537 | configSetup: func(config *proxy.ProxyConfig, c *certutil.CertificateFixtures) { 538 | config.InsecureSkipCidrDenyList = true 539 | config.MaxResponseBodySize = uint32(maxContentLength) 540 | }, 541 | serversSetup: func(c *certutil.CertificateFixtures) []*http.Server { 542 | server := startLargeContentLengthServer(t) 543 | return []*http.Server{server} 544 | }, 545 | } 546 | 547 | client := fixture.setUp(t) 548 | 549 | t.Run("Max content length", func(t *testing.T) { 550 | resp, err := client.Get("http://localhost:12099/8") 551 | if err != nil { 552 | t.Errorf("Error in GET request to target server via proxy: %s\n", err) 553 | } 554 | if resp.StatusCode != 200 { 555 | t.Errorf("Expected status code 200, got %d\n", resp.StatusCode) 556 | } 557 | if resp.ContentLength != int64(maxContentLength) { 558 | t.Errorf("Expected Content-length: %d, found %d", maxContentLength, resp.ContentLength) 559 | } 560 | }) 561 | 562 | t.Run("Over max content length", func(t *testing.T) { 563 | resp, err := client.Get("http://localhost:12099/9") 564 | if err != nil { 565 | t.Errorf("Error in GET request to target server via proxy: %s\n", err) 566 | } 567 | if resp.StatusCode != 502 { 568 | t.Errorf("Expected status code 502, got %d\n", resp.StatusCode) 569 | } 570 | }) 571 | 572 | fixture.tearDown(t) 573 | } 574 | 575 | func TestChunkedResponseContentLengthLimit(t *testing.T) { 576 | maxContentLength := 8 * 1024 577 | fixture := &testFixture{ 578 | configSetup: func(config *proxy.ProxyConfig, c *certutil.CertificateFixtures) { 579 | config.InsecureSkipCidrDenyList = true 580 | config.MaxResponseBodySize = uint32(maxContentLength) 581 | }, 582 | serversSetup: func(c *certutil.CertificateFixtures) []*http.Server { 583 | server := startLargeContentLengthServer(t) 584 | return []*http.Server{server} 585 | }, 586 | } 587 | 588 | client := fixture.setUp(t) 589 | 590 | t.Run("Max content length", func(t *testing.T) { 591 | resp, err := client.Get("http://localhost:12099/8k") 592 | if err != nil { 593 | t.Fatalf("Error in GET request to target server via proxy: %s\n", err) 594 | } 595 | if resp.StatusCode != 200 { 596 | t.Fatalf("Expected status code 200, got %d\n", resp.StatusCode) 597 | } 598 | responseData, err := ioutil.ReadAll(resp.Body) 599 | if err != nil { 600 | t.Fatalf("Error reading response data: %s\n", err) 601 | } 602 | if len(responseData) != maxContentLength { 603 | t.Fatalf("Expected Content-length: %d, found %d", maxContentLength, len(responseData)) 604 | } 605 | }) 606 | 607 | // NOTE: this isn't a great test because if the proxy cuts off the response at a chunk 608 | // boundary, the client can parse it correctly, otherwise the parsing fails. In this particular 609 | // instance, it looks like the response is being cut off at a chunk boundary. 610 | t.Run("Over max content length", func(t *testing.T) { 611 | resp, err := client.Get("http://localhost:12099/oversize") 612 | if err != nil { 613 | t.Errorf("Error in GET request to target server via proxy: %s\n", err) 614 | } 615 | responseData, err := ioutil.ReadAll(resp.Body) 616 | if err != nil { 617 | t.Fatalf("Got error %s\n", err) 618 | } 619 | if len(responseData) != maxContentLength { 620 | t.Fatalf("Expected response length %d, got %d", maxContentLength, len(responseData)) 621 | } 622 | }) 623 | fixture.tearDown(t) 624 | } 625 | 626 | func TestRequestContentLength(t *testing.T) { 627 | headerChan := make(chan map[string][]string, 1) 628 | fixture := &testFixture{ 629 | configSetup: func(config *proxy.ProxyConfig, c *certutil.CertificateFixtures) { 630 | config.InsecureSkipCidrDenyList = true 631 | }, 632 | serversSetup: func(certificates *certutil.CertificateFixtures) []*http.Server { 633 | var servers []*http.Server 634 | httpServer := startTargetServer(t, headerChan) 635 | return append(servers, httpServer) 636 | }, 637 | } 638 | 639 | client := fixture.setUp(t) 640 | 641 | t.Run("Request with content length header is passed along to target", func(t *testing.T) { 642 | resp, err := client.Post(fmt.Sprintf("http://localhost:%s/target", httpTargetServerPort), "text/plain", strings.NewReader("Hello webhook sentry!")) 643 | if err != nil { 644 | t.Errorf("Error in GET request to target server via proxy: %s\n", err) 645 | } 646 | if resp.StatusCode != 200 { 647 | t.Errorf("Expected status code 200, got %d\n", resp.StatusCode) 648 | } 649 | headers := <-headerChan 650 | cl, ok := headers["Content-Length"] 651 | if !ok { 652 | t.Error("No Content-Length found in request headers") 653 | } 654 | 655 | expectedLen := len("Hello webhook sentry!") 656 | if cl[0] != strconv.Itoa(expectedLen) { 657 | t.Errorf("Expected Content-Length %d, but found %s", expectedLen, cl[0]) 658 | } 659 | }) 660 | 661 | t.Run("Request with Transfer-Encoding chunked is passed along to target", func(t *testing.T) { 662 | req, err := http.NewRequest("POST", fmt.Sprintf("http://localhost:%s/target", httpTargetServerPort), strings.NewReader("Hello webhook sentry!")) 663 | if err != nil { 664 | t.Fatal(err) 665 | } 666 | req.TransferEncoding = []string{"chunked"} 667 | resp, err := client.Do(req) 668 | if err != nil { 669 | t.Errorf("Error in GET request to target server via proxy: %s\n", err) 670 | } 671 | if resp.StatusCode != 200 { 672 | t.Errorf("Expected status code 200, got %d\n", resp.StatusCode) 673 | } 674 | headers := <-headerChan 675 | _, ok := headers["Content-Length"] 676 | if ok { 677 | t.Error("Expected absence of Content-Length in request headers, but was found") 678 | } 679 | 680 | te, ok := headers["Transfer-Encoding"] 681 | if !ok { 682 | t.Error("Expected Transfer-Encoding in request headers, but was not found") 683 | } 684 | if te[0] != "chunked" { 685 | t.Errorf("Expected Transfer-Encoding value 'chunked', but was %s", te[0]) 686 | } 687 | }) 688 | 689 | } 690 | 691 | func waitForStartup(t *testing.T, address string) { 692 | i := 0 693 | for { 694 | conn, err := net.Dial("tcp4", address) 695 | if err != nil { 696 | if i > 2 { 697 | t.Error("Proxy did not start up in time") 698 | break 699 | } else { 700 | time.Sleep(500 * time.Millisecond) 701 | i++ 702 | } 703 | } else { 704 | conn.Close() 705 | break 706 | } 707 | } 708 | } 709 | 710 | func startProxy(t *testing.T, p *proxy.ProxyConfig) *http.Server { 711 | proxy.SetupLogging(p) 712 | p.Listeners = make([]proxy.ListenerConfig, 1, 1) 713 | p.Listeners[0] = proxy.ListenerConfig{ 714 | Address: proxyHttpAddress, 715 | Type: proxy.HTTP, 716 | } 717 | proxy := proxy.CreateProxyServers(p)[0] 718 | go func() { 719 | listener, err := net.Listen("tcp4", p.Listeners[0].Address) 720 | if err != nil { 721 | t.Fatalf("Could not start proxy listener: %s\n", err) 722 | } 723 | proxy.Serve(listener) 724 | }() 725 | return proxy 726 | } 727 | 728 | func startTLSProxy(t *testing.T, p *proxy.ProxyConfig) *http.Server { 729 | proxy.SetupLogging(p) 730 | p.Listeners = make([]proxy.ListenerConfig, 1, 1) 731 | p.Listeners[0] = proxy.ListenerConfig{ 732 | Address: proxyHttpsAddress, 733 | Type: proxy.HTTP, 734 | CertFile: "certs/cert.pem", 735 | KeyFile: "certs/key.pem", 736 | } 737 | pServer := proxy.CreateProxyServers(p)[0] 738 | go func() { 739 | listener, err := net.Listen("tcp4", p.Listeners[0].Address) 740 | if err != nil { 741 | t.Fatalf("Could not start proxy listener: %s\n", err) 742 | } 743 | pServer.ServeTLS(listener, p.Listeners[0].CertFile, p.Listeners[0].KeyFile) 744 | }() 745 | return pServer 746 | } 747 | 748 | func startTLSProxyWithCert(t *testing.T, p *proxy.ProxyConfig, proxyCert *tls.Certificate) *http.Server { 749 | proxy.SetupLogging(p) 750 | p.Listeners = make([]proxy.ListenerConfig, 1, 1) 751 | p.Listeners[0] = proxy.ListenerConfig{ 752 | Address: proxyHttpsAddress, 753 | Type: proxy.HTTP, 754 | } 755 | pServer := proxy.CreateProxyServers(p)[0] 756 | go func() { 757 | config := &tls.Config{Certificates: []tls.Certificate{*proxyCert}} 758 | listener, err := tls.Listen("tcp4", p.Listeners[0].Address, config) 759 | if err != nil { 760 | t.Fatalf("Could not start proxy listener: %s\n", err) 761 | } 762 | pServer.Serve(listener) 763 | }() 764 | return pServer 765 | } 766 | 767 | func startTargetServer(t *testing.T, headerChan chan map[string][]string) *http.Server { 768 | serveMux := http.NewServeMux() 769 | serveMux.HandleFunc("/target", func(w http.ResponseWriter, r *http.Request) { 770 | if headerChan != nil { 771 | headers := r.Header.Clone() 772 | // Hack to add Transfer-Encoding, since the headers map doesn't seem to have it 773 | if r.TransferEncoding != nil { 774 | headers["Transfer-Encoding"] = r.TransferEncoding 775 | } 776 | headerChan <- headers 777 | } 778 | w.Header().Set("X-Custom-Header", "custom") 779 | fmt.Fprint(w, "Hello from target") 780 | }) 781 | 782 | server := &http.Server{ 783 | Addr: "127.0.0.1:" + httpTargetServerPort, 784 | Handler: serveMux, 785 | } 786 | go func() { 787 | if err := server.ListenAndServe(); err != http.ErrServerClosed { 788 | t.Fatalf("Failed to start target HTTP server: %s\n", err) 789 | } 790 | }() 791 | return server 792 | } 793 | 794 | func startTargetHTTPSServer(t *testing.T) *http.Server { 795 | return startTargetHTTPSServerWithCert(t, "certs/cert.pem", "certs/key.pem") 796 | } 797 | 798 | func startTargetHTTPSServerWithCert(t *testing.T, certFile string, keyFile string) *http.Server { 799 | serveMux := http.NewServeMux() 800 | serveMux.HandleFunc("/target", func(w http.ResponseWriter, r *http.Request) { 801 | fmt.Fprint(w, "Hello from target HTTPS") 802 | }) 803 | 804 | server := &http.Server{ 805 | Addr: "127.0.0.1:12081", 806 | Handler: serveMux, 807 | } 808 | go func() { 809 | if err := server.ListenAndServeTLS(certFile, keyFile); err != http.ErrServerClosed { 810 | t.Fatalf("HTTPS server failed to start: %s\n", err) 811 | } 812 | }() 813 | return server 814 | } 815 | 816 | func startTargetHTTPSServerWithInMemoryCert(t *testing.T, serverCert *tls.Certificate) *http.Server { 817 | serveMux := http.NewServeMux() 818 | serveMux.HandleFunc("/target", func(w http.ResponseWriter, r *http.Request) { 819 | fmt.Fprint(w, "Hello from target HTTPS") 820 | }) 821 | 822 | server := &http.Server{ 823 | Addr: "127.0.0.1:" + httpsTargetServerPort, 824 | Handler: serveMux, 825 | } 826 | go func() { 827 | config := &tls.Config{Certificates: []tls.Certificate{*serverCert}} 828 | listener, err := tls.Listen("tcp4", server.Addr, config) 829 | if err != nil { 830 | t.Fatalf("Failed to listen on port %s: %s\n", httpsTargetServerPort, err) 831 | } 832 | if err := server.Serve(listener); err != http.ErrServerClosed { 833 | t.Fatalf("HTTPS target server failed to start: %s\n", err) 834 | } 835 | }() 836 | return server 837 | } 838 | 839 | func startSlowToRespondServer(t *testing.T) { 840 | listener, err := net.Listen("tcp4", ":14400") 841 | if err != nil { 842 | t.Fatalf("Failed to start slow server: %s\n", err) 843 | } 844 | conn, err := listener.Accept() 845 | if err != nil { 846 | t.Fatalf("Failed to accept connection in slow server: %s\n", err) 847 | } 848 | defer conn.Close() 849 | time.Sleep(time.Second * 7) 850 | bufw := bufio.NewWriter(conn) 851 | bufw.WriteString("HTTP/1.1 200 OK\r\n") 852 | bufw.WriteString("Connection: Close\r\n") 853 | bufw.WriteString("\r\n") 854 | bufw.Flush() 855 | } 856 | 857 | func startNeverSendsBodyServer(t *testing.T) { 858 | listener, err := net.Listen("tcp4", ":14402") 859 | if err != nil { 860 | t.Fatalf("Failed to start never sends body server: %s\n", err) 861 | } 862 | conn, err := listener.Accept() 863 | if err != nil { 864 | t.Fatalf("Failed to accept connection in never sends body server: %s\n", err) 865 | } 866 | defer conn.Close() 867 | bufw := bufio.NewWriter(conn) 868 | bufw.WriteString("HTTP/1.1 200 OK\r\n") 869 | bufw.WriteString("Connection: Close\r\n") 870 | bufw.WriteString("Content-Length: 5\r\n") 871 | bufw.WriteString("\r\n") 872 | bufw.Flush() 873 | 874 | time.Sleep(time.Second * 5) 875 | bufw.WriteString("hello") 876 | bufw.Flush() 877 | } 878 | 879 | func startTargetHTTPSServerWithClientCertCheck(t *testing.T, serverCert *tls.Certificate, rootCAs *x509.CertPool) *http.Server { 880 | serveMux := http.NewServeMux() 881 | serveMux.HandleFunc("/target", func(w http.ResponseWriter, r *http.Request) { 882 | fmt.Fprint(w, "Hello from target HTTPS with client cert check") 883 | }) 884 | 885 | server := &http.Server{ 886 | Addr: "127.0.0.1:" + httpsTargetServerWithClientCertCheckPort, 887 | Handler: serveMux, 888 | } 889 | tlsConfig := &tls.Config{ 890 | Certificates: []tls.Certificate{*serverCert}, 891 | ClientAuth: tls.RequireAndVerifyClientCert, 892 | ClientCAs: rootCAs, 893 | } 894 | 895 | go func() { 896 | listener, err := tls.Listen("tcp4", "127.0.0.1:"+httpsTargetServerWithClientCertCheckPort, tlsConfig) 897 | if err != nil { 898 | t.Fatalf("Failed to listen on port %s: %s\n", httpsTargetServerWithClientCertCheckPort, err) 899 | } 900 | 901 | if err := server.Serve(listener); err != http.ErrServerClosed { 902 | t.Fatalf("HTTPS server failed to start: %s\n", err) 903 | } 904 | }() 905 | return server 906 | 907 | } 908 | 909 | func startLargeContentLengthServer(t *testing.T) *http.Server { 910 | serveMux := http.NewServeMux() 911 | baseStr := "eight ch" 912 | content := strings.Repeat(baseStr, 1024) 913 | serveMux.HandleFunc("/8", func(w http.ResponseWriter, r *http.Request) { 914 | fmt.Fprint(w, baseStr) 915 | }) 916 | 917 | serveMux.HandleFunc("/9", func(w http.ResponseWriter, r *http.Request) { 918 | fmt.Fprint(w, baseStr+"a") 919 | }) 920 | 921 | serveMux.HandleFunc("/8k", func(w http.ResponseWriter, r *http.Request) { 922 | w.Header().Set("X-Custom-Header", "oversize") 923 | fmt.Fprint(w, content) 924 | }) 925 | serveMux.HandleFunc("/oversize", func(w http.ResponseWriter, r *http.Request) { 926 | fmt.Fprint(w, content+"a") 927 | }) 928 | 929 | server := &http.Server{ 930 | Addr: "127.0.0.1:12099", 931 | Handler: serveMux, 932 | } 933 | go func() { 934 | server.ListenAndServe() 935 | }() 936 | return server 937 | } 938 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 7 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 8 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 9 | cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= 10 | cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= 11 | cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= 12 | cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= 13 | cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= 14 | cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= 15 | cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= 16 | cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= 17 | cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= 18 | cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= 19 | cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= 20 | cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= 21 | cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= 22 | cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= 23 | cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= 24 | cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= 25 | cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= 26 | cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= 27 | cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= 28 | cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= 29 | cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= 30 | cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= 31 | cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= 32 | cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= 33 | cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= 34 | cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= 35 | cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= 36 | cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= 37 | cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= 38 | cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= 39 | cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4= 40 | cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= 41 | cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ= 42 | cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk= 43 | cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o= 44 | cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s= 45 | cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0= 46 | cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= 47 | cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= 48 | cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI= 49 | cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= 50 | cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= 51 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 52 | cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= 53 | cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= 54 | cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= 55 | cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= 56 | cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= 57 | cloud.google.com/go/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA= 58 | cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY= 59 | cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s= 60 | cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM= 61 | cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI= 62 | cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY= 63 | cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI= 64 | cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= 65 | cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= 66 | cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= 67 | cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= 68 | cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= 69 | cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= 70 | cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= 71 | cloud.google.com/go/compute v1.12.0/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= 72 | cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= 73 | cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= 74 | cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= 75 | cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= 76 | cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= 77 | cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= 78 | cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs= 79 | cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc= 80 | cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM= 81 | cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= 82 | cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo= 83 | cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE= 84 | cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I= 85 | cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= 86 | cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo= 87 | cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= 88 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 89 | cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= 90 | cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo= 91 | cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ= 92 | cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4= 93 | cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0= 94 | cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8= 95 | cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU= 96 | cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU= 97 | cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y= 98 | cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= 99 | cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk= 100 | cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w= 101 | cloud.google.com/go/firestore v1.8.0/go.mod h1:r3KB8cAdRIe8znzoPWLw8S6gpDVd9treohhn8b09424= 102 | cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk= 103 | cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg= 104 | cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM= 105 | cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA= 106 | cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o= 107 | cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= 108 | cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0= 109 | cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= 110 | cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= 111 | cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= 112 | cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= 113 | cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= 114 | cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= 115 | cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= 116 | cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= 117 | cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= 118 | cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= 119 | cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM= 120 | cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY= 121 | cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s= 122 | cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= 123 | cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= 124 | cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ= 125 | cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= 126 | cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY= 127 | cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34= 128 | cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs= 129 | cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg= 130 | cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E= 131 | cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU= 132 | cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0= 133 | cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= 134 | cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0= 135 | cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= 136 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 137 | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= 138 | cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= 139 | cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= 140 | cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4= 141 | cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o= 142 | cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk= 143 | cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo= 144 | cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg= 145 | cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= 146 | cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg= 147 | cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c= 148 | cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y= 149 | cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A= 150 | cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4= 151 | cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY= 152 | cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= 153 | cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= 154 | cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= 155 | cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4= 156 | cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0= 157 | cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU= 158 | cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU= 159 | cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc= 160 | cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs= 161 | cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg= 162 | cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= 163 | cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= 164 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 165 | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= 166 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= 167 | cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= 168 | cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= 169 | cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= 170 | cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= 171 | cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= 172 | cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= 173 | cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= 174 | cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= 175 | cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4= 176 | cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0= 177 | cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= 178 | cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo= 179 | cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= 180 | cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= 181 | cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= 182 | cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= 183 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 184 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 185 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 186 | github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= 187 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 188 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 189 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 190 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 191 | github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 192 | github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= 193 | github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= 194 | github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= 195 | github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= 196 | github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= 197 | github.com/armon/go-metrics v0.4.0/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= 198 | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 199 | github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 200 | github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= 201 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 202 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 203 | github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 204 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 205 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 206 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 207 | github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= 208 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 209 | github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= 210 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 211 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 212 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 213 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 214 | github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= 215 | github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= 216 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 217 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 218 | github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 219 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 220 | github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= 221 | github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 222 | github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 223 | github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 224 | github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 225 | github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 226 | github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 227 | github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= 228 | github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 229 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 230 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 231 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 232 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 233 | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 234 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 235 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 236 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 237 | github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= 238 | github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 239 | github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 240 | github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= 241 | github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= 242 | github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= 243 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 244 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 245 | github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= 246 | github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= 247 | github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= 248 | github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= 249 | github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= 250 | github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= 251 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 252 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 253 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 254 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 255 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 256 | github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 257 | github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= 258 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 259 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 260 | github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= 261 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 262 | github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 263 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 264 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 265 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 266 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 267 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 268 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 269 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 270 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 271 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 272 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 273 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 274 | github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 275 | github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 276 | github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 277 | github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= 278 | github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= 279 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 280 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 281 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 282 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 283 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 284 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 285 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 286 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 287 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 288 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 289 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 290 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 291 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 292 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 293 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 294 | github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= 295 | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 296 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 297 | github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 298 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 299 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 300 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 301 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 302 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 303 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 304 | github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 305 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 306 | github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 307 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 308 | github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 309 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 310 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 311 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 312 | github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= 313 | github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 314 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 315 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 316 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 317 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 318 | github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 319 | github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 320 | github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= 321 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 322 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 323 | github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 324 | github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 325 | github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 326 | github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 327 | github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 328 | github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 329 | github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 330 | github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 331 | github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 332 | github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 333 | github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 334 | github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 335 | github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 336 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 337 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 338 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 339 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 340 | github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= 341 | github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= 342 | github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= 343 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 344 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 345 | github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= 346 | github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= 347 | github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= 348 | github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= 349 | github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= 350 | github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= 351 | github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= 352 | github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= 353 | github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= 354 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 355 | github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= 356 | github.com/hashicorp/consul/api v1.15.3/go.mod h1:/g/qgcoBcEXALCNZgRRisyTW0nY86++L0KbeAMXYCeY= 357 | github.com/hashicorp/consul/sdk v0.11.0/go.mod h1:yPkX5Q6CsxTFMjQQDJwzeNmUUF5NUGGbrDsv9wTb8cw= 358 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 359 | github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 360 | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 361 | github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= 362 | github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= 363 | github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= 364 | github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= 365 | github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 366 | github.com/hashicorp/go-immutable-radix v1.3.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 367 | github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 368 | github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= 369 | github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= 370 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 371 | github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= 372 | github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= 373 | github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= 374 | github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= 375 | github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= 376 | github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= 377 | github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= 378 | github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 379 | github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 380 | github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 381 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 382 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 383 | github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= 384 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 385 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 386 | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= 387 | github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= 388 | github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= 389 | github.com/hashicorp/memberlist v0.3.1/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= 390 | github.com/hashicorp/serf v0.9.7/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= 391 | github.com/hashicorp/serf v0.9.8/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= 392 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 393 | github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 394 | github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= 395 | github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 396 | github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= 397 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 398 | github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 399 | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 400 | github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 401 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 402 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 403 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 404 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 405 | github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= 406 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 407 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 408 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 409 | github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= 410 | github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 411 | github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= 412 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 413 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 414 | github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 415 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 416 | github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= 417 | github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= 418 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 419 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 420 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 421 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 422 | github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= 423 | github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= 424 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 425 | github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 426 | github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 427 | github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 428 | github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= 429 | github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 430 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 431 | github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= 432 | github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= 433 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 434 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 435 | github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= 436 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 437 | github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= 438 | github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= 439 | github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= 440 | github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= 441 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 442 | github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= 443 | github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 444 | github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 445 | github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= 446 | github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 447 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 448 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 449 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 450 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 451 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 452 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 453 | github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 454 | github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 455 | github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 456 | github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= 457 | github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= 458 | github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= 459 | github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= 460 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 461 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 462 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 463 | github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= 464 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 465 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 466 | github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= 467 | github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= 468 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 469 | github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= 470 | github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= 471 | github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= 472 | github.com/prometheus/client_golang v1.11.1 h1:+4eQaD7vAZ6DsfsxB15hbE0odUjGI5ARs9yskGu1v4s= 473 | github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= 474 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 475 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 476 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 477 | github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= 478 | github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 479 | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 480 | github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= 481 | github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= 482 | github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ= 483 | github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= 484 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 485 | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 486 | github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= 487 | github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= 488 | github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= 489 | github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= 490 | github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= 491 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 492 | github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= 493 | github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= 494 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 495 | github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= 496 | github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= 497 | github.com/sagikazarmark/crypt v0.8.0/go.mod h1:TmKwZAo97S4Fy4sfMH/HX/cQP5D+ijra2NyLpNNmttY= 498 | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= 499 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 500 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 501 | github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= 502 | github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= 503 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 504 | github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw= 505 | github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= 506 | github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= 507 | github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= 508 | github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= 509 | github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= 510 | github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= 511 | github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= 512 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 513 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 514 | github.com/spf13/viper v1.14.0 h1:Rg7d3Lo706X9tHsJMUjdiwMpHB7W8WnSVOssIY+JElU= 515 | github.com/spf13/viper v1.14.0/go.mod h1:WT//axPky3FdvXHzGw33dNdXXXfFQqmEalje+egj8As= 516 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 517 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 518 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 519 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 520 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 521 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 522 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 523 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 524 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 525 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 526 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 527 | github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 528 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 529 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 530 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 531 | github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= 532 | github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= 533 | github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= 534 | github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 535 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 536 | github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 537 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 538 | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 539 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 540 | go.etcd.io/etcd/api/v3 v3.5.5/go.mod h1:KFtNaxGDw4Yx/BA4iPPwevUTAuqcsPxzyX8PHydchN8= 541 | go.etcd.io/etcd/client/pkg/v3 v3.5.5/go.mod h1:ggrwbk069qxpKPq8/FKkQ3Xq9y39kbFR4LnKszpRXeQ= 542 | go.etcd.io/etcd/client/v2 v2.305.5/go.mod h1:zQjKllfqfBVyVStbt4FaosoX2iYd8fV/GRy/PbowgP4= 543 | go.etcd.io/etcd/client/v3 v3.5.5/go.mod h1:aApjR4WGlSumpnJ2kloS75h6aHUmAyaPLjHMxpc7E7c= 544 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 545 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 546 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 547 | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 548 | go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 549 | go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= 550 | go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= 551 | go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= 552 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 553 | go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 554 | go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= 555 | go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= 556 | go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= 557 | go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= 558 | go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= 559 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 560 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 561 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 562 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 563 | golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= 564 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 565 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 566 | golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= 567 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 568 | golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 569 | golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 570 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 571 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 572 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 573 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 574 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 575 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 576 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 577 | golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 578 | golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= 579 | golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= 580 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 581 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 582 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 583 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 584 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 585 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 586 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 587 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 588 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 589 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= 590 | golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 591 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 592 | golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 593 | golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 594 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 595 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 596 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 597 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 598 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 599 | golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 600 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 601 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 602 | golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 603 | golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 604 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 605 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 606 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 607 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 608 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 609 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 610 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 611 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 612 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 613 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 614 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 615 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 616 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 617 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 618 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 619 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 620 | golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 621 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 622 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 623 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 624 | golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 625 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 626 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 627 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 628 | golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 629 | golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 630 | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 631 | golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 632 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 633 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 634 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 635 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 636 | golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 637 | golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 638 | golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 639 | golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 640 | golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 641 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 642 | golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= 643 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 644 | golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= 645 | golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 646 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 647 | golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 648 | golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 649 | golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 650 | golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 651 | golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 652 | golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 653 | golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 654 | golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 655 | golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 656 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 657 | golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= 658 | golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= 659 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 660 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 661 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 662 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 663 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 664 | golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 665 | golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 666 | golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 667 | golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 668 | golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 669 | golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 670 | golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 671 | golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 672 | golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 673 | golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 674 | golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 675 | golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= 676 | golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= 677 | golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= 678 | golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= 679 | golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= 680 | golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= 681 | golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= 682 | golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= 683 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 684 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 685 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 686 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 687 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 688 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 689 | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 690 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 691 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 692 | golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 693 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 694 | golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 695 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 696 | golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 697 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 698 | golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 699 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 700 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 701 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 702 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 703 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 704 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 705 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 706 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 707 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 708 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 709 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 710 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 711 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 712 | golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 713 | golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 714 | golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 715 | golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 716 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 717 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 718 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 719 | golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 720 | golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 721 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 722 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 723 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 724 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 725 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 726 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 727 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 728 | golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 729 | golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 730 | golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 731 | golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 732 | golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 733 | golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 734 | golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 735 | golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 736 | golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 737 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 738 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 739 | golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 740 | golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 741 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 742 | golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 743 | golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 744 | golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 745 | golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 746 | golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 747 | golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 748 | golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 749 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 750 | golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 751 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 752 | golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 753 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 754 | golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 755 | golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 756 | golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 757 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 758 | golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 759 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 760 | golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 761 | golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 762 | golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 763 | golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 764 | golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 765 | golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 766 | golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 767 | golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 768 | golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 769 | golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 770 | golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 771 | golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 772 | golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 773 | golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 774 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 775 | golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 776 | golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 777 | golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 778 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 779 | golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 780 | golang.org/x/sys v0.0.0-20220908164124-27713097b956 h1:XeJjHH1KiLpKGb6lvMiksZ9l0fVUh+AmGcm0nOMEBOY= 781 | golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 782 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 783 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 784 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 785 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 786 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 787 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 788 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 789 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 790 | golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 791 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 792 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 793 | golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= 794 | golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 795 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 796 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 797 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 798 | golang.org/x/time v0.0.0-20220609170525-579cf78fd858/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 799 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 800 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 801 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 802 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 803 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 804 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 805 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 806 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 807 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 808 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 809 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 810 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 811 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 812 | golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 813 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 814 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 815 | golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 816 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 817 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 818 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 819 | golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 820 | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 821 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 822 | golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 823 | golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 824 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 825 | golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 826 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 827 | golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 828 | golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 829 | golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 830 | golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 831 | golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 832 | golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= 833 | golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 834 | golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 835 | golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 836 | golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 837 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 838 | golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 839 | golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 840 | golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 841 | golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= 842 | golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 843 | golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 844 | golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 845 | golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 846 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 847 | golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 848 | golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= 849 | golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 850 | golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 851 | golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 852 | golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 853 | golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 854 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 855 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 856 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 857 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 858 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 859 | golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 860 | golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= 861 | golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= 862 | golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= 863 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 864 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 865 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 866 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 867 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 868 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 869 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 870 | google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 871 | google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 872 | google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 873 | google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 874 | google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 875 | google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 876 | google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 877 | google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= 878 | google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= 879 | google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= 880 | google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= 881 | google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= 882 | google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= 883 | google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= 884 | google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= 885 | google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= 886 | google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= 887 | google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= 888 | google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= 889 | google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= 890 | google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= 891 | google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= 892 | google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= 893 | google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= 894 | google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= 895 | google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= 896 | google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= 897 | google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= 898 | google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= 899 | google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= 900 | google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= 901 | google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= 902 | google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= 903 | google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g= 904 | google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= 905 | google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= 906 | google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI= 907 | google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= 908 | google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= 909 | google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= 910 | google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= 911 | google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= 912 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 913 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 914 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 915 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 916 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 917 | google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 918 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 919 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 920 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 921 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 922 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 923 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 924 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 925 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 926 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 927 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 928 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 929 | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 930 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 931 | google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 932 | google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 933 | google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= 934 | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 935 | google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 936 | google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 937 | google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 938 | google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 939 | google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 940 | google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 941 | google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 942 | google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 943 | google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= 944 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 945 | google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= 946 | google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 947 | google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 948 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 949 | google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 950 | google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 951 | google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 952 | google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 953 | google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 954 | google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 955 | google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 956 | google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 957 | google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 958 | google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 959 | google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 960 | google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= 961 | google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= 962 | google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= 963 | google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= 964 | google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= 965 | google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= 966 | google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= 967 | google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= 968 | google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= 969 | google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= 970 | google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= 971 | google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= 972 | google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= 973 | google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= 974 | google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= 975 | google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= 976 | google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= 977 | google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= 978 | google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= 979 | google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= 980 | google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= 981 | google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= 982 | google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= 983 | google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= 984 | google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= 985 | google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= 986 | google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= 987 | google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= 988 | google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= 989 | google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= 990 | google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= 991 | google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= 992 | google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= 993 | google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= 994 | google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= 995 | google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= 996 | google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= 997 | google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= 998 | google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= 999 | google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= 1000 | google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= 1001 | google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= 1002 | google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= 1003 | google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= 1004 | google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= 1005 | google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= 1006 | google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= 1007 | google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= 1008 | google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= 1009 | google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= 1010 | google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= 1011 | google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= 1012 | google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= 1013 | google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= 1014 | google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= 1015 | google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw= 1016 | google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= 1017 | google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= 1018 | google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= 1019 | google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= 1020 | google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= 1021 | google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= 1022 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 1023 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 1024 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 1025 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 1026 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 1027 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 1028 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 1029 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 1030 | google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= 1031 | google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= 1032 | google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 1033 | google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 1034 | google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 1035 | google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= 1036 | google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= 1037 | google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= 1038 | google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 1039 | google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 1040 | google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 1041 | google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= 1042 | google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= 1043 | google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= 1044 | google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= 1045 | google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= 1046 | google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= 1047 | google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= 1048 | google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= 1049 | google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= 1050 | google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= 1051 | google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= 1052 | google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= 1053 | google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= 1054 | google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= 1055 | google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= 1056 | google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= 1057 | google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= 1058 | google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= 1059 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 1060 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 1061 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 1062 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 1063 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 1064 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 1065 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 1066 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 1067 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 1068 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 1069 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 1070 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 1071 | google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 1072 | google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 1073 | google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= 1074 | google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 1075 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 1076 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 1077 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 1078 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= 1079 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 1080 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 1081 | gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= 1082 | gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 1083 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 1084 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 1085 | gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 1086 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 1087 | gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 1088 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 1089 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 1090 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 1091 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 1092 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 1093 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 1094 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 1095 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 1096 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 1097 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 1098 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 1099 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 1100 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 1101 | honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 1102 | honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 1103 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 1104 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= 1105 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 1106 | sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= 1107 | --------------------------------------------------------------------------------