mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-22 19:31:44 +00:00
Merge pull request #92850 from tallclair/netexec
Enhance agnhost netexec for SSRF E2Es
This commit is contained in:
commit
17376e6aef
@ -393,7 +393,7 @@ Starts a HTTP(S) server on given port with the following endpoints:
|
|||||||
- `protocol`: The protocol which will be used when making the request. Default value: `http`.
|
- `protocol`: The protocol which will be used when making the request. Default value: `http`.
|
||||||
Acceptable values: `http`, `udp`, `sctp`.
|
Acceptable values: `http`, `udp`, `sctp`.
|
||||||
- `tries`: The number of times the request will be performed. Default value: `1`.
|
- `tries`: The number of times the request will be performed. Default value: `1`.
|
||||||
- `/echo`: Returns the given `msg` (`/echo?msg=echoed_msg`)
|
- `/echo`: Returns the given `msg` (`/echo?msg=echoed_msg`), with the optional status `code`.
|
||||||
- `/exit`: Closes the server with the given code and graceful shutdown. The endpoint's parameters
|
- `/exit`: Closes the server with the given code and graceful shutdown. The endpoint's parameters
|
||||||
are:
|
are:
|
||||||
- `code`: The exit code for the process. Default value: 0. Allows an integer [0-127].
|
- `code`: The exit code for the process. Default value: 0. Allows an integer [0-127].
|
||||||
@ -407,6 +407,8 @@ Starts a HTTP(S) server on given port with the following endpoints:
|
|||||||
it exited.
|
it exited.
|
||||||
- `/hostname`: Returns the server's hostname.
|
- `/hostname`: Returns the server's hostname.
|
||||||
- `/hostName`: Returns the server's hostname.
|
- `/hostName`: Returns the server's hostname.
|
||||||
|
- `/redirect`: Returns a redirect response to the given `location`, with the optional status `code`
|
||||||
|
(`/redirect?location=/echo%3Fmsg=foobar&code=307`).
|
||||||
- `/shell`: Executes the given `shellCommand` or `cmd` (`/shell?cmd=some-command`) and
|
- `/shell`: Executes the given `shellCommand` or `cmd` (`/shell?cmd=some-command`) and
|
||||||
returns a JSON containing the fields `output` (command's output) and `error` (command's
|
returns a JSON containing the fields `output` (command's output) and `error` (command's
|
||||||
error message). Returns `200 OK` if the command succeeded, `417 Expectation Failed` if not.
|
error message). Returns `200 OK` if the command succeeded, `417 Expectation Failed` if not.
|
||||||
@ -419,6 +421,9 @@ If `--tls-cert-file` is added (ideally in conjunction with `--tls-private-key-fi
|
|||||||
will be upgraded to HTTPS. The image has default, `localhost`-based cert/privkey files at
|
will be upgraded to HTTPS. The image has default, `localhost`-based cert/privkey files at
|
||||||
`/localhost.crt` and `/localhost.key` (see: [`porter` subcommand](#porter))
|
`/localhost.crt` and `/localhost.key` (see: [`porter` subcommand](#porter))
|
||||||
|
|
||||||
|
If `--http-override` is set, the HTTP(S) server will always serve the override path & options,
|
||||||
|
ignoring the request URL.
|
||||||
|
|
||||||
It will also start a UDP server on the indicated UDP port that responds to the following commands:
|
It will also start a UDP server on the indicated UDP port that responds to the following commands:
|
||||||
|
|
||||||
- `hostname`: Returns the server's hostname
|
- `hostname`: Returns the server's hostname
|
||||||
|
@ -40,13 +40,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
httpPort = 8080
|
httpPort = 8080
|
||||||
udpPort = 8081
|
udpPort = 8081
|
||||||
sctpPort = -1
|
sctpPort = -1
|
||||||
shellPath = "/bin/sh"
|
shellPath = "/bin/sh"
|
||||||
serverReady = &atomicBool{0}
|
serverReady = &atomicBool{0}
|
||||||
certFile = ""
|
certFile = ""
|
||||||
privKeyFile = ""
|
privKeyFile = ""
|
||||||
|
httpOverride = ""
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdNetexec is used by agnhost Cobra.
|
// CmdNetexec is used by agnhost Cobra.
|
||||||
@ -69,7 +70,7 @@ var CmdNetexec = &cobra.Command{
|
|||||||
- "protocol": The protocol which will be used when making the request. Default value: "http".
|
- "protocol": The protocol which will be used when making the request. Default value: "http".
|
||||||
Acceptable values: "http", "udp", "sctp".
|
Acceptable values: "http", "udp", "sctp".
|
||||||
- "tries": The number of times the request will be performed. Default value: "1".
|
- "tries": The number of times the request will be performed. Default value: "1".
|
||||||
- "/echo": Returns the given "msg" ("/echo?msg=echoed_msg")
|
- "/echo": Returns the given "msg" ("/echo?msg=echoed_msg"), with the optional status "code".
|
||||||
- "/exit": Closes the server with the given code and graceful shutdown. The endpoint's parameters
|
- "/exit": Closes the server with the given code and graceful shutdown. The endpoint's parameters
|
||||||
are:
|
are:
|
||||||
- "code": The exit code for the process. Default value: 0. Allows an integer [0-127].
|
- "code": The exit code for the process. Default value: 0. Allows an integer [0-127].
|
||||||
@ -83,6 +84,8 @@ var CmdNetexec = &cobra.Command{
|
|||||||
it exited.
|
it exited.
|
||||||
- "/hostname": Returns the server's hostname.
|
- "/hostname": Returns the server's hostname.
|
||||||
- "/hostName": Returns the server's hostname.
|
- "/hostName": Returns the server's hostname.
|
||||||
|
- "/redirect": Returns a redirect response to the given "location", with the optional status "code"
|
||||||
|
("/redirect?location=/echo%3Fmsg=foobar&code=307").
|
||||||
- "/shell": Executes the given "shellCommand" or "cmd" ("/shell?cmd=some-command") and
|
- "/shell": Executes the given "shellCommand" or "cmd" ("/shell?cmd=some-command") and
|
||||||
returns a JSON containing the fields "output" (command's output) and "error" (command's
|
returns a JSON containing the fields "output" (command's output) and "error" (command's
|
||||||
error message). Returns "200 OK" if the command succeeded, "417 Expectation Failed" if not.
|
error message). Returns "200 OK" if the command succeeded, "417 Expectation Failed" if not.
|
||||||
@ -91,6 +94,13 @@ var CmdNetexec = &cobra.Command{
|
|||||||
Returns a JSON with the fields "output" (containing the file's name on the server) and
|
Returns a JSON with the fields "output" (containing the file's name on the server) and
|
||||||
"error" containing any potential server side errors.
|
"error" containing any potential server side errors.
|
||||||
|
|
||||||
|
If "--tls-cert-file" is added (ideally in conjunction with "--tls-private-key-file", the HTTP server
|
||||||
|
will be upgraded to HTTPS. The image has default, "localhost"-based cert/privkey files at
|
||||||
|
"/localhost.crt" and "/localhost.key" (see: "porter" subcommand)
|
||||||
|
|
||||||
|
If "--http-override" is set, the HTTP(S) server will always serve the override path & options,
|
||||||
|
ignoring the request URL.
|
||||||
|
|
||||||
It will also start a UDP server on the indicated UDP port that responds to the following commands:
|
It will also start a UDP server on the indicated UDP port that responds to the following commands:
|
||||||
|
|
||||||
- "hostname": Returns the server's hostname
|
- "hostname": Returns the server's hostname
|
||||||
@ -112,6 +122,7 @@ func init() {
|
|||||||
"File containing an x509 private key matching --tls-cert-file")
|
"File containing an x509 private key matching --tls-cert-file")
|
||||||
CmdNetexec.Flags().IntVar(&udpPort, "udp-port", 8081, "UDP Listen Port")
|
CmdNetexec.Flags().IntVar(&udpPort, "udp-port", 8081, "UDP Listen Port")
|
||||||
CmdNetexec.Flags().IntVar(&sctpPort, "sctp-port", -1, "SCTP 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")
|
||||||
}
|
}
|
||||||
|
|
||||||
// atomicBool uses load/store operations on an int32 to simulate an atomic boolean.
|
// atomicBool uses load/store operations on an int32 to simulate an atomic boolean.
|
||||||
@ -135,7 +146,21 @@ func (a *atomicBool) get() bool {
|
|||||||
|
|
||||||
func main(cmd *cobra.Command, args []string) {
|
func main(cmd *cobra.Command, args []string) {
|
||||||
exitCh := make(chan shutdownRequest)
|
exitCh := make(chan shutdownRequest)
|
||||||
addRoutes(exitCh)
|
if httpOverride != "" {
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
addRoutes(mux, exitCh)
|
||||||
|
|
||||||
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
overrideReq, err := http.NewRequestWithContext(r.Context(), "GET", httpOverride, nil)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, fmt.Sprintf("override request failed: %v", err), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mux.ServeHTTP(w, overrideReq)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
addRoutes(http.DefaultServeMux, exitCh)
|
||||||
|
}
|
||||||
|
|
||||||
go startUDPServer(udpPort)
|
go startUDPServer(udpPort)
|
||||||
if sctpPort != -1 {
|
if sctpPort != -1 {
|
||||||
@ -150,22 +175,24 @@ func main(cmd *cobra.Command, args []string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func addRoutes(exitCh chan shutdownRequest) {
|
func addRoutes(mux *http.ServeMux, exitCh chan shutdownRequest) {
|
||||||
http.HandleFunc("/", rootHandler)
|
mux.HandleFunc("/", rootHandler)
|
||||||
http.HandleFunc("/clientip", clientIPHandler)
|
mux.HandleFunc("/clientip", clientIPHandler)
|
||||||
http.HandleFunc("/echo", echoHandler)
|
mux.HandleFunc("/dial", dialHandler)
|
||||||
http.HandleFunc("/exit", func(w http.ResponseWriter, req *http.Request) { exitHandler(w, req, exitCh) })
|
mux.HandleFunc("/echo", echoHandler)
|
||||||
http.HandleFunc("/hostname", hostnameHandler)
|
mux.HandleFunc("/exit", func(w http.ResponseWriter, req *http.Request) { exitHandler(w, req, exitCh) })
|
||||||
http.HandleFunc("/shell", shellHandler)
|
mux.HandleFunc("/healthz", healthzHandler)
|
||||||
http.HandleFunc("/upload", uploadHandler)
|
mux.HandleFunc("/hostname", hostnameHandler)
|
||||||
http.HandleFunc("/dial", dialHandler)
|
mux.HandleFunc("/redirect", redirectHandler)
|
||||||
http.HandleFunc("/healthz", healthzHandler)
|
mux.HandleFunc("/shell", shellHandler)
|
||||||
|
mux.HandleFunc("/upload", uploadHandler)
|
||||||
// older handlers
|
// older handlers
|
||||||
http.HandleFunc("/hostName", hostNameHandler)
|
mux.HandleFunc("/hostName", hostNameHandler)
|
||||||
http.HandleFunc("/shutdown", shutdownHandler)
|
mux.HandleFunc("/shutdown", shutdownHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
func startServer(server *http.Server, exitCh chan shutdownRequest, fn func() error) {
|
func startServer(server *http.Server, exitCh chan shutdownRequest, fn func() error) {
|
||||||
|
log.Printf("Started HTTP server on port %d", httpPort)
|
||||||
go func() {
|
go func() {
|
||||||
re := <-exitCh
|
re := <-exitCh
|
||||||
ctx, cancelFn := context.WithTimeout(context.Background(), re.timeout)
|
ctx, cancelFn := context.WithTimeout(context.Background(), re.timeout)
|
||||||
@ -190,8 +217,18 @@ func rootHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func echoHandler(w http.ResponseWriter, r *http.Request) {
|
func echoHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
log.Printf("GET /echo?msg=%s", r.FormValue("msg"))
|
msg := r.FormValue("msg")
|
||||||
fmt.Fprintf(w, "%s", r.FormValue("msg"))
|
codeString := r.FormValue("code")
|
||||||
|
log.Printf("GET /echo?msg=%s&code=%s", msg, codeString)
|
||||||
|
if codeString != "" {
|
||||||
|
code, err := strconv.Atoi(codeString)
|
||||||
|
if err != nil && codeString != "" {
|
||||||
|
fmt.Fprintf(w, "argument 'code' must be an integer or empty, got %q\n", codeString)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.WriteHeader(code)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "%s", msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func clientIPHandler(w http.ResponseWriter, r *http.Request) {
|
func clientIPHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -488,6 +525,22 @@ func hostNameHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
fmt.Fprint(w, getHostName())
|
fmt.Fprint(w, getHostName())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func redirectHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
location := r.FormValue("location")
|
||||||
|
codeString := r.FormValue("code")
|
||||||
|
log.Printf("%s /redirect?msg=%s&code=%s", r.Method, location, codeString)
|
||||||
|
code := http.StatusFound
|
||||||
|
if codeString != "" {
|
||||||
|
var err error
|
||||||
|
code, err = strconv.Atoi(codeString)
|
||||||
|
if err != nil && codeString != "" {
|
||||||
|
fmt.Fprintf(w, "argument 'code' must be an integer or empty, got %q\n", codeString)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
http.Redirect(w, r, location, code)
|
||||||
|
}
|
||||||
|
|
||||||
// udp server supports the hostName, echo and clientIP commands.
|
// udp server supports the hostName, echo and clientIP commands.
|
||||||
func startUDPServer(udpPort int) {
|
func startUDPServer(udpPort int) {
|
||||||
serverAddress, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%d", udpPort))
|
serverAddress, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%d", udpPort))
|
||||||
@ -497,7 +550,7 @@ func startUDPServer(udpPort int) {
|
|||||||
defer serverConn.Close()
|
defer serverConn.Close()
|
||||||
buf := make([]byte, 2048)
|
buf := make([]byte, 2048)
|
||||||
|
|
||||||
log.Printf("Started UDP server")
|
log.Printf("Started UDP server on port %d", udpPort)
|
||||||
// Start responding to readiness probes.
|
// Start responding to readiness probes.
|
||||||
serverReady.set(true)
|
serverReady.set(true)
|
||||||
defer func() {
|
defer func() {
|
||||||
|
Loading…
Reference in New Issue
Block a user