├── .github └── workflows │ └── build_release_images.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── dependencies ├── archlinux │ ├── build-iso.sh │ ├── build.sh │ └── install.sh ├── debian │ ├── build-iso.sh │ ├── build.sh │ └── install.sh ├── fedora │ ├── build-iso.sh │ ├── build.sh │ └── install.sh ├── silverblue │ ├── build-iso.sh │ ├── build.sh │ └── install.sh └── ubuntu │ ├── build-iso.sh │ ├── build.sh │ └── install.sh ├── doc ├── overview.png └── rough_comparison_lio_vs_tgtd.png ├── install.sh ├── rootfs ├── etc │ ├── init.d │ │ ├── dhcpfallback │ │ ├── iperf3 │ │ └── pxe_menu │ └── uci-defaults │ │ ├── 10-permissions │ │ ├── 75-system │ │ ├── 80-network │ │ ├── 85-tgt │ │ ├── 90-dhcp │ │ ├── 90-firewall │ │ ├── 90-luci │ │ ├── 90-pxe_menu │ │ └── 95-uhttpd ├── srv │ └── pxe │ │ └── cgi-bin │ │ ├── get-menu-ipxe │ │ ├── iscsi-windows.ps1 │ │ ├── iscsistart.sh │ │ └── reload-menu-ipxe └── usr │ ├── lib │ └── opkg │ │ └── info │ │ ├── custom-disable-failsafe.postinst │ │ ├── custom-password.postinst │ │ └── custom-patches.postinst │ ├── libexec │ └── login.sh │ └── share │ └── patches │ └── 00-dnsmasq-init.patch ├── src ├── bootloaderspec-entry.conf ├── bootnet-networkd-unmanaged.network ├── bootnet-nm-unmanaged.service ├── cmdline.txt ├── create-boot-iso.sh ├── dracut.conf ├── grub-entry.sh ├── isbootifname ├── os-release └── tar2cpio.sh ├── test ├── README.md ├── archlinux │ ├── README │ ├── mk-vm-test-archlinux.sh │ ├── postinstall-in-vm.sh │ ├── run-in-vm.sh │ ├── test.sh │ ├── user_configuration.json │ └── user_disk_layout.json ├── common │ ├── create-isolated-network.sh │ ├── isolated-network.xml │ ├── mk-vm-test-initiator-uefi.sh │ └── mk-vm-test-initiator.sh ├── debian │ ├── mk-vm-test-target-debian.sh │ ├── preseed.cfg │ ├── run-in-vm.sh │ └── test.sh ├── fedora │ ├── fedora-minimal.ks │ ├── mk-vm-test-target-fedora.sh │ ├── run-in-vm.sh │ └── test.sh ├── freebsd │ └── mk-vm-test-target-freebsd.sh ├── qemu-pxe.sh ├── qemu.sh ├── silverblue │ ├── kickstart.ks │ ├── mk-vm-test-target-silverblue.sh │ ├── run-in-vm.sh │ └── test.sh ├── ubuntu │ ├── mk-vm-test-target-ubuntu.sh │ ├── notes │ ├── run-in-vm.sh │ └── test.sh ├── uefi-fedora │ ├── fedora-minimal.ks │ ├── mk-vm-test-target-uefi-fedora.sh │ ├── run-in-vm.sh │ └── test.sh └── win10 │ ├── mk-vm-test-initiator-uefi.sh │ └── mk-vm-test-target-win10.sh ├── uninstall.sh └── update.sh /.github/workflows/build_release_images.yml: -------------------------------------------------------------------------------- 1 | name: Build-Release-Images 2 | on: 3 | push: 4 | tags: 5 | - 'v*' 6 | workflow_dispatch: {} 7 | jobs: 8 | build-release-images: 9 | name: Build and Release Firmware Images 10 | runs-on: ubuntu-22.04 11 | steps: 12 | - name: Checkout code 13 | uses: actions/checkout@v2 14 | - name: Update APT 15 | run: 'sudo apt-get update' 16 | - name: Install dependencies 17 | run: 'sudo PKGMGR_OPTS="--yes" dependencies/debian/build.sh' 18 | - name: Build firmware images 19 | run: 'make images CURL_OPTS="-s"' 20 | - name: Install ISO dependencies 21 | run: 'sudo PKGMGR_OPTS="--yes" dependencies/debian/build-iso.sh' 22 | - name: Build ISO 23 | run: 'make iso' 24 | - name: Build EFI 25 | run: 'make efi' 26 | - name: Release 27 | uses: softprops/action-gh-release@v1 28 | with: 29 | body: | 30 | Automated release. Please see the project README.md for more information. 31 | 32 | [Build logs.](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) 33 | prerelease: True 34 | files: | 35 | build/images/* 36 | env: 37 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | rootfs/etc/dropbear/ 3 | .vscode 4 | *.swp 5 | /notes -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ALL_CURL_OPTS := $(CURL_OPTS) -L --fail 2 | 3 | #VERSION := 22.03-SNAPSHOT 4 | VERSION := 22.03.5 5 | BOARD := x86 6 | SUBTARGET := 64 7 | BUILDER := openwrt-imagebuilder-$(VERSION)-$(BOARD)-$(SUBTARGET).Linux-x86_64 8 | PROFILE := generic 9 | EXTRA_IMAGE_NAME := iscsi-target-ramdisk 10 | # Example WiFi support: "wpad-openssl kmod-iwlwifi iwlwifi-firmware-iwl8265" 11 | # Example Emulated device support: "kmod-veth wpad-openssl kmod-mac80211-hwsim" 12 | PACKAGES := luci tgt blkid lsblk iperf3 luci-app-commands atop tcpdump ethtool -libustream-wolfssl libustream-openssl luci-ssl-openssl 13 | 14 | BUILD_DIR := build 15 | OUTPUT_DIR := $(BUILD_DIR)/$(BUILDER)/bin/targets/$(BOARD)/$(SUBTARGET) 16 | 17 | 18 | all: images 19 | 20 | 21 | $(BUILD_DIR)/downloads: 22 | mkdir -p \ 23 | $(BUILD_DIR)/downloads.tmp \ 24 | $(BUILD_DIR)/downloads.tmp/ipxe/x86 \ 25 | $(BUILD_DIR)/downloads.tmp/ipxe/x86_64 26 | # OpenWrt Image Builder 27 | cd $(BUILD_DIR)/downloads.tmp \ 28 | && curl $(ALL_CURL_OPTS) -O https://downloads.openwrt.org/releases/$(VERSION)/targets/$(BOARD)/$(SUBTARGET)/$(BUILDER).tar.xz 29 | # iPXE 30 | cd $(BUILD_DIR)/downloads.tmp/ipxe/x86 \ 31 | && curl $(ALL_CURL_OPTS) -O https://boot.ipxe.org/ipxe.pxe \ 32 | && curl $(ALL_CURL_OPTS) -O https://boot.ipxe.org/undionly.kpxe 33 | cd $(BUILD_DIR)/downloads.tmp/ipxe/x86_64 \ 34 | && curl $(ALL_CURL_OPTS) -O https://boot.ipxe.org/ipxe.efi \ 35 | && curl $(ALL_CURL_OPTS) -O https://boot.ipxe.org/snponly.efi 36 | cd $(BUILD_DIR)/downloads.tmp/ipxe/ \ 37 | && curl $(ALL_CURL_OPTS) -O https://boot.ipxe.org/ipxe.iso \ 38 | && curl $(ALL_CURL_OPTS) -O https://boot.ipxe.org/ipxe.usb 39 | # ISOLINUX for ISO building 40 | cd $(BUILD_DIR)/downloads.tmp \ 41 | && curl $(ALL_CURL_OPTS) -O https://www.kernel.org/pub/linux/utils/boot/syslinux/syslinux-6.03.tar.gz \ 42 | && tar -xf syslinux-6.03.tar.gz 43 | mv $(BUILD_DIR)/downloads.tmp $(BUILD_DIR)/downloads 44 | 45 | 46 | rootfs-contents: $(BUILD_DIR)/downloads 47 | rm -rf $(BUILD_DIR)/rootfs 48 | cp -rv rootfs $(BUILD_DIR)/rootfs 49 | cp -f $(BUILD_DIR)/$(BUILDER)/target/linux/generic/other-files/init $(BUILD_DIR)/rootfs/ 50 | mkdir -p $(BUILD_DIR)/rootfs/srv/pxe/tftp/ipxe/x86 51 | cp -f $(BUILD_DIR)/downloads/ipxe/x86/ipxe.pxe $(BUILD_DIR)/rootfs/srv/pxe/tftp/ipxe/x86 52 | cp -f $(BUILD_DIR)/downloads/ipxe/x86/undionly.kpxe $(BUILD_DIR)/rootfs/srv/pxe/tftp/ipxe/x86 53 | mkdir -p $(BUILD_DIR)/rootfs/srv/pxe/tftp/ipxe/x86_64 54 | cp -f $(BUILD_DIR)/downloads/ipxe/x86_64/ipxe.efi $(BUILD_DIR)/rootfs/srv/pxe/tftp/ipxe/x86_64 55 | cp -f $(BUILD_DIR)/downloads/ipxe/x86_64/snponly.efi $(BUILD_DIR)/rootfs/srv/pxe/tftp/ipxe/x86_64 56 | 57 | 58 | $(BUILD_DIR)/$(BUILDER): $(BUILD_DIR)/downloads 59 | cd $(BUILD_DIR) && tar -xf downloads/$(BUILDER).tar.xz 60 | 61 | 62 | images: $(BUILD_DIR)/$(BUILDER) rootfs-contents 63 | cd $(BUILD_DIR)/$(BUILDER) && make image PROFILE="$(PROFILE)" EXTRA_IMAGE_NAME="$(EXTRA_IMAGE_NAME)" PACKAGES="$(PACKAGES)" FILES="../rootfs" 64 | cat $(OUTPUT_DIR)/sha256sums 65 | mkdir -p $(BUILD_DIR)/images 66 | cp $(OUTPUT_DIR)/openwrt-*-kernel.bin $(BUILD_DIR)/images/$(EXTRA_IMAGE_NAME)-kernel.bin 67 | # TODO: Build initramfs image with OpenWrt ImageBuilder built-in Makefile targets 68 | src/tar2cpio.sh $(OUTPUT_DIR)/openwrt-*-rootfs.tar.gz $(BUILD_DIR)/images/$(EXTRA_IMAGE_NAME)-initrd.img 69 | ls -hs $(BUILD_DIR)/images 70 | 71 | 72 | iso: 73 | echo "Generating ISO / USB boot image" 74 | src/create-boot-iso.sh \ 75 | $(BUILD_DIR)/images/$(EXTRA_IMAGE_NAME).iso \ 76 | "$(EXTRA_IMAGE_NAME)" \ 77 | $(BUILD_DIR)/downloads/syslinux-6.03 \ 78 | $(BUILD_DIR)/images/$(EXTRA_IMAGE_NAME)-kernel.bin \ 79 | $(BUILD_DIR)/images/$(EXTRA_IMAGE_NAME)-initrd.img \ 80 | "consoleblank=600" 81 | 82 | efi: 83 | objcopy \ 84 | --add-section .osrel=src/os-release --change-section-vma .osrel=0x20000 \ 85 | --add-section .cmdline=src/cmdline.txt --change-section-vma .cmdline=0x30000 \ 86 | --add-section .linux=build/images/iscsi-target-ramdisk-kernel.bin --change-section-vma .linux=0x2000000 \ 87 | --add-section .initrd=build/images/iscsi-target-ramdisk-initrd.img --change-section-vma .initrd=0x3000000 \ 88 | /usr/lib/systemd/boot/efi/linuxx64.efi.stub \ 89 | build/images/iscsi-target-ramdisk.efi 90 | 91 | 92 | keys: 93 | # Create persistent ssh host keys 94 | mkdir -p rootfs/etc/dropbear 95 | ssh-keygen -N "" -t rsa -b 2048 -f rootfs/etc/dropbear/dropbear_rsa_host_key 96 | ssh-keygen -N "" -t ed25519 -b 256 -f rootfs/etc/dropbear/dropbear_ed25519_host_key 97 | 98 | 99 | qemu: 100 | test/qemu.sh 101 | 102 | 103 | qemu-pxe: 104 | test/qemu-pxe.sh 105 | 106 | 107 | clean: 108 | rm -rf build 109 | 110 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # iscsi-target-ramdisk 2 | 3 | ## Overview 4 | 5 | This project adds a preconfigured x86_64 OpenWrt ramdisk image to your boot menu that automatically serves your Linux kernels via PXE and storage via iSCSI, allowing you to PXE boot your regular OS over the network on another computer using Dracut `netroot=iscsi:...`. Windows and FreeBSD iSCSI SAN boot are also supported. 6 | 7 | For example, you can run your laptop OS on your more powerful desktop while still having access to all your laptop's files and programs. 8 | 9 | ![Usage Diagram](doc/overview.png) 10 | 11 | You can also customize the OpenWrt ramdisk with any additional network configuration, files and packages you want. 12 | 13 | 14 | ## Quickstart 15 | 16 | (Using Fedora as an example): 17 | 18 | ``` 19 | git clone https://github.com/jwmullally/iscsi-target-ramdisk.git 20 | cd iscsi-target-ramdisk 21 | 22 | # Change boot_partition, boot_path and cmdline_default 23 | gedit rootfs/etc/uci-defaults/90-pxe_menu 24 | # Change tgt.1_1.device 25 | gedit rootfs/etc/uci-defaults/85-tgt 26 | 27 | sudo dependencies/fedora/build.sh 28 | make images 29 | sudo dependencies/fedora/install.sh 30 | sudo ./install.sh 31 | ``` 32 | 33 | 34 | ## Usage 35 | 36 | * Connect your two computers via Ethernet. This can be either a direct connection or through an Ethernet switch already providing DHCP. 37 | 38 | * On the computer where this is installed (target), power on and select `iSCSI Target Ramdisk` from the boot menu. 39 | 40 | * Power on the other computer (initiator), select the BIOS's built-in PXE boot. 41 | 42 | * Both UEFI and Legacy/CSM/BIOS boot are supported, depending on how your target OS is configured. 43 | 44 | * The target OS should now be running on the initiator. 45 | 46 | If you want to share network/WiFi connections or other services from the target to the initiator: 47 | 48 | * Connect to (or the DHCP assigned IP) to access the OpenWrt Admin UI and configure network routing / WiFi, etc. 49 | 50 | If the initiator computer doesn't have network ports, you can also boot via an [iSCSI-to-USB bridge like this](https://github.com/jwmullally/openwrt-rpi4-iscsi-to-usb-bridge). 51 | 52 | 53 | ## Requirements 54 | 55 | * Any modern Linux OS that supports dracut iSCSI. 56 | 57 | * Tested on: Debian 11, Ubuntu 22.04, Fedora 36, Fedora Silverblue 36 and Arch Linux 2022.06.01. 58 | 59 | * Also verified to work with: Windows 10, FreeBSD 13.1. 60 | 61 | * BIOS/UEFI PXE Boot. 62 | 63 | * Disable SecureBoot (see TODO below). 64 | 65 | 66 | ## Precautions 67 | 68 | Try this out first with the VM images in [`test`](test) to see how it works. 69 | 70 | *!! This solution makes slight changes to your `/boot` files. Before continuing, make sure you have a bootable LiveCD in case anything goes wrong.* 71 | 72 | *!! Currently there is NO ENCRYPTION for the iSCSI endpoint. See TODO below. For now, only run this on a trusted network with trusted hosts. Even if your main root partition is encrypted (e.g. with LUKS), MITM attacks are still be possible on the boot files.* 73 | 74 | While remote booting, treat disconnecting the network cable like unplugging your harddrive while your computer is running. The `iscsid` initiator with the default settings should be able to recover from disconnects and reboots of the target (you can experiment with this in the test VMs by the disabling/enabling ethernet links). Changing the settings of the network interface carrying the iSCSI traffic can also bring the interface down, so where we can we set it to unmanaged with permanent DHCP leases. 75 | 76 | 77 | ## Installation 78 | 79 | Download the latest release, or clone this repository and checkout the latest release tag. 80 | 81 | The only changes needed for your OS are to add Dracut iSCSI initiator support to your initramfs, and to create a boot entry for the `iSCSI Target Ramdisk` kernel and initramfs. The [`install.sh`](install.sh) script takes care of this. 82 | 83 | Review and adjust the configuration files in this project to match your system. 84 | 85 | You'll mainly just want to update: 86 | 87 | * [`rootfs/etc/uci-defaults/90-pxe_menu`](rootfs/etc/uci-defaults/90-pxe_menu) 88 | 89 | * `boot_partition` 90 | 91 | * Run `sudo blkid` and replace the UUID with the one from your partition containing `/boot`. 92 | 93 | * `boot_path` 94 | 95 | * Usually `/` if on its own partition, or `/boot/` if its on the main root filesystem. 96 | 97 | * `cmdline_default` 98 | 99 | * Find your default Kernel command line from `/etc/default/grub` or `/proc/cmdline`. Note, this is ignored when it can be read fully from the `/boot/loader/entries` files. 100 | 101 | * [`rootfs/etc/uci-defaults/85-tgt`](rootfs/etc/uci-defaults/85-tgt) 102 | 103 | * Update the list of of block devices to share as iSCSI LUNs. 104 | 105 | * [`rootfs/usr/lib/opkg/info/custom-password.postinst`](rootfs/usr/lib/opkg/info/custom-password.postinst) 106 | 107 | And review: 108 | 109 | * [`src/dracut.conf`](src/dracut.conf) 110 | 111 | * [`src/bootloaderspec-entry.conf`](src/bootloaderspec-entry.conf) 112 | 113 | * [`src/grub-entry.sh`](src/grub-entry.sh) 114 | 115 | * [`Makefile`](Makefile) (e.g. to adjust the [package selection](https://openwrt.org/packages/start) to support more network cards) 116 | 117 | Then proceed with the build and installation steps below. 118 | 119 | 120 | ### Debian 121 | 122 | ``` 123 | sudo dependencies/debian/build.sh 124 | make images 125 | sudo dependencies/debian/install.sh 126 | sudo ./install.sh 127 | ``` 128 | 129 | NOTE: This replaces [`initramfs-tools`](https://packages.debian.org/sid/initramfs-tools) with [`dracut`](https://packages.debian.org/sid/dracut) ([README.Debian](https://sources.debian.org/src/dracut/sid/debian/dracut-core.README.Debian/)). 130 | 131 | ### Ubuntu 132 | 133 | ``` 134 | sudo dependencies/ubuntu/build.sh 135 | make images 136 | sudo dependencies/ubuntu/install.sh 137 | sudo ./install.sh 138 | ``` 139 | 140 | NOTE: This replaces [`initramfs-tools`](https://packages.ubuntu.com/kinetic/initramfs-tools) with [`dracut`](https://packages.ubuntu.com/kinetic/dracut) ([README.Debian](https://sources.debian.org/src/dracut/sid/debian/dracut-core.README.Debian/)). 141 | 142 | ### Fedora 143 | 144 | ``` 145 | sudo dependencies/fedora/build.sh 146 | make images 147 | sudo dependencies/fedora/install.sh 148 | sudo ./install.sh 149 | ``` 150 | 151 | ### Fedora Silverblue 152 | 153 | ``` 154 | dependencies/silverblue/build.sh 155 | toolbox run --container iscsi-target-ramdisk-build make images 156 | sudo dependencies/silverblue/install.sh 157 | sudo ./install.sh 158 | ``` 159 | 160 | ### Arch 161 | 162 | ``` 163 | sudo dependencies/archlinux/build.sh 164 | make images 165 | sudo dependencies/archlinux/install.sh 166 | sudo ./install.sh 167 | ``` 168 | 169 | NOTE: This replaces [mkinitcpio](https://wiki.archlinux.org/title/mkinitcpio) with [Dracut](https://wiki.archlinux.org/title/Dracut). (See [here](https://wiki.archlinux.org/title/ISCSI/Boot) for instructions on using `mkinitcpio`). 170 | 171 | After the install, you will need to move the Dracut generated initramfs into place. This can be done automatically for future kernel installs with [`dracut-hook`](https://aur.archlinux.org/packages/dracut-hook). 172 | 173 | ``` 174 | mv /boot/initramfs-linux.img /boot/initramfs-backup-linux.img 175 | mv /boot/initramfs-$(uname -r).img /boot/initramfs-linux.img 176 | grub-mkconfig -o /boot/grub/grub.cfg 177 | ``` 178 | 179 | ## Installation on FreeBSD 180 | 181 | These instructions are for UEFI BIOS, MBR is TODO. 182 | 183 | Ensure you are using `/dev/gpt/*` labels in `/etc/fstab`, as device names will be different on different hosts. You can use `gpart modify -l` to set them. See [here](https://forums.freebsd.org/threads/labeling-partitions-done-right-on-modern-computers.69250/) for details on how to do this. 184 | 185 | Enable iSCSI Boot: 186 | ``` 187 | # pkg install net/isboot-kmod 188 | git clone https://github.com/jnielsendotnet/isboot.git 189 | cd isboot/src 190 | make 191 | make install 192 | cat > /boot/loader.conf.d/isboot.conf <) or GRUB. 334 | 335 | To remove the boot entry, do the following: 336 | ``` 337 | bcdedit /delete "{34e8383d-73a7-11e9-9cb0-94de8078a7b5}" 338 | ``` 339 | 340 | To remove the EFI image, do: 341 | ``` 342 | mountvol S: /s 343 | del S:\EFI\iscsi-target-ramdisk\iscsi-target-ramdisk.efi 344 | rmdir S:\EFI\iscsi-target-ramdisk 345 | mountvol S: /d 346 | ``` 347 | 348 | ### Configure the iSCSI Target 349 | 350 | To prepare an existing regular Windows 10 installation for iSCSI boot, a persistent iSCSI connection to the target must be configured. When Windows is booted normally from local storage, this iSCSI connection will stay disconnected and unused. 351 | 352 | * Temporarily boot the image/ISO/USB on another system connected directly via Ethernet. 353 | 354 | * (Or alternatively, boot the ISO on the same system as a Hyper-V VM): 355 | 356 | ``` 357 | New-VMSwitch -Name InternalSwitch -SwitchType Internal 358 | New-VM -Name iscsi-target-ramdisk-iso -MemoryStartupBytes 2GB -BootDevice CD -NewVHDPath "C:\Users\Public\Documents\Hyper-V\Virtual Hard Disks\iscsi-target-ramdisk.vhdx" -NewVHDSizeBytes 1GB -Generation 1 -Switch InternalSwitch 359 | Set-VMDvdDrive -VMName iscsi-target-ramdisk-iso -Path "$env:USERPROFILE\Downloads\iscsi-target-ramdisk.iso" 360 | 361 | Start-VM -Name iscsi-target-ramdisk-iso 362 | Restart-NetAdapter -Name "vEthernet (InternalSwitch)" 363 | # Interface should receive a 192.168.200.0/24 IP address 364 | Get-NetIPAddress | Where-Object {$_.InterfaceAlias -eq "vEthernet (InternalSwitch)"} 365 | ``` 366 | 367 | * It should start using the default static IP settings `192.168.200.1`. 368 | 369 | * This will be used to help verify all the iSCSI settings are correct in the `iSCSI Initiator` Windows UI app. 370 | 371 | * Ensure the iSCSI Target Service is running with `/etc/init.d/tgt show`. 372 | 373 | * Visit to enable iSCSI access through the firewall for your IP, or disable the iSCSI firewall rule with `uci set firewall.block_iscsi.enabled=0; /etc/init.d/firewall restart`. 374 | 375 | * Open the `iSCSI Initiator` app (`iscsicpl.exe`) and connect to the target, using the initiator settings from `/etc/config/tgt`: 376 | 377 | * Warning: This will overwrite your existing Windows iSCSI Initiator Name and Reverse CHAP Secret. To preserve them, set them first in `/etc/config/tgt` to match your existing Windows settings. 378 | 379 | * Initiator name: `target` -> ``. 380 | 381 | * iSCSI Initiator Mutual CHAP Secret: `user_out` -> ``. 382 | 383 | * Quick Connect Target: `192.168.200.1`. 384 | 385 | * Connect -> Advanced Options -> Enable CHAP log on: Name, Target secret = `"user_in"` -> ``, ``. 386 | 387 | * Perform mutual authentication: Enabled. 388 | 389 | * Add this connection to the list of Favorite Targets: Enabled. 390 | 391 | * Or alternatively, copy + paste the commands from into an Administrator Powershell), or use the following example: 392 | 393 | ```ps1 394 | Start-Service -Name MSiSCSI 395 | Set-Service -Name MSiSCSI -StartupType Automatic 396 | 397 | Set-InitiatorPort ` 398 | -NodeAddress (Get-InitiatorPort).NodeAddress ` 399 | -NewNodeAddress "iqn.2018-04.org.example:initiator-host" 400 | 401 | Set-IscsiChapSecret ` 402 | -ChapSecret "password5678" 403 | 404 | New-IscsiTargetPortal ` 405 | -TargetPortalAddress "192.168.200.1" 406 | 407 | Connect-IscsiTarget ` 408 | -TargetPortalAddress "192.168.200.1" ` 409 | -NodeAddress "iqn.2018-04.org.example:target-host" ` 410 | -AuthenticationType MUTUALCHAP ` 411 | -ChapUsername "iscsiuser_in" ` 412 | -ChapSecret "password1234" ` 413 | -IsPersistent $true 414 | 415 | Update-HostStorageCache 416 | Get-Disk 417 | ``` 418 | 419 | ### Install the initiator NIC driver and devices 420 | 421 | * Install the driver for the network card used by the initiator. This is complicated as it requires some other way of booting the target OS on the initiator, but only needs to be done once. This can be done in one of the following ways: 422 | 423 | * Attach the target storage directly to the initiator. 424 | 425 | * Boot the [iSCSI target as a USB Mass Storage device](https://github.com/jwmullally/openwrt-rpi4-iscsi-to-usb-bridge). 426 | 427 | * Boot `iscsi-target-ramdisk` on the target and a Linux Live CD on the initiator (e.g. Fedora or Ubuntu). Install `libvirtd` and `virt-manager`, attach the iSCSI drive using the script from , create a Win10 VM with the existing `/dev/sd*` iSCSI drive, add the network card as a "PCI Host Device", then boot Windows in the VM once to install the network driver. This might not work if the hardware is too dissimilar and other system drivers are needed. 428 | 429 | * Set your network card driver to start during early boot. (Seems not always necessary, first try PXE booting without this). 430 | 431 | * E.g. Device Manager -> Intel 82574L -> Driver Details: `e1i65x64.sys`: 432 | 433 | * Regedit: `[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\e1i65x64]`: `Start` = `0` 434 | 435 | * Repeat for each `ControlSet001`, `ControlSet002` etc. 436 | 437 | ### Disable the pagefile 438 | 439 | Disable the pagefile. Without doing this, you may encounter `IRQL_NOT_LESS_OR_EQUAL` STOP codes on network boot. 440 | 441 | * System -> Advanced System Properties -> Performance -> Pagefile: Disabled. 442 | 443 | * (Or alternatively in PowerShell): 444 | ```ps1 445 | $cs = gwmi Win32_ComputerSystem 446 | if ($cs.AutomaticManagedPagefile) { 447 | $cs.AutomaticManagedPagefile = $False 448 | $cs.Put() 449 | } 450 | $pg = gwmi win32_pagefilesetting 451 | if ($pg) { 452 | $pg.Delete() 453 | } 454 | ``` 455 | 456 | ### PXE booting Windows 457 | 458 | After preparing the system with the instructions above, you can now use PXE to iSCSI boot the Windows system. 459 | 460 | * On the same Windows system, boot the `iSCSI Target Ramdisk` image from UEFI, GRUB or USB. 461 | 462 | * On the initiator host, boot using PXE (BIOS/CSM/Legacy or UEFI depending on your Windows installation) and choose the `iBFT SAN boot` option. 463 | 464 | * Windows should now start running. 465 | 466 | 467 | ## Post Installation 468 | 469 | ### Updating 470 | 471 | After the initial install, this solution should work indefinitely even as you upgrade your kernels. 472 | 473 | If you want to make changes to the OpenWrt configuration, you will only need to update `/boot/iscsi-target-ramdisk-kernel.bin` and `/boot/iscsi-target-ramdisk-initrd.img` by doing the following: 474 | 475 | ``` 476 | make images 477 | sudo ./update.sh 478 | ``` 479 | 480 | ### Uninstalling 481 | 482 | ``` 483 | sudo /usr/local/sbin/uninstall-iscsi-target-ramdisk.sh 484 | ``` 485 | 486 | 487 | ### Disabling NetworkManager management of BOOTIF interface 488 | 489 | It's important that the BOOTIF interface stay running as the root filesystem can't perform read or writes when its down. This can cause problems when NetworkManager takes over the interface, and various events can cause it to be restarted (e.g. even the user refreshing the interface in the UI), which can lock the system. To prevent NetworkManager from managing the interface, [this service](src/bootnet-nm-unmanaged.service) is installed automatically. 490 | 491 | 492 | ## Troubleshooting 493 | 494 | On the OpenWrt target: 495 | 496 | * Check `/srv/pxe/pxe_menu/menu.ipxe` and `/srv/pxe/pxe_menu` contain the expected kernels. 497 | 498 | * Update `/etc/config/pxe_menu` and rerun `/etc/init.d/pxe_menu restart` to try find them again. 499 | 500 | * Use `logread -f` to keep an eye on the DHCP and TFTP requests. 501 | 502 | * Use `tcpdump port 81` to check for incoming PXE related HTTP requests (unfortunately OpenWrt uHTTPd does not support request logging). 503 | 504 | * Check the console or `dmesg` output for Ethernet interface corruption warnings (e.g. some `e1000e` models have flaky offloading that needs to be disabled with `ethtool`.) 505 | 506 | * To set breakpoints during initrd, change `/etc/config/pxe_menu` `cmdline_iscsi` to include `rd.shell` or `rd.break=...`. See `dracut.cmdline(7)` for the list of break points. 507 | 508 | On the initiator PXE boot menu: 509 | 510 | * Remove `quiet` from the kernel cmdline to see more debug output during boot. 511 | 512 | On the initiator: 513 | 514 | * Use `iscsiadm -m session -P 1 --print=3` to view the iSCSI connection status and parameters 515 | 516 | * For live updates during testing: `watch -n 0.2 iscsiadm -m session -P 1 --print=3` 517 | 518 | 519 | ## How it works 520 | 521 | On the target host (containing the OS to remote boot): 522 | 523 | * `iSCSI Target Ramdisk` boots with its own kernel and stateless initramfs. 524 | * [`/etc/init.d/pxe_menu`](rootfs/etc/init.d/pxe_menu) is run which discovers the OS kernel images from the `/boot` partition, copies them to `/srv/pxe/pxe_menu` and creates entries in `/srv/pxe/pxe_menu/menu.ipxe`. 525 | * Configuration: [`/etc/uci-defaults/90-pxe_menu`](rootfs/etc/uci-defaults/90-pxe_menu). 526 | * If `/boot/loader/entries` is found, all BootLoaderSpec files are parsed to identify kernel images and cmdline arguments. If not found, entries are created for all kernels matching `/boot/vmlinuz-*` along with their matching initramfs file and the `cmdline_default` arguments. 527 | * The contents of `cmdline_iscsi` are appended to the cmdline, which include the `netroot:iscsi:...` paramaters. 528 | * `/etc/init.d/tgt` starts which exports the disk block devices as iSCSI LUN targets. 529 | * Configuration: [`/etc/uci-defaults/85-tgt`](rootfs/etc/uci-defaults/85-tgt). 530 | * `/etc/init.d/network` starts, which sets the first LAN interface to DHCP by default. 531 | * Configuration: [`/etc/uci-defaults/80-network`](rootfs/etc/uci-defaults/80-network) 532 | * `/etc/init.d/dnsmasq` starts which provides PXE DHCP Proxy boot (to work alongside existing DHCP servers) and serves `/srv/pxe` via TFTP. Regular DHCP allocations are disabled by default. 533 | * Configuration: [`/etc/uci-defaults/90-dhcp`](rootfs/etc/uci-defaults/90-dhcp). 534 | * `/etc/init.d/uhttpd` starts and serves `/srv/pxe` via HTTP access. 535 | * Configuration: [`/etc/uci-defaults/95-uhttpd`](rootfs/etc/uci-defaults/95-uhttpd). 536 | * HTTP BASIC authentication is used to protect `/srv/pxe/pxe_menu` and `/srv/pxe/cgi-bin` containing the boot images and configuration. 537 | * The [`/etc/init.d/pxe_access`](rootfs/etc/init.d/pxe_access) service can be used to enable/disable access to these files. 538 | * [`/etc/init.d/dhcpfallback`](rootfs/etc/init.d/dhcpfallback) starts, which sets LAN to a static IP if no existing DHCP servers were found during the specified time frame. 539 | * If activated, `/etc/config/dhcp` is also changed from PXE DHCP Proxy mode back to regular DHCP server mode. 540 | 541 | On the initiator host (the one to run the OS on): 542 | 543 | * The BIOS starts PXE boot. 544 | * The PXE ROM requests and receives a DHCP boot response, pointing to the iPXE binary on TFTP. 545 | * If an existing DHCP server is present, that server will send a regular DHCP response, and the target host will send a separate PXE DHCP Proxy boot response containing just the boot information. 546 | * iPXE is downloaded and executed, which issues another DHCP request and fetches `/srv/pxe/ipxe/entry.ipxe` over TFTP. 547 | * The user enters a username / password which are used as the authorization for the PXE HTTP requests. 548 | * Caution: Boot files and iSCSI credentials can still be sniffed as they are transferred over the network. Only use on a physically secure network or direct connection. 549 | * iPXE chainloads [`/srv/pxe/cgi-bin/get-menu-ipxe`](rootfs/srv/pxe/cgi-bin/get-menu-ipxe) over HTTP. 550 | * iSCSI access for the requesting initiator host's IP address is allowed through the firewall. 551 | * `/srv/pxe/pxe_menu/menu.ipxe` is returned and executed by iPXE. 552 | * (Optional, default) The iSCSI target connection details are stored in the iBFT ACPI table. 553 | * The user selects a kernel to boot. 554 | * iPXE fetches the kernel and associated initramfs over HTTP. 555 | * iPXE launches the kernel using the included cmdline arguments, which contain the extra `netroot:iscsi:...` parameters. 556 | * The kernel starts, unpacks and launches the init process in the initramfs. 557 | * The Dracut modules are executed. 558 | * The dracut-network iSCSI module sees the `netroot:iscsi:...` arguments and uses them to start an Open iSCSI initiator connection to the `iSCSI Target Ramdisk` host. If successful, the iSCSI target LUN devices now appear as local block devices. 559 | * Booting continues as normal, mounting the root filesystem using the UUID and other regularly supplied cmdline arguments. 560 | * (NetworkManager) The BOOTIF interface is set to unmanaged with [this service](src/bootnet-nm-unmanaged.service) to prevent automatic reconfiguration 561 | * The target OS is now fully loaded on the initiator host. 562 | 563 | 564 | ## Background 565 | 566 | Typical Linux distributions use a simple boot loader (e.g. GRUB) to load the Linux Kernel and an [Initial ramdisk](https://en.wikipedia.org/wiki/Initial_ramdisk) mini root file system. The purpose of this root filesystem is to do everything necessary to prepare the storage block devices and mount the real root filesystem. This provides the OS with great flexibility about how the root filesystem is stored, for example on different types of network storage, logical volumes, RAID arrays, encrypted filesystems etc. All the configuration and complexity is handled by software in the Initial Ramdisk; all the kernel needs is a final mounted directory it can chroot, and continue the init boot sequence. 567 | 568 | Here we add the existing Dracut iSCSI initiator module to the OS's initramfs, which is designed for booting systems installed to remote iSCSI block devices. It stays deactivated and out of the way unless the `netroot:iscsi:...` kernel cmdline arguments are supplied. When activated, the disk's block devices show up on the system just as if they were locally attached. Modern Linux distributions use UUID-based partition identification in `/etc/fstab`, which makes mounting work deterministically regardless of the names of the underlying block devices (e.g. `/dev/sda`, `/dev/sdb` ordering can change based on the order drives or USB keys are inserted). In practice, this means you can do upgrades, kernel updates, bootloader changes, etc. as if you were doing them on the original computer. On modern systems with a 1GbE connection, you should get full 1Gb/s transfer speed and relatively low IOP latency. As modern Linux distributions are mostly plug and play, there should be little issue with your OS seeing a completely different set of hardware. 569 | 570 | To share the OS drives, we can't use the original OS itself, as only one system can be reading/writing the devices at a time (otherwise disk corruption would result), so instead we use a stateless ramdisk image built using the OpenWrt ImageBuilder. OpenWrt is a very flexible embedded system platform, specializing in network routing with a large number of packages available. This image has been configured to automatically share the drives with iSCSI and the kernel+initramfs files with PXE after booting. The separate OpenWrt system also means you don't have to reconfigure your OS to do all this sharing, and you can easily customize it further by adding more files and packages. 571 | 572 | 573 | ## FAQ 574 | 575 | * Q: What about support for Fibre Channel over Ethernet (FCoE)? 576 | * A: iPXE and Dracut support FCoE, but tgtd does not, so LIO would need to be included in OpenWrt to support this. However the benefits (if any) would be marginal and iSCSI is much easier to work with, e.g. ["FCoE vs iSCSI?"](https://arstechnica.com/civis/viewtopic.php?t=1245917). 577 | 578 | 579 | ## Developing 580 | 581 | Patches are welcome. 582 | 583 | Test with the sample VMs in [`test`](test) before opening a pull request. 584 | 585 | Match OpenWrt structure and conventions as much as possible. 586 | 587 | 588 | ## TODO 589 | 590 | * Debian: Tips for disabling default open-iscsi service by default during normal use to prevent error. 591 | 592 | * [MACSEC L2 encryption](https://developers.redhat.com/blog/2016/10/14/macsec-a-different-solution-to-encrypt-network-traffic/) or iSCSI + TLS. 593 | 594 | * SecureBoot. ([Unlikely?](https://forum.openwrt.org/t/x86-uefi-secure-boot-installation/115666)). Provide instructions for self-signed images with `mokutil`? 595 | 596 | * Sort "iSCSI Target Ramdisk" entry under OS entries in bootloader menu. 597 | 598 | * [OpenWrt tgtd](https://github.com/openwrt/packages/blob/master/net/tgt/files/tgt.init): Support CRC32 Header + Data digests. 599 | 600 | * [OpenWrt kernel](https://github.com/openwrt/openwrt/blob/master/package/kernel/linux/modules/block.mk): Add LIO iSCSI target support, use instead of TGT ([rough comparison](doc/rough_comparison_lio_vs_tgtd.png)). 601 | 602 | * Set custom DHCP user class to always fetch newest iPXE. 603 | 604 | * [dracut iscsi](https://github.com/dracutdevs/dracut/blob/master/modules.d/95iscsi/iscsiroot.sh): Re-add support for `rd.iscsi.param` when `rd.iscsi.firmware` is used (for setting `node.session.timeo.replacement_timeout=0` for iBFT). 605 | 606 | * Make procd scripts non-blocking. 607 | 608 | 609 | ## Reference 610 | 611 | [iPXE - Documentation](https://ipxe.org/docs). 612 | 613 | [bootup - System bootup process](https://www.freedesktop.org/software/systemd/man/bootup.html). 614 | 615 | [dracut.cmdline(7)](http://man7.org/linux/man-pages/man7/dracut.cmdline.7.html). 616 | 617 | [dracut.conf(5)](http://man7.org/linux/man-pages/man5/dracut.conf.5.html). 618 | 619 | [BootLoaderSpec](https://www.freedesktop.org/wiki/Specifications/BootLoaderSpec/). 620 | 621 | [systemd kernel-command-line(7)](https://www.freedesktop.org/software/systemd/man/kernel-command-line.html). 622 | 623 | [OpenWrt](https://openwrt.org/docs/start). 624 | 625 | [OpenWrt - UCI configuration](https://openwrt.org/docs/guide-user/base-system/uci). 626 | 627 | [OpenWrt - DHCP](https://openwrt.org/docs/guide-user/base-system/dhcp). 628 | 629 | [OpenWrt - iSCSI](https://openwrt.org/docs/guide-user/services/nas/iscsi). 630 | 631 | [OpenWrt - Image Builder](https://openwrt.org/docs/guide-user/additional-software/imagebuilder). 632 | 633 | [iSCSI Security](https://www.blackhat.com/presentations/bh-usa-05/bh-us-05-Dwivedi-update.pdf). 634 | 635 | 636 | ## Author 637 | 638 | Copyright (C) 2022 Joseph Mullally 639 | 640 | License: [GPLv2](./LICENCE.txt) 641 | 642 | Project: 643 | -------------------------------------------------------------------------------- /dependencies/archlinux/build-iso.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -ex 3 | 4 | # Disabled by default as syslinux modifies /boot 5 | #pacman --sync --needed --noconfirm \ 6 | # cdrkit \ 7 | # syslinux 8 | -------------------------------------------------------------------------------- /dependencies/archlinux/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -ex 3 | 4 | # https://openwrt.org/docs/guide-user/additional-software/imagebuilder#archmanjaro 5 | pacman --sync --needed $PKGMGR_OPTS \ 6 | base-devel \ 7 | gawk \ 8 | gettext \ 9 | git \ 10 | libxslt \ 11 | ncurses \ 12 | openssl \ 13 | python \ 14 | unzip \ 15 | wget \ 16 | zlib 17 | 18 | # Extra build dependencies 19 | pacman --sync --needed $PKGMGR_OPTS \ 20 | cpio 21 | -------------------------------------------------------------------------------- /dependencies/archlinux/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -ex 3 | 4 | # Dracut iSCSI target support 5 | pacman --sync --needed $PKGMGR_OPTS \ 6 | dracut \ 7 | open-iscsi 8 | 9 | pacman --remove $PKGMGR_OPTS \ 10 | mkinitcpio \ 11 | || echo "No package to remove" 12 | -------------------------------------------------------------------------------- /dependencies/debian/build-iso.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -ex 3 | 4 | apt-get install $PKGMGR_OPTS --no-install-recommends \ 5 | genisoimage \ 6 | syslinux-utils -------------------------------------------------------------------------------- /dependencies/debian/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -ex 3 | 4 | # https://openwrt.org/docs/guide-user/additional-software/imagebuilder#debianubuntu 5 | apt-get install $PKGMGR_OPTS --no-install-recommends \ 6 | build-essential \ 7 | file \ 8 | gawk \ 9 | gettext \ 10 | git \ 11 | libncurses5-dev \ 12 | libncursesw5-dev \ 13 | libssl-dev \ 14 | python3 \ 15 | rsync \ 16 | unzip \ 17 | wget \ 18 | xsltproc \ 19 | zlib1g-dev 20 | 21 | # Extra build dependencies 22 | apt-get install $PKGMGR_OPTS --no-install-recommends \ 23 | ca-certificates \ 24 | curl 25 | -------------------------------------------------------------------------------- /dependencies/debian/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -ex 3 | 4 | # Dracut iSCSI target support 5 | # On Debian, this replaces the default initramfs-tools 6 | apt-get install $PKGMGR_OPTS --no-install-recommends \ 7 | dracut \ 8 | dracut-network \ 9 | open-iscsi 10 | 11 | apt-get remove $PKGMGR_OPTS \ 12 | initramfs-tools \ 13 | initramfs-tools-core 14 | -------------------------------------------------------------------------------- /dependencies/fedora/build-iso.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -ex 3 | 4 | dnf install $PKGMGR_OPTS --setopt=install_weak_deps=False \ 5 | genisoimage \ 6 | syslinux 7 | -------------------------------------------------------------------------------- /dependencies/fedora/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -ex 3 | 4 | # https://openwrt.org/docs/guide-user/additional-software/imagebuilder#centosfedora 5 | dnf install $PKGMGR_OPTS --setopt=install_weak_deps=False \ 6 | @c-development \ 7 | @development-libs \ 8 | @development-tools \ 9 | gawk \ 10 | gettext \ 11 | git \ 12 | libxslt \ 13 | ncurses-devel \ 14 | openssl-devel \ 15 | perl-FindBin \ 16 | python3 \ 17 | wget \ 18 | which \ 19 | zlib-devel \ 20 | zlib-static 21 | 22 | # Extra build dependencies 23 | dnf install $PKGMGR_OPTS --setopt=install_weak_deps=False \ 24 | cpio \ 25 | curl 26 | -------------------------------------------------------------------------------- /dependencies/fedora/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -ex 3 | 4 | # Dracut iSCSI target support 5 | dnf install $PKGMGR_OPTS \ 6 | dracut-network \ 7 | iscsi-initiator-utils 8 | -------------------------------------------------------------------------------- /dependencies/silverblue/build-iso.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -ex 3 | 4 | toolbox create --distro fedora --release f36 iscsi-target-ramdisk-build || true 5 | toolbox run --container iscsi-target-ramdisk-build sudo dependencies/fedora/build-iso.sh -------------------------------------------------------------------------------- /dependencies/silverblue/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -ex 3 | 4 | toolbox create --distro fedora --release f36 iscsi-target-ramdisk-build || true 5 | toolbox run --container iscsi-target-ramdisk-build sudo dependencies/fedora/build.sh -------------------------------------------------------------------------------- /dependencies/silverblue/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -ex 3 | 4 | # Dracut iSCSI target support 5 | rpm-ostree install --idempotent \ 6 | dracut-network \ 7 | iscsi-initiator-utils 8 | -------------------------------------------------------------------------------- /dependencies/ubuntu/build-iso.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -ex 3 | 4 | apt-get install $PKGMGR_OPTS --no-install-recommends \ 5 | genisoimage \ 6 | syslinux-utils -------------------------------------------------------------------------------- /dependencies/ubuntu/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -ex 3 | 4 | # https://openwrt.org/docs/guide-user/additional-software/imagebuilder#debianubuntu 5 | apt-get install $PKGMGR_OPTS --no-install-recommends \ 6 | build-essential \ 7 | file \ 8 | gawk \ 9 | gettext \ 10 | git \ 11 | libncurses5-dev \ 12 | libncursesw5-dev \ 13 | libssl-dev \ 14 | python3 \ 15 | rsync \ 16 | unzip \ 17 | wget \ 18 | xsltproc \ 19 | zlib1g-dev 20 | 21 | # Extra build dependencies 22 | apt-get install -$PKGMGR_OPTS --no-install-recommends \ 23 | ca-certificates \ 24 | curl 25 | -------------------------------------------------------------------------------- /dependencies/ubuntu/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -ex 3 | 4 | # Dracut iSCSI target support 5 | # On Ubuntu, this replaces the default initramfs-tools 6 | apt-get install $PKGMGR_OPTS --no-install-recommends \ 7 | dracut \ 8 | dracut-network \ 9 | open-iscsi 10 | 11 | # TODO: Removing these removes ubuntu-server-minimal etc 12 | #apt-get remove $PKGMGR_OPTS \ 13 | # initramfs-tools \ 14 | # initramfs-tools-core 15 | -------------------------------------------------------------------------------- /doc/overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwmullally/iscsi-target-ramdisk/1ef6d1860a4bf67166d4efef42f1bd3480046dd6/doc/overview.png -------------------------------------------------------------------------------- /doc/rough_comparison_lio_vs_tgtd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwmullally/iscsi-target-ramdisk/1ef6d1860a4bf67166d4efef42f1bd3480046dd6/doc/rough_comparison_lio_vs_tgtd.png -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -ex 3 | 4 | HAS_BLS="$(test -d /boot/loader/entries && echo 1 || echo 0)" 5 | HAS_OSTREE="$(test -d /ostree && echo 1 || echo 0)" 6 | HAS_GRUB1="$(test -f /boot/grub/grub.cfg && echo 1 || echo 0)" 7 | HAS_GRUB2="$(test -f /boot/grub2/grub.cfg && echo 1 || echo 0)" 8 | HAS_SYSTEMD="$(test -f /usr/bin/systemctl && echo 1 || echo 0)" 9 | HAS_NETWORKD="$(systemctl --quiet is-enabled systemd-networkd && echo 1 || echo 0)" 10 | HAS_NM="$(test -f /usr/bin/nmcli && echo 1 || echo 0)" 11 | 12 | uninstall_previous() { 13 | if [ -f "/usr/local/sbin/uninstall-iscsi-target-ramdisk.sh" ]; then 14 | echo "Previous version detected, uninstalling before continuing..." 15 | /usr/local/sbin/uninstall-iscsi-target-ramdisk.sh 16 | echo "Previous version uninstalled, continuing with installation" 17 | fi 18 | } 19 | 20 | enable_dracut_iscsi() { 21 | echo "Enabling iSCSI Initiator support in Dracut initramfs" 22 | install -m 0644 -T src/dracut.conf /etc/dracut.conf.d/90-iscsi-target-ramdisk.conf 23 | if [ "$HAS_OSTREE" = "1" ]; then 24 | (rpm-ostree initramfs | grep -q "Initramfs regeneration: enabled") || rpm-ostree initramfs --enable 25 | else 26 | dracut --force 27 | fi 28 | } 29 | 30 | install_boot_entry() { 31 | echo "Installing iSCSI Target Ramdisk boot menu entry" 32 | if [ "$HAS_BLS" = "1" -a "$HAS_OSTREE" = "0" ]; then 33 | install -m 0644 -T src/bootloaderspec-entry.conf /boot/loader/entries/iscsi-target-ramdisk.conf 34 | else 35 | install -m 0755 -T src/grub-entry.sh /etc/grub.d/42_iscsi-target-ramdisk 36 | if [ "$HAS_GRUB1" = "1" ]; then 37 | grub-mkconfig -o /boot/grub/grub.cfg 38 | elif [ "$HAS_GRUB2" = "1" ]; then 39 | grub2-mkconfig -o /boot/grub2/grub.cfg 40 | fi 41 | fi 42 | } 43 | 44 | preserve_kernel_cmdline() { 45 | # Prevent new kernel installs from using iSCSI initiator /proc/cmdline 46 | # for regular bootloader entries. 47 | # Only installed with BootLoaderSpec scripts are in use. 48 | # See /usr/lib/kernel/install.d/90-loaderentry.install 49 | if [ "$HAS_BLS" = "1" -a "$HAS_OSTREE" = "0" -a ! -f /etc/kernel/cmdline -a ! -f /usr/lib/kernel/cmdline ]; then 50 | echo "Preserving current kernel cmdline for future boot loader entries" 51 | BOOT_OPTIONS="" 52 | for i in $(cat /proc/cmdline); do 53 | if [ "${i#initrd=*}" = "$i" -a "${i#BOOT_IMAGE=*}" = "$i" ]; then 54 | BOOT_OPTIONS="${BOOT_OPTIONS}${i} " 55 | fi 56 | done 57 | echo "$BOOT_OPTIONS" > /etc/kernel/cmdline 58 | fi 59 | } 60 | 61 | set_bootif_unmanaged() { 62 | if [ "$HAS_NETWORKD" = "1" ]; then 63 | echo "Setting iSCSI BOOTIF as unmanaged in systemd-networkd" 64 | install -m 0644 -T src/bootnet-networkd-unmanaged.network /etc/systemd/network/00-bootnet-unmanaged.network 65 | elif [ "$HAS_NM" = "1" -a "$HAS_SYSTEMD" = "1" ]; then 66 | echo "Installing script to set iSCSI BOOTIF as unmanaged in NetworkManager" 67 | # Post install 68 | install -m 0644 -T src/bootnet-nm-unmanaged.service /etc/systemd/system/bootnet-nm-unmanaged.service 69 | systemctl daemon-reload 70 | systemctl enable bootnet-nm-unmanaged 71 | fi 72 | } 73 | 74 | uninstall_previous 75 | enable_dracut_iscsi 76 | install_boot_entry 77 | preserve_kernel_cmdline 78 | set_bootif_unmanaged 79 | install -m 0755 -T uninstall.sh /usr/local/sbin/uninstall-iscsi-target-ramdisk.sh 80 | ./update.sh 81 | -------------------------------------------------------------------------------- /rootfs/etc/init.d/dhcpfallback: -------------------------------------------------------------------------------- 1 | #!/bin/sh /etc/rc.common 2 | 3 | START=95 4 | USE_PROCD=1 5 | 6 | 7 | wait_for_lan_ipaddr() { 8 | . /lib/functions/network.sh 9 | echo "Waiting for LAN IP address..." 10 | for loop_idx in $(seq 1 5); do 11 | network_flush_cache 12 | network_get_ipaddr LAN_IPADDR lan 13 | if [ -n "${LAN_IPADDR}" ]; then 14 | echo "LAN IP address found." 15 | return 0 16 | fi 17 | sleep 5 18 | done 19 | echo "LAN IP address timeout." 20 | return 1 21 | } 22 | 23 | start_service() { 24 | 25 | if wait_for_lan_ipaddr; then 26 | echo -e "\nLAN IP address [$(uci get network.lan.proto)]: ${LAN_IPADDR}\n" > /dev/console 27 | echo "LAN IP configuration complete, not falling back to static IP." 28 | return 0 29 | fi 30 | 31 | echo "LAN DHCP failed, falling back to static IP..." 32 | 33 | uci set network.lan.proto='static' 34 | uci set network.lan.ipaddr='192.168.200.1' # TODO: get this from config 35 | uci commit network 36 | 37 | uci set dhcp.lan.proxy='0' 38 | uci rename dhcp.@dnsmasq[0].pxe_prompt=_pxe_prompt 39 | uci rename dhcp.@dnsmasq[0].pxe_service=_pxe_service 40 | uci commit dhcp 41 | 42 | /etc/init.d/network restart 43 | /etc/init.d/dnsmasq restart 44 | 45 | echo -e "\nLAN IP address [static]: $(uci get network.lan.ipaddr)\n" > /dev/console 46 | } 47 | 48 | stop_service() { 49 | 50 | if [ "$(uci get network.lan.proto)" = 'dhcp' ]; then 51 | return 0 52 | fi 53 | 54 | echo "Removing LAN static IP and restoring DHCP configuration..." 55 | 56 | uci set network.lan.proto='dhcp' 57 | uci delete network.lan.ipaddr 58 | uci commit network 59 | 60 | uci set dhcp.lan.proxy='1' 61 | uci rename dhcp.@dnsmasq[0]._pxe_prompt=pxe_prompt 62 | uci rename dhcp.@dnsmasq[0]._pxe_service=pxe_service 63 | uci commit dhcp 64 | 65 | /etc/init.d/network restart 66 | /etc/init.d/dnsmasq restart 67 | 68 | } 69 | -------------------------------------------------------------------------------- /rootfs/etc/init.d/iperf3: -------------------------------------------------------------------------------- 1 | #!/bin/sh /etc/rc.common 2 | 3 | START=65 4 | STOP=85 5 | USE_PROCD=1 6 | 7 | start_service() { 8 | procd_open_instance 9 | procd_set_param command /usr/bin/iperf3 -s 10 | procd_set_param respawn 11 | procd_close_instance 12 | } -------------------------------------------------------------------------------- /rootfs/etc/init.d/pxe_menu: -------------------------------------------------------------------------------- 1 | #!/bin/sh /etc/rc.common 2 | 3 | START=99 4 | USE_PROCD=1 5 | 6 | 7 | wait_for_lan_ipaddr() { 8 | . /lib/functions/network.sh 9 | echo "Waiting for LAN IP address..." 10 | while true; do 11 | network_flush_cache 12 | network_get_ipaddr LAN_IPADDR lan 13 | if [ -n "${LAN_IPADDR}" ]; then 14 | echo "LAN IP address found." 15 | return 0 16 | fi 17 | sleep 5 18 | done 19 | } 20 | 21 | 22 | error_exit() { 23 | echo "Error: " "$*" 24 | exit 1 25 | } 26 | 27 | 28 | validate_pxe_menu_section() { 29 | uci_load_validate pxe_menu pxe_menu "$1" "$2" \ 30 | 'boot_partition:string' \ 31 | 'boot_path:string:/' \ 32 | 'ibft:bool:0' \ 33 | 'cmdline_default:string' \ 34 | 'cmdline_iscsi:string' \ 35 | 'cmdline_sed:string' \ 36 | 'menu_title:string:iSCSI Target Ramdisk PXE Boot Menu' \ 37 | 'menu_username:string' \ 38 | 'menu_password:string' 39 | } 40 | 41 | pxe_menu_config() { 42 | [ "$2" = 0 ] || { 43 | echo "validation failed" 44 | return 1 45 | } 46 | 47 | wait_for_lan_ipaddr 48 | 49 | cat > /srv/pxe/tftp/ipxe/entry.ipxe < "$IPXE_HEADER" < "$IPXE_MENU" < "$IPXE_CONTENTS" < /dev/null || true 114 | 115 | if [ -n "$boot_partition" ]; then 116 | mount -o ro "$boot_partition" /mnt || error_exit "Unable to mount boot_partition: $boot_partition" 117 | test -d "/mnt/$boot_path" || error_exit "Unable to find boot_path $boot_path on boot_partition $boot_partition" 118 | 119 | if [ -d "/mnt/$boot_path/loader/entries" ]; then 120 | echo "Searching for BootLoaderSpec files in /mnt/$boot_path/loader/entries ..." 121 | # Create an menu entry for every BootLoaderSpec file 122 | for entry_file in $(ls -t "/mnt/$boot_path/loader/entries"); do 123 | entry="/mnt/$boot_path/loader/entries/$entry_file" 124 | if [ ! -f "$entry" ]; then 125 | continue 126 | fi 127 | TITLE="$(sed -n 's/^title[ \t]*\(.*\)/\1/p' $entry)" 128 | KERNEL_PATH="$(sed -n 's/^linux[ \t]*\(.*\)/\1/p' $entry)" 129 | INITRD_PATH="$(sed -n 's/^initrd[ \t]*\([^ \t]*\).*/\1/p' $entry)" 130 | OPTIONS="$(sed -n 's/^options[ \t]*\(.*\)/\1/p' $entry | sed "$cmdline_sed")" 131 | if [ -n "$TITLE" -a -n "$KERNEL_PATH" -a -n "$INITRD_PATH" ]; then 132 | LABEL="$(basename "$entry")" 133 | mkdir -p "/srv/pxe/pxe_menu/$LABEL" 134 | KERNEL_FILE="$(basename "$KERNEL_PATH")" 135 | INITRD_FILE="$(basename "$INITRD_PATH")" 136 | KERNEL_IMG="pxe_menu/$LABEL/$KERNEL_FILE" 137 | INITRD_IMG="pxe_menu/$LABEL/$INITRD_FILE" 138 | cp "/mnt/$boot_path/$KERNEL_PATH" "/srv/pxe/$KERNEL_IMG" 139 | cp "/mnt/$boot_path/$INITRD_PATH" "/srv/pxe/$INITRD_IMG" 140 | chmod 644 "/srv/pxe/$KERNEL_IMG" 141 | chmod 644 "/srv/pxe/$INITRD_IMG" 142 | echo -e "item $LABEL $TITLE" >> "$IPXE_MENU" 143 | 144 | echo -e "" >> "$IPXE_CONTENTS" 145 | echo -e ":$LABEL" >> "$IPXE_CONTENTS" 146 | if [ "$ibft" = "1" ]; then 147 | echo -e "set return_from_ibft ${LABEL}_ibft_return" >> "$IPXE_CONTENTS" 148 | echo -e "goto ibft" >> "$IPXE_CONTENTS" 149 | echo -e ":${LABEL}_ibft_return" >> "$IPXE_CONTENTS" 150 | fi 151 | echo -e "kernel \${boot-url}$KERNEL_IMG" >> "$IPXE_CONTENTS" 152 | echo -e "initrd \${boot-url}$INITRD_IMG" >> "$IPXE_CONTENTS" 153 | echo -e "imgargs $KERNEL_FILE initrd=$INITRD_FILE $OPTIONS $cmdline_iscsi" >> "$IPXE_CONTENTS" 154 | echo -e "boot || goto failed">> "$IPXE_CONTENTS" 155 | echo -e "goto start" >> "$IPXE_CONTENTS" 156 | entries_found="$((entries_found+1))" 157 | fi 158 | done 159 | else 160 | echo "Searching for kernels in /mnt/$boot_path ..." 161 | for KERNEL_FILE in $(ls -t "/mnt/$boot_path" | grep 'vmlinuz-.*' | grep -v ".hmac$"); do 162 | KERNEL_VERSION="$(echo $KERNEL_FILE | sed 's/vmlinuz-//g')" 163 | # Find matching Initramfs image 164 | if [ -f "/mnt/$boot_path/initramfs-$KERNEL_VERSION.img" ]; then 165 | INITRD_FILE="initramfs-$KERNEL_VERSION.img" 166 | elif [ -f "/mnt/$boot_path/initrd.img-$KERNEL_VERSION" ]; then 167 | INITRD_FILE="initrd.img-$KERNEL_VERSION" 168 | else 169 | echo "Warning: could not find any initramfs image for PXE TFTP boot matching kernel $KERNEL_FILE, skipping..." 170 | continue 171 | fi 172 | LABEL="$KERNEL_FILE" 173 | mkdir -p "/srv/pxe/pxe_menu/$LABEL" 174 | KERNEL_IMG="pxe_menu/$LABEL/$KERNEL_FILE" 175 | INITRD_IMG="pxe_menu/$LABEL/$INITRD_FILE" 176 | cp "/mnt/$boot_path/$KERNEL_FILE" "/srv/pxe/$KERNEL_IMG" 177 | cp "/mnt/$boot_path/$INITRD_FILE" "/srv/pxe/$INITRD_IMG" 178 | chmod 644 "/srv/pxe/$KERNEL_IMG" 179 | chmod 644 "/srv/pxe/$INITRD_IMG" 180 | echo -e "item $LABEL $LABEL" >> "$IPXE_MENU" 181 | 182 | echo -e "" >> "$IPXE_CONTENTS" 183 | echo -e ":$LABEL" >> "$IPXE_CONTENTS" 184 | if [ "$ibft" = "1" ]; then 185 | echo -e "set return_from_ibft ${LABEL}_ibft_return" >> "$IPXE_CONTENTS" 186 | echo -e "goto ibft" >> "$IPXE_CONTENTS" 187 | echo -e ":${LABEL}_ibft_return" >> "$IPXE_CONTENTS" 188 | fi 189 | echo -e "kernel \${boot-url}$KERNEL_IMG" >> "$IPXE_CONTENTS" 190 | echo -e "initrd \${boot-url}$INITRD_IMG" >> "$IPXE_CONTENTS" 191 | echo -e "imgargs $KERNEL_FILE initrd=$INITRD_FILE $cmdline_default $cmdline_iscsi" >> "$IPXE_CONTENTS" 192 | echo -e "boot || goto failed">> "$IPXE_CONTENTS" 193 | echo -e "goto start" >> "$IPXE_CONTENTS" 194 | entries_found="$((entries_found+1))" 195 | done 196 | fi 197 | umount /mnt || error_exit "Unable to unmount boot partition: '$boot_partition'" 198 | fi 199 | 200 | if [ "$ibft" = "1" ]; then 201 | echo -e "" >> "$IPXE_CONTENTS" 202 | echo -e ":ibft" >> "$IPXE_CONTENTS" 203 | echo -e "set initiator-iqn $(uci get tgt.1.allow_name)" >> "$IPXE_CONTENTS" 204 | echo -e "set username $(uci get tgt.user_in.user)" >> "$IPXE_CONTENTS" 205 | echo -e "set password $(uci get tgt.user_in.password)" >> "$IPXE_CONTENTS" 206 | echo -e "set reverse-username $(uci get tgt.user_out.user)" >> "$IPXE_CONTENTS" 207 | echo -e "set reverse-password $(uci get tgt.user_out.password)" >> "$IPXE_CONTENTS" 208 | 209 | lun_idx=0 210 | drive_idx="$(printf "%i" "0x80")" 211 | while uci get tgt.@lun[$lun_idx] &> /dev/null ; do 212 | lun_idx=$((lun_idx+1)); 213 | target_uri="iscsi:\${target-server-ip}:::$lun_idx:$(uci get tgt.1.name)" 214 | drive="$(printf "%x" "$drive_idx")" 215 | drive_idx=$((drive_idx+1)); 216 | echo "sanhook --drive 0x$drive $target_uri || goto failed" >> "$IPXE_CONTENTS" 217 | done 218 | echo -e "goto \${return_from_ibft}" >> "$IPXE_CONTENTS" 219 | 220 | echo -e "item sanboot iBFT SAN boot" >> "$IPXE_MENU" 221 | echo -e "" >> "$IPXE_CONTENTS" 222 | echo -e ":sanboot" >> "$IPXE_CONTENTS" 223 | echo -e "set return_from_ibft sanboot_ibft_return" >> "$IPXE_CONTENTS" 224 | echo -e "goto ibft" >> "$IPXE_CONTENTS" 225 | echo -e ":sanboot_ibft_return" >> "$IPXE_CONTENTS" 226 | echo -e "sanboot || goto failed" >> "$IPXE_CONTENTS" 227 | echo -e "goto start" >> "$IPXE_CONTENTS" 228 | 229 | entries_found="$((entries_found+1))" 230 | fi 231 | 232 | cat \ 233 | "$IPXE_HEADER" \ 234 | "$IPXE_MENU" \ 235 | "$IPXE_CONTENTS" \ 236 | > /srv/pxe/pxe_menu/menu.ipxe 237 | rm -f /tmp/partial.ipxe.*.tmp 238 | 239 | echo "Found $entries_found boot entries" 240 | echo -e "\nFound $entries_found boot entries for PXE menu\n" > /dev/console 241 | } 242 | 243 | reload_service() { 244 | stop_service 245 | start_service 246 | } 247 | 248 | service_triggers() { 249 | procd_add_reload_trigger "pxe_menu" 250 | procd_add_validation validate_pxe_menu_section 251 | } 252 | 253 | start_service() { 254 | config_load pxe_menu 255 | config_foreach validate_pxe_menu_section pxe_menu pxe_menu_config 256 | } 257 | 258 | stop_service() { 259 | rm -f /srv/pxe/tftp/ipxe/entry.ipxe 260 | rm -rf /srv/pxe/pxe_menu 261 | } 262 | -------------------------------------------------------------------------------- /rootfs/etc/uci-defaults/10-permissions: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | chmod -R go+rx /srv 4 | -------------------------------------------------------------------------------- /rootfs/etc/uci-defaults/75-system: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | uci -q batch << EOF 5 | set system.@system[0].hostname='iscsi-target-ramdisk' 6 | set system.@system[0].ttylogin='1' 7 | commit system 8 | EOF 9 | -------------------------------------------------------------------------------- /rootfs/etc/uci-defaults/80-network: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | uci -q batch << EOF 5 | set network.@device[0].bridge_empty='1' 6 | 7 | # Static IP for LAN interface 8 | # 9 | #set network.lan.proto='static' 10 | #set network.lan.ipaddr='192.168.200.1' 11 | 12 | # DHCP for LAN interface 13 | # Will be set back to 'static' by /etc/init.d/dhcpfallback if no 14 | # existing DHCP server is found. 15 | 16 | set network.lan.proto='dhcp' 17 | del network.lan.ipaddr 18 | del network.lan.ip6assign 19 | 20 | commit network 21 | EOF 22 | -------------------------------------------------------------------------------- /rootfs/etc/uci-defaults/85-tgt: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | # Create tgt iSCSI targets for all specified drives. 5 | # Note these are your entire disks you are sharing, not individual partition devices. 6 | 7 | # For dynamic password generation every boot, use the following. However this 8 | # would also prevent initiator reconnection when the target reboots. 9 | # 10 | # option password "$(cat /dev/urandom | env LC_CTYPE=C tr -dc 023456789ABCDEF | head -c 16)" 11 | # 12 | 13 | uci import tgt < /etc/httpd.srv_pxe.conf < /dev/console 10 | uci -q delete firewall.block_iscsi.src_ip 11 | uci -q add_list firewall.block_iscsi.src_ip="!$REMOTE_ADDR" 12 | uci -q commit firewall 13 | /etc/init.d/firewall reload > /dev/null 2>&1 14 | 15 | cat /srv/pxe/pxe_menu/menu.ipxe 16 | exit 17 | fi 18 | echo "echo $(uci get system.@system[0].hostname): cgi-bin ERROR" 19 | -------------------------------------------------------------------------------- /rootfs/srv/pxe/cgi-bin/iscsi-windows.ps1: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "Content-Type: text/x-powershell" 5 | echo 6 | 7 | . /lib/functions/network.sh 8 | network_flush_cache 9 | network_get_ipaddr lan_addr "lan" 10 | 11 | cat < /dev/null 2>&1 5 | /srv/pxe/cgi-bin/get-menu-ipxe -------------------------------------------------------------------------------- /rootfs/usr/lib/opkg/info/custom-disable-failsafe.postinst: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | # Disable unauthenticated failsafe mode 5 | # Equivilant to CONFIG_TARGET_PREINIT_DISABLE_FAILSAFE=y 6 | 7 | if [ -n "${IPKG_INSTROOT}" ]; then 8 | sed -i -e 's/pi_preinit_no_failsafe=.*$/pi_preinit_no_failsafe="y"/' ${IPKG_INSTROOT}/lib/preinit/00_preinit.conf 9 | fi 10 | -------------------------------------------------------------------------------- /rootfs/usr/lib/opkg/info/custom-password.postinst: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | # Set root password to: pass1234 5 | # To generate a new one, use: mkpasswd --method=md5 --stdin 6 | 7 | if [ -n "${IPKG_INSTROOT}" ]; then 8 | sed -i -e 's,root:[^:]*:\(.*\)$,root:$1$8SUsPCvw$Ln8oo0kUYLOb4LbPR3Eup.:\1,' "${IPKG_INSTROOT}/etc/shadow" 9 | fi 10 | 11 | # See https://openwrt.org/docs/guide-user/security/dropbear.public-key.auth to use key-based auth 12 | -------------------------------------------------------------------------------- /rootfs/usr/lib/opkg/info/custom-patches.postinst: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | # Apply custom patches to filesystem during image build 5 | 6 | if [ -n "${IPKG_INSTROOT}" ]; then 7 | for p in $(ls "${IPKG_INSTROOT}/usr/share/patches/"); do 8 | patch_path="${IPKG_INSTROOT}/usr/share/patches/$p" 9 | patch --posix --no-backup-if-mismatch -d "${IPKG_INSTROOT}/" -p1 < "$patch_path" 10 | rm -f "$patch_path" 11 | done 12 | fi -------------------------------------------------------------------------------- /rootfs/usr/libexec/login.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Override default /usr/libexec/login.sh. 4 | # 5 | # Always require password login for local consoles. 6 | # It's difficult to set ttylogin=1 with uci-defaults before a local user can 7 | # "Please press Enter to activate this console." without authentication, so 8 | # instead we override the small login script. 9 | 10 | #[ "$(uci -q get system.@system[0].ttylogin)" = 1 ] || exec /bin/ash --login 11 | 12 | exec /bin/login 13 | -------------------------------------------------------------------------------- /rootfs/usr/share/patches/00-dnsmasq-init.patch: -------------------------------------------------------------------------------- 1 | dnsmasq: Support PXE Proxy DHCP option in --dhcp-range. 2 | 3 | --- a/etc/init.d/dnsmasq 2022-08-27 20:28:27.877031399 +0100 4 | +++ b/etc/init.d/dnsmasq 2022-08-27 20:57:56.378722678 +0100 5 | @@ -534,15 +534,17 @@ 6 | network_get_subnet subnet "$net" || return 0 7 | network_get_protocol proto "$net" || return 0 8 | 9 | + config_get_bool proxy "$cfg" proxy 0 10 | + 11 | # Do not support non-static interfaces for now 12 | - [ static = "$proto" ] || return 0 13 | + [ static = "$proto" -o "$proxy" = "1" ] || return 0 14 | 15 | # Override interface netmask with dhcp config if applicable 16 | config_get netmask "$cfg" netmask "${subnet##*/}" 17 | 18 | #check for an already active dhcp server on the interface, unless 'force' is set 19 | config_get_bool force "$cfg" force 0 20 | - [ $force -gt 0 ] || dhcp_check "$ifname" || { 21 | + [ $force -gt 0 -o "$proxy" = "1" ] || dhcp_check "$ifname" || { 22 | logger -t dnsmasq \ 23 | "found already running DHCP-server on interface '$ifname'" \ 24 | "refusing to start, use 'option force 1' to override" 25 | @@ -583,7 +585,11 @@ 26 | 27 | eval "$(ipcalc.sh "${subnet%%/*}" $netmask $start $limit)" 28 | 29 | - if [ "$dynamicdhcp" = "0" ] ; then 30 | + if [ "$proxy" = "1" ]; then 31 | + START="${subnet%%/*}" 32 | + END="proxy" 33 | + dhcp6range="::,proxy" 34 | + elif [ "$dynamicdhcp" = "0" ] ; then 35 | END="static" 36 | dhcp6range="::,static" 37 | else 38 | 39 | -------------------------------------------------------------------------------- /src/bootloaderspec-entry.conf: -------------------------------------------------------------------------------- 1 | # Prefix linux and initrd paths with /boot depending on partition setup 2 | # Other useful options: quiet 3 | title iSCSI Target Ramdisk 4 | linux /iscsi-target-ramdisk-kernel.bin 5 | initrd /iscsi-target-ramdisk-initrd.img 6 | options consoleblank=600 7 | -------------------------------------------------------------------------------- /src/bootnet-networkd-unmanaged.network: -------------------------------------------------------------------------------- 1 | [Match] 2 | Name=bootnet 3 | 4 | [Link] 5 | Unmanaged=yes 6 | -------------------------------------------------------------------------------- /src/bootnet-nm-unmanaged.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Set iSCSI bootnet to unmanaged in NetworkManager 3 | After=NetworkManager.service network.target 4 | ConditionPathExists=/sys/class/net/bootnet 5 | 6 | [Service] 7 | Type=oneshot 8 | # Wait until interface has finished any new DHCP requests before continuing 9 | ExecStartPre=/bin/sleep 10 10 | ExecStart=/usr/bin/nmcli device set bootnet managed no 11 | RemainAfterExit=yes 12 | 13 | [Install] 14 | WantedBy=multi-user.target -------------------------------------------------------------------------------- /src/cmdline.txt: -------------------------------------------------------------------------------- 1 | consoleblank=600 2 | -------------------------------------------------------------------------------- /src/create-boot-iso.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | if [ $# -ne 6 ] 5 | then 6 | echo "Create a hybrid CD/USB bootable Linux from Kernel and Initrd files." 7 | echo "usage: create-boot-iso " 8 | exit 1 9 | fi 10 | 11 | OUTPUT_ISO="$1" 12 | NAME="$2" 13 | SYSLINUX_DIR="$3" 14 | KERNEL="$4" 15 | INITRD="$5" 16 | OPTIONS="$6" 17 | 18 | ISOROOT="$(mktemp -d -t isoroot-XXXXXXXXXX)" 19 | trap 'rm -rf "$ISOROOT/isolinux; rmdir $ISOROOT"' EXIT 20 | 21 | mkdir "$ISOROOT/isolinux" 22 | cp -r \ 23 | "$SYSLINUX_DIR/bios/core/isolinux.bin" \ 24 | "$SYSLINUX_DIR/bios/com32/elflink/ldlinux/ldlinux.c32" \ 25 | "$SYSLINUX_DIR/bios/com32/menu/menu.c32" \ 26 | "$SYSLINUX_DIR/bios/com32/menu/vesamenu.c32" \ 27 | "$SYSLINUX_DIR/bios/com32/lib/libcom32.c32" \ 28 | "$SYSLINUX_DIR/bios/com32/libutil/libutil.c32" \ 29 | "$ISOROOT/isolinux" 30 | 31 | cat > "$ISOROOT/isolinux/isolinux.cfg" << EOF 32 | UI menu.c32 33 | #UI vesamenu.c32 34 | DEFAULT $NAME 35 | PROMPT 1 36 | TIMEOUT 50 37 | LABEL $NAME 38 | KERNEL vmlinuz 39 | APPEND initrd=initrd.img $OPTIONS 40 | EOF 41 | 42 | cp "$KERNEL" "$ISOROOT/isolinux/vmlinuz" 43 | cp "$INITRD" "$ISOROOT/isolinux/initrd.img" 44 | 45 | mkisofs \ 46 | -quiet \ 47 | -eltorito-boot isolinux/isolinux.bin \ 48 | -eltorito-catalog isolinux/boot.cat \ 49 | -no-emul-boot \ 50 | -boot-load-size 4 \ 51 | -boot-info-table \ 52 | -eltorito-alt-boot \ 53 | -output "$OUTPUT_ISO" \ 54 | "$ISOROOT" 55 | isohybrid "$OUTPUT_ISO" 56 | 57 | echo "ISO created successfully." 58 | -------------------------------------------------------------------------------- /src/dracut.conf: -------------------------------------------------------------------------------- 1 | # Add Open-iSCSI Initiator support 2 | add_dracutmodules+=" iscsi " 3 | 4 | # Disable host-specific cmdline generation. This is necessary to prevent 5 | # runtime iscsi initiator specific cmdline arguments from being baked into 6 | # the initramfs which would also be used by the original target machine. 7 | hostonly_cmdline="no" 8 | 9 | # hostonly="yes" autodetects and only includes modules used by your system. 10 | # hostonly="no" includes all boot-related network and storage modules to make 11 | # it run on any system. 12 | # 13 | # hostonly="no" will work portably on mosts hosts, but the downside is that it 14 | # includes lots of extra modules you will never use making the initramfs quite 15 | # large (50MB+), which can take up more space in /boot and slow down boot by a 16 | # few seconds. 17 | # 18 | # If you want hostonly="yes", you will need to change "add_drivers" to 19 | # include all network and storage modules needed by every system expected to 20 | # boot this kernel (i.e. both the target and the initiator), as during kernel 21 | # updates the initramfs could be regenerated on any one of those machines, 22 | # meaning autodetect will not include modules exclusive to the other systems 23 | # from the others unless they are manually specified here. 24 | # 25 | # Some distributions force hostonly="yes"(e.g. Arch Linux dracut-hook), in 26 | # those situations you will need to customize "add_drivers". 27 | hostonly="no" 28 | #add_drivers+=" e1000e " 29 | 30 | # Debian 11 fix 31 | install_optional_items+=" /usr/lib/open-iscsi/startup-checks.sh " 32 | -------------------------------------------------------------------------------- /src/grub-entry.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Prefix linux and initrd paths with /boot depending on partition setup 4 | # Other useful options: quiet 5 | 6 | cat <<'EOF' 7 | menuentry 'iSCSI Target Ramdisk' { 8 | load_video 9 | insmod gzio 10 | insmod part_gpt 11 | insmod ext2 12 | echo 'Loading Kernel ...' 13 | linux /iscsi-target-ramdisk-kernel.bin consoleblank=600 14 | echo 'Loading Initial Ramdisk ...' 15 | initrd /iscsi-target-ramdisk-initrd.img 16 | } 17 | EOF 18 | -------------------------------------------------------------------------------- /src/isbootifname: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # $FreeBSD$ 4 | # 5 | 6 | # PROVIDE: isbootifname 7 | # BEFORE: dhclient netif 8 | # KEYWORD: nojail 9 | 10 | . /etc/rc.subr 11 | 12 | name="isbootifname" 13 | rcvar="isbootifname_enable" 14 | 15 | start_cmd="${name}_start" 16 | stop_cmd="${name}_stop" 17 | 18 | load_rc_config $name 19 | : ${isbootifname_enable:=yes} 20 | 21 | isbootifname_start() 22 | { 23 | _isboot_nic="$(sysctl -bi net.isboot.nic)" 24 | if [ -n "$_isboot_nic" ]; then 25 | echo "${name}: Renaming interface ${_isboot_nic} to bootnet0" 26 | ifconfig "${_isboot_nic}" name "bootnet0" 27 | fi 28 | } 29 | 30 | isbootifname_stop() 31 | { 32 | _isboot_nic="$(sysctl -bi net.isboot.nic)" 33 | if [ -n "$_isboot_nic" ]; then 34 | echo "${name}: Renaming interface bootnet0 back to ${_isboot_nic}" 35 | ifconfig "bootnet0" name "${_isboot_nic}" 36 | fi 37 | } 38 | 39 | run_rc_command "$1" 40 | -------------------------------------------------------------------------------- /src/os-release: -------------------------------------------------------------------------------- 1 | NAME="iSCSI Target Ramdisk" 2 | ID=iscsi-target-ramdisk 3 | HOME_URL="https://github.com/jwmullally/iscsi-target-ramdisk" 4 | -------------------------------------------------------------------------------- /src/tar2cpio.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | if [ $# -ne 2 ] 5 | then 6 | echo "Convert a TAR rootfs archive to an initrd CPIO archive" 7 | echo "usage: tar2cpio input.tar output.cpio.gz" 8 | exit 1 9 | fi 10 | 11 | tmpdir="$(mktemp -d -t tar2cpio-XXXXXXXXXX)" 12 | trap 'rm -rf "$tmpdir"' EXIT 13 | 14 | tar -C "$tmpdir" -xf "$1" 15 | ( 16 | cd "$tmpdir"; 17 | find . | cpio -o -R root:root -H newc 18 | ) | gzip > "$2" 19 | 20 | echo "Successfully converted TAR '$1' to CPIO '$2'" -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | ## Testing 2 | 3 | ### Overview 4 | 5 | This folder contains scripts to generate minimal Linux VM images for different distributions, and installs this project in them. After installation, the VM's can be rebooted into the `iSCSI Target Ramdisk` boot entry, then the `test-initiator` VM can be started to test PXE boot to that machine. 6 | 7 | Hardcoded partition UUIDs are used so that we can set the configuration deterministically. 8 | 9 | Each VM has 2 network interfaces, one connected to the regular `NAT` libvirt network for external connectivity, and one connected to the `isolated` network. The `isolated` network has the host DHCP server disabled, so the test target VM can provide DHCP and PXE boot directly to the initiator VM without interference from the host's DHCP server. 10 | 11 | To test PXE DHCP Proxy mode when an existing DHCP server is present (in this case the built-in libvirt dnsmasq server), swap the `isolated` and `NAT` network connections on both the target and initiator VMs. 12 | 13 | ### Example workflow 14 | 15 | * `cd common` 16 | * `./create-isolated-network.sh` 17 | * `./mk-vm-test-initiator.sh` 18 | 19 | * `cd fedora` 20 | * `./mk-vm-test-target-fedora.sh` 21 | * `./test.sh` 22 | 23 | * Reboot `test-target-fedora` into the `iSCSI Target Ramdisk` entry 24 | 25 | * Boot `test-initiator` 26 | 27 | * The `test-target-fedora` OS should now be running on the `test-initiator` 28 | host. 29 | 30 | 31 | ### UEFI vs Legacy BIOS boot 32 | 33 | The example VMs use CSM boot by default. 34 | 35 | If you want to use UEFI for testing, do the following: 36 | 37 | * `mk-vm-test-*.sh`: 38 | 39 | * Add `--boot loader=/usr/share/edk2/ovmf/OVMF_CODE.fd,loader.readonly=yes,loader.type=pflash,nvram.template=/usr/share/edk2/ovmf/OVMF_VARS.fd,loader_secure=no` 40 | 41 | You may have to add EFI partitions in the kickstart/preseed files. 42 | -------------------------------------------------------------------------------- /test/archlinux/README: -------------------------------------------------------------------------------- 1 | Building the test VM 2 | 3 | Download and boot the Arch Linux install ISO: 4 | 5 | ./mk-vm-test-archlinux.sh 6 | 7 | In the VM: 8 | scp in this directory 9 | 10 | archinstall --config user_configuration.json --disk-layout user_disk_layout.json 11 | 12 | (set root password to "pass1234") 13 | 14 | ./postinstall-in-vm.sh 15 | 16 | -------------------------------------------------------------------------------- /test/archlinux/mk-vm-test-archlinux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -ex 3 | 4 | virt-install \ 5 | --connect qemu:///system \ 6 | --name test-target-archlinux \ 7 | --ram 2048 \ 8 | --vcpus 2 \ 9 | --arch x86_64 \ 10 | --os-variant archlinux \ 11 | --disk size=8,serial=abcd1234 \ 12 | --disk size=1,serial=eabc5678 \ 13 | --network network=isolated,mac=52:54:00:46:41:41 \ 14 | --network default,mac=52:54:00:46:41:42 \ 15 | --cdrom https://geo.mirror.pkgbuild.com/iso/latest/archlinux-x86_64.iso 16 | -------------------------------------------------------------------------------- /test/archlinux/postinstall-in-vm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | pacman --sync --needed --noconfirm \ 4 | openssh 5 | 6 | sed -i 's/^#*PermitRootLogin .*$/PermitRootLogin yes/g' /etc/ssh/sshd_config 7 | systemctl enable sshd 8 | systemctl start sshd 9 | 10 | tune2fs -O metadata_csum_seed -U b7b071ef-8c7f-480c-b8d5-a02fdae46f90 /dev/vda1 11 | tune2fs -O metadata_csum_Seed -U 5b6621d0-15ae-4c93-b9d6-f2a197a9ef06 /dev/vda2 12 | 13 | sed -i 's/.*--set=root.*//g' /boot/grub/grub.cfg 14 | sed -i 's/root=UUID=[^ ]*/root=UUID=5b6621d0-15ae-4c93-b9d6-f2a197a9ef06/g' /boot/grub/grub.cfg 15 | 16 | cat > /etc/fstab < 2 | isolated 3 | 4 | 5 | -------------------------------------------------------------------------------- /test/common/mk-vm-test-initiator-uefi.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -ex 3 | 4 | virt-install \ 5 | --connect qemu:///system \ 6 | --name test-initiator-uefi \ 7 | --ram 2048 \ 8 | --vcpus 2 \ 9 | --arch x86_64 \ 10 | --os-variant fedora36 \ 11 | --disk none \ 12 | --network network=isolated,mac=52:54:00:14:d6:9e \ 13 | --network default,mac=52:54:00:14:d6:9f \ 14 | --boot loader=/usr/share/edk2/ovmf/OVMF_CODE.fd,loader.readonly=yes,loader.type=pflash,nvram.template=/usr/share/edk2/ovmf/OVMF_VARS.fd,loader_secure=no 15 | --pxe 16 | -------------------------------------------------------------------------------- /test/common/mk-vm-test-initiator.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -ex 3 | 4 | virt-install \ 5 | --connect qemu:///system \ 6 | --name test-initiator \ 7 | --ram 2048 \ 8 | --vcpus 2 \ 9 | --arch x86_64 \ 10 | --os-variant fedora36 \ 11 | --disk none \ 12 | --network network=isolated,mac=52:54:00:14:d6:9c \ 13 | --network default,mac=52:54:00:14:d6:9d \ 14 | --pxe 15 | -------------------------------------------------------------------------------- /test/debian/mk-vm-test-target-debian.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -ex 3 | 4 | virt-install \ 5 | --connect qemu:///system \ 6 | --name test-target-debian \ 7 | --ram 2048 \ 8 | --vcpus 2 \ 9 | --arch x86_64 \ 10 | --os-variant debian11 \ 11 | --disk size=8,serial=abcd1234 \ 12 | --disk size=1,serial=eabc5678 \ 13 | --network network=isolated,mac=52:54:00:46:41:48 \ 14 | --network default,mac=52:54:00:46:41:49 \ 15 | --location https://deb.debian.org/debian/dists/bullseye/main/installer-amd64/ \ 16 | --initrd-inject=preseed.cfg 17 | -------------------------------------------------------------------------------- /test/debian/preseed.cfg: -------------------------------------------------------------------------------- 1 | #_preseed_V1 2 | 3 | # Preseed file for Debian 11 Bullseye - Minimal installation 4 | 5 | d-i debian-installer/locale string en_US 6 | d-i keyboard-configuration/xkb-keymap select us 7 | 8 | d-i netcfg/choose_interface select enp2s0 9 | d-i netcfg/get_hostname string test-target 10 | d-i netcfg/get_domain string localdomain 11 | d-i netcfg/hostname string test-target-debian 12 | d-i netcfg/wireless_wep string 13 | 14 | d-i hw-detect/load_firmware boolean false 15 | 16 | d-i mirror/country string manual 17 | d-i mirror/http/hostname string http.us.debian.org 18 | d-i mirror/http/directory string /debian 19 | d-i mirror/http/proxy string 20 | 21 | d-i passwd/make-user boolean false 22 | d-i passwd/root-password password pass1234 23 | d-i passwd/root-password-again password pass1234 24 | 25 | d-i clock-setup/utc boolean true 26 | d-i time/zone string Etc/UTC 27 | d-i clock-setup/ntp boolean false 28 | 29 | d-i partman-auto/disk string /dev/vda 30 | d-i partman-auto/method string lvm 31 | d-i partman-auto-lvm/guided_size string max 32 | d-i partman-lvm/device_remove_lvm boolean true 33 | d-i partman-md/device_remove_md boolean true 34 | d-i partman-lvm/confirm boolean true 35 | d-i partman-lvm/confirm_nooverwrite boolean true 36 | d-i partman-auto/choose_recipe select atomic 37 | d-i partman-partitioning/confirm_write_new_label boolean true 38 | d-i partman/choose_partition select finish 39 | d-i partman/confirm boolean true 40 | d-i partman/confirm_nooverwrite boolean true 41 | 42 | tasksel tasksel/first multiselect none 43 | d-i pkgsel/include string openssh-server 44 | d-i pkgsel/upgrade select none 45 | popularity-contest popularity-contest/participate boolean false 46 | 47 | d-i grub-installer/only_debian boolean true 48 | d-i grub-installer/with_other_os boolean true 49 | d-i grub-installer/bootdev string /dev/vda 50 | 51 | d-i preseed/late_command string \ 52 | sed -i 's/^.*PermitRootLogin.*/PermitRootLogin yes/g' /target/etc/ssh/sshd_config; \ 53 | tune2fs -O metadata_csum_seed -U 5b6621d0-15ae-4c93-b9d6-f2a197a9ef06 /dev/mapper/test--target--debian--vg-root; \ 54 | tune2fs -U b7b071ef-8c7f-480c-b8d5-a02fdae46f90 $(df -P /target/boot | tail -n 1 | cut -d' ' -f1); \ 55 | sed -i 's build/vm.tmp/vda.img || true 10 | qemu-img convert -f raw -O qcow2 build/vm.tmp/vda.img build/vm.tmp/vda.qcow2 11 | cp build/images/iscsi-target-ramdisk-kernel.bin build/vm.tmp/vdb/vmlinuz-1.2.3 12 | cp build/images/iscsi-target-ramdisk-initrd.img build/vm.tmp/vdb/initramfs-1.2.3.img 13 | mkfs.ext4 -d build/vm.tmp/vdb -U b7b071ef-8c7f-480c-b8d5-a02fdae46f90 build/vm.tmp/vdb.img 1G 14 | qemu-img convert -f raw -O qcow2 build/vm.tmp/vdb.img build/vm.tmp/vdb.qcow2 15 | rm -rf build/vm.tmp/vda.img build/vm.tmp/vdb build/vm.tmp/vdb.img 16 | mv build/vm.tmp build/vm 17 | fi 18 | 19 | exec qemu-system-x86_64 \ 20 | -nodefaults \ 21 | -smp 2 \ 22 | -m 256 \ 23 | -no-reboot \ 24 | -nographic \ 25 | -serial mon:stdio \ 26 | -kernel build/images/iscsi-target-ramdisk-kernel.bin \ 27 | -initrd build/images/iscsi-target-ramdisk-initrd.img \ 28 | -nic socket,model=virtio,mac=52:54:00:12:34:56,listen=:1234 \ 29 | -nic user,model=virtio,mac=52:54:00:12:34:57,hostfwd=tcp::30022-:22,hostfwd=tcp::30080-:80,hostfwd=tcp::30081-:81 \ 30 | -drive file=build/vm/vda.qcow2,format=qcow2,if=virtio \ 31 | -drive file=build/vm/vdb.qcow2,format=qcow2,if=virtio \ 32 | -append "console=ttyS0" 33 | 34 | -------------------------------------------------------------------------------- /test/silverblue/kickstart.ks: -------------------------------------------------------------------------------- 1 | text 2 | lang en_US.UTF-8 3 | keyboard us 4 | timezone Etc/UTC 5 | firewall --use-system-defaults 6 | services --enabled=sshd 7 | network --hostname test-target-silverblue --bootproto=dhcp --device=enp2s0 --activate 8 | rootpw --plaintext pass1234 9 | shutdown 10 | 11 | ignoredisk --only-use=vda 12 | zerombr 13 | clearpart --drives=disk/by-id/virtio-abcd1234 --initlabel --disklabel=gpt 14 | part biosboot --ondrive=disk/by-id/virtio-abcd1234 --fstype=biosboot --size=1 15 | part /boot --ondrive=disk/by-id/virtio-abcd1234 --fstype=ext4 --size=1024 --mkfsoptions="-U b7b071ef-8c7f-480c-b8d5-a02fdae46f90" 16 | part /boot/efi --ondrive=disk/by-id/virtio-abcd1234 --fstype=efi --size=256 17 | part btrfs.599 --fstype="btrfs" --ondisk=vda --grow --mkfsoptions="-U 5b6621d0-15ae-4c93-b9d6-f2a197a9ef06" 18 | btrfs none --label=fedora_fedora btrfs.599 19 | btrfs /home --subvol --name=home LABEL=fedora_fedora 20 | btrfs / --subvol --name=root LABEL=fedora_fedora 21 | btrfs /var --subvol --name=var LABEL=fedora_fedora 22 | 23 | ostreesetup --osname="fedora" --remote="fedora" --url="file:///ostree/repo" --ref="fedora/36/x86_64/silverblue" --nogpg 24 | 25 | skipx 26 | 27 | %post --erroronfail 28 | sed -i 's/^#*PermitRootLogin .*$/PermitRootLogin yes/g' /etc/ssh/sshd_config 29 | systemctl mask NetworkManager-wait-online.service 30 | systemctl set-default multi-user.target 31 | %end 32 | -------------------------------------------------------------------------------- /test/silverblue/mk-vm-test-target-silverblue.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -ex 3 | 4 | virt-install \ 5 | --connect qemu:///system \ 6 | --name test-target-silverblue \ 7 | --ram 8192 \ 8 | --vcpus 2 \ 9 | --arch x86_64 \ 10 | --os-variant fedora36 \ 11 | --disk size=20,serial=abcd1234 \ 12 | --disk size=1,serial=eabc5678 \ 13 | --network network=isolated,mac=52:54:00:46:41:44 \ 14 | --network default,mac=52:54:00:46:41:45 \ 15 | --location http://dl.fedoraproject.org/pub/fedora/linux/releases/36/Silverblue/x86_64/os/ \ 16 | --initrd-inject=kickstart.ks \ 17 | --extra-args "inst.ks=file:/kickstart.ks" 18 | -------------------------------------------------------------------------------- /test/silverblue/run-in-vm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | 4 | # Run this in the test VM 5 | 6 | toolbox create --distro fedora --release f36 iscsi-target-ramdisk-build || true 7 | toolbox run --container iscsi-target-ramdisk-build rm -rf /root/iscsi-target-ramdisk 8 | podman cp iscsi-target-ramdisk iscsi-target-ramdisk-build:/root/iscsi-target-ramdisk 9 | 10 | toolbox run --container iscsi-target-ramdisk-build sh -c 'PKGMGR_OPTS="--assumeyes" cd /root/iscsi-target-ramdisk && dependencies/fedora/build.sh' 11 | toolbox run --container iscsi-target-ramdisk-build sh -c 'cd /root/iscsi-target-ramdisk && make images' 12 | toolbox run --container iscsi-target-ramdisk-build sh -c 'PKGMGR_OPTS="--assumeyes" cd /root/iscsi-target-ramdisk && dependencies/fedora/build-iso.sh' 13 | toolbox run --container iscsi-target-ramdisk-build sh -c 'cd /root/iscsi-target-ramdisk && make iso' 14 | rm -rf iscsi-target-ramdisk 15 | podman cp iscsi-target-ramdisk-build:/root/iscsi-target-ramdisk iscsi-target-ramdisk 16 | 17 | cd iscsi-target-ramdisk 18 | dependencies/silverblue/install.sh 19 | ./install.sh 20 | -------------------------------------------------------------------------------- /test/silverblue/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | 4 | export SSHPASS=pass1234 5 | sshopts="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" 6 | target_ip="$(virsh --quiet -c qemu:///system domifaddr test-target-silverblue | tail -n1 | awk '{print $4}' | cut -d'/' -f1)" 7 | run_cmd="sshpass -e ssh $sshopts root@$target_ip" 8 | 9 | if [ "$1" = "shell" ]; then 10 | $run_cmd 11 | else 12 | sshpass -e rsync -e "ssh $sshopts" -a --exclude build ../.. "root@$target_ip:iscsi-target-ramdisk" 13 | 14 | $run_cmd "iscsi-target-ramdisk/test/silverblue/run-in-vm.sh" 15 | fi -------------------------------------------------------------------------------- /test/ubuntu/mk-vm-test-target-ubuntu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -ex 3 | 4 | virt-install \ 5 | --connect qemu:///system \ 6 | --name test-target-ubuntu \ 7 | --ram 2048 \ 8 | --vcpus 2 \ 9 | --arch x86_64 \ 10 | --os-variant ubuntu22.04 \ 11 | --disk size=8,serial=abcd1234 \ 12 | --disk size=1,serial=eabc5678 \ 13 | --network network=isolated,mac=52:54:00:46:41:52 \ 14 | --network default,mac=52:54:00:46:41:53 \ 15 | --cdrom /srv/iso/ubuntu-22.04.1-live-server-amd64.iso 16 | 17 | # --location http://archive.ubuntu.com/ubuntu/dists/jammy/main/installer-amd64/ 18 | -------------------------------------------------------------------------------- /test/ubuntu/notes: -------------------------------------------------------------------------------- 1 | systemctl disable systemd-networkd-wait-online 2 | systemctl mask systemd-networkd-wait-online 3 | 4 | TODO: cloud-init autoinstall 5 | -------------------------------------------------------------------------------- /test/ubuntu/run-in-vm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | 4 | # Run this in the test VM 5 | 6 | cd iscsi-target-ramdisk 7 | 8 | PKGMGR_OPTS="--yes" dependencies/ubuntu/build.sh 9 | make images 10 | PKGMGR_OPTS="--yes" dependencies/ubuntu/build-iso.sh 11 | make iso 12 | PKGMGR_OPTS="--yes" dependencies/ubuntu/install.sh 13 | ./install.sh 14 | -------------------------------------------------------------------------------- /test/ubuntu/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | 4 | export SSHPASS=pass1234 5 | sshopts="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" 6 | target_ip="$(virsh --quiet -c qemu:///system domifaddr test-target-ubuntu | tail -n1 | awk '{print $4}' | cut -d'/' -f1)" 7 | run_cmd="sshpass -e ssh $sshopts root@$target_ip" 8 | 9 | if [ "$1" = "shell" ]; then 10 | $run_cmd 11 | else 12 | $run_cmd apt-get install --yes rsync 13 | sshpass -e rsync -e "ssh $sshopts" -a --exclude build ../.. "root@$target_ip:iscsi-target-ramdisk" 14 | 15 | $run_cmd "iscsi-target-ramdisk/test/ubuntu/run-in-vm.sh" 16 | fi 17 | -------------------------------------------------------------------------------- /test/uefi-fedora/fedora-minimal.ks: -------------------------------------------------------------------------------- 1 | # Kickstart file to create minimal Fedora host 2 | # Tested with Fedora 36 3 | 4 | text 5 | lang en_US.UTF-8 6 | keyboard us 7 | timezone Etc/UTC 8 | selinux --enforcing 9 | firewall --enabled --service=mdns 10 | services --enabled=sshd,NetworkManager,chronyd 11 | network --hostname test-target-fedora --bootproto=dhcp --device=enp2s0 --activate 12 | rootpw --plaintext pass1234 13 | shutdown 14 | 15 | zerombr 16 | clearpart --drives=disk/by-id/virtio-abcd1234 --initlabel --disklabel=gpt 17 | part biosboot --ondrive=disk/by-id/virtio-abcd1234 --fstype=biosboot --size=1 18 | part /boot/efi --ondrive=disk/by-id/virtio-abcd1234 --fstype=efi --size=256 19 | part /boot --ondrive=disk/by-id/virtio-abcd1234 --fstype=ext4 --size=512 --mkfsoptions="-U b7b071ef-8c7f-480c-b8d5-a02fdae46f90" 20 | part / --ondrive=disk/by-id/virtio-abcd1234 --grow --fstype=ext4 --mkfsoptions="-U 5b6621d0-15ae-4c93-b9d6-f2a197a9ef06" 21 | 22 | %packages 23 | @core 24 | kernel 25 | %end 26 | 27 | %post --erroronfail 28 | sed -i 's/^#*PermitRootLogin .*$/PermitRootLogin yes/g' /etc/ssh/sshd_config 29 | systemctl mask NetworkManager-wait-online.service 30 | %end 31 | -------------------------------------------------------------------------------- /test/uefi-fedora/mk-vm-test-target-uefi-fedora.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -ex 3 | 4 | virt-install \ 5 | --connect qemu:///system \ 6 | --name test-target-uefi-fedora \ 7 | --ram 2048 \ 8 | --vcpus 2 \ 9 | --arch x86_64 \ 10 | --boot loader=/usr/share/edk2/ovmf/OVMF_CODE.fd,loader.readonly=yes,loader.type=pflash,nvram.template=/usr/share/edk2/ovmf/OVMF_VARS.fd,loader_secure=no \ 11 | --os-variant fedora36 \ 12 | --disk size=8,serial=abcd1234 \ 13 | --disk size=1,serial=eabc5678 \ 14 | --network network=isolated,mac=52:54:00:46:41:50 \ 15 | --network default,mac=52:54:00:46:41:51 \ 16 | --location http://dl.fedoraproject.org/pub/fedora/linux/releases/36/Everything/x86_64/os/ \ 17 | --initrd-inject=fedora-minimal.ks \ 18 | --extra-args "inst.ks=file:/fedora-minimal.ks" 19 | -------------------------------------------------------------------------------- /test/uefi-fedora/run-in-vm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | 4 | # Run this in the test VM 5 | 6 | cd iscsi-target-ramdisk 7 | 8 | PKGMGR_OPTS="--assumeyes" dependencies/fedora/build.sh 9 | make images 10 | PKGMGR_OPTS="--assumeyes" dependencies/fedora/build-iso.sh 11 | make iso 12 | PKGMGR_OPTS="--assumeyes" dependencies/fedora/install.sh 13 | ./install.sh 14 | -------------------------------------------------------------------------------- /test/uefi-fedora/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | 4 | export SSHPASS=pass1234 5 | sshopts="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" 6 | target_ip="$(virsh --quiet -c qemu:///system domifaddr test-target-uefi-fedora | tail -n1 | awk '{print $4}' | cut -d'/' -f1)" 7 | run_cmd="sshpass -e ssh $sshopts root@$target_ip" 8 | 9 | if [ "$1" = "shell" ]; then 10 | $run_cmd 11 | else 12 | $run_cmd dnf install --assumeyes make rsync 13 | sshpass -e rsync -e "ssh $sshopts" -a --exclude build ../.. "root@$target_ip:iscsi-target-ramdisk" 14 | 15 | $run_cmd "iscsi-target-ramdisk/test/fedora/run-in-vm.sh" 16 | fi 17 | -------------------------------------------------------------------------------- /test/win10/mk-vm-test-initiator-uefi.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -ex 3 | 4 | virt-install \ 5 | --connect qemu:///system \ 6 | --name test-initiator-win10 \ 7 | --ram 4096 \ 8 | --cpu Skylake-Client-noTSX-IBRS \ 9 | --vcpus 2 \ 10 | --arch x86_64 \ 11 | --os-variant win10 \ 12 | --disk none \ 13 | --network network=isolated,mac=52:54:00:16:d6:9e \ 14 | --network default,mac=52:54:00:16:d6:9f \ 15 | --boot loader=/usr/share/edk2/ovmf/OVMF_CODE.fd,loader.readonly=yes,loader.type=pflash,nvram.template=/usr/share/edk2/ovmf/OVMF_VARS.fd,loader_secure=no \ 16 | --pxe 17 | -------------------------------------------------------------------------------- /test/win10/mk-vm-test-target-win10.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -ex 3 | 4 | virt-install \ 5 | --connect qemu:///system \ 6 | --name test-target-win10 \ 7 | --ram 4096 \ 8 | --cpu Skylake-Client-noTSX-IBRS \ 9 | --vcpus 2 \ 10 | --arch x86_64 \ 11 | --os-variant win10 \ 12 | --disk size=20,serial=abcd1234 \ 13 | --disk size=1,serial=eabc5678 \ 14 | --network network=isolated,mac=52:54:00:47:41:50 \ 15 | --network default,mac=52:54:00:47:41:51 \ 16 | --boot loader=/usr/share/edk2/ovmf/OVMF_CODE.fd,loader.readonly=yes,loader.type=pflash,nvram.template=/usr/share/edk2/ovmf/OVMF_VARS.fd,loader_secure=no \ 17 | --cdrom /srv/iso/Win10_21H2_English_x64.iso 18 | -------------------------------------------------------------------------------- /uninstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -ex 3 | 4 | HAS_BLS="$(test -d /boot/loader/entries && echo 1 || echo 0)" 5 | HAS_OSTREE="$(test -d /ostree && echo 1 || echo 0)" 6 | HAS_GRUB1="$(test -f /boot/grub/grub.cfg && echo 1 || echo 0)" 7 | HAS_GRUB2="$(test -f /boot/grub2/grub.cfg && echo 1 || echo 0)" 8 | HAS_SYSTEMD="$(test -f /usr/bin/systemctl && echo 1 || echo 0)" 9 | HAS_NETWORKD="$(systemctl --quiet is-enabled systemd-networkd && echo 1 || echo 0)" 10 | HAS_NM="$(test -f /usr/bin/nmcli && echo 1 || echo 0)" 11 | 12 | disable_dracut_iscsi() { 13 | echo "Disabling iSCSI Initiator support in Dracut initramfs" 14 | rm -f /etc/dracut.conf.d/90-iscsi-target-ramdisk.conf 15 | if [ "$HAS_OSTREE" = "1" ]; then 16 | (rpm-ostree initramfs | grep -q "Initramfs regeneration: disabled") || rpm-ostree initramfs --disable 17 | else 18 | # We have just removed the dracut config containing the hostonly options. 19 | # If this uninstall script is being run on the initiator, hostonly mode 20 | # may result in initramfs that dont run on the original target, so to be 21 | # safe we use no-hostonly mode once to ensure working initramfs. 22 | dracut --force --no-hostonly --no-hostonly-cmdline 23 | fi 24 | } 25 | 26 | remove_boot_entry() { 27 | echo "Removing iSCSI Target Ramdisk boot menu entry" 28 | if [ "$HAS_BLS" = "1" -a "$HAS_OSTREE" = "0" ]; then 29 | rm -f /boot/loader/entries/iscsi-target-ramdisk.conf 30 | else 31 | rm -f /etc/grub.d/42_iscsi-target-ramdisk 32 | if [ "$HAS_GRUB1" = "1" ]; then 33 | grub-mkconfig -o /boot/grub/grub.cfg 34 | elif [ "$HAS_GRUB2" = "1" ]; then 35 | grub2-mkconfig -o /boot/grub2/grub.cfg 36 | fi 37 | fi 38 | } 39 | 40 | preserve_kernel_cmdline() { 41 | if [ -f /etc/kernel/cmdline ]; then 42 | echo "Custom /etc/kernel/cmdline detected, review manually and remove to go back to default cmdline detection (e.g. /etc/default/grub:GRUB_CMDLINE_LINUX)." 43 | fi 44 | } 45 | 46 | remove_bootif_unmanaged() { 47 | if [ "$HAS_NETWORKD" = "1" ]; then 48 | echo "Removing setting for iSCSI BOOTIF as unmanaged in systemd-networkd" 49 | rm -f /etc/systemd/network/00-bootnet-unmanaged.network 50 | elif [ "$HAS_NM" = "1" -a "$HAS_SYSTEMD" = "1" ]; then 51 | echo "Removing script to set iSCSI BOOTIF as unmanaged in NetworkManager..." 52 | systemctl disable bootnet-nm-unmanaged 53 | rm -f /etc/systemd/system/bootnet-nm-unmanaged.service 54 | fi 55 | } 56 | 57 | disable_dracut_iscsi 58 | remove_boot_entry 59 | preserve_kernel_cmdline 60 | remove_bootif_unmanaged 61 | rm -f /boot/iscsi-target-ramdisk-kernel.bin 62 | rm -f /boot/iscsi-target-ramdisk-initrd.bin 63 | rm -f /usr/local/sbin/uninstall-iscsi-target-ramdisk.sh 64 | -------------------------------------------------------------------------------- /update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -ex 3 | install -m 0755 build/images/iscsi-target-ramdisk-kernel.bin /boot/ 4 | install -m 0600 build/images/iscsi-target-ramdisk-initrd.img /boot/ 5 | --------------------------------------------------------------------------------