Uma solução

Uma solução

O daemon Strongswan coloca seus arquivos .pid e .ctl em /var/run para detectar se já está em execução.

Quero executar vários desses daemons na mesma máquina em diferentes namespaces de rede. Estou tentando conseguir isso ligando a montagem de diretórios diferentes (digamos /etc/namespace1 para /var/run do namespace1 e /etc/namespace2 para /var/run do namespace2). /var/run é um link simbólico para /run, então vinculo mount a /run como abaixo.

Quase posso conseguir isso assim:

""No namespace padrão""

$:~ sudo echo "red" >> /etc/red/run/pidfile
$:~ sudo echo "blue" >> /etc/blue/run/pidfile
$:~ sudo ip netns exec red

""No namespace vermelho""

$:~ mount --bind /etc/red/run/ /run/
$:~ cat /var/run/pidfile

vermelho

""No namespace azul""

 $:~ mount --bind /etc/blue/run/ /run/
 $:~ cat /var/run/pidfile

azul

Então isso funciona bem. Dessa forma, o daemon, ao criar /var/run/charon.pid enquanto estiver dentro do vermelho, não será confundido com o /var/run/charon.pid do namespace azul e duas instâncias poderão ser iniciadas.

No entanto, aqui está o problema: se eu "sair" do namespace vermelho e entrar novamente via "ip netns exec red bash", a montagem não estará mais presente. Ou seja, não existe /var/run/redfile.

Então, a questão é como posso tornar isso pegajoso? Preciso de alterações no /etc/fstab? Mas não funciona. Se solicitado, posso fornecer detalhes de "não funciona".

Eu estou perdido. Apreciaremos alguma ajuda.

Obrigado!

Responder1

A solução simples seria instruir cada instância do Strongswan a usar um diretório diferente para armazenar o arquivo PID, definindo o valor correto doIPSEC_PIDDIRvariável de ambiente em seu script de início e parada.

Responder2

