summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--awwan.go7
-rw-r--r--awwan_test.go64
-rw-r--r--script_example_test.go2
-rw-r--r--session.go73
-rw-r--r--testdata/encrypt/.awwan.env.plain2
-rw-r--r--testdata/encrypt/.awwan.env.vaultbin0 -> 384 bytes
l---------testdata/encrypt/.awwan.key1
-rw-r--r--testdata/encrypt/.ssh/empty0
-rw-r--r--testdata/encrypt/local.aww1
-rw-r--r--testdata/encrypt/sub/.awwan.env.plain2
-rw-r--r--testdata/encrypt/sub/.awwan.env.vaultbin0 -> 384 bytes
-rw-r--r--testdata/encrypt/sub/local.aww1
-rw-r--r--testdata/encrypt/test.data9
13 files changed, 137 insertions, 25 deletions
diff --git a/awwan.go b/awwan.go
index 18f6a9e..a3fbddc 100644
--- a/awwan.go
+++ b/awwan.go
@@ -49,6 +49,9 @@ const (
// defEncryptExt default file extension for encrypted file.
const defEncryptExt = `.vault`
+// defFileEnvVault default awwan environment file name that is encrypted.
+const defFileEnvVault = `.awwan.env.vault`
+
// defFilePrivateKey define the default private key file name.
const defFilePrivateKey = `.awwan.key`
@@ -225,7 +228,7 @@ func (aww *Awwan) Local(req *Request) (err error) {
sessionDir = filepath.Dir(req.scriptPath)
- ses, err = NewSession(aww.BaseDir, sessionDir)
+ ses, err = NewSession(aww, sessionDir)
if err != nil {
return fmt.Errorf("%s: %w", logp, err)
}
@@ -294,7 +297,7 @@ func (aww *Awwan) Play(req *Request) (err error) {
sessionDir = filepath.Dir(req.scriptPath)
- ses, err = NewSession(aww.BaseDir, sessionDir)
+ ses, err = NewSession(aww, sessionDir)
if err != nil {
return fmt.Errorf("%s: %w", logp, err)
}
diff --git a/awwan_test.go b/awwan_test.go
index 64f6ec0..4e2a17a 100644
--- a/awwan_test.go
+++ b/awwan_test.go
@@ -1,6 +1,7 @@
package awwan
import (
+ "bytes"
"os"
"path/filepath"
"testing"
@@ -142,3 +143,66 @@ func TestAwwanEncrypt(t *testing.T) {
}
}
}
+
+func TestAwwanLocal_withEncryption(t *testing.T) {
+ type testCase struct {
+ script string
+ lineRange string
+ tdataOut string
+ }
+
+ var (
+ tdata *test.Data
+ err error
+ )
+
+ tdata, err = test.LoadData(`testdata/encrypt/test.data`)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var (
+ basedir = filepath.Join(`testdata`, `encrypt`)
+ mockout = bytes.Buffer{}
+ mockerr = bytes.Buffer{}
+ mockrw = mock.ReadWriter{}
+ aww = Awwan{}
+ )
+
+ // Mock terminal to read passphrase for private key.
+ mockrw.BufRead.WriteString("s3cret\r")
+ aww.termrw = &mockrw
+
+ err = aww.init(basedir)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var cases = []testCase{{
+ script: filepath.Join(basedir, `local.aww`),
+ lineRange: `1`,
+ tdataOut: `local.aww:1`,
+ }, {
+ script: filepath.Join(basedir, `sub`, `local.aww`),
+ lineRange: `1`,
+ tdataOut: `sub/local.aww:1`,
+ }}
+
+ var c testCase
+
+ for _, c = range cases {
+ var req = NewRequest(CommandModeLocal, c.script, c.lineRange)
+
+ mockout.Reset()
+ mockerr.Reset()
+ req.stdout = &mockout
+ req.stderr = &mockerr
+
+ err = aww.Local(req)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ test.Assert(t, `stdout`, string(tdata.Output[c.tdataOut]), mockout.String())
+ }
+}
diff --git a/script_example_test.go b/script_example_test.go
index 724dafd..def544a 100644
--- a/script_example_test.go
+++ b/script_example_test.go
@@ -28,7 +28,7 @@ end;
stmt []byte
)
- err = ses.loadEnvFromBytes([]byte(envContent))
+ err = ses.loadRawEnv([]byte(envContent))
if err != nil {
log.Fatal(err)
}
diff --git a/session.go b/session.go
index 443b6ae..528cb87 100644
--- a/session.go
+++ b/session.go
@@ -4,6 +4,7 @@
package awwan
import (
+ "crypto/rsa"
"fmt"
"os"
"os/exec"
@@ -21,9 +22,11 @@ import (
// Session manage and cache SSH client and list of scripts.
// One session have one SSH client, but may contains more than one script.
type Session struct {
- sftpc *sftp.Client
- sshClient *ssh.Client
- vars *ini.Ini
+ privateKey *rsa.PrivateKey
+ sftpc *sftp.Client
+ sshClient *ssh.Client
+
+ vars ini.Ini
BaseDir string
ScriptDir string
@@ -40,7 +43,7 @@ type Session struct {
// NewSession create and initialize the new session based on Awwan base
// directory and the session directory.
-func NewSession(baseDir, sessionDir string) (ses *Session, err error) {
+func NewSession(aww *Awwan, sessionDir string) (ses *Session, err error) {
var (
logp = "newSession"
@@ -48,7 +51,9 @@ func NewSession(baseDir, sessionDir string) (ses *Session, err error) {
)
ses = &Session{
- BaseDir: baseDir,
+ privateKey: aww.privateKey,
+
+ BaseDir: aww.BaseDir,
ScriptDir: sessionDir,
hostname: filepath.Base(sessionDir),
}
@@ -394,7 +399,7 @@ func (ses *Session) executeScriptOnLocal(req *Request, pos linePosition) {
continue
}
- fmt.Fprintf(req.stdout, "\n>>> local: %3d: %s\n", x, stmt.raw)
+ fmt.Fprintf(req.stdout, "\n--> local: %3d: %s\n", x, stmt.raw)
var err error
switch stmt.kind {
@@ -437,7 +442,7 @@ func (ses *Session) executeScriptOnRemote(req *Request, pos linePosition) {
continue
}
- fmt.Fprintf(req.stdout, "\n>>> %s: %3d: %s %s\n",
+ fmt.Fprintf(req.stdout, "\n--> %s: %3d: %s %s\n",
ses.sshClient, x, stmt.cmd, stmt.args)
var err error
@@ -535,42 +540,66 @@ func (ses *Session) loadEnvFromPaths() (err error) {
path string
awwanEnv string
- content []byte
)
for _, path = range ses.paths {
+ // Load unencrypted "awwan.env".
awwanEnv = filepath.Join(path, defEnvFileName)
- content, err = os.ReadFile(awwanEnv)
+ err = ses.loadFileEnv(awwanEnv, false)
if err != nil {
- if os.IsNotExist(err) {
- continue
- }
- return fmt.Errorf("%s: %s: %w", logp, awwanEnv, err)
+ return fmt.Errorf(`%s: %w`, logp, err)
}
- fmt.Printf(">>> loading %q ...\n", awwanEnv)
- err = ses.loadEnvFromBytes(content)
+ // Load encrypted ".awwan.env.vault".
+ awwanEnv = filepath.Join(path, defFileEnvVault)
+
+ err = ses.loadFileEnv(awwanEnv, true)
if err != nil {
- return fmt.Errorf("%s: %w", logp, err)
+ return fmt.Errorf(`%s: %w`, logp, err)
}
}
return nil
}
-func (ses *Session) loadEnvFromBytes(content []byte) (err error) {
- in, err := ini.Parse(content)
+func (ses *Session) loadFileEnv(awwanEnv string, isVault bool) (err error) {
+ var content []byte
+
+ content, err = os.ReadFile(awwanEnv)
+ if err != nil {
+ if os.IsNotExist(err) {
+ return nil
+ }
+ return fmt.Errorf(`%s: %w`, awwanEnv, err)
+ }
+
+ fmt.Printf("--- loading %q ...\n", awwanEnv)
+
+ if isVault {
+ content, err = decrypt(ses.privateKey, content)
+ if err != nil {
+ return err
+ }
+ }
+
+ err = ses.loadRawEnv(content)
if err != nil {
return err
}
- in.Prune()
+ return nil
+}
- if ses.vars == nil {
- ses.vars = in
- return nil
+func (ses *Session) loadRawEnv(content []byte) (err error) {
+ var in *ini.Ini
+
+ in, err = ini.Parse(content)
+ if err != nil {
+ return err
}
+ in.Prune()
ses.vars.Rebase(in)
+
return nil
}
diff --git a/testdata/encrypt/.awwan.env.plain b/testdata/encrypt/.awwan.env.plain
new file mode 100644
index 0000000..26ed285
--- /dev/null
+++ b/testdata/encrypt/.awwan.env.plain
@@ -0,0 +1,2 @@
+[secret]
+pass = this_is_a_secret
diff --git a/testdata/encrypt/.awwan.env.vault b/testdata/encrypt/.awwan.env.vault
new file mode 100644
index 0000000..a4e4bdd
--- /dev/null
+++ b/testdata/encrypt/.awwan.env.vault
Binary files differ
diff --git a/testdata/encrypt/.awwan.key b/testdata/encrypt/.awwan.key
new file mode 120000
index 0000000..aa99eff
--- /dev/null
+++ b/testdata/encrypt/.awwan.key
@@ -0,0 +1 @@
+../encrypt-with-passphrase/.awwan.key \ No newline at end of file
diff --git a/testdata/encrypt/.ssh/empty b/testdata/encrypt/.ssh/empty
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testdata/encrypt/.ssh/empty
diff --git a/testdata/encrypt/local.aww b/testdata/encrypt/local.aww
new file mode 100644
index 0000000..d9127db
--- /dev/null
+++ b/testdata/encrypt/local.aww
@@ -0,0 +1 @@
+echo {{.Val "secret::pass"}}
diff --git a/testdata/encrypt/sub/.awwan.env.plain b/testdata/encrypt/sub/.awwan.env.plain
new file mode 100644
index 0000000..02b2ae0
--- /dev/null
+++ b/testdata/encrypt/sub/.awwan.env.plain
@@ -0,0 +1,2 @@
+[secret]
+pass = this_is_a_secret_in_sub
diff --git a/testdata/encrypt/sub/.awwan.env.vault b/testdata/encrypt/sub/.awwan.env.vault
new file mode 100644
index 0000000..21aa5b1
--- /dev/null
+++ b/testdata/encrypt/sub/.awwan.env.vault
Binary files differ
diff --git a/testdata/encrypt/sub/local.aww b/testdata/encrypt/sub/local.aww
new file mode 100644
index 0000000..d9127db
--- /dev/null
+++ b/testdata/encrypt/sub/local.aww
@@ -0,0 +1 @@
+echo {{.Val "secret::pass"}}
diff --git a/testdata/encrypt/test.data b/testdata/encrypt/test.data
new file mode 100644
index 0000000..9ecdb9f
--- /dev/null
+++ b/testdata/encrypt/test.data
@@ -0,0 +1,9 @@
+<<< local.aww:1
+
+--> local: 1: echo this_is_a_secret
+this_is_a_secret
+
+<<< sub/local.aww:1
+
+--> local: 1: echo this_is_a_secret_in_sub
+this_is_a_secret_in_sub