Como iniciar automaticamente o tmux na sessão SSH?

101

Tenho cerca de dez servidores aos quais me conecto com SSH regularmente. Cada um tem uma entrada no ~/.ssh/configarquivo do meu computador local .

Para evitar perder o controle do meu processo de execução quando minha conexão com a Internet inevitavelmente cai, sempre trabalho dentro de uma tmuxsessão. Eu gostaria de ter uma maneira de fazer o tmux conectar-se automaticamente toda vez que uma conexão SSH for iniciada, então eu não tenho que digitar sempre tmux attach || tmux newapós fazer SSH.

Infelizmente, isso não está sendo tão simples quanto eu esperava.

  • Não quero adicionar nenhum comando aos ~/.bashrcservidores porque só quero isso para sessões SSH, não para sessões locais.
  • Adicionar tmux attach || tmux newaos ~/.ssh/rcservidores simplesmente resulta no erro not a terminalsendo lançado após a conexão, mesmo quando a RequestTTY forceopção é adicionada à linha desse servidor em meu arquivo de configuração SSH local.
Alex Ryan
fonte
Esta continua a ser uma pergunta popular. Gostaria de salientar que o tmux mudou muito desde há cinco anos; as respostas mais elevadas-up não refletem a melhor prática de não fazer isso através do shell em tudo, mas sim via ~/.ssh/config. A resposta que a maioria das pessoas provavelmente precisará é, portanto, stackoverflow.com/a/52838493/5354137 .
Sixtyfive

Respostas:

95

Configuração do lado do servidor:

Para iniciar automaticamente o tmux em seu servidor remoto ao fazer login normalmente via SSH (e apenas SSH), edite o ~/.bashrcdo seu usuário ou root (ou ambos) no servidor remoto de acordo:

if [[ -n "$PS1" ]] && [[ -z "$TMUX" ]] && [[ -n "$SSH_CONNECTION" ]]; then
  tmux attach-session -t ssh_tmux || tmux new-session -s ssh_tmux
fi

Este comando cria uma sessão tmux chamada ssh_tmuxse nenhuma existir, ou reanexa a uma sessão já existente com aquele nome. No caso de sua conexão cair ou quando você esquecer uma sessão semanas atrás, todo login SSH automaticamente o leva de volta à sessão tmux-ssh que você deixou para trás.

Conecte-se a partir do seu cliente:

Nada de especial, apenas ssh user@hostname.

Kingmeffisto
fonte
4
Eu estava procurando por isso, também usei um código muito semelhante ao seu há algum tempo, mas a sessão era o nome de usuário (mudando ssh_tmuxpara $USER)
Iacchus
4
Veja a resposta do moneytoo para comentários úteis sobre o $SSH_TTYvs $SSH_CONNECTIONtambém.
Sr. Tao,
2
você pode usar tmux new-session -A -s ssh_tmuxpara substituir tmux attach-session -t ssh_tmux || tmux new-session -s ssh_tmuxmuito mais curto, se um pouco mais confuso, -Adiz ao tmux para anexar a sessão se ela já existir
Gradiente
3
Para evitar quebrar o "scp", você também precisa verificar se este é um shell interativo:if [[ -n "$PS1" ]] && [[ -z "$TMUX" ]] && [[ -n "$SSH_CONNECTION" ]];
janfrode
3
@janfrode não confie em $PS1, use em [[ $- == *i* ]]vez disso, pois o PS1 pode ser definido mesmo quando não é um shell interativo.
Enrico
56

Tudo bem, encontrei uma solução bastante satisfatória. No meu local ~/.bashrc, escrevi uma função:

function ssh () {/usr/bin/ssh -t $@ "tmux attach || tmux new";}

que basicamente sobrescreve a função de terminal ssh para chamar o programa ssh embutido com os argumentos fornecidos, seguido por "tmux attach || tmux new".

(O $@denota todos os argumentos fornecidos na linha de comando, então ssh -p 123 user@hostnameserá expandido para ssh -t -p 123 user@hostname "tmux attach || tmux new")

(O -targumento é equivalente RequestTTY Forcee necessário para o comando tmux.)

