forked from getsops/sops
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement
sops publish
command (getsops#473)
* Implement `sops publish` command Publishes a file to a pre-configured destination (this lives in the sops config file). Additionally, support re-encryption rules that work just like the creation rules. Initial support for S3/GCS. This is a part of the sops-workspace v2.0 project Includes the addition of a new dependency: github.com/googleapis/gax-go/v2 * code review changes; support global --verbose flag * Switch to recreation_rule with full support Reencryption rule is now recreation rule and supports everything that a creation rule does. Now, when you load a config for a file, you load either the creation rule or the destination rule. I'm not sure about this style long term, but it allows for support to be added for the recreation rules without a bigger refactor of how the config file works. * split loadForFileFromBytes into two functions remove branching based on destination rule or not, create one for creation rules and one for destination rules * pretty diff for keygroup updates in sops publish
- Loading branch information
Showing
38 changed files
with
1,972 additions
and
145 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
package publish | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"io/ioutil" | ||
"os" | ||
"path/filepath" | ||
|
||
"go.mozilla.org/sops" | ||
"go.mozilla.org/sops/cmd/sops/codes" | ||
"go.mozilla.org/sops/cmd/sops/common" | ||
"go.mozilla.org/sops/config" | ||
"go.mozilla.org/sops/keyservice" | ||
"go.mozilla.org/sops/logging" | ||
"go.mozilla.org/sops/version" | ||
|
||
"github.com/sirupsen/logrus" | ||
) | ||
|
||
var log *logrus.Logger | ||
|
||
func init() { | ||
log = logging.NewLogger("PUBLISH") | ||
} | ||
|
||
type Opts struct { | ||
Interactive bool | ||
Cipher sops.Cipher | ||
ConfigPath string | ||
InputPath string | ||
KeyServices []keyservice.KeyServiceClient | ||
InputStore sops.Store | ||
} | ||
|
||
func Run(opts Opts) error { | ||
var fileContents []byte | ||
path, err := filepath.Abs(opts.InputPath) | ||
if err != nil { | ||
return err | ||
} | ||
info, err := os.Stat(path) | ||
if err != nil { | ||
return err | ||
} | ||
if info.IsDir() { | ||
return fmt.Errorf("can't operate on a directory") | ||
} | ||
_, fileName := filepath.Split(path) | ||
|
||
conf, err := config.LoadDestinationRuleForFile(opts.ConfigPath, opts.InputPath, make(map[string]*string)) | ||
if err != nil { | ||
return err | ||
} | ||
if conf.Destination == nil { | ||
return errors.New("no destination configured for this file") | ||
} | ||
|
||
// Check that this is a sops-encrypted file | ||
tree, err := common.LoadEncryptedFile(opts.InputStore, opts.InputPath) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// Re-encrypt if settings exist to do so | ||
if len(conf.KeyGroups[0]) != 0 { | ||
log.Debug("Re-encrypting tree before publishing") | ||
_, err = common.DecryptTree(common.DecryptTreeOpts{ | ||
Cipher: opts.Cipher, | ||
IgnoreMac: false, | ||
Tree: tree, | ||
KeyServices: opts.KeyServices, | ||
}) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
diffs := common.DiffKeyGroups(tree.Metadata.KeyGroups, conf.KeyGroups) | ||
keysWillChange := false | ||
for _, diff := range diffs { | ||
if len(diff.Added) > 0 || len(diff.Removed) > 0 { | ||
keysWillChange = true | ||
} | ||
} | ||
if keysWillChange { | ||
fmt.Printf("The following changes will be made to the file's key groups:\n") | ||
common.PrettyPrintDiffs(diffs) | ||
} | ||
|
||
tree.Metadata = sops.Metadata{ | ||
KeyGroups: conf.KeyGroups, | ||
UnencryptedSuffix: conf.UnencryptedSuffix, | ||
EncryptedSuffix: conf.EncryptedSuffix, | ||
Version: version.Version, | ||
ShamirThreshold: conf.ShamirThreshold, | ||
} | ||
|
||
dataKey, errs := tree.GenerateDataKeyWithKeyServices(opts.KeyServices) | ||
if len(errs) > 0 { | ||
err = fmt.Errorf("Could not generate data key: %s", errs) | ||
return err | ||
} | ||
|
||
err = common.EncryptTree(common.EncryptTreeOpts{ | ||
DataKey: dataKey, | ||
Tree: tree, | ||
Cipher: opts.Cipher, | ||
}) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
fileContents, err = opts.InputStore.EmitEncryptedFile(*tree) | ||
if err != nil { | ||
return common.NewExitError(fmt.Sprintf("Could not marshal tree: %s", err), codes.ErrorDumpingTree) | ||
} | ||
} else { | ||
fileContents, err = ioutil.ReadFile(path) | ||
if err != nil { | ||
return fmt.Errorf("could not read file: %s", err) | ||
} | ||
} | ||
|
||
if opts.Interactive { | ||
var response string | ||
for response != "y" && response != "n" { | ||
fmt.Printf("uploading %s to %s ? (y/n): ", path, conf.Destination.Path(fileName)) | ||
_, err := fmt.Scanln(&response) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
if response == "n" { | ||
return errors.New("Publish canceled") | ||
} | ||
} | ||
|
||
err = conf.Destination.Upload(fileContents, fileName) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func min(a, b int) int { | ||
if a < b { | ||
return a | ||
} | ||
return b | ||
} |
Oops, something went wrong.