The operator no longer answers an "offline?" question and no longer needs network access: the installer finds the package set that ships with the checkout, so the only input left is the dealer number. Installer - Auto-discover the local APT repository (BF_OFFLINE_REPO, then provisioning/offline-repo/built, then /opt/blueforce/offline-repo) and enter offline mode on its own; --offline stays for compatibility. - Fail closed with an explicit Turkish message when nothing is found: the searched locations, what to do, and a named exit reason -- no state touched. - Distinguish "repository absent" (operator error, actionable) from "repository present but invalid" (OFFLINE BLOCKED, verification gate). - Network is informational only in precheck/network modules; no probe runs in offline mode and archive.ubuntu.com is no longer mandatory. ISO builder - Look for built/ first, which is what build-index.sh produces, and accept build/ as a warned legacy path. The two disagreed, so a medium could be built without the package set and fail only at install time. Docs - docs/27, scripts/install/README, provisioning/offline-repo/README and provisioning/firstboot/README describe the discovered flow, the two failure modes, and that the installer downloads nothing. Tests - check-admin-static exercised the "console missing from the clone" branch by deleting a tracked file; git clone restored it from history, so the branch never ran and the warning was effectively untested. Build the stripped source as a real commit instead; verified by mutation.
178 lines
11 KiB
Markdown
178 lines
11 KiB
Markdown
# First boot — terminal wizard, kimlik koruması ve yarıda kalan kurulum
|
||
|
||
Bu dizin, cihazın ilk açılışında sahada çalışan giriş noktasını barındırır:
|
||
|
||
- `bf-firstboot` — bayi numarasını toplayan/doğrulayan ve yerel offline kurucuyu bir kez çalıştıran script. Kurucu paket deposunu kendisi bulur; internet gerekmez, teknisyen hiçbir şey indirmez.
|
||
- `blueforce-firstboot.service` — boot'ta script'i bir kez çalıştıran systemd unit'i.
|
||
- Bu doküman — gerçek davranış, otomasyon yolu, kimlik değişimi prosedürü ve secret kuralları.
|
||
|
||
İlk boot, yalnız Subiquity'nin assisted storage onayıyla tamamlanan kurulumdan ve yeniden başlatmadan sonra çalışır.
|
||
Kimlik formatı `^[0-9]{8}$` (`docs/02-DEVICE-NAMING-AND-INVENTORY.md`) ve merkezi durumlar
|
||
(`PROVISIONED_OFFLINE` → `ENROLLED` → `READY`, `docs/29-DEVICE-ENROLLMENT.md`) bu adımın dışındadır:
|
||
firstboot yalnız offline provision'ı yapar, enrollment ve READY ayrı adımlardır.
|
||
|
||
## 1. Wizard (teknisyen ekranı)
|
||
|
||
Konsolda (TTY mevcut ve otomasyon istenmemiş) teknisyen şu ekranı görür:
|
||
|
||
```text
|
||
# BLUEFORCE FIELD OS
|
||
8 Haneli Bayi Numarası:
|
||
> _
|
||
```
|
||
|
||
Davranış:
|
||
|
||
- Girdi `^[0-9]{8}$` ile doğrulanır. **Hatalı girdi kabul edilmez**; hata satırı ve `(deneme N/3)` ile yeniden sorulur.
|
||
Yalnız baştaki/sondaki boşluk kırpılır (yapıştırma artığı); değerin içinde boşluk veya rakam dışı karakter varsa geçersiz sayılır.
|
||
- 3 hatalı denemeden sonra script net bir hatayla `exit 2` verir ve hiçbir değişiklik yapmaz
|
||
(`BF_FIRSTBOOT_MAX_ATTEMPTS` ile deneme sayısı değiştirilebilir).
|
||
- Doğru girdide onay ekranı gösterilir; onay `e`/`evet`/`y`/`yes` ile alınır:
|
||
|
||
```text
|
||
Device ID (cihaz kimliği): BF-<dealer-id>
|
||
Hostname (makine adı) : bf-<dealer-id>
|
||
Kurulum bu kimlikle başlatılsın mı? [e/H]:
|
||
```
|
||
|
||
- Onay verilmezse kurulum başlamaz; deneme hakkı tükenirse `exit 2`.
|
||
- `read` zaman aşımına uğrarsa (varsayılan 300 sn, `BF_FIRSTBOOT_INPUT_TIMEOUT`) script
|
||
"no terminal input arrived" hatasıyla `exit 2` verir; konsolda kimse olmadığında sonsuza kadar bloklamaz.
|
||
|
||
TTY seçimi: stdin zaten bir terminal ise (systemd `StandardInput=tty-force` durumu) fd 0 kullanılır;
|
||
değilse `/dev/tty` açılır; o da açılamıyorsa wizard atlanır ve argüman/dosya yoluna düşülür.
|
||
|
||
## 2. Kimlik çözümleme sırası
|
||
|
||
| # | Kaynak | Mod |
|
||
|---|---|---|
|
||
| 1 | `--dealer-id <8hane>` | non-interactive (otomasyon) |
|
||
| 2 | `--dealer-id-file <yol>` | non-interactive (otomasyon) |
|
||
| 3 | `--interactive` | wizard (TTY zorunlu) |
|
||
| 4 | `BF_FIRSTBOOT_NONINTERACTIVE=1` | non-interactive; seed dosyası (`/etc/blueforce/dealer-id`) okunur |
|
||
| 5 | Seed dosyası `/etc/blueforce/dealer-id` geçerliyse | non-interactive; wizard atlanır (log satırı yazılır) |
|
||
| 6 | TTY mevcut | wizard |
|
||
| 7 | Hiçbiri | net hata + `exit 2` (argüman/dosya önerilir) |
|
||
|
||
Otomasyon yolu (1, 2, 4) hiçbir soru sormaz: seed/USB ile kurulan cihazda teknisyen girdisi gerekmez.
|
||
1. ve 2. maddeler açıkça verildiği için onay ekranı çıkmaz; bu bilinçli bir otomasyon kapısıdır.
|
||
|
||
### Komut arayüzü
|
||
|
||
```bash
|
||
sudo bf-firstboot # wizard (konsol)
|
||
sudo bf-firstboot --dealer-id <8hane> # otomasyon
|
||
sudo bf-firstboot --dealer-id-file /path/to/dealer-id
|
||
sudo bf-firstboot --check --dealer-id <8hane> # salt-okunur denetim
|
||
sudo bf-firstboot --interactive # seed dosyası olsa bile wizard
|
||
BF_FIRSTBOOT_NONINTERACTIVE=1 sudo bf-firstboot # boot/otomasyon
|
||
```
|
||
|
||
Ortam değişkenleri: `BF_FIRSTBOOT_MARKER` (marker yolu), `STATE_DIR` (varsayılan `/var/lib/blueforce`),
|
||
`BF_INSTALLER_PATH` (kurucu yolu), `BF_ADMIN_PUBLIC_KEY_FILE`, `BF_FIRSTBOOT_MAX_ATTEMPTS`,
|
||
`BF_FIRSTBOOT_INPUT_TIMEOUT`, `BF_FIRSTBOOT_TEST_MODE`, `BF_FIRSTBOOT_NONINTERACTIVE`.
|
||
|
||
### Çıkış kodları
|
||
|
||
| Kod | Anlam |
|
||
|---|---|
|
||
| 0 | Kurulum tamam (marker yazıldı) veya zaten tamamlanmış (idempotent) veya `--check` raporu |
|
||
| 1 | Offline önkoşul eksik (admin public key), root yok, kurucu yok, veya kurucu hata verdi |
|
||
| 2 | Girdi hatası: geçersiz bayi numarası, terminal yok, onay verilmedi, deneme tükendi |
|
||
|
||
## 3. İdempotentlik ve yarıda kalan kurulum
|
||
|
||
- Marker: `/var/lib/blueforce/firstboot-complete` (mode `600`, içinde bayi numarası).
|
||
- Marker varsa script **hiçbir şey yapmaz**: sormaz, yazmaz, kurucuyu çağırmaz, `exit 0`. Wizard bu durumda asla açılmaz.
|
||
- Marker varken farklı bir bayi numarası verilirse identity değişmez; yalnız uyarı yazılır ve admin prosedürüne
|
||
(§4) işaret edilir. Sıradan kullanıcı yoluyla kimlik değiştirilemez.
|
||
- Marker yalnız kurucu başarıyla bittikten sonra yazılır (`set -euo pipefail` sayesinde hata hâlinde yazılmaz).
|
||
- Önceki koşu yarıda kaldıysa (`$STATE_DIR/install-state` dolu) kurucu `--resume` ile çağrılır:
|
||
kurucu `--offline --dealer-id <no> --yes` (+ gerekirse `--resume`) argümanlarıyla koşar.
|
||
- Script kurucuyu `--offline` ile çağırır. **Bu bayrak artık zorunlu değildir**: kurucu yerel
|
||
paket deposunu kendisi bulur (`BF_OFFLINE_REPO` → `<depo>/provisioning/offline-repo/built` →
|
||
`/opt/blueforce/offline-repo`). firstboot bayrağı geriye uyumluluk için verir; sonuç aynıdır:
|
||
yerel depodan, internet olmadan kurulum ve `PROVISIONED_OFFLINE` durumu.
|
||
- `--check` hiçbir dosya/dizin/log üretmez ve kurucuyu çağırmaz.
|
||
|
||
### Depo bulunamazsa / doğrulanamazsa
|
||
|
||
firstboot kurucuyu çağırır; paket deposuna ilişkin karar ve mesaj **kurucunun** sorumluluğundadır.
|
||
İki farklı durum vardır ve ikisinde de firstboot marker **yazmaz** (cihaz provision edilmiş sayılmaz):
|
||
|
||
| Durum | Kim söyler | Ekranda ne görünür (kısaltılmış) | Sonuç |
|
||
|---|---|---|---|
|
||
| Yerel depo hiç yok (üç arama yerinde de yok) | Kurucu | `HATA: Yerel paket deposu yok. Repo sorumlusuna başvurun: provisioning/offline-repo/README.md` … `ÇIKIŞ NEDENİ: yerel paket deposu bulunamadı.` | `exit 1`; hiçbir şey değişmez |
|
||
| Depo bulundu ama doğrulanamadı (checksum, kilit, eksik pin) | Kurucu | `OFFLINE BLOCKED: <sebep>` … `ÇIKIŞ NEDENİ: yerel paket deposu doğrulanamadı…` | `exit 1`; hiçbir şey değişmez |
|
||
| Admin public key eksik/geçersiz | firstboot'un kendi kapısı | `OFFLINE BLOCKED: seed-provided BF_ADMIN_PUBLIC_KEY_FILE is required.` (veya anahtar kuralı satırı) | `exit 1`; kurucu hiç çağrılmaz |
|
||
|
||
Çözüm sahadaki teknisyenin işi değildir: mesajı repo sorumlusuna iletmek ve depo düzeltildikten
|
||
sonra kurulumu tekrar çalıştırmak yeterlidir. Kurulumu yapan kişi hiçbir şey indirmez; paketler
|
||
git deposunda taşınır.
|
||
|
||
## 4. Kimlik değişimi prosedürü (admin / recovery — varsayılan KAPALI)
|
||
|
||
Bayi numarası değişimi **sıradan kullanıcı yolu değildir**. Wizard ve normal koşu bu yolu içermez.
|
||
Yalnız root + açık operatör kapısı + yazılı onay + audit kaydı ile çalışır:
|
||
|
||
1. Fiziksel konsolda root oturumu açılır ve operatör kapısı açıkça verilir: `BF_ALLOW_REIDENTITY=1`.
|
||
2. Komut: `BF_ALLOW_REIDENTITY=1 sudo -E bf-firstboot --force-reidentity --dealer-id <yeni-no>`.
|
||
3. Ekranda eski/yeni kimlik ve "dokuz merkezi sistem güncellenmeli" uyarısı gösterilir.
|
||
4. Onay, **yeni bayi numarasının yeniden yazılması** ile alınır; eşleşmezse hiçbir değişiklik yapılmaz (`exit 2`).
|
||
5. Kurucu yeni kimlikle koşar, eski marker `firstboot-complete.reidentified-<UTC>` olarak saklanır,
|
||
yeni marker yazılır.
|
||
6. Her adım `/var/log/blueforce-reidentity.log` (mode `600`, UTC zaman + `uid=`) ve journal'a yazılır:
|
||
kim, ne zaman, eski → yeni. Audit log yazılamıyorsa değişim **reddedilir** (fail-closed).
|
||
|
||
Kapılar: root değilse `exit 1`; `BF_ALLOW_REIDENTITY=1` yoksa `exit 1`; TTY yoksa `exit 2`.
|
||
Değişimden sonra `docs/02-DEVICE-NAMING-AND-INVENTORY.md` §9'daki yeniden-numaralandırma adımları
|
||
(WireGuard peer, RustDesk/MeshCentral etiketi, Ansible inventory, monitoring etiketi) uygulanır.
|
||
|
||
## 5. Secret kuralları
|
||
|
||
- `BF_ADMIN_PUBLIC_KEY_FILE` **yalnız public SSH key** içerebilir; root sahipliğinde ve grup/dünya
|
||
tarafından yazılamaz olmalıdır (`root:600` veya `root:640`). Private key, inline anahtar veya
|
||
boş/okunamayan dosya `OFFLINE BLOCKED` ile reddedilir.
|
||
- Bu kural değiştirilmedi: dosya `ssh-ed25519`/`ssh-rsa`/`ssh-ecdsa`/`sk-ssh-ed25519` public key satırı
|
||
ile başlamalı ve `PRIVATE KEY` içermemelidir.
|
||
- Script secret **loglamaz**: mesajlarda yalnız dosya yolları ve cihaz kimlikleri geçer; anahtar içeriği,
|
||
token, parola hiçbir satıra yazılmaz (`--force-reidentity` audit kaydı da yalnız kimlikleri yazar).
|
||
- Wizard girdisi ekrana yankılanır (teknisyen yazar); bu ekran token/parola istemez, yalnız bayi numarası alır.
|
||
- Kurucu, yerel paket deposunu kendisi bulur (`BF_OFFLINE_REPO` → `<depo>/provisioning/offline-repo/built`
|
||
→ `/opt/blueforce/offline-repo`); `--offline` bayrağı gerekmez. Internet kurulum için gerekmez.
|
||
Public-key önkoşulu geçtikten sonra:
|
||
* depo hiç yoksa kurucu Türkçe `HATA: Yerel paket deposu yok…` + `ÇIKIŞ NEDENİ: yerel paket deposu bulunamadı.` ile durur;
|
||
* depo bulunup da doğrulanamazsa (geçerli yerel APT indeksleri, checksum/lock ya da tüm somut
|
||
pinli paketler eksik/bozuk) kurucu `OFFLINE BLOCKED: <sebep>` ile durur.
|
||
Her iki durumda cihaz provision edilmiş sayılmaz ve firstboot marker yazmaz. Yalnız yerel depo
|
||
+ public-key önkoşulları geçerse `PROVISIONED_OFFLINE` yazılır. Ayrıntı: §3 ve
|
||
`provisioning/offline-repo/README.md` §3.
|
||
|
||
## 6. systemd unit ve boot'u kilitlememe
|
||
|
||
`blueforce-firstboot.service`:
|
||
|
||
- `ConditionPathExists=/etc/blueforce/admin-authorized-key.pub` — zorunlu seed önkoşulu yoksa unit hiç başlamaz.
|
||
- `ConditionPathExists=!/var/lib/blueforce/firstboot-complete` — provision edilmiş cihazda atlanır (başarısız sayılmaz).
|
||
- `StandardInput=tty-force` + `TTYPath=/dev/tty1` — wizard sahada konsolda görünür.
|
||
- TTY kurulamazsa (konsolsuz/headless sistem) systemd unit'i başarısız sayar, **boot devam eder**;
|
||
script de TTY yoksa argüman/dosya yoluna düşer.
|
||
- `TimeoutStartSec=45min` — konsolda kimse yoksa job sonsuza kadar açık kalmaz.
|
||
- `Type=oneshot`, `RemainAfterExit=yes`, `WantedBy=multi-user.target` — boot'ta bir kez çalışır.
|
||
- `NoNewPrivileges=yes`, `PrivateTmp=yes` korunur. `ProtectSystem=strict` ve `ProtectHome=yes`
|
||
**bilinçli olarak yoktur**: offline kurucu `/etc`, `/usr` ve `/home/blueforce` altına yazar.
|
||
|
||
Not: unit çıktısı konsol TTY'sine gider; aynı satırlar `logger -t bf-firstboot` ile journal'a da yazılır,
|
||
kurucu kendi logunu `/var/log/blueforce-install.log` içinde tutar.
|
||
|
||
## 7. Test
|
||
|
||
```bash
|
||
bash tests/test-firstboot-static.sh
|
||
```
|
||
|
||
Statik test, gerçek systemd/apt işlemi yapmadan şunları doğrular: bayi numarası regex'i, hatalı girdinin
|
||
reddi, wizard ekranı ve onay akışı (pty ile), marker idempotentliği, kimlik değişiminin kapalı olması,
|
||
public-key önkoşulu ve secret sızıntısı olmadığı, `--check` ve `BF_FIRSTBOOT_TEST_MODE` modlarının
|
||
mutasyonsuzluğu. Root gerektiren senaryolar `unshare -r` ile ayrı bir kullanıcı ad alanında koşar;
|
||
bu izin yoksa ilgili testler `SKIP` olarak raporlanır.
|