Skip to content

Commit

Permalink
add a safe mode, conflict resolver (#50)
Browse files Browse the repository at this point in the history
* add a safe mode, conflict resolver

* remove comments

* update readme
  • Loading branch information
therealpaulgg authored Apr 3, 2024
1 parent 01849da commit 6a0ff2b
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 46 deletions.
28 changes: 25 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ During setup, you'll be prompted to choose between using your own server or the

### Uploading Keys

**NOTE**: Uploading keys will overwrite your existing keys on the server. Later versions of ssh-sync may include a conflict resolution for upload, but it is currently not implemented.

To upload your SSH keys and configuration to the server, run:

```shell
Expand All @@ -72,8 +74,6 @@ This command securely transmits your SSH keys and configuration to the chosen se

### Downloading Keys

**NOTE**: ssh-sync currently downloads files to a directory named `.ssh-sync-data`. This is to avoid altering a user's ssh keys as this data is sensitive. v1 release of ssh-sync will include a toggle to use this mode, or to use .ssh-sync directly, and will also include additional safety features like checking with the user before replacing files that have different content.

To download your SSH keys to a new or existing machine, ensuring it's set up for remote access, use:

```shell
Expand All @@ -82,6 +82,26 @@ ssh-sync download

This command fetches your SSH keys from the server, setting up your SSH environment on the machine.

#### Conflict Resolution

In case there is a difference between a local file and one on your server, ssh-sync will let you know and you can opt to overwrite, skip, or save a file with a `.duplicate` extension for you to review manually.

```shell
diff detected for my_key.
1. Overwrite
2. Skip
3. Save new file (as .duplicate extension for manual resolution)
Please choose an option (will skip by default):
```

#### Safe Mode

If you simply want to download your keys to a temporary directory, and not interfere with your primary .ssh directory, you may also do this by using the `--safe-mode` (or `-s`) flag.

```shell
ssh-sync download --safe-mode
```

### Challenge Response

If setting up a new machine with an existing account, use:
Expand All @@ -103,11 +123,13 @@ ssh-sync list-machines
If you need to remove a machine from your SSH-Sync account, use:

```shell
ssh-sync remove-machine
ssh-sync remove-machine <machine-name>
```

Specify the machine you wish to remove following the command.

You may optionally provide the machine name on the command line so you don't have to type it in when running the command.

### Reset

To remove the current machine from your account and clear all SSH-Sync data:
Expand Down
8 changes: 3 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
github.com/google/uuid v1.3.0
github.com/lestrrat-go/jwx/v2 v2.0.21
github.com/samber/lo v1.37.0
github.com/stretchr/testify v1.9.0
github.com/urfave/cli/v2 v2.23.7
)

