Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
Now that #1161486: Move IP blocking into its own module has landed, there are several suggested items to follow up upon. These include:
- Small documentation changes.
- Possible use of state (k/v) to cache a small banlist.
- Find the proper place for this module to be called in new kernel/bootstrap process.
- Convert variable_get() to config system.
Comments #1161486-71: Move IP blocking into its own module has further information.
Comment | File | Size | Author |
---|---|---|---|
#55 | core-followup-1794754-55.patch | 440 bytes | ParisLiakos |
#46 | 1794754-43-45-interdiff.txt | 3 KB | ParisLiakos |
#46 | 1794754-ban_followup-45.patch | 10.42 KB | ParisLiakos |
#43 | 1794754-ban_followup-43.patch | 10.6 KB | ParisLiakos |
#41 | 1794754-ban_followup-39.patch | 10.63 KB | ParisLiakos |
Comments
Comment #1
Lars Toomre CreditAttribution: Lars Toomre commentedHere is an initial patch improving some of the documentation and bringing the code closer to D8 coding standards.
My understanding is that this module only works with IPv4 addresses in 'xxx.xxx.xxx.xxx' format. Should we add documentation somewhere that IPv4 ranges, IPv6 and long IP integers are not supported? I tried to clarify this somewhat by adding 'individual' in various places. Thoughts?
Comment #3
Lars Toomre CreditAttribution: Lars Toomre commentedLooks like we missed moving the ban deletion test from statistics module to ban module. My git skills are not yet good enough to create a new test file for the patch in #1. Hence, I hope that someone else can help.
Comment #4
Lars Toomre CreditAttribution: Lars Toomre commentedI have started looking at converting this module to conform with WSCI and CMI D8 initiatives. In #1161486-25: Move IP blocking into its own module, @Crell stated:
Based upon the above it appears, we want to register this service in the boot_hook of this module. Correct?
As part of the service we would be registering, we basically want to create a very fast list look up function that returns TRUE if the individual IP address occurs in a list that might come from:
I am not sure about the above order, but it seems that a "big" dataset (e.g. like that might be on drupal.org) will need to depend on the database. For other "smaller" datasets, it might well be possible to store the array in any of the other three alternatives. The question then is what is the priority of these other data sources and how do we sync up them up with each other?
Let me raise an example. Let us suppose that three IP addresses are listed in settings.php, 200 are being defined by config yml file, state variable has 2000 elements and the database has the sum of all three 2203 IP addresses. Some of these IP addresses may overlap in different sources and others well be unique. Such an example suggests that we probably want specify some type of variable(s) in ban config object that specifies both whether a particular source should be checked, the priority/weight of that source, and possibly how they should be combined. We also will need to address how to import/export to the preferred source of the list from the other sources.
The other issue I would like to raise in thinking about speed concerns how we are handling this data. For human consumption, we are using stings like 'xxx.xxx.xxx.xxx'. However, routers and other logic devices generally use long intergers to speed up comparison and list operations. Should the new Ban module translate/untranslate IP strings to long integers to further speed up the comparison operations?
Comment #5
Lars Toomre CreditAttribution: Lars Toomre commentedAdding appropriate initiative tags.
Comment #6
sunYou will not duplicate the discussion of #1479466: Convert IP address blocking to config system here. Thanks. ;)
Comment #7
sunOn the patch:
This is a bogus addition and should be removed.
_validate() comes before _submit() and should thus be referenced first.
Let's replace this with "unblock" instead?
"Allow to..."
I don't think that the "individual" helps in any way to clarify the module's functionality. It only seems to add visual/textual clutter/noise. I'd suggest to revert these changes.
On the test failure:
The feature of blocking visitors through Statistics module's top visitor report page actually belongs to Statistics module, not Ban module. Therefore, it makes sense to keep the test in Statistics module. The test just needs to be updated for the changed UI strings.
Comment #8
Lars Toomre CreditAttribution: Lars Toomre commented@sun Thanks for the comments. I did not see that #1479466: Convert IP address blocking to config system was open when I searched. Thanks for that information.
I will incorporate your thoughts in the re-roll of the patch when I have a chance.
Comment #9
Lars Toomre CreditAttribution: Lars Toomre commentedHere is a patch that addresses most of the items in #7.
Comment #10
Crell CreditAttribution: Crell commentedhook_boot() is not where you would integrate this. hook_boot() should be deprecated in favor of the request listener, honestly.
What you'd want is to add a BanBundle class that registers a subscriber. That subscriber then listens to the kernel.request event, and itself depends on the database connection (or the state system, or whatever you're going to back it with). The listener then checks the database to see if the IP address of the $request is on the block list, and if so calls $event->setResponse(new Response(403)); (or whatever it is). If it's not, it does nothing.
There would be no functions at all in the main pipeline.
Honestly I'd do that first before you get into tidying up comments.
Comment #11
Lars Toomre CreditAttribution: Lars Toomre commentedThanks @Crell. However, I have no understanding of what you mean by 'add a BanBundle class that registers a subscriber'. I was simply cleaning up what was moved into this new Ban module.
Comment #12
Crell CreditAttribution: Crell commentedI was responding to this:
Not correct. :-) You'd create a class Drupal\ban\BanBundle (in modules/ban/lib/Drupal/ban/BanBundle.php) and have it register a subscriber. There should be examples in core already. You don't use hook_boot() for that.
I also would argue that change is more important than tidying up grammar on the comments, since the comments will no doubt change as a result of the code changes described above.
Comment #13
mortona2k CreditAttribution: mortona2k commented#9: ban-followup-1794754-9.patch queued for re-testing.
Comment #15
ParisLiakos CreditAttribution: ParisLiakos commentedComment #16
ParisLiakos CreditAttribution: ParisLiakos commentedThis patch
removes ban's hook_boot implementation and does what Crell suggested
removes ban_block_denied()
merges Lars doc fixes.
I am not sure if i should also remove
ban_is_denied()
as well and just query the database directly from subscriber since i+use Drupal\Core\Database\Database;
But i am not sure so leaving it for discussion
Comment #17
ParisLiakos CreditAttribution: ParisLiakos commentedAnd removing copy-paste failures:/
Comment #18
BerdirYes, you should pass $container->get('database') (which is a Database\Connection) as an argument to your class and then execute the query directly on that ($this->connection->query()). you can move ban_is_denied() to a isDenied() method, if it's not used anywhere else.
Comment #19
ParisLiakos CreditAttribution: ParisLiakos commentedthanks for the hints Berdir!!
I left
variable_get('ban_ips');
as is, since there is another issue for this..#1479466: Convert IP address blocking to config systemComment #20
BerdirLooks nice. Wondering if the ip_address() should be passed in to the constructor as well. That would theoretically allow to write unit tests for this as it would be possible to create it with a different ip address that would be blocked.
Comment #21
ParisLiakos CreditAttribution: ParisLiakos commentedSure, yes you are right.
Added
$ip = ''
as parameter of__construct
.ip_adress()
will be used if empty.Comment #22
Crell CreditAttribution: Crell commentedThis forces the DB to be instantiated here. That's too early. Instead, do this:
$container->register('subscriber.ban')->addArgument(new Reference('database'));
$dispatcher->addSubscriberService('subscriber.ban', '\Drupal\ban\EventSubscriber\BanSubscriber');
That lets the subscriber class not get instantiated yet, and is closer to what the syntax will be after the container gets compiled.
Don't use ip_address(). That's a hard dependency, and in fact you don't need it at all. See below.
Here, call $request = $event->getRequest(); That gives you the request object. You can then get the IP address off of that. (I forget what the method is off hand.)
Then, pass that IP address to $this->isDenied($ip).
Comment #23
ParisLiakos CreditAttribution: ParisLiakos commentedthats what i found
$ip = $event->getRequest()->getClientIp();
the moment i uncomment this line, i get a white page :( no error or anything:/ HTTP status 200 but nothing outputs
Comment #24
Crell CreditAttribution: Crell commentedFirst point: Yep, that looks about right.
Second point: Uh, hm. Turn on display_errors in php.ini or your htaccess file and see if it says something more useful? (I may have gotten the syntax wrong.)
Comment #25
ParisLiakos CreditAttribution: ParisLiakos commentedyeah this is my dev enviorment and thats what i find weird..
I have
display_errors = On
display_startup_errors = On
error_reporting = E_ALL
But i had to check apache error logs to find this.Browser just outputs white screen:
[Sat Oct 20 23:15:43 2012] [error] [client 127.0.0.1] Exception thrown when handling an exception (Symfony\\Component\\DependencyInjection\\Exception\\ServiceCircularReferenceException: Circular reference detected for service "subscriber.ban", path: "subscriber.ban".), referer: http://localhost/d8/
Comment #26
Crell CreditAttribution: Crell commentedHm. What's your full build method look like? It sounds like the identifier subscriber.ban may simply be used already. :-)
Comment #27
ParisLiakos CreditAttribution: ParisLiakos commentedthats the full class
Comment #28
Crell CreditAttribution: Crell commentedOh, duh the register() line is wrong. It doesn't specify what class to use. :-) That's what I get for writing code samples in Dreditor...
Try this:
I suppose for bonus points you can make the table name a parameter to the listener, too, or factor the isDenied logic out into a separate IP blocking managing class and make the listener depend on that, instead. I don't know if you want to go that far in this patch but that would be the ideal, and most testable.
Comment #29
ParisLiakos CreditAttribution: ParisLiakos commentedThanks for the guidance Crell and Berdir.
Ok here is so far the work, i am willing to do what Crell suggested, but i guess i will have time in the weekend.
So if anyone wants to finish this off before that here is the working patch with Crell's corrections.
Setting it to NR just to check with bot
Comment #30
ParisLiakos CreditAttribution: ParisLiakos commentedComment #32
ParisLiakos CreditAttribution: ParisLiakos commentedOk, i couldn't let this go.. i was passing class names wrongly. it doesnt need the starting
\
.But when i fixed this, event wouldnt fire...i cant make addSubscriberService work and best docs i could find describe what i am already doing:/
The
BanSubscriber::getSubscribedEvents()
fires, it is being registered into listenerIds..but thats it..i lose track andonKernelRequestBannedIpCheck()
method never fires..and the only error i get its in dblog when i clear cache, where i get a WSOD :Symfony\Component\HttpKernel\Exception\NotFoundHttpException: Unable to find the controller for path "/admin/config/development/performance". Maybe you forgot to add the matching route in your routing configuration? in Symfony\Component\HttpKernel\HttpKernel->handleRaw() (line 118 of /var/www/d8/core/vendor/symfony/http-kernel/Symfony/Component/HttpKernel/HttpKernel.php).
Nothing else in dblog or apache error logs..
thats it..till next time
Comment #33
sunSorry, but I think that @Crell's direction to use Request::getClientIp() is not correct, at least for this issue. The issue/task that intends to do that has to convert all functionality and behavior as well as tests of ip_address() to Request::getClientIp() (which is another case of settings.php variables/config being passed to service, though the service is Request this time). Doing that will also have to ensure that getClientIp() actually detects all forms of reverse-proxies we're able to detect currently, which may or may not require upstream patches. IMHO, that's completely out of scope for this issue.
Aside from that, needs review for latest patch.
Comment #34
Crell CreditAttribution: Crell commentedsun: Request does handle reverse proxies, you just have to set a flag on the class rather than setting a variable_get(). If there's another issue to eliminate ip_address() in favor of Request::getClientIP(), I'm OK with leaving it as is for now if there's a @todo with a link to that issue. (If not, then we need such an issue created.)
Comment #36
BerdirThe linked issue was closed as won't fix, so we should try to get rid of the variable_get() here I think.
Not that this can't really be config() as the point of its existence is to avoid a db query.
I see two options:
a) This is is an optional module now, if you don't want the functionality, just disable it. So, just remove that feature.
b) As documented, this is supposed to be set in settings.php, so we could just use global $conf, like we're doing elsewhere currently to avoid a variable_get() dependency.
At the moment, I'd tend towards a). Opinions?
Comment #37
sunQuoting myself from #1479466-71: Convert IP address blocking to config system:
Thus, my concrete recommendation is to remove the
$conf
/variable stuff entirely. There's a single database table that is queried, and only that.Everyone who thinks that this is slow can install better_ban.module from contrib, or even better, be informed that iptables and other OS-level tools are the proper answer if you really care about performance.
Comment #38
BerdirTotally +1 to that :)
Comment #39
ParisLiakos CreditAttribution: ParisLiakos commentedI agree as well:
Here is my work so far..No interdiff since, pretty much a lot changed..
solution to my problem above was doing
->addTag('event_subscriber')
in the bundle..addSubscribeService directly was not working:/Comment #40
Crell CreditAttribution: Crell commentedKilling the variable_get() entirely++
My previous statement regarding a @todo on ip_address() still stands.
ipv6 support and wildcards would be good, but is a separate issue from this one. Retitling so that we can finish up here and then move on.
Comment #41
ParisLiakos CreditAttribution: ParisLiakos commentedThis patch also removes reference from default.settings.php
Comment #42
sunAlmost done! :)
I think we call these "render array" everywhere else.
-capability
Hum... sounds odd. We don't have a guideline for kernel bundle classes yet, so for now, I'd recommend:
"Defines the Ban bundle."
I think it would be a very wise idea to namespace service IDs of bundles with their corresponding module names; i.e.:
Since this is registered as a service, the class won't ever be constructed with the optional $id parameter, so I think this optional argument can be dropped without replacement.
@todo Convert ip_address() to Request::getClientIP().
Another thing we should not learn from Symfony is to put these metadata-methods to the bottom of event subscriber classes. Instead, it's tremendously helpful to see them first in a class, since this definition is crucial to know.
Doesn't necessarily need to be adjusted in this patch, since we need to polish all event subscribers anyway.
Aside from these minor things, this looks good to go for me.
Comment #43
ParisLiakos CreditAttribution: ParisLiakos commentedYeah this seems like a copy-past failure as well:/
if i got this correctly:
I renamed both services provided:
ban_ip_manager
toban.ip_manager
and
subscriber.ban
toban.subscriber
Yes, but doesnt it makes it more testable? eg you can provide different ip addresses
Besides the last one, all other requested changes are incorporated into this patch..
Comment #44
Crell CreditAttribution: Crell commentedPer comment #22, actually, the constructor shouldn't even have an IP address in it at all, and there should be no IP address property. Instead, isDenied() should be passed an IP address as a string, and it checks that. The ip_address() call (and associated documentation) should be moved to the listener.
By convention, the getSubscribedEvents() method should be the last method in the class.
Per above, this should call ip_address() [old style] / $event->getRequest()->getClientIp() [new style] and pass that value to isDenied($ip).
That way, the ban checking object itself can be reused on any arbitrary IP address at an time to see if something *would* be banned. It's not hard coded to whatever IP address it was instantiated with.
Comment #45
sunSee, @Crell is obviously married to blindly following Symfony's order of class methods in event subscribers, regardless of whether it makes any sense. ;) That's why I mentioned that the adjustment could potentially be left out, as we need to re-evaluate and polish those classes anyway later on. Thus, let's just put it back to the end for now. I'm sorry for the confusion and inconvenience caused.
Comment #46
ParisLiakos CreditAttribution: ParisLiakos commentedThanks for the explanation:)
getSubscribedEvents
to the bottom, since thats how it is being used. Let's change it to another issue, if at all.ip_address()
call to subscriber and passed it as an argument toisDenied()
Edit: @sun: np:)
Comment #47
michaelkors4 CreditAttribution: michaelkors4 commentedYou are amazing,i am not very good at about this.
Feel confused and keep studying from you nowinginging.
Comment #48
sunThanks! This looks ready to fly for me, in case the testbot will confirm.
Comment #49
Crell CreditAttribution: Crell commentedLOL. I hadn't actually seen sun's comment about moving it when I posted that. :-) In any case, yes, if the bot approves of this then so do I.
Comment #51
ParisLiakos CreditAttribution: ParisLiakos commentedThe failure is due to #1825568: Random test failure in Drupal\system\Tests\Entity\EntityFormTest
I think there is no need to re-test this
Comment #52
xjm#46: 1794754-ban_followup-45.patch queued for re-testing.
Comment #53
catchNow that ban is an optional module I think it's fine to remove the settings.php hack. Everything else here looks fine so I've gone ahead and committed/pushed this.
Comment #54
yched CreditAttribution: yched commentedwhy
+use Drupal\Core\Database\Database;
in BanBundle.php ?Comment #55
ParisLiakos CreditAttribution: ParisLiakos commentedYeap no reason to use database there.
removed the line and played around after clearing cache. everything works.
nice catch yched
Comment #56
BerdirWe have dozens of those stale use lines in core, we always forget to remove them and almost every file that I open with Netbeans marks some uses as unused. I guess having a coder review check for that would be useful.
This is RTBC.
Comment #57
Lars Toomre CreditAttribution: Lars Toomre commentedI opened #1831992: Add check for excess 'use' statements at top of code files to add the suggested rule to Coder as suggested in #56.
Comment #58
catchCommitted/pushed the follow-up, thanks!
Comment #60
David_Rothstein CreditAttribution: David_Rothstein commentedSee #1929506: Banned IP addresses can still access cached pages which (I believe) is an interesting side effect of this issue.
Comment #61
podarok#60 FYI #1833442-74: Remove hook_boot()
Comment #61.0
podarokUpdated issue summary.