Skip to content

Commit 2e2b205

Browse files
authored
✨ The terminal tool supports password login #1052
2 parents 02890ec + 688a003 commit 2e2b205

File tree

9 files changed

+841
-268
lines changed

9 files changed

+841
-268
lines changed

backend/database/tool_db.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,8 @@ def add_tool_field(tool_info):
164164
# add tool params
165165
tool_params = tool.params
166166
for ele in tool_params:
167-
ele["default"] = tool_info["params"][ele["name"]]
167+
param_name = ele["name"]
168+
ele["default"] = tool_info["params"].get(param_name)
168169

169170
tool_dict = as_dict(tool)
170171
tool_dict["params"] = tool_params

docker/deploy.sh

Lines changed: 75 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -127,127 +127,6 @@ generate_supabase_keys() {
127127
fi
128128
}
129129

130-
generate_ssh_keys() {
131-
# Function to generate SSH key pair for Terminal tool
132-
133-
if [ "$ENABLE_TERMINAL_TOOL" = "true" ]; then
134-
# Create ssh-keys directory
135-
create_dir_with_permission "$ROOT_DIR/openssh-server/ssh-keys" 700
136-
create_dir_with_permission "$ROOT_DIR/openssh-server/config" 755
137-
138-
# Check if SSH keys already exist
139-
if [ -f "$ROOT_DIR/openssh-server/ssh-keys/openssh_server_key" ] && [ -f "$ROOT_DIR/openssh-server/ssh-keys/openssh_server_key.pub" ]; then
140-
echo "🚧 SSH key pair already exists, skipping generation..."
141-
echo "🔑 Private key: $ROOT_DIR/openssh-server/ssh-keys/openssh_server_key"
142-
echo "🗝️ Public key: $ROOT_DIR/openssh-server/ssh-keys/openssh_server_key.pub"
143-
144-
# Ensure authorized_keys is set up correctly with ONLY our public key
145-
cp "$ROOT_DIR/openssh-server/ssh-keys/openssh_server_key.pub" "$ROOT_DIR/openssh-server/config/authorized_keys"
146-
chmod 644 "$ROOT_DIR/openssh-server/config/authorized_keys"
147-
148-
# Set SSH key path in environment
149-
SSH_PRIVATE_KEY_PATH="$ROOT_DIR/openssh-server/ssh-keys/openssh_server_key"
150-
export SSH_PRIVATE_KEY_PATH
151-
152-
# Add to .env file
153-
update_env_var "SSH_PRIVATE_KEY_PATH" "$SSH_PRIVATE_KEY_PATH"
154-
155-
echo ""
156-
echo "--------------------------------"
157-
echo ""
158-
return 0
159-
fi
160-
161-
echo "🔑 Generating SSH key pair for Terminal tool..."
162-
163-
# Generate SSH key pair using Docker (cross-platform compatible)
164-
echo " 🔐 Using Docker to generate SSH key pair..."
165-
166-
# Create temporary file to capture output
167-
TEMP_OUTPUT="/tmp/ssh_keygen_output_$$.txt"
168-
169-
# Generate ed25519 key pair using the openssh-server container
170-
if docker run --rm -i "$OPENSSH_SERVER_IMAGE" bash -c "ssh-keygen -t ed25519 -f /tmp/id_ed25519 -N '' && cat /tmp/id_ed25519 && echo '---' && cat /tmp/id_ed25519.pub" > "$TEMP_OUTPUT" 2>&1; then
171-
echo " 🔍 SSH key generation completed, extracting keys..."
172-
173-
# Extract private key (everything between -----BEGIN and -----END)
174-
PRIVATE_KEY=$(sed -n '/-----BEGIN OPENSSH PRIVATE KEY-----/,/-----END OPENSSH PRIVATE KEY-----/p' "$TEMP_OUTPUT")
175-
176-
# Extract public key (line that starts with ssh-)
177-
PUBLIC_KEY=$(grep "^ssh-" "$TEMP_OUTPUT" | head -1)
178-
179-
# Remove leading/trailing whitespace
180-
PRIVATE_KEY=$(echo "$PRIVATE_KEY" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
181-
PUBLIC_KEY=$(echo "$PUBLIC_KEY" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
182-
183-
# Validate extracted keys
184-
if [ -z "$PRIVATE_KEY" ]; then
185-
echo " ❌ Failed to extract private key"
186-
ERROR_OCCURRED=1
187-
return 1
188-
fi
189-
190-
if [ -z "$PUBLIC_KEY" ]; then
191-
echo " ❌ Failed to extract public key"
192-
ERROR_OCCURRED=1
193-
return 1
194-
fi
195-
196-
echo " ✅ SSH keys extracted successfully"
197-
198-
if [ -n "$PRIVATE_KEY" ] && [ -n "$PUBLIC_KEY" ]; then
199-
# Save private key
200-
echo "$PRIVATE_KEY" > "$ROOT_DIR/openssh-server/ssh-keys/openssh_server_key"
201-
chmod 600 "$ROOT_DIR/openssh-server/ssh-keys/openssh_server_key"
202-
203-
# Save public key
204-
echo "$PUBLIC_KEY" > "$ROOT_DIR/openssh-server/ssh-keys/openssh_server_key.pub"
205-
chmod 644 "$ROOT_DIR/openssh-server/ssh-keys/openssh_server_key.pub"
206-
207-
# Copy public key to authorized_keys with correct permissions (ensure ONLY our key)
208-
cp "$ROOT_DIR/openssh-server/ssh-keys/openssh_server_key.pub" "$ROOT_DIR/openssh-server/config/authorized_keys"
209-
chmod 644 "$ROOT_DIR/openssh-server/config/authorized_keys"
210-
211-
# Set SSH key path in environment
212-
SSH_PRIVATE_KEY_PATH="$ROOT_DIR/openssh-server/ssh-keys/openssh_server_key"
213-
export SSH_PRIVATE_KEY_PATH
214-
215-
# Add to .env file
216-
update_env_var "SSH_PRIVATE_KEY_PATH" "$SSH_PRIVATE_KEY_PATH"
217-
218-
# Fix SSH host key permissions (must be 600)
219-
find "$ROOT_DIR/openssh-server/config" -name "*_key" -type f -exec chmod 600 {} \; 2>/dev/null || true
220-
221-
echo " ✅ SSH key pair generated successfully!"
222-
echo " 🔑 Private key: $ROOT_DIR/openssh-server/ssh-keys/openssh_server_key"
223-
echo " 🗝️ Public key: $ROOT_DIR/openssh-server/ssh-keys/openssh_server_key.pub"
224-
echo " ⚙️ SSH config: $ROOT_DIR/openssh-server/config/sshd_config (60min session timeout)"
225-
else
226-
echo " ❌ ERROR Failed to extract SSH keys from Docker output"
227-
echo " 📋 Full output saved to: $TEMP_OUTPUT for debugging"
228-
ERROR_OCCURRED=1
229-
return 1
230-
fi
231-
else
232-
echo " ❌ ERROR Docker key generation command failed"
233-
if [ -f "$TEMP_OUTPUT" ]; then
234-
echo "📋 Error output:"
235-
cat "$TEMP_OUTPUT"
236-
fi
237-
ERROR_OCCURRED=1
238-
return 1
239-
fi
240-
241-
# Clean up temp file (only if successful)
242-
if [ "$ERROR_OCCURRED" -eq 0 ]; then
243-
rm -f "$TEMP_OUTPUT"
244-
fi
245-
246-
echo ""
247-
echo "--------------------------------"
248-
echo ""
249-
fi
250-
}
251130

252131
generate_elasticsearch_api_key() {
253132
# Function to generate Elasticsearch API key
@@ -507,19 +386,19 @@ deploy_infrastructure() {
507386
echo "🔧 Starting infrastructure services..."
508387
INFRA_SERVICES="nexent-elasticsearch nexent-postgresql nexent-minio redis"
509388

510-
# Add openssh-server if Terminal tool is enabled
511-
if [ "$ENABLE_TERMINAL_TOOL" = "true" ]; then
389+
# Add openssh-server if Terminal tool container is enabled
390+
if [ "$ENABLE_TERMINAL_TOOL_CONTAINER" = "true" ]; then
512391
INFRA_SERVICES="$INFRA_SERVICES nexent-openssh-server"
513-
echo "🔧 Terminal tool enabled - openssh-server will be included in infrastructure"
392+
echo "🔧 Terminal tool container enabled - openssh-server will be included in infrastructure"
514393
fi
515394

516395
if ! ${docker_compose_command} -p nexent -f "docker-compose${COMPOSE_FILE_SUFFIX}" up -d $INFRA_SERVICES; then
517396
echo " ❌ ERROR Failed to start infrastructure services"
518397
exit 1
519398
fi
520399

521-
if [ "$ENABLE_TERMINAL_TOOL" = "true" ]; then
522-
echo "🔧 Terminal tool (openssh-server) is now available for AI agents"
400+
if [ "$ENABLE_TERMINAL_TOOL_CONTAINER" = "true" ]; then
401+
echo "🔧 Terminal tool container (openssh-server) is now available for AI agents"
523402
fi
524403

525404
# Deploy Supabase services based on DEPLOYMENT_VERSION
@@ -534,7 +413,7 @@ deploy_infrastructure() {
534413
fi
535414

536415
# Start Supabase services
537-
if ! ${docker_compose_command} -p nexent -f "docker-compose-supabase${COMPOSE_FILE_SUFFIX}" up -d; then
416+
if ! $docker_compose_command -p nexent -f "docker-compose-supabase${COMPOSE_FILE_SUFFIX}" up -d; then
538417
echo " ❌ ERROR Failed to start supabase services"
539418
ERROR_OCCURRED=1
540419
return 1
@@ -635,33 +514,33 @@ wait_for_elasticsearch_healthy() {
635514
}
636515

637516
select_terminal_tool() {
638-
# Function to ask if user wants to enable Terminal tool
639-
echo "🔧 Terminal Tool Configuration:"
517+
# Function to ask if user wants to create Terminal tool container
518+
echo "🔧 Terminal Tool Container Setup:"
640519
echo " Terminal tool allows AI agents to execute shell commands via SSH."
641-
echo " This creates an openssh-server container for secure command execution."
520+
echo " This will create an openssh-server container for secure command execution."
642521
if [ -n "$ENABLE_TERMINAL" ]; then
643522
enable_terminal="$ENABLE_TERMINAL"
644523
else
645-
read -p "👉 Do you want to enable Terminal tool? [Y/N] (default: N): " enable_terminal
524+
read -p "👉 Do you want to create Terminal tool container? [Y/N] (default: N): " enable_terminal
646525
fi
647526

648527
# Sanitize potential Windows CR in input
649528
enable_terminal=$(sanitize_input "$enable_terminal")
650529

651530
if [[ "$enable_terminal" =~ ^[Yy]$ ]]; then
652-
export ENABLE_TERMINAL_TOOL="true"
531+
export ENABLE_TERMINAL_TOOL_CONTAINER="true"
653532
export COMPOSE_PROFILES="${COMPOSE_PROFILES:+$COMPOSE_PROFILES,}terminal"
654-
echo "✅ Terminal tool enabled 🔧"
655-
echo " 🔧 Deploying an openssh-server container for secure command execution"
533+
echo "✅ Terminal tool container will be created 🔧"
534+
echo " 🔧 Creating openssh-server container for secure command execution"
656535

657-
# Ask user to specify directory mapping
536+
# Ask user to specify directory mapping for container
658537
default_terminal_dir="/opt/terminal"
659-
echo " 📁 Terminal directory configuration:"
538+
echo " 📁 Terminal container directory mapping:"
660539
echo " • Container path: /opt/terminal (fixed)"
661540
echo " • Host path: You can specify any directory on your host machine"
662541
echo " • Default host path: /opt/terminal (recommended)"
663542
echo ""
664-
read -p " 📁 Enter host directory to mount (default: /opt/terminal): " terminal_mount_dir
543+
read -p " 📁 Enter host directory to mount to container (default: /opt/terminal): " terminal_mount_dir
665544
terminal_mount_dir=$(sanitize_input "$terminal_mount_dir")
666545
TERMINAL_MOUNT_DIR="${terminal_mount_dir:-$default_terminal_dir}"
667546

@@ -672,10 +551,65 @@ select_terminal_tool() {
672551
echo " 📁 Terminal mount configuration:"
673552
echo " • Host: $TERMINAL_MOUNT_DIR"
674553
echo " • Container: /opt/terminal"
554+
echo " • This directory will be created if it doesn't exist"
555+
echo ""
556+
557+
# Setup SSH credentials for Terminal tool container
558+
echo "🔐 Setting up SSH credentials for Terminal tool container..."
559+
560+
# Check if SSH credentials are already set
561+
if [ -n "$SSH_USERNAME" ] && [ -n "$SSH_PASSWORD" ]; then
562+
echo "🚧 SSH credentials already configured, skipping setup..."
563+
echo "👤 Username: $SSH_USERNAME"
564+
echo "🔑 Password: [HIDDEN]"
565+
else
566+
# Prompt for SSH credentials
567+
echo "Please enter SSH credentials for Terminal tool container:"
568+
echo ""
569+
570+
# Get SSH username
571+
if [ -z "$SSH_USERNAME" ]; then
572+
read -p "SSH Username (default: root): " input_username
573+
SSH_USERNAME=${input_username:-root}
574+
fi
575+
576+
# Get SSH password
577+
if [ -z "$SSH_PASSWORD" ]; then
578+
echo "SSH Password (will be hidden): "
579+
read -s input_password
580+
echo ""
581+
if [ -z "$input_password" ]; then
582+
echo "❌ SSH password cannot be empty"
583+
ERROR_OCCURRED=1
584+
return 1
585+
fi
586+
SSH_PASSWORD="$input_password"
587+
fi
588+
589+
# Validate credentials
590+
if [ -z "$SSH_USERNAME" ] || [ -z "$SSH_PASSWORD" ]; then
591+
echo "❌ Both username and password are required"
592+
ERROR_OCCURRED=1
593+
return 1
594+
fi
595+
596+
# Export environment variables
597+
export SSH_USERNAME
598+
export SSH_PASSWORD
599+
600+
# Add to .env file
601+
update_env_var "SSH_USERNAME" "$SSH_USERNAME"
602+
update_env_var "SSH_PASSWORD" "$SSH_PASSWORD"
603+
604+
echo " ✅ SSH credentials configured successfully!"
605+
echo " 👤 Username: $SSH_USERNAME"
606+
echo " 🔑 Password: [HIDDEN]"
607+
echo " ⚙️ Authentication: Password-based"
608+
fi
609+
echo ""
675610
else
676-
export ENABLE_TERMINAL_TOOL="false"
677-
echo "🚫 Terminal tool disabled"
678-
611+
export ENABLE_TERMINAL_TOOL_CONTAINER="false"
612+
echo "🚫 Terminal tool container disabled"
679613
fi
680614
echo ""
681615
echo "--------------------------------"
@@ -740,16 +674,13 @@ main_deploy() {
740674
# Select deployment version, mode and image source
741675
select_deployment_version || { echo "❌ Deployment version selection failed"; exit 1; }
742676
select_deployment_mode || { echo "❌ Deployment mode selection failed"; exit 1; }
743-
select_terminal_tool || { echo "❌ Terminal tool configuration failed"; exit 1; }
677+
select_terminal_tool || { echo "❌ Terminal tool container configuration failed"; exit 1; }
744678
choose_image_env || { echo "❌ Image environment setup failed"; exit 1; }
745679

746680
# Add permission
747681
prepare_directory_and_data || { echo "❌ Permission setup failed"; exit 1; }
748682
generate_minio_ak_sk || { echo "❌ MinIO key generation failed"; exit 1; }
749683

750-
if [ "$ENABLE_TERMINAL_TOOL" = "true" ]; then
751-
generate_ssh_keys || { echo "❌ SSH key generation failed"; exit 1; }
752-
fi
753684

754685
# Generate Supabase secrets
755686
generate_supabase_keys || { echo "❌ Supabase secrets generation failed"; exit 1; }

docker/docker-compose.prod.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,10 +200,10 @@ services:
200200
container_name: nexent-openssh-server
201201
hostname: nexent-openssh-server
202202
environment:
203-
- DEV_USER=linuxserver.io
203+
- DEV_USER=${SSH_USERNAME:-linuxserver.io}
204+
- DEV_PASSWORD=${SSH_PASSWORD:-nexent123}
204205
volumes:
205206
- ${TERMINAL_MOUNT_DIR:-./workspace}:/opt/terminal
206-
- ${ROOT_DIR}/openssh-server/config:/tmp/ssh_keys:ro # 只读挂载SSH公钥
207207
networks:
208208
- nexent
209209
restart: always

docker/docker-compose.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,12 +214,12 @@ services:
214214
container_name: nexent-openssh-server
215215
hostname: nexent-openssh-server
216216
environment:
217-
- DEV_USER=linuxserver.io
217+
- DEV_USER=${SSH_USERNAME:-linuxserver.io}
218+
- DEV_PASSWORD=${SSH_PASSWORD:-nexent123}
218219
ports:
219220
- "2222:22" # SSH port
220221
volumes:
221222
- ${TERMINAL_MOUNT_DIR:-./workspace}:/opt/terminal
222-
- ${ROOT_DIR}/openssh-server/ssh-keys:/tmp/ssh_keys:ro
223223
networks:
224224
- nexent
225225
restart: always

docker/generate_env.sh

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,25 @@ update_env_file() {
7373
echo "ELASTICSEARCH_API_KEY=$ELASTICSEARCH_API_KEY" >> ../.env
7474
fi
7575
fi
76+
77+
# Update or add SSH credentials (only if they were set)
78+
if [ -n "$SSH_USERNAME" ]; then
79+
if grep -q "^SSH_USERNAME=" ../.env; then
80+
sed -i.bak "s~^SSH_USERNAME=.*~SSH_USERNAME=$SSH_USERNAME~" ../.env
81+
else
82+
echo "" >> ../.env
83+
echo "# SSH Terminal Tool Credentials" >> ../.env
84+
echo "SSH_USERNAME=$SSH_USERNAME" >> ../.env
85+
fi
86+
fi
87+
88+
if [ -n "$SSH_PASSWORD" ]; then
89+
if grep -q "^SSH_PASSWORD=" ../.env; then
90+
sed -i.bak "s~^SSH_PASSWORD=.*~SSH_PASSWORD=$SSH_PASSWORD~" ../.env
91+
else
92+
echo "SSH_PASSWORD=$SSH_PASSWORD" >> ../.env
93+
fi
94+
fi
7695
echo " ✅ Generated keys updated successfully"
7796

7897
# Force update development environment service URLs for localhost access
@@ -218,6 +237,12 @@ show_summary() {
218237
if [ -n "$SERVICE_ROLE_KEY" ]; then
219238
echo " 🔑 SERVICE_ROLE_KEY: $SERVICE_ROLE_KEY"
220239
fi
240+
if [ -n "$SSH_USERNAME" ]; then
241+
echo " 👤 SSH_USERNAME: $SSH_USERNAME"
242+
fi
243+
if [ -n "$SSH_PASSWORD" ]; then
244+
echo " 🔑 SSH_PASSWORD: [HIDDEN]"
245+
fi
221246
if [ -z "$ELASTICSEARCH_API_KEY" ]; then
222247
echo " ⚠️ Note: To generate ELASTICSEARCH_API_KEY later, please:"
223248
echo " 1. Start Elasticsearch: docker-compose -p nexent up -d nexent-elasticsearch"

0 commit comments

Comments
 (0)