Fabric: Problema con las variables ambientales y PATH en el servidor remoto (sin fuente .bashrc)

Creado en 9 oct. 2016  ·  22Comentarios  ·  Fuente: fabric/fabric

Hola,

Estoy tratando de cargar el archivo .bashrc (es decir, fuente /home/ubuntu/.bashrc) cuando ejecuto el comando de ejecución de fab para agregar algunas variables ambientales y expandir la variable de ruta:

ejecutar ('fuente /home/ubuntu/.bashrc && echo $ PATH')

Esto me muestra solo:

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

en lugar de una lista mucho más larga de rutas que veo cuando inicio sesión en el servidor remoto manualmente.

¿Cómo consigo que sea fabuloso importar correctamente el archivo .bashrc en mi directorio de inicio remoto?

¡Gracias!

Comentario más útil

Esto es una cosa bash, no fabulosa.

Los archivos que bash decide obtener al inicio pueden ser complicados http://blog.flowblok.id.au/2013-02/shell-startup-scripts.html y debian / ubuntu (y la mayoría de las distribuciones) tienen alguna personalización en /etc/profile y en el valor predeterminado ~/.bashrc .

El shell predeterminado que usa fab es /bin/bash -l -c , y -l convierte en un shell de "inicio de sesión". Sin la personalización de debian / ubuntu, es posible que un shell de "inicio de sesión" bash genere ~/.bash_profile pero no ~/.bashrc .

Pero en ubuntu 16.04, parece obtener .bashrc de forma predeterminada incluso para el shell de inicio de sesión. Pero las líneas agregadas en la parte inferior de un .bashrc predeterminado no se procesan, porque se rescata cerca de la parte superior si detecta que se ejecuta de forma no interactiva.

Aquí agregué dos líneas al usuario predeterminado 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
...

Aquí hay un fabfile con el que estoy probando:

from fabric.api import run, env, task

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

Los 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 comentarios

Esto es una cosa bash, no fabulosa.

Los archivos que bash decide obtener al inicio pueden ser complicados http://blog.flowblok.id.au/2013-02/shell-startup-scripts.html y debian / ubuntu (y la mayoría de las distribuciones) tienen alguna personalización en /etc/profile y en el valor predeterminado ~/.bashrc .

El shell predeterminado que usa fab es /bin/bash -l -c , y -l convierte en un shell de "inicio de sesión". Sin la personalización de debian / ubuntu, es posible que un shell de "inicio de sesión" bash genere ~/.bash_profile pero no ~/.bashrc .

Pero en ubuntu 16.04, parece obtener .bashrc de forma predeterminada incluso para el shell de inicio de sesión. Pero las líneas agregadas en la parte inferior de un .bashrc predeterminado no se procesan, porque se rescata cerca de la parte superior si detecta que se ejecuta de forma no interactiva.

Aquí agregué dos líneas al usuario predeterminado 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
...

Aquí hay un fabfile con el que estoy probando:

from fabric.api import run, env, task

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

Los 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: 
...

Suena legítimo de un vistazo, ¡gracias @ploxiln!

¡Gracias por la respuesta detallada!

Esto parece diferir del comportamiento estándar de ssh y fue difícil de depurar. Cuando hice ssh manualmente, todo funcionó bien, mientras que la tela tenía un valor de PATH diferente debido a que no se ejecutaba .bash_profile en OSX.

@bitprophet, ¿ esto puede ser un error de OSX?

@ code-tree ¿Está utilizando Fabric 1 o 2? 1.x utiliza envoltorios de shell explícitos, por lo que se diferenciaría del cliente OpenSSH estándar; la versión 2 no realiza ningún ajuste y debería comportarse mucho más como el cliente OpenSSH en términos de lo que el sshd está ejecutando en su nombre.

La versión 1 tiene algunas opciones de valor de configuración env para cambiar esa envoltura de shell, por lo que puede intentar agregarle indicadores, como -i que IIRC es bash para "ejecutar modo interactivo y obtener algunos archivos adicionales".

Eso es extraño porque estoy usando la versión 2.2.1 . Actualmente tengo que hacer lo siguiente para que mi aplicación funcione:
c.run('bash -l -c "python3 ./configure.py"')
Como cuando instalé Python 3, se agregó a PATH a través de mi .bash_profile , que no está siendo ejecutado por Fabric. Tampoco he realizado ninguna modificación en la configuración estándar de sshd incorporada.

Eso es extraño entonces, tendré que ver si puedo replicar. Acabo de hacer un seguimiento rápido de la cordura para demostrar lo que quiero decir con respecto a que la tela 2 no hace nada 'especial' con la ejecución impulsada por sshd:

@ code-tree también, si puede, publique algunos detalles más sobre lo que está ejecutando exactamente y otros detalles ambientales (sistema operativo / versión del cliente y del servidor, etc., etc.).

Claro, estoy ejecutando una versión antigua de OSX como servidor. Si puede confirmar que todo funciona bien en un MacOS más nuevo, entonces bueno. Pero si no, ¿quizás un problema con MacOS como servidor en general?

  • 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 al margen, me pregunto si este es realmente un caso de diagnóstico erróneo:

la tela tenía un valor de PATH diferente debido a que no se ejecutaba .bash_profile en OSX

¿Podría ser, en cambio, un ejemplo de # 1744, donde Fabric 2 se diferencia de ssh en términos de desviar las variables de entorno locales por el conducto? (Depende de las variables env exactas en cuestión y si podrían ser similares local y remotamente; @ code-tree podría refutar fácilmente esta teoría al verificar dos veces los valores exactos en juego: su bash_profile local y remoto ... 😁)


Pero voy a solucionar este problema directamente de todos modos, solo para saber que no estoy loco con respecto a mi comprensión de lo que está haciendo el sshd en términos de shells y archivos de origen.

También me pregunto si esto está relacionado con el escenario / inteligencia de # 1816, que también trata sobre shells y archivos de inicio. Es _probablemente_ no directamente relevante debido a la afirmación aquí sobre ssh comportándose de una manera y Fabric comportándose de otra manera (en comparación con "cómo el sshd invoca el shell normalmente"), pero vale la pena vincularlo de todos modos.

Descubrí que tratando de tropezar con un montón de buceo profundo en código sshd que recuerdo haber hecho en los últimos meses, relacionado con algo en este sentido. Sin embargo, no puedo encontrarlo ahora, lo cual es irritante. EDITAR: ah, creo que fue un problema de Paramiko no relacionado (no vincular porque no tiene sentido confundir las cosas) sobre la ejecución de comandos antes de la autenticación. está bien.

Muy bien, entonces, ¿qué está sucediendo realmente cuando solicitamos el comando ejecutivo?

  • SSH-the-protocol quiere que hagas una solicitud de canal shell (que carga el programa de shell de inicio de sesión definido por el usuario y no toma ningún command ) o un canal exec solicitud (que solo dice "ejecuta una cadena de comando dada, que puede contener una ruta" y no especifica más)
  • Paramiko hace lo último, como vimos anteriormente.
  • Entonces, ¿qué está haciendo OpenSSH en sí? Estaré mirando mi pago local de openssh-portable (en 775f8a23f2353f5869003c57a213d14b28e0736e )

Bueno, primero voy a tontear un poco. Ejecutando contra un contenedor Debian 8.10 arbitrario (¡que está ejecutando OpenSSH 6.7!) Veo esto en los registros de nivel DEBUG3 al hacer 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

Haciendo 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

Bien, ambos están haciendo exec ... eso es de esperar. ¿Algo diferente en el árbol de procesos? no debería ser, y ... no lo hay. ambos se ven así:

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

Entonces, ¿definitivamente no hay caparazón en juego? ¿Qué está haciendo? ¿Puedo usar shelly cosas como && ?

¡Yo puedo! por ejemplo, whoami && id funciona bien. Eso es un poco extraño dado mi conocimiento del control de procesos de Unix; parece poco probable que sshd esté haciendo literalmente exec(string) , pero si estuviera usando un shell para interpretar, ¿no veríamos eso en pstree ?

Creo que es hora de profundizar realmente en openssh-portable y ver qué es qué.


Además, eh, hice parte de este rastreo (pero por una razón / enfoque diferente) en el pasado, como en diciembre de 2016: https://github.com/paramiko/paramiko/pull/398#issuecomment -264281759. Lo había olvidado por completo ...


La razón por la que no veo el shell en pstree se debe al uso de execve , que reemplaza el proceso principal en lugar de generar un hijo. Bueno saber. (EDIT: mal, que simplemente se cambia de proc niño sshd con la cáscara; el propio depósito puede entonces también estará haciendo una llamada exec estilo reemplazar-me; véanse las observaciones).


¡De todos modos, entonces! Esto todavía debería significar que los clientes de Fabric y OpenSSH están haciendo exactamente lo mismo en términos de ejecución de shell; Nada de lo que esté haciendo cualquiera de ellos sería modificar estas partes del código base de OpenSSH. Siempre debe ser algo así como bash -c "python3 ./configure.py" , con el valor real de bash dependiendo del usuario de nivel macOS de @ code-tree y su shell configurado.

