Cloud Shell + Tailscale: acesso remoto sem senhas

Configurei o Cloud Shell na malha Tailscale e acesso pelo terminal local com SSH Ed25519.

O problema

Eu uso o Google Cloud Shell quase todo dia. Para quem não conhece, o Cloud Shell é como um “computadorzinho virtual de graça” que o Google te empresta. Ele já vem com 5GB de espaço salvo, ferramentas de programação como Node e Python, e roda direto na aba do seu navegador de internet.

O problema é que usar um terminal dentro do navegador web fica lento e desconfortável quando você precisa trabalhar a sério — para colar códigos, usar atalhos de teclado ou dividir a tela. O que eu queria era acessar esse mesmo computador do Google, mas usando meu próprio aplicativo de terminal no computador local (o Alacritty) ou até pelo celular quando estivesse fora de casa, sem depender da aba do Chrome aberta.

A barreira para fazer isso não era simples. O Cloud Shell fica trancado atrás das proteções digitais (os chamados firewalls) do Google. Você não consegue simplesmente usar o comando padrão de conexão remota, o ssh usuario@ip. O Cloud Shell é uma máquina efêmera (ou seja, temporária, que “reseta” de tempos em tempos) dentro de um centro de dados do Google, sem um endereço de IP público fixo e sem portas abertas para a internet. Para conseguir entrar nele vindo de fora, eu precisava criar um “túnel” privado e seguro. Foi aí que o Tailscale entrou na história.

Levei uma tarde inteira para fechar essa configuração. A maior parte do tempo foi gasta tentando resolver duas armadilhas: permissões de administrador (root) que somem sempre que o Cloud Shell reinicia, e um arquivo de inicialização (.bashrc) que eu mesmo estraguei no meio do caminho — o que acabou me ensinando uma lição valiosa.

A arquitetura em uma frase

Vou desenhar como as coisas se conectam antes de mostrar os comandos, porque foi justamente por pular essa parte que eu errei na primeira tentativa:

Terminal local ──(Tailscale mesh)──> Cloud Shell (100.x.x.x)

                                          └── SSH Ed25519 (sem senha)

O Tailscale funciona como uma rede privada virtual (VPN do tipo mesh): ele cria uma grande “sala de estar virtual” onde cada um dos seus dispositivos ganha um endereço de IP exclusivo (começando com 100.x.x.x) que só existe dentro dessa sua rede secreta. O Cloud Shell entra nessa rede como se fosse mais um aparelho da sua casa. A partir daí, o acesso via SSH (o protocolo seguro para controlar computadores à distância) funciona diretamente, como se o Cloud Shell estivesse na sua própria rede local (LAN).

Tailscale no Cloud Shell — sem root, persistindo entre sessões

O detalhe que me custou muito tempo: o Cloud Shell é um ambiente efêmero (temporário). Quando você o reinicia, quase tudo o que instalou no sistema é apagado. Não dá para simplesmente usar o comando de instalação tradicional de administrador (sudo apt install tailscale) e achar que está resolvido. A solução foi baixar o programa pronto (o binário estático) e instalá-lo dentro da sua pasta pessoal, a $HOME (que é o único lugar que não é apagado quando a máquina reseta).

O passo a passo foi: baixar o programa do Tailscale, extrair a pasta em ~/tailscale/, iniciar o serviço de segundo plano (o daemon chamado tailscaled) apontando para um ponto de comunicação local (o socket), e rodar o comando tailscale up para conectar na minha conta. Assim que entra na rede privada, o Cloud Shell recebe um IP no formato 100.x.x.x — e é esse número que você usa para se conectar a ele a partir de outros aparelhos.

Entendendo a função de cada peça técnica: o tailscaled (o serviço em segundo plano) precisa de um socket (um canal de comunicação interno) para conversar com a ferramenta de linha de comando. Sem esse canal, o comando tailscale up não encontra nada e falha. Além disso, esse socket precisa ficar gravado em um caminho onde seu usuário comum tenha permissão de escrita — por isso usei o parâmetro --socket apontando para a pasta $HOME.

O que quebra entre sessões: o arquivo do programa continua salvo no $HOME, mas o serviço em segundo plano (daemon) para de rodar quando a máquina reinicia. Você precisa iniciar o tailscaled novamente (ou colocar isso num script que roda automático ao entrar) toda vez que o Cloud Shell reiniciar. Rodei esse reinício tantas vezes que acabou virando um atalho no meu .bashrc.

Chaves SSH Ed25519 — adeus senhas

Usar senhas de texto para acessar servidores remotos (VPS) é um risco de segurança. Em vez disso, usei chaves criptográficas do tipo Ed25519 (em vez do padrão antigo RSA): elas geram arquivos de chave menores, mais rápidos e considerados muito mais seguros hoje em dia.

Dentro do Cloud Shell, rodei o comando:

ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -N ""

O parâmetro -N "" serve para criar a chave sem uma senha de proteção (passphrase). Isso faz sentido quando você quer automatizar a conexão entre máquinas que são todas suas, mas não deve ser feito se a chave for usada em um servidor compartilhado com outras pessoas.

O detalhe em que me confundi de primeira: você precisa enviar a sua chave pública para o computador que vai receber a conexão. Se o objetivo é acessar o Cloud Shell a partir do seu computador pessoal, é a chave pública do seu PC que deve ser gravada no arquivo ~/.ssh/authorized_keys do Cloud Shell (e não o inverso, que foi o erro que cometi de cabeça e me fez perder 20 minutos achando que nada funcionava).

Quando o Cloud Shell precisa iniciar a conexão no sentido oposto (ele acessando o seu computador local), usamos um intermediador de tráfego (um proxy SOCKS5) do próprio Tailscale rodando no espaço de usuário:

ALL_PROXY=socks5://127.0.0.1:1055 ssh -o StrictHostKeyChecking=no \
  -o UserKnownHostsFile=/dev/null seu_usuario@100.x.x.x \
  "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys" \
  < ~/.ssh/id_ed25519.pub

Aquela instrução ALL_PROXY no início é o que faz o comando SSH “enxergar” a rede privada do Tailscale. Sem ela, o endereço 100.x.x.x não é encontrado e a conexão trava sem explicar o motivo.

O .bashrc corrompido — quando você é o próprio invasor

No meio de toda essa configuração, eu estava criando atalhos (aliases) e pequenas funções dentro do arquivo ~/.bashrc (o arquivo de texto que guarda as configurações e atalhos do seu terminal, como uma função empurrar-() para automatizar o envio das chaves). Só que cometi um erro simples: esqueci de fechar uma chave } no código. Salvei o arquivo e fechei o terminal.

Quando tentei abrir o terminal de novo, recebi este aviso de erro:

bash: /home/seu_usuario/.bashrc: linha 152: erro de sintaxe próximo ao token inesperado '}'

E aí o caos começou. Quando o arquivo .bashrc está com erro de código, ele não consegue carregar a variável PATH — que é o “mapa” que diz ao sistema onde os seus programas estão instalados. O resultado? Ferramentas como o nvm, o claude e o meu atalho claude-glm sumiram. Para o terminal, tudo virou comando não encontrado. É aquele tipo de falha que dá a sensação de que o sistema operacional inteiro quebrou, quando na verdade foi apenas um único caractere errado num arquivo de texto.

Tentei consertar o arquivo usando o comando sed (uma ferramenta de edição de texto por linha de comando), me confundi com as regras de busca (regex), e tomei a decisão mais rápida: cortar fora o final do arquivo a partir da última linha que eu sabia que estava certa.

# Salva só as primeiras 138 linhas (que eu sabia que estavam intactas)
head -n 138 ~/.bashrc > ~/.bashrc_limpo
mv ~/.bashrc_limpo ~/.bashrc
source ~/.bashrc

Depois disso, reescrevi os blocos de código que tinham quebrado, mas dessa vez usando o formato cat << 'EOF' (conhecido como heredoc com aspas). Esse formato serve para inserir blocos de texto grandes sem o risco de o terminal interpretar aspas ou caracteres especiais de forma errada. Funcionou de primeira.

O que aprendi

A lição principal que serve para qualquer trabalho em TI: em ambientes temporários (efêmeros), programar de forma defensiva vale mais do que confiar na memória. Eu poderia simplesmente “tentar me lembrar” de não errar a chave } da próxima vez, mas em computadores que resetam o tempo todo, qualquer pequeno erro num arquivo de inicialização derruba todo o seu acesso. Depois desse dia, passei a usar sempre o formato heredoc (<< 'EOF') para adicionar configurações em arquivos: ele ignora aspas e variáveis no meio do texto, e se houver algum erro de digitação, você recebe um aviso claro de erro de sintaxe (syntax error) em vez de ver o seu sistema “sumir” com os comandos (por quebrar o PATH).

E o segundo aprendizado: se todos os seus comandos sumirem de uma vez com a mensagem “comando não encontrado”, o problema quase sempre é o arquivo .bashrc corrompido, e não que os programas foram desinstalados. Antes de tentar reinstalar tudo, abra e confira o seu .bashrc.

Leia também

Comentários

Carregando comentários…

Deixe um comentário