From e4662b77f1e042a55422164f5b008efc6a233347 Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Fri, 2 Apr 2021 15:33:19 +0800 Subject: [PATCH 1/9] util: ssh: add ConnectContext method for ClientConfig --- pkg/util/ssh/ssh.go | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/pkg/util/ssh/ssh.go b/pkg/util/ssh/ssh.go index 438478030d..a5ccd01848 100644 --- a/pkg/util/ssh/ssh.go +++ b/pkg/util/ssh/ssh.go @@ -16,8 +16,10 @@ package ssh import ( "bytes" + "context" "fmt" "io" + "net" "os" "strings" "time" @@ -29,6 +31,12 @@ import ( "yunion.io/x/pkg/errors" ) +const ( + ErrBadConfig = errors.Error("bad config") + ErrNetwork = errors.Error("network error") + ErrProtocol = errors.Error("ssh protocol error") +) + type ClientConfig struct { Username string Password string @@ -54,7 +62,7 @@ func (conf ClientConfig) ToSshConfig() (*ssh.ClientConfig, error) { if conf.PrivateKey != "" { signer, err := parsePrivateKey(conf.PrivateKey) if err != nil { - return nil, err + return nil, errors.Wrapf(ErrBadConfig, "parse private key: %v", err) } auths = append(auths, ssh.PublicKeys(signer)) } @@ -75,6 +83,29 @@ func (conf ClientConfig) Connect() (*ssh.Client, error) { return client, nil } +func (conf ClientConfig) ConnectContext(ctx context.Context) (*ssh.Client, error) { + cliConfig, err := conf.ToSshConfig() + if err != nil { + return nil, err + } + + addr := fmt.Sprintf("%s:%d", conf.Host, conf.Port) + d := &net.Dialer{} + netconn, err := d.DialContext(ctx, "tcp", addr) + if err != nil { + return nil, errors.Wrapf(ErrNetwork, "tcp dial: %v", err) + } + + sshconn, chans, reqs, err := ssh.NewClientConn(netconn, addr, cliConfig) + if err != nil { + netconn.Close() + return nil, errors.Wrap(ErrProtocol, err.Error()) + } + + sshc := ssh.NewClient(sshconn, chans, reqs) + return sshc, nil +} + type Client struct { config ClientConfig client *ssh.Client From 6ec27d22b5cc58680a351c3e0ee9522177c7f372 Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Wed, 31 Mar 2021 21:41:49 +0800 Subject: [PATCH 2/9] cloudproxy: apis: add ForwardDetails definition --- pkg/apis/cloudproxy/forwards.go | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/pkg/apis/cloudproxy/forwards.go b/pkg/apis/cloudproxy/forwards.go index c21a1417b6..f78fcf4ffe 100644 --- a/pkg/apis/cloudproxy/forwards.go +++ b/pkg/apis/cloudproxy/forwards.go @@ -15,6 +15,8 @@ package cloudproxy import ( + "time" + "yunion.io/x/onecloud/pkg/apis" ) @@ -38,7 +40,7 @@ type ForwardCreateFromServerInput struct { Type string BindPortReq int `json:",omitzero"` - RemotePort string + RemotePort int `json:",omitzero"` LastSeenTimeout int `json:",omitzero"` } @@ -53,3 +55,23 @@ type ForwardListInput struct { Opaque string } + +type ForwardDetails struct { + ProxyEndpoint string + ProxyEndpointId string + ProxyAgent string + ProxyAgentId string + + Type string + BindPortReq int + BindPort int + RemoteAddr string + RemotePort int + + LastSeen time.Time + LastSeenTimeout int + + Opaque string + + BindAddr string +} From 148481f03edf7da0f5587cfc2c54ecb4a404d7d3 Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Fri, 2 Apr 2021 14:53:25 +0800 Subject: [PATCH 3/9] cloudproxy: forwards: more list filter conditional keys --- pkg/apis/cloudproxy/forwards.go | 5 ++++- pkg/cloudproxy/models/forwards.go | 13 +++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/pkg/apis/cloudproxy/forwards.go b/pkg/apis/cloudproxy/forwards.go index f78fcf4ffe..ded71b9f94 100644 --- a/pkg/apis/cloudproxy/forwards.go +++ b/pkg/apis/cloudproxy/forwards.go @@ -51,7 +51,10 @@ type ForwardListInput struct { ProxyAgentId string ProxyEndpointId string - Type string + Type string + RemoteAddr string + RemotePort *int + BindPortReq *int Opaque string } diff --git a/pkg/cloudproxy/models/forwards.go b/pkg/cloudproxy/models/forwards.go index 64cc8aa78b..5949c57a36 100644 --- a/pkg/cloudproxy/models/forwards.go +++ b/pkg/cloudproxy/models/forwards.go @@ -358,6 +358,7 @@ func (man *SForwardManager) ListItemFilter( ) (*sqlchemy.SQuery, error) { filters := [][2]string{ [2]string{"type", input.Type}, + [2]string{"remote_addr", input.RemoteAddr}, [2]string{"proxy_endpoint_id", input.ProxyEndpointId}, [2]string{"proxy_agent_id", input.ProxyAgentId}, [2]string{"opaque", input.Opaque}, @@ -367,6 +368,18 @@ func (man *SForwardManager) ListItemFilter( q = q.Equals(filter[0], v) } } + intFilters := []struct { + name string + val *int + }{ + {"remote_port", input.RemotePort}, + {"bind_port_req", input.BindPortReq}, + } + for _, filter := range intFilters { + if v := filter.val; v != nil { + q = q.Equals(filter.name, *v) + } + } return q, nil } From 4b01916e64b86705193c1eaf10fb79c2ef7c7c22 Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Fri, 2 Apr 2021 15:37:04 +0800 Subject: [PATCH 4/9] cloudproxy: agent: use yunion.io/x/onecloud/pkg/util/ssh --- pkg/cloudproxy/agent/ssh/client.go | 8 +-- pkg/cloudproxy/agent/ssh/client_config.go | 62 ----------------------- pkg/cloudproxy/agent/ssh/clientset.go | 8 +-- pkg/cloudproxy/agent/worker/worker.go | 11 ++-- 4 files changed, 16 insertions(+), 73 deletions(-) delete mode 100644 pkg/cloudproxy/agent/ssh/client_config.go diff --git a/pkg/cloudproxy/agent/ssh/client.go b/pkg/cloudproxy/agent/ssh/client.go index 1a03dfed8f..c4d2675e79 100644 --- a/pkg/cloudproxy/agent/ssh/client.go +++ b/pkg/cloudproxy/agent/ssh/client.go @@ -26,6 +26,8 @@ import ( "yunion.io/x/log" "yunion.io/x/pkg/errors" "yunion.io/x/pkg/util/sets" + + ssh_util "yunion.io/x/onecloud/pkg/util/ssh" ) type addrMap map[string]interface{} @@ -93,7 +95,7 @@ func (am addrMap) delete(addr string) { } type Client struct { - cc *ClientConfig + cc *ssh_util.ClientConfig c *ssh.Client stopc chan sets.Empty @@ -111,7 +113,7 @@ type Client struct { remoteForwards portMap } -func NewClient(cc *ClientConfig) *Client { +func NewClient(cc *ssh_util.ClientConfig) *Client { c := &Client{ cc: cc, @@ -214,7 +216,7 @@ func (c *Client) Start(ctx context.Context) { } func (c *Client) connect(ctx context.Context) (*ssh.Client, error) { - sshc, err := c.cc.NewClient(ctx) + sshc, err := c.cc.ConnectContext(ctx) return sshc, err } diff --git a/pkg/cloudproxy/agent/ssh/client_config.go b/pkg/cloudproxy/agent/ssh/client_config.go deleted file mode 100644 index c5f8c9b691..0000000000 --- a/pkg/cloudproxy/agent/ssh/client_config.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2019 Yunion -// -// 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 -// -// http://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 ssh - -import ( - "context" - "fmt" - "net" - - "golang.org/x/crypto/ssh" - - "yunion.io/x/pkg/errors" -) - -type ClientConfig struct { - User string - Host string - Port int - Key string -} - -func (cc *ClientConfig) NewClient(ctx context.Context) (*ssh.Client, error) { - signer, err := ssh.ParsePrivateKey([]byte(cc.Key)) - if err != nil { - return nil, errors.Wrap(err, "parse ssh key") - } - sshcc := &ssh.ClientConfig{ - User: cc.User, - Auth: []ssh.AuthMethod{ - ssh.PublicKeys(signer), - }, - HostKeyCallback: ssh.InsecureIgnoreHostKey(), - } - - addr := net.JoinHostPort(cc.Host, fmt.Sprintf("%d", cc.Port)) - d := &net.Dialer{} - netconn, err := d.DialContext(ctx, "tcp", addr) - if err != nil { - return nil, errors.Wrap(err, "net dial") - } - - sshconn, chans, reqs, err := ssh.NewClientConn(netconn, addr, sshcc) - if err != nil { - netconn.Close() - return nil, errors.Wrap(err, "ssh new client conn") - } - - sshc := ssh.NewClient(sshconn, chans, reqs) - return sshc, nil -} diff --git a/pkg/cloudproxy/agent/ssh/clientset.go b/pkg/cloudproxy/agent/ssh/clientset.go index 05a75de780..9cb04742b7 100644 --- a/pkg/cloudproxy/agent/ssh/clientset.go +++ b/pkg/cloudproxy/agent/ssh/clientset.go @@ -16,10 +16,12 @@ package ssh import ( "context" + + ssh_util "yunion.io/x/onecloud/pkg/util/ssh" ) type epClientSet struct { - cc ClientConfig + cc ssh_util.ClientConfig clients []*Client mark bool @@ -62,7 +64,7 @@ func (cs *ClientSet) ClearAllMark() { } } -func (cs *ClientSet) ResetIfChanged(ctx context.Context, epKey string, cc ClientConfig) bool { +func (cs *ClientSet) ResetIfChanged(ctx context.Context, epKey string, cc ssh_util.ClientConfig) bool { epcs, ok := cs.epClients[epKey] if ok { if epcs.cc != cc { @@ -75,7 +77,7 @@ func (cs *ClientSet) ResetIfChanged(ctx context.Context, epKey string, cc Client return false } -func (cs *ClientSet) AddIfNotExist(ctx context.Context, epKey string, cc ClientConfig) bool { +func (cs *ClientSet) AddIfNotExist(ctx context.Context, epKey string, cc ssh_util.ClientConfig) bool { epcs, ok := cs.epClients[epKey] if !ok { epcs := &epClientSet{ diff --git a/pkg/cloudproxy/agent/worker/worker.go b/pkg/cloudproxy/agent/worker/worker.go index 7a728110df..9ef20731a1 100644 --- a/pkg/cloudproxy/agent/worker/worker.go +++ b/pkg/cloudproxy/agent/worker/worker.go @@ -36,6 +36,7 @@ import ( "yunion.io/x/onecloud/pkg/mcclient/auth" cloudproxy_modules "yunion.io/x/onecloud/pkg/mcclient/modules/cloudproxy" "yunion.io/x/onecloud/pkg/util/netutils2" + ssh_util "yunion.io/x/onecloud/pkg/util/ssh" ) type Worker struct { @@ -209,11 +210,11 @@ func (w *Worker) run(ctx context.Context, mss *agentmodels.ModelSets) (err error w.clientSet.ClearAllMark() for _, pep := range mss.ProxyEndpoints { - cc := agentssh.ClientConfig{ - User: pep.User, - Host: pep.Host, - Port: pep.Port, - Key: pep.PrivateKey, + cc := ssh_util.ClientConfig{ + Username: pep.User, + Host: pep.Host, + Port: pep.Port, + PrivateKey: pep.PrivateKey, } if reset := w.clientSet.ResetIfChanged(ctx, pep.Id, cc); reset { log.Warningf("proxy endpoint %s changed, connections reset", pep.Id) From ba7fe6f544c947a146f4747b02348bd9b544cb57 Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Fri, 2 Apr 2021 17:26:59 +0800 Subject: [PATCH 5/9] cloudproxy: agent: fix possible invalid memory access --- pkg/cloudproxy/agent/ssh/clientset.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/cloudproxy/agent/ssh/clientset.go b/pkg/cloudproxy/agent/ssh/clientset.go index 9cb04742b7..4253a799c2 100644 --- a/pkg/cloudproxy/agent/ssh/clientset.go +++ b/pkg/cloudproxy/agent/ssh/clientset.go @@ -70,6 +70,7 @@ func (cs *ClientSet) ResetIfChanged(ctx context.Context, epKey string, cc ssh_ut if epcs.cc != cc { epcs.stop(ctx) delete(cs.epClients, epKey) + cs.AddIfNotExist(ctx, epKey, cc) return true } epcs.setMark() @@ -172,7 +173,7 @@ func (cs *ClientSet) getClient_(epKey string, typ string, create bool) (*Client, clients, ok := cs.epClients[epKey] if !ok || len(clients.clients) == 0 { - if !create { + if !ok || !create { return nil, false } client = NewClient(&clients.cc) From 8701114f781d536c1a996789d8ccc9083dd86c5b Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Fri, 2 Apr 2021 17:50:37 +0800 Subject: [PATCH 6/9] cloudproxy: add metadata model --- pkg/cloudproxy/service/handlers.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/cloudproxy/service/handlers.go b/pkg/cloudproxy/service/handlers.go index e1ac99a3a9..0104919860 100644 --- a/pkg/cloudproxy/service/handlers.go +++ b/pkg/cloudproxy/service/handlers.go @@ -25,6 +25,7 @@ func InitHandlers(app *appsrv.Application) { db.InitAllManagers() db.RegisterModelManager(db.OpsLog) + db.RegisterModelManager(db.Metadata) db.RegisterModelManager(db.TenantCacheManager) db.RegisterModelManager(db.UserCacheManager) for _, manager := range []db.IModelManager{ From ed061d082133609eea347ba5e2278d85e4b3d38d Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Fri, 2 Apr 2021 18:11:57 +0800 Subject: [PATCH 7/9] cloudproxy: agent: ssh: cancel on return from Start --- pkg/cloudproxy/agent/ssh/client.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/cloudproxy/agent/ssh/client.go b/pkg/cloudproxy/agent/ssh/client.go index c4d2675e79..e6ee658377 100644 --- a/pkg/cloudproxy/agent/ssh/client.go +++ b/pkg/cloudproxy/agent/ssh/client.go @@ -143,6 +143,9 @@ func (c *Client) Stop(ctx context.Context) { } func (c *Client) Start(ctx context.Context) { + ctx, cancelFunc := context.WithCancel(ctx) + defer cancelFunc() + pingT := time.NewTimer(17 * time.Second) pingFailCount := 0 const pingMaxFail = 3 From 05545a0e454af43825b009803254c838bc4378e2 Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Wed, 31 Mar 2021 11:05:32 +0800 Subject: [PATCH 8/9] region: guests: add GET /servers//sshable --- pkg/apis/compute/guest_sshable.go | 38 ++++ pkg/compute/models/guest_sshable.go | 299 ++++++++++++++++++++++++++++ 2 files changed, 337 insertions(+) create mode 100644 pkg/apis/compute/guest_sshable.go create mode 100644 pkg/compute/models/guest_sshable.go diff --git a/pkg/apis/compute/guest_sshable.go b/pkg/apis/compute/guest_sshable.go new file mode 100644 index 0000000000..bf36ba0152 --- /dev/null +++ b/pkg/apis/compute/guest_sshable.go @@ -0,0 +1,38 @@ +// Copyright 2019 Yunion +// +// 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 +// +// http://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 compute + +const ( + MethodDirect = "direct" + MethodEIP = "eip" + MethodDNAT = "dnat" + MethodProxyForward = "proxy_forward" +) + +type GuestSshableMethodData struct { + Method string + Host string + Port int + + Sshable bool + Reason string +} + +type GuestSshableOutput struct { + User string + PublicKey string + + MethodTried []GuestSshableMethodData +} diff --git a/pkg/compute/models/guest_sshable.go b/pkg/compute/models/guest_sshable.go new file mode 100644 index 0000000000..bed889bcae --- /dev/null +++ b/pkg/compute/models/guest_sshable.go @@ -0,0 +1,299 @@ +// Copyright 2019 Yunion +// +// 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 +// +// http://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 models + +import ( + "context" + "time" + + "yunion.io/x/jsonutils" + "yunion.io/x/log" + "yunion.io/x/sqlchemy" + + cloudproxy_api "yunion.io/x/onecloud/pkg/apis/cloudproxy" + compute_api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudcommon/db" + "yunion.io/x/onecloud/pkg/compute/sshkeys" + "yunion.io/x/onecloud/pkg/httperrors" + "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/mcclient/auth" + cloudproxy_module "yunion.io/x/onecloud/pkg/mcclient/modules/cloudproxy" + ssh_util "yunion.io/x/onecloud/pkg/util/ssh" +) + +type GuestSshableTryData struct { + User string + Host string + Port int + PrivateKey string + PublicKey string + + MethodTried []compute_api.GuestSshableMethodData +} + +func (tryData *GuestSshableTryData) AddMethodTried(tryMethodData compute_api.GuestSshableMethodData) { + tryData.MethodTried = append(tryData.MethodTried, tryMethodData) +} + +func (tryData *GuestSshableTryData) outputJSON() jsonutils.JSONObject { + out := compute_api.GuestSshableOutput{ + User: tryData.User, + PublicKey: tryData.PublicKey, + + MethodTried: tryData.MethodTried, + } + outJSON := jsonutils.Marshal(out) + return outJSON +} + +func (guest *SGuest) AllowGetDetailsSshable( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, +) bool { + return db.IsProjectAllowGetSpec(userCred, guest, "sshable") +} + +func (guest *SGuest) GetDetailsSshable( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, +) (jsonutils.JSONObject, error) { + tryData := &GuestSshableTryData{ + User: "cloudroot", + } + + // - get admin key + privateKey, publicKey, err := sshkeys.GetSshAdminKeypair(ctx) + if err != nil { + return nil, httperrors.NewInternalServerError("fetch ssh private key: %v", err) + } + tryData.PrivateKey = privateKey + tryData.PublicKey = publicKey + + gns, err := guest.GetNetworks("") + if err != nil { + return nil, httperrors.NewInternalServerError("fetch network interface information: %v", err) + } + type gnInfo struct { + guestNetwork *SGuestnetwork + network *SNetwork + vpc *SVpc + } + var gnInfos []gnInfo + for i := range gns { + gn := &gns[i] + network := gn.GetNetwork() + if network == nil { + continue + } + vpc := network.GetVpc() + if vpc == nil { + continue + } + if vpc.Id == compute_api.DEFAULT_VPC_ID { + // - vpc_id == "default" + if ok := guest.sshableTryDefaultVPC(ctx, tryData, gn); ok { + return tryData.outputJSON(), nil + } + } else { + gnInfos = append(gnInfos, gnInfo{ + guestNetwork: gn, + network: network, + vpc: vpc, + }) + } + } + + // - check eip + if eip, err := guest.GetEipOrPublicIp(); err == nil && eip != nil { + if ok := guest.sshableTryEip(ctx, tryData, eip); ok { + return tryData.outputJSON(), nil + } + } + + sess := auth.GetSession(ctx, userCred, "", "") + // - check existing proxy forward + proxyforwardTried := false + for i := range gnInfos { + gnInfo := &gnInfos[i] + gn := gnInfo.guestNetwork + port := 22 + input := &cloudproxy_api.ForwardListInput{ + Type: cloudproxy_api.FORWARD_TYPE_LOCAL, + RemoteAddr: gn.IpAddr, + RemotePort: &port, + Opaque: guest.Id, + } + params := jsonutils.Marshal(input).(*jsonutils.JSONDict) + params.Set("details", jsonutils.JSONTrue) + res, err := cloudproxy_module.Forwards.List(sess, params) + if err != nil { + log.Warningf("list cloudproxy forwards: %v", err) + continue + } + proxyforwardTried = len(res.Data) != 0 + for _, data := range res.Data { + var fwd cloudproxy_api.ForwardDetails + if err := data.Unmarshal(&fwd); err != nil { + log.Warningf("unmarshal cloudproxy forward list data: %v", err) + continue + } + if ok := guest.sshableTryForward(ctx, tryData, &fwd); ok { + return tryData.outputJSON(), nil + } + } + } + if !proxyforwardTried { + // - create and use new proxy forward + fwdCreateInput := cloudproxy_api.ForwardCreateFromServerInput{ + ServerId: guest.Id, + Type: cloudproxy_api.FORWARD_TYPE_LOCAL, + RemotePort: 22, + } + fwdCreateParams := jsonutils.Marshal(fwdCreateInput) + res, err := cloudproxy_module.Forwards.PerformClassAction(sess, "create-from-server", fwdCreateParams) + if err == nil { + var fwd cloudproxy_api.ForwardDetails + if err := res.Unmarshal(&fwd); err == nil { + if ok := guest.sshableTryForward(ctx, tryData, &fwd); ok { + return tryData.outputJSON(), nil + } + } + } else { + tryData.AddMethodTried(compute_api.GuestSshableMethodData{ + Method: compute_api.MethodProxyForward, + Reason: err.Error(), + }) + } + } + + // - existing dnat rule + for i := range gnInfos { + gnInfo := &gnInfos[i] + gn := gnInfo.guestNetwork + vpc := gnInfo.vpc + + natgwq := NatGatewayManager.Query().SubQuery() + q := NatDEntryManager.Query(). + Equals("internal_ip", gn.IpAddr). + Equals("internal_port", 22). + Equals("ip_protocol", "tcp") + q = q.Join(natgwq, sqlchemy.AND( + sqlchemy.In(natgwq.Field("vpc_id"), vpc.Id), + sqlchemy.Equals(natgwq.Field("id"), q.Field("natgateway_id")), + )) + + var dnats []SNatDEntry + if err := db.FetchModelObjects(NatDEntryManager, q, &dnats); err != nil { + log.Warningf("query dnat to ssh service: %v", err) + continue + } + for j := range dnats { + dnat := &dnats[j] + if ok := guest.sshableTryDnat(ctx, tryData, dnat); ok { + return tryData.outputJSON(), nil + } + } + } + + return tryData.outputJSON(), nil +} + +func (guest *SGuest) sshableTryDnat( + ctx context.Context, + tryData *GuestSshableTryData, + dnat *SNatDEntry, +) bool { + methodData := compute_api.GuestSshableMethodData{ + Method: compute_api.MethodDNAT, + Host: dnat.ExternalIP, + Port: dnat.ExternalPort, + } + return guest.sshableTry( + ctx, tryData, methodData, + ) +} + +func (guest *SGuest) sshableTryForward( + ctx context.Context, + tryData *GuestSshableTryData, + fwd *cloudproxy_api.ForwardDetails, +) bool { + if fwd.BindAddr != "" && fwd.BindPort > 0 { + methodData := compute_api.GuestSshableMethodData{ + Method: compute_api.MethodProxyForward, + Host: fwd.BindAddr, + Port: fwd.BindPort, + } + return guest.sshableTry( + ctx, tryData, methodData, + ) + } + return false +} + +func (guest *SGuest) sshableTryEip( + ctx context.Context, + tryData *GuestSshableTryData, + eip *SElasticip, +) bool { + methodData := compute_api.GuestSshableMethodData{ + Method: compute_api.MethodEIP, + Host: eip.IpAddr, + Port: 22, + } + return guest.sshableTry( + ctx, tryData, methodData, + ) +} + +func (guest *SGuest) sshableTryDefaultVPC( + ctx context.Context, + tryData *GuestSshableTryData, + gn *SGuestnetwork, +) bool { + methodData := compute_api.GuestSshableMethodData{ + Method: compute_api.MethodDirect, + Host: gn.IpAddr, + Port: 22, + } + return guest.sshableTry( + ctx, tryData, methodData, + ) +} + +func (guest *SGuest) sshableTry( + ctx context.Context, + tryData *GuestSshableTryData, + methodData compute_api.GuestSshableMethodData, +) bool { + ctx, _ = context.WithTimeout(ctx, 7*time.Second) + conf := ssh_util.ClientConfig{ + Username: tryData.User, + Host: methodData.Host, + Port: methodData.Port, + PrivateKey: tryData.PrivateKey, + } + ok := false + if client, err := conf.ConnectContext(ctx); err == nil { + defer client.Close() + methodData.Sshable = true + } else { + methodData.Reason = err.Error() + } + tryData.AddMethodTried(methodData) + return ok +} From f9cfee055411f1c385c6526e58b4a406a76b970e Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Fri, 2 Apr 2021 16:01:50 +0800 Subject: [PATCH 9/9] climc: compute: add command server-sshable --- cmd/climc/shell/compute/servers.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/climc/shell/compute/servers.go b/cmd/climc/shell/compute/servers.go index 67fa55e2eb..b874327be7 100644 --- a/cmd/climc/shell/compute/servers.go +++ b/cmd/climc/shell/compute/servers.go @@ -91,6 +91,7 @@ func init() { cmd.Get("status", new(options.ServerIdOptions)) cmd.Get("iso", new(options.ServerIdOptions)) cmd.Get("create-params", new(options.ServerIdOptions)) + cmd.Get("sshable", new(options.ServerIdOptions)) cmd.Get("change-owner-candidate-domains", new(options.ServerChangeOwnerCandidateDomainsOptions)) type ServerTaskShowOptions struct {