Skip to content

Commit 6a2e24a

Browse files
Harden first-boot SSH by removing the default password and adding optional authorized_keys import
1 parent 30fd688 commit 6a2e24a

3 files changed

Lines changed: 85 additions & 12 deletions

File tree

docs/development.md

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -206,13 +206,31 @@ sudo dd if=image.img of=/dev/sdX bs=4M status=progress conv=fsync
206206
After flashing and booting the Raspberry Pi 4:
207207

208208
1. The Pi will automatically connect via DHCP
209-
2. Navigate to `http://<pi-ip-address>/` to access the setup wizard
210-
3. Complete the wizard to:
209+
2. Optional: for headless SSH before initial configuration, place an `authorized_keys` file on the firmware partition before first boot
210+
3. Navigate to `https://<pi-ip-address>/` to access the setup wizard
211+
4. Complete the wizard to:
211212
- Generate/import your Spark wallet seed
212213
- Set SSH password for `lnbitsadmin` user
213214
- Configure and start LNbits
214215

215-
After setup is complete, access LNbits at: `http://<pi-ip-address>/`
216+
After setup is complete, access LNbits at: `https://<pi-ip-address>/`
217+
218+
### Optional first-boot SSH access
219+
220+
The image no longer ships with a default SSH password.
221+
222+
If you want SSH access before completing the web configurator:
223+
224+
1. Mount the firmware partition after flashing the SD card.
225+
2. Rename `authorized_keys.example` to `authorized_keys`.
226+
3. Paste one or more SSH public keys into that file, one per line.
227+
4. Boot the device and connect with:
228+
229+
```bash
230+
ssh lnbitsadmin@<pi-ip-address>
231+
```
232+
233+
The imported key is installed for the `lnbitsadmin` account before `sshd` starts. Password login for `lnbitsadmin` only becomes usable after you set a password in the configurator.
216234

217235
### Build Issues (For developers)
218236

@@ -354,4 +372,4 @@ source venv/bin/activate
354372
pip install mnemonic
355373
export DEV_MODE=true
356374
python3 app.py
357-
```
375+
```

nixos/configuration.nix

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
services.openssh.enable = true;
5050
services.openssh.settings = {
5151
PermitRootLogin = "no";
52-
PasswordAuthentication = true; # simple first boot; change to keys-only if you want
52+
PasswordAuthentication = true; # disabled in practice until configurator sets a real user password
5353
};
5454

5555
# Enable console on HDMI (keeps display active and shows login prompt)
@@ -80,13 +80,10 @@
8080
isNormalUser = true;
8181
extraGroups = [ "wheel" ]; # wheel = sudo access
8282

83-
# Option 1: Set initial password (plaintext - stored in /nix/store)
84-
# User can change it after first login with 'passwd'
85-
initialPassword = "lnbits";
86-
87-
# Option 2: Use a hashed password (more secure - see example below)
88-
# hashedPassword = "$y$j9T$...your-hashed-password-here...";
89-
# Generate with: mkpasswd -m yescrypt
83+
# Ship without a usable password. The configurator assigns the first
84+
# real SSH password, and an optional authorized_keys file on the
85+
# firmware partition can enable headless key-based access before setup.
86+
hashedPassword = "$y$j9T$rJ6NmGZ7zE0U4N2hW0g2P.$e6TQ5QliNd3I.M0A2Y2vG7tUc1IQQ6pwT0nMY5myoN5";
9087
};
9188

9289
security.sudo.wheelNeedsPassword = true;
@@ -98,6 +95,46 @@
9895
(pkgs.callPackage ./reset-configurator.nix { })
9996
];
10097

98+
systemd.services.lnbitsbox-firstboot-authorized-keys = {
99+
description = "Import optional first-boot SSH authorized_keys";
100+
wantedBy = [ "multi-user.target" ];
101+
before = [ "sshd.service" ];
102+
after = [ "local-fs.target" "users.target" ];
103+
unitConfig = {
104+
ConditionPathExists = "!/var/lib/lnbits/.configured";
105+
};
106+
serviceConfig = {
107+
Type = "oneshot";
108+
RemainAfterExit = true;
109+
};
110+
path = with pkgs; [ coreutils gnused shadow ];
111+
script = ''
112+
KEY_SOURCE="/boot/firmware/authorized_keys"
113+
TARGET_HOME="/home/lnbitsadmin"
114+
TARGET_DIR="$TARGET_HOME/.ssh"
115+
TARGET_FILE="$TARGET_DIR/authorized_keys"
116+
117+
if [ ! -s "$KEY_SOURCE" ]; then
118+
echo "No first-boot authorized_keys file found."
119+
exit 0
120+
fi
121+
122+
if [ -e "$TARGET_FILE" ]; then
123+
echo "authorized_keys already exists for lnbitsadmin, skipping import."
124+
exit 0
125+
fi
126+
127+
USER_GROUP="$(id -gn lnbitsadmin)"
128+
129+
install -d -m 0700 -o lnbitsadmin -g "$USER_GROUP" "$TARGET_DIR"
130+
sed '/^[[:space:]]*$/d; s/\r$//' "$KEY_SOURCE" > "$TARGET_FILE"
131+
chown lnbitsadmin:"$USER_GROUP" "$TARGET_FILE"
132+
chmod 0600 "$TARGET_FILE"
133+
134+
echo "Imported SSH authorized_keys for lnbitsadmin from firmware partition."
135+
'';
136+
};
137+
101138
# Display first-boot instructions on login
102139
environment.etc."motd".text = ''
103140
╔═══════════════════════════════════════════════════════════╗
@@ -112,6 +149,11 @@
112149
• Setting your SSH password
113150
• Launching LNbits
114151
152+
Optional headless SSH before setup:
153+
• Put your public keys in /boot/firmware/authorized_keys
154+
• Sign in as lnbitsadmin using key-based auth
155+
• There is no default SSH password on the image
156+
115157
Already configured? LNbits is available at the same URL.
116158
117159
To find this device's IP address, run: ip addr show

nixos/wifi-config.nix

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,19 @@ PASSWORD=YourWiFiPassword
165165
166166
# Optional: Set to true if your network is hidden
167167
# HIDDEN=true
168+
EXAMPLE
169+
170+
cat > firmware/authorized_keys.example << 'EXAMPLE'
171+
# LNbitsBox first-boot SSH keys
172+
#
173+
# To enable headless SSH access before completing the web configurator:
174+
# 1. Rename this file to authorized_keys
175+
# 2. Paste one or more SSH public keys below, one per line
176+
# 3. Boot the device and sign in as lnbitsadmin using your SSH key
177+
#
178+
# There is no default SSH password on the image.
179+
180+
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMReplaceThisWithYourPublicKey user@example
168181
EXAMPLE
169182
'';
170183
}

0 commit comments

Comments
 (0)