mirror of https://github.com/k3s-io/k3s
Merge pull request #49353 from liggitt/aggregator-tls
Automatic merge from submit-queue Use specified ServerName in aggregator TLS validation Fixes #49354 The aggregator sets a ServerName in the proxier tlsConfig, but the code path handling websocket upgrade requests did not honor it, and instead tried to verify TLS using the dialed host * Honors ServerName if already set in tls.Config * Adds unit tests for upgrade functionality via the aggregator * Fixes mutation of shared tlsConfig.ServerName in spdy roundtripper ```release-note Websocket requests to aggregated APIs now perform TLS verification using the service DNS name instead of the backend server's IP address, consistent with non-websocket requests. ```pull/6/head
commit
971c247c0a
|
@ -158,15 +158,16 @@ func (s *SpdyRoundTripper) dial(req *http.Request) (net.Conn, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.tlsConfig == nil {
|
tlsConfig := s.tlsConfig
|
||||||
s.tlsConfig = &tls.Config{}
|
switch {
|
||||||
|
case tlsConfig == nil:
|
||||||
|
tlsConfig = &tls.Config{ServerName: host}
|
||||||
|
case len(tlsConfig.ServerName) == 0:
|
||||||
|
tlsConfig = tlsConfig.Clone()
|
||||||
|
tlsConfig.ServerName = host
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(s.tlsConfig.ServerName) == 0 {
|
tlsConn := tls.Client(rwc, tlsConfig)
|
||||||
s.tlsConfig.ServerName = host
|
|
||||||
}
|
|
||||||
|
|
||||||
tlsConn := tls.Client(rwc, s.tlsConfig)
|
|
||||||
|
|
||||||
// need to manually call Handshake() so we can call VerifyHostname() below
|
// need to manually call Handshake() so we can call VerifyHostname() below
|
||||||
if err := tlsConn.Handshake(); err != nil {
|
if err := tlsConn.Handshake(); err != nil {
|
||||||
|
@ -174,11 +175,11 @@ func (s *SpdyRoundTripper) dial(req *http.Request) (net.Conn, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return if we were configured to skip validation
|
// Return if we were configured to skip validation
|
||||||
if s.tlsConfig != nil && s.tlsConfig.InsecureSkipVerify {
|
if tlsConfig.InsecureSkipVerify {
|
||||||
return tlsConn, nil
|
return tlsConn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := tlsConn.VerifyHostname(host); err != nil {
|
if err := tlsConn.VerifyHostname(tlsConfig.ServerName); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,6 +219,9 @@ func (s *SpdyRoundTripper) dialWithoutProxy(url *url.URL) (net.Conn, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if s.tlsConfig != nil && len(s.tlsConfig.ServerName) > 0 {
|
||||||
|
host = s.tlsConfig.ServerName
|
||||||
|
}
|
||||||
err = conn.VerifyHostname(host)
|
err = conn.VerifyHostname(host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -94,6 +94,9 @@ func DialURL(url *url.URL, transport http.RoundTripper) (net.Conn, error) {
|
||||||
|
|
||||||
// Verify
|
// Verify
|
||||||
host, _, _ := net.SplitHostPort(dialAddr)
|
host, _, _ := net.SplitHostPort(dialAddr)
|
||||||
|
if tlsConfig != nil && len(tlsConfig.ServerName) > 0 {
|
||||||
|
host = tlsConfig.ServerName
|
||||||
|
}
|
||||||
if err := tlsConn.VerifyHostname(host); err != nil {
|
if err := tlsConn.VerifyHostname(host); err != nil {
|
||||||
tlsConn.Close()
|
tlsConn.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -17,6 +17,7 @@ go_test(
|
||||||
library = ":go_default_library",
|
library = ":go_default_library",
|
||||||
tags = ["automanaged"],
|
tags = ["automanaged"],
|
||||||
deps = [
|
deps = [
|
||||||
|
"//vendor/golang.org/x/net/websocket:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
|
|
|
@ -17,21 +17,23 @@ limitations under the License.
|
||||||
package apiserver
|
package apiserver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/tls"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
|
"net/url"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"golang.org/x/net/websocket"
|
||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
"k8s.io/apiserver/pkg/authentication/user"
|
"k8s.io/apiserver/pkg/authentication/user"
|
||||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||||
|
|
||||||
"k8s.io/kube-aggregator/pkg/apis/apiregistration"
|
"k8s.io/kube-aggregator/pkg/apis/apiregistration"
|
||||||
"net/url"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type targetHTTPHandler struct {
|
type targetHTTPHandler struct {
|
||||||
|
@ -92,7 +94,13 @@ func (r *mockedRouter) ResolveEndpoint(namespace, name string) (*url.URL, error)
|
||||||
|
|
||||||
func TestProxyHandler(t *testing.T) {
|
func TestProxyHandler(t *testing.T) {
|
||||||
target := &targetHTTPHandler{}
|
target := &targetHTTPHandler{}
|
||||||
targetServer := httptest.NewTLSServer(target)
|
targetServer := httptest.NewUnstartedServer(target)
|
||||||
|
if cert, err := tls.X509KeyPair(svcCrt, svcKey); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else {
|
||||||
|
targetServer.TLS = &tls.Config{Certificates: []tls.Certificate{cert}}
|
||||||
|
}
|
||||||
|
targetServer.StartTLS()
|
||||||
defer targetServer.Close()
|
defer targetServer.Close()
|
||||||
|
|
||||||
tests := map[string]struct {
|
tests := map[string]struct {
|
||||||
|
@ -120,7 +128,7 @@ func TestProxyHandler(t *testing.T) {
|
||||||
expectedStatusCode: http.StatusInternalServerError,
|
expectedStatusCode: http.StatusInternalServerError,
|
||||||
expectedBody: "missing user",
|
expectedBody: "missing user",
|
||||||
},
|
},
|
||||||
"proxy with user": {
|
"proxy with user, insecure": {
|
||||||
user: &user.DefaultInfo{
|
user: &user.DefaultInfo{
|
||||||
Name: "username",
|
Name: "username",
|
||||||
Groups: []string{"one", "two"},
|
Groups: []string{"one", "two"},
|
||||||
|
@ -147,6 +155,33 @@ func TestProxyHandler(t *testing.T) {
|
||||||
"X-Remote-Group": {"one", "two"},
|
"X-Remote-Group": {"one", "two"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"proxy with user, cabundle": {
|
||||||
|
user: &user.DefaultInfo{
|
||||||
|
Name: "username",
|
||||||
|
Groups: []string{"one", "two"},
|
||||||
|
},
|
||||||
|
path: "/request/path",
|
||||||
|
apiService: &apiregistration.APIService{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "v1.foo"},
|
||||||
|
Spec: apiregistration.APIServiceSpec{
|
||||||
|
Service: &apiregistration.ServiceReference{Name: "test-service", Namespace: "test-ns"},
|
||||||
|
Group: "foo",
|
||||||
|
Version: "v1",
|
||||||
|
CABundle: testCACrt,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedStatusCode: http.StatusOK,
|
||||||
|
expectedCalled: true,
|
||||||
|
expectedHeaders: map[string][]string{
|
||||||
|
"X-Forwarded-Proto": {"https"},
|
||||||
|
"X-Forwarded-Uri": {"/request/path"},
|
||||||
|
"X-Forwarded-For": {"127.0.0.1"},
|
||||||
|
"X-Remote-User": {"username"},
|
||||||
|
"User-Agent": {"Go-http-client/1.1"},
|
||||||
|
"Accept-Encoding": {"gzip"},
|
||||||
|
"X-Remote-Group": {"one", "two"},
|
||||||
|
},
|
||||||
|
},
|
||||||
"fail on bad serving cert": {
|
"fail on bad serving cert": {
|
||||||
user: &user.DefaultInfo{
|
user: &user.DefaultInfo{
|
||||||
Name: "username",
|
Name: "username",
|
||||||
|
@ -218,3 +253,188 @@ func TestProxyHandler(t *testing.T) {
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestProxyUpgrade(t *testing.T) {
|
||||||
|
testcases := map[string]struct {
|
||||||
|
APIService *apiregistration.APIService
|
||||||
|
ExpectError bool
|
||||||
|
ExpectCalled bool
|
||||||
|
}{
|
||||||
|
"valid hostname + CABundle": {
|
||||||
|
APIService: &apiregistration.APIService{
|
||||||
|
Spec: apiregistration.APIServiceSpec{
|
||||||
|
CABundle: testCACrt,
|
||||||
|
Group: "mygroup",
|
||||||
|
Version: "v1",
|
||||||
|
Service: &apiregistration.ServiceReference{Name: "test-service", Namespace: "test-ns"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ExpectError: false,
|
||||||
|
ExpectCalled: true,
|
||||||
|
},
|
||||||
|
"invalid hostname + insecure": {
|
||||||
|
APIService: &apiregistration.APIService{
|
||||||
|
Spec: apiregistration.APIServiceSpec{
|
||||||
|
InsecureSkipTLSVerify: true,
|
||||||
|
Group: "mygroup",
|
||||||
|
Version: "v1",
|
||||||
|
Service: &apiregistration.ServiceReference{Name: "invalid-service", Namespace: "invalid-ns"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ExpectError: false,
|
||||||
|
ExpectCalled: true,
|
||||||
|
},
|
||||||
|
"invalid hostname + CABundle": {
|
||||||
|
APIService: &apiregistration.APIService{
|
||||||
|
Spec: apiregistration.APIServiceSpec{
|
||||||
|
CABundle: testCACrt,
|
||||||
|
Group: "mygroup",
|
||||||
|
Version: "v1",
|
||||||
|
Service: &apiregistration.ServiceReference{Name: "invalid-service", Namespace: "invalid-ns"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ExpectError: true,
|
||||||
|
ExpectCalled: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, tc := range testcases {
|
||||||
|
tcName := k
|
||||||
|
path := "/apis/" + tc.APIService.Spec.Group + "/" + tc.APIService.Spec.Version + "/foo"
|
||||||
|
called := false
|
||||||
|
|
||||||
|
func() { // Cleanup after each test case.
|
||||||
|
backendHandler := http.NewServeMux()
|
||||||
|
backendHandler.Handle(path, websocket.Handler(func(ws *websocket.Conn) {
|
||||||
|
defer ws.Close()
|
||||||
|
body := make([]byte, 5)
|
||||||
|
ws.Read(body)
|
||||||
|
ws.Write([]byte("hello " + string(body)))
|
||||||
|
called = true
|
||||||
|
}))
|
||||||
|
|
||||||
|
backendServer := httptest.NewUnstartedServer(backendHandler)
|
||||||
|
if cert, err := tls.X509KeyPair(svcCrt, svcKey); err != nil {
|
||||||
|
t.Errorf("https (valid hostname): %v", err)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
backendServer.TLS = &tls.Config{Certificates: []tls.Certificate{cert}}
|
||||||
|
}
|
||||||
|
backendServer.StartTLS()
|
||||||
|
defer backendServer.Close()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if called != tc.ExpectCalled {
|
||||||
|
t.Errorf("%s: expected called=%v, got %v", tcName, tc.ExpectCalled, called)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
serverURL, _ := url.Parse(backendServer.URL)
|
||||||
|
proxyHandler := &proxyHandler{
|
||||||
|
contextMapper: &fakeRequestContextMapper{user: &user.DefaultInfo{Name: "username"}},
|
||||||
|
serviceResolver: &mockedRouter{destinationHost: serverURL.Host},
|
||||||
|
proxyTransport: &http.Transport{},
|
||||||
|
}
|
||||||
|
proxyHandler.updateAPIService(tc.APIService)
|
||||||
|
aggregator := httptest.NewServer(proxyHandler)
|
||||||
|
defer aggregator.Close()
|
||||||
|
|
||||||
|
ws, err := websocket.Dial("ws://"+aggregator.Listener.Addr().String()+path, "", "http://127.0.0.1/")
|
||||||
|
if err != nil {
|
||||||
|
if !tc.ExpectError {
|
||||||
|
t.Errorf("%s: websocket dial err: %s", tcName, err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer ws.Close()
|
||||||
|
if tc.ExpectError {
|
||||||
|
t.Errorf("%s: expected websocket error, got none", tcName)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := ws.Write([]byte("world")); err != nil {
|
||||||
|
t.Errorf("%s: write err: %s", tcName, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
response := make([]byte, 20)
|
||||||
|
n, err := ws.Read(response)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%s: read err: %s", tcName, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if e, a := "hello world", string(response[0:n]); e != a {
|
||||||
|
t.Errorf("%s: expected '%#v', got '%#v'", tcName, e, a)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var testCACrt = []byte(`-----BEGIN CERTIFICATE-----
|
||||||
|
MIICxDCCAaygAwIBAgIBATANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDEwd0ZXN0
|
||||||
|
LWNhMCAXDTE3MDcyMDIxMTc1MloYDzIxMTcwNjI2MjExNzUzWjASMRAwDgYDVQQD
|
||||||
|
Ewd0ZXN0LWNhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuv/sT2xH
|
||||||
|
VS1/uXVNAEIwvEb2yTMbXwP6FD38LWkc37Ri7YMB9xiXEDBrbr6K1JThsqyitBxU
|
||||||
|
22QIl53LUm6I7c/vej1tdYtE2rDVuviiiRgy6omR8imVSv9vU024rgDe+nC9zTT1
|
||||||
|
3aNKR03olCG6fkygdcZOghzlyQLhyh8LG75XdnLNksnakum2dNxQ5QIFmBKAuev3
|
||||||
|
A069oRMNjudot+t/nFP9UDZ8dL80PNTNPF22bPsnxiau7KLZ4I0Lf7gt6yHlNcue
|
||||||
|
Fd5sqzqsw/LUFJR5Xuo1+0e7NV3SwCH5CymG6hkboM4Rf5S3EDDyXTxPbXzbQHf1
|
||||||
|
7ksW6gjAxh4x/wIDAQABoyMwITAOBgNVHQ8BAf8EBAMCAqQwDwYDVR0TAQH/BAUw
|
||||||
|
AwEB/zANBgkqhkiG9w0BAQsFAAOCAQEATgmDrW1BjFp+Vmw6T+ojVK4lJuIoerGw
|
||||||
|
TCCqabHs6O1iWkNi5KsY6vV86tofBIEXsf6S3mV2jcBn87+CIbNHlHFKrXwmcydA
|
||||||
|
WOc0LWVqqoeqIvEcMNoWQskzmOOUDTanX9mXkirm8d8BljC351TH17rSjLGzFuNh
|
||||||
|
Cy48xyKFM7kPauNZGfCyaZsGbNJP3Keeu35dOLZMDdBJw7ZvYEUqX7MLOO+d7vlO
|
||||||
|
JGNA5jsU2uBnSo6qsjxfsbGemk2uRO0nLhplWurw+4qzA79D0lKNLtH9yTn12KZb
|
||||||
|
/kUpsOSCtLomjWwp67lQyA/yFXf897pSKMXbnIfZfIlDg51CI3U2Sw==
|
||||||
|
-----END CERTIFICATE-----`)
|
||||||
|
|
||||||
|
// valid for hostname test-service.test-ns.svc
|
||||||
|
// signed by testCACrt
|
||||||
|
var svcCrt = []byte(`-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDDDCCAfSgAwIBAgIBBDANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDEwd0ZXN0
|
||||||
|
LWNhMCAXDTE3MDcyMDIxMjAzN1oYDzIxMTcwNjI2MjEyMDM4WjAjMSEwHwYDVQQD
|
||||||
|
Exh0ZXN0LXNlcnZpY2UudGVzdC1ucy5zdmMwggEiMA0GCSqGSIb3DQEBAQUAA4IB
|
||||||
|
DwAwggEKAoIBAQDOKgoTmlVeDhImiBLBccxdniKkS+FZSaoAEtoTvJG1wjk0ewzF
|
||||||
|
vKhjbHolJ+/qEANiQ6CpTz4hU3m/Iad6IrnmKd1jnkh9yKEaU32B2Xbh6VaV7Sca
|
||||||
|
Hv4cKWTe50sBvufZinTT8hlFcGufFlJIOLXya5t6HH1Ld7Xf2qwNqusHdmFlJko7
|
||||||
|
0By8jhTtD7+2OAJsIPQDWfAsXxFa6LeQ/lqS2DCFnp45DirTNetXoIH8ZJvTBjak
|
||||||
|
bQuAAA3H+61gRm1blIu8/JjHYTDOcUe5pFyrFLFPgA+eIcpIbzTD61UTNhVlusV2
|
||||||
|
eRrBr5BlRM13Zj6ZMcWp0Iiw5QI/W9QU7O4jAgMBAAGjWjBYMA4GA1UdDwEB/wQE
|
||||||
|
AwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMCMGA1UdEQQc
|
||||||
|
MBqCGHRlc3Qtc2VydmljZS50ZXN0LW5zLnN2YzANBgkqhkiG9w0BAQsFAAOCAQEA
|
||||||
|
kpULlml6Ct0cjOuHgDKUnTboFTUm2FHJY27p4NXUvXUMoipg/eSxk0r5JynzXrPa
|
||||||
|
jaJfY2bC45ixLjJv9irp9ER/lGYUcBQ8OHouXy+uKtA5YKHI3wYf8ITZrQwzRpsK
|
||||||
|
C5v7qDW+iyb9dn4T6qgpZvylKtkH5cH31hQooNgjZd5aEq3JSFnCM12IVqs/5kjL
|
||||||
|
NnbPXzeBQ9CHbC+mx7Mm6eSQVtGcQOy4yXFrh2/vrIB2t4gNeWaI1b+7l4MaJjV/
|
||||||
|
kRrOirhZaJ90ow/PdYrILtEAdpeC/2Varpr3l4rIKhkkop4gfPwaFeWhG38shH3E
|
||||||
|
eG5PW2waPpxJzEnGBoAWxQ==
|
||||||
|
-----END CERTIFICATE-----`)
|
||||||
|
|
||||||
|
var svcKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEogIBAAKCAQEAzioKE5pVXg4SJogSwXHMXZ4ipEvhWUmqABLaE7yRtcI5NHsM
|
||||||
|
xbyoY2x6JSfv6hADYkOgqU8+IVN5vyGneiK55indY55IfcihGlN9gdl24elWle0n
|
||||||
|
Gh7+HClk3udLAb7n2Yp00/IZRXBrnxZSSDi18mubehx9S3e139qsDarrB3ZhZSZK
|
||||||
|
O9AcvI4U7Q+/tjgCbCD0A1nwLF8RWui3kP5aktgwhZ6eOQ4q0zXrV6CB/GSb0wY2
|
||||||
|
pG0LgAANx/utYEZtW5SLvPyYx2EwznFHuaRcqxSxT4APniHKSG80w+tVEzYVZbrF
|
||||||
|
dnkawa+QZUTNd2Y+mTHFqdCIsOUCP1vUFOzuIwIDAQABAoIBABiX9z/DZ2+i6hNi
|
||||||
|
pCojcyev154V1zoZiYgct5snIZK3Kq/SBgIIsWW66Q9Jplsbseuk+aN46oZ7OMjO
|
||||||
|
MPZm8ho84EYj+a3XozBKyWwWDxKADW4xLjr1e4bMgVX97Xq11V6kH6+w78bS1GPT
|
||||||
|
+9jVuw7CO3fjsiawjye3JFM1Enh/NeRLEpT/oaQoWIV8b0IQB0VyqrdxWOO0rQhd
|
||||||
|
xA5w39tAZPDQ79MbMQyNWtPgBy0FuulP0GB12PrEbE+SXxsFhWViEwdB5Qx6Gqsx
|
||||||
|
KGn9vB1oaeSuuKIAjyBV0rXszrGektorDchsOY9UQi1mQsPSvvRFTM9T3qqSFIpu
|
||||||
|
oPNQLvECgYEA3ox3WJGjEve6VI4RMRt0l6ZFswNbNaHcTMPVsayqsl9KfebG+uyn
|
||||||
|
Z7TyyoCRzZZQa+3Z9jjW3hAGM9e7MG8jkeHbZpJpZv9X7eB3dgq3eZ1Zt5dyoDrU
|
||||||
|
PTdIPA2efFAf6V1ejyqH9h6RPQMeAb4uFU9nbI4rPagMxRdp5qIveIUCgYEA7Scb
|
||||||
|
0zWplDit4EUo+Fq80wzItwJZv64my8KIkEPpW3Fu6UPQvY74qyhE2fCSCwHqRpYJ
|
||||||
|
jVylyE0GIMx42kjwBgOpi4yEg8M3uMTal+Iy9SgrxZ5cPetaFpEF3Wk7/tz6ppr+
|
||||||
|
wnZQTO2WH3YLzv7JIWVrOKuBNVfNEbguVFWw4IcCgYB54mp2uoSancySBKDLyWKo
|
||||||
|
r6raqQrqK7TQ4iyGO6/dMy1EGQF/ad8hgEu8tn+kHh/7jG/kVyruwc3z1MIze5r6
|
||||||
|
ib00xxktDMnmgRpMLwBffdsmHq7rrGyS/lT0du0G3ocrszRXqo5+MC2RQcTMZZEt
|
||||||
|
oKhfHtn10bT0uKcKZmcjVQKBgEls2WWccMOuhM8yOowic+IYTDC1bpo1Tle6BFQ+
|
||||||
|
YoroZQGd+IwoLv+3ORINNPppfmKaY5y7+aw5hNM025oiCQajraPCPukY0TDI6jEq
|
||||||
|
XMKgzGSkMkUNkFf6UMmLooK3Yneg94232gbnbJqTDvbo1dccMoVaPGgKpjh9QQLl
|
||||||
|
gR0TAoGACFOvhl8txfbkwLeuNeunyOPL7J4nIccthgd2ioFOr3HTou6wzN++vYTa
|
||||||
|
a3OF9jH5Z7m6X1rrwn6J1+Gw9sBme38/GeGXHigsBI/8WaTvyuppyVIXOVPoTvVf
|
||||||
|
VYsTwo5YgV1HzDkV+BNmBCw1GYcGXAElhJI+dCsgQuuU6TKzgl8=
|
||||||
|
-----END RSA PRIVATE KEY-----`)
|
||||||
|
|
Loading…
Reference in New Issue