设计文档

Pomelo 登入管理員(pomelo-dm + pomelo-greeter)

随 Pomelo 0.32 附上 这是开发者写的设计笔记,比较技术;一般使用请看使用手册。

设计文档目前只有繁体中文版。

最後更新:2026-10-19(0.26)

為什麼自己做

LightDM+slick-greeter GDM Pomelo
常駐程式 lightdm(GLib、D-Bus、AccountsService) gdm+gnome-shell(完整 GNOME) pomelo-dm(只靠 libc+libpam)
登入後還在跑的 lightdm + 每個工作階段一個 lightdm 子行程 gdm + 每個工作階段一個 gdm-session-worker pomelo-dm(約 2 MB)+ 每個工作階段一個子行程
登入畫面 GTK gnome-shell pomelo-greeter(Xlib+cairo,約 22 MB,登入後就結束)

測試環境量到的數字(1440×900):pomelo-dm 常駐 2.3 MB;登入畫面 22 MB、閒置時 CPU 0%。

架構

systemd ─ pomelo-dm(root)
            ├─ Xorg :0 vt7 -auth /run/pomelo-dm/:0.auth -nolisten tcp
            ├─ pomelo-greeter(系統帳號 pomelo-greeter,fd 3 = 跟 pomelo-dm 之間的 socket)
            └─ 工作階段子行程(root):PAM 驗證 → setcred → open_session(pam_systemd 登記 logind)
                  └─ 使用者的工作階段(已切換身分):sh 讀 /etc/profile、~/.profile 後 exec pomelo-session

一輪的流程:

  1. 產生新的 MIT-MAGIC-COOKIE(16 個亂數位元組),寫成 X 的授權檔,啟動 X,等 X 送 SIGUSR1 表示準備好 (只認這個 X 自己送的訊號;上一輪留下的會先清掉)。
  2. 以 pomelo-greeter 帳號啟動登入畫面;它只拿得到 X 的 cookie,碰不到密碼檔。
  3. 登入畫面送 LOGIN 使用者 密碼 工作階段。pomelo-dm 先確認帳號可以登入(不是 root、shell 不是 nologin), 再開一個子行程做 PAM 驗證(失敗至少等 1.5 秒)。成功就回 OK,登入畫面淡出結束; pomelo-dm 把登入畫面的行程群組和 pomelo-greeter 帳號的所有行程都結束掉, 再關掉這個 X、用新的 cookie 開一個全新的 X 給使用者(登入畫面用過的連線、留下的視窗、它知道的 cookie 全部作廢)。
  4. 子行程收到新 cookie 後 setcred、open_session(common-session 裡的 pam_systemd 向 logind 登記:XDG_SESSION_TYPE=x11、 XDG_SEAT=seat0、XDG_VTNR=7),把使用者的 cookie 寫到 $XDG_RUNTIME_DIR/pomelo-xauth,切換成使用者, 執行 /usr/share/xsessions 裡選的工作階段(預設 pomelo)。
  5. 工作階段結束(登出)→ close_session → pomelo-dm 關掉 X,回到第 1 步。 每個使用者登出後都換一個全新的 X 和 cookie,上一位的程式不可能殘留在下一個登入畫面上。

登入有多快(0.19)

0.18 以前登入成功後會把 X 整個關掉再重開(為了讓登入畫面留下的東西全部作廢),實體機器上 Xorg 關閉+啟動要好幾秒, 畫面會黑一下。0.19 改成:先把新 cookie 寫進 X 的授權檔、結束登入畫面的所有行程,X 沒有任何連線就會自己「重設」 (清掉所有視窗與資源、重新讀授權檔,舊 cookie 立刻失效),一樣乾淨但只要幾百毫秒;萬一 2 秒內沒有重設才退回重開 X。 另外修正登入畫面淡出後還多等一秒才結束、把複製桌布移到工作階段開始之後。測試環境量到:按 Enter 到工作階段開始 從約 1.5 秒(不含 Xorg 重開)降到約 0.87 秒,其中 0.35 秒是淡出動畫。開 POMELO_DM_DEBUG=1 時日誌會印各階段的毫秒數。

0.20:工作階段開始時 X 不再重設

0.19 在實機(Intel RKL、modesetting)上量到登入後還要約 9 秒桌面才出現。看 /var/log/Xorg.0.log,登入後 X 又初始化了 8 次 (每次約 0.5 秒,畫面清空):X 的預設是「最後一個連線離開就重設」,pomelo-session 在視窗管理員連上之前會先跑 xrdb、xinput、setxkbmap、xrandr 等短命的小程式,每個結束時 X 就重設一次。GDM 啟動 X 時加了 -noreset 所以沒有這問題。 0.20 在登入畫面結束、X 重設完之後,由 pomelo-dm 對 X 保持一條什麼都不做的連線直到登出,之後就不會再重設 (登入畫面期間不保持,因為要靠那次重設清掉登入畫面)。測試環境:按 Enter 到選單列出現從 3.2 秒降到 1.0 秒。 POMELO_DM_DEBUG=1 時若工作階段中 X 又重設,日誌會記「X 在工作階段中重設了」。

