mirror of
https://github.com/kopia/kopia.git
synced 2026-03-17 21:56:14 -04:00
various tweaks
This commit is contained in:
@@ -15,8 +15,8 @@
|
||||
var (
|
||||
backupsCommand = app.Command("backups", "List backup history.")
|
||||
backupsDirectory = backupsCommand.Arg("directory", "Directory to show history of").ExistingDir()
|
||||
backupsAll = backupsCommand.Flag("all", "Show history of all backups").Bool()
|
||||
maxResultsPerPath = backupsCommand.Flag("maxresults", "Maximum number of results").Default("100").Int()
|
||||
backupsAll = backupsCommand.Flag("all", "Show history of all backups.").Bool()
|
||||
maxResultsPerPath = backupsCommand.Flag("maxresults", "Maximum number of results.").Default("100").Int()
|
||||
)
|
||||
|
||||
func runBackupsCommand(context *kingpin.ParseContext) error {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
)
|
||||
|
||||
var (
|
||||
connectCommand = app.Command("connect", "Create new vault and optionally connect to it")
|
||||
connectCommand = app.Command("connect", "Connect to a vault.")
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"strings"
|
||||
"io"
|
||||
|
||||
"github.com/kopia/kopia/repo"
|
||||
"github.com/kopia/kopia/vault"
|
||||
@@ -12,16 +13,12 @@
|
||||
)
|
||||
|
||||
var (
|
||||
createCommand = app.Command("create", "Create new vault and optionally connect to it")
|
||||
createCommandRepository = createCommand.Flag("repository", "Repository path").Required().String()
|
||||
createCommandOnly = createCommand.Flag("only", "Only create, don't connect.").Bool()
|
||||
|
||||
createMaxBlobSize = createCommand.Flag("max-blob-size", "Maximum size of a data chunk in bytes.").Default("4000000").Int()
|
||||
createInlineBlobSize = createCommand.Flag("inline-blob-size", "Maximum size of an inline data chunk in bytes.").Default("32768").Int()
|
||||
|
||||
createVaultEncryptionFormat = createCommand.Flag("vaultencryption", "Vault encryption format").String()
|
||||
createSecurity = createCommand.Flag("security", "Security mode, one of 'none', 'default' or 'custom'.").Default("default").Enum("none", "default", "custom")
|
||||
createCustomFormat = createCommand.Flag("object-format", "Specifies custom object format to be used").String()
|
||||
createCommand = app.Command("create", "Create new vault.")
|
||||
createCommandRepository = createCommand.Flag("repository", "Repository path.").Required().String()
|
||||
createMaxBlobSize = createCommand.Flag("max-blob-size", "Maximum size of a data chunk in bytes.").Default("20000000").Int()
|
||||
createInlineBlobSize = createCommand.Flag("inline-blob-size", "Maximum size of an inline data chunk in bytes.").Default("32768").Int()
|
||||
createVaultEncryptionFormat = createCommand.Flag("vault-encryption", "Vault encryption format").Default("aes-256").Enum(supportedVaultEncryptionFormats()...)
|
||||
createObjectFormat = createCommand.Flag("object-format", "Specifies custom object format to be used").Default("hmac-sha256").Enum(supportedObjectFormats()...)
|
||||
createOverwrite = createCommand.Flag("overwrite", "Overwrite existing data.").Bool()
|
||||
)
|
||||
|
||||
@@ -29,22 +26,33 @@ func init() {
|
||||
createCommand.Action(runCreateCommand)
|
||||
}
|
||||
|
||||
func vaultFormat() *vault.Format {
|
||||
f := vault.NewFormat()
|
||||
if *createVaultEncryptionFormat != "" {
|
||||
f.Encryption = *createVaultEncryptionFormat
|
||||
func vaultFormat() (*vault.Format, error) {
|
||||
f := &vault.Format{
|
||||
Version: "1",
|
||||
Checksum: "hmac-sha-256",
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func repositoryFormat() (*repo.Format, error) {
|
||||
f, err := repo.NewFormat()
|
||||
f.UniqueID = make([]byte, 32)
|
||||
_, err := io.ReadFull(rand.Reader, f.UniqueID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.Encryption = *createVaultEncryptionFormat
|
||||
return f, nil
|
||||
}
|
||||
|
||||
f.MaxBlobSize = *createMaxBlobSize
|
||||
f.MaxInlineBlobSize = *createInlineBlobSize
|
||||
func repositoryFormat() (*repo.Format, error) {
|
||||
f := &repo.Format{
|
||||
Version: "1",
|
||||
Secret: make([]byte, 32),
|
||||
MaxBlobSize: *createMaxBlobSize,
|
||||
MaxInlineBlobSize: *createInlineBlobSize,
|
||||
ObjectFormat: *createObjectFormat,
|
||||
}
|
||||
|
||||
_, err := io.ReadFull(rand.Reader, f.Secret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return f, nil
|
||||
}
|
||||
@@ -79,77 +87,69 @@ func runCreateCommand(context *kingpin.ParseContext) error {
|
||||
return fmt.Errorf("unable to get repository storage: %v", err)
|
||||
}
|
||||
|
||||
repoFormat, err := repositoryFormat()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to initialize repository format: %v", err)
|
||||
}
|
||||
|
||||
fmt.Printf(
|
||||
"Initializing repository in '%s' with format '%v' and maximum object size %v.\n",
|
||||
repositoryStorage.Configuration().Config.ToURL().String(),
|
||||
repoFormat.ObjectFormat,
|
||||
repoFormat.MaxBlobSize)
|
||||
|
||||
masterKey, password, err := getKeyOrPassword(true)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to get credentials: %v", err)
|
||||
}
|
||||
|
||||
var v *vault.Vault
|
||||
vf, err := vaultFormat()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to initialize vault format: %v", err)
|
||||
}
|
||||
|
||||
fmt.Printf(
|
||||
"Initializing vault in '%s' with encryption '%v'.\n",
|
||||
vaultStorage.Configuration().Config.ToURL().String(),
|
||||
vf.Encryption)
|
||||
if masterKey != nil {
|
||||
v, err = vault.CreateWithKey(vaultStorage, vaultFormat(), masterKey)
|
||||
v, err = vault.CreateWithKey(vaultStorage, vf, masterKey)
|
||||
} else {
|
||||
v, err = vault.CreateWithPassword(vaultStorage, vaultFormat(), password)
|
||||
v, err = vault.CreateWithPassword(vaultStorage, vf, password)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot create vault: %v", err)
|
||||
}
|
||||
|
||||
repoFormat, err := repositoryFormat()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to initialize repository format: %v", err)
|
||||
}
|
||||
|
||||
// Make repository to make sure the format is supported.
|
||||
_, err = repo.NewRepository(repositoryStorage, repoFormat)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to initialize repository: %v", err)
|
||||
}
|
||||
|
||||
v.SetRepository(vault.RepositoryConfig{
|
||||
if err := v.SetRepository(vault.RepositoryConfig{
|
||||
Storage: repositoryStorage.Configuration(),
|
||||
Format: repoFormat,
|
||||
})
|
||||
|
||||
if *createCommandOnly {
|
||||
fmt.Println("Created vault:", *vaultPath)
|
||||
return nil
|
||||
}); err != nil {
|
||||
return fmt.Errorf("unable to save repository configuration in vault: %v", err)
|
||||
}
|
||||
|
||||
persistVaultConfig(v)
|
||||
|
||||
fmt.Println("Created and connected to vault:", *vaultPath)
|
||||
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
func getCustomFormat() string {
|
||||
if *createCustomFormat != "" {
|
||||
if repo.SupportedFormats.Find(*createCustomFormat) == nil {
|
||||
fmt.Printf("Format '%s' is not recognized.\n", *createCustomFormat)
|
||||
}
|
||||
return *createCustomFormat
|
||||
}
|
||||
|
||||
fmt.Printf(" %2v | %-30v | %v | %v | %v |\n", "#", "Format", "Hash", "Encryption", "Block ID Length")
|
||||
fmt.Println(strings.Repeat("-", 76) + "+")
|
||||
for i, o := range repo.SupportedFormats {
|
||||
encryptionString := ""
|
||||
if o.IsEncrypted() {
|
||||
encryptionString = fmt.Sprintf("%d-bit", o.EncryptionKeySizeBits())
|
||||
}
|
||||
fmt.Printf(" %2v | %-30v | %4v | %10v | %15v |\n", i+1, o.Name, o.HashSizeBits(), encryptionString, o.BlockIDLength())
|
||||
}
|
||||
fmt.Println(strings.Repeat("-", 76) + "+")
|
||||
|
||||
fmt.Printf("Select format (1-%d): ", len(repo.SupportedFormats))
|
||||
for {
|
||||
var number int
|
||||
|
||||
if n, err := fmt.Scanf("%d\n", &number); n == 1 && err == nil && number >= 1 && number <= len(repo.SupportedFormats) {
|
||||
fmt.Printf("You selected '%v'\n", repo.SupportedFormats[number-1].Name)
|
||||
return repo.SupportedFormats[number-1].Name
|
||||
}
|
||||
|
||||
fmt.Printf("Invalid selection. Select format (1-%d): ", len(repo.SupportedFormats))
|
||||
func supportedVaultEncryptionFormats() []string {
|
||||
return []string{
|
||||
"none",
|
||||
"aes-128",
|
||||
"aes-256",
|
||||
}
|
||||
}
|
||||
|
||||
func supportedObjectFormats() []string {
|
||||
var r []string
|
||||
for _, o := range repo.SupportedFormats {
|
||||
r = append(r, o.Name)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
@@ -20,10 +21,10 @@
|
||||
traceStorage = app.Flag("trace-storage", "Enables tracing of storage operations.").Hidden().Bool()
|
||||
|
||||
vaultPath = app.Flag("vault", "Specify the vault to use.").Envar("KOPIA_VAULT").String()
|
||||
password = app.Flag("password", "Vault password").Envar("KOPIA_PASSWORD").String()
|
||||
passwordFile = app.Flag("passwordfile", "Read password from a file").Envar("KOPIA_PASSWORD_FILE").ExistingFile()
|
||||
key = app.Flag("key", "Vault key (hexadecimal)").Envar("KOPIA_KEY").String()
|
||||
keyFile = app.Flag("keyfile", "Key key file").Envar("KOPIA_KEY_FILE").ExistingFile()
|
||||
password = app.Flag("password", "Vault password.").Envar("KOPIA_PASSWORD").String()
|
||||
passwordFile = app.Flag("passwordfile", "Read vault password from a file.").Envar("KOPIA_PASSWORD_FILE").ExistingFile()
|
||||
key = app.Flag("key", "Specify vault master key (hexadecimal).").Envar("KOPIA_KEY").String()
|
||||
keyFile = app.Flag("keyfile", "Read vault master key from file.").Envar("KOPIA_KEY_FILE").ExistingFile()
|
||||
)
|
||||
|
||||
func failOnError(err error) {
|
||||
@@ -63,8 +64,15 @@ func persistVaultConfig(v *vault.Vault) error {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
json.NewEncoder(f).Encode(vc)
|
||||
return nil
|
||||
|
||||
b, err := json.MarshalIndent(&vc, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = f.Write(b)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func getPersistedVaultConfig() *vaultConfig {
|
||||
@@ -95,7 +103,7 @@ func openVault() (*vault.Vault, error) {
|
||||
}
|
||||
|
||||
if *vaultPath == "" {
|
||||
return nil, fmt.Errorf("vault not connected and not specified, use --vault")
|
||||
return nil, fmt.Errorf("vault not connected and not specified, use --vault or run 'kopia connect'")
|
||||
}
|
||||
|
||||
return openVaultSpecifiedByFlag()
|
||||
@@ -117,11 +125,13 @@ func openVaultSpecifiedByFlag() (*vault.Vault, error) {
|
||||
|
||||
if masterKey != nil {
|
||||
return vault.OpenWithKey(storage, masterKey)
|
||||
} else {
|
||||
return vault.OpenWithPassword(storage, password)
|
||||
}
|
||||
|
||||
return vault.OpenWithPassword(storage, password)
|
||||
}
|
||||
|
||||
var errPasswordTooShort = errors.New("password too short")
|
||||
|
||||
func getKeyOrPassword(isNew bool) ([]byte, string, error) {
|
||||
if *key != "" {
|
||||
k, err := hex.DecodeString(*key)
|
||||
@@ -153,16 +163,20 @@ func getKeyOrPassword(isNew bool) ([]byte, string, error) {
|
||||
|
||||
return nil, strings.TrimSpace(string(f)), nil
|
||||
}
|
||||
|
||||
if isNew {
|
||||
for {
|
||||
fmt.Printf("Enter password: ")
|
||||
fmt.Printf("Enter password to create new vault: ")
|
||||
p1, err := askPass()
|
||||
fmt.Println()
|
||||
if err == errPasswordTooShort {
|
||||
fmt.Printf("Password too short, must be at least %v characters, you entered %v. Try again.", vault.MinPasswordLength, len(p1))
|
||||
fmt.Println()
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
fmt.Println()
|
||||
fmt.Printf("Enter password again: ")
|
||||
fmt.Printf("Re-enter password for verification: ")
|
||||
p2, err := askPass()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
@@ -175,7 +189,7 @@ func getKeyOrPassword(isNew bool) ([]byte, string, error) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("Enter password: ")
|
||||
fmt.Printf("Enter password to open vault: ")
|
||||
p1, err := askPass()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
@@ -191,5 +205,11 @@ func askPass() (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(b), nil
|
||||
p := string(b)
|
||||
|
||||
if len(p) < vault.MinPasswordLength {
|
||||
return p, errPasswordTooShort
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
)
|
||||
|
||||
var (
|
||||
app = kingpin.New("kopia", "Kopia - Online Backup")
|
||||
app = kingpin.New("kopia", "Kopia - Online Backup").Version("0.0.1").Author("http://kopia.github.io/")
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
Reference in New Issue
Block a user