-
Notifications
You must be signed in to change notification settings - Fork 1.7k
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
#2165 Global configuration SecurityOptions
#2170
Conversation
SecurityOptions
to global configuration
There was a problem hiding this 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 fromSteps
- Develop basic config tests
8b0c83b
to
60fa237
Compare
Dear Fabrizio, |
@raman-m sorry for the mail bombing, but switching to develop and running all tests the result is the following Am I missing something to run unit test correctly? |
Oh, you love screenshots, my dear friend... |
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. |
There was a problem hiding this 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(); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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? 🥲
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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())) |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed checking clientIp
There was a problem hiding this comment.
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:
Ocelot/src/Ocelot/LoadBalancer/Lease.cs
Lines 58 to 62 in 41fc9bd
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:
Ocelot/src/Ocelot/LoadBalancer/Lease.cs
Lines 55 to 56 in 41fc9bd
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
Ocelot/src/Ocelot/Requester/MessageInvokerPool.cs
Lines 119 to 120 in 41fc9bd
public static bool operator ==(MessageInvokerCacheKey left, MessageInvokerCacheKey right) => left.Equals(right); | |
public static bool operator !=(MessageInvokerCacheKey left, MessageInvokerCacheKey right) => !(left == right); |
Cool, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
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 ? 🤣
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, next time... 😋
Gui, do you know that this feature and its internal classes can produce 1 million of strings in memory? 😂 |
This reverts commit 3e4d4a4.
…ecurityOptions constructr parameters
b8d2031
to
4a37e8b
Compare
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. |
There was a problem hiding this 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 👇
test/Ocelot.UnitTests/Configuration/SecurityOptionsCreatorTests.cs
Outdated
Show resolved
Hide resolved
test/Ocelot.UnitTests/Configuration/SecurityOptionsCreatorTests.cs
Outdated
Show resolved
Hide resolved
Co-authored-by: Raman Maksimchuk <dotnet044@gmail.com>
@Fabman08 Please stop any development! Don't push anything!.. I'm making final review... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ready for Delivery ✅
SecurityOptions
to global configurationSecurityOptions
SecurityOptions
SecurityOptions
@Fabman08 Thank you, Fabrizio, for this contribution! 👍 |
Closes #2165
Proposed Changes
As the issue #2165 reports, the
SecurityOptions
are missing inGlobalConfiguration
.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