Skip to content

Commit

Permalink
tkn-results: Add service account based auth.
Browse files Browse the repository at this point in the history
The Result API accepts bearer token auth, which is then checked against
the cluster. For human accounts, this means accepting a token that
likely has much higher priviledge than the Result API needs. As an
alternative, this adds support for fetching a service account bearer
token as a delegate for Result operations - the service account is
expected to have much finer permissions, reducing the scope / blast
radius of the credential.

This change:
- Adds a service_account config field
- Refactors client creation to allow for fake dependencies (i.e. k8s
client) to be injected for tests.
- Adds tests for token generation, SSL cert reading.
  • Loading branch information
wlynch authored and tekton-robot committed Dec 2, 2021
1 parent 7d71633 commit ff00628
Show file tree
Hide file tree
Showing 10 changed files with 1,014 additions and 89 deletions.
31 changes: 31 additions & 0 deletions tools/tkn-results/cmd/help.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
Environment Variables:
TKN_RESULTS_SSL_ROOTS_FILE_PATH: Path to local SSL cert to use.
TKN_RESULTS_SSL_SERVER_NAME_OVERRIDE: SSL server name override (useful if using with a proxy such as kubectl port-forward).

Config:
A config file may be stored in `~/.config/tkn/results.yaml` to configure the CLI client.

Fields:
- address: Results API Server address
- service_account: When specified, the CLI will first fetch a bearer token
for the specified ServiceAccount and attach that to Result API requests.
- namespace: ServiceAccount namespace
- name: ServiceAccount name
- token: Bearer token to use for API requests. Takes priority over service_account.
- ssl: SSL connection options
- roots_file_path: Path to a certificate to include in the cert pool. Useful for adding allowed self-signed certs.
- server_name_override: For testing only. Sets the grpc.ssl_target_name_override value for requests.

Example:

```
address: results.dogfooding.tekton.dev:443
token: abcd1234
ssl:
roots_file_path: path/to/file
server_name_override: example.com
service_account:
namespace: default
name: result-reader
```

53 changes: 9 additions & 44 deletions tools/tkn-results/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,16 @@ package cmd

import (
"context"
"crypto/x509"
_ "embed"
"fmt"
"io"
"io/ioutil"
"os"
"strings"
"text/tabwriter"
"time"

"github.com/spf13/cobra"
pb "github.com/tektoncd/results/proto/v1alpha2/results_go_proto"
"github.com/tektoncd/results/tools/tkn-results/internal/config"
"golang.org/x/oauth2"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/oauth"
clientutil "github.com/tektoncd/results/tools/tkn-results/internal/client"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/encoding/prototext"
"google.golang.org/protobuf/proto"
Expand All @@ -27,10 +21,13 @@ import (
)

var (
//go:embed help.txt
help string

RootCmd = &cobra.Command{
Use: "tkn-results",
Short: "tkn CLI plugin for Tekton Results API",
Long: config.EnvVarHelp(),
Long: help,
}
)

Expand All @@ -39,44 +36,12 @@ func Execute() error {
return RootCmd.Execute()
}

// TODO: Refactor this with watcher client code?
func client(ctx context.Context) (pb.ResultsClient, error) {
cfg, err := config.GetConfig()
if err != nil {
return nil, err
}

certs, err := x509.SystemCertPool()
if err != nil {
return nil, err
}
if path := cfg.SSL.RootsFilePath; path != "" {
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
b, err := ioutil.ReadAll(f)
if err != nil {
return nil, fmt.Errorf("unable to read TLS cert file: %v", err)
}
if ok := certs.AppendCertsFromPEM(b); !ok {
return nil, fmt.Errorf("unable to add cert to pool")
}
}
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
conn, err := grpc.DialContext(ctx, cfg.Address, grpc.WithBlock(),
grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(certs, cfg.SSL.ServerNameOverride)),
grpc.WithDefaultCallOptions(grpc.PerRPCCredentials(oauth.TokenSource{
TokenSource: oauth2.StaticTokenSource(&oauth2.Token{AccessToken: cfg.Token}),
})),
)
f, err := clientutil.NewDefaultFactory()
if err != nil {
fmt.Printf("Dial: %v\n", err)
return nil, err
}
return pb.NewResultsClient(conn), nil
return f.Client(ctx)
}

