services/proxy/proxyd: Implement access control at the proxy server.
This change makes proxyd support access control by allowing only servers
that present a blessing that matches AccessList to export themselves
through the proxy.
The change itself is backward compatible, if proxyd built with this
change is started with --access-list='{"In":["..."]}'. However,
my intention is to have this in before the release, so all servers
that use the proxy will already be using this new code.
I admit that I went for a bit of "speed of implementation" over elegance
in an attempt to get this in before we release publicly. I'm happy to do
cleanups after this is in.
MultiPart: 1/3
Closes vanadium/issues#33
Change-Id: Ic6413403f6bbca9c7b2bd330910a5c3e2adb0ef3
diff --git a/cmd/servicerunner/main.go b/cmd/servicerunner/main.go
index 397747d..8d243d8 100644
--- a/cmd/servicerunner/main.go
+++ b/cmd/servicerunner/main.go
@@ -22,6 +22,7 @@
"v.io/v23"
"v.io/v23/options"
"v.io/v23/rpc"
+ "v.io/v23/security"
"v.io/x/ref/envvar"
"v.io/x/ref/lib/signals"
@@ -127,7 +128,7 @@
lspec := v23.GetListenSpec(ctx)
lspec.Addrs = rpc.ListenAddrs{{"ws", "127.0.0.1:0"}}
- proxyShutdown, proxyEndpoint, err := profiles.NewProxy(ctx, lspec, "test/proxy")
+ proxyShutdown, proxyEndpoint, err := profiles.NewProxy(ctx, lspec, security.AllowEveryone(), "test/proxy")
defer proxyShutdown()
vars["PROXY_NAME"] = proxyEndpoint.Name()
diff --git a/profiles/internal/rpc/server.go b/profiles/internal/rpc/server.go
index 102f82a..770b113 100644
--- a/profiles/internal/rpc/server.go
+++ b/profiles/internal/rpc/server.go
@@ -34,6 +34,7 @@
"v.io/x/ref/profiles/internal/lib/publisher"
inaming "v.io/x/ref/profiles/internal/naming"
"v.io/x/ref/profiles/internal/rpc/stream"
+ "v.io/x/ref/profiles/internal/rpc/stream/manager"
"v.io/x/ref/profiles/internal/rpc/stream/vc"
)
@@ -88,6 +89,7 @@
state serverState // track state of the server.
streamMgr stream.Manager // stream manager to listen for new flows.
publisher publisher.Publisher // publisher to publish mounttable mounts.
+ dc vc.DischargeClient // fetches discharges of blessings
listenerOpts []stream.ListenerOpt // listener opts for Listen.
settingsPublisher *pubsub.Publisher // pubsub publisher for dhcp
settingsName string // pubwsub stream name for dhcp
@@ -234,8 +236,8 @@
}
// Make dischargeExpiryBuffer shorter than the VC discharge buffer to ensure we have fetched
// the discharges by the time the VC asks for them.`
- dc := InternalNewDischargeClient(ctx, client, dischargeExpiryBuffer-(5*time.Second))
- s.listenerOpts = append(s.listenerOpts, dc)
+ s.dc = InternalNewDischargeClient(ctx, client, dischargeExpiryBuffer-(5*time.Second))
+ s.listenerOpts = append(s.listenerOpts, s.dc)
s.listenerOpts = append(s.listenerOpts, vc.DialContext{ctx})
blessingsStatsName := naming.Join(statsPrefix, "security", "blessings")
// TODO(caprita): revist printing the blessings with %s, and
@@ -464,7 +466,8 @@
if err != nil {
return nil, nil, verror.New(errFailedToResolveProxy, s.ctx, proxy, err)
}
- ln, ep, err := s.streamMgr.Listen(inaming.Network, resolved, s.principal, s.blessings, s.listenerOpts...)
+ opts := append([]stream.ListenerOpt{proxyAuth{s}}, s.listenerOpts...)
+ ln, ep, err := s.streamMgr.Listen(inaming.Network, resolved, s.principal, s.blessings, opts...)
if err != nil {
return nil, nil, verror.New(errFailedToListenForProxy, s.ctx, resolved, err)
}
@@ -1334,3 +1337,41 @@
//nologcall
return fs.flow.RemoteEndpoint()
}
+
+type proxyAuth struct {
+ s *server
+}
+
+func (a proxyAuth) RPCStreamListenerOpt() {}
+
+func (a proxyAuth) Login(proxy stream.Flow) (security.Blessings, []security.Discharge, error) {
+ var (
+ principal = a.s.principal
+ dc = a.s.dc
+ ctx = a.s.ctx
+ )
+ if principal == nil {
+ return security.Blessings{}, nil, nil
+ }
+ proxyNames, _ := security.RemoteBlessingNames(ctx, security.NewCall(&security.CallParams{
+ LocalPrincipal: principal,
+ RemoteBlessings: proxy.RemoteBlessings(),
+ RemoteDischarges: proxy.RemoteDischarges(),
+ RemoteEndpoint: proxy.RemoteEndpoint(),
+ LocalEndpoint: proxy.LocalEndpoint(),
+ }))
+ blessings := principal.BlessingStore().ForPeer(proxyNames...)
+ tpc := blessings.ThirdPartyCaveats()
+ if len(tpc) == 0 {
+ return blessings, nil, nil
+ }
+ // Ugh! Have to convert from proxyNames to BlessingPatterns
+ proxyPats := make([]security.BlessingPattern, len(proxyNames))
+ for idx, n := range proxyNames {
+ proxyPats[idx] = security.BlessingPattern(n)
+ }
+ discharges := dc.PrepareDischarges(ctx, tpc, security.DischargeImpetus{Server: proxyPats})
+ return blessings, discharges, nil
+}
+
+var _ manager.ProxyAuthenticator = proxyAuth{}
diff --git a/profiles/internal/rpc/stream/manager/listener.go b/profiles/internal/rpc/stream/manager/listener.go
index 3824089..2af4149 100644
--- a/profiles/internal/rpc/stream/manager/listener.go
+++ b/profiles/internal/rpc/stream/manager/listener.go
@@ -27,6 +27,16 @@
"v.io/x/ref/profiles/internal/rpc/stream"
)
+// ProxyAuthenticator is a stream.ListenerOpt that is used when listening via a
+// proxy to authenticate with the proxy.
+type ProxyAuthenticator interface {
+ stream.ListenerOpt
+ // Login returns the Blessings (and the set of Discharges to make them
+ // valid) to send to the proxy. Typically, the proxy uses these to
+ // determine whether it wants to authorize use.
+ Login(proxy stream.Flow) (security.Blessings, []security.Discharge, error)
+}
+
func reg(id, msg string) verror.IDAction {
return verror.Register(verror.ID(pkgPath+id), verror.NoRetry, msg)
}
@@ -47,6 +57,7 @@
errAcceptFailed = reg(".errAcceptFailed", "accept failed{:3}")
errFailedToEstablishVC = reg(".errFailedToEstablishVC", "VC establishment with proxy failed{:_}")
errListenerAlreadyClosed = reg(".errListenerAlreadyClosed", "listener already closed")
+ errRefusedProxyLogin = reg(".errRefusedProxyLogin", "server did not want to listen via proxy{:_}")
)
// listener extends stream.Listener with a DebugString method.
@@ -288,12 +299,19 @@
vlog.VI(1).Infof("Connecting to proxy at %v", ln.proxyEP)
// Requires dialing a VC to the proxy, need to extract options from ln.opts to do so.
var dialOpts []stream.VCOpt
+ var auth ProxyAuthenticator
for _, opt := range opts {
- if dopt, ok := opt.(stream.VCOpt); ok {
- dialOpts = append(dialOpts, dopt)
+ switch v := opt.(type) {
+ case stream.VCOpt:
+ dialOpts = append(dialOpts, v)
+ case ProxyAuthenticator:
+ auth = v
}
}
- // TODO(cnicolaou, ashankar): probably want to set a timeout here. (is this covered by opts?)
+ // TODO(cnicolaou, ashankar): probably want to set a timeout here. (is
+ // this covered by opts?)
+ // TODO(ashankar): Authorize the proxy server as well (similar to how
+ // clients authorize servers in RPCs).
vf, err := ln.manager.FindOrDialVIF(ln.proxyEP, principal, dialOpts...)
if err != nil {
return nil, nil, err
@@ -322,6 +340,12 @@
}
var request proxy.Request
var response proxy.Response
+ if auth != nil {
+ if request.Blessings, request.Discharges, err = auth.Login(flow); err != nil {
+ vf.StopAccepting()
+ return nil, nil, verror.New(stream.ErrSecurity, nil, verror.New(errRefusedProxyLogin, nil, err))
+ }
+ }
enc, err := vom.NewEncoder(flow)
if err != nil {
flow.Close()
diff --git a/profiles/internal/rpc/stream/proxy/protocol.vdl b/profiles/internal/rpc/stream/proxy/protocol.vdl
index 087365f..da87aa0 100644
--- a/profiles/internal/rpc/stream/proxy/protocol.vdl
+++ b/profiles/internal/rpc/stream/proxy/protocol.vdl
@@ -4,6 +4,8 @@
package proxy
+import "v.io/v23/security"
+
// The proxy protocol is:
// (1) Server establishes a VC to the proxy to register its routing id and authenticate.
// (2) The server opens a flow and sends a "Request" message and waits for a "Response"
@@ -16,6 +18,11 @@
// traffic intended for the server's RoutingId to the network connection
// between the server and the proxy.
type Request struct {
+ // Blessings of the server that wishes to be proxied.
+ // Used to authorize the use of the proxy.
+ Blessings security.WireBlessings
+ // Discharges required to make Blessings valid.
+ Discharges []security.WireDischarge
}
// Response is sent by the proxy to the server after processing Request.
diff --git a/profiles/internal/rpc/stream/proxy/protocol.vdl.go b/profiles/internal/rpc/stream/proxy/protocol.vdl.go
index ff7c265..e02fe6b 100644
--- a/profiles/internal/rpc/stream/proxy/protocol.vdl.go
+++ b/profiles/internal/rpc/stream/proxy/protocol.vdl.go
@@ -10,12 +10,20 @@
import (
// VDL system imports
"v.io/v23/vdl"
+
+ // VDL user imports
+ "v.io/v23/security"
)
// Request is the message sent by a server to request that the proxy route
// traffic intended for the server's RoutingId to the network connection
// between the server and the proxy.
type Request struct {
+ // Blessings of the server that wishes to be proxied.
+ // Used to authorize the use of the proxy.
+ Blessings security.Blessings
+ // Discharges required to make Blessings valid.
+ Discharges []security.Discharge
}
func (Request) __VDLReflect(struct {
diff --git a/profiles/internal/rpc/stream/proxy/proxy.go b/profiles/internal/rpc/stream/proxy/proxy.go
index 3944e13..9cf4d92 100644
--- a/profiles/internal/rpc/stream/proxy/proxy.go
+++ b/profiles/internal/rpc/stream/proxy/proxy.go
@@ -7,6 +7,7 @@
import (
"fmt"
"net"
+ "reflect"
"sync"
"time"
@@ -77,10 +78,12 @@
// Proxy routes virtual circuit (VC) traffic between multiple underlying
// network connections.
type Proxy struct {
+ ctx *context.T
ln net.Listener
rid naming.RoutingID
principal security.Principal
blessings security.Blessings
+ authorizer security.Authorizer
mu sync.RWMutex
servers *servermap
processes map[*process]struct{}
@@ -182,17 +185,17 @@
}
// New creates a new Proxy that listens for network connections on the provided
-// (network, address) pair and routes VC traffic between accepted connections.
-// TODO(mattr): This should take a ListenSpec instead of network, address, and
-// pubAddress. However using a ListenSpec requires a great deal of supporting
-// code that should be refactored out of v.io/x/ref/profiles/internal/rpc/server.go.
-func New(ctx *context.T, spec rpc.ListenSpec, names ...string) (shutdown func(), endpoint naming.Endpoint, err error) {
+// ListenSpec and routes VC traffic between accepted connections.
+//
+// Servers wanting to "listen through the proxy" will only be allowed to do so
+// if the blessings they present are accepted to the provided authorization
+// policy (authorizer).
+func New(ctx *context.T, spec rpc.ListenSpec, authorizer security.Authorizer, names ...string) (shutdown func(), endpoint naming.Endpoint, err error) {
rid, err := naming.NewRoutingID()
if err != nil {
return nil, nil, err
}
-
- proxy, err := internalNew(rid, v23.GetPrincipal(ctx), spec)
+ proxy, err := internalNew(rid, ctx, spec, authorizer)
if err != nil {
return nil, nil, err
}
@@ -221,7 +224,7 @@
return shutdown, proxy.endpoint(), nil
}
-func internalNew(rid naming.RoutingID, principal security.Principal, spec rpc.ListenSpec) (*Proxy, error) {
+func internalNew(rid naming.RoutingID, ctx *context.T, spec rpc.ListenSpec, authorizer security.Authorizer) (*Proxy, error) {
if len(spec.Addrs) == 0 {
return nil, verror.New(stream.ErrProxy, nil, verror.New(errEmptyListenSpec, nil))
}
@@ -245,19 +248,23 @@
ln.Close()
return nil, verror.New(stream.ErrProxy, nil, verror.New(errNoAccessibleAddresses, nil, ln.Addr().String()))
}
-
+ if authorizer == nil {
+ authorizer = security.DefaultAuthorizer()
+ }
proxy := &Proxy{
- ln: ln,
- rid: rid,
- servers: &servermap{m: make(map[naming.RoutingID]*server)},
- processes: make(map[*process]struct{}),
+ ctx: ctx,
+ ln: ln,
+ rid: rid,
+ authorizer: authorizer,
+ servers: &servermap{m: make(map[naming.RoutingID]*server)},
+ processes: make(map[*process]struct{}),
// TODO(cnicolaou): should use all of the available addresses
pubAddress: pub[0].String(),
- principal: principal,
+ principal: v23.GetPrincipal(ctx),
statsName: naming.Join("rpc", "proxy", "routing-id", rid.String(), "debug"),
}
- if principal != nil {
- proxy.blessings = principal.BlessingStore().Default()
+ if proxy.principal != nil {
+ proxy.blessings = proxy.principal.BlessingStore().Default()
}
stats.NewStringFunc(proxy.statsName, proxy.debugString)
@@ -341,6 +348,8 @@
response.Error = verror.New(stream.ErrProxy, nil, verror.New(errVomDecoder, nil, err))
} else if err := dec.Decode(&request); err != nil {
response.Error = verror.New(stream.ErrProxy, nil, verror.New(errNoRequest, nil, err))
+ } else if err := p.authorize(server.VC, request); err != nil {
+ response.Error = err
} else if err := p.servers.Add(server); err != nil {
response.Error = verror.Convert(verror.ErrUnknown, nil, err)
} else {
@@ -379,6 +388,30 @@
server.Close(nil)
}
+func (p *Proxy) authorize(vc *vc.VC, request Request) error {
+ var dmap map[string]security.Discharge
+ if len(request.Discharges) > 0 {
+ dmap := make(map[string]security.Discharge)
+ for _, d := range request.Discharges {
+ dmap[d.ID()] = d
+ }
+ }
+ // Blessings must be bound to the same public key as the VC.
+ // (Repeating logic in the RPC server authorization code).
+ if got, want := request.Blessings.PublicKey(), vc.RemoteBlessings().PublicKey(); !request.Blessings.IsZero() && !reflect.DeepEqual(got, want) {
+ return verror.New(verror.ErrNoAccess, nil, fmt.Errorf("malformed request: Blessings sent in proxy.Request are bound to public key %v and not %v", got, want))
+ }
+ return p.authorizer.Authorize(p.ctx, security.NewCall(&security.CallParams{
+ LocalPrincipal: vc.LocalPrincipal(),
+ LocalBlessings: vc.LocalBlessings(),
+ RemoteBlessings: request.Blessings,
+ LocalEndpoint: vc.LocalEndpoint(),
+ RemoteEndpoint: vc.RemoteEndpoint(),
+ LocalDischarges: vc.LocalDischarges(),
+ RemoteDischarges: dmap,
+ }))
+}
+
func (p *Proxy) routeCounters(process *process, counters message.Counters) {
// Since each VC can be routed to a different process, split up the
// Counters into one message per VC.
diff --git a/profiles/internal/rpc/stream/proxy/proxy_test.go b/profiles/internal/rpc/stream/proxy/proxy_test.go
index c84e7cf..cffa224 100644
--- a/profiles/internal/rpc/stream/proxy/proxy_test.go
+++ b/profiles/internal/rpc/stream/proxy/proxy_test.go
@@ -13,10 +13,10 @@
"testing"
"time"
- "v.io/x/lib/vlog"
-
"v.io/v23"
+ "v.io/v23/context"
"v.io/v23/naming"
+ "v.io/v23/security"
"v.io/v23/verror"
_ "v.io/x/ref/profiles"
@@ -33,12 +33,10 @@
//go:generate v23 test generate
func TestProxy(t *testing.T) {
- ctx, shutdown := test.InitForTest()
+ ctx, shutdown := v23Init()
defer shutdown()
- pproxy := testutil.NewPrincipal("proxy")
-
- _, shutdown, proxyEp, err := proxy.InternalNew(naming.FixedRoutingID(0xbbbbbbbbbbbbbbbb), pproxy, v23.GetListenSpec(ctx))
+ _, shutdown, proxyEp, err := proxy.InternalNew(naming.FixedRoutingID(0xbbbbbbbbbbbbbbbb), ctx, security.AllowEveryone())
if err != nil {
t.Fatal(err)
}
@@ -46,8 +44,6 @@
principal := testutil.NewPrincipal("test")
blessings := principal.BlessingStore().Default()
- vlog.Infof("PROXYEP: %s", proxyEp)
-
// Create the stream.Manager for the server.
server1 := manager.InternalNew(naming.FixedRoutingID(0x1111111111111111))
defer server1.Shutdown()
@@ -102,12 +98,60 @@
}
}
-func TestDuplicateRoutingID(t *testing.T) {
- ctx, shutdown := test.InitForTest()
+func TestProxyAuthorization(t *testing.T) {
+ ctx, shutdown := v23Init()
defer shutdown()
- pproxy := testutil.NewPrincipal("proxy")
- _, shutdown, proxyEp, err := proxy.InternalNew(naming.FixedRoutingID(0xbbbbbbbbbbbbbbbb), pproxy, v23.GetListenSpec(ctx))
+ _, shutdown, proxyEp, err := proxy.InternalNew(naming.FixedRoutingID(0xbbbbbbbbbbbbbbbb), ctx, testAuth{"alice", "carol"})
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer shutdown()
+
+ var (
+ alice = testutil.NewPrincipal("alice")
+ bob = testutil.NewPrincipal("bob")
+ carol = testutil.NewPrincipal("carol")
+ dave = testutil.NewPrincipal("dave")
+ )
+ // Make the proxy recognize "alice", "bob" and "carol", but not "dave"
+ v23.GetPrincipal(ctx).AddToRoots(alice.BlessingStore().Default())
+ v23.GetPrincipal(ctx).AddToRoots(bob.BlessingStore().Default())
+ v23.GetPrincipal(ctx).AddToRoots(carol.BlessingStore().Default())
+
+ testcases := []struct {
+ p security.Principal
+ ok bool
+ }{
+ {alice, true}, // passes the auth policy
+ {bob, false}, // recognized, but not included in auth policy
+ {carol, true}, // passes the auth policy
+ {dave, false}, // not recognized, thus doesn't pass the auth policy
+ }
+ for idx, test := range testcases {
+ server := manager.InternalNew(naming.FixedRoutingID(uint64(idx)))
+ _, ep, err := server.Listen(proxyEp.Network(), proxyEp.String(), test.p, test.p.BlessingStore().Default(), proxyAuth{test.p})
+ if (err == nil) != test.ok {
+ t.Errorf("Got ep=%v, err=%v - wanted error:%v", ep, err, !test.ok)
+ }
+ server.Shutdown()
+ }
+}
+
+type proxyAuth struct {
+ p security.Principal
+}
+
+func (proxyAuth) RPCStreamListenerOpt() {}
+func (a proxyAuth) Login(stream.Flow) (security.Blessings, []security.Discharge, error) {
+ return a.p.BlessingStore().Default(), nil, nil
+}
+
+func TestDuplicateRoutingID(t *testing.T) {
+ ctx, shutdown := v23Init()
+ defer shutdown()
+
+ _, shutdown, proxyEp, err := proxy.InternalNew(naming.FixedRoutingID(0xbbbbbbbbbbbbbbbb), ctx, security.AllowEveryone())
if err != nil {
t.Fatal(err)
}
@@ -137,11 +181,11 @@
}
func TestProxyAuthentication(t *testing.T) {
- ctx, shutdown := test.InitForTest()
+ ctx, shutdown := v23Init()
defer shutdown()
- pproxy := testutil.NewPrincipal("proxy")
- _, shutdown, proxyEp, err := proxy.InternalNew(naming.FixedRoutingID(0xbbbbbbbbbbbbbbbb), pproxy, v23.GetListenSpec(ctx))
+ pproxy := v23.GetPrincipal(ctx)
+ _, shutdown, proxyEp, err := proxy.InternalNew(naming.FixedRoutingID(0xbbbbbbbbbbbbbbbb), ctx, security.AllowEveryone())
if err != nil {
t.Fatal(err)
}
@@ -168,16 +212,15 @@
}
func TestServerBlessings(t *testing.T) {
- ctx, shutdown := test.InitForTest()
+ ctx, shutdown := v23Init()
defer shutdown()
var (
- pproxy = testutil.NewPrincipal("proxy")
pserver = testutil.NewPrincipal("server")
pclient = testutil.NewPrincipal("client")
)
- _, shutdown, proxyEp, err := proxy.InternalNew(naming.FixedRoutingID(0xbbbbbbbbbbbbbbbb), pproxy, v23.GetListenSpec(ctx))
+ _, shutdown, proxyEp, err := proxy.InternalNew(naming.FixedRoutingID(0xbbbbbbbbbbbbbbbb), ctx, security.AllowEveryone())
if err != nil {
t.Fatal(err)
}
@@ -221,11 +264,10 @@
}
func TestHostPort(t *testing.T) {
- ctx, shutdown := test.InitForTest()
+ ctx, shutdown := v23Init()
defer shutdown()
- pproxy := testutil.NewPrincipal("proxy")
- _, shutdown, proxyEp, err := proxy.InternalNew(naming.FixedRoutingID(0xbbbbbbbbbbbbbbbb), pproxy, v23.GetListenSpec(ctx))
+ _, shutdown, proxyEp, err := proxy.InternalNew(naming.FixedRoutingID(0xbbbbbbbbbbbbbbbb), ctx, security.AllowEveryone())
if err != nil {
t.Fatal(err)
}
@@ -244,11 +286,10 @@
}
func TestClientBecomesServer(t *testing.T) {
- ctx, shutdown := test.InitForTest()
+ ctx, shutdown := v23Init()
defer shutdown()
- pproxy := testutil.NewPrincipal("proxy")
- _, shutdown, proxyEp, err := proxy.InternalNew(naming.FixedRoutingID(0xbbbbbbbbbbbbbbbb), pproxy, v23.GetListenSpec(ctx))
+ _, shutdown, proxyEp, err := proxy.InternalNew(naming.FixedRoutingID(0xbbbbbbbbbbbbbbbb), ctx, security.AllowEveryone())
if err != nil {
t.Fatal(err)
}
@@ -302,7 +343,7 @@
}
func testProxyIdleTimeout(t *testing.T, testServer bool) {
- ctx, shutdown := test.InitForTest()
+ ctx, shutdown := v23Init()
defer shutdown()
const (
@@ -313,7 +354,6 @@
)
var (
- pproxy = testutil.NewPrincipal("proxy")
pserver = testutil.NewPrincipal("server")
pclient = testutil.NewPrincipal("client")
@@ -329,7 +369,7 @@
// Pause the idle timers.
triggerTimers := vif.SetFakeTimers()
- Proxy, shutdown, proxyEp, err := proxy.InternalNew(naming.FixedRoutingID(0xbbbbbbbbbbbbbbbb), pproxy, v23.GetListenSpec(ctx))
+ Proxy, shutdown, proxyEp, err := proxy.InternalNew(naming.FixedRoutingID(0xbbbbbbbbbbbbbbbb), ctx, security.AllowEveryone())
if err != nil {
t.Fatal(err)
}
@@ -444,3 +484,26 @@
buf.Write(tmp[:n])
}
}
+
+func v23Init() (*context.T, func()) {
+ ctx, shutdown := test.InitForTest()
+ ctx, err := v23.WithPrincipal(ctx, testutil.NewPrincipal("proxy"))
+ if err != nil {
+ panic(err)
+ }
+ return ctx, shutdown
+}
+
+type testAuth []string
+
+func (l testAuth) Authorize(ctx *context.T, call security.Call) error {
+ remote, rejected := security.RemoteBlessingNames(ctx, call)
+ for _, n := range remote {
+ for _, a := range l {
+ if n == a {
+ return nil
+ }
+ }
+ }
+ return fmt.Errorf("%v not in authorized set of %v (rejected: %v)", remote, l, rejected)
+}
diff --git a/profiles/internal/rpc/stream/proxy/testutil_test.go b/profiles/internal/rpc/stream/proxy/testutil_test.go
index 8a1568c..727b8a5 100644
--- a/profiles/internal/rpc/stream/proxy/testutil_test.go
+++ b/profiles/internal/rpc/stream/proxy/testutil_test.go
@@ -5,15 +5,16 @@
package proxy
import (
+ "v.io/v23"
+ "v.io/v23/context"
"v.io/v23/naming"
- "v.io/v23/rpc"
"v.io/v23/security"
)
// These are the internal functions only for use in the proxy_test package.
-func InternalNew(rid naming.RoutingID, p security.Principal, spec rpc.ListenSpec) (*Proxy, func(), naming.Endpoint, error) {
- proxy, err := internalNew(rid, p, spec)
+func InternalNew(rid naming.RoutingID, ctx *context.T, auth security.Authorizer) (*Proxy, func(), naming.Endpoint, error) {
+ proxy, err := internalNew(rid, ctx, v23.GetListenSpec(ctx), auth)
if err != nil {
return nil, nil, nil, err
}
diff --git a/profiles/internal/rpc/test/proxy_test.go b/profiles/internal/rpc/test/proxy_test.go
index e6bd616..4316f55 100644
--- a/profiles/internal/rpc/test/proxy_test.go
+++ b/profiles/internal/rpc/test/proxy_test.go
@@ -56,7 +56,7 @@
expected := len(args)
listenSpec := rpc.ListenSpec{Addrs: rpc.ListenAddrs{{"tcp", "127.0.0.1:0"}}}
- proxyShutdown, proxyEp, err := proxy.New(ctx, listenSpec)
+ proxyShutdown, proxyEp, err := proxy.New(ctx, listenSpec, security.AllowEveryone())
if err != nil {
fmt.Fprintf(stderr, "%s\n", verror.DebugString(err))
return err
diff --git a/profiles/proxy.go b/profiles/proxy.go
index 0a12b4e..9b4a350 100644
--- a/profiles/proxy.go
+++ b/profiles/proxy.go
@@ -8,12 +8,16 @@
"v.io/v23/context"
"v.io/v23/naming"
"v.io/v23/rpc"
+ "v.io/v23/security"
"v.io/x/ref/profiles/internal/rpc/stream/proxy"
)
// NewProxy creates a new Proxy that listens for network connections on the provided
// (network, address) pair and routes VC traffic between accepted connections.
-func NewProxy(ctx *context.T, spec rpc.ListenSpec, names ...string) (shutdown func(), endpoint naming.Endpoint, err error) {
- return proxy.New(ctx, spec, names...)
+//
+// auth encapsulates the authorization policy of the proxy - which
+// servers it is willing to proxy for.
+func NewProxy(ctx *context.T, spec rpc.ListenSpec, auth security.Authorizer, names ...string) (shutdown func(), endpoint naming.Endpoint, err error) {
+ return proxy.New(ctx, spec, auth, names...)
}
diff --git a/profiles/roaming/proxy.go b/profiles/roaming/proxy.go
index 845ad11..5590a7c 100644
--- a/profiles/roaming/proxy.go
+++ b/profiles/roaming/proxy.go
@@ -8,12 +8,16 @@
"v.io/v23/context"
"v.io/v23/naming"
"v.io/v23/rpc"
+ "v.io/v23/security"
"v.io/x/ref/profiles/internal/rpc/stream/proxy"
)
// NewProxy creates a new Proxy that listens for network connections on the provided
// (network, address) pair and routes VC traffic between accepted connections.
-func NewProxy(ctx *context.T, spec rpc.ListenSpec, names ...string) (shutdown func(), endpoint naming.Endpoint, err error) {
- return proxy.New(ctx, spec, names...)
+//
+// auth encapsulates the authorization policy of the proxy - which
+// servers it is willing to proxy for.
+func NewProxy(ctx *context.T, spec rpc.ListenSpec, auth security.Authorizer, names ...string) (shutdown func(), endpoint naming.Endpoint, err error) {
+ return proxy.New(ctx, spec, auth, names...)
}
diff --git a/profiles/static/proxy.go b/profiles/static/proxy.go
index a398460..8693142 100644
--- a/profiles/static/proxy.go
+++ b/profiles/static/proxy.go
@@ -8,12 +8,16 @@
"v.io/v23/context"
"v.io/v23/naming"
"v.io/v23/rpc"
+ "v.io/v23/security"
"v.io/x/ref/profiles/internal/rpc/stream/proxy"
)
// NewProxy creates a new Proxy that listens for network connections on the provided
// (network, address) pair and routes VC traffic between accepted connections.
-func NewProxy(ctx *context.T, spec rpc.ListenSpec, names ...string) (shutdown func(), endpoint naming.Endpoint, err error) {
- return proxy.New(ctx, spec, names...)
+//
+// auth encapsulates the authorization policy of the proxy - which
+// servers it is willing to proxy for.
+func NewProxy(ctx *context.T, spec rpc.ListenSpec, auth security.Authorizer, names ...string) (shutdown func(), endpoint naming.Endpoint, err error) {
+ return proxy.New(ctx, spec, auth, names...)
}
diff --git a/services/device/internal/starter/starter.go b/services/device/internal/starter/starter.go
index 4ad1dac..50a2e9b 100644
--- a/services/device/internal/starter/starter.go
+++ b/services/device/internal/starter/starter.go
@@ -25,6 +25,7 @@
"v.io/v23/context"
"v.io/v23/naming"
"v.io/v23/rpc"
+ "v.io/v23/security"
"v.io/v23/verror"
"v.io/x/lib/vlog"
)
@@ -282,7 +283,9 @@
// under.
ls := v23.GetListenSpec(ctx)
ls.Addrs = rpc.ListenAddrs{{protocol, addr}}
- shutdown, ep, err := roaming.NewProxy(ctx, ls)
+ // TODO(ashankar): Revisit this choice of security.AllowEveryone
+ // See: https://v.io/i/387
+ shutdown, ep, err := roaming.NewProxy(ctx, ls, security.AllowEveryone())
if err != nil {
return nil, verror.New(errCantCreateProxy, ctx, err)
}
diff --git a/services/proxy/proxyd/main.go b/services/proxy/proxyd/main.go
index d9114eb..a617145 100644
--- a/services/proxy/proxyd/main.go
+++ b/services/proxy/proxyd/main.go
@@ -7,15 +7,17 @@
package main
import (
+ "bytes"
+ "encoding/json"
"flag"
"fmt"
"net/http"
- _ "net/http/pprof"
"time"
"v.io/v23"
"v.io/v23/rpc"
"v.io/v23/security"
+ "v.io/v23/security/access"
"v.io/x/lib/vlog"
"v.io/x/ref/lib/signals"
@@ -26,6 +28,7 @@
pubAddress = flag.String("published-address", "", "deprecated - the proxy now uses listenspecs and the address chooser mechanism")
healthzAddr = flag.String("healthz-address", "", "Network address on which the HTTP healthz server runs. It is intended to be used with a load balancer. The load balancer must be able to reach this address in order to verify that the proxy server is running")
name = flag.String("name", "", "Name to mount the proxy as")
+ acl = flag.String("access-list", "", "Blessings that are authorized to listen via the proxy. JSON-encoded representation of access.AccessList. An empty string implies the default authorization policy.")
)
func main() {
@@ -39,7 +42,20 @@
if listenSpec.Proxy != "" {
vlog.Fatalf("proxyd cannot listen through another proxy")
}
- proxyShutdown, proxyEndpoint, err := static.NewProxy(ctx, listenSpec, *name)
+ var authorizer security.Authorizer
+ if len(*acl) > 0 {
+ var list access.AccessList
+ if err := json.NewDecoder(bytes.NewBufferString(*acl)).Decode(&list); err != nil {
+ vlog.Fatalf("invalid --access-list: %v", err)
+ }
+ // Always add ourselves, for the the reserved methods server
+ // started below.
+ list.In = append(list.In, security.DefaultBlessingPatterns(v23.GetPrincipal(ctx))...)
+ vlog.Infof("Using access list to control proxy use: %v", list)
+ authorizer = list
+ }
+
+ proxyShutdown, proxyEndpoint, err := static.NewProxy(ctx, listenSpec, authorizer, *name)
if err != nil {
vlog.Fatal(err)
}
diff --git a/services/proxy/proxyd/proxyd_v23_test.go b/services/proxy/proxyd/proxyd_v23_test.go
index 2e4d4e0..e58c90d 100644
--- a/services/proxy/proxyd/proxyd_v23_test.go
+++ b/services/proxy/proxyd/proxyd_v23_test.go
@@ -42,7 +42,7 @@
)
// Start proxyd
proxyd.WithStartOpts(proxyd.StartOpts().WithCustomCredentials(proxydCreds)).
- Start("--v23.tcp.address=127.0.0.1:0", "--name="+proxyName)
+ Start("--v23.tcp.address=127.0.0.1:0", "--name="+proxyName, "--access-list", "{\"In\":[\"root/server\"]}")
// Start the server that only listens via the proxy
if _, err := t.Shell().StartWithOpts(
t.Shell().DefaultStartOpts().WithCustomCredentials(serverCreds),
diff --git a/services/wspr/internal/app/app_test.go b/services/wspr/internal/app/app_test.go
index 6e14735..e259890 100644
--- a/services/wspr/internal/app/app_test.go
+++ b/services/wspr/internal/app/app_test.go
@@ -316,7 +316,7 @@
return nil, fmt.Errorf("unable to start mounttable: %v", err)
}
proxySpec := rpc.ListenSpec{Addrs: rpc.ListenAddrs{{"tcp", "127.0.0.1:0"}}}
- proxyShutdown, proxyEndpoint, err := profiles.NewProxy(ctx, proxySpec)
+ proxyShutdown, proxyEndpoint, err := profiles.NewProxy(ctx, proxySpec, security.AllowEveryone())
if err != nil {
return nil, fmt.Errorf("unable to start proxy: %v", err)
}
diff --git a/services/wspr/internal/browspr/browspr_test.go b/services/wspr/internal/browspr/browspr_test.go
index bbb0e0a..effb936 100644
--- a/services/wspr/internal/browspr/browspr_test.go
+++ b/services/wspr/internal/browspr/browspr_test.go
@@ -17,6 +17,7 @@
"v.io/v23/naming"
"v.io/v23/options"
"v.io/v23/rpc"
+ "v.io/v23/security"
"v.io/v23/vdl"
vdltime "v.io/v23/vdlroot/time"
"v.io/v23/vom"
@@ -83,7 +84,7 @@
defer shutdown()
proxySpec := rpc.ListenSpec{Addrs: rpc.ListenAddrs{{"tcp", "127.0.0.1:0"}}}
- proxyShutdown, proxyEndpoint, err := profiles.NewProxy(ctx, proxySpec)
+ proxyShutdown, proxyEndpoint, err := profiles.NewProxy(ctx, proxySpec, security.AllowEveryone())
if err != nil {
t.Fatalf("Failed to start proxy: %v", err)
}