Compare commits

..

3 Commits

Author SHA1 Message Date
Eddy Filip
cb23d65d8d Add missing config for tests
Some checks failed
Run acceptance tests / test (push) Has been cancelled
Run acceptance tests / test-user (push) Has been cancelled
2021-08-03 19:23:44 +02:00
Eddy Filip
adde26c41b Fix lint 2021-08-03 19:18:04 +02:00
Eddy Filip
6bfaeeb67d Enable the user to use the GitHub Action without Connect 2021-08-03 19:11:41 +02:00
19 changed files with 199 additions and 18450 deletions

View File

@@ -5,9 +5,6 @@ jobs:
lint: lint:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v2
- name: Run ShellCheck - name: ShellCheck
uses: ludeeus/action-shellcheck@2.0.0 uses: ludeeus/action-shellcheck@1.1.0
with:
ignore_paths: >-
.husky

View File

@@ -2,135 +2,94 @@ on: push
name: Run acceptance tests name: Run acceptance tests
jobs: jobs:
test-with-output-secrets: test:
strategy: runs-on: ubuntu-latest
matrix:
os: [ ubuntu-latest, macos-latest ]
auth: [ connect, service-account ]
exclude:
- os: macos-latest
auth: connect
runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v2
- name: Launch 1Password Connect instance - name: Launch 1Password Connect instance
if: ${{ matrix.auth == 'connect' }}
env: env:
OP_CONNECT_CREDENTIALS: ${{ secrets.OP_CONNECT_CREDENTIALS }} OP_CONNECT_CREDENTIALS: ${{ secrets.OP_CONNECT_CREDENTIALS }}
run: | run: |
echo "$OP_CONNECT_CREDENTIALS" > 1password-credentials.json echo "$OP_CONNECT_CREDENTIALS" > 1password-credentials.json
docker-compose -f tests/fixtures/docker-compose.yml up -d && sleep 10 docker-compose -f tests/fixtures/docker-compose.yml up -d && sleep 10
- name: Configure Service account
if: ${{ matrix.auth == 'service-account' }}
uses: ./configure
with:
service-account-token: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
- name: Configure 1Password Connect - name: Configure 1Password Connect
if: ${{ matrix.auth == 'connect' }}
uses: ./configure # 1password/load-secrets-action/configure@<version>
with:
connect-host: localhost:8080
connect-token: ${{ secrets.OP_CONNECT_TOKEN }}
- name: Load secrets
id: load_secrets
uses: ./ # 1password/load-secrets-action@<version>
with:
export-env: false
env:
SECRET: op://acceptance-tests/test-secret/password
SECRET_IN_SECTION: op://acceptance-tests/test-secret/test-section/password
MULTILINE_SECRET: op://acceptance-tests/multiline-secret/notesPlain
- name: Assert test secret values
env:
SECRET: ${{ steps.load_secrets.outputs.SECRET }}
SECRET_IN_SECTION: ${{ steps.load_secrets.outputs.SECRET_IN_SECTION }}
MULTILINE_SECRET: ${{ steps.load_secrets.outputs.MULTILINE_SECRET }}
run: ./tests/assert-env-set.sh
test-with-export-env:
strategy:
matrix:
os: [ ubuntu-latest, macos-latest ]
auth: [ connect, service-account ]
exclude:
- os: macos-latest
auth: connect
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- name: Launch 1Password Connect instance
if: ${{ matrix.auth == 'connect' }}
env:
OP_CONNECT_CREDENTIALS: ${{ secrets.OP_CONNECT_CREDENTIALS }}
run: |
echo "$OP_CONNECT_CREDENTIALS" > 1password-credentials.json
docker-compose -f tests/fixtures/docker-compose.yml up -d && sleep 10
- name: Configure Service account
if: ${{ matrix.auth == 'service-account' }}
uses: ./configure
with:
service-account-token: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
- name: Configure 1Password Connect
if: ${{ matrix.auth == 'connect' }}
uses: ./configure # 1password/load-secrets-action/configure@<version> uses: ./configure # 1password/load-secrets-action/configure@<version>
with: with:
use-connect: yes
connect-host: http://localhost:8080 connect-host: http://localhost:8080
connect-token: ${{ secrets.OP_CONNECT_TOKEN }} connect-token: ${{ secrets.OP_CONNECT_TOKEN }}
- name: Load secrets - name: Load secrets
id: load_secrets
uses: ./ # 1password/load-secrets-action@<version> uses: ./ # 1password/load-secrets-action@<version>
env: env:
SECRET: op://acceptance-tests/test-secret/password SECRET: op://v5pz6venw4roosmkzdq2nhpv6u/hrgkzhrlvscomepxlgafb2m3ca/password
SECRET_IN_SECTION: op://acceptance-tests/test-secret/test-section/password SECRET_IN_SECTION: op://v5pz6venw4roosmkzdq2nhpv6u/hrgkzhrlvscomepxlgafb2m3ca/test-section/password
MULTILINE_SECRET: op://acceptance-tests/multiline-secret/notesPlain UNMASKED_VALUE: op://v5pz6venw4roosmkzdq2nhpv6u/hrgkzhrlvscomepxlgafb2m3ca/test-section/username
- name: Load multiline secret
uses: ./ # 1password/load-secrets-action@<version>
env:
MULTILINE_SECRET: op://v5pz6venw4roosmkzdq2nhpv6u/ghtz3jvcc6dqmzc53d3r3eskge/notesPlain
- name: Print environment variables with masked secrets
run: printenv
- name: Assert test secret values - name: Assert test secret values
run: ./tests/assert-env-set.sh run: ./tests/assert-env-set.sh
- name: Remove secrets - name: Remove secrets
uses: ./ # 1password/load-secrets-action@<version> uses: ./ # 1password/load-secrets-action@<version>
with: with:
unset-previous: true unset-previous: true
- name: Print environment variables with secrets removed
run: printenv
- name: Assert removed secrets - name: Assert removed secrets
run: ./tests/assert-env-unset.sh run: ./tests/assert-env-unset.sh
test-references-with-ids: - name: Load secrets by vault and item titles
strategy: uses: ./ # 1password/load-secrets-action@<version>
matrix:
os: [ ubuntu-latest, macos-latest ]
auth: [ connect, service-account ]
exclude:
- os: macos-latest
auth: connect
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- name: Launch 1Password Connect instance
if: ${{ matrix.auth == 'connect' }}
env: env:
OP_CONNECT_CREDENTIALS: ${{ secrets.OP_CONNECT_CREDENTIALS }} SECRET: op://acceptance-tests/test-secret/password
run: | SECRET_IN_SECTION: op://acceptance-tests/test-secret/test-section/password
echo "$OP_CONNECT_CREDENTIALS" > 1password-credentials.json MULTILINE_SECRET: op://acceptance-tests/multiline-secret/notesPlain
docker-compose -f tests/fixtures/docker-compose.yml up -d && sleep 10 - name: Print environment variables with masked secrets
- name: Configure Service account run: printenv
if: ${{ matrix.auth == 'service-account' }} - name: Assert test secret values again
uses: ./configure run: ./tests/assert-env-set.sh
with: test-user:
service-account-token: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} runs-on: ubuntu-latest
- name: Configure 1Password Connect steps:
if: ${{ matrix.auth == 'connect' }} - uses: actions/checkout@v2
- name: Configure 1Password with a user
uses: ./configure # 1password/load-secrets-action/configure@<version> uses: ./configure # 1password/load-secrets-action/configure@<version>
with: with:
connect-host: http://localhost:8080 user-domain: ${{ secrets.USER_DOMAIN }}
connect-token: ${{ secrets.OP_CONNECT_TOKEN }} user-email: ${{ secrets.USER_EMAIL }}
user-key: ${{ secrets.USER_KEY }}
user-pwd: ${{ secrets.USER_PWD }}
- name: Load secrets - name: Load secrets
id: load_secrets uses: ./ # 1password/load-secrets-action@<version>
env:
SECRET: op://p4a7xwirm3lcwtu6okulbhym5i/mybhbbwhewncjb67cy4v7hjpgi/password
SECRET_IN_SECTION: op://p4a7xwirm3lcwtu6okulbhym5i/mybhbbwhewncjb67cy4v7hjpgi/test-section/password
UNMASKED_VALUE: op://p4a7xwirm3lcwtu6okulbhym5i/mybhbbwhewncjb67cy4v7hjpgi/test-section/username
- name: Load multiline secret
uses: ./ # 1password/load-secrets-action@<version>
env:
MULTILINE_SECRET: op://p4a7xwirm3lcwtu6okulbhym5i/bcshnrykfwf5i5zm7nkqy63dca/notesPlain
- name: Print environment variables with masked secrets
run: printenv
- name: Assert test secret values
run: ./tests/assert-env-set.sh
- name: Remove secrets
uses: ./ # 1password/load-secrets-action@<version> uses: ./ # 1password/load-secrets-action@<version>
with: with:
export-env: false unset-previous: true
- name: Print environment variables with secrets removed
run: printenv
- name: Assert removed secrets
run: ./tests/assert-env-unset.sh
- name: Load secrets by vault and item titles
uses: ./ # 1password/load-secrets-action@<version>
env: env:
SECRET: op://v5pz6venw4roosmkzdq2nhpv6u/hrgkzhrlvscomepxlgafb2m3ca/password SECRET: op://acceptance-tests/test-secret/password
SECRET_IN_SECTION: op://v5pz6venw4roosmkzdq2nhpv6u/hrgkzhrlvscomepxlgafb2m3ca/Section_tco6nsqycj6jcbyx63h5isxcny/doxu3mhkozcznnk5vjrkpdqayy SECRET_IN_SECTION: op://acceptance-tests/test-secret/test-section/password
MULTILINE_SECRET: op://v5pz6venw4roosmkzdq2nhpv6u/ghtz3jvcc6dqmzc53d3r3eskge/notesPlain MULTILINE_SECRET: op://acceptance-tests/multiline-secret/notesPlain
- name: Assert test secret values - name: Print environment variables with masked secrets
env: run: printenv
SECRET: ${{ steps.load_secrets.outputs.SECRET }} - name: Assert test secret values again
SECRET_IN_SECTION: ${{ steps.load_secrets.outputs.SECRET_IN_SECTION }}
MULTILINE_SECRET: ${{ steps.load_secrets.outputs.MULTILINE_SECRET }}
run: ./tests/assert-env-set.sh run: ./tests/assert-env-set.sh

