Mirror Pypi in un ambiente AWS privato Terraform

 | Intelligenza-Artificiale

Come installi un pacchetto Python nel tuo ambiente se non hai accesso a Internet? Recentemente ho riscontrato questo problema durante la creazione di un ambiente AWS Sagemaker Studio per il mio team su AWS.

Creazione di un ambiente privato AWS per Sagemaker

Per questo particolare progetto, ho configurato Sagemaker in modalità VPC Only con il vincolo di mantenere l'architettura privata, il che significa creare un VPC e sottoreti private, ma senza accesso a Internet.

Pertanto tutte le comunicazioni di rete, inclusa la comunicazione delle applicazioni con le API AWS, devono passare attraverso le interfacce endpoint VPC. Ciò consente di mantenere la connessione protetta poiché i dati inviati e ricevuti non passeranno mai attraverso Internet utilizzando invece il backbone di rete AWS.

È particolarmente adatto per limitare l'esposizione ai rischi per la sicurezza, in particolare quando si elaborano informazioni personali o si devono rispettare alcuni standard di sicurezza.

fotografato da Nadir sYzYgY SU Unsplash

Accesso al repository di pacchetti Pypi da AWS Sagemaker

Nel mio team, i data scientist utilizzano Python come linguaggio principale e talvolta necessitano di pacchetti Python che non sono forniti Immagini Python predefinite di Sagemakerquindi mi concentrerò su questo caso d'uso. Fortunatamente, la soluzione funziona anche per altri linguaggi e repository come npm.

I tuoi utenti in genere proveranno a installare qualsiasi pacchetto di cui abbiano bisogno tramite il comando pip. Tuttavia, poiché non è consentito l'accesso a Internet, questo comando fallirà perché pip non sarà in grado di contattare i server Pypi.org.

Apertura di Internet

Un'opzione è aprire l'accesso a Internet e consentire connessioni HTTP in uscita agli IP Fastly CDN utilizzati dai server Pypi.org. Ma questo non è fattibile nel nostro caso poiché non vogliamo alcuna connessione Internet nell'architettura.

Utilizzando un server Pypi dedicato

Il blog AWS fornisce anche un esempio di utilizzo di un pacchetto Python denominato Bandersnatch. Questo articolo descrive come configurare un server, che agisca come un bastion host, che rispecchierà Pypi e sarà accessibile solo alle tue sottoreti private.

Questa non è un'opzione praticabile poiché devi sapere in anticipo quali pacchetti Python devi fornire e dovrai in qualche modo creare sottoreti pubbliche e fornire al server Pypi l'accesso mirror a Internet.

Utilizzo di AWS Cordeartifact

Questa è in definitiva la soluzione che ho trovato e che funziona nel mio caso.

Artefatto del codice AWS è la soluzione di gestione degli artefatti fornita da AWS. È compatibile con altri servizi AWS come AWS Service Catalog per controllare l'accesso alle risorse all'interno di un'organizzazione.

Per utilizzarlo, dovrai creare un “dominio” che funge da ombrello per gestire l'accesso e applicare policy all'interno della tua organizzazione. Quindi, dovrai creare un repository che servirà i tuoi artefatti alle tue diverse applicazioni.

Inoltre, un repository può avere repository upstream. Pertanto, se un pacchetto Python non è disponibile nel repository di destinazione, la richiesta verrà trasmessa al repository upstream per essere soddisfatta.

Più precisamente, questo flusso di lavoro tiene conto delle versioni del pacchetto. La documentazione ufficiale fornisce un flusso di lavoro dettagliato:

Se my_repo contiene la versione del pacchetto richiesta, viene restituita al client.

Se my_repo non contiene la versione del pacchetto richiesta, CodeArtifact la cerca my_repoi repository upstream. Se viene trovata la versione del pacchetto, viene copiato un riferimento ad essa my_repoe la versione del pacchetto viene restituita al client.

Se nessuno dei due my_repo né i suoi repository upstream contengono la versione del pacchetto, un HTTP 404 Not Found la risposta viene restituita al client.

Fantastico, vero? Memorizzerà anche nella cache la versione del pacchetto per richieste future.

Questa è esattamente la strategia che utilizzeremo, poiché AWS Codeartifact ci consente di definire un repository che ha una connessione esterna come Pypi come repository upstream.

Creazione di risorse AWS Codeartifact con Terraform

Poiché AWS Codeartifact è un servizio AWS, puoi creare facilmente un endpoint VPC nel tuo ambiente VPC per connetterti ad esso.

Nota: sto utilizzando Terraform v1.6.4 e il provider aws v5.38.0

locals {
region = "us-east-1"
}

resource "aws_security_group" "vpce_sg" {
name = "AllowTLS"
description = "Allow TLS inbound traffic and all outbound traffic"
vpc_id = aws_vpc.your_vpc.id

tags = {
Name = "allow_tls_for_vpce"
}
}

