├── .gitignore ├── LICENSE ├── README.md ├── default_wifi.nmconnection_example ├── firstboot.sh ├── fix-ssh-on-pi.bash ├── fix-ssh-on-pi.ini_example ├── index.html ├── index.md ├── ping-example-playbook.yaml ├── put-pi-in-ansible-host.bash └── userconf.txt_example /.gitignore: -------------------------------------------------------------------------------- 1 | fix-ssh-on-pi.ini 2 | wpa_supplicant.conf 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Ken Fallon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fix-ssh-on-pi 2 | 3 | ## Safely enabling ssh in the default Raspberry Pi OS (previously called Raspbian) Image 4 | 5 | This script will make some small but necessary changes to a default [Raspberry Pi OS (previously called Raspbian)](https://www.raspbian.org/) image. 6 | 7 | In episode [hpr2356 :: Safely enabling ssh in the default Raspbian Image](http://hackerpublicradio.org/eps.php?id=2356) I walked through the first steps of automating the update of this base image. It will: 8 | 9 | - Download the latest image zip file 10 | - Verify it is valid 11 | - Extract the image itself 12 | - Enable ssh for secure remote management 13 | - Change the default passwords for the root and pi user 14 | - Secure the ssh server on the Pi 15 | 16 | Since then I improved the script to: 17 | 18 | - Enable connections to your WiFi network (default_wifi.nmconnection) 19 | - Load it's configuration from a ini file keeping sensitive information separate from the main script. 20 | - Using [losetup](http://man7.org/linux/man-pages/man8/losetup.8.html) to greatly simplify the mounting of the image. 21 | - Creation of a [First Boot](https://github.com/nmcclain/raspberian-firstboot) script. 22 | - Moved creation of the passwords and accounts to the ini file 23 | - Added support to select which [Raspberry_Pi_OS Version](https://en.wikipedia.org/wiki/Raspberry_Pi_OS#Versions) to download 24 | - Added support set up default users using [userconf.txt](https://www.raspberrypi.com/documentation/computers/configuration.html#configuring-a-user) 25 | 26 | This script is part of a series "Manage your Raspberry Pi fleet with Ansible" which was covered on [opensource.com](https://opensource.com/article/20/9/raspberry-pi-ansible) and on [Hacker Public Radio](http://hackerpublicradio.org/eps.php?id=3173). 27 | -------------------------------------------------------------------------------- /default_wifi.nmconnection_example: -------------------------------------------------------------------------------- 1 | [connection] 2 | id=CHANGEME 3 | uuid=fa35c960-b21e-4df3-ae4c-393439a45c95 4 | type=wifi 5 | interface-name=wlan0 6 | 7 | [wifi] 8 | mode=infrastructure 9 | ssid=CHANGEME 10 | 11 | [wifi-security] 12 | auth-alg=open 13 | key-mgmt=wpa-psk 14 | psk=CHANGEME 15 | 16 | [ipv4] 17 | method=auto 18 | 19 | [ipv6] 20 | addr-gen-mode=default 21 | method=auto 22 | 23 | [proxy] 24 | -------------------------------------------------------------------------------- /firstboot.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | sleep 2m 3 | macadd=$( ip -brief add | awk '/UP/ {print $1}' | sort | head -1 ) 4 | if [ ! -z "${macadd}" ] 5 | then 6 | macadd=$( sed 's/://g' /sys/class/net/${macadd}/address ) 7 | sed "s/raspberrypi/${macadd}/g" -i /etc/hostname /etc/hosts 8 | fi 9 | /sbin/shutdown -r 5 "reboot in Five minutes" 10 | -------------------------------------------------------------------------------- /fix-ssh-on-pi.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # MIT License 3 | # Copyright (c) 2017 Ken Fallon http://kenfallon.com 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | # v1.1 - changes to reflect that the sha_sum is now SHA-256 24 | # v1.2 - Changes to split settings to different file, and use losetup 25 | # v1.3 - Removed requirement to use xmllint (Thanks MachineSaver) 26 | # Added support for wifi mac naming (Thanks danielo515) 27 | # Moved ethernet naming to firstboot.sh 28 | # v1.4 - Removed option to encrypt clear password. It's more secure to keep store 29 | # the hash anyway. Thanks tillhanke 30 | # v1.5 - Thanks David G 31 | # Introduced functions for common tasks 32 | # Moved creation of the passwords and accounts to the ini file 33 | # Added support to select which Raspberry_Pi_OS Version to download 34 | # Added support set up default users using userconf.txt 35 | # v1.5.1 Fix checking userconf.txt 36 | # 37 | # 38 | # 39 | 40 | # Credits to: 41 | # - Dave Morriss http://hackerpublicradio.org/correspondents.php?hostid=225 42 | # - In-Depth Series: Bash Scripting https://hackerpublicradio.org/series.php?id=42 43 | # - https://gpiozero.readthedocs.io/en/stable/pi_zero_otg.html#legacy-method-sd-card-required 44 | # - https://github.com/nmcclain/raspberian-firstboot 45 | # - https://gist.github.com/magnetikonline/22c1eb412daa350eeceee76c97519da8 46 | 47 | # Settings are made in the settings file (eg fix-ssh-on-pi.ini) 48 | 49 | ########### 50 | # Functions 51 | # 52 | echo_error() { 53 | echo -e "ERROR: $@" #1>&2 54 | } 55 | 56 | echo_debug() { 57 | if [ "${debug}" != "0" ] 58 | then 59 | echo -e "INFO: $@" #1>&2 60 | fi 61 | } 62 | 63 | function umount_sdcard () { 64 | sdcard_mount="$@" 65 | echo_debug "Unmounting ${sdcard_mount}" 66 | sync 67 | umount_response=$( umount --verbose "${sdcard_mount}" 2>&1 ) 68 | if [ $( ls -al "${sdcard_mount}" | wc -l ) -eq "3" ] 69 | then 70 | echo_debug "Sucessfully unmounted \"${sdcard_mount}\"" 71 | sync 72 | else 73 | echo_error "Could not unmount \"${sdcard_mount}\": ${umount_response}" 74 | exit 4 75 | fi 76 | } 77 | 78 | function check_tool() { 79 | command -v "${1}" >/dev/null 2>&1 80 | if [[ ${?} -ne 0 ]] 81 | then 82 | echo_error "Can't find the application \"${1}\" installed on your system." 83 | exit 11 84 | fi 85 | } 86 | 87 | ########### 88 | # Variables 89 | # 90 | download_site="https://downloads.raspberrypi.org" 91 | 92 | ########### 93 | # Checks 94 | # 95 | if [ $(id | grep 'uid=0(root)' | wc -l) -ne "1" ] 96 | then 97 | echo_error "You are not root and some programs (losetup) require it to work" 98 | exit 12 99 | fi 100 | 101 | for this_tool in 7z awk basename cat cd chmod chown command cp curl declare hash echo eval exit grep id ln losetup ls mkdir mount mv rm rmdir sed sha256sum sync touch tr umount wc 102 | do 103 | check_tool "${this_tool}" 104 | done 105 | 106 | settings_file="fix-ssh-on-pi.ini" 107 | 108 | if [ -e "${settings_file}" ] 109 | then 110 | settings_file="${settings_file}" 111 | elif [ -e "${HOME}/${settings_file}" ] 112 | then 113 | settings_file="${HOME}/${settings_file}" 114 | elif [ -e "${HOME}/.config/${settings_file}" ] 115 | then 116 | settings_file="${HOME}/.config/${settings_file}" 117 | elif [ -e "${0%.*}.ini" ] 118 | then 119 | settings_file="${0%.*}.ini" 120 | else 121 | echo_error "Can't find the Settings file \"${settings_file}\"" 122 | exit 1 123 | fi 124 | 125 | if [ "$( grep -ic CHANGEME "${settings_file}" 2>/dev/null )" -ne 0 ] 126 | then 127 | echo_error "The required changes have not been made to the \""${settings_file}"\" file." 128 | echo_error "The following variables need to be changed:" 129 | grep -i CHANGEME "${settings_file}" | awk -F '=' '{print $1}' #1>&2 130 | exit 13 131 | fi 132 | 133 | source "${settings_file}" 134 | echo_debug "Finished loading the settings file \"${settings_file}\"" 135 | 136 | # These variables need to be set in the settings file 137 | variables=( 138 | architecture 139 | debug 140 | first_boot 141 | generation 142 | os_version 143 | pi_password_hash 144 | public_key_file 145 | root_password_hash 146 | wifi_file 147 | working_dir 148 | ) 149 | 150 | for variable in "${variables[@]}" 151 | do 152 | if [[ -z ${!variable+x} ]] 153 | then # indirect expansion here 154 | echo_error "The variable \"${variable}\" is missing from your \""${settings_file}"\" file."; 155 | exit 2 156 | fi 157 | done 158 | 159 | if [ $# -gt 0 ] 160 | then 161 | declare -A hash 162 | for argument 163 | do 164 | if [[ $argument =~ ^[^=]+=.*$ ]] 165 | then 166 | key="${argument%=*}" 167 | value="${argument#*=}" 168 | eval "${key}=${value}" 169 | fi 170 | done 171 | fi 172 | 173 | echo_debug "Requesting \"${generation}\", \"${os_version}\", \"${architecture}\" " 174 | 175 | if [ ! -e "${userconf_txt_file}" ] 176 | then 177 | echo_error "Can't find the userconf.txt file \"${userconf_txt_file}\"" 178 | echo_error "This file should contain a single line of text, consisting of username:encryptedpassword." 179 | echo_error "Where the encrypted password is created using:" 180 | echo_error " echo 'mypassword' | openssl passwd -6 -stdin" 181 | exit 3 182 | fi 183 | 184 | if [ "$( grep -ic CHANGEME "${userconf_txt_file}" 2>/dev/null )" -ne 0 ] 185 | then 186 | echo_error "The required changes have not been made to the \""${userconf_txt_file}"\" file." 187 | echo_error "The following variables need to be changed:" 188 | grep -i CHANGEME "${userconf_txt_file}" | awk -F '=' '{print $1}' #1>&2 189 | exit 13 190 | fi 191 | 192 | 193 | if [ ! -e "${public_key_file}" ] 194 | then 195 | echo_error "Can't find the public key file \"${public_key_file}\"" 196 | echo_error "You can create one using:" 197 | echo_error " ssh-keygen -t ed25519 -f ./${public_key_file} -C \"Raspberry Pi keys\"" 198 | exit 3 199 | fi 200 | 201 | if [ ! -e "${wifi_file}" ] 202 | then 203 | echo_error "Can't find the default_wifi.nmconnection file \"${wifi_file}\"" 204 | echo_error "You can modify the one provided here:" 205 | echo_error " https://github.com/kenfallon/fix-ssh-on-pi/blob/master/default_wifi.nmconnection_example" 206 | exit 14 207 | fi 208 | 209 | if [ ! -e "${first_boot}" ] 210 | then 211 | echo_error "Can't find the first boot script file \"${first_boot}\"" 212 | echo_error "You can use or modify the one provided here:" 213 | echo_error " https://github.com/kenfallon/fix-ssh-on-pi/blob/master/firstboot.sh" 214 | exit 15 215 | fi 216 | 217 | if [ ! -d "${working_dir}" ] 218 | then 219 | echo_error "The working directory \"${working_dir}\" is not a directory." 220 | exit 16 221 | fi 222 | 223 | touch "${working_dir}/~test" >/dev/null 2>&1 224 | if [ "$?" != "0" ] 225 | then 226 | echo_error "Cannot write to the working directory \"${working_dir}\"." 227 | exit 17 228 | fi 229 | rm "${working_dir}/~test" 230 | 231 | ########### 232 | # Decide which disto to use 233 | # 234 | # generation can be either Legacy, or Current 235 | generation=$(echo ${generation} | tr '[:upper:]' '[:lower:]') 236 | if [ "${generation}" == "legacy" ] 237 | then 238 | generation_path="oldstable_" 239 | else 240 | generation_path="" 241 | fi 242 | 243 | # version can be either Lite, Medium, or Full 244 | os_version=$(echo ${os_version} | tr '[:upper:]' '[:lower:]') 245 | if [ "${os_version}" == "medium" ] 246 | then 247 | os_version_path="" 248 | else 249 | os_version_path="${os_version}_" 250 | fi 251 | 252 | # architecture can be either armhf, or arm64 253 | architecture=$(echo ${architecture} | tr '[:upper:]' '[:lower:]') 254 | 255 | shortcut_url="${download_site}/raspios_${generation_path}${os_version_path}${architecture}_latest" 256 | shortcut_url_response=$( curl --silent "${shortcut_url}" --head ) 257 | # find the redirect url and remove color formatting 258 | download_url=$( echo "${shortcut_url_response}" | grep location | sed -e 's/^.*https:/https:/g' -e 's/xz.*$/xz/g' ) 259 | 260 | ########### 261 | # Download the image and checksum files 262 | # 263 | if [[ -z ${download_url} ]] 264 | then 265 | echo_error "Could not find a download URL for \"${shortcut_url}\"."; 266 | exit 21 267 | else 268 | echo_debug "The latest image will be retrieved from ${download_url}" 269 | downloaded_image=$( basename ${download_url} ) 270 | downloaded_image_path="${working_dir}/${downloaded_image}" 271 | curl --silent --head "${download_url}" | sed -e "s/\r//g" > "${downloaded_image_path}.head" 272 | download_url_content_length=$( awk '/content-length: / {print $2}' "${downloaded_image_path}.head" ) 273 | if [ -f "${downloaded_image_path}" ] 274 | then 275 | if [ "${download_url_content_length}" -ne "$( ls -al "${downloaded_image_path}" | awk '{print $5}' )" ] 276 | then 277 | curl --continue-at - "${download_url}" --output "${downloaded_image_path}" 278 | else 279 | echo_debug "Skipping download of ${downloaded_image}" 280 | fi 281 | else 282 | curl --continue-at - "${download_url}" --output "${downloaded_image_path}" 283 | fi 284 | if [ ! -f "${downloaded_image_path}.sha256.ok" ] 285 | then 286 | echo_debug "Checking to see if the sha256 of the downloaded image match \"${downloaded_image}.sha256\"" 287 | curl --silent "${download_url}.sha256" --output "${downloaded_image_path}.sha256" 288 | if [ "$( grep -c "$( sha256sum "${downloaded_image_path}" | awk '{print $1}' )" "${downloaded_image_path}.sha256" )" -eq "1" ] 289 | then 290 | echo_debug "The sha256 match \"${downloaded_image}.sha256\"" 291 | mv "${downloaded_image_path}.sha256" "${downloaded_image_path}.sha256.ok" 292 | else 293 | echo_error "The sha256 did not match \"${downloaded_image}.sha256\"" 294 | exit 5 295 | fi 296 | else 297 | echo_debug "Skipping check of sha256 of the downloaded image match as \"${downloaded_image}.sha256.ok\" file found." 298 | fi 299 | fi 300 | 301 | ########### 302 | # Extract the image files 303 | # 304 | extracted_image_path=$( echo "${downloaded_image_path}" | sed 's/\.xz//g' ) 305 | extracted_image=$( basename ${extracted_image_path} ) 306 | echo_debug "Extracting the image as \"${extracted_image_path}\"" 307 | 7z x -y "${downloaded_image_path}" -o"${working_dir}" 308 | 309 | if [ ! -e "${extracted_image_path}" ] 310 | then 311 | echo_error "Can't find the image \"${extracted_image_path}\"" 312 | exit 6 313 | fi 314 | 315 | ########### 316 | # Mount and change boot partition P1 files 317 | # 318 | sdcard_mount_p1="${working_dir}/${extracted_image}_sdcard_mount_p1" 319 | if [ -d "${sdcard_mount_p1}" ] 320 | then 321 | rmdir "${sdcard_mount_p1}" 322 | fi 323 | 324 | if [ -d "${sdcard_mount_p1}" ] 325 | then 326 | echo_debug "Cannot remove the old mount point \"${sdcard_mount_p1}\"" 327 | exit 20 328 | fi 329 | mkdir -v "${sdcard_mount_p1}" 330 | 331 | echo_debug "Mounting the sdcard boot disk \"${sdcard_mount_p1}\"" 332 | 333 | loop_base=$( losetup --partscan --find --show "${extracted_image_path}" ) 334 | 335 | echo_debug "Mounting \"${loop_base}p1\" to \"${sdcard_mount_p1}\" " 336 | mount ${loop_base}p1 "${sdcard_mount_p1}" 337 | if [ ! -d "${sdcard_mount_p1}/overlays" ] 338 | then 339 | echo_error "Can't find the mounted card \"${sdcard_mount_p1}\"" 340 | exit 7 341 | fi 342 | 343 | cp "${userconf_txt_file}" "${sdcard_mount_p1}/userconf.txt" 344 | if [ -e "${sdcard_mount_p1}/userconf.txt" ] 345 | then 346 | echo_debug "The userconf.txt file \"${userconf_txt_file}\" has been copied" 347 | else 348 | echo_error "Can't find the userconf.txt file \"${sdcard_mount_p1}/userconf.txt\"" 349 | exit 22 350 | fi 351 | 352 | touch "${sdcard_mount_p1}/ssh" 353 | if [ -e "${sdcard_mount_p1}/ssh" ] 354 | then 355 | echo_debug "The ssh config file has been copied" 356 | else 357 | echo_error "Can't find the ssh file \"${sdcard_mount_p1}/ssh\"" 358 | exit 9 359 | fi 360 | 361 | if [ -e "${first_boot}" ] 362 | then 363 | cp "${first_boot}" "${sdcard_mount_p1}/firstboot.sh" 364 | if [ -e "${sdcard_mount_p1}/firstboot.sh" ] 365 | then 366 | echo_debug "The first boot script been copied" 367 | else 368 | echo_error "Can't find the first boot script file \"${sdcard_mount_p1}/firstboot.sh\"" 369 | exit 19 370 | fi 371 | fi 372 | 373 | umount_sdcard "${sdcard_mount_p1}" 374 | losetup --verbose --detach ${loop_base} 375 | rmdir -v "${sdcard_mount_p1}" 376 | 377 | ########### 378 | # Mount and change boot partition P2 files 379 | # 380 | sdcard_mount_p2="${working_dir}/${extracted_image}_sdcard_mount_p2" 381 | 382 | if [ -d "${sdcard_mount_p2}" ] 383 | then 384 | rmdir "${sdcard_mount_p2}" 385 | fi 386 | 387 | if [ -d "${sdcard_mount_p2}" ] 388 | then 389 | echo_debug "Cannot remove the old mount point \"${sdcard_mount_p2}\"" 390 | exit 18 391 | fi 392 | mkdir -v "${sdcard_mount_p2}" 393 | 394 | echo_debug "Mounting the sdcard root disk \"${sdcard_mount_p2}\"" 395 | 396 | loop_base=$( losetup --partscan --find --show "${extracted_image_path}" ) 397 | 398 | echo_debug "Mounting \"${loop_base}p2\" to \"${sdcard_mount_p2}\" " 399 | mount ${loop_base}p2 "${sdcard_mount_p2}" 400 | if [ ! -e "${sdcard_mount_p2}/etc/shadow" ] 401 | then 402 | echo_error "Can't find the mounted card\"${sdcard_mount_p2}/etc/shadow\"" 403 | exit 10 404 | fi 405 | 406 | echo_debug "Change the passwords and sshd_config file" 407 | 408 | sed -e "s#^root:[^:]\+:#root:${root_password_hash}:#" "${sdcard_mount_p2}/etc/shadow" -e "s#^pi:[^:]\+:#pi:${pi_password_hash}:#" -i "${sdcard_mount_p2}/etc/shadow" 409 | sed -e 's;^#PasswordAuthentication.*$;PasswordAuthentication no;g' -e 's;^#PermitRootLogin .*$;PermitRootLogin no;g' -i "${sdcard_mount_p2}/etc/ssh/sshd_config" 410 | mkdir "${sdcard_mount_p2}/home/pi/.ssh" 411 | chmod 0700 "${sdcard_mount_p2}/home/pi/.ssh" 412 | chown 1000:1000 "${sdcard_mount_p2}/home/pi/.ssh" 413 | cat ${public_key_file} >> "${sdcard_mount_p2}/home/pi/.ssh/authorized_keys" 414 | chown 1000:1000 "${sdcard_mount_p2}/home/pi/.ssh/authorized_keys" 415 | chmod 0600 "${sdcard_mount_p2}/home/pi/.ssh/authorized_keys" 416 | 417 | echo "[Unit] 418 | Description=FirstBoot 419 | After=network.target 420 | Before=rc-local.service 421 | ConditionFileNotEmpty=/boot/firstboot.sh 422 | 423 | [Service] 424 | ExecStart=/boot/firstboot.sh 425 | ExecStartPost=/bin/mv /boot/firstboot.sh /boot/firstboot.sh.done 426 | Type=oneshot 427 | RemainAfterExit=no 428 | 429 | [Install] 430 | WantedBy=multi-user.target" > "${sdcard_mount_p2}/lib/systemd/system/firstboot.service" 431 | 432 | cd "${sdcard_mount_p2}/etc/systemd/system/multi-user.target.wants" && ln -s "/lib/systemd/system/firstboot.service" "./firstboot.service" 433 | cd - 434 | 435 | cp "${wifi_file}" "${sdcard_mount_p2}/etc/NetworkManager/system-connections/default_wifi.nmconnection" 436 | if [ -e "${sdcard_mount_p2}/etc/NetworkManager/system-connections/default_wifi.nmconnection" ] 437 | then 438 | chmod 700 "${sdcard_mount_p2}/etc/NetworkManager/system-connections" 439 | chmod 600 "${sdcard_mount_p2}/etc/NetworkManager/system-connections/default_wifi.nmconnection" 440 | echo_debug "The wifi file \"${wifi_file}\" has been copied" 441 | else 442 | echo_error "Can't find the NetworkManager config file \"${sdcard_mount_p2}/default_wifi.nmconnection\"" 443 | exit 8 444 | fi 445 | 446 | umount_sdcard "${sdcard_mount_p2}" 447 | losetup --verbose --detach ${loop_base} 448 | rmdir -v "${sdcard_mount_p2}" 449 | 450 | ########### 451 | # Cleanup and end 452 | # 453 | new_name="${extracted_image_path%.*}-ssh-enabled.img" 454 | mv -v "${extracted_image_path}" "${new_name}" 455 | 456 | echo_debug "" 457 | echo_debug "Now you can burn the disk using something like:" 458 | echo_debug " dd bs=4M status=progress if=${new_name} of=/dev/mmcblk????" 459 | echo_debug "" 460 | -------------------------------------------------------------------------------- /fix-ssh-on-pi.ini_example: -------------------------------------------------------------------------------- 1 | ### Generate the password hash using `mkpasswd -m sha512crypt` 2 | root_password_hash='CHANGEME' 3 | pi_password_hash='CHANGEME' 4 | 5 | ### Generate the password hash using `echo 'mypassword' | openssl passwd -6 -stdin` 6 | ### https://www.raspberrypi.com/documentation/computers/configuration.html#configuring-a-user 7 | userconf_txt_file="/home/CHANGEME/.ssh/userconf.txt" 8 | 9 | ### ssh-keygen -t ed25519 -f ./id_ed25519 -C "Raspberry Pi keys for Ansible" 10 | public_key_file="/home/CHANGEME/.ssh/id_ed25519_pi.pub" 11 | 12 | ### https://www.raspberrypi.com/documentation/computers/configuration.html#setting-up-a-headless-raspberry-pi 13 | wifi_file="/home/CHANGEME/.ssh/default_wifi.nmconnection" 14 | 15 | ### The script to run at boot 16 | first_boot="firstboot.sh" 17 | 18 | ### https://en.wikipedia.org/wiki/Raspberry_Pi_OS#Versions 19 | 20 | # generation can be either Legacy, or Current 21 | generation=CHANGEME 22 | 23 | # version can be either Lite, Medium, or Full 24 | os_version=CHANGEME 25 | 26 | # architecture can be either armhf, or arm64 27 | architecture=CHANGEME 28 | 29 | # where to save the downloaded, and modified images. 30 | working_dir="CHANGEME" 31 | 32 | # For detailed debug information set this to 1 33 | debug=0 34 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Hacker Public Radio ~ The Technology Community Podcast Network 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |

