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

proxy mode: operate on http responses rather than requests #29

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
31 changes: 31 additions & 0 deletions faillog/faillog.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package faillog

import (
"bytes"
"io"
"os"
)

// Logger emits content to stderr on Close() if caller panics or
// Success(false) is called.
type Logger struct {
success bool
buffer bytes.Buffer
}

func (f *Logger) Close() error {
if f.success {
return nil
}

_, err := io.Copy(os.Stderr, &f.buffer)
return err
}

func (f *Logger) Success(success bool) {
f.success = success
}

func (f *Logger) Write(p []byte) (n int, err error) {
return f.buffer.Write(p)
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ require (
github.com/kenshaw/baseconv v0.1.0
github.com/mitchellh/go-homedir v1.1.0
github.com/smartystreets/goconvey v1.6.4 // indirect
github.com/stretchr/testify v1.7.0
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c // indirect
gopkg.in/ini.v1 v1.62.0
)
11 changes: 11 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ github.com/buger/goterm v0.0.0-20200322175922-2f3e71b85129 h1:gfAMKE626QEuKG3si0
github.com/buger/goterm v0.0.0-20200322175922-2f3e71b85129/go.mod h1:u9UyCz2eTrSGy6fbupqJ54eY5c4IC8gREQ1053dK12U=
github.com/clbanning/mxj/v2 v2.3.2 h1:DSkU65zfrBHtrggxd54X9pK1z/Lw2OwSW5D8p+x1toE=
github.com/clbanning/mxj/v2 v2.3.2/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/iann0036/goproxy v0.0.0-20210327130343-c3ec674b9022 h1:CEm/EWm32SV+k85dmgF8KDXMeN5zsgplaz07utBYIsk=
github.com/iann0036/goproxy v0.0.0-20210327130343-c3ec674b9022/go.mod h1:amSgpNkX5MQL0glEHvPGg0UKbKOhBVem3kNHGhlINS4=
Expand All @@ -12,15 +14,24 @@ github.com/kenshaw/baseconv v0.1.0 h1:eZd+ZgNkU8jxjp/dTwAhPwI923cz1PE7ARRKUPXjZ5
github.com/kenshaw/baseconv v0.1.0/go.mod h1:yy9zGmnnR6vgOxOQb702nVdAG30JhyYZpj/5/m0siRI=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
4 changes: 3 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"flag"
"fmt"
"log"
"net/http"
"os"
"os/exec"
"runtime/pprof"
Expand Down Expand Up @@ -151,7 +152,8 @@ func main() {
handleLoggedCall()
} else if *modeFlag == "proxy" {
readServiceFiles()
createProxy(*bindAddrFlag)
proxy := createProxy()
log.Fatal(http.ListenAndServe(*bindAddrFlag, proxy))
} else {
fmt.Println("ERROR: unknown mode")
}
Expand Down
45 changes: 25 additions & 20 deletions proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,9 @@ func loadCAKeys() error {
return nil
}

func createProxy(addr string) {
var awsHostnameRegexp = regexp.MustCompile(`^.*\.amazonaws\.com(?:\.cn)?$`)

func createProxy() *goproxy.ProxyHttpServer {
err := loadCAKeys()
if err != nil {
log.Fatal(err)
Expand All @@ -153,19 +155,23 @@ func createProxy(addr string) {
proxy := goproxy.NewProxyHttpServer()
proxy.Logger = log.New(io.Discard, "", log.LstdFlags)
proxy.OnRequest().HandleConnect(goproxy.AlwaysMitm)
proxy.OnRequest().DoFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) { // TODO: Move to onResponse for HTTP response codes
proxy.OnRequest().DoFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
body, _ := ioutil.ReadAll(req.Body)
req.Body = ioutil.NopCloser(bytes.NewBuffer(body))
ctx.UserData = body
return req, nil
})
proxy.OnResponse().DoFunc(func(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response {
resp.Request.Body = ioutil.NopCloser(bytes.NewBuffer(ctx.UserData.([]byte)))

isAWSHostname, _ := regexp.MatchString(`^.*\.amazonaws\.com(?:\.cn)?$`, req.Host)
if isAWSHostname {
handleAWSRequest(req, body, 200)
if awsHostnameRegexp.MatchString(resp.Request.Host) {
handleAWSRequest(resp)
}

req.Body = ioutil.NopCloser(bytes.NewBuffer(body))

return req, nil
return resp
})
log.Fatal(http.ListenAndServe(addr, proxy))

return proxy
}

type ServiceDefinition struct {
Expand Down Expand Up @@ -278,9 +284,12 @@ type ActionCandidate struct {
Operation ServiceOperation
}

func handleAWSRequest(req *http.Request, body []byte, respCode int) {
func handleAWSRequest(resp *http.Response) {
req := resp.Request
host := req.Host
uri := req.RequestURI

body, _ := ioutil.ReadAll(req.Body)
req.Body = ioutil.NopCloser(bytes.NewBuffer(body))
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved the request body draining into this handleAWSRequest function to more closely match e.g. what httputil.DumpRequest does.


var endpointUriPrefix string
var service string
Expand Down Expand Up @@ -391,21 +400,17 @@ func handleAWSRequest(req *http.Request, body []byte, respCode int) {
}
} else if serviceDef.Metadata.Protocol == "rest-json" || serviceDef.Metadata.Protocol == "rest-xml" {
// URL param schema
urlobj, err := url.ParseRequestURI(uri)
if err != nil {
return
}
vals := urlobj.Query()
vals := req.URL.Query()

actionCandidates := []ActionCandidate{}

// path part
OperationLoop:
for operationName, operation := range serviceDef.Operations {
path := urlobj.Path
path := req.URL.Path
if serviceDef.Metadata.EndpointPrefix == "s3" && strings.HasPrefix(operation.Http.RequestURI, "/{Bucket}") && endpointUriPrefix != "" { // https://docs.aws.amazon.com/AmazonS3/latest/userguide/VirtualHosting.html#VirtualHostingSpecifyBucket
if len(urlobj.Path) > 1 {
path = "/" + endpointUriPrefix + "/" + urlobj.Path[1:]
if len(req.URL.Path) > 1 {
path = "/" + endpointUriPrefix + "/" + req.URL.Path[1:]
} else {
path = "/" + endpointUriPrefix
}
Expand Down Expand Up @@ -596,7 +601,7 @@ func handleAWSRequest(req *http.Request, body []byte, respCode int) {
Method: action,
Parameters: params,
URIParameters: uriparams,
FinalHTTPStatusCode: respCode,
FinalHTTPStatusCode: resp.StatusCode,
AccessKey: accessKey,
})

Expand Down
60 changes: 60 additions & 0 deletions proxy_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package main

import (
"flag"
"github.com/iann0036/iamlive/faillog"
"github.com/stretchr/testify/assert"
"net/http/httptest"
"os"
"os/exec"
"testing"
)

func TestSimpleRequest(t *testing.T) {
if testing.Short() {
t.Skipf("skipping e2e test")
}

parseConfig()
flag.Parse()
loadMaps()
readServiceFiles()
*modeFlag = "proxy"
Comment on lines +18 to +22
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is pretty ugly, but I figured I didn't want to make bigger changes in a single PR


p := createProxy()
ts := httptest.NewServer(p)
defer ts.Close()

fl := &faillog.Logger{}
defer fl.Close()

cmd := exec.Command("aws", "sts", "get-caller-identity")
cmd.Stdout = fl
cmd.Stderr = fl
cmd.Env = append(os.Environ(),
"AWS_CA_BUNDLE=~/.iamlive/ca.pem",
"HTTP_PROXY="+ts.URL,
"HTTPS_PROXY="+ts.URL,
)

err := cmd.Run()
assert.NoError(t, err)

doc := getPolicyDocument()
assert.JSONEq(t, `
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"sts:GetCallerIdentity"
],
"Resource": "*"
}
]
}
`, string(doc))

fl.Success(!t.Failed())
}
15 changes: 15 additions & 0 deletions vendor/github.com/davecgh/go-spew/LICENSE

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading