├── examples ├── beta │ ├── docker │ │ ├── compose.http.yaml │ │ ├── compose.lets-encrypt.yaml │ │ ├── compose.custom-tls.yaml │ │ ├── connect-sync.env │ │ ├── connect-api.env │ │ ├── compose.template.yaml │ │ └── README.md │ ├── aws-ecs-apigateway-cfn │ │ ├── README.md │ │ └── connect-server.yaml │ └── azure-container-apps-arm │ │ ├── aca-op-connect-server-template.json │ │ └── README.md ├── kubernetes │ ├── op-connect-service.yaml │ ├── op-connect-issuer.yaml │ ├── op-connect-ingress.yaml │ ├── op-connect-deployment.yaml │ └── README.md ├── docker │ └── compose │ │ ├── docker-compose.yaml │ │ └── README.md └── aws-ecs-fargate │ ├── README.md │ └── connect-server.yaml ├── README.md ├── docs ├── configuration.md └── openapi │ └── spec.yaml └── CHANGELOG.md /examples/beta/docker/compose.http.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | api: 3 | ports: [80:8080] 4 | -------------------------------------------------------------------------------- /examples/beta/docker/compose.lets-encrypt.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | api: 3 | environment: 4 | OP_TLS_USE_LETSENCRYPT: 1 5 | ports: [443:8443] 6 | -------------------------------------------------------------------------------- /examples/kubernetes/op-connect-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: op-connect 5 | spec: 6 | selector: 7 | app: op-connect 8 | ports: 9 | - name: connect-api 10 | port: 8080 11 | targetPort: connect-api 12 | -------------------------------------------------------------------------------- /examples/docker/compose/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: "3.4" 2 | 3 | services: 4 | op-connect-api: 5 | image: 1password/connect-api:latest 6 | ports: 7 | - "8080:8080" 8 | volumes: 9 | - "./1password-credentials.json:/home/opuser/.op/1password-credentials.json" 10 | - "data:/home/opuser/.op/data" 11 | op-connect-sync: 12 | image: 1password/connect-sync:latest 13 | volumes: 14 | - "./1password-credentials.json:/home/opuser/.op/1password-credentials.json" 15 | - "data:/home/opuser/.op/data" 16 | 17 | volumes: 18 | data: 19 | -------------------------------------------------------------------------------- /examples/beta/docker/compose.custom-tls.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | api: 3 | environment: [OP_TLS_CERT_FILE=/home/opuser/.op/tls.crt, OP_TLS_KEY_FILE=/home/opuser/.op/tls.key] 4 | ports: [443:8443] 5 | secrets: 6 | - source: op-tls-crt 7 | target: /home/opuser/.op/tls.crt 8 | uid: "999" 9 | gid: "999" 10 | mode: 0440 11 | - source: op-tls-key 12 | target: /home/opuser/.op/tls.key 13 | uid: "999" 14 | gid: "999" 15 | mode: 0440 16 | secrets: 17 | op-tls-crt: 18 | file: certificate.pem 19 | name: op-tls-crt 20 | op-tls-key: 21 | file: key.pem 22 | name: op-tls-key 23 | -------------------------------------------------------------------------------- /examples/beta/docker/connect-sync.env: -------------------------------------------------------------------------------- 1 | # # The logging level of the container. 2 | # # Acceptable values: info, error, debug 3 | # # Default value: info 4 | # OP_LOG_LEVEL=debug 5 | 6 | # # ---Manual bus configuration--- 7 | 8 | # # The port for listening to incoming bus connections from other containers. 9 | # # Acceptable values: Any available port number. 10 | # # Default value: A random free port 11 | # OP_BUS_PORT=11223 12 | 13 | # # A comma-separated list of [hostname]:[bus port] pairs of other containers to connect to. 14 | # # Acceptable values: A comma-separated list of [hostname]:[bus port] pairs. 15 | # # Default value: No default because this variable is optional. 16 | # OP_BUS_PEERS=api:11223 17 | -------------------------------------------------------------------------------- /examples/kubernetes/op-connect-issuer.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: cert-manager.io/v1 2 | kind: ClusterIssuer 3 | metadata: 4 | name: letsencrypt 5 | spec: 6 | acme: 7 | # You *must* replace this value with a valid email address. 8 | # Let's Encrypt will use this to contact you about expiring 9 | # certificates, and issues related to your account. 10 | email: user@example.com 11 | server: https://acme-v02.api.letsencrypt.org/directory 12 | privateKeySecretRef: 13 | # Secret resource that will be used to store the account's private key. 14 | name: letsencrypt-issuer-account-key 15 | solvers: 16 | # Use HTTP01 solver 17 | - http01: 18 | ingress: 19 | # Edit existing Ingress 20 | name: connect-ingress 21 | -------------------------------------------------------------------------------- /examples/docker/compose/README.md: -------------------------------------------------------------------------------- 1 | # Docker Compose 2 | 3 | The `docker-compose.yaml` file in this directory is a simple example of how to run 1Password Connect using Docker Compose. 4 | 5 | ## Getting Started 6 | 7 | Before you begin, make sure you have your Connect server's `1password-credentials.json` file on your machine. If the credentials file is not located in the same directory as the `docker-compose.yaml` file, update the `volumes` section for both the Sync and API containers: 8 | 9 | ```yaml 10 | volumes: 11 | - "/1password-credentials.json:/home/opuser/.op/1password-credentials.json" 12 | ``` 13 | 14 | ### Start the Docker containers: 15 | ```bash 16 | docker-compose up [-d] 17 | ``` 18 | 19 | The Connect API will be accessible through port `8080` on the host machine: 20 | ```curl 21 | curl \ 22 | -H "Accept: application/json" \ 23 | -H "Authorization: Bearer $OP_API_TOKEN" \ 24 | http://localhost:8080/v1/vaults 25 | ``` -------------------------------------------------------------------------------- /examples/kubernetes/op-connect-ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Ingress 3 | metadata: 4 | name: connect-ingress 5 | annotations: 6 | # Use cert-manager ClusterIssuer 7 | cert-manager.io/cluster-issuer: letsencrypt 8 | # Allow cert-manager to edit this Ingress resource in-place 9 | acme.cert-manager.io/http01-edit-in-place: "true" 10 | # Issue temporary self-signed cert 11 | cert-manager.io/issue-temporary-certificate: "true" 12 | spec: 13 | # You may need to specify the IngressClass if your cluster does not have a 14 | # default IngressClass, e.g. for Ingress NGINX Controller, uncomment the 15 | # below line: 16 | # ingressClassName: nginx 17 | rules: 18 | - host: connect.example.com 19 | http: 20 | paths: 21 | - path: / 22 | pathType: Prefix 23 | backend: 24 | service: 25 | name: op-connect 26 | port: 27 | name: connect-api 28 | tls: 29 | - hosts: 30 | - connect.example.com 31 | secretName: connect-cert 32 | -------------------------------------------------------------------------------- /examples/beta/docker/connect-api.env: -------------------------------------------------------------------------------- 1 | # # To enable TLS termination using Let's Encrypt, set to the domain name of a public DNS record that points to the publicly available endpoint for your Connect server. 2 | # OP_TLS_DOMAIN=connect.example.com 3 | 4 | # # The logging level of the container. 5 | # # Acceptable values: info, error, debug 6 | # # Default value: info 7 | # OP_LOG_LEVEL=debug 8 | 9 | # The time (in seconds) to wait for the initial sync to complete. 10 | # Acceptable values: A time duration (for example, 1h, 30m, 20s). 11 | # Default value: 10s (10 seconds) 12 | # OP_SYNC_TIMEOUT=1m 13 | 14 | # # ---Manual bus configuration--- 15 | 16 | # # The port for listening to incoming bus connections from other containers. 17 | # # Acceptable values: Any available port number. 18 | # # Default value: A random free port 19 | # OP_BUS_PORT=11223 20 | 21 | # # A comma-separated list of [hostname]:[bus port] pairs of other containers to connect to. 22 | # # Acceptable values: A comma-separated list of [hostname]:[bus port] pairs. 23 | # # Default value: No default because this variable is optional. 24 | # OP_BUS_PEERS=sync:11223 25 | -------------------------------------------------------------------------------- /examples/beta/docker/compose.template.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | api: 3 | image: 1password/connect-api 4 | networks: [op-connect] 5 | deploy: 6 | replicas: 1 7 | restart_policy: 8 | condition: any 9 | secrets: 10 | - source: credentials 11 | target: /home/opuser/.op/1password-credentials.json 12 | uid: "999" 13 | gid: "999" 14 | mode: 0400 15 | volumes: 16 | - data:/home/opuser/.op/data 17 | env_file: connect-api.env 18 | user: "999:999" 19 | 20 | sync: 21 | image: 1password/connect-sync 22 | networks: [op-connect] 23 | deploy: 24 | replicas: 1 25 | restart_policy: 26 | condition: any 27 | secrets: 28 | - source: credentials 29 | target: /home/opuser/.op/1password-credentials.json 30 | uid: "999" 31 | gid: "999" 32 | mode: 0400 33 | volumes: 34 | - data:/home/opuser/.op/data 35 | env_file: connect-sync.env 36 | user: "999:999" 37 | 38 | volumes: 39 | data: 40 | 41 | secrets: 42 | credentials: 43 | file: 1password-credentials.json 44 | name: credentials 45 | 46 | networks: 47 | op-connect: 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 |

1Password Connect

6 |

Access your 1Password secrets using 1Password Connect

