Skip to content

Commit

Permalink
feat: implement basic RBAC interceptors
Browse files Browse the repository at this point in the history
It is not enforced yet.

Refs siderolabs#3421.

Signed-off-by: Alexey Palazhchenko <alexey.palazhchenko@gmail.com>
  • Loading branch information
AlekSi committed Jun 4, 2021
1 parent 62c702c commit eaa71e0
Show file tree
Hide file tree
Showing 30 changed files with 656 additions and 85 deletions.
2 changes: 1 addition & 1 deletion cmd/talosctl/cmd/mgmt/cluster/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -757,7 +757,7 @@ func init() {
createCmd.Flags().StringVar(&nodeInstallImage, "install-image", helpers.DefaultImage(images.DefaultInstallerImageRepository), "the installer image to use")
createCmd.Flags().StringVar(&nodeVmlinuzPath, "vmlinuz-path", helpers.ArtifactPath(constants.KernelAssetWithArch), "the compressed kernel image to use")
createCmd.Flags().StringVar(&nodeISOPath, "iso-path", "", "the ISO path to use for the initial boot (VM only)")
createCmd.Flags().StringVar(&nodeInitramfsPath, "initrd-path", helpers.ArtifactPath(constants.InitramfsAssetWithArch), "the uncompressed kernel image to use")
createCmd.Flags().StringVar(&nodeInitramfsPath, "initrd-path", helpers.ArtifactPath(constants.InitramfsAssetWithArch), "initramfs image to use")
createCmd.Flags().StringVar(&nodeDiskImagePath, "disk-image-path", "", "disk image to use")
createCmd.Flags().BoolVar(&applyConfigEnabled, "with-apply-config", false, "enable apply config when the VM is starting in maintenance mode")
createCmd.Flags().BoolVar(&bootloaderEnabled, "with-bootloader", true, "enable bootloader to load kernel and initramfs from disk image after install")
Expand Down
6 changes: 3 additions & 3 deletions cmd/talosctl/cmd/mgmt/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ var genConfigCmd = &cobra.Command{
Use: "config <cluster name> <cluster endpoint>",
Short: "Generates a set of configuration files for Talos cluster",
Long: `The cluster endpoint is the URL for the Kubernetes API. If you decide to use
a control plane node, common in a single node control plane setup, use port 6443 as
this is the port that the API server binds to on every control plane node. For an HA
setup, usually involving a load balancer, use the IP and port of the load balancer.`,
a control plane node, common in a single node control plane setup, use port 6443 as
this is the port that the API server binds to on every control plane node. For an HA
setup, usually involving a load balancer, use the IP and port of the load balancer.`,
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
// Validate url input to ensure it has https:// scheme before we attempt to gen
Expand Down
15 changes: 11 additions & 4 deletions cmd/talosctl/cmd/mgmt/gen/csr.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@ import (
"github.com/talos-systems/crypto/x509"

"github.com/talos-systems/talos/pkg/cli"
"github.com/talos-systems/talos/pkg/machinery/constants"
"github.com/talos-systems/talos/pkg/machinery/role"
)

var genCSRCmdFlags struct {
key string
ip string
key string
ip string
roles []string
}

// genCSRCmd represents the `gen csr` command.
Expand Down Expand Up @@ -54,8 +55,13 @@ var genCSRCmd = &cobra.Command{
return fmt.Errorf("invalid IP: %s", genCSRCmdFlags.ip)
}

roles, err := role.Parse(genCSRCmdFlags.roles)
if err != nil {
return err
}

ips := []net.IP{parsed}
opts = append(opts, x509.Organization(constants.RoleAdmin))
opts = append(opts, x509.Organization(roles.Strings()...))
opts = append(opts, x509.IPAddresses(ips))

csr, err := x509.NewCertificateSigningRequest(keyEC, opts...)
Expand All @@ -76,6 +82,7 @@ func init() {
cli.Should(cobra.MarkFlagRequired(genCSRCmd.Flags(), "key"))
genCSRCmd.Flags().StringVar(&genCSRCmdFlags.ip, "ip", "", "generate the certificate for this IP address")
cli.Should(cobra.MarkFlagRequired(genCSRCmd.Flags(), "ip"))
genCSRCmd.Flags().StringSliceVar(&genCSRCmdFlags.roles, "roles", role.MakeSet(role.Admin).Strings(), "roles")

Cmd.AddCommand(genCSRCmd)
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ require (
github.com/smira/go-xz v0.0.0-20201019130106-9921ed7a9935
github.com/spf13/cobra v1.1.3
github.com/stretchr/testify v1.7.0
github.com/talos-systems/crypto v0.2.1-0.20210526123943-7776057f5086
github.com/talos-systems/crypto v0.2.1-0.20210601174604-cd18ef62eb9f
github.com/talos-systems/go-blockdevice v0.2.1-0.20210526155905-30c2bc3cb62a
github.com/talos-systems/go-cmd v0.0.0-20210216164758-68eb0067e0f0
github.com/talos-systems/go-debug v0.2.1-0.20210525175311-3d0a6e1bf5e3
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1220,8 +1220,8 @@ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/talos-systems/crypto v0.2.1-0.20210526123943-7776057f5086 h1:SAyrAftTtxzEUqr9alFt1iezS5vuwCm7/yE8ydR0h+A=
github.com/talos-systems/crypto v0.2.1-0.20210526123943-7776057f5086/go.mod h1:xaNCB2/Bxaj+qrkdeodhRv5eKQVvKOGBBMj58MrIPY8=
github.com/talos-systems/crypto v0.2.1-0.20210601174604-cd18ef62eb9f h1:Xk3zeUZPhvEl9Vs4PlYBohin3QZmizA/YR4URKEyULY=
github.com/talos-systems/crypto v0.2.1-0.20210601174604-cd18ef62eb9f/go.mod h1:xaNCB2/Bxaj+qrkdeodhRv5eKQVvKOGBBMj58MrIPY8=
github.com/talos-systems/go-blockdevice v0.2.1-0.20210526155905-30c2bc3cb62a h1:NLuIVKi5tBnRMgxk185AVGmMUzlRcggb2Abrw9uUq3E=
github.com/talos-systems/go-blockdevice v0.2.1-0.20210526155905-30c2bc3cb62a/go.mod h1:qnn/zDc09I1DA2BUDDCOSA2D0P8pIDjN8pGiRoRaQig=
github.com/talos-systems/go-cmd v0.0.0-20210216164758-68eb0067e0f0 h1:DI+BjK+fcrLBc70Fi50dZocQcaHosqsuWHrGHKp2NzE=
Expand Down
15 changes: 15 additions & 0 deletions internal/app/apid/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/talos-systems/talos/internal/app/apid/pkg/director"
"github.com/talos-systems/talos/internal/app/apid/pkg/provider"
"github.com/talos-systems/talos/pkg/grpc/factory"
"github.com/talos-systems/talos/pkg/grpc/middleware/authz"
"github.com/talos-systems/talos/pkg/grpc/proxy/backend"
"github.com/talos-systems/talos/pkg/machinery/config/configloader"
"github.com/talos-systems/talos/pkg/machinery/constants"
Expand Down Expand Up @@ -118,6 +119,11 @@ func Main() {
var errGroup errgroup.Group

errGroup.Go(func() error {
injector := &authz.Injector{
TrustMetadata: false,
Logger: log.New(log.Writer(), "apid/authz/injector/http ", log.Flags()).Printf,
}

return factory.ListenAndServe(
router,
factory.Port(constants.ApidPort),
Expand All @@ -133,10 +139,17 @@ func Main() {
proxy.WithStreamedDetector(router.StreamedDetector),
)),
),
factory.WithUnaryInterceptor(injector.UnaryInterceptor()),
factory.WithStreamInterceptor(injector.StreamInterceptor()),
)
})

errGroup.Go(func() error {
injector := &authz.Injector{
TrustMetadata: true,
Logger: log.New(log.Writer(), "apid/authz/injector/unix ", log.Flags()).Printf,
}

return factory.ListenAndServe(
router,
factory.Network("unix"),
Expand All @@ -150,6 +163,8 @@ func Main() {
proxy.WithStreamedDetector(router.StreamedDetector),
)),
),
factory.WithUnaryInterceptor(injector.UnaryInterceptor()),
factory.WithStreamInterceptor(injector.StreamInterceptor()),
)
})

Expand Down
3 changes: 3 additions & 0 deletions internal/app/apid/pkg/backend/apid.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"google.golang.org/protobuf/encoding/protowire"
"google.golang.org/protobuf/proto"

"github.com/talos-systems/talos/pkg/grpc/middleware/authz"
"github.com/talos-systems/talos/pkg/machinery/api/common"
"github.com/talos-systems/talos/pkg/machinery/constants"
)
Expand Down Expand Up @@ -64,6 +65,8 @@ func (a *APID) GetConnection(ctx context.Context) (context.Context, *grpc.Client
md.Set("proxyfrom", "unknown")
}

authz.SetRolesToMetadata(ctx, md)

outCtx := metadata.NewOutgoingContext(ctx, md)

a.mu.Lock()
Expand Down
58 changes: 43 additions & 15 deletions internal/app/apid/pkg/backend/apid_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ import (
"google.golang.org/protobuf/proto"

"github.com/talos-systems/talos/internal/app/apid/pkg/backend"
"github.com/talos-systems/talos/pkg/grpc/middleware/authz"
"github.com/talos-systems/talos/pkg/machinery/api/common"
"github.com/talos-systems/talos/pkg/machinery/role"
)

func TestAPIDInterfaces(t *testing.T) {
Expand All @@ -38,29 +40,55 @@ func (suite *APIDSuite) SetupSuite() {
}

func (suite *APIDSuite) TestGetConnection() {
md := metadata.New(nil)
md.Set(":authority", "127.0.0.2")
md.Set("nodes", "127.0.0.1")
md.Set("key", "value1", "value2")
ctx := metadata.NewIncomingContext(context.Background(), md)
md1 := metadata.New(nil)
md1.Set(":authority", "127.0.0.2")
md1.Set("nodes", "127.0.0.1")
md1.Set("key", "value1", "value2")
ctx1 := metadata.NewIncomingContext(authz.ContextWithRoles(context.Background(), role.MakeSet(role.Admin)), md1)

outCtx1, conn1, err1 := suite.b.GetConnection(ctx)
outCtx1, conn1, err1 := suite.b.GetConnection(ctx1)
suite.Require().NoError(err1)
suite.Assert().NotNil(conn1)
suite.Assert().Equal(role.MakeSet(role.Admin), authz.GetRoles(outCtx1))

mdOut1, ok1 := metadata.FromOutgoingContext(outCtx1)
suite.Require().True(ok1)
suite.Assert().Equal([]string{"value1", "value2"}, mdOut1.Get("key"))
suite.Assert().Equal([]string{"127.0.0.2"}, mdOut1.Get("proxyfrom"))

outCtx2, conn2, err2 := suite.b.GetConnection(ctx)
suite.Require().NoError(err2)
suite.Assert().Equal(conn1, conn2) // connection is cached

mdOut2, ok2 := metadata.FromOutgoingContext(outCtx2)
suite.Require().True(ok2)
suite.Assert().Equal([]string{"value1", "value2"}, mdOut2.Get("key"))
suite.Assert().Equal([]string{"127.0.0.2"}, mdOut2.Get("proxyfrom"))
suite.Assert().Equal([]string{"os:admin"}, mdOut1.Get("talos-role"))

suite.Run("Same context", func() {
ctx2 := ctx1
outCtx2, conn2, err2 := suite.b.GetConnection(ctx2)
suite.Require().NoError(err2)
suite.Assert().Equal(conn1, conn2) // connection is cached
suite.Assert().Equal(role.MakeSet(role.Admin), authz.GetRoles(outCtx2))

mdOut2, ok2 := metadata.FromOutgoingContext(outCtx2)
suite.Require().True(ok2)
suite.Assert().Equal([]string{"value1", "value2"}, mdOut2.Get("key"))
suite.Assert().Equal([]string{"127.0.0.2"}, mdOut2.Get("proxyfrom"))
suite.Assert().Equal([]string{"os:admin"}, mdOut2.Get("talos-role"))
})

suite.Run("Other context", func() {
md3 := metadata.New(nil)
md3.Set(":authority", "127.0.0.2")
md3.Set("nodes", "127.0.0.1")
md3.Set("key", "value3", "value4")
ctx3 := metadata.NewIncomingContext(authz.ContextWithRoles(context.Background(), role.MakeSet(role.Reader)), md3)

outCtx3, conn3, err3 := suite.b.GetConnection(ctx3)
suite.Require().NoError(err3)
suite.Assert().Equal(conn1, conn3) // connection is cached
suite.Assert().Equal(role.MakeSet(role.Reader), authz.GetRoles(outCtx3))

mdOut3, ok3 := metadata.FromOutgoingContext(outCtx3)
suite.Require().True(ok3)
suite.Assert().Equal([]string{"value3", "value4"}, mdOut3.Get("key"))
suite.Assert().Equal([]string{"127.0.0.2"}, mdOut3.Get("proxyfrom"))
suite.Assert().Equal([]string{"os:reader"}, mdOut3.Get("talos-role"))
})
}

func (suite *APIDSuite) TestAppendInfoUnary() {
Expand Down
8 changes: 5 additions & 3 deletions internal/app/apid/pkg/provider/tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (

"github.com/talos-systems/talos/pkg/grpc/gen"
"github.com/talos-systems/talos/pkg/machinery/config"
"github.com/talos-systems/talos/pkg/machinery/role"
)

// TLSConfig provides client & server TLS configs for apid.
Expand Down Expand Up @@ -67,13 +68,14 @@ func NewTLSConfig(config config.Provider, endpoints Endpoints) (*TLSConfig, erro
endpointList,
)
if err != nil {
return nil, fmt.Errorf("failed to create remote certificate genertor: %w", err)
return nil, fmt.Errorf("failed to create remote certificate generator: %w", err)
}

tlsConfig.certificateProvider, err = tls.NewRenewingCertificateProvider(
tlsConfig.generator,
dnsNames,
ips,
x509.DNSNames(dnsNames),
x509.IPAddresses(ips),
x509.Organization(string(role.Impersonator)),
)
if err != nil {
return nil, err
Expand Down
2 changes: 1 addition & 1 deletion internal/app/machined/pkg/controllers/k8s/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ roleRef:
// certificates.
//
// This binding should be altered in the future to hold a list of node
// names instead of targeting `system:nodes` so we can revoke invidivual
// names instead of targeting `system:nodes` so we can revoke individual
// node's ability to renew its certs.
var csrRenewalRoleBindingTemplate = []byte(`kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
Expand Down
27 changes: 27 additions & 0 deletions internal/app/machined/pkg/system/services/machined.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package services
import (
"context"
"io"
"log"

v1alpha1server "github.com/talos-systems/talos/internal/app/machined/internal/server/v1alpha1"
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime"
Expand All @@ -16,7 +17,9 @@ import (
"github.com/talos-systems/talos/internal/app/machined/pkg/system/runner/goroutine"
"github.com/talos-systems/talos/pkg/conditions"
"github.com/talos-systems/talos/pkg/grpc/factory"
"github.com/talos-systems/talos/pkg/grpc/middleware/authz"
"github.com/talos-systems/talos/pkg/machinery/constants"
"github.com/talos-systems/talos/pkg/machinery/role"
)

type machinedService struct {
Expand All @@ -25,12 +28,36 @@ type machinedService struct {

// Main is an entrypoint the the API service.
func (s *machinedService) Main(ctx context.Context, r runtime.Runtime, logWriter io.Writer) error {
injector := &authz.Injector{
TrustMetadata: true,
Logger: log.New(logWriter, "machined/authz/injector ", log.Flags()).Printf,
}

authorizer := &authz.Authorizer{
Rules: map[string]role.Set{
"/cluster.ClusterService/HealthCheck": role.MakeSet(role.Admin, role.Reader),
"/machine.MachineService/List": role.MakeSet(role.Admin, role.Reader),
"/machine.MachineService/Version": role.MakeSet(role.Admin, role.Reader),

// TODO(rbac): More rules
},
FallbackRoles: role.MakeSet(role.Admin),
DontEnforce: true, // TODO(rbac): Should be configurable with a feature gate
Logger: log.New(logWriter, "machined/authz/authorizer ", log.Flags()).Printf,
}

// Start the API server.
server := factory.NewServer(
&v1alpha1server.Server{
Controller: s.c,
},
factory.WithLog("machined ", logWriter),

factory.WithUnaryInterceptor(injector.UnaryInterceptor()),
factory.WithStreamInterceptor(injector.StreamInterceptor()),

factory.WithUnaryInterceptor(authorizer.UnaryInterceptor()),
factory.WithStreamInterceptor(authorizer.StreamInterceptor()),
)

listener, err := factory.NewListener(factory.Network("unix"), factory.SocketPath(constants.MachineSocketPath))
Expand Down
2 changes: 1 addition & 1 deletion internal/app/maintenance/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ func genTLSConfig(ips []net.IP) (tlsConfig *tls.Config, provider ttls.Certificat
return nil, nil, fmt.Errorf("failed to create local generator provider: %w", err)
}

provider, err = ttls.NewRenewingCertificateProvider(generator, dnsNames, ips)
provider, err = ttls.NewRenewingCertificateProvider(generator, x509.DNSNames(dnsNames), x509.IPAddresses(ips))
if err != nil {
return nil, nil, fmt.Errorf("failed to create local certificate provider: %w", err)
}
Expand Down
3 changes: 2 additions & 1 deletion internal/app/trustd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
stdlibnet "net"

"github.com/talos-systems/crypto/tls"
"github.com/talos-systems/crypto/x509"
debug "github.com/talos-systems/go-debug"
"github.com/talos-systems/net"
"google.golang.org/grpc"
Expand Down Expand Up @@ -84,7 +85,7 @@ func Main() {

var provider tls.CertificateProvider

provider, err = tls.NewRenewingCertificateProvider(generator, dnsNames, ips)
provider, err = tls.NewRenewingCertificateProvider(generator, x509.DNSNames(dnsNames), x509.IPAddresses(ips))
if err != nil {
log.Fatalln("failed to create local certificate provider:", err)
}
Expand Down
Loading

0 comments on commit eaa71e0

Please sign in to comment.