func printproto(w io.Writer, m proto.Message, format string) error {
Expand All @@ -98,7 +63,7 @@ func printproto(w io.Writer, m proto.Message, format string) error {
for _, r := range t.GetRecords() {
fmt.Fprintln(tw, strings.Join([]string{
r.GetName(),
r.GetData().GetTypeUrl(),
r.GetData().GetType(),
r.GetCreatedTime().AsTime().Truncate(time.Second).Local().String(),
r.GetUpdatedTime().AsTime().Truncate(time.Second).Local().String(),
}, "\t"))
Expand Down
33 changes: 30 additions & 3 deletions tools/tkn-results/docs/tkn-results.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,36 @@ tkn CLI plugin for Tekton Results API

### Synopsis

TKN_RESULTS_SSL_ROOTS_FILE_PATH: Path to local SSL cert to use.
TKN_RESULTS_SSL_SERVER_NAME_OVERRIDE: SSL server name override (useful if using with a proxy such as kubectl port-forward).

Environment Variables:
TKN_RESULTS_SSL_ROOTS_FILE_PATH: Path to local SSL cert to use.
TKN_RESULTS_SSL_SERVER_NAME_OVERRIDE: SSL server name override (useful if using with a proxy such as kubectl port-forward).

Config:
A config file may be stored in `~/.config/tkn/results.yaml` to configure the CLI client.

Fields:
- address: Results API Server address
- service_account: When specified, the CLI will first fetch a bearer token
for the specified ServiceAccount and attach that to Result API requests.
- namespace: ServiceAccount namespace
- name: ServiceAccount name
- token: Bearer token to use for API requests. Takes priority over service_account.
- ssl: SSL connection options
- roots_file_path: Path to a certificate to include in the cert pool. Useful for adding allowed self-signed certs.
- server_name_override: For testing only. Sets the grpc.ssl_target_name_override value for requests.

Example:

```
address: results.dogfooding.tekton.dev:443
token: abcd1234
ssl:
roots_file_path: path/to/file
server_name_override: example.com
service_account:
namespace: default
name: result-reader
```

### Options

Expand Down
43 changes: 33 additions & 10 deletions tools/tkn-results/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,60 @@ go 1.17

require (
github.com/google/go-cmp v0.5.6
github.com/spf13/cobra v1.1.3
github.com/spf13/cobra v1.2.1
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.9.0
github.com/tektoncd/results v0.1.1
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f
google.golang.org/grpc v1.40.0
github.com/tektoncd/results v0.3.2-0.20211115210243-23aa0be931e4
golang.org/x/oauth2 v0.0.0-20211028175245-ba495a64dcb5
google.golang.org/grpc v1.42.0
google.golang.org/protobuf v1.27.1
k8s.io/api v0.21.4
k8s.io/apimachinery v0.21.4
k8s.io/client-go v0.21.4
)

require (
cloud.google.com/go v0.93.3 // indirect
cloud.google.com/go v0.97.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/evanphx/json-patch v4.9.0+incompatible // indirect
github.com/fsnotify/fsnotify v1.5.1 // indirect
github.com/go-logr/logr v0.4.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/googleapis/gnostic v0.5.3 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/imdario/mergo v0.3.11 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/json-iterator/go v1.1.11 // indirect
github.com/magiconair/properties v1.8.5 // indirect
github.com/mitchellh/mapstructure v1.4.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/pelletier/go-toml v1.9.4 // indirect
github.com/russross/blackfriday/v2 v2.0.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
github.com/spf13/afero v1.6.0 // indirect
github.com/spf13/cast v1.4.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/subosito/gotenv v1.2.0 // indirect
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420 // indirect
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf // indirect
golang.org/x/text v0.3.6 // indirect
golang.org/x/net v0.0.0-20211101193420-4a448f8816b3 // indirect
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 // indirect
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71 // indirect
google.golang.org/genproto v0.0.0-20211021150943-2b146023228c // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.63.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
k8s.io/klog/v2 v2.8.0 // indirect
k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7 // indirect
k8s.io/utils v0.0.0-20210111153108-fddb29f9d009 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)
Loading

0 comments on commit ff00628

Please sign in to comment.