Create plugin for firewall rule editing
In order to edit firewall rules the API enforces that the edit include all firewall rules at once. This plugin enables to show, edit, add or remove one firewall rule at a time (by fetching the list of rules and editing it in a single command), with the following usage (copied from the code):
# Adding a rule to allow ssh traffic on TCP port 22 to all ipv4 addresses:
linode-cli fw-rule add 12345 allow-ssh --tcp 22 --ipv4 all --accept
# Editing the previous rule to only allow specific ip4 address range
linode-cli fw-rule edit 12345 allow-ssh --ipv4 192.168.1.0/24 10.0.0.0/16
# Showing the rule
linode-cli fw-rule show 12345 allow-ssh
# Removing the rule
linode-cli fw-rule remove 12345 allow-ssh
# Editing an outbound rule, changing policy from ACCEPT to DROP
linode-cli fw-rule 12345 outbound-label --outbound --drop
The plugin has an extensive --help for other options as well.
Note: this is written to the best of my ability to be compatible with python 2.x and 3.x. However, I do not have a proper testing environment for python2, but since it's EOL was quite a while ago, and since other plugins in this repo are not at all compatible, hopefully this will suffice
Closes #290
This looks great!
Giving this a spin locally, I ran into this:
wsmith@linode::linode-cli[plugin-fw-rule]$ python3 -m linodecli fw-rule show 333 test
Traceback (most recent call last):
File "/usr/local/Cellar/[email protected]/3.8.6/Frameworks/Python.framework/Versions/3.8/lib/python3.8/runpy.py", line 194, in _run_module_as_main
return _run_code(code, main_globals, None,
File "/usr/local/Cellar/[email protected]/3.8.6/Frameworks/Python.framework/Versions/3.8/lib/python3.8/runpy.py", line 87, in _run_code
exec(code, run_globals)
File "/Users/wsmith/source/linode-cli/linodecli/__main__.py", line 5, in <module>
main()
File "/Users/wsmith/source/linode-cli/linodecli/__init__.py", line 509, in main
plugins.invoke(parsed.command, plugin_args, context)
File "/Users/wsmith/source/linode-cli/linodecli/plugins/__init__.py", line 34, in invoke
plugin.call(args, context)
File "/Users/wsmith/source/linode-cli/linodecli/plugins/fw-rule.py", line 139, in call
existing_labels = [r["label"] for r in existing_rules]
File "/Users/wsmith/source/linode-cli/linodecli/plugins/fw-rule.py", line 139, in <listcomp>
existing_labels = [r["label"] for r in existing_rules]
KeyError: 'label'
The "label" field is not documented as required, although the API by design should return all fields in a response model regardless (so I'll be filing an internal bug to get that fixed).
This suggests that there may be lots of firewalls out there whose rules don't have labels (as mine didn't) that won't meaningfully work with this plugin, although I don't see a better option for an identifier in the documented response fields.
I don't know that anything needs to change on this end - I'm fine with this only supporting rules that have labels - but I wanted to note it (and the associated API bug).
Thank you for the remarks, I'll try to think how to handle these cases in the plugin as well
@Dorthu I added a simple explicit test for this error with a better error message, at least users will know that this is the case
Hey @Dorthu any idea if and when this will get merged?
Sorry for the delay, last week was crazy. I'll circle back to this today or tomorrow.
Having talked to the firewall team here, I'm not sure that we want to take this approach - we accept the full rulesets instead of individual rules to ensure consistency of the firewall, since changes will be applied as soon as they're received. There aren't any fields in a firewall rule that can uniquely identify it (labels aren't required and aren't required to be unique either), and it's possible to add duplicate rules as well. The intention was that automated systems would regenerate and resend the complete ruleset on changes, and interactive systems would allow the user to view/modify the entire ruleset in a fetch-update-publish workflow.
I opened #294 as an attempt at the latter, but I'm not sure it fits your use case. If it doesn't, I'd be interested in what problem you're trying to solve, as I'm sure there's a way to do it.
If this plugin does do what you need, you might consider using and/or distributing it as a Third Party Plugin (an example is here), which only requires installing the module and registering it with the CLI before use.
Thanks again for the willingness the create a feature for this, however, I must disagree with several things here.
The most important thing is that an "interactive" approach does not lend itself to programmatic use, and must require user input. The original use-case that inspired this PR and the related issue is to be able to control the firewall programmatically.
For example:
A person wants to open to keep SSH ports closed unless the server is malfunctioning, he should be able to easily prepare a script that opens the right firewall port, fixing the problem, and then closing the port again - all without having to interact with the full rule list and without tedious manual editing.
The manual approach with this plugin would be:
linode-cli fw-rule edit 12345 allow-ssh --accept
ssh my.server.com
linode-cli fw-rule edit 12345 allow-ssh --drop
While it's still possible to be even more sophisticated programmatically without interaction:
linode-cli fw-rule edit 12345 allow-ssh --accept
scp my.server.com:/etc/very-important-logs.log local.log
linode-cli fw-rule edit 12345 allow-ssh --drop
This is fit for example for organization policies (or personal preferences) that disallow certain open ports on a regular basis. This approach is possible on Google Cloud (GCP), AWS and probably other platforms I'm less familiar with. It's currently not possible on Linode. An interactive approach would be a step in the right direction, but it does not solve most problems IMO.
Another use case example would be to preform such on edit in CI/CD operations, such as GitHub actions.
Now, to address several of your additional concerns:
we accept the full rulesets instead of individual rules to ensure consistency of the firewall, since changes will be applied as soon as they're received
This plugin does not and CAN NOT do anything other than interact with the full rule set behind the scenes. In fact it calls the built-in functions of linode-cli to do anything.
labels aren't required and aren't required to be unique either
I assume this is the exception and not the rule for most users, but even if that's wrong, this plugin just refuses to edit anything if labels are found to be duplicate or if they are missing. This fits whoever already has uniquely labeled rules, in addition to those creating new firewall rules from scratch with this plugin (since it enforces labels even if Linode doesn't)
The only downside I can see here is what you wrote in #294 :
these rules are applied in order
This can easily be fixed within the plugin so that rules are not re-ordered if something is edited or removed. Moreover, the actual web interface in Linode Manager for firewalls doesn't allow anything more sophisticated than that - you can only add a rule at the end of the rule list, re-ordering it only after it has been created (although admittedly, this is done before "saving" which then writes the entire list at once). So even when adding a rule, we could issue a warning to the user, or even add additional arguments for simple cases like stacking the new rule as first or last.
The interactive approach does have its merits and use-cases, but it is in my opinion far less useful than a programmatic approach that relies on command line arguments.
Having said all that it's actually possible to combine the two approaches. There is no reason why this plugin couldn't also accept an --interactive flag in which case such editing is possible (using basically what you wrote in #294). This can also be suggested to the user automatically, if non-unique labels (or no labels) are detected while attempting to edit non-interactively.
I'm already using this plugin internally along with several co-workers, so I know its useful, but of course it is you and your team's judgement that counts here, and whatever you decide will be OK. I'll be happy to hear your thoughts on combining both approaches.
Just following up, can this be merged or closed? @Dorthu
@ofersadan85 Thanks for the contribution! Unfortunately, we're not able to merge this PR at the moment due to the possible race conditions that could occur from editing multiple firewall rules in parallel.
That said, I can definitely see the use-case for this plugin and encourage you to consider publishing it as a third-party plugin.
@lgarber-akamai No worries! Thanks for your hard work as well. @Dorthu Thank you as well for your input!
I will consider trying to publish this as a third-party plugin if I have time to dive back into it.