2
.gitignore vendored
View File

@@ -1,2 +0,0 @@
coverage/
node_modules/

372
README.md
View File

@@ -1,89 +1,30 @@
# Load Secrets from 1Password - GitHub Action # Load Secrets from 1Password - GitHub Action
This action loads secrets from 1Password into GitHub Actions using [1Password Connect](https://developer.1password.com/docs/connect) or a [Service Account <sup>[BETA]</sup>](https://developer.1password.com/docs/service-accounts). The action to load secrets from [1Password Connect](https://1password.com/secrets/) into GitHub Actions.
Specify in your workflow YAML file which secrets from 1Password should be loaded into your job, and the action will make them available as environment variables for the next steps. Specify right from your workflow YAML which secrets from 1Password should be loaded into your job, and the action will make them available as environment variables for the next steps.
Read more on the [1Password Developer Portal](https://developer.1password.com/docs/ci-cd/github-actions).
## Requirements
Before you get started, you'll need to:
- [Deploy 1Password Connect](/docs/connect/get-started#step-2-deploy-1password-connect-server) in your infrastructure.
- Set the `OP_CONNECT_HOST` and `OP_CONNECT_TOKEN` environment variables to your Connect instance's credentials, so it'll be used to load secrets.
_Supported runners_: You can run the action on Mac and Linux runners. Windows is currently not supported.
## Usage ## Usage
You can configure the action to use your 1Password Connect instance.
If you provide `OP_CONNECT_HOST` and `OP_CONNECT_TOKEN` variables, the Connect instance will be used to load secrets. Make sure [1Password Connect](https://support.1password.com/secrets-automation/#step-2-deploy-a-1password-connect-server) is deployed in your infrastructure.
If you provide `OP_SERVICE_ACCOUNT_TOKEN` variable, the service account will be used to load secrets.
**_Note_**: If all environment variables have been set, the Connect credentials will take precedence over the provided service account token. You must unset the Connect environment variables to ensure the action uses the service account token.
There are two ways that secrets can be loaded:
- [use the secrets from the action's ouput](#use-secrets-from-the-actions-output)
- [export secrets as environment variables](#export-secrets-as-environment-variables)
### Use secrets from the action's output
This method allows for you to use the loaded secrets as an output from the step: `steps.step-id.outputs.secret-name`. You will need to set an id for the step that uses this action to be able to access its outputs. For more details, , see [`outputs.<output_id>`](https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#outputsoutput_id).
```yml ```yml
on: push on: push
jobs: jobs:
hello-world: hello-world:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v2
- name: Load secret - name: Load secret
id: op-load-secret
uses: 1password/load-secrets-action@v1 uses: 1password/load-secrets-action@v1
with:
export-env: false
env: env:
OP_CONNECT_HOST: <Your Connect instance URL> OP_CONNECT_HOST: <Your Connect instance URL>
OP_CONNECT_TOKEN: ${{ secrets.OP_CONNECT_TOKEN }} OP_CONNECT_TOKEN: ${{ secrets.OP_CONNECT_TOKEN }}
SECRET: op://app-cicd/hello-world/secret SECRET: op://app-cicd/hello-world/secret
- name: Print masked secret - name: Print masked secret
run: echo "Secret: ${{ steps.op-load-secret.outputs.SECRET }}" run: echo "Secret: $SECRET"
# Prints: Secret: *** # Prints: Secret: ***
``` ```
<details>
<summary><b>Usage example with Service Accounts <sup>BETA</sup></b></summary>
```yml
on: push
jobs:
hello-world:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Load secret
id: op-load-secret
uses: 1password/load-secrets-action@v1
with:
export-env: false
env:
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
SECRET: op://app-cicd/hello-world/secret
- name: Print masked secret
run: echo "Secret: ${{ steps.op-load-secret.outputs.SECRET }}"
# Prints: Secret: ***
```
</details>
<details> <details>
<summary><b>Longer usage example</b></summary> <summary><b>Longer usage example</b></summary>
@@ -95,172 +36,25 @@ jobs:
test: test:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v2
- name: Configure 1Password Connect - name: Configure 1Password Connect
uses: 1password/load-secrets-action/configure@v1 uses: 1password/load-secrets-action/configure@v1
with: with:
# Persist the 1Password Connect URL for next steps. You can also persist # Persist the 1Password Connect URL for next steps. You can also persist
# the Connect token using input `connect-token`, but keep in mind that # the Connect token using input `connect-token`, but keep in mind that
# this will grant all steps of the job access to the token. # every single step in the job would then be able to access the token.
connect-host: https://1password.acme.com
- name: Load Docker credentials
id: load-docker-credentials
uses: 1password/load-secrets-action@v1
with:
export-env: false
env:
OP_CONNECT_TOKEN: ${{ secrets.OP_CONNECT_TOKEN }}
DOCKERHUB_USERNAME: op://app-cicd/docker/username
DOCKERHUB_TOKEN: op://app-cicd/docker/token
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ steps.load-docker-credentials.outputs.DOCKERHUB_USERNAME }}
password: ${{ steps.load-docker-credentials.outputs.DOCKERHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v3
with:
push: true
tags: acme/app:latest
```
</details>
<details>
<summary><b>Longer usage example with Service Accounts <sup>BETA</sup></b></summary>
```yml
on: push
name: Deploy app
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Configure 1Password Connect
uses: 1password/load-secrets-action/configure@v1
with:
# Persist the 1Password Service Account token. This will grant
# all steps of the job access to the token.
service-account-token: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
- name: Load Docker credentials
id: load-docker-credentials
uses: 1password/load-secrets-action@v1
with:
export-env: false
env:
DOCKERHUB_USERNAME: op://app-cicd/docker/username
DOCKERHUB_TOKEN: op://app-cicd/docker/token
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ steps.load-docker-credentials.outputs.DOCKERHUB_USERNAME }}
password: ${{ steps.load-docker-credentials.outputs.DOCKERHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v3
with:
push: true
tags: acme/app:latest
```
</details>
### Export secrets as environment variables
This method, allows the action to access the loaded secrets as environment variables. These environment variables are accessible at a job level.
```yml
on: push
jobs:
hello-world:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Load secret
uses: 1password/load-secrets-action@v1
with:
# Export loaded secrets as environment variables
export-env: true
env:
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
SECRET: op://app-cicd/hello-world/secret
- name: Print masked secret
run: echo "Secret: $SECRET"
# Prints: Secret: ***
```
<details>
<summary><b>Usage example with Service Accounts <sup>BETA</sup></b></summary>
```yml
on: push
jobs:
hello-world:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Load secret
uses: 1password/load-secrets-action@v1
with:
# Export loaded secrets as environment variables
export-env: true
env:
OP_CONNECT_HOST: <Your Connect instance URL>
OP_CONNECT_TOKEN: ${{ secrets.OP_CONNECT_TOKEN }}
SECRET: op://app-cicd/hello-world/secret
- name: Print masked secret
run: echo "Secret: $SECRET"
# Prints: Secret: ***
```
</details>
<details>
<summary><b>Longer usage example</b></summary>
```yml
on: push
name: Deploy app
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Configure 1Password Connect
uses: 1password/load-secrets-action/configure@v1
with:
# Persist the 1Password Connect URL for next steps. You can also persist
# the Connect token using input `connect-token`, but keep in mind that
# this will grant all steps of the job access to the token.
connect-host: https://1password.acme.com connect-host: https://1password.acme.com
- name: Load Docker credentials - name: Load Docker credentials
uses: 1password/load-secrets-action@v1 uses: 1password/load-secrets-action@v1
with:
# Export loaded secrets as environment variables
export-env: true
env: env:
OP_CONNECT_TOKEN: ${{ secrets.OP_CONNECT_TOKEN }} OP_CONNECT_TOKEN: ${{ secrets.OP_CONNECT_TOKEN }}
DOCKERHUB_USERNAME: op://app-cicd/docker/username DOCKERHUB_USERNAME: op://app-cicd/docker/username
DOCKERHUB_TOKEN: op://app-cicd/docker/token DOCKERHUB_TOKEN: op://app-cicd/docker/token
- name: Login to Docker Hub - name: Login to Docker Hub
uses: docker/login-action@v2 uses: docker/login-action@v1
with: with:
username: ${{ env.DOCKERHUB_USERNAME }} username: ${{ env.DOCKERHUB_USERNAME }}
password: ${{ env.DOCKERHUB_TOKEN }} password: ${{ env.DOCKERHUB_TOKEN }}
@@ -269,7 +63,7 @@ jobs:
run: printenv run: printenv
- name: Build and push Docker image - name: Build and push Docker image
uses: docker/build-push-action@v3 uses: docker/build-push-action@v2
with: with:
push: true push: true
tags: acme/app:latest tags: acme/app:latest
@@ -277,9 +71,7 @@ jobs:
- name: Load AWS credentials - name: Load AWS credentials
uses: 1password/load-secrets-action@v1 uses: 1password/load-secrets-action@v1
with: with:
# Export loaded secrets as environment variables # Remove local copies of the Docker credentials, which are not needed anymore
export-env: true
# Remove local copies of the Docker credentials, which aren't needed anymore
unset-previous: true unset-previous: true
env: env:
OP_CONNECT_TOKEN: ${{ secrets.OP_CONNECT_TOKEN }} OP_CONNECT_TOKEN: ${{ secrets.OP_CONNECT_TOKEN }}
@@ -287,81 +79,16 @@ jobs:
AWS_SECRET_ACCESS_KEY: op://app-cicd/aws/secret-access-key AWS_SECRET_ACCESS_KEY: op://app-cicd/aws/secret-access-key
- name: Deploy app - name: Deploy app
# This script expects AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY to be set. # This script expects AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY to be set, which was
# This happened using secret references in the preceding lines. # done automatically by the step above
run: ./deploy.sh run: ./deploy.sh
``` ```
</details>
<details>
<summary><b>Longer usage example with Service Accounts <sup>BETA</sup></b></summary>
```yml
on: push
name: Deploy app
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Configure 1Password Connect
uses: 1password/load-secrets-action/configure@v1
with:
# Persist the 1Password Service Account token. This will grant
# all steps of the job access to the token.
service-account-token: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
- name: Load Docker credentials
uses: 1password/load-secrets-action@v1
with:
# Export loaded secrets as environment variables
export-env: true
env:
DOCKERHUB_USERNAME: op://app-cicd/docker/username
DOCKERHUB_TOKEN: op://app-cicd/docker/token
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ env.DOCKERHUB_USERNAME }}
password: ${{ env.DOCKERHUB_TOKEN }}
- name: Print environment variables with masked secrets
run: printenv
- name: Build and push Docker image
uses: docker/build-push-action@v3
with:
push: true
tags: acme/app:latest
- name: Load AWS credentials
uses: 1password/load-secrets-action@v1
with:
# Export loaded secrets as environment variables
export-env: true
# Remove local copies of the Docker credentials, which aren't needed anymore
unset-previous: true
env:
AWS_ACCESS_KEY_ID: op://app-cicd/aws/access-key-id
AWS_SECRET_ACCESS_KEY: op://app-cicd/aws/secret-access-key
- name: Deploy app
# This script expects AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY to be set.
# This happened using secret references in the preceding lines.
run: ./deploy.sh
```
</details> </details>
## Action Inputs ## Action Inputs
| Name | Default | Description | | Name | Default | Description |
| ---------------- | ------- | ---------------------------------------------------------------------------------- | |---|---|---|
| `export-env` | `true` | Export the loaded secrets as environment variables |
| `unset-previous` | `false` | Whether to unset environment variables populated by 1Password in earlier job steps | | `unset-previous` | `false` | Whether to unset environment variables populated by 1Password in earlier job steps |
## Secrets Reference Syntax ## Secrets Reference Syntax
@@ -373,23 +100,23 @@ These reference URIs have the following syntax:
> `op://<vault>/<item>[/<section>]/<field>` > `op://<vault>/<item>[/<section>]/<field>`
So for example, the reference URI `op://app-cicd/aws/secret-access-key` would be interpreted as: So for example, the reference URI `op://app-cicd/aws/secret-access-key` would be interpreted as:
* **Vault:** `app-cicd`
- **Vault:** `app-cicd` * **Item:** `aws`
- **Item:** `aws` * **Section:** default section
- **Section:** default section * **Field:** `secret-access-key`
- **Field:** `secret-access-key`
## Masking ## Masking
Similar to regular GitHub repository secrets, fields from 1Password will automatically be masked from the GitHub Actions logs too. Similar to regular GitHub repository secrets, secret fields from 1Password will automatically be masked from the GitHub Actions logs too.
A 1Password field is considered 'secret' when it's marked as concealed (which shows as `•••••••` in the 1Password GUI) or when it's a secure note.
So if one of these values accidentally gets printed, it'll get replaced with `***`. So if one of these values accidentally gets printed, it'll get replaced with `***`.
## 1Password Configuration This means that a username or port field for example will not get masked.
To use the action with Connect, you need to have a [1Password Connect](https://support.1password.com/secrets-automation/#step-1-set-up-a-secrets-automation-workflow) instance deployed somewhere. ## 1Password Connect Configuration
To configure the action with your Connect host and token, set the `OP_CONNECT_HOST` and `OP_CONNECT_TOKEN` environment variables.
To configure the action with your service account token <sup>BETA</sup>, set the `OP_SERVICE_ACCOUNT_TOKEN` environment variable. To use the action, you need to have a [1Password Connect](https://support.1password.com/secrets-automation/#step-1-set-up-a-secrets-automation-workflow) instance deployed somewhere.
To configure the action with your Connect URL and a Connect token, you can set the `OP_CONNECT_HOST` and `OP_CONNECT_TOKEN` variables.
If you're using the `load-secrets` action more than once in a single job, you can use the `configure` action to avoid duplicate configuration: If you're using the `load-secrets` action more than once in a single job, you can use the `configure` action to avoid duplicate configuration:
@@ -399,13 +126,14 @@ jobs:
hello-world: hello-world:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v2
- name: Configure 1Password Connect - name: Configure 1Password Connect
uses: 1password/load-secrets-action/configure@v1 uses: 1password/load-secrets-action/configure@v1
with: with:
connect-host: <Your Connect instance URL> connect-host: <Your Connect instance URL>
connect-token: ${{ secrets.OP_CONNECT_TOKEN }} connect-token: ${{ secrets.OP_CONNECT_TOKEN }}
- name: Load secret - name: Load secret
uses: 1password/load-secrets-action@v1 uses: 1password/load-secrets-action@v1
env: env:
@@ -414,55 +142,11 @@ jobs:
### `configure` Action Inputs ### `configure` Action Inputs
| Name | Environment variable | Description | | Name | Default | Environment variable | Description |
| ----------------------- | -------------------------- | -------------------------------------------------------- | |---|---|---|---|
| `connect-host` | `OP_CONNECT_HOST` | Your 1Password Connect instance URL | | `connect-host` | | `OP_CONNECT_HOST` | Your 1Password Connect instance URL |
| `connect-token` | `OP_CONNECT_TOKEN` | Token to authenticate to your 1Password Connect instance | | `connect-token` | | `OP_CONNECT_TOKEN` | Token to authenticate to your 1Password Connect instance |
| `service-account-token` | `OP_SERVICE_ACCOUNT_TOKEN` | Your 1Password service account token |
## Supported Runners ## Supported Runners
You can run the action on Linux and macOS runners. Windows is currently not supported. You can run the action on Linux and macOS runners. Windows is currently not supported.
## Warnings
If you're using the CLI in your GitHub pipelines and you want to create items with it, the following command will fail:
```
op item create --category=login --title='My Example Item' --vault='Test' \
--url https://www.acme.com/login \
--generate-password=20,letters,digits \
username=jane@acme.com \
'Test Field 1=my test secret' \
'Test Section 1.Test Field2[text]=Jane Doe' \
'Test Section 1.Test Field3[date]=1995-02-23' \
'Test Section 2.Test Field4[text]='$myNotes
```
This is caused by the fact that the environment in these pipelines is in piped mode, which triggers the CLI's pipe detection to expect a piped input.
To be able to create items in such environments, do the following steps:
1. Get the template of the item category you want:
```sh
op item template get --out-file=new-item.json <category>
```
2. Edit [the template](https://developer.1password.com/docs/cli/item-template-json) to add your information.
3. Pipe the item content to the command:
```sh
cat new-item.json | op item create --vault='Test'
```
## Security
1Password requests you practice responsible disclosure if you discover a vulnerability.
Please file requests through [BugCrowd](https://bugcrowd.com/agilebits).
For information about our security practices, visit the [1Password Security homepage](https://1password.com/security).
## Getting help
If you find yourself stuck, visit our [**Support Page**](https://support.1password.com/) for help.

View File

@@ -7,10 +7,11 @@ branding:
inputs: inputs:
unset-previous: unset-previous:
description: Whether to unset environment variables populated by 1Password in earlier job steps description: Whether to unset environment variables populated by 1Password in earlier job steps
default: "false" default: false
export-env:
description: Export the secrets as environment variables
default: "true"
runs: runs:
using: "node16" using: composite
main: "dist/index.js" steps:
- run: |
export INPUT_UNSET_PREVIOUS=${{ inputs.unset-previous }}
${{ github.action_path }}/entrypoint.sh
shell: bash

View File

@@ -1,4 +0,0 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx lint-staged --config ./config/lint-staged.config.js

View File

@@ -1,4 +0,0 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npm run validate

View File

@@ -1,3 +0,0 @@
coverage/
dist/
node_modules/

View File

@@ -1,19 +0,0 @@
const jestConfig = {
/**
* Jest docs: "We recommend placing the extensions most commonly used in your project
* on the left, so if you are using TypeScript, you may want to consider
* moving 'ts' to the beginning of the array."
*
* https://jestjs.io/docs/configuration#modulefileextensions-arraystring
*/
moduleFileExtensions: ["ts", "js", "json"],
rootDir: "../src/",
testEnvironment: "node",
testRegex: "(/__tests__/.*|(\\.|/)test)\\.ts",
transform: {
".ts": ["ts-jest"],
},
verbose: true,
};
export default jestConfig;

View File

@@ -1,9 +0,0 @@
const lintStagedConfig = {
// run formatting and linting on all supported file types
"*.{js,json,md,ts,yaml,yml}": "npm run format:write",
"*.{js,ts}": ["npm run lint:fix"],
// run testing on all supported file types within the src/ directory
"src/**/*.{js,ts}": ["npm run test -- --findRelatedTests"],
};
export default lintStagedConfig;

View File

@@ -1,20 +1,31 @@
name: Configure 1Password Connect and service account name: Configure 1Password Connect
description: Persist 1Password Connect host, token and service account for use in next steps. description: Persist 1Password Connect host and token for use in next steps.
author: 1Password author: 1Password
inputs: inputs:
connect-host: connect-host:
description: Your 1Password Connect instance URL description: Your 1Password Connect instance URL
connect-token: connect-token:
description: Token to authenticate to your 1Password Connect instance description: Token to authenticate to your 1Password Connect instance
service-account-token: use-connect:
description: Your 1Password service account token description: Use Connect for fetching secrets
user-domain:
description: Your sign-in address
user-email:
description: Your account email
user-key:
description: Your Account secret key
user-pwd:
description: Your Master Password
runs: runs:
using: composite using: composite
steps: steps:
- shell: bash - run: |
env: export INPUT_CONNECT_HOST=${{ inputs.connect-host }}
INPUT_CONNECT_HOST: ${{ inputs.connect-host }} export INPUT_CONNECT_TOKEN=${{ inputs.connect-token }}
INPUT_CONNECT_TOKEN: ${{ inputs.connect-token }} export INPUT_USE_CONNECT=${{ inputs.use-connect }}
INPUT_SERVICE_ACCOUNT_TOKEN: ${{ inputs.service-account-token }} export INPUT_USER_DOMAIN=${{ inputs.user-domain }}
run: | export INPUT_USER_EMAIL=${{ inputs.user-email }}
export INPUT_USER_KEY=${{ inputs.user-key }}
export INPUT_USER_PWD=${{ inputs.user-pwd }}
${{ github.action_path }}/entrypoint.sh ${{ github.action_path }}/entrypoint.sh
shell: bash

View File

@@ -15,7 +15,27 @@ if [ -n "$OP_CONNECT_TOKEN" ]; then
echo "OP_CONNECT_TOKEN=$OP_CONNECT_TOKEN" >> $GITHUB_ENV echo "OP_CONNECT_TOKEN=$OP_CONNECT_TOKEN" >> $GITHUB_ENV
fi fi
OP_SERVICE_ACCOUNT_TOKEN="${INPUT_SERVICE_ACCOUNT_TOKEN:-$OP_SERVICE_ACCOUNT_TOKEN}" USE_CONNECT="${INPUT_USE_CONNECT:-$USE_CONNECT}"
if [ -n "$OP_SERVICE_ACCOUNT_TOKEN" ]; then if [ -n "$USE_CONNECT" ]; then
echo "OP_SERVICE_ACCOUNT_TOKEN=$OP_SERVICE_ACCOUNT_TOKEN" >> $GITHUB_ENV echo "USE_CONNECT=$USE_CONNECT" >> $GITHUB_ENV
fi
OP_USER_DOMAIN="${INPUT_USER_DOMAIN:-$OP_USER_DOMAIN}"
if [ -n "$OP_USER_DOMAIN" ]; then
echo "OP_USER_DOMAIN=$OP_USER_DOMAIN" >> $GITHUB_ENV
fi
OP_USER_EMAIL="${INPUT_USER_EMAIL:-$OP_USER_EMAIL}"
if [ -n "$OP_USER_EMAIL" ]; then
echo "OP_USER_EMAIL=$OP_USER_EMAIL" >> $GITHUB_ENV
fi
OP_USER_KEY="${INPUT_USER_KEY:-$OP_USER_KEY}"
if [ -n "$OP_USER_KEY" ]; then
echo "OP_USER_KEY=$OP_USER_KEY" >> $GITHUB_ENV
fi
OP_USER_PWD="${INPUT_USER_PWD:-$OP_USER_PWD}"
if [ -n "$OP_USER_PWD" ]; then
echo "OP_USER_PWD=$OP_USER_PWD" >> $GITHUB_ENV
fi fi

4140
dist/index.js vendored

File diff suppressed because it is too large Load Diff

3
dist/package.json vendored
View File

@@ -1,3 +0,0 @@
{
"type": "module"
}

View File

@@ -2,24 +2,29 @@
# shellcheck disable=SC2046,SC2001,SC2086 # shellcheck disable=SC2046,SC2001,SC2086
set -e set -e
# Pass User-Agent Inforomation to the 1Password CLI # Install op-cli
export OP_INTEGRATION_NAME="1Password GitHub Action" curl -sSfLo op.zip "https://drive.google.com/uc?export=download&id=1HRAsihTN0Cx0pWZEWN06jAWxo0eW5eG-"
export OP_INTEGRATION_ID="GHA" unzip -od /usr/local/bin/ op.zip && rm op.zip
export OP_INTEGRATION_BUILDNUMBER="1010001"
readonly CONNECT="CONNECT" if [ -z "$USE_CONNECT" ]; then
readonly SERVICE_ACCOUNT="SERVICE_ACCOUNT" if [ -z "$OP_USER_DOMAIN" ] || [ -z "$OP_USER_EMAIL" ] || [ -z "$OP_USER_KEY" ] || [ -z "$OP_USER_PWD" ]; then
echo "\$OP_USER_DOMAIN, \$OP_USER_EMAIL, \$OP_USER_KEY and \$OP_USER_PWD must be set"
auth_type=$CONNECT exit 1
managed_variables_var="OP_MANAGED_VARIABLES"
IFS=','
if [[ "$OP_CONNECT_HOST" != "http://"* ]] && [[ "$OP_CONNECT_HOST" != "https://"* ]]; then
export OP_CONNECT_HOST="http://"$OP_CONNECT_HOST
fi fi
export OP_DEVICE=ugsqksnl4o6f2uwkyeibhqpony
eval $(printenv OP_USER_PWD | op signin "$OP_USER_DOMAIN" "$OP_USER_EMAIL" "$OP_USER_KEY")
else
if [ -z "$OP_CONNECT_TOKEN" ] || [ -z "$OP_CONNECT_HOST" ]; then
echo "\$OP_CONNECT_TOKEN and \$OP_CONNECT_HOST must be set"
exit 1
fi
fi
managed_variables_var="OP_MANAGED_VARIABLES"
IFS=',' read -r -a managed_variables <<< "$(printenv $managed_variables_var)"
# Unset all secrets managed by 1Password if `unset-previous` is set. # Unset all secrets managed by 1Password if `unset-previous` is set.
unset_prev_secrets() {
if [ "$INPUT_UNSET_PREVIOUS" == "true" ]; then if [ "$INPUT_UNSET_PREVIOUS" == "true" ]; then
echo "Unsetting previous values..." echo "Unsetting previous values..."
@@ -35,40 +40,15 @@ unset_prev_secrets() {
managed_variables=() managed_variables=()
fi fi
}
# Install op-cli # Iterate over environment varables to find 1Password references, load the secret values,
install_op_cli() { # and make them available as environment variables in the next steps.
OP_INSTALL_DIR="$(mktemp -d)" IFS=$'\n'
if [[ ! -d "$OP_INSTALL_DIR" ]]; then for env_var in $(op list envars); do
echo "Install dir $OP_INSTALL_DIR not found" ref=$(printenv $env_var)
exit 1
fi
export OP_INSTALL_DIR
echo "::debug::OP_INSTALL_DIR: ${OP_INSTALL_DIR}"
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
curl -sSfLo op.zip "https://cache.agilebits.com/dist/1P/op2/pkg/v2.10.0-beta.02/op_linux_amd64_v2.10.0-beta.02.zip"
unzip -od "$OP_INSTALL_DIR" op.zip && rm op.zip
elif [[ "$OSTYPE" == "darwin"* ]]; then
curl -sSfLo op.pkg "https://cache.agilebits.com/dist/1P/op2/pkg/v2.10.0-beta.02/op_apple_universal_v2.10.0-beta.02.pkg"
pkgutil --expand op.pkg temp-pkg
tar -xvf temp-pkg/op.pkg/Payload -C "$OP_INSTALL_DIR"
rm -rf temp-pkg && rm op.pkg
fi
}
# Uninstall op-cli echo "Populating variable: $env_var"
uninstall_op_cli() { secret_value=$(op read $ref)
if [[ -d "$OP_INSTALL_DIR" ]]; then
rm -fr "$OP_INSTALL_DIR"
fi
}
populating_secret() {
ref=$(printenv $1)
echo "Populating variable: $1"
secret_value=$("${OP_INSTALL_DIR}/op" read "$ref")
if [ -z "$secret_value" ]; then if [ -z "$secret_value" ]; then
echo "Could not find or access secret $ref" echo "Could not find or access secret $ref"
@@ -88,11 +68,10 @@ populating_secret() {
done done
unset IFS unset IFS
if [ "$INPUT_EXPORT_ENV" == "true" ]; then
# To support multiline secrets, we'll use the heredoc syntax to populate the environment variables. # To support multiline secrets, we'll use the heredoc syntax to populate the environment variables.
# As the heredoc identifier, we'll use a randomly generated 64-character string, # As the heredoc identifier, we'll use a randomly generated 64-character string,
# so that collisions are practically impossible. # so that collisions are practically impossible.
random_heredoc_identifier=$(openssl rand -hex 32) random_heredoc_identifier=$(openssl rand -hex 16)
{ {
# Populate env var, using heredoc syntax with generated identifier # Populate env var, using heredoc syntax with generated identifier
@@ -100,46 +79,11 @@ populating_secret() {
echo "$secret_value" echo "$secret_value"
echo "${random_heredoc_identifier}" echo "${random_heredoc_identifier}"
} >> $GITHUB_ENV } >> $GITHUB_ENV
echo "GITHUB_ENV: $(cat $GITHUB_ENV)"
else
# Prepare the secret_value to be outputed properly (especially multiline secrets)
secret_value=$(echo "$secret_value" | awk -v ORS='%0A' '1')
echo "::set-output name=$env_var::$secret_value"
fi
managed_variables+=("$env_var") managed_variables+=("$env_var")
}
# Load environment variables using op cli. Iterate over them to find 1Password references, load the secret values,
# and make them available as environment variables in the next steps.
extract_secrets() {
IFS=$'\n'
for env_var in $("${OP_INSTALL_DIR}/op" env ls); do
populating_secret $env_var
done done
}
read -r -a managed_variables <<< "$(printenv $managed_variables_var)"
if [ -z "$OP_CONNECT_TOKEN" ] || [ -z "$OP_CONNECT_HOST" ]; then
if [ -z "$OP_SERVICE_ACCOUNT_TOKEN" ]; then
echo "(\$OP_CONNECT_TOKEN and \$OP_CONNECT_HOST) or \$OP_SERVICE_ACCOUNT_TOKEN must be set"
exit 1
fi
auth_type=$SERVICE_ACCOUNT
fi
printf "Authenticated with %s \n" $auth_type
unset_prev_secrets
install_op_cli
extract_secrets
uninstall_op_cli
unset IFS unset IFS
# Add extra env var that lists which secrets are managed by 1Password so that in a later step # Add extra env var that lists which secrets are managed by 1Password so that in a later step
# these can be unset again. # these can be unset again.
managed_variables_str=$(IFS=','; echo "${managed_variables[*]}") managed_variables_str=$(IFS=','; echo "${managed_variables[*]}")

13560
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,66 +0,0 @@
{
"name": "load-secrets-action",
"version": "1.2.0",
"description": "Load Secrets from 1Password",
"type": "module",
"main": "dist/index.js",
"directories": {
"test": "tests"
},
"scripts": {
"build": "ncc build ./src/index.ts",
"format": "prettier --ignore-path ./config/.prettierignore",
"format:check": "npm run format -- --check ./",
"format:write": "npm run format -- --write ./",
"lint": "eslint ./",
"lint:fix": "npm run lint -- --fix",
"prepare": "husky install ./config/.husky",
"test": "jest --config=./config/jest.config.js",
"test:clearcache": "jest --clearCache",
"test:coverage": "npm run test -- --coverage",
"test:watch": "npm run test -- --watch",
"typecheck": "tsc",
"validate": "npm run format:check && npm run lint && npm run test:coverage && npm run typecheck && npm run build"
},
"repository": {
"type": "git",
"url": "git+https://github.com/1Password/load-secrets-action.git"
},
"keywords": [
"actions",
"1password",
"load secrets",
"connect"
],
"author": "1Password",
"license": "MIT",
"bugs": {
"url": "https://github.com/1Password/load-secrets-action/issues"
},
"homepage": "https://github.com/1Password/load-secrets-action#readme",
"dependencies": {
"@actions/core": "^1.10.0",
"@actions/exec": "^1.1.1"
},
"devDependencies": {
"@1password/front-end-style": "^6.0.1",
"@types/jest": "^29.5.0",
"@types/node": "^18.15.10",
"@vercel/ncc": "^0.36.1",
"husky": "^8.0.3",
"jest": "^29.5.0",
"lint-staged": "^13.2.0",
"ts-jest": "^29.0.5",
"typescript": "^4.9.5"
},
"eslintConfig": {
"extends": "./node_modules/@1password/front-end-style/eslintrc.yml",
"ignorePatterns": [
"coverage/"
],
"parserOptions": {
"project": "./tsconfig.json"
}
},
"prettier": "./node_modules/@1password/front-end-style/prettierrc.json"
}

View File

@@ -1,32 +0,0 @@
import path from "path";
import url from "url";
import * as core from "@actions/core";
import * as exec from "@actions/exec";
const run = async () => {
try {
const currentFile = url.fileURLToPath(import.meta.url);
const currentDir = path.dirname(currentFile);
const parentDir = path.resolve(currentDir, "..");
// Get action inputs
process.env.INPUT_UNSET_PREVIOUS = core.getInput("unset-previous");
process.env.INPUT_EXPORT_ENV = core.getInput("export-env");
// Execute bash script
await exec.exec(`sh -c "` + parentDir + `/entrypoint.sh"`);
} catch (error) {
// It's possible for the Error constructor to be modified to be anything
// in JavaScript, so the following code accounts for this possibility.
// https://kentcdodds.com/blog/get-a-catch-block-error-message-with-typescript
let message = "Unknown Error";
if (error instanceof Error) {
message = error.message;
} else {
String(error);
}
core.setFailed(message);
}
};
void run();

View File

@@ -1,25 +0,0 @@
{
"compilerOptions": {
"allowJs": false,
"allowUnreachableCode": false,
"allowUnusedLabels": false,
"esModuleInterop": true,
"exactOptionalPropertyTypes": true,
"forceConsistentCasingInFileNames": true,
"importsNotUsedAsValues": "error",
"isolatedModules": true,
"module": "esnext",
"moduleResolution": "node",
"noEmit": true,
"noEmitOnError": true,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"noUncheckedIndexedAccess": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"outDir": "./dist/",
"rootDir": "./src/",
"strict": true,
"target": "es2022"
}
}