Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitc8246e3

Browse files
authored
feat: Add Azure instance identitity authentication (#1064)
This enables zero-trust authentication for Azure instances. Nowwe support the three major clouds: AWS, Azure, and GCP 😎.
1 parent118a47e commitc8246e3

File tree

13 files changed

+348
-15
lines changed

13 files changed

+348
-15
lines changed

‎.vscode/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
"trimprefix",
5656
"unconvert",
5757
"Untar",
58+
"VMID",
5859
"webrtc",
5960
"xerrors",
6061
"yamux"

‎cli/agent.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,19 @@ func workspaceAgent() *cobra.Command {
7676
returnclient.AuthWorkspaceAWSInstanceIdentity(ctx)
7777
}
7878
case"azure-instance-identity":
79-
returnxerrors.Errorf("not implemented")
79+
// This is *only* done for testing to mock client authentication.
80+
// This will never be set in a production scenario.
81+
varazureClient*http.Client
82+
azureClientRaw:=cmd.Context().Value("azure-client")
83+
ifazureClientRaw!=nil {
84+
azureClient,_=azureClientRaw.(*http.Client)
85+
ifazureClient!=nil {
86+
client.HTTPClient=azureClient
87+
}
88+
}
89+
exchangeToken=func(ctx context.Context) (codersdk.WorkspaceAgentAuthenticateResponse,error) {
90+
returnclient.AuthWorkspaceAzureInstanceIdentity(ctx)
91+
}
8092
}
8193

