Fabric: Problema com variáveis ​​ambientais e PATH no servidor remoto (não fornecendo .bashrc)

Criado em 9 out. 2016  ·  22Comentários  ·  Fonte: fabric/fabric

Oi,

Estou tentando carregar o arquivo .bashrc (ou seja, fonte /home/ubuntu/.bashrc) ao executar o comando run do fab para adicionar algumas variáveis ​​ambientais e expandir a variável de caminho:

run ('source /home/ubuntu/.bashrc && echo $ PATH')

Isso me mostra apenas:

[[email protected]] out: / usr / local / sbin: / usr / local / bin: / usr / sbin: / usr / bin: / sbin: / bin: / usr / games: / usr / local / games

em vez de uma lista muito mais longa de caminhos que vejo quando faço login no servidor remoto manualmente.

Como faço para que o fab importe corretamente o arquivo .bashrc em meu diretório inicial remoto?

Obrigado!

Comentários muito úteis

Isso é uma coisa de bash, não uma coisa fabulosa.

Os arquivos que o bash decide fornecer na inicialização podem ser complicados http://blog.flowblok.id.au/2013-02/shell-startup-scripts.html e debian / ubuntu (e a maioria das distros) têm alguma personalização em /etc/profile e no padrão ~/.bashrc .

O shell padrão que o fab usa é /bin/bash -l -c , e o -l torna um shell de "login". Sem a personalização debian / ubuntu, é possível que um shell de "login" do bash forneça ~/.bash_profile mas não ~/.bashrc .

Mas no ubuntu 16.04, parece fonte .bashrc por padrão, mesmo para login-shell. Mas as linhas adicionadas na parte inferior de um .bashrc padrão não são processadas, porque ele sai do topo se detectar uma execução não interativa.

Aqui, adicionei duas linhas ao usuário padrão ubuntu-16.04 .bashrc

# ~/.bashrc: executed by bash(1) for non-login shells.
# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
# for examples

export VAR1=val1

# If not running interactively, don't do anything
case $- in
    *i*) ;;
      *) return;;
esac

export VAR2=val2

# don't put duplicate lines or lines starting with space in the history.
# See bash(1) for more options
HISTCONTROL=ignoreboth
...

Aqui está um fabfile com o qual estou testando:

from fabric.api import run, env, task

<strong i="23">@task</strong>
def get_myvars():
    run("echo VAR1=$VAR1 VAR2=$VAR2")

Os resultados:

$ fab -H testpy05.ec2.st-av.net get_myvars
[testpy05.ec2.st-av.net] Executing task 'get_myvars'
[testpy05.ec2.st-av.net] run: echo VAR1=$VAR1 VAR2=$VAR2
[testpy05.ec2.st-av.net] out: VAR1=val1 VAR2=
[testpy05.ec2.st-av.net] out: 
...

Todos 22 comentários

Isso é uma coisa de bash, não uma coisa fabulosa.

Os arquivos que o bash decide fornecer na inicialização podem ser complicados http://blog.flowblok.id.au/2013-02/shell-startup-scripts.html e debian / ubuntu (e a maioria das distros) têm alguma personalização em /etc/profile e no padrão ~/.bashrc .

O shell padrão que o fab usa é /bin/bash -l -c , e o -l torna um shell de "login". Sem a personalização debian / ubuntu, é possível que um shell de "login" do bash forneça ~/.bash_profile mas não ~/.bashrc .

Mas no ubuntu 16.04, parece fonte .bashrc por padrão, mesmo para login-shell. Mas as linhas adicionadas na parte inferior de um .bashrc padrão não são processadas, porque ele sai do topo se detectar uma execução não interativa.

Aqui, adicionei duas linhas ao usuário padrão ubuntu-16.04 .bashrc

# ~/.bashrc: executed by bash(1) for non-login shells.
# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
# for examples

export VAR1=val1

# If not running interactively, don't do anything
case $- in
    *i*) ;;
      *) return;;
esac

export VAR2=val2

# don't put duplicate lines or lines starting with space in the history.
# See bash(1) for more options
HISTCONTROL=ignoreboth
...

Aqui está um fabfile com o qual estou testando:

from fabric.api import run, env, task

<strong i="23">@task</strong>
def get_myvars():
    run("echo VAR1=$VAR1 VAR2=$VAR2")

Os resultados:

$ fab -H testpy05.ec2.st-av.net get_myvars
[testpy05.ec2.st-av.net] Executing task 'get_myvars'
[testpy05.ec2.st-av.net] run: echo VAR1=$VAR1 VAR2=$VAR2
[testpy05.ec2.st-av.net] out: VAR1=val1 VAR2=
[testpy05.ec2.st-av.net] out: 
...

Parece legítimo à primeira vista, obrigado @ploxiln!

Obrigado pela resposta detalhada!

Isso parece ser diferente do comportamento padrão do ssh e era difícil de depurar. Quando fiz o ssh manualmente, tudo funcionou bem, onde o tecido tinha um valor de PATH diferente devido a não rodar .bash_profile no OSX.

@bitprophet isso pode ser um bug do OSX ??

@ code-tree Você está usando o Fabric 1 ou 2? 1.x usa invólucros de shell explícitos, o que seria o motivo pelo qual seria diferente do cliente OpenSSH padrão; a versão 2 não faz nenhum empacotamento e deve se comportar muito mais como o cliente OpenSSH em termos do que o sshd está executando em seu nome.

A versão 1 tem algumas opções de valor de configuração de env para alterar esse invólucro de shell, então você pode querer tentar adicionar sinalizadores a ele, como -i que IIRC é bash para "executar modo interativo e obter alguns arquivos extras".

Isso é estranho porque estou usando a versão 2.2.1 . Atualmente, tenho que fazer o seguinte para que meu aplicativo funcione:
c.run('bash -l -c "python3 ./configure.py"')
Como quando instalei o Python 3, ele se adicionou ao PATH por meio do meu .bash_profile , que não está sendo executado pelo Fabric. Eu não fiz nenhuma modificação na configuração padrão do sshd embutido.

Isso é estranho então, terei que ver se consigo replicar. Acabei de fazer um rápido rastreamento de sanidade para provar o que quero dizer re: Fabric 2 não está fazendo nada 'especial' re: sshd dirigido execução:

@ code-tree também, se você puder, poste mais alguns detalhes sobre exatamente o que você está executando e outros detalhes ambientais (cliente e servidor OS / versão, etc etc).

Claro, estou executando uma versão antiga do OSX como servidor. Se você pode confirmar que tudo funciona bem em um MacOS mais recente, então tudo bem. Mas se não, talvez haja um problema com o MacOS como servidor em geral?

  • Cliente: Ubuntu 16.04, Python 3.6.6, OpenSSH 7.2p2, Bash 4.3.48, Fabric 2.2.1
  • Servidor: OSX 10.11, Python 3.6.6, OpenSSH 6.9p1, Bash 3.2.57

Nota lateral, estou me perguntando se este é realmente um caso de diagnóstico incorreto:

fabric tinha um valor de PATH diferente devido à não execução de .bash_profile no OSX

Em vez disso, poderia ser um exemplo de # 1744, em que o Tecido 2 difere de ssh em termos de desvios de env locais para baixo do cano? (Depende dos env vars exatos em questão e se eles podem ser semelhantes localmente e remotamente; @ code-tree poderia facilmente refutar esta teoria ao verificar os valores exatos em jogo re: seu bash_profile local e remoto ... 😁)


Mas vou solucionar isso diretamente de qualquer maneira, só para saber que não estou louco com relação ao meu entendimento do que o sshd está fazendo em termos de shells e arquivos de origem.

Também me pergunto se isso está relacionado ao cenário / inteligência de # 1816, que também é sobre shells e arquivos de inicialização. É _provavelmente_ não diretamente relevante por causa da afirmação aqui sobre ssh se comportando de uma maneira e o Fabric se comportando de outra maneira (em vez de ser "apenas como o sshd invoca o shell normalmente"), mas vale a pena vincular de qualquer maneira.

Descobri que tentei tropeçar em um monte de mergulho profundo de código sshd que me lembro de ter feito nos últimos meses, relacionado a algo nesse sentido. Não consigo encontrar agora, o que é irritante. EDIT: ah eu acho que era um problema Paramiko não relacionado (não ligando porque não adianta confundir as coisas) sobre a execução de comandos antes da autenticação. OK.

Tudo bem, então o que realmente está acontecendo quando solicitamos o comando exec?

  • O protocolo SSH deseja que você faça uma solicitação de canal shell (que carrega o programa de shell de login definido pelo usuário e não aceita nenhum command ) ou um canal exec solicitação (que apenas diz "executa a string de comando fornecida, que pode conter um caminho" e não especifica mais)
  • Paramiko faz o último, como vimos acima.
  • Então, o que o próprio OpenSSH está realmente fazendo? Estarei olhando minha verificação local do openssh-portable (em 775f8a23f2353f5869003c57a213d14b28e0736e )

