Estação de Trabalho como Código (Parte 4.5): Menu SSH Interativo
Aprenda a usar menu-ssh, um gerenciador interativo de hosts SSH baseado em fzf. Este tutorial complementar mostra como instalar, configurar e usar a ferramenta para gerenciar múltiplos hosts SSH de forma visual e intuitiva, com suporte completo a ProxyJump e estrutura modular.
Tutorial Anterior: Estação de Trabalho como Código (Parte 4): Organizando Acessos com SSH Config
Introdução
Na Parte 4, aprendemos a criar uma estrutura modular e profissional para gerenciar hosts SSH. Embora a edição manual de arquivos seja poderosa e ofereça controle total, pode ser tedioso quando você tem muitos hosts ou precisa adicionar/remover hosts frequentemente.
Para melhorar a experiência, criamos menu-ssh, um script Bash interativo que oferece uma interface visual baseada em fzf (que você já instalou na Parte 1) para gerenciar hosts SSH de forma intuitiva e segura. O menu-ssh é totalmente integrado com a estrutura modular que criamos, respeitando os arquivos config.d/pessoal.conf, config.d/trabalho.conf e config.d/lab.conf.
Observação: Esta é uma parte complementar e opcional. Se você preferir gerenciar SSH Config manualmente, pode pular para a Parte 5. No entanto, recomendamos usar menu-ssh para melhorar sua produtividade.
Objetivos desta Parte
- Entender o que é menu-ssh e seus benefícios
- Instalar o script menu-ssh no seu ambiente
- Usar menu-ssh para adicionar, remover e listar hosts
- Testar conexões SSH via menu-ssh
- Conectar a hosts usando seleção visual
- Entender como menu-ssh integra com a estrutura modular
- Resolver problemas comuns com troubleshooting
Pré-requisitos
- Conclusão da Parte 4 desta série
- fzf instalado (foi instalado na Parte 1)
- Conhecimento básico de SSH Config (coberto na Parte 4)
A quem se destina
Este tutorial é ideal para:
- SysAdmins: Que querem uma interface visual para gerenciar hosts
- DevOps Engineers: Que usam SSH diariamente e querem mais produtividade
- Desenvolvedores: Que trabalham com múltiplos servidores
- Profissionais de TI: Que querem automatizar tarefas comuns
Pré-conhecimento: Nenhum pré-conhecimento adicional é necessário além da Parte 4.
Tempo Estimado
⏱ 30-40 minutos
Isso inclui:
- Leitura e compreensão: ~10 min
- Instalação: ~5 min
- Testes e exploração: ~10-15 min
- Troubleshooting (se necessário): ~5-10 min
Dica Útil: Se você quiser apenas instalar e usar, pode fazer em 15 minutos. Mas recomendamos ler e entender cada seção.
O que é menu-ssh?
menu-ssh é um script Bash interativo que oferece:
- Interface visual com fzf para seleção de hosts
- Adicionar novos hosts com validação completa
- Remover hosts com confirmação e backup
- Listar todos os hosts com informações formatadas
- Testar conexões SSH
- Conectar a hosts via seleção visual
- Suporte completo a ProxyJump
- Seleção visual de chaves SSH
- Integração com estrutura modular (config.d/)
Por que usar menu-ssh?
| Aspecto | Benefício |
|---|---|
| Interface Amigável | Fácil de usar, mesmo para iniciantes |
| Menos Erros | Validação de entrada em cada passo |
| Organização | Categoriza hosts (pessoal, trabalho, lab) |
| Produtividade | Conectar a hosts com poucos cliques |
| Profissional | Cores, formatação e feedback claro |
| Seguro | Backups automáticos antes de modificações |
| Modular | Integrado com estrutura config.d/ |
Instalando o menu-ssh
Passo 1: Criar o Diretório
1
2
3
4
5
6
7
8
# Crie o diretório para o script
$ mkdir -p ~/workspace-as-code/scripts/menu-ssh
# Verifique se foi criado
$ ls -la ~/workspace-as-code/scripts/
# Você deve ver:
# drwxr-xr-x menu-ssh
Passo 2: Copiar o Script
Copie o script menu-ssh para o diretório. O script completo está abaixo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
$ cat > ~/workspace-as-code/scripts/menu-ssh/menu-ssh << 'EOF'
#!/bin/bash
################################################################################
# menu-ssh - Gerenciador Interativo de Conexões SSH
################################################################################
#
# Descrição:
# Script interativo para gerenciar, listar e conectar a hosts SSH configurados.
# Utiliza fzf para uma interface de seleção intuitiva e oferece funcionalidades
# como adicionar, remover, listar e testar conexões SSH.
# Adaptado para trabalhar com estrutura modular de SSH Config (config.d/).
#
# Dependências:
# - bash (versão 4.0+)
# - fzf (fuzzy finder) - instalado na Parte 1
# - ssh (OpenSSH)
#
# Uso:
# menu-ssh
#
# Arquivos de Configuração:
# ~/.ssh/config - Configuração principal (Include)
# ~/.ssh/config.d/pessoal.conf - Hosts pessoais
# ~/.ssh/config.d/trabalho.conf - Hosts de trabalho
# ~/.ssh/config.d/lab.conf - Hosts de laboratório
#
# Autor: Gean Martins (adaptado para workspace-as-code)
# Versão: 2.0.0
# Data: 2026-02-19
#
################################################################################
set -euo pipefail
# ============================================================================
# Definições de Cores
# ============================================================================
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly BLUE='\033[0;34m'
readonly CYAN='\033[0;36m'
readonly MAGENTA='\033[0;35m'
readonly NC='\033[0m' # No Color
# ============================================================================
# Variáveis Globais
# ============================================================================
readonly SSH_CONFIG="${HOME}/.ssh/config"
readonly SSH_CONFIG_DIR="${HOME}/.ssh/config.d"
readonly CATEGORIES=("pessoal" "trabalho" "lab")
# ============================================================================
# Funções Auxiliares
# ============================================================================
die() {
echo -e "${RED}Erro: $*${NC}" >&2
exit 1
}
warn() {
echo -e "${YELLOW}Aviso: $*${NC}" >&2
}
success() {
echo -e "${GREEN}$*${NC}"
}
info() {
echo -e "${CYAN}$*${NC}"
}
command_exists() {
command -v "$1" &>/dev/null
}
check_fzf() {
if ! command_exists fzf; then
die "fzf não está instalado. Instale com: sudo apt install fzf"
fi
}
initialize_ssh_environment() {
mkdir -p "$HOME/.ssh" "$SSH_CONFIG_DIR"
touch "$SSH_CONFIG"
chmod 700 "$HOME/.ssh"
chmod 600 "$SSH_CONFIG"
for category in "${CATEGORIES[@]}"; do
local config_file="$SSH_CONFIG_DIR/${category}.conf"
if [[ ! -f "$config_file" ]]; then
touch "$config_file"
chmod 600 "$config_file"
fi
done
}
# ============================================================================
# Funções de Backup
# ============================================================================
backup_ssh_config() {
local config_file=$1
local backup_file="${config_file}.bak.$(date +%Y%m%d_%H%M%S)"
cp "$config_file" "$backup_file"
info "Backup criado: $backup_file"
}
# ============================================================================
# Funções de Validação
# ============================================================================
validate_ip() {
local ip=$1
local stat=1
if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
local OIFS=$IFS
IFS='.'
local ip_array=($ip)
IFS=$OIFS
[[ ${ip_array[0]} -le 255 && ${ip_array[1]} -le 255 && \
${ip_array[2]} -le 255 && ${ip_array[3]} -le 255 ]]
stat=$?
fi
return $stat
}
validate_hostname() {
local hostname=$1
[[ "$hostname" =~ ^[a-zA-Z0-9.-]+$ ]]
}
validate_alias() {
local alias=$1
[[ "$alias" =~ ^[a-zA-Z0-9_.-]+$ ]]
}
validate_port() {
local port=$1
[[ $port =~ ^[0-9]+$ ]] && (( port >= 1 && port <= 65535 ))
}
# ============================================================================
# Funções de Leitura de Hosts
# ============================================================================
get_all_hosts() {
grep -h "^Host " "$SSH_CONFIG" "$SSH_CONFIG_DIR"/*.conf 2>/dev/null | \
awk '{print $2}' | grep -v "^\*$" | sort -u
}
get_host_info() {
local alias=$1
awk -v host="$alias" '
$1 == "Host" && $2 == host { found=1; next }
found && /^Host / { exit }
found { print }
' "$SSH_CONFIG" "$SSH_CONFIG_DIR"/*.conf 2>/dev/null
}
get_host_config_file() {
local alias=$1
for config_file in "$SSH_CONFIG" "$SSH_CONFIG_DIR"/*.conf; do
if [[ -f "$config_file" ]] && grep -q "^Host $alias$" "$config_file"; then
echo "$config_file"
return 0
fi
done
return 1
}
list_hosts_formatted() {
local all_hosts
all_hosts=$(get_all_hosts)
if [[ -z "$all_hosts" ]]; then
warn "Nenhum host encontrado na configuração SSH."
return 1
fi
info "--- Hosts SSH Configurados ---"
echo ""
printf "${BLUE}%-20s %-15s %-8s %-15s %s${NC}\n" "ALIAS" "HOSTNAME" "PORT" "USER" "ARQUIVO"
echo "------------------------------------------------------------------------------------"
while IFS= read -r alias; do
local config_file
config_file=$(get_host_config_file "$alias")
local config_name=$(basename "$config_file" .conf)
local hostname=$(get_host_info "$alias" | grep "HostName" | awk '{print $2}' | head -1)
local port=$(get_host_info "$alias" | grep "Port" | awk '{print $2}' | head -1)
local user=$(get_host_info "$alias" | grep "User" | awk '{print $2}' | head -1)
hostname=${hostname:-"N/A"}
port=${port:-"22"}
user=${user:-"N/A"}
printf "%-20s %-15s %-8s %-15s %s\n" "$alias" "$hostname" "$port" "$user" "$config_name"
done <<< "$all_hosts"
echo ""
}
# ============================================================================
# Funções de Gerenciamento de Hosts
# ============================================================================
detect_category() {
info "Selecione a categoria para este host:"
echo ""
local category
category=$(printf "%s\n" "${CATEGORIES[@]}" | fzf --prompt="Categoria > " --height="10%")
if [[ -z "$category" ]]; then
die "Categoria não selecionada."
fi
echo "$category"
}
list_available_keys() {
find "$HOME/.ssh" -maxdepth 1 -type f \( -name "id_*" -o -name "*_key" \) ! -name "*.pub" 2>/dev/null | sort
}
select_ssh_key() {
local available_keys
available_keys=$(list_available_keys)
if [[ -z "$available_keys" ]]; then
warn "Nenhuma chave SSH encontrada em ~/.ssh"
echo "$HOME/.ssh/id_rsa"
return 0
fi
info "Selecione a chave SSH (ou Enter para usar padrão):"
echo ""
local selected_key
selected_key=$(echo "$available_keys" | fzf --prompt="Chave SSH > " --height="15%")
if [[ -z "$selected_key" ]]; then
echo "$HOME/.ssh/id_rsa"
else
echo "$selected_key"
fi
}
select_proxyjump() {
local all_hosts
all_hosts=$(get_all_hosts)
if [[ -z "$all_hosts" ]]; then
return 0
fi
info "Selecione um ProxyJump (ou Enter para pular):"
echo ""
local proxyjump
proxyjump=$(echo "$all_hosts" | fzf --prompt="ProxyJump > " --height="15%")
echo "$proxyjump"
}
add_ssh() {
info "--- Adicionar Novo Host SSH ---"
echo ""
read -rp "Digite o alias do host (ex: vm-01): " alias
if [[ -z "$alias" ]]; then
die "O alias não pode ser vazio."
fi
if ! validate_alias "$alias"; then
die "O alias deve conter apenas letras, números, pontos, hífens e underscores."
fi
if grep -q -E "^\s*Host\s+${alias}\s*$" "$SSH_CONFIG" "$SSH_CONFIG_DIR"/*.conf 2>/dev/null; then
die "O alias '$alias' já existe no arquivo de configuração SSH."
fi
read -rp "Digite o IP ou Hostname do host (ex: 192.168.200.150): " ip
if [[ -z "$ip" ]]; then
die "IP/Hostname não pode ser vazio."
fi
if ! validate_ip "$ip" && ! validate_hostname "$ip"; then
die "IP ou hostname inválido."
fi
read -rp "Digite a porta SSH (padrão: 22): " port
port=${port:-22}
if ! validate_port "$port"; then
die "Porta inválida. Deve ser um número entre 1 e 65535."
fi
read -rp "Digite o usuário (padrão: $USER): " user
user=${user:-$USER}
local key
key=$(select_ssh_key)
local proxyjump
proxyjump=$(select_proxyjump)
read -rp "ForwardAgent (y/N): " forward_agent
local category
category=$(detect_category)
local config_file="$SSH_CONFIG_DIR/${category}.conf"
if [[ -n "$key" && ! -f "$key" ]]; then
warn "O arquivo de chave '$key' não existe."
read -rp "Deseja continuar? (s/N): " confirm
if [[ ! "$confirm" =~ ^[sS]([iI][mM])?$ ]]; then
echo "Cancelado."
return 1
fi
fi
backup_ssh_config "$config_file"
{
echo ""
echo "Host $alias"
echo " HostName $ip"
echo " Port $port"
echo " User $user"
[[ -n "$key" ]] && echo " IdentityFile $key"
[[ -n "$proxyjump" ]] && echo " ProxyJump $proxyjump"
[[ "$forward_agent" =~ ^[yY] ]] && echo " ForwardAgent yes"
} >> "$config_file"
success "Host '$alias' adicionado com sucesso em $category.conf!"
}
remove_ssh() {
info "--- Remover Host SSH ---"
echo ""
local all_hosts
all_hosts=$(get_all_hosts)
if [[ -z "$all_hosts" ]]; then
warn "Nenhum host para remover."
return 1
fi
local host_to_remove
host_to_remove=$(echo "$all_hosts" | fzf --prompt="Selecione o host para REMOVER: " --height="15%")
if [[ -z "$host_to_remove" ]]; then
echo "Remoção cancelada."
return 0
fi
local config_file
config_file=$(get_host_config_file "$host_to_remove")
warn "Você está prestes a remover o host '$host_to_remove'."
warn "A seguinte entrada será removida de $(basename "$config_file"):"
echo "----------------------------------------------------"
get_host_info "$host_to_remove"
echo "----------------------------------------------------"
echo ""
read -rp "Tem certeza que deseja continuar? (s/N): " confirm
if [[ "$confirm" =~ ^[sS]([iI][mM])?$ ]]; then
backup_ssh_config "$config_file"
sed -i "/^Host $host_to_remove$/,/^$/d" "$config_file"
success "Host '$host_to_remove' removido com sucesso."
else
echo "Remoção cancelada pelo usuário."
fi
}
test_ssh() {
info "--- Testar Conexão SSH ---"
echo ""
local all_hosts
all_hosts=$(get_all_hosts)
if [[ -z "$all_hosts" ]]; then
warn "Nenhum host para testar."
return 1
fi
local host_to_test
host_to_test=$(echo "$all_hosts" | fzf --prompt="Selecione o host para TESTAR: " --height="15%")
if [[ -z "$host_to_test" ]]; then
return 0
fi
echo ""
echo -e "${YELLOW}Testando conexão com $host_to_test...${NC}"
echo ""
if ssh -q -o BatchMode=yes -o ConnectTimeout=5 "$host_to_test" exit 2>/dev/null; then
success "Conexão bem-sucedida com $host_to_test"
else
echo -e "${RED}Falha na conexão com $host_to_test${NC}"
echo ""
warn "Dica: Use 'ssh -v $host_to_test' para mais detalhes"
fi
}
connect_ssh() {
local alias=$1
success "Conectando ao host '$alias'..."
echo ""
ssh "$alias"
}
# ============================================================================
# Menu Principal
# ============================================================================
main() {
check_fzf
initialize_ssh_environment
local choice
choice=$( (echo "Adicionar novo host"; echo "Remover host"; echo "Listar hosts"; echo "Testar conexão"; echo "---"; get_all_hosts) | fzf --prompt="SSH Menu > " --height="40%" --border )
case "$choice" in
"")
echo "Nenhuma ação selecionada."
;;
"Adicionar novo host")
add_ssh
;;
"Remover host")
remove_ssh
;;
"Listar hosts")
list_hosts_formatted
;;
"Testar conexão")
test_ssh
;;
"---")
echo "Nenhuma ação selecionada."
;;
*)
if [[ -n "$choice" ]]; then
connect_ssh "$choice"
else
die "Opção inválida selecionada."
fi
;;
esac
}
main "$@"
EOF
# Dê permissão de execução
$ chmod +x ~/workspace-as-code/scripts/menu-ssh/menu-ssh
# Verifique se foi criado
$ ls -la ~/workspace-as-code/scripts/menu-ssh/
# Você deve ver:
# -rwxr-xr-x menu-ssh
Passo 3: Criar Link Simbólico
Para usar o script de qualquer lugar, crie um link simbólico em /usr/local/bin:
1
2
3
4
5
6
7
8
9
10
11
# Crie o link simbólico
$ sudo ln -s ~/workspace-as-code/scripts/menu-ssh/menu-ssh /usr/local/bin/menu-ssh
# Verifique se foi criado
$ ls -la /usr/local/bin/menu-ssh
# Você deve ver:
# lrwxrwxrwx menu-ssh -> /home/user/workspace-as-code/scripts/menu-ssh/menu-ssh
# Teste se funciona
$ menu-ssh
Informação: O link simbólico permite chamar
menu-sshde qualquer diretório sem precisar do caminho completo.
Passo 4: Adicionar ao setup_symlinks.sh (Opcional)
Se quiser automatizar a criação do link simbólico, adicione ao script setup_symlinks.sh:
1
2
3
4
5
6
7
8
9
# Edite o script
$ vim ~/workspace-as-code/scripts/setup_symlinks.sh
# Adicione após a seção de symlinks:
# Cria link para menu-ssh
if [ -f ~/workspace-as-code/scripts/menu-ssh/menu-ssh ]; then
sudo ln -sf ~/workspace-as-code/scripts/menu-ssh/menu-ssh /usr/local/bin/menu-ssh
success "Link criado para menu-ssh"
fi
Usando o menu-ssh
Iniciando o Menu
1
2
3
4
# Execute o menu-ssh
$ menu-ssh
# Você verá a tela principal com opções
Estrutura do Menu
O menu-ssh oferece as seguintes opções:
| Opção | Função |
|---|---|
| Adicionar novo host | Adiciona um novo host SSH interativamente |
| Remover host | Remove um host existente com confirmação |
| Listar hosts | Mostra todos os hosts com informações |
| Testar conexão | Testa a conexão com um host |
| [Hosts] | Conecta diretamente a um host |
Exemplos de Uso
Exemplo 1: Adicionar um Novo Host Pessoal
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ menu-ssh
# Selecione "Adicionar novo host"
# Digite o alias: meu-vps
# Digite o IP: 203.0.113.42
# Digite a porta: 2222
# Digite o usuário: ubuntu
# Selecione a chave: ~/.ssh/vps_key
# Selecione ProxyJump: (Enter para pular)
# ForwardAgent: N
# Selecione a categoria: pessoal
# Resultado:
# Host 'meu-vps' adicionado com sucesso em pessoal.conf!
Exemplo 2: Adicionar um Host com ProxyJump
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ menu-ssh
# Selecione "Adicionar novo host"
# Digite o alias: servidor-interno
# Digite o IP: 10.10.1.50
# Digite a porta: 22
# Digite o usuário: admin
# Selecione a chave: ~/.ssh/trabalho_key
# Selecione ProxyJump: jump.trabalho
# ForwardAgent: N
# Selecione a categoria: trabalho
# Resultado:
# Host 'servidor-interno' adicionado com sucesso em trabalho.conf!
Exemplo 3: Listar Todos os Hosts
1
2
3
4
5
6
7
8
9
10
11
12
13
$ menu-ssh
# Selecione "Listar hosts"
# Você verá algo como:
# --- Hosts SSH Configurados ---
#
# ALIAS HOSTNAME PORT USER ARQUIVO
# ------------------------------------------------------------------------------------
# meu-vps 203.0.113.42 2222 ubuntu pessoal
# servidor-interno 10.10.1.50 22 admin trabalho
# jump.trabalho 203.0.113.5 22 seu_usuario trabalho
# lab-vm1 192.168.1.100 22 ubuntu lab
Exemplo 4: Testar Conexão
1
2
3
4
5
6
7
8
9
$ menu-ssh
# Selecione "Testar conexão"
# Selecione o host: meu-vps
# Você verá:
# Testando conexão com meu-vps...
#
# Conexão bem-sucedida com meu-vps
Exemplo 5: Conectar a um Host
1
2
3
4
5
6
7
8
$ menu-ssh
# Você verá todos os hosts listados
# Selecione: servidor-interno
# Resultado:
# Conectando ao host 'servidor-interno'...
# [Conecta ao servidor via ProxyJump automaticamente]
Exemplo 6: Remover um Host
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ menu-ssh
# Selecione "Remover host"
# Selecione o host: meu-vps
# Você verá:
# Aviso: Você está prestes a remover o host 'meu-vps'.
# A seguinte entrada será removida de pessoal.conf:
# Host meu-vps
# HostName 203.0.113.42
# Port 2222
# User ubuntu
# IdentityFile ~/.ssh/vps_key
# Tem certeza que deseja continuar? (s/N): s
# Resultado:
# Host 'meu-vps' removido com sucesso.
Entendendo o Script menu-ssh
O script é bem estruturado e segue boas práticas. Vamos entender as principais seções:
Seção 1: Inicialização
1
set -euo pipefail
Garante que o script falhe se houver erro, variável indefinida ou falha em pipe.
Seção 2: Cores e Formatação
1
2
3
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
# ... etc
Define cores para tornar a saída mais legível.
Seção 3: Funções Auxiliares
1
2
3
4
die() # Exibe erro e sai
warn() # Exibe aviso
success() # Exibe sucesso
info() # Exibe informação
Funções para padronizar mensagens.
Seção 4: Leitura de Hosts
1
2
3
get_all_hosts() # Extrai todos os hosts
get_host_info() # Extrai informações de um host
get_host_config_file() # Encontra o arquivo onde o host está
Funções para ler a configuração SSH modular.
Seção 5: Gerenciamento de Hosts
1
2
3
4
add_ssh() # Adiciona novo host
remove_ssh() # Remove host
test_ssh() # Testa conexão
connect_ssh() # Conecta a host
Funções principais do script.
Seção 6: Menu Principal
1
main()
Orquestra o fluxo do programa.
Recursos Avançados
Seleção Visual de Chaves SSH
Quando você adiciona um novo host, o script lista todas as chaves SSH disponíveis em ~/.ssh:
1
2
3
4
5
# Selecione a chave SSH (ou Enter para usar padrão):
# ~/.ssh/id_rsa
# ~/.ssh/github_key
# ~/.ssh/trabalho_key
# ~/.ssh/vps_key
Basta usar as setas para navegar e Enter para selecionar.
Seleção de ProxyJump
O script lista todos os hosts já configurados para seleção como ProxyJump:
1
2
3
4
# Selecione um ProxyJump (ou Enter para pular):
# jump.trabalho
# jump.interno
# bastion.empresa
Isso garante que você não digite um host inexistente.
Categorização Automática
Ao adicionar um host, o script pede para selecionar a categoria:
1
2
3
4
# Selecione a categoria para este host:
# pessoal
# trabalho
# lab
O host será adicionado ao arquivo correto (pessoal.conf, trabalho.conf ou lab.conf).
Backups Automáticos
Antes de modificar qualquer arquivo, o script cria um backup:
1
# Backup criado: /home/user/.ssh/config.d/pessoal.conf.bak.20260219_143022
Se algo der errado, você pode restaurar o backup.
Troubleshooting
Erro: “fzf não está instalado”
Problema: Você recebe esse erro ao executar menu-ssh.
Solução: fzf foi instalado na Parte 1. Se não estiver, instale:
1
2
3
4
$ sudo apt install fzf
# Verifique
$ fzf --version
Erro: “Permission denied” ao executar menu-ssh
Problema: O script não tem permissão de execução.
Solução:
1
2
3
4
5
6
7
# Dê permissão de execução
$ chmod +x ~/workspace-as-code/scripts/menu-ssh/menu-ssh
# Verifique
$ ls -la ~/workspace-as-code/scripts/menu-ssh/menu-ssh
# Deve ser: -rwxr-xr-x
Erro: “command not found: menu-ssh”
Problema: O link simbólico não foi criado ou está quebrado.
Solução:
1
2
3
4
5
6
7
8
9
# Verifique se o link existe
$ ls -la /usr/local/bin/menu-ssh
# Se não existir, crie
$ sudo ln -s ~/workspace-as-code/scripts/menu-ssh/menu-ssh /usr/local/bin/menu-ssh
# Se estiver quebrado, recrie
$ sudo rm /usr/local/bin/menu-ssh
$ sudo ln -s ~/workspace-as-code/scripts/menu-ssh/menu-ssh /usr/local/bin/menu-ssh
Erro: “Host already exists”
Problema: Você tenta adicionar um host que já existe.
Solução:
1
2
3
4
5
6
7
8
9
# Verifique se o host existe
$ grep "^Host seu-host$" ~/.ssh/config ~/.ssh/config.d/*
# Se existir, remova-o primeiro
$ menu-ssh
# Selecione "Remover host"
# Selecione o host que deseja remover
# Depois adicione novamente
Erro: “Invalid IP or hostname”
Problema: Você digitou um IP ou hostname inválido.
Solução:
1
2
3
4
# IPs válidos: 192.168.1.1, 203.0.113.42
# Hostnames válidos: example.com, server.example.com, my-server
# Tente novamente com um IP ou hostname válido
Erro: “Port invalid”
Problema: Você digitou uma porta inválida.
Solução:
1
2
3
4
# Portas válidas: 1-65535
# Porta padrão SSH: 22
# Tente novamente com um número entre 1 e 65535
Hosts não aparecem no menu
Problema: Você adicionou hosts, mas não aparecem no menu.
Solução:
1
2
3
4
5
6
7
8
9
10
11
12
# Verifique se os arquivos de configuração existem
$ ls -la ~/.ssh/config.d/
# Você deve ver:
# -rw------- pessoal.conf
# -rw------- trabalho.conf
# -rw------- lab.conf
# Verifique se há hosts nos arquivos
$ cat ~/.ssh/config.d/pessoal.conf
# Se estiverem vazios, adicione hosts via menu-ssh
Conexão falha ao testar
Problema: O teste de conexão falha.
Solução:
1
2
3
4
5
6
7
8
9
10
11
12
# Teste com verbose para mais detalhes
$ ssh -v seu-host
# Verifique:
# 1. Se o host está online
# 2. Se a chave SSH está correta
# 3. Se o usuário está correto
# 4. Se a porta está correta
# 5. Se o ProxyJump está configurado corretamente
# Se usar ProxyJump, teste o jump host primeiro
$ ssh -v jump.trabalho
ProxyJump não funciona
Problema: Conexão via ProxyJump falha.
Solução:
1
2
3
4
5
6
7
8
9
10
11
12
13
# Verifique se o jump host está configurado
$ grep -A 5 "^Host jump.trabalho" ~/.ssh/config.d/trabalho.conf
# Verifique se consegue conectar ao jump host
$ ssh -v jump.trabalho
# Se funcionar, teste o servidor interno
$ ssh -v servidor-interno
# Se não funcionar, verifique:
# 1. Se o jump host está online
# 2. Se o ProxyJump está escrito corretamente
# 3. Se a chave SSH do jump host está correta
Backup não foi criado
Problema: Você não vê o arquivo de backup.
Solução:
1
2
3
4
5
6
7
8
9
10
11
# Backups são criados no mesmo diretório do arquivo original
# com sufixo .bak.TIMESTAMP
# Procure por backups
$ ls -la ~/.ssh/config.d/*.bak*
# Você deve ver algo como:
# -rw------- pessoal.conf.bak.20260219_143022
# Para restaurar um backup
$ cp ~/.ssh/config.d/pessoal.conf.bak.20260219_143022 ~/.ssh/config.d/pessoal.conf
Dicas e Boas Práticas
Dica 1: Use Categorias Consistentemente
Organize seus hosts em categorias:
- pessoal: VPS pessoais, servidores de hobby
- trabalho: Infraestrutura corporativa, jump hosts
- lab: Máquinas virtuais de laboratório, testes
Dica 2: Use Nomes Descritivos
1
2
3
4
5
6
7
8
9
# Bom
vps-pessoal
jump.trabalho
db-server-prod
# Ruim
vm1
host2
servidor
Dica 3: Sempre Teste Conexões
Após adicionar um host, teste a conexão:
1
2
3
$ menu-ssh
# Selecione "Testar conexão"
# Selecione o host que acabou de adicionar
Dica 4: Mantenha Backups
O script cria backups automaticamente, mas você pode fazer backups manuais:
1
$ cp -r ~/.ssh/config.d ~/.ssh/config.d.backup
Dica 5: Use ProxyJump para Redes Internas
Se você tem servidores em uma rede interna, use ProxyJump:
1
2
3
# Ao adicionar um host interno:
# Selecione ProxyJump: jump.trabalho
# Isso permite conectar sem SSH manual para o jump host
Dica 6: Revise Configurações Periodicamente
1
2
3
4
5
# Listar todos os hosts
$ menu-ssh
# Selecione "Listar hosts"
# Remova hosts que não usa mais
Integrando com Seu Fluxo de Trabalho
Adicionar menu-ssh ao Git
1
2
3
4
5
# Adicione o script ao repositório
$ cd ~/workspace-as-code
$ git add scripts/menu-ssh/menu-ssh
$ git commit -m "feat: add interactive ssh menu manager"
$ git push origin main
Usar menu-ssh em Scripts
Você pode chamar menu-ssh de outros scripts:
1
2
3
4
5
#!/bin/bash
# Seu script
# Conectar a um host via menu-ssh
menu-ssh
Criar Alias para menu-ssh
Se preferir um nome mais curto:
1
2
3
4
5
# Adicione ao ~/.bash_aliases
alias sshmenu='menu-ssh'
# Agora você pode usar
$ sshmenu
Conclusão
O menu-ssh é uma ferramenta poderosa que melhora significativamente a experiência de gerenciamento de hosts SSH. Ele oferece:
✓ Interface intuitiva com fzf ✓ Integração perfeita com estrutura modular ✓ Validação completa de entrada ✓ Backups automáticos ✓ Suporte a ProxyJump e múltiplas chaves ✓ Categorização automática de hosts ✓ Feedback claro e profissional
O Que Você Alcançou
✓ Instalação do menu-ssh ✓ Criação de link simbólico para acesso global ✓ Compreensão de como o script funciona ✓ Capacidade de adicionar, remover e listar hosts ✓ Testes de conexão e conexão via interface visual ✓ Integração com estrutura workspace-as-code
Próximos Passos Imediatos
- Teste o menu-ssh:
1 2
$ menu-ssh # Selecione "Listar hosts"
- Adicione seus primeiros hosts:
1 2
$ menu-ssh # Selecione "Adicionar novo host"
- Teste conexões:
1 2
$ menu-ssh # Selecione "Testar conexão"
- Commit suas mudanças:
1 2 3 4
$ cd ~/workspace-as-code $ git add scripts/menu-ssh/ $ git commit -m "feat: add interactive ssh menu manager" $ git push origin main
Próximo Tutorial
Com a automação e interface visual para SSH estabelecidas, o próximo passo é explorar a customização da interface gráfica, tornando nosso ambiente de trabalho visualmente coeso e agradável.
- Próximo: Estação de Trabalho como Código (Parte 5): Customizando o GNOME Terminal e a Interface do Ubuntu
Recursos Adicionais
- Documentação Oficial do SSH
- fzf - GitHub
- SSH ProxyJump Documentation
- OpenSSH Best Practices
- Bash Scripting Guide
Fim da Parte 4.5
Próxima: Customizando o GNOME Terminal e a Interface do Ubuntu