8294
ifexchangeToken!=nil {

‎cli/agent_test.go

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,66 @@ import (
1515

1616
funcTestWorkspaceAgent(t*testing.T) {
1717
t.Parallel()
18+
t.Run("Azure",func(t*testing.T) {
19+
t.Parallel()
20+
instanceID:="instanceidentifier"
21+
certificates,metadataClient:=coderdtest.NewAzureInstanceIdentity(t,instanceID)
22+
client:=coderdtest.New(t,&coderdtest.Options{
23+
AzureCertificates:certificates,
24+
})
25+
user:=coderdtest.CreateFirstUser(t,client)
26+
coderdtest.NewProvisionerDaemon(t,client)
27+
version:=coderdtest.CreateTemplateVersion(t,client,user.OrganizationID,&echo.Responses{
28+
Parse:echo.ParseComplete,
29+
Provision: []*proto.Provision_Response{{
30+
Type:&proto.Provision_Response_Complete{
31+
Complete:&proto.Provision_Complete{
32+
Resources: []*proto.Resource{{
33+
Name:"somename",
34+
Type:"someinstance",
35+
Agents: []*proto.Agent{{
36+
Auth:&proto.Agent_InstanceId{
37+
InstanceId:instanceID,
38+
},
39+
}},
40+
}},
41+
},
42+
},
43+
}},
44+
})
45+
template:=coderdtest.CreateTemplate(t,client,user.OrganizationID,version.ID)
46+
coderdtest.AwaitTemplateVersionJob(t,client,version.ID)
47+
workspace:=coderdtest.CreateWorkspace(t,client,codersdk.Me,template.ID)
48+
coderdtest.AwaitWorkspaceBuildJob(t,client,workspace.LatestBuild.ID)
49+
50+
cmd,_:=clitest.New(t,"agent","--auth","azure-instance-identity","--url",client.URL.String())
51+
ctx,cancelFunc:=context.WithCancel(context.Background())
52+
defercancelFunc()
53+
gofunc() {
54+
// A linting error occurs for weakly typing the context value here,
55+
// but it seems reasonable for a one-off test.
56+
// nolint
57+
ctx=context.WithValue(ctx,"azure-client",metadataClient)
58+
err:=cmd.ExecuteContext(ctx)
59+
require.NoError(t,err)
60+
}()
61+
coderdtest.AwaitWorkspaceAgents(t,client,workspace.LatestBuild.ID)
62+
resources,err:=client.WorkspaceResourcesByBuild(ctx,workspace.LatestBuild.ID)
63+
require.NoError(t,err)
64+
dialer,err:=client.DialWorkspaceAgent(ctx,resources[0].Agents[0].ID,nil)
65+
require.NoError(t,err)
66+
deferdialer.Close()
67+
_,err=dialer.Ping()
68+
require.NoError(t,err)
69+
cancelFunc()
70+
})
71+
1872
t.Run("AWS",func(t*testing.T) {
1973
t.Parallel()
2074
instanceID:="instanceidentifier"
2175
certificates,metadataClient:=coderdtest.NewAWSInstanceIdentity(t,instanceID)
2276
client:=coderdtest.New(t,&coderdtest.Options{
23-
AWSInstanceIdentity:certificates,
77+
AWSCertificates:certificates,
2478
})
2579
user:=coderdtest.CreateFirstUser(t,client)
2680
coderdtest.NewProvisionerDaemon(t,client)
@@ -74,7 +128,7 @@ func TestWorkspaceAgent(t *testing.T) {
74128
instanceID:="instanceidentifier"
75129
validator,metadata:=coderdtest.NewGoogleInstanceIdentity(t,instanceID,false)
76130
client:=coderdtest.New(t,&coderdtest.Options{
77-
GoogleInstanceIdentity:validator,
131+
GoogleTokenValidator:validator,
78132
})
79133
user:=coderdtest.CreateFirstUser(t,client)
80134
coderdtest.NewProvisionerDaemon(t,client)

‎coderd/azureidentity/azureidentity.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package azureidentity
2+
3+
import (
4+
"context"
5+
"crypto/x509"
6+
"encoding/base64"
7+
"encoding/json"
8+
"io"
9+
"net/http"
10+
"regexp"
11+
12+
"go.mozilla.org/pkcs7"
13+
"golang.org/x/xerrors"
14+
)
15+
16+
// allowedSigners matches valid common names listed here:
17+
// https://docs.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service?tabs=linux#tabgroup_14
18+
varallowedSigners=regexp.MustCompile(`^(.*\.)?metadata\.(azure\.(com|us|cn)|microsoftazure\.de)$`)
19+
20+
typemetadatastruct {
21+
VMIDstring`json:"vmId"`
22+
}
23+
24+
// Validate ensures the signature was signed by an Azure certificate.
25+
// It returns the associated VM ID if successful.
26+
funcValidate(ctx context.Context,signaturestring,options x509.VerifyOptions) (string,error) {
27+
data,err:=base64.StdEncoding.DecodeString(signature)
28+
iferr!=nil {
29+
return"",xerrors.Errorf("decode base64: %w",err)
30+
}
31+
pkcs7Data,err:=pkcs7.Parse(data)
32+
iferr!=nil {
33+
return"",xerrors.Errorf("parse pkcs7: %w",err)
34+
}
35+
signer:=pkcs7Data.GetOnlySigner()
36+
ifsigner==nil {
37+
return"",xerrors.New("no signers for signature")
38+
}
39+
if!allowedSigners.MatchString(signer.Subject.CommonName) {
40+
return"",xerrors.Errorf("unmatched common name of signer: %q",signer.Subject.CommonName)
41+
}
42+
ifoptions.Intermediates==nil {
43+
options.Intermediates=x509.NewCertPool()
44+
for_,certURL:=rangesigner.IssuingCertificateURL {
45+
req,err:=http.NewRequestWithContext(ctx,"GET",certURL,nil)
46+
iferr!=nil {
47+
return"",xerrors.Errorf("new request %q: %w",certURL,err)
48+
}
49+
res,err:=http.DefaultClient.Do(req)
50+
iferr!=nil {
51+
return"",xerrors.Errorf("perform request %q: %w",certURL,err)
52+
}
53+
data,err:=io.ReadAll(res.Body)
54+
iferr!=nil {
55+
return"",xerrors.Errorf("read body %q: %w",certURL,err)
56+
}
57+
cert,err:=x509.ParseCertificate(data)
58+
iferr!=nil {
59+
return"",xerrors.Errorf("parse certificate %q: %w",certURL,err)
60+
}
61+
options.Intermediates.AddCert(cert)
62+
}
63+
}
64+
65+
_,err=signer.Verify(options)
66+
iferr!=nil {
67+
return"",xerrors.Errorf("verify certificates: %w",err)
68+
}
69+
70+
varmetadatametadata
71+
err=json.Unmarshal(pkcs7Data.Content,&metadata)
72+
iferr!=nil {
73+
return"",xerrors.Errorf("unmarshal metadata: %w",err)
74+
}
75+
returnmetadata.VMID,nil
76+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package azureidentity_test
2+
3+
import (
4+
"context"
5+
"crypto/x509"
6+
"testing"
7+
8+
"github.com/stretchr/testify/require"
9+
10+
"github.com/coder/coder/coderd/azureidentity"
11+
)
12+
13+
const (
14+
signature=`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`
15+
)
16+
17+
funcTestValidate(t*testing.T) {
18+
t.Parallel()
19+
vm,err:=azureidentity.Validate(context.Background(),signature, x509.VerifyOptions{})
20+
require.NoError(t,err)
21+
require.Equal(t,"bd8e7443-24a0-41f3-b949-8baf4fd1c573",vm)
22+
}

‎coderd/coderd.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package coderd
22

33
import (
44
"context"
5+
"crypto/x509"
56
"fmt"
67
"net/http"
78
"net/url"
@@ -35,6 +36,7 @@ type Options struct {
3536

3637
AgentConnectionUpdateFrequency time.Duration
3738
AWSCertificates awsidentity.Certificates
39+
AzureCertificates x509.VerifyOptions
3840
GoogleTokenValidator*idtoken.Validator
3941
ICEServers []webrtc.ICEServer
4042
SecureAuthCookiebool
@@ -172,6 +174,7 @@ func New(options *Options) (http.Handler, func()) {
172174
})
173175
})
174176
r.Route("/workspaceagents",func(r chi.Router) {
177+
r.Post("/azure-instance-identity",api.postWorkspaceAuthAzureInstanceIdentity)
175178
r.Post("/aws-instance-identity",api.postWorkspaceAuthAWSInstanceIdentity)
176179
r.Post("/google-instance-identity",api.postWorkspaceAuthGoogleInstanceIdentity)
177180
r.Route("/me",func(r chi.Router) {

‎coderd/coderdtest/coderdtest.go

Lines changed: 70 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"crypto/rsa"
99
"crypto/sha256"
1010
"crypto/x509"
11+
"crypto/x509/pkix"
1112
"database/sql"
1213
"encoding/base64"
1314
"encoding/json"
@@ -24,6 +25,7 @@ import (
2425
"time"
2526

2627
"cloud.google.com/go/compute/metadata"
28+
"github.com/fullsailor/pkcs7"
2729
"github.com/golang-jwt/jwt"
2830
"github.com/google/uuid"
2931
"github.com/moby/moby/pkg/namesgenerator"
@@ -49,9 +51,10 @@ import (
4951
)
5052

5153
typeOptionsstruct {
52-
AWSInstanceIdentity awsidentity.Certificates
53-
GoogleInstanceIdentity*idtoken.Validator
54-
SSHKeygenAlgorithm gitsshkey.Algorithm
54+
AWSCertificates awsidentity.Certificates
55+
AzureCertificates x509.VerifyOptions
56+
GoogleTokenValidator*idtoken.Validator
57+
SSHKeygenAlgorithm gitsshkey.Algorithm
5558
}
5659

5760
// New constructs an in-memory coderd instance and returns
@@ -60,11 +63,11 @@ func New(t *testing.T, options *Options) *codersdk.Client {
6063
ifoptions==nil {
6164
options=&Options{}
6265
}
63-
ifoptions.GoogleInstanceIdentity==nil {
66+
ifoptions.GoogleTokenValidator==nil {
6467
ctx,cancelFunc:=context.WithCancel(context.Background())
6568
t.Cleanup(cancelFunc)
6669
varerrerror
67-
options.GoogleInstanceIdentity,err=idtoken.NewValidator(ctx,option.WithoutAuthentication())
70+
options.GoogleTokenValidator,err=idtoken.NewValidator(ctx,option.WithoutAuthentication())
6871
require.NoError(t,err)
6972
}
7073

@@ -117,8 +120,9 @@ func New(t *testing.T, options *Options) *codersdk.Client {
117120
Database:db,
118121
Pubsub:pubsub,
119122

120-
AWSCertificates:options.AWSInstanceIdentity,
121-
GoogleTokenValidator:options.GoogleInstanceIdentity,
123+
AWSCertificates:options.AWSCertificates,
124+
AzureCertificates:options.AzureCertificates,
125+
GoogleTokenValidator:options.GoogleTokenValidator,
122126
SSHKeygenAlgorithm:options.SSHKeygenAlgorithm,
123127
TURNServer:turnServer,
124128
})
@@ -414,6 +418,65 @@ func NewAWSInstanceIdentity(t *testing.T, instanceID string) (awsidentity.Certif
414418
}
415419
}
416420

421+
// NewAzureInstanceIdentity returns a metadata client and ID token validator for faking
422+
// instance authentication for Azure.
423+
funcNewAzureInstanceIdentity(t*testing.T,instanceIDstring) (x509.VerifyOptions,*http.Client) {
424+
privateKey,err:=rsa.GenerateKey(rand.Reader,2048)
425+
require.NoError(t,err)
426+
427+
rawCertificate,err:=x509.CreateCertificate(rand.Reader,&x509.Certificate{
428+
SerialNumber:big.NewInt(2022),
429+
NotAfter:time.Now().AddDate(1,0,0),
430+
Subject: pkix.Name{
431+
CommonName:"metadata.azure.com",
432+
},
433+
},&x509.Certificate{},&privateKey.PublicKey,privateKey)
434+
require.NoError(t,err)
435+
436+
certificate,err:=x509.ParseCertificate(rawCertificate)
437+
require.NoError(t,err)
438+
439+
signed,err:=pkcs7.NewSignedData([]byte(`{"vmId":"`+instanceID+`"}`))
440+
require.NoError(t,err)
441+
err=signed.AddSigner(certificate,privateKey, pkcs7.SignerInfoConfig{})
442+
require.NoError(t,err)
443+
signatureRaw,err:=signed.Finish()
444+
require.NoError(t,err)
445+
signature:=make([]byte,base64.StdEncoding.EncodedLen(len(signatureRaw)))
446+
base64.StdEncoding.Encode(signature,signatureRaw)
447+
448+
payload,err:=json.Marshal(codersdk.AzureInstanceIdentityToken{
449+
Signature:string(signature),
450+
Encoding:"pkcs7",
451+
})
452+
require.NoError(t,err)
453+
454+
certPool:=x509.NewCertPool()
455+
certPool.AddCert(certificate)
456+
457+
return x509.VerifyOptions{
458+
Intermediates:certPool,
459+
Roots:certPool,
460+
},&http.Client{
461+
Transport:roundTripper(func(r*http.Request) (*http.Response,error) {
462+
// Only handle metadata server requests.
463+
ifr.URL.Host!="169.254.169.254" {
464+
returnhttp.DefaultTransport.RoundTrip(r)
465+
}
466+
switchr.URL.Path {
467+
case"/metadata/attested/document":
468+
return&http.Response{
469+
StatusCode:http.StatusOK,
470+
Body:io.NopCloser(bytes.NewReader(payload)),
471+
Header:make(http.Header),
472+
},nil
473+
default:
474+
panic("unhandled route: "+r.URL.Path)
475+
}
476+
}),
477+
}
478+
}
479+
417480
funcrandomUsername()string {
418481
returnstrings.ReplaceAll(namesgenerator.GetRandomName(0),"_","-")
419482
}

‎coderd/workspaceresourceauth.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,31 @@ import (
88
"net/http"
99

1010
"github.com/coder/coder/coderd/awsidentity"
11+
"github.com/coder/coder/coderd/azureidentity"
1112
"github.com/coder/coder/coderd/database"
1213
"github.com/coder/coder/coderd/httpapi"
1314
"github.com/coder/coder/codersdk"
1415

1516
"github.com/mitchellh/mapstructure"
1617
)
1718

19+
// Azure supports instance identity verification:
20+
// https://docs.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service?tabs=linux#tabgroup_14
21+
func (api*api)postWorkspaceAuthAzureInstanceIdentity(rw http.ResponseWriter,r*http.Request) {
22+
varreq codersdk.AzureInstanceIdentityToken
23+
if!httpapi.Read(rw,r,&req) {
24+
return
25+
}
26+
instanceID,err:=azureidentity.Validate(r.Context(),req.Signature,api.AzureCertificates)
27+
iferr!=nil {
28+
httpapi.Write(rw,http.StatusUnauthorized, httpapi.Response{
29+
Message:fmt.Sprintf("validate: %s",err),
30+
})
31+
return
32+
}
33+
api.handleAuthInstanceID(rw,r,instanceID)
34+
}
35+
1836
// AWS supports instance identity verification:
1937
// https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-identity-documents.html
2038
// Using this, we can exchange a signed instance payload for an agent token.

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp