3
votes

Récupérer dynamiquement le secret des actions GitHub

J'essaie de récupérer dynamiquement un secret GitHub à l'aide d'actions GitHub au moment de l'exécution:

Disons que j'ai deux secrets GitHub:

  1. SECRET_ORANGES: "Ceci est un secret orange"
  2. SECRET_APPLES: "Ceci est un secret de pomme"

Dans mon action GitHub, j'ai une autre variable d'environnement qui différera entre les branches

name: Test

env:
  CUSTOMER: CUSTOMER1

jobs:
  build:
    runs-on: ubuntu-latest
    env:
      AWS_ACCESS_KEY_ID: ${{ env.CUSTOMER }}_AWS_ACCESS_KEY_ID
    steps:
    - uses: actions/checkout@v2
    - run: |
        AWS_ACCESS_KEY_ID=${{ secrets[env.AWS_ACCESS_KEY_ID] }}
        echo "AWS_ACCESS_KEY_ID = $AWS_ACCESS_KEY_ID"

Essentiellement, je veux trouver un moyen de faire une sorte de substitution de variable pour obtenir le bon secret. Donc, dans l'un de mes emplois pour enfants, je veux faire quelque chose comme:

{{ secrets.$FRUIT_NAME }}

J'ai essayé les approches suivantes sans succès:

secrets['$FRUIT_NAME'] }}

J'ai même essayé une approche plus simple sans concaténation juste pour essayer de la faire fonctionner

secrets['SECRET_$FRUIT_NAME'] }}

et

env:
  FRUIT_SECRET: {{ 'SECRET_' + env.FRUIT_NAME }}

Aucun de ces éléments n'a fonctionné.

Toutes mes excuses si je n'ai pas très bien expliqué cela. J'ai essayé de garder mon exemple aussi simple que possible.

Quelqu'un a-t-il une idée de la façon d'y parvenir?

Alternativement, ce que j'essaie de faire est de stocker des secrets sur une base par branche

Par exemple:

Dans customer1 branche de code SECRET_CREDENTIAL="abc123" : SECRET_CREDENTIAL="abc123"

Dans customer2 branche de code SECRET_CREDENTIAL="def456" : SECRET_CREDENTIAL="def456"

Ensuite, je peux accéder à la valeur correcte pour SECRET_CREDENTIAL fonction de la branche dans laquelle je me trouve.

Merci!

Mise à jour: je me rapproche un peu plus de ce que j'essaie de réaliser:

env:
  FRUIT_NAME: APPLES


5 commentaires

En pensant à haute voix ici, puisque la syntaxe est {{ secrets.SECRET_NAME }} , vous pouvez peut-être essayer: {{ secrets['$FRUIT_NAME'] }} , ou {{ secrets.$FRUIT_NAME }} . Si les deux ne fonctionnent pas, je vous suggère de modifier votre question et de mentionner que vous avez déjà essayé les deux


Merci pour la suggestion, quand je fais ça et que je fais écho, c'est vide. Il semble qu'il n'analyse pas correctement $ FRUIT_NAME en tant que valeur du secret.


Et {{ secrets[$FRUIT_NAME] }} ?


Malheureusement non - j'ai juste essayé et obtenu le Unexpected symbol: '$FRUIT_NAME'. Located at position 9 within expression: secrets[$FRUIT_NAME]


Apparemment, c'est faisable, a répondu avec une solution. J'ai été vraiment surpris de savoir que c'était possible, merci de m'avoir fait renforcer mes connaissances avec les expressions GitHub Actions :)


6 Réponses :


6
votes

Mise à jour - août 2020

Suite à une expérience pratique avec ce projet terraform-monorepo , voici un exemple de la façon dont j'ai réussi à utiliser des noms secrets de manière dynamique

  1. Les noms des secrets sont alignés sur les noms d'environnements et les noms de branches - development , staging et production
  2. $GITHUB_REF_SLUG provient de l' action Slug GitHub qui récupère le nom de la branche
  3. Les commandes qui effectuent l'analyse sont