13 | From the Hacker Public Radio Episode hpr2356 :: Safely enabling ssh in the default Raspbian Image" hosted by Ken Fallon CC-BY-SA 14 |

15 | 16 |

In this post I will show you how to take a default Raspbian Image and safely enable ssh by allowing remote access only with authorized keys.

17 |

Recently, and correctly, the official Rasbian Pixel distribution disabled ssh with the note that  from now on SSH will be disabled by default on our images. To understand why this is a good thing please read A security update for raspbian pixel. In short, having 11 million computers out there in the hands of non security professionals, with a known username and password, is not a good idea.

18 |

That said there are many cases where you want to access your Pi remotely, and a key part of that is the ability to access it securely via ssh.

19 |

The Raspberry Pi site offers a solution for how to reactivate ssh. One option is via the GUI, Preferences > Interfaces> SSHEnabled. Another is via the console sudo raspi-config > Interfacing Options > SSHYes > Ok > Finish. The third offers a more interesting option.

20 |

For headless setup, SSH can be enabled by placing a file named ssh, without any extension, onto the boot partition of the SD card. When the Pi boots, it looks for the ssh file. If it is found, SSH is enabled, and the file is deleted. The content of the file does not matter: it could contain text, or nothing at all.

21 |

This is exactly what we want. Normally you would burn the image, then boot it in a Pi with a keyboard, screen and mouse attached, and then add the file. A shortcut to that would be to burn the image, eject it, insert it again, mount the sdcard boot partition, and then create a file called ssh.

