Skip to content

Commit 6491df4

Browse files
committed
feat: add SSH settings for device and namespace
Add allow-based SSH settings for both namespace and device configuration, update the backend migrations and auth flow, and wire the React console to edit the new settings consistently. Closes #6136
1 parent afc8753 commit 6491df4

68 files changed

Lines changed: 2608 additions & 237 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

agent/installer.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ func registerInstallerCommands(rootCmd *cobra.Command) {
8888
installCmd.Flags().String("preferred-identity", "", "Preferred device identity")
8989
installCmd.Flags().Uint("keepalive-interval", 30, "Keepalive interval in seconds")
9090
installCmd.MarkFlagRequired("server-address") //nolint:errcheck
91-
installCmd.MarkFlagRequired("tenant-id") //nolint:errcheck
91+
installCmd.MarkFlagRequired("tenant-id") //nolint:errcheck
9292

9393
rootCmd.AddCommand(installCmd)
9494

@@ -169,7 +169,7 @@ func writeAgentEnvFile(cfg installerConfig) error {
169169
fmt.Fprintf(&buf, "SHELLHUB_KEEPALIVE_INTERVAL=%d\n", cfg.KeepaliveInterval)
170170
}
171171

172-
return os.WriteFile(agentEnvFile, buf.Bytes(), 0600)
172+
return os.WriteFile(agentEnvFile, buf.Bytes(), 0o600)
173173
}
174174

175175
func writeAgentServiceFile(binaryPath string) error {
@@ -183,7 +183,7 @@ func writeAgentServiceFile(binaryPath string) error {
183183
return err
184184
}
185185

186-
return os.WriteFile(agentServiceFile, buf.Bytes(), 0644)
186+
return os.WriteFile(agentServiceFile, buf.Bytes(), 0o644)
187187
}
188188

189189
func agentUninstall() error {

api/routes/device.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,10 @@ func (h *Handler) UpdateDevice(c gateway.Context) error {
251251
return err
252252
}
253253

254+
if c.Tenant() != nil {
255+
req.TenantID = c.Tenant().ID
256+
}
257+
254258
if err := h.service.UpdateDevice(c.Ctx(), req); err != nil {
255259
return err
256260
}

api/services/device.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,40 @@ func (s *service) UpdateDevice(ctx context.Context, req *requests.DeviceUpdate)
349349
device.Name = strings.ToLower(req.Name)
350350
}
351351

352+
if req.SSH != nil {
353+
if device.SSH == nil {
354+
device.SSH = models.DefaultSSHSettings()
355+
}
356+
357+
if req.SSH.AllowPassword != nil {
358+
device.SSH.AllowPassword = *req.SSH.AllowPassword
359+
}
360+
if req.SSH.AllowPublicKey != nil {
361+
device.SSH.AllowPublicKey = *req.SSH.AllowPublicKey
362+
}
363+
if req.SSH.AllowRoot != nil {
364+
device.SSH.AllowRoot = *req.SSH.AllowRoot
365+
}
366+
if req.SSH.AllowEmptyPasswords != nil {
367+
device.SSH.AllowEmptyPasswords = *req.SSH.AllowEmptyPasswords
368+
}
369+
if req.SSH.AllowTTY != nil {
370+
device.SSH.AllowTTY = *req.SSH.AllowTTY
371+
}
372+
if req.SSH.AllowTCPForwarding != nil {
373+
device.SSH.AllowTCPForwarding = *req.SSH.AllowTCPForwarding
374+
}
375+
if req.SSH.AllowWebEndpoints != nil {
376+
device.SSH.AllowWebEndpoints = *req.SSH.AllowWebEndpoints
377+
}
378+
if req.SSH.AllowSFTP != nil {
379+
device.SSH.AllowSFTP = *req.SSH.AllowSFTP
380+
}
381+
if req.SSH.AllowAgentForwarding != nil {
382+
device.SSH.AllowAgentForwarding = *req.SSH.AllowAgentForwarding
383+
}
384+
}
385+
352386
if err := s.store.DeviceUpdate(ctx, device); err != nil { // nolint:revive
353387
return err
354388
}
@@ -376,6 +410,10 @@ func (s *service) mergeDevice(ctx context.Context, tenantID string, oldDevice *m
376410
}
377411

