Olá a todos,
A situação
Atualmente, não há uma maneira fácil de substituir a URL de índice PyPI padrão para usar uma URL apontada para um espelho. Em ambientes corporativos, exigir que os desenvolvedores usem um espelho de repositório é bastante comum:
Infelizmente, isso não parece ser facilmente acomodado pelo pipenv. Embora o espelho possa ser explicitamente adicionado ao Pipfile como fonte para esses pacotes, isso quebra a portabilidade.
Deve haver uma maneira de substituir a localização do índice PyPI, especificando um espelho (verdadeiro). Isso só seria aplicável ao PyPI, e não a outros repositórios de terceiros (estes ainda seriam especificados explicitamente no Pipfile).
Proposta geral
O Docker acomoda essa situação permitindo que o usuário especifique um espelho de registro no arquivo de configuração do daemon. Da mesma forma, seria ótimo se o usuário do pipenv pudesse especificar um espelho (verdadeiro) para PyPI, por meio de uma variável de ambiente, arquivo de configuração ou parâmetro de linha de comando. Se esse valor for definido, pipenv deve usar o espelho para todos os pacotes PyPI, mesmo se uma conexão com PyPI estiver disponível. Em alguns ambientes corporativos, o PyPI permanece desbloqueado, mas a política determina que o espelho seja usado pelos outros motivos mencionados acima.
Considerações de implementação
Discussão relacionada
Isso foi discutido em #python e #pypa no Freenode. Após algumas discussões construtivas, foi decidido que seria útil abrir uma questão aqui para discussão. Agradeço o esforço de todos para resolver este problema.
/cc @uranusjr @ncoghlan @altendky @njsmith
Estou convencido de que isso é uma coisa que acontece comumente (FW corporativo / proxy de cache) - sinto que precisamos de uma configuração de substituição para especificar um espelho para usar em vez de pypi se o encontrarmos no pipfile - como PIPENV_PYPI_MIRROR
ou PIPENV_PYPI_CACHING_PROXY
ou algo assim para especificar que deve ser tentado primeiro, dividido em sources
na frente de pypi basicamente.
Parece que cumpre o objetivo? Se sim, podemos marcar no gênio da implementação para nos dizer por que isso é bom ou ruim (@ncoghlan)
Vou começar com uma nota de cautela: até que o PyPI tenha implementado um mecanismo de assinatura de pacote semelhante ao PEP 458 para fornecer uma maneira independente de TLS para pip
para garantir que os pacotes que nominalmente se originam do PyPI realmente correspondam ao que o PyPI publicou , oferecer a capacidade de redirecionar o tráfego de forma transparente para um servidor diferente é genuinamente preocupante do ponto de vista da segurança.
Infelizmente, esse vetor de ataque específico já está aberto por meio de pip.conf
, portanto, oferecer algo comparável no nível pipenv
não tornará nada pior do que já é.
Além disso, acho que um mecanismo de reescrita de URL de repositório de propósito geral poderia ser realmente mais fácil de documentar e explicar do que algo específico do PyPI, pelo menos na camada de capacidade básica. Algo como:
pipenv --override-source-url 'default=https://pypi-proxy.example.com/api' --override-source-url 'https://pypi.python.org/simple=https://pypi-proxy.example.com/api' --override-source-url 'https://pypi.org/simple=https://pypi-proxy.example.com/api' install
(O único bit específico do PyPI seria usar "default" para se referir à fonte de download padrão do pip, conforme específico em pip.conf
).
No entanto, soletrar todo o mapa de substituição de URL de origem sempre seria difícil de usar na prática, portanto, algumas opções para o açúcar da CLI podem parecer:
pipenv --override-source-urls <config file> install
pipenv --pypi-mirror https://pypi-proxy.example.com/api install
Se deve ou não expor a camada --override-source-url
imediatamente é uma questão diferente - pode fazer mais sentido implementar a opção --pypi-mirror
mais simples primeiro e apenas manter a possibilidade de --override-source-url
e --override-source-urls
como possíveis opções futuras em mente ao fazê-lo.
Um mapeamento geral de {given URL: override URL}
foi meu primeiro pensamento também, mas em consideração adicional, existem alguns argumentos para o PyPI de caixa especial:
O PyPI é bastante único por ter um URL público bem conhecido e muitos espelhos
O PyPI na verdade tem vários URLs (por exemplo, provavelmente teremos Pipfiles flutuando por um tempo com https://pypi.python.org/simple
e https://pypi.org/simple
, e talvez também https://pypi.python.org/simple/
e https://pypi.org/simple/
com a barra final?), e seria bom se pudéssemos resolver isso uma vez, em vez de forçar cada usuário a descobrir por conta própria
@njsmith Veja a sugestão de açúcar --pypi-mirror <URL>
na última parte do meu post - se a implementação inicial se concentrasse apenas nisso, a capacidade geral de reescrita de URL poderia começar como um detalhe de implementação interno (impulsionado pelo fato de que " PyPI" tem vários URLs que, em última análise, resolvem para o mesmo local) e, em seguida, são considerados para exposição como um recurso por si só mais tarde (após ter sido confirmado que está funcionando conforme desejado para o uso primário de --pypi-mirror
caso).
Ah, certo, eu perdi isso :-)
Existe uma regra geral de mapeamento de argumentos de linha de comando para algum tipo de configuração mais persistente? Imagino que a maioria dos usuários gostaria de configurá-lo uma vez e depois esquecê-lo.
@coghlan escreveu:
Vou começar com uma nota de cautela: até que o PyPI tenha implementado um mecanismo de assinatura de pacote semelhante ao PEP 458 para fornecer uma maneira independente de TLS para o pip garantir que os pacotes que se originam nominalmente do PyPI realmente correspondam ao que o PyPI publicou, oferecendo a capacidade redirecionar o tráfego de forma transparente para um servidor diferente é genuinamente preocupante do ponto de vista da segurança.
Se estou lendo meu Pipfile.lock
corretamente, não há relação armazenada entre um pacote e de qual fonte ele foi instalado. Dado que o conjunto de recursos existente permite que várias fontes sejam especificadas, isso não cria um problema semelhante? Um sync
pode acabar recebendo um pacote de uma fonte diferente daquela que estava acostumada ao criar o arquivo de bloqueio.
Pipfile.lock
armazena uma lista de hashes de artefatos aceitáveis para cada dependência fixada; No momento da geração de bloqueio, optar explicitamente por uma fonte em Pipfile
está dizendo "Confio que esta fonte não mexe comigo e usarei o TLS para verificar se estou realmente falando com este ponto de origem". (Acho que há um problema em algum lugar discutindo a perspectiva de vincular pacotes específicos a repositórios de origem específicos, embora possa estar em pip
ou em um dos outros repositórios PyPA, em vez de aqui)
Alterar a URL de índice padrão (ou adicionar uma URL de índice extra) em pip.conf
, ou usar o recurso de substituição proposto aqui por meio de um arquivo de configuração ou mecanismo baseado em perfil de shell é diferente: isso está dizendo "I, ou algum processo arbitrário I executado em algum momento com acesso de gravação ao meu diretório pessoal (como o arquivo setup.py de um sdist), decidi definir minhas configurações para confiar nessa fonte de pacotes". E mesmo um esquema de assinatura como o PEP 458 não é uma defesa completa contra esses tipos de travessuras se as próprias chaves públicas usadas para verificação estiverem armazenadas em algum lugar dentro de seu diretório pessoal, em vez de em um diretório que exija privilégios elevados para modificação.
Existem boas razões pelas quais as organizações com requisitos de segurança rigorosos executam compilações em servidores bloqueados com acesso limitado à Internet em geral ou monitoram esses tipos de problemas no nível da rede :)
Observe também que se você usar vários índices e um pacote vier do índice não primário, ele será indicado no arquivo de bloqueio.
As preocupações do pep 458 eram essencialmente o que eu tinha em mente, já que as coisas que são urls diferentes, mas na realidade apontam para o pypi, são diferentes do que se você apenas copiasse localmente o pypi e afirmasse que era o mesmo.
Eu, ou algum processo arbitrário que executei em algum momento com acesso de gravação ao meu diretório pessoal (como o arquivo setup.py de um sdist), decidi definir minhas configurações para confiar nessa fonte de pacotes
Se este é o seu modelo de ameaça, não vejo como algo que o pipenv possa fazer afetará muito isso. Alguém que pode modificar a configuração do seu diretório pessoal também pode fazer coisas como inserir um novo diretório em $PATH
e inserir um pipenv falso lá que faça o que quiser.
@njsmith este também é o modelo de ameaça do pip, porque a instalação do pacote requer que a execução de código arbitrário dos arquivos sdist setup.py
seja permitida. Esse código de fato pode substituir coisas em seu diretório pessoal, como suas configurações, ou adicionar coisas ao seu caminho, ou qualquer número de coisas. É por isso que privilegiar explicitamente o pypi (um índice conhecido e confiável) e exigir a verificação de hash é um bom passo em direção à segurança. Ele permite o controle centralizado e a eliminação de ameaças de segurança conhecidas e identifica a verificação dos pacotes que você está baixando de forma distribuída. O que o arquivo de bloqueio que você baixou disse sobre o hash que você deveria estar recebendo? Não corresponde ao que você está obtendo do índice? Para que este modo de operação falhe, você precisa ter falhas em mais de uma máquina local, índice e camada de rede, porque você está falando sobre ter vários pacotes corrompidos em sua pilha de aplicativos trabalhando em conjunto verificando hashes em um índice confiável , e em muitos casos os próprios hashes vieram de outra fonte não envolvida. Então agora você precisa ter, no mínimo, toda a verificação de hash em pip e pipenv de alguma forma adulterada de forma que gere hashes idênticos aos que você espera, mas instala outras coisas maliciosas?
Acho que o que estou dizendo é que, se sua máquina local estiver comprometida, não há nada que pip ou pipenv faça para salvá-lo. Mas podemos garantir que o pacote que você está baixando é o que você estava procurando, do local onde deveria procurá-lo, o que pode fornecer um elemento na cadeia de segurança.
@ncoghlan @njsmith como tudo isso contribui para o movimento de recuar contra sudo pip install...
e o senso geral, acho que todos nós temos que, se você for usar pip, provavelmente também não deve usar seu gerenciador de pacotes do sistema para instalar coisas python em geral. Talvez essa não seja realmente uma pergunta do pipenv, mas é onde está a discussão agora e isso pode orientar os próximos passos ...
@techalchemy Não vejo nenhuma conexão com este tópico? Acho que a conclusão de tudo o que foi dito acima é que permitir que os usuários substituam qual espelho pipenv usa para PyPI não introduz nenhuma ameaça adicional, e fazer sudo pipenv
nem faz sentido em primeiro lugar, certo?
@njsmith não, eu não acho que alguém deveria usar sudo pipenv
, como eu mencionei, não é realmente sobre o assunto, mas já que seguimos um pouco o caminho do modelo de ameaça, achei que valeria a pena explorar. Especificamente:
E mesmo um esquema de assinatura como o PEP 458 não é uma defesa completa contra esses tipos de travessuras se as próprias chaves públicas usadas para verificação estiverem armazenadas em algum lugar dentro de seu diretório pessoal, em vez de em um diretório que exija privilégios elevados para modificação.
Existem boas razões pelas quais as organizações com requisitos de segurança rigorosos executam compilações em servidores bloqueados com acesso limitado à Internet em geral ou monitoram esses tipos de problemas no nível da rede :)
Se uma defesa, pelo menos em alguma capacidade, depende do armazenamento de chaves em um local privilegiado, mas estamos desaconselhando o uso de instalações python privilegiadas, acho que vale a pena discutir. Talvez eu esteja errado. Mas definitivamente parece relacionado ao comentário de @ncoghlan (mas não sudo pipenv
, isso nunca deveria ser uma coisa)
Sim, provavelmente parecia que veio do nada, apenas um pensamento aleatório. Espero que o contexto adicional esclareça um pouco
Eu voto para mantermos esta questão no tópico de ajudar as pessoas que precisam usar espelhos PyPI, em vez de entrar em uma discussão especulativa de como podemos implementar o TUF. (De qualquer forma, não acho que haja muito que possamos ou devamos fazer para tentar nos defender contra um invasor que tenha acesso arbitrário de gravação ao diretório inicial do usuário.)
Ok, então vamos definir o comportamento que esperamos ou preferimos. Meu entendimento de trabalho atual é que:
--pypi-mirror
for passado ou PIPENV_PYPI_MIRROR
for definido, devemos preferir queEle deve substituir apenas o PyPI, não outros URLs. Acho que provavelmente há apenas alguns URLs PyPI diferentes em uso, para que possam ser listados, e se perdermos um, alguém registrará um bug, ele será adicionado e, em breve, teremos todos eles.
Parece-me a abordagem certa.
O que @njsmith disse combina com minha perspectiva também. Os 3 URLs de repositório que eu sugiro substituir em um PR inicial seriam:
pip
)A barra ou não à direita provavelmente é melhor tratada como uma etapa de normalização de URL, em vez de listar as URLs separadamente.
Observe que as solicitações Pipfile têm uma barra final (no momento da redação) , portanto, provavelmente precisamos lidar com isso de uma maneira ou de outra.
Certo, meu pensamento foi:
str.rstrip
provavelmente seria bom o suficiente para a tarefa, mesmo que removesse um número arbitrário de barras finais, ou então poderíamos ser mais rigorosos sobre isso, e remova no máximo uma barra final)Impressionante. Eu acho que isso é o suficiente para trabalhar e simples o suficiente para construir. Obrigado a todos!
Espero que o recurso de espelho possa ser adicionado em breve ~
Estou enfrentando esse problema também. A situação é:
Minha estratégia de implantação já configura um pip.conf em todo o sistema que se refere ao servidor PyPI interno. Surpreendentemente, descobri que essa configuração é ignorada pelo Pipenv.
Estou percebendo que se eu fosse mover/renomear o PyPI interno, vários aplicativos com Pipfiles teriam que ser atualizados e seus arquivos Pipfile.lock regenerados. Uma opção de espelho forneceria a funcionalidade desejada. Também funcionaria e seria menos redundante se o Pipenv pudesse apenas ler a configuração do sistema para o Pip.
PRs bem-vindos neste aqui btw
Oi. Tenho a mesma necessidade, mas dividiria esse recurso de substituição em outro ticket.
Aqui está minha proposta de comportamento esperado:
E em um segundo ticket, as opções —override podem ser implementadas. Faz sentido, por exemplo, dentro de um CI ou algo assim.
Como uma nota lateral: nós usamos muito pipenv em produção agora, mas eu preciso lembrar a todos com muita frequência que eles precisam alterar seu Pipfile manualmente quando iniciam um novo projeto para acessar nosso repositório Arrifactory Pypi (para informações, o Nexus também faz um Pypi cache de graça e funciona muito bem!). Temos um firewall muito limitado e é uma prática muito boa dentro de uma empresa armazenar em cache as dependências externas, para que possam fazer backup e verificar vulnerabilidades, por exemplo.
Se um recurso simples semelhante ao arquivo de configuração geral ou do usuário (como já fazemos para pip ou npm), para que possamos implantá-lo em todas as nossas estações de trabalho para que nossos desenvolvedores cometam menos erros, isso seria perfeito para mim)
Talvez eu tenha perdido alguma coisa, mas isso parece uma regressão. Estamos no 11.6.0 há um tempo, e pipenv alegremente delegou às configurações em nosso pip.conf, que apontam para um espelho pypi interno.
Alguma ideia de quando isso quebrou? Isso torna o pipenv completamente inutilizável em nosso contexto. Estou tendo problemas para ver isso como um "recurso ausente" quando aparentemente estava funcionando bem por um longo tempo.
Para ser claro: depois de atualizar para 2018.05.18, mesmo com o espelho especificado em nosso Pipfile[.lock], o pipenv tenta instalar novos pacotes do pypi.org.
Talvez o que estou vendo seja um problema separado deste...
@brettdh É difícil dizer sem ver seu ambiente, mas acho que não é o mesmo problema. Eu sugiro que você faça uma divisão entre os lançamentos para ver exatamente onde isso mudou e abra um novo problema para ele.
Estou trabalhando no PR para isso.
Eu acho que isso foi regredido em relação à configuração padrão. Pode ter sido pego em uma onda de atualizações para o pip 10 que ainda não foram lançadas, mas acredito que podemos pegar isso sem muita dificuldade se @JacobHenner já não estiver adicionando
Presumo que você esteja falando sobre usar o devpi como proxy de cache para o PyPi oficial. Para o próprio pip, você precisaria modificar /etc/pip.conf
e /usr/lib64/python3.6/disutils/distutils.cfg
para pip usar seu servidor devpi local para todas as solicitações.
No entanto, parece que o pipenv ignora essas configurações de todo o sistema, então você é forçado a modificar a configuração [[source]]
no Pipfile para fazer referência ao seu servidor devpi. Mas se você publicar seu Pipfile externamente, os contribuidores externos terão que remover suas configurações de [[source]]
para realmente construir seu próprio ambiente.
Eu acho que o pipenv deve apenas respeitar as configurações globais de /etc/pip.conf
e /usr/lib.../distutils.cfg
@polski-g
Presumo que você esteja falando sobre usar o devpi como proxy de cache para o PyPi oficial
Nexus Repository, mas sim, a mesma ideia.
No entanto, parece que o pipenv ignora essas configurações de todo o sistema
Como @techalchemy mencionou, acredito que pipenv (11.6.0) costumava respeitar pip.conf
(homedir também), mas a versão mais recente não - especificamente, há um URL pypi.org codificado em algum lugar (dependência resolução, IIRC) que não pode ser substituído.
Eu acho que o pipenv deve respeitar as configurações globais de /etc/pip.conf e /usr/lib.../distutils.cfg
Concordo - embora pessoalmente eu não precise modificar distutils.cfg
no meu caso de uso.
IIRC houve uma resolução para não respeitar o pip.conf, mas você precisará cavar fundo no rastreador de problemas para encontrá-lo. De qualquer forma, o navio partiu e, com o espelhamento do PyPI quase pronto, é improvável que isso mude no futuro próximo.
Estou bastante confiante de que esse recurso será lançado na próxima versão (que será lançada no próximo dia ou dois com sorte)
Também não tenho certeza sobre isso, mas é possível que precisemos chamar .load()
depois de criarmos o analisador de configuração aqui para obter os padrões de configuração
https://github.com/pypa/pipenv/blob/master/pipenv/project.py#L573 -#L577
@uranusjr desde que a configuração de espelhamento funcione (ou seja, não use a URL pypi.org codificada que mencionei), não vejo nenhum problema com o pipenv ter sua própria configuração para isso e ignorar os pip.
@brettdh Você poderia fazer o check-out da minha filial e confirmar se atende ao seu
caso de uso em seu ambiente?
>
@JacobHenner sim, obrigado. Meu teste inicial com a opção --pypi-mirror
( pipenv install
, pipenv lock
) parece funcionar bem. Deixei uma pequena sugestão no PR.
Estou um pouco preocupado, porém, que os URLs codificados para pypi.org ainda apareçam espalhados pelas fontes pipenv. Não posso ter certeza de quais são substituídos corretamente das entradas [[source]]
e não consigo lembrar exatamente qual fluxo de trabalho causou meu problema acima. Portanto, é difícil dizer se foi corrigido. 😬
Sim, após este lançamento, estou planejando uma grande limpeza de código. Coisas do cli movendo-se para o cli, borbulhando exceções lá e manipulando todas as saídas lá, desduplicando código duplicado, etc. Vai ser muito trabalho e ajuda será apreciada se alguém quiser se voluntariar :p
Acabei de puxar a versão recente e ainda está codificando o pypi.org nas fontes. O objetivo é pegar a variável ambiental ou o pypi-mirror e colocar isso como padrão para [[source]]?
editar:
Apenas vasculhou o código .. Parece que você tem
if PIPENV_TEST_INDEX:
DEFAULT_SOURCE = {
u"url": PIPENV_TEST_INDEX,
u"verify_ssl": True,
u"name": u"custom",
}
else:
DEFAULT_SOURCE = {
u"url": u"https://pypi.org/simple",
u"verify_ssl": True,
u"name": u"pypi",
}
Eu acho que se você mudasse isso If PIPENV_TEST_INDEX para a variável de ambiente PIPENV_PYPI_MIRROR seria um bom começo
A solução discutida aqui foi implementada há muito tempo. O trecho que você citou é um padrão , ou seja, usado se você não fornecer uma fonte ao criar o Pipfile.
Não, a fonte não deve ser alterada no Pipfile. O objetivo desta mudança
era permitir que os usuários substituíssem os URLs PyPI com um espelho, _sem_ alterar
o Pipfile.
@JacobHenner O código de manipulação do espelho pós-processa a lista de fontes e substitui pypi.org
URLs por referências ao espelho especificado.
É isso que permite que a substituição do espelho funcione mesmo se houver uma entrada pypi.org
explícita no Pipfile
. pipenv
então depende dessa mesma lógica para substituir sua própria fonte padrão também.
Se houver atualmente casos em que esse pós-processamento não está sendo aplicado corretamente, isso é um novo relatório de bug em relação ao recurso já implementado, em vez de uma solicitação de recurso.
Acho que esse último comentário foi feito para @kylecribbs?
@JacobHenner Ah, desculpe - interpretei mal seu comentário dizendo que essa mudança não atingiu seu objetivo original, e não como uma resposta a Kyle que visava esclarecer qual era realmente esse resultado.
Comentários muito úteis
Ele deve substituir apenas o PyPI, não outros URLs. Acho que provavelmente há apenas alguns URLs PyPI diferentes em uso, para que possam ser listados, e se perdermos um, alguém registrará um bug, ele será adicionado e, em breve, teremos todos eles.