Bem, primeiro vou brincar um pouco. Executando em um contêiner arbitrário do Debian 8.10 (que está executando o OpenSSH 6.7!) Eu vejo isso nos registros do nível DEBUG3 ao fazer ssh localhost whoami :

debug1: server_input_channel_req: channel 0 request exec reply 1
debug1: session_by_channel: session 0 channel 0
debug1: session_input_channel_req: session 0 req exec
Starting session: command for root from 172.17.0.1 port 42598

Fazendo fab -H localhost -- whoami :

debug1: server_input_channel_req: channel 0 request exec reply 1
debug1: session_by_channel: session 0 channel 0
debug1: session_input_channel_req: session 0 req exec
Starting session: command for root from 172.17.0.1 port 42610

OK, os dois estão fazendo exec ... isso é esperado. Algo diferente na árvore do processo? não deveria ser, e ... não há. ambos se parecem com isto:

sshd,1 -D -e
  └─sshd,1535
      └─pstree,1537 -alpU

Então, definitivamente não há concha em jogo? O que está fazendo? Posso usar shelly coisas como && ?

Eu posso! por exemplo, whoami && id funciona bem. Portanto, isso é um pouco estranho, dado o meu entendimento de controle de processos Unix; parece improvável que o sshd esteja literalmente fazendo exec(string) , mas se estivesse usando um shell para interpretar, não veríamos isso em pstree ?

Acho que é hora de realmente mergulhar no openssh-portable e ver o que é.


Além disso, huh, eu _fazia_ parte desse rastreamento (mas por um motivo / foco diferente) no passado, como em dezembro de 2016: https://github.com/paramiko/paramiko/pull/398#issuecomment -264281759. Tinha esquecido totalmente ...


A razão pela qual não estou vendo o shell em pstree é por causa do uso de execve , que substitui o processo pai em vez de gerar um filho. Bom saber. (EDIT: errado, que apenas substitui o proc criança sshd com a casca; o próprio shell pode então também estar fazendo uma chamada exec estilo substituir-me, veja abaixo comentários.)


Enfim, então! Isso ainda deve significar que os clientes do Fabric e do OpenSSH estão fazendo exatamente a mesma coisa em termos de execução de shell; nada que qualquer um deles esteja fazendo seria modificar essas partes da base de código do OpenSSH. Deve ser sempre algo como bash -c "python3 ./configure.py" , com o valor real de bash dependendo do nível de usuário do macOS de @ code-tree e seu shell configurado.

Faz-me perguntar se meu palpite sobre a transmissão env var ser o diferenciador está correto, já que essa é a única outra coisa que posso pensar sobre como os dois ambientes de execução podem ser diferentes. FWIW em meu teste focado em CLI, env relata o mesmo em ambos os sistemas e, independentemente desses outros problemas relacionados ao ambiente, o comportamento _padrão_ (devido à política de segurança SSH) deve ser sempre que nenhum env local "vaza" para o outro lado.

Sim, tenho quase certeza de que o sshd sempre executa a string de comando no shell do usuário (configurado em / etc / passwd). No entanto, ele adiciona "exec" antes da string de comando se puder analisar a string de comando e determinar que é um único comando.

$ ssh testdeploy02.ec2.st-av.net pstree -a
...
  |-sshd -D
  |   `-sshd 
  |       `-sshd  
  |           `-pstree -a
$ ssh testdeploy02.ec2.st-av.net 'pstree -a && sleep 1'
...
  |-sshd -D
  |   `-sshd 
  |       `-sshd  
  |           `-bash -c pstree -a && sleep 1
  |               `-pstree -a
$ ssh testdeploy02.ec2.st-av.net 'VAR=-a sh -c "exec pstree \$VAR"'
...
  |-sshd -D
  |   `-sshd 
  |       `-sshd  
  |           `-pstree -a

Quanto às variáveis ​​de ambiente, elas são controladas principalmente pelas configurações do cliente ssh e do servidor sshd:

