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/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{}