Skip to content

feat/gen ssh key#3656

Open
fiftin wants to merge 4 commits intodevelopfrom
feat/gen_ssh_key
Open

feat/gen ssh key#3656
fiftin wants to merge 4 commits intodevelopfrom
feat/gen_ssh_key

Conversation

@fiftin
Copy link
Copy Markdown
Collaborator

@fiftin fiftin commented Feb 21, 2026

  • feat(secrets): generate private ssh key on server
  • feat(secrets): gen SSH key on server
  • fix(secrets): show public key for updated secrets too
  • feat(secrets): don't allow user override plain field

@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request implements a server-side SSH key generation feature for Semaphore UI. Users can now optionally generate SSH key pairs on the server instead of providing their own private keys. The generated public key is displayed to users for copying to authorized_keys files on target systems.

Changes:

  • Added server-side SSH key generation using RSA 2048-bit keys with public/private key pair creation
  • Implemented UI components to display generated public keys in a dialog with copy-to-clipboard functionality
  • Extended database schema to store public key metadata in the Plain field for SSH keys
  • Added generate_ssh_key flag to control key generation behavior in the frontend and backend

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
services/server/access_key_svc.go Implements maybeGenerateSSHPrivateKey function to generate SSH keys and store public key in Plain field
db/AccessKey.go Adds GenerateSSHKey transient field for API communication
db/sql/access_key.go Updates SQL queries to persist Plain field for both create and update operations
db/bolt/access_key.go Contains commented implementation note for BoltDB Plain field handling
api/projects/keys.go Returns Plain field in create response to enable frontend public key display
web/src/views/project/Keys.vue Adds public key display dialog with copy functionality after key creation/update
web/src/components/KeyForm.vue Adds generate_ssh_key checkbox and displays public key for existing generated keys

Comment thread web/src/components/KeyForm.vue

<v-checkbox
v-model="item.generate_ssh_key"
label="Generate SSH Key"
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The checkbox label "Generate SSH Key" is hardcoded and not internationalized, while other labels in the form use the i18n translation function. This creates inconsistency with the rest of the codebase where all user-facing strings are translated.

Consider changing label="Generate SSH Key" to :label="$t('generateSshKey')" and adding the corresponding translation keys.

Suggested change
label="Generate SSH Key"
:label="$t('generateSshKey')"

Copilot uses AI. Check for mistakes.
Comment on lines +133 to +147
<div style="position: relative">
<pre
style="
overflow: auto;
background: gray;
color: white;
border-radius: 10px;
margin-top: 5px;
"
class="pa-2"
>{{ publicKey }}</pre
>

<CopyClipboardButton
style="position: absolute; right: 0; top: 0; transform: scale(0.9);"
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inline styles are used extensively for the public key display section. Consider extracting these styles to a scoped <style> section or using CSS classes for better maintainability and consistency. This makes it easier to maintain and update styling across the application.

Suggested change
<div style="position: relative">
<pre
style="
overflow: auto;
background: gray;
color: white;
border-radius: 10px;
margin-top: 5px;
"
class="pa-2"
>{{ publicKey }}</pre
>
<CopyClipboardButton
style="position: absolute; right: 0; top: 0; transform: scale(0.9);"
<div class="public-key-container">
<pre
class="pa-2 public-key-display"
>{{ publicKey }}</pre
>
<CopyClipboardButton
class="public-key-copy-button"

Copilot uses AI. Check for mistakes.
Comment on lines +34 to +41
style="
overflow: auto;
background: gray;
color: white;
border-radius: 10px;
margin-top: 5px;
"
class="pa-2"
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inline styles are duplicated between KeyForm.vue and Keys.vue for displaying the public key. The same styling is applied to the <pre> element in both files (gray background, white color, border-radius, etc.). Consider creating a reusable component or shared styles to avoid this duplication and ensure consistency.

Suggested change
style="
overflow: auto;
background: gray;
color: white;
border-radius: 10px;
margin-top: 5px;
"
class="pa-2"
class="pa-2 mt-1 rounded overflow-auto grey darken-3 white--text"

Copilot uses AI. Check for mistakes.
Comment on lines +155 to +156
const isGeneratedOnCreate = e && e.action === 'new';
const isGeneratedOnUpdate = e && e.action === 'edit' && e.item && e.item.generate_ssh_key;
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic for determining if a public key should be shown on update (line 156) assumes that if generate_ssh_key is true, a key was generated. However, this flag comes from the request body, not the response. If the backend fails to generate the key (but doesn't return an error), or if the key generation is skipped for some reason, the dialog may still appear with an empty public key. Consider adding an additional check to ensure the public key was actually generated before showing the dialog.

Copilot uses AI. Check for mistakes.
Comment on lines +68 to +103
func maybeGenerateSSHPrivateKey(key *db.AccessKey) error {
if !key.GenerateSSHKey || key.Type != db.AccessKeySSH {
key.Plain = nil
return nil
}

var b bytes.Buffer
privateKeyFile := bufio.NewWriter(&b)

publicKey, err := util.GeneratePrivateKey(privateKeyFile)
if err != nil {
return err
}

err = privateKeyFile.Flush()
if err != nil {
return err
}

key.SshKey.PrivateKey = b.String()

type sshPublicKey struct {
PublicKey string `json:"public_key"`
}

plainBytes, err := json.Marshal(sshPublicKey{
PublicKey: publicKey,
})
if err != nil {
return err
}

plain := string(plainBytes)
key.Plain = &plain
return nil
}
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SSH key generation functionality introduced by this PR lacks automated test coverage. Consider adding tests to verify that maybeGenerateSSHPrivateKey correctly generates keys when GenerateSSHKey is true, sets the Plain field with the public key, and properly handles the case when GenerateSSHKey is false. Tests should also verify that the generated private key is valid and properly encrypted.

Example test cases to add:

  • Test that GenerateSSHKey=true generates both private and public keys
  • Test that GenerateSSHKey=false preserves existing private key
  • Test that Plain field is set correctly with public key JSON
  • Test that generated keys are valid RSA keys

Copilot uses AI. Check for mistakes.
v-model="item.generate_ssh_key"
label="Generate SSH Key"
v-if="!isReadOnly && item.type === 'ssh'"
:disabled="formSaving || !canEditSecrets"
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The checkbox for generating SSH keys is only disabled when canEditSecrets is false, but it should also be disabled for existing keys where override_secret is required. When editing an existing key without override_secret checked, users can toggle generate_ssh_key, but this won't have any effect because the secret won't be updated. This creates a confusing user experience.

Consider adding || (!isNew && !item.override_secret) to the disabled condition to make it clear that SSH key generation only works when secrets can be edited.

Suggested change
:disabled="formSaving || !canEditSecrets"
:disabled="formSaving || !canEditSecrets || (!isNew && !item.override_secret)"

Copilot uses AI. Check for mistakes.
:max-width="700"
v-model="createdPublicKeyDialog"
:save-button-text="null"
title="Generated SSH Public Key"
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dialog title "Generated SSH Public Key" is hardcoded and not internationalized. All other strings in the file use the i18n translation function (e.g., $t('create'), $t('save')). This string should be extracted to the translation files for consistency and to support multiple languages.

Consider changing title="Generated SSH Public Key" to title="$t('generatedSshPublicKey')" and adding the corresponding translation keys.

Suggested change
title="Generated SSH Public Key"
:title="$t('generatedSshPublicKey')"

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants