-
Notifications
You must be signed in to change notification settings - Fork 16
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
allow unknown protocols #49
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -391,8 +391,8 @@ func (c *Controller) Run(ctx context.Context) error { | |
|
||
packet, err := parsePacket(*a.Payload) | ||
if err != nil { | ||
klog.Infof("Can not process packet %d applying default policy (failOpen: %v): %v", *a.PacketID, c.config.FailOpen, err) | ||
c.nfq.SetVerdict(*a.PacketID, verdict) //nolint:errcheck | ||
klog.Infof("Can not process packet %d, allowing ... : %v", *a.PacketID, err) | ||
c.nfq.SetVerdict(*a.PacketID, nfqueue.NfAccept) //nolint:errcheck | ||
return 0 | ||
} | ||
packet.id = *a.PacketID | ||
|
@@ -645,7 +645,7 @@ func (c *Controller) syncNFTablesRules(ctx context.Context) error { | |
} | ||
} | ||
|
||
for _, hook := range []knftables.BaseChainHook{knftables.ForwardHook} { | ||
for _, hook := range []knftables.BaseChainHook{knftables.ForwardHook, knftables.OutputHook} { | ||
chainName := string(hook) | ||
tx.Add(&knftables.Chain{ | ||
Name: chainName, | ||
|
@@ -656,6 +656,16 @@ func (c *Controller) syncNFTablesRules(ctx context.Context) error { | |
tx.Flush(&knftables.Chain{ | ||
Name: chainName, | ||
}) | ||
|
||
if hook == knftables.OutputHook { | ||
// accept all outbound traffic to the loopback interface | ||
tx.Add(&knftables.Rule{ | ||
Chain: chainName, | ||
Rule: knftables.Concat( | ||
"oif", "lo", "accept"), | ||
}) | ||
Comment on lines
+662
to
+666
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @uablrek can you check in your PR this solves the problem? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added this update, but the address matches should never hit anything with "lo" since such packet would be a "martian" There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It worked! My world is crumbling! My reasoning is that since the set's only contains pod-addresses, packets matching whose sets can't be routed to/from "lo", since that would make them martians. Unless... a SatefulSet pod actually have a loopback address as pod-address. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 commentThe reason will be displayed to describe this comment to others. Learn more. The modified chain for reference:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This rule definitely seems wrong. I think sometimes packets are considered by the kernel to be traversing Anyway, the failure here is probably caused by the fact that in ipvs, packets traverse the network stack twice (from source to Anyway, point is, the iptables/nftables logic is going to end up doing the wrong thing for some packets in ipvs mode. The fix might be to ignore packets with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know Calico needs to explicitly know whether you're using iptables or ipvs, so it's possible we actually need different logic There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this rule fixes the IPv6 jobs, ipvs is unrelated here There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
So, what I want to express is "we only need to process in OUTPUT local generated packets that are sent outside (not localhost dest)" I had this issue in the past with kubelet probes and we solved it using the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
oh... so... the issue is that if we add processing on the
Why should it matter though? We shouldn't be blocking any packets that are destined for localhost. With the
boo!
A packet from a pod-network pod addressed to |
||
} | ||
|
||
// instead of aggregating all the expresion in one rule, use two different | ||
// rules to understand if is causing issues with UDP packets with the same | ||
// tuple (https://github.com/kubernetes-sigs/kube-network-policies/issues/12) | ||
|
@@ -665,6 +675,13 @@ func (c *Controller) syncNFTablesRules(ctx context.Context) error { | |
"ct", "state", "established,related", "accept"), | ||
}) | ||
|
||
// only process protocols supported by Kubernetes network policies | ||
tx.Add(&knftables.Rule{ | ||
Chain: chainName, | ||
Rule: knftables.Concat( | ||
"meta", "l4proto", "!=", "{ tcp, udp, sctp }", "accept"), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ping @danwinship There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (There's no real reason to use |
||
}) | ||
|
||
action := fmt.Sprintf("queue num %d", c.config.QueueID) | ||
if c.config.FailOpen { | ||
action += " bypass" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -58,7 +58,7 @@ func parsePacket(b []byte) (packet, error) { | |
// NextHeader (not extension headers supported) | ||
protocol = int(b[6]) | ||
default: | ||
return t, fmt.Errorf("unknown versions %d", version) | ||
return t, fmt.Errorf("unknown IP version %d", version) | ||
} | ||
|
||
var dataOffset int | ||
|
@@ -73,7 +73,7 @@ func parsePacket(b []byte) (packet, error) { | |
t.proto = v1.ProtocolSCTP | ||
dataOffset = hdrlen + 8 | ||
default: | ||
return t, fmt.Errorf("unknown protocol %d", protocol) | ||
return t, fmt.Errorf("IP family %s unknown transport protocol %d", t.family, protocol) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You don't want to log an error for every ICMPv6 message. You should add a (It seems that your callback only gets called for IP and IPv6 protocol packets, right? And, in particular, not ARP and ICMP packets, which are separate L3 protocols, unlike ICMPv6/NDP, which are subprotocols of IPv6.) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. see the next commit, only TCP , UDP and SCTP packets are sent to the userspace There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At the moment I think all fragments will also have an unknown protocol. But I am about to fix that (#15) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
That may fix the fragment problem. The kernel will reassemble packets before sending them to user-space (perhaps it already does, in which case #15 isn't a kind/bug after all). |
||
} | ||
// TCP, UDP and SCTP srcPort and dstPort are the first 4 bytes after the IP header | ||
t.srcPort = int(binary.BigEndian.Uint16(b[hdrlen : hdrlen+2])) | ||
|
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'm not sure this is really correct
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 means this packet is not TCP or UDP or SCTP
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.
No, it means "a parsing error occurred". I'm not sure we want to say all parse errors should be "accepts".
(I'm not sure we don't want that either...)
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 see, it can only happens if is not ip or ipv6 or one of those protocols ... but indeed is confusing the way it is now
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.
All packets with unknown protocol (not TCP or UDP or SCTP) must be accepted. Unfortunately there is no
v1.ProtocolUnknown
constant, but we can type-cast:but that feels a bit hackish...
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.
Would it be ok to just store the proto as an int in
struct packet
, and let the caller match on "syscall.IPPROTO_*". IMO that's the most flexible.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 point was that it doesn't seem right to say that every possible type of error from
parsePacket
should result in the packet being accepted. IMO it would be better to haveparsePacket
return both an error and a verdict, even if that verdict happens to be "accept" for all of the errors it currently catches.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.
After some investigation, I agree: all unrecognized packets shall not be accepted (#51 (comment)). However, I think
parsePacket()
should not set a verdict. It should parse the packet and let the caller decide, but then is can't return TCP,UDP,SCTP or an error for everything else.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.
that sounds like the best idea
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 have experimented some with this, and I think the best is to keep packet.proto as v1.Protocol, and add a new "ipproto int" in the packet structure.
An IPPROTO_ROUTING extension header, or extension header out-of-order will return a