22 |

I don’t like either of these solutions as they involve varying amounts of user intervention. I want a solution that will automatically leave me with a modified image at the end without any intervention (aka human error) on my part.

23 |

So I want to build a script that can handle the following steps:

24 | 32 |

I could add to this list and customize every aspect of the image, but my experience has shown that the more you modify, the more maintenance you will need to do. When changes are made to the base Rasbian image, you will need to fix your scripts, and worse is the job of updating all those already deployed Pi’s.

33 |

A better approach is to use the base images and control them with automation tools like Ansible, chef, puppet, cfengine, etc. This allows the images to be treated as Cattle rather than Pets, to see what that means see Architectures for open and scalable clouds, by Randy Bias, VP Technology at EMC, Director at OpenStack Foundation.

34 |

Another approach to consider would be to Network Boot your Raspberry Pi and in that way the sdcard is barely used, and all traffic is run off the network. If you are deploying a lot of pi’s in a area with a good physical network then this is a great option as well. This has the advantage that all the files are kept on the network and can be completely controlled from a central location.

35 |

If you can’t be bothered to stick around and find out how I did it, you can download the script fix-ssh-on-pi.bash. Remember that it is intended more as inspiration rather than a working tool out of the box. I deliberately wrote it so you must edit it to make it fit your needs.

36 |

The remainder of this post is step by step instructions that lead to the creation of the script file, with credit been given to the sites that offered each part of the solution.

37 |

Download Raspian

38 |

Go to the website and download either Raspbian Jessie with desktop, or Raspbian Jessie Lite the Minimal image.

39 |

You can use the tool sha1sum, to confirm they are not corrupt. The sha1sums can be found on the download page. The images are regularly updated, as are the checksums and these are the values when I started researching this post. The images had already been updated by the time I posted it, proving how necessary it is to keep the changes small.

40 |
$ sha1sum 2017-06-21-raspbian-jessie.zip | grep 82a4ecfaadbef4cfb8625f673b5f7b91c24c8e32
 41 | $ sha1sum 2017-06-21-raspbian-jessie-lite.zip | grep a4b352525da188841ab53c859e61888030eb854e
42 |

I use the tool 7z to extract the img files. The tool is Free(dom) Software and is available in the repositories of most distributions. I like it because it is truly the one tool to deal with all the sorts of compressed formats you may come across.

43 |
$ 7z x 2017-06-21-raspbian-jessie.zip
 44 | $ 7z x 2017-06-21-raspbian-jessie-lite.zip
45 |

I will also record the size of the files to compare later.

46 |
 $ ls -al 2017-06-21-raspbian-jessie*img
 47 |  -rw-r--r--. 1 user user 4659931113 Jun 21 12:11 2017-06-21-raspbian-jessie.img
 48 |  -rw-r--r--. 1 user user 1304672023 Jun 21 11:49 2017-06-21-raspbian-jessie-lite.img
 49 | 
50 |

What is a “image” file

51 |

Well it is a bit by bit copy of the disk that the Rasbian Pixel developers have on their desks. That means that when you have matching checksums, you are guaranteed that every single one and zero on your sdcard is identical to the one they had on theirs. When you “burn” the image, you are taking this file, reading it and then writing it so that the order of every one and zero on your sdcard, is identical to the order on the developers sdcard.

52 |

What you end up with is a computer disk drive that starts with information telling the computer that it contains two different partitions, and what file systems they use. Both of these come stuffed with all the files that make up the Rasbian Pixel desktop. Have a quick look at the answer posted by SG60 to the question What is the boot sequence? for a great info graphic.

53 |

The linux mount command has a built in option to mount these files on your computer as another disk drive. People have been mounting CD and DVD images for years, taking advantage of the loop option in mount.

54 |

Enable sshd

55 |

So according to the RaspberryPi.org article we need to add a file to the /boot directory called ssh. So to write the file we simply need to mount the filesystem so we can write to it. With the Rasbian image it’s a little bit more complicated, because unlike CD images, we do not know where the partitions start and end. There is partition information at the beginning of the sdcard but the size of this information could change on the next release. We can still mount it but we need to know the offset, or how far to skip forward, before the actual partition begins.

56 |

Exactly how much you need to skip forward was answered by user arrange in the post Mount single partition from image of entire disk (device) . The approach advised was to use the fdisk command with the –list and –units option to get the information needed to calculate the offset.

57 |
-l, --list
 58 | List the partition tables for the specified devices and then exit.
 59 | -u, --units[=unit]
 60 | When  listing  partition tables, show sizes in 'sectors' or in 'cylinders'.
 61 | 
62 |

To find out where the /boot image starts, we first need to see how many bytes are in a “Unit”. Then we need to find how many Units from the Start the partition is. In our case the units are 512 bytes, and the Start is 8192 for the /boot partition which I know happens to be labeled W95 FAT32 (LBA). The boot partition is usually the smallest one:

63 |
# fdisk --list --units 2017-06-21-raspbian-jessie.img
 64 | Disk 2017-06-21-raspbian-jessie.img: 4.3 GiB, 4659930624 bytes, 9101427 sectors
 65 | Units: sectors of 1 * 512 = 512 bytes
 66 | Sector size (logical/physical): 512 bytes / 512 bytes
 67 | I/O size (minimum/optimal): 512 bytes / 512 bytes
 68 | Disklabel type: dos
 69 | Disk identifier: 0x19ce0afe
 70 | 
 71 | Device                     Boot Start     End Sectors Size  Id Type
 72 | 2017-06-21-raspbian-jessie.img1  8192   93486   85295 41.7M  c W95 FAT32 (LBA)
 73 | 2017-06-21-raspbian-jessie.img2 94208 9101426 9007219  4.3G 83 Linux
74 |

So to do this we need to filter for the line with Units, and then get the second to last column. Here we use the awk command to do both the filtering and the extraction.

75 |
fdisk --list --units  "2017-06-21-raspbian-jessie.img" | awk '/^Units/ {print $(NF-1)}')
 76 | 512
 77 | 
78 |

In the same way we can get the value for the Start

79 |
fdisk --list --units  "2017-06-21-raspbian-jessie.img" | awk '/W95 FAT32/ {print $2}' 
 80 | 8192
 81 | 
82 |

Now we have all we need to calculate the offset in bytes.

83 |
# echo $((8192 * 512))
 84 | 4194304
 85 | 
86 |

And now putting it all together we can mount the image

87 |
mount -o loop,offset=4194304 2017-06-21-raspbian-jessie.img /mnt/sdcard
 88 | 
89 |

We can bundle all those commands together to give us enough to mount any similar image. Have a look at the Bash Series on Hacker Public Radio for a detailed in-depth look at these assignments.

90 |
extracted_image="2017-06-21-raspbian-jessie.img"
 91 | sdcard_mount="/mnt/sdcard"
 92 | 
 93 | echo "Mounting the sdcard boot disk"
 94 | unit_size=$(fdisk --list --units  "${extracted_image}" | awk '/^Units/ {print $(NF-1)}')
 95 | start_boot=$( fdisk --list --units  "${extracted_image}" | awk '/W95 FAT32/ {print $2}' )
 96 | offset_boot=$((${start_boot} * ${unit_size})) 
 97 | mount -o loop,offset="${offset_boot}" "${extracted_image}" "${sdcard_mount}"
 98 | 
99 |

We can confirm it’s mounted

100 |
# mount | grep sdcard
101 | /home/user/Downloads/2017-06-21-raspbian-jessie.img on /mnt/sdcard type vfat (rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=ascii,shortname=mixed,errors=remount-ro)
102 | 
103 |

And it is also available to the file system on your computer

