Compare commits

...
2 Commits
Author SHA1 Message Date
Richard Zink 126c7719b9 Release v202611-00034 2026-11-15 12:15:04 +01:00
Richard ZinkandClaude Sonnet 5.5 fccaee4b94 Fix ssh/scp/sftp for containers running as a uid without passwd entry
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-15 12:15:02 +01:00
6 changed files with 34 additions and 3 deletions

No files matched your search

+7
View File
@@ -3,6 +3,13 @@
All notable changes to CodeCraft. Versions follow [Semantic Versioning](https://semver.org/);
until 1.0, minor versions may contain breaking changes.
## [v202611-00034] – 2026-11-15
### Fixed
- `ssh`, `scp` and `sftp` failed with "No user exists for uid 99" because the container runs as a uid
without passwd entry; the image now wraps them with nss_wrapper (also used by the key wrappers).
## [v202611-00033] – 2026-11-15
### Added
+5 -1
View File
@@ -85,7 +85,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential pkg-config libssl-dev cmake zlib1g-dev binutils-multiarch \
chromium fonts-dejavu-core fonts-noto-color-emoji poppler-utils openmpt123 \
openscad xvfb xauth \
openssh-client rsync mc \
openssh-client libnss-wrapper rsync mc \
&& rm -rf /var/lib/apt/lists/*
# Language servers for diagnostics after edits (rust-analyzer/gopls/clangd can be
# installed by agents: rustup component add rust-analyzer, go install …).
@@ -98,6 +98,10 @@ COPY --from=dockercli /docker /usr/local/bin/docker
COPY server/ghidra /app/ghidra
# git for agent shells: commits get the co-author trailer of the model (server.commit_trailer).
COPY --chmod=755 docker/git-coauthor /usr/local/lib/codecraft/bin/git
# ssh/scp/sftp also work when the container runs as a uid without passwd entry.
COPY --chmod=755 docker/ssh-nss /usr/local/bin/ssh
COPY --chmod=755 docker/ssh-nss /usr/local/bin/scp
COPY --chmod=755 docker/ssh-nss /usr/local/bin/sftp
COPY server/resources /app/resources
# The agent's home lives on the data volume: tools it installs in user space
# (pip --user, pipx, rustup, npm -g, downloaded binaries) survive rebuilds.
+1 -1
View File
@@ -11,7 +11,7 @@
</p>
<p align="center">
<img alt="Version v202611-00033" src="https://img.shields.io/badge/version-v202611--00033-d69e2e?style=flat-square">
<img alt="Version v202611-00034" src="https://img.shields.io/badge/version-v202611--00034-d69e2e?style=flat-square">
<img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-689d6a?style=flat-square">
<img alt="Rust backend" src="https://img.shields.io/badge/backend-Rust-b7410e?style=flat-square&logo=rust">
<img alt="React PWA" src="https://img.shields.io/badge/frontend-React%20PWA-2d81ff?style=flat-square&logo=react">
Executable
+13
View File
@@ -0,0 +1,13 @@
#!/bin/sh
# ssh, scp and sftp for containers that run as an arbitrary uid (docker-compose `user:`):
# OpenSSH refuses to start without a passwd entry for the uid ("No user exists for uid N").
# Without an entry, nss_wrapper provides one for this process; otherwise nothing changes.
if ! getent passwd "$(id -u)" >/dev/null 2>&1; then
d=${TMPDIR:-/tmp}/codecraft-nss-$(id -u)
mkdir -p "$d" 2>/dev/null
printf 'codecraft:x:%s:%s:CodeCraft:%s:/bin/sh\n' "$(id -u)" "$(id -g)" "${HOME:-/}" > "$d/passwd"
printf 'codecraft:x:%s:\n' "$(id -g)" > "$d/group"
export NSS_WRAPPER_PASSWD="$d/passwd" NSS_WRAPPER_GROUP="$d/group"
export LD_PRELOAD="libnss_wrapper.so${LD_PRELOAD:+:$LD_PRELOAD}"
fi
exec "/usr/bin/$(basename "$0")" "$@"
+4 -1
View File
@@ -126,7 +126,10 @@ pub fn write_files(app: &App, user: &User, s: &SshSettings) -> Result<()> {
chmod(&host_cfg, 0o600)?;
for (bin, config) in [(bin_dir(&home), format!("'{}'", host_cfg.display())), (home.join(SANDBOX_BIN), "\"$HOME/.ssh/config-sandbox\"".to_string())] {
std::fs::create_dir_all(&bin)?;
for (name, real) in [("ssh", "/usr/bin/ssh"), ("scp", "/usr/bin/scp"), ("sftp", "/usr/bin/sftp")] {
for name in ["ssh", "scp", "sftp"] {
// The image's /usr/local/bin wrappers cope with a uid without passwd entry.
let local = format!("/usr/local/bin/{name}");
let real = if Path::new(&local).exists() { local } else { format!("/usr/bin/{name}") };
let p = bin.join(name);
std::fs::write(&p, format!("#!/bin/sh\nexec {real} -F {config} \"$@\"\n"))?;
chmod(&p, 0o755)?;
+4
View File
@@ -3,6 +3,10 @@
* under "unreleased"; scripts/release.sh renames the key to the release version.
*/
export const WHATS_NEW: Record<string, { en: string[]; de: string[] }> = {
"202611-00034": {
en: ["ssh/scp/sftp now work in the container (fixes \"No user exists for uid\")"],
de: ["ssh/scp/sftp funktionieren jetzt im Container (behebt \"No user exists for uid\")"],
},
"202611-00033": {
en: ["SSH keys in Settings → Account: agents, terminal and git use them to reach other servers"],
de: ["SSH-Schlüssel unter Einstellungen → Account: Agenten, Terminal und Git nutzen sie für den Zugriff auf andere Server"],