From mboxrd@z Thu Jan 1 00:00:00 1970 From: Adolf Belka <adolf.belka@ipfire.org> To: development@lists.ipfire.org Subject: Re: [PATCH] RPZ: update code to include WEBGUI and additional languages Date: Sat, 01 Mar 2025 11:18:14 +0100 Message-ID: <66701a67-cdf8-423b-ad4b-e9e44d598f1c@ipfire.org> In-Reply-To: <A985611A-C6E2-4EFA-B996-5C97B9F084CB@ipfire.org> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============8878883580042416022==" List-Id: <development.lists.ipfire.org> --===============8878883580042416022== Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Hi Jon, Would you like to have this topic added to the agenda for the next Dev=20 Conf Call scheduled for 10th March? The time of the conf call is 8:00 to 10:00pm Central European Time. Best regards, Adolf. On 14/02/2025 13:07, Michael Tremer wrote: > Hello Jon, > > It very much depends on the kind of contribution. A one-line patch obviousl= y has fewer strings attached to it than a larger patch set like this. > > However, we have outlined the process in the wiki already, starting from he= re: > > https://www.ipfire.org/docs/devel/submit-patches > > This contains some useful pointers about the how (how do I actually make my= changes happen, how do I build IPFire, etc), and at the bottom it contains a= lot of information about the format the changes should be submitted; split i= nto smaller chunks that are ideally as independent from each other so that th= ey can be individually reviewed and merged. Usually a development process tak= es long time and we have already shipped parts of code that we will need for = certain features that are not ready yet. This is a good practice to let code = mature, especially when it is touching rather critical bits like the firewall= and networking stacks. > > There are also some guidelines on how to write a good commit message and ho= w to use Git tags: > > https://www.ipfire.org/docs/devel/git/commit-messages > https://www.ipfire.org/docs/devel/git/tags > > Then there is something about how to get in touch with the right person and= legal stuff: > > https://www.ipfire.org/docs/devel/contact > https://www.ipfire.org/docs/legal/ipca > > Finally, we have a bit of an underused roadmap section on the wiki. It woul= d be nice if we could use that a little bit more because then it would be eas= ier for everyone to keep track of progress on certain features; people could = see what is being worked on and see if they can help development and testing = and so on: > > https://www.ipfire.org/docs/roadmap > > There is a template on how to create new pages: > > https://www.ipfire.org/docs/roadmap/template > > And this is a good example of what this could look like: > > https://www.ipfire.org/docs/roadmap/openvpn-26 > > All of these steps are coming *after* there has been some initial discussio= n about what actually has a chance to become part of the distribution. For th= at, we do not have any specific guidelines because it is not very trivial to = write these things. There are just too many possibilities. In the past, there= has also been very little need for this, but that does not mean that there h= ave not been problems before. > > The reason why I am raising the bar this high here is simply that we have m= ade mistakes in the past that we don=E2=80=99t want to repeat. We have learne= d a couple of lessons in a not very pleasant way and I under no circumstances= would want to do this again. The objective is that we want to provide an exc= ellent distribution. Although IPFire of course has its shortcomings here and = there, it is a very stable distribution and we have a very good track record = that I want to keep. This is what our users deserve. > > In the past, people have =E2=80=9Cdropped=E2=80=9D their patches on this li= st (or sometimes elsewhere) and we were left with dealing with the entire int= egration only to find out later what problems there were hidden in the code. = The original author(s) had no interest in fixing any of that because it worke= d just fine for them, and so why spend any time on the problems of somebody e= lse? Usually I am the fallback for this and I simply don=E2=80=99t want to be= that. I have lots of my own projects inside IPFire that are moving at snail = speed because fixing existing code usually takes priority over writing new co= de. > > Therefore we need a commitment to sort out these problems in the first plac= e. It has to be proven that people actually *care* about the patches that the= y post here. I am not sure this needs writing down as this should be the same= policy with almost any open source project. If you contribute a line, there = is probably less maintenance required in the future, but if you contribute a = large code base, then you will need to look after it for the foreseeable futu= re. It is your feature and not mine after all. > > Then, what actually has a chance to make it into the distribution? Probably= not a lot. IPFire has a very clear use case. There will not be any space for= a desktop environment and running Chrome on it, we also don=E2=80=99t it to = make coffee and cook me a dinner. We would currently only accept things that = were actually maintainable by the current team in case a contributor moves on= (see above), because we simply only have so much man power. We already have = a large zoo of features that are very abandoned and we are potentially lookin= g at getting rid of more things simply because we cannot support them properl= y. Time just doesn=E2=80=99t permit. Adding something large is therefore very= difficult at the moment. > > I understand that in this specific case you have been trying to not involve= the development team and I understand your motivation. But you cannot forget= about how much time and effort a review process can take. Therefore we want = to plan things well; we want to even split it; and we want to have a conversa= tion in advance so that the roadmap is clear and the actual code review ideal= ly only becomes a formality. > > All of this above has been for a general case. Please read through this and= feel free to ask any questions if something isn=E2=80=99t clear. > > To move forward with this feature, we should start by planning a roadmap. W= e need to discuss what this project should cover and what it should not cover= . I believe we don=E2=80=99t need to talk much about implementation details b= ecause you have figured out a lot of them; we need to find what feature we wa= nt to provide to our users. Are you up for that? > > Best, > -Michael > >> On 13 Feb 2025, at 21:34, jon <jon.murphy(a)ipfire.org> wrote: >> >> Michael, >> >> I=E2=80=99ve read through your comments a few times and I ended up with ma= ny more questions. >> >> >>> What I rather mean is that it has never been added as a topic on the agen= da and it has not been pitched by yourself. >> >> To me the efforts to get new code accepted seem to have changed and it see= med easier in the past. In the past I made the Core Team aware via the Dev M= ailing List and wrote a simple two or three paragraphs of "What is it? / What= is the value? / Here is the code" >> >> >> So in an effort to move forward: How exactly is something presented to th= e Core Team? >> >> Is there an example of a recent effort that was presented that I can see a= s a sample? (This type of info can also be added to the Wiki) >> >> I understand you want it this way, but I don=E2=80=99t know what exactly i= s needed. Please be specific. >> >> >> Jon >> >> PS - I am not ignoring your other comments, I am just trying to move forwa= rd and keep things simple. >> >> >> >>> On Feb 8, 2025, at 1:27=E2=80=AFPM, Michael Tremer <michael.tremer(a)ipfi= re.org> wrote: >>> >>> Hello Jon, >>> >>> Thanks for your reply. And good that you are copying everyone into this c= onversation. >>> >>>> On 8 Feb 2025, at 18:41, jon <jon.murphy(a)ipfire.org> wrote: >>>> >>>> Michael, >>>> >>>>> I think I have covered this all at lengths before that this project has= been started as a separate effort >>>> Yes, this has been a separate effort (a very public separate effort). Y= es, as you pointed this out early on with the "proof-of-concept" and then my = request for people to help test RPZ. Nothing was hidden. >>>> >>>> This was done because you (and maybe others) did not have the time and I= wanted to help and because I needed assistance with RPZ. I tried my best to= do this without bothering you. >>> I don=E2=80=99t that it is accurate that nobody wanted to help on this. T= he list was always open - although not every email has been replied to swiftl= y it is also your responsibility to raise a question again if it was missed. = People here have open ears. >>> >>> It was also stated on this very list on in our documentation that working= on something without involving the core team is a risky undertaking. Of cour= se IPFire is free software and so everyone is free to fork if they wish to do= so. >>> >>>>> and as far as I am aware none of the other team members has been involv= ed. This has not been discussed either on this list, on our calls. >>>> You were aware many steps along the way. See your email on July 28, 202= 4, August 15, 2024, September 30, 2024, December 23, 2024, and January 16. M= y attempts to get the team involved were met with "things are busy" and somet= imes silence. (Yes, I get it, people are busy.) >>>> >>>> You and Adolf, Leo, Erik and Bernhard have been aware since the beginnin= g. You mention you were aware of the "proof-of-concept". If you include tho= se beginning posts, since Sep 2023. >>> Yes, I am aware of a proof-of-concept that I have been running myself for= a long time. I am also aware of the efforts that you have been taking. >>> >>> Yet I don=E2=80=99t think there has ever been any joint effort, or am I s= eeing that wrong? >>> >>>>> This has not been discussed . . . on our calls. >>>> On the July 28th you stated: >>>> "We have talked about RPZ many times on the monthly call since the URL f= ilter feature is falling more and more out of fashion. I think there is also = many posts about this on the forum." >>>> >>>> Please don=E2=80=99t insult me again by stating "you know what I mean". >>>> >>>> And it has been discussed but not documented in the Monthly Meeting note= s. >>> I am not at all insulting you. I don=E2=80=99t want to take this down to = a personal level at all. This is a public mailing list and people who read th= is don=E2=80=99t need to listen to an argument we are having. They are here f= or the tech inside IPFire. >>> >>> When I wrote that it has not been discussed that does not mean that we ha= ve not been touching on the topic. We have been talking about lots of things = on the calls, the weather, politics, how our pets are. None of that makes it = to the logs. What I rather mean is that it has never been added as a topic on= the agenda and it has not been pitched by yourself. >>> >>>>> Instead there has been a separate conversation on the forum with the oc= casional dip here to the list. But that was not a regular two-way conversatio= n. >>>> Regular conversation on the Dev Mailing list is many times met with sile= nce. I get it, people are busy. >>>> >>>> And regular two-way conversation doesn=E2=80=99t happen on the list. At= least not with me. I=E2=80=99d be happy to point out the posts that were me= t with silence. >>>> Again, I get it, people are busy. >>> And you think my emails are not being met with silence? This has nothing = to do with this specific topic. This has something to do with how occupied pe= ople are and how engaged they are on certain topics. Not everyone is involved= in all the things and simply will ignore emails simply based on their subjec= t line. >>> >>>> But the "dip here to the list" were my attempts to get a conversation st= arted. As I said, many time met with silence. >>>> >>>> The only place I was not met with silence was on the Community. You hav= e a great group of people in the Community. It is a shame you don=E2=80=99t = want to have others help. It would reduce your workload. >>> You should stop making statements that are not true. Who doesn=E2=80=99t = want anyone to help? >>> >>> Not having this conversation on a Saturday evening would reduce my worklo= ad. At least it would free up time for something else. Helping with the thing= s that are already on the go would reduce the workload of the entire team. St= arting one thing at a time and finishing it is a lot better to manage than st= arting a hundred things and not even finish one. I can tell you that I alread= y have a hundred things on the go. >>> >>>>> Therefore, what am I supposed to do with this email? >>>> To me it is beyond obvious=E2=80=A6 >>>> >>>> If it isn=E2=80=99t what you want, then guide me with how to do this the= correct way. And be specific. I am trying to help. I am trying to make th= ings better. I am trying to do things the right way. >>> To me it isn=E2=80=99t. This is yet another project that has been dumped = to the list like so many before and later on everyone has left to have the te= am deal with the rest. >>> >>> It is a huge patch set. You explained what the vision is, but that is abo= ut it. There is no chance this will continue if this disagreement isn=E2=80= =99t solved first. I didn=E2=80=99t even look at the code. >>> >>>>> I don=E2=80=99t want to merge code that I don=E2=80=99t agree with. >>>> I asked multiple times if you "agreed with the concept" and again, met w= ith silence. Yes I get it, people are busy. >>> Having support for RPZ? Yes, it was definitely on the roadmap. That I agr= ee with. >>> >>>>> So many fundamental things that I have been raising have either not bee= n discussed or outright dismissed. >>>> You mentioned this a in the past, but for some reason you do not disclos= e what I dismissed. Why do you continue to make this harder, wouldn=E2=80=99= t it not be easier to tell me what I have dismissed? >>>> >>>> I have sent multiple emails trying to answer your concerns and comments.= On July 28, Aug 14, Aug 22, Aug 23, Sep 30, etc. >>>> >>>> I=E2=80=99ve gone through all of the questions you asked and I cannot fi= nd a "dismissed" item. >>> Maybe I need to be *more clear*. I feel humoured by this. >>> >>> It is late on a Saturday and I want my dinner soon, but certainly I have = stated that this should never be an add-on considering it is supposed to repl= ace URL Filter. We should never allow people to add their own sources. I have= also stated that we cannot download any lists over HTTPS again and again and= again. The implementation that we have here seems to exactly do that and the= refore I think that my feedback has been dismissed entirely. >>> >>>>> I don=E2=80=99t want to merge code that has no future inside IPFire as = there is no constructive conversation with the maintainers of it. >>>> The maintainers of Unbound and/or RPZ? >>>> >>>> The maintainers of Hagezi list, the threatfox list, the urlhaus list, et= c.? >>>> >>>> What else? The maintainers or the RPZ scripts? That is me. Let=E2=80= =99s talk! >>> You. I don=E2=80=99t care much about the providers of the lists. >>> >>>> See, this is where it gets confusing. There are hundreds of open source= packages as part of IPFire. Pick the last five years of items added to the = IPFire build. You're telling me you have "constructive conversation with the= maintainers" of all of the added packages? >>> They publish their software and they don=E2=80=99t care whether I am pull= ing it or not. They publish it with the commitment to maintain it - sometimes= for better and sometimes for worse. >>> >>> You care about me pulling your code and I don=E2=80=99t know whether you = would commit to maintain this. >>> >>> These two are very different cases. >>> >>>> Pick the IP Blocklists list (i.e., 3CORESEC, ABUSECH, DSHIELD, SPAMHAUS,= etc.) or the Suricata lists (i.e., Emergingthreats.net, Abuse.ch, etc.). S= o you=E2=80=99ve have "constructive conversation with the maintainers"? >>> Yes, occasionally I have phone calls with a few of these providers. >>> >>>>> Having been trying for a long time to make you aware of this, nothing o= f this should come as a surprise. >>>> Ha! Yes a surprise. In the beginning you seemed interested as IPFire n= eeded a replacement for URL Filter. You asked good questions about the lists= picked, asked for the value to the users, etc. And I answered the best I co= uld. >>>> >>>> You even asked: =E2=80=9CWhy is this realised as an add-on and not part = of the core system?=E2=80=9D from your Jul 28, 2024 email. >>> Ah, so, why is the patch creating an add-on? Not that I am saying that wh= at I say is law, but it has not been challenged either. If my input is being = ignored, why should I put this to the top of my list of priorities? I am not = disappointed about this, just trying to be very good with my time. >>> >>>> And on January 16, 2025 I wrote a message looking for help. And you wer= e kind to respond quickly. So in three weeks time, since the kind response, = something has changed. You went from supportive to "this". >>>> >>>> So yes, I am surprised. >>> Well, maybe I should not have replied to that email. It was clear that yo= u were on some path that was not right, but you were not interested before in= finding the right path from the beginning. >>> >>>>> Please consider if that can be changed and if there is a path forward w= ith this. >>>> Be more specific, what has to change? What exactly did I dismiss? >>> Dismissal is just my assumption. I don=E2=80=99t know what you actually d= id with my feedback. I can only see the end product that does not seem contai= n much of it. Repeatedly I have been pointing out that we should think before= we build. I am sure a lot of hours have now gone into some code that simply = does not satisfy me. And I am not not talking about the code itself, what it = does is what I don=E2=80=99t think is right for us. >>> >>> The process is very clear for me that we should first of all think whethe= r we want a certain feature now. Then there should be a clear roadmap for eve= ryone to follow; tasks can be split-up as we go and hopefully then have somet= hing that is maintainable, interesting for our users and even would do us pro= ud. This is how this should work. >>> >>> So, what has to change? I don=E2=80=99t think with shouting at each other= , throwing patches around and making me generally unhappy is a good start. >>> >>> -Michael >>> >>>> Jon >>>> >>>> >>>> >>>>> On Feb 6, 2025, at 2:13=E2=80=AFPM, Michael Tremer <michael.tremer(a)ip= fire.org> wrote: >>>>> >>>>> Hello Jon, >>>>> >>>>> Well, here we are again with another patch regarding this feature. >>>>> >>>>> I cannot quite see from your email what the question is, but if this is= a request to have this merged into IPFire, I am once again sorry to disappoi= nt you. >>>>> >>>>> I think I have covered this all at lengths before that this project has= been started as a separate effort and as far as I am aware none of the other= team members has been involved. This has not been discussed either on this l= ist, on our calls. Instead there has been a separate conversation on the foru= m with the occasional dip here to the list. But that was not a regular two-wa= y conversation. Therefore, what am I supposed to do with this email? >>>>> >>>>> I don=E2=80=99t want to merge code that I don=E2=80=99t agree with. So = many fundamental things that I have been raising have either not been discuss= ed or outright dismissed. >>>>> >>>>> I don=E2=80=99t want to merge code that has no future inside IPFire as = there is no constructive conversation with the maintainers of it. >>>>> >>>>> Having been trying for a long time to make you aware of this, nothing o= f this should come as a surprise. >>>>> >>>>> Please consider if that can be changed and if there is a path forward w= ith this. >>>>> >>>>> All the best, >>>>> -Michael >>>>> >>>>>> On 6 Feb 2025, at 16:35, Jon Murphy <jon.murphy(a)ipfire.org> wrote: >>>>>> >>>>>> What is it? >>>>>> Response Policy Zone (RPZ) is a mechanism to define local policies in a >>>>>> standardized way and load those policies from external sources. >>>>>> Bottom line: RPZ allows admins to easily block access to websites via = DNS lookup. >>>>>> >>>>>> RPZ can block websites via categories. Examples include: fake website= s, annoying >>>>>> pop-up ads, newly registered domains, DoH bypass sites, bad "host" ser= vices, >>>>>> maliscious top level domains (e.g., *.zip, *.mov), piracy, gambling, p= ornography, >>>>>> and more. RPZ lists come from various RPZ providers and their availab= le >>>>>> catagories. >>>>>> >>>>>> This RPZ add-on enables the RPZ functionality by adding a couple lines= in a >>>>>> configuration file. This add-on simply adds configuration files and a= dds >>>>>> scripts (config, metrics and sleep) to make RPZ easier for the admin t= o use. >>>>>> >>>>>> The RPZ scripts include additional languages: German, Spanish, French,= Turkish, >>>>>> and Italian. >>>>>> >>>>>> RPZ itself was release in 2010 and has been part of the IPFire build s= ince ~2015. >>>>>> >>>>>> Why is it needed? What is its value? >>>>>> >>>>>> - The RPZ concept places this filtering into IPFire, our internet acce= ss >>>>>> gateway, which is (should be) solely used as DNS source of the interna= l network. >>>>>> >>>>>> - As most sites use HTTPS it makes it difficult to filter traffic with= URL >>>>>> Filter without also properly configuring conventional (non-transparent) >>>>>> mode on the proxy. RPZ is a nice replacement for the URL Filter. >>>>>> >>>>>> - No need to install and maintain an additional device like PiHole or = AdBlock >>>>>> browser extensions on multiple user devices. >>>>>> >>>>>> - This is an additional layer of protection for users. Less worry some= one will >>>>>> click on something that gets them into trouble. And, saying this with = emphasis, >>>>>> the ability to do it in one place! >>>>>> >>>>>> - Blocked sites save on unneeded traffic and can lessen the threat of = malware >>>>>> in advertisements >>>>>> >>>>>> - Logging allows the admin to see the site blocked and take actions >>>>>> >>>>>> - RPZ will be used at the home, home-office (work from home), schools, >>>>>> ministerial, and at the office. Device counts are small (2-6) to medi= um (~80) >>>>>> to mediam-large (200+). >>>>>> >>>>>> - RPZ can block ads, popups, phishing, scammers, spyware, malware, ann= oying >>>>>> popups, NSFW links, DOH servers, and the usual internet trash. >>>>>> >>>>>> ------------------------------ >>>>>> >>>>>> Change Log for RPZ add-on >>>>>> >>>>>> rpz-1.0.0-18 on 2025-02-05 >>>>>> - Build for approval & release as IPFire add-on >>>>>> >>>>>> --- >>>>>> >>>>>> rpz-beta-0.1.18-18.ipfire on 2025-02-01 >>>>>> rpz.cgi: >>>>>> - new feature: added a mod key to force a unbound restart >>>>>> >>>>>> rpz-config and rpz-make: >>>>>> - new feature: added action for unbound restart `rpz-config unbound-re= start` >>>>>> >>>>>> rpz-metrics: >>>>>> - simple reformatting >>>>>> - rename far right column from "last update" to "last download" >>>>>> >>>>>> --- >>>>>> >>>>>> rpz-beta-0.1.17-17.ipfire on 2024-12-09 >>>>>> rpz-make >>>>>> - bug fix: corrected validation regex for wildcards like: `*.domain.co= m` >>>>>> >>>>>> --- >>>>>> >>>>>> rpz-beta-0.1.16-16.ipfire on 2024-11-18 >>>>>> rpz-make >>>>>> - new feature: updated validation regex >>>>>> - bug fix: moved validation to beginning of process. Now we validate = before >>>>>> creating config files. >>>>>> >>>>>> rpz.cgi: >>>>>> - new feature: use CSS color variables of the main ipfire theme >>>>>> - bug fix: empty zonefile remarks were stored as =E2=80=9Cundef=E2=80= =9D and caused a warning >>>>>> - bug fix: HTML textarea removes the first empty line in a custom list >>>>>> - thank you Leo! >>>>>> >>>>>> --- >>>>>> >>>>>> rpz-beta-0.1.15-15.ipfire on 2024-11-04 >>>>>> rpz.cgi: >>>>>> - new feature: added new language file for Turkish (thank you Peppe) >>>>>> >>>>>> rpz-make >>>>>> - bug fix: corrected empty allow/block list issue. An empty allow/blo= ck list >>>>>> will now remove contents of allow/block.rpz files and remove unneeded >>>>>> allow/block.conf file. (thank you iptom) >>>>>> >>>>>> --- >>>>>> >>>>>> rpz-beta-0.1.14-14.ipfire on 2024-10-29 >>>>>> rpz-config: >>>>>> - bug fix: correct missing rpz extension. `rpz-config list` displayed = URL >>>>>> incorrectly (thank you Bernhard) >>>>>> >>>>>> rpz.cgi: >>>>>> - bug fix: remove extra `"` in language files (thank you Bernhard) >>>>>> - new feature: slightly dim "apply" button when not enabled >>>>>> >>>>>> --- >>>>>> >>>>>> rpz-beta-0.1.13-13.ipfire on 2024-10-27 >>>>>> - skipped >>>>>> >>>>>> --- >>>>>> >>>>>> rpz-beta-0.1.12-12.ipfire on 2024-10-21 >>>>>> rpz.cgi: >>>>>> - new feature: added new language file for French (thank you gw-ipfir= e) >>>>>> >>>>>> --- >>>>>> >>>>>> rpz-beta-0.1.11-11.ipfire on 2024-10-18 >>>>>> rpz.cgi: >>>>>> - new feature: added new language file for Italian (thank you umberto) >>>>>> - new feature: added new language file for Spanish (thank you Roberto) >>>>>> >>>>>> --- >>>>>> >>>>>> rpz-beta-0.1.10-10.ipfire on 2024-10-15 >>>>>> rpz-make: >>>>>> - bug fix: corrected validation error for a custom list entry (thank y= ou siosios) >>>>>> - e.g., `*.cloudflare-dns.com` >>>>>> >>>>>> install.sh: >>>>>> - bug fix: add chown to correct user created files >>>>>> >>>>>> update.sh: >>>>>> - bug fix: add chown to correct user created files (thank you siosios) >>>>>> >>>>>> --- >>>>>> >>>>>> rpz-beta-0.1.9-9.ipfire on 2024-10-08 >>>>>> rpz.cgi: >>>>>> - new feature: added new language file for German (thank you Leo) >>>>>> - bug fix: add missing "rpz exitcode 110" >>>>>> - bug fix: corrected missing RPZ menu item at menu > IPFire >>>>>> >>>>>> --- >>>>>> >>>>>> rpz-beta-0.1.8-8.ipfire on 2024-10-04 >>>>>> - skipped >>>>>> >>>>>> --- >>>>>> >>>>>> rpz-beta-0.1.7-7.ipfire on 2024-10-03 >>>>>> All: >>>>>> - new feature: includes beta version numbers for pakfire package, >>>>>> instead of only `rpz-1.0.0-1.ipfire`, for each release. >>>>>> >>>>>> rpz.cgi: >>>>>> - new feature: added new WebGUI at `rpz.cgi` >>>>>> - a BIG thank you to Leo Hofmann for all of his work creating the webg= ui!! >>>>>> - bug fix: corrected missing RPZ menu item at menu > IPFire >>>>>> >>>>>> rpz-make: >>>>>> - new feature: validate entries in allowlist and blocklist >>>>>> - new feature: add "no-reload" option for WebGUI >>>>>> >>>>>> rpz-metrics: >>>>>> - new feature: info can be sorted by name, by hit count, by line count= , by >>>>>> "enabled" list or all lists >>>>>> >>>>>> backups: >>>>>> - bug fix: include all files in `/var/ipfire/dns/rpz` directory in bac= kup >>>>>> >>>>>> update.sh: >>>>>> - bug fix: corrected ownership for `/var/ipfire/dns/rpz` directory dur= ing an >>>>>> update >>>>>> >>>>>> Build: >>>>>> - bug fix: `block.rpz.conf` and `block.rpz` from build. Files to be c= reated >>>>>> by `rpz-make` >>>>>> >>>>>> WebGUI and German language file >>>>>> Contribution-by: Leo-Andres Hofmann <hofmann(a)leo-andres.de> >>>>>> >>>>>> Spanish language file >>>>>> Contribution-by: Roberto Pe=C3=B1a >>>>>> >>>>>> Italian language file >>>>>> Contribution-by: Umberto Parma >>>>>> >>>>>> French language file >>>>>> Contribution-by: gw-ipfire >>>>>> >>>>>> Turkish language file >>>>>> Contribution-by: Peppe Tech >>>>>> >>>>>> Contribution-by: Bernhard Bitsch <bbitsch(a)ipfire.org> >>>>>> Contribution-by: Erik Kapfer <erik.kapfer(a)ipfire.org> >>>>>> Signed-off-by: Jon Murphy <jon.murphy(a)ipfire.org >>>>>> --- >>>>>> config/backup/includes/rpz | 4 + >>>>>> config/cfgroot/manualpages | 1 + >>>>>> config/menu/EX-rpz.menu | 6 + >>>>>> config/rootfiles/common/configroot | 1 + >>>>>> config/rootfiles/common/web-user-interface | 1 + >>>>>> config/rootfiles/packages/rpz | 20 + >>>>>> config/rpz/00-rpz.conf | 10 + >>>>>> config/rpz/rpz-config | 130 +++ >>>>>> config/rpz/rpz-functions | 85 ++ >>>>>> config/rpz/rpz-make | 203 +++++ >>>>>> config/rpz/rpz-metrics | 170 ++++ >>>>>> config/rpz/rpz-sleep | 58 ++ >>>>>> config/rpz/rpz.de.pl | 30 + >>>>>> config/rpz/rpz.en.pl | 30 + >>>>>> config/rpz/rpz.es.pl | 30 + >>>>>> config/rpz/rpz.fr.pl | 30 + >>>>>> config/rpz/rpz.it.pl | 30 + >>>>>> config/rpz/rpz.tr.pl | 30 + >>>>>> html/cgi-bin/rpz.cgi | 923 +++++++++++++++++++++ >>>>>> lfs/rpz | 96 +++ >>>>>> make.sh | 3 +- >>>>>> src/paks/rpz/install.sh | 36 + >>>>>> src/paks/rpz/uninstall.sh | 38 + >>>>>> src/paks/rpz/update.sh | 52 ++ >>>>>> 24 files changed, 2016 insertions(+), 1 deletion(-) >>>>>> create mode 100644 config/backup/includes/rpz >>>>>> create mode 100644 config/menu/EX-rpz.menu >>>>>> create mode 100644 config/rootfiles/packages/rpz >>>>>> create mode 100644 config/rpz/00-rpz.conf >>>>>> create mode 100644 config/rpz/rpz-config >>>>>> create mode 100644 config/rpz/rpz-functions >>>>>> create mode 100644 config/rpz/rpz-make >>>>>> create mode 100755 config/rpz/rpz-metrics >>>>>> create mode 100755 config/rpz/rpz-sleep >>>>>> create mode 100644 config/rpz/rpz.de.pl >>>>>> create mode 100644 config/rpz/rpz.en.pl >>>>>> create mode 100644 config/rpz/rpz.es.pl >>>>>> create mode 100644 config/rpz/rpz.fr.pl >>>>>> create mode 100644 config/rpz/rpz.it.pl >>>>>> create mode 100644 config/rpz/rpz.tr.pl >>>>>> create mode 100644 html/cgi-bin/rpz.cgi >>>>>> create mode 100644 lfs/rpz >>>>>> create mode 100644 src/paks/rpz/install.sh >>>>>> create mode 100644 src/paks/rpz/uninstall.sh >>>>>> create mode 100644 src/paks/rpz/update.sh >>>>>> >>>>>> diff --git a/config/backup/includes/rpz b/config/backup/includes/rpz >>>>>> new file mode 100644 >>>>>> index 000000000..36513e494 >>>>>> --- /dev/null >>>>>> +++ b/config/backup/includes/rpz >>>>>> @@ -0,0 +1,4 @@ >>>>>> +/var/ipfire/dns/rpz/* >>>>>> +/etc/unbound/zonefiles/allow.rpz >>>>>> +/etc/unbound/zonefiles/block.rpz >>>>>> +/etc/unbound/local.d/*rpz.conf >>>>>> diff --git a/config/cfgroot/manualpages b/config/cfgroot/manualpages >>>>>> index 1f7e01efc..d3a48c633 100644 >>>>>> --- a/config/cfgroot/manualpages >>>>>> +++ b/config/cfgroot/manualpages >>>>>> @@ -70,6 +70,7 @@ pakfire.cgi=3Dconfiguration/ipfire/pakfire >>>>>> wlanap.cgi=3Daddons/wireless >>>>>> tor.cgi=3Daddons/tor >>>>>> samba.cgi=3Daddons/samba >>>>>> +rpz.cgi=3Daddons/rpz >>>>>> >>>>>> # Logs menu >>>>>> logs.cgi/summary.dat=3Dconfiguration/logs/summary >>>>>> diff --git a/config/menu/EX-rpz.menu b/config/menu/EX-rpz.menu >>>>>> new file mode 100644 >>>>>> index 000000000..2f4daf410 >>>>>> --- /dev/null >>>>>> +++ b/config/menu/EX-rpz.menu >>>>>> @@ -0,0 +1,6 @@ >>>>>> +$subipfire->{'20.rpz'} =3D { >>>>>> + 'caption' =3D> $Lang::tr{'rpz'}, >>>>>> + 'uri' =3D> '/cgi-bin/rpz.cgi', >>>>>> + 'title' =3D> "RPZ", >>>>>> + 'enabled' =3D> 1, >>>>>> +}; >>>>>> diff --git a/config/rootfiles/common/configroot b/config/rootfiles/com= mon/configroot >>>>>> index 9839eee45..b30d6aae4 100644 >>>>>> --- a/config/rootfiles/common/configroot >>>>>> +++ b/config/rootfiles/common/configroot >>>>>> @@ -120,6 +120,7 @@ var/ipfire/menu.d/70-log.menu >>>>>> #var/ipfire/menu.d/EX-apcupsd.menu >>>>>> #var/ipfire/menu.d/EX-guardian.menu >>>>>> #var/ipfire/menu.d/EX-mympd.menu >>>>>> +#var/ipfire/menu.d/EX-rpz.menu >>>>>> #var/ipfire/menu.d/EX-samba.menu >>>>>> #var/ipfire/menu.d/EX-tor.menu >>>>>> #var/ipfire/menu.d/EX-transmission.menu >>>>>> diff --git a/config/rootfiles/common/web-user-interface b/config/rootf= iles/common/web-user-interface >>>>>> index 816241dae..e00464076 100644 >>>>>> --- a/config/rootfiles/common/web-user-interface >>>>>> +++ b/config/rootfiles/common/web-user-interface >>>>>> @@ -69,6 +69,7 @@ srv/web/ipfire/cgi-bin/proxy.cgi >>>>>> srv/web/ipfire/cgi-bin/qos.cgi >>>>>> srv/web/ipfire/cgi-bin/remote.cgi >>>>>> srv/web/ipfire/cgi-bin/routing.cgi >>>>>> +#srv/web/ipfire/cgi-bin/rpz.cgi >>>>>> #srv/web/ipfire/cgi-bin/samba.cgi >>>>>> srv/web/ipfire/cgi-bin/services.cgi >>>>>> srv/web/ipfire/cgi-bin/shutdown.cgi >>>>>> diff --git a/config/rootfiles/packages/rpz b/config/rootfiles/packages= /rpz >>>>>> new file mode 100644 >>>>>> index 000000000..1c8663049 >>>>>> --- /dev/null >>>>>> +++ b/config/rootfiles/packages/rpz >>>>>> @@ -0,0 +1,20 @@ >>>>>> +etc/unbound/local.d/00-rpz.conf >>>>>> +etc/unbound/zonefiles >>>>>> +etc/unbound/zonefiles/allow.rpz >>>>>> +usr/sbin/rpz-config >>>>>> +usr/sbin/rpz-functions >>>>>> +usr/sbin/rpz-make >>>>>> +usr/sbin/rpz-metrics >>>>>> +usr/sbin/rpz-sleep >>>>>> +var/ipfire/addon-lang/rpz.de.pl >>>>>> +var/ipfire/addon-lang/rpz.en.pl >>>>>> +var/ipfire/addon-lang/rpz.es.pl >>>>>> +var/ipfire/addon-lang/rpz.fr.pl >>>>>> +var/ipfire/addon-lang/rpz.it.pl >>>>>> +var/ipfire/addon-lang/rpz.tr.pl >>>>>> +var/ipfire/backup/addons/includes/rpz >>>>>> +var/ipfire/dns/rpz >>>>>> +var/ipfire/dns/rpz/allowlist >>>>>> +var/ipfire/dns/rpz/blocklist >>>>>> +var/ipfire/menu.d/EX-rpz.menu >>>>>> +srv/web/ipfire/cgi-bin/rpz.cgi >>>>>> diff --git a/config/rpz/00-rpz.conf b/config/rpz/00-rpz.conf >>>>>> new file mode 100644 >>>>>> index 000000000..f005a4f2e >>>>>> --- /dev/null >>>>>> +++ b/config/rpz/00-rpz.conf >>>>>> @@ -0,0 +1,10 @@ >>>>>> +server: >>>>>> + module-config: "respip validator iterator" >>>>>> + >>>>>> +rpz: >>>>>> + name: allow.rpz >>>>>> + zonefile: /etc/unbound/zonefiles/allow.rpz >>>>>> + rpz-action-override: passthru >>>>>> + rpz-log: yes >>>>>> + rpz-log-name: allow >>>>>> + rpz-signal-nxdomain-ra: yes >>>>>> diff --git a/config/rpz/rpz-config b/config/rpz/rpz-config >>>>>> new file mode 100644 >>>>>> index 000000000..c72d50f9b >>>>>> --- /dev/null >>>>>> +++ b/config/rpz/rpz-config >>>>>> @@ -0,0 +1,130 @@ >>>>>> +#!/bin/bash >>>>>> +#####################################################################= ########## >>>>>> +# = # >>>>>> +# IPFire.org - A linux based firewall = # >>>>>> +# Copyright (C) 2024-2025 IPFire Team <info(a)ipfire.org> = # >>>>>> +# = # >>>>>> +# This program is free software: you can redistribute it and/or modi= fy # >>>>>> +# it under the terms of the GNU General Public License as published = by # >>>>>> +# the Free Software Foundation, either version 3 of the License, or = # >>>>>> +# (at your option) any later version. = # >>>>>> +# = # >>>>>> +# This program is distributed in the hope that it will be useful, = # >>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty of = # >>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the = # >>>>>> +# GNU General Public License for more details. = # >>>>>> +# = # >>>>>> +# You should have received a copy of the GNU General Public License = # >>>>>> +# along with this program. If not, see <http://www.gnu.org/licenses= />. # >>>>>> +# = # >>>>>> +#####################################################################= ########## >>>>>> + >>>>>> +version=3D"2025-01-11 - v44" >>>>>> + >>>>>> +############### Functions ############### >>>>>> + >>>>>> +source /usr/sbin/rpz-functions >>>>>> + >>>>>> +############### Main ############### >>>>>> + >>>>>> +tagName=3D"unbound" >>>>>> + >>>>>> +rpzAction=3D"${1}" # input RPZ action >>>>>> +rpzName=3D"${2}" # input RPZ name >>>>>> +rpzURL=3D"${3}" # input RPZ URL >>>>>> +rpzOption1=3D"${4}" # input RPZ option #1 >>>>>> +rpzOption2=3D"${5}" # input RPZ option #2 >>>>>> + >>>>>> +rpzConfig=3D"/etc/unbound/local.d/${rpzName}.rpz.conf" # output z= one conf file >>>>>> +rpzFile=3D"/etc/unbound/zonefiles/${rpzName}.rpz" # output f= or RPZ file >>>>>> + >>>>>> +rpzLog=3D"yes" # log default is yes >>>>>> +ucReload=3D"yes" # reload default is yes >>>>>> + >>>>>> +while [[ $# -gt 0 ]] ; do >>>>>> + case "$1" in >>>>>> + --no-log ) rpzLog=3D"no" ;; >>>>>> + --no-reload ) ucReload=3D"no" ; checkConf=3D"no" ;; >>>>>> + esac >>>>>> + shift # Shift after checking all the cases to get next opti= on >>>>>> +done >>>>>> + >>>>>> +case "${rpzAction}" in >>>>>> + # add new rpz list >>>>>> + add ) >>>>>> + check_name "${rpzName}" # is this a valid name? >>>>>> + # does this config already exist? If yes, then exit >>>>>> + if [[ -f "${rpzConfig}" ]] ; then >>>>>> + msg_log "error: rpz: duplicate - ${rpzConfig} already exi= sts. exit" >>>>>> + exit 104 >>>>>> + fi >>>>>> + >>>>>> + # is this a valid URL? >>>>>> + regex=3D'^https://[-[:alnum:]\+&@#/%?=3D~_|!:,.;]*[-[:alnum:]= \+&@#/%=3D~_|]' >>>>>> + if ! [[ "${rpzURL}" =3D~ $regex ]] ; then >>>>>> + msg_log "error: rpz: the URL is not valid: \"${rpzURL}\".= exit." >>>>>> + exit 105 >>>>>> + fi >>>>>> + >>>>>> + # create the zone config file >>>>>> + { >>>>>> + echo "rpz:" >>>>>> + echo " name: ${rpzName}.rpz" >>>>>> + echo " zonefile: ${rpzFile}" >>>>>> + echo " url: ${rpzURL}" >>>>>> + echo " rpz-action-override: nxdomain" >>>>>> + echo " rpz-log: ${rpzLog}" >>>>>> + echo " rpz-log-name: ${rpzName}" >>>>>> + echo " rpz-signal-nxdomain-ra: yes" >>>>>> + } > "${rpzConfig}" >>>>>> + >>>>>> + # set-up zonefile >>>>>> + # create an empty rpz file if it does not exist >>>>>> + if [[ ! -f "${rpzFile}" ]] ; then >>>>>> + touch "${rpzFile}" >>>>>> + # unbound requires these settings for rpz files >>>>>> + set_permissions "${rpzFile}" "${rpzConfig}" >>>>>> + fi >>>>>> + ;; >>>>>> + >>>>>> + # trash config file & rpz file >>>>>> + remove ) >>>>>> + if ! [[ -f "${rpzConfig}" ]] ; then >>>>>> + msg_log "error: rpz: cannot remove ${rpzConfig}, does not= exist. exit" >>>>>> + exit 106 >>>>>> + fi >>>>>> + >>>>>> + msg_log "info: rpz: remove config file & rpz file \"${rpzName= }\"" >>>>>> + rm "${rpzConfig}" >>>>>> + rm "${rpzFile}" >>>>>> + ;; >>>>>> + >>>>>> + reload ) >>>>>> + check_unbound_conf "${checkConf}" >>>>>> + ;; >>>>>> + >>>>>> + list ) >>>>>> + awk -F':' '/^\s*name:/{ gsub(/[[:blank:]]|\.rpz/, "",$2) ; NA= ME=3D$2 } \ >>>>>> + /^\s*url:/{ gsub(/[[:blank:]]/, "") ; print NAME"=3D"$2":= "$3} ' \ >>>>>> + /etc/unbound/local.d/*rpz.conf >>>>>> + exit >>>>>> + ;; >>>>>> + >>>>>> + unbound-restart ) >>>>>> + check_unbound_conf "${checkConf}" >>>>>> + unbound_restart >>>>>> + exit >>>>>> + ;; >>>>>> + >>>>>> + * ) >>>>>> + msg_log "error: rpz: missing or incorrect parameter" >>>>>> + printf "Usage: $(basename "$0") <ACTION> <NAME> <URL> <OPTI= ON> <OPTION>\n" >>>>>> + printf "Version: ${version}\n" >>>>>> + exit 108 >>>>>> + ;; >>>>>> + >>>>>> +esac >>>>>> + >>>>>> +unbound_control_reload "${ucReload}" >>>>>> + >>>>>> +exit >>>>>> diff --git a/config/rpz/rpz-functions b/config/rpz/rpz-functions >>>>>> new file mode 100644 >>>>>> index 000000000..ace1d2690 >>>>>> --- /dev/null >>>>>> +++ b/config/rpz/rpz-functions >>>>>> @@ -0,0 +1,85 @@ >>>>>> +#!/bin/bash >>>>>> +#####################################################################= ########## >>>>>> +# = # >>>>>> +# IPFire.org - A linux based firewall = # >>>>>> +# Copyright (C) 2024 IPFire Team <info(a)ipfire.org> = # >>>>>> +# = # >>>>>> +# This program is free software: you can redistribute it and/or modi= fy # >>>>>> +# it under the terms of the GNU General Public License as published = by # >>>>>> +# the Free Software Foundation, either version 3 of the License, or = # >>>>>> +# (at your option) any later version. = # >>>>>> +# = # >>>>>> +# This program is distributed in the hope that it will be useful, = # >>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty of = # >>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the = # >>>>>> +# GNU General Public License for more details. = # >>>>>> +# = # >>>>>> +# You should have received a copy of the GNU General Public License = # >>>>>> +# along with this program. If not, see <http://www.gnu.org/licenses= />. # >>>>>> +# = # >>>>>> +#####################################################################= ########## >>>>>> + >>>>>> +version=3D"2024-12-10 - v02" >>>>>> + >>>>>> +############### Functions ############### >>>>>> + >>>>>> +msg_log () { >>>>>> + logger --tag "${tagName}" "$*" >>>>>> + if tty --silent ; then >>>>>> + echo "${tagName}:" "$*" >>>>>> + fi >>>>>> +} >>>>>> + >>>>>> +# check for a valid name >>>>>> +check_name () { >>>>>> + local theName=3D"${1}" >>>>>> + >>>>>> + regex=3D'^[a-zA-Z0-9_]+$' # no dash or plus, alpha nu= meric only >>>>>> + regex1=3D'^(allow|block)$' # allow and block are reser= ved NAMEs >>>>>> + if [[ ! "${theName}" =3D~ $regex ]] || [[ "${theName}" =3D~ $rege= x1 ]] ; then >>>>>> + msg_log "error: rpz: the NAME is not valid: \"${theName}\". e= xit." >>>>>> + exit 101 >>>>>> + fi >>>>>> +} >>>>>> + >>>>>> +set_permissions () { >>>>>> + chown nobody:nobody "$@" >>>>>> + chmod 644 "$@" >>>>>> +} >>>>>> + >>>>>> +check_unbound_conf () { >>>>>> + local thecheckConf=3D"${1:-yes}" # check config default is= yes >>>>>> + >>>>>> + # check the above config files >>>>>> + if [[ "${thecheckConf}" =3D=3D yes ]] ; then >>>>>> + msg_log "info: rpz: check for errors with \"unbound-checkconf= \"" >>>>>> + >>>>>> + if ! unbound-checkconf ; then >>>>>> + msg_log "error: rpz: unbound-checkconf found invalid conf= iguration." >>>>>> + msg_log \ >>>>>> + "error: rpz: In Terminal run the command \"unbound-chec= kconf\" for more information. exit." >>>>>> + exit 102 >>>>>> + fi >>>>>> + fi >>>>>> +} >>>>>> + >>>>>> +unbound_control_reload () { >>>>>> + local theReload=3D"${1:-yes}" # reload default is yes >>>>>> + >>>>>> + if [[ "${theReload}" =3D=3D yes ]] ; then >>>>>> + # reload due to the changes >>>>>> + msg_log "info: rpz: run \"unbound-control reload\"" >>>>>> + >>>>>> + if ! unbound-control reload ; then >>>>>> + msg_log "error: rpz: unbound-control reload. exit." >>>>>> + exit 109 >>>>>> + fi >>>>>> + fi >>>>>> +} >>>>>> + >>>>>> +unbound_restart () { >>>>>> + # restart due to the changes >>>>>> + msg_log "info: rpz: run \"unbound restart\"" >>>>>> + >>>>>> + /usr/local/bin/unboundctrl restart >>>>>> +} >>>>>> diff --git a/config/rpz/rpz-make b/config/rpz/rpz-make >>>>>> new file mode 100644 >>>>>> index 000000000..927d55170 >>>>>> --- /dev/null >>>>>> +++ b/config/rpz/rpz-make >>>>>> @@ -0,0 +1,203 @@ >>>>>> +#!/bin/bash >>>>>> +#####################################################################= ########## >>>>>> +# = # >>>>>> +# IPFire.org - A linux based firewall = # >>>>>> +# Copyright (C) 2024-2025 IPFire Team <info(a)ipfire.org> = # >>>>>> +# = # >>>>>> +# This program is free software: you can redistribute it and/or modi= fy # >>>>>> +# it under the terms of the GNU General Public License as published = by # >>>>>> +# the Free Software Foundation, either version 3 of the License, or = # >>>>>> +# (at your option) any later version. = # >>>>>> +# = # >>>>>> +# This program is distributed in the hope that it will be useful, = # >>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty of = # >>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the = # >>>>>> +# GNU General Public License for more details. = # >>>>>> +# = # >>>>>> +# You should have received a copy of the GNU General Public License = # >>>>>> +# along with this program. If not, see <http://www.gnu.org/licenses= />. # >>>>>> +# = # >>>>>> +#####################################################################= ########## >>>>>> + >>>>>> +version=3D"2025-01-11 - v14" >>>>>> + >>>>>> +############### Functions ############### >>>>>> + >>>>>> +source /usr/sbin/rpz-functions >>>>>> + >>>>>> +# create the config file for allow >>>>>> +make_allow_config () { >>>>>> + local theLog=3D"${1:-yes}" # log defa= ult ON >>>>>> + local theConfig=3D"/etc/unbound/local.d/00-rpz.conf" # output z= one conf file >>>>>> + local theList=3D"/var/ipfire/dns/rpz/allowlist" # input cu= stom list of domains >>>>>> + >>>>>> + msg_log "info: rpz: make config file \"00-rpz.conf\"" >>>>>> + >>>>>> + echo "server: >>>>>> + module-config: \"respip validator iterator\"" > "${theConfig}" >>>>>> + >>>>>> + # does allow list exist? >>>>>> + if [[ -s "${theList}" ]] && grep -q . "${theList}" ; then >>>>>> + >>>>>> + echo "rpz: >>>>>> + name: allow.rpz >>>>>> + zonefile: /etc/unbound/zonefiles/allow.rpz >>>>>> + rpz-action-override: passthru >>>>>> + rpz-log: ${theLog} >>>>>> + rpz-log-name: allow >>>>>> + rpz-signal-nxdomain-ra: yes" >> "${theConfig}" >>>>>> + >>>>>> + fi >>>>>> + >>>>>> + # set-up zonefile - unbound requires these settings for rpz files >>>>>> + set_permissions "${theConfig}" >>>>>> +} >>>>>> + >>>>>> +# create the config file for block >>>>>> +make_block_config () { >>>>>> + local theLog=3D"${1:-yes}" # log = default ON >>>>>> + local theConfig=3D"/etc/unbound/local.d/block.rpz.conf" # outp= ut zone conf file >>>>>> + local theList=3D"/var/ipfire/dns/rpz/blocklist" # inpu= t custom list of domains >>>>>> + >>>>>> + msg_log "info: rpz: make config file \"block.rpz.conf\"" >>>>>> + >>>>>> + # does block list exist? >>>>>> + if [[ -s "${theList}" ]] && grep -q . "${theList}" ; then >>>>>> + >>>>>> + echo "rpz: >>>>>> + name: block.rpz >>>>>> + zonefile: /etc/unbound/zonefiles/block.rpz >>>>>> + rpz-action-override: nxdomain >>>>>> + rpz-log: ${theLog} >>>>>> + rpz-log-name: block >>>>>> + rpz-signal-nxdomain-ra: yes" > "${theConfig}" >>>>>> + >>>>>> + # set-up zonefile - unbound requires these settings for rpz = files >>>>>> + set_permissions "${theConfig}" >>>>>> + else >>>>>> + # no - trash the config file >>>>>> + rm --verbose /etc/unbound/local.d/block.rpz.conf >>>>>> + fi >>>>>> +} >>>>>> + >>>>>> +# create an RPZ file for allow or block >>>>>> +make_rpz_file () { >>>>>> + local theName=3D"${1}" # allow or block >>>>>> + local theAction=3D'.' # the default is nxdomain or block >>>>>> + local actionList >>>>>> + >>>>>> + local theList=3D"/var/ipfire/dns/rpz/${theName}list" # i= nput custom list of domains >>>>>> + local theZonefile=3D"/etc/unbound/zonefiles/${theName}.rpz" # o= utput file for RPZ >>>>>> + >>>>>> + # does a list exist? >>>>>> + if [[ -s "${theList}" ]] && grep -q . "${theList}" ; then >>>>>> + >>>>>> + # for allow set to passthru >>>>>> + [[ "${theName}" =3D=3D allow ]] && theAction=3D'rpz-passthru.' >>>>>> + >>>>>> + # drop any extra "blanks" and add "CNAME <RPZ action>." to e= ach line >>>>>> + actionList=3D$( awk '{$1=3D$1};1' "${theList}" | >>>>>> + sed "/^[^;].*[[:alnum:]]/ s|$| CNAME ${theAction}|" ) >>>>>> + >>>>>> + msg_log "info: rpz: create zonefile for \"${theName}list\"" >>>>>> + >>>>>> +echo "; Name: ${theName} list >>>>>> +; Last modified: $(date "+%Y-%m-%d at %H.%M.%S %Z") >>>>>> +; >>>>>> +; domains with actions list >>>>>> +; >>>>>> +${actionList}" > "${theZonefile}" >>>>>> + >>>>>> + # set-up zonefile - unbound requires these settings for rpz = files >>>>>> + set_permissions "${theZonefile}" >>>>>> + # set-up allow/block list files >>>>>> + set_permissions "${theList}" >>>>>> + else >>>>>> + msg_log "info: rpz: the ${theList} is empty." >>>>>> + >>>>>> + rm --verbose "${theZonefile}" # trash the RPZ file >>>>>> + fi >>>>>> +} >>>>>> + >>>>>> +# check if allow/block list is valid >>>>>> +validate_list () { >>>>>> + local theName=3D"${1}" # allow or block >>>>>> + local theList=3D"/var/ipfire/dns/rpz/${theName}list" # input cu= stom list of domains >>>>>> + >>>>>> + # remove good: >>>>>> + # - properly formated domain names with or without leading wildca= rd >>>>>> + # - properly formated top level domain (TLD) names with wildcard >>>>>> + # - blank lines and comment lines >>>>>> + # remaining lines are considered "bad" >>>>>> + bad_lines=3D$( sed --regexp-extended \ >>>>>> + '/^(\*\.)?([a-zA-Z0-9](([a-zA-Z0-9\-]){0,61}[a-zA-Z0-9])?\.)+= ([a-zA-Z]{2,}|xn--[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])$/d ; >>>>>> + /^(\*\.)([a-z]{2,61}|xn--[a-z0-9]{1,60})$/d ; >>>>>> + /^$/d ; /^;/d' "${theList}" ) >>>>>> + >>>>>> + if [[ ! -z "${bad_lines}" ]] ; then >>>>>> + msg_log "error: rpz: invalid line(s) in ${theList}." >>>>>> + printf "%s\n" "bad line(s): ${bad_lines}" >>>>>> + exit 110 >>>>>> + fi >>>>>> +} >>>>>> + >>>>>> + >>>>>> +############### Main ############### >>>>>> + >>>>>> +tagName=3D"unbound" >>>>>> + >>>>>> +rpzName=3D"${1}" # input RPZ name >>>>>> + >>>>>> +rpzLog=3D"yes" # log default is yes >>>>>> +ucReload=3D"yes" # reload default is yes >>>>>> + >>>>>> +while [[ $# -gt 0 ]] ; do >>>>>> + case "$1" in >>>>>> + --no-log ) rpzLog=3D"no" ;; >>>>>> + --no-reload ) ucReload=3D"no" ; checkConf=3D"no" ;; >>>>>> + esac >>>>>> + shift # Shift after checking all the cases to get next opti= on >>>>>> +done >>>>>> + >>>>>> +case "${rpzName}" in >>>>>> + # make a new allow or block rpz file >>>>>> + >>>>>> + allow ) >>>>>> + validate_list 'allow' # is the allowlist valid? >>>>>> + make_allow_config "${rpzLog}" >>>>>> + make_rpz_file 'allow' >>>>>> + ;; >>>>>> + >>>>>> + allowblock ) >>>>>> + validate_list 'allow' # is the list valid? >>>>>> + make_allow_config "${rpzLog}" >>>>>> + make_rpz_file 'allow' >>>>>> + ;& >>>>>> + >>>>>> + block ) >>>>>> + validate_list 'block' # is the blocklist valid? >>>>>> + make_block_config "${rpzLog}" >>>>>> + make_rpz_file 'block' >>>>>> + ;; >>>>>> + >>>>>> + reload ) >>>>>> + check_unbound_conf "${checkConf}" >>>>>> + ;; >>>>>> + >>>>>> + unbound-restart ) >>>>>> + check_unbound_conf "${checkConf}" >>>>>> + unbound_restart >>>>>> + exit >>>>>> + ;; >>>>>> + >>>>>> + * ) >>>>>> + msg_log "error: rpz: missing or incorrect parameter" >>>>>> + printf "Usage: $(basename "$0") <NAME> <OPTION> <OPTION>\n" >>>>>> + printf "Version: ${version}\n" >>>>>> + exit 108 >>>>>> + ;; >>>>>> +esac >>>>>> + >>>>>> +unbound_control_reload "${ucReload}" >>>>>> + >>>>>> +exit >>>>>> diff --git a/config/rpz/rpz-metrics b/config/rpz/rpz-metrics >>>>>> new file mode 100755 >>>>>> index 000000000..4d43e1629 >>>>>> --- /dev/null >>>>>> +++ b/config/rpz/rpz-metrics >>>>>> @@ -0,0 +1,170 @@ >>>>>> +#!/bin/bash >>>>>> +#####################################################################= ########## >>>>>> +# = # >>>>>> +# IPFire.org - A linux based firewall = # >>>>>> +# Copyright (C) 2024 IPFire Team <info(a)ipfire.org> = # >>>>>> +# = # >>>>>> +# This program is free software: you can redistribute it and/or modi= fy # >>>>>> +# it under the terms of the GNU General Public License as published = by # >>>>>> +# the Free Software Foundation, either version 3 of the License, or = # >>>>>> +# (at your option) any later version. = # >>>>>> +# = # >>>>>> +# This program is distributed in the hope that it will be useful, = # >>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty of = # >>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the = # >>>>>> +# GNU General Public License for more details. = # >>>>>> +# = # >>>>>> +# You should have received a copy of the GNU General Public License = # >>>>>> +# along with this program. If not, see <http://www.gnu.org/licenses= />. # >>>>>> +# = # >>>>>> +#####################################################################= ########## >>>>>> + >>>>>> +version=3D"2025-01-20 - v25" >>>>>> + >>>>>> +############### Main ############### >>>>>> + >>>>>> +weeks=3D"2" # default to two message logs >>>>>> +sortBy=3D"name" # default "by name" >>>>>> +rpzActive=3D"enabled" # default "enabled only" >>>>>> + >>>>>> +while [[ $# -gt 0 ]] ; do >>>>>> + case "$1" in >>>>>> + --by-names | --by-name | name ) sortBy=3D"name" ;; >>>>>> + >>>>>> + --by-hits | --by-hit | hits | hit ) sortBy=3D"hit" ;; >>>>>> + >>>>>> + --by-lines | --by-line | lines | line ) sortBy=3D"line" = ;; >>>>>> + >>>>>> + --by-effect ) sortBy=3D"effect" ;; >>>>>> + >>>>>> + --enabled-only ) rpzActive=3D"enabled" ;; >>>>>> + >>>>>> + --active-all | --all | all ) rpzActive=3D"all" ;; >>>>>> + >>>>>> + [0-9] | [0-9][0-9] ) weeks=3D$1 ;; >>>>>> + esac >>>>>> + shift # Shift after checking all the cases to get next opti= on >>>>>> +done >>>>>> + >>>>>> +# get the list of message logs for N weeks >>>>>> +messageLogs=3D$( find /var/log/messages* -type f | sort --version-sor= t | >>>>>> + head -"${weeks}" ) >>>>>> + >>>>>> +# get the list of RPZ names & counts from the message log(s) >>>>>> +rpzNameCount=3D$( for logf in ${messageLogs} ; do >>>>>> + zgrep --text --fixed-strings 'info: rpz: applied' "${logf}" | >>>>>> + awk '$10 ~ /\[\w*]/ { print $10 }' ; >>>>>> + done | sort | uniq --count ) >>>>>> + >>>>>> +# flip results and remove brackets `[` and `]` >>>>>> +rpzNameCount=3D$( echo "${rpzNameCount}" | >>>>>> + awk '{ print $2, $1 }' | >>>>>> + sed --regexp-extended 's|^\[(.*)\]|\1|' ) >>>>>> + >>>>>> +# grab only names >>>>>> +rpzNames=3D$( echo "${rpzNameCount}" | awk '{ print $1 }' ) >>>>>> + >>>>>> +# get list of RPZ files >>>>>> +rpzFileList=3D$( find /etc/unbound/zonefiles -type f -iname "*.rpz" ) >>>>>> + >>>>>> +# get basename of those files >>>>>> +rpzBaseNames=3D$( echo "${rpzFileList}" | >>>>>> + sed 's|/etc/unbound/zonefiles/||g ; s|\.rpz||g ;' ) >>>>>> + >>>>>> +# add to rpzNames >>>>>> +rpzNames=3D"${rpzNames}"$'\n'"${rpzBaseNames}" >>>>>> + >>>>>> +# drop duplicate names >>>>>> +rpzNames=3D$( echo "${rpzNames}" | sort --unique ) >>>>>> + >>>>>> +# get line count for each RPZ >>>>>> +lineCount=3D$( echo "${rpzFileList}" | xargs wc -l ) >>>>>> + >>>>>> +# get comment line count and blank line count for each RPZ >>>>>> +commentCount=3D$( echo "${rpzFileList}" | xargs grep --count -e "^$" = -e "^;" ) >>>>>> + >>>>>> +# get modified date each RPZ >>>>>> +modDateList=3D$( echo "${rpzFileList}" | xargs stat -c '%.10y %n' ) >>>>>> + >>>>>> +ucListAuthZones=3D$( unbound-control list_auth_zones ) >>>>>> + >>>>>> +# get width of RPZ names >>>>>> +pWidth=3D$( echo "${rpzNames}" | awk '{ print $1" " }' | wc -L ) >>>>>> +pFormat=3D"%-${pWidth}s %-8s %-8s %8s %12s %12s\n" >>>>>> + >>>>>> +# print title line >>>>>> +printf "${pFormat}" "name" "hits" "active" "lines" " hits/line" "last= download" >>>>>> +printf -- "--------------" >>>>>> + >>>>>> +theResults=3D"" >>>>>> +totalLines=3D0 >>>>>> +totalHits=3D0 >>>>>> +while read -r theName >>>>>> +do >>>>>> + printf -- "--" # pretend progress bar >>>>>> + >>>>>> + # is this RPZ list active? >>>>>> + theActive=3D"disabled" >>>>>> + if grep --quiet "^${theName}\.rpz" <<< "${ucListAuthZones}" >>>>>> + then >>>>>> + theActive=3D"enabled" >>>>>> + else >>>>>> + [[ "${rpzActive}" =3D=3D enabled ]] && continue >>>>>> + fi >>>>>> + >>>>>> + # get hit count >>>>>> + theHits=3D"0" >>>>>> + if output=3D$( grep "^${theName}\s" <<< "${rpzNameCount}" ) ; then >>>>>> + theHits=3D$( echo "${output}" | awk '{ print $2 }' ) >>>>>> + totalHits=3D$(( totalHits + theHits )) >>>>>> + fi >>>>>> + >>>>>> + # get line count >>>>>> + theLines=3D"n/a" >>>>>> + hitsPerLine=3D"0" >>>>>> + if output=3D$( grep --fixed-strings "/${theName}.rpz" <<< "${line= Count}" ) ; then >>>>>> + theLines=3D$( echo "${output}" | awk '{ print $1 }' ) >>>>>> + totalLines=3D$(( totalLines + theLines )) >>>>>> + >>>>>> + if [[ "${theLines}" -gt 2 ]] ; then >>>>>> + hitsPerLine=3D$(( 100 * theHits / theLines )) >>>>>> + fi >>>>>> + fi >>>>>> + >>>>>> + # get modification date >>>>>> + theModDate=3D"n/a" >>>>>> + if output=3D$( grep --fixed-strings "/${theName}.rpz" <<< "${modD= ateList}" ) ; then >>>>>> + theModDate=3D$( echo "${output}" | awk '{ print $1 }' ) >>>>>> + fi >>>>>> + >>>>>> + # add to results list >>>>>> + theResults+=3D"${theName} ${theHits} ${theActive} ${theLines} ${h= itsPerLine} ${theModDate}"$'\n' >>>>>> + >>>>>> +done <<< "${rpzNames}" >>>>>> + >>>>>> +case "${sortBy}" in >>>>>> + # sort by "active" then by "name" >>>>>> + name) sortArg=3D(-k3,3r -k1,1) ;; >>>>>> + >>>>>> + # sort by "active" then by "hits" then by "name" >>>>>> + hit) sortArg=3D(-k3,3r -k2,2nr -k1,1) ;; >>>>>> + >>>>>> + # sort by "active" then by "lines" then by "name" >>>>>> + line) sortArg=3D(-k3,3r -k4,4nr -k1,1) ;; >>>>>> + >>>>>> + # sort by "active" then by "effect" then by "name" >>>>>> + effect) sortArg=3D(-k3,3r -k5,5nr -k1,1) ;; >>>>>> +esac >>>>>> + >>>>>> +printf -- "--------------\n" >>>>>> +# remove blank lines, sort, print as columns >>>>>> +echo "${theResults}" | >>>>>> + awk '!/^[[:space:]]*$/' | >>>>>> + sort "${sortArg[@]}" | >>>>>> + awk --assign=3Dwidth=3D"${pWidth}" \ >>>>>> + '{ printf "%-*s %-8s %-8s %8s %10s %% %12s\n", width, $1, $2,= $3, $4, $5, $6 }' >>>>>> + >>>>>> +printf "${pFormat}" "" "=3D=3D=3D=3D=3D=3D=3D" "" "=3D=3D=3D=3D=3D=3D= =3D=3D" "" "" >>>>>> +printf "${pFormat}" "Totals -->" "${totalHits}" "" "${totalLines}" ""= "" >>>>>> + >>>>>> +exit >>>>>> diff --git a/config/rpz/rpz-sleep b/config/rpz/rpz-sleep >>>>>> new file mode 100755 >>>>>> index 000000000..dd3603599 >>>>>> --- /dev/null >>>>>> +++ b/config/rpz/rpz-sleep >>>>>> @@ -0,0 +1,58 @@ >>>>>> +#!/bin/bash >>>>>> +#####################################################################= ########## >>>>>> +# = # >>>>>> +# IPFire.org - A linux based firewall = # >>>>>> +# Copyright (C) 2024 IPFire Team <info(a)ipfire.org> = # >>>>>> +# = # >>>>>> +# This program is free software: you can redistribute it and/or modi= fy # >>>>>> +# it under the terms of the GNU General Public License as published = by # >>>>>> +# the Free Software Foundation, either version 3 of the License, or = # >>>>>> +# (at your option) any later version. = # >>>>>> +# = # >>>>>> +# This program is distributed in the hope that it will be useful, = # >>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty of = # >>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the = # >>>>>> +# GNU General Public License for more details. = # >>>>>> +# = # >>>>>> +# You should have received a copy of the GNU General Public License = # >>>>>> +# along with this program. If not, see <http://www.gnu.org/licenses= />. # >>>>>> +# = # >>>>>> +#####################################################################= ########## >>>>>> + >>>>>> +version=3D"2024-08-16" # v05 >>>>>> + >>>>>> +############### Functions ############### >>>>>> + >>>>>> +# send message to message log >>>>>> +msg_log () { >>>>>> + logger --tag "${tagName}" "$*" >>>>>> + if tty --silent ; then >>>>>> + echo "${tagName}:" "$*" >>>>>> + fi >>>>>> +} >>>>>> + >>>>>> +############### Main ############### >>>>>> + >>>>>> +tagName=3D"unbound" >>>>>> + >>>>>> +sleepTime=3D"${1:-5m}" # default to sleep for 5m (5 minut= es) >>>>>> + >>>>>> +zoneList=3D$( unbound-control list_auth_zones | awk '{print $1}' ) >>>>>> + >>>>>> +for zone in ${zoneList} ; do >>>>>> + printf "disable ${zone}\t" >>>>>> + unbound-control rpz_disable "${zone}" >>>>>> +done >>>>>> + >>>>>> +msg_log "info: rpz: disabled all zones for ${sleepTime}" >>>>>> + >>>>>> +sleep "${sleepTime}" >>>>>> + >>>>>> +for zone in ${zoneList} ; do >>>>>> + printf "enable ${zone}\t" >>>>>> + unbound-control rpz_enable "${zone}" >>>>>> +done >>>>>> + >>>>>> +msg_log "info: rpz: enabled all zones" >>>>>> + >>>>>> +exit >>>>>> diff --git a/config/rpz/rpz.de.pl b/config/rpz/rpz.de.pl >>>>>> new file mode 100644 >>>>>> index 000000000..3770c6bb0 >>>>>> --- /dev/null >>>>>> +++ b/config/rpz/rpz.de.pl >>>>>> @@ -0,0 +1,30 @@ >>>>>> +# Added for Response Policy Zone (RPZ) add-on >>>>>> +%tr =3D (%tr, >>>>>> +'rpz' =3D> 'Response Policy Zones (RPZ)', >>>>>> +'rpz apply' =3D> '=C3=9Cbernehmen', >>>>>> +'rpz cl allow enable' =3D> 'Benutzerdefinierte Allowlist aktivieren:', >>>>>> +'rpz cl allow info' =3D> 'Zugelassene Domains (eine pro Zeile)<br>Bei= spiel: domain.com, *.domain.com', >>>>>> +'rpz cl allow' =3D> 'Benutzerdefinierte Allowlist', >>>>>> +'rpz cl block enable' =3D> 'Benutzerdefinierte Blocklist aktivieren:', >>>>>> +'rpz cl block info' =3D> 'Gesperrte Domains (eine pro Zeile)<br>Beisp= iel: domain.com, *.domain.com', >>>>>> +'rpz cl block' =3D> 'Benutzerdefinierte Blocklist', >>>>>> +'rpz cl' =3D> 'Benutzerdefinierte Listen', >>>>>> +'rpz exitcode 101' =3D> 'Der Name enth=C3=A4lt unzul=C3=A4ssige Zeich= en', >>>>>> +'rpz exitcode 102' =3D> 'unbound-checkconf hat eine fehlerhafte Konfi= guration ermittelt. F=C3=BChren Sie das Kommando unbound-checkconf auf der Ko= nsole aus, um weitere Informationen zu erhalten.', >>>>>> +'rpz exitcode 103' =3D> 'Die benutzerdefinierte Allow-/Blocklist ist = leer', >>>>>> +'rpz exitcode 104' =3D> 'Ein Eintrag mit identischem Namen existiert = bereits', >>>>>> +'rpz exitcode 105' =3D> 'Die URL ist ung=C3=BCltig', >>>>>> +'rpz exitcode 106' =3D> 'Eintrag kann nicht entfernt werden, der Name= existiert nicht', >>>>>> +'rpz exitcode 107' =3D> 'Der Name ist ung=C3=BCltig - nur "allow" ode= r "block" m=C3=B6glich', >>>>>> +'rpz exitcode 108' =3D> 'Fehlende oder inkorrekte Parameter', >>>>>> +'rpz exitcode 109' =3D> 'unbound-control reload ist fehlgeschlagen', >>>>>> +'rpz exitcode 110' =3D> 'Die benutzerdefinierte Allow-/Blocklist enth= =C3=A4lt unzul=C3=A4ssige Eintr=C3=A4ge', >>>>>> +'rpz exitcode 201' =3D> 'Die Anmerkung enth=C3=A4lt unzul=C3=A4ssige = Zeichen', >>>>>> +'rpz exitcode 202' =3D> 'Ung=C3=BCltiger Eintrag in der benutzerdefin= ierten Allowlist, Zeile ', >>>>>> +'rpz exitcode 203' =3D> 'Ung=C3=BCltiger Eintrag in der benutzerdefin= ierten Blocklist, Zeile ', >>>>>> +'rpz exitcode 204' =3D> 'Ausgew=C3=A4hlter Eintrag existiert nicht: ', >>>>>> +'rpz zf editor' =3D> 'Zonendatei-Eintrag bearbeiten', >>>>>> +'rpz zf imported' =3D> '(importiert aus rpz-config)', >>>>>> +'rpz zf remark info' =3D> 'Erlaubte Zeichen sind a-z, A-Z, 0-9 und Un= terstriche', >>>>>> +'rpz zf' =3D> 'Zonendateien', >>>>>> +); >>>>>> diff --git a/config/rpz/rpz.en.pl b/config/rpz/rpz.en.pl >>>>>> new file mode 100644 >>>>>> index 000000000..0720a8940 >>>>>> --- /dev/null >>>>>> +++ b/config/rpz/rpz.en.pl >>>>>> @@ -0,0 +1,30 @@ >>>>>> +# Added for Response Policy Zone (RPZ) add-on >>>>>> +%tr =3D (%tr, >>>>>> +'rpz' =3D> 'Response Policy Zones (RPZ)', >>>>>> +'rpz apply' =3D> 'Apply', >>>>>> +'rpz cl allow enable' =3D> 'Enable custom allowlist:', >>>>>> +'rpz cl allow info' =3D> 'Allowed domains (one per line)<br>Example: = domain.com, *.domain.com', >>>>>> +'rpz cl allow' =3D> 'Custom allowlist', >>>>>> +'rpz cl block enable' =3D> 'Enable custom blocklist:', >>>>>> +'rpz cl block info' =3D> 'Blocked domains (one per line)<br>Example: = domain.com, *.domain.com', >>>>>> +'rpz cl block' =3D> 'Custom blocklist', >>>>>> +'rpz cl' =3D> 'Custom lists', >>>>>> +'rpz exitcode 101' =3D> 'the NAME is not valid', >>>>>> +'rpz exitcode 102' =3D> 'unbound-checkconf found invalid configuratio= n. In the Terminal run the command unbound-checkconf for more information', >>>>>> +'rpz exitcode 103' =3D> 'the allow/block list is empty', >>>>>> +'rpz exitcode 104' =3D> 'duplicate - NAME already exists', >>>>>> +'rpz exitcode 105' =3D> 'the URL is not valid', >>>>>> +'rpz exitcode 106' =3D> 'cannot remove the NAME does not exist', >>>>>> +'rpz exitcode 107' =3D> 'the NAME is not valid - "allow" or "block" o= nly', >>>>>> +'rpz exitcode 108' =3D> 'missing or incorrect parameter', >>>>>> +'rpz exitcode 109' =3D> 'unbound-control reload failed', >>>>>> +'rpz exitcode 110' =3D> 'custom Allowlist/Blocklist contains invalid = entries', >>>>>> +'rpz exitcode 201' =3D> 'the REMARK is not valid', >>>>>> +'rpz exitcode 202' =3D> 'invalid entry in allowlist, line ', >>>>>> +'rpz exitcode 203' =3D> 'invalid entry in blocklist, line ', >>>>>> +'rpz exitcode 204' =3D> 'Selected entry does not exist: ', >>>>>> +'rpz zf editor' =3D> 'Edit zonefiles entry', >>>>>> +'rpz zf imported' =3D> '(imported from rpz-config)', >>>>>> +'rpz zf remark info' =3D> 'Valid characters are a-z, A-Z, 0-9 and und= erscore.', >>>>>> +'rpz zf' =3D> 'Zonefiles', >>>>>> +); >>>>>> diff --git a/config/rpz/rpz.es.pl b/config/rpz/rpz.es.pl >>>>>> new file mode 100644 >>>>>> index 000000000..98628e4aa >>>>>> --- /dev/null >>>>>> +++ b/config/rpz/rpz.es.pl >>>>>> @@ -0,0 +1,30 @@ >>>>>> +# Added for Response Policy Zone (RPZ) add-on >>>>>> +%tr =3D (%tr, >>>>>> +'rpz' =3D> 'Zonas de pol=C3=ADtica de respuesta (RPZ)', >>>>>> +'rpz apply' =3D> 'Aplicar', >>>>>> +'rpz cl allow enable' =3D> 'Habilitar la lista blanca personalizada:', >>>>>> +'rpz cl allow info' =3D> 'Dominio permitido (uno por l=C3=ADnea)<br>E= jemplo: domain.com, *.domain.com', >>>>>> +'rpz cl allow' =3D> 'Lista blanca personalizada', >>>>>> +'rpz cl block enable' =3D> 'Habilitar la lista negra personalizada:', >>>>>> +'rpz cl block info' =3D> 'Dominio bloqueado (uno por l=C3=ADnea)<br>E= jemplo: domain.com, *.domain.com', >>>>>> +'rpz cl block' =3D> 'Lista negra personalizada', >>>>>> +'rpz cl' =3D> 'Lista personalizada', >>>>>> +'rpz exitcode 101' =3D> 'El NOMBRE no es v=C3=A1lido', >>>>>> +'rpz exitcode 102' =3D> 'unbound-checkconf ha encontrado una configur= aci=C3=B3n no v=C3=A1lida. Desde Terminal, ejecute el comando unbound-checkco= nf para mayor informaci=C3=B3n', >>>>>> +'rpz exitcode 103' =3D> 'La lista de permitidos/bloqueados est=C3=A1 = vac=C3=ADa', >>>>>> +'rpz exitcode 104' =3D> 'duplicado - NOMBRE ya existe', >>>>>> +'rpz exitcode 105' =3D> 'la URL no es v=C3=A1lida', >>>>>> +'rpz exitcode 106' =3D> 'no es posible eliminar el NOMBRE que no exis= te', >>>>>> +'rpz exitcode 107' =3D> 'el NOMBRE no es v=C3=A1lido - s=C3=B3lo "per= mitir" o "bloquear"', >>>>>> +'rpz exitcode 108' =3D> 'par=C3=A1metro faltante o incorrecto', >>>>>> +'rpz exitcode 109' =3D> 'Error al recargar unbound-control', >>>>>> +'rpz exitcode 110' =3D> 'la Lista blanca/Lista negra personalizada co= ntiene entradas no v=C3=A1lidas', >>>>>> +'rpz exitcode 201' =3D> 'el COMENTARIO no es v=C3=A1lido', >>>>>> +'rpz exitcode 202' =3D> 'entrada no v=C3=A1lida en la lista blanca, l= =C3=ADnea ', >>>>>> +'rpz exitcode 203' =3D> 'entrada no v=C3=A1lida en la lista negra, l= =C3=ADnea ', >>>>>> +'rpz exitcode 204' =3D> 'La entrada seleccionada no existe: ', >>>>>> +'rpz zf editor' =3D> 'Editar la entrada de archivos de zona', >>>>>> +'rpz zf imported' =3D> '(importado de rpz-config)', >>>>>> +'rpz zf remark info' =3D> 'Los caracteres v=C3=A1lidos son a-z, A-Z, = 0-9 y gui=C3=B3n bajo', >>>>>> +'rpz zf' =3D> 'Archivos de zona', >>>>>> +); >>>>>> diff --git a/config/rpz/rpz.fr.pl b/config/rpz/rpz.fr.pl >>>>>> new file mode 100644 >>>>>> index 000000000..f35f3c2d0 >>>>>> --- /dev/null >>>>>> +++ b/config/rpz/rpz.fr.pl >>>>>> @@ -0,0 +1,30 @@ >>>>>> +# Added for Response Policy Zone (RPZ) add-on >>>>>> +%tr =3D (%tr, >>>>>> +'rpz' =3D> 'Response Policy Zones (RPZ)', >>>>>> +'rpz apply' =3D> 'Appliquer', >>>>>> +'rpz cl allow enable' =3D> 'Activer la liste d\'autorisations personn= alis=C3=A9e:', >>>>>> +'rpz cl allow info' =3D> 'Domaines autoris=C3=A9s (un par ligne)<br>E= xample: domain.com, *.domain.com', >>>>>> +'rpz cl allow' =3D> 'Liste d\'autorisations personnalis=C3=A9e', >>>>>> +'rpz cl block enable' =3D> 'Activer la liste de blocage personnalis= =C3=A9e:', >>>>>> +'rpz cl block info' =3D> 'Domaines bloqu=C3=A9s (un par ligne)<br>Exa= mple: domain.com, *.domain.com', >>>>>> +'rpz cl block' =3D> 'liste de blocage personnalis=C3=A9', >>>>>> +'rpz cl' =3D> 'Listes personnalis=C3=A9es', >>>>>> +'rpz exitcode 101' =3D> 'le NOM n\'est pas valide', >>>>>> +'rpz exitcode 102' =3D> 'unbound-checkconf configuration non valide t= rouv=C3=A9e. Dans le terminal, ex=C3=A9cutez la commande unbound-checkconf po= ur plus d\'informations', >>>>>> +'rpz exitcode 103' =3D> 'la liste autoriser/bloquer est vide', >>>>>> +'rpz exitcode 104' =3D> 'le NOM existe d=C3=A9j=C3=A0', >>>>>> +'rpz exitcode 105' =3D> 'L\'URL n\'est pas valide', >>>>>> +'rpz exitcode 106' =3D> 'impossible de supprimer le NOM n\'existe pas= ', >>>>>> +'rpz exitcode 107' =3D> 'le NOM n\'est pas valide - =C2=AB autoriser = =C2=BB ou =C2=AB bloquer =C2=BB seulement', >>>>>> +'rpz exitcode 108' =3D> 'param=C3=A8tre manquant ou incorrect', >>>>>> +'rpz exitcode 109' =3D> 'unbound-control rechargement =C3=A9chou=C3= =A9', >>>>>> +'rpz exitcode 110' =3D> 'la liste autoriser/bloquer contient des entr= =C3=A9es non valides', >>>>>> +'rpz exitcode 201' =3D> 'la REMARQUE n\'est pas valable', >>>>>> +'rpz exitcode 202' =3D> 'entr=C3=A9e non valide dans la liste d\'auto= risation, ligne ', >>>>>> +'rpz exitcode 203' =3D> 'entr=C3=A9e non valide dans la liste de bloc= s, ligne ', >>>>>> +'rpz exitcode 204' =3D> 'L\'entr=C3=A9e s=C3=A9lectionn=C3=A9e n\'exi= ste pas: ', >>>>>> +'rpz zf editor' =3D> 'Modifier l\'entr=C3=A9e Fichiers Zone', >>>>>> +'rpz zf imported' =3D> '(import=C3=A9 de rpz-config)', >>>>>> +'rpz zf remark info' =3D> 'Les caract=C3=A8res valides sont a-z, A-Z,= 0-9 et soulignement.', >>>>>> +'rpz zf' =3D> 'Fichiers Zone', >>>>>> +); >>>>>> diff --git a/config/rpz/rpz.it.pl b/config/rpz/rpz.it.pl >>>>>> new file mode 100644 >>>>>> index 000000000..ee81605c9 >>>>>> --- /dev/null >>>>>> +++ b/config/rpz/rpz.it.pl >>>>>> @@ -0,0 +1,30 @@ >>>>>> +# Added for Response Policy Zone (RPZ) add-on >>>>>> +%tr =3D (%tr, >>>>>> +'rpz' =3D> 'Response Policy Zones (RPZ)', >>>>>> +'rpz apply' =3D> 'Applica', >>>>>> +'rpz cl allow enable' =3D> 'Abilita la Whitelist personalizzata:', >>>>>> +'rpz cl allow info' =3D> 'Domini consentiti (uno per riga)<br>Esempio= : domain.com, *.domain.com', >>>>>> +'rpz cl allow' =3D> 'Whitelist personalizzata', >>>>>> +'rpz cl block enable' =3D> 'Abilita la Blacklist personalizzata:', >>>>>> +'rpz cl block info' =3D> 'Domini bloccati (uno per riga)<br>Esempio: = domain.com, *.domain.com', >>>>>> +'rpz cl block' =3D> 'Blacklist personalizzata', >>>>>> +'rpz cl' =3D> 'Liste personalizzate', >>>>>> +'rpz exitcode 101' =3D> 'il NOME non =C3=A8 valido', >>>>>> +'rpz exitcode 102' =3D> 'unbound-checkconf ha trovato una configurazi= one non valida. Dal Terminale esegui il comando unbound-checkconf per maggior= i informazioni', >>>>>> +'rpz exitcode 103' =3D> 'l\'elenco consentiti/bloccati =C3=A8 vuoto', >>>>>> +'rpz exitcode 104' =3D> 'duplicato - NAME esiste di gi=C3=A0', >>>>>> +'rpz exitcode 105' =3D> 'l\'URL non =C3=A8 valido', >>>>>> +'rpz exitcode 106' =3D> 'non =C3=A8 possibile rimuovere il NOME non e= siste', >>>>>> +'rpz exitcode 107' =3D> 'il NOME non =C3=A8 valido - solo "consenti" = o "blocca"', >>>>>> +'rpz exitcode 108' =3D> 'parametro mancante o non corretto', >>>>>> +'rpz exitcode 109' =3D> 'ricaricamento del controllo non associato no= n riuscito', >>>>>> +'rpz exitcode 110' =3D> 'la Whitelist/Blacklist personalizzata contie= ne voci non valide', >>>>>> +'rpz exitcode 201' =3D> 'l"OSSERVAZIONE non =C3=A8 valida', >>>>>> +'rpz exitcode 202' =3D> 'voce non valida nella Whitelist, riga ', >>>>>> +'rpz exitcode 203' =3D> 'voce non valida nella Blacklist, riga ', >>>>>> +'rpz exitcode 204' =3D> 'La voce selezionata non esiste: ', >>>>>> +'rpz zf editor' =3D> 'Modifica la voce dei file di zona', >>>>>> +'rpz zf imported' =3D> '(importato da rpz-config)', >>>>>> +'rpz zf remark info' =3D> 'I caratteri validi sono a-z, A-Z, 0-9 e tr= attino basso', >>>>>> +'rpz zf' =3D> 'Zonefiles', >>>>>> +); >>>>>> diff --git a/config/rpz/rpz.tr.pl b/config/rpz/rpz.tr.pl >>>>>> new file mode 100644 >>>>>> index 000000000..00226e192 >>>>>> --- /dev/null >>>>>> +++ b/config/rpz/rpz.tr.pl >>>>>> @@ -0,0 +1,30 @@ >>>>>> +# I=EF=BF=BDin eklendi Ayriyeten Response Policy Zone (RPZ) >>>>>> +%tr =3D (%tr, >>>>>> +'rpz' =3D> 'Response Policy Zones (RPZ)', >>>>>> +'rpz apply' =3D> 'Uygulamak', >>>>>> +'rpz cl allow enable' =3D> '=EF=BF=BDzel Etkinlestir allowlist:', >>>>>> +'rpz cl allow info' =3D> 'Izin verilmis domains (satir basina bir)<br= >=EF=BF=BDrnegin: domain.com, *.domain.com', >>>>>> +'rpz cl allow' =3D> 'Etkinlestir allowlist', >>>>>> +'rpz cl block enable' =3D> '=EF=BF=BDzel Etkinlestir blocklist:', >>>>>> +'rpz cl block info' =3D> 'Engellenmis domains (satir basina bir)<br>= =EF=BF=BDrnegin: domain.com, *.domain.com', >>>>>> +'rpz cl block' =3D> 'Engellenmis blocklist', >>>>>> +'rpz cl' =3D> 'Engellenmis lists', >>>>>> +'rpz exitcode 101' =3D> 'NAME ge=EF=BF=BDerli degil', >>>>>> +'rpz exitcode 102' =3D> 'unbound-checkconf ge=EF=BF=BDersiz yapilandi= rma bulundu. Terminalde daha fazla bilgi i=EF=BF=BDin Unbound-checkConf komut= unu =EF=BF=BDalistirin', >>>>>> +'rpz exitcode 103' =3D> 'allow/block liste bos', >>>>>> +'rpz exitcode 104' =3D> 'kopyalamak - NAME zaten var', >>>>>> +'rpz exitcode 105' =3D> 'URL ge=EF=BF=BDerli degil', >>>>>> +'rpz exitcode 106' =3D> '=EF=BF=BDikarilamiyor NAME yok', >>>>>> +'rpz exitcode 107' =3D> 'NAME ge=EF=BF=BDerli degil - "allow" veya "b= lock" yalniz', >>>>>> +'rpz exitcode 108' =3D> 'Parametre eksik veya yanlis', >>>>>> +'rpz exitcode 109' =3D> 'unbound-control basarisiz', >>>>>> +'rpz exitcode 110' =3D> 'Engellenmis Allowlist/Blocklist ge=EF=BF=BDe= rsiz girisler i=EF=BF=BDerir', >>>>>> +'rpz exitcode 201' =3D> 'REMARK ge=EF=BF=BDerli degil', >>>>>> +'rpz exitcode 202' =3D> 'Ge=EF=BF=BDersiz giris allowlist, line ', >>>>>> +'rpz exitcode 203' =3D> 'Ge=EF=BF=BDersiz giris blocklist, line ', >>>>>> +'rpz exitcode 204' =3D> 'Se=EF=BF=BDilen giris yok: ', >>>>>> +'rpz zf editor' =3D> 'yazimlamak zonefiles giris', >>>>>> +'rpz zf imported' =3D> '(ithal edildi rpz-config)', >>>>>> +'rpz zf remark info' =3D> 'Ge=EF=BF=BDerli karakterler a-z, A-Z, 0-9v= e alt=EF=BF=BDst.', >>>>>> +'rpz zf' =3D> 'Zonefiles', >>>>>> +); >>>>>> diff --git a/html/cgi-bin/rpz.cgi b/html/cgi-bin/rpz.cgi >>>>>> new file mode 100644 >>>>>> index 000000000..a821c92ac >>>>>> --- /dev/null >>>>>> +++ b/html/cgi-bin/rpz.cgi >>>>>> @@ -0,0 +1,923 @@ >>>>>> +#!/usr/bin/perl >>>>>> +#####################################################################= ########## >>>>>> +# = # >>>>>> +# IPFire.org - A linux based firewall = # >>>>>> +# Copyright (C) 2005-2024 IPFire Team <info(a)ipfire.org> = # >>>>>> +# = # >>>>>> +# This program is free software: you can redistribute it and/or modif= y # >>>>>> +# it under the terms of the GNU General Public License as published b= y # >>>>>> +# the Free Software Foundation, either version 3 of the License, or = # >>>>>> +# (at your option) any later version. = # >>>>>> +# = # >>>>>> +# This program is distributed in the hope that it will be useful, = # >>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty of = # >>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the = # >>>>>> +# GNU General Public License for more details. = # >>>>>> +# = # >>>>>> +# You should have received a copy of the GNU General Public License = # >>>>>> +# along with this program. If not, see <http://www.gnu.org/licenses/= >. # >>>>>> +# = # >>>>>> +#####################################################################= ########## >>>>>> + >>>>>> +use strict; >>>>>> +use Scalar::Util qw(looks_like_number); >>>>>> + >>>>>> +# debugging >>>>>> +#use warnings; >>>>>> +#use CGI::Carp 'fatalsToBrowser'; >>>>>> +#use Data::Dumper; >>>>>> + >>>>>> +require '/var/ipfire/general-functions.pl'; >>>>>> +require "${General::swroot}/lang.pl"; >>>>>> +require "${General::swroot}/header.pl"; >>>>>> + >>>>>> +###--- Extra HTML ---### >>>>>> +my $extraHead =3D <<END >>>>>> +<style> >>>>>> + /* alternating row background */ >>>>>> + .tbl tr:nth-child(2n+2) { >>>>>> + background-color: var(--color-light-grey); >>>>>> + } >>>>>> + .tbl tr:nth-child(2n+3) { >>>>>> + background-color: var(--color-grey); >>>>>> + } >>>>>> + /* text styles */ >>>>>> + .tbl th:not(:last-child) { >>>>>> + text-align: left; >>>>>> + } >>>>>> + div.right { >>>>>> + text-align: right; >>>>>> + margin-top: 0.5em; >>>>>> + } >>>>>> + /* customlist input */ >>>>>> + textarea.domainlist { >>>>>> + margin: 0.5em 0; >>>>>> + resize: vertical; >>>>>> + min-height: 10em; >>>>>> + overflow: auto; >>>>>> + white-space: pre; >>>>>> + } >>>>>> + button[type=3Dsubmit]:disabled { >>>>>> + opacity: 0.6; >>>>>> + } >>>>>> +</style> >>>>>> +END >>>>>> +; >>>>>> +###--- End of extra HTML ---### >>>>>> + >>>>>> + >>>>>> +### Settings ### >>>>>> + >>>>>> +# Request DNS service reload after configuration change >>>>>> +my $RPZ_RELOAD_FLAG =3D "${General::swroot}/dns/rpz/reload.flag"; >>>>>> + >>>>>> +# Configuration file for all available zonefiles >>>>>> +# Format: index, name (unique), enabled (on/off), URL, remark >>>>>> +my $ZONEFILES_CONF =3D "${General::swroot}/dns/rpz/zonefiles.conf"; >>>>>> + >>>>>> +# Configuration file for custom lists >>>>>> +# IDs: 0=3Dallowlist, 1=3Dblocklist, 2=3Doptions (allow/block enabled) >>>>>> +my $CUSTOMLISTS_CONF =3D "${General::swroot}/dns/rpz/customlists.conf= "; >>>>>> + >>>>>> +# Export custom lists to rpz-config >>>>>> +my $RPZ_ALLOWLIST =3D "${General::swroot}/dns/rpz/allowlist"; >>>>>> +my $RPZ_BLOCKLIST =3D "${General::swroot}/dns/rpz/blocklist"; >>>>>> + >>>>>> + >>>>>> +### Preparation ### >>>>>> + >>>>>> +# Create missing config files >>>>>> +unless(-f $ZONEFILES_CONF) { &General::system('touch', "$ZONEFILES_CO= NF"); } >>>>>> +unless(-f $CUSTOMLISTS_CONF) { &General::system('touch', "$CUSTOMLIST= S_CONF"); } >>>>>> + >>>>>> + >>>>>> +## Global gui data >>>>>> +my $errormessage =3D ""; >>>>>> + >>>>>> +## Global configuration data >>>>>> +my %zonefiles =3D (); >>>>>> +my %customlists =3D (); >>>>>> +&_zonefiles_load(); >>>>>> +&_customlists_load(); >>>>>> + >>>>>> +## Global CGI form data >>>>>> +my %cgiparams =3D (); >>>>>> +&Header::getcgihash(\%cgiparams); >>>>>> + >>>>>> +my $action =3D $cgiparams{'ACTION'} // 'NONE'; >>>>>> +my $action_key =3D $cgiparams{'KEY'} // ''; # entry being edited, emp= ty =3D none/new >>>>>> + >>>>>> + >>>>>> +###--- Process form actions ---### >>>>>> + >>>>>> +# Zonefiles action: Check whether the requested entry exists >>>>>> +if((substr($action, 0, 3) eq 'ZF_') && ($action_key)) { >>>>>> + unless(defined $zonefiles{$action_key}) { >>>>>> + $errormessage =3D &_rpz_error_tr(204, $action_key); >>>>>> + $action =3D 'NONE'; >>>>>> + } >>>>>> +} >>>>>> + >>>>>> +## Perform actions >>>>>> +if($action eq 'ZF_SAVE') { ## Save new or modified zonefiles entry >>>>>> + if(&_action_zf_save()) { >>>>>> + $action =3D 'NONE'; # success, return to main page >>>>>> + &_http_prg_redirect(); >>>>>> + } else { >>>>>> + $action =3D 'ZF_EDIT'; # error occured, keep editing >>>>>> + } >>>>>> + >>>>>> +} elsif($action eq 'ZF_TOGGLE') { ## Toggle on/off >>>>>> + if(&_action_zf_toggle()) { >>>>>> + $action =3D 'NONE'; >>>>>> + &_http_prg_redirect(); >>>>>> + } >>>>>> + >>>>>> +} elsif($action eq 'ZF_REMOVE') { ## Remove entry >>>>>> + if(&_action_zf_remove()) { >>>>>> + $action =3D 'NONE'; >>>>>> + &_http_prg_redirect(); >>>>>> + } >>>>>> + >>>>>> +} elsif($action eq 'CL_SAVE') { ## Save custom lists >>>>>> + if(&_action_cl_save()) { >>>>>> + $action =3D 'NONE'; >>>>>> + &_http_prg_redirect(); >>>>>> + } >>>>>> + >>>>>> +} elsif($action eq 'RPZ_RELOAD') { ## Reload dns configuration >>>>>> + if(&_action_rpz_reload()) { >>>>>> + $action =3D 'NONE'; >>>>>> + &_http_prg_redirect(); >>>>>> + } >>>>>> + >>>>>> +} elsif($action eq 'UNB_RESTART') { ## Restart unbound service >>>>>> + if(&_action_unb_restart()) { >>>>>> + $action =3D 'NONE'; >>>>>> + &_http_prg_redirect(); >>>>>> + } >>>>>> + >>>>>> +} >>>>>> + >>>>>> + >>>>>> +###--- Start GUI ---### >>>>>> + >>>>>> +## Start http output >>>>>> +&Header::showhttpheaders(); >>>>>> + >>>>>> +# Start HTML >>>>>> +&Header::openpage($Lang::tr{'rpz'}, 1, $extraHead); >>>>>> +&Header::openbigbox('100%', 'left', ''); >>>>>> + >>>>>> +# Show error messages >>>>>> +if($errormessage) { >>>>>> + &_print_message($errormessage); >>>>>> +} >>>>>> + >>>>>> +# Handle zonefile add/edit mode >>>>>> +if($action eq "ZF_EDIT") { >>>>>> + &_print_zonefile_editor(); >>>>>> + >>>>>> + # Finalize page and exit cleanly >>>>>> + &Header::closebigbox(); >>>>>> + &Header::closepage(); >>>>>> + exit(0); >>>>>> +} >>>>>> + >>>>>> +# Show gui elements >>>>>> +&_print_zonefiles(); >>>>>> +&_print_customlists(); >>>>>> +&_print_gui_extras(); >>>>>> + >>>>>> +&Header::closebigbox(); >>>>>> +&Header::closepage(); >>>>>> + >>>>>> +###--- End of GUI ---### >>>>>> + >>>>>> + >>>>>> +###--- Internal configuration file functions ---### >>>>>> + >>>>>> +# Load all available zonefiles from rpz-config and the internal confi= guration >>>>>> +sub _zonefiles_load { >>>>>> + # Clean start >>>>>> + %zonefiles =3D (); >>>>>> + >>>>>> + # Source 1: Get the currently enabled zonefiles from rpz-config (exp= ected format [name]=3D[URL]) >>>>>> + my @enabled_files =3D &General::system_output('/usr/sbin/rpz-config'= , 'list'); >>>>>> + >>>>>> + foreach my $row (@enabled_files) { >>>>>> + chomp($row); >>>>>> + >>>>>> + # Use regex instead of split() to skip non-matching lines >>>>>> + next unless($row =3D~ /^(\w+)=3D(.+)$/); >>>>>> + my ($name, $url) =3D ($1, $2); >>>>>> + >>>>>> + # Unique names are already guaranteed by rpz-config >>>>>> + if(&_rpz_validate_zonefile($name, $url, '', 0) =3D=3D 0) { >>>>>> + # Populate global data hash, mark all found entries as enabled >>>>>> + my %entry =3D ('enabled' =3D> 'on', >>>>>> + 'url' =3D> $url, >>>>>> + 'remark' =3D> $Lang::tr{'rpz zf imported'}); >>>>>> + >>>>>> + $zonefiles{$name} =3D \%entry; >>>>>> + } >>>>>> + } >>>>>> + >>>>>> + # Source 2: Get additional data and disabled entries from configurat= ion file >>>>>> + my %configured_files =3D (); >>>>>> + &General::readhasharray($ZONEFILES_CONF, \%configured_files); >>>>>> + >>>>>> + foreach my $row (values (%configured_files)) { >>>>>> + my ($name, $enabled, $url, $remark) =3D @$row; >>>>>> + $remark //=3D ""; >>>>>> + >>>>>> + next unless($name); >>>>>> + >>>>>> + # Check whether this row belongs to an entry already imported from r= pz-config >>>>>> + if(defined $zonefiles{$name}) { >>>>>> + # Existing entry, only merge additional data >>>>>> + $zonefiles{$name}{'remark'} =3D $remark; >>>>>> + } else { >>>>>> + # Skip entry if it is marked as enabled but not found by rpz-config.= It was then deleted manually >>>>>> + if($enabled ne 'on') { >>>>>> + # Populate global data hash >>>>>> + my %entry =3D ('enabled' =3D> 'off', >>>>>> + 'url' =3D> $url // "", >>>>>> + 'remark' =3D> $remark); >>>>>> + >>>>>> + $zonefiles{$name} =3D \%entry; >>>>>> + } >>>>>> + } >>>>>> + } >>>>>> +} >>>>>> + >>>>>> +# Save internal zonefiles configuration >>>>>> +sub _zonefiles_save_conf { >>>>>> + my $index =3D 0; >>>>>> + my %export =3D (); >>>>>> + >>>>>> + # Loop trough all zonefiles and create "hasharray" type export >>>>>> + foreach my $name (keys %zonefiles) { >>>>>> + my @entry =3D ($name, >>>>>> + $zonefiles{$name}{'enabled'}, >>>>>> + $zonefiles{$name}{'url'}, >>>>>> + $zonefiles{$name}{'remark'}); >>>>>> + >>>>>> + $export{$index++} =3D \@entry; >>>>>> + } >>>>>> + >>>>>> + &General::writehasharray($ZONEFILES_CONF, \%export); >>>>>> +} >>>>>> + >>>>>> +# Load custom lists from rpz-config and the internal configuration >>>>>> +sub _customlists_load { >>>>>> + # Clean start >>>>>> + %customlists =3D (); >>>>>> + >>>>>> + # Load configuration file >>>>>> + my %lists_conf =3D (); >>>>>> + &General::readhasharray($CUSTOMLISTS_CONF, \%lists_conf); >>>>>> + >>>>>> + # Get list options, enabled by default to start import >>>>>> + $customlists{'allow'}{'enabled'} =3D $lists_conf{2}[0] // 'on'; >>>>>> + $customlists{'block'}{'enabled'} =3D $lists_conf{2}[1] // 'on'; >>>>>> + >>>>>> + # Import enabled list from rpz-config, otherwise retrieve stored or = empty list from configuration file >>>>>> + if($customlists{'allow'}{'enabled'} eq 'on') { >>>>>> + &_customlist_import('allow', $RPZ_ALLOWLIST); >>>>>> + } else { >>>>>> + $customlists{'allow'}{'list'} =3D $lists_conf{0} // []; >>>>>> + } >>>>>> + if($customlists{'block'}{'enabled'} eq 'on') { >>>>>> + &_customlist_import('block', $RPZ_BLOCKLIST); >>>>>> + } else { >>>>>> + $customlists{'block'}{'list'} =3D $lists_conf{1} // []; >>>>>> + } >>>>>> +} >>>>>> + >>>>>> +# Save internal custom lists configuration >>>>>> +sub _customlists_save_conf { >>>>>> + my %export =3D (); >>>>>> + >>>>>> + # Match IDs with import function >>>>>> + $export{0} =3D $customlists{'allow'}{'list'}; >>>>>> + $export{1} =3D $customlists{'block'}{'list'}; >>>>>> + $export{2} =3D [$customlists{'allow'}{'enabled'}, $customlists{'bloc= k'}{'enabled'}]; >>>>>> + >>>>>> + &General::writehasharray($CUSTOMLISTS_CONF, \%export); >>>>>> +} >>>>>> + >>>>>> +# Import a custom list from plain file, returns empty list if file is= missing >>>>>> +sub _customlist_import { >>>>>> + my ($listname, $filename) =3D @_; >>>>>> + my @list =3D (); >>>>>> + >>>>>> + # File exists, load and check all lines >>>>>> + if(-f $filename) { >>>>>> + open(my $FH, '<', $filename) or die "Can't read $filename: $!"; >>>>>> + while(my $line =3D <$FH>) { >>>>>> + chomp($line); >>>>>> + push(@list, $line); >>>>>> + } >>>>>> + close($FH); >>>>>> + >>>>>> + # Clean up imported data >>>>>> + &_rpz_validate_customlist(\@list, 1); >>>>>> + } >>>>>> + >>>>>> + $customlists{$listname}{'list'} =3D \@list; >>>>>> +} >>>>>> + >>>>>> +# Export a custom list to plain file or clear file if list is disabled >>>>>> +sub _customlist_export { >>>>>> + my ($listname, $filename) =3D @_; >>>>>> + return unless(defined $customlists{$listname}); >>>>>> + >>>>>> + # Write enabled domain list to file, otherwise save empty file >>>>>> + open(my $FH, '>', $filename) or die "Can't write $filename: $!"; >>>>>> + >>>>>> + if($customlists{$listname}{'enabled'} eq 'on') { >>>>>> + foreach my $line (@{$customlists{$listname}{'list'}}) { >>>>>> + print $FH "$line\n"; >>>>>> + } >>>>>> + } else { >>>>>> + print $FH "; Note: This list is currently disabled by $ENV{'SCRIPT_N= AME'}\n"; >>>>>> + } >>>>>> + >>>>>> + close($FH); >>>>>> +} >>>>>> + >>>>>> + >>>>>> +###--- Internal gui functions ---### >>>>>> + >>>>>> +# Show simple message box >>>>>> +sub _print_message { >>>>>> + my ($message, $title) =3D @_; >>>>>> + $title ||=3D $Lang::tr{'error messages'}; >>>>>> + >>>>>> + &Header::openbox('100%', 'left', $title); >>>>>> + print "<span>$message</span>"; >>>>>> + &Header::closebox(); >>>>>> +} >>>>>> + >>>>>> +# Show all zone files and related gui elements >>>>>> +sub _print_zonefiles { >>>>>> + &Header::openbox('100%', 'left', $Lang::tr{'rpz zf'}); >>>>>> + >>>>>> + print <<END >>>>>> +<table class=3D"tbl" width=3D"100%"> >>>>>> + <tr> >>>>>> + <th>$Lang::tr{'name'}</th> >>>>>> + <th>URL</th> >>>>>> + <th>$Lang::tr{'remark'}</th> >>>>>> + <th colspan=3D"3">$Lang::tr{'action'}</th> >>>>>> + </tr> >>>>>> +END >>>>>> +; >>>>>> + >>>>>> + # Sort zonefiles by name and loop trough all entries >>>>>> + foreach my $name (sort keys %zonefiles) { >>>>>> + >>>>>> + # Toggle button label translation >>>>>> + my $toggle_tr =3D ($zonefiles{$name}{'enabled'} eq 'on') ? $Lang::tr= {'click to disable'} : $Lang::tr{'click to enable'}; >>>>>> + >>>>>> + print <<END >>>>>> + <tr> >>>>>> + <td>$name</td> >>>>>> + <td>$zonefiles{$name}{'url'}</td> >>>>>> + <td>$zonefiles{$name}{'remark'}</td> >>>>>> + >>>>>> + <td align=3D"center" width=3D"5%"> >>>>>> + <form method=3D"post" action=3D"$ENV{'SCRIPT_NAME'}"> >>>>>> + <input type=3D"hidden" name=3D"KEY" value=3D"$name"> >>>>>> + <input type=3D"hidden" name=3D"ACTION" value=3D"ZF_TOGGLE"> >>>>>> + <input type=3D"image" src=3D"/images/$zonefiles{$name}{'enabled'}.gi= f" title=3D"$toggle_tr" alt=3D"$toggle_tr"> >>>>>> + </form> >>>>>> + </td> >>>>>> + <td align=3D"center" width=3D"5%"> >>>>>> + <form method=3D"post" action=3D"$ENV{'SCRIPT_NAME'}"> >>>>>> + <input type=3D"hidden" name=3D"KEY" value=3D"$name"> >>>>>> + <input type=3D"hidden" name=3D"ACTION" value=3D"ZF_EDIT"> >>>>>> + <input type=3D"image" src=3D"/images/edit.gif" title=3D"$Lang::tr{'e= dit'}" alt=3D"$Lang::tr{'edit'}"> >>>>>> + </form> >>>>>> + </td> >>>>>> + <td align=3D"center" width=3D"5%"> >>>>>> + <form method=3D"post" action=3D"$ENV{'SCRIPT_NAME'}"> >>>>>> + <input type=3D"hidden" name=3D"KEY" value=3D"$name"> >>>>>> + <input type=3D"hidden" name=3D"ACTION" value=3D"ZF_REMOVE"> >>>>>> + <input type=3D"image" src=3D"/images/delete.gif" title=3D"$Lang::tr{= 'remove'}" alt=3D"$Lang::tr{'remove'}"> >>>>>> + </form> >>>>>> + </td> >>>>>> + </tr> >>>>>> +END >>>>>> +; >>>>>> + } >>>>>> + >>>>>> + # Disable reload button if not needed >>>>>> + my $reload_state =3D &_rpz_needs_reload() ? "" : " disabled"; >>>>>> + >>>>>> + print <<END >>>>>> +</table> >>>>>> + >>>>>> +<div class=3D"right"> >>>>>> + <form method=3D"post" action=3D"$ENV{'SCRIPT_NAME'}"> >>>>>> + <input type=3D"hidden" name=3D"KEY" value=3D""> >>>>>> + <button type=3D"submit" name=3D"ACTION" value=3D"ZF_EDIT">$Lang::tr{= 'add'}</button> >>>>>> + <button type=3D"submit" name=3D"ACTION" value=3D"RPZ_RELOAD" class= =3D"commit"$reload_state>$Lang::tr{'rpz apply'}</button> >>>>>> + </form> >>>>>> +</div> >>>>>> +END >>>>>> +; >>>>>> + >>>>>> + &Header::closebox(); >>>>>> +} >>>>>> + >>>>>> +# Show zonefiles entry editor >>>>>> +sub _print_zonefile_editor { >>>>>> + >>>>>> + # Key specified: Edit existing entry >>>>>> + if(($action_key) && (defined $zonefiles{$action_key})) { >>>>>> + # Load data to be edited, but don't override already present values = (allows user to edit after error) >>>>>> + $cgiparams{'ZF_NAME'} //=3D $action_key; >>>>>> + $cgiparams{'ZF_URL'} //=3D $zonefiles{$action_key}{'url'}; >>>>>> + $cgiparams{'ZF_REMARK'} //=3D $zonefiles{$action_key}{'remark'}; >>>>>> + } >>>>>> + >>>>>> + # Fallback to empty form >>>>>> + $cgiparams{'ZF_NAME'} //=3D ""; >>>>>> + $cgiparams{'ZF_URL'} //=3D ""; >>>>>> + $cgiparams{'ZF_REMARK'} //=3D ""; >>>>>> + >>>>>> + &Header::openbox('100%', 'left', $Lang::tr{'rpz zf editor'}); >>>>>> + >>>>>> + print <<END >>>>>> +<form method=3D"post" action=3D"$ENV{'SCRIPT_NAME'}"> >>>>>> +<input type=3D"hidden" name=3D"KEY" value=3D"$action_key"> >>>>>> +<table width=3D"100%"> >>>>>> + <tr> >>>>>> + <td width=3D"20%">$Lang::tr{'name'}: <img src=3D"/blob.gif" alt= =3D"*"></td> >>>>>> + <td><input type=3D"text" name=3D"ZF_NAME" value=3D"$cgiparams{'ZF_NA= ME'}" size=3D"40" maxlength=3D"32" title=3D"$Lang::tr{'rpz zf remark info'}" = pattern=3D"[a-zA-Z0-9_]{1,32}" required></td> >>>>>> + </tr> >>>>>> + <tr> >>>>>> + <td width=3D"20%">URL: <img src=3D"/blob.gif" alt=3D"*"></td> >>>>>> + <td><input type=3D"url" name=3D"ZF_URL" value=3D"$cgiparams{'ZF_URL'= }" size=3D"40" maxlength=3D"128" required></td> >>>>>> + </tr> >>>>>> + <tr> >>>>>> + <td width=3D"20%">$Lang::tr{'remark'}:</td> >>>>>> + <td><input type=3D"text" name=3D"ZF_REMARK" value=3D"$cgiparams{'ZF_= REMARK'}" size=3D"40" maxlength=3D"32"></td> >>>>>> + </tr> >>>>>> + <tr> >>>>>> + <td colspan=3D"2"><hr></td> >>>>>> + </tr> >>>>>> + <tr> >>>>>> + <td width=3D"55%"><img src=3D"/blob.gif" alt=3D"*"> $Lang::tr{'= required field'}</td> >>>>>> + <td align=3D"right"><button type=3D"submit" name=3D"ACTION" value=3D= "ZF_SAVE">$Lang::tr{'save'}</button></td> >>>>>> + </tr> >>>>>> +</table> >>>>>> +</form> >>>>>> + >>>>>> +<div class=3D"right"> >>>>>> + <form method=3D"post" action=3D"$ENV{'SCRIPT_NAME'}"> >>>>>> + <button type=3D"submit" name=3D"ACTION" value=3D"NONE">$Lang::tr{'ba= ck'}</button> >>>>>> + </form> >>>>>> +</div> >>>>>> +END >>>>>> +; >>>>>> + >>>>>> + &Header::closebox(); >>>>>> +} >>>>>> + >>>>>> +# Show custom allow/block files and related gui elements >>>>>> +sub _print_customlists { >>>>>> + >>>>>> + # Load lists from config, unless they are currently being edited >>>>>> + if($action ne 'CL_SAVE') { >>>>>> + $cgiparams{'ALLOW_LIST'} =3D join("\n", @{$customlists{'allow'}{'lis= t'}}); >>>>>> + $cgiparams{'BLOCK_LIST'} =3D join("\n", @{$customlists{'block'}{'lis= t'}}); >>>>>> + >>>>>> + $cgiparams{'ALLOW_ENABLED'} =3D ($customlists{'allow'}{'enabled'} eq= 'on') ? 'on' : undef; >>>>>> + $cgiparams{'BLOCK_ENABLED'} =3D ($customlists{'block'}{'enabled'} eq= 'on') ? 'on' : undef; >>>>>> + } >>>>>> + >>>>>> + # Fallback to empty form >>>>>> + $cgiparams{'ALLOW_LIST'} //=3D ""; >>>>>> + $cgiparams{'BLOCK_LIST'} //=3D ""; >>>>>> + >>>>>> + # HTML checkboxes, unchecked =3D no or undef value in POST data >>>>>> + my %checked =3D (); >>>>>> + $checked{'ALLOW_ENABLED'} =3D (defined $cgiparams{'ALLOW_ENABLED'}) = ? " checked" : ""; >>>>>> + $checked{'BLOCK_ENABLED'} =3D (defined $cgiparams{'BLOCK_ENABLED'}) = ? " checked" : ""; >>>>>> + >>>>>> + # Disable reload button if not needed >>>>>> + my $reload_state =3D &_rpz_needs_reload() ? "" : " disabled"; >>>>>> + >>>>>> + &Header::openbox('100%', 'left', $Lang::tr{'rpz cl'}); >>>>>> + >>>>>> + print <<END >>>>>> +<form method=3D"post" action=3D"$ENV{'SCRIPT_NAME'}"> >>>>>> +<table width=3D"100%"> >>>>>> + <tr> >>>>>> + <td colspan=3D"2"><b>$Lang::tr{'rpz cl allow'}</b><br>$Lang::tr{'rpz= cl allow info'}</td> >>>>>> + <td colspan=3D"2"><b>$Lang::tr{'rpz cl block'}</b><br>$Lang::tr{'rpz= cl block info'}</td> >>>>>> + </tr> >>>>>> + <tr> >>>>>> + <td colspan=3D"2"><textarea name=3D"ALLOW_LIST" class=3D"domainlist"= cols=3D"45"> >>>>>> +$cgiparams{'ALLOW_LIST'}</textarea></td> >>>>>> + <td colspan=3D"2"><textarea name=3D"BLOCK_LIST" class=3D"domainlist"= cols=3D"45"> >>>>>> +$cgiparams{'BLOCK_LIST'}</textarea></td> >>>>>> + </tr> >>>>>> + <tr> >>>>>> + <td><label for=3D"allow_enabled">$Lang::tr{'rpz cl allow enable'}</l= abel></td> >>>>>> + <td width=3D"15%"><input type=3D"checkbox" name=3D"ALLOW_ENABLED" id= =3D"allow_enabled"$checked{'ALLOW_ENABLED'}></td> >>>>>> + <td><label for=3D"block_enabled">$Lang::tr{'rpz cl block enable'}</l= abel></td> >>>>>> + <td width=3D"15%"><input type=3D"checkbox" name=3D"BLOCK_ENABLED" id= =3D"block_enabled"$checked{'BLOCK_ENABLED'}></td> >>>>>> + </tr> >>>>>> + <tr> >>>>>> + <td colspan=3D"4"><hr></td> >>>>>> + </tr> >>>>>> + <tr> >>>>>> + <td align=3D"right" colspan=3D"4"> >>>>>> + <button type=3D"submit" name=3D"ACTION" value=3D"CL_SAVE">$Lang::tr{= 'save'}</button> >>>>>> + <button type=3D"submit" name=3D"ACTION" value=3D"RPZ_RELOAD" class= =3D"commit"$reload_state>$Lang::tr{'rpz apply'}</button> >>>>>> + </td> >>>>>> +</table> >>>>>> +</form> >>>>>> +END >>>>>> +; >>>>>> + >>>>>> + &Header::closebox(); >>>>>> +} >>>>>> + >>>>>> +# Output javascript and extra gui elements >>>>>> +sub _print_gui_extras { >>>>>> + >>>>>> + # Apply/Restart button modifier key handler >>>>>> + if(&_rpz_needs_reload()) { >>>>>> + print <<END >>>>>> +<script> >>>>>> + // Commit modifier key handler >>>>>> + (function(jq, document) { >>>>>> + var keyEventsOn =3D false; // Keyboard events attached >>>>>> + var keyModify =3D false; // Modifier key pressed >>>>>> + var mouseHover =3D false; // Mouse over commit button >>>>>> + var btnModified =3D false; // Button modified to "Restart" >>>>>> + >>>>>> + // Document-level key events, enable only while cursor is over button >>>>>> + function attachKeyEvents() { >>>>>> + if(keyEventsOn) { >>>>>> + return; >>>>>> + } >>>>>> + keyEventsOn =3D true; >>>>>> + >>>>>> + jq(document).on("keydown.rpz", function(event) { >>>>>> + if((!keyModify) && event.shiftKey) { >>>>>> + keyModify =3D true; >>>>>> + handleModify(); >>>>>> + } >>>>>> + }); >>>>>> + jq(document).on("keyup.rpz", function(event) { >>>>>> + if(keyModify && (!event.shiftKey)) { >>>>>> + keyModify =3D false; >>>>>> + handleModify(); >>>>>> + } >>>>>> + }); >>>>>> + } >>>>>> + function removeKeyEvents() { >>>>>> + keyModify =3D false; >>>>>> + if(keyEventsOn) { >>>>>> + jq(document).off("keydown.rpz keyup.rpz"); >>>>>> + keyEventsOn =3D false; >>>>>> + } >>>>>> + } >>>>>> + >>>>>> + // Attach mouse hover events to commit buttons >>>>>> + function attachMouseEvents() { >>>>>> + jq("button.commit").on("mouseenter", function(event) { >>>>>> + if(!mouseHover) { >>>>>> + mouseHover =3D true; >>>>>> + attachKeyEvents(); >>>>>> + // Handle already pressed key >>>>>> + keyModify =3D !!(event.shiftKey); >>>>>> + handleModify(); >>>>>> + } >>>>>> + }); >>>>>> + >>>>>> + // Cursor moved away: Disable key listener to minimize events >>>>>> + jq("button.commit").on("mouseleave", function() { >>>>>> + if(mouseHover) { >>>>>> + mouseHover =3D false; >>>>>> + removeKeyEvents(); >>>>>> + handleModify(); >>>>>> + } >>>>>> + }); >>>>>> + } >>>>>> + >>>>>> + // Modify commit button >>>>>> + function handleModify() { >>>>>> + let modify =3D mouseHover && keyModify; >>>>>> + if(btnModified !=3D modify) { >>>>>> + if(modify) { >>>>>> + jq("button.commit").text("$Lang::tr{'restart'}").val("UNB_RESTART"); >>>>>> + } else { >>>>>> + jq("button.commit").text("$Lang::tr{'rpz apply'}").val("RPZ_RELOAD"); >>>>>> + } >>>>>> + btnModified =3D modify; >>>>>> + } >>>>>> + } >>>>>> + >>>>>> + // jQuery DOM ready >>>>>> + jq(function() { >>>>>> + attachMouseEvents(); >>>>>> + }); >>>>>> + })(jQuery, document); >>>>>> +</script> >>>>>> +END >>>>>> +; >>>>>> + } # End of modifier key handler >>>>>> + >>>>>> +} >>>>>> + >>>>>> + >>>>>> +###--- Internal action processing functions ---### >>>>>> + >>>>>> +# Toggle zonefile on/off >>>>>> +sub _action_zf_toggle { >>>>>> + return unless(defined $zonefiles{$action_key}); >>>>>> + >>>>>> + my $result =3D 0; >>>>>> + my $enabled =3D $zonefiles{$action_key}{'enabled'}; >>>>>> + >>>>>> + # Perform toggle action >>>>>> + if($enabled eq 'on') { >>>>>> + $enabled =3D 'off'; >>>>>> + $result =3D &General::system('/usr/sbin/rpz-config', 'remove', $acti= on_key, '--no-reload'); >>>>>> + } else { >>>>>> + $enabled =3D 'on'; >>>>>> + $result =3D &General::system('/usr/sbin/rpz-config', 'add', $action_= key, $zonefiles{$action_key}{'url'}, '--no-reload'); >>>>>> + } >>>>>> + >>>>>> + # Check for errors, request service reload on success >>>>>> + return unless &_rpz_check_result($result, 1); >>>>>> + >>>>>> + # Save changes >>>>>> + $zonefiles{$action_key}{'enabled'} =3D $enabled; >>>>>> + &_zonefiles_save_conf(); >>>>>> + >>>>>> + return 1; >>>>>> +} >>>>>> + >>>>>> +# Remove zonefile >>>>>> +sub _action_zf_remove { >>>>>> + return unless(defined $zonefiles{$action_key}); >>>>>> + >>>>>> + # Remove from rpz-config if currently active >>>>>> + if($zonefiles{$action_key}{'enabled'} eq 'on') { >>>>>> + my $result =3D &General::system('/usr/sbin/rpz-config', 'remove', $a= ction_key, '--no-reload'); >>>>>> + >>>>>> + # Check for errors, request service reload on success >>>>>> + return unless &_rpz_check_result($result, 1); >>>>>> + } >>>>>> + >>>>>> + # Remove from data hash and save changes >>>>>> + delete $zonefiles{$action_key}; >>>>>> + &_zonefiles_save_conf(); >>>>>> + >>>>>> + # Clear action_key, as the entry is now removed entirely >>>>>> + $action_key =3D ""; >>>>>> + >>>>>> + return 1; >>>>>> +} >>>>>> + >>>>>> +# Create or update zonefile entry >>>>>> +# Returns undef if gui needs to stay in editor mode >>>>>> +sub _action_zf_save { >>>>>> + my $result =3D 0; >>>>>> + >>>>>> + my $name =3D $cgiparams{'ZF_NAME'} // ""; >>>>>> + my $url =3D $cgiparams{'ZF_URL'} // ""; >>>>>> + my $remark =3D $cgiparams{'ZF_REMARK'} // ""; >>>>>> + my $enabled =3D 'on'; # Enable new entries by default >>>>>> + >>>>>> + # Note on variables: >>>>>> + # name =3D unique key, will be used to address the entry >>>>>> + # action_key =3D name of the entry being edited, empty for new entry >>>>>> + >>>>>> + # Only check for unique name if it changed >>>>>> + # (this also checks new entries because the action_key is empty in t= his case) >>>>>> + $result =3D &_rpz_validate_zonefile($name, $url, $remark, (lc($name)= ne lc($action_key))); >>>>>> + return unless &_rpz_check_result($result, 0); >>>>>> + >>>>>> + # Edit existing entry: Determine what was changed >>>>>> + if(($action_key) && (defined $zonefiles{$action_key})) { >>>>>> + # Name und URL remain unchanged, only save remark and finish >>>>>> + if(($name eq $action_key) && ($url eq $zonefiles{$action_key}{'url'}= )) { >>>>>> + $zonefiles{$action_key}{'remark'} =3D $remark; >>>>>> + &_zonefiles_save_conf(); >>>>>> + >>>>>> + return 1; >>>>>> + } >>>>>> + >>>>>> + # Entry was changed and needs to be recreated, preserve status >>>>>> + $enabled =3D $zonefiles{$action_key}{'enabled'}; >>>>>> + >>>>>> + # Remove from rpz-config >>>>>> + return unless &_action_zf_remove(); >>>>>> + } >>>>>> + >>>>>> + # Add new entry to rpz-config >>>>>> + if($enabled eq 'on') { >>>>>> + $result =3D &General::system('/usr/sbin/rpz-config', 'add', $name, $= url, '--no-reload'); >>>>>> + >>>>>> + # Check for errors, request service reload on success >>>>>> + return unless &_rpz_check_result($result, 1); >>>>>> + } >>>>>> + >>>>>> + # Add to global data hash and save changes >>>>>> + my %entry =3D ('enabled' =3D> $enabled, >>>>>> + 'url' =3D> $url, >>>>>> + 'remark' =3D> $remark); >>>>>> + >>>>>> + $zonefiles{$name} =3D \%entry; >>>>>> + &_zonefiles_save_conf(); >>>>>> + >>>>>> + return 1; >>>>>> +} >>>>>> + >>>>>> +# Save custom lists >>>>>> +sub _action_cl_save { >>>>>> + return unless((defined $cgiparams{'ALLOW_LIST'}) && (defined $cgipar= ams{'BLOCK_LIST'})); >>>>>> + >>>>>> + my $result =3D 0; >>>>>> + >>>>>> + my @allowlist =3D split(/\R/, $cgiparams{'ALLOW_LIST'}); >>>>>> + my @blocklist =3D split(/\R/, $cgiparams{'BLOCK_LIST'}); >>>>>> + >>>>>> + # Validate lists >>>>>> + $result =3D &_rpz_validate_customlist(\@allowlist); >>>>>> + if($result !=3D 0) { >>>>>> + $errormessage =3D &_rpz_error_tr(202, $result); >>>>>> + return; >>>>>> + } >>>>>> + $result =3D &_rpz_validate_customlist(\@blocklist); >>>>>> + if($result !=3D 0) { >>>>>> + $errormessage =3D &_rpz_error_tr(203, $result); >>>>>> + return; >>>>>> + } >>>>>> + >>>>>> + # Add to global data hash and save changes >>>>>> + $customlists{'allow'}{'list'} =3D \@allowlist; >>>>>> + $customlists{'block'}{'list'} =3D \@blocklist; >>>>>> + $customlists{'allow'}{'enabled'} =3D (defined $cgiparams{'ALLOW_ENAB= LED'}) ? 'on' : 'off'; >>>>>> + $customlists{'block'}{'enabled'} =3D (defined $cgiparams{'BLOCK_ENAB= LED'}) ? 'on' : 'off'; >>>>>> + >>>>>> + &_customlists_save_conf(); >>>>>> + &_customlist_export('allow', $RPZ_ALLOWLIST); >>>>>> + &_customlist_export('block', $RPZ_BLOCKLIST); >>>>>> + >>>>>> + # Make new lists, request service reload on success >>>>>> + $result =3D &General::system('/usr/sbin/rpz-make', 'allowblock', '--= no-reload'); >>>>>> + return unless &_rpz_check_result($result, 1); >>>>>> + >>>>>> + return 1; >>>>>> +} >>>>>> + >>>>>> +# Trigger rpz-config reload >>>>>> +sub _action_rpz_reload { >>>>>> + return 1 unless &_rpz_needs_reload(); >>>>>> + >>>>>> + # Immediately clear flag to prevent multiple reloads >>>>>> + if(-f $RPZ_RELOAD_FLAG) { >>>>>> + unlink($RPZ_RELOAD_FLAG) or die "Can't remove $RPZ_RELOAD_FLAG: $!"; >>>>>> + } >>>>>> + >>>>>> + # Perform reload, recreate reload flag on error to enable retry >>>>>> + my $result =3D &General::system('/usr/sbin/rpz-config', 'reload'); >>>>>> + if(not &_rpz_check_result($result, 0)) { >>>>>> + &General::system('touch', "$RPZ_RELOAD_FLAG"); >>>>>> + return; >>>>>> + } >>>>>> + >>>>>> + return 1; >>>>>> +} >>>>>> + >>>>>> +# Trigger unbound restart >>>>>> +sub _action_unb_restart { >>>>>> + return 1 unless &_rpz_needs_reload(); >>>>>> + >>>>>> + # Immediately clear flag to prevent multiple restarts >>>>>> + if(-f $RPZ_RELOAD_FLAG) { >>>>>> + unlink($RPZ_RELOAD_FLAG) or die "Can't remove $RPZ_RELOAD_FLAG: $!"; >>>>>> + } >>>>>> + >>>>>> + # Perform restart, unboundctrl always exits zero >>>>>> + &General::system('/usr/local/bin/unboundctrl', 'restart'); >>>>>> + >>>>>> + return 1; >>>>>> +} >>>>>> + >>>>>> + >>>>>> +###--- Internal rpz-config functions ---### >>>>>> + >>>>>> +# Translate rpz-config exitcodes and messages >>>>>> +# 100-199: rpz-config, 200-299: webgui >>>>>> +sub _rpz_error_tr { >>>>>> + my ($error, $append) =3D @_; >>>>>> + $append //=3D ''; >>>>>> + >>>>>> + # Translate numeric exit codes >>>>>> + if(looks_like_number($error)) { >>>>>> + if(defined $Lang::tr{"rpz exitcode $error"}) { >>>>>> + $error =3D $Lang::tr{"rpz exitcode $error"}; >>>>>> + } >>>>>> + } >>>>>> + >>>>>> + return "RPZ $Lang::tr{'error'}: $error" . &Header::escape($append); >>>>>> +} >>>>>> + >>>>>> +# Check result of rpz-config system call, request reload on success >>>>>> +sub _rpz_check_result { >>>>>> + my ($result, $request_reload) =3D @_; >>>>>> + $request_reload //=3D 0; >>>>>> + >>>>>> + # exitcode 0 =3D success >>>>>> + if($result !=3D 0) { >>>>>> + $errormessage =3D &_rpz_error_tr($result); >>>>>> + return; >>>>>> + } >>>>>> + >>>>>> + # Set reload flag >>>>>> + if($request_reload) { >>>>>> + &General::system('touch', "$RPZ_RELOAD_FLAG"); >>>>>> + } >>>>>> + >>>>>> + return 1; >>>>>> +} >>>>>> + >>>>>> +# Test whether reload flag is set >>>>>> +sub _rpz_needs_reload { >>>>>> + return (-f $RPZ_RELOAD_FLAG); >>>>>> +} >>>>>> + >>>>>> +# Validate a zonefile entry, returns rpz-config exitcode on failure. = Use _rpz_check_result to verify. >>>>>> +# unique =3D check for unique name >>>>>> +sub _rpz_validate_zonefile { >>>>>> + my ($name, $url, $remark, $unique) =3D @_; >>>>>> + $unique //=3D 1; >>>>>> + >>>>>> + unless($name =3D~ /^[a-zA-Z0-9_]{1,32}$/) { >>>>>> + return 101; >>>>>> + } >>>>>> + unless($url =3D~ /^[\w+\.:;\/\\&@#%?=3D\-~|!]{1,128}$/) { >>>>>> + return 105; >>>>>> + } >>>>>> + unless($remark =3D~ /^[\w \-()\.:;*\/\\?!&=3D]{0,32}$/) { >>>>>> + return 201; >>>>>> + } >>>>>> + >>>>>> + # Check against already existing names >>>>>> + if($unique) { >>>>>> + foreach my $existing (keys %zonefiles) { >>>>>> + if(lc($name) eq lc($existing)) { >>>>>> + return 104; >>>>>> + } >>>>>> + } >>>>>> + } >>>>>> + >>>>>> + return 0; >>>>>> +} >>>>>> + >>>>>> +# Validate a custom list, returns number of rejected line on failure.= Check for non-zero results. >>>>>> +# listref =3D array reference, cleanup =3D remove invalid entries ins= tead of returning an error >>>>>> +sub _rpz_validate_customlist { >>>>>> + my ($listref, $cleanup) =3D @_; >>>>>> + $cleanup //=3D 0; >>>>>> + >>>>>> + foreach my $index (reverse 0..$#{$listref}) { >>>>>> + my $row =3D @$listref[$index]; >>>>>> + next unless($row); # Skip/allow empty lines >>>>>> + >>>>>> + # Reject/remove everything besides wildcard domains and remarks >>>>>> + if((not &General::validwildcarddomainname($row)) && (not $row =3D~ /= ^;[\w \-()\.:;*\/\\?!&=3D]*$/)) { >>>>>> + unless($cleanup) { >>>>>> + # +1 for user friendly line number and to ensure non-zero exitcode >>>>>> + return $index + 1; >>>>>> + } >>>>>> + >>>>>> + # Remove current row >>>>>> + splice(@$listref, $index, 1); >>>>>> + } >>>>>> + } >>>>>> + >>>>>> + return 0; >>>>>> +} >>>>>> + >>>>>> + >>>>>> +###--- Internal misc functions ---### >>>>>> + >>>>>> +# Send HTTP 303 redirect headers for post/request/get pattern >>>>>> +# (Must be sent before calling &Header::showhttpheaders()) >>>>>> +sub _http_prg_redirect { >>>>>> + my $location =3D "https://$ENV{'SERVER_NAME'}:$ENV{'SERVER_PORT'}$EN= V{'SCRIPT_NAME'}"; >>>>>> + print "Status: 303 See Other\n"; >>>>>> + print "Location: $location\n"; >>>>>> +} >>>>>> diff --git a/lfs/rpz b/lfs/rpz >>>>>> new file mode 100644 >>>>>> index 000000000..7ddbc38e5 >>>>>> --- /dev/null >>>>>> +++ b/lfs/rpz >>>>>> @@ -0,0 +1,96 @@ >>>>>> +#####################################################################= ########## >>>>>> +# = # >>>>>> +# IPFire.org - A linux based firewall = # >>>>>> +# Copyright (C) 2024 IPFire Team <info(a)ipfire.org> = # >>>>>> +# = # >>>>>> +# This program is free software: you can redistribute it and/or modif= y # >>>>>> +# it under the terms of the GNU General Public License as published b= y # >>>>>> +# the Free Software Foundation, either version 3 of the License, or = # >>>>>> +# (at your option) any later version. = # >>>>>> +# = # >>>>>> +# This program is distributed in the hope that it will be useful, = # >>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty of = # >>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the = # >>>>>> +# GNU General Public License for more details. = # >>>>>> +# = # >>>>>> +# You should have received a copy of the GNU General Public License = # >>>>>> +# along with this program. If not, see <http://www.gnu.org/licenses/= >. # >>>>>> +# = # >>>>>> +#####################################################################= ########## >>>>>> + >>>>>> +#####################################################################= ########## >>>>>> +# Definitions >>>>>> +#####################################################################= ########## >>>>>> + >>>>>> +include Config >>>>>> + >>>>>> +SUMMARY =3D response policy zone - RPZ reputation system for unbou= nd DNS >>>>>> + >>>>>> +VER =3D 1.0.0 >>>>>> + >>>>>> +THISAPP =3D rpz-$(VER) >>>>>> +DIR_APP =3D $(DIR_SRC)/$(THISAPP) >>>>>> +TARGET =3D $(DIR_INFO)/$(THISAPP) >>>>>> + >>>>>> +PROG =3D rpz >>>>>> +PAK_VER =3D 18 >>>>>> + >>>>>> +DEPS =3D >>>>>> + >>>>>> +SERVICES =3D >>>>>> + >>>>>> +#####################################################################= ########## >>>>>> +# Top-level Rules >>>>>> +#####################################################################= ########## >>>>>> + >>>>>> +install : $(TARGET) >>>>>> + >>>>>> +check : >>>>>> + >>>>>> +download : >>>>>> + >>>>>> +b2 : >>>>>> + >>>>>> +dist: >>>>>> + @$(PAK) >>>>>> + >>>>>> +#####################################################################= ########## >>>>>> +# Installation Details >>>>>> +#####################################################################= ########## >>>>>> + >>>>>> +$(TARGET) : >>>>>> + @$(PREBUILD) >>>>>> + @rm -rf $(DIR_APP) >>>>>> + >>>>>> + # RPZ scripts >>>>>> + install --verbose --mode=3D755 \ >>>>>> + $(DIR_CONF)/rpz/{rpz-config,rpz-metrics,rpz-sleep,rpz-make,rpz-func= tions} \ >>>>>> + --target-directory=3D/usr/sbin >>>>>> + >>>>>> + # RPZ config files >>>>>> + mkdir -pv /etc/unbound/local.d >>>>>> + install --verbose --mode=3D644 --owner=3Dnobody --group=3Dnobody \ >>>>>> + $(DIR_CONF)/rpz/00-rpz.conf \ >>>>>> + --target-directory=3D/etc/unbound/local.d >>>>>> + chown --verbose --recursive nobody:nobody /etc/unbound/local.d >>>>>> + >>>>>> + # RPZ custom list files for allow and block >>>>>> + mkdir -pv /var/ipfire/dns/rpz >>>>>> + touch /var/ipfire/dns/rpz/{allowlist,blocklist} >>>>>> + chown --verbose --recursive nobody:nobody /var/ipfire/dns/rpz >>>>>> + >>>>>> + # RPZ zone files >>>>>> + # create empty RPZ config file to avoid a unbound config error >>>>>> + mkdir -pv /etc/unbound/zonefiles >>>>>> + touch /etc/unbound/zonefiles/allow.rpz >>>>>> + chown --verbose --recursive nobody:nobody /etc/unbound/zonefiles >>>>>> + >>>>>> + # Install addon-specific language-files >>>>>> + install --verbose --mode=3D004 $(DIR_CONF)/rpz/rpz.*.pl \ >>>>>> + --target-directory=3D/var/ipfire/addon-lang >>>>>> + >>>>>> + # Install backup definition >>>>>> + cp -vf $(DIR_CONF)/backup/includes/rpz /var/ipfire/backup/addons/inc= ludes/rpz >>>>>> + >>>>>> + @rm -rf $(DIR_APP) >>>>>> + @$(POSTBUILD) >>>>>> diff --git a/make.sh b/make.sh >>>>>> index 827ea9e77..a77535b13 100755 >>>>>> --- a/make.sh >>>>>> +++ b/make.sh >>>>>> @@ -390,7 +390,7 @@ prepareenv() { >>>>>> if [ "${free_space}" -lt "${required_space}" ]; then >>>>>> # Add any consumed space >>>>>> while read -r consumed_space path; do >>>>>> - (( free_space +=3D consumed_space / 1024 / 1024 )) >>>>>> + (( free_space +=3D consumed_space / 1024 / 1024 )) >>>>>> done <<< "$(du --summarize --bytes "${BUILD_DIR}" "${IMAGES_DIR}" "${L= OG_DIR}" 2>/dev/null)" >>>>>> fi >>>>>> >>>>>> @@ -2087,6 +2087,7 @@ build_system() { >>>>>> lfsmake2 btrfs-progs >>>>>> lfsmake2 inotify-tools >>>>>> lfsmake2 grub-btrfs >>>>>> + lfsmake2 rpz >>>>>> >>>>>> lfsmake2 linux >>>>>> lfsmake2 rtl8812au >>>>>> diff --git a/src/paks/rpz/install.sh b/src/paks/rpz/install.sh >>>>>> new file mode 100644 >>>>>> index 000000000..ef99bf742 >>>>>> --- /dev/null >>>>>> +++ b/src/paks/rpz/install.sh >>>>>> @@ -0,0 +1,36 @@ >>>>>> +#!/bin/bash >>>>>> +#####################################################################= ########## >>>>>> +# = # >>>>>> +# IPFire.org - A linux based firewall = # >>>>>> +# Copyright (C) 2024 IPFire Team <info(a)ipfire.org> = # >>>>>> +# = # >>>>>> +# This program is free software: you can redistribute it and/or modi= fy # >>>>>> +# it under the terms of the GNU General Public License as published = by # >>>>>> +# the Free Software Foundation, either version 3 of the License, or = # >>>>>> +# (at your option) any later version. = # >>>>>> +# = # >>>>>> +# This program is distributed in the hope that it will be useful, = # >>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty of = # >>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the = # >>>>>> +# GNU General Public License for more details. = # >>>>>> +# = # >>>>>> +# You should have received a copy of the GNU General Public License = # >>>>>> +# along with this program. If not, see <http://www.gnu.org/licenses= />. # >>>>>> +# = # >>>>>> +#####################################################################= ########## >>>>>> +# >>>>>> +. /opt/pakfire/lib/functions.sh >>>>>> +extract_files >>>>>> +restore_backup ${NAME} >>>>>> + >>>>>> +# fix user created files >>>>>> +chown --verbose --recursive nobody:nobody \ >>>>>> + /var/ipfire/dns/rpz \ >>>>>> + /etc/unbound/zonefiles \ >>>>>> + /etc/unbound/local.d >>>>>> + >>>>>> +# Update Language cache >>>>>> +/usr/local/bin/update-lang-cache >>>>>> + >>>>>> +# restart unbound to load config file >>>>>> +/etc/init.d/unbound restart >>>>>> diff --git a/src/paks/rpz/uninstall.sh b/src/paks/rpz/uninstall.sh >>>>>> new file mode 100644 >>>>>> index 000000000..e11427df3 >>>>>> --- /dev/null >>>>>> +++ b/src/paks/rpz/uninstall.sh >>>>>> @@ -0,0 +1,38 @@ >>>>>> +#!/bin/bash >>>>>> +#####################################################################= ########## >>>>>> +# = # >>>>>> +# IPFire.org - A linux based firewall = # >>>>>> +# Copyright (C) 2024 IPFire Team <info(a)ipfire.org> = # >>>>>> +# = # >>>>>> +# This program is free software: you can redistribute it and/or modi= fy # >>>>>> +# it under the terms of the GNU General Public License as published = by # >>>>>> +# the Free Software Foundation, either version 3 of the License, or = # >>>>>> +# (at your option) any later version. = # >>>>>> +# = # >>>>>> +# This program is distributed in the hope that it will be useful, = # >>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty of = # >>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the = # >>>>>> +# GNU General Public License for more details. = # >>>>>> +# = # >>>>>> +# You should have received a copy of the GNU General Public License = # >>>>>> +# along with this program. If not, see <http://www.gnu.org/licenses= />. # >>>>>> +# = # >>>>>> +#####################################################################= ########## >>>>>> +# >>>>>> +. /opt/pakfire/lib/functions.sh >>>>>> + >>>>>> +# stop unbound to delete RPZ conf file >>>>>> +/etc/init.d/unbound stop >>>>>> + >>>>>> +make_backup ${NAME} >>>>>> +remove_files >>>>>> + >>>>>> +# delete rpz config files. Otherwise unbound will throw error: >>>>>> +# "[1723428668] unbound-control[17117:0] error: connect: Connectio= n refused for 127.0.0.1 port 8953" >>>>>> +/bin/rm --verbose --force /etc/unbound/local.d/*.rpz.conf >>>>>> + >>>>>> +# Update Language cache >>>>>> +/usr/local/bin/update-lang-cache >>>>>> + >>>>>> +# start unbound to load unbound config file >>>>>> +/etc/init.d/unbound start >>>>>> diff --git a/src/paks/rpz/update.sh b/src/paks/rpz/update.sh >>>>>> new file mode 100644 >>>>>> index 000000000..9bc340bc6 >>>>>> --- /dev/null >>>>>> +++ b/src/paks/rpz/update.sh >>>>>> @@ -0,0 +1,52 @@ >>>>>> +#!/bin/bash >>>>>> +#####################################################################= ########## >>>>>> +# = # >>>>>> +# IPFire.org - A linux based firewall = # >>>>>> +# Copyright (C) 2024 IPFire Team <info(a)ipfire.org> = # >>>>>> +# = # >>>>>> +# This program is free software: you can redistribute it and/or modi= fy # >>>>>> +# it under the terms of the GNU General Public License as published = by # >>>>>> +# the Free Software Foundation, either version 3 of the License, or = # >>>>>> +# (at your option) any later version. = # >>>>>> +# = # >>>>>> +# This program is distributed in the hope that it will be useful, = # >>>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty of = # >>>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the = # >>>>>> +# GNU General Public License for more details. = # >>>>>> +# = # >>>>>> +# You should have received a copy of the GNU General Public License = # >>>>>> +# along with this program. If not, see <http://www.gnu.org/licenses= />. # >>>>>> +# = # >>>>>> +#####################################################################= ########## >>>>>> +# >>>>>> +. /opt/pakfire/lib/functions.sh >>>>>> + >>>>>> +# from update.sh >>>>>> +extract_backup_includes >>>>>> + >>>>>> +# stop unbound to delete RPZ conf file >>>>>> +/etc/init.d/unbound stop >>>>>> + >>>>>> +# from uninstall.sh >>>>>> +make_backup ${NAME} >>>>>> +remove_files >>>>>> + >>>>>> +# delete rpz config files. Otherwise unbound will throw error: >>>>>> +# "unbound-control[nn:0] error: connect: Connection refused for 12= 7.0.0.1 port 8953" >>>>>> +/bin/rm --verbose --force /etc/unbound/local.d/*.rpz.conf >>>>>> + >>>>>> +# from install.sh >>>>>> +extract_files >>>>>> +restore_backup ${NAME} >>>>>> + >>>>>> +# fix user created files >>>>>> +chown --verbose --recursive nobody:nobody \ >>>>>> + /var/ipfire/dns/rpz \ >>>>>> + /etc/unbound/zonefiles \ >>>>>> + /etc/unbound/local.d >>>>>> + >>>>>> +# Update Language cache >>>>>> +/usr/local/bin/update-lang-cache >>>>>> + >>>>>> +# restart unbound to load config files >>>>>> +/etc/init.d/unbound start >>>>>> --=20 >>>>>> 2.39.5 >>>>>> >>>> Jon >>>> >>>> >>>> --=20 >>>> Jon Murphy >>>> jon.murphy(a)ipfire.org >> >> Jon >> >> >> --=20 >> Jon Murphy >> jon.murphy(a)ipfire.org >> >> >> >> --=20 Sent from my laptop --===============8878883580042416022==--