Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement blackbox in Golang #250

Merged
merged 76 commits into from
Jul 24, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
ff792cf
vendor github.com/pkg/errors vendor/github.com/urfave/cli
tlimoncelli Apr 21, 2018
57702a1
New command: blackbox: Parses subcommands and calls bash scripts.
tlimoncelli Apr 21, 2018
3a5e4b6
Implement status and nlist commands
tlimoncelli Apr 22, 2018
5681903
Add "blackbox admin nlist" command.
tlimoncelli Apr 22, 2018
d393d7e
First draft of ndecrypt
tlimoncelli Apr 22, 2018
a6b9c17
Merge branch 'master' into golang
tlimoncelli Jul 3, 2018
3cc7e46
Partial commit
tlimoncelli Sep 10, 2018
e1e6dee
Merge branch 'master' into golang
tlimoncelli Sep 10, 2018
b62ec37
Merge branch 'master' into golang
tlimoncelli Dec 4, 2018
5671b71
Merge branch 'master' into golang
tlimoncelli Dec 9, 2018
ffde3ae
Restructure
tlimoncelli Dec 9, 2018
a5cd829
wip!
tlimoncelli Dec 18, 2018
a47f038
Merge branch 'master' into golang
tlimoncelli Mar 3, 2019
36e1774
Merge branch 'master' into golang
Apr 19, 2020
1fb5701
NEW: DESIGN.md
Apr 19, 2020
b3e052b
Merge branch 'master' into golang
tlimoncelli Jun 6, 2020
c9bd587
tmp
tlimoncelli Jun 6, 2020
a630c6b
flags.go and parse.go complete
tlimoncelli Jun 6, 2020
0bdffb3
Draft
tlimoncelli Jun 6, 2020
a6ec716
Box discovers admins and files.
tlimoncelli Jun 6, 2020
50fa2e5
Snapshot
tlimoncelli Jun 7, 2020
d7bdd49
Fixed the signature mismatch problem!!!
tlimoncelli Jun 7, 2020
ac8105c
linting
tlimoncelli Jun 7, 2020
4979113
Implement Discover()
tlimoncelli Jun 7, 2020
bc5dab7
Implement: files list
tlimoncelli Jun 7, 2020
2425c0f
"blackbox status" works.
tlimoncelli Jun 7, 2020
bf1f1e2
Unvendor
tlimoncelli Jun 7, 2020
148aa3d
Unvendor
tlimoncelli Jun 7, 2020
6c260d1
Cleanup
tlimoncelli Jun 7, 2020
126f60f
cleanup
tlimoncelli Jun 7, 2020
edad8a8
snapshot
tlimoncelli Jun 7, 2020
66eeea2
snapshot!
tlimoncelli Jun 7, 2020
9998758
Rename pkgs
tlimoncelli Jun 7, 2020
8c329b1
linting
tlimoncelli Jun 7, 2020
56665df
Implement decrypt --askagent and --umask
tlimoncelli Jun 8, 2020
f860c73
Random fixes
tlimoncelli Jun 8, 2020
b3aa045
snapshot
tlimoncelli Jun 8, 2020
df2bb78
integration test framework works
tlimoncelli Jun 9, 2020
164376f
Integration tests work all the way through "init".
tlimoncelli Jun 10, 2020
2c8400c
init completed
tlimoncelli Jun 11, 2020
b544a02
Mostly working
tlimoncelli Jun 12, 2020
e831a5a
logging works
tlimoncelli Jun 12, 2020
0a0068d
fix logging
tlimoncelli Jun 12, 2020
a189a4a
wip!
tlimoncelli Jun 14, 2020
e0e61f5
wip!
tlimoncelli Jun 15, 2020
7276fae
Now working: init, admin add, file add, encrypt, decrypt, shred
tlimoncelli Jun 15, 2020
b17a11a
Cat works!
tlimoncelli Jun 16, 2020
a47bc5d
cat and other commands now work. shell commands replaced
tlimoncelli Jun 16, 2020
fa2a643
create binv2
tlimoncelli Jun 19, 2020
0518006
integration tests working
tlimoncelli Jun 19, 2020
b963da0
Now works on CentOS 7
TomOnTime Jun 19, 2020
b3e9e52
aRough out Docs
tlimoncelli Jun 23, 2020
eccea8d
fix comment
tlimoncelli Jul 2, 2020
ac66afd
linting
tlimoncelli Jul 2, 2020
bea3af3
Refactor how NeedsCommit is done
tlimoncelli Jul 2, 2020
4f79084
add missing files
tlimoncelli Jul 3, 2020
56ecdc8
git commit messages are more pretty
tlimoncelli Jul 3, 2020
433b357
cleanup
tlimoncelli Jul 3, 2020
c0d1883
Fixing commits
tlimoncelli Jul 3, 2020
0eb9fd1
bx.* dirs are now relative
tlimoncelli Jul 4, 2020
afcec7d
linting
tlimoncelli Jul 4, 2020
6b8deb4
Clean up redact
tlimoncelli Jul 4, 2020
7ab79e0
Fix bug: filenames with spaces dont work
tlimoncelli Jul 4, 2020
6ff8d0d
macos doesn't need gpg-agent
tlimoncelli Jul 4, 2020
68a17b5
Fix umask display. All --all commands now work from any directory
tlimoncelli Jul 5, 2020
fdb2559
Fix --debug. Fix --group
tlimoncelli Jul 5, 2020
fa53022
Fix links
tlimoncelli Jul 5, 2020
b3b54fa
Refactor makesafe
tlimoncelli Jul 5, 2020
259ba65
cleanig up code
tlimoncelli Jul 6, 2020
b6d4fee
linting
tlimoncelli Jul 6, 2020
f886ea5
mDocs
tlimoncelli Jul 14, 2020
ccc2439
fix reencrypt commit messages
tlimoncelli Jul 17, 2020
92e791a
Fix
tlimoncelli Jul 18, 2020
9e088db
Rewrite shred detector
tlimoncelli Jul 18, 2020
4c47af8
do some integration tests from a funny subdir
tlimoncelli Jul 18, 2020
053b8a6
Rewrite shred detector
tlimoncelli Jul 18, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Now working: init, admin add, file add, encrypt, decrypt, shred
  • Loading branch information
