From 3efaa5954a1f2eb828b604809ff1a1afb0049415 Mon Sep 17 00:00:00 2001 From: Tim Baumann Date: Sun, 13 Sep 2026 20:28:42 +0200 Subject: [PATCH] Improve repository presentation: README, icon, logo and security policy - README with badges, "Add repository" button, rationale, feature list, Mermaid overview, installation, quality table, credits and license notes. - App icon (128x128) and logo (250x100) for the Home Assistant app store, with SVG sources in .github/assets. - SECURITY.md pointing to GitHub private vulnerability reporting and to NLnet Labs for issues in Unbound itself. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_0175nKroeTB8UBzm1SJQa4Eg --- .github/assets/icon.svg | 26 ++++++++++ .github/assets/logo.svg | 5 ++ README.md | 102 +++++++++++++++++++++++++++++++--------- SECURITY.md | 15 ++++++ unbound/icon.png | Bin 0 -> 5953 bytes unbound/logo.png | Bin 0 -> 9275 bytes 6 files changed, 127 insertions(+), 21 deletions(-) create mode 100644 .github/assets/icon.svg create mode 100644 .github/assets/logo.svg create mode 100644 SECURITY.md create mode 100644 unbound/icon.png create mode 100644 unbound/logo.png diff --git a/.github/assets/icon.svg b/.github/assets/icon.svg new file mode 100644 index 0000000..3873b19 --- /dev/null +++ b/.github/assets/icon.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.github/assets/logo.svg b/.github/assets/logo.svg new file mode 100644 index 0000000..0c9f7c3 --- /dev/null +++ b/.github/assets/logo.svg @@ -0,0 +1,5 @@ + + + Unbound + for Home Assistant + diff --git a/README.md b/README.md index d30fb6d..3e94d07 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,93 @@ -# TimInTech Home Assistant Apps +

+ Unbound for Home Assistant +

-Home Assistant app repository. +

Unbound for Home Assistant

-## Apps +

+ Recursive, caching and DNSSEC-validating DNS resolver as a Home Assistant app —
+ the private upstream for Pi-hole, without any third-party DNS provider. +

-### [Unbound](./unbound) +

+ CI + Version + Architectures: amd64, aarch64 + License: MIT +

-Recursive, DNSSEC-validating DNS resolver on `127.0.0.1:5335` — a local upstream for Pi-hole running on Home Assistant. -Supports `amd64` and `aarch64`. See the [documentation](./unbound/DOCS.md). +

+ Add this repository to your Home Assistant instance +

