Skip to content

Commit

Permalink
Machine ID: SPIFFE support in tbot (#37772)
Browse files Browse the repository at this point in the history
* Experiment with issuing SVIDs from `tbot`

* Fix missing license headers

* Remove interceptors for now

* Add basic required grpc server interceptors

* Break out CA rotation handler

* Tidy up service structure and omit more cert attributes

* Various tidying and adding godoc comments

* Move dependency to main require block

* Fix tests and add test for spiffe-workload-api in config

* Add config tests for SPIFFESVIDOutput

* Add config tests for SPIFFEWorkloadAPIService

* Add otel spans

* Add e2e test for the spiffe workload api functionality

* Fix trust bundle fetching to use correct client

* Reuse outputs service to produce identity for spiffe workload service

* Add godocs for SPIFFESVIDOUTPUT

* Tidy up Render

* Add consts for pem types

* Improve logging

Co-authored-by: Isaiah Becker-Mayer <[email protected]>

---------

Co-authored-by: Isaiah Becker-Mayer <[email protected]>
  • Loading branch information
strideynet and Isaiah Becker-Mayer authored Mar 6, 2024
1 parent 4c26c2a commit 5a00a67
Show file tree
Hide file tree
Showing 24 changed files with 1,532 additions and 81 deletions.
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ require (
github.com/gravitational/trace v1.3.1
github.com/gravitational/ttlmap v0.0.0-20171116003245-91fd36b9004c
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.0
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.1
github.com/guptarohit/asciigraph v0.5.6
github.com/hashicorp/go-version v1.6.0
github.com/hashicorp/golang-lru/v2 v2.0.7
Expand Down Expand Up @@ -165,6 +166,7 @@ require (
github.com/sirupsen/logrus v1.9.3
github.com/snowflakedb/gosnowflake v1.8.0
github.com/spf13/cobra v1.8.0
github.com/spiffe/go-spiffe/v2 v2.1.7
github.com/stretchr/testify v1.8.4
github.com/tiktoken-go/tokenizer v0.1.0
github.com/ucarion/urlpath v0.0.0-20200424170820-7ccc79b76bbb
Expand Down Expand Up @@ -355,7 +357,6 @@ require (
github.com/gorilla/mux v1.8.1 // indirect
github.com/gosuri/uitable v0.0.4 // indirect
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.1 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 // indirect
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect
Expand Down Expand Up @@ -488,6 +489,7 @@ require (
github.com/xlab/treeprint v1.2.0 // indirect
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
github.com/yuin/gopher-lua v1.1.0 // indirect
github.com/zeebo/errs v1.3.0 // indirect
github.com/zeebo/xxh3 v1.0.2 // indirect
github.com/zmap/zcrypto v0.0.0-20230310154051-c8b263fd8300 // indirect
github.com/zmap/zlint/v3 v3.5.0 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1357,6 +1357,8 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
github.com/spiffe/go-spiffe/v2 v2.1.7 h1:VUkM1yIyg/x8X7u1uXqSRVRCdMdfRIEdFBzpqoeASGk=
github.com/spiffe/go-spiffe/v2 v2.1.7/go.mod h1:QJDGdhXllxjxvd5B+2XnhhXB/+rC8gr+lNrtOryiWeE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
Expand Down Expand Up @@ -1441,6 +1443,8 @@ github.com/zalando/go-keyring v0.2.2 h1:f0xmpYiSrHtSNAVgwip93Cg8tuF45HJM6rHq/A5R
github.com/zalando/go-keyring v0.2.2/go.mod h1:sI3evg9Wvpw3+n4SqplGSJUMwtDeROfD4nsFz4z9PG0=
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
github.com/zeebo/errs v1.3.0 h1:hmiaKqgYZzcVgRL1Vkc1Mn2914BbzB0IBxs+ebeutGs=
github.com/zeebo/errs v1.3.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4=
github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
Expand Down
44 changes: 0 additions & 44 deletions lib/tbot/bot/service.go

This file was deleted.

6 changes: 6 additions & 0 deletions lib/tbot/config/bot.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,11 @@ package config
import (
"context"

"google.golang.org/grpc"

"github.com/gravitational/teleport/api/client/proto"
"github.com/gravitational/teleport/api/client/webclient"
machineidv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/machineid/v1"
trustpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/trust/v1"
"github.com/gravitational/teleport/api/types"
)
Expand Down Expand Up @@ -55,4 +58,7 @@ type provider interface {

// GetCertAuthority uses the impersonatedClient to call GetCertAuthority.
GetCertAuthority(ctx context.Context, id types.CertAuthID, loadKeys bool) (types.CertAuthority, error)

// SignX509SVIDs uses the impersonatedClient to call SignX509SVIDs.
SignX509SVIDs(ctx context.Context, in *machineidv1pb.SignX509SVIDsRequest, opts ...grpc.CallOption) (*machineidv1pb.SignX509SVIDsResponse, error)
}
10 changes: 10 additions & 0 deletions lib/tbot/config/bot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@ import (
"github.com/jonboulle/clockwork"
"github.com/stretchr/testify/require"
"golang.org/x/crypto/ssh"
"google.golang.org/grpc"

"github.com/gravitational/teleport/api/client/proto"
"github.com/gravitational/teleport/api/client/webclient"
"github.com/gravitational/teleport/api/constants"
machineidv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/machineid/v1"
trustpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/trust/v1"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/lib/auth/testauthority"
Expand Down Expand Up @@ -143,6 +145,14 @@ func (p *mockProvider) AuthPing(_ context.Context) (*proto.PingResponse, error)
}, nil
}

func (p *mockProvider) SignX509SVIDs(
ctx context.Context,
in *machineidv1pb.SignX509SVIDsRequest,
opts ...grpc.CallOption,
) (*machineidv1pb.SignX509SVIDsResponse, error) {
return nil, nil
}

func (p *mockProvider) GenerateHostCert(
ctx context.Context, req *trustpb.GenerateHostCertRequest,
) (*trustpb.GenerateHostCertResponse, error) {
Expand Down
35 changes: 30 additions & 5 deletions lib/tbot/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ type BotConfig struct {
Onboarding OnboardingConfig `yaml:"onboarding,omitempty"`
Storage *StorageConfig `yaml:"storage,omitempty"`
Outputs Outputs `yaml:"outputs,omitempty"`
Services Services `yaml:"services,omitempty"`
Services ServiceConfigs `yaml:"services,omitempty"`

Debug bool `yaml:"debug"`
AuthServer string `yaml:"auth_server"`
Expand Down Expand Up @@ -341,6 +341,13 @@ func (conf *BotConfig) CheckAndSetDefaults() error {
}
}

// Validate configured services
for i, service := range conf.Services {
if err := service.CheckAndSetDefaults(); err != nil {
return trace.Wrap(err, "validating service[%d]", i)
}
}

if conf.CertificateTTL == 0 {
conf.CertificateTTL = DefaultCertificateTTL
}
Expand Down Expand Up @@ -385,11 +392,17 @@ func (conf *BotConfig) CheckAndSetDefaults() error {
return nil
}

// Services assists polymorphic unmarshaling of a slice of Services.
type Services []bot.Service
// ServiceConfig is an interface over the various service configurations.
type ServiceConfig interface {
Type() string
CheckAndSetDefaults() error
}

func (o *Services) UnmarshalYAML(node *yaml.Node) error {
var out []bot.Service
// ServiceConfigs assists polymorphic unmarshaling of a slice of ServiceConfigs.
type ServiceConfigs []ServiceConfig

func (o *ServiceConfigs) UnmarshalYAML(node *yaml.Node) error {
var out []ServiceConfig
for _, node := range node.Content {
header := struct {
Type string `yaml:"type"`
Expand All @@ -405,6 +418,12 @@ func (o *Services) UnmarshalYAML(node *yaml.Node) error {
return trace.Wrap(err)
}
out = append(out, v)
case SPIFFEWorkloadAPIServiceType:
v := &SPIFFEWorkloadAPIService{}
if err := node.Decode(v); err != nil {
return trace.Wrap(err)
}
out = append(out, v)
default:
return trace.BadParameter("unrecognized service type (%s)", header.Type)
}
Expand Down Expand Up @@ -458,6 +477,12 @@ func (o *Outputs) UnmarshalYAML(node *yaml.Node) error {
return trace.Wrap(err)
}
out = append(out, v)
case SPIFFESVIDOutputType:
v := &SPIFFESVIDOutput{}
if err := node.Decode(v); err != nil {
return trace.Wrap(err)
}
out = append(out, v)
default:
return trace.BadParameter("unrecognized output type (%s)", header.Type)
}
Expand Down
15 changes: 14 additions & 1 deletion lib/tbot/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,20 @@ func TestBotConfig_YAML(t *testing.T) {
},
},
},
Services: []bot.Service{
Services: []ServiceConfig{
&SPIFFEWorkloadAPIService{
Listen: "unix:///var/run/spiffe.sock",
SVIDs: []SVIDRequest{
{
Path: "/bar",
Hint: "my hint",
SANS: SVIDRequestSANs{
DNS: []string{"foo.bar"},
IP: []string{"10.0.0.1"},
},
},
},
},
&ExampleService{
Message: "llama",
},
Expand Down
10 changes: 10 additions & 0 deletions lib/tbot/config/output_client_credential.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,16 @@ func (o *UnstableClientCredentialOutput) SSHClientConfig() (*ssh.ClientConfig, e
return o.facade.SSHClientConfig()
}

// Facade returns the underlying facade
func (o *UnstableClientCredentialOutput) Facade() (*identity.Facade, error) {
o.mu.Lock()
defer o.mu.Unlock()
if o.facade == nil {
return nil, trace.BadParameter("credentials not yet ready")
}
return o.facade, nil
}

// Render implements the Destination interface and is called regularly by the
// bot with new credentials. Render passes these credentials down to the
// underlying facade so that they can be used in TLS/SSH configs.
Expand Down
Loading

0 comments on commit 5a00a67

Please sign in to comment.