mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-10-16 15:07:59 +08:00
fix: upgrade ssh keypair to ed25519 (#25738)
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
This commit is contained in:
@@ -578,11 +578,11 @@ func init() {
|
||||
}
|
||||
R(&HostSSHLoginOptions{}, "host-ssh", "SSH login of a host", func(s *mcclient.ClientSession, args *HostSSHLoginOptions) error {
|
||||
i, e := modules.Hosts.PerformAction(s, args.ID, "login-info", nil)
|
||||
privateKey := ""
|
||||
var privateKeys []string
|
||||
if e != nil {
|
||||
if httputils.ErrorCode(e) == 404 || strings.Contains(e.Error(), "ciphertext too short") {
|
||||
var err error
|
||||
privateKey, err = modules.Sshkeypairs.FetchPrivateKeyBySession(context.Background(), s)
|
||||
privateKeys, err = modules.Sshkeypairs.FetchAdminPrivateKeysBySession(context.Background(), s)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "fetch private key")
|
||||
}
|
||||
@@ -624,10 +624,27 @@ func init() {
|
||||
if args.Port != 22 {
|
||||
port = args.Port
|
||||
}
|
||||
sshCli, err := ssh.NewClient(host, port, user, passwd, privateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
if len(privateKeys) == 0 {
|
||||
privateKeys = append(privateKeys, "")
|
||||
}
|
||||
|
||||
var sshCli *ssh.Client
|
||||
var errs []error
|
||||
for _, privateKey := range privateKeys {
|
||||
cli, err := ssh.NewClient(host, port, user, passwd, privateKey)
|
||||
if err == nil {
|
||||
sshCli = cli
|
||||
break
|
||||
} else {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
if sshCli == nil {
|
||||
return errors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
log.Infof("ssh %s:%d", host, port)
|
||||
if err := sshCli.RunTerminal(); err != nil {
|
||||
return err
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
"k8s.io/apimachinery/pkg/util/errors"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
@@ -826,15 +827,15 @@ func init() {
|
||||
return fmt.Errorf("Not found ip address from server %s", opts.ID)
|
||||
}
|
||||
|
||||
privateKey := ""
|
||||
var privateKeys []string
|
||||
params := jsonutils.NewDict()
|
||||
if len(opts.Key) > 0 {
|
||||
key, e := ioutil.ReadFile(opts.Key)
|
||||
key, e := os.ReadFile(opts.Key)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
params.Add(jsonutils.NewString(string(key)), "private_key")
|
||||
privateKey = string(key)
|
||||
privateKeys = append(privateKeys, string(key))
|
||||
}
|
||||
|
||||
i, e := modules.Servers.PerformAction(s, srvid, "login-info", params)
|
||||
@@ -873,6 +874,7 @@ func init() {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closeForward(s, srvid, forwardItem)
|
||||
host = forwardItem.ProxyAddr
|
||||
port = forwardItem.ProxyPort
|
||||
}
|
||||
@@ -881,7 +883,7 @@ func init() {
|
||||
|
||||
if opts.UseCloudroot {
|
||||
var err error
|
||||
privateKey, err = modules.Sshkeypairs.FetchPrivateKeyBySession(context.Background(), s)
|
||||
privateKeys, err = modules.Sshkeypairs.FetchProjectPrivateKeysBySession(context.Background(), s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -889,30 +891,26 @@ func init() {
|
||||
user = "cloudroot"
|
||||
}
|
||||
|
||||
if len(privateKeys) == 0 {
|
||||
privateKeys = append(privateKeys, "")
|
||||
}
|
||||
|
||||
var sshCli *ssh.Client
|
||||
err = nil
|
||||
for ; sshCli == nil; sshCli, err = ssh.NewClient(host, port, user, passwd, privateKey) {
|
||||
var errs []error
|
||||
for _, privateKey := range privateKeys {
|
||||
cli, err := ssh.NewClient(host, port, user, passwd, privateKey)
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
if opts.Host != "" {
|
||||
return err
|
||||
}
|
||||
if forwardItem != nil {
|
||||
closeForward(s, srvid, forwardItem)
|
||||
return err
|
||||
sshCli = cli
|
||||
break
|
||||
} else {
|
||||
if vpcid != "default" {
|
||||
forwardItem, e = openForward(s, srvid)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
host = forwardItem.ProxyAddr
|
||||
port = forwardItem.ProxyPort
|
||||
}
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
if sshCli == nil {
|
||||
return errors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
log.Infof("ssh %s:%d", host, port)
|
||||
if err := sshCli.RunTerminal(); err != nil {
|
||||
if forwardItem != nil {
|
||||
@@ -921,9 +919,6 @@ func init() {
|
||||
return err
|
||||
}
|
||||
|
||||
if forwardItem != nil {
|
||||
closeForward(s, srvid, forwardItem)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -15,58 +15,49 @@
|
||||
package compute
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/models"
|
||||
modules "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
|
||||
"yunion.io/x/onecloud/pkg/util/procutils"
|
||||
)
|
||||
|
||||
type SshkeypairQueryOptions struct {
|
||||
Project string `help:"get keypair for specific project"`
|
||||
Admin bool `help:"get admin keypair, sysadmin ONLY option"`
|
||||
}
|
||||
|
||||
func getSshKeypair(s *mcclient.ClientSession, args *SshkeypairQueryOptions) ([]models.SshKeypair, error) {
|
||||
ctx := context.Background()
|
||||
if len(args.Project) > 0 {
|
||||
return modules.Sshkeypairs.FetchKeypairsByProject(ctx, s, args.Project)
|
||||
} else if args.Admin {
|
||||
return modules.Sshkeypairs.FetchAdminKeypairsBySession(ctx, s)
|
||||
} else {
|
||||
return modules.Sshkeypairs.FetchProjectKeypairsBySession(ctx, s)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
type SshkeypairQueryOptions struct {
|
||||
Project string `help:"get keypair for specific project"`
|
||||
Admin bool `help:"get admin keypair, sysadmin ONLY option"`
|
||||
}
|
||||
|
||||
getSshKeypair := func(s *mcclient.ClientSession, args *SshkeypairQueryOptions) (string, string, error) {
|
||||
query := jsonutils.NewDict()
|
||||
if args.Admin {
|
||||
query.Add(jsonutils.JSONTrue, "admin")
|
||||
}
|
||||
var keys jsonutils.JSONObject
|
||||
if len(args.Project) == 0 {
|
||||
listResult, err := modules.Sshkeypairs.List(s, query)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
keys = listResult.Data[0]
|
||||
} else {
|
||||
result, err := modules.Sshkeypairs.GetById(s, args.Project, query)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
keys = result
|
||||
}
|
||||
privKey, _ := keys.GetString("private_key")
|
||||
pubKey, _ := keys.GetString("public_key")
|
||||
return privKey, pubKey, nil
|
||||
}
|
||||
|
||||
R(&SshkeypairQueryOptions{}, "sshkeypair-show", "Get ssh keypairs", func(s *mcclient.ClientSession, args *SshkeypairQueryOptions) error {
|
||||
privKey, pubKey, err := getSshKeypair(s, args)
|
||||
keypairs, err := getSshKeypair(s, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Print(privKey)
|
||||
fmt.Print(pubKey)
|
||||
|
||||
if len(keypairs) == 0 {
|
||||
return errors.Wrap(errors.ErrNotFound, "no ssh key found")
|
||||
}
|
||||
for _, keypair := range keypairs {
|
||||
fmt.Print(keypair.PrivateKey)
|
||||
fmt.Print(keypair.PublicKey)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
@@ -75,10 +66,13 @@ func init() {
|
||||
TargetDir string `help:"Target directory to save cloud ssh keypair"`
|
||||
}
|
||||
R(&SshkeypairInjectOptions{}, "sshkeypair-inject", "Inject ssh keypairs to local path", func(s *mcclient.ClientSession, args *SshkeypairInjectOptions) error {
|
||||
_, pubKey, err := getSshKeypair(s, &args.SshkeypairQueryOptions)
|
||||
keypairs, err := getSshKeypair(s, &args.SshkeypairQueryOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(keypairs) == 0 {
|
||||
return errors.Wrap(errors.ErrNotFound, "no ssh key found")
|
||||
}
|
||||
targetDir := args.TargetDir
|
||||
if targetDir == "" {
|
||||
homeDir, err := os.UserHomeDir()
|
||||
@@ -100,7 +94,7 @@ func init() {
|
||||
}
|
||||
oldKeys = string(output)
|
||||
}
|
||||
var MergeAuthorizedKeys = func(oldKeys string, pubKey string) string {
|
||||
var MergeAuthorizedKeys = func(oldKeys string, keyPairs []models.SshKeypair) string {
|
||||
const sshKeySignature = "@yunioncloudpods"
|
||||
var allkeys = make(map[string]string)
|
||||
if len(oldKeys) > 0 {
|
||||
@@ -118,10 +112,9 @@ func init() {
|
||||
}
|
||||
}
|
||||
}
|
||||
candiateKeys := []string{pubKey}
|
||||
for _, k := range candiateKeys {
|
||||
if len(k) > 0 {
|
||||
k = strings.TrimSpace(k)
|
||||
for _, k := range keyPairs {
|
||||
if len(k.PublicKey) > 0 {
|
||||
k := strings.TrimSpace(k.PublicKey)
|
||||
dat := strings.Split(k, " ")
|
||||
if len(dat) > 1 {
|
||||
if _, ok := allkeys[dat[1]]; !ok {
|
||||
@@ -137,7 +130,7 @@ func init() {
|
||||
return strings.Join(keys, "\n") + "\n"
|
||||
}
|
||||
|
||||
newKeys := MergeAuthorizedKeys(oldKeys, pubKey)
|
||||
newKeys := MergeAuthorizedKeys(oldKeys, keypairs)
|
||||
if output, err := procutils.NewCommand(
|
||||
"sh", "-c", fmt.Sprintf("echo '%s' > %s", newKeys, authFile)).Output(); err != nil {
|
||||
return errors.Wrapf(err, "write public keys: %s", output)
|
||||
|
||||
@@ -125,11 +125,11 @@ func (ai *SAnsiblePlaybookInstance) runPlaybook(ctx context.Context, userCred mc
|
||||
ar = obj.(*SAnsiblePlaybookReference)
|
||||
}
|
||||
var (
|
||||
privateKey string
|
||||
err error
|
||||
privateKeys []string
|
||||
err error
|
||||
)
|
||||
if privateKey, err = compute.Sshkeypairs.FetchPrivateKey(ctx, userCred); err != nil {
|
||||
return err
|
||||
if privateKeys, err = compute.Sshkeypairs.FetchProjectPrivateKeys(ctx, userCred); err != nil {
|
||||
return errors.Wrap(err, "unable to fetch private keys")
|
||||
}
|
||||
_, err = db.Update(ai, func() error {
|
||||
ai.StartTime = time.Now()
|
||||
@@ -157,7 +157,7 @@ func (ai *SAnsiblePlaybookInstance) runPlaybook(ctx context.Context, userCred mc
|
||||
}
|
||||
sess := ansiblev2.NewOfflineSession().
|
||||
Inventory(ai.Inventory).
|
||||
PrivateKey(privateKey).
|
||||
PrivateKeys(privateKeys).
|
||||
ConfigYaml(params.YAMLString()).
|
||||
PlaybookPath(ar.PlaybookPath).
|
||||
OutputWriter(&ansiblePlaybookOutputWriter{ai}).
|
||||
|
||||
@@ -276,11 +276,11 @@ func (apb *SAnsiblePlaybook) runPlaybook(ctx context.Context, userCred mcclient.
|
||||
if err := ansible.ValidatePlaybook(pb); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(pb.PrivateKey) == 0 {
|
||||
if k, err := compute.Sshkeypairs.FetchPrivateKey(ctx, userCred); err != nil {
|
||||
return err
|
||||
if len(pb.PrivateKeys) == 0 {
|
||||
if keys, err := compute.Sshkeypairs.FetchProjectPrivateKeys(ctx, userCred); err != nil {
|
||||
return errors.Wrap(err, "unable to fetch private keys")
|
||||
} else {
|
||||
pb.PrivateKey = []byte(k)
|
||||
pb.PrivateKeys = keys
|
||||
}
|
||||
}
|
||||
// init tmpdir clean policy
|
||||
|
||||
@@ -252,9 +252,9 @@ func (apb *SAnsiblePlaybookV2) runPlaybook(ctx context.Context, userCred mcclien
|
||||
// time.Sleep(50 * time.Second)
|
||||
|
||||
var (
|
||||
privateKey string
|
||||
err error
|
||||
files = map[string][]byte{}
|
||||
privateKeys []string
|
||||
err error
|
||||
files = map[string][]byte{}
|
||||
)
|
||||
if apb.Files != "" {
|
||||
obj, err := jsonutils.ParseString(apb.Files)
|
||||
@@ -274,7 +274,7 @@ func (apb *SAnsiblePlaybookV2) runPlaybook(ctx context.Context, userCred mcclien
|
||||
}
|
||||
}
|
||||
// init private key
|
||||
if privateKey, err = compute.Sshkeypairs.FetchPrivateKey(ctx, userCred); err != nil {
|
||||
if privateKeys, err = compute.Sshkeypairs.FetchProjectPrivateKeys(ctx, userCred); err != nil {
|
||||
return errors.Wrap(err, "fetch private key")
|
||||
}
|
||||
|
||||
@@ -306,7 +306,7 @@ func (apb *SAnsiblePlaybookV2) runPlaybook(ctx context.Context, userCred mcclien
|
||||
sess := ansiblev2.NewSession().
|
||||
Inventory(apb.Inventory).
|
||||
Playbook(apb.Playbook).
|
||||
PrivateKey(privateKey).
|
||||
PrivateKeys(privateKeys).
|
||||
Requirements(requirements).
|
||||
Files(files).
|
||||
OutputWriter(&ansiblePlaybookOutputWriter{apb}).
|
||||
|
||||
@@ -91,9 +91,9 @@ func (v *ValidatorAnsiblePlaybook) Validate(ctx context.Context, data *jsonutils
|
||||
return httperrors.NewInputParameterError("%s", err.Error())
|
||||
}
|
||||
// add LF for privateKey
|
||||
if len(pb.PrivateKey) > 0 && pb.PrivateKey[len(pb.PrivateKey)-1] != 10 {
|
||||
pb.PrivateKey = append(pb.PrivateKey, 10)
|
||||
}
|
||||
// if len(pb.PrivateKey) > 0 && pb.PrivateKey[len(pb.PrivateKey)-1] != 10 {
|
||||
// pb.PrivateKey = append(pb.PrivateKey, 10)
|
||||
//}
|
||||
pbJson := jsonutils.Marshal(pb)
|
||||
if serialized := pbJson.String(); len(serialized) > PlaybookMaxBytes {
|
||||
return httperrors.NewBadRequestError("playbook too big, got %d bytes, exceeding %d",
|
||||
|
||||
@@ -1636,7 +1636,7 @@ func (b *SBaremetalInstance) GetServerSSHClient() (*ssh.Client, error) {
|
||||
return nil, errors.Error("No server")
|
||||
}
|
||||
|
||||
privateKey, err := modules.Sshkeypairs.FetchPrivateKey(context.TODO(), auth.AdminCredential())
|
||||
privateKeys, err := modules.Sshkeypairs.FetchProjectPrivateKeys(context.TODO(), auth.AdminCredential())
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Get server %s login info", s.GetId())
|
||||
}
|
||||
@@ -1645,12 +1645,14 @@ func (b *SBaremetalInstance) GetServerSSHClient() (*ssh.Client, error) {
|
||||
for idx, nic := range nics {
|
||||
if nic.Ip != "" {
|
||||
for _, user := range []string{"cloudroot", "root"} {
|
||||
sshCli, err := ssh.NewClient(nic.Ip, 22, user, "", privateKey)
|
||||
if err != nil {
|
||||
err = errors.Wrapf(err, "New server %s ssh client %s@%s", s.GetName(), user, nic.Ip)
|
||||
errs = append(errs, err)
|
||||
} else {
|
||||
return sshCli, nil
|
||||
for _, privateKey := range privateKeys {
|
||||
sshCli, err := ssh.NewClient(nic.Ip, 22, user, "", privateKey)
|
||||
if err != nil {
|
||||
err = errors.Wrapf(err, "New server %s ssh client %s@%s", s.GetName(), user, nic.Ip)
|
||||
errs = append(errs, err)
|
||||
} else {
|
||||
return sshCli, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -35,9 +35,10 @@ import (
|
||||
type SProxyEndpoint struct {
|
||||
db.SVirtualResourceBase
|
||||
|
||||
User string `nullable:"false" list:"user" update:"user" create:"optional"`
|
||||
Host string `nullable:"false" list:"user" update:"user" create:"required"`
|
||||
Port int `nullable:"false" list:"user" update:"user" create:"optional"`
|
||||
User string `nullable:"false" list:"user" update:"user" create:"optional"`
|
||||
Host string `nullable:"false" list:"user" update:"user" create:"required"`
|
||||
Port int `nullable:"false" list:"user" update:"user" create:"optional"`
|
||||
|
||||
PrivateKey string `nullable:"false" update:"user" list:"admin" get:"admin" create:"required"` // do not allow get, list
|
||||
|
||||
IntranetIpAddr string `width:"16" charset:"ascii" nullable:"true" list:"user" create:"required"`
|
||||
@@ -73,7 +74,7 @@ func (man *SProxyEndpointManager) PerformCreateFromServer(ctx context.Context, u
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if serverInfo.PrivateKey == "" {
|
||||
if len(serverInfo.PrivateKeys) == 0 {
|
||||
return nil, httperrors.NewBadRequestError("cannot find ssh private key for this server")
|
||||
}
|
||||
|
||||
@@ -111,21 +112,42 @@ func (man *SProxyEndpointManager) PerformCreateFromServer(ctx context.Context, u
|
||||
if portStr, ok := serverInfo.Server.Metadata[compute_apis.SSH_PORT]; ok {
|
||||
port, _ = strconv.Atoi(portStr)
|
||||
}
|
||||
proxyendpoint := &SProxyEndpoint{
|
||||
User: "cloudroot",
|
||||
Host: host,
|
||||
Port: port,
|
||||
PrivateKey: serverInfo.PrivateKey,
|
||||
createAndVerifyPE := func(privateKey string) (*SProxyEndpoint, error) {
|
||||
proxyendpoint := &SProxyEndpoint{
|
||||
User: "cloudroot",
|
||||
Host: host,
|
||||
Port: port,
|
||||
PrivateKey: privateKey,
|
||||
|
||||
IntranetIpAddr: nic.IpAddr,
|
||||
IntranetIpAddr: nic.IpAddr,
|
||||
}
|
||||
proxyendpoint.SetModelManager(man, proxyendpoint)
|
||||
proxyendpoint.Name = name
|
||||
proxyendpoint.DomainId = serverInfo.Server.DomainId
|
||||
proxyendpoint.ProjectId = serverInfo.Server.ProjectId
|
||||
|
||||
if err := proxyendpoint.remoteCheckMake(ctx, userCred); err != nil {
|
||||
return nil, errors.Wrap(err, "remoteCheckMake")
|
||||
}
|
||||
return proxyendpoint, nil
|
||||
}
|
||||
proxyendpoint.SetModelManager(man, proxyendpoint)
|
||||
proxyendpoint.Name = name
|
||||
proxyendpoint.DomainId = serverInfo.Server.DomainId
|
||||
proxyendpoint.ProjectId = serverInfo.Server.ProjectId
|
||||
|
||||
if err := proxyendpoint.remoteCheckMake(ctx, userCred); err != nil {
|
||||
return nil, err
|
||||
var proxyendpoint *SProxyEndpoint
|
||||
var errs []error
|
||||
for _, privateKey := range serverInfo.PrivateKeys {
|
||||
pe, err := createAndVerifyPE(privateKey)
|
||||
if err == nil {
|
||||
proxyendpoint = pe
|
||||
break
|
||||
} else {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
if proxyendpoint == nil {
|
||||
if len(errs) > 0 {
|
||||
return nil, errors.NewAggregate(errs)
|
||||
} else {
|
||||
return nil, errors.Wrap(httperrors.ErrInvalidStatus, "server has no usable ssh private key")
|
||||
}
|
||||
}
|
||||
|
||||
if err := man.TableSpec().Insert(ctx, proxyendpoint); err != nil {
|
||||
|
||||
@@ -123,7 +123,7 @@ func (proxyendpoint *SProxyEndpoint) remoteConfigure(ctx context.Context, userCr
|
||||
host.SetVar("ansible_port", fmt.Sprintf("%d", proxyendpoint.Port))
|
||||
host.SetVar("ansible_become", "yes")
|
||||
pb := &ansible.Playbook{
|
||||
PrivateKey: []byte(proxyendpoint.PrivateKey),
|
||||
PrivateKeys: []string{proxyendpoint.PrivateKey},
|
||||
Inventory: ansible.Inventory{
|
||||
Hosts: []ansible.Host{host},
|
||||
},
|
||||
|
||||
@@ -29,7 +29,7 @@ type serverInfo struct {
|
||||
|
||||
// PrivateKey is the one corresponds to userCred when getting this
|
||||
// serverInfo instance. It can be empty
|
||||
PrivateKey string
|
||||
PrivateKeys []string
|
||||
}
|
||||
|
||||
func (si *serverInfo) GetNic() *compute_apis.GuestnetworkShortDesc {
|
||||
@@ -71,11 +71,11 @@ func getServerInfo(
|
||||
return nil, httperrors.NewServerError("unmarshal server %s: %v", serverId, err)
|
||||
}
|
||||
|
||||
privateKey, _ := compute_modules.Sshkeypairs.FetchPrivateKey(ctx, userCred)
|
||||
privateKeys, _ := compute_modules.Sshkeypairs.FetchProjectPrivateKeys(ctx, userCred)
|
||||
|
||||
serverInfo := &serverInfo{
|
||||
Server: server,
|
||||
PrivateKey: privateKey,
|
||||
Server: server,
|
||||
PrivateKeys: privateKeys,
|
||||
}
|
||||
return serverInfo, nil
|
||||
}
|
||||
|
||||
@@ -87,11 +87,20 @@ func (guest *SGuest) GetDetailsSshable(
|
||||
if err != nil {
|
||||
return nil, httperrors.NewInternalServerError("fetch ssh private key: %v", err)
|
||||
}
|
||||
tryData.PrivateKey = privateKey
|
||||
tryData.PublicKey = publicKey
|
||||
var sshTryErrs []error
|
||||
for i := range privateKey {
|
||||
tryData.PrivateKey = privateKey[i]
|
||||
tryData.PublicKey = publicKey[i]
|
||||
|
||||
if err := guest.sshableTryEach(ctx, userCred, tryData); err != nil {
|
||||
return nil, err
|
||||
if err := guest.sshableTryEach(ctx, userCred, tryData); err != nil {
|
||||
sshTryErrs = append(sshTryErrs, err)
|
||||
} else {
|
||||
sshTryErrs = nil
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(sshTryErrs) > 0 {
|
||||
return nil, errors.NewAggregate(sshTryErrs)
|
||||
}
|
||||
|
||||
{
|
||||
@@ -497,7 +506,7 @@ func (guest *SGuest) PerformMakeSshable(
|
||||
},
|
||||
}
|
||||
if input.PrivateKey != "" {
|
||||
pb.PrivateKey = []byte(input.PrivateKey)
|
||||
pb.PrivateKeys = []string{input.PrivateKey}
|
||||
} else if input.Password != "" {
|
||||
host.SetVar("ansible_password", input.Password)
|
||||
}
|
||||
@@ -537,8 +546,8 @@ func (guest *SGuest) GetDetailsMakeSshableCmd(
|
||||
|
||||
varVals := [][2]string{
|
||||
{"user", "cloudroot"},
|
||||
{"adminpub", strings.TrimSpace(adminPublicKey)},
|
||||
{"projpub", strings.TrimSpace(projectPublicKey)},
|
||||
{"adminpub", strings.TrimSpace(adminPublicKey[0])},
|
||||
{"projpub", strings.TrimSpace(projectPublicKey[0])},
|
||||
}
|
||||
shellCmd := ""
|
||||
for i := range varVals {
|
||||
@@ -547,23 +556,18 @@ func (guest *SGuest) GetDetailsMakeSshableCmd(
|
||||
}
|
||||
|
||||
shellCmd += `
|
||||
group="$user"
|
||||
sshdir="/home/$user/.ssh"
|
||||
keyfile="$sshdir/authorized_keys"
|
||||
`
|
||||
shellCmd += `
|
||||
id -g "$group" &>/dev/null || groupadd "$group"
|
||||
id -u "$user" &>/dev/null || useradd --create-home --gid "$group" "$user"
|
||||
mkdir -p "$sshdir"
|
||||
grep -q -F "$adminpub" "$keyfile" &>/dev/null || echo "$adminpub" >>"$keyfile"
|
||||
grep -q -F "$projpub" "$keyfile" &>/dev/null || echo "$projpub" >>"$keyfile"
|
||||
chown -R "$user:$group" "$sshdir"
|
||||
chmod -R 700 "$sshdir"
|
||||
chmod -R 600 "$keyfile"
|
||||
|
||||
if ! grep -q "^$user " /etc/sudoers; then
|
||||
echo "$user ALL=(ALL) NOPASSWD: ALL" | EDITOR='tee -a' visudo
|
||||
fi
|
||||
sshdir=/opt/$user/.ssh
|
||||
keyfile=$sshdir/authorized_keys
|
||||
grep -q "^$user:" /etc/group || groupadd "$user"
|
||||
shell=$(command -v bash || command -v zsh || true)
|
||||
id -u "$user" >/dev/null 2>&1 || useradd ${shell:+--shell "$shell"} -d $(dirname $sshdir) --create-home -g "$user" "$user"
|
||||
install -d -m 700 -o "$user" -g "$user" "$sshdir"
|
||||
for k in "$adminpub" "$projpub"; do
|
||||
grep -qF "$k" "$keyfile" 2>/dev/null || echo "$k" >>"$keyfile"
|
||||
done
|
||||
chown "$user:$user" "$keyfile"
|
||||
chmod 600 "$keyfile"
|
||||
grep -q "^$user " /etc/sudoers || echo "$user ALL=(ALL) NOPASSWD: ALL" | EDITOR='tee -a' visudo
|
||||
`
|
||||
output = compute_api.GuestMakeSshableCmdOutput{
|
||||
ShellCmd: shellCmd,
|
||||
@@ -590,6 +594,7 @@ func (guest *SGuest) PerformSetSshport(ctx context.Context, userCred mcclient.To
|
||||
}
|
||||
return nil, guest.SetSshPort(ctx, userCred, input.Port)
|
||||
}
|
||||
|
||||
func (guest *SGuest) GetDetailsSshport(
|
||||
ctx context.Context,
|
||||
userCred mcclient.TokenCredential,
|
||||
|
||||
@@ -5707,8 +5707,8 @@ func (self *SGuest) GetDeployConfigOnHost(ctx context.Context, userCred mcclient
|
||||
log.Errorf("fail to get ssh project public key %s", err)
|
||||
}
|
||||
|
||||
config.Add(jsonutils.NewString(adminPubKey), "admin_public_key")
|
||||
config.Add(jsonutils.NewString(projPubKey), "project_public_key")
|
||||
config.Add(jsonutils.NewString(adminPubKey[0]), "admin_public_key")
|
||||
config.Add(jsonutils.NewString(projPubKey[0]), "project_public_key")
|
||||
|
||||
config.Add(jsonutils.NewString(deployAction), "action")
|
||||
|
||||
|
||||
@@ -329,10 +329,11 @@ func (manager *SKubeNodePoolManager) ValidateCreateData(ctx context.Context, use
|
||||
keypair := keypairObj.(*SKeypair)
|
||||
input.PublicKey = keypair.PublicKey
|
||||
} else {
|
||||
_, input.PublicKey, err = sshkeys.GetSshAdminKeypair(ctx)
|
||||
_, pubKeys, err := sshkeys.GetSshAdminKeypair(ctx)
|
||||
if err != nil {
|
||||
return nil, httperrors.NewGeneralError(errors.Wrapf(err, "GetSshAdminKeypair"))
|
||||
}
|
||||
input.PublicKey = pubKeys[0]
|
||||
}
|
||||
|
||||
if input.DesiredInstanceCount > 0 {
|
||||
|
||||
@@ -3247,7 +3247,11 @@ func (self *SManagedVirtualizationRegionDriver) RequestCreateKubeCluster(ctx con
|
||||
opts.RoleName, _ = params.GetString("role_name")
|
||||
opts.PrivateAccess, _ = params.Bool("private_access")
|
||||
opts.PublicAccess, _ = params.Bool("public_access")
|
||||
_, opts.PublicKey, _ = sshkeys.GetSshAdminKeypair(ctx)
|
||||
_, pubKeys, err := sshkeys.GetSshAdminKeypair(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetSshAdminKeypair")
|
||||
}
|
||||
opts.PublicKey = pubKeys[0]
|
||||
|
||||
iregion, err := cluster.GetIRegion(ctx)
|
||||
if err != nil {
|
||||
|
||||
@@ -31,10 +31,15 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/models"
|
||||
)
|
||||
|
||||
func AddSshKeysHandler(prefix string, app *appsrv.Application) {
|
||||
// 1. 获取用户自己项目的密钥对,总是返回公钥+私钥
|
||||
// 2. 如果是regionadmin所在系统项目(system),指定admin=true,则获取全局管理密钥
|
||||
app.AddHandler2("GET", fmt.Sprintf("%s/sshkeypairs", prefix), auth.Authenticate(sshKeysHandler), nil, "get_sshkeys", nil)
|
||||
// 1. 获取指定项目(tenant_id)的项目密钥对,只有域管理员或者系统管理员级别才能获得私钥
|
||||
// 2. 无法获取全局管理密钥
|
||||
app.AddHandler2("GET", fmt.Sprintf("%s/sshkeypairs/<tenant_id>", prefix), auth.Authenticate(adminSshKeysHandler), nil, "get_sshkeys", nil)
|
||||
}
|
||||
|
||||
@@ -80,7 +85,7 @@ func sshKeysHandler(ctx context.Context, w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
|
||||
func sendSshKey(ctx context.Context, w http.ResponseWriter, userCred mcclient.TokenCredential, projectId string, isAdmin bool, publicOnly bool) {
|
||||
var privKey, pubKey string
|
||||
var privKey, pubKey []string
|
||||
|
||||
if isAdmin {
|
||||
if policy.PolicyManager.Allow(rbacscope.ScopeSystem, userCred, consts.GetServiceType(), "sshkeypairs", policy.PolicyActionGet).Result.IsAllow() {
|
||||
@@ -93,12 +98,24 @@ func sendSshKey(ctx context.Context, w http.ResponseWriter, userCred mcclient.To
|
||||
privKey, pubKey, _ = GetSshProjectKeypair(ctx, projectId)
|
||||
}
|
||||
|
||||
keypairs := make([]models.SshKeypair, 0, len(privKey))
|
||||
for i := range pubKey {
|
||||
keypair := models.SshKeypair{
|
||||
PublicKey: pubKey[i],
|
||||
}
|
||||
if !publicOnly {
|
||||
keypair.PrivateKey = privKey[i]
|
||||
}
|
||||
keypairs = append(keypairs, keypair)
|
||||
}
|
||||
|
||||
ret := jsonutils.NewDict()
|
||||
|
||||
if !publicOnly {
|
||||
ret.Add(jsonutils.NewString(privKey), "private_key")
|
||||
ret.Add(jsonutils.NewString(privKey[0]), "private_key")
|
||||
}
|
||||
ret.Add(jsonutils.NewString(pubKey), "public_key")
|
||||
ret.Add(jsonutils.NewString(pubKey[0]), "public_key")
|
||||
ret.Add(jsonutils.Marshal(keypairs), "keypairs")
|
||||
body := jsonutils.NewDict()
|
||||
body.Add(ret, "sshkeypair")
|
||||
appsrv.SendJSON(w, body)
|
||||
|
||||
@@ -16,6 +16,7 @@ package sshkeys
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/pkg/utils"
|
||||
|
||||
@@ -25,14 +26,20 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
sshAdminPrivateKey = "admin-ssh-private-key"
|
||||
sshAdminPublicKey = "admin-ssh-public-key"
|
||||
sshAdminRsaPrivateKey = "admin-ssh-private-key"
|
||||
sshAdminRsaPublicKey = "admin-ssh-public-key"
|
||||
|
||||
sshPrivateKey = "project-ssh-private-key"
|
||||
sshPublicKey = "project-ssh-public-key"
|
||||
sshAdminEd25519PrivateKey = "admin-ssh-ed25519-private-key"
|
||||
sshAdminEd25519PublicKey = "admin-ssh-ed25519-public-key"
|
||||
|
||||
sshRsaPrivateKey = "project-ssh-private-key"
|
||||
sshRsaPublicKey = "project-ssh-public-key"
|
||||
|
||||
sshEd25519PrivateKey = "project-ssh-ed25519-private-key"
|
||||
sshEd25519PublicKey = "project-ssh-ed25519-public-key"
|
||||
)
|
||||
|
||||
func _getKeys(ctx context.Context, tenantId string, privateKey, publicKey string) (string, string, error) {
|
||||
func _getKeys(ctx context.Context, tenantId string, privateKey, publicKey string, autocreate bool) (string, string, error) {
|
||||
tenant, err := db.TenantCacheManager.FetchTenantById(ctx, tenantId)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
@@ -40,21 +47,43 @@ func _getKeys(ctx context.Context, tenantId string, privateKey, publicKey string
|
||||
private := tenant.GetMetadata(ctx, privateKey, nil)
|
||||
public := tenant.GetMetadata(ctx, publicKey, nil)
|
||||
userCred := auth.AdminCredential()
|
||||
if len(private) == 0 || len(public) == 0 {
|
||||
private, public, _ = seclib2.GenerateRSASSHKeypair()
|
||||
if (len(private) == 0 || len(public) == 0) && autocreate {
|
||||
if strings.Contains(privateKey, "ed25519") {
|
||||
private, public, _ = seclib2.GenerateED25519SSHKeypair()
|
||||
} else {
|
||||
private, public, _ = seclib2.GenerateRSASSHKeypair()
|
||||
}
|
||||
private, _ = utils.EncryptAESBase64(tenantId, private)
|
||||
tenant.SetMetadata(ctx, privateKey, private, userCred)
|
||||
tenant.SetMetadata(ctx, publicKey, public, userCred)
|
||||
} else if len(private) > 0 {
|
||||
private, _ = utils.DescryptAESBase64(tenantId, private)
|
||||
}
|
||||
private, _ = utils.DescryptAESBase64(tenantId, private)
|
||||
return private, public, nil
|
||||
}
|
||||
|
||||
func GetSshProjectKeypair(ctx context.Context, tenantId string) (string, string, error) {
|
||||
return _getKeys(ctx, tenantId, sshPrivateKey, sshPublicKey)
|
||||
func GetSshProjectKeypair(ctx context.Context, tenantId string) ([]string, []string, error) {
|
||||
return getSshProjectKeypair(ctx, tenantId, sshEd25519PrivateKey, sshEd25519PublicKey, sshRsaPrivateKey, sshRsaPublicKey)
|
||||
}
|
||||
|
||||
func GetSshAdminKeypair(ctx context.Context) (string, string, error) {
|
||||
func getSshProjectKeypair(ctx context.Context, tenantId string, ed25519PrivKey, ed25519PubKey, rsaPrivKey, rsaPubKey string) ([]string, []string, error) {
|
||||
ed25519Priv, ed25519Pub, err := _getKeys(ctx, tenantId, ed25519PrivKey, ed25519PubKey, true)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
rsaPriv, rsaPub, _ := _getKeys(ctx, tenantId, rsaPrivKey, rsaPubKey, false)
|
||||
private := []string{ed25519Priv}
|
||||
if len(rsaPriv) > 0 {
|
||||
private = append(private, rsaPriv)
|
||||
}
|
||||
public := []string{ed25519Pub}
|
||||
if len(rsaPub) > 0 {
|
||||
public = append(public, rsaPub)
|
||||
}
|
||||
return private, public, nil
|
||||
}
|
||||
|
||||
func GetSshAdminKeypair(ctx context.Context) ([]string, []string, error) {
|
||||
userCred := auth.AdminCredential()
|
||||
return _getKeys(ctx, userCred.GetProjectId(), sshAdminPrivateKey, sshAdminPublicKey)
|
||||
return getSshProjectKeypair(ctx, userCred.GetProjectId(), sshAdminEd25519PrivateKey, sshAdminEd25519PublicKey, sshAdminRsaPrivateKey, sshAdminRsaPublicKey)
|
||||
}
|
||||
|
||||
@@ -277,18 +277,14 @@ func DeployAdminAuthorizedKeys(s *mcclient.ClientSession) error {
|
||||
return errors.Wrapf(err, "mkdir .ssh %s", output)
|
||||
}
|
||||
|
||||
query := jsonutils.NewDict()
|
||||
query.Set("admin", jsonutils.JSONTrue)
|
||||
ret, err := modules.Sshkeypairs.List(s, query)
|
||||
keypairs, err := modules.Sshkeypairs.FetchAdminKeypairsBySession(s.GetContext(), s)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "modules.Sshkeypairs.List")
|
||||
}
|
||||
if len(ret.Data) == 0 {
|
||||
if len(keypairs) == 0 {
|
||||
return errors.Wrap(httperrors.ErrNotFound, "Not found admin sshkey")
|
||||
}
|
||||
keys := ret.Data[0]
|
||||
adminPublicKey, _ := keys.GetString("public_key")
|
||||
pubKeys := &deployapi.SSHKeys{AdminPublicKey: adminPublicKey}
|
||||
pubKeys := &deployapi.SSHKeys{AdminPublicKey: keypairs[0].PublicKey}
|
||||
|
||||
var oldKeys string
|
||||
authFile := path.Join(sshDir, "authorized_keys")
|
||||
|
||||
@@ -15,6 +15,6 @@
|
||||
package models
|
||||
|
||||
type SshKeypair struct {
|
||||
PrivateKey string
|
||||
PublicKey string
|
||||
PrivateKey string `json:"private_key"`
|
||||
PublicKey string `json:"public_key"`
|
||||
}
|
||||
|
||||
@@ -48,42 +48,84 @@ func (this *SSshkeypairManager) List(s *mcclient.ClientSession, params jsonutils
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (this *SSshkeypairManager) FetchPrivateKey(ctx context.Context, userCred mcclient.TokenCredential) (string, error) {
|
||||
func (this *SSshkeypairManager) FetchProjectPrivateKeys(ctx context.Context, userCred mcclient.TokenCredential) ([]string, error) {
|
||||
s := auth.GetSession(ctx, userCred, "")
|
||||
return this.FetchPrivateKeyBySession(ctx, s)
|
||||
return this.FetchProjectPrivateKeysBySession(ctx, s)
|
||||
}
|
||||
|
||||
func (this *SSshkeypairManager) FetchPrivateKeyBySession(ctx context.Context, s *mcclient.ClientSession) (string, error) {
|
||||
kp, err := this.FetchKeypairBySession(ctx, s)
|
||||
func (this *SSshkeypairManager) FetchProjectPrivateKeysBySession(ctx context.Context, s *mcclient.ClientSession) ([]string, error) {
|
||||
kp, err := this.FetchProjectKeypairsBySession(ctx, s)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "FetchKeypairBySession")
|
||||
return nil, errors.Wrap(err, "FetchProjectKeypairsBySession")
|
||||
}
|
||||
return kp.PrivateKey, nil
|
||||
keys := make([]string, 0, len(kp))
|
||||
for _, kp := range kp {
|
||||
keys = append(keys, kp.PrivateKey)
|
||||
}
|
||||
return keys, nil
|
||||
}
|
||||
|
||||
func (this *SSshkeypairManager) FetchKeypairBySession(ctx context.Context, s *mcclient.ClientSession) (*models.SshKeypair, error) {
|
||||
userCred := s.GetToken()
|
||||
func (this *SSshkeypairManager) FetchAdminPrivateKeysBySession(ctx context.Context, s *mcclient.ClientSession) ([]string, error) {
|
||||
kp, err := this.FetchAdminKeypairsBySession(ctx, s)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "FetchAdminKeypairsBySession")
|
||||
}
|
||||
keys := make([]string, 0, len(kp))
|
||||
for _, kp := range kp {
|
||||
keys = append(keys, kp.PrivateKey)
|
||||
}
|
||||
return keys, nil
|
||||
}
|
||||
|
||||
func (this *SSshkeypairManager) FetchAdminKeypairsBySession(ctx context.Context, s *mcclient.ClientSession) ([]models.SshKeypair, error) {
|
||||
return this.fetchKeypairsBySession(ctx, s, true)
|
||||
}
|
||||
|
||||
func (this *SSshkeypairManager) FetchProjectKeypairsBySession(ctx context.Context, s *mcclient.ClientSession) ([]models.SshKeypair, error) {
|
||||
return this.fetchKeypairsBySession(ctx, s, false)
|
||||
}
|
||||
|
||||
func (this *SSshkeypairManager) fetchKeypairsBySession(ctx context.Context, s *mcclient.ClientSession, isAdmin bool) ([]models.SshKeypair, error) {
|
||||
jd := jsonutils.NewDict()
|
||||
var jr jsonutils.JSONObject
|
||||
if userCred.HasSystemAdminPrivilege() {
|
||||
if isAdmin {
|
||||
jd.Set("admin", jsonutils.JSONTrue)
|
||||
r, err := Sshkeypairs.List(s, jd)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get admin ssh key")
|
||||
}
|
||||
r, err := this.List(s, jd)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get admin ssh key")
|
||||
}
|
||||
if len(r.Data) == 0 {
|
||||
return nil, errors.Wrap(errors.ErrNotFound, "no ssh key found")
|
||||
}
|
||||
return unmarshalSshKeypairs(r.Data[0])
|
||||
}
|
||||
|
||||
func (this *SSshkeypairManager) FetchKeypairsByProject(ctx context.Context, s *mcclient.ClientSession, projectId string) ([]models.SshKeypair, error) {
|
||||
jd := jsonutils.NewDict()
|
||||
if len(projectId) == 0 {
|
||||
projectId = s.GetProjectId()
|
||||
}
|
||||
r, err := Sshkeypairs.GetById(s, projectId, jd)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get project ssh key")
|
||||
}
|
||||
return unmarshalSshKeypairs(r)
|
||||
}
|
||||
|
||||
func unmarshalSshKeypairs(jr jsonutils.JSONObject) ([]models.SshKeypair, error) {
|
||||
kps := make([]models.SshKeypair, 0, 2)
|
||||
if jr.Contains("keypairs") {
|
||||
if err := jr.Unmarshal(&kps, "keypairs"); err != nil {
|
||||
return nil, errors.Wrap(err, "unmarshal ssh key")
|
||||
}
|
||||
jr = r.Data[0]
|
||||
} else {
|
||||
r, err := Sshkeypairs.GetById(s, userCred.GetProjectId(), jd)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get project ssh key")
|
||||
kp := models.SshKeypair{}
|
||||
if err := jr.Unmarshal(&kp); err != nil {
|
||||
return nil, errors.Wrap(err, "unmarshal ssh key")
|
||||
}
|
||||
jr = r
|
||||
kps = append(kps, kp)
|
||||
}
|
||||
kp := &models.SshKeypair{}
|
||||
if err := jr.Unmarshal(kp); err != nil {
|
||||
return nil, errors.Wrap(err, "unmarshal ssh key")
|
||||
}
|
||||
return kp, nil
|
||||
return kps, nil
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
@@ -17,7 +17,6 @@ package ansible
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@@ -51,10 +50,10 @@ func (pbs pbState) String() string {
|
||||
}
|
||||
|
||||
type Playbook struct {
|
||||
Inventory Inventory
|
||||
Modules []Module
|
||||
PrivateKey []byte
|
||||
Files map[string][]byte
|
||||
Inventory Inventory
|
||||
Modules []Module
|
||||
PrivateKeys []string
|
||||
Files map[string][]byte
|
||||
|
||||
tmpdir string
|
||||
noCleanOnExit bool
|
||||
@@ -75,7 +74,7 @@ func (pb *Playbook) Copy() *Playbook {
|
||||
pb1 := NewPlaybook()
|
||||
pb1.Inventory = gotypes.DeepCopy(pb.Inventory).(Inventory)
|
||||
pb1.Modules = gotypes.DeepCopy(pb.Modules).([]Module)
|
||||
pb1.PrivateKey = gotypes.DeepCopy(pb.PrivateKey).([]byte)
|
||||
pb1.PrivateKeys = gotypes.DeepCopy(pb.PrivateKeys).([]string)
|
||||
pb1.Files = gotypes.DeepCopy(pb.Files).(map[string][]byte)
|
||||
return pb1
|
||||
}
|
||||
@@ -102,8 +101,21 @@ func (pb *Playbook) Running() bool {
|
||||
return pb.state == pbStateRunning
|
||||
}
|
||||
|
||||
func (pb *Playbook) Run(ctx context.Context) error {
|
||||
errs := make([]error, 0, len(pb.PrivateKeys))
|
||||
for _, privateKey := range pb.PrivateKeys {
|
||||
err := pb.runOnce(ctx, privateKey)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return errors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
// Run runs the playbook
|
||||
func (pb *Playbook) Run(ctx context.Context) (err error) {
|
||||
func (pb *Playbook) runOnce(ctx context.Context, privateKey string) error {
|
||||
var (
|
||||
tmpdir string
|
||||
)
|
||||
@@ -126,10 +138,9 @@ func (pb *Playbook) Run(ctx context.Context) (err error) {
|
||||
}
|
||||
|
||||
// make tmpdir
|
||||
tmpdir, err = os.MkdirTemp("", "onecloud-ansible")
|
||||
tmpdir, err := os.MkdirTemp("", "onecloud-ansible")
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "making tmp dir")
|
||||
return
|
||||
return errors.Wrap(err, "making tmp dir")
|
||||
}
|
||||
pb.tmpdir = tmpdir
|
||||
defer func() {
|
||||
@@ -143,55 +154,45 @@ func (pb *Playbook) Run(ctx context.Context) (err error) {
|
||||
|
||||
// write out inventory
|
||||
inventory := filepath.Join(tmpdir, "inventory")
|
||||
err = ioutil.WriteFile(inventory, pb.Inventory.Data(), os.FileMode(0600))
|
||||
err = os.WriteFile(inventory, pb.Inventory.Data(), os.FileMode(0600))
|
||||
if err != nil {
|
||||
err = errors.Wrapf(err, "writing inventory %s", inventory)
|
||||
return
|
||||
return errors.Wrapf(err, "writing inventory %s", inventory)
|
||||
}
|
||||
|
||||
// write out private key
|
||||
var privateKey string
|
||||
if len(pb.PrivateKey) > 0 {
|
||||
privateKey = filepath.Join(tmpdir, "private_key")
|
||||
err = ioutil.WriteFile(privateKey, pb.PrivateKey, os.FileMode(0600))
|
||||
var privateKeyFile string
|
||||
if len(privateKey) > 0 {
|
||||
privateKeyFile = filepath.Join(tmpdir, "private_key")
|
||||
err = os.WriteFile(privateKeyFile, []byte(privateKey), os.FileMode(0600))
|
||||
if err != nil {
|
||||
err = errors.Wrapf(err, "writing private key %s", privateKey)
|
||||
return
|
||||
return errors.Wrapf(err, "writing private key %s", privateKeyFile)
|
||||
}
|
||||
}
|
||||
|
||||
// write out files
|
||||
for name, content := range pb.Files {
|
||||
path, err2 := fileutils2.JoinInside(tmpdir, name)
|
||||
if err2 != nil {
|
||||
err = errors.Wrapf(err2, "playbook file %s", name)
|
||||
return
|
||||
path, err := fileutils2.JoinInside(tmpdir, name)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "playbook file %s", name)
|
||||
}
|
||||
dir := filepath.Dir(path)
|
||||
err = os.MkdirAll(dir, os.FileMode(0700))
|
||||
if err != nil {
|
||||
err = errors.Wrapf(err, "mkdir -p %s", dir)
|
||||
return
|
||||
return errors.Wrapf(err, "mkdir -p %s", dir)
|
||||
}
|
||||
err = ioutil.WriteFile(path, content, os.FileMode(0600))
|
||||
err = os.WriteFile(path, content, os.FileMode(0600))
|
||||
if err != nil {
|
||||
err = errors.Wrapf(err, "writing file %s", name)
|
||||
return
|
||||
return errors.Wrapf(err, "writing file %s", name)
|
||||
}
|
||||
}
|
||||
|
||||
// run modules one by one
|
||||
var errs []error
|
||||
defer func() {
|
||||
if len(errs) > 0 {
|
||||
err = errors.NewAggregate(errs)
|
||||
}
|
||||
}()
|
||||
for _, m := range pb.Modules {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
err = ctx.Err()
|
||||
return
|
||||
errs = append(errs, errors.Wrap(ctx.Err(), "context done"))
|
||||
return errors.NewAggregate(errs)
|
||||
default:
|
||||
}
|
||||
modArgs := strings.Join(m.Args, " ")
|
||||
@@ -212,7 +213,7 @@ func (pb *Playbook) Run(ctx context.Context) (err error) {
|
||||
stderr, _ := cmd.StderrPipe()
|
||||
if err1 := cmd.Start(); err1 != nil {
|
||||
errs = append(errs, errors.Wrapf(err1, "run module %q, args %q", m.Name, modArgs))
|
||||
return
|
||||
continue
|
||||
}
|
||||
// Mix stdout, stderr
|
||||
if pb.outputWriter != nil {
|
||||
@@ -221,10 +222,10 @@ func (pb *Playbook) Run(ctx context.Context) (err error) {
|
||||
}
|
||||
if err1 := cmd.Wait(); err1 != nil {
|
||||
errs = append(errs, errors.Wrapf(err1, "wait module %q, args %q", m.Name, modArgs))
|
||||
// continue to next
|
||||
continue
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return errors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
func (pb *Playbook) OutputWriter(w io.Writer) {
|
||||
|
||||
@@ -17,6 +17,8 @@ package ansiblev2
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"yunion.io/x/pkg/errors"
|
||||
)
|
||||
|
||||
type OfflineSession struct {
|
||||
@@ -31,6 +33,16 @@ type OfflineSession struct {
|
||||
configYaml string
|
||||
}
|
||||
|
||||
type tryOfflineSession struct {
|
||||
*OfflineSession
|
||||
|
||||
privateKeyIndex int
|
||||
}
|
||||
|
||||
func (trySession *tryOfflineSession) GetPrivateKey() string {
|
||||
return trySession.GetPrivateKeys()[trySession.privateKeyIndex]
|
||||
}
|
||||
|
||||
func NewOfflineSession() *OfflineSession {
|
||||
sess := &OfflineSession{
|
||||
PlaybookSessionBase: NewPlaybookSessionBase(),
|
||||
@@ -38,8 +50,8 @@ func NewOfflineSession() *OfflineSession {
|
||||
return sess
|
||||
}
|
||||
|
||||
func (sess *OfflineSession) PrivateKey(s string) *OfflineSession {
|
||||
sess.privateKey = s
|
||||
func (sess *OfflineSession) PrivateKeys(s []string) *OfflineSession {
|
||||
sess.privateKeys = s
|
||||
return sess
|
||||
}
|
||||
|
||||
@@ -85,6 +97,19 @@ func (sess *OfflineSession) GetConfigYaml() string {
|
||||
return sess.configYaml
|
||||
}
|
||||
|
||||
func (sess *OfflineSession) Run(ctx context.Context) (err error) {
|
||||
return runnable{sess}.Run(ctx)
|
||||
func (sess *OfflineSession) Run(ctx context.Context) error {
|
||||
privateKeys := sess.GetPrivateKeys()
|
||||
errs := make([]error, 0, len(privateKeys))
|
||||
for i := range privateKeys {
|
||||
trySession := &tryOfflineSession{
|
||||
OfflineSession: sess,
|
||||
privateKeyIndex: i,
|
||||
}
|
||||
err := runnable{trySession}.Run(ctx)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
errs = append(errs, err)
|
||||
}
|
||||
return errors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
@@ -262,7 +262,7 @@ func (r runnable) Run(ctx context.Context) (err error) {
|
||||
}
|
||||
|
||||
type PlaybookSessionBase struct {
|
||||
privateKey string
|
||||
privateKeys []string
|
||||
|
||||
inventory string
|
||||
outputWriter io.Writer
|
||||
@@ -280,8 +280,8 @@ func NewPlaybookSessionBase() PlaybookSessionBase {
|
||||
}
|
||||
}
|
||||
|
||||
func (pb *PlaybookSessionBase) GetPrivateKey() string {
|
||||
return pb.privateKey
|
||||
func (pb *PlaybookSessionBase) GetPrivateKeys() []string {
|
||||
return pb.privateKeys
|
||||
}
|
||||
|
||||
func (pb *PlaybookSessionBase) IsKeepTmpdir() bool {
|
||||
|
||||
@@ -17,6 +17,8 @@ package ansiblev2
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"yunion.io/x/pkg/errors"
|
||||
)
|
||||
|
||||
type Session struct {
|
||||
@@ -27,6 +29,16 @@ type Session struct {
|
||||
files map[string][]byte
|
||||
}
|
||||
|
||||
type trySession struct {
|
||||
*Session
|
||||
|
||||
privateKeyIndex int
|
||||
}
|
||||
|
||||
func (trySession *trySession) GetPrivateKey() string {
|
||||
return trySession.GetPrivateKeys()[trySession.privateKeyIndex]
|
||||
}
|
||||
|
||||
func NewSession() *Session {
|
||||
sess := &Session{
|
||||
PlaybookSessionBase: NewPlaybookSessionBase(),
|
||||
@@ -35,8 +47,8 @@ func NewSession() *Session {
|
||||
return sess
|
||||
}
|
||||
|
||||
func (sess *Session) PrivateKey(s string) *Session {
|
||||
sess.privateKey = s
|
||||
func (sess *Session) PrivateKeys(s []string) *Session {
|
||||
sess.privateKeys = s
|
||||
return sess
|
||||
}
|
||||
|
||||
@@ -103,6 +115,19 @@ func (sess *Session) GetFile() map[string][]byte {
|
||||
return sess.files
|
||||
}
|
||||
|
||||
func (sess *Session) Run(ctx context.Context) (err error) {
|
||||
return runnable{sess}.Run(ctx)
|
||||
func (sess *Session) Run(ctx context.Context) error {
|
||||
privateKeys := sess.GetPrivateKeys()
|
||||
errs := make([]error, 0, len(privateKeys))
|
||||
for i := range privateKeys {
|
||||
trySession := &trySession{
|
||||
Session: sess,
|
||||
privateKeyIndex: i,
|
||||
}
|
||||
err := runnable{trySession}.Run(ctx)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
errs = append(errs, err)
|
||||
}
|
||||
return errors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ func NewClimcSshCommand(info *webconsole.ClimcSshInfo, s *mcclient.ClientSession
|
||||
return nil, fmt.Errorf("Invalid username %q", info.Username)
|
||||
}
|
||||
targetIp := helper.FetchClimcTargetIp()
|
||||
privateKey, err := helper.GetValidPrivateKey(targetIp, 22, info.Username, "")
|
||||
privateKey, err := helper.GetValidPrivateKey(targetIp, 22, info.Username, s)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get cloud admin private key")
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules/compute"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules/k8s"
|
||||
@@ -78,45 +79,17 @@ func FetchClimcTargetIp() string {
|
||||
return podIp
|
||||
}
|
||||
|
||||
func GetValidPrivateKey(host string, port int, username string, projectId string) (string, error) {
|
||||
func GetValidPrivateKey(host string, port int, username string, cliSession *mcclient.ClientSession) (string, error) {
|
||||
errs := []error{}
|
||||
ctx := context.Background()
|
||||
admin := auth.GetAdminSession(ctx, o.Options.Region)
|
||||
for _, gf := range []func() (jsonutils.JSONObject, error){
|
||||
func() (jsonutils.JSONObject, error) {
|
||||
if projectId == "" {
|
||||
return nil, errors.Error("project_id is empty")
|
||||
}
|
||||
key, err := compute.Sshkeypairs.GetById(admin, projectId, jsonutils.Marshal(map[string]bool{"admin": true}))
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Sshkeypairs.GetById(%s)", projectId)
|
||||
}
|
||||
return key, nil
|
||||
},
|
||||
func() (jsonutils.JSONObject, error) {
|
||||
query := jsonutils.NewDict()
|
||||
query.Set("admin", jsonutils.JSONTrue)
|
||||
ret, err := compute.Sshkeypairs.List(admin, query)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "modules.Sshkeypairs.List")
|
||||
}
|
||||
if len(ret.Data) == 0 {
|
||||
return nil, errors.Wrap(httperrors.ErrNotFound, "Not found admin sshkey")
|
||||
}
|
||||
keys := ret.Data[0]
|
||||
return keys, nil
|
||||
},
|
||||
} {
|
||||
key, err := gf()
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
privKey, err := key.GetString("private_key")
|
||||
if err != nil {
|
||||
errs = append(errs, errors.Wrapf(err, "get private_key"))
|
||||
continue
|
||||
}
|
||||
var privateKeys []string
|
||||
projectPrivateKeys, _ := compute.Sshkeypairs.FetchProjectPrivateKeysBySession(ctx, cliSession)
|
||||
privateKeys = append(privateKeys, projectPrivateKeys...)
|
||||
adminPrivateKeys, _ := compute.Sshkeypairs.FetchAdminPrivateKeysBySession(ctx, admin)
|
||||
privateKeys = append(privateKeys, adminPrivateKeys...)
|
||||
|
||||
for _, privKey := range privateKeys {
|
||||
signer, err := ssh.ParsePrivateKey([]byte(privKey))
|
||||
if err != nil {
|
||||
errs = append(errs, errors.Wrapf(err, "ParsePrivateKey"))
|
||||
|
||||
@@ -134,7 +134,7 @@ func (s *SSshSession) IsNeedLogin() (bool, error) {
|
||||
return true, errors.Error("username is empty")
|
||||
}
|
||||
}
|
||||
privateKey, err := helper.GetValidPrivateKey(s.Host, s.Port, s.Username, s.us.GetProjectId())
|
||||
privateKey, err := helper.GetValidPrivateKey(s.Host, s.Port, s.Username, s.us)
|
||||
if err != nil {
|
||||
return true, errors.Wrap(err, "try to use cloud admin private_key for ssh login")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user