resource "aws_vpc_security_group_ingress_rule" "allow_tls_ipv4" {
security_group_id = aws_security_group.allow_tls.id
cidr_ipv4 = aws_vpc.your_vpc.cidr_block
from_port = 443
ip_protocol = "tcp"
to_port = 443
}

data "aws_iam_policy_document" "codeartifact_vpce_base_policy" {
statement {
sid = "EnableRoles"
effect = "Allow"
actions = (
"codeartifact:GetAuthorizationToken",
"codeartifact:GetRepositoryEndpoint",
"codeartifact:ReadFromRepository",
"sts:GetServiceBearerToken"
)
resources = (
"*",
)
principals {
type = "AWS"
identifiers = (
aws_iam_role.your_sagemaker_execution_role.arn
)
}
}
}

resource "aws_vpc_endpoint" "codeartifact_api_vpce" {
vpc_id = aws_vpc.your_vpc.id
service_name = "com.amazonaws.${local.region}.codeartifact.api"
vpc_endpoint_type = "Interface"
subnet_ids = aws_subnets.your_private_subnets.ids

security_group_ids = (
aws_security_group.vpce_sg.id,
)

private_dns_enabled = true
policy = data.aws_iam_policy_document.codeartifact_vpce_base_policy.json
tags = { Name = "codeartifact-api-vpc-endpoint" }
}

Quindi, dovrai creare le diverse risorse necessarie affinché Codeartifact gestisca le tue richieste di nuovi pacchetti Python eseguendo il mirroring di Pypi: un dominio, un repository Pypi con una connessione esterna e un repository che definisce Pypi come repository upstream.

resource "aws_codeartifact_domain" "my_domain" {
domain = "my-domain"

encryption_key = ""

tags = { Name = "my-codeartifact-domain" }
}

resource "aws_codeartifact_repository" "public_pypi" {
repository = "pypi-store"
domain = aws_codeartifact_domain.my_domain.domain

external_connections {
external_connection_name = "public:pypi"
}

tags = { Name = "pypi-store-repository" }
}

resource "aws_codeartifact_repository" "my_repository" {
repository = "my_repository"
domain = aws_codeartifact_domain.my_domain.domain

upstream {
repository_name = aws_codeartifact_repository.public_pypi.repository
}

tags = { Name = "my-codeartifact-repository" }
}

data "aws_iam_policy_document" "my_repository_policy_document" {
statement {
effect = "Allow"

principals {
type = "AWS"
identifiers = (aws_iam_role.your_sagemaker_execution_role.arn)
}

actions = ("codeartifact:ReadFromRepository")
resources = (aws_codeartifact_repository.my_repository.arn)
}
}

resource "aws_codeartifact_repository_permissions_policy" "my_repository_policy" {
repository = aws_codeartifact_repository.my_repository.repository
domain = aws_codeartifact_domain.my_domain.domain
policy_document = data.aws_iam_policy_document.my_repository_policy_document.json
}

Ecco qui! Ora puoi configurare facilmente un mirror Pypi per il tuo ambiente privato.

Per rendere le cose utilizzabili, dovrai anche dire ai comandi pip di indirizzare le richieste a un indice specifico. Fortunatamente, AWS ha creato un'API per svolgere il lavoro pesante per te. Basta aggiungere questo al tuo codice per farlo funzionare:

aws codeartifact login --tool pip --repository $CODE_ARTIFACT_REPOSITOR_ARN --domain $CODE_ARTIFACT_DOMAIN_ID --domain-owner $ACCOUNT_ID --region $REGION

Ultimo ma non meno importante, aggiungi un endpoint VPC per AWS Codeartifact nel tuo VPC.

data "aws_iam_policy_document" "codeartifact_vpce_base_policy" {
statement {
sid = "EnableRoles"
effect = "Allow"
actions = (
"codeartifact:GetAuthorizationToken",
"codeartifact:GetRepositoryEndpoint",
"codeartifact:ReadFromRepository",
"sts:GetServiceBearerToken"
)
resources = (
"*",
)
principals {
type = "AWS"
identifiers = (
aws_iam_role.your_sagemaker_execution_role.arn
)
}
}
}

resource "aws_vpc_endpoint" "codeartifact_api_vpce" {
vpc_id = aws_vpc.your_vpc.id
service_name = "com.amazonaws.${local.region}.codeartifact.api"
vpc_endpoint_type = "Interface"
subnet_ids = aws_subnets.your_private_subnets.ids

security_group_ids = (
aws_security_group.vpce_sg.id,
)

private_dns_enabled = true
policy = data.aws_iam_policy_document.codeartifact_vpce_base_policy.json
tags = { Name = "codeartifact-api-vpc-endpoint" }
}

Se desideri ricevere notifiche per i miei prossimi post riguardanti AWS e altro ancora, per favore iscriviti qui.

Sapevi che puoi battere le mani più volte?

Fonte: towardsdatascience.com

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *