diff --git a/cmd/climc/shell/compute/hosts.go b/cmd/climc/shell/compute/hosts.go index ee8203b1ae0..1d4835aa73f 100644 --- a/cmd/climc/shell/compute/hosts.go +++ b/cmd/climc/shell/compute/hosts.go @@ -71,11 +71,7 @@ func init() { cmd.Get("tap-config", &options.BaseIdOptions{}) R(&options.BaseIdOptions{}, "host-logininfo", "Get SSH login information of a host", func(s *mcclient.ClientSession, args *options.BaseIdOptions) error { - srvid, e := modules.Hosts.GetId(s, args.ID, nil) - if e != nil { - return e - } - i, e := modules.Hosts.GetLoginInfo(s, srvid, nil) + i, e := modules.Hosts.PerformAction(s, args.ID, "login_info", nil) if e != nil { return e } @@ -576,11 +572,7 @@ func init() { Port int `help:"SSH service port" default:"22"` } R(&HostSSHLoginOptions{}, "host-ssh", "SSH login of a host", func(s *mcclient.ClientSession, args *HostSSHLoginOptions) error { - srvid, e := modules.Hosts.GetId(s, args.ID, nil) - if e != nil { - return e - } - i, e := modules.Hosts.GetLoginInfo(s, srvid, nil) + i, e := modules.Hosts.PerformAction(s, args.ID, "login_info", nil) privateKey := "" if e != nil { if httputils.ErrorCode(e) == 404 || e.Error() == "ciphertext too short" { diff --git a/cmd/climc/shell/compute/servers.go b/cmd/climc/shell/compute/servers.go index 66df257e91d..a86e5b51c18 100644 --- a/cmd/climc/shell/compute/servers.go +++ b/cmd/climc/shell/compute/servers.go @@ -301,11 +301,6 @@ func init() { }) R(&options.ServerLoginInfoOptions{}, "server-logininfo", "Get login info of a server", func(s *mcclient.ClientSession, opts *options.ServerLoginInfoOptions) error { - srvid, e := modules.Servers.GetId(s, opts.ID, nil) - if e != nil { - return e - } - params := jsonutils.NewDict() if len(opts.Key) > 0 { privateKey, e := ioutil.ReadFile(opts.Key) @@ -315,7 +310,7 @@ func init() { params.Add(jsonutils.NewString(string(privateKey)), "private_key") } - i, e := modules.Servers.GetLoginInfo(s, srvid, params) + i, e := modules.Servers.PerformAction(s, opts.ID, "login_info", params) if e != nil { return e } @@ -867,7 +862,7 @@ func init() { privateKey = string(key) } - i, e := modules.Servers.GetLoginInfo(s, srvid, params) + i, e := modules.Servers.PerformAction(s, srvid, "login_info", params) if e != nil { return e } diff --git a/pkg/apis/compute/guests.go b/pkg/apis/compute/guests.go index 2c366dacd64..8d438034d38 100644 --- a/pkg/apis/compute/guests.go +++ b/pkg/apis/compute/guests.go @@ -1083,3 +1083,15 @@ type NetworkAddrConf struct { Masklen int `json:"masklen"` Gateway string `json:"gateway"` } + +type ServerLoginInfoInput struct { + PrivateKey string `json:"private_key"` +} + +type ServerLoginInfoOutput struct { + Username string `json:"username"` + Updated string `json:"updated"` + LoginKey string `json:"login_key"` + Keypair string `json:"keypair"` + Password string `json:"password"` +} diff --git a/pkg/apis/compute/host.go b/pkg/apis/compute/host.go index ecd85b1550b..313cbae0f80 100644 --- a/pkg/apis/compute/host.go +++ b/pkg/apis/compute/host.go @@ -615,3 +615,12 @@ type HostError struct { type HostSyncErrorsInput struct { HostErrors []HostError } + +type HostLoginInfoInput struct { +} + +type HostLoginInfoOutput struct { + Ip string `json:"ip"` + Username string `json:"username"` + Password string `json:"password"` +} diff --git a/pkg/compute/models/guest_logininfo.go b/pkg/compute/models/guest_logininfo.go new file mode 100644 index 00000000000..4e407c23330 --- /dev/null +++ b/pkg/compute/models/guest_logininfo.go @@ -0,0 +1,97 @@ +// 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" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/utils" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/httperrors" + "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/util/seclib2" +) + +func (guest *SGuest) PerformLoginInfo( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input api.ServerLoginInfoInput, +) (*api.ServerLoginInfoOutput, error) { + metadata, err := guest.GetAllMetadata(ctx, userCred) + if err != nil { + return nil, errors.Wrap(err, "GetAllMetadata") + } + output := &api.ServerLoginInfoOutput{} + output.Username = metadata["login_account"] + output.Updated = metadata["login_key_timestamp"] + output.LoginKey = metadata["login_key"] + + if len(output.LoginKey) > 0 { + var passwd string + keypair := guest.getKeypair() + if keypair != nil { + if len(input.PrivateKey) > 0 { + passwd, err = seclib2.DecryptBase64(input.PrivateKey, output.LoginKey) + if err != nil { + return nil, errors.Wrap(err, "DecryptBase64") + } + } else { + return nil, errors.Wrap(httperrors.ErrInputParameter, "empty private key") + } + } else { + passwd, err = utils.DescryptAESBase64(guest.Id, output.LoginKey) + if err != nil { + return nil, errors.Wrap(err, "DescryptAESBase64") + } + } + output.Password = passwd + } + + return output, nil +} + +func (host *SHost) PerformLoginInfo( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input api.HostLoginInfoInput, +) (*api.HostLoginInfoOutput, error) { + metadata, err := host.GetAllMetadata(ctx, userCred) + if err != nil { + return nil, errors.Wrap(err, "GetAllMetadata") + } + + login_key := metadata["password"] + // decrypt twice + passwd, err := utils.DescryptAESBase64(host.Id, login_key) + if err != nil { + return nil, errors.Wrap(err, "DescryptAESBase64") + } + passwd, err = utils.DescryptAESBase64(host.Id, passwd) + if err != nil { + return nil, errors.Wrap(err, "DescryptAESBase64 twice") + } + + ret := &api.HostLoginInfoOutput{} + ret.Password = passwd + ret.Username = metadata["username"] + ret.Ip = metadata["ip"] + + return ret, nil +}