剩下的時間:開機後第一次登入時,使用者的 systemd(user@.service)要先啟動完成,pam_systemd 會等它 (這台實測約 3 秒,大多是剛開機時系統還忙);這跟用哪個登入管理員無關,之後再登入就不用等。

登入畫面

  • 上方:日期(10月19日 週一)與大時鐘;右上角電量。
  • 下方:選到的使用者在正中間(大頭像、名字、圓角密碼框),其他使用者小頭像排在左右;點頭像或 ← → 切換。
  • 密碼框:打字顯示圓點,右邊出現 → 按鈕;驗證時轉圈;錯誤時左右晃動、框變紅、顯示原因;⇪ 大寫鎖定提示。
  • 最下面:睡眠、重新開機、關機。左下角:工作階段(裝了兩個以上才出現)。
  • 背景:該使用者上次用的桌布(登入時由 pomelo-dm 以「使用者身分」讀取、只收 PNG/JPEG,存到 /var/lib/pomelo-dm/users/<名字>.wallpaper);沒設桌布就是跟桌面一樣的內建漸層。頭像用 ~/.face 或 AccountsService 的圖示,都沒有就畫一個灰色的人像。
  • 語言跟系統(/etc/default/locale),13 種翻譯都有。

設定:/etc/pomelo/dm.conf

鍵 說明
autologin-user 開機自動登入的帳號(用 /etc/pam.d/pomelo-dm-autologin,不問密碼;只在開機第一次)
autologin-session 自動登入的工作階段
default-session 預設工作階段(pomelo)
hide-users 不出現在登入畫面的帳號(逗號分開)
show-other-user 顯示「其他使用者」(手動輸入帳號)
background 背景:user(使用者自己的桌布,預設)、image(/var/lib/pomelo-dm/background)、gradient(內建漸層)
clock 時鐘:24h、12h、off
text-size 文字大小:normal、large(放大 18%)
user-list true 顯示使用者清單;false 只有「名稱與密碼」兩個欄位
power-buttons 顯示睡眠、重新開機、關機按鈕
message 顯示在登入畫面下方的一段話(300 位元組以內),例如失物招領的電話
vt X 跑在第幾個 VT(7)
xserver 自訂 X 指令

用「系統設定 → 登入畫面」調整(0.21)

不用自己編輯 dm.conf:系統設定裡的「登入畫面」頁可以選背景(使用者自己的桌布/指定圖片/漸層; 指定圖片時列出目前的桌布、/usr/share/backgrounds 與「圖片」資料夾的縮圖,也可以輸入路徑)、時鐘、文字大小、 登入方式(使用者清單或名稱與密碼)、「其他使用者」、不顯示的帳號、自動登入、登入畫面訊息、電源按鈕。

  • 預覽:用目前(還沒套用)的設定開 pomelo-greeter --preview,按 Esc 離開,不用登出就能看到樣子。
  • 套用:透過 pkexec 執行 pomelo-dm --set key=value … [--set-background 圖片],要輸入管理員密碼。 pomelo-dm 會檢查每個鍵與值(帳號要存在、工作階段要裝了、不能有控制字元…),保留 dm.conf 裡的註解與其他行, 寫到暫存檔再改名(不會寫一半)。背景圖一樣在「已切換成呼叫者身分」的子行程裡讀(不能藉此讀到別人的檔案), 只收 PNG/JPEG、16384 像素、25 MB 以內,存成 /var/lib/pomelo-dm/background。
  • 下次出現登入畫面(登出或重新開機)就會套用,不用重新啟動 pomelo-dm。

安全設計

0.17 發佈前做過一次獨立的安全審查,找到的問題都已修正(下面每一項都是)。

  • 登入畫面以沒有權限的系統帳號執行;確認 setuid(0) 失敗才繼續。
  • root 不能從登入畫面登入(PAM 的 pam_succeed_if user != root,程式裡也擋)。
  • PAM 對話只回答第一個密碼問題;還有其他問題(例如一次性密碼)就拒絕,不會拿密碼亂答。
  • 不存在的帳號也會等 2 秒才回答,不能用回應時間猜帳號。
  • 使用者的桌布、頭像一定在「已切換成使用者」的子行程裡讀(使用者可以把桌布路徑設成 /etc/shadow), 只讀一般檔案(FIFO、裝置檔不讀),5 秒讀不完就強制結束;root 這邊檢查是 PNG/JPEG、圖片寬高 (頭像 4096、桌布 16384 以內)與檔案大小(2 MB/25 MB),寫檔用 O_EXCL|O_NOFOLLOW, 存成 root:pomelo-greeter 0640(只有登入畫面讀得到;家目錄可能是別人不能讀的)。
  • libpomelo 的圖片解碼也有尺寸上限並檢查記憶體配置,尺寸異常的圖不會讓程式當掉; 萬一登入畫面還是連續當掉,pomelo-dm 會改成不載入任何使用者圖片的模式,避免所有人都登不進來。
  • pomelo-dm 自己也檢查帳號能不能登入(登入畫面沒有權限、不能信任),不存在與不能登入的帳號回應時間相同。
  • 睡眠、重新開機、關機:還有其他人登入(例如 ssh)時拒絕;用 systemctl --check-inhibitors=yes, 系統更新等程式要求「暫時不要關機」時也會拒絕。
  • 工作階段的輸出寫到 ~/.xsession-errors;開不了(例如被換成捷徑)就丟掉,不會寫進系統日誌。
  • 結束 pomelo-dm(例如關機)時,工作階段會收到 SIGTERM,6 秒內不結束就強制結束,一定會關閉 PAM session。
  • X 只開 UNIX socket(-nolisten tcp),沒有 cookie 的程式連不上。
  • 每次登入前與登出後都關掉 X、換新的 cookie。
  • systemd 單元用 ExecCondition 判斷是不是目前的登入管理員;不擋開機畫面(plymouth)結束, 文字登入(Ctrl+Alt+F3)這條退路一直可以用。

