From 9faf122416103edc12e7dca4b64d91e13b94a940 Mon Sep 17 00:00:00 2001 From: Michael Quigley Date: Thu, 27 Mar 2025 14:01:10 -0400 Subject: [PATCH 1/6] new 'logic' layer in Agent for private access (#922) --- agent/access.go | 10 ++++++++++ agent/accessPrivate.go | 32 ++++++++++++++++++++++++-------- agent/releaseAccess.go | 15 +++++++++------ agent/share.go | 26 ++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 14 deletions(-) diff --git a/agent/access.go b/agent/access.go index 45a4da66..083381dd 100644 --- a/agent/access.go +++ b/agent/access.go @@ -6,6 +6,16 @@ import ( "github.com/openziti/zrok/cmd/zrok/subordinate" ) +type AccessPrivateRequest struct { + Token string + BindAddress string + AutoMode bool + AutoAddress string + AutoStartPort uint16 + AutoEndPort uint16 + ResponseHeaders []string +} + type access struct { frontendToken string token string diff --git a/agent/accessPrivate.go b/agent/accessPrivate.go index 5cb9b9ae..75f4034b 100644 --- a/agent/accessPrivate.go +++ b/agent/accessPrivate.go @@ -12,14 +12,14 @@ import ( "os" ) -func (i *agentGrpcImpl) AccessPrivate(_ context.Context, req *agentGrpc.AccessPrivateRequest) (*agentGrpc.AccessPrivateResponse, error) { +func (a *Agent) AccessPrivate(req *AccessPrivateRequest) (frontendToken string, err error) { root, err := environment.LoadRoot() if err != nil { - return nil, err + return "", err } if !root.IsEnabled() { - return nil, errors.New("unable to load environment; did you 'zrok enable'?") + return "", errors.New("unable to load environment; did you 'zrok enable'?") } accCmd := []string{os.Args[0], "access", "private", "--subordinate", "-b", req.BindAddress, req.Token} @@ -38,7 +38,7 @@ func (i *agentGrpcImpl) AccessPrivate(_ context.Context, req *agentGrpc.AccessPr autoEndPort: uint16(req.AutoEndPort), responseHeaders: req.ResponseHeaders, sub: subordinate.NewMessageHandler(), - agent: i.agent, + agent: a, } acc.sub.MessageHandler = func(msg subordinate.Message) { logrus.Info(msg) @@ -74,20 +74,36 @@ func (i *agentGrpcImpl) AccessPrivate(_ context.Context, req *agentGrpc.AccessPr acc.process, err = proctree.StartChild(acc.sub.Tail, accCmd...) if err != nil { - return nil, err + return "", err } <-acc.sub.BootComplete if bootErr == nil { go acc.monitor() - i.agent.addAccess <- acc - return &agentGrpc.AccessPrivateResponse{FrontendToken: acc.frontendToken}, nil + a.addAccess <- acc + return acc.frontendToken, nil } else { if err := proctree.WaitChild(acc.process); err != nil { logrus.Errorf("error joining: %v", err) } - return nil, fmt.Errorf("unable to start access: %v", bootErr) + return "", fmt.Errorf("unable to start access: %v", bootErr) + } +} + +func (i *agentGrpcImpl) AccessPrivate(_ context.Context, req *agentGrpc.AccessPrivateRequest) (*agentGrpc.AccessPrivateResponse, error) { + if frontendToken, err := i.agent.AccessPrivate(&AccessPrivateRequest{ + Token: req.Token, + BindAddress: req.BindAddress, + AutoMode: req.AutoMode, + AutoAddress: req.AutoAddress, + AutoStartPort: uint16(req.AutoStartPort), + AutoEndPort: uint16(req.AutoEndPort), + ResponseHeaders: req.ResponseHeaders, + }); err == nil { + return &agentGrpc.AccessPrivateResponse{FrontendToken: frontendToken}, nil + } else { + return nil, err } } diff --git a/agent/releaseAccess.go b/agent/releaseAccess.go index 86e2f8d2..582ab3ed 100644 --- a/agent/releaseAccess.go +++ b/agent/releaseAccess.go @@ -7,13 +7,16 @@ import ( "github.com/sirupsen/logrus" ) -func (i *agentGrpcImpl) ReleaseAccess(_ context.Context, req *agentGrpc.ReleaseAccessRequest) (*agentGrpc.ReleaseAccessResponse, error) { - if acc, found := i.agent.accesses[req.FrontendToken]; found { - i.agent.rmAccess <- acc +func (a *Agent) ReleaseAccess(frontendToken string) error { + if acc, found := a.accesses[frontendToken]; found { + a.rmAccess <- acc logrus.Infof("released access '%v'", acc.frontendToken) - } else { - return nil, errors.Errorf("agent has no access with frontend token '%v'", req.FrontendToken) + return errors.Errorf("agent has no access with frontend token '%v'", frontendToken) } - return nil, nil + return nil +} + +func (i *agentGrpcImpl) ReleaseAccess(_ context.Context, req *agentGrpc.ReleaseAccessRequest) (*agentGrpc.ReleaseAccessResponse, error) { + return nil, i.agent.ReleaseAccess(req.FrontendToken) } diff --git a/agent/share.go b/agent/share.go index d90ad0fb..edc7f146 100644 --- a/agent/share.go +++ b/agent/share.go @@ -9,6 +9,32 @@ import ( "time" ) +type SharePrivateRequest struct { + Target string + BackendMode string + Insecure bool + Closed bool + AccessGrants []string +} + +type SharePublicRequest struct { + Target string + BasicAuth []string + FrontendSelection []string + BackendMode string + Insecure bool + OauthProvider string + OauthCheckInterval string + Closed bool + AccessGrants []string +} + +type ShareReservedRequest struct { + Token string + OverrideEndpoint string + Insecure bool +} + type share struct { token string frontendEndpoints []string From ab9490e44062acc8e8496e5ca42f2454143c9019 Mon Sep 17 00:00:00 2001 From: Michael Quigley Date: Thu, 27 Mar 2025 14:11:28 -0400 Subject: [PATCH 2/6] release share; share private (#922) --- agent/releaseShare.go | 15 +++++++++------ agent/sharePrivate.go | 32 +++++++++++++++++++++++--------- 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/agent/releaseShare.go b/agent/releaseShare.go index 0759e1c5..4b6f8303 100755 --- a/agent/releaseShare.go +++ b/agent/releaseShare.go @@ -7,13 +7,16 @@ import ( "github.com/sirupsen/logrus" ) -func (i *agentGrpcImpl) ReleaseShare(_ context.Context, req *agentGrpc.ReleaseShareRequest) (*agentGrpc.ReleaseShareResponse, error) { - if shr, found := i.agent.shares[req.Token]; found { - i.agent.rmShare <- shr +func (a *Agent) ReleaseShare(shareToken string) error { + if shr, found := a.shares[shareToken]; found { + a.rmShare <- shr logrus.Infof("released share '%v'", shr.token) - } else { - return nil, errors.Errorf("agent has no share with token '%v'", req.Token) + errors.Errorf("agent has no share with token '%v'", shareToken) } - return nil, nil + return nil +} + +func (i *agentGrpcImpl) ReleaseShare(_ context.Context, req *agentGrpc.ReleaseShareRequest) (*agentGrpc.ReleaseShareResponse, error) { + return nil, i.agent.ReleaseShare(req.Token) } diff --git a/agent/sharePrivate.go b/agent/sharePrivate.go index 350ff5aa..d4e07778 100644 --- a/agent/sharePrivate.go +++ b/agent/sharePrivate.go @@ -13,14 +13,14 @@ import ( "os" ) -func (i *agentGrpcImpl) SharePrivate(_ context.Context, req *agentGrpc.SharePrivateRequest) (*agentGrpc.SharePrivateResponse, error) { +func (a *Agent) SharePrivate(req *SharePrivateRequest) (shareToken string, err error) { root, err := environment.LoadRoot() if err != nil { - return nil, err + return "", err } if !root.IsEnabled() { - return nil, errors.New("unable to load environment; did you 'zrok enable'?") + return "", errors.New("unable to load environment; did you 'zrok enable'?") } shrCmd := []string{os.Args[0], "share", "private", "--subordinate", "-b", req.BackendMode} @@ -28,7 +28,7 @@ func (i *agentGrpcImpl) SharePrivate(_ context.Context, req *agentGrpc.SharePriv shareMode: sdk.PrivateShareMode, backendMode: sdk.BackendMode(req.BackendMode), sub: subordinate.NewMessageHandler(), - agent: i.agent, + agent: a, } shr.sub.MessageHandler = func(msg subordinate.Message) { logrus.Info(msg) @@ -63,20 +63,34 @@ func (i *agentGrpcImpl) SharePrivate(_ context.Context, req *agentGrpc.SharePriv shr.process, err = proctree.StartChild(shr.sub.Tail, shrCmd...) if err != nil { - return nil, err + return "", err } <-shr.sub.BootComplete if bootErr == nil { go shr.monitor() - i.agent.addShare <- shr - return &agentGrpc.SharePrivateResponse{Token: shr.token}, nil - + a.addShare <- shr + return shr.token, nil + } else { if err := proctree.WaitChild(shr.process); err != nil { logrus.Errorf("error joining: %v", err) } - return nil, fmt.Errorf("unable to start share: %v", bootErr) + return "", fmt.Errorf("unable to start share: %v", bootErr) + } +} + +func (i *agentGrpcImpl) SharePrivate(_ context.Context, req *agentGrpc.SharePrivateRequest) (*agentGrpc.SharePrivateResponse, error) { + if shareToken, err := i.agent.SharePrivate(&SharePrivateRequest{ + Target: req.Target, + BackendMode: req.BackendMode, + Insecure: req.Insecure, + Closed: req.Closed, + AccessGrants: req.AccessGrants, + }); err == nil { + return &agentGrpc.SharePrivateResponse{Token: shareToken}, nil + } else { + return nil, err } } From b5df385da406f5c7977bc59512d8ef81c22cbccc Mon Sep 17 00:00:00 2001 From: Michael Quigley Date: Thu, 27 Mar 2025 14:23:57 -0400 Subject: [PATCH 3/6] share public; share reserved (#922) --- agent/share.go | 27 ++++++++++++++++++--------- agent/sharePublic.go | 38 +++++++++++++++++++++++++++----------- agent/shareReserved.go | 26 ++++++++++++++++++++++---- 3 files changed, 67 insertions(+), 24 deletions(-) diff --git a/agent/share.go b/agent/share.go index edc7f146..ab633154 100644 --- a/agent/share.go +++ b/agent/share.go @@ -18,15 +18,16 @@ type SharePrivateRequest struct { } type SharePublicRequest struct { - Target string - BasicAuth []string - FrontendSelection []string - BackendMode string - Insecure bool - OauthProvider string - OauthCheckInterval string - Closed bool - AccessGrants []string + Target string + BasicAuth []string + FrontendSelection []string + BackendMode string + Insecure bool + OauthProvider string + OauthEmailAddressPatterns []string + OauthCheckInterval string + Closed bool + AccessGrants []string } type ShareReservedRequest struct { @@ -35,6 +36,14 @@ type ShareReservedRequest struct { Insecure bool } +type ShareReservedResponse struct { + Token string + BackendMode string + ShareMode string + FrontendEndpoints []string + Target string +} + type share struct { token string frontendEndpoints []string diff --git a/agent/sharePublic.go b/agent/sharePublic.go index fb584eda..33891da3 100644 --- a/agent/sharePublic.go +++ b/agent/sharePublic.go @@ -13,14 +13,14 @@ import ( "os" ) -func (i *agentGrpcImpl) SharePublic(_ context.Context, req *agentGrpc.SharePublicRequest) (*agentGrpc.SharePublicResponse, error) { +func (a *Agent) SharePublic(req *SharePublicRequest) (shareToken string, frontendEndpoint []string, err error) { root, err := environment.LoadRoot() if err != nil { - return nil, err + return "", nil, err } if !root.IsEnabled() { - return nil, errors.New("unable to load environment; did you 'zrok enable'?") + return "", nil, errors.New("unable to load environment; did you 'zrok enable'?") } shrCmd := []string{os.Args[0], "share", "public", "--subordinate", "-b", req.BackendMode} @@ -28,7 +28,7 @@ func (i *agentGrpcImpl) SharePublic(_ context.Context, req *agentGrpc.SharePubli shareMode: sdk.PublicShareMode, backendMode: sdk.BackendMode(req.BackendMode), sub: subordinate.NewMessageHandler(), - agent: i.agent, + agent: a, } shr.sub.MessageHandler = func(msg subordinate.Message) { logrus.Info(msg) @@ -87,23 +87,39 @@ func (i *agentGrpcImpl) SharePublic(_ context.Context, req *agentGrpc.SharePubli shr.process, err = proctree.StartChild(shr.sub.Tail, shrCmd...) if err != nil { - return nil, err + return "", nil, err } <-shr.sub.BootComplete if bootErr == nil { go shr.monitor() - i.agent.addShare <- shr - return &agentGrpc.SharePublicResponse{ - Token: shr.token, - FrontendEndpoints: shr.frontendEndpoints, - }, nil + a.addShare <- shr + return shr.token, shr.frontendEndpoints, nil } else { if err := proctree.WaitChild(shr.process); err != nil { logrus.Errorf("error joining: %v", err) } - return nil, fmt.Errorf("unable to start share: %v", bootErr) + return "", nil, fmt.Errorf("unable to start share: %v", bootErr) + } +} + +func (i *agentGrpcImpl) SharePublic(_ context.Context, req *agentGrpc.SharePublicRequest) (*agentGrpc.SharePublicResponse, error) { + if shareToken, frontendEndpoints, err := i.agent.SharePublic(&SharePublicRequest{ + Target: req.Target, + BasicAuth: req.BasicAuth, + FrontendSelection: req.FrontendSelection, + BackendMode: req.BackendMode, + Insecure: req.Insecure, + OauthProvider: req.OauthProvider, + OauthEmailAddressPatterns: req.OauthEmailAddressPatterns, + OauthCheckInterval: req.OauthCheckInterval, + Closed: req.Closed, + AccessGrants: req.AccessGrants, + }); err == nil { + return &agentGrpc.SharePublicResponse{Token: shareToken, FrontendEndpoints: frontendEndpoints}, nil + } else { + return nil, err } } diff --git a/agent/shareReserved.go b/agent/shareReserved.go index cfa5eebc..f5e3e588 100644 --- a/agent/shareReserved.go +++ b/agent/shareReserved.go @@ -12,7 +12,7 @@ import ( "os" ) -func (i *agentGrpcImpl) ShareReserved(_ context.Context, req *agentGrpc.ShareReservedRequest) (*agentGrpc.ShareReservedResponse, error) { +func (a *Agent) ShareReserved(req *ShareReservedRequest) (*ShareReservedResponse, error) { root, err := environment.LoadRoot() if err != nil { return nil, err @@ -26,7 +26,7 @@ func (i *agentGrpcImpl) ShareReserved(_ context.Context, req *agentGrpc.ShareRes shr := &share{ reserved: true, sub: subordinate.NewMessageHandler(), - agent: i.agent, + agent: a, } shr.sub.MessageHandler = func(msg subordinate.Message) { logrus.Info(msg) @@ -60,8 +60,8 @@ func (i *agentGrpcImpl) ShareReserved(_ context.Context, req *agentGrpc.ShareRes if bootErr == nil { go shr.monitor() - i.agent.addShare <- shr - return &agentGrpc.ShareReservedResponse{ + a.addShare <- shr + return &ShareReservedResponse{ Token: shr.token, BackendMode: string(shr.backendMode), ShareMode: string(shr.shareMode), @@ -76,3 +76,21 @@ func (i *agentGrpcImpl) ShareReserved(_ context.Context, req *agentGrpc.ShareRes return nil, fmt.Errorf("unable to start share: %v", bootErr) } } + +func (i *agentGrpcImpl) ShareReserved(_ context.Context, req *agentGrpc.ShareReservedRequest) (*agentGrpc.ShareReservedResponse, error) { + if resp, err := i.agent.ShareReserved(&ShareReservedRequest{ + Token: req.Token, + OverrideEndpoint: req.OverrideEndpoint, + Insecure: req.Insecure, + }); err == nil { + return &agentGrpc.ShareReservedResponse{ + Token: resp.Token, + BackendMode: resp.BackendMode, + ShareMode: resp.ShareMode, + FrontendEndpoints: resp.FrontendEndpoints, + Target: resp.Target, + }, nil + } else { + return nil, err + } +} From d319f32ea2f703eca2ebc23a44937af8d4d02206 Mon Sep 17 00:00:00 2001 From: Michael Quigley Date: Thu, 27 Mar 2025 15:13:10 -0400 Subject: [PATCH 4/6] full persistence for private accesses and reserved shares (#922) --- agent/access.go | 2 + agent/accessPrivate.go | 5 +- agent/agent.go | 108 ++++++++++++++++++++++++++++++---- agent/registry.go | 31 ++++++++++ agent/share.go | 2 + agent/sharePrivate.go | 1 + agent/sharePublic.go | 1 + agent/shareReserved.go | 1 + environment/env_core/model.go | 1 + environment/env_v0_3/api.go | 6 +- environment/env_v0_4/api.go | 4 ++ environment/env_v0_4/dirs.go | 8 +++ 12 files changed, 157 insertions(+), 13 deletions(-) create mode 100644 agent/registry.go diff --git a/agent/access.go b/agent/access.go index 083381dd..2f4f66af 100644 --- a/agent/access.go +++ b/agent/access.go @@ -26,6 +26,8 @@ type access struct { autoEndPort uint16 responseHeaders []string + request *AccessPrivateRequest + process *proctree.Child sub *subordinate.MessageHandler diff --git a/agent/accessPrivate.go b/agent/accessPrivate.go index 75f4034b..bde95c49 100644 --- a/agent/accessPrivate.go +++ b/agent/accessPrivate.go @@ -34,9 +34,10 @@ func (a *Agent) AccessPrivate(req *AccessPrivateRequest) (frontendToken string, bindAddress: req.BindAddress, autoMode: req.AutoMode, autoAddress: req.AutoAddress, - autoStartPort: uint16(req.AutoStartPort), - autoEndPort: uint16(req.AutoEndPort), + autoStartPort: req.AutoStartPort, + autoEndPort: req.AutoEndPort, responseHeaders: req.ResponseHeaders, + request: req, sub: subordinate.NewMessageHandler(), agent: a, } diff --git a/agent/agent.go b/agent/agent.go index 7d9d616a..13001454 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -19,16 +19,17 @@ import ( ) type Agent struct { - cfg *AgentConfig - httpEndpoint string - root env_core.Root - agentSocket string - shares map[string]*share - addShare chan *share - rmShare chan *share - accesses map[string]*access - addAccess chan *access - rmAccess chan *access + cfg *AgentConfig + httpEndpoint string + root env_core.Root + agentSocket string + shares map[string]*share + addShare chan *share + rmShare chan *share + accesses map[string]*access + addAccess chan *access + rmAccess chan *access + persistRegistry bool } func NewAgent(cfg *AgentConfig, root env_core.Root) (*Agent, error) { @@ -67,6 +68,12 @@ func (a *Agent) Run() error { go a.manager() go a.gateway(a.cfg) + a.persistRegistry = false + if err := a.ReloadRegistry(); err != nil { + logrus.Errorf("error reloading registry '%v'", err) + } + a.persistRegistry = true + srv := grpc.NewServer() agentGrpc.RegisterAgentServer(srv, &agentGrpcImpl{agent: a}) if err := srv.Serve(l); err != nil { @@ -79,6 +86,7 @@ func (a *Agent) Run() error { func (a *Agent) Shutdown() { logrus.Infof("stopping") + a.persistRegistry = false if err := os.Remove(a.agentSocket); err != nil { logrus.Warnf("unable to remove agent socket: %v", err) } @@ -96,6 +104,60 @@ func (a *Agent) Config() *AgentConfig { return a.cfg } +func (a *Agent) ReloadRegistry() error { + registryPath, err := a.root.AgentRegistry() + if err != nil { + return err + } + registry, err := LoadRegistry(registryPath) + if err != nil { + return err + } + logrus.Infof("loaded %d reserved shares, %d accesses", len(registry.ReservedShares), len(registry.PrivateAccesses)) + for _, req := range registry.ReservedShares { + if resp, err := a.ShareReserved(req); err == nil { + logrus.Infof("restarted reserved share '%v' -> '%v'", req, resp) + } else { + logrus.Errorf("error restarting reserved share '%v': %v", req, err) + } + } + for _, req := range registry.PrivateAccesses { + if resp, err := a.AccessPrivate(req); err == nil { + logrus.Infof("restarted private access '%v' -> '%v'", req, resp) + } else { + logrus.Errorf("error restarting private access '%v': %v", req, err) + } + } + logrus.Infof("reload complete") + return nil +} + +func (a *Agent) SaveRegistry() error { + r := &Registry{} + for _, shr := range a.shares { + if shr.request != nil { + switch shr.request.(type) { + case *ShareReservedRequest: + logrus.Infof("persisting reserved share '%v'", shr.token) + r.ReservedShares = append(r.ReservedShares, shr.request.(*ShareReservedRequest)) + } + } + } + for _, acc := range a.accesses { + if acc.request != nil { + r.PrivateAccesses = append(r.PrivateAccesses, acc.request) + } + } + registryPath, err := a.root.AgentRegistry() + if err != nil { + return err + } + if err := r.Save(registryPath); err != nil { + return err + } + return nil +} + func (a *Agent) gateway(cfg *AgentConfig) { logrus.Info("started") defer logrus.Warn("exited") @@ -132,6 +194,12 @@ func (a *Agent) manager() { logrus.Infof("adding new share '%v'", inShare.token) a.shares[inShare.token] = inShare + if a.persistRegistry { + if err := a.SaveRegistry(); err != nil { + logrus.Errorf("unable to persist registry: %v", err) + } + } + case outShare := <-a.rmShare: if shr, found := a.shares[outShare.token]; found { logrus.Infof("removing share '%v'", shr.token) @@ -147,6 +215,13 @@ func (a *Agent) manager() { } } delete(a.shares, shr.token) + + if a.persistRegistry { + if err := a.SaveRegistry(); err != nil { + logrus.Errorf("unable to persist registry: %v", err) + } + } + } else { logrus.Debug("skipping unidentified (orphaned) share removal") } @@ -155,6 +230,12 @@ func (a *Agent) manager() { logrus.Infof("adding new access '%v'", inAccess.frontendToken) a.accesses[inAccess.frontendToken] = inAccess + if a.persistRegistry { + if err := a.SaveRegistry(); err != nil { + logrus.Errorf("unable to persist registry: %v", err) + } + } + case outAccess := <-a.rmAccess: if acc, found := a.accesses[outAccess.frontendToken]; found { logrus.Infof("removing access '%v'", acc.frontendToken) @@ -168,6 +249,13 @@ func (a *Agent) manager() { logrus.Errorf("error deleting access '%v': %v", acc.frontendToken, err) } delete(a.accesses, acc.frontendToken) + + if a.persistRegistry { + if err := a.SaveRegistry(); err != nil { + logrus.Errorf("unable to persist registry: %v", err) + } + } + } else { logrus.Debug("skipping unidentified (orphaned) access removal") } diff --git a/agent/registry.go b/agent/registry.go new file mode 100644 index 00000000..7cb2a5d0 --- /dev/null +++ b/agent/registry.go @@ -0,0 +1,31 @@ +package agent + +import ( + "encoding/json" + "os" +) + +type Registry struct { + ReservedShares []*ShareReservedRequest + PrivateAccesses []*AccessPrivateRequest +} + +func LoadRegistry(path string) (*Registry, error) { + data, err := os.ReadFile(path) + if err != nil { + return nil, err + } + r := &Registry{} + if err := json.Unmarshal(data, r); err != nil { + return nil, err + } + return r, nil +} + +func (r *Registry) Save(path string) error { + data, err := json.MarshalIndent(r, "", " ") + if err != nil { + return err + } + return os.WriteFile(path, data, 0644) +} diff --git a/agent/share.go b/agent/share.go index ab633154..7c5c3e8e 100644 --- a/agent/share.go +++ b/agent/share.go @@ -60,6 +60,8 @@ type share struct { closed bool accessGrants []string + request interface{} + process *proctree.Child sub *subordinate.MessageHandler diff --git a/agent/sharePrivate.go b/agent/sharePrivate.go index d4e07778..d5f24155 100644 --- a/agent/sharePrivate.go +++ b/agent/sharePrivate.go @@ -27,6 +27,7 @@ func (a *Agent) SharePrivate(req *SharePrivateRequest) (shareToken string, err e shr := &share{ shareMode: sdk.PrivateShareMode, backendMode: sdk.BackendMode(req.BackendMode), + request: req, sub: subordinate.NewMessageHandler(), agent: a, } diff --git a/agent/sharePublic.go b/agent/sharePublic.go index 33891da3..5db0a521 100644 --- a/agent/sharePublic.go +++ b/agent/sharePublic.go @@ -27,6 +27,7 @@ func (a *Agent) SharePublic(req *SharePublicRequest) (shareToken string, fronten shr := &share{ shareMode: sdk.PublicShareMode, backendMode: sdk.BackendMode(req.BackendMode), + request: req, sub: subordinate.NewMessageHandler(), agent: a, } diff --git a/agent/shareReserved.go b/agent/shareReserved.go index f5e3e588..d3301197 100644 --- a/agent/shareReserved.go +++ b/agent/shareReserved.go @@ -25,6 +25,7 @@ func (a *Agent) ShareReserved(req *ShareReservedRequest) (*ShareReservedResponse shrCmd := []string{os.Args[0], "share", "reserved", "--subordinate"} shr := &share{ reserved: true, + request: req, sub: subordinate.NewMessageHandler(), agent: a, } diff --git a/environment/env_core/model.go b/environment/env_core/model.go index 84a1d98a..f7d5f74c 100644 --- a/environment/env_core/model.go +++ b/environment/env_core/model.go @@ -29,6 +29,7 @@ type Root interface { DeleteZitiIdentityNamed(name string) error AgentSocket() (string, error) + AgentRegistry() (string, error) } type Environment struct { diff --git a/environment/env_v0_3/api.go b/environment/env_v0_3/api.go index cab59ed0..cf0f610a 100644 --- a/environment/env_v0_3/api.go +++ b/environment/env_v0_3/api.go @@ -194,7 +194,11 @@ func (r *Root) DeleteZitiIdentityNamed(name string) error { } func (r *Root) AgentSocket() (string, error) { - return "", errors.Errorf("this environment version does not support agent sockets; please 'zrok update' this environment") + return "", errors.Errorf("this environment version does not support the zrok Agent; please 'zrok update' this environment") +} + +func (r *Root) AgentRegistry() (string, error) { + return "", errors.Errorf("this environment version does not support the zrok Agent; please 'zrok update' this environment") } func (r *Root) Obliterate() error { diff --git a/environment/env_v0_4/api.go b/environment/env_v0_4/api.go index 1f53dc8b..d2aec7e6 100644 --- a/environment/env_v0_4/api.go +++ b/environment/env_v0_4/api.go @@ -196,6 +196,10 @@ func (r *Root) AgentSocket() (string, error) { return agentSocket() } +func (r *Root) AgentRegistry() (string, error) { + return agentRegistry() +} + func (r *Root) Obliterate() error { zrd, err := rootDir() if err != nil { diff --git a/environment/env_v0_4/dirs.go b/environment/env_v0_4/dirs.go index bf18febf..328eeb80 100644 --- a/environment/env_v0_4/dirs.go +++ b/environment/env_v0_4/dirs.go @@ -61,3 +61,11 @@ func agentSocket() (string, error) { } return filepath.Join(zrd, "agent.socket"), nil } + +func agentRegistry() (string, error) { + zrd, err := rootDir() + if err != nil { + return "", err + } + return filepath.Join(zrd, "agent-registry.json"), nil +} From 76d83dad4e91e5517c02532332ae2bea94569564 Mon Sep 17 00:00:00 2001 From: Michael Quigley Date: Thu, 27 Mar 2025 15:16:10 -0400 Subject: [PATCH 5/6] changelog (#922) --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d452e0e2..947ede35 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## v1.0.1 +FEATURE: The zrok Agent now persists private accesses and reserved shares between executions. Any `zrok access private` instances or `zrok share reserved` instances created using the agent are now persisted to a registry stored in `${HOME}/.zrok`. When restarting the agent these accesses and reserved shares are re-created from the data in this registry (https://github.com/openziti/zrok/pull/922) + CHANGE: let the Docker instance set the Caddy HTTPS port (https://github.com/openziti/zrok/pull/920) CHANGE: Add Traefik option for TLS termination in the Docker instance (https://github.com/openziti/zrok/issues/808) From 0c3f6347e357a965e8f0bceeeabeb8b687b5d42d Mon Sep 17 00:00:00 2001 From: Michael Quigley Date: Fri, 28 Mar 2025 13:15:05 -0400 Subject: [PATCH 6/6] better json naming/name indirection (#922) --- agent/access.go | 14 +++++++------- agent/registry.go | 12 ++++++++++-- agent/share.go | 36 ++++++++++++++++++------------------ 3 files changed, 35 insertions(+), 27 deletions(-) diff --git a/agent/access.go b/agent/access.go index 2f4f66af..97d6ae3d 100644 --- a/agent/access.go +++ b/agent/access.go @@ -7,13 +7,13 @@ import ( ) type AccessPrivateRequest struct { - Token string - BindAddress string - AutoMode bool - AutoAddress string - AutoStartPort uint16 - AutoEndPort uint16 - ResponseHeaders []string + Token string `json:"token"` + BindAddress string `json:"bind_address"` + AutoMode bool `json:"auto_mode"` + AutoAddress string `json:"auto_address"` + AutoStartPort uint16 `json:"auto_start_port"` + AutoEndPort uint16 `json:"auto_end_port"` + ResponseHeaders []string `json:"response_headers"` } type access struct { diff --git a/agent/registry.go b/agent/registry.go index 7cb2a5d0..f3d500c8 100644 --- a/agent/registry.go +++ b/agent/registry.go @@ -2,12 +2,16 @@ package agent import ( "encoding/json" + "fmt" "os" ) +const RegistryV = "1" + type Registry struct { - ReservedShares []*ShareReservedRequest - PrivateAccesses []*AccessPrivateRequest + V string `json:"v"` + ReservedShares []*ShareReservedRequest `json:"reserved_shares"` + PrivateAccesses []*AccessPrivateRequest `json:"private_accesses"` } func LoadRegistry(path string) (*Registry, error) { @@ -19,10 +23,14 @@ func LoadRegistry(path string) (*Registry, error) { if err := json.Unmarshal(data, r); err != nil { return nil, err } + if r.V != RegistryV { + return nil, fmt.Errorf("invalid registry version '%v'; expected '%v", r.V, RegistryV) + } return r, nil } func (r *Registry) Save(path string) error { + r.V = RegistryV data, err := json.MarshalIndent(r, "", " ") if err != nil { return err diff --git a/agent/share.go b/agent/share.go index 7c5c3e8e..7064acf2 100644 --- a/agent/share.go +++ b/agent/share.go @@ -10,30 +10,30 @@ import ( ) type SharePrivateRequest struct { - Target string - BackendMode string - Insecure bool - Closed bool - AccessGrants []string + Target string `json:"target"` + BackendMode string `json:"backend_mode"` + Insecure bool `json:"insecure"` + Closed bool `json:"closed"` + AccessGrants []string `json:"access_grants"` } type SharePublicRequest struct { - Target string - BasicAuth []string - FrontendSelection []string - BackendMode string - Insecure bool - OauthProvider string - OauthEmailAddressPatterns []string - OauthCheckInterval string - Closed bool - AccessGrants []string + Target string `json:"target"` + BasicAuth []string `json:"basic_auth"` + FrontendSelection []string `json:"frontend_selection"` + BackendMode string `json:"backend_mode"` + Insecure bool `json:"insecure"` + OauthProvider string `json:"oauth_provider"` + OauthEmailAddressPatterns []string `json:"oauth_email_address_patterns"` + OauthCheckInterval string `json:"oauth_check_interval"` + Closed bool `json:"closed"` + AccessGrants []string `json:"access_grants"` } type ShareReservedRequest struct { - Token string - OverrideEndpoint string - Insecure bool + Token string `json:"token"` + OverrideEndpoint string `json:"override_endpoint"` + Insecure bool `json:"insecure"` } type ShareReservedResponse struct {