> For the complete documentation index, see [llms.txt](https://docs.p0.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.p0.dev/integrations/resource-integrations/docker.md).

# Docker Hub

Install P0 for a Docker Hub organization so people can request just-in-time member, editor or owner roles in it.

This page documents how to install P0 for a Docker Hub organization so people can request a just-in-time member, editor or owner role in it. When a request is approved, P0 changes the role of someone who's already in the organization, and puts their previous role back when access ends. Someone who isn't in the organization gets an invite with the requested role. When their access ends, P0 cancels any pending invite to their email, and removes them from the organization if they joined.

{% hint style="info" %}
The Docker Hub integration is in beta. Contact P0 to enable it for your organization. Functionality and configuration may change.
{% endhint %}

## How it works

* **You create the organization access token.** P0 acts on your organization with an organization access token that an owner of the organization generates. The token can edit members and invites, and has no repository scopes.
* **The token stays in your cloud.** You store the token in AWS Secrets Manager or Google Secret Manager, under a name you choose. P0 never reads the token. The connector does.
* **The connector runs in your cloud.** You deploy P0's connector on AWS Lambda or Google Cloud Run, from Terraform that the installer generates. P0 invokes the connector to read and change roles, and the connector calls Docker Hub with your token. One connector can serve many organizations.
* **You choose the email domains the connector acts for.** The connector's deployment carries an allowed domains pattern that only you can change. The connector gives no role to anyone whose email domain is outside it.
* **P0 matches people by email.** P0 looks for a member of the organization whose Docker email is the email the requestor signs in to P0 with. A requestor never names a Docker account.
* **An approver can tell an invite from a role change.** A request shows **Docker username when requested**, the requestor's Docker username in the organization when they made the request. A request without one is for someone P0 will invite. P0 looks the requestor up again after approval, and that lookup decides: someone who joined in between gets a role change instead of an invite.

P0 changes organization roles only. It doesn't add anyone to a team, and it doesn't change a team's access to a repository.

For what happens when access ends, see [Requesting Docker Hub access](/integrations/resource-integrations/docker/requesting-access.md#when-your-access-ends).

## Before you begin

You need:

* **A Docker Team or Business plan.** Organization access tokens need one.
* **An owner of the Docker Hub organization**, to create the organization access token.
* **Each member's Docker email equal to their P0 email.** P0 matches a requestor to a member of the organization by the email they sign in to P0 with. If a member's Docker account uses another email, P0 doesn't find them in the organization. It invites them at their P0 email instead, or, if Docker Hub knows that address as an account already in the organization, the request fails after approval. Have members set their Docker email to that address before they request access, and keep it until their access ends.
* [**Terraform**](https://developer.hashicorp.com/terraform/install), to deploy the connector and to let it read the token.

You deploy the connector on AWS Lambda or on Google Cloud Run, and store the token in the same cloud's secret manager. The secret can be in another account or project than the connector's.

For a connector on AWS Lambda, you also need:

* [**AWS IAM management**](/integrations/resource-integrations/aws.md#setting-up-aws-iam-management) **for the account the connector runs in.** P0 invokes the connector through it.
* [**AWS Secrets Manager**](/integrations/resource-integrations/aws-secrets-manager.md) **integration for the account that holds the token.** The installer lists only the accounts where it's installed. That account needs [AWS IAM management](/integrations/resource-integrations/aws.md#setting-up-aws-iam-management) installed first.
* **Terraform 1.4 or later, run where Docker and the AWS CLI are installed**, with the AWS CLI signed in to the connector's account. Applying the connector's Terraform copies P0's connector image from Docker Hub into Amazon ECR.
* **Commercial AWS regions** for the connector and the token's secret. The Docker Hub integration doesn't support AWS GovCloud, China, ISO or European Sovereign Cloud regions.

For a connector on Google Cloud Run, you also need:

* [**Google Cloud IAM management**](/integrations/resource-integrations/google-cloud.md#install-iam-management-on-each-project) **for the project the connector runs in.** P0 invokes the connector through it.
* [**Google Secret Manager**](/integrations/resource-integrations/google-secret-manager.md) **integration for the project that holds the token.** The installer lists only the projects where it's installed.
* **The IAM, Cloud Run and Secret Manager APIs** enabled in the connector's project: `iam.googleapis.com`, `run.googleapis.com` and `secretmanager.googleapis.com`.
* **The `gcloud` CLI**, to store the token.

## Install Docker Hub

You install a connector first, then each organization it serves. One connector can serve many organizations, so most installs need one connector.

1. Open the P0 dashboard at [p0.app](https://p0.app) and log in to your organization.
2. In the sidebar, click **Integrations**. Under **Resources**, click **Docker Hub**.

Docker Hub has two components. You install the **Connector** component in steps 1 to 3. You install the **Organization** component in steps 4 to 8, which you repeat for every Docker Hub organization that P0 should manage.

### Step 1: Add a connector

You can't change where a connector runs once you add it, so decide its cloud, its account or project, its region and its name now.

1. Select the **Connector** component, and click **Add connector**. The form is titled **Installing a new connector**.
2. In **Connector ID**, enter a name for this connector in P0, such as `prod`. Each organization you add picks its connector by this ID.
3. In **Connector hosting**, choose **AWS Lambda** or **Google Cloud Run**, and fill in its fields. The list of accounts or projects selects its first entry for you, so check it. If the list is empty, install the integration it names first. See [Before you begin](#before-you-begin).

For **AWS Lambda**:

| Field                | What to enter                                                                                                                                                                 |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **AWS account ID**   | The account the connector runs in. The list shows the accounts where P0's AWS IAM management is installed.                                                                    |
| **Connector name**   | The name of the connector's Lambda function, 2 to 64 characters. The connector's Terraform creates the function, and an IAM role of the same name that the connector runs as. |
| **Connector region** | An AWS region, such as `us-west-2`                                                                                                                                            |

For **Google Cloud Run**:

| Field                | What to enter                                                                                                                                                                        |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **GCP project ID**   | The project the connector runs in. The list shows the projects where P0's Google Cloud IAM management is installed.                                                                  |
| **Connector name**   | The name of the connector's Cloud Run service, 6 to 30 characters. The connector's Terraform creates the service, and a service account of the same name that the connector runs as. |
| **Connector region** | A Google Cloud region, such as `us-west1`                                                                                                                                            |

You can't choose **Google Cloud Run** until you install P0's Google Cloud IAM management. P0 says why under the option, and in place of a Cloud Run connector's Terraform: `P0 invokes a connector on Cloud Run as the service account of its Google Cloud integration, which isn't installed. Install P0's Google Cloud IAM management for the connector's project first.`

4. Click **Next**. P0 saves the connector and opens its setup page, titled **Configuring connector** followed by the connector's ID.

A connector ID uses lowercase letters, digits and hyphens, starts and ends with a letter or digit, and has up to 63 characters. A connector name uses lowercase letters, digits and hyphens, starts with a letter, and ends with a letter or digit. On AWS Lambda, it can't have two hyphens in a row.

If P0 can't save the connector, it shows why at the top of the form, under **Error configuring Docker Hub**. See [When you add a connector or an organization](#when-you-add-a-connector-or-an-organization) for each message. Fix it, then click **Next** again.

### Step 2: Set the allowed domains

The setup page has the connector's Terraform on the **Terraform** tab, filled in with the values from step 1. Before you apply it, set the allowed domains.

The connector acts only for people whose email domain matches its allowed domains pattern. The pattern is the `domain_allow_pattern` setting near the top of the connector's Terraform, which passes it to the connector as `DOMAIN_ALLOW_PATTERN`. The setting is required. The connector won't start without a pattern, with a blank one, or with one that isn't a valid regular expression.

The pattern is a regular expression that the connector tests against the part of an email after the `@`, once it has dropped any whitespace around the pattern. P0 fills in the domain of the email you're signed in with, anchored at both ends. For `example.com`, the Terraform reads:

```terraform
domain_allow_pattern = "^example\\.com$"
```

That's the regular expression `^example\.com$`, with each backslash doubled as a Terraform string needs. If P0 doesn't know your email's domain, it fills in `example.com` itself, which matches nobody, so replace it.

Change the pattern to cover everyone who should be able to request a role. For two domains, use `^(example\.com|example\.org)$`.

{% hint style="warning" %}
**Keep the `^` and the `$`.** Nothing anchors the pattern for you. Without them, a pattern for `example.com` also matches `notexample.com` and `example.com.example.net`, and the connector acts for addresses at those domains too. Escape each dot as well, because a bare `.` matches any character.
{% endhint %}

* One pattern covers every organization the connector serves. To allow different domains for different organizations, add a connector for each.
* You set the pattern on your own deployment, and P0 can't change it. Each organization's page shows it as **Allowed domains**, as the connector reported it the last time P0 validated the organization. If it leaves out the domain of your own email, the page warns you. See [step 8](#step-8-validate).
* The connector checks the pattern when someone requests a role and when P0 grants one. A narrower pattern never stops access from ending: P0 still cancels an invite, removes someone it invited, or lowers a role back. The one exception is a role that an admin lowered, during someone's access, below the role they had before it. Raising it back needs their email domain to match, and if it doesn't, they keep the lower role.
* To change the pattern later, see [Change the install later](#change-the-install-later).

### Step 3: Deploy the connector

The Terraform that P0 shows carries the reference to P0's connector image, deploys the connector where step 1 says, and lets P0 invoke it. The connector reaches Docker Hub over the internet, so it needs no VPC. See [What the connector's deployment creates](#what-the-connectors-deployment-creates).

1. Copy the Terraform from the **Terraform** tab, and add it to your configuration with the pattern from step 2. Leave the account or project, the region and the connector name as they are. P0 invokes the connector exactly there.
2. For a connector on AWS Lambda, before you apply it:
   * The Terraform has its own `required_providers` block for the `docker` provider. If your configuration already has one, move `docker` into yours and delete the snippet's block.
   * Add the credential settings your other AWS providers use, such as `profile` or `assume_role`, to the `aws` provider that the Terraform declares for the connector's account and region.
3. Apply it.
4. Click **Next**. P0 checks that it can invoke the connector, then opens the connector's configure page.
5. The configure page shows the fields from step 1, which you can't change. For a connector on Cloud Run, it also shows the **Connector service URI** that P0 found for the service. Click **Finish**.

If the check fails right after you apply the Terraform, wait a minute and click **Next** again. See [When P0 validates the install](#when-p0-validates-the-install) for each message.

Until you click **Finish**, the connector doesn't appear in an organization's **Connector** list.

### Step 4: Add an organization

Once you add an organization, you can't change its connector, the secret manager and the account or project that hold its token, or the secret's name. Decide them now.

1. On the Docker Hub integration's page, select the **Organization** component, and click **Add organization**. The form is titled **Installing a new organization**.
2. In **Organization name**, enter the organization's name on Docker Hub, as in its repositories' names: `<organization>/<repository>`.
3. In **Connector**, pick the connector that acts on this organization. The list shows the connectors you've finished installing, each with where it runs. If you haven't finished one, the field says `Install a Docker Hub connector first.` Go back to steps 1 to 3, up to **Finish**.
4. In **Secret manager provider**, choose the secret manager in the connector's cloud, and fill in its fields. The field starts as **AWS Secrets Manager**, so change it for a connector on Cloud Run.

| Secret manager provider                                 | Field              | What to enter                                                                                                                     |
| ------------------------------------------------------- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------- |
| **AWS Secrets Manager**, for a connector on AWS Lambda  | **Installation**   | The AWS account the secret is in. The list shows the accounts where P0's AWS Secrets Manager integration is installed.            |
|                                                         | **Secrets region** | The secret's AWS region, such as `us-west-2`                                                                                      |
| **Google Secret Manager**, for a connector on Cloud Run | **Installation**   | The Google Cloud project the secret is in. The list shows the projects where P0's Google Secret Manager integration is installed. |

5. In **Access token secret**, enter the name of the secret that holds, or will hold, the organization access token. Enter the name, not the ARN or the full resource name, and never the token itself. P0 refuses any name that starts with `dckr_`, as every Docker access token does.
6. Click **Next**. P0 saves the organization and opens its setup page, titled **Configuring organization** followed by the organization's name.

You can create the secret before or after you add the organization. You store the token in it in step 6. In AWS Secrets Manager, the secret's name can't end in a hyphen and six letters or digits, such as `-AbCdEf`, because Secrets Manager reads that as the suffix of a full ARN.

If the secret is in another AWS account than the connector's, encrypt it with a customer-managed KMS key. Another account can't use the default `aws/secretsmanager` key.

The setup page lists five steps. Its third step, **Deploy the connector**, is already done, and names the connector and where it runs. Steps 5 to 8 below are the other four.

### Step 5: Create the organization access token

An owner of the organization creates the token:

1. On the setup page, click **Open Docker Home**, and open your organization.
2. Go to **Identity & auth**, then **Access tokens**, and click **Generate access token**.
3. Under the organization's scopes, select **Member Edit** and **Invite Edit**. Select no repository scopes.
4. Finish generating the token, and copy it. Docker shows the token once.

The token needs each scope for these jobs:

| Scope       | What P0 uses it for                                                                                                                   |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| Member Edit | Find a requestor among the organization's members, change a member's role, and remove a member that P0 invited when their access ends |
| Invite Edit | Invite a requestor who isn't in the organization, and cancel the invite when their access ends                                        |

For more on these tokens, see Docker's guide to [organization access tokens](https://docs.docker.com/security/access-tokens/organization-access-tokens/).

### Step 6: Store the token

The setup page shows the commands below with the secret's name and place from step 4 filled in.

1. Run this first, paste the token and press Enter. It reads the token without showing it:

```bash
read -rs DOCKER_TOKEN
```

2. Create the secret. In AWS Secrets Manager:

```bash
printf %s "$DOCKER_TOKEN" | aws secretsmanager create-secret \
  --name <secret-name> \
  --secret-string file:///dev/stdin \
  --region <region>
```

In Google Secret Manager:

```bash
printf %s "$DOCKER_TOKEN" | gcloud secrets create <secret-name> \
  --project <project-id> \
  --data-file=-
```

For a secret in another AWS account than the connector's, the setup page's command also passes `--kms-key-id <kms-key-arn>`. Replace `<kms-key-arn>` with the ARN of the secret's customer-managed KMS key.

If the secret already exists, store the token as a new version instead. The setup page shows that command too, and so does [Change the install later](#change-the-install-later).

3. Clear the token from your shell:

```bash
unset DOCKER_TOKEN
```

### Step 7: Let the connector read the token

The **Terraform** tab at the bottom of the setup page has the Terraform for this step, which lets the connector read this one secret and no other.

1. Copy the Terraform from the **Terraform** tab, and add it to your configuration.
2. On AWS, add the credential settings your other AWS providers use to the `aws` provider that the Terraform declares. If a customer-managed KMS key encrypts the secret, set `kms_key_arn` to that key's ARN.
3. Apply it.

The Terraform grants the connector this, and nothing else:

| Secret manager        | What the Terraform grants                                                                                                                                                                                                                                                                                                       |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| AWS Secrets Manager   | A policy on the connector's role with `secretsmanager:GetSecretValue` on the token's secret. Its name is `ReadDockerHubToken-<organization>`, cut to IAM's limit of 128 characters. If you set `kms_key_arn`, the policy also has `kms:Decrypt` on that key, through Secrets Manager only.                                      |
| Google Secret Manager | `roles/secretmanager.secretAccessor` and `roles/secretmanager.viewer` for the connector's service account, on the secret itself and not on its project. The connector needs both to read the token. Each adds one member to the secret's IAM policy and replaces nothing. The secret must exist before you apply the Terraform. |

**If the secret is in another AWS account than the connector's**, the tab has two configurations:

1. The first is for the connector's account. Set `kms_key_arn` in it to the ARN of the customer-managed key that encrypts the secret. Terraform won't plan it until you do.
2. The second is for the account that holds the secret. Apply it after the connector's own Terraform, because it names the connector's role. It sets the secret's resource policy so that the connector's role can read the secret. It also outputs a statement that lets the connector's role decrypt with the key. Add that statement to the key's policy where you manage the key.

{% hint style="warning" %}
The second configuration replaces the secret's whole resource policy. If the secret already has one, leave the `aws_secretsmanager_secret_policy` resource out. Add its statement, the one that lets the connector's role read the secret, to the policy you already have.
{% endhint %}

{% hint style="warning" %}
**Keep the connector's read access to the token secrets of the organizations it serves.** P0 names the secret in each call to the connector, so any other secret the connector can read, whether through a wildcard or through a grant on the whole account or project, is one a call could point it at.
{% endhint %}

### Step 8: Validate

1. Click **Next**. P0 asks the connector to sign in to Docker Hub with the token, list the organization's members and try an invite as a dry run, which sends nothing and uses no seat.
2. P0 opens the organization's configure page. It shows the fields from step 4, which you can't change, and **Allowed domains**, the pattern the connector reported. Check that it's the pattern you set in step 2. If the pattern leaves out the domain of the email you're signed in with, the **Validate** step shows a warning: `The connector allows the email domains <pattern>, which leaves out your own, <domain>. If people at <domain> should be able to request roles in <org>, change domain_allow_pattern in the connector's Terraform and apply it.`
3. Click **Finish**. People can't request roles in the organization until you do.

If a check fails, P0 shows what to fix at the top of the page, under **Error configuring Docker Hub**. See [When P0 validates the install](#when-p0-validates-the-install) for each message. Fix it, then click **Next** again. If P0 says the connector isn't allowed to read the secret right after you apply the Terraform, wait a few seconds and click **Next** again.

{% hint style="success" %}
People can now request roles in the organization. See [Requesting Docker Hub access](/integrations/resource-integrations/docker/requesting-access.md).
{% endhint %}

## What the connector's deployment creates

The connector's Terraform gives the connector no secret. Each organization's Terraform adds read access to that organization's token secret, as in [step 7](#step-7-let-the-connector-read-the-token). P0 gets only what it needs to invoke the connector.

On AWS Lambda:

| Resource                                               | What it's for                                                                                                                                                    |
| ------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| An ECR repository                                      | Holds the copy of P0's connector image that Lambda runs. Its tags can't be overwritten, so pushing to the repository can't replace the image the connector runs. |
| A CloudWatch log group, `/aws/lambda/<connector-name>` | The connector's logs, kept for 30 days. Change `log_retention_days` in the Terraform to keep them for longer or shorter.                                         |
| An IAM role, named after the connector                 | The role the connector runs as, with `logs:CreateLogStream` and `logs:PutLogEvents` on its own log group.                                                        |
| The Lambda function                                    | Runs the connector. It calls `hub.docker.com` over the internet, so it needs no VPC.                                                                             |
| A policy on P0's role in the account                   | `lambda:InvokeFunction` on the connector's function, and nothing else.                                                                                           |

On Google Cloud Run:

| Resource                                                  | What it's for                                                                                                                                                                               |
| --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| A service account, named after the connector              | The identity the connector runs as.                                                                                                                                                         |
| The Cloud Run service                                     | Runs the connector, from P0's connector image on Docker Hub. It has no VPC. P0 calls it from outside Google Cloud, and it calls `hub.docker.com` over the internet.                         |
| Two IAM bindings on the service, for P0's service account | `roles/run.invoker`, to invoke the connector, and `roles/run.viewer`, to read the service's URL when you install. The connector also checks that each call comes from P0's service account. |

## Access policies

In [access policies](/policy-studio/policy-studio/configure-your-first-access-policy.md#resource), Docker Hub is the service `docker`, and its access type is `role`.

### Available filters

A rule can filter on these properties of a request, each with the key `name`:

| Filter         | What it matches                                  | Example         |
| -------------- | ------------------------------------------------ | --------------- |
| `organization` | The organization's name, as you entered it in P0 | `my-docker-org` |
| `role`         | The role: `member`, `editor` or `owner`          | `owner`         |

Patterns are unanchored, case-sensitive regular expressions. Use `^` and `$` to match a whole name.

### Example access policy

These policies send requests for member or editor in `my-docker-org` to your Security Reviewers, and deny owner in every organization:

```yaml
- requestor:
    type: any
  resource:
    type: integration
    service: docker
    accessType: role
    filters:
      organization:
        effect: keep
        key: name
        pattern: ^my-docker-org$
      role:
        effect: keep
        key: name
        pattern: ^(member|editor)$
  approval:
    - type: p0
- requestor:
    type: any
  resource:
    type: integration
    service: docker
    accessType: role
    filters:
      role:
        effect: keep
        key: name
        pattern: ^owner$
  approval:
    - type: deny
```

## Error messages

P0 shows these messages when you add a connector or an organization, when it validates the install, and on requests, where requestors and approvers see them. Once you've fixed the cause, retry what failed.

### When you add a connector or an organization

P0 checks the form when you click **Next** in [step 1](#step-1-add-a-connector) or [step 4](#step-4-add-an-organization), and saves nothing if a check fails. The message shows at the top of the form, under **Error configuring Docker Hub**. Fix the field it names, then click **Next** again.

| Message                                                                                                                                                                                                | What to do                                                                                                                                                                                                                 |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `The connector <id> is already added. To change it, remove <id> and add it again.`                                                                                                                     | Open the connector from the list instead, or choose another ID.                                                                                                                                                            |
| `Use lowercase letters, digits and hyphens only, starting and ending with a letter or digit, up to 63 characters.`                                                                                     | Choose a connector ID that follows the rule.                                                                                                                                                                               |
| `Use 2 to 64 characters for the connector name: lowercase letters, digits and single hyphens, starting with a letter and ending with a letter or digit.`                                               | For AWS Lambda, choose a **Connector name** that follows the rule.                                                                                                                                                         |
| `Use 6 to 30 characters for the connector name: lowercase letters, digits and hyphens, starting with a letter and ending with a letter or digit. The connector's service account takes the same name.` | For Cloud Run, choose a **Connector name** that follows the rule.                                                                                                                                                          |
| `Enter the AWS region the connector runs in, such as us-west-2.`, or a message like it for another account, project or region field                                                                    | Enter that field as the message says.                                                                                                                                                                                      |
| `P0's Docker Hub integration supports only AWS's commercial regions for now.`                                                                                                                          | Use a commercial AWS region for the connector and the secret.                                                                                                                                                              |
| `The Docker Hub organization <org> is already added. To change it, remove <org> and add it again.`                                                                                                     | Open the organization from the list instead.                                                                                                                                                                               |
| `Enter the organization's name as Docker Hub shows it: 2 to 255 lowercase letters, digits, hyphens and underscores, starting with a letter or digit.`                                                  | Enter the name as it appears in the organization's repository names.                                                                                                                                                       |
| `Pick the connector that acts on this organization. Install a Docker Hub connector first if there's none.`                                                                                             | Pick a connector. If the list is empty, finish steps 1 to 3 for a connector first, up to **Finish**.                                                                                                                       |
| `The token's secret must be in the connector's cloud: AWS Secrets Manager for a connector on AWS Lambda, or Google Secret Manager for one on Cloud Run.`                                               | Change **Secret manager provider** to the one in the connector's cloud.                                                                                                                                                    |
| `Enter the name of the secret that holds, or will hold, the organization access token.`                                                                                                                | Fill in **Access token secret**.                                                                                                                                                                                           |
| `That's a Docker access token, not the name of its secret. Enter the name of the secret that holds the token. P0 never needs the token itself, so generate a new one if it was shared anywhere else.`  | Enter the secret's name in **Access token secret**. P0 shows this message for any name that starts with `dckr_`, so use a secret whose name starts another way. If you pasted the token anywhere else, generate a new one. |
| `That doesn't look like a secret name. Enter the name of the secret that holds the organization access token, not its ARN, its full resource name or the token itself.`                                | Enter the secret's name. Never paste the token itself into the form.                                                                                                                                                       |
| `Secrets Manager can't find a secret by its name when the name ends in a hyphen and six characters, like -AbCdEf. Use a secret whose name doesn't end that way.`                                       | Choose a secret name that doesn't end in a hyphen and six letters or digits.                                                                                                                                               |

### When P0 validates the install

P0 validates a connector when you click **Next** in [step 3](#step-3-deploy-the-connector), and an organization when you click **Next** in [step 8](#step-8-validate). A failed check shows at the top of the page, under **Error configuring Docker Hub**. Fix the cause, then click **Next** again.

Where a message says `<where it runs>`, it names the connector's function or service, such as `the Lambda function p0-docker in AWS account 123456789012, region us-west-2`.

For a connector:

| Message                                                                                                                                                                       | What to do                                                                                                                                                        |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `P0 couldn't find <where it runs>. It may still be deploying. Check that the connector's Terraform applied, then retry.`                                                      | Check that the Terraform applied, and that it deploys the connector with the account or project, region and name from step 1. Then retry.                         |
| `P0 isn't allowed to invoke <where it runs>. The connector's Terraform lets P0's role call lambda:InvokeFunction on the function: check that it applied, then retry.`         | For AWS Lambda, apply the connector's Terraform, which lets P0's role in the account invoke the function. Right after you apply it, wait a few seconds and retry. |
| `P0 isn't allowed to invoke <where it runs>. The connector's Terraform grants P0 roles/run.invoker on the service: check that it applied, then retry.`                        | For Cloud Run, apply the connector's Terraform, which grants P0 `roles/run.invoker` on the service. Right after you apply it, wait a few seconds and retry.       |
| `P0 isn't allowed to read <where it runs>, so it can't find its URL. The connector's Terraform grants P0 roles/run.viewer on the service: check that it applied, then retry.` | For Cloud Run, apply the connector's Terraform, which grants P0 `roles/run.viewer` on the service. Then retry.                                                    |
| `<where it runs> has no URL yet. It may still be deploying. Check that the connector's Terraform applied, then retry.`                                                        | For Cloud Run, wait for the service to finish deploying, then retry.                                                                                              |
| `P0 found <where it runs>, but it isn't ready yet. It may still be starting. Retry in a minute, and check its logs if it isn't ready by then.`                                | For Cloud Run, wait a minute and retry. If the service still isn't ready, check its logs.                                                                         |
| Any other message                                                                                                                                                             | Check the connector's logs for why the call failed, then retry.                                                                                                   |

A connector whose allowed domains pattern is blank, or isn't a valid regular expression, doesn't start. Its logs then have a line that starts `DOMAIN_ALLOW_PATTERN isn't`. Fix the pattern as in [step 2](#step-2-set-the-allowed-domains), and apply the Terraform again.

For an organization:

| Message                                                                                                                                                                                                                                                                                                                                     | What to do                                                                                                                                                                                                                                                                  |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `The connector <id> isn't installed. Install it again with the same ID and hosting, or remove <org> and add it again with another connector.`                                                                                                                                                                                               | Add the connector again, as in steps 1 to 3, with the same ID and hosting.                                                                                                                                                                                                  |
| `The connector couldn't find the Docker Hub organization access token secret <secret>. Check the token secret in the P0 install, and that the secret still exists.`                                                                                                                                                                         | Store the token under the name and in the place that the setup page shows, as in [step 6](#step-6-store-the-token). If you named the wrong secret in step 4, remove the organization and add it again.                                                                      |
| `The connector isn't allowed to read the Docker Hub organization access token secret <secret>. Grant the connector's identity secretsmanager:GetSecretValue on that secret, and kms:Decrypt on its KMS key if it uses a customer-managed key.`, or the same message naming `secretmanager.versions.access` and `secretmanager.versions.get` | Apply the organization's Terraform, as in [step 7](#step-7-let-the-connector-read-the-token). If a customer-managed KMS key encrypts the secret, set its ARN too. For a secret in another AWS account, apply both configurations and add the statement to the key's policy. |
| `The Docker Hub organization access token secret <secret> is empty. Store the organization access token in it.`                                                                                                                                                                                                                             | Store the token in the secret as a new version, as in [Change the install later](#change-the-install-later).                                                                                                                                                                |
| `Docker Hub didn't accept the organization access token for <org>. Check that the secret holds a current organization access token that <org> generated, then retry.`                                                                                                                                                                       | Check that the secret holds the token, that this organization generated it, and that it hasn't expired and no one revoked it. Store a current token as a new version.                                                                                                       |
| `The organization access token for <org> can't list its members or invite people. Generate a token with the Member Edit and Invite Edit organization scopes, store it as a new version of the secret, then retry.`, or the same message for one of the two scopes                                                                           | Generate a token with both scopes, as in [step 5](#step-5-create-the-organization-access-token), and store it as a new version.                                                                                                                                             |
| `P0 invoked <where it runs>, but it didn't answer as the Docker Hub connector. Check that it runs the connector's image, and check its logs, then retry.`                                                                                                                                                                                   | Check that the function or service is the one the connector's Terraform deployed, and apply that Terraform again. If it is, check the connector's logs.                                                                                                                     |
| `P0 couldn't reach the connector, <where it runs>. It may have been removed, or P0 may no longer be allowed to invoke it. Check that the connector's Terraform is still applied, then retry.`                                                                                                                                               | Check that the connector still runs where its page in P0 says, and apply its Terraform again.                                                                                                                                                                               |
| `Docker Hub is temporarily unavailable; please try again later`                                                                                                                                                                                                                                                                             | Docker Hub is limiting the connector's requests, or the connector couldn't reach Docker Hub. Click **Next** again in a few minutes.                                                                                                                                         |

A message that starts `Docker Hub couldn't find` or `Docker Hub refused the connector's request` can show here too. See [On requests](#on-requests) for both.

### On requests

Requestors and approvers see these messages on a request. A revoke that failed has a **Retry revoke** button on the request's page, and a grant that failed has none. For the messages a requestor can fix themselves, see [Requesting Docker Hub access](/integrations/resource-integrations/docker/requesting-access.md#when-p0-refuses-your-request).

| Message                                                                                                                                                                                                             | What to do                                                                                                                                                                                                                                                                                                                                                  |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Your email domain isn't allowed for Docker Hub organization <org>. Ask your P0 admin to check the connector's allowed domains.`                                                                                    | The requestor's email domain doesn't match the connector's pattern. If they should be able to request roles, change the pattern as in [step 2](#step-2-set-the-allowed-domains).                                                                                                                                                                            |
| `More than one member of <org> has the email <email>. Ask your Docker Hub admin to fix this.`                                                                                                                       | Two Docker accounts in the organization have the requestor's email. An owner of the organization removes one, or changes its email. The same message fails a revoke for someone P0 invited, and the revoke keeps failing until the owner fixes it. Then click **Retry revoke**.                                                                             |
| `Docker Hub says you're already in <org>, but no member has the email <email>. Your Docker account probably uses a different email. Ask your Docker Hub admin to check it.`                                         | The requestor's Docker account is in the organization under another email, so P0 didn't find it. The requestor sets their Docker email to the email they sign in to P0 with, then requests again.                                                                                                                                                           |
| `Docker Hub refused the invite to <org>: <reason>.`                                                                                                                                                                 | Docker Hub's reason says what to fix. If the organization has no seat left, free one or add one. The requestor then requests again.                                                                                                                                                                                                                         |
| `Docker Hub refused to cancel one of the pending invites to <org>: <reason>`                                                                                                                                        | When someone's access ended, P0 cancelled a pending invite to their email, and Docker Hub refused to cancel another. An owner of the organization cancels the invites to that email that are still pending, in Docker Home. If the request has a **Retry revoke** button, click it.                                                                         |
| `Docker Hub refused to remove <username> from <org>: <reason>. P0 cancelled the pending invite to <email>, but <username> is still a member of <org>.`, or the same message with the number of invites P0 cancelled | When someone's access ended, P0 cancelled the pending invite to their email, and Docker Hub then refused to remove them from the organization. Docker Hub's reason says why, for example that they're its last owner. Fix what it names in Docker Home, or remove them from the organization there. If the request has a **Retry revoke** button, click it. |
| `Docker Hub couldn't find <username> in <org>. Check that it still exists, and that the organization access token is for <org>.`, or the same message naming only `<org>`                                           | Docker Hub has no such member, or no such organization for the token. Check that the organization's name in P0 is its name on Docker Hub, and that the secret holds a token that this organization generated. If the requestor left the organization, they request again, and P0 invites them.                                                              |
| `Docker Hub refused the connector's request for <org> (<status>): <reason>`                                                                                                                                         | Docker Hub wouldn't make the change, and its reason says why. Fix what it names in Docker Home. The requestor then requests again, or, for a revoke that failed, you click **Retry revoke**.                                                                                                                                                                |
| `P0 couldn't finish removing your previous access to <org>. Ask an admin to fix it in Docker Hub and finish the revoke in P0, then request again.`                                                                  | The revoke or the expiry of an earlier request of the requestor's for this organization failed. Open that request, fix the cause its error names, and click **Retry revoke**. The requestor can then request again.                                                                                                                                         |
| `P0 couldn't reach the Docker Hub connector. Ask your P0 admin to check its deployment.`                                                                                                                            | Check that the connector still runs where its page in P0 says, and apply its Terraform again so that P0 can invoke it. Check too that you still have P0's AWS IAM management or Google Cloud IAM management installed for its account or project.                                                                                                           |
| `P0's Docker Hub connector couldn't read the organization access token. Ask your P0 admin to check the Docker Hub install.`                                                                                         | The connector couldn't find the token's secret, isn't allowed to read it, or found it empty. Check that the secret exists under the name on the organization's page in P0, that it holds the token, and that you've applied the organization's Terraform, as in [step 7](#step-7-let-the-connector-read-the-token).                                         |
| A message that starts `There is an unrecoverable error in your organization's Docker Hub installation`, and says that Docker Hub didn't accept or refused the organization access token                             | The token expired, someone revoked it, or it doesn't have both scopes. Store a current token with both scopes as a new version of the secret, as in [Change the install later](#change-the-install-later). You don't need to install the integration again.                                                                                                 |
| `The Docker Hub connector for <org> isn't installed, so P0 can't change roles in <org>. Ask your P0 admin to install it. Access that P0 already granted in <org> may need to be removed by hand.`                   | Someone removed the organization's connector, or added it again without finishing its install. Install it as in steps 1 to 3, up to **Finish**, with the ID and hosting it had. Then click **Retry revoke** on any request whose revoke failed, or remove that access in Docker Home.                                                                       |
| `Docker Hub is temporarily unavailable; please try again later`                                                                                                                                                     | Docker Hub is limiting the connector's requests, or the connector couldn't reach Docker Hub. Try again in a few minutes.                                                                                                                                                                                                                                    |

## Change the install later

* **Rotate the token.** Generate a new organization access token with the same two scopes, as in [step 5](#step-5-create-the-organization-access-token), and store it as a new version of the same secret. Read the token as in [step 6](#step-6-store-the-token), then run the command for your secret manager.

  In AWS Secrets Manager:

  ```bash
  printf %s "$DOCKER_TOKEN" | aws secretsmanager put-secret-value \
    --secret-id <secret-name> \
    --secret-string file:///dev/stdin \
    --region <region>
  ```

  In Google Secret Manager:

  ```bash
  printf %s "$DOCKER_TOKEN" | gcloud secrets versions add <secret-name> \
    --project <project-id> \
    --data-file=-
  ```

  Then clear the token from your shell with `unset DOCKER_TOKEN`. The connector reads the secret itself, so nothing changes in P0 or in your Terraform.
* **Change the allowed domains.** Change `domain_allow_pattern` in the connector's Terraform, and apply it again. See [step 2](#step-2-set-the-allowed-domains) for what the pattern matches.
* **Copy the Terraform again.** Open a connector or an organization you've set up. Its Terraform is under **Show the connector's Terraform**, or **Show the Terraform that lets the connector read the token**, and, for a secret in another AWS account, **Show the Terraform for the secret's account**.
* **Move a connector, or change an organization's connector or secret.** You can't change these once you add a connector or an organization. Remove it, then add it again with the new values. Do this only when no one holds a role through P0 in the organizations involved, because P0 can't take that access back while an organization or its connector isn't installed. Check for active grants on the **Activity** tab of [Access Management](/access-management/access-management.md).
* **Remove a connector.** Remove one only when no organization uses it. P0 lets you remove a connector that organizations still use, and then can't change roles in those organizations until you add the connector again with the same ID and hosting. You may have to remove the access that P0 already granted there by hand.

## Limitations

* **P0 grants the three core roles only.** Requestors can ask for member, editor or owner, and never for one of the organization's custom roles. Someone who holds a custom role in the organization can't request any role there.
* **A pending invite takes a seat.** It holds one of the organization's seats until the person accepts it or their access ends. If Docker Hub refuses the invite, for example because the organization has no seat left, the request fails after approval with Docker Hub's reason.
* **Access time starts when P0 sends the invite.** It doesn't wait for the person to accept, so someone who accepts late has less time with the role.
* **Your identity provider can undo a removal.** If the organization has just-in-time provisioning or SCIM turned on, your identity provider can add back a member that P0 removed when their access ended.
* **P0 finds an invited person by email when their access ends.** P0 looks for the invite, and then for a member, by the email it invited. If their Docker account has another email by then, P0 finds no one. It ends the request without an error, and the account stays in the organization. Check the organization's members in Docker Home, and remove the account if it's listed.
* **On AWS, one connector serves a few dozen organizations.** Each organization adds a policy to the connector's role, and AWS limits a role's inline policies to 10,240 characters in all. Past that, add another connector for the rest.
