Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# Keep the supply chain patched. GitHub Actions are pinned to commit SHAs in the
# workflows; dependabot bumps those SHAs (and the version comment) when a new
# release lands, and opens PRs for Go module updates. Security fixes to the Go
# standard library ride in via the toolchain (setup-go check-latest), not here.
# release lands, and opens PRs for Go module updates. Dependabot does not update
# the fixed release toolchain: Go security releases require a coordinated change
# to go.mod, release.yml, prepare-release.sh, and the release documentation.
version: 2
updates:
- package-ecosystem: github-actions
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/mirror-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
workflow_dispatch:
inputs:
tag:
description: Immutable release tag to mirror, for example v2.8.3
description: Immutable release tag to mirror, for example vX.Y.Z
required: true
type: string

Expand Down
33 changes: 18 additions & 15 deletions .github/workflows/stage-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -123,24 +123,27 @@ jobs:
shell: bash
run: |
set -Eeuo pipefail
lookup="$(mktemp)"
set +e
timeout -k 5 60 gh api --include "repos/${GH_REPO}/releases/tags/${TAG}" >"$lookup" 2>&1
lookup_status=$?
set -e
if [[ "$lookup_status" -eq 0 ]]; then
echo "release or draft $TAG already exists; refusing to refresh any remote asset" >&2
exit 1
# The tag-specific REST endpoint exposes only published releases. The
# authenticated list also exposes drafts to this write-capable token,
# so enumerate every page and refuse any existing use of the tag.
release_records="$(timeout -k 5 60 gh api --paginate \
"repos/${GH_REPO}/releases?per_page=100" \
--jq '.[] | [.tag_name, (.id|tostring)] | @tsv')" || {
echo "could not prove release $TAG is absent" >&2
exit 1
}
match_count=0
if [[ -n "$release_records" ]]; then
while IFS=$'\t' read -r actual_tag release_id extra; do
[[ -z "$extra" && -n "$actual_tag" && "$release_id" =~ ^[1-9][0-9]*$ ]] \
|| { echo "release enumeration returned malformed identity data" >&2; exit 1; }
[[ "$actual_tag" != "$TAG" ]] || match_count=$((match_count + 1))
done <<<"$release_records"
fi
[[ "$lookup_status" -eq 1 ]] \
|| { echo "release lookup failed with unexpected status $lookup_status" >&2; exit 1; }
[[ "$(grep -Ec '^HTTP/[0-9.]+ [0-9]{3}([[:space:]]|$)' "$lookup")" -eq 1 \
&& "$(grep -Ec '^HTTP/[0-9.]+ 404([[:space:]]|$)' "$lookup")" -eq 1 ]] || {
cat "$lookup" >&2
echo "could not prove release $TAG is absent" >&2
(( match_count == 0 )) || {
echo "release or draft $TAG already exists; refusing to refresh any remote asset" >&2
exit 1
}
rm -f -- "$lookup"

# Re-resolve the protected tag immediately before the first write;
# neither queueing nor an environment delay can make it stale.
Expand Down
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@
*.out
coverage.txt

# Release staging (produced by scripts/release.sh)
# Release build and staging output
/dist/

# Python test caches
__pycache__/
*.py[cod]

# Editor / OS junk
.DS_Store
*.swp
Expand Down
278 changes: 229 additions & 49 deletions CHANGELOG.md

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
<img alt="License" src="https://img.shields.io/badge/License-MIT-green?style=flat-square">
</p>

> One command creates a time-limited SSH administrator account for a trusted collaborator and removes it automatically when it expires.
> One command creates a time-limited SSH administrator account for a trusted collaborator and schedules automatic revocation at expiry.

**linux-temp-admin** avoids sharing the root password and never stores the invite's private key on the server. It creates a temporary account, prints a bundle you can forward privately, and later removes the account, SSH key, and sudo grant.
**linux-temp-admin** avoids sharing the root password and never stores the invite's private key on the server. It creates a temporary account, prints a bundle you can forward privately, and schedules revocation of the managed account, SSH key, and sudo grant.

The program is one static binary for amd64 and arm64 Linux, on both glibc and musl. Account, SSH, and scheduler operations still use the host's standard administration tools.

Expand All @@ -28,7 +28,7 @@ The tool then:

1. creates a temporary account with a random name;
2. generates a one-time SSH key and prints an invite bundle;
3. grants passwordless sudo by default and removes the account after 24 hours;
3. grants passwordless sudo by default and schedules automatic revocation after 24 hours;
4. checks the effective sshd configuration before creation, refusing a definite blocker and reporting incomplete knowledge as `UNVERIFIED`.

The quick start obtains the installer from the official mirror and sends it to a root shell. `set -o pipefail` propagates curl failures, so a failed install does not continue to `invite`; it **does not authenticate the script or stop an already received partial script from beginning execution**. Once the installer is running, the downloaded binary is still verified with SHA-256 and an ed25519 signature. Use the [high-assurance first-install procedure](docs/installing.en.md#high-assurance-first-install) when the script must be authenticated before execution.
Expand Down Expand Up @@ -70,7 +70,7 @@ The real private key is shown only once. Never put an invite bundle in a group c
## Inspect and revoke

```bash
# Show all temporary accounts
# Show all registered temporary accounts
/usr/bin/sudo /usr/local/sbin/linux-temp-admin status

# Choose an account from a list and revoke it
Expand All @@ -80,7 +80,7 @@ The real private key is shown only once. Never put an invite bundle in a group c
/usr/bin/sudo /usr/local/sbin/linux-temp-admin revoke --user xxvcc-a1b2c3d4e5
```

By default, the account, home directory, SSH key, sudo grant, and any tool-created sshd exception are removed after 24 hours. Revoke access immediately when work is finished even when automatic removal is enabled.
By default, automatic revocation is scheduled after 24 hours. When the complete account identity can still be checked, a successful revoke removes the personal crontab, UID-matched `at`/`batch` jobs, account, home directory, SSH key, sudo grant, and any tool-created sshd exception. If the account disappeared outside the tool, revoke cleans only the registry, name-scoped grants, and tasks it can still identify safely; it does not guess at Home or mail cleanup after losing the identity witness. If a safety check or cleanup fails, the command returns nonzero, retains the account when it still exists and the registry witness, and attempts to disable any surviving account for a systemd retry or manual recovery. Revoke access immediately when work is finished even when automatic revocation is enabled.

## Everyday commands

Expand Down Expand Up @@ -121,7 +121,7 @@ When public-key login is disabled, create an account-scoped sshd exception:
/usr/bin/sudo /usr/local/sbin/linux-temp-admin invite --sudo --fix-sshd
```

This does not modify the global sshd policy, and the exception is removed with the account. See the [operator guide](docs/operator-guide.en.md) for automation, password login, permanent accounts, and complete troubleshooting.
This does not modify the global sshd policy, and a successful account revoke removes the exception. See the [operator guide](docs/operator-guide.en.md) for automation, password login, permanent accounts, and complete troubleshooting.

## Security essentials

Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
<img alt="License" src="https://img.shields.io/badge/License-MIT-green?style=flat-square">
</p>

> 一条命令,为可信协作者创建一个有时限、用完自动删除的临时 SSH 管理员账号。
> 一条命令,为可信协作者创建一个有时限、安排到期自动撤销的临时 SSH 管理员账号。

**linux-temp-admin** 不需要分享 root 密码,也不会在服务器保存邀请私钥。它会创建临时账号、输出可私聊转发的邀请包,并在到期时自动撤销账号、SSH key 和 sudo 授权。
**linux-temp-admin** 不需要分享 root 密码,也不会在服务器保存邀请私钥。它会创建临时账号、输出可私聊转发的邀请包,并安排在到期时撤销受管账号、SSH key 和 sudo 授权。

程序是一个支持 glibc 和 musl 的静态二进制,适用于 amd64 和 arm64 Linux。账号、SSH 和定时任务操作仍会调用系统已有的标准管理工具。

Expand All @@ -28,7 +28,7 @@ curl -fsSL https://dl.ll.cd/linux-temp-admin/install.sh | /usr/bin/sudo /bin/sh

1. 创建一个随机命名的临时账号;
2. 生成一次性 SSH key,并在终端显示邀请包;
3. 默认授予免密 sudo,并在 24 小时后自动删除账号
3. 默认授予免密 sudo,并安排在 24 小时后自动撤销
4. 在创建前检查当前 sshd 配置:明确阻止登录就拒绝,无法完整判断则如实标记 `UNVERIFIED`。

快速入口从官方镜像取得安装脚本并交给 root shell。`set -o pipefail` 会传播 curl 失败,因此安装失败后不会继续创建邀请;它**不认证脚本本身,也不能阻止已经收到的部分脚本开始执行**。安装器启动后,下载的二进制仍会经过 SHA-256 和 ed25519 签名验证。需要在执行前认证安装脚本时,请使用[高保证首次安装流程](docs/installing.md#高保证首次安装)。
Expand Down Expand Up @@ -70,7 +70,7 @@ ssh -i ./xxvcc-a1b2c3d4e5.key -p 22 xxvcc-a1b2c3d4e5@203.0.113.10
## 查看和撤销

```bash
# 查看全部临时账号
# 查看全部已登记临时账号
/usr/bin/sudo /usr/local/sbin/linux-temp-admin status

# 从列表选择并撤销
Expand All @@ -80,7 +80,7 @@ ssh -i ./xxvcc-a1b2c3d4e5.key -p 22 xxvcc-a1b2c3d4e5@203.0.113.10
/usr/bin/sudo /usr/local/sbin/linux-temp-admin revoke --user xxvcc-a1b2c3d4e5
```

默认会在 24 小时后自动删除账号、家目录、SSH key、sudo 授权和本工具创建的 sshd 例外。即使启用了自动删除,用完后也应立即手动撤销。
默认会安排在 24 小时后自动撤销。对于仍可用完整身份核对的账号,撤销成功会删除个人 crontab、UID 匹配的 `at`/`batch` 任务、账号、家目录、SSH key、sudo 授权和本工具创建的 sshd 例外;若账号已在程序外消失,只清理可安全识别的登记、按用户名授权和任务,不会猜测删除失去身份见证的 Home/mail。若安全检查或清理失败,程序会返回非零,保留账号(若仍存在)和登记,并尽力禁用仍存在的账号,供 systemd 重试或人工处理。即使启用了自动撤销,用完后也应立即手动撤销。

## 常用命令

Expand Down Expand Up @@ -121,7 +121,7 @@ ssh -i ./xxvcc-a1b2c3d4e5.key -p 22 xxvcc-a1b2c3d4e5@203.0.113.10
/usr/bin/sudo /usr/local/sbin/linux-temp-admin invite --sudo --fix-sshd
```

该操作不会修改 sshd 全局策略,并会在撤销账号时删除对应例外。自动化调用、密码登录、永久账号和完整故障处理见[管理员指南](docs/operator-guide.md)。
该操作不会修改 sshd 全局策略;成功撤销账号时会删除对应例外。自动化调用、密码登录、永久账号和完整故障处理见[管理员指南](docs/operator-guide.md)。

## 安全要点

Expand Down
2 changes: 1 addition & 1 deletion docs/installing.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ This guide is for administrators who install and maintain `linux-temp-admin`. Se
- root access plus curl, OpenSSL 3, sha256sum, and timeout for installation;
- `getent` or `nslookup` for GitHub CDN fallback, so every redirect target can be validated and pinned to a public address.

The binary has no dynamic-library or language-runtime dependency. Account lifecycle operations still use the system's `id`, `useradd`/`adduser`, `userdel`/`deluser`, `usermod`, and `chage`; granting sudo also requires `sudo`. Missing tools can be installed through apt, dnf, yum, or apk after interactive confirmation.
The binary has no dynamic-library or language-runtime dependency. Account lifecycle operations still use the system's `id`, `useradd`, `userdel`, `usermod`, and `chage`; password login additionally requires `chpasswd`, while granting sudo requires `sudo` and `visudo` for pre-commit policy validation. The tool does not fall back to a distro `adduser`/`deluser` or an arbitrary BusyBox account applet: command names alone cannot prove equivalent arguments, configuration, or compile-time shadow/group semantics. Missing tools can be installed through apt, dnf, yum, or apk after interactive confirmation.

Arch Linux has no safe partial-upgrade mode, while `pacman -Syu` upgrades the whole system. The tool therefore never runs pacman automatically while creating an account. Complete the prompted upgrade and dependency installation deliberately first.

Expand Down
2 changes: 1 addition & 1 deletion docs/installing.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
- 安装需要 root 权限、curl、OpenSSL 3、sha256sum 和 timeout;
- GitHub CDN 回退还需要 `getent` 或 `nslookup`,用于验证并固定每个重定向目标的公网地址。

二进制本身不依赖动态库或语言运行时。账号生命周期仍会使用系统的 `id`、`useradd`/`adduser`、`userdel`/`deluser`、`usermod` 和 `chage`;授予 sudo 时还需要 `sudo`。缺失依赖可在交互确认后通过 apt、dnf、yum 或 apk 安装。
二进制本身不依赖动态库或语言运行时。账号生命周期仍会使用系统的 `id`、`useradd`、`userdel`、`usermod` 和 `chage`;密码登录还需要 `chpasswd`,授予 sudo 时还需要 `sudo` 和用于写入前策略校验的 `visudo`。程序不回退到发行版 `adduser`/`deluser` 或任意 BusyBox 账号 applet:这些实现的参数、配置及编译期 shadow/group 语义不能仅凭命令名证明与 shadow 工具链等价。缺失依赖可在交互确认后通过 apt、dnf、yum 或 apk 安装。

Arch Linux 不允许安全的部分升级,而 `pacman -Syu` 会升级整个系统,因此本工具不会在创建账号时自动运行 pacman。请根据提示由管理员先完成完整升级和依赖安装。

Expand Down
Loading