Skip to content
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

Add option to overwrite dial address #515

Merged
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions services/httpoverrpc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# HTTPOVERRPC
This directory contains the http over rpc services. It is a simple service that allows to send http requests to from target hosts


## Usage

### sanssh httpoverrpc get
Make a HTTP(-S) request to a specified port on the remote host.

```bash
sanssh <sanssh-args> get [-method <method>] [-header <header>] [-dial-address <dial-address>] [-insecure-skip-tls-verify] [-show-response-headers] [-body <body>] [-protocol <protocol>] [-hostname <hostname>] <remoteport> <request_uri>:
```

Where:
- `<body>` body to send in request
- `<dial-address>` host:port to dial to. If not provided would dial to original host and port
- `<header>` Header to send in the request, may be specified multiple times.
- `<hostname>` ip address or domain name to specify host (default "localhost")
- `<method>` method to use in the HTTP request (default "GET")
- `<protocol>` protocol to communicate with specified hostname (default "http")
- `<remoteport>` port to connect to on the remote host
- `<request_uri>` URI to request

Flags:
- `-show-response-headers` If provided, print response code and headers
- `-insecure-skip-tls-verify` If provided, skip TLS verification


Note:
1. The prefix / in request_uri is always needed, even there is nothing to put
2. If we use `--hostname` to send requests to a specified host instead of the default localhost, and want to use sansshell proxy action
to proxy requests, don't forget to add `--allow-any-host` for proxy action

Examples:
```bash
# send get request to https://localhost:9090/hello
httpoverrpc get --hostname 10.1.23.4 --protocol https 9090 /hello
# send get request with url http://example.com:9090/hello, but deal to localhost:9090
httpoverrpc get --hostname example.com --dialAddress localhost:9090 9090 /hello
```
29 changes: 25 additions & 4 deletions services/httpoverrpc/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import (
"github.com/Snowflake-Labs/sansshell/client"
pb "github.com/Snowflake-Labs/sansshell/services/httpoverrpc"
"github.com/Snowflake-Labs/sansshell/services/util"
"github.com/Snowflake-Labs/sansshell/services/util/validator"
)

const subPackage = "httpoverrpc"
Expand Down Expand Up @@ -226,16 +227,20 @@ type getCmd struct {
protocol string
hostname string
insecureSkipVerify bool
dialAddress string
}

func (*getCmd) Name() string { return "get" }
func (*getCmd) Synopsis() string { return "Makes a HTTP call to a port on a remote host" }
func (*getCmd) Synopsis() string { return "Makes a HTTP(-S) call to a port on a remote host" }
func (*getCmd) Usage() string {
return `get [-method METHOD] [-header Header...] [-body body] [-protocol Protocol] [-hostname Hostname] remoteport request_uri:
return `get [-method METHOD] [-header Header...] [-dial-address dialAddress] [-body body] [-protocol Protocol] [-hostname Hostname] remoteport request_uri:
Make a HTTP request to a specified port on the remote host.

Example:
Examples:
# send get request to https://localhost:9090/hello
httpoverrpc get --hostname 10.1.23.4 --protocol https 9090 /hello
sfc-gh-ikryvanos marked this conversation as resolved.
Show resolved Hide resolved
# send get request with url http://example.com:9090/hello, but dial to localhost:9090
httpoverrpc get --hostname example.com --dialAddress localhost:9090 9090 /hello

Note:
1. The prefix / in request_uri is always needed, even there is nothing to put
Expand All @@ -252,6 +257,7 @@ func (g *getCmd) SetFlags(f *flag.FlagSet) {
f.StringVar(&g.body, "body", "", "Body to send in request")
f.BoolVar(&g.showResponseHeaders, "show-response-headers", false, "If true, print response code and headers")
f.BoolVar(&g.insecureSkipVerify, "insecure-skip-tls-verify", false, "If true, skip TLS cert verification")
f.StringVar(&g.dialAddress, "dial-address", "", "host:port to dial to. If not provided would dial to original host and port")
}

func (g *getCmd) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
Expand All @@ -270,6 +276,14 @@ func (g *getCmd) Execute(ctx context.Context, f *flag.FlagSet, args ...interface
return subcommands.ExitUsageError
}

if g.dialAddress != "" {
_, _, err := validator.ParseHostAndPort(g.dialAddress)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to parse dial address \"%q\": %v\n", g.dialAddress, err)
return subcommands.ExitUsageError
}
}

