Skip to content

Commit b3dd9df

Browse files
authored
Completely refactored plugin (#8)
Version v1.0.0 Completely refactored plugin BREAKING CHANGES: - Using host ssh client instead of internal implementation - Interactive sessions must be stated explicitly with -i - Pseudo-TTY allocation must be stated explicitly with -t
1 parent 6182f47 commit b3dd9df

File tree

9 files changed

+466
-305
lines changed

9 files changed

+466
-305
lines changed

README.md

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,22 @@ kubectl krew install nsenter
1414

1515
```bash
1616
GLOBAL OPTIONS:
17-
--kubeconfig value kubernetes client config path (default: $HOME/.kube/config) [$KUBECONFIG]
18-
--container value, -c value use namespace of specified container. By default first running container will taken
19-
--context value override current context from kubeconfig
20-
--namespace value, -n value override namespace of current context from kubeconfig
21-
--user value, -u value set username for ssh connection to node (default: "johndoe") [$USER]
22-
--password, -s force ask for node password prompt (default: false)
23-
--ssh-auth-sock value sets ssh-agent socket (default: current shell auth sock) [$SSH_AUTH_SOCK]
24-
--host value override node ip
25-
--port value, -p value sets ssh port (default: "22")
26-
--ns value define container's pid linux namespaces to enter. sends transparently to nsenter cmd (default: "n")
27-
--help, -h show help (default: false)
28-
--version, -v print the version (default: false)
29-
17+
--kubeconfig value kubernetes client config path (default: $HOME/.kube/config) [$KUBECONFIG]
18+
--container value, -c value use namespace of specified container. By default first running container will taken
19+
--context value override current context from kubeconfig
20+
--namespace value, -n value override namespace of current context from kubeconfig
21+
--user value, -u value set username for ssh connection to node
22+
--password, -s force ask for node password prompt (default: false)
23+
--ssh-auth-sock value sets ssh-agent socket (default: current shell auth sock) [$SSH_AUTH_SOCK]
24+
--host value override node ip
25+
--port value, -p value sets ssh port
26+
--ns value [ --ns value ] define container's pid linux namespaces to enter. Sends transparently to nsenter cmd (default: "n")
27+
--interactive, -i keep ssh session stdin (default: false)
28+
--tty, -t allocate pseudo-TTY for ssh session (default: false)
29+
--ssh-opt value, -o value [ --ssh-opt value, -o value ] same as -o for ssh client
30+
--use-node-name, -j use kubernetes node name to connect with ssh. Useful with ssh configs (default: true) [$KUBECTL_NSENTER_USE_NODE_NAME]
31+
--help, -h show help
32+
--version, -v print the version
3033
```
3134
3235
## What the kind is kubectl-nsenter?
@@ -38,6 +41,7 @@ GLOBAL OPTIONS:
3841
First we gotta talk about requirements:
3942

4043
- You **must** have a **root access** to node (with password or not) where pod is running
44+
- Your client station **must** have ssh client binary in $PATH
4145
- Your node **must** have CRI client for discovering container's pid (e.g. `crictl` for **containerd** or `docker` for **docker engine**)
4246
4347
If you can handle this requirements, we're moving on':
@@ -63,23 +67,24 @@ $ kubectl-nsenter -u vagrant --ns m --ns p httpbin-5876b4fbc9-rtvrq mount -t xf
6367
**Or start a full shell session as well**:
6468
6569
```bash
66-
$ kubectl-nsenter -u vagrant httpbin-5876b4fbc9-rtvrq bash
70+
$ kubectl-nsenter -it httpbin-5876b4fbc9-rtvrq bash
6771
[root@w-01 ~]#
6872
```
6973
70-
And so on!
74+
Note, that ssh session requires keeping stdin (-i) and allocating pseudo-TTY (-t). Same as `docker run -it alpine sh`.
75+
76+
**Ultimate feature! Dump traffic from pod right on your station's wireshark!**
77+
78+
```bash
79+
kubectl-nsenter postgres tcpdump -nnni any -w- | wireshark -ki-
80+
```
7181

7282
## Init Containers
7383

7484
If desired pod is still initializing, nsenter will pick currently running container or fail, if none of init containers is running.
7585

7686
## Supported technologies
7787

78-
SSH:
79-
80-
- Ssh-agent;
81-
- Password.
82-
8388
Container Runtimes Clients:
8489

8590
- docker;
@@ -88,8 +93,3 @@ Container Runtimes Clients:
8893
OS:
8994

9095
- Unix-like.
91-
92-
## Known limitations
93-
94-
- Unfortunately, there are only interactive session with tty allocating available.
95-
- Currently there is no way to use this plugin on Windows because tty issues.

cmd/kubectl-nsenter/main.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,28 +13,30 @@ import (
1313

1414
var Version = "local"
1515

16+
const appName = "kubectl-nsenter"
17+
1618
func main() {
1719
app := &cli.App{
18-
Name: "kubectl-nsenter",
20+
Name: appName,
1921
Version: Version,
2022
Compiled: time.Now(),
2123
Authors: []*cli.Author{
2224
{
2325
Name: "pabateman",
2426
},
2527
},
26-
Copyright: 2022 pabateman",
27-
HelpName: "kubectl-nsenter",
28+
Copyright: fmt.Sprintf(%d pabateman", time.Time.Year(time.Now())),
29+
HelpName: appName,
2830
Usage: "kubectl plugin for pod's linux namespaces command execution " +
2931
"via direct node ssh connection",
30-
UsageText: `kubectl-nsenter [flags] [pod name] [command]
32+
UsageText: fmt.Sprintf(`%s [flags] [pod name] [command]
3133
3234
Example:
3335
34-
kubectl-nsenter -u node_user sample-pod-0 ip address
36+
%s -u node_user sample-pod-0 ip address
3537
36-
kubectl-nsenter -u node_user -p 2222 postgres-1 tcpdump -nni any port 5432
37-
`,
38+
%s -u node_user -p 2222 postgres-1 tcpdump -nni any port 5432
39+
`, appName, appName, appName),
3840
UseShortOptionHandling: true,
3941
EnableBashCompletion: true,
4042
HideHelpCommand: true,

internal/config/config.go

Lines changed: 80 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,6 @@ import (
77
"k8s.io/client-go/util/homedir"
88
)
99

10-
type Config struct {
11-
KubeConfig string
12-
KubeContext string
13-
Namespace string
14-
PodName string
15-
Container string
16-
Command []string
17-
SSHUser string
18-
SSHRequirePassword bool
19-
SSHSocketPath string
20-
SSHHost string
21-
SSHPort string
22-
LinuxNs []string
23-
}
24-
2510
const (
2611
argKubeconfig = "kubeconfig"
2712
argContainer = "container"
@@ -30,9 +15,13 @@ const (
3015
argUser = "user"
3116
argPassword = "password"
3217
argSSHAuthSock = "ssh-auth-sock"
18+
argSSHOpts = "ssh-opt"
3319
argHost = "host"
3420
argPort = "port"
3521
argNs = "ns"
22+
argInteractive = "interactive"
23+
argTTY = "tty"
24+
argUseNodeName = "use-node-name"
3625
)
3726

3827
var (
@@ -42,88 +31,119 @@ var (
4231
Usage: "kubernetes client config path",
4332
EnvVars: []string{"KUBECONFIG"},
4433
Value: fmt.Sprintf("%s/.kube/config", homedir.HomeDir()),
45-
Required: false,
4634
DefaultText: "$HOME/.kube/config",
4735
},
4836
&cli.StringFlag{
49-
Name: argContainer,
50-
Aliases: []string{"c"},
51-
Usage: "use namespace of specified container. By default first running container will taken",
52-
Value: "",
53-
Required: false,
37+
Name: argContainer,
38+
Aliases: []string{"c"},
39+
Usage: "use namespace of specified container. By default first running container will taken",
40+
Value: "",
5441
},
5542
&cli.StringFlag{
56-
Name: argContext,
57-
Usage: "override current context from kubeconfig",
58-
Value: "",
59-
Required: false,
43+
Name: argContext,
44+
Usage: "override current context from kubeconfig",
45+
Value: "",
6046
},
6147
&cli.StringFlag{
62-
Name: argNamespace,
63-
Aliases: []string{"n"},
64-
Usage: "override namespace of current context from kubeconfig",
65-
Value: "",
66-
Required: false,
48+
Name: argNamespace,
49+
Aliases: []string{"n"},
50+
Usage: "override namespace of current context from kubeconfig",
51+
Value: "",
6752
},
6853
&cli.StringFlag{
69-
Name: argUser,
70-
Aliases: []string{"u"},
71-
Usage: "set username for ssh connection to node",
72-
EnvVars: []string{"USER"},
73-
Required: false,
54+
Name: argUser,
55+
Aliases: []string{"u"},
56+
Usage: "set username for ssh connection to node",
7457
},
7558
&cli.BoolFlag{
7659
Name: argPassword,
7760
Aliases: []string{"s"},
7861
Usage: "force ask for node password prompt",
79-
Value: false,
8062
},
8163
&cli.StringFlag{
8264
Name: argSSHAuthSock,
8365
Usage: "sets ssh-agent socket",
8466
EnvVars: []string{"SSH_AUTH_SOCK"},
8567
DefaultText: "current shell auth sock",
86-
Required: false,
8768
},
8869
&cli.StringFlag{
89-
Name: argHost,
90-
Usage: "override node ip",
91-
Required: false,
70+
Name: argHost,
71+
Usage: "override node ip",
9272
},
9373
&cli.StringFlag{
94-
Name: argPort,
95-
Aliases: []string{"p"},
96-
Usage: "sets ssh port",
97-
Value: "22",
98-
Required: false,
74+
Name: argPort,
75+
Aliases: []string{"p"},
76+
Usage: "sets ssh port",
9977
},
10078
&cli.StringSliceFlag{
101-
Name: argNs,
102-
Usage: "define container's pid linux namespaces to enter. Sends transparently to nsenter cmd",
103-
Value: cli.NewStringSlice("n"),
104-
Required: false,
79+
Name: argNs,
80+
Usage: "define container's pid linux namespaces to enter. Sends transparently to nsenter cmd",
81+
Value: cli.NewStringSlice("n"),
82+
},
83+
&cli.BoolFlag{
84+
Name: argInteractive,
85+
Aliases: []string{"i"},
86+
Usage: "keep ssh session stdin",
87+
Value: false,
88+
},
89+
&cli.BoolFlag{
90+
Name: argTTY,
91+
Aliases: []string{"t"},
92+
Usage: "allocate pseudo-TTY for ssh session",
93+
Value: false,
94+
},
95+
&cli.StringSliceFlag{
96+
Name: argSSHOpts,
97+
Aliases: []string{"o"},
98+
Usage: "same as -o for ssh client",
99+
},
100+
&cli.BoolFlag{
101+
Name: argUseNodeName,
102+
Aliases: []string{"j"},
103+
Usage: "use kubernetes node name to connect with ssh. Useful with ssh configs",
104+
EnvVars: []string{"KUBECTL_NSENTER_USE_NODE_NAME"},
105+
Value: true,
105106
},
106107
}
107108
stringFlags = []string{argKubeconfig, argContainer, argContext, argNamespace, argUser, argSSHAuthSock, argHost, argPort}
108109
)
109110

110-
func NewConfig(clictx *cli.Context) (Config, error) {
111+
type Config struct {
112+
KubeConfig string
113+
KubeContext string
114+
Namespace string
115+
PodName string
116+
Container string
117+
Command []string
118+
SSHUser string
119+
SSHRequirePassword bool
120+
SSHSocketPath string
121+
SSHHost string
122+
SSHPort string
123+
SSHOpts []string
124+
Interactive bool
125+
TTY bool
126+
UseNodeName bool
127+
LinuxNs []string
128+
}
129+
130+
func NewConfig(clictx *cli.Context) (*Config, error) {
111131
podName := clictx.Args().First()
112132
if podName == "" {
113-
return Config{}, errorWithCliHelp(clictx, "you must specify pod name!")
133+
return nil, errorWithCliHelp(clictx, "you must specify pod name!")
114134
}
115135

116136
command := clictx.Args().Tail()
117137
if len(command) == 0 {
118-
return Config{}, errorWithCliHelp(clictx, "you must provide a command!")
138+
return nil, errorWithCliHelp(clictx, "you must provide a command!")
119139
}
120140

121141
err := validateStringFlagsNonEmpty(clictx, stringFlags)
122142
if err != nil {
123-
return Config{}, err
143+
return nil, err
124144
}
125145

126-
return Config{
146+
return &Config{
127147
KubeConfig: clictx.String(argKubeconfig),
128148
KubeContext: clictx.String(argContext),
129149
Namespace: clictx.String(argNamespace),
@@ -135,6 +155,10 @@ func NewConfig(clictx *cli.Context) (Config, error) {
135155
SSHRequirePassword: clictx.Bool(argPassword),
136156
SSHHost: clictx.String(argHost),
137157
SSHPort: clictx.String(argPort),
158+
SSHOpts: clictx.StringSlice(argSSHOpts),
159+
Interactive: clictx.Bool(argInteractive),
160+
TTY: clictx.Bool(argTTY),
161+
UseNodeName: clictx.Bool(argUseNodeName),
138162
LinuxNs: clictx.StringSlice(argNs),
139163
}, nil
140164
}
@@ -150,13 +174,13 @@ func validateStringFlagsNonEmpty(clictx *cli.Context, flags []string) error {
150174
return nil
151175
}
152176

153-
func errorWithCliHelp(clictx *cli.Context, msg string) error {
177+
func errorWithCliHelp(clictx *cli.Context, a any) error {
154178
err := cli.ShowAppHelp(clictx)
155179
if err != nil {
156180
return err
157181
}
158182
// nolint:stylecheck
159-
return fmt.Errorf("%s\n", msg)
183+
return fmt.Errorf("%s\n", a)
160184
}
161185

162186
func errorWithCliHelpf(clictx *cli.Context, format string, a ...any) error {

0 commit comments

Comments
 (0)