378412
log.WithFields(logFields).Debug("updating new device name to preserve old device identity")
413+
if oldDevice.SSH != nil {
414+
newDevice.SSH = oldDevice.SSH
415+
}
416+
379417
newDevice.Name = oldDevice.Name
380418
if err := s.store.DeviceUpdate(ctx, newDevice); err != nil {
381419
log.WithError(err).WithFields(logFields).Error("failed to update new device name")

api/services/device_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1699,13 +1699,35 @@ func TestUpdateDeviceStatus(t *testing.T) {
16991699
TenantID: "00000000-0000-0000-0000-000000000000",
17001700
Status: models.DeviceStatusAccepted,
17011701
Identity: &models.DeviceIdentity{MAC: "aa:bb:cc:dd:ee:ff"},
1702+
SSH: &models.SSHSettings{
1703+
AllowPassword: false,
1704+
AllowPublicKey: true,
1705+
AllowRoot: false,
1706+
AllowEmptyPasswords: true,
1707+
AllowTTY: false,
1708+
AllowTCPForwarding: true,
1709+
AllowWebEndpoints: false,
1710+
AllowSFTP: true,
1711+
AllowAgentForwarding: false,
1712+
},
17021713
}
17031714
mergedDevice := &models.Device{
17041715
UID: "new-device",
17051716
Name: "device-name",
17061717
TenantID: "00000000-0000-0000-0000-000000000000",
17071718
Status: models.DeviceStatusPending,
17081719
Identity: &models.DeviceIdentity{MAC: "aa:bb:cc:dd:ee:ff"},
1720+
SSH: &models.SSHSettings{
1721+
AllowPassword: false,
1722+
AllowPublicKey: true,
1723+
AllowRoot: false,
1724+
AllowEmptyPasswords: true,
1725+
AllowTTY: false,
1726+
AllowTCPForwarding: true,
1727+
AllowWebEndpoints: false,
1728+
AllowSFTP: true,
1729+
AllowAgentForwarding: false,
1730+
},
17091731
}
17101732
finalDevice := &models.Device{
17111733
UID: "new-device",
@@ -1714,6 +1736,17 @@ func TestUpdateDeviceStatus(t *testing.T) {
17141736
Status: models.DeviceStatusAccepted,
17151737
StatusUpdatedAt: now,
17161738
Identity: &models.DeviceIdentity{MAC: "aa:bb:cc:dd:ee:ff"},
1739+
SSH: &models.SSHSettings{
1740+
AllowPassword: false,
1741+
AllowPublicKey: true,
1742+
AllowRoot: false,
1743+
AllowEmptyPasswords: true,
1744+
AllowTTY: false,
1745+
AllowTCPForwarding: true,
1746+
AllowWebEndpoints: false,
1747+
AllowSFTP: true,
1748+
AllowAgentForwarding: false,
1749+
},
17171750
}
17181751

17191752
storeMock.

api/services/namespace.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,15 @@ func (s *service) CreateNamespace(ctx context.Context, req *requests.NamespaceCr
6666
Settings: &models.NamespaceSettings{
6767
SessionRecord: true,
6868
ConnectionAnnouncement: "",
69+
AllowPassword: true,
70+
AllowPublicKey: true,
71+
AllowRoot: true,
72+
AllowEmptyPasswords: true,
73+
AllowTTY: true,
74+
AllowTCPForwarding: true,
75+
AllowWebEndpoints: true,
76+
AllowSFTP: true,
77+
AllowAgentForwarding: true,
6978
},
7079
TenantID: req.TenantID,
7180
Type: models.NewDefaultType(),
@@ -176,6 +185,42 @@ func (s *service) EditNamespace(ctx context.Context, req *requests.NamespaceEdit
176185
namespace.Settings.ConnectionAnnouncement = *req.Settings.ConnectionAnnouncement
177186
}
178187

188+
if req.Settings.AllowPassword != nil {
189+
namespace.Settings.AllowPassword = *req.Settings.AllowPassword
190+
}
191+
192+
if req.Settings.AllowPublicKey != nil {
193+
namespace.Settings.AllowPublicKey = *req.Settings.AllowPublicKey
194+
}
195+
196+
if req.Settings.AllowRoot != nil {
197+
namespace.Settings.AllowRoot = *req.Settings.AllowRoot
198+
}
199+
200+
if req.Settings.AllowEmptyPasswords != nil {
201+
namespace.Settings.AllowEmptyPasswords = *req.Settings.AllowEmptyPasswords
202+
}
203+
204+
if req.Settings.AllowTTY != nil {
205+
namespace.Settings.AllowTTY = *req.Settings.AllowTTY
206+
}
207+
208+
if req.Settings.AllowTCPForwarding != nil {
209+
namespace.Settings.AllowTCPForwarding = *req.Settings.AllowTCPForwarding
210+
}
211+
212+
if req.Settings.AllowWebEndpoints != nil {
213+
namespace.Settings.AllowWebEndpoints = *req.Settings.AllowWebEndpoints
214+
}
215+
216+
if req.Settings.AllowSFTP != nil {
217+
namespace.Settings.AllowSFTP = *req.Settings.AllowSFTP
218+
}
219+
220+
if req.Settings.AllowAgentForwarding != nil {
221+
namespace.Settings.AllowAgentForwarding = *req.Settings.AllowAgentForwarding
222+
}
223+
179224
if err := s.store.NamespaceUpdate(ctx, namespace); err != nil {
180225
return nil, err
181226
}

0 commit comments

Comments
 (0)