Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#2165 Global configuration SecurityOptions #2170

Merged
merged 15 commits into from
Nov 20, 2024

Conversation

Fabman08
Copy link
Contributor

@Fabman08 Fabman08 commented Oct 11, 2024

Closes #2165

Proposed Changes

As the issue #2165 reports, the SecurityOptions are missing in GlobalConfiguration.

This pull request adds the ability to configure Allowed and Blocked IPs globally: the global configuration will be applied for every route unless an existing SecurityOptions is present on it

@Fabman08 Fabman08 marked this pull request as ready for review October 11, 2024 06:53
@raman-m raman-m added Security Options Ocelot feature: Security Options Configuration Ocelot feature: Configuration labels Oct 11, 2024
@raman-m raman-m added this to the October'24 milestone Oct 11, 2024
@raman-m raman-m added the Oct'24 October 2024 release label Oct 11, 2024
@raman-m raman-m changed the title Adds SecurityOptions to Global Configuration Adds SecurityOptions to global configuration Oct 11, 2024
Copy link
Member

@raman-m raman-m left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absence of Acceptance Tests❗

What I realized, currently, there are no Security acceptance tests in place, which is undoubtedly a significant concern.

Please develop acceptance tests:

  • Create "Security" folder in Ocelot.AcceptanceTests project
  • Add IpSecurityTests class inheriting from Steps
  • Develop basic config tests

@raman-m raman-m requested a review from RaynaldM October 14, 2024 08:52
@Fabman08 Fabman08 requested a review from raman-m October 15, 2024 10:32
@Fabman08 Fabman08 force-pushed the fix/ipBlockAllowGlobalCnf branch from 8b0c83b to 60fa237 Compare October 15, 2024 11:02
@raman-m
Copy link
Member

raman-m commented Oct 17, 2024

Dear Fabrizio,
Why are you pushing commits to the origin when the tests fail? Please verify that the tests pass locally before pushing new commits to the origin. My mailbox is overflowing with failed build reports from your feature branch!

@raman-m raman-m marked this pull request as draft October 17, 2024 12:29
@Fabman08
Copy link
Contributor Author

Dear Fabrizio, Why are you pushing commits to the origin when the tests fail? Please verify that the tests pass locally before pushing new commits to the origin. My mailbox is overflowing with failed build reports from your feature branch!

@raman-m sorry for the mail bombing, but switching to develop and running all tests the result is the following

Ocelot_develop_summary

Am I missing something to run unit test correctly?

@raman-m
Copy link
Member

raman-m commented Oct 18, 2024

Oh, you love screenshots, my dear friend...
image
Current builds in develop: Passed
Top commit checks status: ce0227c has passed
All CircleCI builds here: develop builds

@raman-m
Copy link
Member

raman-m commented Oct 18, 2024

but switching to develop and running all tests the result is the following

Regarding the execution of tests in Visual Studio locally, we have certain precise tests that are highly sensitive to CPU resource availability. These tests generally pass when run silently through the terminal. They are designed to pass 99% of the time, but occasionally they may fail. In Visual Studio, additional UI tasks may be running, and if your PC's CPU is at 90-100% capacity, these precise tests might fail. Usually, running the tests again resolves the issue. Ensure that your PC's CPU is not busy, and Visual Studio is not engaged in background compilation or other intensive tasks. If the tests fail, attempt to run them once more.

@raman-m
Copy link
Member

raman-m commented Oct 18, 2024

My proof that we should not panic. Below are the results from my local testing in Visual Studio, which indicate that everything is functioning correctly.
image
Everything is fine!

@ggnaegi
Copy link
Member

ggnaegi commented Oct 25, 2024

@raman-m @Fabman08 could you please rebase instead of merge?

Copy link
Member

@ggnaegi ggnaegi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok for me, but I would add some changes...

}