tlimoncelli committed Jun 15, 2020
commit 7276fae1b51407f60107c653b35b535d4d03916b
9 changes: 5 additions & 4 deletions cmd/blackbox/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,9 @@ func flags() *cli.App {
EnvVars: []string{"BLACKBOX_UMASK", "DECRYPT_UMASK"},
},
&cli.BoolFlag{
Name: "debug",
Usage: "Show debug output",
Name: "debug",
Usage: "Show debug output",
EnvVars: []string{"BLACKBOX_DEBUG"},
},
}

Expand Down Expand Up @@ -142,7 +143,7 @@ func flags() *cli.App {
Name: "add",
Usage: "Registers file with the system",
Flags: []cli.Flag{
&cli.BoolFlag{Name: "leave", Usage: "Do not remove plaintext version"},
&cli.BoolFlag{Name: "shred", Usage: "Remove plaintext afterwords"},
},
Action: func(c *cli.Context) error { return cmdFileAdd(c) },
},
Expand All @@ -168,7 +169,7 @@ func flags() *cli.App {

{
Name: "shred",
Usage: "Shred the plaintext",
Usage: "Shred files, or --all for all registered files",
Flags: []cli.Flag{
&cli.BoolFlag{Name: "all", Usage: "All registered files"},
},
Expand Down
10 changes: 5 additions & 5 deletions cmd/blackbox/drive.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@ func allOrSomeFiles(c *cli.Context) error {
// Keep these functions in alphabetical order.

func cmdAdminAdd(c *cli.Context) error {
if !c.Args().Present() {
if c.NArg() == 0 || c.NArg() > 2 {
return fmt.Errorf(
"Must specify at least one admin's GnuPG user-id (i.e. email address)")
"Must specify one admin's GnuPG user-id (i.e. email address) and optionally the directory of the pubkey data (default ~/.GnuPG)")
}
bx := box.NewFromFlags(c)
return bx.AdminAdd(c.Args().Slice())
return bx.AdminAdd(c.Args().Get(0), c.Args().Get(1))
}

func cmdAdminList(c *cli.Context) error {
Expand Down Expand Up @@ -115,7 +115,7 @@ func cmdFileAdd(c *cli.Context) error {
return fmt.Errorf("Must specify at least one file name")
}
bx := box.NewFromFlags(c)
return bx.FileAdd(c.Args().Slice(), c.Bool("overwrite"))
return bx.FileAdd(c.Args().Slice(), c.Bool("shred"))
}

func cmdFileList(c *cli.Context) error {
Expand Down Expand Up @@ -163,7 +163,7 @@ func cmdShred(c *cli.Context) error {
return err
}
bx := box.NewFromFlags(c)
return bx.Shred(c.Args().Slice()...)
return bx.Shred(c.Args().Slice())
}

func cmdStatus(c *cli.Context) error {
Expand Down
15 changes: 15 additions & 0 deletions integrationTest/asserts.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"io/ioutil"
"os"
"testing"

"github.com/andreyvit/diff"
)

func assertFileMissing(t *testing.T, name string) {
Expand Down Expand Up @@ -41,6 +43,19 @@ func assertFileEmpty(t *testing.T, name string) {
}
}

func assertFileContents(t *testing.T, name string, contents string) {
t.Helper()
c, err := ioutil.ReadFile(name)
if err != nil {
t.Fatal(err)
}

if w, g := contents, string(c); w != g {
t.Errorf("assertFileContents(%q) mismatch (-got +want):\n%s",
name, diff.LineDiff(g, w))
}
}

func assertFilePerms(t *testing.T, name string, perms os.FileMode) {
t.Helper()
s, err := os.Stat(name)
Expand Down
55 changes: 38 additions & 17 deletions integrationTest/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,17 @@ func TestStatus(t *testing.T) {
checkOutput(t, "status", "000-status.txt")
}

func TestShred(t *testing.T) {
compile(t)
makeHomeDir(t, "shred")
runBB(t, "init", "yes")

makeFile(t, "shredme.txt", "File with SHREDME in it.")
assertFileExists(t, "shredme.txt")
runBB(t, "shred", "shredme.txt")
assertFileMissing(t, "shredme.txt")
}

func TestStatus_notreg(t *testing.T) {
compile(t)
makeHomeDir(t, "init")
Expand Down Expand Up @@ -123,36 +134,39 @@ func TestBasic(t *testing.T) {
phase("Alice creates a repo. Creates secret.txt.")
makeFile(t, "secret.txt", "this is my secret")

phase("Alice creates a GPG key...")
makeAdmin(t, "alice", "Alice Example", "alice@example.com")
phase("Alice creates a GPG key")
gpgdir := makeAdmin(t, "alice", "Alice Example", "alice@example.com")
become(t, "alice")

runBB(t, "admin", "add", "alice@example.com")

}

func TestAlice(t *testing.T) {
// Create an empty repo with a user named Alice who
// performs many operations. All files are valid.
compile(t)
setup(t)
populateDummyRepo(t, *vcsToTest)
phase("Alice enrolls as an admin")
runBB(t, "admin", "add", "alice@example.com", gpgdir)

// encrypt
runBB(t, "encrypt", "foo.txt")
phase("Alice registers foo.txt")
plaintextFoo := "I am the foo.txt file!\n"
makeFile(t, "foo.txt", plaintextFoo)
runBB(t, "file", "add", "--shred", "foo.txt")
//runBB(t, "encrypt", "--shred", "foo.txt")
// We shred the plaintext so that we are sure that when Decrypt runs,
// we can verify the contents wasn't just sitting there all the time.
assertFileMissing(t, "foo.txt")
assertFileExists(t, "foo.txt.gpg")

phase("Alice decrypts foo.txt")
// decrypt
runBB(t, "decrypt", "foo.txt")
assertFileExists(t, "foo.txt")
assertFileExists(t, "foo.txt.gpg")
assertFileContents(t, "foo.txt", plaintextFoo)

// reencrypt
// encrypts (without shredding)
phase("Alice encrypts foo.txt (again)")
runBB(t, "encrypt", "foo.txt")
assertFileExists(t, "foo.txt")
assertFileExists(t, "foo.txt.gpg")
assertFileContents(t, "foo.txt", plaintextFoo)

// edit
invalidArgs(t, "edit")
invalidArgs(t, "edit", "--all")
// reencrypt

// cat

Expand All @@ -167,4 +181,11 @@ func TestAlice(t *testing.T) {
// setupUser(t, "bob", "b")
// runBB(t, "init")
// runBB(t, "admin", "add", "alice@")

// FYI: test "admins add" with multiple people.

// Edit requires a name, and doesn't work with --all.
// invalidArgs(t, "edit")
// invalidArgs(t, "edit", "--all")

// }
15 changes: 5 additions & 10 deletions integrationTest/ithelpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,16 +105,9 @@ func makeHomeDir(t *testing.T, testname string) {
}
}

func populateDummyRepo(t *testing.T, vcsname string) {
func createDummyFilesAdmin(t *testing.T) {
// This creates a repo with real data, except any .gpg file
// is just garbage.

t.Helper()
logDebug.Printf("populateDummyRepo()\n")

}

func createDummyFilesAdmin(t *testing.T) {
addLineSorted(t, ".blackbox/blackbox-admins.txt", "user1@example.com")
addLineSorted(t, ".blackbox/blackbox-admins.txt", "user2@example.com")
addLineSorted(t, ".blackbox/blackbox-files.txt", "foo.txt")
Expand Down Expand Up @@ -305,7 +298,7 @@ func phase(msg string) {
logDebug.Println("********************")
}

func makeAdmin(t *testing.T, name, fullname, email string) {
func makeAdmin(t *testing.T, name, fullname, email string) string {
testing.Init()

dir, err := filepath.Abs(filepath.Join(os.Getenv("HOME"), ".gnupg-"+name))
Expand Down Expand Up @@ -337,8 +330,10 @@ func makeAdmin(t *testing.T, name, fullname, email string) {
"--homedir", u.dir,
"--batch",
"--passphrase", "",
"--quick-generate-key", "u.email",
"--quick-generate-key", u.email,
)

return u.dir
}

func become(t *testing.T, name string) {
Expand Down
3 changes: 3 additions & 0 deletions integrationTest/run-tests-verbose.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash

rm -rf /tmp/bbhome-* && BLACKBOX_DEBUG=true go test -verbose "$@"
3 changes: 3 additions & 0 deletions integrationTest/run-tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash

rm -rf /tmp/bbhome-* && go test "$@"
4 changes: 3 additions & 1 deletion models/crypters.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,7 @@ type Crypter interface {
// Decrypt name+".gpg", possibly overwriting name.
Decrypt(filename string, umask int, overwrite bool) error
// Encrypt name, overwriting name+".gpg"
Encrypt(filename string, umask int, receivers []string) error
Encrypt(filename string, umask int, receivers []string) (string, error)
// AddNewKey extracts keyname from sourcedir's GnuPG chain to destdir keychain.
AddNewKey(keyname, sourcedir, destdir string) ([]string, error)
}
2 changes: 1 addition & 1 deletion models/vcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type Vcs interface {
// IgnoreAnywhere tells the VCS to ignore these files anywhere rin the repo.
IgnoreAnywhere(repobasedir string, files ...string) error
// SuggestTracking tells the VCS to suggest the user commit these files.
SuggestTracking(repobasedir string, message string, files ...string) error
SuggestTracking(repobasedir string, message string, files []string) error

// TestingInitRepo initializes a repo of this type (for use by integration tests)
TestingInitRepo() error
Expand Down
31 changes: 31 additions & 0 deletions pkg/bbutil/filestats.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"sort"
"strings"
"time"
Expand Down Expand Up @@ -49,6 +50,36 @@ func Touch(name string) error {
return os.Chtimes(name, currentTime, currentTime)
}

// ShredFiles securely erases a list of files.
func ShredFiles(names []string) error {
var path, flag string
var err error
if path, err = exec.LookPath("shred"); err == nil {
flag = "-u"
} else if path, err = exec.LookPath("srm"); err == nil {
flag = "-f"
} else if path, err = exec.LookPath("rm"); err == nil {
flag = "-f"
// FIXME(tlim): Test if "rm -P $tempfile" returns a error.
// If it doesn't, flag = "-Pf"
}

// TODO(tlim) DO the shredding in parallel like in v1.

for _, n := range names {
fmt.Printf("SHREDDING (%q, %q): %q\n", path, flag, n)
e := RunBash(path, flag, n)
if e != nil {
err = e
fmt.Printf("ERROR: %v", e)
} else {
fmt.Println()
}
}
fmt.Println("DONE.")
return err
}

// ReadFileLines is like ioutil.ReadFile() but returns an []string.
func ReadFileLines(filename string) ([]string, error) {
b, err := ioutil.ReadFile(filename)
Expand Down
17 changes: 16 additions & 1 deletion pkg/bbutil/runbash.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package bbutil

import (
"bytes"
"fmt"
"log"
"os"
Expand All @@ -24,7 +25,7 @@ func RunBash(command string, args ...string) error {
return nil
}

// RunBash runs a Bash command.
// RunBashOutput runs a Bash command, captures output.
func RunBashOutput(command string, args ...string) (string, error) {
cmd := exec.Command(command, args...)
cmd.Stdin = os.Stdin
Expand All @@ -35,3 +36,17 @@ func RunBashOutput(command string, args ...string) (string, error) {
}
return string(out), err
}

// RunBashInput runs a Bash command, sends input on stdin.
func RunBashInput(input string, command string, args ...string) error {

cmd := exec.Command(command, args...)
cmd.Stdin = bytes.NewBuffer([]byte(input))
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
return fmt.Errorf("RunBashInput err=%w", err)
}
return err
}
22 changes: 14 additions & 8 deletions pkg/box/box.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ package box

import (
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"sort"
"strings"

"github.com/StackExchange/blackbox/v2/pkg/bblog"
Expand Down Expand Up @@ -194,9 +194,13 @@ func (bx *Box) getAdmins() error {
bx.logDebug.Printf("Admins file: %q", fn)
a, err := bbutil.ReadFileLines(fn)
if err != nil {
return fmt.Errorf("getAdmins can't load admins (%q): %v", fn, err)
return fmt.Errorf("getAdmins can't load %q: %v", fn, err)
}
if !sort.StringsAreSorted(a) {
return fmt.Errorf("file corrupt. Lines not sorted: %v", fn)
}
bx.Admins = a

return nil
}

Expand All @@ -210,15 +214,17 @@ func (bx *Box) getFiles() error {

// Try the legacy file:
fn := filepath.Join(bx.ConfigDir, "blackbox-files.txt")
b, err := ioutil.ReadFile(fn)
bx.logDebug.Printf("Files file: %q", fn)
a, err := bbutil.ReadFileLines(fn)
if err != nil {
return fmt.Errorf("getFiles can't read %q: %v", fn, err)
return fmt.Errorf("getFiles can't load %q: %v", fn, err)
}
if !sort.StringsAreSorted(a) {
return fmt.Errorf("file corrupt. Lines not sorted: %v", fn)
}
bx.Files = a

c := strings.TrimSpace(string(b))

bx.Files = strings.Split(c, "\n")
bx.FilesSet = make(map[string]bool)
bx.FilesSet = make(map[string]bool, len(bx.Files))
for _, s := range bx.Files {
bx.FilesSet[s] = true
}
Expand Down
Loading