Skip to content

Conversation

@vakalapa
Copy link
Contributor

@vakalapa vakalapa commented Nov 18, 2020

Reason for Change:

NPM creates IPsets for the following today:

  • NameSpace
  • Label
  • LabelKey:Value
  • NamedPorts
  • CIDR blocks

Namespace and namespace labels are getting a ns- prefix helping NPM to distinguish between PodLabels and NameSpaceLabels. But there are several instances where the IPSet names for Labels and NamedPorts are surfacing. This occurs when a podLabel http:test and a namedPort http is created. An example can be seen in #733 . NPM hashes http from label and creates a IpSet with this hash, then when it is adding namedPort http it would hash to the same value and it logs this below error.

Stderr: [exit status 1, ipset v6.34: Syntax error: Elem separator in <IP_ADDR>,<PORT_NUM>, but settype hash:net supports none.]

Using ":" as prefix delimiter as k8s does not allow using a ":" in label names.

Issue Fixed:
#733

Further Enhancements
Name clash probability is still high. Work item is added to have a robust logic while adding IpSets

@vakalapa vakalapa marked this pull request as ready for review November 18, 2020 04:42
@vakalapa vakalapa linked an issue Nov 18, 2020 that may be closed by this pull request
npm/pod_test.go Outdated
if err := npMgr.AddPod(podObj); err != nil {
t.Errorf("TestAddPod failed @ AddPod")
}
if !ipsMgr.Exists(util.GetHashedName("app:test-pod"), "1.2.3.4,8080", "") {
Copy link
Contributor

Choose a reason for hiding this comment

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

Will it be port:app:test-pod?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes thank you.

@codecov
Copy link

codecov bot commented Nov 18, 2020

Codecov Report

Merging #734 (3aba506) into master (28207fc) will increase coverage by 0.29%.
The diff coverage is 47.12%.

@@            Coverage Diff             @@
##           master     #734      +/-   ##
==========================================
+ Coverage   39.06%   39.36%   +0.29%     
==========================================
  Files          83       83              
  Lines       10713    10827     +114     
==========================================
+ Hits         4185     4262      +77     
- Misses       6022     6061      +39     
+ Partials      506      504       -2     

npm/npm.go Outdated
iptMgr.UninitNpmChains()

log.Logf("Azure-NPM creating, cleaning existing IPSets")
destroyErr := ipsm.NewIpsetManager().Destroy()
Copy link
Member

Choose a reason for hiding this comment

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

Wont this cleanup all the ipsets from that node? We should only clean the ones created by Azure-NPM. Also ignore the error as this is best effort to clean the old ipsets.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As we discussed offline, NPM is not saving state any state while updating, to avoid any leak of ipsets, we will destroy everything. I logged a work item to add state fullness to this logic.

Copy link
Member

Choose a reason for hiding this comment

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

Agree with Neha here, an ipset flush wipes out all ipsets, including ones NPM may have not created, this is where we need to iterate through all ipsets that contain azure-npm

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added support to just delete azure-npm-(numerics) ipsets and lists

IpsetNomatch string = "nomatch"

//Prefixes for ipsets
NamedPortIPSetPrefix string = "port:"
Copy link
Member

@neaggarwMS neaggarwMS Nov 18, 2020

Choose a reason for hiding this comment

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

Can we rename the value from port: to portname: (or namedport:):, port seems very common

for _, portRule := range rule.Ports {
if portRule.Port != nil && portRule.Port.IntValue() == 0 {
portName := portRule.Port.String()
portName := util.NamedPortIPSetPrefix + portRule.Port.String()
Copy link
Member

Choose a reason for hiding this comment

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

Can we confirm this? We can still have a port rule without a named port, right?

Copy link
Member

Choose a reason for hiding this comment

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

Okay checked the logic, all port strings are getting appending to Namedports and in the end we call util.DropEmptyFields(namedPorts) (Line: 1438). This is weird. But with the updated logic empty port strings will not be dropped. We should only add if portRule.port.String() is not empty. This should have been the logic at first place instead of double parsing :-/

Copy link
Member

Choose a reason for hiding this comment

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

We should still add a test with policy which has port rules (without named ports) and one without

Copy link
Contributor Author

Choose a reason for hiding this comment

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

NPM is allowing this below network policy, corrected the behavior now.

  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: backend
      ports:
        - port: ""

Copy link
Member

@neaggarwMS neaggarwMS left a comment

Choose a reason for hiding this comment

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

Left minor comments, rest looks good.

npm/ipsm/ipsm.go Outdated
entry.operationFlag = util.IpsetDestroyFlag
entry.set = ipsetName
if _, err := ipsMgr.Run(entry); err != nil {
metrics.SendErrorMetric(util.IpsmID, "Error: failed to destroy ipset %s", ipsetName)
Copy link
Member

Choose a reason for hiding this comment

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

Can you add the FunctionName in the log statement for better debugability: DestroyNpmIpsets (We dont print the FileName/Line# in the Geneva output, although we should.)

npm/ipsm/ipsm.go Outdated

if _, err := ipsMgr.Run(entry); err != nil {
metrics.SendErrorMetric(util.IpsmID, "Error: failed to flush ipset %s", ipsetName)
log.Logf("Error: failed to flush ipset %s", ipsetName)
Copy link
Member

Choose a reason for hiding this comment

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

SendErrorMetric also logs the error. Actually that is an incorrect functionName it should be SendErrorLog or SendLog

npm/ipsm/ipsm.go Outdated
entry.set = ipsetName
if _, err := ipsMgr.Run(entry); err != nil {
metrics.SendErrorMetric(util.IpsmID, "Error: failed to destroy ipset %s", ipsetName)
log.Logf("Error: failed to destroy ipset %s", ipsetName)
Copy link
Member

Choose a reason for hiding this comment

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

Again remove the duplicate logF line

}

func getPortType(portRule networkingv1.NetworkPolicyPort) string {
if portRule.Port == nil {
Copy link
Member

Choose a reason for hiding this comment

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

nit: We can combine 1st and last check
if portRule.Port == nil || portRule.Port.IntValue() != 0
{
return "validport"
} else ...

npm/npm.go Outdated
log.Logf("Azure-NPM creating, cleaning existing Azure NPM IPSets")
destroyErr := ipsm.NewIpsetManager().DestroyNpmIpsets()
if destroyErr != nil {
log.Logf("Azure-NPM error occurred while destroying existing IPSets err: %s", destroyErr.Error())
Copy link
Contributor

Choose a reason for hiding this comment

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

nit, wondering, if DestroyNpmIpsets did metrics.SendErrorMetric inside its function, whether do we want to log here again.

npm/ipsm/ipsm.go Outdated
for _, matchedItem := range ipsetRegexSlice {
if len(matchedItem) == 2 {
itemString := string(matchedItem[1])
if strings.Contains(itemString, "azure-npm") {
Copy link
Contributor

@csfmomo csfmomo Nov 19, 2020

Choose a reason for hiding this comment

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

nit, do we want to use a const for "azure-npm"

csfmomo
csfmomo previously approved these changes Nov 19, 2020
csfmomo
csfmomo previously approved these changes Nov 19, 2020

func getPortType(portRule networkingv1.NetworkPolicyPort) string {
if portRule.Port == nil || portRule.Port.IntValue() != 0 {
return "validport"
Copy link
Member

Choose a reason for hiding this comment

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

can we make these consts in case we end up using this elsewhere?

neaggarwMS
neaggarwMS previously approved these changes Nov 20, 2020
@vakalapa vakalapa dismissed stale reviews from neaggarwMS and csfmomo via 3aba506 November 20, 2020 01:23
@vakalapa vakalapa merged commit 8ae7b8a into master Nov 20, 2020
@vakalapa vakalapa deleted the vakr/npm_port_name branch November 20, 2020 07:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[NPM] issue with using same string for namedPort and labelKey

5 participants