public SecurityOptions(string allowed = null, string blocked = null)
: this()
{
if (!string.IsNullOrEmpty(allowed))
{
IPAllowedList.Add(allowed);
IPAllowedList = IPAllowedList.Append(allowed).ToList();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is in my opinion a bit over-engineered, I would keep it simple, avoid calling this() and simply instantiate the two lists and then add the elements.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skipped due to raman-m answer

Gui, do you know that this feature and its internal classes can produce 1 million of strings in memory?

Copy link
Member

@ggnaegi ggnaegi Nov 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@raman-m @Fabman08 I'm probably too dumb, but could you clarify? My point is, as soon as you call SecurityOptions(...): this(), the lists will be recreated... So what's the relation with the 1 million of strings in memory? My understanding of it was, that you could end up with very big lists for reach route.

Copy link
Member

@raman-m raman-m Nov 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ggnaegi The issue lies in the design of the IPSecurityPolicy, which relies on processing string objects, often referred to as a flawed design (aka Chinese design, the author is Chinese developer who developed Security feat in 2017/18). It is my firm belief that IP policy processing should be based on IpAddress objects instead of solely relying on strings.

Another concern of mine is that using the Contains method to compare strings, this is a workable solution but it requires having a list of all IP strings. However, in general, it is incorrect to compare IP strings due to the presence of the . dot character mixed with digits. The appropriate comparator for IP strings should position the segments as follows:

  • 10.20.1.200 should be converted to _10._20.__1.200
  • 10.1.10.100 should be converted to _10.__1._10.100
  • 111.222.123.123 should remain unchanged.

The optimal solution is to convert IP to IpAddress or even to a long value. I believe the internal state of IpAddress is based on long values.

So what's the relation with the 1 million of strings in memory?

This is a problem of the design issue mentioned above, descendant problem. The Creator class logic parses the IpAddress object and converts it into strings.
Let's consider the following test cases:

  • the range 192.168.1.1-192.168.255.255: it produces 2562, a maximum of 65 536 strings in the list
  • the range 10.1.1.1-10.255.255.255: it generates 2563, a maximum of 16 777 216 string objects in the list, resulting in 16 million items❗ This highlights a significant problem.

However, this design issue is out of the current scope. Let the author to complete this config-feat.

Copy link
Member

@raman-m raman-m Nov 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Fabman08 Do you understand this code review issue?
Would you prefer to keep calling base constructor this() but remove producing new list .ToList()?
Or would you remove this() but wanna keep .ToList()? 😃

P.S. To be fair, I don't understand why did you propose this change? The old code seems pretty optimized and it's valid which was based on this()...
Wanted to fix Visual Studio warning with .Add to upgrade to .Append, right? 🥲

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@raman-m Yes, I understand the issue reported, and I choose to keep calling this() and remove .ToList().
Fixed in last commit

Copy link
Member

@raman-m raman-m Nov 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will add my concerns as a new TODO-task for the next refactoring round.

@@ -18,7 +18,7 @@ public async Task<Response> Security(DownstreamRoute downstreamRoute, HttpContex

if (securityOptions.IPBlockedList != null)
{
if (securityOptions.IPBlockedList.Exists(f => f == clientIp.ToString()))
if (securityOptions.IPBlockedList.Contains(clientIp.ToString()))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ClientIp could be null, it's not handled

Copy link
Contributor Author

@Fabman08 Fabman08 Nov 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed checking clientIp

Copy link
Member

@raman-m raman-m Nov 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not to develop operators aka equality operator methods for IpAddress and string objects, if their classes have no already implemented ones? 😉
After operators implementation it will be possible to simply write: IPBlockedList.Contains(clientIp) because of IEquatable<T> interface.

See examples of operator implementations, current ones. The awesome possibility of operators as long as other methods is having 2 arguments of different types. But classic operators override the operation for 2 operands of the same type.

Different types of 2 operands:

public static bool operator ==(ServiceHostAndPort h, Lease l) => h == l.HostAndPort;
public static bool operator !=(ServiceHostAndPort h, Lease l) => !(h == l);
public static bool operator ==(Lease l, ServiceHostAndPort h) => l.HostAndPort == h;
public static bool operator !=(Lease l, ServiceHostAndPort h) => !(l == h);

The same types of 2 operands:

public static bool operator ==(Lease x, Lease y) => x.HostAndPort == y.HostAndPort; // ignore -> x.Connections == y.Connections;
public static bool operator !=(Lease x, Lease y) => !(x == y);

and
public static bool operator ==(MessageInvokerCacheKey left, MessageInvokerCacheKey right) => left.Equals(right);
public static bool operator !=(MessageInvokerCacheKey left, MessageInvokerCacheKey right) => !(left == right);

Cool, right?

Copy link
Member

@raman-m raman-m Nov 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The funny fact is that YARP project developers sometimes implement operators: only 4 types in whole project ! 🤣
But they are Microsoft senior engineers with 150K+ salaries, right @ggnaegi ? 😄
And those operators are pretty elementary, simple equality.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe in a next Feature PR ? 🤣

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, next time... 😋

src/Ocelot/Security/IPSecurity/IPSecurityPolicy.cs Outdated Show resolved Hide resolved
src/Ocelot/Configuration/Creator/SecurityOptionsCreator.cs Outdated Show resolved Hide resolved
@raman-m
Copy link
Member

raman-m commented Oct 25, 2024

Ok for me, but I would add some changes...

Gui, do you know that this feature and its internal classes can produce 1 million of strings in memory? 😂

@raman-m raman-m added Winter'25 Winter 2025 release and removed Oct'24 October 2024 release labels Oct 26, 2024
@raman-m raman-m modified the milestones: October'24, Autumn'24 Oct 26, 2024
@Fabman08 Fabman08 force-pushed the fix/ipBlockAllowGlobalCnf branch from b8d2031 to 4a37e8b Compare November 19, 2024 07:47
@raman-m
Copy link
Member

raman-m commented Nov 19, 2024

Could we reduce the number of rebase operations that involve force pushes? A rebase is necessary when conflicts arise, but if are no conflicts, it would be better to simply merge from develop. The reason I bring this up is messages from the team during code reviews seem to be lost after rebasing.
Ultimately, I would prefer not to lose our code review results.

@raman-m
Copy link
Member

raman-m commented Nov 19, 2024

@ggnaegi commented on Oct 25

@raman-m @Fabman08 could you please rebase instead of merge?

I disagree! Read my msg above! There were no conflicts. Let's rebase only in case of merge conflicts...

Copy link
Member

@raman-m raman-m left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My final suggestions below 👇

@raman-m
Copy link
Member

raman-m commented Nov 20, 2024

@Fabman08 Please stop any development! Don't push anything!.. I'm making final review...

Copy link
Member

@raman-m raman-m left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ready for Delivery ✅

@raman-m raman-m changed the title Adds SecurityOptions to global configuration Global configuration SecurityOptions Nov 20, 2024
@raman-m raman-m changed the title Global configuration SecurityOptions #2165 Global configuration SecurityOptions Nov 20, 2024
@raman-m raman-m merged commit 7a5046f into ThreeMammals:develop Nov 20, 2024
1 check passed
@raman-m
Copy link
Member

raman-m commented Nov 20, 2024

@Fabman08 Thank you, Fabrizio, for this contribution! 👍
We completed the feature in 40 days. Not bad! 😄

@Fabman08
Copy link
Contributor Author

@Fabman08 Thank you, Fabrizio, for this contribution! 👍
We completed the feature in 40 days. Not bad! 😄

@raman-m thank you too!! It's a pleasure for me collaborate for this project! I'm sorry for the misunderstanding, I hope to be more accurate the next collaboration

@raman-m raman-m added Nov'24 November 2024 release and removed Winter'25 Winter 2025 release labels Nov 22, 2024
@raman-m raman-m modified the milestones: Autumn'24, November'24 Nov 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Configuration Ocelot feature: Configuration Nov'24 November 2024 release Security Options Ocelot feature: Security Options
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Issue with IP Blocking and Allowing in global configuration
5 participants