From a71663ed4e6f69c780c2eae3aeb5539a60e247e6 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 20 Apr 2020 10:18:02 +0200 Subject: [PATCH] Update documentation for v5.0 Signed-off-by: DL6ER --- .markdownlint.json | 6 +- README.md | 5 +- docs/database/ftl.md | 124 +++++++++ docs/database/gravity/example-adlists-1.png | Bin 0 -> 29309 bytes docs/database/gravity/example-clients-1.png | Bin 0 -> 9937 bytes docs/database/gravity/example-clients-2.png | Bin 0 -> 10771 bytes docs/database/gravity/example-clients-3.png | Bin 0 -> 10332 bytes docs/database/gravity/example-domain-1.png | Bin 0 -> 8759 bytes docs/database/gravity/example-domain-2.png | Bin 0 -> 8984 bytes docs/database/gravity/example-domain-3.png | Bin 0 -> 8577 bytes docs/database/gravity/example-domain-4.png | Bin 0 -> 11337 bytes docs/database/gravity/example-domain-5.png | Bin 0 -> 11024 bytes docs/database/gravity/example-domain-6.png | Bin 0 -> 11064 bytes docs/database/gravity/example-groups.png | Bin 0 -> 9732 bytes docs/database/gravity/example-new-black.png | Bin 0 -> 4155 bytes docs/database/gravity/example-new-white.png | Bin 0 -> 4433 bytes docs/database/gravity/example.md | 264 ++++++++++++++++++++ docs/database/gravity/groups.md | 38 +++ docs/database/gravity/index.md | 69 +++++ docs/database/index.md | 8 + docs/ftldns/compile.md | 8 +- docs/ftldns/configfile.md | 100 ++++---- docs/ftldns/database.md | 136 +--------- docs/ftldns/debugging.md | 17 +- docs/ftldns/regex/overview.md | 26 +- docs/ftldns/regex/tutorial.md | 2 +- mkdocs.yml | 8 +- 27 files changed, 598 insertions(+), 213 deletions(-) create mode 100644 docs/database/ftl.md create mode 100644 docs/database/gravity/example-adlists-1.png create mode 100644 docs/database/gravity/example-clients-1.png create mode 100644 docs/database/gravity/example-clients-2.png create mode 100644 docs/database/gravity/example-clients-3.png create mode 100644 docs/database/gravity/example-domain-1.png create mode 100644 docs/database/gravity/example-domain-2.png create mode 100644 docs/database/gravity/example-domain-3.png create mode 100644 docs/database/gravity/example-domain-4.png create mode 100644 docs/database/gravity/example-domain-5.png create mode 100644 docs/database/gravity/example-domain-6.png create mode 100644 docs/database/gravity/example-groups.png create mode 100644 docs/database/gravity/example-new-black.png create mode 100644 docs/database/gravity/example-new-white.png create mode 100644 docs/database/gravity/example.md create mode 100644 docs/database/gravity/groups.md create mode 100644 docs/database/gravity/index.md create mode 100644 docs/database/index.md diff --git a/.markdownlint.json b/.markdownlint.json index 9e66b35..e81f2a4 100644 --- a/.markdownlint.json +++ b/.markdownlint.json @@ -9,6 +9,9 @@ "MD007": { "indent": 4 }, + "MD012": { + "maximum": 2 + }, "MD013": false, "MD024": false, "MD025": false, @@ -22,7 +25,8 @@ "strong", "p", "sub", - "table", "tbody", "th", "tr", "td" + "table", "tbody", "th", "tr", "td", + "details", "summary" ] }, "MD035": { diff --git a/README.md b/README.md index 621bb62..3d1a62f 100644 --- a/README.md +++ b/README.md @@ -30,13 +30,14 @@ When working on this repo, it is advised that you review your changes locally be Please make sure you fork the repo and change the clone URL in the example below for your fork: -- Linux Mint / Ubuntu instructions (tested on Linux Mint 18): +- Linux Mint / Ubuntu 18.04 LTS / 19.10: - Preparations (only required once): ```bash git clone https://github.com/YOUR-USERNAME/docs cd docs - sudo pip install -r requirements.txt + sudo apt install python3-pip + sudo pip3 install -r requirements.txt ``` - Running the docs server: diff --git a/docs/database/ftl.md b/docs/database/ftl.md new file mode 100644 index 0000000..1580e78 --- /dev/null +++ b/docs/database/ftl.md @@ -0,0 +1,124 @@ +Pi-hole *FTL*DNS uses the well-known relational database management system SQLite3 as its long-term storage of query data. In contrast to many other database management solutions, *FTL*DNS does not need a server database engine as the database engine is directly embedded in *FTL*DNS. It seems an obvious choice as it is probably the most widely deployed database engine - it is used today by several widespread web browsers, operating systems, and embedded systems (such as mobile phones), among others. Hence, it is rich in supported platforms and offered features. SQLite implements most of the SQL-92 standard for SQL and can be used for high-level queries. + +The long-term query database was the first database that was added to the Pi-hole project. +We update this database periodically and on the exit of *FTL*DNS (triggered e.g. by a `service pihole-FTL restart`). The updating frequency can be controlled by the parameter [`DBINTERVAL`](../ftldns/configfile.md#dbinterval) and defaults to once per minute. We think this interval is sufficient to protect against data losses due to power failure events. *FTL*DNS needs the database to populate its internal history of the most recent 24 hours. If the database is disabled, *FTL*DNS will show an empty query history after a restart. + +The location of the database can be configured by the config parameter [`DBFILE`](../ftldns/configfile.md#dbfile). It defaults to `/etc/pihole/pihole-FTL.db`. If the given file does not exist, *FTL*DNS will create a new (empty) database file. + +Another way of controlling the size of the long-term database is by setting a maximum age for log queries to keep using the config parameter [`MAXDBDAYS`](../ftldns/configfile.md#maxdbdays). It defaults to 365 days, i.e. queries that are older than one year get periodically removed to limit the growth of the long-term database file. + +The config parameter [`DBIMPORT`](../ftldns/configfile.md#dbimport) controls whether `FTL` loads information from the database on startup. It needs to do this to populate the internal data structure with the most recent history. However, as importing from the database on disk can delay FTL on very large deploys, it can be disabled using this option. + +--- + +### Split database + +You can split your long-term database by periodically rotating the database file (do this only when `pihole-FTL` is *not* running). The individual database contents can easily be merged when required. +This could be implemented by running a monthly `cron` job such as: + +```bash +sudo service pihole-FTL stop +sudo mv /etc/pihole/pihole-FTL.db /media/backup/pihole-FTL_$(date +"%m-%y").db +sudo service pihole-FTL start +``` + +Note that DNS resolution will not be available as long as `pihole-FTL` is stopped. + +### Backup database + +The database can be backed up while FTL is running when using the SQLite3 Online backup method, e.g., + +```bash +sqlite3 /etc/pihole/pihole-FTL.db ".backup /home/pi/pihole-FTL.db.backup" +``` + +will create `/home/pi/pihole-FTL.db.backup` which is a copy of your long-term database. + +--- + +The long-term database contains three tables: + +### Query Table + +Label | Type | Allowed to by empty | Content +--- | --- | ---- | ----- +`id` | integer | No | autoincrement ID for the table, only used by SQLite3, not by *FTL*DNS +`timestamp` | integer | No | Unix timestamp when this query arrived at *FTL*DNS (used as index) +`type` | integer | No | Type of this query (see [Supported query types](ftl.md#supported-query-types)) +`status` | integer | No | How was this query handled by *FTL*DNS? (see [Supported status types](ftl.md#supported-status-types)) +`domain` | text | No | Requested domain +`client` | text | No | Requesting client (IP address) +`forward` | text | Yes | Forward destination used for this query (only set if `status == 2`) + +### Counters table + +This table contains counter values integrated over the entire lifetime of the table + +Label | Type | Allowed to by empty | Content +--- | --- | ---- | ----- +`id` | integer | No | ID for the table used to select a counter (see below) +`value` | integer | No | Value of a given counter + +ID | Interpretation +--- | --- +0 | Total number of queries +1 | Total number of blocked queries + +### FTL table + +The FTL table contains some data used by *FTL*DNS for determining which queries to save to the database. This table does not contain any entries of general interest. + +SQLite3 syntax used to create this table: + +### Supported query types + +ID | Query Type +--- | --- +1 | A +2 | AAAA +3 | ANY +4 | SRV +5 | SOA +6 | PTR +7 | TXT + + + + + +### Supported status types + +ID | Status | | Details +--- | --- | --- | --- +0 | Unknown | ❔ | was not answered by forward destination +1 | Blocked | ❌ | Domain contained in [gravity database](../database/gravity/index.md#gravity-table-gravity) +2 | Allowed | ✅ | Forwarded +3 | Allowed | ✅ | Known, replied to from cache +4 | Blocked | ❌ | Domain matched by a [regex blacklist](../database/gravity/index.md#regex-table-regex) filter +5 | Blocked | ❌ | Domain contained in [exact blacklist](../database/gravity/index.md#blacklist-table-blacklist) +6 | Blocked | ❌ | By upstream server (known blocking page IP address) +7 | Blocked | ❌ | By upstream server (`0.0.0.0` or `::`) +8 | Blocked | ❌ | By upstream server (`NXDOMAIN` with `RA` bit unset) +9 | Blocked | ❌ | Domain contained in [gravity database](../database/gravity/index.md#gravity-table-gravity)
*Blocked during deep CNAME inspection* +10 | Blocked | ❌ | Domain matched by a [regex blacklist](../database/gravity/index.md#regex-table-regex) filter
*Blocked during deep CNAME inspection* +11 | Blocked | ❌ | Domain contained in [exact blacklist](../database/gravity/index.md#blacklist-table-blacklist)
*Blocked during deep CNAME inspection* + +### Example for interaction with the long-term query database + +In addition to the interactions the Pi-hole database API offers, you can also run your own SQL commands against the database. If you want to obtain the three most queries domains for all time, you could use + +```bash +sqlite3 "/etc/pihole/pihole-FTL.db" "SELECT domain,count(domain) FROM queries WHERE (STATUS == 2 OR STATUS == 3) GROUP BY domain ORDER BY count(domain) DESC LIMIT 3" +``` + +which would return something like + +```text +discourse.pi-hole.net|421095 +www.pi-hole.net|132483 +posteo.de|130243 +``` + +showing the domain and the number of times it was found in the long-term database. Note that such a request might take a very long time for computation as the entire history of queries has to be processed for this. + +{!abbreviations.md!} diff --git a/docs/database/gravity/example-adlists-1.png b/docs/database/gravity/example-adlists-1.png new file mode 100644 index 0000000000000000000000000000000000000000..15e1dd4cdd0599e660f653dde8a8714e5ce2f39e GIT binary patch literal 29309 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 z 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 a^;K8McfM&Dp_IJ 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 Ba`ylL literal 0 HcmV?d00001 diff --git a/docs/database/gravity/example-clients-2.png b/docs/database/gravity/example-clients-2.png new file mode 100644 index 0000000000000000000000000000000000000000..013c77357b01ce3aae8822ce5286f9acbea0c80b GIT binary patch literal 10771 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 X>!>)FQ59pyACso4o=Tyz#jF1X^K$|e literal 0 HcmV?d00001 diff --git a/docs/database/gravity/example-clients-3.png b/docs/database/gravity/example-clients-3.png new file mode 100644 index 0000000000000000000000000000000000000000..836627a2f310feb88692e9856bace8206aa6b971 GIT binary patch literal 10332 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 literal 0 HcmV?d00001 diff --git a/docs/database/gravity/example-domain-1.png b/docs/database/gravity/example-domain-1.png new file mode 100644 index 0000000000000000000000000000000000000000..3e9d95b62aebe769607e8c134d1b328501e63e08 GIT binary patch literal 8759 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 literal 0 HcmV?d00001 diff --git a/docs/database/gravity/example-domain-2.png b/docs/database/gravity/example-domain-2.png new file mode 100644 index 0000000000000000000000000000000000000000..127acdec7163438bb07898a1327c7c04c28616e6 GIT binary patch literal 8984 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 AaR2}S literal 0 HcmV?d00001 diff --git a/docs/database/gravity/example-domain-4.png b/docs/database/gravity/example-domain-4.png new file mode 100644 index 0000000000000000000000000000000000000000..ac85cc8e2be091b50290dcedc8cb6b87cd1e8617 GIT binary patch literal 11337 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 literal 0 HcmV?d00001 diff --git a/docs/database/gravity/example-domain-5.png b/docs/database/gravity/example-domain-5.png new file mode 100644 index 0000000000000000000000000000000000000000..0c3f330b3838365500d07ad4ca6a247049bd6d40 GIT binary patch literal 11024 zcma)?2UJtR)~FFsRFI&EC`H7E0)h&N)KEke6c8!Wr3na9rARvzY0^PD8W8D7klq8* zYeb}%&_fR;fg~j7JoNtefA{|HuJ_)_T9Y$-&FtBG&&=B2N_Lorn)0cW94BdLXihzT z^gxq_h7P2mIU;bJ?ogs3l&3;NLrd0pqNTLCMHt7-jg8Fw8lIk=n_F7NL4Tf|em{Nq zQ@gVD!*>#<+}~P7QR%@0{jfn21Q|!SLBxJHm4}+~liQ6pijPc+7s1gKi^rxOKeix< zJX26m(7R2AHX2kNkgY$OTZh)5rD6-k=h1_EM6|bt(j#y>`QZc2h)LpPjFyrLFhGL# z=bOT;)$F}veyr{e20g?NeS7I!w1hO-!<0ujn;2LpO^r9YmCm4x0>xj|u7OaS>q8}@ zm@y(`pag%@PF(6Mb~aT>#eiN>O>-0|sw6kIbA!0uR}g6ClG(R==md4yA*K={jz;P} zs-7K8_p{lq+NWVoqs|JSrD=RBWJNS|L zZD@C9Y;;spL}X)gb7g+i!qT#9Cg;nSFT?m9OHq-p{WWWgQL&#R9QB+UU0=L)eJkqM z4$;uuDttp5r%n_3L>B2aH8r(ltTQHab423W@>^q?9DSNB-6>Ux{ljRfJzrngqtAV# zv#|3pUw=x=c1c5`%cy-?_Sx@>wN5_G&Sy=XYGvEuW}6xIKhrVTiQTg~`ak(C`;8HO zcXS5hGJY!>cge){xwLl+y3P*Z`vV7;3><0l{Bv;Nk)oIrO~c_PFLHP1Ex#Lo{P=M_ zF7)$E|AU`xAvLBKn(8k1&M`H17B^dad;6qVnTG@gyQc?`_xB6m`;sB({rmjhF7MYg zG<*Y(AINKY99bVca)jp6ak>&lGpO^J=~~IvT+a&`_HpUQ&ee9|AId#l`bNJDBh&q- z55lNSBkFv}k>$*DrB3fsc%L2y{qUJR52qFl=X0XfR)}gyYlAqkt?LzoKs?Bt+jB}d z^WEFTc*d>Q8lQ{s{G_8nHQmeF4|3_{DaO?BdRaLxP>w}VVfJ|51HudGBf^&hN`4{o zQAO%3t+-*6Ybh#$D(#P23Oh3}NVq%;5CXQ1;l)*Fv~P#v`9gsdP~S#RLQCszS%H3` zDVI|}a`CzAX+KZdMOV6CKdqCWp0+*l+`KQLcOp(8pd>C!+bX?sFREY2Z*KEq#Oj8~ z!JQtd+r$@RVprcMT`?FlmX(mc`#RZto`&_CPj5OG{koR@P0wQimwc1J=YyX_`pQ~v zWA7L=CNo%M*10(4X9&{vd|T?aGE{ggQF7koY0tH9ACC?Xi`P>xA15HibsI-TJI)xO z@%#2T?>mh@jjHOjV&V^yjl151&K3f7Rlv)O#YGOuHq$G6-J3a1SMgPq<(}7%&$+Mg z3`HODXIc$@!j;kR_>-*-)d=qN>%wflw=LGR zc&7D)I9Gq$ph?v4(hI$kBf>O(%93hA6W<#r&v^Y&y9RffGg>KK;ZeeA)XJ*@>6(eR zwQukfP#hs)fm~^zHI zAlZ6I&nW3P!YjP;HMgZvB6vSr(3HRS;0a*tMihvSJ(dP85;+xn@gvektHR2qMFr6B z;L~{pAIagjl+cHJFGSt$5ce#iw&enM4+c#J;QCv)7}pp0$>w{fLhmXoc-Z2*2QdS) zWae@XZ7BEii2$Mz+2M#E0(yXL9fLGw4nnpZ?G3uWpmba$nL~8~Z80L5aP+wxwWssK z!(%2<7J5nmr8@^TbZ9Vc7-KlLDE+8ntEC&;a5Q&=i1w;G;dBM_et%281h331#XcVi zUy{9LIfSy?&PB*_V+ndawaV<|@BsJ}az^#XcZ5Ju8XyrT#ZL}l7P`XvEw})Be&|QN z0kGF}>*QjS_?k!5aCHf)d}8`Qm71BPE~ed+El3U*X>@0}eve5<{P`t(M?ho*wKkEF zxl#G$qTI5uvd$Q0w95AVaWfjfFV>QV0+alCH}V~^G}T^_QrPweb02Y>(Mx>vbzm13 zNKJ@z=>v8lL#s>5uPA+(&HDG!HICBf{Hk|w8(i-ivSCX5kC_*M7bD}oRW-ledHgze zfKTiiW8#h+e#<)y3$RX}82BYX4)miOA<_Lq@Q5E`xTJE_Yo9@y@?WBV9jE%7PmWih z?^)LZ&lz{ppr*w!_F>xJ(VtLL4)NX z%r<64Bqid_MCF7nr};$Xn|b%m!j10n?yvh(ZXI3j&zo%pvpNd3l=N5W;vps!S;uaq$=1GQfn-&ls8{ym(GlbN9T z5f>gBzyCw#rfZ!g)^cK&C9-{2 z@C*ImN$U@;%dlcEEJ6!-RFfCR@Yb9=AWDH0wCMX4++$o7ONRH-tzjb*S~x-Fujg1p@+KnMzLSCdL+1%sJET+%|wd zu#jA~=Iblfhw@ki#!>52XP;wp3oWSm2K-(;{B%LHj*np7*|X1D2R#ZvmYye0aXqi7 z7A?@PWnC46m5W0DCXL(s!3|PkG%UvhE-i;r?j=5D4QYWr(Wb5cwn-Q7+eiA>!~QQP zzl9ed;iL<8l))WLS2xcBE>zvIl}|$sY^vJ*A9pYC@ctn%bkjSiU!T^cTm(qy8MaOn zF#qrhk$O;xltp1keVwW~Ik<+NFxU=tCik^6eL8?VTJ1}2)- z7Uq|36AnNQEK{BKoS!vEQf;%wW8drRmyPKfXhsW^zm6FhYpf>^#s#kY%x8Yc94OY( zKwoO@z;$Nmmt~Zc)p307PN5jiG-rk+tt3)_sq&@2(>l-cG5=64bI1j2xS{ z7)I2sy5qwz&V0_8G6I*wy{Rv|t2HR{q+I+|Siuk8TOFBI2`1m(!7!FTIm5B>TheT$ zgoEMeOFn1s#4>ej^}lVy7}&R!xZQn{vg%QGuDNqb%9h(-XC1(Tnc8y zZ$@}cZqi+Kyw$Q0^m^-ldX3Sjgrr1?8N8$V~`;&`H_NlvGkD6nzvLZ&;n6z}- zA17b2oY(8BIqT6D`|50Q(v>eGw|Z^1z5&A_Zd0h<;FH6@tsGzQC5@@|YZ+u$Byj*Q z-zJF=6T z8oJV=7uhL+w3O0Sur?8Ss57}(6tzpmIh%Fd2lulYf5XCNLT&no+GdP3-OFHj8-4x7c0=lmdq3-*KfLs8-@5Owec?U!&J|1i zsE|9%aWAXBcYFxQ=YVCrV62~e#Ph9b_h4+WltcSeqwAH7I_gQj*aA6lO*XFU)6H|; z<2{jZA8c51$%x6)mJrJuvYaO}FmFyBd&yk^(}qd)?{FA?_&8KM(2DP}_T{mpZ3f#F zt&c6OdNSt#PXsW!kRYPV9_aP>mQbCZ#x@`9ij_EHvy9H!Cqo`IZTd58%H-whUaGFc zuOvnn4)3*xj!XA^?@ic*sagySL;=S}3T^aQ~=l;0IS9n`q`jj~D41RsQ_WZ zICzNWYi8AU^&1)el)~`lhOarbT|=pb^%wicGdb7*OQmM5NCu6fea3pqqh{uzOLI~; zE!6@YrjGM-eXTtouM5Oos3+To!XI`I$ei|g&D4&XjTY+L<~yV1`feT}`yu_{bN+$abjO& zn-H}q5-bf0ukM{GLq!p8;Y0y}#P|6eXqEo#CV?o*^vsdcow{s^Q;lL*E@e}AZ*W*Sr?|&mp!7ki z(ZG?9uJ2j9BCoz+>xbXGeER|u+ah3fCeck}gSi#u$D1OocyUKucF8z^AU-w=z0bhI zAsvC!T_G0KoL3ICG$_&J0{q@50himi3?D7^B!^HUqi0KIb?;UveqfPbA%F%VptON1 zMHRU(ZX!8{YLpF}F0vlDbfXRRb=_od#gCrme6WAU{vBNrakikJlLE)zw*UZfJ{khI zI(WD#+yB)-)Y)jbK0ZykHYfe4~DB%=+tjIbmQCc{fmGD*t(+6Ma=n zxP^-U1*`C~+pOh3RX!Y7QK}F;TA(ea0eNDm)qLsrleR4JG?-jR7qmP2RE-PZjh%dd zHA9{f2H)OK01hs+kB;ljz{r6E@NGV@6X$_sNqXIOe7UBn18N2M!BzyZ%t}g&B1MSk7&JKugtxw918{pX4HdFm z;)upWZX&V*V!&yDj)L>5cL79`JvQgE2jz4o#xTH2FTK~Lbf3+-o??Gvm`kt80L@;% zFS{Mq2Io|hLEd#+aIi&Pwz_SHZCtBoI+&%FO&AG{4B5W^vn(r6jJVT^oWta^e}J-f z88$~gjr-0pTWt5V05ebaY%C&rdHKf1ky4A6_b9b21weE!qKXb!zJ4Mr)V|A+Ws zSFEcgvjTJK6ea5Ooe>`-C5;>GVn@!-m>3W1z^t#6OqyK#4ZBpk*fZlee>SiK)Aq8M z4cA+Kxaj@Eh#`pTJ&3h^2I#>{fn9X%N$>FDla#u8B44c`m0En{>995zv5+XR(S%v7 zozvRIQn7qcGm!dic;5J_Vuz$O<3+eA{xmQL=LY0am;%TKx9VW;qd~wal~(s|YM%n% zNohd~ppY0ajftjY^~knajyui@r+v4%M~aS^yIW1vU6}vz;`Bk!tLd*%WFDv33A=#) zM*8)88k9FHL4yu2Wp|YO3}?}VgCKZ6l~u+CT2oUzAm~p?qySazGcx<`9QKAy8#&1b z=5-hHbbGFxuuEA$AB=F!1dmBP2YH{K3!S_6gj>d}uoHguo@E?xe%^WV zb=~04VYdPY$A!dtXI-z8MA)Zw>pB;Q<Q@xGKJgPukP2Bl((Z!@AGq{2tK+dqr@It+dBtypfId`4*B~$fez=1u>t;RLPT2 zE7g2Rri+|GZQKSA+dE~>6A#R{5VjKZZXFjy7E3>HRM{BDnd+oG+4I)XwRh-G(%rTV zYWT7mqx;2va^a#fC?J?~@AN+pBR3M80cS zaeS-yI-s~nt}?1?ISaXxne+pAWAmgFiBC>0AnRuOB1o{v$wLm?;ZXoz>}Kv9SZz8u znC?l&-x*=1O=BPG@@tmVaoImPf4T1KR*1pFYToOUi<;}#Sn|770tuB#_KJJVXZ%DN6f-?PFW(C|E-WmIQnuNk^S0~@ zwxH7{qBiB5{gbg9NY3fk-j@qSkJ&qQdbwQQe}lPDCC>0E9F-qb~L0i)4XcDe{ZVz{Y^g}V)Tfhv&eq)sFMY{U{!_!FXL=> zZTx{c1k*w@vsk4w3=kqE!+u1x$JN{UEw0#fy~A@_(eB5@t#hA$7Jo=j4w#B;jS z0dhi*QPd{hgL}|Ybn2I1=evGt(WI>(Ah~rOYYy}FIhestki7JnJ&r+Qh5*ZUE{eF@ zKvpCr_2JaVwv>z)T=EShfXOc7oHTA}ec*ePmDJH)NOGGTZUMMzwT5;tnz2szS3ELPseuI_?1Z5tUN6S;+)r#+ zpqXDYi-EHkpL3|1czS;T6GvM{YiXn=ApV_Uhut6sPFAM20>xkh9E(Uu)vI3^YoA(Q zNq>Y}MJFn)QxHCn>pmnof@Xw}8Onu0W^*&&hIKy7scdn;TiT=z%9vUXt-;EcN6NTj^VCXU>Al zC_S-#lO~7#(_FJsQ2F=jp~%=m&ZLlYi%JiZl)QJU#0k%?d48-%ZqE~J%x^k9SrFzk z-9+8|s>EZo$0w0MVeru(Gkwx40LJCd$<{ek+13+BqB&s~rU)Ss3{JOUPZ4b#I4`~k z$4_#p=?9!LE{6tYhloS`)y^}YG&cjg4t65pB}P|zschg*?X0j)4G~we`it?StHef) zp0gP`Z5UHjz7y%HDy~wCmvCIc_1Iw63lpZ-dv@!A6B$RVhmG9awbiZ;TI#>#LULE0 zRjcpLr7xx?fyhpT>6ywhI}FE}gxZU#_}C?T~uS!(&=ApbTz3t=>Uyg%EC6 z&%n~I<`#uvHZ_J%g^yg@IxG13-B@b1sP7eru^lf}n^#-A4A5ZXkd;8gP%cg$N!oY+ z8Sc9h*?34OfAj0#20V|=ofTf%$9ukUO#KrT zcpBx%UsOzY0s5goTsZxBIxNYEs(V##dJI?l_84hm9C=@cbTNss5LH=*;@KJ75K3BG ztq3Csa4Z9V*pe2ognJ*X`uF>DCON?lccSpnenBeJcH!Z=ZF6u=3PZmI;`72AYt-hG zdXG~tOkzv@?`(yXU)6ywUO?6mY6l&&R5vf_0+Z9ne9F*9?ynE{NW0XgGpGXOQYDbp zFse!kL#4vEvEa>O4Z`n;I`+$`N5yt?Up0i z#xUqXQXvW>mhqT=^D;0A6EdCnQn>pOnt}J}tT`cbGR?WYXTx|n$f1z=oP$S8$b;Lv z?Dzu|nQz~HXxxK<-gH$Ugj{C>B4lpoVJS{t$BV!plL%ag!w|f7{kiGQ>+N$MUWZsI zxXW{)%~E3vL27|To;0~ET+xzv9~15QlBCS09(zNjwEr4th(ZzFZJB;dniNkTWRiP! z-&$_Mb_vF8&^oT}H7^6jRI!Y;N_?`~&josrexMk>fLBoV=B!DYdavR08>Y+7XP9se z0vDzk8N)G-a=ov3BGU{g&rk^B#7sBJTS@ji=BQJ}$WPDjyPj)p#rhiy34o$)RWLR_ z;o6fkGb+AFX@h0==B1K``l!Mr$1MjbJ^*_#i)NC(3(?-wUi7pESJley{J(*c+Jy28 zlGfvxKWJem3JbjwjG-VRFxxn2A3Fu5LWyvA_LeaJ_salre0LQI3mwA*+b%IfbsgIm z;J`M_qBk9Qwn~o4bEGDoRh_=q{>xZwapS((_vE>)2a})89vemRS>#pwAL)mDB4d9F zVHMm`o=jAk<>nK0soGKeCItXP1a2aOf_Xa`uX`*y^2c1%tt;jW7nvx$E9{`1!#u03 zw6{@C{#(w#plng4B_0mT?_ceQEfx4=D8oeQYzx-FMS+_+U)e?AD&xH$FDkIYSEYj* ziadCj6sD746xS7;%K6G!U0MB=pE*VEmc$Cb8hD<6i-JYM0gIb58FsO<&6xUXxj9Pq z`aX*^`g)G!(n%nX`huLhs7^6);8z1Z-Yq9;KO`!#_=wKMuWM(9zaywio^B|65RFAu zANh!CkeEnz29dWSPmmHWNoAW~^_ETTEh2Fn;ZhoEFwrjYdCI5E8jRl$3>%PAe&hDjF!2x4bwh ze66V}RJ>Bq$2|`43~O<;$ktwnzT(}_vr1)P<}G~lG57Qz9&GOWKX~wU+W+K1w4Uh} zDmvOX=+u{vO0nka-6T})_8S@V!5s!D1LF?5bCQoCuz^0-oRV&Z8+MqDZf1xHyyfK8FuY6l`pV*-`BrrSDX)0Tr1XR3a&kh-FjjoORVeG-yCh*w@#`7Gf$83wNXKt>%e^ zV&|@@z`lp^xbn7L-=s^d7Dg08cT<2&vl?ro2zJP)I_v|gmMkEFk0ysfT%Z@w4;K(C z^XMvztQ#AYh+SQU5neUapB49O?e0#$?+eZk8X(MaYrs+-G3e*#tq@=03+jc7ceM#7 z*Z%-pcE|_#y27LHVFz0cw;vwcn zwtcgYFr2EEF24aQe)UgGBibd@S7NR2r^f(h#+>KdWi6F7`ZKuu`xoHLgWb#_X1Szx zp5JWJl$Io`CEi<}q&6BG<)J@7cF5v$%0_<-m~jCRND5AixLbqT__Uu*Rqvw2t62|T zp`T${%AtMix@gk1nZ}s}Hm2|OHR`YQHAC;Z<>?^Kg)3&)eLo+ z6qfA{WbEgCwzck8a>SS%tQzbrmHtV;`aE^#dhn+ub|73ymgDg9J~Ei`yOuL5ZsAER zYw%cv{3!{CI1OJ__;7Q-`TE`>Bd}|Lf`zNgWNc{{Q&g4I^(t{6Ne&+TvL}?*ewj)_ zz4nyI)5WI|`|M>sDx&ge9;*6*4&obL2Z2uoJO_k9H4WvRjyuoo9IPb;kIFzP41cf% z<83j2cA{kv+)eolK_=s6DpT#OhdGrMl4GDY3;$)CKZzS68B&sAe~<`DVk9Uw{^he7 z48*hj_zz9Nu0kXr=C4p}G5#M^cIJ@E_y`C7@NHxkjHtt~4NFX>bS+)p%4@^i z$%o%R%-rOzfIa2R4!J`$)EkFWA%u_Qy(28aw3o);Ke<30)O!^IPqjSBc>2r<@-+QO zI(5T=`(OeC4u41ehu1wIh3b#2TZg3lsLh}s^~eF;9>Y3pl&1vlc>3FdV8sxbOSmvqOo+lqYoB8YqFk1e*S8Y3XXbP+9I8 z_%FFQQC5E+~dBABn_9exrUsGpwp5;ENv?}b7wT)_#L?-wo%VN zsy6UlccQLw;`~<=Ys@A3V*gKG5>}HO?3Ztrlw%nT@sxL;u|v_=&kv5`{J<_lCiZ!s z(ulp#^+e9JDtLhrJ#8l7XqPK}lUfB9=OT>1;;~XiGp~>g%y?2#JNF6ZPJHl^4b%O4 zI-YAR^%dlU-d0EzppLC3!53K#A)T-VsDx4Oy735{kb3)-h4sX@(R&q4Y!0zxeUjF(?$mK7WSL|P8~3O^u~JMYYQ_) zmkplI>YCWzwm8`4l#hK-bgetnyj%ri)Q05UH|H167t)#&9~}v*x*IXrM4GuxH$1fY zGIH4%hzGWbK?+8NfR_`(F{g5l7~HlgXE zO*yP;M%If&Pm^EjqQ~jE3*bJ>ONdv7YDQa@*P7H?LwQ1 zt{$imm9_>Ti$}>5fiVRFt)D&@Xcdg<;2GY%t&+~w+?CU9v?A)BIL?zt4HU_W7r^Yp z+y>r<1bun0aOYRz^#%%3VXMH>I?(~b_zI}_`C3o0S z^@4wnP8UV=Ke)6B4w3g#?3tULKkLhW6s;>_cP;=jFVbA>uq+0Ejm%@^nb1#a+yjDD zpfyU9Ht83SPcG`y1b7^3-04~-Q`n*v0cRguTTO-C4Wv4<(0UY2de`_$4)XYRO+ zJy^M;zf=J%9Tp?1diawo;hHkM%slBwZRpaQ{rsmPKVRyT1&om>+>gP8R~ zJP;cJO_QDjBju*&L#kv%`vwquuSR(Yg1h`Gqa~q7WJgF5 z_j@0G_Bee&B3}8fNq&wc%YHTeeOZ!no{p-zWl1tpzb4RS$AEk7DWM8Qu?o!FZ{L%>O$ltWDa+$!c}2?a<4SY`XkWLDe)Z8LmhO7}>M22)d$T^#rI-Mo*=8AHcw zt-L$rG5m+sHqaZ|f0o+Bz+dhhec?@e3?sLmOVCyuDRNKU`~IgU=Reft{IgKU--IOv zFgQu8zXY#%@!`mqv?ZYfT+fkGP;`XO;=qbek~CKM2=k}XmFBUM+Jj<+mjV9^=-sHw literal 0 HcmV?d00001 diff --git a/docs/database/gravity/example-domain-6.png b/docs/database/gravity/example-domain-6.png new file mode 100644 index 0000000000000000000000000000000000000000..6242511635153b93ef45b27bb31ebeb647e379c2 GIT binary patch literal 11064 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 literal 0 HcmV?d00001 diff --git a/docs/database/gravity/example-groups.png b/docs/database/gravity/example-groups.png new file mode 100644 index 0000000000000000000000000000000000000000..7d37a47ca6da473b35130213b5b15c619c029f7c GIT binary patch literal 9732 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%0D%=@>{uJ+(C*6&VX>0B-qM_cs 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 W!E2xb!1yN~n1-6JYT?bt0sjHpF*9!f literal 0 HcmV?d00001 diff --git a/docs/database/gravity/example-new-black.png b/docs/database/gravity/example-new-black.png new file mode 100644 index 0000000000000000000000000000000000000000..bd5318c5007b94653f0bd495e61dc83d202bccf8 GIT binary patch literal 4155 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 b1`t|MZH+nez-#63zYCUafJgnb+*{ZWYBVJ1*z z*Wv{lu<*@7396(UK1c#aKG>?+2Gs5V3%SqabZm8%l%s}^-Iu@GJaJ5E(}^0g(1)c= zQ1*sP|FYLsQc{8%zY5F$4v-dl3*UIzs4GHs5U8r@KO2z7V`1{}^4W>vaKtkusEK=C zpML^wo&=Px1KV}J9<|%R`VY7Y)cMyABA}pm6Zq@bGL6ugj(ko80424c9x^aW0uBeG zbgg1a>RK8r(W_W@n6X>*mzGXEzCF#yGH*VuWo2V!tUkt?H2UG0*XO#Xk_2A z^KN&LskU#+K@ajNA~8f$v5f#!4DKX4s~QusJ zQJ??V07(5Y`bq$4EXv9_2>oiW-S=HLVSE@@Q4nv2o@w<}w}-uG`dyi|36-wueuTg`BI_m+7UJPjX-|%*xGFT?SFPVh49}vQYy|s=E}Kvyx2JAP_r;nv#Nn58Zmd6g|DfdHFiKzzg0u zKq@`EP716=OcV>_Eag$vAEnO6HH0`y0=IrA+NR~tH-z4Eka4J zDX8`269^s!R^>>;szI+Mmhd{P_`kC4-o9Pv?3`6zRbXt7i

Bte5f74hWt4L^~Jn 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 literal 0 HcmV?d00001 diff --git a/docs/database/gravity/example.md b/docs/database/gravity/example.md new file mode 100644 index 0000000..f33e44c --- /dev/null +++ b/docs/database/gravity/example.md @@ -0,0 +1,264 @@ +# Per-client blocking example + +In this example, we describe how to set up a blocking rule for three specific clients. All remaining (and newly added) clients in the network are "unmanaged", i.e., they use Pi-hole as usual. The examples shown here are built upon each other, i.e., example 5 might make no sense without the context of example 3. + +Don't forget to run + +```bash +pihole restartdns reload-lists +``` + +after your database modifications to have FTL flush its internal domain-blocking cache (separate from the DNS cache). + +## Prerequisites + +1. **Add three groups.** + + The `Unassociated` group has a special meaning and cannot be deleted. All domains, clients, and adlists without a specific group assignment are automatically managed through this group. Disabling this group will disable Pi-hole blocking for all unmanaged devices. + + ![Adding three groups](example-groups.png) + +

Raw database instructions + ```sql + INSERT INTO "group" (id, name) VALUES (1, 'Group 1'); + INSERT INTO "group" (id, name) VALUES (2, 'Group 2'); + INSERT INTO "group" (id, name) VALUES (3, 'Group 3'); + ``` +
+ +2. **Add three clients.** + + Add three clients at your will, their IP addresses might differ from the ones in this example. + + ![Adding three clients](example-clients-1.png) + +
Raw database instructions + ```sql + INSERT INTO client (id, ip) VALUES (1, '192.168.0.101'); + INSERT INTO client (id, ip) VALUES (2, '192.168.0.102'); + INSERT INTO client (id, ip) VALUES (3, '192.168.0.103'); + ``` +
+ +3. **Link the clients to the created groups.** Don't forget to save each assignment by clicking on the corresponding green pen icon in the same row. + + ![Link groups and clients](example-clients-2.png) + +
Raw database instructions + ```sql + INSERT INTO client_by_group (client_id, group_id) VALUES (1, 1); + INSERT INTO client_by_group (client_id, group_id) VALUES (2, 2); + INSERT INTO client_by_group (client_id, group_id) VALUES (3, 3); + ``` +
+ +## Example 1: Exclude from blocking + +**Task:** Exclude client 1 from Pi-hole's blocking by removing client 1 from the `Unassociated` group (see comment above). + +![Change client groups assignment](example-clients-3.png) + +
Raw database instructions +```sql +DELETE FROM client_by_group WHERE client_id = 1 AND group_id = 0; +``` +
+ +**Result** + +Client | Group | Domain | Blocked +------------- | ----- | ------ | ------- +*all other* | 0 | doubleclick.net | Yes +192.168.0.101 | 1 | doubleclick.net | **No** +192.168.0.102 | 2 | doubleclick.net | Yes +192.168.0.103 | 3 | doubleclick.net | Yes +192.168.0.104 | 4 | doubleclick.net | Yes + + +All three clients got automatically assigned to the default (`Unassociated`) group when they were added. The default group includes all adlists and list domains (if not already changed by the user). When we remove the default group for client `192.168.0.101`, we effectively remove all associations to any adlists and domains. This leaves this client completely unblocked. + +## Example 2: Blocklist management + +**Task:** Assign adlist with ID 1 to group 1 (in addition to the default assignment to group 0). This results in client `192.168.0.101` using *only this* adlist (we removed the default association in the last step). + +![Change blocklist group assignment](example-adlists-1.png) + +
Raw database instructions +```sql +INSERT INTO adlist_by_group (adlist_id, group_id) VALUES (1,1); +``` +
+ +**Result** + +Client | Group | Domain | Blocked +------------- | ----- | ------ | ------- +*all other* | 0 | doubleclick.net | Yes +192.168.0.101 | 1 | doubleclick.net | **Yes** +192.168.0.102 | 2 | doubleclick.net | Yes +192.168.0.103 | 3 | doubleclick.net | Yes +192.168.0.104 | 4 | doubleclick.net | Yes + + +`192.168.0.101` gets `doubleclick.net` blocked as it uses an adlist including this domain. All other clients stay unchanged. + +## Example 3: Blacklisting + +**Task:** Add a single domain that should be **blacklisted only for group 1** (client `192.168.0.101`). + +### Step 1 + +Add the domain to be blocked + +![Add new exact blacklist domain](example-new-black.png) + +![Resulting row in list of domains](example-domain-1.png) + +
Raw database instructions +```sql +INSERT INTO domainlist (type, domain, comment) VALUES (1, 'blacklisted.com', 'Blacklisted for members of group 1'); +``` +
+ +**Result** + +Client | Group | Domain | Blocked +------------- | ----- | ------ | ------- +*all other* | 0 | blacklisted.com | Yes +192.168.0.101 | 1 | blacklisted.com | **No** +192.168.0.102 | 2 | blacklisted.com | Yes +192.168.0.103 | 3 | blacklisted.com | Yes +192.168.0.104 | 4 | blacklisted.com | Yes + + +Note that Pi-hole is *not* blocking this domain for client `192.168.0.101` as we removed the default assignment through group 0 above. All remaining clients are linked through the unassociated group to this domain and see it as being blocked. + +### Step 2 + +Assign this domain to group 1 + +![Assign group to a new domain](example-domain-2.png) + +
Raw database instructions +```sql +INSERT INTO domainlist_by_group (domainlist_id, group_id) VALUES (1, 1); +``` +(the `domainlist_id` might be different for you, check with `SELECT last_insert_rowid();` after step 1) +
+ +**Result** + +Client | Group | Domain | Blocked +------------- | ----- | ------ | ------- +*all other* | 0 | blacklisted.com | Yes +192.168.0.101 | 1 | blacklisted.com | **Yes** +192.168.0.102 | 2 | blacklisted.com | Yes +192.168.0.103 | 3 | blacklisted.com | Yes +192.168.0.104 | 4 | blacklisted.com | Yes + +All clients see this domain as being blocked. Client 1 due to a direct assignment through group 1, all remaining clients through the default group 0 (unchanged). + +### Step 3 + +Remove default assignment to all clients not belonging to a group + +![Remove unassociated group from new domain](example-domain-3.png) + +
Raw database instructions +```sql +DELETE FROM domainlist_by_group WHERE domainlist_id = 1 AND group_id = 0; +``` +(the `domainlist_id` might be different for you, see above) +
+ +**Result** + +Client | Group | Domain | Blocked +------------- | ----- | ------ | ------- +*all other* | 0 | blacklisted.com | **No** +192.168.0.101 | 1 | blacklisted.com | Yes +192.168.0.102 | 2 | blacklisted.com | **No** +192.168.0.103 | 3 | blacklisted.com | **No** +192.168.0.104 | 4 | blacklisted.com | **No** + +While client 1 keeps its explicit assignment through group 1, the remaining clients lost their unassignments when we removed group 0 from the assignment. + +## Example 4: Whitelisting + +**Task:** Add a single domain that should be **whitelisted only for group 2** (client `192.168.0.102`). + +### Step 1 + +Add the domain to be whitelisted + +![Add a new exact whitelist domain](example-new-white.png) + +![Resulting row in list of domains](example-domain-4.png) + +
Raw database instructions +```sql +INSERT INTO domainlist (type, domain, comment) VALUES (0, 'doubleclick.net', 'Whitelisted for members of group 2'); +``` +
+ +**Result** + +Client | Group | Domain | Blocked +------------- | ----- | ------ | ------- +*all other* | 0 | doubleclick.net | **No** +192.168.0.101 | 1 | doubleclick.net | Yes +192.168.0.102 | 2 | doubleclick.net | **No** +192.168.0.103 | 3 | doubleclick.net | **No** +192.168.0.104 | 4 | doubleclick.net | **No** + +Client `192.168.0.101` is not whitelisting this domain as we removed the default assignment through group 0 above. All remaining clients are linked through the default group to this domain and see it as being whitelisted. Note that this is completely analog to [step 1](#step-1_1) of [example 3](#example-3-blacklisting). + +### Step 2 + +Remove default group assignment + +![Remove default group assignment](example-domain-5.png) + +
Raw database instructions +```sql +DELETE FROM domainlist_by_group WHERE domainlist_id = 2 AND group_id = 0; +``` +
+ +**Result** + +Client | Group | Domain | Blocked +------------- | ----- | ------ | ------- +*all other* | 0 | doubleclick.net | **Yes** +192.168.0.101 | 1 | doubleclick.net | Yes +192.168.0.102 | 2 | doubleclick.net | **Yes** +192.168.0.103 | 3 | doubleclick.net | **Yes** +192.168.0.104 | 4 | doubleclick.net | **Yes** + +Requests from all clients are blocked as the new whitelist entry is not associated with any group and, hence, is not used by any client. + +### Step 3 + +Assign this domain to group 2 + +![Assign group to a new domain](example-domain-6.png) + +
Raw database instructions +```sql +INSERT INTO domainlist_by_group (domainlist_id, group_id) VALUES (2, 2); +``` +(the `domainlist_id` might be different for you, check with `SELECT last_insert_rowid();` after step 1) +
+ + +**Result** + +Client | Group | Domain | Blocked +------------- | ----- | ------ | ------- +*all other* | 0 | doubleclick.net | Yes +192.168.0.101 | 1 | doubleclick.net | **No** +192.168.0.102 | 2 | doubleclick.net | Yes +192.168.0.103 | 3 | doubleclick.net | Yes +192.168.0.104 | 4 | doubleclick.net | Yes + +Client 2 got the whitelist entry explicitly assigned to. Accordingly, client 2 does not get the domain blocked whereas all remaining clients still see this domain as blocked. diff --git a/docs/database/gravity/groups.md b/docs/database/gravity/groups.md new file mode 100644 index 0000000..49326e2 --- /dev/null +++ b/docs/database/gravity/groups.md @@ -0,0 +1,38 @@ +## Group management + +Any blocklist or domain on the white-/black-/regex-lists can be managed through groups. This allows not only grouping them together to highlight their relationship, but also enabling/disabling them together if one, for instance, wants to visit a specific service only temporarily. + +Groups are defined in the `group` table and can have an optional description in addition to the mandatory name of the group. + +Label | Type | Uniqueness enforced | Content +----- | ---- | ------------------- | -------- +`id` | integer | Yes | Unique ID for database operations +`enabled` | boolean | No | Flag whether domains in this group should be used
(`0` = disabled, `1` = enabled) +`name` | text | No | Mandatory group name +`comment` | text | No | Optional field for arbitrary user comments + +Group management is implemented using so-called linking tables. Hence, it is possible to + +- associate domains (and clients!) with any number of groups, +- manage adlists together with groups, +- use the same groups for black- and whitelisted domains at the same time. + +The linking tables are particularly simple, as they only link group `id`s with list `id`s. As an example, we describe the `domainlist_by_group` table. The `adlist` and `client` linking tables are constructed similarly. + +Label | Type | Content +----- | ---- | ------- +`domainlist_id` | integer | `id` of domain in the `domainlist` table +`group_id` | integer | `id` of associated group in the `group` table + +Group `0` is special as it is automatically assigned to domains and clients not being a member of other groups. Each newly added client or domain gets assigned to group zero when being added. + +## Effect of group management + +The great flexibility to manage domains in no, one, or multiple groups may result in unexpected behavior when, e.g., the domains are enabled in some but disabled in other groups. For the sake of convenience, we describe the possible configurations and whether *FTL*DNS uses these domains (✔) or not (✘) in such cases. + +- Domain disabled: ✘
Note that the domain is never imported by *FTL*DNS, even if it is contained in an enabled group. + +- Domain enabled: It depends... + - Not managed by a group: ✔ + - Contained in one or more groups (at least one enabled): ✔ + - Contained in one or more groups (all disabled): ✘ diff --git a/docs/database/gravity/index.md b/docs/database/gravity/index.md new file mode 100644 index 0000000..30ebc4f --- /dev/null +++ b/docs/database/gravity/index.md @@ -0,0 +1,69 @@ +Pi-hole uses the well-known relational database management system SQLite3 for managing the various domains that are used to control the DNS filtering system. The database-based domain management has been added with Pi-hole v5.0. + +## Domain tables (`domainlist`) + +The database stores white-, and blacklists which are directly relevant for Pi-hole's domain blocking behavior. The `domainlist` table contains all domains on the white- and blacklists. It has a few extra fields to store data related to a given domain such as the `enabled` state, the dates when the domain was added and when it was last modified, and an optional comment. + +The date fields are defined as `INTEGER` fields as they expect numerical timestamps also known as *UNIX time*. The `date_added` and `date_modified` fields are initialized with the current timestamp converted to UNIX time. The `comment` field is optional and can be empty. + +Pi-hole's *FTL*DNS reads the tables through the various view, omitting any disabled domains. + +Label | Type | Uniqueness enforced | Content +----- | ---- | ------------------- | -------- +`id` | integer | Yes | Unique ID for database operations +`type` | integer | No | `0` = exact whitelist,
`1` = exact blacklist,
`2` = regex whitelist,
`3` = regex blacklist +`domain` | text | Yes | Domain +`enabled` | boolean | No | Flag whether domain should be used by `pihole-FTL`
(`0` = disabled, `1` = enabled) +`date_added` | integer | No | Timestamp when domain was added +`date_modified` | integer | No | Timestamp when domain was last modified, automatically updated when a record is changed +`comment` | text | No | Optional field for arbitrary user comments, only field that is allowed to be `NULL` + +## Adlist Table (`adlist`) + +The `adlist` table contains all sources for domains to be collected by `pihole -g`. Just like the other tables, it has a few extra fields to store metadata related to a given source. + +Label | Type | Uniqueness enforced | Content +----- | ---- | ------------------- | -------- +`id` | integer | Yes | Unique ID for database operations +`address` | text | Yes | The URL of the list +`enabled` | boolean | No | Flag whether domain should be used by `pihole-FTL`
(`0` = disabled, `1` = enabled) +`date_added` | integer | No | Timestamp when domain was added +`date_modified` | integer | No | Timestamp when domain was last modified, automatically updated when a record is changed +`comment` | text | No | Optional field for arbitrary user comments + +## Gravity Table (`gravity`) + +The `gravity` table consists of the domains that have been processed by Pi-hole's `gravity` (`pihole -g`) command. The domains in this list are the collection of domains sourced from the configured sources (see the [`adlist` table](index.md#adlist-table-adlist). + +During each run of `pihole -g`, this table is flushed and completely rebuilt from the newly obtained set of domains to be blocked. + +Label | Type | Content +----- | ---- | ------- +`domain` | text | Blocked domain compiled from adlist referenced by `adlist_id` +`adlist_id` | integer | ID associated to adlists in table `adlist` + +Uniqueness is enforced on pairs of (`domain`, `adlist_id`). In other words: domains can be added multiple times, however, only when they are referencing different adlists as their origins. + +## Client table (`client`) + +Clients are identified by their IP addresses. Each client automatically gets a unique identifier (`id`). + +Label | Type | Content +----- | ---- | ------- +`id` | integer | Client ID (autoincrementing) +`ip` | text | IP address of the client (IPv4 or IPv6), Uniqueness is enforced +`date_added` | integer | Timestamp when a client was added +`date_modified` | integer | Timestamp when a client was last modified, automatically updated when a record is changed +`comment` | text | Optional field for arbitrary user comments, the only field that is allowed to be `NULL` + +## Audit Table (`domain_audit`) + +The `domain_audit` table contains domains that have been audited by the user on the web interface. + +Label | Type | Uniqueness enforced | Content +----- | ---- | ------------------- | -------- +`id` | integer | Yes | Unique ID for database operations +`domain` | text | Yes | Domain +`date_added` | integer | No | Unix timestamp when domain was added + +{!abbreviations.md!} diff --git a/docs/database/index.md b/docs/database/index.md new file mode 100644 index 0000000..4015ddf --- /dev/null +++ b/docs/database/index.md @@ -0,0 +1,8 @@ +Pi-hole uses the well-known relational database management system SQLite3 both for its long-term storage of query data and for its domain management. In contrast to many other database management solutions, Pi-hole does not need a server database engine as the database engine is directly embedded in *FTL*DNS. It seems an obvious choice as it is probably the most widely deployed database engine - it is used today by several widespread web browsers, operating systems, and embedded systems (such as mobile phones), among others. Hence, it is rich in supported platforms and offered features. SQLite implements most of the SQL-92 standard for SQL and can be used for high-level queries. + +Details concerning the databases, their contained tables and exemplary SQL commands allowing even complex requests to Pi-hole's databases are described on the subpages of this category. + +- [Query database `/etc/pihole/pihole-FTL.db`](ftl.md) +- [Domain database `/etc/pihole/gravity.db`](gravity/index.md) + +{!abbreviations.md!} diff --git a/docs/ftldns/compile.md b/docs/ftldns/compile.md index ef84d3b..4b53e03 100644 --- a/docs/ftldns/compile.md +++ b/docs/ftldns/compile.md @@ -19,12 +19,12 @@ sudo dnf install gcc gmp-devel gmp-static m4 --- -You'll also need to compile `nettle` as *FTL*DNS uses `libnettle` for handling DNSSEC. Compile and install a recent version of `nettle` (we tested 3.4): +You'll also need to compile `nettle` as *FTL*DNS uses `libnettle` for handling DNSSEC. Compile and install a recent version of `nettle` (we recommend 3.5): ```bash -wget https://ftp.gnu.org/gnu/nettle/nettle-3.4.tar.gz -tar -xvzf nettle-3.4.tar.gz -cd nettle-3.4 +wget https://ftp.gnu.org/gnu/nettle/nettle-3.5.tar.gz +tar -xvzf nettle-3.5.tar.gz +cd nettle-3.5 ./configure make sudo make install diff --git a/docs/ftldns/configfile.md b/docs/ftldns/configfile.md index d0b2cb2..c56a085 100755 --- a/docs/ftldns/configfile.md +++ b/docs/ftldns/configfile.md @@ -2,6 +2,8 @@ You can create a file `/etc/pihole/pihole-FTL.conf` that will be read by *FTL*DN Possible settings (**the option shown first is the default**): +--- + ### DNS settings #### `BLOCKINGMODE=NULL|IP-NODATA-AAAA|IP|NXDOMAIN` {#blocking_mode data-toc-label='Blocking Mode'} @@ -9,87 +11,83 @@ Possible settings (**the option shown first is the default**): How should `FTL` reply to blocked queries?
**[More details](blockingmode.md)** +#### `CNAME_DEEP_INSPECT=true|false` {#cname_deep_inspect data-toc-label='Deep CNAME inspection'} + +Use this option to disable deep CNAME inspection. This might be beneficial for very low-end devices + +--- + ### Statistics settings #### `MAXLOGAGE=24.0` {#maxlogage data-toc-label='Max Log Age'} Up to how many hours of queries should be imported from the database and logs? Maximum is 24.0 ---- - #### `PRIVACYLEVEL=0|1|2|3|4` {#privacylevel data-toc-label='Privacy Level'} Which privacy level is used?
**[More details](privacylevels.md)** ---- - #### `IGNORE_LOCALHOST=no|yes` {#ignore_localhost data-toc-label='Ignore localhost'} Should `FTL` ignore queries coming from the local machine? ---- - #### `AAAA_QUERY_ANALYSIS=yes|no` {#aaaa_query_analysis data-toc-label='AAAA Query Analysis'} Allow `FTL` to analyze AAAA queries from pihole.log? ---- - #### `ANALYZE_ONLY_A_AND_AAAA=false|true` {#analyze_only_a_and_aaaa data-toc-label='Analyze A and AAAA Only'} Should `FTL` only analyze A and AAAA queries? -### Socket settings +--- + +### Other settings #### `SOCKET_LISTENING=localonly|all` {#socket_listening data-toc-label='Socket Listening'} Listen only for local socket connections or permit all connections ---- - #### `FTLPORT=4711` {#ftlport data-toc-label='FTLDNS Port'} On which port should FTL be listening? -### Hostname resolution - #### `RESOLVE_IPV6=yes|no` {#resolve_ipv6 data-toc-label='Resolve IPV6'} Should `FTL` try to resolve IPv6 addresses to hostnames? ---- - #### `RESOLVE_IPV4=yes|no` {#resolve_ipv4 data-toc-label='Resolve IPV4'} Should `FTL` try to resolve IPv4 addresses to hostnames? -### Database settings +#### `DELAY_STARTUP=0` {#delay_startup data-toc-label='Delay resolver startup'} -**[Further details concerning the database](database.md)** +In certain configurations you may want FTL to wait a given amount of time before trying to start the DNS revolver. This is typically found when network interfaces appear only late during system startup and the interface startup prority are configured incorrectly. This setting takes any integer value between 0 and 300 seconds -#### `DBIMPORT=yes|no` {#dbimport data-toc-label='DB Import'} +--- + +### Long-term database settings + +**[Further details concerning the database](../database/index.md)** + +#### `DBIMPORT=yes|no` {#dbimport data-toc-label='Use database'} Should `FTL` load information from the database on startup to be aware of the most recent history? ---- - -#### `MAXDBDAYS=365` {#maxdbdays data-toc-label='Max DB Days'} +#### `MAXDBDAYS=365` {#maxdbdays data-toc-label='Max age'} How long should queries be stored in the database? Setting this to `0` disables the database ---- - -#### `DBINTERVAL=1.0` {#dbinterval data-toc-label='DB Interval'} +#### `DBINTERVAL=1.0` {#dbinterval data-toc-label='Storing Interval'} How often do we store queries in FTL's database [minutes]? ---- - -#### `DBFILE=/etc/pihole/pihole-FTL.db` {#dbfile data-toc-label='DB File'} +#### `DBFILE=/etc/pihole/pihole-FTL.db` {#dbfile data-toc-label='Database Filename'} Specify path and filename of FTL's SQLite3 long-term database. Setting this to `DBFILE=` disables the database altogether +--- + ### File options #### `LOGFILE=/var/log/pihole-FTL.log` {#file_LOGFILE data-toc-label='Log file'} @@ -112,70 +110,54 @@ File containing the socket FTL's API is listening on. Config file of Pi-hole containing, e.g., the current blocking status (do not change). -#### `AUDITLISTFILE=/etc/pihole/auditlog.list` {#file_AUDITLISTFILE data-toc-label='Audit list file'} - -List containing the audited domains. - #### `MACVENDORDB=/etc/pihole/macvendor.db` {#file_MACVENDORDB data-toc-label='MacVendor database file'} Database containing MAC -> Vendor information for the network table. +#### `GRAVITYDB=/etc/pihole/gravity.db` {#file_GRAVITYDB data-toc-label='Gravity database'} + +Specify path and filename of FTL's SQLite3 gravity database. This database contains all domains relevant for Pi-hole's DNS blocking + +--- + ### Debugging options #### `DEBUG_ALL=false|true` {#debug_all data-toc-label='Debug All'} Enable all debug flags. If this is set to true, all other debug config options are ignored. ---- - #### `DEBUG_DATABASE=false|true` {#debug_database data-toc-label='Debug Database'} Print debugging information about database actions. This prints performed SQL statements as well as some general information such as the time it took to store the queries and how many have been saved to the database. ---- - #### `DEBUG_NETWORKING=false|true` {#debug_networking data-toc-label='Debug networking'} Prints a list of the detected interfaces on startup of `pihole-FTL`. Also, prints whether these interfaces are IPv4 or IPv6 interfaces. ---- - #### `DEBUG_LOCKS=false|true` {#debug_locks data-toc-label='Debug Locks'} Print information about shared memory locks. Messages will be generated when waiting, obtaining, and releasing a lock. ---- - #### `DEBUG_QUERIES=false|true` {#debug_queries data-toc-label='Debug Queries'} Print extensive query information (domains, types, replies, etc.). This has always been part of the legacy `debug` mode of `pihole-FTL`. ---- - #### `DEBUG_FLAGS=false|true` {#debug_flags data-toc-label='Debug Flags'} Print flags of queries received by the DNS hooks. Only effective when `DEBUG_QUERIES` is enabled as well. ---- - #### `DEBUG_SHMEM=false|true` {#debug_shmem data-toc-label='Debug Shared Memory'} Print information about shared memory buffers. Messages are either about creating or enlarging shmem objects or string injections. ---- - #### `DEBUG_GC=false|true` {#debug_gc data-toc-label='Debug GC'} Print information about garbage collection (GC): What is to be removed, how many have been removed and how long did GC take. ---- - #### `DEBUG_ARP=false|true` {#debug_arp data-toc-label='Debug ARP'} Print information about ARP table processing: How long did parsing take, whether read MAC addresses are valid, and if the `macvendor.db` file exists. ---- - #### `DEBUG_REGEX=false|true` {#debug_regex data-toc-label='Debug REGEX'} Controls if *FTL*DNS should print extended details about regex matching into `pihole-FTL.log`.
@@ -184,28 +166,32 @@ Due to legacy reasons, we also support the following setting to be used for enab Note that if one of them is set to `true`, the other one cannot be used to disable this setting again.
**[More details](regex/overview.md)** ---- - #### `DEBUG_API=false|true` {#debug_api data-toc-label='Debug Telnet'} Print extra debugging information during telnet API calls. Currently only used to send extra information when getting all queries. ---- - #### `DEBUG_OVERTIME=false|true` {#debug_overtime data-toc-label='Debug overTime'} Print information about overTime memory operations, such as initializing or moving overTime slots. ---- - #### `DEBUG_EXTBLOCKED=false|true` {#debug_extblocked data-toc-label='Debug externally blocked'} Print information about why FTL decided that certain queries were recognized as being externally blocked. ---- - #### `DEBUG_CAPS=false|true` {#debug_caps data-toc-label='Debug Linux capabilities'} Print information about capabilities granted to the pihole-FTL process. The current capabilities are printed on receipt of `SIGHUP`, i.e., the current set of capabilities can be queried without restarting `pihole-FTL` (by setting `DEBUG_CAPS=true` and thereafter sending `killall -HUP pihole-FTL`). +#### `DEBUG_DNSMASQ_LINES=false|true` {#debug_dnsmasq_lines data-toc-label='Debug dnsmasq lines'} + +Print file and line causing a `dnsmasq` event into FTL's log files. This is handy to implement additional hooks missing from FTL. + +#### `DEBUG_VECTORS=false|true` {#debug_vectors data-toc-label='Debug FTL vectors'} + +FTL uses dynamically allocated vectors for various tasks. This config option enables extensive debugging information such as information about allocation, referencing, deletion, and appending. + +#### `DEBUG_RESOLVER=false|true` {#debug_resolver data-toc-label='Debug FTL resolver'} + +Extensive information about host name resolution like which DNS servers are used in the first and second host name resolving tries (only affecting internally generated PTR queries). + {!abbreviations.md!} diff --git a/docs/ftldns/database.md b/docs/ftldns/database.md index e7bba7e..fd42c2f 100644 --- a/docs/ftldns/database.md +++ b/docs/ftldns/database.md @@ -1,137 +1,3 @@ -Pi-hole *FTL*DNS uses the well-known relational database management system SQLite3 as its long-term storage of query data. In contrast to many other database management solutions, *FTL*DNS does not need a server database engine as the database engine is directly embedded in *FTL*DNS. It seems an obvious choice as it is probably the most widely deployed database engine - it is used today by several widespread web browsers, operating systems, and embedded systems (such as mobile phones), among others. Hence, it is rich in supported platforms and offered features. SQLite implements most of the SQL-92 standard for SQL and can be used for high-level queries. - -We update the database file periodically and on exit of *FTL*DNS (triggered e.g. by a `service pihole-FTL restart`). The updating frequency can be controlled by the parameter [`DBINTERVAL`](configfile.md#dbinterval) and defaults to once per minute. We think this interval is sufficient to protect against data losses due to power failure events. *FTL*DNS needs the database to populate its internal history of the most recent 24 hours. If the database is disabled, *FTL*DNS will show an empty query history after a restart. - -The location of the database can be configured by the config parameter [`DBFILE`](configfile.md#dbfile). It defaults to `/etc/pihole/pihole-FTL.db`. If the given file does not exist, *FTL*DNS will create a new (empty) database file. - -Another way of controlling the size of the long-term database is setting a maximum age for log queries to keep using the config parameter [`MAXDBDAYS`](configfile.md#maxdbdays). It defaults to 365 days, i.e. queries that are older than one year get periodically removed to limit the growth of the long-term database file. - -The config parameter [`DBIMPORT`](configfile.md#dbimport) controls whether `FTL` loads information from the database on startup. It needs to do this to populate the internal data structure with the most recent history. However, as importing from the database on disk can delay FTL on very large deploys, it can be disabled using this option. - --- - -### Split database - -You can split your long-term database by periodically rotating the database file (do this only when `pihole-FTL` is *not* running). The individual database contents can easily be merged when required. -This could be implemented by running a monthly `cron` job such as: - -```bash -sudo service pihole-FTL stop -sudo mv /etc/pihole/pihole-FTL.db /media/backup/pihole-FTL_$(date +"%m-%y").db -sudo service pihole-FTL start -``` - -Note that DNS resolution will not be available as long as `pihole-FTL` is stopped. - -### Backup database - -The database can be backed up while FTL is running when using the SQLite3 Online backup method, e.g., - -```bash -sqlite3 /etc/pihole/pihole-FTL.db ".backup /home/pi/pihole-FTL.db.backup" -``` - -will create `/home/pi/pihole-FTL.db.backup` which is a copy of your long-term database. - +redirect: /database/ --- - -The long-term database contains three tables: - -### Query Table - -Label | Type | Allowed to by empty | Content ---- | --- | ---- | ----- -`id` | integer | No | autoincrement ID for the table, only used by SQLite3, not by *FTL*DNS -`timestamp` | integer | No | Unix timestamp when this query arrived at *FTL*DNS (used as index) -`type` | integer | No | Type of this query (see [Supported query types](database.md#supported-query-types)) -`status` | integer | No | How was this query handled by *FTL*DNS? (see [Supported status types](database.md#supported-status-types)) -`domain` | text | No | Requested domain -`client` | text | No | Requesting client (IP address) -`forward` | text | Yes | Forward destination used for this query (only set if `status == 2`) - -SQLite3 syntax used to create this table: - -```sql -CREATE TABLE queries ( id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp INTEGER NOT NULL, type INTEGER NOT NULL, status INTEGER NOT NULL, domain TEXT NOT NULL, client TEXT NOT NULL, forward TEXT ); -CREATE INDEX idx_queries_timestamps ON queries (timestamp); -``` - -### Counters table - -This table contains counter values integrated over the entire lifetime of the table - -Label | Type | Allowed to by empty | Content ---- | --- | ---- | ----- -`id` | integer | No | ID for the table used to select a counter (see below) -`value` | integer | No | Value of a given counter - -Counter ID | Interpretation ---- | --- -0 | Total number of queries -1 | Total number of blocked queries (Query `status` 1, 4 or 5) - -SQLite3 syntax used to create this table: - -```sql -CREATE TABLE counters ( id INTEGER PRIMARY KEY NOT NULL, value INTEGER NOT NULL ); -``` - -### FTL table - -The FTL table contains some data used by *FTL*DNS for determining which queries to save to the database. This table does not contain any entries of general interest. - -SQLite3 syntax used to create this table: - -```sql -CREATE TABLE ftl ( id INTEGER PRIMARY KEY NOT NULL, value BLOB NOT NULL ); -``` - -### Supported query types - -ID | Query Type ---- | --- -1 | A -2 | AAAA -3 | ANY -4 | SRV -5 | SOA -6 | PTR -7 | TXT - - - - - -### Supported status types - -ID | Query Type ---- | --- -0 | Unknown status (was not answered by forward destination) -1 | Blocked by `gravity.list` -2 | Permitted + forwarded -3 | Permitted + replied to from cache -4 | Blocked by wildcard -5 | Blocked by `black.list` -6 | Blocked by upstream server (known blocking page IP address) -7 | Blocked by upstream server (`0.0.0.0` or `::`) -8 | Blocked by upstream server (`NXDOMAIN` with `RA` bit unset) - -### Example for interaction with the FTL long-term database - -In addition to the interactions the Pi-hole database API offers, you can also run your own SQL commands against the database. If you want to obtain the three most queries domains for all time, you could use - -```bash -sqlite3 "/etc/pihole/pihole-FTL.db" "SELECT domain,count(domain) FROM queries WHERE (STATUS == 2 OR STATUS == 3) GROUP by domain order by count(domain) desc limit 3" -``` - -which would return something like - -```text -discourse.pi-hole.net|421095 -www.pi-hole.net|132483 -posteo.de|130243 -``` - -showing the domain and the number of times it was found in the long-term database. Note that such a request might take very long for computation as the entire history of queries have to be processed for this. - -{!abbreviations.md!} diff --git a/docs/ftldns/debugging.md b/docs/ftldns/debugging.md index c52ff54..99644da 100644 --- a/docs/ftldns/debugging.md +++ b/docs/ftldns/debugging.md @@ -1,13 +1,22 @@ Once you are used to it, you can skip most of the steps. Debugging *FTL*DNS is quite easy. `pihole-FTL` has been designed so that a debugger can be attached to an already running process. This will give you insights into how software (not limited to `pihole-FTL`) works. +### Prerequeirements (only required once) + 1. Install `screen` and `gdb` using `sudo apt-get install screen gdb` 2. Start a screen session (it will allow you to come back even if the SSH connection died) * If you don't know about `screen`, then read about it (you *will* love it!) 3. Start a screen session using `screen` -4. Use `sudo gdb -p $(pidof pihole-FTL)` to attach the debugger to the already running `pihole-FTL` process -5. Once loading of the symbols has finished (the `(gdb)` input prompt is shown), run `handle SIGHUP nostop SIGPIPE nostop` -6. Enter `continue` to continue the operation of `pihole-FTL` inside the debugger. All debugger features are now available. -7. When `pihole-FTL` has crashed, copy & paste the terminal output into a (new) issue. Also, type `backtrace` and include its output. We might ask for additional information in order to isolate your particular issue. +4. Configure `gdb` by installing a globally valid intialization file: + ```bash + echo "handle SIGHUP nostop SIGPIPE nostop SIGTERM nostop SIG32 nostop SIG34 nostop SIG35 nostop" | sudo tee /root/.gdbinit + ``` + You can omit this step, however, you will have to remember to run the quoted line on *every start* of `gdb` in order to properly debug FTL. + +### Start of debugging session + +1. Use `sudo gdb -p $(pidof pihole-FTL)` to attach the debugger to the already running `pihole-FTL` process +2. Once loading of the symbols has finished (the `(gdb)` input prompt is shown), enter `continue` to continue the operation of `pihole-FTL` inside the debugger. All debugger features are now available. +3. When `pihole-FTL` has crashed, copy & paste the terminal output into a (new) issue. Also, type `backtrace` and include its output. We might ask for additional information in order to isolate your particular issue. diff --git a/docs/ftldns/regex/overview.md b/docs/ftldns/regex/overview.md index baad6d4..ae5a932 100644 --- a/docs/ftldns/regex/overview.md +++ b/docs/ftldns/regex/overview.md @@ -1,17 +1,27 @@ -A regular expression, or RegEx for short, is a pattern that can be used for building arbitrarily complex blocking rules in *FTL*DNS. +A regular expression, or RegEx for short, is a pattern that can be used for building arbitrarily complex filter rules in *FTL*DNS. We implement the POSIX Extended Regular Expressions similar to the one used by the UNIX `egrep` (or `grep -E`) command. Our implementation is light and fast as each domain is only checked once for a match (if you query `google.com`, it will be checked against your RegEx. Any subsequent query to the same domain will not be checked again until you restart `pihole-FTL`). -## How to use regular expressions for blocking +## Hierarchy of regex filters in *FTL*DNS -*FTL*DNS reads in regular expression filters from `/etc/pihole/regex.list` (one expression per line, lines starting with `#` will be skipped). -To tell *FTL*DNS to reload the list, either: +*FTL*DNS uses a specific hierarchy to ensure regex filters work as you expect them to. Whitelisting always has priority over blacklisting. +There are two locations where regex filters are important: -- Execute the `>recompile-regex` API command (`echo ">recompile-regex" | nc localhost 4711`) or +1. On loading the blocking domains form the `gravity` database table, *FTL*DNS skips not only exactly whitelisted domains but also those that match enabled whitelist regex filters. +2. When a queried domain matches a blacklist regex filter, the query will *not* be blocked if the domain *also* matches an exact or a regex whitelist entry. + +## How to use regular expressions for filtering domains + +*FTL*DNS reads in regular expression filters from the two [`regex` database views](../../database/gravity/index.md). +To tell *FTL*DNS to reload the list of regex filters, either: + +- Execute `pihole restartdns reload-lists` or - Send `SIGHUP` to `pihole-FTL` (`sudo killall -SIGHUP pihole-FTL`) or - Restart the service (`sudo service pihole-FTL restart`) +The first command is to be preferred as it ensures that the DNS cache itself remains intact. Hence, it is also the fastest of the available options. + ## Pi-hole Regex debugging mode To ease the usage of regular expression filters in *FTL*DNS, we offer a regex debugging mode. Set @@ -25,11 +35,11 @@ in your `/etc/pihole/pihole-FTL.conf` and restart `pihole-FTL` to enable or disa Once the debugging mode is enabled, each match will be logged to `/var/log/pihole-FTL.log` in the following format: ```text -[2018-07-17 17:40:51.304] Regex in line 2 "((^)|(\.))twitter\." matches "whatever.twitter.com" +[2018-07-17 17:40:51.304] Regex blacklist (DB ID 15) >> MATCH: "whatever.twitter.com" vs. "((^)|(\.))twitter\." ``` -The given line number corresponds to the line in the file `/etc/pihole/regex.list`. +The given DB ID corresponds to the ID of the corresponding row in the `domainlist` database table. -Note that validation is only done on the first occurrence of a domain to increase the computational efficiency of *FTL*DNS. +Note that validation is only done on the first occurrence of a domain to increase the computational efficiency of *FTL*DNS. The result of this evaluation is stored in an internal DNS cache that is separate from `dnsmasq`'s own DNS cache. This allows us to only flush this special cache when modifying the black- and whitelists *without* having to flush the entire DNS cache collected so far. {!abbreviations.md!} diff --git a/docs/ftldns/regex/tutorial.md b/docs/ftldns/regex/tutorial.md index 30ab211..d96962f 100644 --- a/docs/ftldns/regex/tutorial.md +++ b/docs/ftldns/regex/tutorial.md @@ -1,6 +1,6 @@ # Pi-hole regular expressions tutorial -We provide a short but thorough introduction to our regular expressions implementation. This may come in handy if you are designing blocking rules (see also our cheat sheet below!). In our implementation, all characters match themselves except for the following special characters: `.[{}()\*+?|^$`. If you want to match those, you need to escape them like `\.` for a literal period, but no rule without exception (see character groups below for further details). +We provide a short but thorough introduction to our regular expressions implementation. This may come in handy if you are designing blocking or whitelisting rules (see also our cheat sheet below!). In our implementation, all characters match themselves except for the following special characters: `.[{}()\*+?|^$`. If you want to match those, you need to escape them like `\.` for a literal period, but no rule without exception (see character groups below for further details). ## Anchors (`^` and `$`) diff --git a/mkdocs.yml b/mkdocs.yml index d64f199..59d331c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -60,6 +60,13 @@ nav: - 'Updating': main/update.md - 'Pi-hole Core': - 'The pihole command': core/pihole-command.md + - 'Databases': + - 'Overview': database/index.md + - 'Query database': database/ftl.md + - 'Domain database': + - 'Overview': database/gravity/index.md + - 'Group management': database/gravity/groups.md + - 'Examples': database/gravity/example.md - 'FTLDNS': - 'Overview': ftldns/index.md - 'Configuration': ftldns/configfile.md @@ -70,7 +77,6 @@ nav: - "Overview": ftldns/regex/overview.md - "Tutorial": ftldns/regex/tutorial.md - 'Privacy levels': ftldns/privacylevels.md - - 'Long-term database': ftldns/database.md - 'Telnet API': ftldns/telnet-api.md - 'Compatibility': ftldns/compatibility.md - 'Install from source': ftldns/compile.md