diff --git a/test/images/agnhost/README.md b/test/images/agnhost/README.md index ab4fe8bd44a..459d3213e1d 100644 --- a/test/images/agnhost/README.md +++ b/test/images/agnhost/README.md @@ -621,6 +621,29 @@ Usage: kubectl exec test-agnhost -- /agnhost serve-hostname [--tcp] [--udp] [--http] [--close] [--port ] ``` +### tcp-reset + +Starts a simple TCP servers that reads only one byte of the connection and then closes it, +having the effect of sending a TCP RST to the client. + +The subcommand can accept the following flags: + +- `port` (default: `8080`): The port number to listen to. + +Usage: + +```console + kubectl exec test-agnhost -- /agnhost tcp-reset [--port ] +``` + +Important: This behavior only works if the client send more than 1 byte and is OS +dependant, it is guaranteed to work on Linux. + +```console +echo -n 1 | nc 192.168.1.4:8080 # FIN +echo -n 12 | nc 192.168.1.4:8080 # RST +``` + ### test-webserver Starts a simple HTTP fileserver which serves any file specified in the URL path, if it exists. diff --git a/test/images/agnhost/VERSION b/test/images/agnhost/VERSION index 7cd20263835..f454b8169e3 100644 --- a/test/images/agnhost/VERSION +++ b/test/images/agnhost/VERSION @@ -1 +1 @@ -2.43 +2.44 diff --git a/test/images/agnhost/agnhost.go b/test/images/agnhost/agnhost.go index bfc0f190d65..7f32499a14e 100644 --- a/test/images/agnhost/agnhost.go +++ b/test/images/agnhost/agnhost.go @@ -45,6 +45,7 @@ import ( "k8s.io/kubernetes/test/images/agnhost/porter" resconsumerctrl "k8s.io/kubernetes/test/images/agnhost/resource-consumer-controller" servehostname "k8s.io/kubernetes/test/images/agnhost/serve-hostname" + tcpreset "k8s.io/kubernetes/test/images/agnhost/tcp-reset" testwebserver "k8s.io/kubernetes/test/images/agnhost/test-webserver" "k8s.io/kubernetes/test/images/agnhost/webhook" ) @@ -81,6 +82,7 @@ func main() { rootCmd.AddCommand(resconsumerctrl.CmdResourceConsumerController) rootCmd.AddCommand(servehostname.CmdServeHostname) rootCmd.AddCommand(testwebserver.CmdTestWebserver) + rootCmd.AddCommand(tcpreset.CmdTCPReset) rootCmd.AddCommand(webhook.CmdWebhook) rootCmd.AddCommand(openidmetadata.CmdTestServiceAccountIssuerDiscovery) rootCmd.AddCommand(grpchealthchecking.CmdGrpcHealthChecking) diff --git a/test/images/agnhost/tcp-reset/tcp_reset.go b/test/images/agnhost/tcp-reset/tcp_reset.go new file mode 100644 index 00000000000..a7ab5edb3c9 --- /dev/null +++ b/test/images/agnhost/tcp-reset/tcp_reset.go @@ -0,0 +1,78 @@ +/* +Copyright 2023 The Kubernetes 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. +*/ + +// A small utility to send RST on the TCP connection +// Ref: https://gosamples.dev/connection-reset-by-peer/ + +package tcpreset + +import ( + "fmt" + "log" + "net" + "os" + "os/signal" + "syscall" + + "github.com/spf13/cobra" +) + +// CmdTCPReset is used by agnhost Cobra. +var CmdTCPReset = &cobra.Command{ + Use: "tcp-reset", + Short: "Serves on a tcp port and RST the connections received", + Long: `Serves on a tcp port and RST the connections received.`, + Args: cobra.MaximumNArgs(0), + Run: main, +} + +var ( + port int +) + +func init() { + CmdTCPReset.Flags().IntVar(&port, "port", 8080, "Port number.") +} + +func main(cmd *cobra.Command, args []string) { + + listener, err := net.Listen("tcp", fmt.Sprintf(":%d", port)) + if err != nil { + log.Fatalf("Error from net.Listen(): %s", err) + } + go func() { + for { + conn, err := listener.Accept() + if err != nil { + log.Fatalf("Error from Accept(): %s", err) + } + // if there are still data in the buffer to read + // and the connection is closed, it will send a RST. + buf := make([]byte, 1) + conn.Read(buf) + conn.Close() + log.Printf("TCP request from %s", conn.RemoteAddr().String()) + } + }() + + log.Printf("Serving on port %d.\n", port) + signals := make(chan os.Signal, 1) + signal.Notify(signals, syscall.SIGTERM) + sig := <-signals + log.Printf("Shutting down after receiving signal: %s.\n", sig) + log.Printf("Awaiting pod deletion.\n") + +}