Merge pull request #2130 from nybidari:iptables

PiperOrigin-RevId: 303208407
This commit is contained in:
gVisor bot
2020-03-26 15:47:00 -07:00
22 changed files with 451 additions and 17 deletions
+41
View File
@@ -509,3 +509,44 @@ const (
// Enable all flags.
XT_UDP_INV_MASK = 0x03
)
// IPTOwnerInfo holds data for matching packets with owner. It corresponds
// to struct ipt_owner_info in libxt_owner.c of iptables binary.
type IPTOwnerInfo struct {
// UID is user id which created the packet.
UID uint32
// GID is group id which created the packet.
GID uint32
// PID is process id of the process which created the packet.
PID uint32
// SID is session id which created the packet.
SID uint32
// Comm is the command name which created the packet.
Comm [16]byte
// Match is used to match UID/GID of the socket. See the
// XT_OWNER_* flags below.
Match uint8
// Invert flips the meaning of Match field.
Invert uint8
}
// SizeOfIPTOwnerInfo is the size of an XTOwnerMatchInfo.
const SizeOfIPTOwnerInfo = 34
// Flags in IPTOwnerInfo.Match. Corresponding constants are in
// include/uapi/linux/netfilter/xt_owner.h.
const (
// Match the UID of the packet.
XT_OWNER_UID = 1 << 0
// Match the GID of the packet.
XT_OWNER_GID = 1 << 1
// Match if the socket exists for the packet. Forwarded
// packets do not have an associated socket.
XT_OWNER_SOCKET = 1 << 2
)
+1
View File
@@ -29,6 +29,7 @@ func TestSizes(t *testing.T) {
{IPTGetEntries{}, SizeOfIPTGetEntries},
{IPTGetinfo{}, SizeOfIPTGetinfo},
{IPTIP{}, SizeOfIPTIP},
{IPTOwnerInfo{}, SizeOfIPTOwnerInfo},
{IPTReplace{}, SizeOfIPTReplace},
{XTCounters{}, SizeOfXTCounters},
{XTEntryMatch{}, SizeOfXTEntryMatch},
+12
View File
@@ -863,3 +863,15 @@ func (t *Task) SetOOMScoreAdj(adj int32) error {
atomic.StoreInt32(&t.tg.oomScoreAdj, adj)
return nil
}
// UID returns t's uid.
// TODO(gvisor.dev/issue/170): This method is not namespaced yet.
func (t *Task) UID() uint32 {
return uint32(t.Credentials().EffectiveKUID)
}
// GID returns t's gid.
// TODO(gvisor.dev/issue/170): This method is not namespaced yet.
func (t *Task) GID() uint32 {
return uint32(t.Credentials().EffectiveKGID)
}
+1
View File
@@ -7,6 +7,7 @@ go_library(
srcs = [
"extensions.go",
"netfilter.go",
"owner_matcher.go",
"targets.go",
"tcp_matcher.go",
"udp_matcher.go",
+3 -4
View File
@@ -517,11 +517,10 @@ func SetEntries(stk *stack.Stack, optVal []byte) *syserr.Error {
}
// TODO(gvisor.dev/issue/170): Support other chains.
// Since we only support modifying the INPUT chain and redirect for
// PREROUTING chain right now, make sure all other chains point to
// ACCEPT rules.
// Since we only support modifying the INPUT, PREROUTING and OUTPUT chain right now,
// make sure all other chains point to ACCEPT rules.
for hook, ruleIdx := range table.BuiltinChains {
if hook != stack.Input && hook != stack.Prerouting {
if hook == stack.Forward || hook == stack.Postrouting {
if _, ok := table.Rules[ruleIdx].Target.(stack.AcceptTarget); !ok {
nflog("hook %d is unsupported.", hook)
return syserr.ErrInvalidArgument
@@ -0,0 +1,128 @@
// Copyright 2020 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package netfilter
import (
"fmt"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/binary"
"gvisor.dev/gvisor/pkg/tcpip/stack"
"gvisor.dev/gvisor/pkg/usermem"
)
const matcherNameOwner = "owner"
func init() {
registerMatchMaker(ownerMarshaler{})
}
// ownerMarshaler implements matchMaker for owner matching.
type ownerMarshaler struct{}
// name implements matchMaker.name.
func (ownerMarshaler) name() string {
return matcherNameOwner
}
// marshal implements matchMaker.marshal.
func (ownerMarshaler) marshal(mr stack.Matcher) []byte {
matcher := mr.(*OwnerMatcher)
iptOwnerInfo := linux.IPTOwnerInfo{
UID: matcher.uid,
GID: matcher.gid,
}
// Support for UID match.
// TODO(gvisor.dev/issue/170): Need to support gid match.
if matcher.matchUID {
iptOwnerInfo.Match = linux.XT_OWNER_UID
} else if matcher.matchGID {
panic("GID match is not supported.")
} else {
panic("UID match is not set.")
}
buf := make([]byte, 0, linux.SizeOfIPTOwnerInfo)
return marshalEntryMatch(matcherNameOwner, binary.Marshal(buf, usermem.ByteOrder, iptOwnerInfo))
}
// unmarshal implements matchMaker.unmarshal.
func (ownerMarshaler) unmarshal(buf []byte, filter stack.IPHeaderFilter) (stack.Matcher, error) {
if len(buf) < linux.SizeOfIPTOwnerInfo {
return nil, fmt.Errorf("buf has insufficient size for owner match: %d", len(buf))
}
// For alignment reasons, the match's total size may
// exceed what's strictly necessary to hold matchData.
var matchData linux.IPTOwnerInfo
binary.Unmarshal(buf[:linux.SizeOfIPTOwnerInfo], usermem.ByteOrder, &matchData)
nflog("parseMatchers: parsed IPTOwnerInfo: %+v", matchData)
if matchData.Invert != 0 {
return nil, fmt.Errorf("invert flag is not supported for owner match")
}
// Support for UID match.
// TODO(gvisor.dev/issue/170): Need to support gid match.
if matchData.Match&linux.XT_OWNER_UID != linux.XT_OWNER_UID {
return nil, fmt.Errorf("owner match is only supported for uid")
}
// Check Flags.
var owner OwnerMatcher
owner.uid = matchData.UID
owner.gid = matchData.GID
owner.matchUID = true
return &owner, nil
}
type OwnerMatcher struct {
uid uint32
gid uint32
matchUID bool
matchGID bool
invert uint8
}
// Name implements Matcher.Name.
func (*OwnerMatcher) Name() string {
return matcherNameOwner
}
// Match implements Matcher.Match.
func (om *OwnerMatcher) Match(hook stack.Hook, pkt stack.PacketBuffer, interfaceName string) (bool, bool) {
// Support only for OUTPUT chain.
// TODO(gvisor.dev/issue/170): Need to support for POSTROUTING chain also.
if hook != stack.Output {
return false, true
}
// If the packet owner is not set, drop the packet.
// Support for uid match.
// TODO(gvisor.dev/issue/170): Need to support gid match.
if pkt.Owner == nil || !om.matchUID {
return false, true
}
// TODO(gvisor.dev/issue/170): Need to add tests to verify
// drop rule when packet UID does not match owner matcher UID.
if pkt.Owner.UID() != om.uid {
return false, false
}
return true, false
}
+6
View File
@@ -126,6 +126,12 @@ func (p *provider) Socket(t *kernel.Task, stype linux.SockType, protocol int) (*
ep, e = eps.Stack.NewRawEndpoint(transProto, p.netProto, wq, associated)
} else {
ep, e = eps.Stack.NewEndpoint(transProto, p.netProto, wq)
// Assign task to PacketOwner interface to get the UID and GID for
// iptables owner matching.
if e == nil {
ep.SetOwner(t)
}
}
if e != nil {
return nil, syserr.TranslateNetstackError(e)
+15
View File
@@ -244,6 +244,14 @@ func (e *endpoint) WritePacket(r *stack.Route, gso *stack.GSO, params stack.Netw
ip := e.addIPHeader(r, &pkt.Header, pkt.Data.Size(), params)
pkt.NetworkHeader = buffer.View(ip)
// iptables filtering. All packets that reach here are locally
// generated.
ipt := e.stack.IPTables()
if ok := ipt.Check(stack.Output, pkt); !ok {
// iptables is telling us to drop the packet.
return nil
}
if r.Loop&stack.PacketLoop != 0 {
// The inbound path expects the network header to still be in
// the PacketBuffer's Data field.
@@ -280,7 +288,14 @@ func (e *endpoint) WritePackets(r *stack.Route, gso *stack.GSO, pkts []stack.Pac
return len(pkts), nil
}
// iptables filtering. All packets that reach here are locally
// generated.
ipt := e.stack.IPTables()
for i := range pkts {
if ok := ipt.Check(stack.Output, pkts[i]); !ok {
// iptables is telling us to drop the packet.
continue
}
ip := e.addIPHeader(r, &pkts[i].Header, pkts[i].DataSize, params)
pkts[i].NetworkHeader = buffer.View(ip)
}
+8 -1
View File
@@ -13,7 +13,10 @@
package stack
import "gvisor.dev/gvisor/pkg/tcpip/buffer"
import (
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/buffer"
)
// A PacketBuffer contains all the data of a network packet.
//
@@ -59,6 +62,10 @@ type PacketBuffer struct {
// Hash is the transport layer hash of this packet. A value of zero
// indicates no valid hash has been set.
Hash uint32
// Owner is implemented by task to get the uid and gid.
// Only set for locally generated packets.
Owner tcpip.PacketOwner
}
// Clone makes a copy of pk. It clones the Data field, which creates a new
+2
View File
@@ -56,6 +56,8 @@ func (f *fakeTransportEndpoint) Stats() tcpip.EndpointStats {
return nil
}
func (f *fakeTransportEndpoint) SetOwner(owner tcpip.PacketOwner) {}
func newFakeTransportEndpoint(s *stack.Stack, proto *fakeTransportProtocol, netProto tcpip.NetworkProtocolNumber, uniqueID uint64) tcpip.Endpoint {
return &fakeTransportEndpoint{stack: s, TransportEndpointInfo: stack.TransportEndpointInfo{NetProto: netProto}, proto: proto, uniqueID: uniqueID}
}
+12
View File
@@ -336,6 +336,15 @@ type ControlMessages struct {
PacketInfo IPPacketInfo
}
// PacketOwner is used to get UID and GID of the packet.
type PacketOwner interface {
// UID returns UID of the packet.
UID() uint32
// GID returns GID of the packet.
GID() uint32
}
// Endpoint is the interface implemented by transport protocols (e.g., tcp, udp)
// that exposes functionality like read, write, connect, etc. to users of the
// networking stack.
@@ -470,6 +479,9 @@ type Endpoint interface {
// Stats returns a reference to the endpoint stats.
Stats() EndpointStats
// SetOwner sets the task owner to the endpoint owner.
SetOwner(owner PacketOwner)
}
// EndpointInfo is the interface implemented by each endpoint info struct.
+10 -2
View File
@@ -73,6 +73,9 @@ type endpoint struct {
route stack.Route `state:"manual"`
ttl uint8
stats tcpip.TransportEndpointStats `state:"nosave"`
// owner is used to get uid and gid of the packet.
owner tcpip.PacketOwner
}
func newEndpoint(s *stack.Stack, netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, *tcpip.Error) {
@@ -133,6 +136,10 @@ func (e *endpoint) Close() {
// ModerateRecvBuf implements tcpip.Endpoint.ModerateRecvBuf.
func (e *endpoint) ModerateRecvBuf(copied int) {}
func (e *endpoint) SetOwner(owner tcpip.PacketOwner) {
e.owner = owner
}
// IPTables implements tcpip.Endpoint.IPTables.
func (e *endpoint) IPTables() (stack.IPTables, error) {
return e.stack.IPTables(), nil
@@ -321,7 +328,7 @@ func (e *endpoint) write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, <-c
switch e.NetProto {
case header.IPv4ProtocolNumber:
err = send4(route, e.ID.LocalPort, v, e.ttl)
err = send4(route, e.ID.LocalPort, v, e.ttl, e.owner)
case header.IPv6ProtocolNumber:
err = send6(route, e.ID.LocalPort, v, e.ttl)
@@ -415,7 +422,7 @@ func (e *endpoint) GetSockOpt(opt interface{}) *tcpip.Error {
}
}
func send4(r *stack.Route, ident uint16, data buffer.View, ttl uint8) *tcpip.Error {
func send4(r *stack.Route, ident uint16, data buffer.View, ttl uint8, owner tcpip.PacketOwner) *tcpip.Error {
if len(data) < header.ICMPv4MinimumSize {
return tcpip.ErrInvalidEndpointState
}
@@ -444,6 +451,7 @@ func send4(r *stack.Route, ident uint16, data buffer.View, ttl uint8) *tcpip.Err
Header: hdr,
Data: data.ToVectorisedView(),
TransportHeader: buffer.View(icmpv4),
Owner: owner,
})
}
+2
View File
@@ -392,3 +392,5 @@ func (ep *endpoint) Info() tcpip.EndpointInfo {
func (ep *endpoint) Stats() tcpip.EndpointStats {
return &ep.stats
}
func (ep *endpoint) SetOwner(owner tcpip.PacketOwner) {}
+9
View File
@@ -80,6 +80,9 @@ type endpoint struct {
// Connect(), and is valid only when conneted is true.
route stack.Route `state:"manual"`
stats tcpip.TransportEndpointStats `state:"nosave"`
// owner is used to get uid and gid of the packet.
owner tcpip.PacketOwner
}
// NewEndpoint returns a raw endpoint for the given protocols.
@@ -159,6 +162,10 @@ func (e *endpoint) Close() {
// ModerateRecvBuf implements tcpip.Endpoint.ModerateRecvBuf.
func (e *endpoint) ModerateRecvBuf(copied int) {}
func (e *endpoint) SetOwner(owner tcpip.PacketOwner) {
e.owner = owner
}
// IPTables implements tcpip.Endpoint.IPTables.
func (e *endpoint) IPTables() (stack.IPTables, error) {
return e.stack.IPTables(), nil
@@ -348,10 +355,12 @@ func (e *endpoint) finishWrite(payloadBytes []byte, route *stack.Route) (int64,
}
break
}
hdr := buffer.NewPrependable(len(payloadBytes) + int(route.MaxHeaderLength()))
if err := route.WritePacket(nil /* gso */, stack.NetworkHeaderParams{Protocol: e.TransProto, TTL: route.DefaultTTL(), TOS: stack.DefaultTOS}, stack.PacketBuffer{
Header: hdr,
Data: buffer.View(payloadBytes).ToVectorisedView(),
Owner: e.owner,
}); err != nil {
return 0, nil, err
}
+3 -2
View File
@@ -276,7 +276,7 @@ func (l *listenContext) createConnectingEndpoint(s *segment, iss seqnum.Value, i
// and then performs the TCP 3-way handshake.
//
// The new endpoint is returned with e.mu held.
func (l *listenContext) createEndpointAndPerformHandshake(s *segment, opts *header.TCPSynOptions, queue *waiter.Queue) (*endpoint, *tcpip.Error) {
func (l *listenContext) createEndpointAndPerformHandshake(s *segment, opts *header.TCPSynOptions, queue *waiter.Queue, owner tcpip.PacketOwner) (*endpoint, *tcpip.Error) {
// Create new endpoint.
irs := s.sequenceNumber
isn := generateSecureISN(s.id, l.stack.Seed())
@@ -284,6 +284,7 @@ func (l *listenContext) createEndpointAndPerformHandshake(s *segment, opts *head
if err != nil {
return nil, err
}
ep.owner = owner
// listenEP is nil when listenContext is used by tcp.Forwarder.
deferAccept := time.Duration(0)
@@ -414,7 +415,7 @@ func (e *endpoint) handleSynSegment(ctx *listenContext, s *segment, opts *header
}()
defer s.decRef()
n, err := ctx.createEndpointAndPerformHandshake(s, opts, &waiter.Queue{})
n, err := ctx.createEndpointAndPerformHandshake(s, opts, &waiter.Queue{}, e.owner)
if err != nil {
e.stack.Stats().TCP.FailedConnectionAttempts.Increment()
e.stats.FailedConnectionAttempts.Increment()
+6 -4
View File
@@ -745,7 +745,7 @@ func (e *endpoint) sendSynTCP(r *stack.Route, tf tcpFields, opts header.TCPSynOp
func (e *endpoint) sendTCP(r *stack.Route, tf tcpFields, data buffer.VectorisedView, gso *stack.GSO) *tcpip.Error {
tf.txHash = e.txHash
if err := sendTCP(r, tf, data, gso); err != nil {
if err := sendTCP(r, tf, data, gso, e.owner); err != nil {
e.stats.SendErrors.SegmentSendToNetworkFailed.Increment()
return err
}
@@ -787,7 +787,7 @@ func buildTCPHdr(r *stack.Route, tf tcpFields, pkt *stack.PacketBuffer, gso *sta
}
}
func sendTCPBatch(r *stack.Route, tf tcpFields, data buffer.VectorisedView, gso *stack.GSO) *tcpip.Error {
func sendTCPBatch(r *stack.Route, tf tcpFields, data buffer.VectorisedView, gso *stack.GSO, owner tcpip.PacketOwner) *tcpip.Error {
optLen := len(tf.opts)
if tf.rcvWnd > 0xffff {
tf.rcvWnd = 0xffff
@@ -816,6 +816,7 @@ func sendTCPBatch(r *stack.Route, tf tcpFields, data buffer.VectorisedView, gso
pkts[i].DataSize = packetSize
pkts[i].Data = data
pkts[i].Hash = tf.txHash
pkts[i].Owner = owner
buildTCPHdr(r, tf, &pkts[i], gso)
off += packetSize
tf.seq = tf.seq.Add(seqnum.Size(packetSize))
@@ -833,14 +834,14 @@ func sendTCPBatch(r *stack.Route, tf tcpFields, data buffer.VectorisedView, gso
// sendTCP sends a TCP segment with the provided options via the provided
// network endpoint and under the provided identity.
func sendTCP(r *stack.Route, tf tcpFields, data buffer.VectorisedView, gso *stack.GSO) *tcpip.Error {
func sendTCP(r *stack.Route, tf tcpFields, data buffer.VectorisedView, gso *stack.GSO, owner tcpip.PacketOwner) *tcpip.Error {
optLen := len(tf.opts)
if tf.rcvWnd > 0xffff {
tf.rcvWnd = 0xffff
}
if r.Loop&stack.PacketLoop == 0 && gso != nil && gso.Type == stack.GSOSW && int(gso.MSS) < data.Size() {
return sendTCPBatch(r, tf, data, gso)
return sendTCPBatch(r, tf, data, gso, owner)
}
pkt := stack.PacketBuffer{
@@ -849,6 +850,7 @@ func sendTCP(r *stack.Route, tf tcpFields, data buffer.VectorisedView, gso *stac
DataSize: data.Size(),
Data: data,
Hash: tf.txHash,
Owner: owner,
}
buildTCPHdr(r, tf, &pkt, gso)
+7
View File
@@ -603,6 +603,9 @@ type endpoint struct {
// txHash is the transport layer hash to be set on outbound packets
// emitted by this endpoint.
txHash uint32
// owner is used to get uid and gid of the packet.
owner tcpip.PacketOwner
}
// UniqueID implements stack.TransportEndpoint.UniqueID.
@@ -1132,6 +1135,10 @@ func (e *endpoint) ModerateRecvBuf(copied int) {
e.rcvListMu.Unlock()
}
func (e *endpoint) SetOwner(owner tcpip.PacketOwner) {
e.owner = owner
}
// IPTables implements tcpip.Endpoint.IPTables.
func (e *endpoint) IPTables() (stack.IPTables, error) {
return e.stack.IPTables(), nil
+1 -1
View File
@@ -157,7 +157,7 @@ func (r *ForwarderRequest) CreateEndpoint(queue *waiter.Queue) (tcpip.Endpoint,
TSVal: r.synOptions.TSVal,
TSEcr: r.synOptions.TSEcr,
SACKPermitted: r.synOptions.SACKPermitted,
}, queue)
}, queue, nil)
if err != nil {
return nil, err
}
+1 -1
View File
@@ -199,7 +199,7 @@ func replyWithReset(s *segment) {
seq: seq,
ack: ack,
rcvWnd: 0,
}, buffer.VectorisedView{}, nil /* gso */)
}, buffer.VectorisedView{}, nil /* gso */, nil /* PacketOwner */)
}
// SetOption implements stack.TransportProtocol.SetOption.
+10 -2
View File
@@ -143,6 +143,9 @@ type endpoint struct {
// TODO(b/142022063): Add ability to save and restore per endpoint stats.
stats tcpip.TransportEndpointStats `state:"nosave"`
// owner is used to get uid and gid of the packet.
owner tcpip.PacketOwner
}
// +stateify savable
@@ -484,7 +487,7 @@ func (e *endpoint) write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, <-c
useDefaultTTL = false
}
if err := sendUDP(route, buffer.View(v).ToVectorisedView(), e.ID.LocalPort, dstPort, ttl, useDefaultTTL, e.sendTOS); err != nil {
if err := sendUDP(route, buffer.View(v).ToVectorisedView(), e.ID.LocalPort, dstPort, ttl, useDefaultTTL, e.sendTOS, e.owner); err != nil {
return 0, nil, err
}
return int64(len(v)), nil, nil
@@ -886,7 +889,7 @@ func (e *endpoint) GetSockOpt(opt interface{}) *tcpip.Error {
// sendUDP sends a UDP segment via the provided network endpoint and under the
// provided identity.
func sendUDP(r *stack.Route, data buffer.VectorisedView, localPort, remotePort uint16, ttl uint8, useDefaultTTL bool, tos uint8) *tcpip.Error {
func sendUDP(r *stack.Route, data buffer.VectorisedView, localPort, remotePort uint16, ttl uint8, useDefaultTTL bool, tos uint8, owner tcpip.PacketOwner) *tcpip.Error {
// Allocate a buffer for the UDP header.
hdr := buffer.NewPrependable(header.UDPMinimumSize + int(r.MaxHeaderLength()))
@@ -916,6 +919,7 @@ func sendUDP(r *stack.Route, data buffer.VectorisedView, localPort, remotePort u
Header: hdr,
Data: data,
TransportHeader: buffer.View(udp),
Owner: owner,
}); err != nil {
r.Stats().UDP.PacketSendErrors.Increment()
return err
@@ -1356,3 +1360,7 @@ func (*endpoint) Wait() {}
func isBroadcastOrMulticast(a tcpip.Address) bool {
return a == header.IPv4Broadcast || header.IsV4MulticastAddress(a) || header.IsV6MulticastAddress(a)
}
func (e *endpoint) SetOwner(owner tcpip.PacketOwner) {
e.owner = owner
}

Some files were not shown because too many files have changed in this diff Show More