Alex Ryan
fonte
22
Se a sua versão do tmuxsuportar, considere usar o tmux new -A fooqual será anexado a uma sessão existente chamada, foose possível, criando-a, se necessário. Isso permite que você simplifique sua função para /usr/bin/ssh -t "$@" tmux new -A(e certifique-se de citar $@!).
chepner de
2
Nota: se algumas das máquinas às quais você se conecta regularmente não têm o tmux instalado, você pode querer dizer function sshtou algo parecido para que possa continuar a usar sshnormalmente. Caso contrário, basta digitar /usr/bin/sshno prompt de comando sempre que se conectar a uma máquina sem tmux :)
Alex Ryan
1
Se você for preguiçoso, pode apenas usar o ssht para se conectar às sessões remotas do tmux. Os usuários do OS X podem tocá-lo via brew e os usuários do Linux podem criar um pacote via fpm com este Makefile ou simplesmente copiar sshtpara ~/bin.
brejoc
1
Haha legal! Parece um pouco exagero para mim embrulhar esta linha em um repositório Github inteiro com Makefiles e cerveja e tal, mas ei, quanto mais fácil, melhor!
Alex Ryan
1
Resolvido:ssh -t user@hostname "LANG=$LANG tmux attach || tmux new"
alecdwm
23

Conectar:

ssh user@host -t "tmux new-session -s user || tmux attach-session -t user"

Durante a sessão:

Use Ctrl+dpara terminar a sessão (a janela do tmux fecha) ou Ctrl+b dpara desconectar temporariamente da sessão e conectar-se a ela novamente mais tarde.

Lembrar! Se o seu servidor reiniciou a sessão perdida!

Quando você estiver dentro do tmux a qualquer momento, você pode usar Ctrl+b spara ver a lista de sessões e mudar a atual para outra.

Corrija seu .bashrc:

Eu recomendo que você defina a função universal em seu .bashrc:

function tmux-connect {
    TERM=xterm-256color ssh -p ${3:-22} $1@$2 -t "tmux new-session -s $1 || tmux attach-session -t $1"
}

Ele usa a 22porta por padrão. Defina também seus aliases de conexão rápida:

alias office-server='tmux-connect $USER 192.168.1.123'
alias cloud-server='tmux-connect root my.remote.vps.server.com 49281'

Login sem senha:

E se você não quiser digitar a senha todas as vezes, gere .sshchaves para fazer o login automaticamente :

ssh-keygen -t rsa
eval "$(ssh-agent -s)" && ssh-add ~/.ssh/id_rsa

Coloque sua chave pública no host remoto:

ssh-copy-id -p <port> user@hostname

Dicas adicionais:

Se você deseja usar o id de sessão temporário que corresponde a uma sessão bash local, use como id do tmux :

SID=$USER-$BASHPID
ssh user@host -t "tmux new-session -s $SID || tmux attach-session -t $SID"
DenisKolodin
fonte
1
Um truque para evitar que ||, em alguns casos de uso é a de incluir new-sessionno .tmux.confe apenas usar sempre tmux a -t 0.
Florian Wendelborn
4
Em versões mais recentes do tmux, você também pode usar o tmux new-session -Aqual anexará se existir, caso contrário, criará um novo.
dragon788
18

tmux 3.1 ou mais recente¹ na máquina remota

Em seu local ~/.ssh/config, coloque²:

Host myhost
  Hostname host
  User user
  RequestTTY yes
  RemoteCommand tmux new -A -s foobar

Não relacionado, mas se você estiver lidando com caracteres não ASCII, recomendo mudar para tmux -u …para habilitar explicitamente o suporte Unicode, mesmo em máquinas que não têm as variáveis ​​de ambiente adequadas definidas.

tmux 3.0a ou mais antigo na máquina remota

Quase o mesmo que acima, mas altere a última linha para³:

  RemoteCommand tmux at -t foobar || tmux new -s foobar

¹ Em 2020-10-29, a lista de distribuições enviadas com tmux 3.1 ou mais recente já é bastante longa.

² newé a abreviação de new-session.

³ até a abreviação de attach-session.


Método alternativo usando o authorized_keysarquivo do controle remoto :

Se você preferir não ter um ~/.ssh/configarquivo por qualquer motivo, ou deseja que a máquina remota force a máquina conectada a se conectar / abrir a sessão, adicione ao seu controle remoto ~/.ssh/authorized_keys:

command="tmux at -t foobar || tmux new -s foobar" pubkey user@client

Isso, é claro, funcionará com todos os clientes que tenham a chave privada correspondente instalada, o que pode ser uma vantagem ou uma desvantagem, dependendo do que você deseja. Existe o risco de que, caso algo dê errado, não seja mais possível conectar.

