'zrok2 create share'/'zrok2 delete share' with changes in semantics for 'zrok2 share private --share-token' (#1140)

This commit is contained in:
Michael Quigley
2026-02-03 15:54:26 -05:00
parent c4d64e77f2
commit d848fa8f96
4 changed files with 181 additions and 40 deletions
+65
View File
@@ -0,0 +1,65 @@
package main
import (
"github.com/michaelquigley/df/dl"
"github.com/openziti/zrok/v2/environment"
"github.com/openziti/zrok/v2/sdk/golang/sdk"
"github.com/openziti/zrok/v2/tui"
"github.com/spf13/cobra"
)
func init() {
createCmd.AddCommand(newCreateShareCommand().cmd)
}
type createShareCommand struct {
cmd *cobra.Command
backendMode string
shareToken string
open bool
accessGrants []string
}
func newCreateShareCommand() *createShareCommand {
cmd := &cobra.Command{
Use: "share",
Short: "Create a private share without starting a backend",
Args: cobra.NoArgs,
}
command := &createShareCommand{cmd: cmd}
cmd.Flags().StringVarP(&command.backendMode, "backend-mode", "b", "proxy", "The backend mode {proxy, web, tcpTunnel, udpTunnel, caddy, drive, socks}")
cmd.Flags().StringVarP(&command.shareToken, "share-token", "s", "", "Request a specific share token name")
cmd.Flags().BoolVar(&command.open, "open", false, "Enable open permission mode")
cmd.Flags().StringArrayVar(&command.accessGrants, "access-grant", []string{}, "zrok accounts that are allowed to access this share")
cmd.Run = command.run
return command
}
func (cmd *createShareCommand) run(_ *cobra.Command, _ []string) {
root, err := environment.LoadRoot()
if err != nil {
tui.Error("error loading environment", err)
}
if !root.IsEnabled() {
tui.Error("unable to load environment; did you 'zrok2 enable'?", nil)
}
req := &sdk.ShareRequest{
BackendMode: sdk.BackendMode(cmd.backendMode),
ShareMode: sdk.PrivateShareMode,
PrivateShareToken: cmd.shareToken,
PermissionMode: sdk.ClosedPermissionMode,
AccessGrants: cmd.accessGrants,
}
if cmd.open {
req.PermissionMode = sdk.OpenPermissionMode
}
shr, err := sdk.CreateShare(root, req)
if err != nil {
tui.Error("unable to create share", err)
}
dl.Infof("created share '%v'", shr.Token)
}
@@ -7,27 +7,27 @@ import (
)
func init() {
rootCmd.AddCommand(newUnshareCommand().cmd)
deleteCmd.AddCommand(newDeleteShareCommand().cmd)
}
type unshareCommand struct {
type deleteShareCommand struct {
cmd *cobra.Command
envZId string
}
func newUnshareCommand() *unshareCommand {
func newDeleteShareCommand() *deleteShareCommand {
cmd := &cobra.Command{
Use: "unshare",
Short: "Remove a share",
Use: "share <shareToken>",
Short: "Delete a share",
Args: cobra.ExactArgs(1),
}
command := &unshareCommand{cmd: cmd}
command := &deleteShareCommand{cmd: cmd}
cmd.Flags().StringVar(&command.envZId, "envzid", "", "Override environment ziti identifier")
cmd.Run = command.run
return command
}
func (cmd *unshareCommand) run(_ *cobra.Command, args []string) {
func (cmd *deleteShareCommand) run(_ *cobra.Command, args []string) {
env, auth := mustGetEnvironmentAuth()
zrok, err := env.Client()
if err != nil {
@@ -46,5 +46,5 @@ func (cmd *unshareCommand) run(_ *cobra.Command, args []string) {
dl.Fatal(err)
}
dl.Infof("removed share '%v' from environment '%v'", req.Body.ShareToken, req.Body.EnvZID)
dl.Infof("deleted share '%v' from environment '%v'", req.Body.ShareToken, req.Body.EnvZID)
}
+85 -32
View File
@@ -60,7 +60,7 @@ func newSharePrivateCommand() *sharePrivateCommand {
headless, _ = root.Headless()
}
cmd.Flags().StringVarP(&command.backendMode, "backend-mode", "b", "proxy", "The backend mode {proxy, web, tcpTunnel, udpTunnel, caddy, drive, socks}")
cmd.Flags().StringVarP(&command.shareToken, "share-token", "s", "", "Request a specific share token name")
cmd.Flags().StringVarP(&command.shareToken, "share-token", "s", "", "Use an existing share instead of creating new")
cmd.Flags().BoolVar(&command.headless, "headless", headless, "Disable TUI and run headless")
cmd.Flags().BoolVar(&command.subordinate, "subordinate", false, "Enable agent mode")
cmd.MarkFlagsMutuallyExclusive("headless", "subordinate")
@@ -74,7 +74,7 @@ func newSharePrivateCommand() *sharePrivateCommand {
return command
}
func (cmd *sharePrivateCommand) run(_ *cobra.Command, args []string) {
func (cmd *sharePrivateCommand) run(cobraCmd *cobra.Command, args []string) {
if cmd.subordinate {
logrus.SetFormatter(&logrus.JSONFormatter{TimestampFormat: time.RFC3339Nano})
dlOpts := dl.DefaultOptions().SetTrimPrefix(trimPrefix).SetLevel(slog.LevelInfo)
@@ -94,14 +94,43 @@ func (cmd *sharePrivateCommand) run(_ *cobra.Command, args []string) {
detectAndRouteToAgent(
cmd.subordinate, cmd.forceLocal, cmd.forceAgent,
root,
func() { cmd.shareLocal(args, root) },
func() { cmd.shareAgent(args, root) },
func() { cmd.shareLocal(cobraCmd, args, root) },
func() { cmd.shareAgent(cobraCmd, args, root) },
)
}
func (cmd *sharePrivateCommand) shareLocal(args []string, root env_core.Root) {
func (cmd *sharePrivateCommand) shareLocal(cobraCmd *cobra.Command, args []string, root env_core.Root) {
var shr *sdk.Share
var skipDelete bool
var backendMode string
if cmd.shareToken != "" {
// using existing share - verify it exists and get its backend mode
if cobraCmd.Flags().Changed("backend-mode") {
cmd.error("unable to create share", errors.New("--backend-mode cannot be specified when using --share-token"))
}
shareDetail, err := sdk.GetShareDetail(root, cmd.shareToken)
if err != nil {
cmd.error("share not found", err)
}
if shareDetail.ShareMode != "private" {
cmd.error("share is not private", errors.New("invalid share mode"))
}
backendMode = shareDetail.BackendMode
shr = &sdk.Share{
Token: shareDetail.ShareToken,
FrontendEndpoints: shareDetail.FrontendEndpoints,
}
skipDelete = true
} else {
backendMode = cmd.backendMode
skipDelete = false
}
// validate and process backend mode (nil = allow all modes for private shares)
target, forceHeadless, err := validateBackendMode(cmd.backendMode, args, nil)
target, forceHeadless, err := validateBackendMode(backendMode, args, nil)
if err != nil {
cmd.error("unable to create share", err)
}
@@ -116,25 +145,27 @@ func (cmd *sharePrivateCommand) shareLocal(args []string, root env_core.Root) {
cmd.error("unable to load ziti identity configuration", err)
}
req := &sdk.ShareRequest{
BackendMode: sdk.BackendMode(cmd.backendMode),
ShareMode: sdk.PrivateShareMode,
PrivateShareToken: cmd.shareToken,
Target: target,
PermissionMode: sdk.ClosedPermissionMode,
AccessGrants: cmd.accessGrants,
}
if cmd.open {
req.PermissionMode = sdk.OpenPermissionMode
}
if shr == nil {
// create ephemeral share (existing behavior)
req := &sdk.ShareRequest{
BackendMode: sdk.BackendMode(backendMode),
ShareMode: sdk.PrivateShareMode,
Target: target,
PermissionMode: sdk.ClosedPermissionMode,
AccessGrants: cmd.accessGrants,
}
if cmd.open {
req.PermissionMode = sdk.OpenPermissionMode
}
shr, err := sdk.CreateShare(root, req)
if err != nil {
cmd.error("unable to create share", err)
shr, err = sdk.CreateShare(root, req)
if err != nil {
cmd.error("unable to create share", err)
}
}
shareDescription := fmt.Sprintf("access your share with: %v", tui.Code.Render(fmt.Sprintf("zrok2 access private %v", shr.Token)))
mdl := newShareModel(shr.Token, []string{shareDescription}, sdk.PrivateShareMode, sdk.BackendMode(cmd.backendMode))
mdl := newShareModel(shr.Token, []string{shareDescription}, sdk.PrivateShareMode, sdk.BackendMode(backendMode))
if !cmd.headless && !cmd.subordinate {
proxy.SetCaddyLoggingWriter(mdl)
}
@@ -143,13 +174,13 @@ func (cmd *sharePrivateCommand) shareLocal(args []string, root env_core.Root) {
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
<-c
cmd.shutdown(root, shr)
cmd.shutdown(root, shr, skipDelete)
os.Exit(0)
}()
requests := make(chan *endpoints.Request, 1024)
switch cmd.backendMode {
switch backendMode {
case "proxy":
cfg := &proxy.BackendConfig{
IdentityPath: zif,
@@ -239,7 +270,7 @@ func (cmd *sharePrivateCommand) shareLocal(args []string, root env_core.Root) {
be, err := proxy.NewCaddyfileBackend(cfg)
if err != nil {
cmd.shutdown(root, shr)
cmd.shutdown(root, shr, skipDelete)
cmd.error("unable to create 'caddy' backend", err)
}
@@ -353,7 +384,7 @@ func (cmd *sharePrivateCommand) shareLocal(args []string, root env_core.Root) {
}
close(requests)
cmd.shutdown(root, shr)
cmd.shutdown(root, shr, skipDelete)
}
}
@@ -367,18 +398,40 @@ func (cmd *sharePrivateCommand) error(msg string, err error) {
panic(errors.Wrap(err, msg))
}
func (cmd *sharePrivateCommand) shutdown(root env_core.Root, shr *sdk.Share) {
func (cmd *sharePrivateCommand) shutdown(root env_core.Root, shr *sdk.Share, skipDelete bool) {
dl.Debugf("shutting down '%v'", shr.Token)
if err := sdk.DeleteShare(root, shr); err != nil {
dl.Errorf("error shutting down '%v': %v", shr.Token, err)
if !skipDelete {
if err := sdk.DeleteShare(root, shr); err != nil {
dl.Errorf("error shutting down '%v': %v", shr.Token, err)
}
}
dl.Debugf("shutdown complete")
}
func (cmd *sharePrivateCommand) shareAgent(args []string, root env_core.Root) {
func (cmd *sharePrivateCommand) shareAgent(cobraCmd *cobra.Command, args []string, root env_core.Root) {
var target string
var backendMode string
switch cmd.backendMode {
if cmd.shareToken != "" {
// using existing share - verify it exists and get its backend mode
if cobraCmd.Flags().Changed("backend-mode") {
tui.Error("--backend-mode cannot be specified when using --share-token", nil)
}
shareDetail, err := sdk.GetShareDetail(root, cmd.shareToken)
if err != nil {
tui.Error("share not found", err)
}
if shareDetail.ShareMode != "private" {
tui.Error("share is not private", nil)
}
backendMode = shareDetail.BackendMode
} else {
backendMode = cmd.backendMode
}
switch backendMode {
case "proxy":
if len(args) != 1 {
tui.Error("the 'proxy' backend mode expects a <target>", nil)
@@ -450,7 +503,7 @@ func (cmd *sharePrivateCommand) shareAgent(args []string, root env_core.Root) {
target = "socks"
default:
tui.Error(fmt.Sprintf("invalid backend mode '%v'", cmd.backendMode), nil)
tui.Error(fmt.Sprintf("invalid backend mode '%v'", backendMode), nil)
}
client, conn, err := agentClient.NewClient(root)
@@ -462,7 +515,7 @@ func (cmd *sharePrivateCommand) shareAgent(args []string, root env_core.Root) {
shr, err := client.SharePrivate(context.Background(), &agentGrpc.SharePrivateRequest{
Target: target,
PrivateShareToken: cmd.shareToken,
BackendMode: cmd.backendMode,
BackendMode: backendMode,
Insecure: cmd.insecure,
Closed: !cmd.open,
AccessGrants: cmd.accessGrants,
+23
View File
@@ -5,6 +5,7 @@ import (
httptransport "github.com/go-openapi/runtime/client"
"github.com/openziti/zrok/v2/environment/env_core"
"github.com/openziti/zrok/v2/rest_client_zrok/metadata"
"github.com/openziti/zrok/v2/rest_client_zrok/share"
"github.com/openziti/zrok/v2/rest_model_zrok"
"github.com/pkg/errors"
@@ -116,3 +117,25 @@ func DeleteShare(root env_core.Root, shr *Share) error {
return nil
}
func GetShareDetail(root env_core.Root, shareToken string) (*rest_model_zrok.Share, error) {
if !root.IsEnabled() {
return nil, errors.New("environment is not enabled")
}
zrok, err := root.Client()
if err != nil {
return nil, errors.Wrap(err, "error getting zrok client")
}
auth := httptransport.APIKeyAuth("X-TOKEN", "header", root.Environment().AccountToken)
params := metadata.NewGetShareDetailParams()
params.ShareToken = shareToken
resp, err := zrok.Metadata.GetShareDetail(params, auth)
if err != nil {
return nil, errors.Wrap(err, "error getting share detail")
}
return resp.Payload, nil
}