Skip to content

Commit

Permalink
address review comments
Browse files Browse the repository at this point in the history
  • Loading branch information
aaronhurt authored and magiconair committed Feb 18, 2018
1 parent 57dd6e5 commit 58c2d82
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 31 deletions.
4 changes: 2 additions & 2 deletions docs/content/cfg/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ Add a route for a service `svc` for the `src` (e.g. `/path` or `:port`) to a `ds

Option | Description
------------------------------------------ | -----------
`allow=ip:10.0.0.0/8,ip:192.168.0.0/24` | Restrict access to source addresses within the `10.0.0.0/8` or `192.168.0.0/24` CIDR mask. All other requests will be denied.
`deny=ip:10.0.0.0/8,ip:1.2.3.4/32` | Deny requests that source from the `10.0.0.0/8` CIDR mask or `1.2.3.4`. All other requests will be allowed.
`allow=ip:10.0.0.0/8,ip:fe80::/10` | Restrict access to source addresses within the `10.0.0.0/8` or `fe80::/10` CIDR mask. All other requests will be denied.
`deny=ip:10.0.0.0/8,ip:fe80::1234` | Deny requests that source from the `10.0.0.0/8` CIDR mask or `fe80::1234`. All other requests will be allowed.
`strip=/path` | Forward `/path/to/file` as `/to/file`
`proto=tcp` | Upstream service is TCP, `dst` must be `:port`
`proto=https` | Upstream service is HTTPS
Expand Down
16 changes: 11 additions & 5 deletions docs/content/feature/access-control.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,28 @@ Currently only source ip control is available.
<!--more-->

To allow access to a route from clients within the `192.168.1.0/24`
and `10.0.0.0/8` subnet you would add the following option:
and `fe80::/10` subnet you would add the following option:

```
allow=ip:192.168.1.0/24,ip:10.0.0.0/8
allow=ip:192.168.1.0/24,ip:fe80::/10
```

With this specified only clients sourced from those two subnets will
be allowed. All other requests to that route will be denied.


Inversely, to only deny a specific set of clients you can use the
Inversely, to deny a specific set of clients you can use the
following option syntax:

```
deny=ip:1.2.3.4/32,100.123.0.0/16
deny=ip:fe80::1234,100.123.0.0/16
```

With this configuration access will be denied to any clients with
the `1.2.3.4` address or coming from the `100.123.0.0/16` network.
the `fe80::1234` address or coming from the `100.123.0.0/16` network.

Single host addresses (addresses without a prefix) will have a
`/32` prefix, for IPv4, or a `/128` prefix, for IPv6, added automatically.
That means `1.2.3.4` is equivalent to `1.2.3.4/32` and `fe80::1234`
is equivalent to `fe80::1234/128` when specifying
address blocks for `allow` or `deny` rules.
30 changes: 30 additions & 0 deletions proxy/http_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,36 @@ func TestProxySTSHeader(t *testing.T) {
}
}

func TestProxyChecksHeaderForAccessRules(t *testing.T) {
var hdr http.Header = make(http.Header)
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
hdr = r.Header
}))
defer server.Close()

proxy := httptest.NewServer(&HTTPProxy{
Config: config.Proxy{},
Transport: http.DefaultTransport,
Lookup: func(r *http.Request) *route.Target {
tgt := &route.Target{
URL: mustParse(server.URL),
Opts: map[string]string{"allow": "ip:127.0.0.0/8,ip:fe80::/10,ip:::1"},
}
tgt.ProcessAccessRules()
return tgt
},
})
defer proxy.Close()

req, _ := http.NewRequest("GET", proxy.URL, nil)
req.Header.Set("X-Forwarded-For", "1.2.3.4")
resp, _ := mustDo(req)

if got, want := resp.StatusCode, http.StatusForbidden; got != want {
t.Errorf("got %v want %v", got, want)
}
}

func TestProxyNoRouteHTML(t *testing.T) {
want := "<html>503</html>"
noroute.SetHTML(want)
Expand Down
1 change: 0 additions & 1 deletion proxy/tcp/sni_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ func (p *SNIProxy) ServeTCP(in net.Conn) error {
addr := t.URL.Host

if t.AccessDeniedTCP(in) {
log.Print("[INFO] route rules denied access to ", t.URL.String())
return nil
}

Expand Down
1 change: 0 additions & 1 deletion proxy/tcp/tcp_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ func (p *Proxy) ServeTCP(in net.Conn) error {
addr := t.URL.Host

if t.AccessDeniedTCP(in) {
log.Print("[INFO] route rules denied access to ", t.URL.String())
return nil
}

Expand Down
48 changes: 29 additions & 19 deletions route/access_rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func (t *Target) AccessDeniedHTTP(r *http.Request) bool {
}

// check remote source and return if denied
if ip != nil && t.denyByIP(ip) {
if t.denyByIP(ip) {
return true
}

Expand All @@ -43,7 +43,8 @@ func (t *Target) AccessDeniedHTTP(r *http.Request) bool {
if ip = net.ParseIP(xff); ip == nil {
log.Printf("[WARN] failed to parse xff address %s", xff)
}
if ip != nil && t.denyByIP(ip) {
// check xff source and return if denied
if t.denyByIP(ip) {
return true
}
}
Expand All @@ -55,8 +56,12 @@ func (t *Target) AccessDeniedHTTP(r *http.Request) bool {

// AccessDeniedTCP checks rules on the target for TCP proxy routes.
func (t *Target) AccessDeniedTCP(c net.Conn) bool {
// currently only one function - more may be added in the future
return t.denyByIP(net.ParseIP(c.RemoteAddr().String()))
ip := net.ParseIP(c.RemoteAddr().String())
if t.denyByIP(ip) {
return true
}
// default allow
return false
}

func (t *Target) denyByIP(ip net.IP) bool {
Expand All @@ -78,6 +83,8 @@ func (t *Target) denyByIP(ip net.IP) bool {
}
}
// we checked all the blocks - deny this request
log.Printf("[INFO] route rules denied access from %s to %s",
ip.String(), t.URL.String())
return true
}

Expand All @@ -91,6 +98,8 @@ func (t *Target) denyByIP(ip net.IP) bool {
}
if block.Contains(ip) {
// specific deny matched - deny this request
log.Printf("[INFO] route rules denied access from %s to %s",
ip.String(), t.URL.String())
return true
}
}
Expand All @@ -100,6 +109,22 @@ func (t *Target) denyByIP(ip net.IP) bool {
return false
}

// ProcessAccessRules processes access rules from options specified on the target route
func (t *Target) ProcessAccessRules() error {
if t.Opts["allow"] != "" && t.Opts["deny"] != "" {
return errors.New("specifying allow and deny on the same route is not supported")
}

for _, allowDeny := range []string{"allow", "deny"} {
if t.Opts[allowDeny] != "" {
if err := t.parseAccessRule(allowDeny); err != nil {
return err
}
}
}
return nil
}

func (t *Target) parseAccessRule(allowDeny string) error {
var accessTag string
var temps []string
Expand Down Expand Up @@ -147,18 +172,3 @@ func (t *Target) parseAccessRule(allowDeny string) error {

return nil
}

func (t *Target) processAccessRules() error {
if t.Opts["allow"] != "" && t.Opts["deny"] != "" {
return errors.New("specifying allow and deny on the same route is not supported")
}

for _, allowDeny := range []string{"allow", "deny"} {
if t.Opts[allowDeny] != "" {
if err := t.parseAccessRule(allowDeny); err != nil {
return err
}
}
}
return nil
}
7 changes: 5 additions & 2 deletions route/access_rules_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package route
import (
"net"
"net/http"
"net/url"
"testing"
)

Expand Down Expand Up @@ -140,10 +141,11 @@ func TestAccessRules_denyByIP(t *testing.T) {
tt := tt // capture loop var

t.Run(tt.desc, func(t *testing.T) {
if err := tt.target.processAccessRules(); err != nil {
if err := tt.target.ProcessAccessRules(); err != nil {
t.Errorf("%d: %s - failed to process access rules: %s",
i, tt.desc, err.Error())
}
tt.target.URL, _ = url.Parse("http://testing.test/")
if deny := tt.target.denyByIP(tt.remote); deny != tt.denied {
t.Errorf("%d: %s\ngot denied: %t\nwant denied: %t\n",
i, tt.desc, deny, tt.denied)
Expand Down Expand Up @@ -207,10 +209,11 @@ func TestAccessRules_AccessDeniedHTTP(t *testing.T) {
req.RemoteAddr = tt.remote

t.Run(tt.desc, func(t *testing.T) {
if err := tt.target.processAccessRules(); err != nil {
if err := tt.target.ProcessAccessRules(); err != nil {
t.Errorf("%d: %s - failed to process access rules: %s",
i, tt.desc, err.Error())
}
tt.target.URL, _ = url.Parse("http://testing.test/")
if deny := tt.target.AccessDeniedHTTP(req); deny != tt.denied {
t.Errorf("%d: %s\ngot denied: %t\nwant denied: %t\n",
i, tt.desc, deny, tt.denied)
Expand Down
2 changes: 1 addition & 1 deletion route/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func (r *Route) addTarget(service string, targetURL *url.URL, fixedWeight float6
}
}

if err = t.processAccessRules(); err != nil {
if err = t.ProcessAccessRules(); err != nil {
log.Printf("[ERROR] failed to process access rules: %s",
err.Error())
}
Expand Down

0 comments on commit 58c2d82

Please sign in to comment.