ip netns execjá vincule montagens /etc/netns/<netns name>/*no arquivo/diretório correspondente em /etc. Portanto, você pode compilar o StrongSwan com, por exemplo, --with-piddir=/etc/ipsec.d/rune criar os diretórios necessários para que cada instância crie seus arquivos PID em diretórios separados:

# mkdir -p /etc/ipsec.d/run
# mkdir -p /etc/netns/<netns name 1>/ipsec.d/run
# mkdir -p /etc/netns/<netns name 2>/ipsec.d/run

Mais detalhes podem ser encontrados nowiki do StrongSwan.

Responder3

Causa do problema AFAIK

ip netns add ${NSNAME}cria um namespace persistente, montando-o em /run/netns/${NSNAME}. Isso por si só ainda não envolve namespaces de montagem.

Fazer ip netns exec ${NSNAME} cmdgera umtemporáriomount namespace que existe até cmda saída, depois disso o namespace de montagem é limpo, apenas o namespace de rede permanece. Isto é necessário para conseguir a montagem mágica do /etc/netns/${NSNAME}/*. Infelizmente, todas as montagens feitas por eles cmdtambém são limpas.

De man ip-netns:

ip netns execautomatiza o tratamento dessa configuração, convenção de arquivos para aplicativos que não reconhecem namespace de rede, criando um namespace de montagem e montagem vinculada de todos os arquivos de configuração de namespace por rede em seu local tradicional em /etc.

Uma solução

Versão curta

    # Init for persistent mount namespaces
    $ mkdir /run/mntns \
        && mount --bind --make-private /run/mntns /run/mntns

    $ NSNAME=red

    # Create persistent network+mount namespaces
    $ ip netns add ${NSNAME} \
        && touch "/run/mntns/${NSNAME}" \
        && ip netns exec "${NSNAME}" \
            sh -x -c "nsenter -t $$ --mount mount --bind /proc/\$\$/ns/mnt '/run/mntns/${NSNAME}' && :"

    # Do NOT use `ip netns exec`
    # (Re)enter the namespaces with `nsenter`, mounts are persistent
    $ nsenter --net=/run/netns/${NSNAME} --mount=/run/mntns/${NSNAME} \
          program arg1 arg2

Versão longa

É possível criar namespaces de rede e de montagem persistentes.

Isso é amplamente adaptado de exemplos na unsharepágina de manual:http://man7.org/linux/man-pages/man1/unshare.1.html

A principal implicação é que, em vez de ip netns execagora nsenter, deve ser usado para usar ambos os namespaces de rede + montagem, pelo menos onde as montagens são importantes.

Passo 1. Prepare-se. Criar diretório para namespaces de montagem persistentes

Isto precisa ser feito uma vez, por exemplo, após a inicialização, NÃO para cada namespace. O caminho aqui é arbitrário. Estou tentando usar uma nomenclatura semelhante à usada por ip netns.

    # Try to create a dir for persistent mount namespaces
    # and if it got created (e.g. first attempt since boot),
    # then bind mount it with --make-private as required by `unshare --mount=/path`
    $ mkdir /run/mntns \
        && mount --bind --make-private /run/mntns /run/mntns

Observe que eles mkdir ... && mount ...não são atômicos e onde há vários serviços iniciando simultaneamente fazendo essa sequência, uma condição de corrida pode ocorrer.

Etapa 2. Criar namespaces persistentes de rede + montagem

2a. método A, simples, sem montagens mágicas.

Isso cria namespaces de montagem + rede persistentes. É simples, mas não cria montagens mágicas para arquivos /etc/netns/${NSNAME}/*.

    $ NSNAME=red # give it a name

    # For network namespace use same paths as used by `ip netns` for interoperability
    # touch can fail to update timestamp if file is already mounted
    # Try to create files for persistent mounts.
    # and if touch is successful
    # then create persistent mount and network namespaces
    # Instead of `true` can run a command right away e.g. /bin/bash
    # As it is this is approximate equivalent of `ip netns add ${NANAME}`
    $ touch /run/netns/${NSNAME} /run/mntns/${NSNAME} \
        && unshare --net=/run/netns/${NSNAME} --mount=/run/mntns/${NSNAME} true

2b. método B, aproveite ip netns execpara fazer montagens, mas torne-as persistentes.

Para fazer essas montagens de ligação, ip netns exec ...mas torná-las persistentes, você pode fazer a configuração da seguinte maneira. Observe que isso deve ser feito SOMENTE uma vez ao criar o namespace, não sempre.

    $ ip netns add ${NSNAME} \
        && touch "/run/mntns/${NSNAME}" \
        && ip netns exec "${NSNAME}" \
            sh -x -c "nsenter -t $$ --mount mount --bind /proc/\$\$/ns/mnt '/run/mntns/${NSNAME}' && :"

Para uma tentativa de explicação, veja no final.

Etapas 3. Use-o

Agora, o nsenterdeve ser usado para iniciar comandos dentro de ambos os namespaces, conforme necessário para obter resultados de montagem persistentes. Isso ocorre independentemente de a configuração ter sido feita com unshareou ip netns exec NAME sh ... nsneter ... mount ....

    # Get a shell inside the namespaces
    $ nsenter --net=/run/netns/${NSNAME} --mount=/run/mntns/${NSNAME} \
        /bin/bash

por exemplo, com comandos do OP:

    # Do your additional mounts (can also be done above as command to `unshare`)
    # This is approximate equivalent of 
    #   `ip netns exec ${NANAME} mount --bind /etc/red/run/ /run/`
    $ nsenter --net=/run/netns/${NSNAME} --mount=/run/mntns/${NSNAME} \
          mount --bind /etc/${NSNAME}/run/ /run/

    # Re-enter the namespaces, check that the mounts are still there
    $ nsenter --net=/run/netns/${NSNAME} --mount=/run/mntns/${NSNAME} \
          sh -c 'ls /run/'

Importante

Essa abordagem funciona, mas em vez de usar ip netns execuse nsenter --net=... --mount=...! Se ip netns exec ...for usado, ele substituirá (durante sua execução) o namespace de montagem, o que pode ser aceitável para comandos que não se importam com a persistência da montagem, por exemplo

    # This does not touch any files, only operates on network device
    ip netns exec ${NSNAME} ip link set up dev lo

Etapa Z. - Limpar

Independentemente de qual dos dois métodos acima foi usado para criar os namespaces, a limpeza pode ser feita por qualquer um dos dois métodos:

    # This deletes the /mnt/netns/${NSNAME} for us
    ip netns del ${NSNAME}
    # Unmount and delete mount namespace holder
    umount "/mnt/mntns/${NSNAME}" \
        && rm "/mnt/mntns/${NSNAME}"

ou

    # Unmount and delete both namespaces' holders
    umount "/mnt/mntns/${NSNAME}" "/mnt/netns/${NSNAME}" \
        && rm "/mnt/mntns/${NSNAME}" "/mnt/netns/${NSNAME}"

Explicação

Desculpas pelo texto longo. Isto é em parte para meu próprio benefício.

Este comando da etapa2bacima merece alguma explicação.

O comando novamente.

    $ ip netns add ${NSNAME} \
        && touch "/run/mntns/${NSNAME}" \
        && ip netns exec "${NSNAME}" \
            sh -x -c "nsenter -t $$ --mount mount --bind /proc/\$\$/ns/mnt '/run/mntns/${NSNAME}' && :"

Deman unshare

Opcionalmente, os namespaces podem se tornar persistentes vinculando a montagem de arquivos /proc/pid/ns/type a um caminho do sistema de arquivos e inseridos com nsenter(1) mesmo após o término do programa (exceto namespaces PID onde o processo init em execução permanente é necessário). Quando um namespace persistente não for mais necessário, sua persistência poderá ser cancelada com umount(8).

Início da explicação

Aqui está o que ele faz, se dividido em etapas simples. Observe que dois shells são necessários para esta demonstração.

    # SHELL-1

    # Init
    $ mkdir /run/mntns \
        && mount --bind --make-private /run/mntns /run/mntns

    # Prep
    $ NSNAME=red

    # Create namespace
    $ ip netns add ${NSNAME}
    # Start a shell inside the namespace
    $ ip netns exec "${NSNAME}" /bin/bash

    # Now inside a shell inside a namespace.
    # Now need to make a bind mount it this shell's mount namespace
    $ echo $$ # Use this in SHELL2 AS SHELL1_PID
    # Go to SHELL-2 before exiting this one
    # SHELL-2

    # Set this to PID from SHELL-1
    $ SHELL1_PID=????

    $ NSNAME=red
    $ touch "/run/mntns/${NSNAME}"

    $ mount --bind /proc/${SHELL1_PID}/ns/mnt "/run/mntns/${NSNAME}"

Agora, ambos os namespaces de rede + montagem são persistentes e podem ser inseridos novamente nsentercomo mostrado anteriormente.

A parte importante é a ip netns execmontagem/proc/pid/ns/net namespace de montagem do shell externoantes ip netns exec..saídas.

Explicação cont.

Portanto, a resposta é: de dentro ip netns exec ...do do nsenter, mas não como um comando direto ip netns exec ${NSNAME} nsenter ..., porque isso exec()deixaria fork()e destruiria o namespace temporário, mas causaria um fork() executando um shell intermediário que mantém o namespace temporário ativo, e o fork nsenterfaz mount. O seguinte está mais próximo de como funciona o comando longo.

    # Init
    $ mkdir /run/mntns \
        && mount --bind --make-private /run/mntns /run/mntns

    # Prep
    $ export NSNAME=red
    $ touch "/run/mntns/${NSNAME}"

    $ export MOUNT_PARENT_PID=$$ # for simplicity

    # Create namespace
    $ ip netns add ${NSNAME}
    # Start a shell inside the namespace, the intermediate shell is important
    $ ip netns exec "${NSNAME}" /bin/bash
    # Now inside a forked namespaced shell

    # Need to mount `ip netns exec` temporary mount namespace to a file
    # within MOUNT_PARENT_PID's namespace to make it persistent
    # nsenter will reference it later

    $$ nsenter -t ${MOUNT_PARENT_PID} --mount \
           mount --bind /proc/$$/ns/mnt "/run/mntns/${NSNAME}"

    # Yes, here $MOUNT_PARENT_PID can be replaced by $PPID
    # But if any intermediate sub-shells and forks get introduced
    # then $PPID can point to a different process.

Parece que algumas versões bashinvocadas com -c commandpara um único comando otimizam o fork()e que fork() é realmente necessário aqui, então force-o a fork() com && :no final do comando (veja a versão resumida no início).

Montagem manual/etc/netns/${NSNAME}/*

A resposta original tinha esta seção, então aqui está, embora provavelmente não seja muito útil.

Recriando /mnt/netns/${NSNAME}/vincular montagens*

A desvantagem de usar unshareé que você não obtém a montagem automática de ligação /etc/netns/NETNS_NAME/*fornecida pelo ip netns exec ${NSNAME} cmd, o que não é difícil de adicionar manualmente.

    # either during unshare or later as nsenter.
    # The important difference is here it is done once and result persists
    # unlike with `ip netns exec` which does this every time
    NSNAME="${NSNAME}" \
      nsenter --net=/run/netns/${NSNAME} --mount=/run/mntns/${NSNAME} \
      /bin/sh -e -x -c \
      'cd "/etc/netns/${NSNAME}"; for f in *; do mount --bind "/etc/netns/${NSNAME}/${f}" "/etc/${f}"; done;'

informação relacionada