This serves two purposes:
1. on upload, the receiver (node) will exit with error and generate an
SCP error audit event.
2. on download, the receiver (tsh) will print a meaningful message from
the sender (node)
Fixes#2861
This code is not caught by linters because it's exported and they assume
there's some external users.
Since teleport is relatively self-contained, we can tell for sure
whether something is called or not.
There seem to be some test stubs in `lib/auth` that can generate TLS
key/cert but without supporting k8s users/groups.
Rewrite cert generation in `kube_integration_test.go` to use the same
logic as `auth.Server.ProcessKubeCSR`.
Auth server has to trust the proxy with all k8s CSR fields. Usage is the
only field it can enforce, to somewhat limit the blast radius of
compromising this cert flow.
When a proxy forwards a k8s request to another proxy in a trusted leaf
cluster, it dynamically generates a new client key/cert to impersonate
the user. This key/cert is presented to the leaf proxy as if a user was
directly making a request to it.
Auth server, when processing the CSR, would load user identity from the
backend. If this user had temporary role grants via workflow API, they
would not be loaded from the backend.
This means that if a user accesses k8s through:
```
kubectl -> root proxy -> leaf proxy -> k8s
```
the second hop would drop their temporary role grants.
To fix this, preserve full user identity from the original client cert
presented to root proxy, as encoded in CSR Subject. The auth server
implicitly trusts the CSR Subject that the proxy presents.
This also requires fixing the Subject encoding on the proxy side, which
was inconsistent with how auth server does it.
One downside here is: a compromised proxy can mint k8s client certs for
known users with arbitrary roles.
* Add UpdateUser rpc to proto
* Differentiate between create and update in github,oidc,saml
* Edit updated_by event field to be more generic (used with contexts to capture user modifying records)
* Update security issue by removing secrets from user when update/upsert/create (forrest)
* Update createUser in resource_command and require force for updates
The existing kubeCreds already has tls.Config.
Add a new wrapTransport method to it, which handles added bearer tokens
or basic auth to the request before it goes out.
Use this wrapTransport to inject tokens for both SPDY connections and
catch-all forwarder requests.
Also simplify a few unrelated parts:
- don't manually call serverName verification in SPDY dialer
- have requestCertificate return a finished tls.Config instead of an
intermediate type
By importing the magic `k8s.io/client-go/plugin/pkg/client/auth`
package, we compile-in support for auth plugins: gcp, azure, oidc,
openstack.
Without this, users can't provide a kubeconfig using those authn
methods to a proxy.
With gocheck, tests only run if you call `check.TestingT(t)` from a
dummy `func Test(t *testing.T)`.
Added the missing dummy function call in: `lib/services/suite`,
`lib/shell`. The `lib/shell` tests also turned out to be broken.
If you call the dummy wrapper twice, all tests will run twice.
This was happening in `lib/events/s3sessions` and `lib/services/local`.
There are two new ways you can generate a kubeconfig:
- `tctl auth sign --user=foo --format=kubernetes --out=kubeconfig` for
admins
- `tsh login --format=kubernetes -o kubeconfig` for users
This allows admins to generate long-lived kubeconfigs for e.g. CI
systems.
A tricky part is getting the kubernetes endpoint for a proxy in `tctl`.
It does its best to guess the address, but falls back to asking user to
pass `--proxy` flag.
It looks like right now, the proxy info available via the auth server's
API doesn't have kubernetes public_addr for proxies.
Fixes#2825
When building binaries locally, they get linked against a local version
of libc. This makes the resulting binary change based on which machine
it was built on.
By always building in docker, we use the libc version from the build
container, so at least it's predictable.
Ensure main Makefile dependencies are correctly spelled-out so that
`make image` doesn't use stale local binaries. Binaries should always
get rebuilt, with docker.
Fixed findings:
```
lib/auth/clt.go:294:12: SA1019: grpc.WithDialer is deprecated: use WithContextDialer instead. Will be supported throughout 1.x. (staticcheck)
dialer := grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) {ion error: desc = "transport: Erro
^
lib/auth/clt.go:1462:5: SA1019: grpc.Code is deprecated: use status.Code instead. (staticcheck)site-A Type:proxy Addr:{Addr:local
if grpc.Code(err) != codes.Unimplemented {
^
lib/auth/clt.go:1500:5: SA1019: grpc.Code is deprecated: use status.Code instead. (staticcheck)rsetunnel/agentpool.go:312
if grpc.Code(err) != codes.Unimplemented {
^
lib/auth/saml.go:294:33: SA5011: possible nil pointer dereference (staticcheck)
events.EventUser: re.auth.Username,
^
lib/auth/auth_test.go:119:2: SA4006: this value of `ws` is never used (staticcheck)ats. stats:map[connected:1 connect
ws, err := s.a.AuthenticateWebUser(AuthenticateUserRequest{
^
lib/auth/auth_test.go:160:2: SA4006: this value of `ws` is never used (staticcheck)ver.go:425
ws, err := s.a.AuthenticateWebUser(AuthenticateUserRequest{
^
lib/auth/auth_test.go:243:2: SA4006: this value of `roles` is never used (staticcheck)es that discovery protocol recover
roles, err = s.a.ValidateToken(tok)
^
lib/auth/password_test.go:228:2: SA4006: this value of `err` is never used (staticcheck)necting -> connected. leaseID:5 ta
token, err := s.a.CreateResetPasswordToken(context.TODO(), CreateResetPasswordTokenRequest{7.0.0.1:46150. reversetunnel/srv.g
^
lib/auth/tls_test.go:109:2: SA4006: this value of `err` is never used (staticcheck)ry(c *check.C) {
err = s.server.AuthServer.Trust(remoteServer, nil)
^
lib/auth/tls_test.go:195:2: SA4006: this value of `err` is never used (staticcheck)meout=10000&_sync=OFF, poll stream
err = s.server.AuthServer.Trust(remoteServer, nil)
^
lib/auth/tls_test.go:360:2: SA4006: this value of `newProxy` is never used (staticcheck) be received and the connection ad
newProxy, err := s.server.NewClient(TestBuiltin(teleport.RoleProxy))o/src/github.com/gravitational/tel
^
lib/auth/tls_test.go:388:2: SA4006: this value of `err` is never used (staticcheck)o/src/github.com/gravitational/tel
err = s.server.Auth().autoRotateCertAuthorities()
^
lib/auth/tls_test.go:1074:2: SA4006: this value of `err` is never used (staticcheck)uest, we will recover session id a
err = os.MkdirAll(filepath.Join(uploadDir, "upload", "sessions", defaults.Namespace), 0755)reversetunnel/agent.go:458
^
lib/auth/tls_test.go:1263:2: SA4006: this value of `err` is never used (staticcheck)quests channel. leaseID:5 target:l
err = clt.UpsertPassword(user, pass)
^
lib/auth/tls_test.go:1691:2: SA4006: this value of `err` is never used (staticcheck)
userCerts, err = adminClient.GenerateUserCerts(context.TODO(), proto.UserCertsRequest{et:localhost:20088 reversetunnel/t
^
lib/auth/auth_with_roles.go:860:32: SA1029: should not use built-in type string as key for value; define your own type to avoid collisions (staticcheck)
return context.WithValue(ctx, events.AccessRequestUpdateBy, user)ts is received, if it's been longe
^
lib/auth/auth_with_roles.go:876:32: SA1029: should not use built-in type string as key for value; define your own type to avoid collisions (staticcheck)
return context.WithValue(ctx, events.AccessRequestDelegator, delegator)ime = f.process.Clock.Now()
^
lib/auth/middleware.go:316:69: SA1029: should not use built-in type string as key for value; define your own type to avoid collisions (staticcheck)
requestWithContext := r.WithContext(context.WithValue(baseContext, ContextUser, user))rocess.Infof("Teleport has recover
^ion to @remote-auth-server [] in r
lib/auth/apiserver.go:285:42: SA1029: should not use built-in type string as key for value; define your own type to avoid collisions (staticcheck)
ctx := context.WithValue(r.Context(), contextParams, p)et:localhost:20088 reversetunnel/t
^
```
Original finding list:
```
tool/tctl/common/node_command.go:163:3: SA1006: printf-style function with dynamic format string and no further arguments should use print-style function instead (staticcheck)
fmt.Printf(string(out))
^
tool/tctl/common/status_command.go:110:2: SA1006: printf-style function with dynamic format string and no further arguments should use print-style function instead (staticcheck)
fmt.Printf(view())
^
tool/tctl/common/status_command.go:126:3: SA1006: printf-style function with dynamic format string and no further arguments should use print-style function instead (staticcheck)
fmt.Printf(view())
^
tool/tctl/common/token_command.go:201:3: SA1006: printf-style function with dynamic format string and no further arguments should use print-style function instead (staticcheck)
fmt.Printf(tokensView())
^
tool/tctl/common/token_command.go:207:3: SA1006: printf-style function with dynamic format string and no further arguments should use print-style function instead (staticcheck)
fmt.Printf(string(data))
^
tool/tctl/common/user_command.go:248:2: SA1006: printf-style function with dynamic format string and no further arguments should use print-style function instead (staticcheck)
fmt.Printf(string(out))
^
tool/tctl/common/user_command.go:294:3: SA1006: printf-style function with dynamic format string and no further arguments should use print-style function instead (staticcheck)
fmt.Printf(string(out))
^
integration/helpers.go:200:2: SA4006: this value of `err` is never used (staticcheck)
cryptoPubKey, err := sshutils.CryptoPublicKey(cfg.Pub)
^
integration/helpers.go:399:3: SA4006: this value of `roles` is never used (staticcheck)
roles = append(roles, role)
^
integration/helpers.go:597:4: SA4006: this value of `roles` is never used (staticcheck)
roles = append(roles, role)
^
integration/helpers.go:599:4: SA4006: this value of `roles` is never used (staticcheck)
roles = user.Roles
^
integration/integration_test.go:1625:2: SA4006: this value of `err` is never used (staticcheck)
adminsRole, err := services.NewRole(mainAdmins, services.RoleSpecV3{
^
integration/integration_test.go:2185:2: SA4006: this value of `output` is never used (staticcheck)
output, err = runCommand(main, []string{"echo", "hello world"}, cfg, 1)
^
integration/integration_test.go:2340:2: SA4006: this value of `output` is never used (staticcheck)
output, err = runCommand(main, []string{"echo", "hello world"}, cfgProxy, 1)
^
integration/kube_integration_test.go:154:2: SA4006: this value of `err` is never used (staticcheck)
role, err := services.NewRole("kubemaster", services.RoleSpecV3{
^
integration/kube_integration_test.go:321:2: SA4006: this value of `err` is never used (staticcheck)
role, err := services.NewRole("kubemaster", services.RoleSpecV3{
^
integration/kube_integration_test.go:366:2: SA4006: this value of `err` is never used (staticcheck)
role, err := services.NewRole("kubemaster", services.RoleSpecV3{
^
integration/kube_integration_test.go:386:2: SA4006: this value of `err` is never used (staticcheck)
pods, err := s.CoreV1().Pods(kubeSystemNamespace).List(metav1.ListOptions{
^
integration/kube_integration_test.go:465:2: SA4006: this value of `err` is never used (staticcheck)
mainRole, err := services.NewRole("main-kube", services.RoleSpecV3{
^
integration/kube_integration_test.go:579:2: SA4006: this value of `err` is never used (staticcheck)
pods, err := proxyClient.CoreV1().Pods(kubeSystemNamespace).List(metav1.ListOptions{
^
integration/kube_integration_test.go:727:2: SA4006: this value of `err` is never used (staticcheck)
mainRole, err := services.NewRole("main-kube", services.RoleSpecV3{
^
integration/kube_integration_test.go:840:2: SA4006: this value of `err` is never used (staticcheck)
pods, err := proxyClient.CoreV1().Pods(kubeSystemNamespace).List(metav1.ListOptions{
^
integration/kube_integration_test.go:1008:2: SA4006: this value of `err` is never used (staticcheck)
role, err := services.NewRole("kubemaster", services.RoleSpecV3{
^
tool/teleport/common/teleport_test.go:83:2: SA4006: this value of `cmd` is never used (staticcheck)
cmd, conf := Run(Options{
^
tool/teleport/common/teleport_test.go:91:2: SA4006: this value of `cmd` is never used (staticcheck)
cmd, conf = Run(Options{
^
tool/tsh/tsh.go:170:2: SA4006: this value of `cmdLine` is never used (staticcheck)
cmdLine := []string{}
^
integration/helpers.go:399:11: SA4010: this result of append is never used, except maybe in other appends (staticcheck)
roles = append(roles, role)
^
integration/helpers.go:597:12: SA4010: this result of append is never used, except maybe in other appends (staticcheck)
roles = append(roles, role)
^
integration/integration_test.go:1092:7: SA4000: identical expressions on the left and right side of the '&&' operator (staticcheck)
for len(b.Tunnel.GetSites()) < 2 && len(b.Tunnel.GetSites()) < 2 {
^
integration/integration_test.go:1426:6: SA4000: identical expressions on the left and right side of the '&&' operator (staticcheck)
for len(main.Tunnel.GetSites()) < 2 && len(main.Tunnel.GetSites()) < 2 {
^
integration/integration_test.go:1691:6: SA4000: identical expressions on the left and right side of the '&&' operator (staticcheck)
for len(main.Tunnel.GetSites()) < 2 && len(main.Tunnel.GetSites()) < 2 {
^
integration/integration_test.go:1895:6: SA4000: identical expressions on the left and right side of the '&&' operator (staticcheck)
for len(main.Tunnel.GetSites()) < 2 && len(main.Tunnel.GetSites()) < 2 {
^
integration/kube_integration_test.go:548:6: SA4000: identical expressions on the left and right side of the '&&' operator (staticcheck)
for len(main.Tunnel.GetSites()) < 2 && len(main.Tunnel.GetSites()) < 2 {
^
integration/kube_integration_test.go:814:6: SA4000: identical expressions on the left and right side of the '&&' operator (staticcheck)
for len(main.Tunnel.GetSites()) < 2 && len(main.Tunnel.GetSites()) < 2 {
^
```
Fixed regressions in how the cluster name was set in the profile when
doing "tsh login <clusterName>". The following examples illustrate
expected behavior.
Suppose root cluster is example.com and the leaf cluster is
leaf.example.com.
* "tsh login" will update profile to example.com.
* "tsh login example.com" will update profile to example.com.
* "tsh login leaf.example.com" will update profile to
leaf.example.com.
* "tsh login invalid.example.com" will return an error.
Note: The profile is only updated when a identity flag is NOT provided.
This means the following command will NOT update the profile:
tsh --proxy=example.com login --out=certs.pem leaf.example.com
* Add image types, AMI IDs, extend AuthASG timeout
Added options for m4.large and m5.large. Added AMI IDs for all regions. Extended the timeout on the Auth ASG from 20 minutes to 30 minutes.
* Update ent.yaml
Co-authored-by: Ben Arent <ben@gravitational.com>
Co-authored-by: Gus Luxton <gus@gravitational.com>
Changes the lifetime of agent forwarding to be scoped
to the underlying ssh connection, instead of the
specific ssh channel which initially passed the agent
forwarding request.
Fixed findings:
```
lib/sshutils/server_test.go:163:2: SA4006: this value of `clt` is never used (staticcheck)
clt, err := ssh.Dial("tcp", srv.Addr(), &cc)
^
lib/sshutils/server_test.go:91:3: SA5001: should check returned error before deferring ch.Close() (staticcheck)
defer ch.Close()
^
lib/shell/shell_test.go:33:2: SA4006: this value of `shell` is never used (staticcheck)
shell, err = GetLoginShell("non-existent-user")
^
lib/cgroup/cgroup_test.go:111:2: SA9003: empty branch (staticcheck)
if err != nil {
^
lib/cgroup/cgroup_test.go:119:2: SA5001: should check returned error before deferring service.Close() (staticcheck)
defer service.Close()
^
lib/client/keystore_test.go:138:2: SA4006: this value of `keyCopy` is never used (staticcheck)
keyCopy, err = s.store.GetKey("host.a", "bob")
^
lib/client/api.go:1604:3: SA4004: the surrounding loop is unconditionally terminated (staticcheck)
return makeProxyClient(sshClient, m), nil
^
lib/backend/test/suite.go:156:2: SA4006: this value of `err` is never used (staticcheck)
result, err = s.B.GetRange(ctx, prefix("/prefix/c/c1"), backend.RangeEnd(prefix("/prefix/c/cz")), backend.NoLimit)
^
lib/utils/timeout_test.go:84:2: SA1019: t.Dial is deprecated: Use DialContext instead, which allows the transport to cancel dials as soon as they are no longer needed. If both are set, DialContext takes priority. (staticcheck)
t.Dial = func(network string, addr string) (net.Conn, error) {
^
lib/utils/websocketwriter.go:83:3: SA4006: this value of `err` is never used (staticcheck)
utf8, err = w.encoder.String(string(data))
^
lib/utils/loadbalancer_test.go:134:2: SA4006: this value of `out` is never used (staticcheck)
out, err = Roundtrip(frontend.String())
^
lib/utils/loadbalancer_test.go:209:2: SA4006: this value of `out` is never used (staticcheck)
out, err = RoundtripWithConn(conn)
^
lib/srv/forward/sshserver.go:582:3: SA4004: the surrounding loop is unconditionally terminated (staticcheck)
return
^
lib/service/service.go:347:4: SA4006: this value of `err` is never used (staticcheck)
i, err = auth.GenerateIdentity(process.localAuth, id, principals, dnsNames)
^
lib/service/signals.go:60:3: SA1016: syscall.SIGKILL cannot be trapped (did you mean syscall.SIGTERM?) (staticcheck)
syscall.SIGKILL, // fast shutdown
^
lib/config/configuration_test.go:184:2: SA4006: this value of `conf` is never used (staticcheck)
conf, err = ReadFromFile(s.configFileBadContent)
^
lib/config/configuration.go:129:2: SA5001: should check returned error before deferring reader.Close() (staticcheck)
defer reader.Close()
^
lib/kube/kubeconfig/kubeconfig_test.go:227:2: SA4006: this value of `err` is never used (staticcheck)
tlsCert, err := ca.GenerateCertificate(tlsca.CertificateRequest{
^
lib/srv/sess.go:720:3: SA4006: this value of `err` is never used (staticcheck)
result, err := s.term.Wait()
^
lib/multiplexer/multiplexer_test.go:169:11: SA1006: printf-style function with dynamic format string and no further arguments should use print-style function instead (staticcheck)
_, err = fmt.Fprintf(conn, proxyLine.String())
^
lib/multiplexer/multiplexer_test.go:221:11: SA1006: printf-style function with dynamic format string and no further arguments should use print-style function instead (staticcheck)
_, err = fmt.Fprintf(conn, proxyLine.String())
^
```
Fixed findings:
```
lib/services/github_test.go:99:2: SA4006: this value of `kubeUsers` is never used (staticcheck)
logins, kubeGroups, kubeUsers = connector.MapClaims(GithubClaims{
^
lib/services/github_test.go:107:2: SA4006: this value of `kubeUsers` is never used (staticcheck)
logins, kubeGroups, kubeUsers = connector.MapClaims(GithubClaims{
^
lib/services/local/configuration_test.go:84:2: SA4006: this value of `clusterConfig` is never used (staticcheck)
clusterConfig, err := services.NewClusterConfig(services.ClusterConfigSpecV3{
^
lib/services/local/configuration_test.go:102:2: SA4006: this value of `clusterConfig` is never used (staticcheck)
clusterConfig, err := services.NewClusterConfig(services.ClusterConfigSpecV3{})
^
lib/services/local/presence_test.go:108:2: SA4006: this value of `gotTC` is never used (staticcheck)
gotTC, err = presenceBackend.GetTrustedCluster("foo")
^
lib/services/suite/suite.go:157:2: SA4006: this value of `err` is never used (staticcheck)
out, err := s.WebS.GetUser("user1", false)
^
lib/services/suite/suite.go:208:2: SA4006: this value of `u` is never used (staticcheck)
u, err = s.WebS.GetUser("foo", false)
^
lib/services/suite/suite.go:277:2: SA4006: this value of `err` is never used (staticcheck)
err = s.CAS.CompareAndSwapCertAuthority(&newCA, ca)
^
lib/services/suite/suite.go:339:2: SA4006: this value of `err` is never used (staticcheck)
out, err = s.PresenceS.GetProxies()
^
lib/services/suite/suite.go:1136:5: SA4006: this value of `err` is never used (staticcheck)
role, err := services.NewRole("role1", services.RoleSpecV3{
^
lib/services/suite/suite.go:1166:5: SA4006: this value of `err` is never used (staticcheck)
err := s.Users().UpsertUser(user)
^
```
Fixed findings:
```
lib/events/test/suite.go:88:2: SA4006: this value of `err` is never used (staticcheck)
err := s.Log.EmitAuditEvent(events.UserLocalLogin, events.EventFields{
^
lib/events/auditlog_test.go:98:2: SA4006: this value of `err` is never used (staticcheck)
fileHandler, err := filesessions.NewHandler(filesessions.Config{
^
lib/events/auditlog_test.go:121:2: SA4006: this value of `err` is never used (staticcheck)
err = os.MkdirAll(filepath.Join(uploadDir, "upload", "sessions", defaults.Namespace), 0755)
^
lib/events/auditlog_test.go:224:2: SA4006: this value of `err` is never used (staticcheck)
fileHandler, err := filesessions.NewHandler(filesessions.Config{
^
lib/events/auditlog_test.go:246:2: SA4006: this value of `err` is never used (staticcheck)
err = os.MkdirAll(filepath.Join(uploadDir, "upload", "sessions", defaults.Namespace), 0755)
^
lib/events/auditlog_test.go:381:2: SA4006: this value of `err` is never used (staticcheck)
fileHandler, err := filesessions.NewHandler(filesessions.Config{
^
lib/events/auditlog_test.go:403:2: SA4006: this value of `err` is never used (staticcheck)
fileHandler, err := filesessions.NewHandler(filesessions.Config{
^
lib/events/auditlog_test.go:431:2: SA4006: this value of `err` is never used (staticcheck)
err := os.MkdirAll(filepath.Join(uploadDir, "upload", "sessions", defaults.Namespace), 0755)
^
lib/events/filesessions/fileuploader.go:84:2: SA4006: this value of `err` is never used (staticcheck)
_, err := os.Stat(filepath.Dir(path))
^
lib/events/dynamoevents/dynamoevents.go:352:2: SA4006: this value of `err` is never used (staticcheck)
attributeValues, err := dynamodbattribute.MarshalMap(attributes)
^
lib/events/dynamoevents/dynamoevents.go:405:2: SA4006: this value of `err` is never used (staticcheck)
attributeValues, err := dynamodbattribute.MarshalMap(attributes)
^
```
Fixed issues:
```
lib/web/apiserver_test.go:499:2: SA4006: this value of `re` is never used (staticcheck)
re, err = pack.clt.Get(context.Background(), pack.clt.Endpoint("webapi", "sites"), url.Values{})
^
lib/web/apiserver_test.go:590:2: SA4006: this value of `re` is never used (staticcheck)
re, err = newPack.clt.Get(context.Background(), pack.clt.Endpoint("webapi", "sites"), url.Values{})
^
lib/web/apiserver_test.go:598:2: SA4006: this value of `re` is never used (staticcheck)
re, err = oldClt.Get(context.Background(), pack.clt.Endpoint("webapi", "sites"), url.Values{})
^
lib/web/apiserver_test.go:608:2: SA4006: this value of `re` is never used (staticcheck)
re, err = newPack.clt.Get(context.Background(), pack.clt.Endpoint("webapi", "sites"), url.Values{})
^
lib/web/apiserver_test.go:1106:2: SA4006: this value of `err` is never used (staticcheck)
loginReq, err := json.Marshal(createSessionReq{
^
lib/web/apiserver_test.go:1120:2: SA4006: this value of `err` is never used (staticcheck)
re, err := clt.Client.RoundTrip(func() (*http.Response, error) {
^
lib/web/apiserver_test.go:1148:2: SA4006: this value of `re` is never used (staticcheck)
re, err = clt.Get(context.Background(), clt.Endpoint("webapi", "sites"), url.Values{})
^
lib/web/apiserver_test.go:1154:2: SA4006: this value of `re` is never used (staticcheck)
re, err = clt.Get(context.Background(), clt.Endpoint("webapi", "sites"), url.Values{})
^
lib/web/apiserver_test.go:1192:2: SA4006: this value of `err` is never used (staticcheck)
data, err := json.Marshal(auth.ChangePasswordWithTokenRequest{
^
lib/web/apiserver_test.go:1206:2: SA4006: this value of `err` is never used (staticcheck)
re, err = clt.Client.RoundTrip(func() (*http.Response, error) {
^
lib/web/apiserver_test.go:1246:2: SA4006: this value of `err` is never used (staticcheck)
data, err := json.Marshal(auth.ChangePasswordWithTokenRequest{
^
lib/web/apiserver_test.go:1260:2: SA4006: this value of `err` is never used (staticcheck)
re, err = clt.Client.RoundTrip(func() (*http.Response, error) {
^
lib/web/static_test.go:93:2: SA4006: this value of `n` is never used (staticcheck)
n, err = f.Seek(-50, io.SeekEnd)
^
lib/web/static_test.go:100:2: SA4006: this value of `n` is never used (staticcheck)
n, err = f.Seek(-50, io.SeekCurrent)
^
```
`localAgent` can be nil if `Config.SkipLocalAuth` was set (such as when
passing `-i` flag to `tsh`). Several places in client code didn't handle
missing `localAgent` causing nil pointer panics.
Fixes#3032
If TLS client key/cert or CA cert are missing, the kubeconfig ends up
generated successfully, but with those fields empty. For a user, this
looks like a successful `tsh login` with `kubectl` not working
afterwards with cryptic x509 errors.
We should always have the necessary fields provided. If not, `tsh login`
should say exactly what was missing.
Local keystore records trusted TLS CA certs in `<host>/certs.pem`.
When loading client.Key, also load the TLS CA cert for
`key.TrustedCertificates` field.
Without this field, `kubeconfig.Update` called by tsh can't populate the
CA cert in kubeconfig, which causes x509 validation errors with kubectl.
With OSS version and without using the github connector (only local
auth), logged in user won't have any `kubernetes_groups`. Without
usernames too, user can login but can't use kubectl.