Skip to content

Commit

Permalink
Merge pull request #65 from uw-labs/as-recursive-decrypt
Browse files Browse the repository at this point in the history
implemented recursive decryption via cli
  • Loading branch information
asiyani authored Sep 16, 2022
2 parents 83e2491 + 7c97e64 commit 6aa6d74
Show file tree
Hide file tree
Showing 10 changed files with 320 additions and 61 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v2
uses: actions/setup-go@v3
with:
go-version: 1.17
go-version: 1.19
- name: Test
run: make test
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2
uses: goreleaser/goreleaser-action@v3
with:
# either 'goreleaser' (default) or 'goreleaser-pro'
distribution: goreleaser
Expand Down
58 changes: 29 additions & 29 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ name: "CodeQL"

on:
push:
branches: [ master ]
branches: [master]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
branches: [master]
schedule:
- cron: '30 11 * * 3'
- cron: "30 11 * * 3"

jobs:
analyze:
Expand All @@ -32,40 +32,40 @@ jobs:
strategy:
fail-fast: false
matrix:
language: [ 'go' ]
language: ["go"]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed

steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Checkout repository
uses: actions/checkout@v3

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main

# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2

# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl

# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language

#- run: |
# make bootstrap
# make release
#- run: |
# make bootstrap
# make release

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,23 @@ go install github.com/uw-labs/[email protected]
directory structure until it finds the file. This allows using different
keys for different subdirectories within a repository.

5. If strongbox keyring file is stored on different location `-keyring` can be used.
ie `strongbox [-keyring <keyring_file_path>] -gen-key key-name`

6. Following commands can be used to manually decrypt file without gitOps
```
# decrypt using default keyring file `$HOME/.strongbox_keyring`
strongbox -decrypt -recursive <path>
# decrypt using `keyring_file_path`
strongbox -keyring <keyring_file_path> -decrypt -recursive <path>
# decrypt using private key `<key>`
strongbox -key <key> -decrypt -recursive <path>
# decrypt single file with given key
strongbox -decrypt -key <key>
```
## Existing project

Strongbox uses [clean and smudge
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/uw-labs/strongbox

go 1.17
go 1.19

require (
github.com/jacobsa/crypto v0.0.0-20190317225127-9f44e2d11115
Expand Down
5 changes: 0 additions & 5 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,6 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
Expand Down
2 changes: 1 addition & 1 deletion integration_tests/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM golang:1.15-alpine
FROM golang:1.19-alpine

RUN apk --no-cache add git

Expand Down
133 changes: 126 additions & 7 deletions integration_tests/main_test.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
//go:build integration

package main

import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"testing"
Expand Down Expand Up @@ -33,15 +34,23 @@ func assertCommand(t *testing.T, dir, name string, arg ...string) (out []byte) {
}

func assertWriteFile(t *testing.T, filename string, data []byte, perm os.FileMode) {
err := ioutil.WriteFile(filename, data, perm)
err := os.WriteFile(filename, data, perm)
if err != nil {
t.Fatal(err)
}
}

func assertReadFile(t *testing.T, filename string) string {
data, err := os.ReadFile(filename)
if err != nil {
t.Fatal(err)
}
return string(data)
}

func keyIDFromKR(t *testing.T, name string) (keyID string) {
func keysFromKR(t *testing.T, name string) (key, keyID string) {
kr := make(map[string]interface{})
krf, err := ioutil.ReadFile(HOME + "/.strongbox_keyring")
krf, err := os.ReadFile(HOME + "/.strongbox_keyring")
if err != nil {
t.Fatal(err)
}
Expand All @@ -54,11 +63,12 @@ func keyIDFromKR(t *testing.T, name string) (keyID string) {
for k := range kes {
desc := kes[k].(map[interface{}]interface{})["description"].(string)
if name == desc {
return kes[k].(map[interface{}]interface{})["key-id"].(string)
return kes[k].(map[interface{}]interface{})["key"].(string),
kes[k].(map[interface{}]interface{})["key-id"].(string)
}
}
t.Fatal(fmt.Sprintf("no keyId for give desc: %s", name))
return ""
return "", ""
}

func TestMain(m *testing.M) {
Expand Down Expand Up @@ -97,7 +107,7 @@ func TestMain(m *testing.M) {

func TestSimpleEnc(t *testing.T) {
repoDir := HOME + "/test-proj"
keyID := keyIDFromKR(t, "test00")
_, keyID := keysFromKR(t, "test00")
secVal := "secret123wombat"

ga := `secret filter=strongbox diff=strongbox
Expand Down Expand Up @@ -153,3 +163,112 @@ func TestMissingKey(t *testing.T) {
// remove the file
assertCommand(t, "/", "rm", repoDir+"/secrets/sec-missing-key")
}

func TestRecursiveDecryption(t *testing.T) {
repoDir := HOME + "/test-rec-dec"

assertCommand(t, "/", "mkdir", "-p", repoDir+"/secrets/")
assertCommand(t, "/", "mkdir", "-p", repoDir+"/app/secrets/")

assertCommand(t, repoDir, "git", "init")

// generate new private keys
assertCommand(t, "/", "strongbox", "-gen-key", "rec-dec-01")
assertCommand(t, "/", "strongbox", "-gen-key", "rec-dec-02")

pKey1, keyID1 := keysFromKR(t, "rec-dec-01")
pKey2, keyID2 := keysFromKR(t, "rec-dec-02")

secVal := "secret123wombat"

ga := `secret filter=strongbox diff=strongbox
secrets/* filter=strongbox diff=strongbox
*/secrets/* filter=strongbox diff=strongbox`

// setup root keyID and nested app folder with different keyID
assertWriteFile(t, repoDir+"/.gitattributes", []byte(ga), 0644)
assertWriteFile(t, repoDir+"/.strongbox-keyid", []byte(keyID1), 0644)
assertWriteFile(t, repoDir+"/app/.strongbox-keyid", []byte(keyID2), 0644)

// Write plan secrets
assertWriteFile(t, repoDir+"/secret", []byte(secVal+"01"), 0644)
assertWriteFile(t, repoDir+"/secrets/s2", []byte(secVal+"02"), 0644)
assertWriteFile(t, repoDir+"/app/secrets/s3", []byte(secVal+"03"), 0644)

// set test dir as Home because git command will use default strongbox key
assertCommand(t, repoDir, "git", "add", ".")
assertCommand(t, repoDir, "git", "commit", "-m", "\"TestSimpleEnc\"")

// Make sure files are encrypted
ptOut01, _ := command(repoDir, "git", "show", "--", "secret")
encOut01, _ := command(repoDir, "git", "show", "HEAD:secret")
assert.Contains(t, string(ptOut01), secVal+"01", "should be in plain text")
assert.Contains(t, string(encOut01), "STRONGBOX ENCRYPTED RESOURCE", "should be encrypted")

ptOut02, _ := command(repoDir, "git", "show", "--", "secrets/s2")
encOut02, _ := command(repoDir, "git", "show", "HEAD:secrets/s2")
assert.Contains(t, string(ptOut02), secVal+"02", "should be in plain text")
assert.Contains(t, string(encOut02), "STRONGBOX ENCRYPTED RESOURCE", "should be encrypted")

ptOut03, _ := command(repoDir, "git", "show", "--", "app/secrets/s3")
encOut03, _ := command(repoDir, "git", "show", "HEAD:app/secrets/s3")
assert.Contains(t, string(ptOut03), secVal+"03", "should be in plain text")
assert.Contains(t, string(encOut03), "STRONGBOX ENCRYPTED RESOURCE", "should be encrypted")

// TEST 1 (using default keyring file location)
//override local file with encrypted content
assertWriteFile(t, repoDir+"/secret", encOut01, 0644)
assertWriteFile(t, repoDir+"/secrets/s2", encOut02, 0644)
assertWriteFile(t, repoDir+"/app/secrets/s3", encOut03, 0644)

// run command from the root of the target folder without path arg
assertCommand(t, repoDir, "strongbox", "-decrypt", "-recursive")

// make sure all files are decrypted
assert.Contains(t, assertReadFile(t, repoDir+"/secret"), secVal+"01", "should be in plain text")
assert.Contains(t, assertReadFile(t, repoDir+"/secrets/s2"), secVal+"02", "should be in plain text")
assert.Contains(t, assertReadFile(t, repoDir+"/app/secrets/s3"), secVal+"03", "should be in plain text")

// TEST 2 (using custom keyring file location)
// override local file with encrypted content
assertWriteFile(t, repoDir+"/secret", encOut01, 0644)
assertWriteFile(t, repoDir+"/secrets/s2", encOut02, 0644)
assertWriteFile(t, repoDir+"/app/secrets/s3", encOut03, 0644)

keyRingPath := repoDir + "/.keyring"
// move keyring file
assertCommand(t, "/", "mv", HOME+"/.strongbox_keyring", keyRingPath)
// run command from outside of the target folder
assertCommand(t, "/", "strongbox", "-keyring", keyRingPath, "-decrypt", "-recursive", repoDir)

// make sure all files are decrypted
assert.Contains(t, assertReadFile(t, repoDir+"/secret"), secVal+"01", "should be in plain text")
assert.Contains(t, assertReadFile(t, repoDir+"/secrets/s2"), secVal+"02", "should be in plain text")
assert.Contains(t, assertReadFile(t, repoDir+"/app/secrets/s3"), secVal+"03", "should be in plain text")

// TEST 3.1 (using given private key)
// override local file with encrypted content
assertWriteFile(t, repoDir+"/secret", encOut01, 0644)
assertWriteFile(t, repoDir+"/secrets/s2", encOut02, 0644)
assertWriteFile(t, repoDir+"/app/secrets/s3", encOut03, 0644)

//since rec-dec-01 is not used to encrypt app folders secret so expect error
command(repoDir, "strongbox", "-key", pKey1, "-decrypt", "-recursive", ".")

assert.Contains(t, assertReadFile(t, repoDir+"/secret"), secVal+"01", "should be in plain text")
assert.Contains(t, assertReadFile(t, repoDir+"/secrets/s2"), secVal+"02", "should be in plain text")
assert.Contains(t, assertReadFile(t, repoDir+"/app/secrets/s3"), "STRONGBOX ENCRYPTED RESOURCE", "should be encrypted")

// TEST 3.2 (using custom keyring file location)
// override local file with encrypted content
assertWriteFile(t, repoDir+"/secret", encOut01, 0644)
assertWriteFile(t, repoDir+"/secrets/s2", encOut02, 0644)
assertWriteFile(t, repoDir+"/app/secrets/s3", encOut03, 0644)

//since rec-dec-02 is not used to encrypt root folders secrets so expect error
command(repoDir, "strongbox", "-key", pKey2, "-decrypt", "-recursive", ".")

assert.Contains(t, assertReadFile(t, repoDir+"/secret"), "STRONGBOX ENCRYPTED RESOURCE", "should be encrypted")
assert.Contains(t, assertReadFile(t, repoDir+"/secrets/s2"), "STRONGBOX ENCRYPTED RESOURCE", "should be encrypted")
assert.Contains(t, assertReadFile(t, repoDir+"/app/secrets/s3"), secVal+"03", "should be in plain text")
}
2 changes: 1 addition & 1 deletion integration_tests/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ go get -t .
go install

go get -t ./integration_tests/
go test -v ./integration_tests/
go test -v -tags=integration ./integration_tests/
5 changes: 2 additions & 3 deletions keyring.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package main

import (
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
Expand Down Expand Up @@ -57,7 +56,7 @@ func (kr *fileKeyRing) Key(keyID []byte) ([]byte, error) {

func (kr *fileKeyRing) Load() error {

bytes, err := ioutil.ReadFile(kr.fileName)
bytes, err := os.ReadFile(kr.fileName)
if err != nil {
return err
}
Expand All @@ -81,5 +80,5 @@ func (kr *fileKeyRing) Save() error {
}
}

return ioutil.WriteFile(kr.fileName, ser, 0600)
return os.WriteFile(kr.fileName, ser, 0600)
}
Loading

0 comments on commit 6aa6d74

Please sign in to comment.