104 |
# ls -al /mnt/sdcard
105 | total 21141
106 | drwxr-xr-x. 3 root root 2048 Jan 1 1970 .
107 | drwxr-xr-x. 25 root root 4096 Jun 14 13:28 ..
108 | -rwxr-xr-x. 1 root root 15660 May 15 21:09 bcm2708-rpi-0-w.dtb
109 | -rwxr-xr-x. 1 root root 15197 May 15 21:09 bcm2708-rpi-b.dtb
110 | -rwxr-xr-x. 1 root root 15456 May 15 21:09 bcm2708-rpi-b-plus.dtb
111 | -rwxr-xr-x. 1 root root 14916 May 15 21:09 bcm2708-rpi-cm.dtb
112 | -rwxr-xr-x. 1 root root 16523 May 15 21:09 bcm2709-rpi-2-b.dtb
113 | -rwxr-xr-x. 1 root root 17624 May 15 21:09 bcm2710-rpi-3-b.dtb
114 | -rwxr-xr-x. 1 root root 16380 May 15 21:09 bcm2710-rpi-cm3.dtb
115 | -rwxr-xr-x. 1 root root 50268 Apr 28 09:18 bootcode.bin
116 | -rwxr-xr-x. 1 root root 229 Jun 21 12:11 cmdline.txt
117 | -rwxr-xr-x. 1 root root 1590 Jun 21 10:50 config.txt
118 | -rwxr-xr-x. 1 root root 18693 Aug 21 2015 COPYING.linux
119 | -rwxr-xr-x. 1 root root 2581 May 15 21:09 fixup_cd.dat
120 | -rwxr-xr-x. 1 root root 6660 May 15 21:09 fixup.dat
121 | -rwxr-xr-x. 1 root root 9802 May 15 21:09 fixup_db.dat
122 | -rwxr-xr-x. 1 root root 9798 May 15 21:09 fixup_x.dat
123 | -rwxr-xr-x. 1 root root 145 Jun 21 12:11 issue.txt
124 | -rwxr-xr-x. 1 root root 4577000 May 15 21:09 kernel7.img
125 | -rwxr-xr-x. 1 root root 4378160 May 15 21:09 kernel.img
126 | -rwxr-xr-x. 1 root root 1494 Nov 18 2015 LICENCE.broadcom
127 | -rwxr-xr-x. 1 root root 18974 Jun 21 12:11 LICENSE.oracle
128 | drwxr-xr-x. 2 root root 9728 Jun 21 10:36 overlays
129 | -rwxr-xr-x. 1 root root 657828 May 15 21:09 start_cd.elf
130 | -rwxr-xr-x. 1 root root 4990532 May 15 21:09 start_db.elf
131 | -rwxr-xr-x. 1 root root 2852356 May 15 21:09 start.elf
132 | -rwxr-xr-x. 1 root root 3935908 May 15 21:09 start_x.elf
133 | 
134 |

With the disk available to use we can easily create an empty file using a text editor. I will use the touch command to do this:

135 |
# touch /mnt/sdcard/ssh
136 | # ls -al /mnt/sdcard/ssh
137 | -rwxr-xr-x. 1 root root 0 Jul 5 17:37 /mnt/sdcard/ssh
138 | 
139 |

We are not finished yet but for the moment I will unmount the image using umount /mnt/sdcard.

140 |

At this point you could just use the image but you would be putting your system at risk. If you did you will also get a pop up warning from Rasbian to tell you how unsafe your system is. You may not be worried about anyone accessing something on the Pi, or even your network. However this computer in it’s present state can still be exploited by someone. The nasty things that they do can all be traced back to your home or business. So let’s do the right thing and configure access only via ssh-keys.

141 |

ssh-keys

142 |

Surprisingly this is one of the few cases where using a secure system is actually a lot more convenient than normal. With the image as it is now, you would need to type in the username pi and password Raspbian every single time you want to access the server. When we are finished you can just enter your password for your keys once, and after that you can ssh to any server without ever been presented with a login prompt !

143 |

Have a read of “How to setup SSH keys and why?“, by Ravi for a nice overview.

144 |

Creating ssh keys

145 |

We will use the ssh-keygen command, and use -t to specify the key type, -f to specify where to write the file, and -C to add a comment. You will be asked to enter a password for the private key. Make sure this is a nice long and complicated but easy to remember one.

146 |
# ssh-keygen -t ed25519 -f ./id_ed25519 -C "Raspberry Pi keys for Ansible"
147 | Generating public/private ed25519 key pair.
148 | Enter passphrase (empty for no passphrase):
149 | Enter same passphrase again:
150 | Your identification has been saved in ./id_ed25519.
151 | Your public key has been saved in ./id_ed25519.pub.
152 | The key fingerprint is:
153 | SHA256:rHS3ccWV02X35Jhk5Qtfpe8VP6otTVRFNicf9Cb3S+w Raspberry Pi keys for Ansible
154 | The key's randomart image is:
155 | +--[ED25519 256]--+
156 | |              =*^|
157 | |             + ^X|
158 | |             .O.@|
159 | |       .     o+=*|
160 | |      . S o o  *=|
161 | |     . o . + .+.+|
162 | |      .   . o. E.|
163 | |           .o.   |
164 | |           ...   |
165 | +----[SHA256]-----+
166 | 
167 |

As expected this will create two files. The private key has no extension, and the public one ends with .pub.

168 |
# ls -haltr id_ed25519*
169 | -rw-r--r--. 1 root root 111 Jul 12 13:07 id_ed25519.pub
170 | -rw-------. 1 root root 419 Jul 12 13:07 id_ed25519
171 | 
172 |

You should be very careful to keep your private key secret (id_ed25519 in our example). The other file id_ed25519.pub is your public key and it doesn’t matter who has access to that. For a great explanation of why it doesn’t matter I recommend watching this video. It is stitched together from the course in Modern cryptography done by Brit Cruise of the Khan Academy.

173 |

I have no intention of ever using these keys, so I will display their contents here.

174 |

The public key will be kept in a safe place and never distributed to the sdcards. Don’t forget to make a few backups.

175 |
# cat id_ed25519
176 | -----BEGIN OPENSSH PRIVATE KEY-----
177 | b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
178 | QyNTUxOQAAACBolD9Vd7dNmbbyUUvtY5kwQf7+cucR9kEjUdt6JgEOTQAAAKCj+dG4o/nR
179 | uAAAAAtzc2gtZWQyNTUxOQAAACBolD9Vd7dNmbbyUUvtY5kwQf7+cucR9kEjUdt6JgEOTQ
180 | AAAEA4xoxtetO+V9hv+TD/WMIWcD8JSLNQuzonezfO1+kAi2iUP1V3t02ZtvJRS+1jmTBB
181 | /v5y5xH2QSNR23omAQ5NAAAAHVJhc3BiZXJyeSBQaSBrZXlzIGZvciBBbnNpYmxl
182 | -----END OPENSSH PRIVATE KEY-----
183 | 
184 | # cat id_ed25519.pub
185 | ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGiUP1V3t02ZtvJRS+1jmTBB/v5y5xH2QSNR23omAQ5N Raspberry Pi keys for Ansible
186 | 
187 |

Now that you have your ssh keys ready, we are ready to copy the public key to the Rasbian image. The private key you should copy into your own .ssh/ directory to your own home directory, on your computer.

188 |

The changes we now need to make are all on the same image but in the other partition. We can use the same trick we did earlier and mount the other partition. Remember that we are looking for file system type Linux instead of W95 FAT32 (LBA). So this commands will just mount the other partition for us, we are using the same mount point on our own computer:

189 |
extracted_image="2017-06-21-raspbian-jessie.img"
190 | sdcard_mount="/mnt/sdcard"
191 | 
192 | echo "Mounting the sdcard root disk"
193 | unit_size=$(fdisk --list --units  "${extracted_image}" | awk '/^Units/ {print $(NF-1)}')
194 | start_boot=$( fdisk --list --units  "${extracted_image}" | awk '/Linux/ {print $2}' )
195 | offset_boot=$((${start_boot} * ${unit_size})) 
196 | mount -o loop,offset="${offset_boot}" "${extracted_image}" "${sdcard_mount}"
197 | 
198 |

The second partition on the sdcard is now mounted allowing us to modify it easily.

199 |

Normally you would use a tool called ssh-copy-id to copy the public keys to the raspberry pi, using username and password for login. It actually creates, or adds to, a special file called authorized_keys in a hidden .ssh directory of the users home directory, with very restricted file access permissions on the directory and file level.

200 |

If we want to do this on the mounted image partition, we first need to create the directory, then change permissions to octal 0700. This will allow the owner to, read, write, and execute (change into) the directory.

201 |
# mkdir /mnt/sdcard/home/pi/.ssh
202 | # chmod 0700 /mnt/sdcard/home/pi/.ssh
203 | # chown 1000:1000 /mnt/sdcard/home/pi/.ssh
204 | # ls -aln /mnt/sdcard/home/pi/ | grep .ssh
205 | drwx------. 2 1000 1000 4096 Jul 12 13:55 .ssh
206 | 
207 |

Now that the directory is in place we can copy over the public key. I’m using the cat (from concatenate) command, to simulate what ssh-copy-id would do. We know the file is not there, so it will create it. If the file was there this command would add the new key to the end of it.

208 |
# cat id_ed25519.pub >> /mnt/sdcard/home/pi/.ssh/authorized_keys
209 | 
210 |

And it is copied over.

211 |
# cat /mnt/sdcard/home/pi/.ssh/authorized_keys
212 | ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGiUP1V3t02ZtvJRS+1jmTBB/v5y5xH2QSNR23omAQ5N Raspberry Pi keys for Ansible
213 | 
214 |

As you can imagine, ssh is very paranoid about it’s settings. All the permissions on the directory and files are checked on each login. If any are missing or have the wrong access permissions set, it will not allow you to access the system. While this may be frustrating when you are troubleshooting, just remember it is for your own good.

215 |

The chown command is used to change the owner of the file. You normally could use the username pi, but as we are setting this on a different computer, that user doesn’t exist. To get around this we need to use the id number for the pi user and the pi group.

216 |
# egrep "^pi" /mnt/sdcard/etc/passwd
217 | pi:x:1000:1000:,,,:/home/pi:/bin/bash
218 | # egrep "^pi" /mnt/sdcard/etc/group
219 | pi:x:1000:
220 |

As we can see that is 1000 in both cases, so knowing this we can fix the ownership and access permissions. This time we use octal 0600 which will not allow the file to execute. For a file, that means not allowing the file to run computer code.

221 |
# chown 1000:1000 /mnt/sdcard/home/pi/.ssh/authorized_keys
222 | # chmod 0600 /mnt/sdcard/home/pi/.ssh/authorized_keys
223 | # ls -aln /mnt/sdcard/home/pi/.ssh/authorized_keys
224 | -rw-------. 1 1000 1000 111 Jul 12 13:55 /mnt/sdcard/home/pi/.ssh/authorized_keys
225 | 
226 |

Restricting Root and Requiring Keys on the Pi’s ssh server

227 |

I also want to make some changes to how the ssh server operates. Getting started with SSH security and configuration is a good, if IBM specific, introduction to the topic.

228 |

We want to prevent any login by the user root over ssh, and only allow login with ssh-keys. This means changing two settings in the configuration for the ssh server that will run on the Pi itself. It’s the program that listens for connections from your PC. The man page for sshd_config shows us the two changes we need to make.

229 |
PermitRootLogin If this option is set to no, root is not allowed to log in (via ssh).
230 | PasswordAuthentication Specifies whether password authentication is allowed. The default is yes.
231 |

