Skip to content

Commit ac063b6

Browse files
committed
Merge pull request #25 from google/proxy-binary
examples/main: move to cmd/proxy and support as de facto proxy binary.
2 parents 076b719 + b5c3637 commit ac063b6

File tree

3 files changed

+322
-319
lines changed

3 files changed

+322
-319
lines changed

README.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,26 +16,31 @@ anywhere that Go can target.
1616

1717
## Requirements
1818

19-
Go 1.5 (for changes in crypto/tls)
19+
Go 1.5
2020

2121
## Getting Started
2222

23+
### Installation
24+
Martian Proxy can be installed using `go install`
25+
26+
go install github.com/google/martian/cmd/proxy
27+
2328
### Start the Proxy
24-
Running an instance of Martian is as simple as
29+
Assuming you've installed Martian, running the proxy is as simple as
2530

26-
go run examples/main.go
31+
$GOPATH/bin/proxy
2732

2833
If you want to see system logs as Martian is running, pass in the verbosity
2934
flag:
3035

31-
go run examples/main.go -- -v
36+
$GOPATH/bin/proxy -v
3237

3338
For logging of requests and responses a [logging modifier](https://github.com/google/martian/wiki/Modifier-Reference#logging) is available
3439

3540
By default, Martian will be running on port 8080. The port can be specified via
3641
a flag:
3742

38-
go run examples/main.go -- --port=9999
43+
$GOPATH/bin/proxy -port=9999
3944

4045
### Configure
4146
Once Martian is running, you need to configure its behavior. Without

cmd/proxy/main.go

Lines changed: 312 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,312 @@
1+
// Copyright 2015 Google Inc. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// proxy is an HTTP/S proxy configurable via an HTTP API.
16+
//
17+
// It can be dynamically configured/queried at runtime by issuing requests to
18+
// proxy specific paths using JSON.
19+
//
20+
// Supported configuration endpoints:
21+
//
22+
// POST http://martian.proxy/configure
23+
//
24+
// sets the request and response modifier of the proxy; modifiers adhere to the
25+
// following top-level JSON structure:
26+
//
27+
// {
28+
// "package.Modifier": {
29+
// "scope": ["request", "response"],
30+
// "attribute 1": "value",
31+
// "attribute 2": "value"
32+
// }
33+
// }
34+
//
35+
// modifiers may be "stacked" to provide support for additional behaviors; for
36+
// example, to add a "Martian-Test" header with the value "true" for requests
37+
// with the domain "www.example.com" the JSON message would be:
38+
//
39+
// {
40+
// "url.Filter": {
41+
// "scope": ["request"],
42+
// "host": "www.example.com",
43+
// "modifier": {
44+
// "header.Modifier": {
45+
// "name": "Martian-Test",
46+
// "value": "true"
47+
// }
48+
// }
49+
// }
50+
// }
51+
//
52+
// url.Filter parses the JSON object in the value of the "url.Filter" attribute;
53+
// the "host" key tells the url.Filter to filter requests if the host explicitly
54+
// matches "www.example.com"
55+
//
56+
// the "modifier" key within the "url.Filter" JSON object contains another
57+
// modifier message of the type header.Modifier to run iff the filter passes
58+
//
59+
// groups may also be used to run multiple modifiers sequentially; for example to
60+
// log requests and responses after adding the "Martian-Test" header to the
61+
// request, but only when the host matches www.example.com:
62+
//
63+
// {
64+
// "url.Filter": {
65+
// "host": "www.example.com",
66+
// "modifier": {
67+
// "fifo.Group": {
68+
// "modifiers": [
69+
// {
70+
// "header.Modifier": {
71+
// "scope": ["request"],
72+
// "name": "Martian-Test",
73+
// "value": "true"
74+
// }
75+
// },
76+
// {
77+
// "log.Logger": { }
78+
// }
79+
// ]
80+
// }
81+
// }
82+
// }
83+
// }
84+
//
85+
// modifiers are designed to be composed together in ways that allow the user to
86+
// write a single JSON structure to accomplish a variety of functionality
87+
//
88+
// GET http://martian.proxy/verify
89+
//
90+
// retrieves the verifications errors as JSON with the following structure:
91+
//
92+
// {
93+
// "errors": [
94+
// {
95+
// "message": "request(url) verification failure"
96+
// },
97+
// {
98+
// "message": "response(url) verification failure"
99+
// }
100+
// ]
101+
// }
102+
//
103+
// verifiers also adhere to the modifier interface and thus can be included in the
104+
// modifier configuration request; for example, to verify that all requests to
105+
// "www.example.com" are sent over HTTPS send the following JSON to the
106+
// configuration endpoint:
107+
//
108+
// {
109+
// "url.Filter": {
110+
// "scope": ["request"],
111+
// "host": "www.example.com",
112+
// "modifier": {
113+
// "url.Verifier": {
114+
// "scope": ["request"],
115+
// "scheme": "https"
116+
// }
117+
// }
118+
// }
119+
// }
120+
//
121+
// sending a request to "http://martian.proxy/verify" will then return errors from the url.Verifier
122+
//
123+
// POST http://martian.proxy/verify/reset
124+
//
125+
// resets the verifiers to their initial state; note some verifiers may start in
126+
// a failure state (e.g., pingback.Verifier is failed if no requests have been
127+
// seen by the proxy)
128+
//
129+
// GET http://martian.proxy/authority.cer
130+
//
131+
// prompts the user to install the CA certificate used by the proxy if MITM is enabled
132+
//
133+
// passing the -cors flag will enable CORS support for the endpoints so that they
134+
// may be called via AJAX
135+
//
136+
// The flags are:
137+
// -addr=":8080"
138+
// host:port of the proxy
139+
// -api="martian.proxy"
140+
// hostname that can be used to reference the configuration API when
141+
// configuring through the proxy
142+
// -cert=""
143+
// PEM encoded X.509 CA certificate; if set, it will be set as the
144+
// issuer for dynamically-generated certificates during man-in-the-middle
145+
// -key=""
146+
// PEM encoded private key of cert (RSA or ECDSA); if set, the key will be used
147+
// to sign dynamically-generated certificates during man-in-the-middle
148+
// -generate-ca-cert=false
149+
// generates a CA certificate and private key to use for man-in-the-middle;
150+
// the certificate is only valid while the proxy is running and will be
151+
// discarded on shutdown
152+
// -organization="Martian Proxy"
153+
// organization name set on the dynamically-generated certificates during
154+
// man-in-the-middle
155+
// -validity="1h"
156+
// window of time around the time of request that the dynamically-generated
157+
// certificate is valid for; the duration is set such that the total valid
158+
// timeframe is double the value of validity (1h before & 1h after)
159+
// -cors=false
160+
// allow the proxy to be configured via CORS requests; such as when
161+
// configuring the proxy via AJAX
162+
package main
163+
164+
import (
165+
"crypto/tls"
166+
"crypto/x509"
167+
"flag"
168+
"log"
169+
"net"
170+
"net/http"
171+
"os"
172+
"os/signal"
173+
"path/filepath"
174+
"time"
175+
176+
"github.com/google/martian"
177+
"github.com/google/martian/cors"
178+
"github.com/google/martian/fifo"
179+
"github.com/google/martian/header"
180+
"github.com/google/martian/martianhttp"
181+
"github.com/google/martian/mitm"
182+
"github.com/google/martian/verify"
183+
184+
_ "github.com/google/martian/body"
185+
_ "github.com/google/martian/cookie"
186+
_ "github.com/google/martian/martianlog"
187+
_ "github.com/google/martian/martianurl"
188+
_ "github.com/google/martian/method"
189+
_ "github.com/google/martian/pingback"
190+
_ "github.com/google/martian/priority"
191+
_ "github.com/google/martian/querystring"
192+
_ "github.com/google/martian/status"
193+
)
194+
195+
var (
196+
addr = flag.String("addr", ":8080", "host:port of the proxy")
197+
api = flag.String("api", "martian.proxy", "hostname for the API")
198+
generateCA = flag.Bool("generate-ca-cert", false, "generate CA certificate and private key for MITM")
199+
cert = flag.String("cert", "", "CA certificate used to sign MITM certificates")
200+
key = flag.String("key", "", "private key of the CA used to sign MITM certificates")
201+
organization = flag.String("organization", "Martian Proxy", "organization name for MITM certificates")
202+
validity = flag.Duration("validity", time.Hour, "window of time that MITM certificates are valid")
203+
allowCORS = flag.Bool("cors", false, "allow CORS requests to configure the proxy")
204+
)
205+
206+
func main() {
207+
flag.Parse()
208+
209+
p := martian.NewProxy()
210+
211+
var x509c *x509.Certificate
212+
var priv interface{}
213+
214+
if *generateCA {
215+
var err error
216+
x509c, priv, err = mitm.NewAuthority("martian.proxy", "Martian Authority", 30*24*time.Hour)
217+
if err != nil {
218+
log.Fatal(err)
219+
}
220+
} else if *cert != "" && *key != "" {
221+
tlsc, err := tls.LoadX509KeyPair(*cert, *key)
222+
if err != nil {
223+
log.Fatal(err)
224+
}
225+
priv = tlsc.PrivateKey
226+
227+
x509c, err = x509.ParseCertificate(tlsc.Certificate[0])
228+
if err != nil {
229+
log.Fatal(err)
230+
}
231+
}
232+
233+
if x509c != nil && priv != nil {
234+
mc, err := mitm.NewConfig(x509c, priv)
235+
if err != nil {
236+
log.Fatal(err)
237+
}
238+
239+
mc.SetValidity(*validity)
240+
mc.SetOrganization(*organization)
241+
242+
p.SetMITM(mc)
243+
244+
// Expose certificate authority.
245+
ah := martianhttp.NewAuthorityHandler(x509c)
246+
configure("/authority.cer", ah)
247+
}
248+
249+
fg := fifo.NewGroup()
250+
251+
hbhm := header.NewHopByHopModifier()
252+
fg.AddRequestModifier(hbhm)
253+
fg.AddRequestModifier(header.NewForwardedModifier())
254+
fg.AddRequestModifier(header.NewBadFramingModifier())
255+
256+
vm := header.NewViaModifier("martian")
257+
fg.AddRequestModifier(vm)
258+
259+
m := martianhttp.NewModifier()
260+
fg.AddRequestModifier(m)
261+
fg.AddResponseModifier(m)
262+
263+
fg.AddResponseModifier(hbhm)
264+
fg.AddResponseModifier(vm)
265+
266+
p.SetRequestModifier(fg)
267+
p.SetResponseModifier(fg)
268+
269+
// Proxy specific handlers.
270+
// These handlers take precendence over proxy traffic and will not be
271+
// intercepted.
272+
273+
// Configure modifiers.
274+
configure("/configure", m)
275+
276+
// Verify assertions.
277+
vh := verify.NewHandler()
278+
vh.SetRequestVerifier(m)
279+
vh.SetResponseVerifier(m)
280+
configure("/verify", vh)
281+
282+
// Reset verifications.
283+
rh := verify.NewResetHandler()
284+
rh.SetRequestVerifier(m)
285+
rh.SetResponseVerifier(m)
286+
configure("/verify/reset", rh)
287+
288+
l, err := net.Listen("tcp", *addr)
289+
if err != nil {
290+
log.Fatal(err)
291+
}
292+
293+
log.Println("martian: proxy started on:", l.Addr())
294+
295+
go p.Serve(l)
296+
297+
sigc := make(chan os.Signal, 1)
298+
signal.Notify(sigc, os.Interrupt, os.Kill)
299+
300+
<-sigc
301+
302+
log.Println("martian: shutting down")
303+
}
304+
305+
// configure installs a configuration handler at path.
306+
func configure(path string, handler http.Handler) {
307+
if *allowCORS {
308+
handler = cors.NewHandler(handler)
309+
}
310+
311+
http.Handle(filepath.Join(*api, path), handler)
312+
}

0 commit comments

Comments
 (0)