Skip to content

External CI/CD Platform Credentials

Jenkins Credentials

Store GitHub App credentials in Jenkins credential store.

Jenkins Setup

  1. Navigate to Manage JenkinsCredentials
  2. Select appropriate domain (usually (global))
  3. Click Add Credentials
  4. Create two credentials:

App ID (Secret text):

  • Kind: Secret text
  • Scope: Global
  • Secret: 123456
  • ID: github-app-id
  • Description: GitHub App ID for Core App

Private Key (Secret file):

  • Kind: Secret file
  • Scope: Global
  • File: Upload .pem file
  • ID: github-app-private-key
  • Description: GitHub App Private Key for Core App

Pipeline Usage

pipeline {
    agent any

    environment {
        GH_APP_ID = credentials('github-app-id')
        GH_APP_PRIVATE_KEY = credentials('github-app-private-key')
    }

    stages {
        stage('Generate Token') {
            steps {
                script {
                    // Install dependencies
                    sh 'pip install pyjwt cryptography requests'

                    // Generate token using Python script
                    def token = sh(
                        script: '''
                            python3 - <<'EOF'
import jwt
import time
import requests
import os

app_id = os.environ['GH_APP_ID']
with open(os.environ['GH_APP_PRIVATE_KEY'], 'r') as f:
    private_key = f.read()

payload = {
    'iat': int(time.time()) - 60,
    'exp': int(time.time()) + (10 * 60),
    'iss': app_id
}

jwt_token = jwt.encode(payload, private_key, algorithm='RS256')

# Get installation ID (replace with your org)
headers = {
    'Authorization': f'Bearer {jwt_token}',
    'Accept': 'application/vnd.github+json'
}
response = requests.get('https://api.github.com/app/installations', headers=headers)
installation_id = response.json()[0]['id']

# Generate installation token
response = requests.post(
    f'https://api.github.com/app/installations/{installation_id}/access_tokens',
    headers=headers
)
print(response.json()['token'])
EOF
                        ''',
                        returnStdout: true
                    ).trim()

                    env.GITHUB_TOKEN = token
                }
            }
        }

        stage('Use Token') {
            steps {
                sh '''
                    gh api /repos/my-org/my-repo/issues
                '''
            }
        }
    }
}

Jenkins Secret Masking

Jenkins doesn't automatically mask GitHub App tokens. Use credential masking plugins or avoid echoing tokens in logs.

GitLab CI Variables

Store credentials as protected and masked CI/CD variables.

GitLab CI Setup

  1. Navigate to SettingsCI/CDVariables
  2. Add two variables:

CORE_APP_ID:

  • Type: Variable
  • Environment scope: All (or specific)
  • Flags: ☑ Protected, ☑ Masked
  • Value: 123456

CORE_APP_PRIVATE_KEY:

  • Type: File
  • Environment scope: All (or specific)
  • Flags: ☑ Protected, ☑ Masked
  • Value: Complete PEM file contents

GitLab Variable Types

  • Use File type for private key to avoid escaping issues
  • Private key becomes available as file at $CORE_APP_PRIVATE_KEY path

GitLab Pipeline Usage

generate_token:
  stage: setup
  image: python:3.11
  before_script:
    - pip install pyjwt cryptography requests
  script:
    - |
      python3 - <<'EOF' > token.txt
      import jwt
      import time
      import requests
      import os

      app_id = os.environ['CORE_APP_ID']
      with open(os.environ['CORE_APP_PRIVATE_KEY'], 'r') as f:
          private_key = f.read()

      payload = {
          'iat': int(time.time()) - 60,
          'exp': int(time.time()) + (10 * 60),
          'iss': app_id
      }

      jwt_token = jwt.encode(payload, private_key, algorithm='RS256')

      headers = {
          'Authorization': f'Bearer {jwt_token}',
          'Accept': 'application/vnd.github+json'
      }
      response = requests.get('https://api.github.com/app/installations', headers=headers)
      installation_id = response.json()[0]['id']

      response = requests.post(
          f'https://api.github.com/app/installations/{installation_id}/access_tokens',
          headers=headers
      )
      print(response.json()['token'])
      EOF
    - export GITHUB_TOKEN=$(cat token.txt)
  artifacts:
    reports:
      dotenv: token.txt

use_token:
  stage: deploy
  dependencies:
    - generate_token
  script:
    - gh api /repos/my-org/my-repo/issues

CircleCI Contexts

Store credentials in CircleCI contexts for secure sharing across projects.

CircleCI Setup

  1. Navigate to Organization SettingsContexts
  2. Create context (e.g., github-app-credentials)
  3. Add environment variables:
  4. CORE_APP_ID: 123456
  5. CORE_APP_PRIVATE_KEY: Complete PEM contents (use \n for line breaks)

CircleCI Pipeline Usage

version: 2.1

jobs:
  generate-token:
    docker:
      - image: cimg/python:3.11
    steps:
      - checkout
      - run:
          name: Install dependencies
          command: pip install pyjwt cryptography requests
      - run:
          name: Generate GitHub App token
          command: |
            python3 - <<'EOF' > /tmp/token.txt
            import jwt
            import time
            import requests
            import os

            app_id = os.environ['CORE_APP_ID']
            # CircleCI stores multi-line secrets with \n - convert back
            private_key = os.environ['CORE_APP_PRIVATE_KEY'].replace('\\n', '\n')

            payload = {
                'iat': int(time.time()) - 60,
                'exp': int(time.time()) + (10 * 60),
                'iss': app_id
            }

            jwt_token = jwt.encode(payload, private_key, algorithm='RS256')

            headers = {
                'Authorization': f'Bearer {jwt_token}',
                'Accept': 'application/vnd.github+json'
            }
            response = requests.get('https://api.github.com/app/installations', headers=headers)
            installation_id = response.json()[0]['id']

            response = requests.post(
                f'https://api.github.com/app/installations/{installation_id}/access_tokens',
                headers=headers
            )
            print(response.json()['token'])
            EOF
      - run:
          name: Use token
          command: |
            export GITHUB_TOKEN=$(cat /tmp/token.txt)
            gh api /repos/my-org/my-repo/issues

workflows:
  version: 2
  deploy:
    jobs:
      - generate-token:
          context: github-app-credentials

CircleCI Multi-line Secrets

CircleCI stores multi-line values with escaped \n. Convert back to actual newlines: private_key.replace('\\n', '\n')

Platform Comparison

Platform Secret Type Masking Scoping File Support Multi-line Support
GitHub Actions Native secrets Automatic Repo/org/env Via secrets Yes (native)
Jenkins Credentials Plugin-based Global/folder Secret file type Yes (file)
GitLab CI CI/CD variables Automatic (masked flag) Project/group File type Yes (file type)
CircleCI Context variables Automatic Organization Environment variable Yes (escaped \n)
Bitbucket Pipelines Repository variables Manual Repository/deployment Environment variable Yes (escaped \n)

Comments