Skip to content
File

Blob: tun/server/session_rpc.go

go163 lines
1package server
2 
3import (
4 "context"
5 "crypto/rand"
6 "crypto/x509"
7 "errors"
8 "time"
9 
10 "go.miragespace.co/specter/spec/pki"
11 "go.miragespace.co/specter/spec/protocol"
12 "go.miragespace.co/specter/spec/rpc"
13 "go.miragespace.co/specter/spec/transport"
14 "go.miragespace.co/specter/spec/tun"
15 
16 "github.com/twitchtv/twirp"
17)
18 
19func sessionAttachment(ctx context.Context) (transport.PhysicalConn, *x509.Certificate, error) {
20 d := rpc.GetDelegation(ctx)
21 if d == nil || d.Certificate == nil {
22 return nil, nil, twirp.Unauthenticated.Error("missing client certificate")
23 }
24 if _, err := pki.ExtractCertificateIdentity(d.Certificate); err != nil {
25 return nil, nil, twirp.Unauthenticated.Error(err.Error())
26 }
27 provider, ok := d.Conn.(transport.PhysicalConnProvider)
28 if !ok || provider.PhysicalConn() == nil {
29 return nil, nil, twirp.Unimplemented.Error("transport does not expose a physical connection")
30 }
31 return provider.PhysicalConn(), d.Certificate, nil
32}
33 
34func (s *Server) reserveSession(sess *session) error {
35 if err := s.sessions.reserve(sess); err != nil {
36 var rpcError twirp.Error
37 if !errors.As(err, &rpcError) || rpcError.Code() != twirp.FailedPrecondition {
38 sess.fail(err.Error())
39 }
40 return err
41 }
42 return nil
43}
44 
45func (s *Server) OpenEphemeralSession(ctx context.Context, _ *protocol.OpenEphemeralSessionRequest) (*protocol.OpenSessionResponse, error) {
46 conn, cert, err := sessionAttachment(ctx)
47 if err != nil {
48 return nil, err
49 }
50 label, inv, err := tun.EphemeralLabel(s.Chord.ID(), cert.RawSubjectPublicKeyInfo)
51 if err != nil {
52 return nil, twirp.InternalErrorWith(err)
53 }
54 sess := &session{
55 alias: tun.SessionAlias(inv),
56 hostname: label,
57 conn: conn,
58 mode: ephemeral,
59 spki: cert.RawSubjectPublicKeyInfo,
60 certNotAfter: cert.NotAfter,
61 }
62 if err := s.reserveSession(sess); err != nil {
63 return nil, err
64 }
65 if !sess.activate(ctx, time.Time{}) {
66 sess.fail("ephemeral activation canceled")
67 return nil, twirp.Aborted.Error("ephemeral activation canceled")
68 }
69 return &protocol.OpenSessionResponse{
70 Hostname: label,
71 Apex: s.Apex,
72 Node: s.TunnelTransport.Identity(),
73 }, nil
74}
75 
76func (s *Server) OpenDelegatedSession(ctx context.Context, req *protocol.OpenDelegatedSessionRequest) (resp *protocol.OpenSessionResponse, err error) {
77 conn, cert, err := sessionAttachment(ctx)
78 if err != nil {
79 return nil, err
80 }
81 slot := req.GetRouteSlot()
82 if slot < 1 || slot > tun.NumRedundantLinks {
83 return nil, twirp.InvalidArgument.Errorf("route_slot must be between 1 and %d", tun.NumRedundantLinks)
84 }
85 var inv [16]byte
86 if _, err := rand.Read(inv[:]); err != nil {
87 return nil, twirp.InternalErrorWith(err)
88 }
89 sess := &session{
90 alias: tun.SessionAlias(inv),
91 conn: conn,
92 mode: delegated,
93 certNotAfter: cert.NotAfter,
94 }
95 if err := s.reserveSession(sess); err != nil {
96 return nil, err
97 }
98 defer func() {
99 if err != nil {
100 sess.fail(err.Error())
101 }
102 }()
103 secret, err := tun.ParseDelegationToken(req.GetToken())
104 if err != nil {
105 return nil, twirp.InvalidArgument.Error(err.Error())
106 }
107 start := time.Now()
108 rec, err := s.readDelegation(ctx, tun.DelegationID(secret))
109 if errors.Is(err, errDelegationNotFound) || errors.Is(err, errDelegationInvalid) {
110 return nil, twirp.Unauthenticated.Error(err.Error())
111 }
112 if err != nil {
113 return nil, twirp.Unavailable.Error(err.Error())
114 }
115 if rec.GetExpiresAt() != 0 && rec.GetExpiresAt() <= start.Unix() {
116 return nil, twirp.PermissionDenied.Error("grant has expired")
117 }
118 sess.mu.Lock()
119 sess.hostname, sess.grantID, sess.owner = rec.GetHostname(), rec.GetId(), rec.GetOwner()
120 if rec.GetExpiresAt() != 0 {
121 sess.grantExpiry = time.Unix(rec.GetExpiresAt(), 0)
122 }
123 sess.mu.Unlock()
124 leaseKey := []byte(tun.ClientLeaseKey(rec.GetOwner()))
125 lease, err := s.Chord.Acquire(ctx, leaseKey, 30*time.Second)
126 if err != nil {
127 return nil, twirp.Unavailable.Errorf("acquiring owner lease: %v", err)
128 }
129 defer s.Chord.Release(ctx, leaseKey, lease)
130 if err := s.verifyDelegatedAuthority(ctx, rec); err != nil {
131 return nil, delegationAuthorityError(err)
132 }
133 route := &protocol.TunnelRoute{
134 ClientDestination: &protocol.Node{
135 Address: sess.alias,
136 Rendezvous: true,
137 },
138 ChordDestination: s.ChordTransport.Identity(),
139 TunnelDestination: s.TunnelTransport.Identity(),
140 Hostname: sess.hostname,
141 }
142 data, err := route.MarshalVT()
143 if err != nil {
144 return nil, twirp.InternalErrorWith(err)
145 }
146 publishCtx, cancel := context.WithTimeout(ctx, publishTimeout)
147 defer cancel()
148 if err := s.Chord.Put(publishCtx, []byte(tun.RoutingKey(sess.hostname, int(slot))), data); err != nil {
149 return nil, twirp.Unavailable.Errorf("publishing slot %d failed: %v", slot, err)
150 }
151 if !sess.activate(ctx, sess.authorizationDeadline(start, sess.grantExpiry)) {
152 return nil, twirp.Aborted.Error("activation canceled or authorization expired; the published route now names a dead session")
153 }
154 go s.maintainSession(s.ParentContext, sess)
155 return &protocol.OpenSessionResponse{
156 Hostname: sess.hostname,
157 Apex: s.Apex,
158 GrantId: rec.GetId(),
159 ExpiresAt: rec.GetExpiresAt(),
160 Node: s.TunnelTransport.Identity(),
161 }, nil
162}