Sessenta e cinco
fonte
por que em tmux atvez de tmux a? Também seria sensato usar uma sessão nomeada para isso ou o tmux seria anexado a sessões "aleatórias" existentes ao se logar no host.
Eric
Como você suspende a sessão do tmux? ssh entra em estado de limbo kindda após bater Ctrl+A Ctrl+Z.
Eric
Ele simplesmente se desconecta. No que me diz respeito, esse é o comportamento que eu esperava e com o qual estou feliz.
Sixtyfive
1
Ctrl-B Dfunciona o tratamento em comparação com Ctrl-B Ctrl-Z. Obrigado!
Eric,
1
Esta deve ser, imho, a resposta mais votada. Eu estava procurando exatamente por (2).
cduguet
17

Usei linhas de @kingmeffisto (não tenho permissão para comentar essa resposta) e adicionei uma saída para encerrar o tmux também encerrar a conexão ssh. No entanto, isso quebrou as sessões de SFTP, então tive que verificar em $SSH_TTYvez de $SSH_CONNECTION.

EDITAR 4/2018: Adicionado teste para terminal interativo via [[ $- =~ i ]]para permitir que ferramentas como o Ansible funcionem.

if [ -z "$TMUX" ] && [ -n "$SSH_TTY" ] && [[ $- =~ i ]]; then
    tmux attach-session -t ssh || tmux new-session -s ssh
    exit
fi
dinheiro demais
fonte
15

Conforme descrito nesta postagem do blog, você pode fazer o ssh e depois anexar a uma sessão tmux existente com um único comando:

ssh hostname -t tmux attach -t 0
Fabian Pedregosa
fonte
1
Isso é o que minha resposta faz (embora eu use tmux attach || tmux newpara que uma nova sessão do tmux não seja criada para cada conexão). A parte complicada é que o comando correto é ssh -t user@host tmux attach || tmux newe a única maneira de criar um alias para algo que precisa de um argumento dentro da string de comando é criar uma nova função, como fiz acima.
Alex Ryan
Eu sei, mas algumas pessoas (como eu) podem preferir uma linha que não envolva a definição de uma função
Fabian Pedregosa
3
Isso se conecta a uma sessão chamada '0'. Ou seja, a forma geral éssh [hostname] -t tmux attach -t [sessionName]
David Doria
1
Isso funcionou muito bem para mim .. Combinado isso vai unix.stackexchange.com/a/116674 .. agora minha GUI do putty se parece com isso .. imgur.com/uFhxN30 . Posso desconectar as sessões com Cntrl + b + d. Muito simples e conveniente ..
alpha_989
2

byobu é um wrapper muito útil para tmux / screen. Conecta-se a uma sessão existente, se houver, ou cria uma nova.

Eu o uso com autossh, que reconecta a sessão ssh de maneira elegante. Altamente recomendado em caso de problemas de conectividade intermitente.

function ssh-tmux(){
  if ! command -v autossh &> /dev/null; then echo "Install autossh"; fi
  autossh -M 0 $* -t 'byobu || {echo "Install byobu-tmux on server..."} && bash'
}
Sandeep
fonte
2

Você pode achar isto útil - usa ssh em um loop e se reconecta ou se conecta a uma sessão tmux existente para que você tenha uma maneira fácil e confiável de se reconectar após uma queda de rede

#!/bin/bash
#
# reconnect to or spawn a new tmux session on the remote host via ssh.
# If the network connection is lost, ssh will reconnect after a small
# delay.
#

SSH_HOSTNAME=$1
TMUX_NAME=$2
PORT=$3

if [[ "$PORT" != "" ]]
then
    PORT="-p $PORT"
fi

if [ "$TMUX_NAME" = "" ]
then
    SSH_UNIQUE_ID_FILE="/tmp/.ssh-UNIQUE_ID.$LOGNAME"

    if [ -f $SSH_UNIQUE_ID_FILE ]
    then
        TMUX_NAME=`cat $SSH_UNIQUE_ID_FILE`
        TMUX_NAME=`expr $TMUX_NAME + $RANDOM % 100`
    else
        TMUX_NAME=`expr $RANDOM % 1024`
    fi

    echo $TMUX_NAME > $SSH_UNIQUE_ID_FILE

    TMUX_NAME="id$TMUX_NAME"
fi

echo Connecting to tmux $TMUX_NAME on hostname $SSH_HOSTNAME

