Note: this article is reposted from my company intranet blog.
Introduction
I’ve been researching how to provide container shell login for users. This blog organizes my thoughts, exploring two approaches: one accessing pods in the Kubernetes cluster via a proxy; the other based on webshell but allowing users to log in locally. It analyzes both approaches’ security and functional usage.
User Requirements
Although we’re a PaaS platform — developers provide code and configuration and we deploy it — when the following problems arise, developers may want to log into their own containers and run certain commands:
- Environment debugging during development: viewing environment variables, test-connecting databases, printing logs and verifying code behavior
- Already-online services: developers expect to manually execute some scripts; using a real shell to run scripts is faster and more convenient
Solution - Adding a Shell Container
In the current environment, launching an application creates three kubernetes services: a Deployment, a Service, and an Ingress.
Ingress forwards traffic to the corresponding Pod.
To avoid affecting already-running online services, the shell login function doesn’t happen in the online container; instead a new Pod is started separately to provide the shell service.
Since the Kubernetes cluster doesn’t use the host network but an internal IP allocated by flannel, after this Pod starts, how can it be accessed from outside the Kubernetes cluster:
- Add a jump server or proxy service providing an external egress, with the proxy server accessing Kubernetes’ internal resources
- Use WebShell: our platform assigns a domain for the service, plus authentication to improve security
Below I introduce the research results for both approaches.
ssh Proxy Usage - ProxyCommand
The ssh discussed in this part is OpenSSH, which must support the ProxyCommand parameter.
Service Proxying TCP Ports
Since we provide an external proxy, how do we distinguish each application internally? Here I plan to use the Service-proxying-TCP-ports feature. The whole service is as follows:
1 |
|
For different applications, as long as they’re pods in k8s, we access the corresponding service in the form telnet appXX-ssh 22.
See the appendix for the Dockerfile and Kubernetes configuration files.
Using a Jump Server
The jump server’s ssh config is as follows
1 | Host deb |
1 | +---------------+ |
Using a Proxy – HTTPProxy
This is one approach I researched:
OpenSSH’s ProxyCommand works by using a child process to establish a TCP connection, then communicating with the parent process via pipes. After understanding this, we can actually rely on HTTP to establish the connection and then transfer TCP packets.
1 | # note: the nc here must be netcat-openbsd (the openbsd version of nc; gnu's nc has no -X feature) |
Here 127.0.0.1:8888 is the http proxy opened on the local machine; for a fairly simple proxy server see HTTP proxy principles and implementation
1 | var http = require('http'); |
For the concrete online service, I personally lean toward HTTPProxy. Two benefits:
- Establishing a
Jump Serverrequires maintaining an OpenSSH server; an http proxy only requires maintaining a fairly small amount of code - In an http proxy, you can conveniently control which services users may reach — a whitelist can be built into the program, making security higher than a
Jump Server
WebShell - Web and Local Functionality
Basic WebShell
The WebShell research mainly revolves around gotty, mainly relying on gotty+tmux to support machine access.
1 | # its function is creating a new window with tmux |
Visiting http://127.0.0.1:9000 shows:

The gotty Client
gotty’s README introduces how to access the GoTTY service from a terminal — gotty-client was released:
1 | go get github.com/moul/gotty-client/cmd/gotty-client |
1 | ┌─────────────────┐ |
The basic principle is reusing GoTTY’s websocket. But trying it out, I found its tmux support is really poor.

For heavy tmux users this is almost completely unusable — the character printing is too outrageous.
Comparing the Two Approaches
With the two candidate approaches above — one based on native ssh, the other resembling WebShell — I’ll discuss both in terms of difficulty, security, and maintenance cost.
Functionality and Implementation Difficulty Discussion
Functionally: Both approaches can meet users’ requirement of connecting into containers.
I normally use native ssh too much; personally I hope to leverage the various features OpenSSH provides. WebSSH gives a gaudy feeling; shortcut key support has problems — one
Ctrl-Wmeans the page gets closed.Implementation difficulty:
- The former needs an added service as proxy server, plus good private key management
- The latter needs combining with WebShell tools like gotty, likewise needing good permission control; it also supports password login itself
Maintenance and Decommission Cost Discussion
The former requires maintaining the proxy server, plus the ProxyCommand for local connections.
The latter may require maintaining gotty and possibly gotty-client in future.
The latter has higher maintenance cost.
Security Discussion
Data transmission encryption:
- The former encrypts via ssh itself
- The latter can use websocket over https, encrypting transmission via https
Cluster security guarantee (not-logged-in state):
- The former provides a layer-4 proxy, so security needs special attention — otherwise service ports in the cluster that shouldn’t be exposed may be exposed (solution proposed below)
- The latter’s login happens entirely from the web page; its security matches the PaaS platform
Cluster security guarantee (logged-in state): In both systems user operations can be recorded, but after granting root permission users may change that themselves. In both systems users can access all other services in the cluster — hard to restrict for now.
Regarding the layer-4 proxy service’s security, I suggest guaranteeing it the following ways (taking the http proxy server as an example):
- This proxy server only allows internal network access
- This proxy server adds
basic auth, requiring users to give username and password when connecting - This proxy only allows certain domains — like
sshservice domains resemblingapp01-ssh— to pass, disallowing access to other domains or ips besides ssh domains
If the above 3 points can be achieved, the proxy server’s security is basically equal to WebShell’s.
Summary
This article explored two approaches for externally connecting to Kubernetes pods: one accessing pods in the Kubernetes cluster via ssh proxy; the other based on WebShell but allowing users to log in locally. It analyzed both approaches’ functionality, implementation difficulty, and security.
From a personal perspective, I lean toward the proxy approach — after all, native ssh has many companion tools,
e.g. scp, sshfs, and configurations like LocalForward can also be leveraged.
Known Problems and Solutions
While testing remote server connections, the following problem appeared:
1 | ~ ❤ ssh -o "ProxyCommand=nc -X connect -x 10.202.37.229:8081 %h %p" [email protected] |
After capturing packets with wireshark, I saw netcat splitting the tcp packets, causing the remote server to parse incomplete ssh packets and be unable to respond.

Given the above problem, I decided to write a simple proxy tool myself to replace nc. Here’s the simple version of the tool:
1 | # -*- coding: utf-8 -*- |
Appendix
Enabling the sshd Service in Kubernetes
Dockerfile
1 | # ssh-test |
- Kubernetes configuration files
1 |
|