ov-coder/skills/sshd/SKILL.md
OpenSSH server and client on port 22 for remote access. Use when working with SSH access, remote login, or sshd configuration in containers/VMs.
npx skillsauth add overthinkos/overthink-plugins sshdInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
| Property | Value |
|----------|-------|
| Ports | 22 |
| Install files | layer.yml |
openssh-server (RPM / deb) — SSH daemonopenssh-clients (RPM) / openssh-client (deb, singular) — SSH client tools (ssh, scp, sftp)openssh (pac) — Arch metapackage bundling both daemon and clientsudo (rpm / pac / deb) — required for the NOPASSWD rule this layer writesrpm: (Fedora), pac: (Arch), deb: (Debian/Ubuntu) — full parity across all four supported package-format families. The openssh-server / openssh-clients naming differs per distro; package-existence tests use package_map: to resolve (see below).
Cross-distro package-test pattern: the sshd layer's
openssh-server-package check uses package_map: to resolve the right
name per distro — openssh-server on Fedora/Debian, openssh on Arch.
This is the canonical worked example for the package_map feature; see
/ov-build:eval "Cross-distro package names (package_map:)" for the
mechanics and the priority ordering (fedora:43 > fedora when both
match).
- id: openssh-server-package
package: openssh-server # default
package_map:
archlinux: openssh
fedora: openssh-server
fedora:43: openssh-server
debian: openssh-server
ubuntu: openssh-server
installed: true
getent passwd 1000The sudoers drop-in at /etc/sudoers.d/ov-user targets the actual uid-1000 account, whatever it happens to be named on the running base image. The layer no longer hardcodes a literal user — instead it discovers the account name at build time via getent passwd 1000:
tasks:
- cmd: |
account=$(getent passwd 1000 | cut -d: -f1)
if [ -z "$account" ]; then
echo "sshd layer: no uid-1000 account found — refusing to write sudoers" >&2
exit 1
fi
printf '%s ALL=(ALL) NOPASSWD: ALL\n' "$account" > /etc/sudoers.d/ov-user
chmod 0440 /etc/sudoers.d/ov-user
user: root
This works uniformly across both user-policy modes:
| Image | Resolved account | Sudoers content |
|---|---|---|
| fedora-coder, arch-coder, debian-coder | user (create mode — /ov-build:image "user_policy") | user ALL=(ALL) NOPASSWD: ALL |
| ubuntu-coder | ubuntu (adopt mode — /ov-foundation:ubuntu base_user:) | ubuntu ALL=(ALL) NOPASSWD: ALL |
Why not ${USER} substitution? The generator substitutes ${USER} in task fields (paths, URLs, etc.) but not inside cmd: command text — cmd: is passed verbatim to bash, and bash at RUN time doesn't have $USER exported. getent is the robust, fully-generic alternative. See /ov-build:layer "${VAR} substitution scope" and /ov-build:image "user_policy" for the full architectural context.
# image.yml -- typically used via bootc-base composition
my-image:
layers:
- sshd
bootc-base composition layer (used in bootc images)/ov-foundation:aurora (disabled)/ov-selkies:selkies-desktop-bootc (via bootc-base) — worked example exercising the dual-mode sudo test below/etc/sudoers.d/ov-user (the NOPASSWD rule written by this layer) is
root:root 0750 — the non-root test user (uid 1000 in containers)
cannot traverse /etc/sudoers.d/. A file: /etc/sudoers.d/ov-user; exists: true
test reports "missing" even when the file is present. Use
command: sudo -n -l; stdout: [{contains: NOPASSWD}] to verify the
semantic instead. See /ov-build:eval Authoring Gotcha #10.127.0.0.1:${HOST_PORT:2222}, not
${CONTAINER_IP}:${HOST_PORT:2222}. See /ov-build:eval Gotcha #1.runuser -u user -- wrapperov eval image runs with USER=1000 on container images but USER=0 on bootc images (bootc intentionally keeps USER=root because systemd manages user sessions via login). A naïve sudo -n -l; contains: NOPASSWD check fails on bootc — running as root prints root's Defaults block, which doesn't contain the literal string NOPASSWD. The layer's current test drops to user explicitly when running as root:
- id: sudoers-ov-user
command: |
if [ "$(id -u)" = "0" ]; then
runuser -u user -- sudo -n -l
else
sudo -n -l
fi
exit_status: 0
stdout:
- contains: "NOPASSWD"
Portability note: use runuser -u user -- <cmd>, not
runuser -l user -s /bin/bash -c '<cmd>'. On Arch util-linux (2.42+),
the -l … -c form swallows the wrapped command's stdout — reproduced
cleanly: runuser -l user -s /bin/bash -c 'sudo -n -l' prints nothing
and exits 0, while runuser -u user -- sudo -n -l prints the full
NOPASSWD listing. The layer was fixed to -u … -- after this was
caught during arch-ov bring-up. See /ov-build:eval Authoring Gotcha #11.
/ov-foundation:bootc-base -- composition that includes this layer/ov-foundation:bootc-config -- the bootc boot wiring (tty1 autologin + systemd-user supervisord) that typically runs alongside this layer/ov-foundation:cloud-init -- depends on sshd for VM provisioning/ov-selkies:selkies-desktop-bootc -- canonical bootc worked example that exercises the dual-mode sudo test/ov-coder:ubuntu-coder -- canonical adopt-mode example; sudoers correctly targets ubuntu via getent/ov-coder:debian-coder -- canonical create-mode deb-family example; sudoers targets user/ov-foundation:ubuntu -- declares the base_user: block that makes ubuntu-coder run as ubuntu/ov-build:eval -- declarative testing framework (gotchas #10 and #11, package_map:, exclude_distros:)/ov-build:image -- user_policy: field (create / adopt / auto) that drives which account this layer's sudoers targets/ov-build:layer -- layer authoring (${VAR} substitution scope, cmd: vs write:)Use when the user asks about:
tools
OpenCharly CLI (charly) binary installed into container/VM images for in-container use. Use when working with charly binary deployment inside containers, native D-Bus support, or the full charly toolchain (charly binary + virtualization + gocryptfs + socat).
development
Operator CachyOS workstation profile — a kind:local template + target:local deploy that installs the full dev stack (30 candies) onto a CachyOS host via ShellExecutor. Lives in the overthinkos/cachyos submodule. MUST be invoked before editing or applying the charly-cachyos workstation profile.
tools
Fedora box with the full charly toolchain using shared candies. Rootless-first — runs as uid=1000 with passwordless sudo (no root, no cap_add: ALL). Same candy list as charly-arch. Includes NVIDIA GPU runtime. MUST be invoked before building, deploying, configuring, or troubleshooting the charly-fedora box.
tools
Arch Linux box with the full charly toolchain. Rootless-first — runs as uid=1000 with passwordless sudo (no root, no cap_add: ALL). Composes /charly-coder:charly-mcp so the box is reachable as an MCP gateway on port 18765. NVIDIA GPU runtime composed in. MUST be invoked before building, deploying, configuring, or troubleshooting the charly-arch box.