安裝與切換(重要:先試用)

sudo apt install ./pomelo-greeter_*.deb ./pomelo-dm_*.deb
sudo pomelo-dm-switch try     # 在 Ctrl+Alt+F7 開一個 Pomelo 登入畫面試用,不改任何設定
sudo pomelo-dm-switch on      # 確定沒問題再改成每次開機都用
sudo pomelo-dm-switch off     # 換回原本的(GDM/LightDM),記得是哪一個

萬一登不進去:按 Ctrl+Alt+F3 用文字登入,執行 sudo pomelo-dm-switch off,再 sudo reboot。 移除 pomelo-dm 時如果它正是目前的登入管理員,會自動先換回原本的。

Fedora、Rocky(0.26)

  • PAM 用 Red Hat 系的寫法(pomelo-dm.pam.redhat:password-auth、postlogin、pam_selinux close/open、pam_namespace, 參考 Fedora 上 LightDM 與 GDM 的設定)。這些系統沒有 pam-auth-update,鑰匙圈的自動解鎖直接寫在 pomelo-dm 的 PAM 設定裡 (-auth/-session optional pam_pomelo_keyring.so,沒裝鑰匙圈時略過)。
  • SELinux:安裝時用 semanage fcontext 把 /usr/sbin/pomelo-dm 標成 xdm_exec_t、/var/lib/pomelo-dm 標成 xdm_var_lib_t、 /run/pomelo-dm 標成 xdm_var_run_t,讓 pomelo-dm 跟 GDM、LightDM 一樣跑在 xdm_t,登入後 pam_selinux 才能把工作階段切到 使用者的安全標籤。沒有 semanage 時會提醒安裝 policycoreutils-python-utils。移除時刪掉這些規則。
  • 系統帳號用 useradd -r 建立;系統語言讀 /etc/locale.conf。
  • pomelo-dm-switch:這些系統沒有 /etc/X11/default-display-manager,改從 display-manager.service 連到的服務檔讀 ExecStart 判斷目前的登入管理員;換回時認得 /usr/sbin/gdm。用模擬的目錄結構(POMELO_DM_SWITCH_ROOT)測過 status/on/off。
  • 還沒在實體的 Fedora、Rocky 上測過(特別是 SELinux enforcing 時的登入流程),使用手冊裡請使用者先用 try。

測試(不需要 root)

pomelo-dm --test --display :9 --xserver "Xvfb -screen 0 1440x900x24" \
          --pam-dir 測試用PAM目錄 --state 目錄 --sessions 工作階段目錄
pomelo-greeter --preview        # 只看登入畫面,密碼 demo 會「成功」

測試模式以目前的使用者執行(不切換身分、不登記 logind、電源按鈕只印訊息)。已在這個模式驗證: 錯誤密碼(約 1.5 秒後框變紅並顯示原因)、正確密碼登入、登入前換新 X 與 cookie 後工作階段仍能連上、 工作階段拿到的環境變數、登出後回到登入畫面、自動登入、登入畫面當掉後自動重開與連續當掉時的安全模式、 工作階段中結束 pomelo-dm 會把工作階段、X、登入畫面全部收掉、尺寸異常的 JPEG/PNG(65000×65000) 不會讓登入畫面當掉、電源按鈕的提示、「其他使用者」、工作階段選單、多語言、1280×720~1920×1080 的排版。 「還有其他人登入」的判斷用這台主機真的 loginctl 測過(有 ssh 時拒絕、closing 的不算)。

還沒驗證、只能在實體機器上測的:以 root 執行時的身分切換、pam_systemd/logind 登記 (音量、外接碟等權限)、真正的 Xorg 與 VT 7、pomelo-keyring 用登入密碼自動解鎖、關機/重新開機。

還沒做

  • 在登入狀態下「切換使用者」(同時兩個人登入)。
  • 螢幕鎖定(pomelo-lock)時從登入畫面切到別的使用者。
  • 遠端桌面(xrdp 有自己的登入流程,不受影響)。
  • Wayland。