$ grep Env /etc/ssh/*_config
/etc/ssh/ssh_config:    SendEnv LANG LC_*
/etc/ssh/sshd_config:AcceptEnv LANG LC_*

Portanto, o cliente ssh envia explicitamente algumas variáveis ​​(fora da string de comando em si) e elas são filtradas pelo sshd. Mas aparentemente existem algumas exceções:

$ ssh testdeploy02.ec2.st-av.net env | grep TERM
$ ssh -t testdeploy02.ec2.st-av.net env | grep TERM
TERM=xterm-256color

Na verdade, acho que o comportamento "exec automático para substituir o shell por um único comando" é um recurso do bash:

$ dash -c "pstree -a"
...
  ├─sshd -D
  │   └─sshd 
  │       └─sshd  
  │           └─bash
  │               └─dash -c pstree -a
  │                   └─pstree -a
$ bash -c "pstree -a"
  ├─sshd -D
  │   └─sshd 
  │       └─sshd  
  │           └─bash
  │               └─pstree -a

EDITAR: Para ser completo:

$ bash -c "pstree -a && sleep 1"
  ├─sshd -D
  │   └─sshd 
  │       └─sshd  
  │           └─bash
  │               └─bash -c pstree -a && sleep 1
  │                   └─pstree -a

Ah sim, eu estava enganado, execve significa apenas que o proc filho sshd é o que é substituído, então o que acontece depois disso depende do próprio shell e o que ele faz por -c xxx . Meus testes foram em zsh, fwiw.

Além disso, @ploxiln sim, a coisa com env vars está fazendo referência a outros tickets vinculados acima, embora

Desculpe, acho que este é um alarme falso. Eu não sabia que havia qualquer diferença entre executar um comando via ssh embutido e executar após o login via ssh. Quando eu faço ssh host 'echo $PATH' ele também não atualizou o PATH onde como echo $PATH após o login funciona bem.

Eu ainda não entendo por que o ssh faz isso (parece que realmente tenho que fazer ssh host 'bash -l -c "python3 ./configure.py"' ). Independentemente disso, é seguro dizer que, afinal, esse não é um problema do Fabric. Desculpe pela confusão.

@ code-tree está relacionado ao que foi mencionado anteriormente - shells têm alguns modos diferentes nos quais operam, freqüentemente chamados de 'login' e 'interativo' (e geralmente aqueles estão no topo de um modo básico que não é considerado nenhum dos dois) e qual modo o shell está sendo executado, muda o conjunto de arquivos rc que ele carrega.

O ponto saliente aqui é que bash -c xxx não é considerado "interativo" e, portanto, pula o carregamento de certos arquivos - como .bash_profile - mas apenas executa bash sem -c geralmente é interativo e carrega arquivos de perfil.

Como você viu acima - sshd sempre executa bash -c <command send down the pipe> quando você pede para executar um único comando (como o Fabric faz, ou como ssh host command faz) e, portanto, está sempre no modo não interativo. (A menos que você faça o que o Fabric 1 faz e execute o seu próprio shell aninhado com -l ... mas então você está executando bash -c "bash -l -c \"oh god escaping is hard help\"" e vou comer 🥃 agora.

De qualquer forma, nem tudo está perdido, eu preciso refrescar minha memória dessa parte das coisas a cada poucos anos, aparentemente, então uh ... agora estou revigorado 😆

Isso é uma coisa de bash, não uma coisa fabulosa.

Os arquivos que o bash decide fornecer na inicialização podem ser complicados http://blog.flowblok.id.au/2013-02/shell-startup-scripts.html e debian / ubuntu (e a maioria das distros) têm alguma personalização em /etc/profile e no padrão ~/.bashrc .

O shell padrão que o fab usa é /bin/bash -l -c , e o -l torna um shell de "login". Sem a personalização debian / ubuntu, é possível que um shell de "login" do bash forneça ~/.bash_profile mas não ~/.bashrc .

Mas no ubuntu 16.04, parece fonte .bashrc por padrão, mesmo para login-shell. Mas as linhas adicionadas na parte inferior de um .bashrc padrão não são processadas, porque ele sai do topo se detectar uma execução não interativa.

Aqui, adicionei duas linhas ao usuário padrão ubuntu-16.04 .bashrc

# ~/.bashrc: executed by bash(1) for non-login shells.
# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
# for examples

export VAR1=val1

# If not running interactively, don't do anything
case $- in
    *i*) ;;
      *) return;;
esac

export VAR2=val2

# don't put duplicate lines or lines starting with space in the history.
# See bash(1) for more options
HISTCONTROL=ignoreboth
...

Aqui está um fabfile com o qual estou testando:

from fabric.api import run, env, task

<strong i="24">@task</strong>
def get_myvars():
    run("echo VAR1=$VAR1 VAR2=$VAR2")

Os resultados:

$ fab -H testpy05.ec2.st-av.net get_myvars
[testpy05.ec2.st-av.net] Executing task 'get_myvars'
[testpy05.ec2.st-av.net] run: echo VAR1=$VAR1 VAR2=$VAR2
[testpy05.ec2.st-av.net] out: VAR1=val1 VAR2=
[testpy05.ec2.st-av.net] out: 
...

Você salvou minha vida. Esta é a resposta perfeita que estou encontrando :) Muito obrigado!

Esta página foi útil?
0 / 5 - 0 avaliações