Private Package Repositories
Stackery supports installing packages from public repositories during the build process for Function resources. This includes public repositories like npm for Node.js functions and PyPI for Python functions. Stackery can also be configured to install packages from private repositories, such as GitHub Packages.
Global npm Private Repository Access
Private package repositories require authentication using tokens or passwords. You can provide these as build environment variables to ensure private repositories are available during every build.
First, a quick note about security. Build environment variables are stored encrypted at rest and transmitted in encrypted form to the Stackery CLI during the packaging process. However, we still recommend using read-only authentication tokens where available. This prevents the possibility of misuse of private registry tokens to upload malicious packages.
Private package repository providers usually provide information on how to configure npm access by adding settings to a .npmrc file. This is an example from JFrog:
@jfrog:registry=http://localhost:8081/artifactory/api/npm/npm-local/
//localhost:8081/artifactory/api/npm/npm-local/:_password=cGFzc3dvcmQ=
//localhost:8081/artifactory/api/npm/npm-local/:username=admin
//localhost:8081/artifactory/api/npm/npm-local/:email=myemail@email.com
//localhost:8081/artifactory/api/npm/npm-local/:always-auth=true
These settings can also be set through build environment variables with some small tweaks. One environment variable must be set for each line. Split the line at the =
character; the left side will be used to create the environment variable name, and the right side will be the value of the environment variable. Then, prepend npm_config_
to the environment variable name.
Lastly, some setting names won't work when using environment variables, but they have aliases that do work. The following table provides aliases that work for common settings npm private registry providers use:
Setting Name | Alias To Use In Environment Variable Names |
---|---|
:_password | :password |
:_authToken | :token |
:alwaysAuth | :always-auth |
Taking all the above into account, the following environment variables would be set for the JFrog example configuration:
Environment Variable Name | Value |
---|---|
npm_config_@jfrog:registry | http://localhost:8081/artifactory/api/npm/npm-local/ |
npm_config_//localhost:8081/artifactory/api/npm/npm-local/:password | cGFzc3dvcmQ= |
npm_config_//localhost:8081/artifactory/api/npm/npm-local/:username | admin |
npm_config_//localhost:8081/artifactory/api/npm/npm-local/:email | myemail@email.com |
npm_config_localhost:8081/artifactory/api/npm/npm-local/:always-auth | true |
Custom Per-Stack Private Registry Integration
If you want a per-stack registry setup or a more customized registry setup you can create and fetch authentication tokens and other data from a central AWS Secrets Manager Secret in a pre-build deploy hook.
Upgrade the Stackery Role. The Stackery Role must be version 1.25.0 or later in AWS accounts where private package repositories will be used in deployments. See Updating Stackery Role for instructions on how to update the Stackery Role.
Create an API key. Follow your repository provider to generate a private API key. For example, GitHub repositories require a personal access token, which can be created following their tokens documentation.
Create an AWS KMS Customer Managed Key (CMK) for encryption. AWS Secrets Manager, which we use to store the API key, has a default KMS key it uses for encryption. Unfortunately, default KMS keys cannot be used when accessing secrets across AWS accounts. To support cross-account access we'll create a new customer key:
$ aws kms create-key --description 'Key to encrypt private repository API keys for Stackery' --policy "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":\"*\",\"Action\":[\"kms:Decrypt\",\"kms:DescribeKey\"],\"Resource\":\"*\",\"Condition\":{\"StringEquals\":{\"aws:PrincipalOrgID\":\"`aws organizations describe-organization --query Organization.Id --output text`\"},\"ArnLike\":{\"aws:PrincipalArn\":[\"arn:aws:iam::*:role/stackery/stackery-factory-role\",\"arn:aws:iam::*:role/stackery/stackery-deployer\"]}}},{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::`aws sts get-caller-identity --query Account --output text`:root\"},\"Action\":\"kms:*\",\"Resource\":\"*\"}]}"
If you need to use a specific AWS credential profile and/or region you should export the
AWS_PROFILE
andAWS_REGION
environment variables before running the AWS commands in this document. Many commands have substitutions embedded within to fetch your AWS Account and/or Organization IDs. Exporting the profile and region as environment variables ensures they are used for both the embedded substitutions and the parent commands.Note the key's ARN in the output, it should look like:
arn:aws:kms:us-east-1:012345678901:key/8e7be4ac-a392-4bdd-8ff8-1ab1683de829
Store the API key in AWS Secrets Manager. The following command will create a new AWS Secrets Manager Secret which stores your API key in encrypted form using the KMS CMK created above.
$ aws secretsmanager create-secret --name /stackery/ghpackagerepotoken --description 'GitHub Packages Personal Access Token' --secret-string '<API key>' --kms-key-id '<KMS key ARN from previous step>'
Note the secret's ARN in the output, it should look like:
arn:aws:secretsmanager:us-east-1:012345678901:secret:/stackery/ghpackagerepotoken-hCsqhY
Add a permission to retrieve the API key. Now that the API key is stored in AWS Secrets Manager we need to provide the Stackery CodeBuild environment with permissions to read it. Run the following command to provide read access to the key for the AWS IAM Roles used in Stackery CodeBuild environments across all AWS accounts in the same AWS Organization:
$ aws secretsmanager put-resource-policy --secret-id /stackery/ghpackagerepotoken --resource-policy "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":\"*\",\"Action\":\"secretsmanager:GetSecretValue\",\"Resource\":\"*\",\"Condition\":{\"StringEquals\":{\"aws:PrincipalOrgID\":\"`aws organizations describe-organization --query Organization.Id --output text`\"},\"ForAllValues:StringEquals\":{\"secretsmanager:VersionStage\":\"AWSCURRENT\"},\"ArnLike\":{\"aws:PrincipalArn\":[\"arn:aws:iam::*:role/stackery/stackery-factory-role\",\"arn:aws:iam::*:role/stackery/stackery-deployer\"]}}}]}"
Retrieve API key in pre-build hook. Add a pre-build hook to each stack needing private package repository access by creating the file
deployHooks/stackery.prebuild.npmrc
to retrieve the API key and place it in the appropriate location (in this example we use it to set up an npm configuration for GitHub Packages access):#!/bin/bash -e # Provide the AWS Secrets Manager Secret ARN from step 4 here SECRET_ARN=<secret ARN> echo "Fetching private package repo API key from secret '${SECRET_ARN}'" # Parse AWS region from secret ARN IFS=: read -ra ARN_PARTS <<< "${SECRET_ARN}" REGION="${ARN_PARTS[3]}" echo "//npm.pkg.github.com/:_authToken=`aws secretsmanager get-secret-value --region "${REGION}" --secret-id "${SECRET_ARN}" --query SecretString --output text`" >> ~/.npmrc
When the stack is prepared the pre-build hook will retrieve the API key from AWS Secrets Manager and set up npm to use it when pulling packages from the GitHub packages private repository. When the build phase is run, private packages will be installable by Node.js runtime functions.