Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit a502a5f

Browse files
authored
feat: Add AWS instance identity authentication (#570)
* feat: Add AWS instance identity authentication This allows zero-trust authentication for all AWS instances. Prior to this, AWS instances could be used by passing `CODER_TOKEN` as an environment variable to the startup script. AWS explicitly states that secrets should not be passed in startup scripts because it's user-readable. * Fix sha256 verbosity * Fix HTTP client being exposed on auth
1 parent 01957da commit a502a5f

13 files changed

+583
-37
lines changed

Makefile

-1
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,5 @@ site/out:
9090
.PHONY: site/out
9191

9292
lint:
93-
@echo "--- golangci-lint"
9493
golangci-lint run
9594
.PHONY: lint

cli/cliui/prompt.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ func Prompt(cmd *cobra.Command, opts PromptOptions) (string, error) {
3636
signal.Notify(interrupt, os.Interrupt)
3737
defer signal.Stop(interrupt)
3838

39-
errCh := make(chan error)
39+
errCh := make(chan error, 1)
4040
lineCh := make(chan string)
4141
go func() {
4242
var line string

cli/workspaceagent.go

+36-8
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package cli
22

33
import (
44
"context"
5+
"net/http"
56
"net/url"
67
"time"
78

@@ -39,6 +40,11 @@ func workspaceAgent() *cobra.Command {
3940
}
4041
logger := slog.Make(sloghuman.Sink(cmd.OutOrStdout())).Leveled(slog.LevelDebug)
4142
client := codersdk.New(coderURL)
43+
44+
// exchangeToken returns a session token.
45+
// This is abstracted to allow for the same looping condition
46+
// regardless of instance identity auth type.
47+
var exchangeToken func(context.Context) (codersdk.WorkspaceAgentAuthenticateResponse, error)
4248
switch auth {
4349
case "token":
4450
if token == "" {
@@ -53,29 +59,51 @@ func workspaceAgent() *cobra.Command {
5359
if gcpClientRaw != nil {
5460
gcpClient, _ = gcpClientRaw.(*metadata.Client)
5561
}
62+
exchangeToken = func(ctx context.Context) (codersdk.WorkspaceAgentAuthenticateResponse, error) {
63+
return client.AuthWorkspaceGoogleInstanceIdentity(ctx, "", gcpClient)
64+
}
65+
case "aws-instance-identity":
66+
// This is *only* done for testing to mock client authentication.
67+
// This will never be set in a production scenario.
68+
var awsClient *http.Client
69+
awsClientRaw := cmd.Context().Value("aws-client")
70+
if awsClientRaw != nil {
71+
awsClient, _ = awsClientRaw.(*http.Client)
72+
if awsClient != nil {
73+
client.HTTPClient = awsClient
74+
}
75+
}
76+
exchangeToken = func(ctx context.Context) (codersdk.WorkspaceAgentAuthenticateResponse, error) {
77+
return client.AuthWorkspaceAWSInstanceIdentity(ctx)
78+
}
79+
case "azure-instance-identity":
80+
return xerrors.Errorf("not implemented")
81+
}
5682

57-
ctx, cancelFunc := context.WithTimeout(cmd.Context(), 30*time.Second)
83+
if exchangeToken != nil {
84+
// Agent's can start before resources are returned from the provisioner
85+
// daemon. If there are many resources being provisioned, this time
86+
// could be significant. This is arbitrarily set at an hour to prevent
87+
// tons of idle agents from pinging coderd.
88+
ctx, cancelFunc := context.WithTimeout(cmd.Context(), time.Hour)
5889
defer cancelFunc()
5990
for retry.New(100*time.Millisecond, 5*time.Second).Wait(ctx) {
6091
var response codersdk.WorkspaceAgentAuthenticateResponse
6192

62-
response, err = client.AuthWorkspaceGoogleInstanceIdentity(ctx, "", gcpClient)
93+
response, err = exchangeToken(ctx)
6394
if err != nil {
64-
logger.Warn(ctx, "authenticate workspace with Google Instance Identity", slog.Error(err))
95+
logger.Warn(ctx, "authenticate workspace", slog.F("method", auth), slog.Error(err))
6596
continue
6697
}
6798
client.SessionToken = response.SessionToken
68-
logger.Info(ctx, "authenticated with Google Instance Identity")
99+
logger.Info(ctx, "authenticated", slog.F("method", auth))
69100
break
70101
}
71102
if err != nil {
72103
return xerrors.Errorf("agent failed to authenticate in time: %w", err)
73104
}
74-
case "aws-instance-identity":
75-
return xerrors.Errorf("not implemented")
76-
case "azure-instance-identity":
77-
return xerrors.Errorf("not implemented")
78105
}
106+
79107
closer := agent.New(client.ListenWorkspaceAgent, &peer.ConnOptions{
80108
Logger: logger,
81109
})

cli/workspaceagent_test.go

+55-1
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,66 @@ import (
1414

1515
func TestWorkspaceAgent(t *testing.T) {
1616
t.Parallel()
17+
t.Run("AWS", func(t *testing.T) {
18+
t.Parallel()
19+
instanceID := "instanceidentifier"
20+
certificates, metadataClient := coderdtest.NewAWSInstanceIdentity(t, instanceID)
21+
client := coderdtest.New(t, &coderdtest.Options{
22+
AWSInstanceIdentity: certificates,
23+
})
24+
user := coderdtest.CreateFirstUser(t, client)
25+
coderdtest.NewProvisionerDaemon(t, client)
26+
version := coderdtest.CreateProjectVersion(t, client, user.OrganizationID, &echo.Responses{
27+
Parse: echo.ParseComplete,
28+
Provision: []*proto.Provision_Response{{
29+
Type: &proto.Provision_Response_Complete{
30+
Complete: &proto.Provision_Complete{
31+
Resources: []*proto.Resource{{
32+
Name: "somename",
33+
Type: "someinstance",
34+
Agent: &proto.Agent{
35+
Auth: &proto.Agent_InstanceId{
36+
InstanceId: instanceID,
37+
},
38+
},
39+
}},
40+
},
41+
},
42+
}},
43+
})
44+
project := coderdtest.CreateProject(t, client, user.OrganizationID, version.ID)
45+
coderdtest.AwaitProjectVersionJob(t, client, version.ID)
46+
workspace := coderdtest.CreateWorkspace(t, client, "me", project.ID)
47+
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
48+
49+
cmd, _ := clitest.New(t, "workspaces", "agent", "--auth", "aws-instance-identity", "--url", client.URL.String())
50+
ctx, cancelFunc := context.WithCancel(context.Background())
51+
defer cancelFunc()
52+
go func() {
53+
// A linting error occurs for weakly typing the context value here,
54+
// but it seems reasonable for a one-off test.
55+
// nolint
56+
ctx = context.WithValue(ctx, "aws-client", metadataClient)
57+
err := cmd.ExecuteContext(ctx)
58+
require.NoError(t, err)
59+
}()
60+
coderdtest.AwaitWorkspaceAgents(t, client, workspace.LatestBuild.ID)
61+
resources, err := client.WorkspaceResourcesByBuild(ctx, workspace.LatestBuild.ID)
62+
require.NoError(t, err)
63+
dialer, err := client.DialWorkspaceAgent(ctx, resources[0].ID, nil, nil)
64+
require.NoError(t, err)
65+
defer dialer.Close()
66+
_, err = dialer.Ping()
67+
require.NoError(t, err)
68+
cancelFunc()
69+
})
70+
1771
t.Run("GoogleCloud", func(t *testing.T) {
1872
t.Parallel()
1973
instanceID := "instanceidentifier"
2074
validator, metadata := coderdtest.NewGoogleInstanceIdentity(t, instanceID, false)
2175
client := coderdtest.New(t, &coderdtest.Options{
22-
GoogleTokenValidator: validator,
76+
GoogleInstanceIdentity: validator,
2377
})
2478
user := coderdtest.CreateFirstUser(t, client)
2579
coderdtest.NewProvisionerDaemon(t, client)

coderd/awsidentity/awsidentity.go

+218
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
package awsidentity
2+
3+
import (
4+
"crypto"
5+
"crypto/rsa"
6+
"crypto/sha256"
7+
"crypto/x509"
8+
"encoding/base64"
9+
"encoding/json"
10+
"encoding/pem"
11+
12+
"golang.org/x/xerrors"
13+
)
14+
15+
// Region represents the AWS locations a public-key covers.
16+
type Region string
17+
18+
const (
19+
Other Region = "other"
20+
HongKong Region = "hongkong"
21+
Bahrain Region = "bahrain"
22+
CapeTown Region = "capetown"
23+
Milan Region = "milan"
24+
China Region = "china"
25+
GovCloud Region = "govcloud"
26+
)
27+
28+
var (
29+
All = []Region{Other, HongKong, Bahrain, CapeTown, Milan, China, GovCloud}
30+
)
31+
32+
// Certificates hold public keys for various AWS regions. See:
33+
type Certificates map[Region]string
34+
35+
// Identity represents a validated document and signature.
36+
type Identity struct {
37+
InstanceID string
38+
Region Region
39+
}
40+
41+
// https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-identity-documents.html
42+
type awsInstanceIdentityDocument struct {
43+
InstanceID string `json:"instanceId"`
44+
}
45+
46+
// Validate ensures the document was signed by an AWS public key.
47+
// Regions that aren't provided in certificates will use defaults.
48+
func Validate(signature, document string, certificates Certificates) (Identity, error) {
49+
if certificates == nil {
50+
certificates = Certificates{}
51+
}
52+
for _, region := range All {
53+
if _, ok := certificates[region]; ok {
54+
continue
55+
}
56+
defaultCertificate, exists := defaultCertificates[region]
57+
if !exists {
58+
panic("dev error: no certificate exists for region " + region)
59+
}
60+
certificates[region] = defaultCertificate
61+
}
62+
63+
var instanceIdentity awsInstanceIdentityDocument
64+
err := json.Unmarshal([]byte(document), &instanceIdentity)
65+
if err != nil {
66+
return Identity{}, xerrors.Errorf("parse document: %w", err)
67+
}
68+
rawSignature, err := base64.StdEncoding.DecodeString(signature)
69+
if err != nil {
70+
return Identity{}, xerrors.Errorf("decode signature: %w", err)
71+
}
72+
hashedDocument := sha256.Sum256([]byte(document))
73+
74+
for region, certificate := range certificates {
75+
regionBlock, rest := pem.Decode([]byte(certificate))
76+
if len(rest) != 0 {
77+
return Identity{}, xerrors.Errorf("invalid certificate for %q. %d bytes remain", region, len(rest))
78+
}
79+
regionCert, err := x509.ParseCertificate(regionBlock.Bytes)
80+
if err != nil {
81+
return Identity{}, xerrors.Errorf("parse certificate: %w", err)
82+
}
83+
regionPublicKey, valid := regionCert.PublicKey.(*rsa.PublicKey)
84+
if !valid {
85+
return Identity{}, xerrors.Errorf("certificate for %q was not an rsa key", region)
86+
}
87+
err = rsa.VerifyPKCS1v15(regionPublicKey, crypto.SHA256, hashedDocument[:], rawSignature)
88+
if err != nil {
89+
continue
90+
}
91+
return Identity{
92+
InstanceID: instanceIdentity.InstanceID,
93+
Region: region,
94+
}, nil
95+
}
96+
return Identity{}, rsa.ErrVerification
97+
}
98+
99+
// Default AWS certificates for regions.
100+
// https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/verify-signature.html
101+
var defaultCertificates = Certificates{
102+
Other: `-----BEGIN CERTIFICATE-----
103+
MIIDIjCCAougAwIBAgIJAKnL4UEDMN/FMA0GCSqGSIb3DQEBBQUAMGoxCzAJBgNV
104+
BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdTZWF0dGxlMRgw
105+
FgYDVQQKEw9BbWF6b24uY29tIEluYy4xGjAYBgNVBAMTEWVjMi5hbWF6b25hd3Mu
106+
Y29tMB4XDTE0MDYwNTE0MjgwMloXDTI0MDYwNTE0MjgwMlowajELMAkGA1UEBhMC
107+
VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1NlYXR0bGUxGDAWBgNV
108+
BAoTD0FtYXpvbi5jb20gSW5jLjEaMBgGA1UEAxMRZWMyLmFtYXpvbmF3cy5jb20w
109+
gZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAIe9GN//SRK2knbjySG0ho3yqQM3
110+
e2TDhWO8D2e8+XZqck754gFSo99AbT2RmXClambI7xsYHZFapbELC4H91ycihvrD
111+
jbST1ZjkLQgga0NE1q43eS68ZeTDccScXQSNivSlzJZS8HJZjgqzBlXjZftjtdJL
112+
XeE4hwvo0sD4f3j9AgMBAAGjgc8wgcwwHQYDVR0OBBYEFCXWzAgVyrbwnFncFFIs
113+
77VBdlE4MIGcBgNVHSMEgZQwgZGAFCXWzAgVyrbwnFncFFIs77VBdlE4oW6kbDBq
114+
MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHU2Vh
115+
dHRsZTEYMBYGA1UEChMPQW1hem9uLmNvbSBJbmMuMRowGAYDVQQDExFlYzIuYW1h
116+
em9uYXdzLmNvbYIJAKnL4UEDMN/FMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF
117+
BQADgYEAFYcz1OgEhQBXIwIdsgCOS8vEtiJYF+j9uO6jz7VOmJqO+pRlAbRlvY8T
118+
C1haGgSI/A1uZUKs/Zfnph0oEI0/hu1IIJ/SKBDtN5lvmZ/IzbOPIJWirlsllQIQ
119+
7zvWbGd9c9+Rm3p04oTvhup99la7kZqevJK0QRdD/6NpCKsqP/0=
120+
-----END CERTIFICATE-----`,
121+
HongKong: `-----BEGIN CERTIFICATE-----
122+
MIICSzCCAbQCCQDtQvkVxRvK9TANBgkqhkiG9w0BAQsFADBqMQswCQYDVQQGEwJV
123+
UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHU2VhdHRsZTEYMBYGA1UE
124+
ChMPQW1hem9uLmNvbSBJbmMuMRowGAYDVQQDExFlYzIuYW1hem9uYXdzLmNvbTAe
125+
Fw0xOTAyMDMwMzAwMDZaFw0yOTAyMDIwMzAwMDZaMGoxCzAJBgNVBAYTAlVTMRMw
126+
EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdTZWF0dGxlMRgwFgYDVQQKEw9B
127+
bWF6b24uY29tIEluYy4xGjAYBgNVBAMTEWVjMi5hbWF6b25hd3MuY29tMIGfMA0G
128+
CSqGSIb3DQEBAQUAA4GNADCBiQKBgQC1kkHXYTfc7gY5Q55JJhjTieHAgacaQkiR
129+
Pity9QPDE3b+NXDh4UdP1xdIw73JcIIG3sG9RhWiXVCHh6KkuCTqJfPUknIKk8vs
130+
M3RXflUpBe8Pf+P92pxqPMCz1Fr2NehS3JhhpkCZVGxxwLC5gaG0Lr4rFORubjYY
131+
Rh84dK98VwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAA6xV9f0HMqXjPHuGILDyaNN
132+
dKcvplNFwDTydVg32MNubAGnecoEBtUPtxBsLoVYXCOb+b5/ZMDubPF9tU/vSXuo
133+
TpYM5Bq57gJzDRaBOntQbX9bgHiUxw6XZWaTS/6xjRJDT5p3S1E0mPI3lP/eJv4o
134+
Ezk5zb3eIf10/sqt4756
135+
-----END CERTIFICATE-----`,
136+
Bahrain: `-----BEGIN CERTIFICATE-----
137+
MIIDPDCCAqWgAwIBAgIJAMl6uIV/zqJFMA0GCSqGSIb3DQEBCwUAMHIxCzAJBgNV
138+
BAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMRAwDgYDVQQHDAdTZWF0dGxlMSAw
139+
HgYDVQQKDBdBbWF6b24gV2ViIFNlcnZpY2VzIExMQzEaMBgGA1UEAwwRZWMyLmFt
140+
YXpvbmF3cy5jb20wIBcNMTkwNDI2MTQzMjQ3WhgPMjE5ODA5MjkxNDMyNDdaMHIx
141+
CzAJBgNVBAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMRAwDgYDVQQHDAdTZWF0
142+
dGxlMSAwHgYDVQQKDBdBbWF6b24gV2ViIFNlcnZpY2VzIExMQzEaMBgGA1UEAwwR
143+
ZWMyLmFtYXpvbmF3cy5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALVN
144+
CDTZEnIeoX1SEYqq6k1BV0ZlpY5y3KnoOreCAE589TwS4MX5+8Fzd6AmACmugeBP
145+
Qk7Hm6b2+g/d4tWycyxLaQlcq81DB1GmXehRkZRgGeRge1ePWd1TUA0I8P/QBT7S
146+
gUePm/kANSFU+P7s7u1NNl+vynyi0wUUrw7/wIZTAgMBAAGjgdcwgdQwHQYDVR0O
147+
BBYEFILtMd+T4YgH1cgc+hVsVOV+480FMIGkBgNVHSMEgZwwgZmAFILtMd+T4YgH
148+
1cgc+hVsVOV+480FoXakdDByMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGlu
149+
Z3RvbjEQMA4GA1UEBwwHU2VhdHRsZTEgMB4GA1UECgwXQW1hem9uIFdlYiBTZXJ2
150+
aWNlcyBMTEMxGjAYBgNVBAMMEWVjMi5hbWF6b25hd3MuY29tggkAyXq4hX/OokUw
151+
DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOBgQBhkNTBIFgWFd+ZhC/LhRUY
152+
4OjEiykmbEp6hlzQ79T0Tfbn5A4NYDI2icBP0+hmf6qSnIhwJF6typyd1yPK5Fqt
153+
NTpxxcXmUKquX+pHmIkK1LKDO8rNE84jqxrxRsfDi6by82fjVYf2pgjJW8R1FAw+
154+
mL5WQRFexbfB5aXhcMo0AA==
155+
-----END CERTIFICATE-----`,
156+
CapeTown: `-----BEGIN CERTIFICATE-----
157+
MIICNjCCAZ+gAwIBAgIJAKumfZiRrNvHMA0GCSqGSIb3DQEBCwUAMFwxCzAJBgNV
158+
BAYTAlVTMRkwFwYDVQQIExBXYXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdTZWF0
159+
dGxlMSAwHgYDVQQKExdBbWF6b24gV2ViIFNlcnZpY2VzIExMQzAgFw0xOTExMjcw
160+
NzE0MDVaGA8yMTk5MDUwMjA3MTQwNVowXDELMAkGA1UEBhMCVVMxGTAXBgNVBAgT
161+
EFdhc2hpbmd0b24gU3RhdGUxEDAOBgNVBAcTB1NlYXR0bGUxIDAeBgNVBAoTF0Ft
162+
YXpvbiBXZWIgU2VydmljZXMgTExDMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
163+
gQDFd571nUzVtke3rPyRkYfvs3jh0C0EMzzG72boyUNjnfw1+m0TeFraTLKb9T6F
164+
7TuB/ZEN+vmlYqr2+5Va8U8qLbPF0bRH+FdaKjhgWZdYXxGzQzU3ioy5W5ZM1VyB
165+
7iUsxEAlxsybC3ziPYaHI42UiTkQNahmoroNeqVyHNnBpQIDAQABMA0GCSqGSIb3
166+
DQEBCwUAA4GBAAJLylWyElEgOpW4B1XPyRVD4pAds8Guw2+krgqkY0HxLCdjosuH
167+
RytGDGN+q75aAoXzW5a7SGpxLxk6Hfv0xp3RjDHsoeP0i1d8MD3hAC5ezxS4oukK
168+
s5gbPOnokhKTMPXbTdRn5ZifCbWlx+bYN/mTYKvxho7b5SVg2o1La9aK
169+
-----END CERTIFICATE-----`,
170+
Milan: `-----BEGIN CERTIFICATE-----
171+
MIICNjCCAZ+gAwIBAgIJAOZ3GEIaDcugMA0GCSqGSIb3DQEBCwUAMFwxCzAJBgNV
172+
BAYTAlVTMRkwFwYDVQQIExBXYXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdTZWF0
173+
dGxlMSAwHgYDVQQKExdBbWF6b24gV2ViIFNlcnZpY2VzIExMQzAgFw0xOTEwMjQx
174+
NTE5MDlaGA8yMTk5MDMyOTE1MTkwOVowXDELMAkGA1UEBhMCVVMxGTAXBgNVBAgT
175+
EFdhc2hpbmd0b24gU3RhdGUxEDAOBgNVBAcTB1NlYXR0bGUxIDAeBgNVBAoTF0Ft
176+
YXpvbiBXZWIgU2VydmljZXMgTExDMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
177+
gQCjiPgW3vsXRj4JoA16WQDyoPc/eh3QBARaApJEc4nPIGoUolpAXcjFhWplo2O+
178+
ivgfCsc4AU9OpYdAPha3spLey/bhHPRi1JZHRNqScKP0hzsCNmKhfnZTIEQCFvsp
179+
DRp4zr91/WS06/flJFBYJ6JHhp0KwM81XQG59lV6kkoW7QIDAQABMA0GCSqGSIb3
180+
DQEBCwUAA4GBAGLLrY3P+HH6C57dYgtJkuGZGT2+rMkk2n81/abzTJvsqRqGRrWv
181+
XRKRXlKdM/dfiuYGokDGxiC0Mg6TYy6wvsR2qRhtXW1OtZkiHWcQCnOttz+8vpew
182+
wx8JGMvowtuKB1iMsbwyRqZkFYLcvH+Opfb/Aayi20/ChQLdI6M2R5VU
183+
-----END CERTIFICATE-----`,
184+
China: `-----BEGIN CERTIFICATE-----
185+
MIICSzCCAbQCCQCQu97teKRD4zANBgkqhkiG9w0BAQUFADBqMQswCQYDVQQGEwJV
186+
UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHU2VhdHRsZTEYMBYGA1UE
187+
ChMPQW1hem9uLmNvbSBJbmMuMRowGAYDVQQDExFlYzIuYW1hem9uYXdzLmNvbTAe
188+
Fw0xMzA4MjExMzIyNDNaFw0yMzA4MjExMzIyNDNaMGoxCzAJBgNVBAYTAlVTMRMw
189+
EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdTZWF0dGxlMRgwFgYDVQQKEw9B
190+
bWF6b24uY29tIEluYy4xGjAYBgNVBAMTEWVjMi5hbWF6b25hd3MuY29tMIGfMA0G
191+
CSqGSIb3DQEBAQUAA4GNADCBiQKBgQC6GFQ2WoBl1xZYH85INUMaTc4D30QXM6f+
192+
YmWZyJD9fC7Z0UlaZIKoQATqCO58KNCre+jECELYIX56Uq0lb8LRLP8tijrQ9Sp3
193+
qJcXiH66kH0eQ44a5YdewcFOy+CSAYDUIaB6XhTQJ2r7bd4A2vw3ybbxTOWONKdO
194+
WtgIe3M3iwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAHzQC5XZVeuD9GTJTsbO5AyH
195+
ZQvki/jfARNrD9dgBRYZzLC/NOkWG6M9wlrmks9RtdNxc53nLxKq4I2Dd73gI0yQ
196+
wYu9YYwmM/LMgmPlI33Rg2Ohwq4DVgT3hO170PL6Fsgiq3dMvctSImJvjWktBQaT
197+
bcAgaZLHGIpXPrWSA2d+
198+
-----END CERTIFICATE-----`,
199+
GovCloud: `-----BEGIN CERTIFICATE-----
200+
MIIDCzCCAnSgAwIBAgIJAIe9Hnq82O7UMA0GCSqGSIb3DQEBCwUAMFwxCzAJBgNV
201+
BAYTAlVTMRkwFwYDVQQIExBXYXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdTZWF0
202+
dGxlMSAwHgYDVQQKExdBbWF6b24gV2ViIFNlcnZpY2VzIExMQzAeFw0yMTA3MTQx
203+
NDI3NTdaFw0yNDA3MTMxNDI3NTdaMFwxCzAJBgNVBAYTAlVTMRkwFwYDVQQIExBX
204+
YXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdTZWF0dGxlMSAwHgYDVQQKExdBbWF6
205+
b24gV2ViIFNlcnZpY2VzIExMQzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA
206+
qaIcGFFTx/SO1W5G91jHvyQdGP25n1Y91aXCuOOWAUTvSvNGpXrI4AXNrQF+CmIO
207+
C4beBASnHCx082jYudWBBl9Wiza0psYc9flrczSzVLMmN8w/c78F/95NfiQdnUQP
208+
pvgqcMeJo82cgHkLR7XoFWgMrZJqrcUK0gnsQcb6kakCAwEAAaOB1DCB0TALBgNV
209+
HQ8EBAMCB4AwHQYDVR0OBBYEFNWV53gWJz72F5B1ZVY4O/dfFYBPMIGOBgNVHSME
210+
gYYwgYOAFNWV53gWJz72F5B1ZVY4O/dfFYBPoWCkXjBcMQswCQYDVQQGEwJVUzEZ
211+
MBcGA1UECBMQV2FzaGluZ3RvbiBTdGF0ZTEQMA4GA1UEBxMHU2VhdHRsZTEgMB4G
212+
A1UEChMXQW1hem9uIFdlYiBTZXJ2aWNlcyBMTEOCCQCHvR56vNju1DASBgNVHRMB
213+
Af8ECDAGAQH/AgEAMA0GCSqGSIb3DQEBCwUAA4GBACrKjWj460GUPZCGm3/z0dIz
214+
M2BPuH769wcOsqfFZcMKEysSFK91tVtUb1soFwH4/Lb/T0PqNrvtEwD1Nva5k0h2
215+
xZhNNRmDuhOhW1K9wCcnHGRBwY5t4lYL6hNV6hcrqYwGMjTjcAjBG2yMgznSNFle
216+
Rwi/S3BFXISixNx9cILu
217+
-----END CERTIFICATE-----`,
218+
}

0 commit comments

Comments
 (0)