diff --git a/zsshlib/flags.go.back b/zsshlib/flags.go.back deleted file mode 100644 index 77d6cde..0000000 --- a/zsshlib/flags.go.back +++ /dev/null @@ -1,124 +0,0 @@ -package zsshlib - -import ( - "fmt" - "os" - "os/user" - "path/filepath" - "runtime" - "strings" - - "github.com/sirupsen/logrus" - "github.com/spf13/cobra" -) - -type SshFlags struct { - ZConfig string - SshKeyPath string - Debug bool - ServiceName string - OIDC OIDCFlags -} - -type OIDCFlags struct { - Mode bool - Issuer string - ClientID string - ClientSecret string - CallbackPort string -} - -type Creds struct { - IdentityFile string -} - -type ScpFlags struct { - SshFlags - Recursive bool -} - -func (f *SshFlags) GetUserAndIdentity(input string) (string, string) { - username := ParseUserName(input) - f.DebugLog(" username set to: %s", username) - targetIdentity := ParseTargetIdentity(input) - f.DebugLog("targetIdentity set to: %s", targetIdentity) - return username, targetIdentity -} - -func ParseUserName(input string) string { - var username string - if strings.ContainsAny(input, "@") { - userServiceName := strings.Split(input, "@") - username = userServiceName[0] - } else { - curUser, err := user.Current() - if err != nil { - logrus.Fatal(err) - } - username = curUser.Username - if strings.Contains(username, "\\") && runtime.GOOS == "windows" { - username = strings.Split(username, "\\")[1] - } - } - return username -} - -func ParseTargetIdentity(input string) string { - var targetIdentity string - if strings.ContainsAny(input, "@") { - targetIdentity = strings.Split(input, "@")[1] - } else { - targetIdentity = input - } - - if strings.Contains(targetIdentity, ":") { - return strings.Split(targetIdentity, ":")[0] - } - return targetIdentity -} - -func ParseFilePath(input string) string { - if strings.Contains(input, ":") { - colPos := strings.Index(input, ":") + 1 - return input[colPos:] - } - return input -} - -func MarkOidcagsRequired(cmd *cobra.Command) { - cmd.MarkFlagRequired("") -} - -// TODO: Add config file support -func (f *SshFlags) OIDCFlags(cmd *cobra.Command, exeName string) { - cmd.Flags().StringVarP(&f.OIDC.CallbackPort, "CallbackPort", "p", "63275", "Port for Callback. default: 63275") - cmd.Flags().StringVarP(&f.OIDC.ClientID, "ClientID", "n", "cid1", "IdP ClientID. default: 12345678") - cmd.Flags().StringVarP(&f.OIDC.ClientSecret, "ClientSecret", "e", "", "IdP ClientSecret. default: (empty string - use PKCE)") - cmd.Flags().StringVarP(&f.OIDC.Issuer, "OIDCIssuer", "a", "https://dev-yourid.okta.com", "URL of the OpenID Connect provider. required") - cmd.Flags().BoolVarP(&f.OIDC.Mode, "oidc", "o", false, "toggle OIDC mode. default: false") -} - -func (f *SshFlags) InitFlags(cmd *cobra.Command, exeName string) { - cmd.Flags().StringVarP(&f.ServiceName, "service", "s", exeName, fmt.Sprintf("service name. default: %s", exeName)) - cmd.Flags().StringVarP(&f.ZConfig, "ZConfig", "c", "", fmt.Sprintf("Path to ziti config file. default: $HOME/.ziti/%s.json", f.ServiceName)) - cmd.Flags().StringVarP(&f.SshKeyPath, "SshKeyPath", "i", "", "Path to ssh key. default: $HOME/.ssh/id_rsa") - cmd.Flags().BoolVarP(&f.Debug, "debug", "d", false, "pass to enable additional debug information") - - if f.SshKeyPath == "" { - userHome, err := os.UserHomeDir() - if err != nil { - logrus.Fatalf("could not find UserHomeDir? %v", err) - } - f.SshKeyPath = filepath.Join(userHome, SSH_DIR, ID_RSA) - } - f.DebugLog(" flags.SshKeyPath set to: %s", f.SshKeyPath) - - if f.ZConfig == "" { - userHome, err := os.UserHomeDir() - if err != nil { - logrus.Fatalf("could not find UserHomeDir? %v", err) - } - f.ZConfig = filepath.Join(userHome, ".ziti", fmt.Sprintf("%s.json", exeName)) - } - f.DebugLog(" ZConfig set to: %s", f.ZConfig) -} diff --git a/zsshlib/ssh.go.back b/zsshlib/ssh.go.back deleted file mode 100644 index 635e36d..0000000 --- a/zsshlib/ssh.go.back +++ /dev/null @@ -1,408 +0,0 @@ -/* - Copyright NetFoundry, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package zsshlib - -import ( - "context" - "fmt" - "github.com/google/uuid" - "github.com/gorilla/securecookie" - "github.com/zitadel/oidc/v2/pkg/client/rp/cli" - "github.com/zitadel/oidc/v2/pkg/oidc" - "io" - "io/ioutil" - "net" - "os" - "path/filepath" - "strconv" - "strings" - "sync" - "time" - - "github.com/openziti/sdk-golang/ziti" - "github.com/pkg/errors" - "github.com/pkg/sftp" - "github.com/zitadel/oidc/v2/pkg/client/rp" - httphelper "github.com/zitadel/oidc/v2/pkg/http" - "golang.org/x/oauth2" - - "github.com/sirupsen/logrus" - "golang.org/x/crypto/ssh" - "golang.org/x/crypto/ssh/terminal" -) - -const ( - ID_RSA = "id_rsa" - SSH_DIR = ".ssh" -) - -var ( - // OktaAuthScope is the Okta authorization scope(s). - OktaAuthScope = "openid profile" - //OktaAuthScope = "okta.users.read.self openid profile" - - ErrTokenIsNil = errors.New("ID token is nil") -) - -func RemoteShell(client *ssh.Client) error { - session, err := client.NewSession() - if err != nil { - return err - } - - stdInFd := int(os.Stdin.Fd()) - stdOutFd := int(os.Stdout.Fd()) - - oldState, err := terminal.MakeRaw(stdInFd) - if err != nil { - logrus.Fatal(err) - } - defer func() { - _ = session.Close() - _ = terminal.Restore(stdInFd, oldState) - }() - - session.Stdout = os.Stdout - session.Stderr = os.Stderr - session.Stdin = os.Stdin - - termWidth, termHeight, err := terminal.GetSize(stdOutFd) - if err != nil { - logrus.Fatal(err) - } - - fmt.Println("connected.") - - if err := session.RequestPty("xterm", termHeight, termWidth, ssh.TerminalModes{ssh.ECHO: 1}); err != nil { - return err - } - - err = session.Shell() - if err != nil { - return err - } - session.Wait() - return nil -} - -func Dial(config *ssh.ClientConfig, conn net.Conn) (*ssh.Client, error) { - c, chans, reqs, err := ssh.NewClientConn(conn, "", config) - if err != nil { - return nil, err - } - return ssh.NewClient(c, chans, reqs), nil -} - -// Config represents a config for the OIDC auth flow. -type Config struct { - // CallbackPath is the path of the callback handler. - CallbackPath string - - // CallbackPort is the port of the callback handler. - CallbackPort string - - // Issuer is the URL of the OpenID Connect provider. - Issuer string - - // HashKey is used to authenticate values using HMAC. - HashKey []byte - - // BlockKey is used to encrypt values using AES. - BlockKey []byte - - // IDToken is the ID token returned by the OIDC provider. - IDToken string - - // Logger function for debug. - Logf func(format string, args ...interface{}) - - oauth2.Config -} - -// GetToken starts a local HTTP server, opens the web browser to initiate the OIDC Discovery and -// Token Exchange flow, blocks until the user completes authentication and is redirected back, and returns -// the OIDC tokens. -func GetToken(ctx context.Context, config *Config) (string, error) { - if err := config.validateAndSetDefaults(); err != nil { - return "", fmt.Errorf("invalid config: %w", err) - } - - cookieHandler := httphelper.NewCookieHandler(config.HashKey, config.BlockKey, httphelper.WithUnsecure()) - - options := []rp.Option{ - rp.WithCookieHandler(cookieHandler), - rp.WithVerifierOpts(rp.WithIssuedAtOffset(5 * time.Second)), - } - if config.ClientSecret == "" { - options = append(options, rp.WithPKCE(cookieHandler)) - } - - relyingParty, err := rp.NewRelyingPartyOIDC(config.Issuer, config.ClientID, config.ClientSecret, config.RedirectURL, config.Scopes, options...) - if err != nil { - logrus.Fatalf("error creating relyingParty %s", err.Error()) - } - - //ctx := context.Background() - state := func() string { - return uuid.New().String() - } - - tokens := cli.CodeFlow[*oidc.IDTokenClaims](ctx, relyingParty, config.CallbackPath, config.CallbackPort, state) - - return tokens.IDToken, nil - //return "", nil -} - -// validateAndSetDefaults validates the config and sets default values. -func (c *Config) validateAndSetDefaults() error { - if c.ClientID == "" { - return fmt.Errorf("ClientID must be set") - } - - c.HashKey = securecookie.GenerateRandomKey(32) - c.BlockKey = securecookie.GenerateRandomKey(32) - - if c.Logf == nil { - c.Logf = func(string, ...interface{}) {} - } - - c.Scopes = strings.Split(OktaAuthScope, " ") - - return nil -} - -type SshConfigFactory interface { - Address() string - Hostname() string - Port() int - User() string - Config() *ssh.ClientConfig - KeyPath() string -} - -type SshConfigFactoryImpl struct { - user string - host string - port int - keyPath string - resolveAuthOnce sync.Once - authMethods []ssh.AuthMethod -} - -func NewSshConfigFactoryImpl(user string, keyPath string) *SshConfigFactoryImpl { - factory := &SshConfigFactoryImpl{ - user: user, - host: "", - port: 22, - keyPath: keyPath, - } - return factory -} - -func (factory *SshConfigFactoryImpl) User() string { - return factory.user -} -func (factory *SshConfigFactoryImpl) Hostname() string { - return factory.host -} - -func (factory *SshConfigFactoryImpl) Port() int { - return factory.port -} - -func (factory *SshConfigFactoryImpl) KeyPath() string { - return factory.keyPath -} - -func (factory *SshConfigFactoryImpl) Address() string { - return factory.host + ":" + strconv.Itoa(factory.port) -} - -func (factory *SshConfigFactoryImpl) Config() *ssh.ClientConfig { - factory.resolveAuthOnce.Do(func() { - var methods []ssh.AuthMethod - - if fileMethod, err := sshAuthMethodFromFile(factory.keyPath); err == nil { - methods = append(methods, fileMethod) - } else { - logrus.Error(err) - } - - if agentMethod := sshAuthMethodAgent(); agentMethod != nil { - methods = append(methods, sshAuthMethodAgent()) - } - - methods = append(methods) - - factory.authMethods = methods - }) - - return &ssh.ClientConfig{ - User: factory.user, - Auth: factory.authMethods, - HostKeyCallback: ssh.InsecureIgnoreHostKey(), - } -} - -func sshAuthMethodFromFile(keyPath string) (ssh.AuthMethod, error) { - content, err := ioutil.ReadFile(keyPath) - if err != nil { - return nil, fmt.Errorf("could not read zssh file [%s]: %w", keyPath, err) - } - - if signer, err := ssh.ParsePrivateKey(content); err == nil { - return ssh.PublicKeys(signer), nil - } else { - if err.Error() == "zssh: no key found" { - return nil, fmt.Errorf("no private key found in [%s]: %w", keyPath, err) - } else if err.(*ssh.PassphraseMissingError) != nil { - return nil, fmt.Errorf("file is password protected [%s] %w", keyPath, err) - } else { - return nil, fmt.Errorf("error parsing private key from [%s]L %w", keyPath, err) - } - } -} - -func SendFile(client *sftp.Client, localPath string, remotePath string) error { - localFile, err := ioutil.ReadFile(localPath) - - if err != nil { - return errors.Wrapf(err, "unable to read local file %v", localFile) - } - - rmtFile, err := client.OpenFile(remotePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC) - - if err != nil { - return errors.Wrapf(err, "unable to open remote file %v", remotePath) - } - defer rmtFile.Close() - - _, err = rmtFile.Write(localFile) - if err != nil { - return err - } - - return nil -} - -func RetrieveRemoteFiles(client *sftp.Client, localPath string, remotePath string) error { - - rf, err := client.Open(remotePath) - if err != nil { - return fmt.Errorf("error opening remote file [%s] (%w)", remotePath, err) - } - defer func() { _ = rf.Close() }() - - lf, err := os.OpenFile(localPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.ModePerm) - if err != nil { - return fmt.Errorf("error opening local file [%s] (%w)", localPath, err) - } - defer func() { _ = lf.Close() }() - - _, err = io.Copy(lf, rf) - if err != nil { - return fmt.Errorf("error copying remote file to local [%s] (%w)", remotePath, err) - } - logrus.Infof("%s => %s", remotePath, localPath) - - return nil -} - -func EstablishClient(f SshFlags, userName, targetIdentity, token string) *ssh.Client { - //credentials := edge_apis.NewJwtCredentials(token) - cfg, err := getConfig(f.ZConfig) - if err != nil { - logrus.Errorf("config file not found. using defaults") - /* - cfg = &ziti.Config{ - ZtAPI: "https://ec2-3-134-108-218.us-east-2.compute.amazonaws.com:8441/edge/client/v1", - Credentials: credentials, - } - */ - } - /* - caPool, caErr := ziti.GetControllerWellKnownCaPool("https://ec2-3-134-108-218.us-east-2.compute.amazonaws.com:8441") - if caErr != nil { - panic(caErr) - } - credentials.CaPool = caPool - */ - cfg.ConfigTypes = append(cfg.ConfigTypes, "all") - ctx, err := ziti.NewContext(cfg) - - if err = ctx.Authenticate(); err != nil { - panic(err) - } - - _, ok := ctx.GetService(f.ServiceName) - if !ok { - logrus.Fatalf("service not found: %s", f.ServiceName) - } - - dialOptions := &ziti.DialOptions{ - ConnectTimeout: 0, - Identity: targetIdentity, - AppData: nil, - } - svc, err := ctx.DialWithOptions(f.ServiceName, dialOptions) - - if err != nil { - logrus.Fatalf("error when dialing service name %s. %v", f.ServiceName, err) - } - - factory := NewSshConfigFactoryImpl(userName, f.SshKeyPath) - config := factory.Config() - sshConn, err := Dial(config, svc) - if err != nil { - logrus.Fatalf("error dialing SSH Conn: %v", err) - } - return sshConn -} - -func (f *SshFlags) DebugLog(msg string, args ...interface{}) { - if f.Debug { - logrus.Infof(msg, args...) - } -} - -func getConfig(cfgFile string) (*ziti.Config, error) { - if _, err := os.Stat(cfgFile); err != nil { - // create default - return nil, nil - } - zitiCfg, err := ziti.NewConfigFromFile(cfgFile) - if err != nil { - return nil, errors.New(fmt.Sprintf("failed to load ziti configuration file: %v", cfgFile)) - } - return zitiCfg, nil -} - -// AppendBaseName tags file name on back of remotePath if the path is blank or a directory/* -func AppendBaseName(c *sftp.Client, remotePath string, localPath string, debug bool) string { - localPath = filepath.Base(localPath) - if remotePath == "" { - remotePath = filepath.Base(localPath) - } else { - info, err := c.Lstat(remotePath) - if err == nil && info.IsDir() { - remotePath = filepath.Join(remotePath, localPath) - } else if debug { - logrus.Infof("Remote File/Directory: %s doesn't exist [%v]", remotePath, err) - } - } - return remotePath -}