+ +## Why Unbound? + +Pi-hole filters DNS, but it still forwards every allowed query to an upstream resolver such as your ISP, Google or +Cloudflare — which then sees your complete query history. With this app, Pi-hole forwards to Unbound on the same +host instead, and Unbound resolves each name itself, starting at the root servers. + +- **No single upstream provider** receives all of your queries. +- **DNSSEC validation** rejects forged or manipulated answers. +- **Everything stays on your Home Assistant host** — no extra device, container host or VM. + +> Queries to the root, TLD and authoritative servers are unencrypted. Unbound removes the central upstream provider, +> but it does not hide DNS traffic from someone who can observe your internet connection. + +## Features + +- Recursive, caching resolver with prefetching of popular entries +- DNSSEC validation with an automatically maintained trust anchor (RFC 5011); ships both root keys KSK-2017 and KSK-2024 +- Listens **only on `127.0.0.1:5335`**: not reachable from the LAN, no conflict with DNS on port 53 or mDNS on port 5353 +- Runs as the unprivileged user `unbound` from the first command on, without any Linux capabilities +- Protection against DNS rebinding for private IPv4 and IPv6 ranges +- Zero configuration: install, start, point Pi-hole at it +- `amd64` and `aarch64` (e.g. Home Assistant Green/Yellow, Raspberry Pi 4/5, x86 mini PCs and VMs) +- Automatic patch releases when Unbound, OpenSSL or any other Alpine package in the image receives an update + +## How it works + +```mermaid +flowchart LR + clients["LAN clients"] -- "DNS, port 53" --> pihole["Pi-hole app
filtering"] + pihole -- "127.0.0.1:5335" --> unbound["Unbound app
recursion + DNSSEC"] + unbound -- "iterative queries" --> internet["Root, TLD and
authoritative servers"] +``` ## Installation -1. *Settings → Apps → App store → ⋮ → Repositories* -2. Add `https://github.com/TimInTech/ha-addon-unbound` -3. Install **Unbound**, start it, then set `127.0.0.1#5335` as the only upstream DNS server in Pi-hole. +1. Click the button above, or open *Settings → Apps → App store → ⋮ → Repositories* and add + `https://github.com/TimInTech/ha-addon-unbound`. +2. Install **Unbound** and start it. +3. In Pi-hole open *Settings → DNS*, disable all upstream servers and set the custom upstream `127.0.0.1#5335`. + Leave *Use DNSSEC* in Pi-hole disabled — Unbound already validates. + +The Pi-hole app must use the host network, for example +[casperklein's Pi-hole app](https://github.com/casperklein/homeassistant-addons/tree/master/pi-hole). +Do not combine Unbound with the DNSCrypt option of that app: its dnscrypt-proxy also listens on `127.0.0.1:5335`. + +Details, verification and limitations: **[documentation](unbound/DOCS.md)**. + +## Quality and maintenance + +| Area | What runs | +| --- | --- | +| **CI** (`ci.yml`) | App linter, ShellCheck, `amd64`/`aarch64` builds and `tests/smoke.sh --hardened` (resolution, DNSSEC, restart, unprivileged start with `--cap-drop ALL`) on every push to `main` and every pull request | +| **Package updates** (`package-updates.yml`) | Every Monday: rebuilds the image and compares the Alpine packages with [`unbound/apk-packages.txt`](unbound/apk-packages.txt). On a change, a read-only `check` job runs the smoke test, an `aarch64` build and the linter; only then does the `release` job, which runs no third-party actions, commit a patch release with changelog | +| **Dependabot** | Weekly pull requests for the Alpine base image and the GitHub Actions, which are pinned to commit SHAs | + +GitHub pauses scheduled workflows after 60 days without repository activity; re-enable *Package updates* in the +Actions tab if that happens. + +## Security + +Please report vulnerabilities privately, see [SECURITY.md](SECURITY.md). -## Maintenance +## Credits -- **CI** (`ci.yml`): app linter, ShellCheck, amd64/aarch64 builds and `tests/smoke.sh --hardened` - (resolution, DNSSEC, restart, unprivileged start) on every push to `main` and every pull request. -- **Package updates** (`package-updates.yml`, Mondays and on demand): rebuilds the image and compares the installed - Alpine packages with `unbound/apk-packages.txt`. On a change, a read-only `check` job runs the amd64 smoke test, - an aarch64 build with config check and the app linter; only then does the `release` job, which runs no - third-party actions, commit a patch release with changelog. -- **Dependabot**: weekly pull requests for the Alpine base image and the GitHub Actions, which are pinned to commit - SHAs. A new Alpine branch changes the package set, so the next package-update run releases it. -- GitHub pauses scheduled workflows after 60 days without repository activity; re-enable *Package updates* - in the Actions tab if that happens. +[Unbound](https://github.com/NLnetLabs/unbound) is developed by [NLnet Labs](https://www.nlnetlabs.nl/) and +licensed under the BSD 3-Clause license. This project packages it for Home Assistant and is not affiliated with +NLnet Labs or the Home Assistant project. ## License -MIT +The files in this repository are licensed under the [MIT License](LICENSE). diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..3530caa --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,15 @@ +# Security policy + +## Supported versions + +Only the latest release of the Unbound app receives fixes. Updated Unbound and Alpine packages are picked up by the +weekly package-update workflow and published as a new patch release; install it from Home Assistant when offered. + +## Reporting a vulnerability + +Please do **not** open a public issue for security problems. + +- **This app** (Dockerfile, entry script, configuration, workflows): report privately via + [GitHub private vulnerability reporting](https://github.com/TimInTech/ha-addon-unbound/security/advisories/new). + Reports are handled on a best-effort basis; please include the app version and steps to reproduce. +- **Unbound itself**: report to NLnet Labs as described at . diff --git a/unbound/icon.png b/unbound/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..fed2ad2bc1d68110f0cd2c74b002e69f4c52d56f GIT binary patch literal 5953 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 literal 0 HcmV?d00001 diff --git a/unbound/logo.png b/unbound/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..12b7a556bca1268da78dec07e8d9dbc4e0e0d20c GIT binary patch literal 9275 zcmV-BB*fc^P)TXc#Z5sx1AJ7HyQb`0NjbV`oO)rRwu+>CfPBbfK z9+;h7?0>H;EMk#bg)S2&7>?yX-Y&vKM9%aC1`|ou$4^=0f$4XvFs`s&{Hr>CyorTH z3=>l5slOVQrfN(FZmL}*RUfC8$i36QW4hcU3uir-hlNFq5>)6-`;P2r8t(T%&aPc# zRUfCW$g4xPtJtqAm_B|778Wr`fY6)v9of-sxR(oZx+U`Zr&rhqwoFUx7O{wr(5ss3 zpIUG7hKc-GnQS-XT|El_ZZ+dAVvCx2bdd>$r%!H)JS>qTfOIk{;-|}m3D)`qiknD~dZizfe}9{eEv~ZO&ZNT*Y+X3 zX-ce$ygKkc5uX4HixY-8`i?kX4BbeKEo+Ul+sN#CHiP?i=h}%O9} ztSm(NjV|)4uX^m>T3DPwB>7iaTS0z)yhSb|TzvLf48HPGZtn35nmQ7rdSQj7Wh`2| znYk<0vv$WWOoihqa!=b`w&uU|RF~*om4!ukNb*%93P4gl->N`;Hd+)ZgbgiX!)XNWsyUo@^G)Yq78h2T8u_E;o&A zG@?m-99Li1g&|j7%9R&%CeCJS)Qbv*%WblB%Xa3h+`va)eoI+pWh06l5OE*<`;!ea z?qOlk7}#kfkqg4PXSLzZE4nkF&xN#VmUC+3M}Vc~})UwxNGQ{*jD9NgUN0tR2%g9|#Ii4o&d$)+lscJ62H>J7ZT zZVP$EB~@o46}cz<(J4>H_6@K&C6MH+M{1-ZHw>Xqw{sYL<)vKTy&H~%CdaG(QKh7! zk`KQ4hIyZEWZBm5F;ycHIaorshz;z~c$1%zk;dXbK6t$9+-M}l$8pOgUAg6wu5=ss zBD?bPJtx8xIWgXvu{d=wqC4>R6T}j^#ZLhH$s+Q+q7uH@cL;!=w`oaQa#G_L8mY)F zv0KClRZ~BaioEfD&_|0)`Tc84Sh!{r<&`b~n#9?;_0q09bNe+kkMcsB8Z435Npj}U zS5VIP3R>x0G5e7dR-vB=$-RfHK&0kPCTh+oS~>USNb#<()TD&QsQ{c1ktg>V)=kxS`D}^ZHd~kGl!Z!@k~y@qY0}#} zEJ@?$A71rJQACxg1ywyq;KG^!VP9jFbxI-p0yL%OyJ377X_kS5aWNutfQj!c4G_6E zn|B>x;yX*3c;~gzFIx}p6s*O~--O;ZACrIQL7L9-r9g#=>N4+<%fWnfHFR&U4OUl@L zq%i8`M@8h8*ezlN&wkK|x0*)3VsUmG^)k>R(l~49eXh+Cd3_MYay}UyqVD3$9*zCv zjlt>7Q)!#ll-;@cwZ?0k)|Atmr_>rJJS1|F$C%cW`)0LPm%D#Wfatr$E9UG6a9lq4 zV%&0_2GIh>RDXB$y5n>t?$Ws=gV*$|-geeaxOx@|KBxjvw}Dq|dd2Cq-d z96Hcj03lH=PP%+p4~N5Ch|;F!{Vs}{#-qu7hI|-jv)^%aX-?3mxAe_QaJXI1VdGvP z0X^2NQUyVA9B8s9C3DE5Ma$*ziBv&5c*ZYy0qZ)dRjrDP86>`2rFs+@Q1 zXXQ!naJ&BK$Q*WyAcsNE43<7zjHrmbkpv%aue(WJpE%+1(-L_w6us4S+1>)KuO-q& zHq|B4Z>XalS)OK?=KCmZLY%+G<*s~R`t}Xjy4$kvz~p4pwG?;+sBONqttzi4_Zj?L zJ?axx6b6_!eH*x@mT28=hV4VSY+%3wkEF|nwNcei5Y7vcw!Nsi1gT#Osv#J$D)KG6 z5AwvTA9yNh2pw9ss5#f6RSRsMM-)%I`T<*ZA8eF5L@M%F@1_e}hSbcxE07BWYZiA( zF4e4hiTVNdNSFg;3FGQq^bH$vD;v?nF5q%;siay|N3Ol*?kn@2%{Yu=Trd;e?F~?Ed zy4<1gYrw~+og)9JXG$m`hbr#n~7$tW&KN>w3X)taHcZ~dgqJI)Ze*)Lid=#b*p z<%z|u%i|I42kdE#8i!?c!sj^3B3Iz?dGFV(Yrmx989e!$D+A&^dCL`C($V8{V%xsM zJT~`zqTAq!g2+z>oht`RlA8}MUNwIofPT85wAk4DC;>k>?NFcW4B?@o<+HZ}=vNCW zoXh9_S>a9JBI%HP`H*wHg0k?xTW%d(ylVD(09~-aUA$t>dxAXRS6sTIgx}PsUQt8X zTYRK!sB>w~0RUY;wWK7k{1L!sWtT#G@9#FX{~9kBXjMGzTrqd9E?D5!1qMzgkd%u+BS7!0&#^1%4U1;%xT+^UuA+zWW0_U4p1$ zL@aV4YJ%UsKbO&S{zbLDhZnQ@bCy@%+@G^Dn)BDJ8xa9j7>zY~LTndlae2W4)6O zpt?T9nL~aN>%JJ_$6!-SHkIvx<#M{$xAuN#w*||-N?<#E%ha}Y)+?HrM)3P(`ra?~ zd(TI&o?M6^}Jl}y5bUUyy)CI zQbGTVx-jyJ?f?{)mNW97$NKhBAx1>5?u6PiH0mM5;0X_hI1$de;-F7iTbw;$v{Y16 z0jm1yP5n+D@{jL@-{uE7t$}p}Kvm-EjukXz(EBAu9rsK9-|*}Y71a>`hiv2+k%~N8 zP1L7eU&5C=J)57pwL6VxZm(?y!Siom$2;q`L{ujqj;qK4)jah=}aE-YeWQ9<>8z~>^yul z?0VH)(eV~JpsHK_!%Xy+Q1LyBGsLF}r0|0-T^i6bXjDWyCHKt=oF_IVpn0a^(+~CO z!z=WZ>mCZQFq+zX*x7*3iZOxD{c`rd=AeCe)p|}BMhPWgoT61y?_mL}-{i7^iHdZN zcKLyWaSaqX;(5rKVa#n+xZM19>Ko)2mjW=T*9F{m*)PJl3=ab@JD)+lJcFD0C1w0} z>KjzJ8r}*rRdJhUsENZKKNY`)7>4R=VHF-3 zHg&m1c?UG_o7GH^-}=RK8F97^)n61{H1Juq$0|*6i3t zesLMgzy6N3J9YyeJ8*v}K4nACP(O&uHdI;L?s~5?7BUv!%+p2XVr91 zN=h8_97!cl;q|r_(U($s5Ba6Sav)9&Zc+&l=enZh^H%tTsC)t!f%brU>^mM)~$gMUDu?W#zcs zZtS+&y`X;n{a%K@{5re*cBCaWiDSmS14vGY4@h>ek-hKVsv7T%@UIOy~c;-og4K z`7F5gYMLkdw(uMY@w{^H0DkqSX_S;#GX0aaO#j65Fg-K989Db2!+4D zGKWqQ)j#>3V1U8^KSw8J)V|)o!T9V)_c^Yo$N|b+ZVKu!5|LkA%55*s3lKTLE#14( zC4|Gp-!EOu#sh(~HX9G*^7JzQ){uuTZCi0m_ih2K zH<5#9J~hr}zeQki1CkY>+*!PG_Oyr!4---O9k8pGC@Sb9#VhA+@IO(wa@HzSbs(NC znst#UhWYEFrL({CKjB<4YZGX8J@S~0O2AUz#-t>#Y!bo;wa3zl0`;kXgD@f$dG-H$ zkJP?ze9^is94>SQJ-76$9hhd|BfGe=lEvG844vn;A1SU39B(pB<&&>=1Wj|e(8;3p zU)NgKz4=vU)*=;oWLoFw(wuzf%DFcw=1rg<1EHQ!`xTjDs?p84a?bq7N)4NDovY?d zQMgn*eQw{u5tVOLwSUoyxl=-AP_lCF+jh6@LREcU%&3;~Od)u8W=01ip zLg4#g^+aW8@yfYl#ctzJ_v_i_`CZ#xtN6gfaaygRY;i=FQKyGIH)>A{V)EWa7gS%H6Ao^9IH{(&En% zHCm`j9ESTK<>sMkV+gTCO3$G!#b$O=F`5{-cNZ+57q~jE7UaBv@u?Y2&NSW920(1C zgGI~cZ>z5njB1+o9R4%6-E4>Ab_u)cwXgm-AlE(AB&p;`H*wpVVz>_$7dyVu^;4s` z(#sc*vLX~YB5d7L`){pd>(EnXXJs@GY7QzbDS?zG@f3#GnU>Ndp0psQp5tsb&dO-c zcLxKeqwA~-z}5qYiH69dP1O?6%~8*|5hz+c`w#)kXzNeaweG0;SH-Ih&L@BM%cd+`#A?`UbW07R!{o+{|0|Rm_MyW73S%RgB@N zCYdU)A9-x}NilyO3!L$ng^6V@XXnFXvQFN&PLv7iO*LGRiwOIV7V_q2n;Ny!jlr86 zx3a%_iS7v&xh!00Q$v=6_D4vz8$|4-B0W22R(f{MEbp)u&rfTbo}F_fZPLuEBF#S< zWK5cIPkMIFk@QJ3Z;B+J(#qm%75>|4%9MaT?cStLoSl|2X~sE`l;ur}=jWW6Hfj3t zwP9CJ0=%h4BJ%3E|NGu@Tp^a9#v(4$@-PM8Y*%4hl#ZzRQ0|zlhYyVz z9$*o*;fTZY?-<%7UV!E{Ax!&e(M3gmi)TH1;I6a=kXO2MvE5DQ?GHW}#PP#1;^W-+ zsBnKIWqD&5?&k!1|GHxye}XT84Hi~Ukpt{Fkjwnl8yQl|f|iC*X}Z}dxg>P4+x7{+s` z{1PYj!_(t8v(-`J?0>1+(OV7^f3_2%w%!t|P zlXC`(>L5kVP}pf0#?yx%9ku}=eR9r1HD#KJ4#iWK?JnG-^GA>TA!E{vF^K#I;llLn zoSOxnIXot7c{qQA#vpJBtoLykBCkGv_U(@;EUnwWd0lKS*vmC5rZDyBG7`=;NjTSJ z>d|HNTk$tG7u3G&wmuY=mGj(^)r}+aFtqN*U94h$0HC6;fH6ZROfcf@?hh2sGen*? zRo+mTk&&Hqho>B~4a!Kk>^y*?>+a> zYzh1dpk?;V-oO+^{$gU>Ey{o6P4kdx>NhIv1Koh|yxlDxd-SRX$ZXUcVJJ>j^O&U` zrH>){s={&uc^sv)O>KV%FjZCqn=94N~Rts*220WH|6b zRg6-gEv6RcJ~n)HbvlI-L4Rg5q~!4E;en1L09CUT5gTY*hu!h7-H!~YPJcK(JLf2{ zJ9l)}$JOz3$BdX=b?pw9>3$v6foJ?>VdCyb29#EnIWKQa)^vdM$vGWGc?=-$v8+$i zvvVpyw&ji<{xLB|o%_+l5qb6hGnTDo)wb|=1^#*KlAt0#C#4mOd)-Idq~`p2Yxt&} zSADmeoHbqphtU>!c&+yU42<@G=9cb0w$)Sg8{jOjXhn619FTW&$7Hpr{^yR9_I?rC z<7KGvz~~`|fTN1m#1rMm>f=}>Cgb6`SA<2Y=@#&Liyp*1uv)84r&k%hUpscd! z*c6f++vBciF;MG(U#xKKf}<>Q1YD+a|IBwd;tX)AStli~3YH!67z(w?v1+zSY(~x{ zLus4TjH1dimL1wgiM!zr7LSya^5DWH#|8}>MdZ;@x+wX8w2W(0V@}zGR;Z5w$%qXV zh>$NzO&RSc%uE1MRpnSY;#-c11RZr1qyV78W$WbqaLFB$H7PK%4i{mSz)XRK0@Hx4 zD#mbt!(&J6%N>(-6Na1%Acpxq!2Tz)4j&$qHPCRI=isqSvqTmy3>dh{8$IHcyprVh zh>QbyxMP8j|XYNt%pZN}R z@4FqFQM*k!Zw{6hQDM4i*(8N`dfm&E?MwOKz!q{Y8Aj8%B!+&L!>*De030eS;w(q~ z?eV)!<-r9@*m%ZU&8q^+Qa+0G%wHCUD&N$*jez% zT}P_pwR&!PE8=X|sVM_<#}0p|Iy`Oij3iNh-)YK}M++*Fx;vb4C*ByBEGN@8MR>&bH*3jWq>>!JwJ^JL*d8((A7@dKjl;x~MP?njT7 z$g2ZB*tCsj7d7m+(ljmsLxh6LQg)T(@od){d8g++ba15c(8kx^3+dP+ zC=)+g%?Dq9S9|>ft|bhSM@j1kMh_`g(VIZ7DN523G1;i1i&IKua&$`Ykaw2jWt2H; znyb^2oMj?%Cu$5ReDv-HJG47A{ywLg^cRs{??pigTuC=Nu833i?QO zU%DnAZ0$|a&(pJWoTV;j9K}ZWzlS7yJg;0l zgiF)fGy01+d2`RknmFe-Ys;cu_trbloHbuE`mK*W=BXN8i#FPYV{y7Le} z*LJOEbG}FDU7NMx_A@S`Lvr2o=c@41iuFwRUJ~bsrDm#f<*3}zM!<6-!J-vUXxy7!GZe zL{;^pkd?QvIF1M)sl%SwEaKTP;0Zl3A`e#n*{#xe@QO3|9If~wYrOfQD%$(8j&+Sp|K_MV?p7kKb|5- z*p*O8=e^_aZutIO3yVg;05~u^yI57m0hY)k7dfgj&JwvrEK##Tys&-Td_+9^fi02O znLd=r1$q0ZYu|hBi=QMu zCSM9?J(s6cUawHSq|6d|{YAddFpTRhkz2$f0k)kArcc}fwoH(3Es=+)hhKU@zBLRZ z^U&jW?ZCn!1_^4!tzi229cD%4Wx(5($o8k4@3$ZIm)rG^a7tK4^s)m*oTb)-HEx7v)FkmMAGi^#nK=hi6a)DyV^UkmcG zy-C5W1EWX#*dMd7h*jz(^qR;b()`{(bTzT{6VXc*=`3(MBFTX5Cna)^(FiBVKG3ad z$QpI)NBK|Rv&mZ4YH_mBAfa1WSe%^H-=l(sg~dsWC3FjmQvgfo78a)fme4IMP5~^T dTUeX|{67q<6#ugw02}}S002ovPDHLkV1nrOQdR%} literal 0 HcmV?d00001