All checks were successful
Build k8s-mgmt-pod image / Build & Push (push) Successful in 53s
214 lines
8.1 KiB
Markdown
214 lines
8.1 KiB
Markdown
# k8s-mgmt-pod
|
|
|
|
`k8s-mgmt-pod` is a Debian-based Kubernetes bastion image with:
|
|
|
|
- `lfk` preinstalled from a pinned GitHub release `.deb`
|
|
- `kubectl` preinstalled from a pinned upstream binary release
|
|
- `helm` preinstalled from a pinned upstream binary release
|
|
- `k9s` preinstalled from a pinned GitHub release tarball
|
|
- `kubectl` and `helm` bash autocompletion enabled for both `admin` and `user`
|
|
- local `openssh-server` on port `2222`
|
|
- a browser-based SSH client on port `3000`
|
|
- a custom ASCII-art MOTD on login
|
|
- two provisioned users: `admin` and `user`
|
|
|
|
## Why WebSSH2
|
|
|
|
This image uses [WebSSH2](https://github.com/billchurch/webssh2) for browser access instead of a generic web terminal wrapper. WebSSH2 is an actively maintained SSH client that speaks SSH to the local `sshd` in the same container, which matches the bastion use case better than wrapping a shell command directly.
|
|
|
|
One caveat is worth stating clearly: browser-based SSH tools do not keep the private-key handshake entirely in the browser. If a user chooses key-based login in the WebSSH2 UI, the key material is passed to the server-side SSH client for the actual SSH authentication flow.
|
|
|
|
## Ports
|
|
|
|
- `2222`: native SSH into the container's local `sshd`
|
|
- `3000`: browser UI entrypoint; `/` and `/ssh` are forced to a fixed local WebSSH2 session for `127.0.0.1` and rewritten back to that host if changed in the URL
|
|
|
|
## Runtime environment variables
|
|
|
|
The entrypoint configures authentication on every container start. A user can have both SSH public keys and a password at the same time.
|
|
|
|
| Variable | Meaning |
|
|
|---|---|
|
|
| `ADMIN_SSH_PUBKEY` | One or more newline-separated SSH public keys for `admin` |
|
|
| `USER_SSH_PUBKEY` | One or more newline-separated SSH public keys for `user` |
|
|
| `ADMIN_PASSWORD` | Optional password for `admin`; can be used with or without `ADMIN_SSH_PUBKEY` |
|
|
| `USER_PASSWORD` | Optional password for `user`; can be used with or without `USER_SSH_PUBKEY` |
|
|
| `ENABLE_BOOTSTRAP_KUBECONFIG` | Set to `true` to copy a mounted kubeconfig into both users' homes at startup; defaults to `false` |
|
|
| `BOOTSTRAP_KUBECONFIG` | Path to the mounted kubeconfig source file when bootstrap is enabled; defaults to `/bootstrap/kubeconfig/config` |
|
|
|
|
Behavior per user:
|
|
|
|
1. If `*_SSH_PUBKEY` is non-empty, the entrypoint writes `authorized_keys`, fixes ownership and permissions, and enables SSH key login for that user.
|
|
2. If `*_PASSWORD` is set, the entrypoint also sets that password, even when a public key is present.
|
|
3. If no password is supplied but a public key is present, the entrypoint locks password login for that user and leaves key-based login enabled.
|
|
4. If neither a public key nor a password is supplied, the entrypoint generates a random password, stores it in the container state directory, applies it, and logs it once to container stdout with a clear label.
|
|
|
|
If both users end up key-only, `sshd` is configured for public-key-only login. If either user has an explicit or generated password, `sshd` enables password and keyboard-interactive authentication so both native SSH and WebSSH2 can use the same underlying auth methods.
|
|
|
|
## Build
|
|
|
|
```bash
|
|
docker build --load -t k8s-mgmt-pod:local .
|
|
```
|
|
|
|
If your Docker CLI is backed by Buildx with the `docker-container` driver, a plain `docker build` may finish successfully but leave the image only in the build cache. `--load` imports the result into your local Docker image store so `docker run k8s-mgmt-pod:local` works afterward.
|
|
|
|
Pinned build arguments can be overridden when you need to bump releases:
|
|
|
|
```bash
|
|
docker build --load \
|
|
--build-arg LFK_VERSION=0.14.9 \
|
|
--build-arg KUBECTL_VERSION=1.31.0 \
|
|
--build-arg HELM_VERSION=3.16.2 \
|
|
--build-arg K9S_VERSION=0.40.10 \
|
|
--build-arg WEBSSH2_VERSION=5.0.1 \
|
|
-t k8s-mgmt-pod:local .
|
|
```
|
|
|
|
## Run with Docker Compose
|
|
|
|
The included [docker-compose.yaml](docker-compose.yaml) enables kubeconfig bootstrap for local use. It mounts your host `${HOME}/.kube` directory read-only and copies `${HOME}/.kube/config` into both `/home/admin/.kube/config` and `/home/user/.kube/config` at container startup with the correct ownership and `0600` permissions.
|
|
|
|
That bootstrap is explicitly opt-in and defaults to off in the container itself, so it does not run in Kubernetes unless you deliberately enable it.
|
|
|
|
When both are available, the bootstrap kubeconfig is applied after the service-account kubeconfig and therefore takes precedence.
|
|
|
|
Start it with:
|
|
|
|
```bash
|
|
docker compose up --build
|
|
```
|
|
|
|
Or detached:
|
|
|
|
```bash
|
|
docker compose up -d --build
|
|
docker compose logs -f
|
|
```
|
|
|
|
If `2222` or `3000` is already in use on your host, override the published ports:
|
|
|
|
```bash
|
|
HOST_SSH_PORT=32222 HOST_WEBSSH_PORT=33000 docker compose up -d --build
|
|
```
|
|
|
|
You can override the default passwords or inject SSH public keys through your shell environment before starting Compose:
|
|
|
|
```bash
|
|
export ADMIN_PASSWORD='change-this-admin-password'
|
|
export USER_PASSWORD='change-this-user-password'
|
|
export ADMIN_SSH_PUBKEY="$(cat ~/.ssh/id_ed25519.pub)"
|
|
export USER_SSH_PUBKEY="$(cat ~/.ssh/id_ed25519.pub)"
|
|
docker compose up -d --build
|
|
```
|
|
|
|
You can also start from the example environment file:
|
|
|
|
```bash
|
|
cp .env.example .env
|
|
docker compose up -d --build
|
|
```
|
|
|
|
If `${HOME}/.kube/config` is missing on the host, the container logs that bootstrap was skipped and continues normally.
|
|
|
|
## Run with SSH public keys
|
|
|
|
```bash
|
|
docker run --rm \
|
|
-p 2222:2222 \
|
|
-p 3000:3000 \
|
|
-e ADMIN_SSH_PUBKEY="$(cat ~/.ssh/id_ed25519.pub)" \
|
|
-e USER_SSH_PUBKEY="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIexample user@example" \
|
|
k8s-mgmt-pod:local
|
|
```
|
|
|
|
Then connect either with native SSH:
|
|
|
|
```bash
|
|
ssh admin@localhost -p 2222
|
|
```
|
|
|
|
Or in a browser at `http://localhost:3000/`.
|
|
|
|
## Run with password fallback
|
|
|
|
```bash
|
|
docker run --rm \
|
|
-p 2222:2222 \
|
|
-p 3000:3000 \
|
|
-e ADMIN_PASSWORD='change-this-admin-password' \
|
|
-e USER_PASSWORD='change-this-user-password' \
|
|
k8s-mgmt-pod:local
|
|
```
|
|
|
|
If that fails with a message like `unable to upgrade to tcp, received 500`, the container may still be fine and the error may be coming from the attached run path in your Docker frontend. Try detached mode and inspect the logs instead:
|
|
|
|
```bash
|
|
docker run -d --name k8s-mgmt-pod \
|
|
-p 2222:2222 \
|
|
-p 3000:3000 \
|
|
-e ADMIN_PASSWORD='change-this-admin-password' \
|
|
-e USER_PASSWORD='change-this-user-password' \
|
|
k8s-mgmt-pod:local
|
|
|
|
docker logs -f k8s-mgmt-pod
|
|
```
|
|
|
|
If Docker reports `bind: address already in use`, change the host-side ports or let Docker choose them automatically:
|
|
|
|
```bash
|
|
docker run --rm \
|
|
-p 127.0.0.1::2222 \
|
|
-p 127.0.0.1::3000 \
|
|
-e ADMIN_PASSWORD='change-this-admin-password' \
|
|
-e USER_PASSWORD='change-this-user-password' \
|
|
k8s-mgmt-pod:local
|
|
```
|
|
|
|
## Run with both SSH key and password enabled
|
|
|
|
```bash
|
|
docker run --rm \
|
|
-p 2222:2222 \
|
|
-p 3000:3000 \
|
|
-e ADMIN_SSH_PUBKEY="$(cat ~/.ssh/id_ed25519.pub)" \
|
|
-e ADMIN_PASSWORD='change-this-admin-password' \
|
|
-e USER_SSH_PUBKEY="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIexample user@example" \
|
|
-e USER_PASSWORD='change-this-user-password' \
|
|
k8s-mgmt-pod:local
|
|
```
|
|
|
|
With that configuration, each user can authenticate with either their SSH key or their password.
|
|
|
|
## Run with generated password fallback
|
|
|
|
If you omit both the public-key and password variables for a user, the container generates a random password for that user and prints it once on startup.
|
|
|
|
```bash
|
|
docker run --rm \
|
|
-p 2222:2222 \
|
|
-p 3000:3000 \
|
|
-e ADMIN_SSH_PUBKEY="$(cat ~/.ssh/id_ed25519.pub)" \
|
|
k8s-mgmt-pod:local
|
|
```
|
|
|
|
Retrieve the generated password from logs immediately:
|
|
|
|
```bash
|
|
docker logs <container-name>
|
|
```
|
|
|
|
## CI publishing
|
|
|
|
The repository includes [.github/workflows/build-workflow.yaml](.github/workflows/build-workflow.yaml), modeled on the requested reference workflow. It:
|
|
|
|
- runs weekly, on `main`, on matching tags, on pull requests, and manually
|
|
- detects GitHub vs Forgejo and picks the correct registry and token
|
|
- builds `linux/amd64` only
|
|
- pushes only for non-PR runs
|
|
- tags `main` as `latest`, date-based, and date-plus-short-sha
|
|
- tags non-`main` branches with `dev-<branch>-*`
|
|
|
|
## Kubernetes deployment
|
|
|
|
Plain manifests live under [k8s/README.md](k8s/README.md) and the rest of the [k8s](k8s) directory.
|