Expand All @@ -17,7 +18,7 @@ require (
github.com/gobwas/httphead v0.1.0 // indirect
github.com/gobwas/pool v0.2.1 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/kr/pretty v0.1.0 // indirect
github.com/lestrrat-go/blackmagic v1.0.2 // indirect
github.com/lestrrat-go/httpcc v1.0.1 // indirect
github.com/lestrrat-go/httprc v1.0.5 // indirect
Expand All @@ -26,13 +27,10 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/segmentio/asm v1.2.0 // indirect
github.com/stretchr/testify v1.9.0 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
golang.org/x/crypto v0.21.0 // indirect
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
37 changes: 7 additions & 30 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@ github.com/gobwas/ws v1.1.0 h1:7RFti/xnNkMJnrK7D1yQ/iCIB5OrrY/54/H930kIbHA=
github.com/gobwas/ws v1.1.0/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k=
github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU=
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
Expand Down Expand Up @@ -46,42 +49,16 @@ github.com/urfave/cli/v2 v2.23.7 h1:YHDQ46s3VghFHFf1DdF+Sh7H4RqhcM+t0TmZRJx4oJY=
github.com/urfave/cli/v2 v2.23.7/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM=
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57 h1:LQmS1nU0twXLA96Kt7U9qtHJEbBk3z6Q0V4UXjZkpr4=
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023 h1:0c3L82FDQ5rt1bjTBlchS8t6RQ6299/+5bWMnRLh+uI=
golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
9 changes: 8 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,14 @@ func main() {
Action: actions.Upload,
},
{
Name: "download",
Name: "download",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "safe-mode",
Aliases: []string{"s"},
Usage: "Safe mode will sync to an alternate directory (.ssh-sync-data)",
},
},
Action: actions.Download,
},
{
Expand Down
13 changes: 10 additions & 3 deletions pkg/actions/download.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,18 +61,25 @@ func Download(c *cli.Context) error {
}
data.Keys[i].Data = decryptedKey
}
isSafeMode := c.Bool("safe-mode")
var directory string
if isSafeMode {
fmt.Println("Executing in safe mode (keys writing to .ssh-sync-data)")
directory = ".ssh-sync-data"
} else {
directory = ".ssh"
}
if err := utils.WriteConfig(lo.Map(data.SshConfig, func(config dto.SshConfigDto, i int) models.Host {
return models.Host{
Host: config.Host,
Values: config.Values,
IdentityFiles: config.IdentityFiles,
}
})); err != nil {
}), directory); err != nil {
return err
}
for _, key := range data.Keys {

if err := utils.WriteKey(key.Data, key.Filename); err != nil {
if err := utils.WriteKey(key.Data, key.Filename, directory); err != nil {
return err
}
}
Expand Down
38 changes: 34 additions & 4 deletions pkg/utils/write.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package utils

import (
"bufio"
"errors"
"fmt"
"os"
Expand All @@ -10,12 +11,12 @@ import (
"github.com/therealpaulgg/ssh-sync/pkg/models"
)

func WriteConfig(hosts []models.Host) error {
func WriteConfig(hosts []models.Host, sshDirectory string) error {
user, err := user.Current()
if err != nil {
return err
}
p := filepath.Join(user.HomeDir, "/.ssh-sync-data")
p := filepath.Join(user.HomeDir, sshDirectory)
if _, err := os.Stat(p); errors.Is(err, os.ErrNotExist) {

if err := os.MkdirAll(p, 0700); err != nil {
Expand Down Expand Up @@ -49,17 +50,45 @@ func WriteConfig(hosts []models.Host) error {
return nil
}

func WriteKey(key []byte, filename string) error {
func WriteKey(key []byte, filename string, sshDirectory string) error {
user, err := user.Current()
if err != nil {
return err
}
p := filepath.Join(user.HomeDir, "/.ssh-sync-data")
p := filepath.Join(user.HomeDir, sshDirectory)
if _, err := os.Stat(p); errors.Is(err, os.ErrNotExist) {
if err := os.MkdirAll(p, 0700); err != nil {
return err
}
}
_, err = os.OpenFile(filepath.Join(p, filename), os.O_RDONLY, 0600)
if err != nil && !errors.Is(err, os.ErrNotExist) {
return err
} else if err == nil {
existingData, err := os.ReadFile(filepath.Join(p, filename))
if err != nil {
return err
}
if string(existingData) != string(key) {
var answer string
scanner := bufio.NewScanner(os.Stdin)
fmt.Printf("diff detected for %s.\n", filename)
fmt.Println("1. Overwrite")
fmt.Println("2. Skip")
fmt.Println("3. Save new file (as .duplicate extension for manual resolution)")
fmt.Print("Please choose an option (will skip by default): ")

if err := ReadLineFromStdin(scanner, &answer); err != nil {
return err
}
fmt.Println()
if answer == "3" {
filename = filename + ".duplicate"
} else if answer == "2" || answer != "1" {
return nil
}
}
}
file, err := os.OpenFile(filepath.Join(p, filename), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
return err
Expand All @@ -68,5 +97,6 @@ func WriteKey(key []byte, filename string) error {
if _, err := file.Write(key); err != nil {
return err
}

return nil
}

0 comments on commit 6a0ff2b

Please sign in to comment.