From e7bd00c1df74fed6aae7cd75f939a1920fd1e212 Mon Sep 17 00:00:00 2001 From: macmpi <16296055+macmpi@users.noreply.github.com> Date: Thu, 23 Nov 2023 22:13:27 +0100 Subject: [PATCH] version 1.2 New feature: auto-update apkovl file with latest release on master gadget: improved setup if several ports available and/or some unconnected wlan: only install & start wpa_supplicant if needed updated doc --- README.md | 15 +- headless.apkovl.tar.gz | Bin 7547 -> 8525 bytes headless.apkovl.tar.gz.sha512 | 1 + make.sh | 3 +- overlay/usr/local/bin/headless_bootstrap | 267 ++++++++++++++--------- sample_auto-updt | 10 + sample_unattended.sh | 7 +- 7 files changed, 189 insertions(+), 114 deletions(-) create mode 100644 headless.apkovl.tar.gz.sha512 create mode 100644 sample_auto-updt diff --git a/README.md b/README.md index 8427b02..f2c2f74 100644 --- a/README.md +++ b/README.md @@ -25,15 +25,18 @@ Extra files may be added next to `headless.apkovl.tar.gz` to customise boostrapp - `interfaces`[^3] (*optional*): define network interfaces at will, if defaults DCHP-based are not suitable. - `authorized_keys` (*optional*): provide client's public SSH key to secure `root` ssh login. - `ssh_host_*_key*` (*optional*): provide server's custom ssh keys to be injected (may be stored), instead of using bundled ones[^2] (not stored). Providing an empty key file will trigger new keys generation (ssh server may take longer to start). - - -**Goody:** seamless USB-serial & USB-ethernet gadget mode (PiZero for instance):\ -On supporting Pi devices, just add `dtoverlay=dwc2,dr_mode=peripheral` in `usercfg.txt` (or `config.txt`), and plug USB cable into host Computer port.\ -Serial terminal can then be connected-to from host Computer (e.g. `cu -l ttyACM0` on Linux. xon/xoff flow control).\ -Alternatively, with host Computer set-up to share networking with USB interface as 10.42.0.1 gateway, one can log into device from host with: `ssh root@10.42.0.2`. +- `auto-updt` (*optional*): allow apkovl file automatic update with latest from master branch. If it contains *reboot* keyword all in one line, system will reboot after succesful update (unless ssh session is active or `unattended.sh` script is available). Main execution steps are logged: `cat /var/log/messages | grep headless`. +## Goody: +Seamless USB-serial & USB-ethernet gadget mode (*e.g. PiZero*): +- Make sure dwc2/dwc3 driver is loaded accordingly, and device configuration is set to `peripheral` mode: this may be hardware (including cable) and/or software driven.\ +(on supporting Pi devices, just add `dtoverlay=dwc2,dr_mode=peripheral` in `usercfg.txt` (or `config.txt`) to force by software) +- Plug USB cable into host Computer port before boot.\ +Serial terminal can then be connected-to from host Computer (e.g. `cu -l ttyACM0` on Linux. xon/xoff flow control).\ +Alternatively, with host Computer set-up to share networking with USB interface as 10.42.0.1 gateway, one can log into device from host with: `ssh root@10.42.0.2`. + [^1]: Initial boot fully preserves system's original state (config files & installed packages): a fresh system will therefore come-up as unconfigured. [^2]: About bundled ssh keys: this overlay is meant to **quickly bootstrap** system in order to then proceed with proper install; therefore it purposely embeds [some ssh keys](https://github.com/macmpi/alpine-linux-headless-bootstrap/tree/main/overlay/tmp/.trash) so that bootstrapping is as fast as possible. Those temporary keys are moved in RAM /tmp: they will **not be stored/reused** once actual system install is performed (whether or not ssh server is installed in final setup). diff --git a/headless.apkovl.tar.gz b/headless.apkovl.tar.gz index 8a57b83d8c4d9a00aef5eeab6b579d268fb77a0a..56a53329bb17d8d6d6f2139ec0219cadb2c2e0e4 100644 GIT binary patch delta 7874 zcmZ|R<6qtnpvLiR-r396^1_{4#xj@fmb<@sakZ>v+qK-Xxoj-kywC4EJ5SE_==~2| zuh-}0(}W~I6^#P(zk`E6H9Tl2&`x>KxzX8b_Odc`Xc^AUJ}yI))(}Tg3* zpM-XOcJdQhr~F=aZteG`(n+)K4*#msdSJMI&~m=hpsIAKgfAg7$}G!TMC zvuU})cXERItyRU1=2rMnAlj*xFXsMWt#@I102qWOA0T_CHaY)#qS)kztPfRh`WwE= zlS2_7cj@H)k^W3Qf>GaHaa(FS2An@bap5Gg@1T)aasI1guf;xTLB&;P$B~yq+^?dq zs2BkYP{usYZe3m#L$=XTPP+%obpn`6`qGMuF-7W0|LV%YPgn&Dm(FMQm+b~PVd}TQ zzJJd<*|?)X&5pCZqxGgb@4MpiO*B82SvLgp+yWj(q}%nR)o!=m`$p22(cXQ(6~;Qq zZ`eRa#%zY~j)v7p*kEmQM+z5m#eQKhK*1*>iNQ|%gE!e+{rHE z9hJo90~9}8^aM&J0o@!~>HhNUH2}@Ob~I);o@iT_riwUiXa+FeF3{Q?-~QZmmRv*o zU*4&5`ogT=pzw!KuqCF$>%FiP%zKf)%gXX1)lw*+{nW$u8i)eU49%H`b^y2KZ62?x z1n}?IkJtlN_Xm#`M^{jlAjwtqxBFxR!d}KH=vS(KfG%8P>K-b83T3RjGYMAK(Xw0* z-Zoft?=6_@+FKp2K?fkWIP5MdpQsvF&i-B-;0Jj(Gy3o%-QCKaiSUv=5#($Ym?l~0T!b#aSwb$XCm0oA z-x=?zhO*C~bGuL{>aSP!yK(AtcQMeK@6aJ-MDS%vX-*tiA$K?i{Cxb^?ER(j=m;$M z{!n`K<>4y&N^a}m35vG^G4-QeXAuqAM$jYsco9z5zTxt$>O7ip&BZ;X}3X_>;|5-67#ljn~j_(jNL(NCnikpeJD3aKo~)9X<3i zh1_1~eBLbQ@+h}eqbW2gfGqsW+I{PH^kJ6u=*9vpR!&1o_yf1yMO-x4MEwEG#ZpPu z9=zTG)}8RpA;!-q8@;@PBAZArN*1f`N$J*AbsVGpz3ppmOHxxj_Vlz-y?77S^_P8rpi=eN8{KozB|QS7 z_c7;^)2Z~d&17f|*-OFGd(Ji#HO(7%%L%MIxa;0*hgvs$hdxkdudu_=`{HA|?+X`< z@bqyv_#ouK!ag>TAPK<&F(0Ps>*F#WE#giiyKs0zDN3J*i30hv=2NyKDOnMWOJb?X z+)D6hgUd#kYo5@33?JY|LUnvH%caUa7G&%0e1Bic4!be1YipDuOZRbwifpTFYY9gu zEgmjy@hNvNZ)!qKzj!tT_(UMO-`DbFBY`&ZhVYAYe7?4~j3w9s)L%QQ9bIjmKWvf? z{On_x+=+XW|E+{eb6kPR%2cqFqnvQ0!8b#S9hc~jhN_|@-5TR$>r<4BlX*nt^gB>SYkk1gg>cItp2K_z zZ1$;kOL)z+FmI!*-BcVY#-z9M+eZYA^~R;Y(Jj8LIpx1d0S=L55s#&m!+f!MPS%)* zC>akI{=6di78+5i+|ImQycjEQ60e3?5NnP?TNzRQN;GA0>dNw)F3F zuw+0=r8Yrm7!c&N9tL-a3kK(u!*HudS^JPQ-w~%={Z;lHDqAA9IhGHNT2WKnz;^@ck=V#kEw~H{uhvsnNTlk5}z~{CT*5OiXa@uFA^_XCj!?*pY$(F(djpvq~+MSlMFb zw92y9zRYnp`aMLnGNsdkd@|EUZ*lbNXCbTzUsn`K%8JayzJF`)9-8g`Q<9zVCJb!Y0VT7=5)*C^&8G6SYgA+o_^Bmw?{nSY#VkSKQgRFts*Y z0^}!oNhCb`%0YZ)Qfgf9m><`W{2!-got3_F;n=rnX?$uZBu$~RVR~um%v7kw&K-Ra zMY|;F7otA~m8eqO^s7t4DqIejNqJw!gbWvj-vP0_%i%P2CxTiv;>6|r_BN~8N-8;& z`e%83e8+fvuXj+zvi+%`U(L6&Dgr$v&AUG%E0NZ_7$|%eph5a>B@MCe zjQ21%EX*JyJt_@C3*);nyPd~~ONtA8&1BmN(6MUSUz>I={cr(WBZ6bB_>KY1fe=wG zS%8{CJ9D-p1(TbX*BpONoJ@MRde|@|17QXWF{cD0Bws2v=l3fc&%w#D*-Rvcro96@ z&$dkQ4>}{xg%y#4nbaiLHhwa<$mV8zHHV^f&#CWY#mhbh$=V#|DKnsLo4+F5G-0$z zj2KoT0t3ln7A=f*46~qfm?R9ttjtbkLIr@DhHn08Ljv&7DaQxH~*fz_fW3IfVG+e zO-1Lh$$^a3Jn|KG&%)Kp+(c>SF#}1*sk5_rue~}FRo;(V8`_EOn0=Lr%XKHnNA(AS zQP7#FraC}E)qY*J;3*30j`72OMnHe6LBc3tW@u0IscyByJs?#`}e zRhy|5QB<&wzPHZP9pTU7D15jY(Hbpm9y(8Iiciw{+@Rn7N_=B}(X`aq=wm)hzA9*1 zN$m!qJ`X?g9+U;UlY7hxv`5zaZ=Q%2cXl@+Wy67*g~%HHf?P_T$qt zZ}GpS^|dtaIkcG166@al6RLTDCZ4~PWLvyJiO!)6GY5B@C4ZoqZ|>dnuLtc1Z&1}3 zXaLc$Ga`T=@D9c9MtTQw{t0-3e}Kbt5$?91FK#ZNIiX#zJFxzE0p*S+vvw5?ZFyzR zsvX5+e)|*Xdj!vLFdHjc)N-S9c!J!}v6i;3u(c%sb>9b3W8O(C$?*&uRv21!pV zj2Lw7zN4m>^WcP}jSYT`hOAc*Cb}N+tTJ218=+#w`CriZcn&Z{ZxZNk!UL!UL(#*V zOoBMSvp()rbWa7m?hcsr@?T$^O|nlpxSzPs7+%A@&gJyHq}=kuKwnKT9}h0<0)dy+ z2d}&?vWScIRg|5l$HneRa`!L)BKCCV4f`JU-EsTr z4cg?@G@(qp3_Lg7v3^oOVqE@Que^n2)nI^uz|fAv1jpkQUCSYp;HYdNUyv%89S?=% zxVPQeku7CZwVBLo@#t!FXNw&OIkOL-xmWt%wjlQ{oO0_s-ZLMQbsd&0>}z$W1wgMy z>z{XowOzc$7qfZdLMLyK9l3nD2*>EqtZ7uw(vhEifEGycig($W&BKz>M$q2iF8c<- zR2^q-F#9392yL7UFTsa#xJRsk$v5Ttir#KoWQbBydNb|9DSMBfUudJZ>RO~_cXos zRBl39a3cO*btHn-j$uf<#zxru*hg?y5rZqJ2jwR7DUT``nfu7$yd?NO-U@OyZ|?;wI9#D9cp`5qSu~`Ix4# zxf+fw(>Zgwxg<;TyvntP)bNrmhtmKHE#y4Z`0B>`gy692$c@5RHxRv(NlAj1@Z6&6 z@5g4w+jF8im9!>~r*D6JttW=;e}x%012GBqJ$U>MXxvJRCGxn6z7o2rANdIuygFt} zM>@aar!mY(c7lNf>u>*e1jEFsp*4&z&X#^p?8{?Um0 z{x>cG)BFeo3NyMrJ0L%=8D*T+<=2gJ{fr|+2S0Ao66idaQjx&|;#$T`y=;t59`bn5seC(( zm4y9*Z+io$NvP(6JwcFCZ26&9*8_Mre9)eoT#OVGDYMKy5%cOWXs{TT*^hhL7e-_v zU23}D#A!gsH4dMvdksv@#f;yF*nOVkgRVBCq8*%|0@CakBUw#2mXlrqmQ=pL1o?@t zm>u<=UrLj*=O*7w(_7YIziIe$1;%tA4kCw8h?7*#Dr8wxF8wLizujPVr?TpK9zepe zc!vY=GCTATPA4Nx@V`3f*lbx#DLUBI+-=~tSYFg!FOEC0N!}4y{Ge|vXU45rW&o;c%Vl&GpXIjdstshmD;yco6*aM6Bui#z?W8xoGuEgBh zWaY`qk)+|JTmc*JUd)YL2pY{Uk|SdLMnx4=WTkNp+_Z#K`~5qRpsGUT+U#4hN0U7M z=O!>BL6re9l8|fg6rfEkz`@z>IS7L*_%7Gaq`)D9>#JDT{Z$Sd>4UxA!xqYV9%zI- zGE?q4JiX8?CkCt~pD0GwCT)%9qI>Ag_^C!=xg785)Ivt#^~7J7fd;#5CXcDCG4|vG zq61-K`Vp>a+Ik8ghS8^1WvuM0^z5dv2_t03kr`oHpAh)#cp;pZ@V@F~VA&8O>}Qo3 zF-9$;sY_3V-DCrHW;QaX|M+H@UOB;G>$BD3>b7mPldsY4`>}ab>~E0ymA{@Z-R>P7 zK3Gg_gCDEN{p1azBrl?P%qxsY)I_YW%?U$fr08P#qSwzL>kD3e6Qowk!@%|lJvfNB|Mq7%J|5{Vn zcO~94h~7Y+)gdWhQ7d66)&<0(+a4*t(Pp~g+2oK2@^l9$$6MhuUVU-@mk^s{(Kr$I zP9wmB=&=G6&ch&`zKO7M5U1j|CZL7DDoc*NQ}d&caZl9?QWy|=Ih%&**$v)2Uig5r zhEbLGFnF(LU&&U3j$@`b?bhtb#!BY(Q9o5#NKzb4WW++gX$k#WooI7=tL-OBiJ{SB zsY8kVxtVe#Q_u)h^&9Komi5fGa_xU+xSz;+gG~e$AiU%R@Yj^H!8fBHvnZzzOoX4?7VNWs+x(>)WCwXKtX z;aFusbF_a()w&NeG|%89?E`S{uk~*)1l8#8!yf`%bEEN`>h254iM3b~y>?0x>2_cc z;&Fk*n4Zqp8zw#e+;kTPCk)P<>%^LGcunogxMwm1qHmD|1Q59}eeg1eD}29~p`*VM zDG@&+o&`ooD_!_R1`GE}-Y@5HS6|Ep1$SgI+fNhlzhFK;aq}JHH`u$TG2Yv~9g_ac zhlCIsoRFc1SemE=i|E|z^d^#DrazMxP@4m^A4{ozHwFYF5(kC;Wp1Ns7eKSdz4H1@ z+PRI(;#Gf3zwL7xq091->sXip?5BU*#1^dl{F$(|aYxrd;;mjI!~rE{A2(P~5gi)u zEk4-Jvc8$6`MbbU6`Jy)y`(0++V04nt>>9X9Upx_>QQ{4s~8qBnsSug>Qs9YlNk>9 zFeNP^orjVw7oGN%NJUdpw#9cmB^;%;##YwVIA`q(vD~|d78A+>HjB4-p*6=+A#;*l zAllbV_Gar1Xi0GHMep0e3al(z;tw_wAI7U<#%HgP&LPP({dCa=Ck3_5az`%tz(=Ie z>^`{|Yx+?q$tAv_{TMUl+jN@DeP=cx=>uG2Z6RI)+(-<9t!|2sciBnoHd+@rRvJ3j z<9+?anZu#{w0lEssz-8FN=mRDiSx^hILs16_mr{a%$}@)d=>RiSa^qMAZ?y-?Tb90 z9#zePp3>0t9k8I}8{YFaD61jIF^6H2t}Q69h_h4jWv#F_PsJN7!aodiAYAtDyOUtOPD9#y;L*!iF@gJvrY9 zM@=q%qX(Vl7{w>q%RH3fnTI1nh#6fRoTj=``UmBw52C@IN(u8yekDK-nt)mFQq};8 zh)6Np`a6PtMYXv`8!DLbHZqrzL5%^! z&zL_o^(1>scJ?0s+XFEAzOO8Qcw9}_D(uCCuM&1-gvE-Nth>%L6bag}Q89xxW->mM z_7OLx>_K_d!HM>ae(Zid&N|Ik;||IRRxXY_keD;JR(Fsc8L!8)KHn4y=w@En= zpQ>iQdON)KVYv(I>4j_;Fsf#!-8kTuiq%|WU<^?!QmPN??g4|6jJs63qnWD$l$CU7 zjY)#Ena(4xq7X?D_$@@|p#5mob9E7P1hw?BFf;sIKUDJ~J7$UA^3RUzijH%^;fnGP z7n_R{WG`pG*jrL)u54BQY4>jnEXfbFz>n4G{uUaAB7IO>uf=$X2>5srS)o8W#(6m*d{Y8D*kgCO@Nh`AXl8+zaDbEf?e4#lEx>H2RjQ!)3s~eH_gvd zW!~>LmjzFc!1#=fG&G=TF!0^rB7Puiitv`{@~w1lev<Up?GJI8!)G?{~ zPlc2$m8ZayggG6)AP>!i?td`To zaUEOSoGqKjS;}&mC|akg1No91NM)s+KF=^4GD<$CFYWZhGEwW#D7mBdbX3;DB~MNs zPfsXw+&?x$H;P3P6_<=VaadaeqF_EU$% zAY^Gc^N-IRxkf~hbG|y<4=fpsyi5FPTTxs1vj{5?etw841>z91p3Z0{a-hgQhHE;t z-?sc8Ukg>C8_DlkogenqkI+4LZ2ibRlkR)%g^C{xCRIdbqMWdYdpC5xtB=l+>Y7n% zzs&;+OeqfDhP|KL|Is2LXJ`sm3rk z;q9yKWW0X0=b0=gwbdzTEkT+yL}Ki+aszBPYT8}AN4ZO|nHN=SK1MhH^)GJb*-0?k z$73*Ii@MFa>|9w|f_!+p9w=zakJ&oM#0fYeo}!M88ec6xjk;mF5%h+eW0tS`DUr)2 z-o!d<15v(0y1HV)Zdm&OjPBPUr;J{ohp#n{}oEGrV4!~?HtWg1G!!eVx8&nmG=G~)JagZUy}|{kOd4; zO9WMhT$1r@K_Iuerfd;zar#xgLNqQVh-BHslA`y|-h4Ga{?YO-*+axfGbG>g>zMv+unj1+RhFr z2P}>diD+${qX8IHa?ri@t23>`2jB_f_Pl@faJxT#J$spZ!3!v@+0S8tv6#u(zFh>qV@*NUtX<2@2n-in<@XXq*nik`27B>=1r8&wSkfZq{I46T&E=R z{S>k8Ggqe#u7-hUK*ng=c6x&8%kN4QwGcoO`3oid!!a|@u(SrV#1Ca1RUmpHENVXL z5>3M15&jF7Ng`al4t~|`=hYEie%-%u2Nw12X4!$M{r*blS&~*>M=@-#7u4H=f{XDA zrxE2`c8YQG{G6_-Y~jkaCnb1(8f1UBP{wl!W{}i2Rz}ze1_LLq*ssndd=Sb_DS4aJ z(?dQ`8D~!|5!8TQ6kKp}y@By?)Q~Xr?DTX}%G&3xF}L~8wQc(`(f7RXrrcsHj-t$XJaI`Il^+2dS*Qn6WL0OYprh|IzPZ>AxF`Y zroGj%PNDxTV@j1eKt229;}TO`xW+tYogs28&x%mYAe%RDrcxwkBn~j0Wh9W5HHvyL ztCj&MZwn=F6LqM$20Rreo*aD6bxLK7-L;RX=1f>oK3a^YQA%kdL3vmr6%967a`P#k zAP{njzeW>3HaLK^gu8P6>R?J<(}+BP!-0ZrYr0?8h|^GByY7m7AbxTKF3$nEPYAUG7l2VnpvCn17}7eB5=!%%>>7a1Ud|< za?z);#ZNa?e`&5x^_YfaDVIWP@kB_H46NTuC*6-%irw?~sgM#m^2f)%aguu#u^JsI e=(NoyS9|jz7MNTZnEwGQ1DLh| delta 6865 zcmV;?8ZPC{Li;*@ABzY8000000t4(^>9d^HmG@WkR}k5g#KlO>4y}|@12kJNG!66u zUsUY-(yR@h{O?1zWyO&k$4*Jl&iI{r#dgnK;CGgL&h7jB)?Iw@nF*eWB8bO%rVr1L zYXm1yf^_x$NKZ)bN0DqjMurEOP8UH82KQ7|-mACi}=|4gs)U*CSG5(Qndzs%f4>QoT zwOu_C{y2tzfIop@&-(u)^wl30jT0A(vhs`CeOY$}0DJ}N>gw`Rh%=untFey5s5Sgs z`x<%rCw8`df{Ed#j%~h1v0cdo`EMy?pUIpdZtJ zH}mbHt3FvD|5E=E^h5to;mEW8{~q-Jovi&crGHoa4=Ug0)#BBx+fDS5$UbZPr_led zu-n!zJl}gN|4)4A|L41B{r^4a|KDW|Z2b1UAprkeJ~-{{n~!Y5->ZCN7Jfv8pH0HQ z)c?GH^s2ga{Ld8w{_^+_#YpTq{`(d1f3HBH?S+2($}NlFv*&-XkK#WPn_a@Q{eKGk z|5W$^!B=NxgRc*ADjDgMLgsG>*4=tQ^61XDeC9rj}j<4 z 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 Llqqnk0C)fZ065BH diff --git a/headless.apkovl.tar.gz.sha512 b/headless.apkovl.tar.gz.sha512 new file mode 100644 index 0000000..2682b6c --- /dev/null +++ b/headless.apkovl.tar.gz.sha512 @@ -0,0 +1 @@ +003cb6812a4db690db522bb26de14f83d55e2c0b2a8d626a35c90bb9c6cce3181c605f6b51722cd54c712815952faf1072336eb932f80e683748869e27027afb headless.apkovl.tar.gz diff --git a/make.sh b/make.sh index ca10909..0297fb7 100755 --- a/make.sh +++ b/make.sh @@ -30,7 +30,8 @@ if [ -n "$build_path" ]; then doas tar cv -C "$build_path"/overlay --no-recursion \ $(doas find "$build_path"/overlay/ | sed "s|$build_path/overlay/||" | sort | xargs ) | \ gzip -c9n > headless.apkovl.tar.gz - TZ=UTC touch -cm -t "$t_stamp" headless.apkovl.tar.gz + sha512sum headless.apkovl.tar.gz > headless.apkovl.tar.gz.sha512 + TZ=UTC touch -cm -t "$t_stamp" headless.apkovl.tar.gz* doas rm -rf "$build_path" fi diff --git a/overlay/usr/local/bin/headless_bootstrap b/overlay/usr/local/bin/headless_bootstrap index 6ba5a96..8935156 100755 --- a/overlay/usr/local/bin/headless_bootstrap +++ b/overlay/usr/local/bin/headless_bootstrap @@ -3,7 +3,7 @@ # SPDX-FileCopyrightText: Copyright 2022-2023, macmpi # SPDX-License-Identifier: MIT -HDLSBSTRP_VERSION="1.1" +HDLSBSTRP_VERSION="1.2" _apk() { local cmd="$1" @@ -12,11 +12,11 @@ _apk() { case $cmd in add) # install only if not already present if ! apk info | grep -wq "${pkg}"; then - apk add "$pkg" && printf '%s ' "${pkg}" >> /tmp/.trash/installed + apk add "$pkg" && printf '%s ' "${pkg}" >>/tmp/.trash/installed fi ;; del) # delete only if previously installed - if grep -wq "$pkg" /tmp/.trash/installed > /dev/null 2>&1; then + if grep -wq "$pkg" /tmp/.trash/installed >/dev/null 2>&1; then apk del "$pkg" && sed -i 's/\b'"${pkg}"'\b//' /tmp/.trash/installed fi ;; @@ -27,14 +27,14 @@ _apk() { } _preserve() { -# create a back-up of element (file, folder, symlink) +# Create a back-up of element (file, folder, symlink). [ -z "${1}" ] && return 1 [ -e "${1}" ] && cp -a "${1}" "${1}".orig } _restore() { -# remove element (file, folder, symlink) and replace by -# previous back-up if available +# Remove element (file, folder, symlink) and replace by +# previous back-up if available. [ -z "${1}" ] && return 1 rm -rf "${1}" [ -e "${1}".orig ] && mv -f "${1}".orig "${1}" @@ -46,11 +46,11 @@ alias _logger='logger -st "${0##*/}"' ##### End of part to be duplicated into headless_cleanup (do not alter!) _prep_cleanup() { -## Prep for final headless_cleanup -# clears any installed packages and settings -# copy begininng of this file to keep functions -sed -n '/^#* End .*alter!)$/q;p' /usr/local/bin/headless_bootstrap > /tmp/.trash/headless_cleanup -cat <<-EOF >> /tmp/.trash/headless_cleanup +## Prep for final headless_cleanup: +# clears any installed packages and settings. +# Copy begininng of this file to keep functions. +sed -n '/^#* End .*alter!)$/q;p' /usr/local/bin/headless_bootstrap >/tmp/.trash/headless_cleanup +cat <<-EOF >>/tmp/.trash/headless_cleanup # Redirect stdout and errors to console as service won't show messages exec 1>/dev/console 2>&1 @@ -64,44 +64,45 @@ cat <<-EOF >> /tmp/.trash/headless_cleanup _restore "/etc/hostname" rm -f /etc/modprobe.d/headless_gadget.conf - # remove from boot service to avoid spurious openrc recalls from unattended script + # Remove from boot service to avoid spurious openrc recalls from unattended script. rm -f /etc/runlevels/default/headless_bootstrap rm -f /usr/local/bin/headless_bootstrap - # Run unattended script if available - install -m755 "${ovlpath}"/unattended.sh /tmp/headless_unattended > /dev/null 2>&1 && \ + # Run unattended script if available. + install -m755 ${ovlpath}/unattended.sh /tmp/headless_unattended >/dev/null 2>&1 && \ _logger "Starting headless_unattended service" && \ rc-service headless_unattended start rm -f /etc/init.d/headless_* _logger "Clean-up done, enjoy !" - cat /tmp/.trash/banner > /dev/console - if [ -c /dev/ttyGS0 ]; then - # Enabling terminal login into ttyGS0 serial for 60 sec - # no choice than making permanent change to pre 3.19 versions of /etc/securetty - grep -q "ttyGS0" /etc/securetty || echo "ttyGS0" >> /etc/securetty - /sbin/getty -L 115200 ttyGS0 vt100 & + cat /tmp/.trash/banner >/dev/console + if [ -c /dev/ttyGS${gdgt_id} ]; then + # Enabling terminal login into valid serial port: + # no choice than making permanent change to /etc/securetty (Alpine 3.19 already has ttyGS0). + grep -q "ttyGS${gdgt_id}" /etc/securetty || echo "ttyGS${gdgt_id}" >>/etc/securetty + /sbin/getty -L 115200 /dev/ttyGS${gdgt_id} vt100 & fi + exit 0 EOF chmod +x /tmp/.trash/headless_cleanup } _setup_sshd() { -## Setup temporary SSH server (root login, no password) -# We use some bundled (or optionaly provided) keys to avoid generation at startup and save time +## Setup temporary SSH server (root login, no password): +# we use some bundled (or optionaly provided) keys to avoid generation at startup and save time. _apk add openssh-server _preserve "/etc/ssh/sshd_config" _preserve "/etc/conf.d/sshd" -cat <<-EOF > /etc/ssh/sshd_config +cat <<-EOF >/etc/ssh/sshd_config PermitRootLogin yes Banner /tmp/.trash/banner EOF -# Client authorized_keys or no authentication -if install -m600 "${ovlpath}"/authorized_keys /tmp/.trash/authorized_keys > /dev/null 2>&1; then +# Client authorized_keys or no authentication. +if install -m600 "${ovlpath}"/authorized_keys /tmp/.trash/authorized_keys >/dev/null 2>&1; then _logger "Enabling public key SSH authentication..." - cat <<-EOF >> /etc/ssh/sshd_config + cat <<-EOF >>/etc/ssh/sshd_config AuthenticationMethods publickey AuthorizedKeysFile /tmp/.trash/authorized_keys # relax strict mode as authorized_keys are inside /tmp @@ -109,7 +110,7 @@ if install -m600 "${ovlpath}"/authorized_keys /tmp/.trash/authorized_keys > /dev EOF else _logger "No SSH authentication." - cat <<-EOF >> /etc/ssh/sshd_config + cat <<-EOF >>/etc/ssh/sshd_config AuthenticationMethods none PermitEmptyPasswords yes EOF @@ -118,8 +119,8 @@ fi # Server keys: inject optional custom keys, or generate new (might be stored), # or use bundeled ones (not stored) local keygen_stance="sshd_disable_keygen=yes" -if install -m600 "${ovlpath}"/ssh_host_*_key* /etc/ssh/ > /dev/null 2>&1; then - # check for empty key within injected ones: if found, generate new keys +if install -m600 "${ovlpath}"/ssh_host_*_key* /etc/ssh/ >/dev/null 2>&1; then + # Check for empty key within injected ones: if found, generate new keys. if find /etc/ssh/ -maxdepth 1 -type f -name 'ssh_host_*_key*' -empty | grep -q .; then rm /etc/ssh/ssh_host_*_key* keygen_stance="" @@ -130,63 +131,93 @@ if install -m600 "${ovlpath}"/ssh_host_*_key* /etc/ssh/ > /dev/null 2>&1; then fi else _logger "Using bundled ssh keys from RAM..." - cat <<-EOF >> /etc/ssh/sshd_config + cat <<-EOF >>/etc/ssh/sshd_config HostKey /tmp/.trash/ssh_host_ed25519_key HostKey /tmp/.trash/ssh_host_rsa_key EOF fi -echo "$keygen_stance" >> /etc/conf.d/sshd +echo "$keygen_stance" >>/etc/conf.d/sshd rc-service sshd restart } +_updt_apkovl() { +## Update apkovl overlay file & eventually reboot +# URL redirects to apkovl file on github master: is.gd shortener provides basic analytics. +# Analytics are public and can be checked at https://is.gd/stats.php?url=apkovl_master +# Privacy policy: https://is.gd/privacy.php +local file_url="https://is.gd/apkovl_master" +local sha_url="https://github.com/macmpi/alpine-linux-headless-bootstrap/raw/main/headless.apkovl.tar.gz.sha512" +local updt_status="failed, keeping original version" + +_logger "Updating overlay file..." +if wget -q -O /tmp/apkovl -T 10 "$file_url" >/dev/null 2>&1 && \ + wget -q -O /tmp/sha -T 10 "$sha_url" >/dev/null 2>&1 && \ + [ "$( sha512sum /tmp/apkovl | awk '{print $1}' )" = "$( awk '{print $1}' /tmp/sha )" ]; then + _is_ro && mount -o remount,rw "${ovlpath}" + cp /tmp/apkovl "${ovl}" + _is_ro && mount -o remount,ro "${ovlpath}" + ! [ "$( sha512sum "${ovl}" | awk '{print $1}' )" = "$( awk '{print $1}' /tmp/sha )" ] && \ + _logger "Bad update: original apkovl file may be altered, please check!..." && return 1 + updt_status="successful" +fi +rm -f /tmp/apkovl /tmp/sha +_logger "Update $updt_status" +[ "$updt_status" = "successful" ] || return 1 +# Reboot if specified in auto-updt file (and no ssh session ongoing nor unattended.sh script available). +! pgrep -a -P "$( cat /run/sshd.pid 2>/dev/null )" 2>/dev/null | grep -q "sshd: root@pts" && \ + ! [ -f "${ovlpath}"/unattended.sh ] && \ + grep -q "^reboot$" "${ovlpath}"/auto-updt && \ + _logger "Will reboot in 3sec..." && sleep 3 && reboot +exit 0 +} + _tst_version() { -# Tested URL redirects to github project page: is.gd shortener provides basic analytics. +## Compare current version with latest online, notify & eventally calls for update +# URL redirects to github project page: is.gd shortener provides basic analytics. # Analytics are public and can be checked at https://is.gd/stats.php?url=apkovl_run # Privacy policy: https://is.gd/privacy.php local new_vers="" -local status="failed" local ref="/macmpi/alpine-linux-headless-bootstrap/releases/tag/v" -if wget -q -O /tmp/homepg -T 10 https://is.gd/apkovl_run > /dev/null 2>&1; then - status="success" +local url="https://is.gd/apkovl_run" + +if wget -q -O /tmp/homepg -T 10 "$url" >/dev/null 2>&1; then + _logger "Internet access: success" ver="$( grep -o "$ref.*\"" /tmp/homepg | grep -Eo '[0-9]+[\.[0-9]+]*' )" rm -f /tmp/homepg - [ -n "$ver" ] && ! [ "$ver" = "$HDLSBSTRP_VERSION" ] && \ - new_vers="!! Version $ver is available on Github project page !!" && \ - _logger "$new_vers" && \ - printf '%s\n\n' "$new_vers" >> /tmp/.trash/banner + if [ -n "$ver" ] && ! [ "$ver" = "$HDLSBSTRP_VERSION" ]; then + new_vers="!! Version $ver is available on Github project page !!" + _logger "$new_vers" + printf '%s\n\n' "$new_vers" >>/tmp/.trash/banner + # Optionally update apkovl if key-file allows it. + [ -f "${ovlpath}"/auto-updt ] && _updt_apkovl & + fi +else + _logger "Internet access: failed" fi -_logger "Internet access: $status" } _setup_networking() { -## Setup Network interfaces -local has_wifi +## Setup network interfaces. +local has_wifi wlan_lst _has_wifi() { return "$has_wifi"; } -find /sys/class/ieee80211/*/device/net/* -maxdepth 0 -type d -exec basename {} \; > /tmp/.wlan_list 2>/dev/null -[ -s /tmp/.wlan_list ] && [ -f "${ovlpath}"/wpa_supplicant.conf ] +wlan_lst="$( find /sys/class/net/*/phy80211 -exec \ + sh -c 'printf %s\| "$( basename "$( dirname "$0" )" )"' {} \; 2>/dev/null )" +wlan_lst="${wlan_lst%\|}" +[ -n "$wlan_lst" ] && [ -f "${ovlpath}"/wpa_supplicant.conf ] has_wifi=$? -if _has_wifi; then - _logger "Configuring wifi..." - _apk add wpa_supplicant - _preserve "/etc/wpa_supplicant/wpa_supplicant.conf" - install -m600 "${ovlpath}"/wpa_supplicant.conf /etc/wpa_supplicant/wpa_supplicant.conf - rc-service wpa_supplicant restart -else - _logger "No wifi interface or SSID/pass file supplied" -fi _preserve "/etc/network/interfaces" -if ! install -m644 "${ovlpath}"/interfaces /etc/network/interfaces > /dev/null 2>&1; then +if ! install -m644 "${ovlpath}"/interfaces /etc/network/interfaces >/dev/null 2>&1; then _logger "No interfaces file supplied, building defaults..." - cat <<-EOF > /etc/network/interfaces + cat <<-EOF >/etc/network/interfaces auto lo iface lo inet loopback EOF for dev in /sys/class/net/*; do - # shellcheck disable=SC2034 # Unused IFINDEX while still sourced from uevent + # shellcheck disable=SC2034 # Unused IFINDEX while still sourced from uevent. local DEVTYPE INTERFACE IFINDEX DEVTYPE="" # shellcheck source=/dev/null @@ -195,31 +226,37 @@ if ! install -m644 "${ovlpath}"/interfaces /etc/network/interfaces > /dev/null 2 lo) ;; eth) - cat <<-EOF >> /etc/network/interfaces + cat <<-EOF >>/etc/network/interfaces auto $INTERFACE iface $INTERFACE inet dhcp EOF ;; *) - _has_wifi && grep -q "$INTERFACE" /tmp/.wlan_list && \ - cat <<-EOF >> /etc/network/interfaces + # According to below we could rely on DEVTYPE for wlan devices + # https://lists.freedesktop.org/archives/systemd-devel/2014-January/015999.html + # but...some wlan might still be ill-behaved: use wlan_lst + # shellcheck disable=SC2169 # ash does support string replacement. + _has_wifi && ! [ "${wlan_lst/$INTERFACE/}" = "$wlan_lst" ] && \ + cat <<-EOF >>/etc/network/interfaces auto $INTERFACE iface $INTERFACE inet dhcp EOF + # Ensure considered gadget interface is actually the connected one (may have several). [ "$DEVTYPE" = "gadget" ] && \ - cat <<-EOF >> /etc/network/interfaces && cat <<-EOF > /etc/resolv.conf - auto $INTERFACE - iface $INTERFACE inet static - address 10.42.0.2/24 - gateway 10.42.0.1 + find /sys/class/udc/*/device/gadget."${gdgt_id}"/net/"$INTERFACE" -maxdepth 0 >/dev/null 2>&1 && \ + cat <<-EOF >>/etc/network/interfaces && cat <<-EOF >/etc/resolv.conf + auto $INTERFACE + iface $INTERFACE inet static + address 10.42.0.2/24 + gateway 10.42.0.1 - EOF - nameserver 208.67.222.222 - nameserver 208.67.220.220 + EOF + nameserver 208.67.222.222 + nameserver 208.67.220.220 - EOF + EOF ;; esac done @@ -230,35 +267,53 @@ echo "Using following network interfaces:" cat /etc/network/interfaces echo "###################################" +if _has_wifi && grep -qE "$wlan_lst" /etc/network/interfaces; then + _logger "Configuring wifi..." + _apk add wpa_supplicant + _preserve "/etc/wpa_supplicant/wpa_supplicant.conf" + install -m600 "${ovlpath}"/wpa_supplicant.conf /etc/wpa_supplicant/wpa_supplicant.conf + rc-service wpa_supplicant restart +else + _logger "No wifi interface or SSID/pass file supplied" +fi + _preserve "/etc/hostname" -echo "alpine-headless" > /etc/hostname +echo "alpine-headless" >/etc/hostname hostname -F /etc/hostname rc-service networking restart -rm -f /tmp/.wlan_list } _setup_gadget() { -## load composite USB Serial/USB Ethernel driver & setup terminal +## Load composite USB Serial/USB Ethernel driver & setup terminal. _logger "Enabling USB-gadget Serial and Ethernet ports" -lsmod | grep -q "dwc2" || modprobe -qs dwc2 -# remove conflicting modules in case they were initially loaded (cmdline.txt) -modprobe -rq g_serial g_ether g_cdc -modprobe -q g_cdc && sleep 1 -# once driver has settled check if cable is connected: unload if not -[ "$( cat "$udc_gadget"/current_speed )" = "UNKNOWN" ] && \ - _logger "USB cable not connected !!" && modprobe -rq g_cdc && return 1 +# Remove conflicting modules in case they were initially loaded (cmdline.txt). +modprobe -r g_serial g_ether g_cdc +modprobe g_cdc +# Wait for g_cdc to settle and serial ports become available +timeout 1 sh <<-EOF + while ! grep -q "ttyGS" /proc/devices; do sleep 0.2; done + EOF -# default serial config: xon/xoff flow control -stty -g -F /dev/ttyGS0 >/dev/null 2>&1 -# notes to users willing to connect from Linux Ubuntu-based host terminal: -# - user on host needs to be part of dialout group (reboot required), and -# - disable spurious AT commands from ModemManager on host-side Gadget serial port -# you may create a /etc/udev/rules.d/99-ttyacms-gadget.rules as per: -# https://linux-tips.com/t/prevent-modem-manager-to-capture-usb-serial-devices/284/2 -# ATTRS{idVendor}=="0525" ATTRS{idProduct}=="a4aa", ENV{ID_MM_DEVICE_IGNORE}="1" - -setconsole /dev/ttyGS0 +# Determine which gadget ID is connected with USB cable (assume just one max). +# (setting console to unconnected serial port would block boot) +gdgt_id="$( find /sys/class/udc/*/current_speed -exec \ + sh -c 'grep -vq "UNKNOWN" "$0" && find ${0/current_speed/}device/gadget.* -maxdepth 0' {} \; \ + | sed 's/\/.*gadget\.//' )" +if [ -c /dev/ttyGS"${gdgt_id}" ]; then + # Default serial config: xon/xoff flow control. + stty -F /dev/ttyGS"${gdgt_id}" + setconsole /dev/ttyGS"${gdgt_id}" + # Notes to users willing to connect from Linux Ubuntu-based host terminal: + # - user on host needs to be part of dialout group (reboot required), and + # - disable spurious AT commands from ModemManager on host-side Gadget serial port + # one may create a /etc/udev/rules.d/99-ttyacms-gadget.rules as per: + # https://linux-tips.com/t/prevent-modem-manager-to-capture-usb-serial-devices/284/2 + # ATTRS{idVendor}=="0525" ATTRS{idProduct}=="a4aa", ENV{ID_MM_DEVICE_IGNORE}="1" +else + _logger "USB-gadget port not connected !" + modprobe -r g_cdc +fi } @@ -269,31 +324,35 @@ setconsole /dev/ttyGS0 exec 1>/dev/console 2>&1 _logger "Alpine Linux headless bootstrap v$HDLSBSTRP_VERSION by macmpi" -# help randomness for wpa_supplicant and sshd (urandom until 3.16) +# Help randomness for wpa_supplicant and sshd (urandom until 3.16). rc-service seedrng restart || rc-service urandom restart -# setup USB gadget mode if such device mode is enabled -udc_gadget="$( dirname "$( find -L /sys/class/udc/* -maxdepth 2 -type f -name "is_a_peripheral" 2>/dev/null)" )" -[ "$( cat "$udc_gadget"/is_a_peripheral 2>/dev/null )" = "0" ] && \ - _setup_gadget +# Setup USB gadget ports if some ports are enabled in peripheral mode. +# Note: we assume dwc2/dwc3 is pre-loaded, we just check mode. +gdgt_id="" +find /sys/class/udc/*/is_a_peripheral -print0 2>/dev/null | \ + xargs -0 cat 2>/dev/null | grep -q "0" && \ + _setup_gadget -# Determine ovl file location -# grab used ovl filename from dmesg +# Determine ovl file location. +# Grab used ovl filename from dmesg. ovl="$( dmesg | grep -o 'Loading user settings from .*:' | awk '{print $5}' | sed 's/:.*$//' )" if [ -f "${ovl}" ]; then ovlpath="$( dirname "$ovl" )" else - # search path again as mountpoint have been changed later in the boot process... + # Search path again as mountpoint have been changed later in the boot process... ovl="$( basename "${ovl}" )" ovlpath=$( find /media -maxdepth 2 -type d -path '*/.*' -prune -o -type f -name "${ovl}" -exec dirname {} \; | head -1 ) ovl="${ovlpath}/${ovl}" fi -# Create banner file +# Create banner file. warn="" -grep -q "${ovlpath}.*[[:space:]]ro[[:space:],]" /proc/mounts; RO=$? -[ "$RO" -eq "0" ] && warn="(remount partition rw!)" -cat <<-EOF > /tmp/.trash/banner +grep -q "${ovlpath}.*[[:space:]]ro[[:space:],]" /proc/mounts; is_ro=$? +_is_ro() { return "$is_ro"; } + +_is_ro && warn="(remount partition rw!)" +cat <<-EOF >/tmp/.trash/banner Alpine Linux headless bootstrap v$HDLSBSTRP_VERSION by macmpi @@ -306,12 +365,12 @@ cat <<-EOF > /tmp/.trash/banner _setup_networking -# Test latest available version online -# Can be skipped by creating a 'opt-out'-named dummy file aside apkovl file +# Test latest available version online. +# Can be skipped by creating a 'opt-out'-named dummy file aside apkovl file. [ -f "${ovlpath}"/opt-out ] || _tst_version & -# setup sshd unless unattended.sh script prevents it -grep -q "^#NO_SSH$" "${ovlpath}"/unattended.sh > /dev/null 2>&1 \ +# Setup sshd unless unattended.sh script prevents it. +grep -q "^#NO_SSH$" "${ovlpath}"/unattended.sh >/dev/null 2>&1 \ || _setup_sshd _prep_cleanup diff --git a/sample_auto-updt b/sample_auto-updt new file mode 100644 index 0000000..5fbe9ba --- /dev/null +++ b/sample_auto-updt @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: Copyright 2022-2023, macmpi +# SPDX-License-Identifier: MIT + +# Automated reboot after update is cancelled if: +# - there is an opened ssh session +# - unattended.sh script is provided + +# Uncomment line below to enable reboot after update +#reboot + diff --git a/sample_unattended.sh b/sample_unattended.sh index 110e74f..c03ba8f 100644 --- a/sample_unattended.sh +++ b/sample_unattended.sh @@ -37,10 +37,11 @@ else fi # also works in case volume is mounted read-only -grep -q "${ovlpath}.*[[:space:]]ro[[:space:],]" /proc/mounts; RO=$? -[ "$RO" -eq "0" ] && mount -o remount,rw "${ovlpath}" +grep -q "${ovlpath}.*[[:space:]]ro[[:space:],]" /proc/mounts; is_ro=$? +_is_ro() { return "$is_ro"; } +_is_ro && mount -o remount,rw "${ovlpath}" rm -f "${ovl}" -[ "$RO" -eq "0" ] && mount -o remount,ro "${ovlpath}" +_is_ro && mount -o remount,ro "${ovlpath}" ########################################################