env:
  SECRET_PREFIX: A
  SECRET_NAME: ${{ env.SECRET_PREFIX }}_FRUIT_NAME

Exemple complet

SECRET_NAME = A_FRUIT_NAME
SECRET_NAME = A_FRUIT_NAME
SECRET_VALUE = ***

Après avoir lu ceci - Syntaxe de contexte et d'expression pour les actions GitHub , en me concentrant sur l' objet env , j'ai découvert que:

Dans le cadre d'une expression, vous pouvez accéder aux informations de contexte en utilisant l'une des deux syntaxes.

Syntaxe de l'index: github ['sha']

Syntaxe de déréférencement de propriété: github.sha

Donc, le même comportement s'applique aux secrets , vous pouvez faire des secrets[secret_name] , donc vous pouvez faire ce qui suit

    - name: Run a multi-line script
      env:
        SECRET_NAME: A_FRUIT_NAME
      run: |
        echo "SECRET_NAME = $SECRET_NAME"
        echo "SECRET_NAME = ${{ env.SECRET_NAME }}"
        SECRET_VALUE=${{ secrets[env.SECRET_NAME] }}
        echo "SECRET_VALUE = $SECRET_VALUE"

Ce qui aboutit à

name: pipeline

on:
  push:
    branches: [development, staging, production]
    paths-ignore:
      - "README.md"