We will use the sed command below. If this doesn’t make any sense then see the complete guide to sed by fellow Hacker Public Radio contributor Dave Morriss. I’m using the ‘;‘ as a delimiter.

232 |
sed -e 's;^#PasswordAuthentication.*$;PasswordAuthentication no;g' -e 's;^PermitRootLogin .*$;PermitRootLogin no;g' -i /mnt/sdcard/etc/ssh/sshd_config
233 | 
234 |

Change root and pi user password

235 |

Normally you would use passwd to change your password, but in our case we are directly editing the files where they are stored. Back in the day the passwords were kept in the /etc/passwd file. However that has long since moved to /etc/shadow. Here we can check the current settings for the root and pi user on the image.

236 |
# egrep 'root|pi' /mnt/sdcard/etc/passwd
237 | root:x:0:0:root:/root:/bin/bash
238 | pi:x:1000:1000:,,,:/home/pi:/bin/bash
239 | 
240 |

The :x: in the second column shows that the “encrypted password is stored in /etc/shadow file”.

241 |
# egrep 'root|pi' /mnt/sdcard/etc/shadow
242 | root:*:17338:0:99999:7:::
243 | pi:$6$hio1BNCX$Qux8hGsSy.a.pEoI/TkcGGJlEJOdCAgcTtImxDugQVO1e.6cxgsQ4pFRL2cJvn9AjCZKX4RfOgupS2gQrFhrF/:17338:0:99999:7:::
244 | 
245 |

We are going to use a small python program which I found in the question “How to create an SHA-512 hashed password for shadow?“. The answer by davey gives a way to do this that we can use in our script. The example password I’m using is from XKCD, but you should use something else because the password correct horse battery staple is now to be found in every list of passwords to try.

246 |

Note: that your hash will be different to this:

247 |
# root_password="$( python3 -c 'import crypt; print(crypt.crypt("correct horse battery staple", crypt.mksalt(crypt.METHOD_SHA512)))' )"
248 | # echo ${root_password}
249 | $6$Fx9Jh3AIHbOYrIOd$lsrwr1d2cVl0crybGGgZZclpEcVivPhA5N8LAhHQoKOOrM.tFQV/fmWpfdGzGsJVtaaYFARk1YKt/TVfZzMCC/
250 | 
251 |

We also want to change the pi users password, also use a different one to the one here.

252 |
# pi_password="$( python3 -c 'import crypt; print(crypt.crypt("wrong cart charger paperclip", crypt.mksalt(crypt.METHOD_SHA512)))' )"
253 | # echo ${pi_password}
254 | $6$u1n2Lx4GuxyfsEYS$FzbrDIRqXdlly191/74t8QkfQHUC.3mRwx/fu0C8J36m02PQZokEXfJGlgq2fydQ5a8s/185Mkb6MRdxWqdSF.
255 | 
256 |

Now that we have assigned the passwords to two variables we can use sed with the `#` as a delimiter. This is because the traditional delimiter of forward slash, `/` is probably part of your password hash.

257 |