var reqHeaders []*pb.Header
for _, v := range g.headers {
split := strings.SplitN(v, ":", 2)
Expand All @@ -280,7 +294,12 @@ func (g *getCmd) Execute(ctx context.Context, f *flag.FlagSet, args ...interface
reqHeaders = append(reqHeaders, &pb.Header{Key: split[0], Values: []string{strings.TrimSpace(split[1])}})
}

proxy := pb.NewHTTPOverRPCClientProxy(state.Conn)
var dialConfig *pb.DialConfig = nil
if g.dialAddress != "" {
dialConfig = &pb.DialConfig{
DialAddress: &g.dialAddress,
}
}

req := &pb.HostHTTPRequest{
Request: &pb.HTTPRequest{
Expand All @@ -295,8 +314,10 @@ func (g *getCmd) Execute(ctx context.Context, f *flag.FlagSet, args ...interface
Tlsconfig: &pb.TLSConfig{
InsecureSkipVerify: g.insecureSkipVerify,
},
Dialconfig: dialConfig,
}

proxy := pb.NewHTTPOverRPCClientProxy(state.Conn)
resp, err := proxy.HostOneMany(ctx, req)
retCode := subcommands.ExitSuccess
if err != nil {
Expand Down
20 changes: 19 additions & 1 deletion services/httpoverrpc/client/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,20 @@ var (
errInvalidURLMissingHost = fmt.Errorf("no host in the request URL")
)

// HTTPTransporter implementation of [http.RoundTripper], which proxy HTTP requests over RPC to target host to send request from it
type HTTPTransporter struct {
conn *proxy.Conn
insecureSkipVerify bool
dialConfigFactory DialConfigFactory
}

type httpTransporterOptions struct {
insecureSkipVerify bool
dialConfigFactory DialConfigFactory
}

type DialConfigFactory func(req *http.Request) *pb.DialConfig

type Option interface {
apply(*httpTransporterOptions)
}
Expand All @@ -63,7 +68,13 @@ func WithInsecureSkipVerify(insecureSkipVerify bool) Option {
})
}

func NewHTTPTransporter(conn *proxy.Conn, opts ...Option) *HTTPTransporter {
func WithCustomDialConfig(dialConfigFactory DialConfigFactory) Option {
return optionFunc(func(o *httpTransporterOptions) {
o.dialConfigFactory = dialConfigFactory
})
}

func NewHTTPTransporter(conn *proxy.Conn, opts ...Option) http.RoundTripper {
options := &httpTransporterOptions{
insecureSkipVerify: false,
}
Expand All @@ -74,6 +85,7 @@ func NewHTTPTransporter(conn *proxy.Conn, opts ...Option) *HTTPTransporter {
return &HTTPTransporter{
conn: conn,
insecureSkipVerify: options.insecureSkipVerify,
dialConfigFactory: options.dialConfigFactory,
}
}

Expand Down Expand Up @@ -152,6 +164,11 @@ func (c *HTTPTransporter) RoundTrip(req *http.Request) (*http.Response, error) {
return nil, err
}
}

var dialConfig *pb.DialConfig = nil
if c.dialConfigFactory != nil {
dialConfig = c.dialConfigFactory(req)
}
reqPb := &pb.HostHTTPRequest{
Request: &pb.HTTPRequest{
RequestUri: req.URL.RequestURI(),
Expand All @@ -164,6 +181,7 @@ func (c *HTTPTransporter) RoundTrip(req *http.Request) (*http.Response, error) {
Tlsconfig: &pb.TLSConfig{
InsecureSkipVerify: c.insecureSkipVerify,
},
Dialconfig: dialConfig,
}

port, errPort := getPort(req, reqPb.Protocol)
Expand Down
Loading
Loading