Me hace preguntarme si mi corazonada acerca de que la transmisión env var es el diferenciador es precisa, ya que esa es la única otra cosa en la que puedo pensar: en qué podrían diferir los dos entornos de ejecución. FWIW en mis pruebas centradas en CLI, env informa lo mismo en ambos sistemas, e independientemente de esos otros problemas relacionados con el entorno, el comportamiento _default_ (debido a la política de seguridad SSH) siempre debe ser que ningún entorno local "se filtre" a el otro lado.

Sí, estoy bastante seguro de que sshd siempre ejecuta la cadena de comandos en el shell del usuario (configurado en / etc / passwd). Sin embargo, antepone "exec" a la cadena de comandos si puede analizar la cadena de comandos y determinar que es un solo 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

En cuanto a las variables de entorno, en su mayoría están controladas por el cliente ssh y las configuraciones del servidor sshd:

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

Entonces, el cliente ssh envía explícitamente algunas variables (fuera de la propia cadena de comandos) y son filtradas por sshd. Pero aparentemente hay algunas excepciones:

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

En realidad, creo que el comportamiento del "ejecutivo automático para reemplazar el shell con un solo comando" es una característica de 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 completar:

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

Ah sí, estaba equivocado, execve solo significa que el proceso secundario sshd es lo que se reemplaza, así que lo que sucede después de eso depende del shell mismo y lo que hace por -c xxx . Mis pruebas estaban en zsh, fwiw.

También @ploxiln sí, lo que pasa con env vars es hacer referencia a otros tickets vinculados anteriormente, aunque queda por ver si @ code-tree realmente está experimentando un síntoma diferente a esos.

Lo siento, creo que esto es una falsa alarma. No sabía que había alguna diferencia entre ejecutar un comando a través de ssh en línea y ejecutar después de iniciar sesión a través de ssh. Cuando hago ssh host 'echo $PATH' , tampoco ha actualizado PATH donde como echo $PATH después de iniciar sesión funciona bien.

Todavía no entiendo por qué ssh hace esto (parece que tengo que hacer ssh host 'bash -l -c "python3 ./configure.py"' ). Independientemente, es seguro decir que, después de todo, esto no es un problema de Fabric. Perdón por la confusion.

@ code-tree está relacionado con lo que se mencionó anteriormente: los shells tienen algunos modos diferentes en los que operan, con frecuencia llamados 'inicio de sesión' e 'interactivo' (y generalmente están en la parte superior de un modo base que no se considera ninguno) y qué modo el shell se está ejecutando, cambia qué conjunto de archivos rc carga.

El punto más destacado aquí es que bash -c xxx no se considera "interactivo" y, por lo tanto, omite la carga de ciertos archivos, como .bash_profile , pero simplemente ejecuta bash sin -c suele ser interactivo y carga archivos de perfil.

Como vio anteriormente, sshd siempre ejecuta bash -c <command send down the pipe> cuando le pide que ejecute un solo comando (como lo hace Fabric, o como lo hace ssh host command ) y, por lo tanto, siempre está en modo no interactivo. (A menos que haga lo que hace Fabric 1 y ejecute su propio shell anidado con -l ... pero luego está ejecutando bash -c "bash -l -c \"oh god escaping is hard help\"" y voy a ir a tener algunos 🥃 ahora.

De todos modos, no todo está perdido, necesito refrescar mi memoria de esta parte de las cosas cada pocos años, aparentemente así que eh ... ahora estoy refrescado 😆

Esto es una cosa bash, no fabulosa.

Los archivos que bash decide obtener al inicio pueden ser complicados http://blog.flowblok.id.au/2013-02/shell-startup-scripts.html y debian / ubuntu (y la mayoría de las distribuciones) tienen alguna personalización en /etc/profile y en el valor predeterminado ~/.bashrc .

El shell predeterminado que usa fab es /bin/bash -l -c , y -l convierte en un shell de "inicio de sesión". Sin la personalización de debian / ubuntu, es posible que un shell de "inicio de sesión" bash genere ~/.bash_profile pero no ~/.bashrc .

Pero en ubuntu 16.04, parece obtener .bashrc de forma predeterminada incluso para el shell de inicio de sesión. Pero las líneas agregadas en la parte inferior de un .bashrc predeterminado no se procesan, porque se rescata cerca de la parte superior si detecta que se ejecuta de forma no interactiva.

Aquí agregué dos líneas al usuario predeterminado 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
...

Aquí hay un fabfile con el que estoy probando:

from fabric.api import run, env, task

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

Los 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: 
...

Me salvaste la vida. Esta es la respuesta perfecta que estoy encontrando :) ¡Muchas gracias!

¿Fue útil esta página
0 / 5 - 0 calificaciones