Skip to content

Commit d37687b

Browse files
committed
feat(refresh): add user public key to authorized_keys on refresh
When keys are refreshed (brev refresh, brev shell, brev open, etc.), write the user's public key to ~/.ssh/authorized_keys if not already present. Appends without overwriting existing keys.
1 parent 7567a87 commit d37687b

3 files changed

Lines changed: 67 additions & 6 deletions

File tree

pkg/cmd/refresh/refresh.go

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ type RefreshStore interface {
2222
ssh.SSHConfigurerV2Store
2323
GetCurrentUser() (*entity.User, error)
2424
GetCurrentUserKeys() (*entity.UserKeys, error)
25+
WriteAuthorizedKey(publicKey string) error
2526
Chmod(string, fs.FileMode) error
2627
MkdirAll(string, fs.FileMode) error
2728
GetBrevCloudflaredBinaryPath() (string, error)
@@ -62,7 +63,7 @@ func RunRefreshBetter(store RefreshStore) error {
6263
return breverrors.WrapAndTrace(err)
6364
}
6465

65-
cu, err := GetConfigUpdater(store)
66+
cu, keys, err := GetConfigUpdater(store)
6667
if err != nil {
6768
return breverrors.WrapAndTrace(err)
6869
}
@@ -72,6 +73,11 @@ func RunRefreshBetter(store RefreshStore) error {
7273
return breverrors.WrapAndTrace(err)
7374
}
7475

76+
err = store.WriteAuthorizedKey(keys.PublicKey)
77+
if err != nil {
78+
return breverrors.WrapAndTrace(err)
79+
}
80+
7581
privateKeyPath, err := store.GetPrivateKeyPath()
7682
if err != nil {
7783
return breverrors.WrapAndTrace(err)
@@ -91,7 +97,7 @@ func RunRefresh(store RefreshStore) error {
9197
return breverrors.WrapAndTrace(err)
9298
}
9399

94-
cu, err := GetConfigUpdater(store)
100+
cu, keys, err := GetConfigUpdater(store)
95101
if err != nil {
96102
return breverrors.WrapAndTrace(err)
97103
}
@@ -101,6 +107,11 @@ func RunRefresh(store RefreshStore) error {
101107
return breverrors.WrapAndTrace(err)
102108
}
103109

110+
err = store.WriteAuthorizedKey(keys.PublicKey)
111+
if err != nil {
112+
return breverrors.WrapAndTrace(err)
113+
}
114+
104115
privateKeyPath, err := store.GetPrivateKeyPath()
105116
if err != nil {
106117
return breverrors.WrapAndTrace(err)
@@ -139,20 +150,20 @@ func RunRefreshAsync(rstore RefreshStore) *RefreshRes {
139150
return &res
140151
}
141152

142-
func GetConfigUpdater(store RefreshStore) (*ssh.ConfigUpdater, error) {
153+
func GetConfigUpdater(store RefreshStore) (*ssh.ConfigUpdater, *entity.UserKeys, error) {
143154
configs, err := ssh.GetSSHConfigs(store)
144155
if err != nil {
145-
return nil, breverrors.WrapAndTrace(err)
156+
return nil, nil, breverrors.WrapAndTrace(err)
146157
}
147158

148159
keys, err := store.GetCurrentUserKeys()
149160
if err != nil {
150-
return nil, breverrors.WrapAndTrace(err)
161+
return nil, nil, breverrors.WrapAndTrace(err)
151162
}
152163

153164
cu := ssh.NewConfigUpdater(store, configs, keys.PrivateKey)
154165

155-
return cu, nil
166+
return cu, keys, nil
156167
}
157168

158169
func GetCloudflare(refreshStore RefreshStore) store.Cloudflared {

pkg/files/files.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"os"
88
"os/exec"
99
"path/filepath"
10+
"strings"
1011

1112
breverrors "github.com/brevdev/brev-cli/pkg/errors"
1213
"golang.org/x/text/encoding/charmap"
@@ -75,6 +76,10 @@ func GetSSHPrivateKeyPath(home string) string {
7576
return fpath
7677
}
7778

79+
func GetAuthorizedKeysPath(home string) string {
80+
return filepath.Join(home, ".ssh", "authorized_keys")
81+
}
82+
7883
func GetUserSSHConfigPath(home string) (string, error) {
7984
sshConfigPath := filepath.Join(home, ".ssh", "config")
8085
return sshConfigPath, nil
@@ -210,6 +215,39 @@ func OverwriteJSON(fs afero.Fs, filepath string, v interface{}) error {
210215

211216
// write
212217

218+
// WriteAuthorizedKey ensures the given public key is present in ~/.ssh/authorized_keys.
219+
// It appends the key only if it's not already there.
220+
func WriteAuthorizedKey(fs afero.Fs, publicKey string, home string) error {
221+
authorizedKeysPath := GetAuthorizedKeysPath(home)
222+
err := fs.MkdirAll(filepath.Dir(authorizedKeysPath), 0o700)
223+
if err != nil {
224+
return breverrors.WrapAndTrace(err)
225+
}
226+
227+
publicKey = strings.TrimSpace(publicKey)
228+
229+
existing, err := afero.ReadFile(fs, authorizedKeysPath)
230+
if err != nil && !os.IsNotExist(err) {
231+
return breverrors.WrapAndTrace(err)
232+
}
233+
234+
if strings.Contains(string(existing), publicKey) {
235+
return nil
236+
}
237+
238+
content := string(existing)
239+
if len(content) > 0 && !strings.HasSuffix(content, "\n") {
240+
content += "\n"
241+
}
242+
content += publicKey + "\n"
243+
244+
err = afero.WriteFile(fs, authorizedKeysPath, []byte(content), 0o600)
245+
if err != nil {
246+
return breverrors.WrapAndTrace(err)
247+
}
248+
return nil
249+
}
250+
213251
func WriteSSHPrivateKey(fs afero.Fs, data string, home string) error {
214252
pkPath := GetSSHPrivateKeyPath(home)
215253
err := fs.MkdirAll(filepath.Dir(pkPath), defaultFilePermission)

pkg/store/ssh.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,18 @@ func (f FileStore) WritePrivateKey(pem string) error {
235235
return nil
236236
}
237237

238+
func (f FileStore) WriteAuthorizedKey(publicKey string) error {
239+
home, err := f.UserHomeDir()
240+
if err != nil {
241+
return breverrors.WrapAndTrace(err)
242+
}
243+
err = files.WriteAuthorizedKey(f.fs, publicKey, home)
244+
if err != nil {
245+
return breverrors.WrapAndTrace(err)
246+
}
247+
return nil
248+
}
249+
238250
func (f FileStore) GetPrivateKeyPath() (string, error) {
239251
home, err := f.UserHomeDir()
240252
if err != nil {

0 commit comments

Comments
 (0)