-
Notifications
You must be signed in to change notification settings - Fork 486
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support foreign trust domains admin ids config #3642
Changes from 14 commits
bd3af89
a42ecfb
aebc767
890fd0f
1ba2fb3
6ad6274
e1c306e
7291f2c
8d98af1
af453cf
c9df0ee
0be5985
55c936f
fa07255
ec23d9c
25a95b9
3f743fc
e70e7b0
ec21334
5c51de8
47abc90
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,170 @@ | ||||||||||||||||||||||
package endpoints | ||||||||||||||||||||||
|
||||||||||||||||||||||
import ( | ||||||||||||||||||||||
"context" | ||||||||||||||||||||||
"crypto" | ||||||||||||||||||||||
"crypto/tls" | ||||||||||||||||||||||
"crypto/x509" | ||||||||||||||||||||||
"fmt" | ||||||||||||||||||||||
"sync/atomic" | ||||||||||||||||||||||
"time" | ||||||||||||||||||||||
|
||||||||||||||||||||||
"github.com/andres-erbsen/clock" | ||||||||||||||||||||||
"github.com/spiffe/go-spiffe/v2/bundle/x509bundle" | ||||||||||||||||||||||
"github.com/spiffe/go-spiffe/v2/spiffeid" | ||||||||||||||||||||||
"github.com/spiffe/go-spiffe/v2/spiffetls/tlsconfig" | ||||||||||||||||||||||
"github.com/spiffe/go-spiffe/v2/svid/x509svid" | ||||||||||||||||||||||
"github.com/spiffe/spire/pkg/common/telemetry" | ||||||||||||||||||||||
"github.com/spiffe/spire/pkg/common/x509util" | ||||||||||||||||||||||
"github.com/spiffe/spire/pkg/server/cache/dscache" | ||||||||||||||||||||||
"github.com/spiffe/spire/proto/spire/common" | ||||||||||||||||||||||
) | ||||||||||||||||||||||
|
||||||||||||||||||||||
var ( | ||||||||||||||||||||||
lastMisconfigLogTime = new(atomic.Int64) | ||||||||||||||||||||||
misconfigClk = clock.New() | ||||||||||||||||||||||
) | ||||||||||||||||||||||
|
||||||||||||||||||||||
const misconfigLogEvery = time.Minute | ||||||||||||||||||||||
|
||||||||||||||||||||||
// shouldLogFederationMisconfiguration returns true if the last time a misconfiguration | ||||||||||||||||||||||
// was logged was more than misconfigLogEvery ago. | ||||||||||||||||||||||
func shouldLogFederationMisconfiguration() bool { | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmm... should we be doing this logging rate limiting per trust domain? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, that's a good idea! |
||||||||||||||||||||||
now := misconfigClk.Now() | ||||||||||||||||||||||
|
||||||||||||||||||||||
lastLogTime := lastMisconfigLogTime.Load() | ||||||||||||||||||||||
if now.Sub(time.Unix(lastLogTime, 0)) >= misconfigLogEvery { | ||||||||||||||||||||||
lastMisconfigLogTime.Store(now.Unix()) | ||||||||||||||||||||||
return true | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
return false | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
// bundleGetter fetches the bundle for the given trust domain and parse it as x509 certificates. | ||||||||||||||||||||||
func (e *Endpoints) bundleGetter(ctx context.Context, td *spiffeid.TrustDomain) ([]*x509.Certificate, error) { | ||||||||||||||||||||||
commonServerBundle, err := e.DataStore.FetchBundle(dscache.WithCache(ctx), td.IDString()) | ||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||
return nil, fmt.Errorf("get bundle from datastore: %w", err) | ||||||||||||||||||||||
} | ||||||||||||||||||||||
if commonServerBundle == nil { | ||||||||||||||||||||||
if *td != e.TrustDomain && shouldLogFederationMisconfiguration() { | ||||||||||||||||||||||
e.Log. | ||||||||||||||||||||||
WithField(telemetry.TrustDomain, td.String()). | ||||||||||||||||||||||
Warn( | ||||||||||||||||||||||
"No bundle found for foreign admin trust domain, admins from this trust domain will not be able to connect. " + | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||
"Make sure this trust domain is correctly federated.", | ||||||||||||||||||||||
) | ||||||||||||||||||||||
} | ||||||||||||||||||||||
return nil, fmt.Errorf("no bundle found for trust domain %s", e.TrustDomain.String()) | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. String() method is implicitly called when using %s or %q formatting:
Suggested change
|
||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
serverBundle, err := parseBundle(e.TrustDomain, commonServerBundle) | ||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||
return nil, fmt.Errorf("parse bundle: %w", err) | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this error will result in: "parse bundle: parse bundle: SOME ERROR" I think we may remove fmt error from here or from parseBundle There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
return serverBundle.X509Authorities(), nil | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
// serverSpiffeVerificationFunc returns a function that is used for peer certificate verification on TLS connections. | ||||||||||||||||||||||
// The returned function will verify that the peer certificate is valid, and apply a custom authorization with machMemberOrOneOff. | ||||||||||||||||||||||
// If the peer certificate is not provided, the function will not make any verification and return nil. | ||||||||||||||||||||||
func (e *Endpoints) serverSpiffeVerificationFunc(bundleSource x509bundle.Source) func(_ [][]byte, _ [][]*x509.Certificate) error { | ||||||||||||||||||||||
return func(rawCerts [][]byte, _ [][]*x509.Certificate) error { | ||||||||||||||||||||||
if rawCerts == nil { | ||||||||||||||||||||||
return nil | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
return tlsconfig.VerifyPeerCertificate( | ||||||||||||||||||||||
bundleSource, | ||||||||||||||||||||||
tlsconfig.AdaptMatcher(machMemberOrOneOff(e.TrustDomain, e.AdminIDs...)), | ||||||||||||||||||||||
)(rawCerts, nil) | ||||||||||||||||||||||
} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
// machMemberOrOneOff is a custom spiffeid.Matcher, which will validate that the peerSpiffeID belongs to the server | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||
// trust domain or if it is included in the admin_ids configuration permissive list. | ||||||||||||||||||||||
func machMemberOrOneOff(trustDomain spiffeid.TrustDomain, adminIds ...spiffeid.ID) spiffeid.Matcher { | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||
return func(peerSpiffeID spiffeid.ID) error { | ||||||||||||||||||||||
permissiveIDsSet := make(map[spiffeid.ID]struct{}) | ||||||||||||||||||||||
for _, adminID := range adminIds { | ||||||||||||||||||||||
permissiveIDsSet[adminID] = struct{}{} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's lift this out of the closure so that we don't recompute this map on every invocation of the matcher.
Suggested change
|
||||||||||||||||||||||
|
||||||||||||||||||||||
if !peerSpiffeID.MemberOf(trustDomain) { | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||
if _, ok := permissiveIDsSet[peerSpiffeID]; !ok { | ||||||||||||||||||||||
return fmt.Errorf("unexpected ID %q", permissiveIDsSet) | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||
} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
return nil | ||||||||||||||||||||||
} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
// parseBundle parses a *x509bundle.Bundle from a *common.bundle. | ||||||||||||||||||||||
func parseBundle(td spiffeid.TrustDomain, commonBundle *common.Bundle) (*x509bundle.Bundle, error) { | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a little unfortunate that we parse this on each handshake. I think we are already doing that today, but we should probably push this conversion behind the cache so we don't have to do it so much. That can happen in a later PR though. |
||||||||||||||||||||||
var caCerts []*x509.Certificate | ||||||||||||||||||||||
for _, rootCA := range commonBundle.RootCas { | ||||||||||||||||||||||
rootCACerts, err := x509.ParseCertificates(rootCA.DerBytes) | ||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||
return nil, fmt.Errorf("parse bundle: %w", err) | ||||||||||||||||||||||
} | ||||||||||||||||||||||
caCerts = append(caCerts, rootCACerts...) | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
return x509bundle.FromX509Authorities(td, caCerts), nil | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
type x509SVIDSource struct { | ||||||||||||||||||||||
getter func() *tls.Certificate | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
func newX509SVIDSource(getter func() *tls.Certificate) x509svid.Source { | ||||||||||||||||||||||
return &x509SVIDSource{getter: getter} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
func (xs *x509SVIDSource) GetX509SVID() (*x509svid.SVID, error) { | ||||||||||||||||||||||
tlsCert := xs.getter() | ||||||||||||||||||||||
|
||||||||||||||||||||||
certificates, err := x509util.RawCertsToCertificates(tlsCert.Certificate) | ||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||
return nil, err | ||||||||||||||||||||||
} | ||||||||||||||||||||||
if len(certificates) == 0 { | ||||||||||||||||||||||
return nil, fmt.Errorf("no certificates found") | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
id, err := x509svid.IDFromCert(certificates[0]) | ||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||
return nil, err | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
privateKey, ok := tlsCert.PrivateKey.(crypto.Signer) | ||||||||||||||||||||||
if !ok { | ||||||||||||||||||||||
return nil, fmt.Errorf("agent certificate private key type %T is unexpectedly not a signer", tlsCert.PrivateKey) | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
return &x509svid.SVID{ | ||||||||||||||||||||||
ID: id, | ||||||||||||||||||||||
Certificates: certificates, | ||||||||||||||||||||||
PrivateKey: privateKey, | ||||||||||||||||||||||
}, nil | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
type bundleSource struct { | ||||||||||||||||||||||
getter func(*spiffeid.TrustDomain) ([]*x509.Certificate, error) | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think we to pass a pointer to the trust domain into the getter. The trust domain struct type is intended to be passed by value (it holds a single string for state).
Suggested change
|
||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
func newBundleSource(getter func(*spiffeid.TrustDomain) ([]*x509.Certificate, error)) x509bundle.Source { | ||||||||||||||||||||||
return &bundleSource{getter: getter} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
func (bs *bundleSource) GetX509BundleForTrustDomain(trustDomain spiffeid.TrustDomain) (*x509bundle.Bundle, error) { | ||||||||||||||||||||||
authorities, err := bs.getter(&trustDomain) | ||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||
return nil, err | ||||||||||||||||||||||
} | ||||||||||||||||||||||
bundle := x509bundle.FromX509Authorities(trustDomain, authorities) | ||||||||||||||||||||||
return bundle.GetX509BundleForTrustDomain(trustDomain) | ||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,173 @@ | ||||||
package endpoints | ||||||
|
||||||
import ( | ||||||
"crypto/tls" | ||||||
"crypto/x509" | ||||||
"encoding/hex" | ||||||
"fmt" | ||||||
"testing" | ||||||
|
||||||
"github.com/spiffe/go-spiffe/v2/bundle/x509bundle" | ||||||
"github.com/spiffe/go-spiffe/v2/spiffeid" | ||||||
"github.com/spiffe/go-spiffe/v2/svid/x509svid" | ||||||
"github.com/spiffe/spire/test/testca" | ||||||
"github.com/stretchr/testify/assert" | ||||||
) | ||||||
|
||||||
var ( | ||||||
testRSACertificate = fromHex("3082024b308201b4a003020102020900e8f09d3fe25beaa6300d06092a864886f70d01010b0500301f310b3009060355040a1302476f3110300e06035504031307476f20526f6f74301e170d3136303130313030303030305a170d3235303130313030303030305a301a310b3009060355040a1302476f310b300906035504031302476f30819f300d06092a864886f70d010101050003818d0030818902818100db467d932e12270648bc062821ab7ec4b6a25dfe1e5245887a3647a5080d92425bc281c0be97799840fb4f6d14fd2b138bc2a52e67d8d4099ed62238b74a0b74732bc234f1d193e596d9747bf3589f6c613cc0b041d4d92b2b2423775b1c3bbd755dce2054cfa163871d1e24c4f31d1a508baab61443ed97a77562f414c852d70203010001a38193308190300e0603551d0f0101ff0404030205a0301d0603551d250416301406082b0601050507030106082b06010505070302300c0603551d130101ff0402300030190603551d0e041204109f91161f43433e49a6de6db680d79f60301b0603551d230414301280104813494d137e1631bba301d5acab6e7b30190603551d1104123010820e6578616d706c652e676f6c616e67300d06092a864886f70d01010b0500038181009d30cc402b5b50a061cbbae55358e1ed8328a9581aa938a495a1ac315a1a84663d43d32dd90bf297dfd320643892243a00bccf9c7db74020015faad3166109a276fd13c3cce10c5ceeb18782f16c04ed73bbb343778d0c1cf10fa1d8408361c94c722b9daedb4606064df4c1b33ec0d1bd42d4dbfe3d1360845c21d33be9fae7") | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Typically we include the PEM encoded certificate in tests and use the pemutil.ParseCertificate. It's a little more readable that way. |
||||||
) | ||||||
|
||||||
func Test_x509SVIDSource_GetX509SVID(t *testing.T) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should try and follow the naming guidelines from the testing package. In this case, since there is only one part of the x509svid source that we are testing, I'd suggest the following:
Suggested change
|
||||||
ca := testca.New(t, spiffeid.RequireTrustDomainFromString("example.org")) | ||||||
|
||||||
serverCert, serverKey := ca.CreateX509Certificate( | ||||||
testca.WithID(spiffeid.RequireFromPath(trustDomain, "/spire/server")), | ||||||
) | ||||||
certRaw := make([][]byte, len(serverCert)) | ||||||
for i, cert := range serverCert { | ||||||
certRaw[i] = cert.Raw | ||||||
} | ||||||
|
||||||
type fields struct { | ||||||
getter func() *tls.Certificate | ||||||
} | ||||||
tests := []struct { | ||||||
name string | ||||||
fields fields | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not just have getter here? what benefit do we get from the fields struct? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. usually i create the fields struct for increasing the test readability, but in this case it can be simplified since there's just one field |
||||||
want *x509svid.SVID | ||||||
wantErr error | ||||||
}{ | ||||||
{ | ||||||
name: "success, with certificate", | ||||||
fields: fields{func() *tls.Certificate { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: we generally discourage struct initialization using positional fields and prefer naming the fields explicitly. |
||||||
return &tls.Certificate{ | ||||||
Certificate: certRaw, | ||||||
PrivateKey: serverKey, | ||||||
} | ||||||
}}, | ||||||
want: &x509svid.SVID{ | ||||||
ID: spiffeid.RequireFromString("spiffe://example.org/spire/server"), | ||||||
Certificates: serverCert, | ||||||
PrivateKey: serverKey, | ||||||
}, | ||||||
}, | ||||||
{ | ||||||
name: "error, malformed certificate", | ||||||
fields: fields{func() *tls.Certificate { | ||||||
return &tls.Certificate{ | ||||||
Certificate: [][]byte{testRSACertificate[1:]}, | ||||||
PrivateKey: serverKey, | ||||||
} | ||||||
}}, | ||||||
wantErr: fmt.Errorf("x509: malformed certificate"), | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: when not using formatting, prefer |
||||||
}, | ||||||
{ | ||||||
name: "error, certificate with no uri", | ||||||
fields: fields{func() *tls.Certificate { | ||||||
return &tls.Certificate{ | ||||||
Certificate: [][]byte{testRSACertificate}, | ||||||
PrivateKey: serverKey, | ||||||
} | ||||||
}}, | ||||||
wantErr: fmt.Errorf("certificate contains no URI SAN"), | ||||||
}, | ||||||
{ | ||||||
name: "error, with empty certificates", | ||||||
fields: fields{func() *tls.Certificate { | ||||||
return &tls.Certificate{ | ||||||
Certificate: [][]byte{}, | ||||||
PrivateKey: serverKey, | ||||||
} | ||||||
}}, | ||||||
wantErr: fmt.Errorf("no certificates found"), | ||||||
}, | ||||||
{ | ||||||
name: "error, with no private key", | ||||||
fields: fields{func() *tls.Certificate { | ||||||
return &tls.Certificate{ | ||||||
Certificate: [][]byte{serverCert[0].Raw}, | ||||||
} | ||||||
}}, | ||||||
wantErr: fmt.Errorf("agent certificate private key type <nil> is unexpectedly not a signer"), | ||||||
}, | ||||||
} | ||||||
for _, tt := range tests { | ||||||
t.Run(tt.name, func(t *testing.T) { | ||||||
xs := newX509SVIDSource(tt.fields.getter) | ||||||
got, err := xs.GetX509SVID() | ||||||
if tt.wantErr != nil { | ||||||
assert.EqualError(t, err, tt.wantErr.Error()) | ||||||
} else { | ||||||
assert.Equal(t, tt.want.ID, got.ID) | ||||||
|
||||||
assert.Equal(t, tt.want, got) | ||||||
} | ||||||
}) | ||||||
} | ||||||
} | ||||||
|
||||||
func Test_bundleSource_GetX509BundleForTrustDomain(t *testing.T) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
type fields struct { | ||||||
getter func(*spiffeid.TrustDomain) ([]*x509.Certificate, error) | ||||||
} | ||||||
type args struct { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What benefit do we get from the args struct? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same thing about the fields struct, but we can simplify here also 😄 |
||||||
trustDomain spiffeid.TrustDomain | ||||||
} | ||||||
tests := []struct { | ||||||
name string | ||||||
fields fields | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same thing here. |
||||||
args args | ||||||
want *x509bundle.Bundle | ||||||
wantErr error | ||||||
}{ | ||||||
{ | ||||||
name: "success, with authorities", | ||||||
fields: fields{func(domain *spiffeid.TrustDomain) ([]*x509.Certificate, error) { | ||||||
return []*x509.Certificate{&x509.Certificate{}}, nil | ||||||
}}, | ||||||
args: args{ | ||||||
trustDomain: spiffeid.RequireTrustDomainFromString("example.org"), | ||||||
}, | ||||||
want: x509bundle.FromX509Authorities( | ||||||
spiffeid.RequireTrustDomainFromString("example.org"), | ||||||
[]*x509.Certificate{&x509.Certificate{}}), | ||||||
}, | ||||||
{ | ||||||
name: "success, empty authorities list", | ||||||
fields: fields{func(domain *spiffeid.TrustDomain) ([]*x509.Certificate, error) { | ||||||
return []*x509.Certificate{}, nil | ||||||
}}, | ||||||
args: args{ | ||||||
trustDomain: spiffeid.RequireTrustDomainFromString("example.org"), | ||||||
}, | ||||||
want: x509bundle.FromX509Authorities(spiffeid.RequireTrustDomainFromString("example.org"), []*x509.Certificate{}), | ||||||
}, | ||||||
{ | ||||||
name: "error, error on getter function", | ||||||
fields: fields{func(domain *spiffeid.TrustDomain) ([]*x509.Certificate, error) { | ||||||
return nil, fmt.Errorf("some error") | ||||||
}}, | ||||||
args: args{ | ||||||
trustDomain: spiffeid.RequireTrustDomainFromString("example.org"), | ||||||
}, | ||||||
wantErr: fmt.Errorf("some error"), | ||||||
}, | ||||||
} | ||||||
for _, tt := range tests { | ||||||
t.Run(tt.name, func(t *testing.T) { | ||||||
bs := newBundleSource(tt.fields.getter) | ||||||
got, err := bs.GetX509BundleForTrustDomain(tt.args.trustDomain) | ||||||
if tt.wantErr != nil { | ||||||
assert.EqualError(t, err, tt.wantErr.Error()) | ||||||
} else { | ||||||
assert.Equal(t, tt.want, got) | ||||||
} | ||||||
}) | ||||||
} | ||||||
} | ||||||
|
||||||
func fromHex(s string) []byte { | ||||||
b, _ := hex.DecodeString(s) | ||||||
return b | ||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is something up with the indentation here. Also, i think this file has imposed a column limit on comments, we should probably stick with that.