The following command will replace the existing password field for root and the user pi. That is the (the :*:  and the long string “$6$hio1BNCX$Qux8hGsSy.a.pEoI/TkcGGJlEJOdCAgcTtImxDugQVO1e.6cxgsQ4pFRL2cJvn9AjCZKX4RfOgupS2gQrFhrF/” in the /etc/shadow file.

258 |
sed -i -e "s#^root:[^:]\+:#root:${root_password}:#" /mnt/sdcard/etc/shadow
259 | sed -i -e "s#^pi:[^:]\+:#pi:${pi_password}:#" /mnt/sdcard/etc/shadow
260 | 
261 |

And now we see they are changed

262 |
# egrep 'root|pi' /mnt/sdcard/etc/shadow
263 | root:$6$Fx9Jh3AIHbOYrIOd$lsrwr1d2cVl0crybGGgZZclpEcVivPhA5N8LAhHQoKOOrM.tFQV/fmWpfdGzGsJVtaaYFARk1YKt/TVfZzMCC/:17338:0:99999:7:::
264 | pi:$6$u1n2Lx4GuxyfsEYS$FzbrDIRqXdlly191/74t8QkfQHUC.3mRwx/fu0C8J36m02PQZokEXfJGlgq2fydQ5a8s/185Mkb6MRdxWqdSF.:17338:0:99999:7:::
265 | 
266 |

WARNING Keep in mind that we are editing the files on the mounted sdcard and not the one on your own computer !!!

267 |

Burn the image

268 |

Now we need to unmount the image again.

269 |
# umount /mnt/sdcard.
270 |

Just so we don’t get confused I’m going to rename both files

271 |
# mv -v 2017-06-21-raspbian-jessie.img 2017-06-21-raspbian-jessie-ssh-enabled.img
272 | '2017-06-21-raspbian-jessie.img' -> '2017-06-21-raspbian-jessie-ssh-enabled.img'
273 | # mv -v 2017-06-21-raspbian-jessie-lite.img 2017-06-21-raspbian-jessie-lite-ssh-enabled.img
274 | '2017-06-21-raspbian-jessie-lite.img' -> '2017-06-21-raspbian-jessie-lite-ssh-enabled.img'
275 | 
276 |

Now we can see that the file size have remained the same but sha1sums have changed. This is exactly why you should always check the hash of files you download from the Internet.

277 |
# ls -al 2017-06-21-raspbian-jessie*img
278 | -rw-r--r--. 1 user user 1304672023 Jul 5 17:42 2017-06-21-raspbian-jessie-lite-ssh-enabled.img
279 | -rw-r--r--. 1 user user 4659931113 Jul 5 17:41 2017-06-21-raspbian-jessie-ssh-enabled.img
280 | 
281 | # sha1sum 2017-06-21-raspbian-jessie*img
282 | 41168af20116cbd607c7c14194a4523af1b48250 2017-06-21-raspbian-jessie-lite-ssh-enabled.img
283 | 5d075b28494f1be8f81b79f3b20f7e3011e00473 2017-06-21-raspbian-jessie-ssh-enabled.img
284 | 
285 |

You are now ready to use the modified image. To burn it you can run the lsblk command, insert the sdcard you want to overwrite with the new image, then use lsblk again to locate the new disk.

286 |
        # lsblk
287 |         NAME                                          MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINT
288 |         sdb                                             8:16   1   7.4G  0 disk
289 |         └─sdb1                                          8:17   1   7.4G  0 part  /mnt/SANZA
290 |         sda                                             8:0    0 238.5G  0 disk
291 |         ├─sda2                                          8:2    0   500M  0 part  /boot
292 |         ├─sda3                                          8:3    0 237.8G  0 part
293 |         │ └─luks-12321122-1234-1234-1234-123456767755 253:0    0 237.8G  0 crypt
294 |         │   ├─fedora_root                             253:1    0   230G  0 lvm   /
295 |         │   └─fedora_swap                             253:2    0   7.8G  0 lvm   [SWAP]
296 |         └─sda1                                          8:1    0   200M  0 part  /boot/efi
297 |         mmcblk0        
298 | 
299 |

In my case it came up mmcblk0, which translates to /dev/mmcblk0. We can use dd command as you would normally.

300 |
# dd bs=4M status=progress if=2017-06-21-raspbian-jessie-ssh-enabled.img of=/dev/mmcblk0
301 |

Once you are finished you can put the sdcard into a Raspberry Pi and turn it on. Once you find out it’s IP Address you can ssh to it using your new keys.

302 |
ssh -i id_ed25519 pi@192.168.1.5
303 |

This will ask you for a password. The password is the one you set for your private ssh key above and not the password for the pi user. To avoid been asked every time we can simply use ssh-agent to manage our keys for us. Simply type the command ssh-agent and enter the password for your ssh key. If that works, you can then login without been asked for a password on any computer that has your public key listed in their authorized_keys file.

304 |

That’s it – Live long and Prosper.

305 | 306 | 307 | 308 | -------------------------------------------------------------------------------- /index.md: -------------------------------------------------------------------------------- 1 | From the Hacker Public Radio Episode [hpr2356 :: Safely enabling ssh in 2 | the default Raspbian Image\" hosted by Ken Fallon 3 | CC-BY-SA](https://hackerpublicradio.org/eps.php?id=2356) 4 | 5 | In this post I will show you how to take a default Raspbian Image and 6 | safely enable ssh by allowing remote access only with authorized keys. 7 | 8 | Recently, and correctly, the official Rasbian Pixel distribution 9 | disabled ssh with the note that  *from now on SSH will be disabled by 10 | default on our images. *To understand why this is a good thing please 11 | read [A security update for raspbian 12 | pixel](https://www.raspberrypi.org/blog/a-security-update-for-raspbian-pixel/). 13 | In short, having 11 million computers out there in the hands of non 14 | security professionals, with a known username and password, is not a 15 | good idea. 16 | 17 | That said there are many cases where you want to access your Pi 18 | remotely, and a key part of that is the ability to access it securely 19 | via ssh. 20 | 21 | The Raspberry Pi site offers a 22 | [solution](https://www.raspberrypi.org/documentation/remote-access/ssh/) 23 | for how to reactivate ssh. One option is via the GUI, *Preferences* \> 24 | *Interfaces*\> *SSH* \> *Enabled*. Another is via the console *sudo 25 | raspi-config* > *Interfacing Options *\> *SSH* \> *Yes \> Ok \> Finish*. 26 | The third offers a more interesting option. 27 | 28 | > For headless setup, SSH can be enabled by placing a file named ssh, 29 | > without any extension, onto the boot partition of the SD card. When 30 | > the Pi boots, it looks for the ssh file. If it is found, SSH is 31 | > enabled, and the file is deleted. The content of the file does not 32 | > matter: it could contain text, or nothing at all. 33 | 34 | This is exactly what we want. Normally you would burn the image, then 35 | boot it in a Pi with a keyboard, screen and mouse attached, and then add 36 | the file. A shortcut to that would be to burn the image, eject it, 37 | insert it again, mount the sdcard boot partition, and then create a file 38 | called *ssh*. 39 | 40 | I don't like either of these solutions as they involve varying amounts 41 | of user intervention. I want a solution that will automatically leave me 42 | with a modified image at the end without any intervention (aka human 43 | error) on my part. 44 | 45 | So I want to build a script that can handle the following steps: 46 | 47 | - Download the latest image zip file 48 | - Verify it is valid 49 | - Extract the image itself 50 | - Enable ssh 51 | - Change the default passwords for the root and pi user 52 | - Secure the ssh server on the Pi 53 | 54 | I could add to this list and customize every aspect of the image, but my 55 | experience has shown that the more you modify, the more maintenance you 56 | will need to do. When changes are made to the base Rasbian image, you 57 | will need to fix your scripts, and worse is the job of updating all 58 | those already deployed Pi's. 59 | 60 | A better approach is to use the base images and control them with 61 | automation tools like [Ansible](https://www.ansible.com/), 62 | [chef](https://www.chef.io/chef/), [puppet](https://puppet.com), 63 | [cfengine](https://cfengine.com/), etc. This allows the images to be 64 | treated as Cattle rather than Pets, to see what that means see 65 | [Architectures for open and scalable 66 | clouds](https://www.slideshare.net/randybias/architectures-for-open-and-scalable-clouds), 67 | by Randy Bias, VP Technology at EMC, Director at OpenStack Foundation. 68 | 69 | Another approach to consider would be to [Network 70 | Boot](https://www.raspberrypi.org/documentation/hardware/raspberrypi/bootmodes/net_tutorial.md) 71 | your Raspberry Pi and in that way the sdcard is barely used, and all 72 | traffic is run off the network. If you are deploying a lot of pi's in a 73 | area with a good physical network then this is a great option as 74 | well. This has the advantage that all the files are kept on the network 75 | and can be completely controlled from a central location. 76 | 77 | If you can't be bothered to stick around and find out how I did it, you 78 | can download the script 79 | [fix-ssh-on-pi.bash](http://hackerpublicradio.org/eps/hpr2356/fix-ssh-on-pi.bash.txt). 80 | Remember that it is intended more as inspiration rather than a working 81 | tool out of the box. I deliberately wrote it so you must edit it to make 82 | it fit your needs. 83 | 84 | The remainder of this post is step by step instructions that lead to the 85 | creation of the script file, with credit been given to the sites that 86 | offered each part of the solution. 87 | 88 | ## Download Raspian 89 | 90 | Go to the [website](https://www.raspberrypi.org/downloads/raspbian) and 91 | download either Raspbian Jessie with desktop, or Raspbian Jessie Lite 92 | the Minimal image. 93 | 94 | You can use the tool [*sha1sum*](https://en.wikipedia.org/wiki/Sha1sum), 95 | to confirm they are not corrupt. The sha1sums can be found on the 96 | download page. The images are regularly updated, as are the checksums 97 | and these are the values when I started researching this post. The 98 | images had already been updated by the time I posted it, proving how 99 | necessary it is to keep the changes small. 100 | 101 | $ sha1sum 2017-06-21-raspbian-jessie.zip | grep 82a4ecfaadbef4cfb8625f673b5f7b91c24c8e32 102 | $ sha1sum 2017-06-21-raspbian-jessie-lite.zip | grep a4b352525da188841ab53c859e61888030eb854e 103 | 104 | I use the tool [*7z*](http://www.7-zip.org/) to extract the img files. 105 | The tool is Free(dom) Software and is available in the repositories of 106 | most distributions. I like it because it is truly the one tool to deal 107 | with all the sorts of compressed formats you may come across. 108 | 109 | $ 7z x 2017-06-21-raspbian-jessie.zip 110 | $ 7z x 2017-06-21-raspbian-jessie-lite.zip 111 | 112 | I will also record the size of the files to compare later. 113 | 114 | $ ls -al 2017-06-21-raspbian-jessie*img 115 | -rw-r--r--. 1 user user 4659931113 Jun 21 12:11 2017-06-21-raspbian-jessie.img 116 | -rw-r--r--. 1 user user 1304672023 Jun 21 11:49 2017-06-21-raspbian-jessie-lite.img 117 | 118 | ## What is a "image" file 119 | 120 | Well it is a bit by bit copy of the disk that the Rasbian Pixel 121 | developers have on their desks. That means that when you have matching 122 | checksums, you are guaranteed that every single one and zero on your 123 | sdcard is **identical** to the one they had on theirs. When you "burn" 124 | the image, you are taking this file, reading it and then writing it so 125 | that the order of every one and zero on your sdcard, is identical to the 126 | order on the developers sdcard. 127 | 128 | What you end up with is a computer disk drive that starts with 129 | information telling the computer that it contains two different 130 | partitions, and what file systems they use. Both of these come stuffed 131 | with all the files that make up the Rasbian Pixel desktop. Have a quick 132 | look at the answer posted by 133 | [SG60](https://raspberrypi.stackexchange.com/users/9625) to the question 134 | [What is the boot 135 | sequence?](https://raspberrypi.stackexchange.com/questions/10442/what-is-the-boot-sequence) for 136 | a great info graphic. 137 | 138 | The linux [mount](https://en.wikipedia.org/wiki/Mount_(Unix)) command 139 | has a built in option to mount these files on your computer as another 140 | disk drive. People have been 141 | [mounting](https://askubuntu.com/questions/164227/how-to-mount-an-iso-file#193632) 142 | CD and DVD images for years, taking advantage of the loop option in 143 | mount. 144 | 145 | ## Enable sshd 146 | 147 | So according to the RaspberryPi.org article we need to add a file to the 148 | */boot* directory called *ssh*. So to write the file we simply need to 149 | mount the filesystem so we can write to it. With the Rasbian image it's 150 | a little bit more complicated, because unlike CD images, we do not know 151 | where the partitions start and end. There is partition information at 152 | the beginning of the sdcard but the size of this information could 153 | change on the next release. We can still mount it but we need to know 154 | the offset, or how far to skip forward, before the actual partition 155 | begins. 156 | 157 | Exactly how much you need to skip forward was answered by user 158 | [arrange](https://askubuntu.com/users/9340/arrange) in the post [Mount 159 | single partition from image of entire disk 160 | (device)](https://askubuntu.com/questions/69363/mount-single-partition-from-image-of-entire-disk-device) . 161 | The approach advised was to use 162 | the [fdisk](https://linux.die.net/man/8/fdisk) command with the *--list* 163 | and *--units* option to get the information needed to calculate the 164 | offset. 165 | 166 | -l, --list 167 | List the partition tables for the specified devices and then exit. 168 | -u, --units[=unit] 169 | When  listing  partition tables, show sizes in 'sectors' or in 'cylinders'. 170 | 171 | To find out where the */boot* image starts, we first need to see how 172 | many bytes are in a "Unit". Then we need to find how many Units from the 173 | Start the partition is. In our case the units are *512 bytes*, and the 174 | Start is *8192* for the */boot* partition which I know happens to 175 | be labeled *W95 FAT32 (LBA)*. The boot partition is usually the smallest 176 | one: 177 | 178 | # fdisk --list --units 2017-06-21-raspbian-jessie.img 179 | Disk 2017-06-21-raspbian-jessie.img: 4.3 GiB, 4659930624 bytes, 9101427 sectors 180 | Units: sectors of 1 * 512 = 512 bytes 181 | Sector size (logical/physical): 512 bytes / 512 bytes 182 | I/O size (minimum/optimal): 512 bytes / 512 bytes 183 | Disklabel type: dos 184 | Disk identifier: 0x19ce0afe 185 | 186 | Device Boot Start End Sectors Size Id Type 187 | 2017-06-21-raspbian-jessie.img1 8192 93486 85295 41.7M c W95 FAT32 (LBA) 188 | 2017-06-21-raspbian-jessie.img2 94208 9101426 9007219 4.3G 83 Linux 189 | 190 | So to do this we need to filter for the line with *Units*, and then get 191 | the second to last column. Here we use the 192 | [awk](http://hobbypublicradio.org/series.php?id=94) command to do both 193 | the filtering and the extraction. 194 | 195 | fdisk --list --units "2017-06-21-raspbian-jessie.img" | awk '/^Units/ {print $(NF-1)}') 196 | 512 197 | 198 | In the same way we can get the value for the *Start* 199 | 200 | fdisk --list --units "2017-06-21-raspbian-jessie.img" | awk '/W95 FAT32/ {print $2}' 201 | 8192 202 | 203 | Now we have all we need to calculate the offset in bytes. 204 | 205 | # echo $((8192 * 512)) 206 | 4194304 207 | 208 | And now putting it all together we can mount the image 209 | 210 | mount -o loop,offset=4194304 2017-06-21-raspbian-jessie.img /mnt/sdcard 211 | 212 | We can bundle all those commands together to give us enough to mount any 213 | similar image. Have a look at the [Bash 214 | Series](http://hackerpublicradio.org/series.php?id=42) on Hacker Public 215 | Radio for a detailed in-depth look at these assignments. 216 | 217 | extracted_image="2017-06-21-raspbian-jessie.img" 218 | sdcard_mount="/mnt/sdcard" 219 | 220 | echo "Mounting the sdcard boot disk" 221 | unit_size=$(fdisk --list --units "${extracted_image}" | awk '/^Units/ {print $(NF-1)}') 222 | start_boot=$( fdisk --list --units "${extracted_image}" | awk '/W95 FAT32/ {print $2}' ) 223 | offset_boot=$((${start_boot} * ${unit_size})) 224 | mount -o loop,offset="${offset_boot}" "${extracted_image}" "${sdcard_mount}" 225 | 226 | We can confirm it's mounted 227 | 228 | # mount | grep sdcard 229 | /home/user/Downloads/2017-06-21-raspbian-jessie.img on /mnt/sdcard type vfat (rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=ascii,shortname=mixed,errors=remount-ro) 230 | 231 | And it is also available to the file system on your computer 232 | 233 | # ls -al /mnt/sdcard 234 | total 21141 235 | drwxr-xr-x. 3 root root 2048 Jan 1 1970 . 236 | drwxr-xr-x. 25 root root 4096 Jun 14 13:28 .. 237 | -rwxr-xr-x. 1 root root 15660 May 15 21:09 bcm2708-rpi-0-w.dtb 238 | -rwxr-xr-x. 1 root root 15197 May 15 21:09 bcm2708-rpi-b.dtb 239 | -rwxr-xr-x. 1 root root 15456 May 15 21:09 bcm2708-rpi-b-plus.dtb 240 | -rwxr-xr-x. 1 root root 14916 May 15 21:09 bcm2708-rpi-cm.dtb 241 | -rwxr-xr-x. 1 root root 16523 May 15 21:09 bcm2709-rpi-2-b.dtb 242 | -rwxr-xr-x. 1 root root 17624 May 15 21:09 bcm2710-rpi-3-b.dtb 243 | -rwxr-xr-x. 1 root root 16380 May 15 21:09 bcm2710-rpi-cm3.dtb 244 | -rwxr-xr-x. 1 root root 50268 Apr 28 09:18 bootcode.bin 245 | -rwxr-xr-x. 1 root root 229 Jun 21 12:11 cmdline.txt 246 | -rwxr-xr-x. 1 root root 1590 Jun 21 10:50 config.txt 247 | -rwxr-xr-x. 1 root root 18693 Aug 21 2015 COPYING.linux 248 | -rwxr-xr-x. 1 root root 2581 May 15 21:09 fixup_cd.dat 249 | -rwxr-xr-x. 1 root root 6660 May 15 21:09 fixup.dat 250 | -rwxr-xr-x. 1 root root 9802 May 15 21:09 fixup_db.dat 251 | -rwxr-xr-x. 1 root root 9798 May 15 21:09 fixup_x.dat 252 | -rwxr-xr-x. 1 root root 145 Jun 21 12:11 issue.txt 253 | -rwxr-xr-x. 1 root root 4577000 May 15 21:09 kernel7.img 254 | -rwxr-xr-x. 1 root root 4378160 May 15 21:09 kernel.img 255 | -rwxr-xr-x. 1 root root 1494 Nov 18 2015 LICENCE.broadcom 256 | -rwxr-xr-x. 1 root root 18974 Jun 21 12:11 LICENSE.oracle 257 | drwxr-xr-x. 2 root root 9728 Jun 21 10:36 overlays 258 | -rwxr-xr-x. 1 root root 657828 May 15 21:09 start_cd.elf 259 | -rwxr-xr-x. 1 root root 4990532 May 15 21:09 start_db.elf 260 | -rwxr-xr-x. 1 root root 2852356 May 15 21:09 start.elf 261 | -rwxr-xr-x. 1 root root 3935908 May 15 21:09 start_x.elf 262 | 263 | With the disk available to use we can easily create an empty file using 264 | a text editor. I will use the 265 | [touch](https://en.wikipedia.org/wiki/Touch_(Unix)) command to do this: 266 | 267 | # touch /mnt/sdcard/ssh 268 | # ls -al /mnt/sdcard/ssh 269 | -rwxr-xr-x. 1 root root 0 Jul 5 17:37 /mnt/sdcard/ssh 270 | 271 | We are not finished yet but for the moment I will unmount the image 272 | using *umount /mnt/sdcard*. 273 | 274 | At this point you *could* just use the image but you would be putting 275 | your system at risk. If you did you will also get a pop up warning from 276 | Rasbian to tell you how unsafe your system is. You may not be worried 277 | about anyone accessing something on the Pi, or even your network. 278 | However this computer in it's present state can still be exploited by 279 | someone. The nasty things that they do can all be traced back to your 280 | home or business. So let's do the right thing and configure access only 281 | via ssh-keys. 282 | 283 | ## ssh-keys 284 | 285 | Surprisingly this is one of the few cases where using a secure system is 286 | actually a lot more convenient than normal. With the image as it is now, 287 | you would need to type in the username pi and password Raspbian every 288 | single time you want to access the server. When we are finished you can 289 | just enter your password for your keys once, and after that you can ssh 290 | to any server without ever been presented with a login prompt ! 291 | 292 | Have a read of "[How to setup SSH keys and 293 | why?](http://www.aboutlinux.info/2005/09/how-to-setup-ssh-keys-and-why.html)", 294 | by Ravi for a nice overview. 295 | 296 | ### Creating ssh keys 297 | 298 | We will use the *ssh-keygen* command, and use *-t* to specify the key 299 | type, *-f* to specify where to write the file, and *-C* to add a 300 | comment. You will be asked to enter a password for the private key. Make 301 | sure this is a nice long and complicated but easy to remember one. 302 | 303 | # ssh-keygen -t ed25519 -f ./id_ed25519 -C "Raspberry Pi keys for Ansible" 304 | Generating public/private ed25519 key pair. 305 | Enter passphrase (empty for no passphrase): 306 | Enter same passphrase again: 307 | Your identification has been saved in ./id_ed25519. 308 | Your public key has been saved in ./id_ed25519.pub. 309 | The key fingerprint is: 310 | SHA256:rHS3ccWV02X35Jhk5Qtfpe8VP6otTVRFNicf9Cb3S+w Raspberry Pi keys for Ansible 311 | The key's randomart image is: 312 | +--[ED25519 256]--+ 313 | | =*^| 314 | | + ^X| 315 | | .O.@| 316 | | . o+=*| 317 | | . S o o *=| 318 | | . o . + .+.+| 319 | | . . o. E.| 320 | | .o. | 321 | | ... | 322 | +----[SHA256]-----+ 323 | 324 | As expected this will create two files. The private key has no 325 | extension, and the public one ends with *.pub*. 326 | 327 | # ls -haltr id_ed25519* 328 | -rw-r--r--. 1 root root 111 Jul 12 13:07 id_ed25519.pub 329 | -rw-------. 1 root root 419 Jul 12 13:07 id_ed25519 330 | 331 | You should be **very** careful to keep your private key secret 332 | (*id_ed25519* in our example). The other file *id_ed25519.pub* is your 333 | public key and it doesn't matter who has access to that. For a great 334 | explanation of why it doesn't matter I recommend watching 335 | [this](https://www.youtube.com/watch?v=YEBfamv-_do) video. It is 336 | stitched together from the course in [Modern 337 | cryptography](https://www.khanacademy.org/computing/computer-science/cryptography/modern-crypt/v/the-fundamental-theorem-of-arithmetic-1) 338 | done by Brit Cruise of the Khan Academy. 339 | 340 | I have no intention of ever using these keys, so I will display their 341 | contents here. 342 | 343 | The public key will be kept in a safe place and never distributed to the 344 | sdcards. Don't forget to make a few backups. 345 | 346 | # cat id_ed25519 347 | -----BEGIN OPENSSH PRIVATE KEY----- 348 | b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW 349 | QyNTUxOQAAACBolD9Vd7dNmbbyUUvtY5kwQf7+cucR9kEjUdt6JgEOTQAAAKCj+dG4o/nR 350 | uAAAAAtzc2gtZWQyNTUxOQAAACBolD9Vd7dNmbbyUUvtY5kwQf7+cucR9kEjUdt6JgEOTQ 351 | AAAEA4xoxtetO+V9hv+TD/WMIWcD8JSLNQuzonezfO1+kAi2iUP1V3t02ZtvJRS+1jmTBB 352 | /v5y5xH2QSNR23omAQ5NAAAAHVJhc3BiZXJyeSBQaSBrZXlzIGZvciBBbnNpYmxl 353 | -----END OPENSSH PRIVATE KEY----- 354 | 355 | # cat id_ed25519.pub 356 | ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGiUP1V3t02ZtvJRS+1jmTBB/v5y5xH2QSNR23omAQ5N Raspberry Pi keys for Ansible 357 | 358 | Now that you have your ssh keys ready, we are ready to copy the 359 | **public** key to the Rasbian image. The **private** key you should copy 360 | into your own *.ssh/* directory to your own home directory, on your 361 | computer. 362 | 363 | The changes we now need to make are all on the same image but in the 364 | other partition. We can use the same trick we did earlier and mount the 365 | other partition. Remember that we are looking for file system type 366 | *Linux* instead of *W95 FAT32 (LBA)*. So this commands will just mount 367 | the *other* partition for us, we are using the same mount point on our 368 | own computer*:* 369 | 370 | extracted_image="2017-06-21-raspbian-jessie.img" 371 | sdcard_mount="/mnt/sdcard" 372 | 373 | echo "Mounting the sdcard root disk" 374 | unit_size=$(fdisk --list --units "${extracted_image}" | awk '/^Units/ {print $(NF-1)}') 375 | start_boot=$( fdisk --list --units "${extracted_image}" | awk '/Linux/ {print $2}' ) 376 | offset_boot=$((${start_boot} * ${unit_size})) 377 | mount -o loop,offset="${offset_boot}" "${extracted_image}" "${sdcard_mount}" 378 | 379 | The second partition on the sdcard is now mounted allowing us to modify 380 | it easily. 381 | 382 | Normally you would use a tool called 383 | [*ssh-copy-id*](https://linux.die.net/man/1/ssh-copy-id) to copy the 384 | public keys to the raspberry pi, using username and password for login. 385 | It actually creates, or adds to, a special file called 386 | *authorized_keys* in a hidden *.ssh* directory of the users home 387 | directory, with very restricted file access permissions on the directory 388 | and file level. 389 | 390 | If we want to do this on the mounted image partition, we first need to 391 | create the directory, then change permissions to [octal 392 | 0700](http://www.perlfect.com/articles/chmod.shtml). This will allow the 393 | owner to, read, write, and execute (change into) the directory. 394 | 395 | # mkdir /mnt/sdcard/home/pi/.ssh 396 | # chmod 0700 /mnt/sdcard/home/pi/.ssh 397 | # chown 1000:1000 /mnt/sdcard/home/pi/.ssh 398 | # ls -aln /mnt/sdcard/home/pi/ | grep .ssh 399 | drwx------. 2 1000 1000 4096 Jul 12 13:55 .ssh 400 | 401 | Now that the directory is in place we can copy over the **public** key. 402 | I'm using the [cat](https://en.wikipedia.org/wiki/Cat_%28Unix%29) 403 | (from concatenate) command, to simulate what ssh-copy-id would do. We 404 | know the file is not there, so it will create it. If the file was there 405 | this command would add the new key to the end of it. 406 | 407 | # cat id_ed25519.pub >> /mnt/sdcard/home/pi/.ssh/authorized_keys 408 | 409 | And it is copied over. 410 | 411 | # cat /mnt/sdcard/home/pi/.ssh/authorized_keys 412 | ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGiUP1V3t02ZtvJRS+1jmTBB/v5y5xH2QSNR23omAQ5N Raspberry Pi keys for Ansible 413 | 414 | As you can imagine, ssh is very paranoid about it's settings. All the 415 | permissions on the directory and files are checked on each login. If any 416 | are missing or have the wrong access permissions set, it will not allow 417 | you to access the system. While this may be frustrating when you are 418 | troubleshooting, just remember it is for your own good. 419 | 420 | The [*chown*](https://en.wikipedia.org/wiki/Chown) command is used to 421 | change the owner of the file. You normally could use the username 422 | **pi**, but as we are setting this on a different computer, that user 423 | doesn't exist. To get around this we need to use the id number for the 424 | pi user and the pi group. 425 | 426 | # egrep "^pi" /mnt/sdcard/etc/passwd 427 | pi:x:1000:1000:,,,:/home/pi:/bin/bash 428 | # egrep "^pi" /mnt/sdcard/etc/group 429 | pi:x:1000: 430 | 431 | As we can see that is **1000** in both cases, so knowing this we can fix 432 | the ownership and access permissions. This time we use [octal 433 | 0600](http://www.perlfect.com/articles/chmod.shtml) which will not allow 434 | the file to execute. For a file, that means not allowing the file to run 435 | computer code. 436 | 437 | # chown 1000:1000 /mnt/sdcard/home/pi/.ssh/authorized_keys 438 | # chmod 0600 /mnt/sdcard/home/pi/.ssh/authorized_keys 439 | # ls -aln /mnt/sdcard/home/pi/.ssh/authorized_keys 440 | -rw-------. 1 1000 1000 111 Jul 12 13:55 /mnt/sdcard/home/pi/.ssh/authorized_keys 441 | 442 | ### Restricting Root and Requiring Keys on the Pi's ssh server 443 | 444 | I also want to make some changes to how the ssh server operates. 445 | [Getting started with SSH security and 446 | configuration](https://www.ibm.com/developerworks/aix/library/au-sshsecurity/index.html) 447 | is a good, if IBM specific, introduction to the topic. 448 | 449 | We want to prevent any login by the user *root* over ssh, and only allow 450 | login with ssh-keys. This means changing two settings in the 451 | configuration for the ssh server that will run on the Pi itself. It's 452 | the program that listens for connections from your PC. The man page for 453 | [sshd_config](https://linux.die.net/man/5/sshd_config) shows us the two 454 | changes we need to make. 455 | 456 | PermitRootLogin If this option is set to no, root is not allowed to log in (via ssh). 457 | PasswordAuthentication Specifies whether password authentication is allowed. The default is yes. 458 | 459 | We will use the [sed](https://en.wikipedia.org/wiki/Sed) command below. 460 | If this doesn't make any sense then see the complete guide to 461 | [sed](http://hackerpublicradio.org/series.php?id=90) by fellow Hacker 462 | Public Radio contributor [Dave 463 | Morriss](http://hackerpublicradio.org/correspondents.php?hostid=225). 464 | I'm using the '*;*' as a delimiter. 465 | 466 | sed -e 's;^#PasswordAuthentication.*$;PasswordAuthentication no;g' -e 's;^PermitRootLogin .*$;PermitRootLogin no;g' -i /mnt/sdcard/etc/ssh/sshd_config 467 | 468 | ### Change root and pi user password 469 | 470 | Normally you would use 471 | *[passwd](https://en.wikipedia.org/wiki/Passwd)* to change your 472 | password, but in our case we are directly editing the files where they 473 | are stored. Back in the day the passwords were kept in the */etc/passwd* 474 | file. However that has long since moved to */etc/shadow*. Here we can 475 | check the current settings for the *root* and *pi* user on the image. 476 | 477 | # egrep 'root|pi' /mnt/sdcard/etc/passwd 478 | root:x:0:0:root:/root:/bin/bash 479 | pi:x:1000:1000:,,,:/home/pi:/bin/bash 480 | 481 | The *:x:* in the [second 482 | column](https://www.cyberciti.biz/faq/understanding-etcpasswd-file-format/) 483 | shows that the "encrypted password is stored in 484 | [/etc/shadow](https://en.wikipedia.org/wiki/Passwd#Shadow_file) file". 485 | 486 | # egrep 'root|pi' /mnt/sdcard/etc/shadow 487 | root:*:17338:0:99999:7::: 488 | pi:$6$hio1BNCX$Qux8hGsSy.a.pEoI/TkcGGJlEJOdCAgcTtImxDugQVO1e.6cxgsQ4pFRL2cJvn9AjCZKX4RfOgupS2gQrFhrF/:17338:0:99999:7::: 489 | 490 | We are going to use a small python program which I found in the question 491 | "*[How to create an SHA-512 hashed password for 492 | shadow?](https://serverfault.com/questions/330069/how-to-create-an-sha-512-hashed-password-for-shadow)*". 493 | The answer by [davey](https://serverfault.com/users/5894/davey) gives a 494 | way to do this that we can use in our script. The example password I'm 495 | using is from [XKCD](https://www.xkcd.com/936/), but you should use 496 | something else because the password *correct horse battery staple* is 497 | now to be found in every list of passwords to try. 498 | 499 | ***Note:*** that your hash will be different to this: 500 | 501 | # root_password="$( python3 -c 'import crypt; print(crypt.crypt("correct horse battery staple", crypt.mksalt(crypt.METHOD_SHA512)))' )" 502 | # echo ${root_password} 503 | $6$Fx9Jh3AIHbOYrIOd$lsrwr1d2cVl0crybGGgZZclpEcVivPhA5N8LAhHQoKOOrM.tFQV/fmWpfdGzGsJVtaaYFARk1YKt/TVfZzMCC/ 504 | 505 | We also want to change the *pi* users password, also use a different one 506 | to the one here. 507 | 508 | # pi_password="$( python3 -c 'import crypt; print(crypt.crypt("wrong cart charger paperclip", crypt.mksalt(crypt.METHOD_SHA512)))' )" 509 | # echo ${pi_password} 510 | $6$u1n2Lx4GuxyfsEYS$FzbrDIRqXdlly191/74t8QkfQHUC.3mRwx/fu0C8J36m02PQZokEXfJGlgq2fydQ5a8s/185Mkb6MRdxWqdSF. 511 | 512 | Now that we have assigned the passwords to two variables we can use sed 513 | with the \`*\#*\` as a delimiter. This is because the traditional 514 | delimiter of forward slash, \`*/*\` is probably part of your password 515 | hash. 516 | 517 | The following command will replace the existing password field for root 518 | and the user pi. That is the (the *:\*:*  and the long string 519 | "*\$6\$hio1BNCX\$Qux8hGsSy.a.pEoI/TkcGGJlEJOdCAgcTtImxDugQVO1e.6cxgsQ4pFRL2cJvn9AjCZKX4RfOgupS2gQrFhrF/"* 520 | in the */etc/shadow* file. 521 | 522 | sed -i -e "s#^root:[^:]\+:#root:${root_password}:#" /mnt/sdcard/etc/shadow 523 | sed -i -e "s#^pi:[^:]\+:#pi:${pi_password}:#" /mnt/sdcard/etc/shadow 524 | 525 | And now we see they are changed 526 | 527 | # egrep 'root|pi' /mnt/sdcard/etc/shadow 528 | root:$6$Fx9Jh3AIHbOYrIOd$lsrwr1d2cVl0crybGGgZZclpEcVivPhA5N8LAhHQoKOOrM.tFQV/fmWpfdGzGsJVtaaYFARk1YKt/TVfZzMCC/:17338:0:99999:7::: 529 | pi:$6$u1n2Lx4GuxyfsEYS$FzbrDIRqXdlly191/74t8QkfQHUC.3mRwx/fu0C8J36m02PQZokEXfJGlgq2fydQ5a8s/185Mkb6MRdxWqdSF.:17338:0:99999:7::: 530 | 531 | ***WARNING*** Keep in mind that we are editing the files on the mounted 532 | sdcard and *not* the one on your own computer !!! 533 | 534 | ## Burn the image 535 | 536 | Now we need to unmount the image again. 537 | 538 | # umount /mnt/sdcard. 539 | 540 | Just so we don't get confused I'm going to rename both files 541 | 542 | # mv -v 2017-06-21-raspbian-jessie.img 2017-06-21-raspbian-jessie-ssh-enabled.img 543 | '2017-06-21-raspbian-jessie.img' -> '2017-06-21-raspbian-jessie-ssh-enabled.img' 544 | # mv -v 2017-06-21-raspbian-jessie-lite.img 2017-06-21-raspbian-jessie-lite-ssh-enabled.img 545 | '2017-06-21-raspbian-jessie-lite.img' -> '2017-06-21-raspbian-jessie-lite-ssh-enabled.img' 546 | 547 | Now we can see that the file size have remained the same but sha1sums 548 | have changed. This is exactly why you should always check the hash of 549 | files you download from the Internet. 550 | 551 | # ls -al 2017-06-21-raspbian-jessie*img 552 | -rw-r--r--. 1 user user 1304672023 Jul 5 17:42 2017-06-21-raspbian-jessie-lite-ssh-enabled.img 553 | -rw-r--r--. 1 user user 4659931113 Jul 5 17:41 2017-06-21-raspbian-jessie-ssh-enabled.img 554 | 555 | # sha1sum 2017-06-21-raspbian-jessie*img 556 | 41168af20116cbd607c7c14194a4523af1b48250 2017-06-21-raspbian-jessie-lite-ssh-enabled.img 557 | 5d075b28494f1be8f81b79f3b20f7e3011e00473 2017-06-21-raspbian-jessie-ssh-enabled.img 558 | 559 | You are now ready to use the modified image. To burn it you can run the 560 | *[lsblk](https://linux.die.net/man/8/lsblk)* command, insert the sdcard 561 | you want to overwrite with the new image, then use 562 | [lsblk](https://linux.die.net/man/8/lsblk) again to locate the new disk. 563 | 564 | # lsblk 565 | NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT 566 | sdb 8:16 1 7.4G 0 disk 567 | └─sdb1 8:17 1 7.4G 0 part /mnt/SANZA 568 | sda 8:0 0 238.5G 0 disk 569 | ├─sda2 8:2 0 500M 0 part /boot 570 | ├─sda3 8:3 0 237.8G 0 part 571 | │ └─luks-12321122-1234-1234-1234-123456767755 253:0 0 237.8G 0 crypt 572 | │ ├─fedora_root 253:1 0 230G 0 lvm / 573 | │ └─fedora_swap 253:2 0 7.8G 0 lvm [SWAP] 574 | └─sda1 8:1 0 200M 0 part /boot/efi 575 | mmcblk0 576 | 577 | In my case it came up mmcblk0, which translates to */dev/mmcblk0.* We 578 | can use [*dd*](https://en.wikipedia.org/wiki/Dd_(Unix)) command as you 579 | would 580 | [normally](https://www.raspberrypi.org/documentation/installation/installing-images/linux.md). 581 | 582 | # dd bs=4M status=progress if=2017-06-21-raspbian-jessie-ssh-enabled.img of=/dev/mmcblk0 583 | 584 | Once you are finished you can put the sdcard into a Raspberry Pi and 585 | turn it on. Once you find out it's IP Address you can ssh to it using 586 | your new keys. 587 | 588 | ssh -i id_ed25519 pi@192.168.1.5 589 | 590 | This will ask you for a password. The password is the one you set for 591 | your private ssh key above and not the password for the *pi* user. To 592 | avoid been asked every time we can simply use 593 | [ssh-agent](https://en.wikipedia.org/wiki/Ssh-agent) to manage our keys 594 | for us. Simply type the command *ssh-agent* and enter the password for 595 | your ssh key. If that works, you can then login without been asked for a 596 | password on any computer that has your **public** key listed in 597 | their *authorized_keys* file. 598 | 599 | That's it -- Live long and Prosper. 600 | -------------------------------------------------------------------------------- /ping-example-playbook.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Test Ping 3 | hosts: all 4 | tasks: 5 | - action: ping 6 | -------------------------------------------------------------------------------- /put-pi-in-ansible-host.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # MIT License 3 | # Copyright (c) 2017 Ken Fallon http://kenfallon.com 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | # Fix to ignore more networks. 24 | 25 | parent='all_pies' # The section where you group your RaspberryPi's 26 | 27 | ################################################## 28 | # Editing below this line should not be necessary 29 | 30 | if [ $(id | grep 'uid=0' | wc -l) -ne 1 ] 31 | then 32 | echo "Error: You need to be root" 33 | exit 1 34 | fi 35 | 36 | echo "[${parent}]" 37 | 38 | if [ $( which arp-scan 2>/dev/null | wc -l ) -eq 1 ] 39 | then 40 | ifconfig | awk -F ':' '/: flags/ {print $1}' | grep -vE 'lo|loop|docker|ppp|vmnet' | while read interface 41 | do 42 | arp-scan --quiet --interface ${interface} --localnet --numeric --ignoredups 2>/dev/null | grep -E "([0-9]{1,3}[\.]){3}[0-9]{1,3}.*([0-9A-Fa-f]{2}[:]){5}[0-9A-Fa-f]{2}" 43 | done 44 | else 45 | base=$(route -n | egrep '^0.0.0.0' | awk '{print $2}' | awk -F '.' '{print $1"."$2"."$3}') 46 | for node in {1..254} 47 | do ( ping -n -c 1 -W 1 ${base}.${node} >/dev/null 2>&1 & ) 48 | done 49 | arp -n | grep ether | awk '{print $1" "$3}' 50 | fi | grep -iE 'b8:27:eb|dc:a6:32|e4:5f:01' | sort | while read ip mac 51 | do 52 | host_name=$( awk '/nameserver/ {print $2}' /etc/resolv.conf | while read nameserver 53 | do 54 | host ${ip}c ${nameserver} 55 | done | grep pointer | head -1 | awk '{print $NF}' ) 56 | 57 | if [ -z "${host_name}" ] 58 | then 59 | host_name=$( grep ${ip} /etc/hosts | awk '{print $2}' | head -1) 60 | fi 61 | 62 | if [ -z "${host_name}" ] 63 | then 64 | host_name="$( echo ${mac} | sed 's/://g' )" 65 | fi 66 | 67 | echo "${host_name} ansible_host=${ip}" 68 | done 69 | -------------------------------------------------------------------------------- /userconf.txt_example: -------------------------------------------------------------------------------- 1 | CHANGEME:CHANGEME 2 | 3 | --------------------------------------------------------------------------------