SLEEP=0
while true; do

    ssh $PORT -o TCPKeepAlive=no -o ServerAliveInterval=15 -Y -X -C -t -o BatchMode=yes $SSH_HOSTNAME "tmux attach-session -t $TMUX_NAME || tmux -2 -u new-session -s $TMUX_NAME"
    SLEEP=10
    if [ $SLEEP -gt 0 ]
    then
        echo Reconnecting to session $TMUX_NAME on hostname $SSH_HOSTNAME in $SLEEP seconds
        sleep $SLEEP
    fi
done
Neil McGill
fonte
2

Este é o que realmente cria uma ótima experiência do usuário. Ele inicia automaticamente o tmux sempre que você abre o terminal (tanto fisicamente quanto ssh). Você pode iniciar seu trabalho em um dispositivo, sair do terminal e retomar no outro. Se ele detectar alguém já conectado à sessão, ele criará uma nova sessão. Coloque-o no servidor , dependendo do seu shell ~/.zshrcou ~/.bashrc.

 if [[ -z "$TMUX" ]] ;then
     ID="$( tmux ls | grep -vm1 attached | cut -d: -f1 )" # get the id of a deattached session
     if [[ -z "$ID" ]] ;then # if not available attach to a new one
         tmux new-session
     else
         tmux attach-session -t "$ID" # if available attach to it
     fi
fi
Stanislaw Baranski
fonte
0

Sei que estou revivendo um tópico antigo, mas fiz alguns trabalhos na solução bashrc e acho que tem alguma utilidade:

#attach to the next available tmux session that's not currently occupied
if [[ -z "$TMUX" ]] && [ "SSH_CONNECTION" != "" ];
then
    for i in `seq 0 10`; do #max of 10 sessions - don't want an infinite loop until we know this works
            SESH=`tmux list-clients -t "$USER-$i-tmux" 2>/dev/null` #send errors to /dev/null - if the session doesn't exist it will throw an error, but we don't care
            if [ -z "$SESH" ] #if there's no clients currently connected to this session
            then
                tmux attach-session -t "$USER-$i-tmux" || tmux new-session -s "$USER-$i-tmux" #attach to it
                break #found one and using it, don't keep looping (this will actually run after tmux exits AFAICT)
            fi #otherwise, increment session counter and keep going
    done

fi

Há um limite de 10 (11) sessões por enquanto - eu não queria matar meu servidor com um loop infinito em bashrc. Parece funcionar de forma bastante confiável, exceto o erro de falha do tmux em clientes de lista se a sessão não existir.

Brydon Gibson
fonte
0

Essa maneira permite que você se reconecte a uma instância antiga do tmux se sua sessão ssh cair. O execsalva um garfo é claro.

if [ -z "$TMUX"  ]; then
  pid=$(tmux ls | grep -vm1 "(attached)" | cut -d: -f1)
  if [ -z "$pid" ]; then
    tmux new -d -s $pid
  fi

  exec tmux attach -t $pid
fi
ericcurtina
fonte
0

Acrescentar ao fundo do seu servidor remoto de~/.bashrc , (ou, eventualmente, a sua /etc/.bashrc.shared(1)) :

# ======================== PUT THIS LAST IN .BASHRC ==========================
# --- If we're run by SSH, then auto start `tmux` and possibly re-attach user.
#       $-         interactive only via current option flags
#       -z $TMUX   no tmux nesting
#       $SSH_TTY   SSH must be running, and in a shell
#
if [[ $- == *i* ]] && [[ -z "$TMUX" ]] && [[ -n "$SSH_TTY" ]];  then
  tmux attach-session -t "$USER"  || tmux new-session -s "$USER" && exit
fi

Muitas dicas boas acima combinadas aqui , por exemplo, $-e $SSH_TTYsão melhores, eu acho.

E gosto de adicionar alguns comentários para ajudar esse velho a se lembrar do que está acontecendo sem ter que procurar.

E, finalmente, gosto de um exitfinal para voltar para casa de forma limpa quando terminar.

Obrigado a todos.


Note que eu forneço um compartilhado /etc/.bashrc.sharedno final do usuário e do root .bashrc, para coisas comuns usadas em ambos, como colorized ls, vários aliases, funções e extensões de caminho, ou seja, não quero código redundante em meu root / .bashrc nem usuário /.bashrc.

Vista elíptica
fonte