|
| 1 | +# syntax=docker/dockerfile:1 |
| 2 | + |
| 3 | +FROM node:20-bookworm-slim |
| 4 | + |
| 5 | +ENV NODE_ENV=production \ |
| 6 | + # Runtime directory required by gnome-keyring/DBus |
| 7 | + XDG_RUNTIME_DIR=/home/node/.xdg/runtime |
| 8 | + |
| 9 | +# Runtime deps: libsecret + gnome-keyring + dbus (provides org.freedesktop.secrets) + build deps for keytar |
| 10 | +RUN apt-get update \ |
| 11 | + && apt-get install -y --no-install-recommends \ |
| 12 | + libsecret-1-0 \ |
| 13 | + libsecret-tools \ |
| 14 | + libglib2.0-bin \ |
| 15 | + gnome-keyring \ |
| 16 | + dbus \ |
| 17 | + dbus-x11 \ |
| 18 | + ca-certificates \ |
| 19 | + # build deps (in case keytar needs to compile) |
| 20 | + python3 \ |
| 21 | + make \ |
| 22 | + g++ \ |
| 23 | + pkg-config \ |
| 24 | + libsecret-1-dev \ |
| 25 | + && rm -rf /var/lib/apt/lists/* |
| 26 | + |
| 27 | +## Install lark-mcp globally (will install keytar as dependency) |
| 28 | +RUN npm install -g @larksuiteoapi/lark-mcp@latest \ |
| 29 | + && npm cache clean --force |
| 30 | + |
| 31 | +## Prepare XDG and user-writable dirs before dropping privileges |
| 32 | +RUN mkdir -p ${XDG_RUNTIME_DIR} \ |
| 33 | + && mkdir -p /home/node/.local/state /home/node/.local/share/lark-mcp \ |
| 34 | + && chown -R node:node ${XDG_RUNTIME_DIR} /home/node/.local |
| 35 | + |
| 36 | +## Dev dependencies were pruned in the build stage; no need to prune again here |
| 37 | + |
| 38 | +# Initialize DBus and gnome-keyring (secrets) so keytar can operate |
| 39 | +RUN <<'EOF' |
| 40 | +cat >/usr/local/bin/docker-entrypoint.sh <<'SCRIPT' |
| 41 | +#!/usr/bin/env bash |
| 42 | +set -e |
| 43 | + |
| 44 | +# Prepare DBus session; address uses XDG_RUNTIME_DIR |
| 45 | +if [[ -z "${DBUS_SESSION_BUS_ADDRESS:-}" ]]; then |
| 46 | + mkdir -p "${XDG_RUNTIME_DIR}" |
| 47 | + dbus-daemon --session --address="unix:path=${XDG_RUNTIME_DIR}/bus" --fork |
| 48 | + export DBUS_SESSION_BUS_ADDRESS="unix:path=${XDG_RUNTIME_DIR}/bus" |
| 49 | +fi |
| 50 | + |
| 51 | +# Create required directories with correct permissions |
| 52 | +mkdir -p "${XDG_RUNTIME_DIR}/keyring" |
| 53 | +mkdir -p "${HOME}/.local/share/keyrings" |
| 54 | +chmod 0700 "${XDG_RUNTIME_DIR}/keyring" |
| 55 | +chmod 0700 "${HOME}/.local/share/keyrings" |
| 56 | + |
| 57 | +# Start gnome-keyring daemon properly |
| 58 | +if command -v gnome-keyring-daemon >/dev/null 2>&1; then |
| 59 | + # Kill any existing keyring processes |
| 60 | + pkill -f gnome-keyring 2>/dev/null || true |
| 61 | + sleep 0.5 |
| 62 | + |
| 63 | + # Create an initial login keyring file |
| 64 | + current_ts="$(date +%s)" |
| 65 | + cat > "${HOME}/.local/share/keyrings/login.keyring" <<KEYRING_EOF |
| 66 | +[keyring] |
| 67 | +display-name=Login |
| 68 | +ctime=${current_ts} |
| 69 | +mtime=${current_ts} |
| 70 | +lock-on-idle=false |
| 71 | +lock-after=false |
| 72 | +KEYRING_EOF |
| 73 | + chmod 0600 "${HOME}/.local/share/keyrings/login.keyring" |
| 74 | + |
| 75 | + # Start gnome-keyring daemon with control directory (suppress stderr warnings) |
| 76 | + eval $(gnome-keyring-daemon --start --daemonize --components=secrets --control-directory="${XDG_RUNTIME_DIR}/keyring" 2>/dev/null) |
| 77 | + |
| 78 | + # Wait for the daemon to be available and control socket to exist |
| 79 | + for i in {1..50}; do |
| 80 | + if [[ -S "${XDG_RUNTIME_DIR}/keyring/control" ]] && gdbus introspect --session --dest org.freedesktop.secrets --object-path /org/freedesktop/secrets >/dev/null 2>&1; then |
| 81 | + break |
| 82 | + fi |
| 83 | + sleep 0.1 |
| 84 | + done |
| 85 | + |
| 86 | + # Export control directory for the session |
| 87 | + export GNOME_KEYRING_CONTROL="${XDG_RUNTIME_DIR}/keyring" |
| 88 | + |
| 89 | + # Create login collection via D-Bus |
| 90 | + if gdbus introspect --session --dest org.freedesktop.secrets --object-path /org/freedesktop/secrets >/dev/null 2>&1; then |
| 91 | + # Try to create login collection |
| 92 | + printf "" | gdbus call --session \ |
| 93 | + --dest org.freedesktop.secrets \ |
| 94 | + --object-path /org/freedesktop/secrets \ |
| 95 | + --method org.freedesktop.secrets.Service.CreateCollection \ |
| 96 | + "{'org.freedesktop.Secret.Collection.Label': <'login'>}" \ |
| 97 | + "login" >/dev/null 2>&1 || true |
| 98 | + |
| 99 | + # Set login as the default alias |
| 100 | + gdbus call --session \ |
| 101 | + --dest org.freedesktop.secrets \ |
| 102 | + --object-path /org/freedesktop/secrets \ |
| 103 | + --method org.freedesktop.secrets.Service.SetAlias \ |
| 104 | + "login" "/org/freedesktop/secrets/collection/login" >/dev/null 2>&1 || true |
| 105 | + fi |
| 106 | +fi |
| 107 | + |
| 108 | +# Suppress gnome-keyring warnings by redirecting stderr for the main process |
| 109 | +exec "$@" 2> >(grep -v "couldn't access control socket\|discover_other_daemon" >&2) |
| 110 | +SCRIPT |
| 111 | +chmod +x /usr/local/bin/docker-entrypoint.sh |
| 112 | +EOF |
| 113 | + |
| 114 | +USER node |
| 115 | + |
| 116 | +EXPOSE 3000 |
| 117 | + |
| 118 | +ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh", "lark-mcp"] |
| 119 | + |
| 120 | +# Show help by default; override CMD to pass CLI arguments |
| 121 | +CMD ["--help"] |
| 122 | + |
| 123 | + |
0 commit comments