Repository: kubernetes/kubernetes (language: go)

Issue:
Commands and output are logged when attached to a container via kubectl
When attached to a container, all commands and output executed during an attached session will be visible via pods/log that could be scraped.

**Repro steps:**
Run a pod and directly attach to its shell with the following command:
> kubectl run -it my-pod —image=busybox — sh

Once the pod is started and attached, anything that is typed in the terminal is not only visible in the Kubernetes console (for example in Openshift pod logs tab), but also scraped by the log-scrapers like fluentbit and are forwarded to ELK/EFK/etc.
Many SREs, infra engineers, troubleshooters run pods like this to troubleshoot and spot problems, and many times credentials and other sensitive information is used during troubleshooting. 

e.g.
> curl -u username:mypass http://example.com/ 
will log username and password on the console and eventually into logs

**Note**: when exec into a container, that session is never logged

**Proposal:**
Add a warning to remind users that `all commands and output executed when attached to a container will be visible via pods/log` to help users who might not expect this behavior

Initially reported by @yosuf


Discussion / hints:
/sig auth
/sig node
/triage accepted
I think also
/sig security
@ritazh 

So we just need to issue a warning when kubectl attach, right?

like

```shell
$ kubectl run -it mypod --image=busybox -- sh
all commands and output executed when attached to a container will be visible via pods/log
/ #
```


> @ritazh
> 
> So we just need to issue a warning when kubectl attach, right?
> 
> like
> 
> ```shell
> $ kubectl run -it mypod --image=busybox -- sh
> all commands and output executed when attached to a container will be visible via pods/log
> / #
> ```

Yes that would be a good start.
> > @ritazh
> > So we just need to issue a warning when kubectl attach, right?
> > like
> > ```shell
> > $ kubectl run -it mypod --image=busybox -- sh
> > all commands and output executed when attached to a container will be visible via pods/log
> > / #
> > ```
> 
> Yes that would be a good start.

Before making any PRs or changes, we should discuss this at a SIG Auth / node / etc meeting.
Hi all, one thing on this where we might want to note this in docs and/or warnings, is `kubectl debug pod` . I'd *guess* that debugging scenarios have quite a high likelihood of people putting credentials or sensitive data in shells, ~and as the debug container doesn't (by default) share the process/PID namespace of the main container, the shell output is captured in logs, from what I can see.~

**edit** from more tests looks like data from ephemeral containers is captured even if process namespace sharing is enabled...

Fix patch:
diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/attach/attach.go b/staging/src/k8s.io/kubectl/pkg/cmd/attach/attach.go
index b38760a1c861c..0bda9b34c6191 100644
--- a/staging/src/k8s.io/kubectl/pkg/cmd/attach/attach.go
+++ b/staging/src/k8s.io/kubectl/pkg/cmd/attach/attach.go
@@ -307,6 +307,7 @@ func (o *AttachOptions) Run() error {
 	}
 
 	if !o.Quiet {
+		_, _ = fmt.Fprintln(o.ErrOut, "All commands and output from this session will be recorded in container logs, including credentials and sensitive information passed through the command prompt.")
 		fmt.Fprintln(o.ErrOut, "If you don't see a command prompt, try pressing enter.")
 	}
 	if err := t.Safe(o.AttachFunc(o, containerToAttach, t.Raw, sizeQueue)); err != nil {


Test patch:
diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/attach/attach_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/attach/attach_test.go
index ac217e08cb987..6a599656e450a 100644
--- a/staging/src/k8s.io/kubectl/pkg/cmd/attach/attach_test.go
+++ b/staging/src/k8s.io/kubectl/pkg/cmd/attach/attach_test.go
@@ -242,6 +242,7 @@ func TestAttach(t *testing.T) {
 		pod                                                         *corev1.Pod
 		remoteAttachErr                                             bool
 		expectedErr                                                 string
+		expectedErrOut                                              []string
 	}{
 		{
 			name:         "pod attach",
@@ -251,6 +252,10 @@ func TestAttach(t *testing.T) {
 			attachPath:   "/api/" + version + "/namespaces/test/pods/foo/attach",
 			pod:          attachPod(),
 			container:    "bar",
+			expectedErrOut: []string{
+				"All commands and output from this session will be recorded in container logs, including credentials and sensitive information passed through the command prompt.",
+				"If you don't see a command prompt, try pressing enter.",
+			},
 		},
 		{
 			name:            "pod attach error",
@@ -305,10 +310,11 @@ func TestAttach(t *testing.T) {
 			if test.remoteAttachErr {
 				remoteAttach.err = fmt.Errorf("attach error")
 			}
+			streams, _, _, errOut := genericiooptions.NewTestIOStreams()
 			options := &AttachOptions{
 				StreamOptions: exec.StreamOptions{
 					ContainerName: test.container,
-					IOStreams:     genericiooptions.NewTestIOStreamsDiscard(),
+					IOStreams:     streams,
 				},
 				Attach:        remoteAttach,
 				GetPodTimeout: 1000,
@@ -349,6 +355,14 @@ func TestAttach(t *testing.T) {
 			if remoteAttach.url.Query().Get("container") != "bar" {
 				t.Errorf("%s: Did not have query parameters: %s", test.name, remoteAttach.url.Query())
 			}
+			if test.expectedErrOut != nil {
+				for _, expect := range test.expectedErrOut {
+					if !strings.Contains(errOut.String(), expect) {
+						t.Errorf("%s: expected message %s not found, got: %s", test.name, expect, strings.ReplaceAll(errOut.String(), "\n", ""))
+						return
+					}
+				}
+			}
 		})
 	}
 }