feat(secrets): use ssh agent for git

This commit is contained in:
Denis Gukov 2023-09-23 17:47:27 +02:00
parent 58850e9129
commit 43b342b3e8
5 changed files with 96 additions and 73 deletions

View File

@ -7,11 +7,14 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"github.com/ansible-semaphore/semaphore/lib"
"io"
"io/ioutil"
"math/big"
"os"
"path"
"strconv"
"time"
"github.com/ansible-semaphore/semaphore/util"
)
@ -64,15 +67,20 @@ const (
type AccessKeyInstallation struct {
InstallationKey int64
SshAgent *lib.SshAgent
}
func (key AccessKeyInstallation) Destroy() error {
path := key.GetPath()
_, err := os.Stat(path)
if key.SshAgent != nil {
return key.SshAgent.Close()
}
installPath := key.GetPath()
_, err := os.Stat(installPath)
if os.IsNotExist(err) {
return nil
}
return os.Remove(path)
return os.Remove(installPath)
}
// GetPath returns the location of the access key once written to disk
@ -80,7 +88,23 @@ func (key AccessKeyInstallation) GetPath() string {
return util.Config.TmpPath + "/access_key_" + strconv.FormatInt(key.InstallationKey, 10)
}
func (key *AccessKey) Install(usage AccessKeyRole) (installation AccessKeyInstallation, err error) {
func (key *AccessKey) startSshAgent(logger lib.Logger) (lib.SshAgent, error) {
sshAgent := lib.SshAgent{
Logger: logger,
Keys: []lib.SshAgentKey{
{
Key: []byte(key.SshKey.PrivateKey),
Passphrase: []byte(key.SshKey.Passphrase),
},
},
SocketFile: path.Join(util.Config.TmpPath, fmt.Sprintf("ssh-agent-%d-%d.sock", time.Now().Unix(), 0)),
}
return sshAgent, sshAgent.Listen()
}
func (key *AccessKey) Install(usage AccessKeyRole, logger lib.Logger) (installation AccessKeyInstallation, err error) {
rnd, err := rand.Int(rand.Reader, big.NewInt(1000000000))
if err != nil {
return
@ -92,7 +116,7 @@ func (key *AccessKey) Install(usage AccessKeyRole) (installation AccessKeyInstal
return
}
path := installation.GetPath()
installationPath := installation.GetPath()
err = key.DeserializeSecret()
@ -104,12 +128,16 @@ func (key *AccessKey) Install(usage AccessKeyRole) (installation AccessKeyInstal
case AccessKeyRoleGit:
switch key.Type {
case AccessKeySSH:
err = ioutil.WriteFile(path, []byte(key.SshKey.PrivateKey+"\n"), 0600)
var agent lib.SshAgent
agent, err = key.startSshAgent(logger)
installation.SshAgent = &agent
//err = ioutil.WriteFile(installationPath, []byte(key.SshKey.PrivateKey+"\n"), 0600)
}
case AccessKeyRoleAnsiblePasswordVault:
switch key.Type {
case AccessKeyLoginPassword:
err = ioutil.WriteFile(path, []byte(key.LoginPassword.Password), 0600)
err = ioutil.WriteFile(installationPath, []byte(key.LoginPassword.Password), 0600)
}
case AccessKeyRoleAnsibleBecomeUser:
switch key.Type {
@ -122,14 +150,17 @@ func (key *AccessKey) Install(usage AccessKeyRole) (installation AccessKeyInstal
if err != nil {
return
}
err = ioutil.WriteFile(path, bytes, 0600)
err = ioutil.WriteFile(installationPath, bytes, 0600)
default:
err = fmt.Errorf("access key type not supported for ansible user")
}
case AccessKeyRoleAnsibleUser:
switch key.Type {
case AccessKeySSH:
err = ioutil.WriteFile(path, []byte(key.SshKey.PrivateKey+"\n"), 0600)
var agent lib.SshAgent
agent, err = key.startSshAgent(logger)
installation.SshAgent = &agent
//err = ioutil.WriteFile(installationPath, []byte(key.SshKey.PrivateKey+"\n"), 0600)
case AccessKeyLoginPassword:
content := make(map[string]string)
content["ansible_user"] = key.LoginPassword.Login
@ -139,7 +170,7 @@ func (key *AccessKey) Install(usage AccessKeyRole) (installation AccessKeyInstal
if err != nil {
return
}
err = ioutil.WriteFile(path, bytes, 0600)
err = ioutil.WriteFile(installationPath, bytes, 0600)
default:
err = fmt.Errorf("access key type not supported for ansible user")

View File

@ -1,14 +1,5 @@
package db
import (
"fmt"
"github.com/ansible-semaphore/semaphore/lib"
"path"
"time"
"github.com/ansible-semaphore/semaphore/util"
)
const (
InventoryStatic = "static"
InventoryStaticYaml = "static-yaml"
@ -33,28 +24,28 @@ type Inventory struct {
Type string `db:"type" json:"type"`
}
func (i *Inventory) StartSshAgent(logger lib.Logger) (lib.SshAgent, error) {
sshAgent := lib.SshAgent{
Logger: logger,
Keys: []lib.SshAgentKey{
{
Key: []byte(i.SSHKey.SshKey.PrivateKey),
Passphrase: []byte(i.SSHKey.SshKey.Passphrase),
},
},
SocketFile: path.Join(util.Config.TmpPath, fmt.Sprintf("ssh-agent-%d-%d.sock", time.Now().Unix(), 0)),
}
if i.BecomeKeyID != nil {
sshAgent.Keys = append(sshAgent.Keys, lib.SshAgentKey{
Key: []byte(i.BecomeKey.SshKey.PrivateKey),
Passphrase: []byte(i.BecomeKey.SshKey.Passphrase),
})
}
return sshAgent, sshAgent.Listen()
}
//func (i *Inventory) StartSshAgent(logger lib.Logger) (lib.SshAgent, error) {
//
// sshAgent := lib.SshAgent{
// Logger: logger,
// Keys: []lib.SshAgentKey{
// {
// Key: []byte(i.SSHKey.SshKey.PrivateKey),
// Passphrase: []byte(i.SSHKey.SshKey.Passphrase),
// },
// },
// SocketFile: path.Join(util.Config.TmpPath, fmt.Sprintf("ssh-agent-%d-%d.sock", time.Now().Unix(), 0)),
// }
//
// if i.BecomeKeyID != nil {
// sshAgent.Keys = append(sshAgent.Keys, lib.SshAgentKey{
// Key: []byte(i.BecomeKey.SshKey.PrivateKey),
// Passphrase: []byte(i.BecomeKey.SshKey.Passphrase),
// })
// }
//
// return sshAgent, sshAgent.Listen()
//}
func FillInventory(d Store, inventory *Inventory) (err error) {
if inventory.SSHKeyID != nil {

View File

@ -20,7 +20,8 @@ func (c CmdGitClient) makeCmd(r GitRepository, targetDir GitRepositoryDirType, a
cmd.Env = os.Environ()
cmd.Env = append(cmd.Env, fmt.Sprintln("GIT_TERMINAL_PROMPT=0"))
if r.Repository.SSHKey.Type == db.AccessKeySSH {
sshCmd := "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i " + c.keyInstallation.GetPath()
cmd.Env = append(cmd.Env, fmt.Sprintf("SSH_AUTH_SOCK=%s", c.keyInstallation.SshAgent.SocketFile))
sshCmd := "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
if util.Config.SshConfigPath != "" {
sshCmd += " -F " + util.Config.SshConfigPath
}
@ -43,7 +44,7 @@ func (c CmdGitClient) makeCmd(r GitRepository, targetDir GitRepositoryDirType, a
func (c CmdGitClient) run(r GitRepository, targetDir GitRepositoryDirType, args ...string) error {
var err error
c.keyInstallation, err = r.Repository.SSHKey.Install(db.AccessKeyRoleGit)
c.keyInstallation, err = r.Repository.SSHKey.Install(db.AccessKeyRoleGit, r.Logger)
if err != nil {
return err
@ -59,7 +60,7 @@ func (c CmdGitClient) run(r GitRepository, targetDir GitRepositoryDirType, args
}
func (c CmdGitClient) output(r GitRepository, targetDir GitRepositoryDirType, args ...string) (out string, err error) {
c.keyInstallation, err = r.Repository.SSHKey.Install(db.AccessKeyRoleGit)
c.keyInstallation, err = r.Repository.SSHKey.Install(db.AccessKeyRoleGit, r.Logger)
if err != nil {
return
}

View File

@ -10,14 +10,14 @@ import (
func (t *LocalJob) installInventory() (err error) {
if t.Inventory.SSHKeyID != nil {
t.sshKeyInstallation, err = t.Inventory.SSHKey.Install(db.AccessKeyRoleAnsibleUser)
t.sshKeyInstallation, err = t.Inventory.SSHKey.Install(db.AccessKeyRoleAnsibleUser, t.Logger)
if err != nil {
return
}
}
if t.Inventory.BecomeKeyID != nil {
t.becomeKeyInstallation, err = t.Inventory.BecomeKey.Install(db.AccessKeyRoleAnsibleBecomeUser)
t.becomeKeyInstallation, err = t.Inventory.BecomeKey.Install(db.AccessKeyRoleAnsibleBecomeUser, t.Logger)
if err != nil {
return
}
@ -41,3 +41,20 @@ func (t *LocalJob) installStaticInventory() error {
// create inventory file
return ioutil.WriteFile(path, []byte(t.Inventory.Inventory), 0664)
}
func (t *LocalJob) destroyKeys() {
err := t.sshKeyInstallation.Destroy()
if err != nil {
t.Log("Can't destroy inventory user key, error: " + err.Error())
}
err = t.becomeKeyInstallation.Destroy()
if err != nil {
t.Log("Can't destroy inventory become user key, error: " + err.Error())
}
err = t.vaultFileInstallation.Destroy()
if err != nil {
t.Log("Can't destroy inventory vault password file, error: " + err.Error())
}
}

View File

@ -216,23 +216,6 @@ func (t *LocalJob) getPlaybookArgs(username string, incomingVersion *string) (ar
return
}
func (t *LocalJob) destroyKeys() {
err := t.sshKeyInstallation.Destroy()
if err != nil {
t.Log("Can't destroy inventory user key, error: " + err.Error())
}
err = t.becomeKeyInstallation.Destroy()
if err != nil {
t.Log("Can't destroy inventory become user key, error: " + err.Error())
}
err = t.vaultFileInstallation.Destroy()
if err != nil {
t.Log("Can't destroy inventory vault password file, error: " + err.Error())
}
}
func (t *LocalJob) Run(username string, incomingVersion *string) (err error) {
t.SetStatus(lib.TaskRunningStatus)
@ -258,16 +241,16 @@ func (t *LocalJob) Run(username string, incomingVersion *string) (err error) {
if t.Inventory.SSHKey.Type == db.AccessKeySSH && t.Inventory.SSHKeyID != nil {
var sshAgent lib.SshAgent
sshAgent, err = t.Inventory.StartSshAgent(t.Logger)
//var sshAgent lib.SshAgent
//sshAgent, err = t.Inventory.StartSshAgent(t.Logger)
//
//if err != nil {
// return
//}
//
//defer sshAgent.Close()
if err != nil {
return
}
defer sshAgent.Close()
environmentVariables = append(environmentVariables, fmt.Sprintf("SSH_AUTH_SOCK=%s", sshAgent.SocketFile))
environmentVariables = append(environmentVariables, fmt.Sprintf("SSH_AUTH_SOCK=%s", t.sshKeyInstallation.SshAgent.SocketFile))
}
return t.Playbook.RunPlaybook(args, &environmentVariables, func(p *os.Process) {
@ -484,7 +467,7 @@ func (t *LocalJob) installVaultKeyFile() (err error) {
return nil
}
t.vaultFileInstallation, err = t.Template.VaultKey.Install(db.AccessKeyRoleAnsiblePasswordVault)
t.vaultFileInstallation, err = t.Template.VaultKey.Install(db.AccessKeyRoleAnsiblePasswordVault, t.Logger)
return
}