jobs:
  terraform:
    runs-on: ubuntu-latest

    env:
      ### -----------------------
      ### Available in all steps, change app_name to your app_name
      TF_VAR_app_name: tfmonorepo
      ### -----------------------

    steps:
      - uses: actions/checkout@v2
      - name: Inject slug/short variables
        uses: rlespinasse/github-slug-action@v2.x
      - name: prepare-files-folders
        run: |
          mkdir -p ${GITHUB_REF_SLUG}/
          cp live/*.${GITHUB_REF_SLUG} ${GITHUB_REF_SLUG}/
          cp live/*.tf ${GITHUB_REF_SLUG}/
          cp live/*.tpl ${GITHUB_REF_SLUG}/ 2>/dev/null || true
          mv ${GITHUB_REF_SLUG}/backend.tf.${GITHUB_REF_SLUG} ${GITHUB_REF_SLUG}/backend.tf
      - name: install-terraform
        uses: little-core-labs/install-terraform@v1
        with:
          version: 0.12.28
      - name: set-aws-credentials
        run: |
          echo "::set-env name=AWS_ACCESS_KEY_ID_SECRET_NAME::AWS_ACCESS_KEY_ID_${GITHUB_REF_SLUG}"
          echo "::set-env name=AWS_SECRET_ACCESS_KEY_SECRET_NAME::AWS_SECRET_ACCESS_KEY_${GITHUB_REF_SLUG}"
      - name: terraform-apply
        run: |
          export AWS_ACCESS_KEY_ID=${{ secrets[env.AWS_ACCESS_KEY_ID_SECRET_NAME] }}
          export AWS_SECRET_ACCESS_KEY=${{ secrets[env.AWS_SECRET_ACCESS_KEY_SECRET_NAME] }}
          cd ${GITHUB_REF_SLUG}/
          terraform version
          rm -rf .terraform
          terraform init -input=false
          terraform get
          terraform validate
          terraform plan -out=plan.tfout -var environment=${GITHUB_REF_SLUG}
          terraform apply -auto-approve plan.tfout 
          rm -rf .terraform

Puisque SECRET_VALUE est expurgé, nous pouvons supposer que le vrai secret a été récupéré.

Les choses que j'ai apprises -

  1. Vous ne pouvez pas référencer env partir d'un autre env , donc cela ne fonctionnera pas

          - name: set-aws-credentials
            run: |
              echo "::set-env name=AWS_ACCESS_KEY_ID_SECRET_NAME::AWS_ACCESS_KEY_ID_${GITHUB_REF_SLUG}"
              echo "::set-env name=AWS_SECRET_ACCESS_KEY_SECRET_NAME::AWS_SECRET_ACCESS_KEY_${GITHUB_REF_SLUG}"
          - name: terraform-apply
            run: |
              export AWS_ACCESS_KEY_ID=${{ secrets[env.AWS_ACCESS_KEY_ID_SECRET_NAME] }}
              export AWS_SECRET_ACCESS_KEY=${{ secrets[env.AWS_SECRET_ACCESS_KEY_SECRET_NAME] }}
    

    Le résultat de SECRET_NAME est _FRUIT_NAME , pas bon

  2. Vous pouvez utiliser des expressions de contexte dans votre code, pas seulement dans env , vous pouvez le voir dans SECRET_VALUE=${{ secrets[env.SECRET_NAME] }} , ce qui est cool

Et bien sûr - voici le flux de travail que j'ai testé - https://github.com/unfor19/gha-play/runs/595345435?check_suite_focus=true - cochez l'action Run a multi-line script


13 commentaires

Fantastique! C'est génial. Je joue avec ça depuis des heures et je n'ai nulle part. Merci beaucoup. Alors, comment cela fonctionnerait-il avec le nom du fruit si j'ai besoin d'une concaténation de chaînes: APPLE_SECRET1 APPLE_SECRET2 ORANGE_SECRET1 ORANGE_SECRET2 Alors je veux avoir A_FRUIT_NAME défini sur APPLE ou ORANGE, mais ensuite utiliser cet espace réservé ailleurs dans le script pour récupérer à la fois SECRET1 et SECRET2?


Ou si cela peut aider, je peux donner un exemple plus concret de ce que j'essaie de réaliser, basé sur les informations d'identification AWS ... J'ai deux branches (CUSTOMER1 et CUSTOMER2). Chaque branche que je souhaite définir une variable appelée "CUSTOMER" dans le fichier yml et elle sera définie sur CUSTOMER1 ou CUSTOMER2 Ensuite, dans Secrets, j'ai 2 ensembles d'informations d'identification AWS ... AWS_SECRET_ACCESS_KEY à la valeur de CUSTOMER1_AWS_SECRET_ACCESS_KEY


Puisque vous avez mentionné que CUSTOMER est un nom de branche, je pense que vous pouvez utiliser github.ref , puis vous pouvez faire ceci: env: AWS_ACCESS_KEY_ID: ${{ github.ref }}_AWS_ACCESS_KEY_ID puis utiliser ${{ secrets[env.AWS_ACCESS_KEY_ID] }} sens?


De plus, au cas où vous auriez besoin d'une CLI pour ajouter / mettre à jour / supprimer / lister vos secrets, j'ai créé une CLI afin que je puisse éviter d'utiliser l'interface graphique - github.com/unfor19/githubsecrets


Excusez-moi, je trouble davantage les choses en essayant de garder les choses simples. J'ai des succursales distinctes pour le client 1 et le client 2, mais les noms des succursales ne correspondent pas directement. Les noms des succursales sont peut-être plus complexes (fonctionnalité / client / client1 / développer) etc. Existe-t-il un moyen de faire cela sans utiliser directement le nom de la succursale? En utilisant une seule variable env plus haut dans le fichier contenant le préfixe secret: par exemple CUSTOMER: Customer1 (même si le nom de la branche est totalement indépendant)


J'ai mis à jour ma question initiale avec ce que j'ai jusqu'à présent


Je pense que cela signifie que vous devrez définir un flux de travail différent par client (succursale). Je ne sais pas comment vous pouvez utiliser secrets[s_name] sans utiliser env ou coder en dur le nom du secret ... C'est là où je ne suis plus sûr d'être utile


OK super. Merci de votre aide. Je pense que votre réponse est assez proche de ce que j'essaie de réaliser. J'essayais d'éviter d'avoir des variables codées en dur dans mes fichiers YML et d'avoir à la place une seule variable à mettre à jour chaque fois que je créais une nouvelle succursale pour un client, mais je pense que ce que j'essaye est peut-être trop compliqué pour peu de gain. Je pense que je peux me contenter de coder en dur les changements dans chaque branche. Merci quand même pour votre aide!


Je pense que j'ai pu réaliser ce que je voulais en utilisant le nom du workflow! Je répondrai à ma propre question ci-dessous


Je pense que vous abusez de git, avoir une succursale par client dans le même dépôt semble être une mauvaise approche: / Je ne suis pas sûr de ce que vous essayez de réaliser, mais cela ne semble pas gérable ou évolutif


Je sais que cela sort du sujet de la question initiale, mais comment aborderiez-vous ce qui précède? J'ai un exemple d'application que je personnalise avec différentes saveurs (couleurs, mises à jour de texte, coordonnées) pour plusieurs clients. Je suis le seul à avoir accès au code (les clients eux-mêmes n'ont pas accès au code). Le modèle de branchement était pour que je puisse apporter des modifications à la copie principale du code, puis tirer les modifications / améliorations dans les branches spécifiques du client qui contiennent leurs modifications spécifiques. Comment feriez-vous cela?


On dirait que vous essayez d'obtenir une étiquette blanche pour votre application - une application de base qui comprend des fonctionnalités de base, puis l'application de chaque client est basée sur l'application de base, y compris certaines modifications de vars d'environnement et peut-être même plus d'applications. Peut-être utiliser Docker? Créez une image de base, puis chaque client aura son propre dépôt, mais son image sera basée sur votre image de base. Cela signifie que vous devrez définir un pipeline approprié, mais cela semble être une solution plus simple que d'avoir plusieurs clients en tant que succursales dans le même dépôt


Continuons cette discussion en chat .



2
votes

J'ai pu y parvenir en utilisant le nom du workflow comme variable spécifique à la branche.

Pour chaque branche que je crée, je mets simplement à jour cette valeur unique en haut du fichier YML, puis j'ajoute GitHub Secrets pour correspondre au nom du workflow:

name: CUSTOMER1

jobs:
  build:
    runs-on: ubuntu-latest
    env:
      AWS_ACCESS_KEY_ID: ${{ github.workflow }}_AWS_ACCESS_KEY_ID
    steps:
    - uses: actions/checkout@v2
    - run: echo "::set-env name=AWS_ACCESS_KEY_ID::${{ secrets[env.AWS_ACCESS_KEY_ID] }}"
    - run: echo $AWS_ACCESS_KEY_ID


0 commentaires

4
votes

Au cas où cela peut aider, après avoir lu les réponses ci-dessus qui ont vraiment aidé, la stratégie que j'ai décidé d'utiliser consiste à stocker mes secrets comme suit:

  • DB_USER_MASTER
  • DB_PASSWORD_MASTER
  • DB_USER_TEST
  • DB_PASSWORD_TEST

Où MASTER est la branche principale de l'environnement de production et TEST est la branche de test de l'environnement de test.

Ensuite, en utilisant les solutions suggérées dans ce thread, la clé est de générer dynamiquement les clés de la variable secrets . Ces clés sont générées via une étape intermédiaire (appelée vars dans l'exemple ci-dessous) à l'aide des outputs :

name: Pulumi up
on:
  push:
    branches:
      - master
      - test
jobs:
  up:
    name: Update
    runs-on: ubuntu-latest
    steps:
      - name: Create variables
        id: vars 
        run: |
          branch=${GITHUB_REF##*/} 
          echo "::set-output name=DB_USER::DB_USER_${branch^^}"
          echo "::set-output name=DB_PASSWORD::DB_PASSWORD_${branch^^}"
      - uses: actions/checkout@v2
        with:
          fetch-depth: 1
      - uses: docker://pulumi/actions
        with:
          args: up -s ${GITHUB_REF##*/} -y
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          GOOGLE_CREDENTIALS: ${{ secrets.GOOGLE_CREDENTIALS }}
          PULUMI_ACCESS_TOKEN: ${{ secrets.PULUMI_ACCESS_TOKEN }}  
          DB_USER: ${{ secrets[steps.vars.outputs.DB_USER] }}  
          DB_PASSWORD: ${{ secrets[steps.vars.outputs.DB_PASSWORD] }}  

Remarquez le hack pour obtenir la branche en majuscules: ${branch^^} . Cela est nécessaire car GitHub force les secrets en majuscules.


2 commentaires

Bon travail sur celui-ci! Je ne connais pas la référence de l'étape (vars), merci pour le partage


C'est bien! Merci!



2
votes

Je suis tombé sur cette question en essayant d'implémenter la sélection secrète basée sur l'environnement pour une action Github.

Cette variable-mapper l' action ( https://github.com/marketplace/actions/variable-mapper ) met en œuvre le concept souhaité de mapper une variable clé ou un nom d'environnement secrets ou d' autres valeurs prédéfinies.

L'exemple d'utilisation montre ceci:

on: [push]
name: Export variables corresponding to regular expression-matched keys
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: kanga333/variable-mapper@v1 
      with:
        key: ${{GITHUB_REF#refs/heads/}}
        map: |
          {
            "master": {
              "environment": "production",
              "AWS_ACCESS_KEY_ID": ${{ secrets.PROD_AWS_ACCESS_KEY_ID }},
              "AWS_SECRET_ACCESS_KEY": ${{ secrets.PROD_AWS_ACCESS_KEY_ID }}
            },
            "staging": {
              "environment": "staging",
              "AWS_ACCESS_KEY_ID": ${{ secrets.STG_AWS_ACCESS_KEY_ID }},
              "AWS_SECRET_ACCESS_KEY": ${{ secrets.STG_AWS_ACCESS_KEY_ID }}
            },
            ".*": {
              "environment": "development",
              "AWS_ACCESS_KEY_ID": ${{ secrets.DEV_AWS_ACCESS_KEY_ID }},
              "AWS_SECRET_ACCESS_KEY": ${{ secrets.DEV_AWS_ACCESS_KEY_ID }}
            }
          }
    - name: Echo environment
      run: echo ${{ env.environment }}


1 commentaires

Oh sympa! Cela semble être une excellente approche. Merci!



2
votes

Il existe une option beaucoup plus propre pour y parvenir en utilisant la fonction de formatage .

Étant donné les secrets définis DEV_A et TEST_A, les deux travaux suivants utiliseront ces deux secrets:

name: Secrets

on:
  workflow_dispatch:
    inputs:
      env:
        description: "Environment to deploy to"
        required: true

jobs:
  secrets:
    name: secrets
    runs-on: ubuntu-18.04
    steps:
      - run: echo ${{ secrets[format('{0}_A', github.event.inputs.env)] }}

Cela fonctionne également avec les entrées fournies via les workflows manuels (l'événement workflow_dispatch ):

name: Secrets

on: [push]

jobs:

  dev:
    name: dev
    runs-on: ubuntu-18.04
    env:
      ENVIRONMENT: DEV
    steps:
      - run: echo ${{ secrets[format('{0}_A', env.ENVIRONMENT)] }}

  test:
    name: test
    runs-on: ubuntu-18.04
    env:
      ENVIRONMENT: TEST
    steps:
      - run: echo ${{ secrets[format('{0}_A', env.ENVIRONMENT)] }}


0 commentaires

1
votes

Actions GitHub: abandon des commandes set-env et add-path

Voici la nouvelle méthode à compter du 1er octobre 2020

jobs:
  example-job:
    name: example
    runs-on: ubuntu-latest
    steps:
    - name: log untrusted output
      run: |

        # disable command workflow processing
        echo "::stop-commands::`echo -n ${{ github.token }} | sha256sum | head -c 64`"

        # log untrusted output
        echo "untrusted output"

        # enable workflow command processing
        echo "::`echo -n ${{ github.token }} | sha256sum | head -c 64`::"


0 commentaires