Cilium e a Defesa do CI/CD: Como um Projeto Open Source de Nível Kernel Kubernetes Protege sua Cadeia de Suprimentos
Cilium opera no caminho de rede de nível kernel em milhões de pods Kubernetes: de provedores de nuvem a clusters próprios de bancos e empresas de telecomunicações. Se alguém comprometesse o pipeline de construção do Cilium, a área de impacto seria comparável a um incidente SolarWinds, mas no ecossistema nativo da nuvem. Portanto, a abordagem do projeto para a segurança do CI/CD é interessante não apenas para os mantenedores de outros projetos open source: os mesmos padrões são úteis para qualquer equipe que constrói artefatos de produção no GitHub Actions. A equipe VK Cloud traduziu o artigo com configurações YAML específicas, decisões de design e uma lista honesta do que o Cilium ainda não fez.
Introdução
Os últimos doze meses foram difíceis para a cadeia de suprimentos open source. A Axios foi comprometida no npm - um trojan de acesso remoto (RAT) foi fornecido dentro de lançamentos de aparência normal. O pacote PyPI LiteLLM foi sequestrado para roubar variáveis de ambiente. Foram publicados forks de typosquatting do Trivy - um cálculo para aqueles que cometem erros de digitação ao digitar go install. E o exemplo canônico - a invasão SolarWinds de 2020 - ainda é uma história instrutiva à qual todos retornam: os invasores se infiltraram no sistema de construção e distribuíram malware por meio de atualizações Orion regulares para cerca de 18.000 organizações, incluindo agências federais dos EUA, OTAN e Microsoft. O malware ficou inativo por meses. A invasão não foi notada na maior parte do ano.
Cilium opera no caminho de rede de nível kernel de milhões de pods Kubernetes. Se nossa cadeia de suprimentos fosse comprometida, a área de impacto seria grande. Fortalecer a proteção contra tal cenário é um trabalho constante, e queremos registrar em detalhes o que estamos fazendo. A maior parte do que foi descrito não está vinculada ao Cilium: qualquer projeto open source que execute CI/CD no GitHub Actions pode aplicar esses padrões. Também observamos áreas onde ainda não estamos à altura - de repente, algo servirá como um ponto de partida útil para outros.
Em resumo: níveis de proteção do pipeline
Se não tiver tempo para ler tudo, aqui está o que o Cilium está fazendo para fortalecer a cadeia de suprimentos hoje, por níveis de pipeline:
| Nível | Controle | O que faz | Quem executa as construções |
|---|---|---|---|
| Gatilhos | Controle de gatilhos via Ariane | Somente membros verificados da organização podem executar o fluxo de trabalho CI a partir de comentários em PRs, de uma lista explícita de fluxos de trabalho permitidos. | |
| Código que o CI executa | Checkouts de duas fases para pull_request_target | Código confiável (composite actions, scripts, lógica de assinatura) é carregado do branch base. O head PR é usado apenas como contexto de construção do Docker e nunca é executado como um script. | |
| Quem revisa as alterações do CI | Gates CODEOWNERS | Tudo em .github/ requer revisão da equipe CI responsável pela segurança, e auto-approve.yaml requer um mantenedor. | |
| Quais dependências o CI puxa | Ações e imagens fixadas por SHA | Cada link uses: aponta para um SHA de commit de 40 caracteres. As imagens de contêiner são fixadas por @sha256: digest. Renovate mantém os pins atualizados e espera alguns dias antes de pegar novos lançamentos. | |
| Quais módulos Go entram no binário | Dependências Go vendidas | Tudo é commitado em vendor/ e revisado pela equipe @cilium/vendor, para que um módulo de typosquatting ou sequestrado apareça como uma diff durante a revisão. | |
| Como os fluxos de trabalho podem parecer | Análise estática do fluxo de trabalho | CodeQL requer a especificação obrigatória de permissions: em cada fluxo de trabalho, actionlint captura padrões inseguros, e ambos marcam a injeção de expressões GitHub Actions em blocos run:. | |
| Quais credenciais estão disponíveis | Isolação de credenciais CI e produção | As credenciais CI só podem ser enviadas para as tags de desenvolvimento *-ci. As credenciais do registro de produção estão atrás do ambiente protegido release, que requer a aprovação do mantenedor. | |
| O que os consumidores podem verificar | Lançamentos assinados | Cada imagem de lançamento e gráfico Helm é assinado pelo Sigstore Cosign via keyless OIDC, com atestações SBOM (Software Bill of Materials) anexadas. |
Onde ainda não estamos à altura
Lacunas que estamos fechando
Não há proveniência SLSA, não há revisão de dependências durante o PR, não há govulncheck no CI, e vários links internos @main que precisam ser transferidos para um repositório separado de composite-actions.
Em seguida, cada linha é analisada em mais detalhes - com decisões de design e o que decidimos intencionalmente não fazer (por exemplo, bifurcar cada ação de terceiros em sua própria organização).
Quem e o que pode ser executado no CI
A primeira pergunta em qualquer história sobre a cadeia de suprimentos CI é: quem pode executar a construção e que código ela executa? Muitas compromissos de CI começam aqui - enganando o sistema para que ele execute código controlado pelo invasor com privilégios elevados.
Limitação de gatilhos de fluxo de trabalho via Ariane
Ariane é um bot do GitHub escrito por nós internamente para executar (dispatch) fluxos de trabalho CI a partir de comentários em PRs. Quando um mantenedor escreve /test ou /ci-eks em uma solicitação de pull, Ariane verifica se o comentarista é membro da equipe organization-members, determina quais fluxos de trabalho executar (incluindo dependências - por exemplo, testes que primeiro precisam de uma nova construção de imagem) e os executa via workflow_dispatch.
A parte interessante é a allow-list (lista de permissões). Somente membros verificados da organização podem executar o fluxo de trabalho, e o conjunto de fluxos de trabalho disponíveis é listado na configuração manualmente:
yaml# .github/ariane-config.yaml allowed-teams: - organization-members triggers: /test\s*: workflows: - conformance-aws-cni.yaml - conformance-clustermesh.yaml - conformance-eks.yaml # ...and so on depends-on: - /build-images-dependency /ci-aks: workflows: - conformance-aks.yaml depends-on: - /build-images-dependency
Um comentarista externo aleatório com /test no PR será ignorado. Ele não executará os caros conjuntos de conformidade dos provedores de nuvem e não queimará nossos minutos CI.
Separação de código confiável e não confiável no CI
Quando alguém abre um PR, precisamos construir seu código, mas não podemos confiar nele. Este é o problema clássico pull_request_target. Evitamos pull_request_target sempre que possível, mas ainda é necessário para vários fluxos de trabalho - e os envolvemos com medidas de proteção.
O fluxo de trabalho de construção de imagem é um exemplo canônico. Ele divide o checkout em dois:
yaml# .github/workflows/build-images-ci.yaml - name: Checkout base or default branch (trusted) uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ github.base_ref || github.event.repository.default_branch }} persist-credentials: false # ...trusted setup steps run here, including loading composite actions... # Warning: since this is a privileged workflow, subsequent workflow job # steps must take care not to execute untrusted code. - name: Checkout pull request branch (NOT TRUSTED) uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false ref: ${{ steps.tag.outputs.sha }}
O primeiro checkout pega o branch base - o código que já foi revisado e mesclado - para que você possa carregar composite actions, scripts e lógica de assinatura Cosign de uma fonte confiável. E somente depois disso, o fluxo de trabalho executa o checkout do head PR - e este checkout é usado puramente como contexto de construção para docker build. Nada do branch PR é executado como um script.
Relatórios de segurança sobre este padrão vêm regularmente. Scanners automatizados e pesquisadores bem-intencionados veem "pull_request_target mais um segundo checkout" e o marcam como uma vulnerabilidade. Na maioria dos casos, eles estão certos. No nosso caso, o fluxo de trabalho é intencionalmente projetado para que o padrão permaneça seguro:
- Nenhuma etapa
run:executa scripts do checkout não confiável. Cada bloco shell após o segundo checkout é escrito inline no fluxo de trabalho YAML (verificações de uso de disco, cópia de arquivos, saída de digest). Nada é conectado do branch PR. - Nenhuma
composite actionstambém é carregada do checkout não confiável. Todas ascomposite actions(set-runtime-image, cosign, set-env-variables) vêm do checkout confiável do branch base ou do diretório salvo../cilium-base-branch/. Também estamos trabalhando para transferir essascomposite actionspara um repositório separado para que você não precise fazer o checkout do código-fonte para executá-las. - Docker BuildKit realmente executa um Dockerfile não confiável - este é o ponto principal da construção da imagem CI a partir do PR. BuildKit funciona em isolamento: nem variáveis de ambiente do GitHub Actions, nem segredos do repositório, nem acesso ao armazenamento de credenciais Docker do runner. Não há segredos nos argumentos de construção que passamos - apenas uma referência à imagem de tempo de execução e o nome da variante do operador.
- Dados não confiáveis entram em exatamente uma ação confiável. O arquivo
runtime-image*.txtdo PR é fornecido para a ação confiável set-runtime-image, que verifica se a referência da imagem começa comquay.io/cilium/e remove as quebras de linha - para que o invasor não possa arrastar uma injeçãoGITHUB_ENV. É impossível redirecionar a construção para qualquer coisa fora do namespace Cilium. - No escopo - apenas as credenciais CI. Docker login usa
QUAY_USERNAME_CI / QUAY_PASSWORD_CI, que só podem ser enviados para o registro de desenvolvimento-ci. Não há credenciais de produção no runner.
O pior resultado possível de uma construção de PR comprometida é uma imagem CI maliciosa no registro de desenvolvimento. Esta é a mesma área de impacto que qualquer sistema CI que constrói código de contribuidores carrega. Agradecemos cada relatório e lemos todos eles com atenção, mas este padrão é intencional.
CODEOWNERS como um gate de revisão
Confiamos bastante em CODEOWNERS para garantir que as alterações sempre cheguem àqueles que entendem melhor a área. Para a configuração do CI, isso significa que tudo em .github/ pertence a @cilium/github-sec (nossa equipe CI responsável pela segurança) mais @cilium/ci-structure, e workflow auto-approve.yaml pertence a @cilium/cilium-maintainers:
# CODEOWNERS
/.github/ @cilium/github-sec @cilium/ci-structure
/.github/ariane-config.yaml @cilium/github-sec @cilium/ci-structure
/.github/renovate.json5 @cilium/github-sec @cilium/ci-structure
/.github/workflows/ @cilium/github-sec @cilium/ci-structure
/.github/workflows/auto-approve.yaml @cilium/cilium-maintainers
Ninguém pode alterar o pipeline CI sem uma revisão explícita da equipe responsável por sua segurança.
Bloqueio de dependências
Assim que você controla quem executa as construções, a próxima pergunta é: que código essas construções puxam? Um fluxo de trabalho fixado que puxa uma dependência comprometida permanece um fluxo de trabalho comprometido.
Fixando GitHub Actions por SHA digest
A coisa mais valiosa que qualquer projeto pode fazer aqui é parar de confiar em tags mutáveis.
Cada diretiva uses: em nossos arquivos de fluxo de trabalho se refere a ações por um SHA de commit completo de 40 caracteres, com uma versão legível por humanos em um comentário no final (fixando a ação por um SHA de commit completo, SHA pinning):
yaml- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
Se alguém comprometer a tag v6 em actions/checkout e forçar o envio de código malicioso, nossos fluxos de trabalho não o puxarão. Eles são fixados em um commit específico. A mesma história para cada ação de terceiros que usamos: docker/build-push-action, sigstore/cosign-installer, golangci/golangci-lint-action, e dezenas mais. As imagens de contêiner que são usadas diretamente nas etapas do fluxo de trabalho, também fixamos - por @sha256: digest, para que mesmo as ferramentas executadas dentro do CI sejam endereçadas por conteúdo.
O pinning tem um ponto cego irritante - dependências transitivas. Quando fixamos actions/checkout@de0fac2e..., sabemos exatamente qual código é executado para esta ação. Mas se actions/checkout se referir a outra ação por tag (uses: some-org/some-helper@v1), a resolução ocorre em tempo de execução e é invisível para nós. Um invasor que comprometeu uma dependência aninhada ainda pode alcançar nosso pipeline.
Correção em andamento: o bloqueio de dependências no nível do fluxo de trabalho foi anunciado no roteiro de segurança do GitHub Actions para 2026. Uma seção dependencies: será adicionada ao fluxo de trabalho YAML, que bloqueia todas as dependências diretas e transitivas de ações por SHA de commit - semelhante ao que go.mod + go.sum faz para Go. Vamos conectá-lo assim que aparecer.
Atualizações automatizadas com uma fronteira de confiança
Manter os SHA-pins manualmente seria doloroso, então não fazemos isso. A configuração Renovate estende o preset helpers:pinGitHubActionDigests e define pinDigests: true globalmente. Quando uma nova versão de uma ação é lançada, Renovate abre um PR com a atualização SHA. Permanecemos atualizados e não voltamos para uma referência mutável.
Renovate funciona como um bot auto-hospedado em uma programação horária, por meio de um GitHub App dedicado com permissões granulares em vez de um token de acesso pessoal.
vulnerabilityAlerts estão habilitados, para que CVEs conhecidos na árvore de dependências se transformem imediatamente em um PR.
Recentemente, adicionamos um período de espera (cooldown) Renovate para não pegar lançamentos muito novos no momento em que aparecem. Dado o ritmo atual dos ataques à cadeia de suprimentos, alguns dias geralmente são suficientes para que um pacote comprometido seja notado e revogado (yanked):
json# .github/renovate.json5 { // Dependency cooldown: skip versions published less than 5 days ago "matchUpdateTypes": ["major", "minor", "patch"], "minimumReleaseAge": "5 days" }, { "matchPackageNames": [ "actions/{/,}**", // GitHub's official actions "docker/{/,}**", // Official Docker actions "cilium/{/,}**", // Our own ecosystem "k8s.io/{/,}**", // Kubernetes official "sigs.k8s.io/{/,}**", // Kubernetes SIGs "golang.org/x/{/,}**", // Go experimental "github.com/golang/{/,}**", // Go official org "github.com/prometheus/{/,}**", "github.com/hashicorp/{/,}**", "go.etcd.io/etcd/{/,}**", // ...trimmed ], "automerge": true, "automergeType": "pr", "groupName": "auto-merge-trusted-deps", "reviewers": ["ciliumbot"] }
As atualizações desta lista permitida são mescladas automaticamente após a passagem do CI. Tudo o mais requer uma revisão humana.
O fluxo de trabalho auto-approve adiciona outra proteção duplicada: verifica se o PR foi criado por cilium-renovate[bot] e se a solicitação de revisão foi realmente iniciada pelo próprio bot, e não por uma pessoa fingindo ser ele:
yamlif: ${{ github.event.pull_request.user.login == 'cilium-renovate[bot]' && (github.triggering_actor == 'cilium-renovate[bot]' || github.triggering_actor == 'auto-committer[bot]') }}
Se essas condições não forem atendidas, a autoaprovação não ocorrerá.
Vendorização de módulos Go
Todas as dependências Go são vendidas e commitadas no repositório. O CI verifica se não há discrepâncias entre go.mod, go.sum e vendor/. As construções são reproduzíveis e não se comunicam com proxies de módulo externos durante a construção, para que um módulo falsificado no proxy nunca chegue até nós. Também executamos verificações de licença (go run ./tools/licensecheck) para manter as dependências com licenças indesejadas fora da árvore.
Devemos bifurcar as ações em nossa própria organização?
Em teoria, sim. Se tivéssemos bifurcado cada ação de terceiros em cilium/ e a fixado no SHA de nossa bifurcação, o comprometimento upstream não teria chegado até nós. Alguns projetos com altos requisitos de segurança fazem isso.
Decidimos não fazer isso - principalmente porque o custo operacional é real, e o ganho de segurança é menor do que parece à primeira vista:
- Carga de suporte. Usamos dezenas de ações de terceiros. Sincronizar bifurcações com patches de segurança upstream se torna uma preocupação constante, e uma bifurcação desatualizada com vulnerabilidades não corrigidas é, por si só, um problema de segurança.
- Melhorias perdidas. As ações upstream corrigem regularmente bugs e fornecem recursos de segurança. As bifurcações adicionam atrito e impedem que isso seja capturado.
- Complexidade Renovate. O pipeline de atualização teria que rastrear lançamentos upstream, abrir PRs contra cada bifurcação e, em seguida, atualizar os fluxos de trabalho de consumo. A cadeia dobra.
SHA-pinning nos dá uma garantia realmente importante de imutabilidade: um commit específico é um commit específico, independentemente da organização em que ele está. Em combinação com o Renovate, que oferece atualizações à medida que novas versões são lançadas, obtemos um ganho de segurança sem um imposto operacional. Se um grande provedor de ações fosse repetidamente comprometido, bifurcar os de alto risco seria uma escalada sensata, mas isso ainda não aconteceu.
O mesmo compromisso - para dependências Go
A pergunta "devemos bifurcá-lo?" também se aplica à nossa árvore de dependências Go. Cilium puxa centenas de módulos Go: bibliotecas de cliente Kubernetes, gRPC, etcd, Prometheus e muito mais. Bifurcá-los e mantê-los todos é irrealista.
Go tem uma posição inicial um pouco melhor do que npm ou PyPI, porque os caminhos de importação incluem explicitamente a fonte (github.com/stretchr/testify) - isso elimina completamente uma classe de ataques de confusão de dependência. O typosquatting, no entanto, ainda é uma ameaça real. A pesquisa de Michael Henriksen encontrou pacotes Go de typosquatting na natureza, incluindo uma bifurcação de urfave/cli, registrada como utfave (uma letra trocada), que enviou o nome do host, sistema operacional e arquitetura para um servidor remoto. Substituir este callback por um shell reverso teria sido uma alteração de uma linha.
E typosquatting não é o pior caso. SolarWinds mostrou que um fornecedor legítimo e amplamente confiável pode ter seu pipeline de construção comprometido - e, em seguida, distribui malware por meio de atualizações regulares. O mesmo pode acontecer com qualquer módulo Go: um invasor ganha acesso à conta do mantenedor, publica uma versão maliciosa, o proxy a armazena em cache, e qualquer pessoa que execute go get a puxa. É por isso que vendemos: isso transfere a decisão de confiança do tempo de construção, onde é invisível, para o tempo de revisão, onde uma pessoa pode ver a diff.
Vendorização é a principal proteção aqui. Um caminho de importação de typosquatting aparece como uma diff em vendor/ durante a revisão do código, e não é resolvido silenciosamente por meio de um proxy de módulo. Isso não captura um erro de digitação no momento em que é feito (uma aposta de que o revisor notará um caminho desconhecido no PR), mas em combinação com o gate CODEOWNERS, funciona bem até agora.
Também prestamos atenção às dependências que tomamos. A configuração Renovate tem uma lista explícita de dependências desativadas que gerenciamos manualmente - ou porque precisam de atualizações coordenadas (como sigs.k8s.io/gateway-api junto com testes de conformidade), ou porque mantemos uma bifurcação com patches específicos do projeto (como github.com/cilium/dns), ou porque a dependência é algo que desenvolvemos nós mesmos e queremos atualizar intencionalmente (como github.com/cilium/ebpf - esta não é uma bifurcação, mas uma biblioteca Go separada, mantida sob a organização Cilium). As alterações em vendor/ são revisadas pela equipe dedicada @cilium/vendor por meio do mesmo mecanismo CODEOWNERS acima.
Há um provérbio Go que vale a pena citar: "Um pouco de cópia é melhor do que um pouco de dependência". Levamos isso a sério - fora do estilo. Periodicamente, auditamos bibliotecas de terceiros e reduzimos ativamente a árvore. Se uma dependência existe apenas para uma pequena função utilitária, a substituímos por algumas linhas copiadas inline. Cada dependência removida é uma que nunca pode ser comprometida, a árvore do fornecedor se torna menor e a revisão de futuras alterações de dependência é simplificada. O efeito se acumula.
Capturando erros com análise estática
Mesmo com as políticas corretas, erros acontecem. Um contribuinte bem-intencionado pode adicionar um fluxo de trabalho sem permissions: ou usar ubuntu-latest em vez de um runner fixado. Usamos análise estática para pegar isso antes da revisão.
Onde o fluxo de trabalho precisa de acesso de gravação (assinatura de lançamentos, OIDC para Cosign), apenas a área específica é declarada - por exemplo, id-token: write ou contents: write. Onde não é necessário - então permissions: read-all ou permissions: {}, para desativar explicitamente os padrões mais amplos. Mas não confiamos na memória para isso. CodeQL é executado em cada push e PR com a regra actions/missing-workflow-permissions ativada, e o fluxo de trabalho falha em qualquer arquivo de fluxo de trabalho alterado em que as permissões não sejam explicitamente especificadas.
Além disso, actionlint verifica estaticamente cada arquivo de fluxo de trabalho em busca de erros de sintaxe, padrões inseguros e configurações incorretas. O mesmo pipeline de lint também exige o cumprimento obrigatório dos acordos do projeto: cada trabalho e etapa tem um nome, nenhum trabalho usa uma tag de runner flutuante ubuntu-latest (fixamos em ubuntu-24.04), não há espaços em branco no final dos arquivos de fluxo de trabalho.
Uma classe de vulnerabilidade vale a pena destacar - a injeção de expressões do GitHub Actions. A sintaxe ${{ }} no fluxo de trabalho YAML é uma substituição de texto que ocorre antes que o bash veja a string. Se um invasor controlar o valor a ser substituído (título do PR, nome do branch), ele poderá injetar comandos shell arbitrários via ;, $(...) ou backticks. Bash não tem ideia de onde o valor veio. A correção é primeiro atribuir o valor a uma variável de ambiente e se referir a ela como "$MY_VAR" no bloco run:, para que o bash a trate como uma variável, independentemente do conteúdo. A equipe de segurança do GitHub nos informou sobre isso há algum tempo, e corrigimos todos os casos. Este é um bug sutil - fácil de fazer e difícil de notar na revisão, por isso a análise estática é tão importante: actionlint e CodeQL marcam o uso de ${{ }} em blocos run:, onde a entrada não confiável flui.
Protegendo as credenciais
Partimos do princípio de que qualquer nível individual pode falhar. Se um fluxo de trabalho CI for comprometido, a pergunta se torna: o que o invasor realmente alcançará? A resposta deve ser: nada que importe.
Padrões fortes
Por padrão, nosso GITHUB_TOKEN é limitado às permissões mínimas de leitura em conteúdo e pacotes. Os fluxos de trabalho que precisam de mais devem incluir explicitamente (opt in) direitos adicionais - para que um fluxo de trabalho em que as permissões foram esquecidas não acabe com amplo acesso de gravação em toda a organização.
Isolação de credenciais CI e produção
Mantemos dois conjuntos separados de credenciais de registro por trás de ambientes GitHub protegidos separados:
- As credenciais CI podem ser enviadas para nosso registro de imagens de desenvolvimento (
quay.io/cilium/*-ci) e estão disponíveis para construções CI. Mesmo que um fluxo de trabalho CI seja de alguma forma comprometido, essas credenciais não poderão ser enviadas para as tags de produção da imagem. - As credenciais de produção estão atrás do ambiente
release, que requer a aprovação explícita do mantenedor antes que a execução do fluxo de trabalho possa tocá-las. Nem uma bifurcação, nem um branch de recurso, nem uma construção CI obterão esses segredos. Apenas as construções de lançamento acionadas por tag que o mantenedor aprovou.
No pior caso, com um CI comprometido, um invasor pode publicar uma imagem -ci maliciosa. Publicar em quay.io/cilium/cilium:v1.x.x ou docker.io/cilium/cilium:v1.x.x não será possível. As credenciais simplesmente não estão no runner.
Cada chamada para actions/checkout também define persist-credentials: false, para que o GITHUB_TOKEN nunca apareça na configuração git do runner, de onde uma etapa posterior poderia pegá-lo.
Assinando e atestando o que entregamos
As seções anteriores são sobre como impedir que o mal entre no pipeline. Este é sobre como dar aos consumidores a capacidade de verificar o que sai dele.
Cada imagem de contêiner que lançamos (cilium, operator-*, hubble-relay, clustermesh-apiserver) é assinada com Sigstore Cosign via keyless OIDC. Não há chaves de assinatura de longa duração que possam ser roubadas.
Uma ação composta reutilizável lida com o pipeline de assinatura:
yaml# .github/actions/cosign/action.yaml - name: Install Cosign uses: sigstore/cosign-installer@cad07c2e89fa2edd6e2d7bab4c1aa38e53f76003 # v4.1.1 - name: Generate SBOM uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0 with: artifact-name: sbom_${{ inputs.sbom_name }}.spdx.json output-file: ./sbom_${{ inputs.sbom_name }}.spdx.json image: ${{ inputs.image_tag }} - name: Sign Container Image shell: bash run: cosign sign -y "${{ inputs.image }}" - name: Attach SBOM Attestation shell: bash run: | cosign attest -y \ --predicate "./sbom_${{ inputs.sbom_name }}.spdx.json" \ --type spdxjson \ "${{ inputs.image }}"
Isso é executado para cada construção de imagem de lançamento e para nossos artefatos OCI de gráficos Helm. As instruções de verificação estão na documentação do Cilium.
As construções de lançamento também são executadas dentro de ambientes protegidos (release, release-tool, release-helm), para que as credenciais do registro de produção sejam limitadas pelas regras de proteção do ambiente. Não é possível executar uma construção de lançamento de uma bifurcação ou branch de recurso.
Equipe de segurança do Cilium
Se você já relatou um problema de segurança a um projeto (por meio de avisos de segurança do GitHub ou security@cilium.org)








