Agnhost: make it possible to pass the addresses to listen on for udp

The current udp implementation listens on any for tcp, udp and tcp. There
are some cases where it makes sense to listen on specific addresses
(especially udp, see https://github.com/kubernetes/kubernetes/issues/95565).
This is because UDP is connectionless, and in order to conntrack to
work, the application must ensure that the src of the reply is the same
as the dest of the request. The easiest way to do that is to bind
explicitly on an ip.
Here we pass an optional parameter that contains a comma separated list
of addresses.

Signed-off-by: Federico Paolinelli <fpaoline@redhat.com>
This commit is contained in:
Federico Paolinelli 2021-02-08 12:10:54 +01:00
parent 66cbf0196b
commit 5125f1fc31

View File

@ -37,19 +37,23 @@ import (
"github.com/spf13/cobra"
utilnet "k8s.io/apimachinery/pkg/util/net"
"k8s.io/apimachinery/pkg/util/sets"
)
var (
httpPort = 8080
udpPort = 8081
sctpPort = -1
shellPath = "/bin/sh"
serverReady = &atomicBool{0}
certFile = ""
privKeyFile = ""
httpOverride = ""
httpPort = 8080
udpPort = 8081
sctpPort = -1
shellPath = "/bin/sh"
serverReady = &atomicBool{0}
certFile = ""
privKeyFile = ""
httpOverride = ""
udpListenAddresses = ""
)
const bindToAny = ""
// CmdNetexec is used by agnhost Cobra.
var CmdNetexec = &cobra.Command{
Use: "netexec",
@ -123,6 +127,7 @@ func init() {
CmdNetexec.Flags().IntVar(&udpPort, "udp-port", 8081, "UDP Listen Port")
CmdNetexec.Flags().IntVar(&sctpPort, "sctp-port", -1, "SCTP Listen Port")
CmdNetexec.Flags().StringVar(&httpOverride, "http-override", "", "Override the HTTP handler to always respond as if it were a GET with this path & params")
CmdNetexec.Flags().StringVar(&udpListenAddresses, "udp-listen-addresses", "", "A comma separated list of ip addresses the udp servers listen from")
}
// atomicBool uses load/store operations on an int32 to simulate an atomic boolean.
@ -162,7 +167,14 @@ func main(cmd *cobra.Command, args []string) {
addRoutes(http.DefaultServeMux, exitCh)
}
go startUDPServer(udpPort)
udpBindTo, err := parseAddresses(udpListenAddresses)
if err != nil {
log.Fatal(err)
}
for _, address := range udpBindTo {
go startUDPServer(address, udpPort)
}
if sctpPort != -1 {
go startSCTPServer(sctpPort)
}
@ -539,15 +551,15 @@ func redirectHandler(w http.ResponseWriter, r *http.Request) {
}
// udp server supports the hostName, echo and clientIP commands.
func startUDPServer(udpPort int) {
serverAddress, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%d", udpPort))
func startUDPServer(address string, udpPort int) {
serverAddress, err := net.ResolveUDPAddr("udp", net.JoinHostPort(address, strconv.Itoa(udpPort)))
assertNoError(err, fmt.Sprintf("failed to resolve UDP address for port %d", sctpPort))
serverConn, err := net.ListenUDP("udp", serverAddress)
assertNoError(err, fmt.Sprintf("failed to create listener for UDP address %v", serverAddress))
defer serverConn.Close()
buf := make([]byte, 2048)
log.Printf("Started UDP server on port %d", udpPort)
log.Printf("Started UDP server on port %s %d", address, udpPort)
// Start responding to readiness probes.
serverReady.set(true)
defer func() {
@ -639,3 +651,21 @@ func assertNoError(err error, detail string) {
log.Fatalf("Error occurred: %s:%v", detail, err)
}
}
func parseAddresses(addresses string) ([]string, error) {
if addresses == "" {
return []string{bindToAny}, nil
}
// Using a set to remove duplicates
res := make([]string, 0)
split := strings.Split(addresses, ",")
for _, address := range split {
netAddr := net.ParseIP(address)
if netAddr == nil {
return nil, fmt.Errorf("parseAddress: invalid address %s", address)
}
res = append(res, address)
}
set := sets.NewString(res...)
return set.List(), nil
}