7 | 8 | Get started 9 | 10 |
11 | 12 | --- 13 | 14 | With 1Password Connect, you can securely access your 1Password items and vaults in your company's apps and cloud infrastructure using a private REST API or the 1Password CLI. 15 | ## ✨ Get started 16 | Check out our [developer documentation](https://developer.1password.com/docs/connect/get-started) to get started. Or see our deployment examples [here](https://github.com/1Password/connect/tree/main/examples). 17 | ## 💙 Community & Support 18 | 19 | - File an [issue](https://github.com/1Password/connect/issues) for bugs and feature requests. 20 | - Join the [Developer Slack workspace](https://join.slack.com/t/1password-devs/shared_invite/zt-1halo11ps-6o9pEv96xZ3LtX_VE0fJQA) 21 | - Subscribe to the [Developer Newsletter](https://1password.com/dev-subscribe/) 22 | 23 | -------------------------------------------------------------------------------- /examples/beta/aws-ecs-apigateway-cfn/README.md: -------------------------------------------------------------------------------- 1 | # AWS ECS with Fargate and API Gateway 2 | 3 | The CloudFormation document in this folder will create the following resources, along with the necessary rules, groups, and policies to make them work. 4 | 5 | The result is a publicly accessible hostname (via HTTPS) that routes requests to a 1Password Connect instance running in AWS Fargate, using API Gateway for secure ingress. You can use your own VPC and subnets. 6 | 7 | To customize further, like deploying into an existing ECS cluster, use this CloudFormation as a starting point and tweak it with a text editor or Amazon's CloudFormation Designer tool. 8 | 9 | ## Networking Resources 10 | 11 | - A VPC Link for API Gateway to route to the Fargate service 12 | - An API Gateway with HTTPS 13 | - A VPC (optional, if you don’t provide your own) 14 | - 2 Public subnets (optional, if you don’t provide your own) 15 | - An Internet Gateway (optional, if creating a VPC) 16 | 17 | 18 | ## ECS Resources 19 | 20 | - An ECS Cluster 21 | - Task Definition 22 | - `1password/connect-api` container 23 | - `1password/connect-sync` container 24 | - Service Discovery for internal routing 25 | 26 | ## Getting Started 27 | 28 | When [importing this CloudFormation template](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-using-console-create-stack-template.html), you’ll be prompted to provide the following: 29 | 30 | - **CredentialsJson**: The plain JSON contents of your `1password-credentials.json` file (e.g., `{"token":"your-token"}`). The template will encode this to base64 and store it securely in AWS Secrets Manager. 31 | - **VPCID**, **PublicSubnets** (optional): Provide these if you want to use an existing VPC and subnets. Leave as default to create a new VPC. 32 | - **VPCCIDR** (optional): Sets the CIDR for a new VPC if you’re creating one; ignored if using an existing VPC. 33 | 34 | For more details, see the deployment note in the template description. -------------------------------------------------------------------------------- /examples/aws-ecs-fargate/README.md: -------------------------------------------------------------------------------- 1 | # AWS ECS with Fargate 2 | 3 | The CloudFormation document in this folder will create the following resources, along with the necessary rules, groups, and policies to make them work. 4 | 5 | The result is a publicly accessible hostname (via HTTPS) that routes requests to a 1Password Connect instance running in AWS Fargate. You can use your own VPC and subnets, or optionally set up DNS records automatically with Route 53. 6 | 7 | To customize further, like deploying into an existing ECS cluster, use this CloudFormation as a starting point and tweak it with a text editor or Amazon's CloudFormation Designer tool. 8 | 9 | ## Networking Resources 10 | 11 | - An ACM certificate for HTTPS 12 | - An ALB (Application Load Balancer) with HTTPS 13 | - A VPC (optional, if you don’t provide your own) 14 | - 2 Public subnets (optional, if you don’t provide your own) 15 | - An Internet Gateway (optional, if creating a VPC) 16 | - Route 53 DNS records (optional, if you provide a hosted zone ID) 17 | 18 | ## ECS Resources 19 | 20 | - An ECS Cluster 21 | - Task Definition 22 | - `1password/connect-api` container 23 | - `1password/connect-sync` container 24 | - Service Discovery for internal routing 25 | 26 | ## Getting Started 27 | 28 | When [importing this CloudFormation template](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-using-console-create-stack-template.html), you’ll be prompted to provide the following: 29 | 30 | - **CredentialsJson**: The plain JSON contents of your `1password-credentials.json` file. The template will encode this to base64 and store it securely in AWS Secrets Manager. 31 | - **DomainName**: A fully qualified domain name for the ALB (e.g., `connect.example.com`). This is required for HTTPS. 32 | - **Route53HostedZoneID** (optional): If you’re using Route 53, provide the hosted zone ID to automatically create DNS records. Leave blank to set up DNS manually with your registrar. 33 | - **VPCID**, **PublicSubnets** (optional): Provide these if you want to use an existing VPC and subnets. Leave as default to create a new VPC. 34 | - **VPCCIDR** (optional): Sets the CIDR for a new VPC if you’re creating one; ignored if using an existing VPC. 35 | 36 | ### DNS Setup (If Not Using Route 53) 37 | If you leave `Route53HostedZoneID` blank, the deployment will pause at the `HTTPSCertificate` creation step, waiting for DNS validation: 38 | 1. Check the ACM console for the certificate (status: `Pending validation`). 39 | 2. Add the provided CNAME record to your DNS registrar to validate the certificate. 40 | 3. Wait for the certificate to be issued (status: `Issued`), then the deployment will continue. 41 | 4. After deployment, use the `ExternalUrl` output (or `CNAME Name` and `CNAME Value`) to create an `A` (with alias) or `CNAME` record in your registrar to point to the ALB. 42 | 43 | For more details, see the deployment note in the template description. -------------------------------------------------------------------------------- /examples/kubernetes/op-connect-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: op-connect 5 | spec: 6 | selector: 7 | matchLabels: 8 | app: op-connect 9 | template: 10 | metadata: 11 | labels: 12 | app: op-connect 13 | spec: 14 | volumes: 15 | - name: shared-data 16 | emptyDir: {} 17 | - name: credentials 18 | secret: 19 | secretName: op-credentials 20 | initContainers: 21 | - name: sqlite-permissions 22 | image: alpine:3.12 23 | command: 24 | - "/bin/sh" 25 | - "-c" 26 | args: 27 | - "mkdir -p /home/opuser/.op/data && chown -R 999 /home/opuser && chmod -R 700 /home/opuser && chmod -f -R 600 /home/opuser/.op/config || :" 28 | volumeMounts: 29 | - mountPath: /home/opuser/.op/data 30 | name: shared-data 31 | containers: 32 | - name: connect-api 33 | image: 1password/connect-api:latest 34 | resources: 35 | limits: 36 | memory: "128Mi" 37 | cpu: "0.2" 38 | ports: 39 | - name: connect-api 40 | containerPort: 8080 41 | env: 42 | - name: OP_SESSION 43 | value: /home/opuser/.config/1password-credentials.json 44 | volumeMounts: 45 | - mountPath: /home/opuser/.op/data 46 | name: shared-data 47 | - mountPath: /home/opuser/.config 48 | name: credentials 49 | readOnly: true 50 | livenessProbe: 51 | httpGet: 52 | path: /heartbeat 53 | port: connect-api 54 | initialDelaySeconds: 3 55 | periodSeconds: 3 56 | readinessProbe: 57 | httpGet: 58 | path: /health 59 | port: connect-api 60 | initialDelaySeconds: 5 61 | periodSeconds: 5 62 | - name: connect-sync 63 | image: 1password/connect-sync:latest 64 | resources: 65 | limits: 66 | memory: "128Mi" 67 | cpu: "0.2" 68 | ports: 69 | - name: connect-sync 70 | containerPort: 8081 71 | env: 72 | - name: OP_SESSION 73 | value: /home/opuser/.config/1password-credentials.json 74 | - name: OP_HTTP_PORT 75 | value: "8081" 76 | volumeMounts: 77 | - mountPath: /home/opuser/.op/data 78 | name: shared-data 79 | - mountPath: /home/opuser/.config 80 | name: credentials 81 | readOnly: true 82 | livenessProbe: 83 | httpGet: 84 | path: /heartbeat 85 | port: connect-sync 86 | initialDelaySeconds: 3 87 | periodSeconds: 3 88 | readinessProbe: 89 | httpGet: 90 | path: /health 91 | port: connect-sync 92 | initialDelaySeconds: 5 93 | periodSeconds: 5 94 | -------------------------------------------------------------------------------- /docs/configuration.md: -------------------------------------------------------------------------------- 1 | # Connect Server Configuration 2 | 3 | The Connect server consists of 2 containers, `1password/connect-api` and `1password/connect-sync` running in the same network. The two containers require a shared volume to store an encrpyted copy of your data. 4 | 5 | ## Data Volume 6 | 7 | - `/home/opuser/.op/data`: The default location of the shared volume 8 | 9 | **Note**: If you use are setting the `XDG_DATA_HOME` environment variable to a path other than `/home/opuser` you will need to mount your data volume at that path as well. 10 | 11 | ## Environment variables 12 | 13 | The following environment variable configuration options are available for both contains: 14 | 15 | - `OP_SESSION`: path to the 1password-credentials.json file 16 | - `OP_HTTP_PORT`: port used by the HTTP server 17 | - `OP_LOG_LEVEL`: set the logging level of the container 18 | - `XDG_DATA_HOME`: set the path where the `.op/data` directory should be created 19 | - `OP_BUS_PORT`: the port used for listening to incoming bus connections from other containers (by default, this is a random free port) 20 | - `OP_BUS_PEERS`: a comma-separated listed of `[hostname]:[bus port]` pairs of other containers to connect to (see _Manual bus configuration_ for more details) 21 | 22 | All other configuration options are only relevant for the `1password/connect-api` container: 23 | 24 | - `OP_HTTPS_PORT`: port used by the HTTP sever when TLS is configured (see below) 25 | - `OP_SYNC_TIMEOUT`: define how long to wait for initial sync to complete 26 | 27 | When using TLS with own certificate: 28 | 29 | - `OP_TLS_KEY_FILE`: path to the private key file. 30 | - `OP_TLS_CERT_FILE`: path to the certificate file. This should be the full certificate chain. 31 | 32 | When using TLS with Let's Encrypt: 33 | 34 | - `OP_TLS_USE_LETSENCRYPT`: should be set to any value. 35 | - `OP_TLS_DOMAIN`: the (sub-)domain for which to request a certificate. The DNS-records for this domain must point to the Connect server. 36 | 37 | ## Manual bus configuration (v1.5.0 and above) 38 | By default, the 2 containers automatically discover and connect to the shared bus. This discovery mechanism requires the `NET_BROADCAST` capability, which cannot always be granted to containers. 39 | 40 | It is also possible to manually configure the shared bus: 41 | 1. Assign a static port for the bus by setting the `OP_BUS_PORT` environment variable to a free port for all containers. 42 | 2. Set the `OP_BUS_PEERS` environment variable for the api container to `[hostname]:[bus port]`, where hostname is the name of the sync container and bus port the value of `OP_BUS_PORT` for the sync container. 43 | 3. Set the `OP_BUS_PEERS` environment variable for the sync container to `[hostname]:[bus port]`, where hostname is the name of the api container and bus port the value of `OP_BUS_PORT` for the api container. 44 | 45 | For example, if the containers are called `op-connect-api` and `op-connect-sync`. The configuration of `op-connect-api` would be: 46 | ``` 47 | OP_BUS_PORT=11223 48 | OP_BUS_PEERS=op-connect-sync:11223 49 | ``` 50 | And for `op-connect-sync`: 51 | ``` 52 | OP_BUS_PORT=11223 53 | OP_BUS_PEERS=op-connect-api:11223 54 | ``` 55 | -------------------------------------------------------------------------------- /examples/beta/azure-container-apps-arm/aca-op-connect-server-template.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "logAnalyticsWorkspaceName": { 6 | "type": "string", 7 | "defaultValue": "op-connect-con-app-log-analytics", 8 | "metadata": { 9 | "description": "The name of the Log Analytics Workspace." 10 | } 11 | }, 12 | "containerAppName": { 13 | "type": "string", 14 | "defaultValue": "op-connect-con-app", 15 | "metadata": { 16 | "description": "The name of the Container App." 17 | } 18 | }, 19 | "containerAppEnvName": { 20 | "type": "string", 21 | "defaultValue": "op-connect-con-env", 22 | "metadata": { 23 | "description": "The name of the Container App Environment." 24 | } 25 | }, 26 | "credentials": { 27 | "type": "SecureString", 28 | "metadata": { 29 | "description": "The plain text contents of 1password-credentials.json." 30 | } 31 | } 32 | }, 33 | "resources": [ 34 | { 35 | "type": "Microsoft.OperationalInsights/workspaces", 36 | "apiVersion": "2022-10-01", 37 | "name": "[parameters('logAnalyticsWorkspaceName')]", 38 | "location": "[resourceGroup().location]", 39 | "properties": { 40 | "sku": { 41 | "name": "PerGB2018" 42 | } 43 | } 44 | }, 45 | { 46 | "type": "Microsoft.App/managedEnvironments", 47 | "apiVersion": "2023-05-01", 48 | "name": "[parameters('containerAppEnvName')]", 49 | "location": "[resourceGroup().location]", 50 | "dependsOn": [ 51 | "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName'))]" 52 | ], 53 | "properties": { 54 | "appLogsConfiguration": { 55 | "destination": "log-analytics", 56 | "logAnalyticsConfiguration": { 57 | "customerId": "[reference(resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName'))).customerId]", 58 | "sharedKey": "[listKeys(resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName')), '2022-10-01').primarySharedKey]" 59 | } 60 | }, 61 | "zoneRedundant": false 62 | } 63 | }, 64 | { 65 | "type": "Microsoft.App/containerApps", 66 | "apiVersion": "2023-05-01", 67 | "name": "[parameters('containerAppName')]", 68 | "location": "[resourceGroup().location]", 69 | "dependsOn": [ 70 | "[resourceId('Microsoft.App/managedEnvironments', parameters('containerAppEnvName'))]" 71 | ], 72 | "properties": { 73 | "managedEnvironmentId": "[resourceId('Microsoft.App/managedEnvironments', parameters('containerAppEnvName'))]", 74 | "configuration": { 75 | "secrets": [ 76 | { 77 | "name": "credentials", 78 | "value": "[parameters('credentials')]" 79 | } 80 | ], 81 | "activeRevisionsMode": "Single", 82 | "ingress": { 83 | "external": true, 84 | "targetPort": 8080, 85 | "allowInsecure": false, 86 | "traffic": [ 87 | { 88 | "latestRevision": true, 89 | "weight": 100 90 | } 91 | ] 92 | } 93 | }, 94 | "template": { 95 | "containers": [ 96 | { 97 | "name": "connect-api", 98 | "image": "docker.io/1password/connect-api:latest", 99 | "resources": { 100 | "cpu": 0.25, 101 | "memory": "0.5Gi" 102 | }, 103 | "volumeMounts": [ 104 | { 105 | "volumeName": "data", 106 | "mountPath": "/home/opuser/.op/data" 107 | }, 108 | { 109 | "volumeName": "credentials", 110 | "mountPath": "/home/opuser/.op/1password-credentials.json", 111 | "subPath": "1password-credentials.json" 112 | } 113 | ] 114 | }, 115 | { 116 | "name": "connect-sync", 117 | "image": "docker.io/1password/connect-sync:latest", 118 | "resources": { 119 | "cpu": 0.25, 120 | "memory": "0.5Gi" 121 | }, 122 | "env": [ 123 | { 124 | "name": "OP_HTTP_PORT", 125 | "value": "8081" 126 | } 127 | ], 128 | "volumeMounts": [ 129 | { 130 | "volumeName": "data", 131 | "mountPath": "/home/opuser/.op/data" 132 | }, 133 | { 134 | "volumeName": "credentials", 135 | "mountPath": "/home/opuser/.op/1password-credentials.json", 136 | "subPath": "1password-credentials.json" 137 | } 138 | ] 139 | } 140 | ], 141 | "volumes": [ 142 | { 143 | "name": "data", 144 | "storageType": "EmptyDir" 145 | }, 146 | { 147 | "name": "credentials", 148 | "storageType": "Secret", 149 | "secrets": [ 150 | { 151 | "secretRef": "credentials", 152 | "path": "1password-credentials.json" 153 | } 154 | ] 155 | } 156 | ], 157 | "scale": { 158 | "minReplicas": 1, 159 | "maxReplicas": 1 160 | } 161 | } 162 | } 163 | } 164 | ] 165 | } -------------------------------------------------------------------------------- /examples/kubernetes/README.md: -------------------------------------------------------------------------------- 1 | # Kubernetes Deployment 2 | 3 | This is an example Kubernetes deployment for 1Password Connect that uses [cert-manager](https://cert-manager.io) to provision a TLS certificate for your external domain. 4 | 5 | Included in this folder are: 6 | 7 | - [`op-connect-deployment.yaml`](op-connect-deployment.yaml): Deployment resource to create the Pod for 1Password Connect 8 | - `connect-sync` container to sync vault(s) from 1Password.com 9 | - `connect-api` container to serve 1Password Connect API 10 | - Shared ephemeral volume for data 11 | - [`op-connect-service.yaml`](op-connect-service.yaml): Service resource for connecting to the above Pod 12 | - [`op-connect-issuer.yaml`](op-connect-issuer.yaml): cert-manager ClusterIssuer resource to manage TLS certificates using [Let's Encrypt](https://letsencrypt.org/) 13 | - [`op-connect-ingress.yaml`](op-connect-ingress.yaml): Ingress resource to route TLS requests to 1Password Connect 14 | 15 | ## Prerequisites 16 | 17 | - 1Password Connect server credentials file (see [Quick Start](/README.md#quick-start) in the root of this repository) 18 | - Kubernetes cluster with available [ingress controller](https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/), e.g.: 19 | - [Ingress NGINX Controller](https://kubernetes.github.io/ingress-nginx/deploy/) 20 | - [GKE Ingress](https://cloud.google.com/kubernetes-engine/docs/concepts/ingress) 21 | - [AWS Load Balancer Controller](https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.4/deploy/installation/) 22 | - [Azure Application Gateway Ingress Controller](https://learn.microsoft.com/en-us/azure/application-gateway/tutorial-ingress-controller-add-on-new) 23 | - [cert-manager](https://cert-manager.io) installed on your cluster (see [Installation - cert-manager Documentation](https://cert-manager.io/docs/installation/)) 24 | - A DNS provider in which you can create public records. 25 | 26 | ## Getting started 27 | 28 | 1. If you haven't already done so, save your `1password-credentials.json` file locally. 29 | 30 | 2. Create a Kubernetes Secret for the credentials file: 31 | 32 | ```sh 33 | kubectl create secret generic op-credentials --from-file=1password-credentials.json=./1password-credentials.json 34 | ``` 35 | 36 | > **Note** 37 | > 38 | > If saved with a different filename or somewhere other than the working 39 | > directory, replace `./1password-credentials.json` with the path to your 40 | > credentials file, i.e.: 41 | > 42 | > ```sh 43 | > kubectl create secret generic op-credentials --from-file=1password-credentials.json=path/to/credentials-file.json 44 | > ``` 45 | 46 | 3. Clone this repository and switch to this directory: 47 | 48 | ```sh 49 | git clone https://github.com/1Password/connect.git 50 | cd ./connect/examples/kubernetes 51 | ``` 52 | 53 | 4. Edit [`op-connect-issuer.yaml`](op-connect-issuer.yaml). Replace the value for `spec.acme.email` with a valid email address: 54 | 55 | ```yaml 56 | ... 57 | spec: 58 | acme: 59 | ... 60 | email: user@example.com 61 | ... 62 | ``` 63 | 64 | Save the file. 65 | 66 | 5. Edit [`op-connect-ingress.yaml`](op-connect-ingress.yaml). Replace `connect.example.com` with the fully-qualified domain name you will use for 1Password Connect (use the same FQDN for both fields): 67 | 68 | ```yaml 69 | ... 70 | spec: 71 | rules: 72 | - host: connect.example.com 73 | ... 74 | tls: 75 | - hosts: 76 | - connect.example.com 77 | ... 78 | ``` 79 | 80 | > **Warning** 81 | > 82 | > Depending on your chosen ingress controller, you may also need to add 83 | > additional annotations or make other changes to this manifest. 84 | 85 | Save the file. 86 | 87 | ## Deploy 1Password Connect 88 | 89 | 1. Apply the manifests to your cluster: 90 | 91 | ```sh 92 | kubectl apply -f . 93 | ``` 94 | 95 | > **Note** 96 | > 97 | > The `.` in this command applies all `.yaml` and `.json` files in the 98 | > working directory. If this includes the 1Password Connect credentials 99 | > file, the output returned by `kubectl` may include something similar to: 100 | > 101 | >>```output 102 | >> error: error validating "1password-credentials.json":... 103 | >> ``` 104 | > 105 | > This error can safely be ignored. 106 | 107 | 2. Get the IP address of the ingress controller: 108 | 109 | ```sh 110 | kubectl get ing/connect-ingress 111 | ``` 112 | 113 | The IP address for this Ingress resource will be listed under the `ADDRESS` column. 114 | 115 | > **Note** 116 | > 117 | > Depending on your cluster or cloud provider, it may be several minutes 118 | > before an IP address is assigned. If there is no IP address listed yet, 119 | > run the same command again in a few minutes. 120 | 121 | 3. Create a DNS record that points to the above address. cert-manager will automatically acquire a TLS certificate on your behalf (this may take a few minutes). 122 | 123 | ## Test the connection 124 | 125 | Confirm public access by connecting using HTTPS from outside your cluster: 126 | 127 | > **Note** 128 | > 129 | > You will need to set the value of the `$OP_API_TOKEN` variable to an active token for this Connect environment. 130 | 131 | ```sh 132 | curl \ 133 | -X GET \ 134 | -H "Accept: application/json" \ 135 | -H "Authorization: Bearer $OP_API_TOKEN" \ 136 | https://connect.example.com/v1/vaults 137 | ``` 138 | 139 | This will also intialize the connection to 1Password.com and complete the first valut data sync. 140 | 141 | ## Appendix 142 | 143 | ### Updating 1Password Connect 144 | 145 | When a new version of 1Password Connect is released, set the image version for each of the containers to rollout a new revision: 146 | 147 | ```sh 148 | OP_CONNECT_VERSION=1.5.7; kubectl set image deploy/op-connect \ 149 | connect-api=1password/connect-api:$OP_CONNECT_VERSION \ 150 | connect-sync=1password/connect-sync:$OP_CONNECT_VERSION 151 | ``` 152 | -------------------------------------------------------------------------------- /examples/beta/azure-container-apps-arm/README.md: -------------------------------------------------------------------------------- 1 | # Deploy 1Password Connect Server on Azure Container Apps using the Azure Portal 2 | 3 | _Learn how to deploy 1Password Connect Server on the [Azure Container Apps](https://azure.microsoft.com/en-us/products/container-apps/#overview) service._ 4 | 5 | This deployment consists of two [containers](https://learn.microsoft.com/en-us/azure/container-apps/containers): One for the Connect API and another for Connect Sync. There's also an [ingress](https://learn.microsoft.com/en-us/azure/container-apps/ingress-overview) for the API container. There are a few benefits to deploying 1Password Connect Server on Azure Container Apps: 6 | 7 | - **Low cost:** For standard deployments, the service will host your Connect Server for ~$16 USD/month (as of January 2024). Container Apps pricing is variable based on activity, and you can learn more on [Microsoft's pricing page](https://azure.microsoft.com/en-us/pricing/details/container-apps/). 8 | - **Automatic DNS record management:** You don't need to manage a DNS record. Azure Container Apps automatically provides a unique one for your Connect Server domain. 9 | - **Automatic TLS certificate management:** Azure Container Apps automatically handles TLS certificate management on your behalf. 10 | 11 | ## Before you begin 12 | 13 | Before you begin, ensure you have an Azure account with permission to create a Container App. You'll also need the `1password-credentials.json` file for your 1Password Connect Server, which contains the credentials for authenticating the server. 14 | 15 | > [!NOTE] 16 | > If you don't have an Azure account, you can sign up for a free trial with starting credit: https://azure.microsoft.com/free/ 17 | 18 | ## Step 1: Download the template file 19 | 20 | 1. Download the [ARM(Azure Resource Manager) template](./aca-op-connect-server-template.json) file using the download icon at the top right. 21 | 22 | ## Step 2: Create the Container App 23 | 24 | 1. Sign in to the Azure Portal and go to the [Deploy a custom template](https://portal.azure.com/#create/Microsoft.Template) page. 25 | 2. Click **Build your own template in the editor**. 26 | 3. Click **Load file** and upload the template file you downloaded earlier. Then click **Save**. 27 | 4. Fill out the following fields: 28 | - **Subscription** : Choose the subscription you prefer. 29 | - **Resource group**: Choose an existing Resource Group or create a new one using **Create new** button. 30 | - **Region**: Choose the region you prefer. 31 | - **Container App Name**: Enter a name you'd like to use, default will be `op-connect-con-app`. 32 | - **Container App Env Name**: Enter a name you'd like to use, default will be `op-connect-con-env`. 33 | - **Container App Log Analytics Name**: Enter a name you'd like to use, default will be `op-connect-con-app-log-analytics`. 34 | - **Credentials** : Paste the entire contents of the `1password-credentials.json` file. 35 | 5. Click **Review + create**. 36 | 6. Once the validation succeeds, click **Create**. It is expected to take a couple of minutes to complete the deployment. 37 | 38 | ## Step 3: Test your Connect Server 39 | 40 | Once your deployment is complete, click **Go to resource group** and click on the container app you created. 41 | 42 | To test if your Connect Server is online, choose **Overview** in your application's sidebar, then copy your **Application Url** link. This is your **Connect Server URL**. Use your 1Password Connect access token to interact with the API (e.g., via a `curl` request or a client application) to verify that your Connect Server is connected to your 1Password account. 43 | 44 | ```sh 45 | curl --silent --show-error --request GET --header "Accept: application/json" \ 46 | --header "Authorization: Bearer mF_9.B5f-4.1JqM" \ 47 | https://op-connect.example.com/v1/vaults 48 | ``` 49 | 50 |
51 | Example JSON response: 52 | 53 | ```json 54 | [ 55 | { 56 | "attributeVersion": 1, 57 | "contentVersion": 9, 58 | "createdAt": "2025-03-21T13:06:35Z", 59 | "id": "hgm3sn37vkj3lsdkeouq46wyca", 60 | "items": 5, 61 | "name": "REDACTED", 62 | "type": "USER_CREATED", 63 | "updatedAt": "2025-04-18T16:38:40Z" 64 | }, 65 | { 66 | "attributeVersion": 1, 67 | "contentVersion": 2, 68 | "createdAt": "2025-03-21T02:15:33Z", 69 | "id": "mn4w65hq6ar7nc2fc7qgoru3ki", 70 | "items": 1, 71 | "name": "REDACTED", 72 | "type": "USER_CREATED", 73 | "updatedAt": "2025-03-21T02:16:32Z" 74 | }, 75 | { 76 | "attributeVersion": 1, 77 | "contentVersion": 94, 78 | "createdAt": "2025-03-21T15:20:41Z", 79 | "id": "z4s56cbejab6q6urod3l5icqky", 80 | "items": 3, 81 | "name": "REDACTED", 82 | "type": "USER_CREATED", 83 | "updatedAt": "2025-04-29T02:49:13Z" 84 | } 85 | ] 86 | ``` 87 | 88 |
89 |
90 | 91 | ## Step 4: Integrate with your application 92 | 93 | To use the Connect Server, configure your application to communicate with the Connect Server URL using the access token provided in your 1Password account. Follow the [1Password Connect integration guide](https://developer.1password.com/docs/connect) to set up your application. 94 | 95 |
96 | 97 | ## Update your Connect Server in the Azure Portal 98 | 99 | > [!TIP] 100 | > Check for 1Password Connect Server updates on the [1Password releases page](https://releases.1password.com/). 101 | 102 | 1. Within your deployed 1Password Connect Server Container App in the [Azure Container Apps Portal](https://portal.azure.com/#view/HubsExtension/BrowseResource/resourceType/Microsoft.App%2FcontainerApps), select **Containers** from the sidebar. 103 | 2. Click **Edit and deploy**. 104 | 3. Select the checkbox next to your **connect-api** container, then choose **Edit**. 105 | 4. Change the version number in the **Image and Tag** field, `1password/connect-api:latest`, to match the latest version from our [1Password releases page](https://releases.1password.com/). 106 | 5. Repeat for the **connect-sync** container, updating `1password/connect-sync:latest` to the same version. 107 | 6. Select **Save**. 108 | 7. Select **Create** to deploy a new revision using the updated images. 109 | 8. Test your Connect Server URL with your access token to verify the update. 110 | 111 | ## Get help 112 | 113 | ### How to update the **credentials** secret 114 | 115 | To use a new `1password-credentials.json` credentials file for your Connect Server, replace the secret in your Container App: 116 | 117 |
118 | Replace your credentials secret using the Azure Portal 119 | 120 | 1. Open the Azure Portal and go to the [Container Apps](https://portal.azure.com/#view/HubsExtension/BrowseResource/resourceType/Microsoft.App%2FcontainerApps) page. 121 | 2. Choose **Secrets** from the Settings section in the sidebar. 122 | 3. Edit the **credentials** secret and paste the entire contents of your new `1password-credentials.json` file. 123 | 4. Select the checkbox and click **Save**. 124 | 5. Choose the **Revisions** from the Application section in the sidebar. 125 | 6. Click your current active revision and choose **Restart** in the details pane. 126 | 7. Test your Connect Server URL with your new access token to [test your Connect Server](#step-3-test-your-connect-server). 127 |
-------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | [//]: # "START/v1.8.0" 2 | 3 | # v1.8.0 4 | This release includes improvements for fetching files with Connect, like support for fetching a file referenced in multiple items and a better error response if a file is not synced yet. 5 | 6 | - [IMPROVED] Connect now returns an improved error response when you try to fetch a file that isn't synced in your Connect server's local storage. {4378} 7 | 8 | - [FIXED] Connect now allows you to fetch a file if the file is referenced in multiple items across different vaults. {4262} 9 | - [FIXED] Connect file storage management can now handle multiple items referencing the same file. {4263} 10 | 11 | [//]: # "START/v1.7.3" 12 | 13 | # v1.7.3 14 | This release updates the dependencies and the images used to build Connect. 15 | 16 | - [IMPROVED] Update go version to 1.22.4. {4146} 17 | 18 | - [SECURITY] Upgraded to Go 1.21.x and Debian 12 Bookworm for building and CI. {3870} 19 | - [SECURITY] Bump google.golang.org/grpc to address a vulnerable dependency alert. {3855} 20 | 21 | [//]: # "START/v1.7.2" 22 | 23 | # v1.7.2 24 | This release contains bug fixes. 25 | 26 | - [FIXED] Connect can update Document items. {3709} 27 | - [FIXED] The first request sent to a new Connect server should not respond with a '401 invalid bearer token' error. {3711} 28 | 29 | [//]: # "START/v1.7.1" 30 | 31 | # v1.7.1 32 | This release contains security and performance improvements. 33 | - [IMPROVED] Connect item usage flushing has been optimised to decrease network traffic to the 1Password servers. {2600} 34 | 35 | - [SECURITY] Addressed bug where PATCH could be abused to read items using a write only token. Credit to Carlos Baraza. {3392} 36 | 37 | 38 | [//]: # "START/v1.7.0" 39 | 40 | # v1.7.0 41 | This release improves an error message and fixes two bugs. 42 | - [IMPROVED] Creating a new DOCUMENT item now correctly returns an error message that this is not supported. {3127} 43 | 44 | - [FIXED] Connect should no longer sporadically return an "database is locked" error when synchronising with the 1Password servers. {3381} 45 | - [FIXED] Requests to /v1/vaults/{vaultUUID}/items no longer causes "Invalid Item UUID" to be logged. {3254} 46 | 47 | 48 | [//]: # "START/v1.6.1" 49 | 50 | # v1.6.1 51 | This release addresses memory leak issues for Connect. 52 | - [FIXED] Addressed a problem that caused Connect's memory consumption to increase with about 100B for every request handled. {2842} 53 | - [FIXED] Addressed a problem that could cause Connect's memory consumption to increase by more than 1KB per request served after one of the containers (connect-api or connect-sync) disconnected. {3362} 54 | 55 | [//]: # "START/v1.6.0" 56 | 57 | # v1.6.0 58 | This release moves Connect to a distroless base image. 59 | - [SECURITY] Connect Docker images are now based on gcr.io/distroless/base to reduce the number of false positives when scanned with container security tools. {3296} 60 | 61 | [//]: # "START/v1.5.8" 62 | 63 | # v1.5.8 64 | This release contains two security improvements. 65 | - [SECURITY] Addressed an issue that allows an attacker with network access to the Connect server to allocate high amounts of memory on the Connect server if it is configured to serve connections over TLS. We inherited this issue from the Go standard library, where the issue has been assigned vulnerability identifier CVE-2022-41717. {3274} 66 | - [SECURITY] Update Debian packages for the Docker release images. {3195} 67 | 68 | [//]: # "START/v1.5.7" 69 | 70 | # v1.5.7 71 | Security updates, and a fix to allow Connect to (re)start if the 1Password server is unavailable. 72 | - [FIXED] The API container will now continue to serve requests if it is restarted when 1Password server in unreachable. {2560} 73 | 74 | - [SECURITY] Update connect images to use latest base image. {2738} 75 | - [SECURITY] Update Debian packages for the Docker release images. {2694} 76 | 77 | [//]: # "START/v1.5.6" 78 | 79 | # v1.5.6 80 | This version addresses bugs related to file objects 81 | 82 | - [FIXED] The file objects now have the correct ID and name. {2625, 2616} 83 | - [FIXED] Categories route will no longer return status 500 when no error is present. {2170} 84 | 85 | [//]: # "START/v1.5.5" 86 | 87 | # v1.5.5 88 | This version addresses a few bugs and a security improvement. 89 | 90 | - [FIXED] Connect now properly returns the `content_path` attribute of the File object. {2598} 91 | 92 | - [SECURITY] Updated Debian packages for the Docker release images. {2516} 93 | 94 | [//]: # "START/v1.5.4" 95 | 96 | # v1.5.4 97 | This release updates the API spec of Connect to mention some supported fields and addresses two bugs. 98 | 99 | - [IMPROVED] The API spec now mentions the "label" field for autofill URL's. {2446} 100 | - [IMPROVED] The API spec now mentions support for the item categories "MEDICAL_RECORD" and "SSH_KEY". {2468} 101 | 102 | - [FIXED] Connect accepts JSON inputs with camelCase keys as used in the SDK's again. {2317} 103 | - [FIXED] Connect no longer return an error mentioning the files directory in some cases when a file is uploaded to a vault that Connect syncs. {2498,2539} 104 | 105 | - [SECURITY] The Docker images are now using an updated version of the Debian base images. {2516} 106 | 107 | [//]: # "START/v1.5.3" 108 | 109 | # v1.5.3 110 | This release contains improvements in validation during the SRP process. 111 | 112 | - [SECURITY] Improved validation of server parameters in SRP process. Credits to Cure53. {2442} 113 | 114 | [//]: # "START/v1.5.2" 115 | 116 | # v1.5.2 117 | This release addressed a few minor bugs and a memory leak. 118 | 119 | - [IMPROVED] Connect now validates `monthYear` and `date` field types. {2075} 120 | 121 | - [FIXED] Addressed a problem that could let the memory consumption of the sync container slowly increase for every incoming request under certain circumstances. {2017} 122 | - [FIXED] Having a single quote as a filter value no longer throws a stack trace and now returns a 400. {1442} 123 | 124 | [//]: # "START/v1.5.1" 125 | 126 | # v1.5.1 127 | This release addresses a few problems related to Connect's synchronization mechanism. 128 | 129 | - [IMPROVED] Connect stays better in sync with 1Password when it experiences connection problems. {2150} 130 | 131 | - [FIXED] Fixed synchronization of templates that could prevent Connect from starting after changing its credentials. {2151} 132 | - [FIXED] Templates should now correctly synchronize to Connect after emptying Connect's database. {2151} 133 | - [FIXED] Connect should no longer return a "failed to initialize database" error when upgrading from Connect v1.3.x. {2005} 134 | 135 | [//]: # "START/v1.5.0" 136 | 137 | # v1.5.0 138 | This release introduces Docker images for the `arm64` and `arm/v7` platforms. 139 | 140 | Furthermore, the bus between the api and sync container can now be manually configured for environments that cannot run containers with the `NET_BROADCAST` capability. 141 | 142 | - [NEW] Bus auto-discovery can now be disabled by explicitly defining peers with the `$OP_BUS_PEERS` environment variable. {1778} 143 | - [NEW] Connect API and Connect Sync Docker images for arm64/v8 and arm/v7 architectures are now available. {1771} 144 | 145 | - [FIXED] The label field is now always correctly set (instead of being empty) for fields that have a USERNAME or PASSWORD purpose. {1657} 146 | 147 | 148 | 149 | [//]: # "START/v1.4.0" 150 | 151 | # v1.4.0 152 | This release introduces enhancements for OTP fields, generated passwords and item usage histories. 153 | 154 | This update includes non-breaking changes to the [OpenAPI spec](docs/openapi/spec.yaml). 155 | 156 | - [NEW] Specific characters can now be excluded from generated passwords by setting `excludeCharacters` in the recipe. {1631} 157 | - [IMPROVED] Added the generated TOTP code to the body of OTP fields in Items. {1557} 158 | - [IMPROVED] Connect now records more detailed item usage histories. {14198} 159 | 160 | 161 | [//]: # "START/v1.3.0" 162 | 163 | # v1.3.0 164 | 165 | This release introduces support for accessing documents and files attached to items. 166 | 167 | ## Features 168 | 169 | - [NEW] Add support for getting files through the API. Details can be found in the [OpenAPI spec](docs/openapi/spec.yaml). 170 | 171 | - [IMPROVED] Item modification requests are now rejected by the Connect API if the authenticated account is in the frozen billing state. {1175} 172 | - [IMPROVED] Items containing one or more file fields can now be updated through Connect. {1386} 173 | - [IMPROVED] API spec now defines valid vault and item IDs. {1399} 174 | - [IMPROVED] Connect API now reports its version number in the `1Password-Connect-Version` header. {1443} 175 | 176 | 177 | ## Fixed 178 | 179 | - [FIXED] Resolved an issue that prevented servers from authenticating with accounts after an admin updated the sign-in url. {1446} 180 | 181 | 182 | [//]: # "START/v1.2.0" 183 | 184 | # v1.2.0 185 | 186 | This release contains several improvements to securing you API requests and better error handling when dealing with Items. 187 | 188 | ## Features 189 | 190 | - [NEW] Add TLS for the Connect API. Provide your own certificates or use LetsEncrypt 191 | 192 | - [IMPROVED] A more user-friendly error is returned when the user attempts to create an item with the "CUSTOM" category. {1382} 193 | 194 | ## Fixed 195 | 196 | - [FIXED] Resolved issue where API could incorrectly report a successful item deletion. {1368} 197 | - [FIXED] Fixed the handling of TOTP fields for items in Connect {1372} 198 | 199 | [//]: # "START/v1.1.1" 200 | 201 | # v1.1.1 202 | 203 | This release contains improvements to account synchronization and token validation. 204 | 205 | [//]: # "START/v1.1.0" 206 | 207 | # v1.1.0 208 | 209 | This release contains several improvements to configuring, running, and troubleshooting a Secrets Automation deployment in your environment. 210 | 211 | The health endpoint returns additional information about the status of the local item database and the "DELETE" endpoint now permanently deletes items instead of moving them to the Trash. 212 | 213 | ## Features 214 | 215 | - [NEW] Updated the Connect API to delete items instead of trashing them. {11613} 216 | - [NEW] Connect can now also be unlocked by sending a bearer token to the /health endpoint. {1305} 217 | - [NEW] The /health endpoint reports the status of the data in the local database under account_data. {1305} 218 | - [NEW] Added support for new API Credential item category. {1344} 219 | 220 | - [IMPROVED] The `OP_LOG_LEVEL` evironment variable can now be used to customize the logging level of the Connect server and the sync component. {1205} 221 | - [IMPROVED] The API request timeout for waiting for a healthy sync is now per-request and can be changed with the `OP_SYNC_TIMEOUT` environment variable. {1305} 222 | - [IMPROVED] Item usage is now reported to 1password.com in batches at most once a second to avoid being rate limited. {1331} 223 | - [IMPROVED] Syncer applies an exponential backoff when retrying if it cannot find a valid credential. {1341} 224 | 225 | ## Fixes 226 | 227 | - [FIXED] Corrected several minor mistakes in the OpenAPI spec. {1326} 228 | - [FIXED] Log error when a connection problem causes the failure of a Connect request. {1302} 229 | - [FIXED] Updating the password for a Login or Password Item correctly updates the Password History. {1350} 230 | 231 | [//]: # "START/v1.0.0" 232 | 233 | # v1.0.0 234 | 235 | The 1Password Connect server provides applications and services with information from 1Password as part of 1Password Secrets Automation. 236 | -------------------------------------------------------------------------------- /examples/beta/aws-ecs-apigateway-cfn/connect-server.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: 2010-09-09 2 | Description: Sets up 1Password Connect on Fargate with API Gateway, stores 1password-credentials.json in Secrets Manager, and makes a VPC if you don’t give one 3 | 4 | # Makes the params look nice in the AWS console 5 | Metadata: 6 | AWS::CloudFormation::Interface: 7 | ParameterGroups: 8 | - Label: 9 | default: Network & Credentials Setup 10 | Parameters: 11 | - VPCID 12 | - VPCCIDR 13 | - PublicSubnets 14 | - CredentialsJson 15 | ParameterLabels: 16 | CredentialsJson: 17 | default: 1Password Credentials JSON 18 | VPCID: 19 | default: VPC ID 20 | VPCCIDR: 21 | default: VPC CIDR 22 | PublicSubnets: 23 | default: Public Subnets 24 | 25 | # Parameters you can tweak when you deploy 26 | Parameters: 27 | VPCID: 28 | Type: String 29 | Description: Got a VPC ID? Toss it here, or leave it blank to make a new one 30 | Default: "" 31 | VPCCIDR: 32 | Type: String 33 | Description: CIDR for a new VPC if we're making one 34 | Default: 10.0.0.0/16 35 | PublicSubnets: 36 | Type: CommaDelimitedList 37 | Description: List of public subnet IDs, need at least 2 if you're using an existing VPC 38 | Default: "" 39 | CredentialsJson: 40 | Type: String 41 | Description: Your 1password-credentials.json as plain JSON 42 | NoEcho: true 43 | MinLength: 1 44 | ConstraintDescription: gotta have something here 45 | 46 | # Check if we need to whip up a new VPC 47 | Conditions: 48 | CreateVPC: 49 | Fn::Equals: 50 | - !Ref VPCID 51 | - "" 52 | 53 | # Predefined CIDRs for the VPC and subnets 54 | Mappings: 55 | SubnetConfig: 56 | VPC: 57 | CIDR: '10.0.0.0/16' 58 | PublicOne: 59 | CIDR: '10.0.1.0/24' 60 | PublicTwo: 61 | CIDR: '10.0.2.0/24' 62 | 63 | Resources: 64 | # Secret to store the base64-encoded credentials 65 | CredentialsSecret: 66 | Type: AWS::SecretsManager::Secret 67 | Properties: 68 | Name: !Sub 'CredentialsSecret-${AWS::StackName}' 69 | SecretString: !Base64 { "Fn::Sub": "${CredentialsJson}" } 70 | 71 | # VPC stuff if we need to make one 72 | VPC: 73 | Condition: CreateVPC 74 | Type: AWS::EC2::VPC 75 | Properties: 76 | CidrBlock: !Ref VPCCIDR 77 | EnableDnsSupport: true 78 | EnableDnsHostnames: true 79 | 80 | PublicSubnetOne: 81 | Condition: CreateVPC 82 | Type: AWS::EC2::Subnet 83 | Properties: 84 | AvailabilityZone: 85 | Fn::Select: 86 | - 0 87 | - Fn::GetAZs: '' 88 | VpcId: !Ref VPC 89 | CidrBlock: !FindInMap ['SubnetConfig', 'PublicOne', 'CIDR'] 90 | MapPublicIpOnLaunch: true 91 | 92 | PublicSubnetTwo: 93 | Condition: CreateVPC 94 | Type: AWS::EC2::Subnet 95 | Properties: 96 | AvailabilityZone: 97 | Fn::Select: 98 | - 1 99 | - Fn::GetAZs: '' 100 | VpcId: !Ref VPC 101 | CidrBlock: !FindInMap ['SubnetConfig', 'PublicTwo', 'CIDR'] 102 | MapPublicIpOnLaunch: true 103 | 104 | InternetGateway: 105 | Condition: CreateVPC 106 | Type: AWS::EC2::InternetGateway 107 | 108 | GatewayAttachment: 109 | Condition: CreateVPC 110 | Type: AWS::EC2::VPCGatewayAttachment 111 | Properties: 112 | VpcId: !Ref VPC 113 | InternetGatewayId: !Ref InternetGateway 114 | 115 | PublicRouteTable: 116 | Condition: CreateVPC 117 | Type: AWS::EC2::RouteTable 118 | Properties: 119 | VpcId: !Ref VPC 120 | 121 | PublicRoute: 122 | Condition: CreateVPC 123 | DependsOn: GatewayAttachment 124 | Type: AWS::EC2::Route 125 | Properties: 126 | RouteTableId: !Ref PublicRouteTable 127 | DestinationCidrBlock: '0.0.0.0/0' 128 | GatewayId: !Ref InternetGateway 129 | 130 | PublicSubnetOneRouteTableAssociation: 131 | Condition: CreateVPC 132 | Type: AWS::EC2::SubnetRouteTableAssociation 133 | Properties: 134 | SubnetId: !Ref PublicSubnetOne 135 | RouteTableId: !Ref PublicRouteTable 136 | 137 | PublicSubnetTwoRouteTableAssociation: 138 | Condition: CreateVPC 139 | Type: AWS::EC2::SubnetRouteTableAssociation 140 | Properties: 141 | SubnetId: !Ref PublicSubnetTwo 142 | RouteTableId: !Ref PublicRouteTable 143 | 144 | # ECS cluster to run stuff 145 | ECSCluster: 146 | Type: AWS::ECS::Cluster 147 | Properties: 148 | CapacityProviders: 149 | - FARGATE 150 | DefaultCapacityProviderStrategy: 151 | - CapacityProvider: FARGATE 152 | Weight: 1 153 | 154 | # SGs for Fargate and API Gateway 155 | FargateContainerSecurityGroup: 156 | Type: AWS::EC2::SecurityGroup 157 | Properties: 158 | GroupDescription: Access to the Fargate containers 159 | VpcId: !If [CreateVPC, !Ref VPC, !Ref VPCID] 160 | 161 | ApiGatewaySecurityGroup: 162 | Type: AWS::EC2::SecurityGroup 163 | Properties: 164 | GroupDescription: API Gateway traffic for 1Password Connect 165 | VpcId: !If [CreateVPC, !Ref VPC, !Ref VPCID] 166 | 167 | # Security group rules 168 | ApiGatewayEgress: # lets API Gateway talk to Fargate 169 | Type: AWS::EC2::SecurityGroupEgress 170 | Properties: 171 | GroupId: !Ref ApiGatewaySecurityGroup 172 | IpProtocol: tcp 173 | FromPort: 8080 174 | ToPort: 8080 175 | DestinationSecurityGroupId: !Ref FargateContainerSecurityGroup 176 | 177 | ConnectIngress: 178 | Type: AWS::EC2::SecurityGroupIngress 179 | Properties: 180 | GroupId: !Ref FargateContainerSecurityGroup 181 | IpProtocol: tcp 182 | FromPort: 8080 183 | ToPort: 8080 184 | SourceSecurityGroupId: !Ref ApiGatewaySecurityGroup 185 | 186 | ConnectSelfIngress: 187 | Type: AWS::EC2::SecurityGroupIngress 188 | Properties: 189 | Description: Ingress from other containers in the same security group 190 | GroupId: !Ref FargateContainerSecurityGroup 191 | IpProtocol: -1 192 | SourceSecurityGroupId: !Ref FargateContainerSecurityGroup 193 | 194 | ConnectPublicEgress: 195 | Type: AWS::EC2::SecurityGroupEgress 196 | Properties: 197 | GroupId: !Ref FargateContainerSecurityGroup 198 | IpProtocol: tcp 199 | FromPort: 443 200 | ToPort: 443 201 | CidrIp: 0.0.0.0/0 202 | Description: HTTPS to external services (e.g., Docker Hub) 203 | 204 | # IAM roles for ECS 205 | ECSTaskExecutionRole: 206 | Type: AWS::IAM::Role 207 | Properties: 208 | AssumeRolePolicyDocument: 209 | Version: '2012-10-17' 210 | Statement: 211 | - Effect: Allow 212 | Principal: 213 | Service: ecs-tasks.amazonaws.com 214 | Action: sts:AssumeRole 215 | Policies: 216 | - PolicyName: AmazonECSTaskExecutionRolePolicy 217 | PolicyDocument: 218 | Version: '2012-10-17' 219 | Statement: 220 | - Effect: Allow 221 | Action: 222 | - ecr:GetAuthorizationToken 223 | - ecr:BatchCheckLayerAvailability 224 | - ecr:GetDownloadUrlForLayer 225 | - ecr:BatchGetImage 226 | - logs:CreateLogStream 227 | - logs:PutLogEvents 228 | Resource: '*' 229 | - PolicyName: SecretAccess 230 | PolicyDocument: 231 | Version: '2012-10-17' 232 | Statement: 233 | - Effect: Allow 234 | Action: 235 | - secretsmanager:GetSecretValue 236 | Resource: !Sub 'arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:CredentialsSecret-${AWS::StackName}*' 237 | 238 | ECSRole: 239 | Type: AWS::IAM::Role 240 | Properties: 241 | AssumeRolePolicyDocument: 242 | Version: '2012-10-17' 243 | Statement: 244 | - Effect: Allow 245 | Principal: 246 | Service: ecs-tasks.amazonaws.com 247 | Action: sts:AssumeRole 248 | Policies: 249 | - PolicyName: ECSPermissions 250 | PolicyDocument: 251 | Version: '2012-10-17' 252 | Statement: 253 | - Effect: Allow 254 | Action: 255 | - ec2:AttachNetworkInterface 256 | - ec2:CreateNetworkInterface 257 | - ec2:DeleteNetworkInterface 258 | - ec2:DescribeNetworkInterfaces 259 | - ec2:DetachNetworkInterface 260 | Resource: '*' 261 | 262 | # Logs in CloudWatch 263 | CloudWatchLogsGroup: 264 | Type: AWS::Logs::LogGroup 265 | Properties: 266 | LogGroupName: onepassword-connect 267 | RetentionInDays: 30 268 | 269 | # Service discovery for internal routing 270 | ServiceDiscoveryNamespace: 271 | Type: AWS::ServiceDiscovery::PrivateDnsNamespace 272 | Properties: 273 | Description: Private DNS namespace for 1Password Connect 274 | Vpc: !If [CreateVPC, !Ref VPC, !Ref VPCID] 275 | Name: onepassword 276 | 277 | ConnectServiceDiscovery: 278 | Type: AWS::ServiceDiscovery::Service 279 | DependsOn: 280 | - ServiceDiscoveryNamespace 281 | Properties: 282 | DnsConfig: 283 | DnsRecords: 284 | - TTL: 60 285 | Type: SRV 286 | Name: connect 287 | NamespaceId: !Ref ServiceDiscoveryNamespace 288 | HealthCheckCustomConfig: 289 | FailureThreshold: 1 290 | 291 | # Task def for the Connect containers 292 | TaskDefinition: 293 | Type: AWS::ECS::TaskDefinition 294 | DependsOn: 295 | - ECSTaskExecutionRole 296 | - CredentialsSecret 297 | - CloudWatchLogsGroup 298 | Properties: 299 | Cpu: 256 300 | Memory: 512 301 | NetworkMode: awsvpc 302 | RequiresCompatibilities: 303 | - FARGATE 304 | ExecutionRoleArn: !Ref ECSTaskExecutionRole 305 | TaskRoleArn: !Ref ECSRole 306 | Volumes: 307 | - Name: data 308 | ContainerDefinitions: 309 | - Name: connect-api 310 | Image: 1password/connect-api:latest 311 | PortMappings: 312 | - ContainerPort: 8080 313 | Protocol: tcp 314 | MountPoints: 315 | - ContainerPath: /home/opuser/.op/data 316 | SourceVolume: data 317 | Secrets: 318 | - Name: OP_SESSION 319 | ValueFrom: !Ref CredentialsSecret 320 | LogConfiguration: 321 | LogDriver: awslogs 322 | Options: 323 | awslogs-group: !Ref CloudWatchLogsGroup 324 | awslogs-region: !Ref AWS::Region 325 | awslogs-stream-prefix: connect-api 326 | - Name: connect-sync 327 | Image: 1password/connect-sync:latest 328 | MountPoints: 329 | - ContainerPath: /home/opuser/.op/data 330 | SourceVolume: data 331 | Environment: 332 | - Name: OP_HTTP_PORT 333 | Value: '8081' 334 | Secrets: 335 | - Name: OP_SESSION 336 | ValueFrom: !Ref CredentialsSecret 337 | LogConfiguration: 338 | LogDriver: awslogs 339 | Options: 340 | awslogs-group: !Ref CloudWatchLogsGroup 341 | awslogs-region: !Ref AWS::Region 342 | awslogs-stream-prefix: connect-sync 343 | 344 | # ECS service to run the containers 345 | ConnectService: 346 | Type: AWS::ECS::Service 347 | DependsOn: 348 | - TaskDefinition 349 | - ApiGatewayStage 350 | - ConnectServiceDiscovery 351 | - FargateContainerSecurityGroup 352 | Properties: 353 | ServiceName: connect 354 | Cluster: !Ref ECSCluster 355 | LaunchType: FARGATE 356 | DesiredCount: 1 357 | TaskDefinition: !Ref TaskDefinition 358 | NetworkConfiguration: 359 | AwsvpcConfiguration: 360 | AssignPublicIp: ENABLED 361 | SecurityGroups: 362 | - !Ref FargateContainerSecurityGroup 363 | Subnets: !If 364 | - CreateVPC 365 | - [!Ref PublicSubnetOne, !Ref PublicSubnetTwo] 366 | - !Ref PublicSubnets 367 | ServiceRegistries: 368 | - ContainerName: connect-api 369 | ContainerPort: 8080 370 | RegistryArn: !GetAtt ConnectServiceDiscovery.Arn 371 | 372 | # API Gateway setup 373 | VpcLink: 374 | Type: AWS::ApiGatewayV2::VpcLink 375 | Properties: 376 | Name: connect-vpc-link 377 | SecurityGroupIds: 378 | - !Ref ApiGatewaySecurityGroup 379 | SubnetIds: !If 380 | - CreateVPC 381 | - [!Ref PublicSubnetOne, !Ref PublicSubnetTwo] 382 | - !Ref PublicSubnets 383 | 384 | ApiGateway: 385 | Type: AWS::ApiGatewayV2::Api 386 | Properties: 387 | Name: onepassword-connect 388 | ProtocolType: HTTP 389 | Description: API Gateway for 1Password Connect 390 | 391 | ApiGatewayIntegration: 392 | Type: AWS::ApiGatewayV2::Integration 393 | DependsOn: 394 | - ApiGateway 395 | - VpcLink 396 | Properties: 397 | ApiId: !Ref ApiGateway 398 | IntegrationType: HTTP_PROXY 399 | IntegrationMethod: ANY 400 | ConnectionType: VPC_LINK 401 | ConnectionId: !Ref VpcLink 402 | IntegrationUri: !GetAtt ConnectServiceDiscovery.Arn 403 | PayloadFormatVersion: 1.0 404 | 405 | ApiGatewayRoute: 406 | Type: AWS::ApiGatewayV2::Route 407 | DependsOn: 408 | - ApiGateway 409 | - ApiGatewayIntegration 410 | Properties: 411 | ApiId: !Ref ApiGateway 412 | RouteKey: $default 413 | AuthorizationType: NONE 414 | Target: !Join 415 | - / 416 | - - integrations 417 | - !Ref ApiGatewayIntegration 418 | 419 | ApiGatewayStage: 420 | Type: AWS::ApiGatewayV2::Stage 421 | DependsOn: 422 | - ApiGateway 423 | - ApiGatewayRoute 424 | Properties: 425 | StageName: $default 426 | ApiId: !Ref ApiGateway 427 | AutoDeploy: true 428 | 429 | Outputs: 430 | ExternalUrl: 431 | Description: The URL of the API Gateway for 1Password Connect 432 | Value: !GetAtt ApiGateway.ApiEndpoint 433 | Export: 434 | Name: !Join [ ':', [ !Ref AWS::StackName, 'ExternalUrl' ] ] 435 | -------------------------------------------------------------------------------- /examples/beta/docker/README.md: -------------------------------------------------------------------------------- 1 | # [Beta] Deploy 1Password Connect Server using Docker Swarm 2 | 3 | This example describes how to deploy 1Password Connect Server as a [stack](https://docs.docker.com/engine/swarm/stack-deploy/) using [Docker Swarm](https://docs.docker.com/engine/swarm/key-concepts/#what-is-a-swarm). The stack includes two [services](https://docs.docker.com/engine/swarm/how-swarm-mode-works/services/) (one each for the Connect API and SYNC containers), a [Docker secret](https://docs.docker.com/engine/swarm/secrets/) for the `1password-credentials.json` file, and optional secrets and configuration required only for using a custom TLS certificate. 4 | 5 | ## In this folder 6 | 7 | - [`README.md`](./README.md): the document that you are reading. 👋😃 8 | - [`compose.template.yaml`](./compose.template.yaml): a [Compose Specification](https://docs.docker.com/compose/compose-file/) format [Compose file](https://docs.docker.com/compose/compose-file/03-compose-file/) for 1Password Connect Server 9 | - [`compose.lets-encrypt.yaml`](./compose.lets-encrypt.yaml): an [override configuration](https://docs.docker.com/compose/multiple-compose-files/merge/) to merge the configuration necessary for Let's Encrypt 10 | - [`compose.http.yaml`](./compose.http.yaml): optional configuration for exposing an HTTP port on the Docker host for forwarding plain-text traffic within a private network from a public TLS termination endpoint 11 | - [`compose.custom-tls.yaml`](./compose.custom-tls.yaml): optional configuration for enabling a self-managed TLS certificate 12 | - [`connect-api.env`](./connect-api.env): an environment file used to customize the connect-api service 13 | - [`connect-sync.env`](./connect-sync.env): an environment file used to customize the connect-sync service 14 | 15 | ## Overview 16 | 17 | The open source Docker Engine tooling can be used to deploy 1Password Connect Server on any supported Linux distribution. A single-node swarm is sufficient for most deployments. 18 | 19 | ## Prerequisites 20 | 21 | - AMD64/ARM64 VM or bare metal server with a Docker-supported Linux distribution (e.g. Ubuntu, Debian, Fedora, etc.) 22 | - Docker Engine (see [Docker Engine installation overview](https://docs.docker.com/engine/install/#server)) installed on the Linux server 23 | - a public DNS A record pointing to the Linux server 24 | - SSH access to the Linux server 25 | - [Docker Desktop](https://docs.docker.com/engine/install/#desktop) or Docker Engine installed on a machine with access to the Linux server and credentials from your 1Password account 26 | 27 | ## Get started 28 | 29 | > [!Note] 30 | > Before proceeding, review the [Get Started](https://developer.1password.com/docs/connect/get-started/) page from our 1Password Developer page. 31 | 32 | Create a public DNS record that points to the public IP address of the Linux server for your Connect server if you wish to have this publicly available. For example, `op-connect-server.example.com`. 33 | 34 | ### 🛠️ Prepare the Linux server 35 | 36 | On the Linux machine that you will be using as the Docker host for your Connect server: 37 | 38 | 1. If you haven't already done so, [install Docker Engine](https://docs.docker.com/engine/install/#server) on the Linux server. Follow the Server instructions; Docker Desktop is not needed for the Linux server. 39 | 2. Follow the [post-install steps](https://docs.docker.com/engine/install/linux-postinstall/) as noted in the documentation to enable running Docker as a non-root user and ensure that Docker Engine starts when the Linux server boots. 40 | 41 | ### 👨‍💻 Prepare your desktop 42 | 43 | All following steps should be run on the same computer where you are already using 1Password, or another machine that can access the Linux server using SSH and has access to the `1password-credentials.json` file from the integration setup: 44 | 45 | 1. If you haven't already done so, install Docker. You can use [Docker Desktop](https://docs.docker.com/engine/install/#desktop), [install Docker Engine from binaries](https://docs.docker.com/engine/install/binaries/), or install Docker using your favorite package manager. 46 | 47 | 2. Open your preferred terminal. Clone this repository and switch to this directory: 48 | 49 | ```sh 50 | git clone https://github.com/1Password/connect.git 51 | cd ./connect/examples/beta/docker 52 | ``` 53 | 54 | 3. Save the `1password-credentials.json` file from [the Connect Server setup](https://start.1password.com/developer-tools/infrastructure-secrets/connect/) to this working directory. 55 | 56 | 4. Open `connect-api.env` in your favorite text editor. Set the value of `OP_TLS_DOMAIN` to the fully qualified domain name of the public DNS record for your Connect server if you wish to have this publicly available, created in [Get started](#get-started). For example: 57 | 58 | ```dotenv 59 | # ... 60 | OP_TLS_DOMAIN=op-connect-server.example.com 61 | 62 | # ... 63 | ``` 64 | 65 | Save the file. 66 | 67 | 5. Create a Docker context to use for connecting to the Linux server: 68 | 69 | _Example command:_ 70 | 71 | ```sh 72 | docker context create op-connect \ 73 | --description "1Password Connect Server Docker host" \ 74 | --docker host=ssh://user@op-connect-server.example.com 75 | ``` 76 | 77 | Copy the example command to a text editor. Replace `user` with the appropriate username for your Linux server and `op-connect-server.example.com` with the host name or IP address used for SSH access to the Linux server before running the command in your terminal. 78 | 79 | > **Note** 80 | > 81 | > 🔑 You can store your SSH keys in 1Password and authenticate this and other SSH workflows without writing a 82 | > private key to disk using the [1Password SSH agent](https://developer.1password.com/docs/ssh/get-started). 83 | 84 | 6. Switch to the Docker context you just created to connect to the Docker host: 85 | 86 | ```sh 87 | docker context use op-connect 88 | ``` 89 | 90 | ### 🪄 Create a swarm 91 | 92 | Run this command to create the swarm: 93 | 94 | ```sh 95 | docker swarm init # --advertise-addr 192.0.2.1 96 | ``` 97 | 98 | > **Note** 99 | > 100 | > Additional nodes may be optionally added to a swarm for fault tolerance. This command adds the Linux server as the 101 | > first (and only) node in the swarm, but Docker _requires_ a unique IP address to advertise to other nodes (even if 102 | > no other nodes will be added). See [How nodes work](https://docs.docker.com/engine/swarm/how-swarm-mode-works/nodes/) 103 | > in the Docker documentation for more details. 104 | > 105 | > If multiple IP addresses are detected, Docker returns an error; uncomment the `--advertise-addr` parameter (delete 106 | > `#`), replace the example IP (`192.0.2.1`) with the appropriate IP address, and run the command again. 107 | 108 | ## Deploy 1Password Connect server 109 | 110 | Use the Compose template to output a canonical configuration for use with Docker Swarm and create the stack from this configuration inline. Your Connect server should automatically acquire and manage a TLS certificate from Let's Encrypt on your behalf if configured this way: 111 | 112 | ```sh 113 | docker stack config --compose-file ./compose.template.yaml \ --compose-file ./compose.lets-encrypt.yaml | 114 | docker stack deploy --compose-file - op-connect 115 | ``` 116 | 117 | ## Test your Connect server 118 | 119 | Run this command to view logs from the service for the Connect server container: 120 | 121 | ```sh 122 | docker service logs op-connect_api --raw 123 | ``` 124 | 125 | Your **Connect server URL** is based on the fully qualified domain name of the DNS record created in [Get started](#get-started). For example: `https://op-connect.example.com`. Replace `mF_9.B5f-4.1JqM` with your bearer token and `https://op-connect.example.com` with your Connect server URL to test the connection and view status information. 126 | 127 | ```sh 128 | curl --silent --show-error --request GET --header "Accept: application/json" \ 129 | --header "Authorization: Bearer mF_9.B5f-4.1JqM" \ 130 | https:/op-connect.example.com/health/v1/vaults 131 | ``` 132 | 133 |
134 | Example JSON response: 135 | 136 | ```json 137 | [ 138 | { 139 | "attributeVersion": 1, 140 | "contentVersion": 9, 141 | "createdAt": "2025-03-21T13:06:35Z", 142 | "id": "hgm3sn37vkj3lsdkeouq46wyca", 143 | "items": 5, 144 | "name": "REDACTED", 145 | "type": "USER_CREATED", 146 | "updatedAt": "2025-04-18T16:38:40Z" 147 | }, 148 | { 149 | "attributeVersion": 1, 150 | "contentVersion": 2, 151 | "createdAt": "2025-03-21T02:15:33Z", 152 | "id": "mn4w65hq6ar7nc2fc7qgoru3ki", 153 | "items": 1, 154 | "name": "REDACTED", 155 | "type": "USER_CREATED", 156 | "updatedAt": "2025-03-21T02:16:32Z" 157 | }, 158 | { 159 | "attributeVersion": 1, 160 | "contentVersion": 94, 161 | "createdAt": "2025-03-21T15:20:41Z", 162 | "id": "z4s56cbejab6q6urod3l5icqky", 163 | "items": 3, 164 | "name": "REDACTED", 165 | "type": "USER_CREATED", 166 | "updatedAt": "2025-04-29T02:49:13Z" 167 | } 168 | ] 169 | ``` 170 | 171 |
172 |
173 | 174 | ## Update 1Password Connect server 175 | 176 | Swarm mode in Docker Engine uses a declarative service model. Services will automatically restart tasks when updating their configuration. 177 | 178 | Use the Docker context from [Prepare your desktop](#-prepare-your-desktop) to connect to your Docker host and manage your stack. 179 | 180 | Update the `op-connect_api` and `op-connect_sync` services with the new image tags from the `1password/connect-api` and `1password/connect-sync`repository on Docker Hub to update your Connect server to a new version: 181 | 182 | - https://hub.docker.com/r/1password/connect-api/tags 183 | - https://hub.docker.com/r/1password/connect-sync/tags 184 | 185 | ```sh 186 | docker service update op-connect_api --image 1password/connect-api:latest && \ 187 | docker service update op-connect_sync --image 1password/connect-sync:latest 188 | 189 | ``` 190 | 191 | ## Rotate credentials 192 | 193 | Docker secrets are immutable and cannot be removed while in use by a Swarm service. To use new secret values in your stack, you must remove the existing secret from the service configuration, replace the Docker secret (or add a new one), and update the service configuration to mount the new secret value. 194 | 195 | For example, if you create new credentials for the Connect server: 196 | 197 | 1. Scale down the `op-connect_api` and `op-connect_sync services to shut down the running Connect server. 198 | 199 | ```sh 200 | docker service scale op-connect_api=0 op-connect_sync=0 201 | ``` 202 | 203 | 2. Update the service definition to unmount the the `1password-credentials.json` Docker secret: 204 | 205 | ```sh 206 | docker service update --secret-rm credentials op-connect_api && \ 207 | docker service update --secret-rm credentials op-connect_sync 208 | ``` 209 | 210 | 3. Remove the secret from the swarm: 211 | 212 | ```sh 213 | docker secret rm credentials 214 | ``` 215 | 216 | 4. Copy the new `1password-credentials.json` file from your 1Password account to your working directory. Create a new `credentials` Docker secret using the new file: 217 | 218 | ```sh 219 | docker secret create credentials ./1password-credentials.json 220 | ``` 221 | 222 | 5. Update the service to mount the new `1password-credentials.json` secret: 223 | 224 | ```sh 225 | docker service update --secret-add source=credentials,target=/home/opuser/.op/1password-credentials.json,uid="999",gid="999",mode=0440 op-connect_api && \ 226 | docker service update --secret-add source=credentials,target=/home/opuser/.op/1password-credentials.json,uid="999",gid="999",mode=0440 op-connect_sync 227 | 228 | ``` 229 | 230 | 6. Scale the `op-connect_api` and `op-connect_sync` services back up to reboot your Connect server: 231 | 232 | ```sh 233 | docker service scale op-connect_api=1 op-connect_sync=1 234 | ``` 235 | 236 | [Test your Connect server](#-test-your-Connect server) using the new bearer token associated with the new `1password-credentials.json` file. 237 | 238 | A similar process can be used to update the values for any other Docker secrets used in your configuration. 239 | 240 | ## Appendix: Customize your Connect server 241 | 242 | Many Connect server configuration changes can be made by adding or removing environment variables. These can be customized by making changes to [`connect-api.env`](./connect-api.env) and [`connect-sync.env`](./connect-sync.env) (that can be committed to your source control) before deploying (or redeploying) your Connect server using the `docker stack deploy` command. For some use cases, it may be desirable to update the configuration "on the fly" from your terminal. 243 | 244 | For example, to reboot your Connect server with debug logging enabled: 245 | 246 | ```sh 247 | docker service update op-connect_api \ 248 | --env-add OP_DEBUG=1 249 | ``` 250 | 251 | To turn off debug logging and inject some color into the logs in your console: 252 | 253 | ```sh 254 | docker service update op-connect_api \ 255 | --env-rm OP_DEBUG \ 256 | --env-add OP_PRETTY_LOGS=1 257 | ``` 258 | 259 | _Pretty logs pair nicely with the `--raw` parameter of the `docker service logs` command). 🤩_ 260 | 261 | ### 🔒 Advanced TLS options 262 | 263 | Identity providers strictly require an HTTPS endpoint with a valid TLS certificate to use for the Connect server URL. Your 1Password Connect server includes an optional CertificateManager component that (by default) acquires and manages a TLS certificate using Let's Encrypt, and terminates TLS traffic at the Connect server container using this certificate. This requires port 443 of the Docker host to be publicly accessible to ensure Let's Encrypt can initiate an inbound connection to your Connect server. 264 | 265 | Other supported options include: 266 | 267 | #### External load balancer or reverse proxy 268 | 269 | To terminate TLS traffic at another public endpoint and redirect private traffic to a Docker host in your private network, a Connect server can be configured to disable the CertificateManager component and serve plain-text HTTP traffic on port 80 of the Docker host. CertificateManager will be enabled if a value is set for the `OP_TLS_DOMAIN` variable, so any value set in `connect-api.env` must be removed (or this line must be commented out). The included `compose.http.yaml` file can be used to set up the port mapping when deploying (or redeploying) a Connect server: 270 | 271 | ```sh 272 | docker stack config \ 273 | --compose-file ./compose.template.yaml \ 274 | --compose-file ./compose.http.yaml | 275 | docker stack deploy --compose-file - op-connect 276 | ``` 277 | 278 | #### Self-managed TLS certificate 279 | 280 | You may supply your own TLS certificate with CertificateManager instead of invoking Let's Encrypt. The value set for `OP_TLS_DOMAIN` must match the common name of the certificate. 281 | 282 | Save the public and private certificate key files as `certificate.pem` and `key.pem` (respectively) to the working directory and use the included `compose.custom-tls.yaml` file when deploying your Connect server to create Docker secrets and configure a Connect server use this certificate when terminating TLS traffic: 283 | 284 | ```sh 285 | docker stack config \ 286 | --compose-file ./compose.template.yaml \ 287 | --compose-file ./compose.custom-tls.yaml | 288 | docker stack deploy --compose-file - op-connect 289 | ``` 290 | -------------------------------------------------------------------------------- /examples/aws-ecs-fargate/connect-server.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: 2010-09-09 2 | Description: >- 3 | Sets up 1Password Connect on Fargate with an ALB for HTTPS ingress, in a public VPC subnet. 4 | Note: If you leave Route53HostedZoneID blank, deployment will pause at HTTPSCertificate creation. 5 | Check the ACM console for a CNAME record to add to your DNS registrar for validation, then wait for the certificate to be issued. 6 | 7 | # Makes the params look nice in the AWS console 8 | Metadata: 9 | AWS::CloudFormation::Interface: 10 | ParameterGroups: 11 | - Label: 12 | default: Network & Credentials Setup 13 | Parameters: 14 | - VPCID 15 | - VPCCIDR 16 | - PublicSubnets 17 | - Route53HostedZoneID 18 | - DomainName 19 | - CredentialsJson 20 | ParameterLabels: 21 | VPCID: 22 | default: VPC ID 23 | VPCCIDR: 24 | default: VPC CIDR 25 | PublicSubnets: 26 | default: Public Subnets 27 | Route53HostedZoneID: 28 | default: Route 53 Hosted Zone 29 | DomainName: 30 | default: 1Password Connect Server domain name 31 | CredentialsJson: 32 | default: 1Password Credentials JSON 33 | 34 | # Stuff you can tweak when you deploy 35 | Parameters: 36 | VPCID: 37 | Type: String 38 | Description: Got a VPC ID? Toss it here, or leave it blank to make a new one 39 | Default: "" 40 | VPCCIDR: 41 | Type: String 42 | Description: CIDR for a new VPC if we're making one 43 | Default: 10.0.0.0/16 44 | PublicSubnets: 45 | Type: CommaDelimitedList 46 | Description: List of public subnet IDs, need at least 2 if you're using an existing VPC 47 | Default: "" 48 | Route53HostedZoneID: 49 | Type: String 50 | Description: Route 53 hosted zone ID for creating DNS records (leave empty to set up manually) 51 | Default: "" 52 | DomainName: 53 | Type: String 54 | Description: Domain name for the ALB (e.g., connect.example.com), must be in Route 53 if hosted zone is provided 55 | MinLength: 1 56 | ConstraintDescription: Need to provide a domain name for HTTPS 57 | CredentialsJson: 58 | Type: String 59 | Description: Your 1password-credentials.json as plain JSON 60 | NoEcho: true 61 | MinLength: 1 62 | ConstraintDescription: gotta have something here 63 | 64 | # Check if we need to whip up a new VPC or create DNS records 65 | Conditions: 66 | CreateVPC: !Equals [!Ref VPCID, ""] 67 | CreateRoute53Records: !Not [!Equals [!Ref Route53HostedZoneID, ""]] 68 | OutputDNSRecordContent: !Not [Condition: CreateRoute53Records] 69 | 70 | # Predefined CIDRs for the VPC and subnets 71 | Mappings: 72 | SubnetConfig: 73 | VPC: 74 | CIDR: '10.0.0.0/16' 75 | PublicOne: 76 | CIDR: '10.0.1.0/24' 77 | PublicTwo: 78 | CIDR: '10.0.2.0/24' 79 | 80 | Resources: 81 | # Secret to store the base64-encoded credentials 82 | CredentialsSecret: 83 | Type: AWS::SecretsManager::Secret 84 | Properties: 85 | Name: !Sub 'CredentialsSecret-${AWS::StackName}' 86 | SecretString: !Base64 { "Fn::Sub": "${CredentialsJson}" } 87 | 88 | # VPC stuff if we need to make one 89 | VPC: 90 | Condition: CreateVPC 91 | Type: AWS::EC2::VPC 92 | Properties: 93 | EnableDnsSupport: true 94 | EnableDnsHostnames: true 95 | CidrBlock: !Ref VPCCIDR 96 | PublicSubnetOne: # first subnet for the VPC 97 | Condition: CreateVPC 98 | Type: AWS::EC2::Subnet 99 | Properties: 100 | AvailabilityZone: !Select [0, !GetAZs ''] 101 | VpcId: !Ref VPC 102 | CidrBlock: !FindInMap ['SubnetConfig', 'PublicOne', 'CIDR'] 103 | MapPublicIpOnLaunch: true 104 | PublicSubnetTwo: # second subnet for the VPC 105 | Condition: CreateVPC 106 | Type: AWS::EC2::Subnet 107 | Properties: 108 | AvailabilityZone: !Select [1, !GetAZs ''] 109 | VpcId: !Ref VPC 110 | CidrBlock: !FindInMap ['SubnetConfig', 'PublicTwo', 'CIDR'] 111 | MapPublicIpOnLaunch: true 112 | InternetGateway: # need this for internet access in the new VPC 113 | Condition: CreateVPC 114 | Type: AWS::EC2::InternetGateway 115 | GatewayAttachement: 116 | Condition: CreateVPC 117 | Type: AWS::EC2::VPCGatewayAttachment 118 | Properties: 119 | VpcId: !Ref VPC 120 | InternetGatewayId: !Ref InternetGateway 121 | PublicRouteTable: 122 | Condition: CreateVPC 123 | Type: AWS::EC2::RouteTable 124 | Properties: 125 | VpcId: !Ref VPC 126 | PublicRoute: # sets up the route to the internet 127 | Condition: CreateVPC 128 | DependsOn: GatewayAttachement 129 | Type: AWS::EC2::Route 130 | Properties: 131 | RouteTableId: !Ref PublicRouteTable 132 | DestinationCidrBlock: '0.0.0.0/0' 133 | GatewayId: !Ref InternetGateway 134 | PublicSubnetOneRouteTableAssociation: 135 | Condition: CreateVPC 136 | Type: AWS::EC2::SubnetRouteTableAssociation 137 | Properties: 138 | SubnetId: !Ref PublicSubnetOne 139 | RouteTableId: !Ref PublicRouteTable 140 | PublicSubnetTwoRouteTableAssociation: 141 | Condition: CreateVPC 142 | Type: AWS::EC2::SubnetRouteTableAssociation 143 | Properties: 144 | SubnetId: !Ref PublicSubnetTwo 145 | RouteTableId: !Ref PublicRouteTable 146 | 147 | # ECS cluster to run stuff 148 | ECSCluster: 149 | Type: AWS::ECS::Cluster 150 | 151 | # SGs for Fargate and ALB 152 | LoadBalancerSG: 153 | Type: AWS::EC2::SecurityGroup 154 | Properties: 155 | GroupDescription: Access to the public facing load balancer 156 | VpcId: !If [CreateVPC, !Ref VPC, !Ref VPCID] 157 | SecurityGroupIngress: 158 | - IpProtocol: tcp 159 | FromPort: 443 160 | ToPort: 443 161 | CidrIp: 0.0.0.0/0 162 | Description: HTTPS from anywhere 163 | SecurityGroupEgress: 164 | - IpProtocol: tcp 165 | FromPort: 8080 166 | ToPort: 8080 167 | CidrIp: !If 168 | - CreateVPC 169 | - !GetAtt VPC.CidrBlock 170 | - 0.0.0.0/0 171 | FargateContainerSecurityGroup: 172 | Type: AWS::EC2::SecurityGroup 173 | Properties: 174 | GroupDescription: Access to the Fargate containers 175 | VpcId: !If [CreateVPC, !Ref VPC, !Ref VPCID] 176 | EcsSecurityGroupIngressFromPublicALB: 177 | Type: AWS::EC2::SecurityGroupIngress 178 | Properties: 179 | Description: Ingress from the public ALB 180 | GroupId: !Ref FargateContainerSecurityGroup 181 | IpProtocol: tcp 182 | FromPort: 8080 183 | ToPort: 8080 184 | SourceSecurityGroupId: !Ref LoadBalancerSG 185 | EcsSecurityGroupIngressFromSelf: 186 | Type: AWS::EC2::SecurityGroupIngress 187 | Properties: 188 | Description: Ingress for sync container communication 189 | GroupId: !Ref FargateContainerSecurityGroup 190 | IpProtocol: tcp 191 | FromPort: 8081 192 | ToPort: 8081 193 | SourceSecurityGroupId: !Ref FargateContainerSecurityGroup 194 | EcsSecurityGroupEgress: 195 | Type: AWS::EC2::SecurityGroupEgress 196 | Properties: 197 | GroupId: !Ref FargateContainerSecurityGroup 198 | IpProtocol: tcp 199 | FromPort: 443 200 | ToPort: 443 201 | CidrIp: 0.0.0.0/0 202 | Description: HTTPS to external services (e.g., Docker Hub) 203 | 204 | # ALB setup 205 | LoadBalancer: 206 | Type: AWS::ElasticLoadBalancingV2::LoadBalancer 207 | Properties: 208 | Scheme: internet-facing 209 | LoadBalancerAttributes: 210 | - Key: idle_timeout.timeout_seconds 211 | Value: '30' 212 | Subnets: !If 213 | - CreateVPC 214 | - [!Ref PublicSubnetOne, !Ref PublicSubnetTwo] 215 | - !Ref PublicSubnets 216 | SecurityGroups: [!Ref LoadBalancerSG] 217 | 218 | HTTPSCertificate: 219 | Type: AWS::CertificateManager::Certificate 220 | Properties: 221 | DomainName: !Ref DomainName 222 | ValidationMethod: DNS 223 | DomainValidationOptions: !If 224 | - CreateRoute53Records 225 | - - DomainName: !Ref DomainName 226 | HostedZoneId: !Ref Route53HostedZoneID 227 | - !Ref AWS::NoValue 228 | 229 | HTTPSListener: 230 | DependsOn: 231 | - LoadBalancer 232 | - HTTPSCertificate 233 | Type: AWS::ElasticLoadBalancingV2::Listener 234 | Properties: 235 | DefaultActions: 236 | - TargetGroupArn: !Ref ConnectTargetGroup 237 | Type: forward 238 | LoadBalancerArn: !Ref LoadBalancer 239 | Port: 443 240 | Protocol: HTTPS 241 | SslPolicy: ELBSecurityPolicy-TLS13-1-2-Res-2021-06 242 | Certificates: 243 | - CertificateArn: !Ref HTTPSCertificate 244 | 245 | ConnectTargetGroup: 246 | Type: AWS::ElasticLoadBalancingV2::TargetGroup 247 | DependsOn: 248 | - LoadBalancer 249 | Properties: 250 | HealthCheckIntervalSeconds: 6 251 | HealthCheckPath: /heartbeat 252 | HealthCheckProtocol: HTTP 253 | HealthCheckTimeoutSeconds: 5 254 | HealthyThresholdCount: 2 255 | TargetType: ip 256 | Name: connect 257 | Port: 8080 258 | Protocol: HTTP 259 | UnhealthyThresholdCount: 2 260 | VpcId: !If [CreateVPC, !Ref VPC, !Ref VPCID] 261 | TargetGroupAttributes: 262 | - Key: deregistration_delay.timeout_seconds 263 | Value: 60 264 | 265 | # DNS record for the ALB (if using Route 53) 266 | DNSRecord: 267 | Condition: CreateRoute53Records 268 | Type: AWS::Route53::RecordSet 269 | Properties: 270 | HostedZoneId: !Ref Route53HostedZoneID 271 | Comment: DNS record pointing to load balancer for 1Password Connect 272 | Name: !Ref DomainName 273 | Type: A 274 | AliasTarget: 275 | DNSName: !GetAtt LoadBalancer.DNSName 276 | HostedZoneId: !GetAtt LoadBalancer.CanonicalHostedZoneID 277 | 278 | # Service discovery for internal routing 279 | ServiceDiscoveryNamespace: 280 | Type: AWS::ServiceDiscovery::PrivateDnsNamespace 281 | Properties: 282 | Description: Private DNS namespace for 1Password Connect 283 | Vpc: !If [CreateVPC, !Ref VPC, !Ref VPCID] 284 | Name: onepassword 285 | 286 | ConnectServiceDiscovery: 287 | Type: AWS::ServiceDiscovery::Service 288 | DependsOn: 289 | - ServiceDiscoveryNamespace 290 | Properties: 291 | DnsConfig: 292 | DnsRecords: 293 | - TTL: 60 294 | Type: SRV 295 | Name: connect 296 | NamespaceId: !Ref ServiceDiscoveryNamespace 297 | HealthCheckCustomConfig: 298 | FailureThreshold: 1 299 | 300 | # IAM roles for ECS 301 | ECSRole: 302 | Type: AWS::IAM::Role 303 | Properties: 304 | AssumeRolePolicyDocument: 305 | Version: '2012-10-17' 306 | Statement: 307 | - Effect: Allow 308 | Principal: 309 | Service: ecs-tasks.amazonaws.com 310 | Action: sts:AssumeRole 311 | Policies: 312 | - PolicyName: ECSFargatePermissions 313 | PolicyDocument: 314 | Version: '2012-10-17' 315 | Statement: 316 | - Effect: Allow 317 | Action: 318 | - ec2:AttachNetworkInterface 319 | - ec2:CreateNetworkInterface 320 | - ec2:DeleteNetworkInterface 321 | - ec2:DescribeNetworkInterfaces 322 | - ec2:DetachNetworkInterface 323 | - elasticloadbalancing:RegisterTargets 324 | - elasticloadbalancing:DeregisterTargets 325 | - elasticloadbalancing:DescribeTargetHealth 326 | Resource: '*' 327 | 328 | ECSTaskExecutionRole: 329 | Type: AWS::IAM::Role 330 | Properties: 331 | AssumeRolePolicyDocument: 332 | Version: '2012-10-17' 333 | Statement: 334 | - Effect: Allow 335 | Principal: 336 | Service: ecs-tasks.amazonaws.com 337 | Action: sts:AssumeRole 338 | Policies: 339 | - PolicyName: AmazonECSTaskExecutionRolePolicy 340 | PolicyDocument: 341 | Version: '2012-10-17' 342 | Statement: 343 | - Effect: Allow 344 | Action: 345 | - ecr:GetAuthorizationToken 346 | - ecr:BatchCheckLayerAvailability 347 | - ecr:GetDownloadUrlForLayer 348 | - ecr:BatchGetImage 349 | - logs:CreateLogStream 350 | - logs:PutLogEvents 351 | Resource: '*' 352 | - PolicyName: SecretAccess 353 | PolicyDocument: 354 | Version: '2012-10-17' 355 | Statement: 356 | - Effect: Allow 357 | Action: 358 | - secretsmanager:GetSecretValue 359 | Resource: !Sub 'arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:CredentialsSecret-${AWS::StackName}*' 360 | 361 | # Logs in CloudWatch 362 | CloudWatchLogsGroup: 363 | Type: AWS::Logs::LogGroup 364 | Properties: 365 | LogGroupName: onepassword-connect 366 | RetentionInDays: 30 367 | 368 | # Task def for the Connect containers 369 | TaskDefinition: 370 | Type: AWS::ECS::TaskDefinition 371 | DependsOn: 372 | - ECSTaskExecutionRole 373 | - CloudWatchLogsGroup 374 | Properties: 375 | Cpu: 256 376 | Memory: 512 377 | NetworkMode: awsvpc 378 | RequiresCompatibilities: 379 | - FARGATE 380 | ExecutionRoleArn: !Ref ECSTaskExecutionRole 381 | TaskRoleArn: !Ref ECSRole 382 | Volumes: 383 | - Name: connect-data 384 | ContainerDefinitions: 385 | - Name: connect-api 386 | Image: 1password/connect-api:latest 387 | PortMappings: 388 | - ContainerPort: 8080 389 | Protocol: tcp 390 | MountPoints: 391 | - ContainerPath: /home/opuser/.op/data 392 | SourceVolume: connect-data 393 | Secrets: 394 | - Name: OP_SESSION 395 | ValueFrom: !Ref CredentialsSecret 396 | LogConfiguration: 397 | LogDriver: awslogs 398 | Options: 399 | awslogs-group: !Ref CloudWatchLogsGroup 400 | awslogs-region: !Ref AWS::Region 401 | awslogs-stream-prefix: connect-api 402 | - Name: connect-sync 403 | Image: 1password/connect-sync:latest 404 | MountPoints: 405 | - ContainerPath: /home/opuser/.op/data 406 | SourceVolume: connect-data 407 | Environment: 408 | - Name: OP_HTTP_PORT 409 | Value: '8081' 410 | Secrets: 411 | - Name: OP_SESSION 412 | ValueFrom: !Ref CredentialsSecret 413 | LogConfiguration: 414 | LogDriver: awslogs 415 | Options: 416 | awslogs-group: !Ref CloudWatchLogsGroup 417 | awslogs-region: !Ref AWS::Region 418 | awslogs-stream-prefix: connect-sync 419 | 420 | # ECS service to run the containers 421 | ConnectService: 422 | Type: AWS::ECS::Service 423 | DependsOn: 424 | - LoadBalancer 425 | - HTTPSListener 426 | Properties: 427 | ServiceName: connect 428 | Cluster: !Ref ECSCluster 429 | LaunchType: FARGATE 430 | NetworkConfiguration: 431 | AwsvpcConfiguration: 432 | AssignPublicIp: ENABLED 433 | SecurityGroups: 434 | - !Ref FargateContainerSecurityGroup 435 | Subnets: !If 436 | - CreateVPC 437 | - [!Ref PublicSubnetOne, !Ref PublicSubnetTwo] 438 | - !Ref PublicSubnets 439 | TaskDefinition: !Ref TaskDefinition 440 | LoadBalancers: 441 | - ContainerName: connect-api 442 | ContainerPort: 8080 443 | TargetGroupArn: !Ref ConnectTargetGroup 444 | ServiceRegistries: 445 | - ContainerName: connect-api 446 | ContainerPort: 8080 447 | RegistryArn: !GetAtt ConnectServiceDiscovery.Arn 448 | 449 | # Stuff you'll need after deployment 450 | Outputs: 451 | CNAMEName: 452 | Condition: OutputDNSRecordContent 453 | Description: Name of a DNS record to point to the load balancer (use with CNAME Value for manual setup) 454 | Value: !Sub ${DomainName}. 455 | CNAMEValue: 456 | Condition: OutputDNSRecordContent 457 | Description: Value of a DNS record to point to the load balancer (use with CNAME Name for manual setup) 458 | Value: !Sub ${LoadBalancer.DNSName}. 459 | ExternalUrl: 460 | Description: The URL of the external load balancer 461 | Value: !Join ['', ['https://', !GetAtt LoadBalancer.DNSName]] 462 | Export: 463 | Name: !Join [':', [!Ref AWS::StackName, 'ExternalUrl']] 464 | -------------------------------------------------------------------------------- /docs/openapi/spec.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.2" 2 | info: 3 | title: 1Password Connect 4 | description: >- 5 | REST API interface for 1Password Connect. 6 | version: "1.7.1" 7 | contact: 8 | name: 1Password Integrations 9 | email: support@1password.com 10 | url: https://support.1password.com/ 11 | servers: 12 | - url: http://localhost:8080/v1 13 | 14 | tags: 15 | - name: Items 16 | description: Access and manage items inside 1Password Vaults 17 | - name: Vaults 18 | description: Access 1Password Vaults 19 | - name: Activity 20 | description: Access API Request Activity 21 | 22 | components: 23 | securitySchemes: 24 | ConnectToken: 25 | type: http 26 | scheme: bearer 27 | bearerFormat: JWT 28 | schemas: 29 | ErrorResponse: 30 | type: object 31 | properties: 32 | status: 33 | type: integer 34 | description: HTTP Status Code 35 | message: 36 | type: string 37 | description: A message detailing the error 38 | File: 39 | type: object 40 | properties: 41 | id: 42 | type: string 43 | description: ID of the file 44 | name: 45 | type: string 46 | description: Name of the file 47 | size: 48 | type: integer 49 | description: Size in bytes of the file 50 | content_path: 51 | type: string 52 | description: Path of the Connect API that can be used to download the contents of this file. 53 | readOnly: true 54 | section: 55 | type: object 56 | description: For files that are in a section, this field describes the section. 57 | properties: 58 | id: 59 | type: string 60 | content: 61 | type: string 62 | format: byte 63 | description: Base64-encoded contents of the file. Only set if size <= OP_MAX_INLINE_FILE_SIZE_KB kb and `inline_files` is set to `true`. 64 | example: 65 | id: 6r65pjq33banznomn7q22sj44e 66 | name: foo.txt 67 | size: 35 68 | content_path: v1/vaults/ionaiwtdvgclrixbt6ztpqcxnq/items/p7eflcy7f5mk7vg6zrzf5rjjyu/files/6r65pjq33banznomn7q22sj44e/content 69 | content: VGhlIGZ1dHVyZSBiZWxvbmdzIHRvIHRoZSBjdXJpb3VzLgo= 70 | Vault: 71 | type: object 72 | properties: 73 | id: 74 | type: string 75 | pattern: '^[\da-z]{26}$' 76 | name: 77 | type: string 78 | description: 79 | type: string 80 | attributeVersion: 81 | type: integer 82 | description: The vault version 83 | contentVersion: 84 | type: integer 85 | description: The version of the vault contents 86 | items: 87 | description: Number of active items in the vault 88 | type: integer 89 | type: 90 | type: string 91 | enum: 92 | - USER_CREATED 93 | - PERSONAL 94 | - EVERYONE 95 | - TRANSFER 96 | createdAt: 97 | type: string 98 | format: date-time 99 | readOnly: true 100 | updatedAt: 101 | type: string 102 | format: date-time 103 | readOnly: true 104 | GeneratorRecipe: 105 | description: The recipe is used in conjunction with the "generate" property to set the character set used to generate a new secure value 106 | type: object 107 | properties: 108 | length: 109 | type: integer 110 | description: Length of the generated value 111 | default: 32 112 | minimum: 1 113 | maximum: 64 114 | characterSets: 115 | type: array 116 | items: 117 | type: string 118 | enum: 119 | - LETTERS 120 | - DIGITS 121 | - SYMBOLS 122 | minimum: 0 123 | maximum: 3 124 | uniqueItems: true 125 | excludeCharacters: 126 | type: string 127 | description: "List of all characters that should be excluded from generated passwords." 128 | example: "abc1" 129 | Item: 130 | type: object 131 | required: 132 | - vault 133 | - category 134 | properties: 135 | id: 136 | type: string 137 | pattern: '^[\da-z]{26}$' 138 | title: 139 | type: string 140 | vault: 141 | type: object 142 | required: 143 | - id 144 | properties: 145 | id: 146 | type: string 147 | pattern: '^[\da-z]{26}$' 148 | category: 149 | type: string 150 | enum: 151 | - "LOGIN" 152 | - "PASSWORD" 153 | - "API_CREDENTIAL" 154 | - "SERVER" 155 | - "DATABASE" 156 | - "CREDIT_CARD" 157 | - "MEMBERSHIP" 158 | - "PASSPORT" 159 | - "SOFTWARE_LICENSE" 160 | - "OUTDOOR_LICENSE" 161 | - "SECURE_NOTE" 162 | - "WIRELESS_ROUTER" 163 | - "BANK_ACCOUNT" 164 | - "DRIVER_LICENSE" 165 | - "IDENTITY" 166 | - "REWARD_PROGRAM" 167 | - "DOCUMENT" 168 | - "EMAIL_ACCOUNT" 169 | - "SOCIAL_SECURITY_NUMBER" 170 | - "MEDICAL_RECORD" 171 | - "SSH_KEY" 172 | - "CUSTOM" 173 | urls: 174 | type: array 175 | items: 176 | type: object 177 | required: 178 | - href 179 | properties: 180 | label: 181 | type: string 182 | primary: 183 | type: boolean 184 | href: 185 | type: string 186 | format: url 187 | example: 188 | - primary: true 189 | href: https://example.com 190 | - href: https://example.org 191 | favorite: 192 | type: boolean 193 | default: false 194 | tags: 195 | type: array 196 | items: 197 | type: string 198 | version: 199 | type: integer 200 | state: 201 | type: string 202 | readOnly: true 203 | enum: 204 | - "ARCHIVED" 205 | - "DELETED" 206 | createdAt: 207 | type: string 208 | format: date-time 209 | readOnly: true 210 | updatedAt: 211 | type: string 212 | format: date-time 213 | readOnly: true 214 | lastEditedBy: 215 | type: string 216 | readOnly: true 217 | FullItem: 218 | allOf: 219 | - $ref: "#/components/schemas/Item" 220 | - type: object 221 | properties: 222 | sections: 223 | type: array 224 | items: 225 | type: object 226 | properties: 227 | id: 228 | type: string 229 | label: 230 | type: string 231 | fields: 232 | type: array 233 | items: 234 | $ref: '#/components/schemas/Field' 235 | files: 236 | type: array 237 | items: 238 | $ref: '#/components/schemas/File' 239 | Field: 240 | type: object 241 | required: 242 | - id 243 | - type 244 | properties: 245 | id: 246 | type: string 247 | section: 248 | type: object 249 | properties: 250 | id: 251 | type: string 252 | type: 253 | type: string 254 | default: "STRING" 255 | enum: 256 | - "STRING" 257 | - "EMAIL" 258 | - "CONCEALED" 259 | - "URL" 260 | - "TOTP" 261 | - "DATE" 262 | - "MONTH_YEAR" 263 | - "MENU" 264 | purpose: 265 | description: Some item types, Login and Password, have fields used for autofill. This property indicates that purpose and is required for some item types. 266 | type: string 267 | enum: 268 | - "" 269 | - "USERNAME" 270 | - "PASSWORD" 271 | - "NOTES" 272 | label: 273 | type: string 274 | value: 275 | type: string 276 | generate: 277 | description: If value is not present then a new value should be generated for this field 278 | type: boolean 279 | default: false 280 | recipe: 281 | $ref: "#/components/schemas/GeneratorRecipe" 282 | entropy: 283 | description: For fields with a purpose of `PASSWORD` this is the entropy of the value 284 | type: number 285 | readOnly: true 286 | APIRequest: 287 | description: Represents a request that was made to the API. Including what Token was used and what resource was accessed. 288 | type: object 289 | properties: 290 | requestId: 291 | description: The unique id used to identify a single request. 292 | type: string 293 | format: uuid 294 | timestamp: 295 | description: The time at which the request was processed by the server. 296 | type: string 297 | format: date-time 298 | readOnly: true 299 | action: 300 | type: string 301 | enum: 302 | - READ 303 | - CREATE 304 | - UPDATE 305 | - DELETE 306 | result: 307 | type: string 308 | enum: 309 | - SUCCESS 310 | - DENY 311 | actor: 312 | type: object 313 | properties: 314 | id: 315 | type: string 316 | format: uuid 317 | account: 318 | type: string 319 | jti: 320 | type: string 321 | userAgent: 322 | type: string 323 | requestIp: 324 | type: string 325 | resource: 326 | type: object 327 | properties: 328 | type: 329 | type: string 330 | enum: 331 | - ITEM 332 | - VAULT 333 | vault: 334 | type: object 335 | properties: 336 | id: 337 | type: string 338 | pattern: '^[\da-z]{26}$' 339 | item: 340 | type: object 341 | properties: 342 | id: 343 | type: string 344 | pattern: '^[\da-z]{26}$' 345 | itemVersion: 346 | type: integer 347 | Patch: 348 | type: array 349 | items: 350 | type: object 351 | properties: 352 | op: 353 | type: string 354 | enum: [ add, remove, replace ] 355 | path: 356 | type: string 357 | description: An RFC6901 JSON Pointer pointing to the Item document, an Item Attribute, and Item Field by Field ID, or an Item Field Attribute 358 | example: "/fields/06gnn2b95example10q91512p5/label" 359 | value: 360 | type: object 361 | required: 362 | - op 363 | - path 364 | ServiceDependency: 365 | description: The state of a registered server dependency. 366 | type: object 367 | properties: 368 | service: 369 | type: string 370 | status: 371 | type: string 372 | message: 373 | type: string 374 | description: Human-readable message for explaining the current state. 375 | 376 | 377 | paths: 378 | /activity: 379 | get: 380 | operationId: GetApiActivity 381 | tags: 382 | - Activity 383 | summary: Retrieve a list of API Requests that have been made. 384 | security: 385 | - ConnectToken: [ ] 386 | parameters: 387 | - in: query 388 | name: limit 389 | schema: 390 | type: integer 391 | example: 10 392 | default: 50 393 | description: How many API Events should be retrieved in a single request. 394 | - in: query 395 | name: offset 396 | schema: 397 | type: integer 398 | example: 50 399 | default: 0 400 | description: How far into the collection of API Events should the response start 401 | responses: 402 | "200": 403 | description: OK 404 | headers: 405 | Content-Range: 406 | description: An decription of what part of the collection has been returned as well as the total size. 407 | schema: 408 | type: string 409 | example: 1-50/1134 410 | content: 411 | application/json: 412 | schema: 413 | type: array 414 | items: 415 | $ref: "#/components/schemas/APIRequest" 416 | "401": 417 | description: Invalid or missing token 418 | content: 419 | application/json: 420 | schema: 421 | $ref: "#/components/schemas/ErrorResponse" 422 | example: 423 | status: 401 424 | message: Invalid token signature 425 | /vaults: 426 | get: 427 | operationId: GetVaults 428 | tags: 429 | - Vaults 430 | summary: Get all Vaults 431 | security: 432 | - ConnectToken: [ ] 433 | parameters: 434 | - in: query 435 | name: filter 436 | schema: 437 | type: string 438 | example: name eq "Some Vault Name" 439 | description: Filter the Vault collection based on Vault name using SCIM eq filter 440 | responses: 441 | "200": 442 | description: OK 443 | content: 444 | application/json: 445 | schema: 446 | type: array 447 | items: 448 | $ref: "#/components/schemas/Vault" 449 | "401": 450 | description: Invalid or missing token 451 | content: 452 | application/json: 453 | schema: 454 | $ref: "#/components/schemas/ErrorResponse" 455 | example: 456 | status: 401 457 | message: Invalid token signature 458 | /vaults/{vaultUuid}: 459 | get: 460 | operationId: GetVaultById 461 | tags: 462 | - Vaults 463 | summary: Get Vault details and metadata 464 | security: 465 | - ConnectToken: [ ] 466 | parameters: 467 | - in: path 468 | name: vaultUuid 469 | schema: 470 | type: string 471 | pattern: '^[\da-z]{26}$' 472 | required: true 473 | description: The UUID of the Vault to fetch Items from 474 | responses: 475 | "200": 476 | description: OK 477 | content: 478 | application/json: 479 | schema: 480 | $ref: "#/components/schemas/Vault" 481 | "401": 482 | description: Invalid or missing token 483 | content: 484 | application/json: 485 | schema: 486 | $ref: "#/components/schemas/ErrorResponse" 487 | example: 488 | status: 401 489 | message: Invalid token signature 490 | "403": 491 | description: Unauthorized access 492 | content: 493 | application/json: 494 | schema: 495 | $ref: "#/components/schemas/ErrorResponse" 496 | example: 497 | status: 403 498 | message: vault {vaultUuid} is not in scope 499 | "404": 500 | description: Vault not found 501 | content: 502 | application/json: 503 | schema: 504 | $ref: "#/components/schemas/ErrorResponse" 505 | example: 506 | status: 404 507 | message: vault {itemUuid} not found 508 | /vaults/{vaultUuid}/items: 509 | get: 510 | operationId: GetVaultItems 511 | tags: 512 | - Items 513 | summary: Get all items for inside a Vault 514 | security: 515 | - ConnectToken: [ ] 516 | parameters: 517 | - in: path 518 | name: vaultUuid 519 | schema: 520 | type: string 521 | pattern: '^[\da-z]{26}$' 522 | required: true 523 | description: The UUID of the Vault to fetch Items from 524 | - in: query 525 | name: filter 526 | schema: 527 | type: string 528 | example: title eq "Some Item Name" 529 | description: Filter the Item collection based on Item name using SCIM eq filter 530 | responses: 531 | "200": 532 | description: OK 533 | content: 534 | application/json: 535 | schema: 536 | type: array 537 | items: 538 | $ref: "#/components/schemas/Item" 539 | "401": 540 | description: Invalid or missing token 541 | content: 542 | application/json: 543 | schema: 544 | $ref: "#/components/schemas/ErrorResponse" 545 | example: 546 | status: 401 547 | message: Invalid token signature 548 | "404": 549 | description: Vault not found 550 | content: 551 | application/json: 552 | schema: 553 | $ref: "#/components/schemas/ErrorResponse" 554 | example: 555 | status: 404 556 | message: vault {vaultUuid} not found 557 | post: 558 | operationId: CreateVaultItem 559 | tags: 560 | - Items 561 | summary: Create a new Item 562 | security: 563 | - ConnectToken: [ ] 564 | parameters: 565 | - in: path 566 | name: vaultUuid 567 | schema: 568 | type: string 569 | pattern: '^[\da-z]{26}$' 570 | required: true 571 | description: The UUID of the Vault to create an Item in 572 | requestBody: 573 | content: 574 | application/json: 575 | schema: 576 | $ref: "#/components/schemas/FullItem" 577 | responses: 578 | "200": 579 | description: OK 580 | content: 581 | application/json: 582 | schema: 583 | $ref: "#/components/schemas/FullItem" 584 | "400": 585 | description: Unable to create item due to invalid input 586 | content: 587 | application/json: 588 | schema: 589 | $ref: "#/components/schemas/ErrorResponse" 590 | example: 591 | status: 400 592 | message: Invalid item category 593 | "401": 594 | description: Invalid or missing token 595 | content: 596 | application/json: 597 | schema: 598 | $ref: "#/components/schemas/ErrorResponse" 599 | example: 600 | status: 401 601 | message: Invalid token signature 602 | "403": 603 | description: Unauthorized access 604 | content: 605 | application/json: 606 | schema: 607 | $ref: "#/components/schemas/ErrorResponse" 608 | example: 609 | status: 403 610 | message: vault {vaultUuid} is not in scope 611 | "404": 612 | description: Item not found 613 | content: 614 | application/json: 615 | schema: 616 | $ref: "#/components/schemas/ErrorResponse" 617 | examples: 618 | vaultNotFound: 619 | summary: Vault not found 620 | value: 621 | status: 404 622 | message: vault {vaultUuid} not found 623 | /vaults/{vaultUuid}/items/{itemUuid}: 624 | get: 625 | operationId: GetVaultItemById 626 | tags: 627 | - Items 628 | summary: Get the details of an Item 629 | security: 630 | - ConnectToken: [ ] 631 | parameters: 632 | - in: path 633 | name: vaultUuid 634 | schema: 635 | type: string 636 | pattern: '^[\da-z]{26}$' 637 | required: true 638 | description: The UUID of the Vault to fetch Item from 639 | - in: path 640 | name: itemUuid 641 | schema: 642 | type: string 643 | pattern: '^[\da-z]{26}$' 644 | required: true 645 | description: The UUID of the Item to fetch 646 | responses: 647 | "200": 648 | description: OK 649 | content: 650 | application/json: 651 | schema: 652 | $ref: "#/components/schemas/FullItem" 653 | "401": 654 | description: Invalid or missing token 655 | content: 656 | application/json: 657 | schema: 658 | $ref: "#/components/schemas/ErrorResponse" 659 | example: 660 | status: 401 661 | message: Invalid token signature 662 | "403": 663 | description: Unauthorized access 664 | content: 665 | application/json: 666 | schema: 667 | $ref: "#/components/schemas/ErrorResponse" 668 | example: 669 | status: 403 670 | message: vault {vaultUuid} is not in scope 671 | "404": 672 | description: Item not found 673 | content: 674 | application/json: 675 | schema: 676 | $ref: "#/components/schemas/ErrorResponse" 677 | examples: 678 | itemNotFound: 679 | summary: Item not found 680 | value: 681 | status: 404 682 | message: item {itemUuid} not found 683 | vaultNotFound: 684 | summary: Vault not found 685 | value: 686 | status: 404 687 | message: vault {vaultUuid} not found 688 | put: 689 | operationId: UpdateVaultItem 690 | tags: 691 | - Items 692 | summary: Update an Item 693 | security: 694 | - ConnectToken: [ ] 695 | parameters: 696 | - in: path 697 | name: vaultUuid 698 | schema: 699 | type: string 700 | pattern: '^[\da-z]{26}$' 701 | required: true 702 | description: The UUID of the Item's Vault 703 | - in: path 704 | name: itemUuid 705 | schema: 706 | type: string 707 | pattern: '^[\da-z]{26}$' 708 | required: true 709 | description: The UUID of the Item to update 710 | requestBody: 711 | content: 712 | application/json: 713 | schema: 714 | $ref: "#/components/schemas/FullItem" 715 | responses: 716 | "200": 717 | description: OK 718 | content: 719 | application/json: 720 | schema: 721 | $ref: "#/components/schemas/FullItem" 722 | "400": 723 | description: Unable to create item due to invalid input 724 | content: 725 | application/json: 726 | schema: 727 | $ref: "#/components/schemas/ErrorResponse" 728 | example: 729 | status: 400 730 | message: The item doesn't have a {example field name} field. 731 | "401": 732 | description: Invalid or missing token 733 | content: 734 | application/json: 735 | schema: 736 | $ref: "#/components/schemas/ErrorResponse" 737 | example: 738 | status: 401 739 | message: Invalid token signature 740 | "403": 741 | description: Unauthorized access 742 | content: 743 | application/json: 744 | schema: 745 | $ref: "#/components/schemas/ErrorResponse" 746 | example: 747 | status: 403 748 | message: vault {vaultUuid} is not in scope 749 | "404": 750 | description: Item not found 751 | content: 752 | application/json: 753 | schema: 754 | $ref: "#/components/schemas/ErrorResponse" 755 | examples: 756 | itemNotFound: 757 | summary: Item not found 758 | value: 759 | status: 404 760 | message: item {itemUuid} not found 761 | vaultNotFound: 762 | summary: Vault not found 763 | value: 764 | status: 404 765 | message: vault {vaultUuid} not found 766 | delete: 767 | operationId: DeleteVaultItem 768 | tags: 769 | - Items 770 | summary: Delete an Item 771 | security: 772 | - ConnectToken: [ ] 773 | parameters: 774 | - in: path 775 | name: vaultUuid 776 | schema: 777 | type: string 778 | pattern: '^[\da-z]{26}$' 779 | required: true 780 | description: The UUID of the Vault the item is in 781 | - in: path 782 | name: itemUuid 783 | schema: 784 | type: string 785 | pattern: '^[\da-z]{26}$' 786 | required: true 787 | description: The UUID of the Item to update 788 | responses: 789 | "204": 790 | description: Successfully deleted an item 791 | "401": 792 | description: Invalid or missing token 793 | content: 794 | application/json: 795 | schema: 796 | $ref: "#/components/schemas/ErrorResponse" 797 | example: 798 | status: 401 799 | message: Invalid token signature 800 | "403": 801 | description: Unauthorized access 802 | content: 803 | application/json: 804 | schema: 805 | $ref: "#/components/schemas/ErrorResponse" 806 | example: 807 | status: 403 808 | message: vault {vaultUuid} is not in scope 809 | "404": 810 | description: Item not found 811 | content: 812 | application/json: 813 | schema: 814 | $ref: "#/components/schemas/ErrorResponse" 815 | examples: 816 | vaultNotFound: 817 | summary: Vault not found 818 | value: 819 | status: 404 820 | message: vault {vaultUuid} not found 821 | patch: 822 | description: > 823 | Applies a modified [RFC6902 JSON Patch](https://tools.ietf.org/html/rfc6902) document to an Item or ItemField. This endpoint only supports `add`, `remove` and `replace` operations. 824 | 825 | 826 | When modifying a specific ItemField, the ItemField's ID in the `path` attribute of the operation object: `/fields/{fieldId}` 827 | 828 | operationId: PatchVaultItem 829 | tags: 830 | - Items 831 | summary: Update a subset of Item attributes 832 | security: 833 | - ConnectToken: [ ] 834 | parameters: 835 | - in: path 836 | name: vaultUuid 837 | schema: 838 | type: string 839 | pattern: '^[\da-z]{26}$' 840 | required: true 841 | description: The UUID of the Vault the item is in 842 | - in: path 843 | name: itemUuid 844 | schema: 845 | type: string 846 | pattern: '^[\da-z]{26}$' 847 | required: true 848 | description: The UUID of the Item to update 849 | requestBody: 850 | content: 851 | application/json: 852 | schema: 853 | $ref: "#/components/schemas/Patch" 854 | examples: 855 | ReplaceAllAttributes: 856 | value: 857 | - op: replace 858 | path: "/" 859 | value: { "title": "New Title", "favorite": true, "tags": [ "tag1", "tag2" ], "...": "Any attr from FullItem schema" } 860 | summary: Replace an entire Item with new fields. Equivalent to a PUT request. 861 | PatchItemAttr: 862 | value: 863 | - op: "replace" 864 | path: "/favorite" 865 | value: true 866 | - op: "remove" 867 | path: "/tags/1" 868 | summary: Update specific Item attributes 869 | PatchItemField: 870 | value: 871 | - op: "add" 872 | path: "/fields" 873 | value: { "label": "New Field", "type": "string", "value": "hunter2" } 874 | summary: Add a new ItemField to the Item 875 | PatchItemFieldWithID: 876 | value: 877 | - op: "replace" 878 | path: "/fields/r9qxq7xnhfhukoxsc8ymqr0y11" 879 | value: { "label": "Replacement Title", "type": "string", "value": "new value" } 880 | - op: "remove" 881 | path: "/fields/h2nl155dshi043yse7wa3u1hs7" 882 | summary: Modify or remove an ItemField. 883 | PatchItemFieldAttr: 884 | value: 885 | - op: "add" 886 | path: "/fields/s2ju540zlna8bdj4uro7sj64rk/label" 887 | value: "New field name" 888 | - op: "remove" 889 | path: "/fields/s2ju540zlna8bdj4uro7sj64rk/value" 890 | summary: Modify a specific ItemField attribute. 891 | 892 | responses: 893 | "200": 894 | description: OK - Item updated. If no Patch operations were provided, Item is unmodified. 895 | content: 896 | application/json: 897 | schema: 898 | $ref: "#/components/schemas/FullItem" 899 | "401": 900 | description: Invalid or missing token 901 | content: 902 | application/json: 903 | schema: 904 | $ref: "#/components/schemas/ErrorResponse" 905 | example: 906 | status: 401 907 | message: Invalid token signature 908 | "403": 909 | description: Unauthorized access 910 | content: 911 | application/json: 912 | schema: 913 | $ref: "#/components/schemas/ErrorResponse" 914 | example: 915 | status: 403 916 | message: vault {vaultUuid} is not in scope 917 | "404": 918 | description: Item not found 919 | content: 920 | application/json: 921 | schema: 922 | $ref: "#/components/schemas/ErrorResponse" 923 | examples: 924 | itemNotFound: 925 | summary: Item not found 926 | value: 927 | status: 404 928 | message: item {itemUuid} not found 929 | vaultNotFound: 930 | summary: Vault not found 931 | value: 932 | status: 404 933 | message: vault {vaultUuid} not found 934 | /vaults/{vaultUuid}/items/{itemUuid}/files: 935 | get: 936 | operationId: GetItemFiles 937 | tags: 938 | - Files 939 | summary: Get all the files inside an Item 940 | security: 941 | - ConnectToken: [ ] 942 | parameters: 943 | - in: path 944 | name: vaultUuid 945 | schema: 946 | type: string 947 | format: uuid 948 | required: true 949 | description: The UUID of the Vault to fetch Items from 950 | - in: path 951 | name: itemUuid 952 | schema: 953 | type: string 954 | format: uuid 955 | required: true 956 | description: The UUID of the Item to fetch files from 957 | - in: query 958 | name: inline_files 959 | schema: 960 | type: boolean 961 | example: true 962 | description: Tells server to return the base64-encoded file contents in the response. 963 | responses: 964 | "200": 965 | description: OK 966 | content: 967 | application/json: 968 | schema: 969 | type: array 970 | items: 971 | $ref: "#/components/schemas/File" 972 | "401": 973 | description: Invalid or missing token 974 | content: 975 | application/json: 976 | schema: 977 | $ref: "#/components/schemas/ErrorResponse" 978 | example: 979 | status: 401 980 | message: Invalid token signature 981 | "404": 982 | description: Item not found 983 | content: 984 | application/json: 985 | schema: 986 | $ref: "#/components/schemas/ErrorResponse" 987 | examples: 988 | itemNotFound: 989 | summary: Item not found 990 | value: 991 | status: 404 992 | message: item {itemUuid} not found 993 | vaultNotFound: 994 | summary: Vault not found 995 | value: 996 | status: 404 997 | message: vault {vaultUuid} not found 998 | "413": 999 | description: File content too large to display 1000 | content: 1001 | application/json: 1002 | schema: 1003 | $ref: "#/components/schemas/ErrorResponse" 1004 | examples: 1005 | fileTooLarge: 1006 | summary: File too large 1007 | value: 1008 | status: 413 1009 | message: File is too large to inline in request. Use the /v1/vaults/{vaultUUID}/items/{itemUUID}/files/{fileUUID}/content endpoint instead. 1010 | /vaults/{vaultUuid}/items/{itemUuid}/files/{fileUuid}: 1011 | get: 1012 | operationId: GetDetailsOfFileById 1013 | tags: 1014 | - Files 1015 | summary: Get the details of a File 1016 | security: 1017 | - ConnectToken: [ ] 1018 | parameters: 1019 | - in: path 1020 | name: vaultUuid 1021 | schema: 1022 | type: string 1023 | format: uuid 1024 | required: true 1025 | description: The UUID of the Vault to fetch Item from 1026 | - in: path 1027 | name: itemUuid 1028 | schema: 1029 | type: string 1030 | format: uuid 1031 | required: true 1032 | description: The UUID of the Item to fetch File from 1033 | - in: path 1034 | name: fileUuid 1035 | schema: 1036 | type: string 1037 | format: uuid 1038 | required: true 1039 | description: The UUID of the File to fetch 1040 | - in: query 1041 | name: inline_files 1042 | schema: 1043 | type: boolean 1044 | example: true 1045 | description: Tells server to return the base64-encoded file contents in the response. 1046 | responses: 1047 | "200": 1048 | description: OK 1049 | content: 1050 | application/json: 1051 | schema: 1052 | $ref: "#/components/schemas/File" 1053 | "401": 1054 | description: Invalid or missing token 1055 | content: 1056 | application/json: 1057 | schema: 1058 | $ref: "#/components/schemas/ErrorResponse" 1059 | example: 1060 | status: 401 1061 | message: Invalid token signature 1062 | "403": 1063 | description: Unauthorized access 1064 | content: 1065 | application/json: 1066 | schema: 1067 | $ref: "#/components/schemas/ErrorResponse" 1068 | example: 1069 | status: 403 1070 | message: vault {vaultUuid} is not in scope 1071 | "404": 1072 | description: File not found 1073 | content: 1074 | application/json: 1075 | schema: 1076 | $ref: "#/components/schemas/ErrorResponse" 1077 | examples: 1078 | fileNotFound: 1079 | summary: File not found 1080 | value: 1081 | status: 404 1082 | message: file {fileUuid} not found 1083 | itemNotFound: 1084 | summary: Item not found 1085 | value: 1086 | status: 404 1087 | message: item {itemUuid} not found 1088 | vaultNotFound: 1089 | summary: Vault not found 1090 | value: 1091 | status: 404 1092 | message: vault {vaultUuid} not found 1093 | "413": 1094 | description: File content too large to display 1095 | content: 1096 | application/json: 1097 | schema: 1098 | $ref: "#/components/schemas/ErrorResponse" 1099 | examples: 1100 | fileTooLarge: 1101 | summary: File too large 1102 | value: 1103 | status: 413 1104 | message: File is too large to inline in request. Use the /v1/vaults/{vaultUUID}/items/{itemUUID}/files/{fileUUID}/content endpoint instead. 1105 | /vaults/{vaultUuid}/items/{itemUuid}/files/{fileUuid}/content: 1106 | parameters: 1107 | - in: path 1108 | name: vaultUuid 1109 | schema: 1110 | type: string 1111 | format: uuid 1112 | required: true 1113 | description: The UUID of the Vault the item is in 1114 | - in: path 1115 | name: itemUuid 1116 | schema: 1117 | type: string 1118 | format: uuid 1119 | required: true 1120 | description: The UUID of the Item the File is in 1121 | - in: path 1122 | name: fileUuid 1123 | required: true 1124 | schema: 1125 | type: string 1126 | description: UUID of the file to get content from 1127 | get: 1128 | operationId: DownloadFileByID 1129 | tags: 1130 | - Files 1131 | summary: Get the content of a File 1132 | security: 1133 | - ConnectToken: [ ] 1134 | responses: 1135 | "200": 1136 | description: "Success" 1137 | content: 1138 | application/octet-stream: 1139 | schema: 1140 | type: string 1141 | format: binary 1142 | headers: 1143 | Content-Disposition: 1144 | schema: 1145 | type: string 1146 | example: attachment; filename="privkey.pem" 1147 | Content-Length: 1148 | schema: 1149 | type: string 1150 | example: "6432" 1151 | "401": 1152 | description: Invalid or missing token 1153 | content: 1154 | application/json: 1155 | schema: 1156 | $ref: "#/components/schemas/ErrorResponse" 1157 | example: 1158 | status: 401 1159 | message: Invalid token signature 1160 | "404": 1161 | description: File not found 1162 | content: 1163 | application/json: 1164 | schema: 1165 | $ref: "#/components/schemas/ErrorResponse" 1166 | examples: 1167 | fileNotFound: 1168 | summary: File not found 1169 | value: 1170 | status: 404 1171 | message: file {fileUuid} not found 1172 | itemNotFound: 1173 | summary: Item not found 1174 | value: 1175 | status: 404 1176 | message: item {itemUuid} not found 1177 | vaultNotFound: 1178 | summary: Vault not found 1179 | value: 1180 | status: 404 1181 | message: vault {vaultUuid} not found 1182 | /heartbeat: 1183 | get: 1184 | operationId: GetHeartbeat 1185 | tags: 1186 | - Health 1187 | summary: Ping the server for liveness 1188 | servers: 1189 | - url: http://localhost:8080 1190 | responses: 1191 | "200": 1192 | description: OK 1193 | content: 1194 | text/plain: 1195 | schema: 1196 | type: string 1197 | example: . 1198 | /health: 1199 | get: 1200 | operationId: GetServerHealth 1201 | tags: 1202 | - Health 1203 | summary: Get state of the server and its dependencies. 1204 | servers: 1205 | - url: http://localhost:8080 1206 | responses: 1207 | "200": 1208 | description: OK 1209 | content: 1210 | application/json: 1211 | schema: 1212 | type: object 1213 | required: [ "name", "version" ] 1214 | properties: 1215 | name: 1216 | type: string 1217 | version: 1218 | type: string 1219 | description: The Connect server's version 1220 | dependencies: 1221 | type: array 1222 | items: 1223 | $ref: "#/components/schemas/ServiceDependency" 1224 | examples: 1225 | WaitingForAPIRequest: 1226 | value: 1227 | name: 1Password Connect API 1228 | version: 1.2.1 1229 | dependencies: 1230 | - service: sync 1231 | status: "TOKEN_NEEDED" 1232 | - service: sqlite 1233 | status: "ACTIVE" 1234 | message: "Connected to./1password.sqlite" 1235 | summary: API server waiting for first authenticated request 1236 | 1237 | /metrics: 1238 | get: 1239 | operationId: GetPrometheusMetrics 1240 | tags: 1241 | - Metrics 1242 | description: See Prometheus documentation for a complete data model. 1243 | summary: Query server for exposed Prometheus metrics 1244 | servers: 1245 | - url: http://localhost:8080 1246 | responses: 1247 | "200": 1248 | description: Successfully returned Prometheus metrics 1249 | content: 1250 | text/plain: 1251 | schema: 1252 | type: string 1253 | example: | 1254 | # HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles. 1255 | # TYPE go_gc_duration_seconds summary 1256 | go_gc_duration_seconds{quantile="0"} 2.9153e-05 1257 | go_gc_duration_seconds{quantile="0.25"} 6.2832e-05 1258 | go_gc_duration_seconds{quantile="0.5"} 9.7187e-05 1259 | go_gc_duration_seconds{quantile="0.75"} 0.000112967 1260 | go_gc_duration_seconds{quantile="1"} 0.000215819 1261 | go_gc_duration_seconds_sum 0.001376862 1262 | go_gc_duration_seconds_count 14 1263 | --------------------------------------------------------------------------------