-
Notifications
You must be signed in to change notification settings - Fork 204
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit beea83e
Showing
6 changed files
with
313 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
*~ | ||
vendor | ||
glide.lock | ||
dist |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# How to Contribute | ||
|
||
*aws-es-proxy* is [Apache 2.0 licensed](LICENSE) and accept contributions via | ||
GitHub pull requests. | ||
|
||
## Getting Started | ||
|
||
- Fork the repository on GitHub | ||
- Read the [README](README.md) for build and test instructions | ||
- Play with the project, submit bugs, submit patches! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
#aws-auth-proxy | ||
|
||
**aws-es-proxy** is a small web server application sitting between your HTTP client (browser, curl, etc...) and Amazon Elasticsearch service. It will sign your requests using latest [AWS Signature Version 4](http://docs.aws.amazon.com/general/latest/gr/signature-version-4.html) before sending the request to Amazon Elasticsearch. When response is back from Amazon Elasticsearch, this response will be sent back to your HTTP client. | ||
|
||
Kibana requests are also signed automatically. | ||
|
||
##Installation | ||
|
||
### Download binary executable: | ||
|
||
**aws-es-proxy** has single executable binaries for Linux, Mac and Windows. | ||
|
||
Download the latest [aws-es-proxy release](http://google.com). | ||
|
||
|
||
### Build from Source | ||
|
||
#### Dependencies: | ||
* go1.5 | ||
* [glide package manager](https://github.com/Masterminds/glide) | ||
|
||
|
||
```sh | ||
#requires go1.5 | ||
export GO15VENDOREXPERIMENT=1 | ||
mkdir -p $GOPATH/src/github.com/abutaha | ||
cd $GOPATH/src/github.com/abutaha | ||
git clone https://github.com/abutaha/aws-es-proxy | ||
cd aws-es-proxy | ||
glide install | ||
go build github.com/abutaha/aws-es-proxy | ||
``` | ||
|
||
##Configuring Credentials | ||
|
||
Before using **aws-es-proxy**, ensure that you've configured your AWS IAM user credentials. The best way to configure credentials on a development machine is to use the `~/.aws/credentials` file, which might look like: | ||
|
||
``` | ||
[default] | ||
aws_access_key_id = AKID1234567890 | ||
aws_secret_access_key = MY-SECRET-KEY | ||
``` | ||
|
||
Alternatively, you can set the following environment variables: | ||
|
||
``` | ||
export AWS_ACCESS_KEY_ID=AKID1234567890 | ||
export AWS_SECRET_ACCESS_KEY=MY-SECRET-KEY | ||
``` | ||
|
||
**aws-es-proxy** also supports `IAM roles`. To use IAM roles, you need to modify your Amazon Elasticsearch access policy to allow access from that role. Below is an Amazon Elasticsearch `access policy` example allowing access from any EC2 instance with an IAM role called `ec2-aws-elasticsearch`. | ||
|
||
``` | ||
{ | ||
"Version": "2012-10-17", | ||
"Statement": [ | ||
{ | ||
"Effect": "Allow", | ||
"Principal": { | ||
"AWS": "arn:aws:iam::012345678910:role/ec2-aws-elasticsearch" | ||
}, | ||
"Action": "es:*", | ||
"Resource": "arn:aws:es:eu-west-1:012345678910:domain/test-es-domain/*" | ||
} | ||
] | ||
} | ||
``` | ||
|
||
|
||
|
||
##Usage example: | ||
|
||
```sh | ||
./aws-es-proxy -endpoint https://test-es-somerandomvalue.eu-west-1.es.amazonaws.com | ||
Listening on 127.0.0.1:9200 | ||
``` | ||
|
||
*aws-es-proxy* listens on 127.0.0.1:9200 if no additional argument is provided. You can change the IP and Port passing the argument `-listen` | ||
|
||
``` | ||
./aws-es-proxy -listen :8080 -endpoint ... | ||
./aws-es-proxy -listen 10.0.0.1:9200 -endpoint ... | ||
``` | ||
|
||
By default, *aws-es-proxy* will not display any message in the console. However, it has the ability to print requests being sent to Amazon Elasticsearch, and the duration it takes to receive the request back. This can be enabled using the option `-verbose` | ||
|
||
``` | ||
./aws-es-proxy -verbose ... | ||
Listening on 127.0.0.1:9200 | ||
2016/10/31 19:48:23 -> GET / 200 1.054s | ||
2016/10/31 19:48:30 -> GET /_cat/indices?v 200 0.199s | ||
2016/10/31 19:48:37 -> GET /_cat/shards?v 200 0.196s | ||
2016/10/31 19:48:49 -> GET /_cat/allocation?v 200 0.179s | ||
2016/10/31 19:49:10 -> PUT /my-test-index 200 0.347s | ||
``` | ||
|
||
For a full list of available options, use `-h`: | ||
|
||
``` | ||
./aws-es-proxy -h | ||
Usage of ./aws-es-proxy: | ||
-endpoint string | ||
Amazon ElasticSearch Endpoint (e.g: https://dummy-host.eu-west-1.es.amazonaws.com) | ||
-listen string | ||
Local TCP port to listen on (default "127.0.0.1:9200") | ||
-verbose | ||
Print user requests | ||
``` | ||
|
||
##Using HTTP Clients | ||
|
||
After you run *aws-es-proxy*, you can now open your Web browser on [http://localhost:9200](http://localhost:9200). Everything should be working as you have your own instance of ElasticSearch running on port 9200. | ||
|
||
To access Kibana, use [http://localhost:9200/_plugin/kibana/](http://localhost:9200/_plugin/kibana/) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
package main | ||
|
||
import ( | ||
"bytes" | ||
"flag" | ||
"fmt" | ||
"io" | ||
"io/ioutil" | ||
"log" | ||
"net/http" | ||
"net/url" | ||
"os" | ||
"strings" | ||
"time" | ||
|
||
"github.com/aws/aws-sdk-go/aws/session" | ||
"github.com/aws/aws-sdk-go/aws/signer/v4" | ||
) | ||
|
||
type proxy struct { | ||
Scheme string | ||
Host string | ||
Region string | ||
Service string | ||
Verbose bool | ||
} | ||
|
||
func copyHeaders(dst, src http.Header) { | ||
for k, vals := range src { | ||
for _, v := range vals { | ||
dst.Add(k, v) | ||
} | ||
} | ||
} | ||
|
||
func replaceBody(req *http.Request) []byte { | ||
if req.Body == nil { | ||
return []byte{} | ||
} | ||
payload, _ := ioutil.ReadAll(req.Body) | ||
req.Body = ioutil.NopCloser(bytes.NewReader(payload)) | ||
return payload | ||
} | ||
|
||
func parseEndpoint(endpoint string, p *proxy) { | ||
link, err := url.Parse(endpoint) | ||
if err != nil { | ||
log.Fatalf("ERROR: Failed parsing endpoint: %s\n", endpoint) | ||
} | ||
|
||
// Only http/https are supported schemes | ||
scheme := func(x string) string { | ||
switch x { | ||
case "http", "https": | ||
return x | ||
} | ||
return "https" | ||
} | ||
link.Scheme = scheme(link.Scheme) | ||
|
||
// Unkown schemes sometimes result in empty host value | ||
if link.Host == "" { | ||
log.Fatalf("ERROR: Empty host information in submitted endpoint (%s)\n", endpoint) | ||
} | ||
|
||
// Extract region and service from link | ||
parts := strings.Split(link.Host, ".") | ||
var region, service string | ||
|
||
if len(parts) == 5 { | ||
region, service = parts[1], parts[2] | ||
} else { | ||
log.Fatalln("ERROR: Submitted endpoint is not a valid Amazon ElasticSearch Endpoint") | ||
} | ||
|
||
// Build proxy struct | ||
p.Scheme = link.Scheme | ||
p.Host = link.Host | ||
p.Region = region | ||
p.Service = service | ||
|
||
} | ||
|
||
func (p *proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { | ||
requestStarted := time.Now() | ||
defer r.Body.Close() | ||
|
||
respondError := func(err error) { | ||
w.WriteHeader(http.StatusBadRequest) | ||
w.Write([]byte(err.Error())) | ||
} | ||
|
||
endpoint := *r.URL | ||
endpoint.Host = p.Host | ||
endpoint.Scheme = p.Scheme | ||
|
||
req, err := http.NewRequest(r.Method, endpoint.String(), r.Body) | ||
if err != nil { | ||
respondError(err) | ||
return | ||
} | ||
|
||
// Start AWS session from ENV, Shared Creds or EC2Role | ||
sess, err := session.NewSession() | ||
if err != nil { | ||
log.Fatalln(err) | ||
} | ||
|
||
// Sign the request with AWSv4 | ||
signer := v4.NewSigner(sess.Config.Credentials) | ||
payload := bytes.NewReader(replaceBody(req)) | ||
signer.Sign(req, payload, p.Service, p.Region, time.Now()) | ||
|
||
resp, err := http.DefaultClient.Do(req) | ||
if err != nil { | ||
log.Println(err) | ||
respondError(err) | ||
return | ||
} | ||
|
||
defer resp.Body.Close() | ||
|
||
// Write back received headers | ||
copyHeaders(w.Header(), resp.Header) | ||
|
||
buf := bytes.Buffer{} | ||
if _, err := io.Copy(&buf, resp.Body); err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
// Send response back | ||
w.WriteHeader(resp.StatusCode) | ||
w.Write(buf.Bytes()) | ||
|
||
// Log everything | ||
if p.Verbose { | ||
requestEnded := time.Since(requestStarted) | ||
log.Printf(" -> %s %s %d %.3fs\n", | ||
r.Method, endpoint.RequestURI(), resp.StatusCode, requestEnded.Seconds()) | ||
} | ||
|
||
} | ||
|
||
func main() { | ||
var endpoint, listenAddress string | ||
var verbose bool | ||
|
||
// TODO: Use a more sophisticated args parser that can enforce arguments | ||
flag.StringVar(&endpoint, "endpoint", "", "Amazon ElasticSearch Endpoint (e.g: https://dummy-host.eu-west-1.es.amazonaws.com)") | ||
flag.StringVar(&listenAddress, "listen", "127.0.0.1:9200", "Local TCP port to listen on") | ||
flag.BoolVar(&verbose, "verbose", false, "Print user requests") | ||
flag.Parse() | ||
|
||
if len(os.Args) < 3 { | ||
fmt.Println("You need to specify Amazon ElasticSearch endpoint.") | ||
fmt.Println("Please run with '-h' for a list of available arguments.") | ||
os.Exit(1) | ||
} | ||
|
||
mux := &proxy{Verbose: verbose} | ||
parseEndpoint(endpoint, mux) | ||
|
||
fmt.Printf("Listening on %s\n", listenAddress) | ||
log.Fatal(http.ListenAndServe(listenAddress, mux)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
#!/bin/bash | ||
|
||
mkdir -p dist | ||
for GOOS in darwin linux windows; do | ||
for GOARCH in 386 amd64; do | ||
echo "Building $GOOS-$GOARCH" | ||
env GOOS=$GOOS GOARCH=$GOARCH go build -o dist/aws-es-proxy-$GOOS-$GOARCH | ||
done | ||
done |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package: github.com/abutaha/aws-es-proxy | ||
license: Apache | ||
owners: | ||
- name: Muslim AbuTaha | ||
email: muslim.adel@gmail.com | ||
import: | ||
- package: github.com/aws/aws-sdk-go | ||
version: ^1.4.22 | ||
subpackages: | ||
- aws/session | ||
- aws/signer/v4 |