Repository: 1Password/connect
Branch: main
Commit: 7485a5981357
Files: 24
Total size: 121.6 KB
Directory structure:
gitextract__1cd96aw/
├── CHANGELOG.md
├── README.md
├── docs/
│ ├── configuration.md
│ └── openapi/
│ └── spec.yaml
└── examples/
├── aws-ecs-fargate/
│ ├── README.md
│ └── connect-server.yaml
├── beta/
│ ├── aws-ecs-apigateway-cfn/
│ │ ├── README.md
│ │ └── connect-server.yaml
│ ├── azure-container-apps-arm/
│ │ ├── README.md
│ │ └── aca-op-connect-server-template.json
│ └── docker/
│ ├── README.md
│ ├── compose.custom-tls.yaml
│ ├── compose.http.yaml
│ ├── compose.lets-encrypt.yaml
│ ├── compose.template.yaml
│ ├── connect-api.env
│ └── connect-sync.env
├── docker/
│ └── compose/
│ ├── README.md
│ └── docker-compose.yaml
└── kubernetes/
├── README.md
├── op-connect-deployment.yaml
├── op-connect-ingress.yaml
├── op-connect-issuer.yaml
└── op-connect-service.yaml
================================================
FILE CONTENTS
================================================
================================================
FILE: CHANGELOG.md
================================================
[//]: # "START/v1.8.0"
# v1.8.0
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.
- [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}
- [FIXED] Connect now allows you to fetch a file if the file is referenced in multiple items across different vaults. {4262}
- [FIXED] Connect file storage management can now handle multiple items referencing the same file. {4263}
[//]: # "START/v1.7.3"
# v1.7.3
This release updates the dependencies and the images used to build Connect.
- [IMPROVED] Update go version to 1.22.4. {4146}
- [SECURITY] Upgraded to Go 1.21.x and Debian 12 Bookworm for building and CI. {3870}
- [SECURITY] Bump google.golang.org/grpc to address a vulnerable dependency alert. {3855}
[//]: # "START/v1.7.2"
# v1.7.2
This release contains bug fixes.
- [FIXED] Connect can update Document items. {3709}
- [FIXED] The first request sent to a new Connect server should not respond with a '401 invalid bearer token' error. {3711}
[//]: # "START/v1.7.1"
# v1.7.1
This release contains security and performance improvements.
- [IMPROVED] Connect item usage flushing has been optimised to decrease network traffic to the 1Password servers. {2600}
- [SECURITY] Addressed bug where PATCH could be abused to read items using a write only token. Credit to Carlos Baraza. {3392}
[//]: # "START/v1.7.0"
# v1.7.0
This release improves an error message and fixes two bugs.
- [IMPROVED] Creating a new DOCUMENT item now correctly returns an error message that this is not supported. {3127}
- [FIXED] Connect should no longer sporadically return an "database is locked" error when synchronising with the 1Password servers. {3381}
- [FIXED] Requests to /v1/vaults/{vaultUUID}/items no longer causes "Invalid Item UUID" to be logged. {3254}
[//]: # "START/v1.6.1"
# v1.6.1
This release addresses memory leak issues for Connect.
- [FIXED] Addressed a problem that caused Connect's memory consumption to increase with about 100B for every request handled. {2842}
- [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}
[//]: # "START/v1.6.0"
# v1.6.0
This release moves Connect to a distroless base image.
- [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}
[//]: # "START/v1.5.8"
# v1.5.8
This release contains two security improvements.
- [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}
- [SECURITY] Update Debian packages for the Docker release images. {3195}
[//]: # "START/v1.5.7"
# v1.5.7
Security updates, and a fix to allow Connect to (re)start if the 1Password server is unavailable.
- [FIXED] The API container will now continue to serve requests if it is restarted when 1Password server in unreachable. {2560}
- [SECURITY] Update connect images to use latest base image. {2738}
- [SECURITY] Update Debian packages for the Docker release images. {2694}
[//]: # "START/v1.5.6"
# v1.5.6
This version addresses bugs related to file objects
- [FIXED] The file objects now have the correct ID and name. {2625, 2616}
- [FIXED] Categories route will no longer return status 500 when no error is present. {2170}
[//]: # "START/v1.5.5"
# v1.5.5
This version addresses a few bugs and a security improvement.
- [FIXED] Connect now properly returns the `content_path` attribute of the File object. {2598}
- [SECURITY] Updated Debian packages for the Docker release images. {2516}
[//]: # "START/v1.5.4"
# v1.5.4
This release updates the API spec of Connect to mention some supported fields and addresses two bugs.
- [IMPROVED] The API spec now mentions the "label" field for autofill URL's. {2446}
- [IMPROVED] The API spec now mentions support for the item categories "MEDICAL_RECORD" and "SSH_KEY". {2468}
- [FIXED] Connect accepts JSON inputs with camelCase keys as used in the SDK's again. {2317}
- [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}
- [SECURITY] The Docker images are now using an updated version of the Debian base images. {2516}
[//]: # "START/v1.5.3"
# v1.5.3
This release contains improvements in validation during the SRP process.
- [SECURITY] Improved validation of server parameters in SRP process. Credits to Cure53. {2442}
[//]: # "START/v1.5.2"
# v1.5.2
This release addressed a few minor bugs and a memory leak.
- [IMPROVED] Connect now validates `monthYear` and `date` field types. {2075}
- [FIXED] Addressed a problem that could let the memory consumption of the sync container slowly increase for every incoming request under certain circumstances. {2017}
- [FIXED] Having a single quote as a filter value no longer throws a stack trace and now returns a 400. {1442}
[//]: # "START/v1.5.1"
# v1.5.1
This release addresses a few problems related to Connect's synchronization mechanism.
- [IMPROVED] Connect stays better in sync with 1Password when it experiences connection problems. {2150}
- [FIXED] Fixed synchronization of templates that could prevent Connect from starting after changing its credentials. {2151}
- [FIXED] Templates should now correctly synchronize to Connect after emptying Connect's database. {2151}
- [FIXED] Connect should no longer return a "failed to initialize database" error when upgrading from Connect v1.3.x. {2005}
[//]: # "START/v1.5.0"
# v1.5.0
This release introduces Docker images for the `arm64` and `arm/v7` platforms.
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.
- [NEW] Bus auto-discovery can now be disabled by explicitly defining peers with the `$OP_BUS_PEERS` environment variable. {1778}
- [NEW] Connect API and Connect Sync Docker images for arm64/v8 and arm/v7 architectures are now available. {1771}
- [FIXED] The label field is now always correctly set (instead of being empty) for fields that have a USERNAME or PASSWORD purpose. {1657}
[//]: # "START/v1.4.0"
# v1.4.0
This release introduces enhancements for OTP fields, generated passwords and item usage histories.
This update includes non-breaking changes to the [OpenAPI spec](docs/openapi/spec.yaml).
- [NEW] Specific characters can now be excluded from generated passwords by setting `excludeCharacters` in the recipe. {1631}
- [IMPROVED] Added the generated TOTP code to the body of OTP fields in Items. {1557}
- [IMPROVED] Connect now records more detailed item usage histories. {14198}
[//]: # "START/v1.3.0"
# v1.3.0
This release introduces support for accessing documents and files attached to items.
## Features
- [NEW] Add support for getting files through the API. Details can be found in the [OpenAPI spec](docs/openapi/spec.yaml).
- [IMPROVED] Item modification requests are now rejected by the Connect API if the authenticated account is in the frozen billing state. {1175}
- [IMPROVED] Items containing one or more file fields can now be updated through Connect. {1386}
- [IMPROVED] API spec now defines valid vault and item IDs. {1399}
- [IMPROVED] Connect API now reports its version number in the `1Password-Connect-Version` header. {1443}
## Fixed
- [FIXED] Resolved an issue that prevented servers from authenticating with accounts after an admin updated the sign-in url. {1446}
[//]: # "START/v1.2.0"
# v1.2.0
This release contains several improvements to securing you API requests and better error handling when dealing with Items.
## Features
- [NEW] Add TLS for the Connect API. Provide your own certificates or use LetsEncrypt
- [IMPROVED] A more user-friendly error is returned when the user attempts to create an item with the "CUSTOM" category. {1382}
## Fixed
- [FIXED] Resolved issue where API could incorrectly report a successful item deletion. {1368}
- [FIXED] Fixed the handling of TOTP fields for items in Connect {1372}
[//]: # "START/v1.1.1"
# v1.1.1
This release contains improvements to account synchronization and token validation.
[//]: # "START/v1.1.0"
# v1.1.0
This release contains several improvements to configuring, running, and troubleshooting a Secrets Automation deployment in your environment.
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.
## Features
- [NEW] Updated the Connect API to delete items instead of trashing them. {11613}
- [NEW] Connect can now also be unlocked by sending a bearer token to the /health endpoint. {1305}
- [NEW] The /health endpoint reports the status of the data in the local database under account_data. {1305}
- [NEW] Added support for new API Credential item category. {1344}
- [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}
- [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}
- [IMPROVED] Item usage is now reported to 1password.com in batches at most once a second to avoid being rate limited. {1331}
- [IMPROVED] Syncer applies an exponential backoff when retrying if it cannot find a valid credential. {1341}
## Fixes
- [FIXED] Corrected several minor mistakes in the OpenAPI spec. {1326}
- [FIXED] Log error when a connection problem causes the failure of a Connect request. {1302}
- [FIXED] Updating the password for a Login or Password Item correctly updates the Password History. {1350}
[//]: # "START/v1.0.0"
# v1.0.0
The 1Password Connect server provides applications and services with information from 1Password as part of 1Password Secrets Automation.
================================================
FILE: README.md
================================================
1Password Connect
Access your 1Password secrets using 1Password Connect
---
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.
## ✨ Get started
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).
## 💙 Community & Support
- File an [issue](https://github.com/1Password/connect/issues) for bugs and feature requests.
- Join the [Developer Slack workspace](https://developer.1password.com/joinslack)
- Subscribe to the [Developer Newsletter](https://1password.com/dev-subscribe/)
================================================
FILE: docs/configuration.md
================================================
# Connect Server Configuration
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.
## Data Volume
- `/home/opuser/.op/data`: The default location of the shared volume
**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.
## Environment variables
The following environment variable configuration options are available for both contains:
- `OP_SESSION`: path to the 1password-credentials.json file
- `OP_HTTP_PORT`: port used by the HTTP server
- `OP_LOG_LEVEL`: set the logging level of the container
- `XDG_DATA_HOME`: set the path where the `.op/data` directory should be created
- `OP_BUS_PORT`: the port used for listening to incoming bus connections from other containers (by default, this is a random free port)
- `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)
All other configuration options are only relevant for the `1password/connect-api` container:
- `OP_HTTPS_PORT`: port used by the HTTP sever when TLS is configured (see below)
- `OP_SYNC_TIMEOUT`: define how long to wait for initial sync to complete
When using TLS with own certificate:
- `OP_TLS_KEY_FILE`: path to the private key file.
- `OP_TLS_CERT_FILE`: path to the certificate file. This should be the full certificate chain.
When using TLS with Let's Encrypt:
- `OP_TLS_USE_LETSENCRYPT`: should be set to any value.
- `OP_TLS_DOMAIN`: the (sub-)domain for which to request a certificate. The DNS-records for this domain must point to the Connect server.
## Manual bus configuration (v1.5.0 and above)
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.
It is also possible to manually configure the shared bus:
1. Assign a static port for the bus by setting the `OP_BUS_PORT` environment variable to a free port for all containers.
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.
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.
For example, if the containers are called `op-connect-api` and `op-connect-sync`. The configuration of `op-connect-api` would be:
```
OP_BUS_PORT=11223
OP_BUS_PEERS=op-connect-sync:11223
```
And for `op-connect-sync`:
```
OP_BUS_PORT=11223
OP_BUS_PEERS=op-connect-api:11223
```
================================================
FILE: docs/openapi/spec.yaml
================================================
openapi: "3.0.2"
info:
title: 1Password Connect
description: >-
REST API interface for 1Password Connect.
version: "1.7.1"
contact:
name: 1Password Integrations
email: support@1password.com
url: https://support.1password.com/
servers:
- url: http://localhost:8080/v1
tags:
- name: Items
description: Access and manage items inside 1Password Vaults
- name: Vaults
description: Access 1Password Vaults
- name: Activity
description: Access API Request Activity
components:
securitySchemes:
ConnectToken:
type: http
scheme: bearer
bearerFormat: JWT
schemas:
ErrorResponse:
type: object
properties:
status:
type: integer
description: HTTP Status Code
message:
type: string
description: A message detailing the error
File:
type: object
properties:
id:
type: string
description: ID of the file
name:
type: string
description: Name of the file
size:
type: integer
description: Size in bytes of the file
content_path:
type: string
description: Path of the Connect API that can be used to download the contents of this file.
readOnly: true
section:
type: object
description: For files that are in a section, this field describes the section.
properties:
id:
type: string
content:
type: string
format: byte
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`.
example:
id: 6r65pjq33banznomn7q22sj44e
name: foo.txt
size: 35
content_path: v1/vaults/ionaiwtdvgclrixbt6ztpqcxnq/items/p7eflcy7f5mk7vg6zrzf5rjjyu/files/6r65pjq33banznomn7q22sj44e/content
content: VGhlIGZ1dHVyZSBiZWxvbmdzIHRvIHRoZSBjdXJpb3VzLgo=
Vault:
type: object
properties:
id:
type: string
pattern: '^[\da-z]{26}$'
name:
type: string
description:
type: string
attributeVersion:
type: integer
description: The vault version
contentVersion:
type: integer
description: The version of the vault contents
items:
description: Number of active items in the vault
type: integer
type:
type: string
enum:
- USER_CREATED
- PERSONAL
- EVERYONE
- TRANSFER
createdAt:
type: string
format: date-time
readOnly: true
updatedAt:
type: string
format: date-time
readOnly: true
GeneratorRecipe:
description: The recipe is used in conjunction with the "generate" property to set the character set used to generate a new secure value
type: object
properties:
length:
type: integer
description: Length of the generated value
default: 32
minimum: 1
maximum: 64
characterSets:
type: array
items:
type: string
enum:
- LETTERS
- DIGITS
- SYMBOLS
minimum: 0
maximum: 3
uniqueItems: true
excludeCharacters:
type: string
description: "List of all characters that should be excluded from generated passwords."
example: "abc1"
Item:
type: object
required:
- vault
- category
properties:
id:
type: string
pattern: '^[\da-z]{26}$'
title:
type: string
vault:
type: object
required:
- id
properties:
id:
type: string
pattern: '^[\da-z]{26}$'
category:
type: string
enum:
- "LOGIN"
- "PASSWORD"
- "API_CREDENTIAL"
- "SERVER"
- "DATABASE"
- "CREDIT_CARD"
- "MEMBERSHIP"
- "PASSPORT"
- "SOFTWARE_LICENSE"
- "OUTDOOR_LICENSE"
- "SECURE_NOTE"
- "WIRELESS_ROUTER"
- "BANK_ACCOUNT"
- "DRIVER_LICENSE"
- "IDENTITY"
- "REWARD_PROGRAM"
- "DOCUMENT"
- "EMAIL_ACCOUNT"
- "SOCIAL_SECURITY_NUMBER"
- "MEDICAL_RECORD"
- "SSH_KEY"
- "CUSTOM"
urls:
type: array
items:
type: object
required:
- href
properties:
label:
type: string
primary:
type: boolean
href:
type: string
format: url
example:
- primary: true
href: https://example.com
- href: https://example.org
favorite:
type: boolean
default: false
tags:
type: array
items:
type: string
version:
type: integer
state:
type: string
readOnly: true
enum:
- "ARCHIVED"
- "DELETED"
createdAt:
type: string
format: date-time
readOnly: true
updatedAt:
type: string
format: date-time
readOnly: true
lastEditedBy:
type: string
readOnly: true
FullItem:
allOf:
- $ref: "#/components/schemas/Item"
- type: object
properties:
sections:
type: array
items:
type: object
properties:
id:
type: string
label:
type: string
fields:
type: array
items:
$ref: '#/components/schemas/Field'
files:
type: array
items:
$ref: '#/components/schemas/File'
Field:
type: object
required:
- id
- type
properties:
id:
type: string
section:
type: object
properties:
id:
type: string
type:
type: string
default: "STRING"
enum:
- "STRING"
- "EMAIL"
- "CONCEALED"
- "URL"
- "TOTP"
- "DATE"
- "MONTH_YEAR"
- "MENU"
purpose:
description: Some item types, Login and Password, have fields used for autofill. This property indicates that purpose and is required for some item types.
type: string
enum:
- ""
- "USERNAME"
- "PASSWORD"
- "NOTES"
label:
type: string
value:
type: string
generate:
description: If value is not present then a new value should be generated for this field
type: boolean
default: false
recipe:
$ref: "#/components/schemas/GeneratorRecipe"
entropy:
description: For fields with a purpose of `PASSWORD` this is the entropy of the value
type: number
readOnly: true
APIRequest:
description: Represents a request that was made to the API. Including what Token was used and what resource was accessed.
type: object
properties:
requestId:
description: The unique id used to identify a single request.
type: string
format: uuid
timestamp:
description: The time at which the request was processed by the server.
type: string
format: date-time
readOnly: true
action:
type: string
enum:
- READ
- CREATE
- UPDATE
- DELETE
result:
type: string
enum:
- SUCCESS
- DENY
actor:
type: object
properties:
id:
type: string
format: uuid
account:
type: string
jti:
type: string
userAgent:
type: string
requestIp:
type: string
resource:
type: object
properties:
type:
type: string
enum:
- ITEM
- VAULT
vault:
type: object
properties:
id:
type: string
pattern: '^[\da-z]{26}$'
item:
type: object
properties:
id:
type: string
pattern: '^[\da-z]{26}$'
itemVersion:
type: integer
Patch:
type: array
items:
type: object
properties:
op:
type: string
enum: [ add, remove, replace ]
path:
type: string
description: An RFC6901 JSON Pointer pointing to the Item document, an Item Attribute, and Item Field by Field ID, or an Item Field Attribute
example: "/fields/06gnn2b95example10q91512p5/label"
value:
type: object
required:
- op
- path
ServiceDependency:
description: The state of a registered server dependency.
type: object
properties:
service:
type: string
status:
type: string
message:
type: string
description: Human-readable message for explaining the current state.
paths:
/activity:
get:
operationId: GetApiActivity
tags:
- Activity
summary: Retrieve a list of API Requests that have been made.
security:
- ConnectToken: [ ]
parameters:
- in: query
name: limit
schema:
type: integer
example: 10
default: 50
description: How many API Events should be retrieved in a single request.
- in: query
name: offset
schema:
type: integer
example: 50
default: 0
description: How far into the collection of API Events should the response start
responses:
"200":
description: OK
headers:
Content-Range:
description: An decription of what part of the collection has been returned as well as the total size.
schema:
type: string
example: 1-50/1134
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/APIRequest"
"401":
description: Invalid or missing token
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
status: 401
message: Invalid token signature
/vaults:
get:
operationId: GetVaults
tags:
- Vaults
summary: Get all Vaults
security:
- ConnectToken: [ ]
parameters:
- in: query
name: filter
schema:
type: string
example: name eq "Some Vault Name"
description: Filter the Vault collection based on Vault name using SCIM eq filter
responses:
"200":
description: OK
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Vault"
"401":
description: Invalid or missing token
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
status: 401
message: Invalid token signature
/vaults/{vaultUuid}:
get:
operationId: GetVaultById
tags:
- Vaults
summary: Get Vault details and metadata
security:
- ConnectToken: [ ]
parameters:
- in: path
name: vaultUuid
schema:
type: string
pattern: '^[\da-z]{26}$'
required: true
description: The UUID of the Vault to fetch Items from
responses:
"200":
description: OK
content:
application/json:
schema:
$ref: "#/components/schemas/Vault"
"401":
description: Invalid or missing token
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
status: 401
message: Invalid token signature
"403":
description: Unauthorized access
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
status: 403
message: vault {vaultUuid} is not in scope
"404":
description: Vault not found
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
status: 404
message: vault {itemUuid} not found
/vaults/{vaultUuid}/items:
get:
operationId: GetVaultItems
tags:
- Items
summary: Get all items for inside a Vault
security:
- ConnectToken: [ ]
parameters:
- in: path
name: vaultUuid
schema:
type: string
pattern: '^[\da-z]{26}$'
required: true
description: The UUID of the Vault to fetch Items from
- in: query
name: filter
schema:
type: string
example: title eq "Some Item Name"
description: Filter the Item collection based on Item name using SCIM eq filter
responses:
"200":
description: OK
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Item"
"401":
description: Invalid or missing token
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
status: 401
message: Invalid token signature
"404":
description: Vault not found
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
status: 404
message: vault {vaultUuid} not found
post:
operationId: CreateVaultItem
tags:
- Items
summary: Create a new Item
security:
- ConnectToken: [ ]
parameters:
- in: path
name: vaultUuid
schema:
type: string
pattern: '^[\da-z]{26}$'
required: true
description: The UUID of the Vault to create an Item in
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/FullItem"
responses:
"200":
description: OK
content:
application/json:
schema:
$ref: "#/components/schemas/FullItem"
"400":
description: Unable to create item due to invalid input
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
status: 400
message: Invalid item category
"401":
description: Invalid or missing token
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
status: 401
message: Invalid token signature
"403":
description: Unauthorized access
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
status: 403
message: vault {vaultUuid} is not in scope
"404":
description: Item not found
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
examples:
vaultNotFound:
summary: Vault not found
value:
status: 404
message: vault {vaultUuid} not found
/vaults/{vaultUuid}/items/{itemUuid}:
get:
operationId: GetVaultItemById
tags:
- Items
summary: Get the details of an Item
security:
- ConnectToken: [ ]
parameters:
- in: path
name: vaultUuid
schema:
type: string
pattern: '^[\da-z]{26}$'
required: true
description: The UUID of the Vault to fetch Item from
- in: path
name: itemUuid
schema:
type: string
pattern: '^[\da-z]{26}$'
required: true
description: The UUID of the Item to fetch
responses:
"200":
description: OK
content:
application/json:
schema:
$ref: "#/components/schemas/FullItem"
"401":
description: Invalid or missing token
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
status: 401
message: Invalid token signature
"403":
description: Unauthorized access
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
status: 403
message: vault {vaultUuid} is not in scope
"404":
description: Item not found
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
examples:
itemNotFound:
summary: Item not found
value:
status: 404
message: item {itemUuid} not found
vaultNotFound:
summary: Vault not found
value:
status: 404
message: vault {vaultUuid} not found
put:
operationId: UpdateVaultItem
tags:
- Items
summary: Update an Item
security:
- ConnectToken: [ ]
parameters:
- in: path
name: vaultUuid
schema:
type: string
pattern: '^[\da-z]{26}$'
required: true
description: The UUID of the Item's Vault
- in: path
name: itemUuid
schema:
type: string
pattern: '^[\da-z]{26}$'
required: true
description: The UUID of the Item to update
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/FullItem"
responses:
"200":
description: OK
content:
application/json:
schema:
$ref: "#/components/schemas/FullItem"
"400":
description: Unable to create item due to invalid input
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
status: 400
message: The item doesn't have a {example field name} field.
"401":
description: Invalid or missing token
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
status: 401
message: Invalid token signature
"403":
description: Unauthorized access
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
status: 403
message: vault {vaultUuid} is not in scope
"404":
description: Item not found
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
examples:
itemNotFound:
summary: Item not found
value:
status: 404
message: item {itemUuid} not found
vaultNotFound:
summary: Vault not found
value:
status: 404
message: vault {vaultUuid} not found
delete:
operationId: DeleteVaultItem
tags:
- Items
summary: Delete an Item
security:
- ConnectToken: [ ]
parameters:
- in: path
name: vaultUuid
schema:
type: string
pattern: '^[\da-z]{26}$'
required: true
description: The UUID of the Vault the item is in
- in: path
name: itemUuid
schema:
type: string
pattern: '^[\da-z]{26}$'
required: true
description: The UUID of the Item to update
responses:
"204":
description: Successfully deleted an item
"401":
description: Invalid or missing token
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
status: 401
message: Invalid token signature
"403":
description: Unauthorized access
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
status: 403
message: vault {vaultUuid} is not in scope
"404":
description: Item not found
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
examples:
vaultNotFound:
summary: Vault not found
value:
status: 404
message: vault {vaultUuid} not found
patch:
description: >
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.
When modifying a specific ItemField, the ItemField's ID in the `path` attribute of the operation object: `/fields/{fieldId}`
operationId: PatchVaultItem
tags:
- Items
summary: Update a subset of Item attributes
security:
- ConnectToken: [ ]
parameters:
- in: path
name: vaultUuid
schema:
type: string
pattern: '^[\da-z]{26}$'
required: true
description: The UUID of the Vault the item is in
- in: path
name: itemUuid
schema:
type: string
pattern: '^[\da-z]{26}$'
required: true
description: The UUID of the Item to update
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/Patch"
examples:
ReplaceAllAttributes:
value:
- op: replace
path: "/"
value: { "title": "New Title", "favorite": true, "tags": [ "tag1", "tag2" ], "...": "Any attr from FullItem schema" }
summary: Replace an entire Item with new fields. Equivalent to a PUT request.
PatchItemAttr:
value:
- op: "replace"
path: "/favorite"
value: true
- op: "remove"
path: "/tags/1"
summary: Update specific Item attributes
PatchItemField:
value:
- op: "add"
path: "/fields"
value: { "label": "New Field", "type": "string", "value": "hunter2" }
summary: Add a new ItemField to the Item
PatchItemFieldWithID:
value:
- op: "replace"
path: "/fields/r9qxq7xnhfhukoxsc8ymqr0y11"
value: { "label": "Replacement Title", "type": "string", "value": "new value" }
- op: "remove"
path: "/fields/h2nl155dshi043yse7wa3u1hs7"
summary: Modify or remove an ItemField.
PatchItemFieldAttr:
value:
- op: "add"
path: "/fields/s2ju540zlna8bdj4uro7sj64rk/label"
value: "New field name"
- op: "remove"
path: "/fields/s2ju540zlna8bdj4uro7sj64rk/value"
summary: Modify a specific ItemField attribute.
responses:
"200":
description: OK - Item updated. If no Patch operations were provided, Item is unmodified.
content:
application/json:
schema:
$ref: "#/components/schemas/FullItem"
"401":
description: Invalid or missing token
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
status: 401
message: Invalid token signature
"403":
description: Unauthorized access
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
status: 403
message: vault {vaultUuid} is not in scope
"404":
description: Item not found
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
examples:
itemNotFound:
summary: Item not found
value:
status: 404
message: item {itemUuid} not found
vaultNotFound:
summary: Vault not found
value:
status: 404
message: vault {vaultUuid} not found
/vaults/{vaultUuid}/items/{itemUuid}/files:
get:
operationId: GetItemFiles
tags:
- Files
summary: Get all the files inside an Item
security:
- ConnectToken: [ ]
parameters:
- in: path
name: vaultUuid
schema:
type: string
format: uuid
required: true
description: The UUID of the Vault to fetch Items from
- in: path
name: itemUuid
schema:
type: string
format: uuid
required: true
description: The UUID of the Item to fetch files from
- in: query
name: inline_files
schema:
type: boolean
example: true
description: Tells server to return the base64-encoded file contents in the response.
responses:
"200":
description: OK
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/File"
"401":
description: Invalid or missing token
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
status: 401
message: Invalid token signature
"404":
description: Item not found
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
examples:
itemNotFound:
summary: Item not found
value:
status: 404
message: item {itemUuid} not found
vaultNotFound:
summary: Vault not found
value:
status: 404
message: vault {vaultUuid} not found
"413":
description: File content too large to display
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
examples:
fileTooLarge:
summary: File too large
value:
status: 413
message: File is too large to inline in request. Use the /v1/vaults/{vaultUUID}/items/{itemUUID}/files/{fileUUID}/content endpoint instead.
/vaults/{vaultUuid}/items/{itemUuid}/files/{fileUuid}:
get:
operationId: GetDetailsOfFileById
tags:
- Files
summary: Get the details of a File
security:
- ConnectToken: [ ]
parameters:
- in: path
name: vaultUuid
schema:
type: string
format: uuid
required: true
description: The UUID of the Vault to fetch Item from
- in: path
name: itemUuid
schema:
type: string
format: uuid
required: true
description: The UUID of the Item to fetch File from
- in: path
name: fileUuid
schema:
type: string
format: uuid
required: true
description: The UUID of the File to fetch
- in: query
name: inline_files
schema:
type: boolean
example: true
description: Tells server to return the base64-encoded file contents in the response.
responses:
"200":
description: OK
content:
application/json:
schema:
$ref: "#/components/schemas/File"
"401":
description: Invalid or missing token
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
status: 401
message: Invalid token signature
"403":
description: Unauthorized access
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
status: 403
message: vault {vaultUuid} is not in scope
"404":
description: File not found
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
examples:
fileNotFound:
summary: File not found
value:
status: 404
message: file {fileUuid} not found
itemNotFound:
summary: Item not found
value:
status: 404
message: item {itemUuid} not found
vaultNotFound:
summary: Vault not found
value:
status: 404
message: vault {vaultUuid} not found
"413":
description: File content too large to display
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
examples:
fileTooLarge:
summary: File too large
value:
status: 413
message: File is too large to inline in request. Use the /v1/vaults/{vaultUUID}/items/{itemUUID}/files/{fileUUID}/content endpoint instead.
/vaults/{vaultUuid}/items/{itemUuid}/files/{fileUuid}/content:
parameters:
- in: path
name: vaultUuid
schema:
type: string
format: uuid
required: true
description: The UUID of the Vault the item is in
- in: path
name: itemUuid
schema:
type: string
format: uuid
required: true
description: The UUID of the Item the File is in
- in: path
name: fileUuid
required: true
schema:
type: string
description: UUID of the file to get content from
get:
operationId: DownloadFileByID
tags:
- Files
summary: Get the content of a File
security:
- ConnectToken: [ ]
responses:
"200":
description: "Success"
content:
application/octet-stream:
schema:
type: string
format: binary
headers:
Content-Disposition:
schema:
type: string
example: attachment; filename="privkey.pem"
Content-Length:
schema:
type: string
example: "6432"
"401":
description: Invalid or missing token
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
status: 401
message: Invalid token signature
"404":
description: File not found
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
examples:
fileNotFound:
summary: File not found
value:
status: 404
message: file {fileUuid} not found
itemNotFound:
summary: Item not found
value:
status: 404
message: item {itemUuid} not found
vaultNotFound:
summary: Vault not found
value:
status: 404
message: vault {vaultUuid} not found
/heartbeat:
get:
operationId: GetHeartbeat
tags:
- Health
summary: Ping the server for liveness
servers:
- url: http://localhost:8080
responses:
"200":
description: OK
content:
text/plain:
schema:
type: string
example: .
/health:
get:
operationId: GetServerHealth
tags:
- Health
summary: Get state of the server and its dependencies.
servers:
- url: http://localhost:8080
responses:
"200":
description: OK
content:
application/json:
schema:
type: object
required: [ "name", "version" ]
properties:
name:
type: string
version:
type: string
description: The Connect server's version
dependencies:
type: array
items:
$ref: "#/components/schemas/ServiceDependency"
examples:
WaitingForAPIRequest:
value:
name: 1Password Connect API
version: 1.2.1
dependencies:
- service: sync
status: "TOKEN_NEEDED"
- service: sqlite
status: "ACTIVE"
message: "Connected to./1password.sqlite"
summary: API server waiting for first authenticated request
/metrics:
get:
operationId: GetPrometheusMetrics
tags:
- Metrics
description: See Prometheus documentation for a complete data model.
summary: Query server for exposed Prometheus metrics
servers:
- url: http://localhost:8080
responses:
"200":
description: Successfully returned Prometheus metrics
content:
text/plain:
schema:
type: string
example: |
# HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles.
# TYPE go_gc_duration_seconds summary
go_gc_duration_seconds{quantile="0"} 2.9153e-05
go_gc_duration_seconds{quantile="0.25"} 6.2832e-05
go_gc_duration_seconds{quantile="0.5"} 9.7187e-05
go_gc_duration_seconds{quantile="0.75"} 0.000112967
go_gc_duration_seconds{quantile="1"} 0.000215819
go_gc_duration_seconds_sum 0.001376862
go_gc_duration_seconds_count 14
================================================
FILE: examples/aws-ecs-fargate/README.md
================================================
# AWS ECS with Fargate
The CloudFormation document in this folder will create the following resources, along with the necessary rules, groups, and policies to make them work.
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.
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.
## Networking Resources
- An ACM certificate for HTTPS
- An ALB (Application Load Balancer) with HTTPS
- A VPC (optional, if you don’t provide your own)
- 2 Public subnets (optional, if you don’t provide your own)
- An Internet Gateway (optional, if creating a VPC)
- Route 53 DNS records (optional, if you provide a hosted zone ID)
## ECS Resources
- An ECS Cluster
- Task Definition
- `1password/connect-api` container
- `1password/connect-sync` container
- Service Discovery for internal routing
## Getting Started
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:
- **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.
- **DomainName**: A fully qualified domain name for the ALB (e.g., `connect.example.com`). This is required for HTTPS.
- **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.
- **VPCID**, **PublicSubnets** (optional): Provide these if you want to use an existing VPC and subnets. Leave as default to create a new VPC.
- **VPCCIDR** (optional): Sets the CIDR for a new VPC if you’re creating one; ignored if using an existing VPC.
### DNS Setup (If Not Using Route 53)
If you leave `Route53HostedZoneID` blank, the deployment will pause at the `HTTPSCertificate` creation step, waiting for DNS validation:
1. Check the ACM console for the certificate (status: `Pending validation`).
2. Add the provided CNAME record to your DNS registrar to validate the certificate.
3. Wait for the certificate to be issued (status: `Issued`), then the deployment will continue.
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.
For more details, see the deployment note in the template description.
================================================
FILE: examples/aws-ecs-fargate/connect-server.yaml
================================================
AWSTemplateFormatVersion: 2010-09-09
Description: >-
Sets up 1Password Connect on Fargate with an ALB for HTTPS ingress, in a public VPC subnet.
Note: If you leave Route53HostedZoneID blank, deployment will pause at HTTPSCertificate creation.
Check the ACM console for a CNAME record to add to your DNS registrar for validation, then wait for the certificate to be issued.
# Makes the params look nice in the AWS console
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: Network & Credentials Setup
Parameters:
- VPCID
- VPCCIDR
- PublicSubnets
- Route53HostedZoneID
- DomainName
- CredentialsJson
ParameterLabels:
VPCID:
default: VPC ID
VPCCIDR:
default: VPC CIDR
PublicSubnets:
default: Public Subnets
Route53HostedZoneID:
default: Route 53 Hosted Zone
DomainName:
default: 1Password Connect Server domain name
CredentialsJson:
default: 1Password Credentials JSON
# Stuff you can tweak when you deploy
Parameters:
VPCID:
Type: String
Description: Got a VPC ID? Toss it here, or leave it blank to make a new one
Default: ""
VPCCIDR:
Type: String
Description: CIDR for a new VPC if we're making one
Default: 10.0.0.0/16
PublicSubnets:
Type: CommaDelimitedList
Description: List of public subnet IDs, need at least 2 if you're using an existing VPC
Default: ""
Route53HostedZoneID:
Type: String
Description: Route 53 hosted zone ID for creating DNS records (leave empty to set up manually)
Default: ""
DomainName:
Type: String
Description: Domain name for the ALB (e.g., connect.example.com), must be in Route 53 if hosted zone is provided
MinLength: 1
ConstraintDescription: Need to provide a domain name for HTTPS
CredentialsJson:
Type: String
Description: Your 1password-credentials.json as plain JSON
NoEcho: true
MinLength: 1
ConstraintDescription: gotta have something here
# Check if we need to whip up a new VPC or create DNS records
Conditions:
CreateVPC: !Equals [!Ref VPCID, ""]
CreateRoute53Records: !Not [!Equals [!Ref Route53HostedZoneID, ""]]
OutputDNSRecordContent: !Not [Condition: CreateRoute53Records]
# Predefined CIDRs for the VPC and subnets
Mappings:
SubnetConfig:
VPC:
CIDR: '10.0.0.0/16'
PublicOne:
CIDR: '10.0.1.0/24'
PublicTwo:
CIDR: '10.0.2.0/24'
Resources:
# Secret to store the base64-encoded credentials
CredentialsSecret:
Type: AWS::SecretsManager::Secret
Properties:
Name: !Sub 'CredentialsSecret-${AWS::StackName}'
SecretString: !Base64 { "Fn::Sub": "${CredentialsJson}" }
# VPC stuff if we need to make one
VPC:
Condition: CreateVPC
Type: AWS::EC2::VPC
Properties:
EnableDnsSupport: true
EnableDnsHostnames: true
CidrBlock: !Ref VPCCIDR
PublicSubnetOne: # first subnet for the VPC
Condition: CreateVPC
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Select [0, !GetAZs '']
VpcId: !Ref VPC
CidrBlock: !FindInMap ['SubnetConfig', 'PublicOne', 'CIDR']
MapPublicIpOnLaunch: true
PublicSubnetTwo: # second subnet for the VPC
Condition: CreateVPC
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Select [1, !GetAZs '']
VpcId: !Ref VPC
CidrBlock: !FindInMap ['SubnetConfig', 'PublicTwo', 'CIDR']
MapPublicIpOnLaunch: true
InternetGateway: # need this for internet access in the new VPC
Condition: CreateVPC
Type: AWS::EC2::InternetGateway
GatewayAttachement:
Condition: CreateVPC
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref InternetGateway
PublicRouteTable:
Condition: CreateVPC
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
PublicRoute: # sets up the route to the internet
Condition: CreateVPC
DependsOn: GatewayAttachement
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: '0.0.0.0/0'
GatewayId: !Ref InternetGateway
PublicSubnetOneRouteTableAssociation:
Condition: CreateVPC
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnetOne
RouteTableId: !Ref PublicRouteTable
PublicSubnetTwoRouteTableAssociation:
Condition: CreateVPC
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnetTwo
RouteTableId: !Ref PublicRouteTable
# ECS cluster to run stuff
ECSCluster:
Type: AWS::ECS::Cluster
# SGs for Fargate and ALB
LoadBalancerSG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Access to the public facing load balancer
VpcId: !If [CreateVPC, !Ref VPC, !Ref VPCID]
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0
Description: HTTPS from anywhere
SecurityGroupEgress:
- IpProtocol: tcp
FromPort: 8080
ToPort: 8080
CidrIp: !If
- CreateVPC
- !GetAtt VPC.CidrBlock
- 0.0.0.0/0
FargateContainerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Access to the Fargate containers
VpcId: !If [CreateVPC, !Ref VPC, !Ref VPCID]
EcsSecurityGroupIngressFromPublicALB:
Type: AWS::EC2::SecurityGroupIngress
Properties:
Description: Ingress from the public ALB
GroupId: !Ref FargateContainerSecurityGroup
IpProtocol: tcp
FromPort: 8080
ToPort: 8080
SourceSecurityGroupId: !Ref LoadBalancerSG
EcsSecurityGroupIngressFromSelf:
Type: AWS::EC2::SecurityGroupIngress
Properties:
Description: Ingress for sync container communication
GroupId: !Ref FargateContainerSecurityGroup
IpProtocol: tcp
FromPort: 8081
ToPort: 8081
SourceSecurityGroupId: !Ref FargateContainerSecurityGroup
EcsSecurityGroupEgress:
Type: AWS::EC2::SecurityGroupEgress
Properties:
GroupId: !Ref FargateContainerSecurityGroup
IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0
Description: HTTPS to external services (e.g., Docker Hub)
# ALB setup
LoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Scheme: internet-facing
LoadBalancerAttributes:
- Key: idle_timeout.timeout_seconds
Value: '30'
Subnets: !If
- CreateVPC
- [!Ref PublicSubnetOne, !Ref PublicSubnetTwo]
- !Ref PublicSubnets
SecurityGroups: [!Ref LoadBalancerSG]
HTTPSCertificate:
Type: AWS::CertificateManager::Certificate
Properties:
DomainName: !Ref DomainName
ValidationMethod: DNS
DomainValidationOptions: !If
- CreateRoute53Records
- - DomainName: !Ref DomainName
HostedZoneId: !Ref Route53HostedZoneID
- !Ref AWS::NoValue
HTTPSListener:
DependsOn:
- LoadBalancer
- HTTPSCertificate
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- TargetGroupArn: !Ref ConnectTargetGroup
Type: forward
LoadBalancerArn: !Ref LoadBalancer
Port: 443
Protocol: HTTPS
SslPolicy: ELBSecurityPolicy-TLS13-1-2-Res-2021-06
Certificates:
- CertificateArn: !Ref HTTPSCertificate
ConnectTargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
DependsOn:
- LoadBalancer
Properties:
HealthCheckIntervalSeconds: 6
HealthCheckPath: /heartbeat
HealthCheckProtocol: HTTP
HealthCheckTimeoutSeconds: 5
HealthyThresholdCount: 2
TargetType: ip
Name: connect
Port: 8080
Protocol: HTTP
UnhealthyThresholdCount: 2
VpcId: !If [CreateVPC, !Ref VPC, !Ref VPCID]
TargetGroupAttributes:
- Key: deregistration_delay.timeout_seconds
Value: 60
# DNS record for the ALB (if using Route 53)
DNSRecord:
Condition: CreateRoute53Records
Type: AWS::Route53::RecordSet
Properties:
HostedZoneId: !Ref Route53HostedZoneID
Comment: DNS record pointing to load balancer for 1Password Connect
Name: !Ref DomainName
Type: A
AliasTarget:
DNSName: !GetAtt LoadBalancer.DNSName
HostedZoneId: !GetAtt LoadBalancer.CanonicalHostedZoneID
# Service discovery for internal routing
ServiceDiscoveryNamespace:
Type: AWS::ServiceDiscovery::PrivateDnsNamespace
Properties:
Description: Private DNS namespace for 1Password Connect
Vpc: !If [CreateVPC, !Ref VPC, !Ref VPCID]
Name: onepassword
ConnectServiceDiscovery:
Type: AWS::ServiceDiscovery::Service
DependsOn:
- ServiceDiscoveryNamespace
Properties:
DnsConfig:
DnsRecords:
- TTL: 60
Type: SRV
Name: connect
NamespaceId: !Ref ServiceDiscoveryNamespace
HealthCheckCustomConfig:
FailureThreshold: 1
# IAM roles for ECS
ECSRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: ecs-tasks.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: ECSFargatePermissions
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- ec2:AttachNetworkInterface
- ec2:CreateNetworkInterface
- ec2:DeleteNetworkInterface
- ec2:DescribeNetworkInterfaces
- ec2:DetachNetworkInterface
- elasticloadbalancing:RegisterTargets
- elasticloadbalancing:DeregisterTargets
- elasticloadbalancing:DescribeTargetHealth
Resource: '*'
ECSTaskExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: ecs-tasks.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: AmazonECSTaskExecutionRolePolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- ecr:GetAuthorizationToken
- ecr:BatchCheckLayerAvailability
- ecr:GetDownloadUrlForLayer
- ecr:BatchGetImage
- logs:CreateLogStream
- logs:PutLogEvents
Resource: '*'
- PolicyName: SecretAccess
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- secretsmanager:GetSecretValue
Resource: !Sub 'arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:CredentialsSecret-${AWS::StackName}*'
# Logs in CloudWatch
CloudWatchLogsGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: onepassword-connect
RetentionInDays: 30
# Task def for the Connect containers
TaskDefinition:
Type: AWS::ECS::TaskDefinition
DependsOn:
- ECSTaskExecutionRole
- CloudWatchLogsGroup
Properties:
Cpu: 256
Memory: 512
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
ExecutionRoleArn: !Ref ECSTaskExecutionRole
TaskRoleArn: !Ref ECSRole
Volumes:
- Name: connect-data
ContainerDefinitions:
- Name: connect-api
Image: 1password/connect-api:latest
PortMappings:
- ContainerPort: 8080
Protocol: tcp
MountPoints:
- ContainerPath: /home/opuser/.op/data
SourceVolume: connect-data
Secrets:
- Name: OP_SESSION
ValueFrom: !Ref CredentialsSecret
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-group: !Ref CloudWatchLogsGroup
awslogs-region: !Ref AWS::Region
awslogs-stream-prefix: connect-api
- Name: connect-sync
Image: 1password/connect-sync:latest
MountPoints:
- ContainerPath: /home/opuser/.op/data
SourceVolume: connect-data
Environment:
- Name: OP_HTTP_PORT
Value: '8081'
Secrets:
- Name: OP_SESSION
ValueFrom: !Ref CredentialsSecret
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-group: !Ref CloudWatchLogsGroup
awslogs-region: !Ref AWS::Region
awslogs-stream-prefix: connect-sync
# ECS service to run the containers
ConnectService:
Type: AWS::ECS::Service
DependsOn:
- LoadBalancer
- HTTPSListener
Properties:
ServiceName: connect
Cluster: !Ref ECSCluster
LaunchType: FARGATE
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: ENABLED
SecurityGroups:
- !Ref FargateContainerSecurityGroup
Subnets: !If
- CreateVPC
- [!Ref PublicSubnetOne, !Ref PublicSubnetTwo]
- !Ref PublicSubnets
TaskDefinition: !Ref TaskDefinition
LoadBalancers:
- ContainerName: connect-api
ContainerPort: 8080
TargetGroupArn: !Ref ConnectTargetGroup
ServiceRegistries:
- ContainerName: connect-api
ContainerPort: 8080
RegistryArn: !GetAtt ConnectServiceDiscovery.Arn
# Stuff you'll need after deployment
Outputs:
CNAMEName:
Condition: OutputDNSRecordContent
Description: Name of a DNS record to point to the load balancer (use with CNAME Value for manual setup)
Value: !Sub ${DomainName}.
CNAMEValue:
Condition: OutputDNSRecordContent
Description: Value of a DNS record to point to the load balancer (use with CNAME Name for manual setup)
Value: !Sub ${LoadBalancer.DNSName}.
ExternalUrl:
Description: The URL of the external load balancer
Value: !Join ['', ['https://', !GetAtt LoadBalancer.DNSName]]
Export:
Name: !Join [':', [!Ref AWS::StackName, 'ExternalUrl']]
================================================
FILE: examples/beta/aws-ecs-apigateway-cfn/README.md
================================================
# AWS ECS with Fargate and API Gateway
The CloudFormation document in this folder will create the following resources, along with the necessary rules, groups, and policies to make them work.
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.
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.
## Networking Resources
- A VPC Link for API Gateway to route to the Fargate service
- An API Gateway with HTTPS
- A VPC (optional, if you don’t provide your own)
- 2 Public subnets (optional, if you don’t provide your own)
- An Internet Gateway (optional, if creating a VPC)
## ECS Resources
- An ECS Cluster
- Task Definition
- `1password/connect-api` container
- `1password/connect-sync` container
- Service Discovery for internal routing
## Getting Started
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:
- **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.
- **VPCID**, **PublicSubnets** (optional): Provide these if you want to use an existing VPC and subnets. Leave as default to create a new VPC.
- **VPCCIDR** (optional): Sets the CIDR for a new VPC if you’re creating one; ignored if using an existing VPC.
For more details, see the deployment note in the template description.
================================================
FILE: examples/beta/aws-ecs-apigateway-cfn/connect-server.yaml
================================================
AWSTemplateFormatVersion: 2010-09-09
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
# Makes the params look nice in the AWS console
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: Network & Credentials Setup
Parameters:
- VPCID
- VPCCIDR
- PublicSubnets
- CredentialsJson
ParameterLabels:
CredentialsJson:
default: 1Password Credentials JSON
VPCID:
default: VPC ID
VPCCIDR:
default: VPC CIDR
PublicSubnets:
default: Public Subnets
# Parameters you can tweak when you deploy
Parameters:
VPCID:
Type: String
Description: Got a VPC ID? Toss it here, or leave it blank to make a new one
Default: ""
VPCCIDR:
Type: String
Description: CIDR for a new VPC if we're making one
Default: 10.0.0.0/16
PublicSubnets:
Type: CommaDelimitedList
Description: List of public subnet IDs, need at least 2 if you're using an existing VPC
Default: ""
CredentialsJson:
Type: String
Description: Your 1password-credentials.json as plain JSON
NoEcho: true
MinLength: 1
ConstraintDescription: gotta have something here
# Check if we need to whip up a new VPC
Conditions:
CreateVPC:
Fn::Equals:
- !Ref VPCID
- ""
# Predefined CIDRs for the VPC and subnets
Mappings:
SubnetConfig:
VPC:
CIDR: '10.0.0.0/16'
PublicOne:
CIDR: '10.0.1.0/24'
PublicTwo:
CIDR: '10.0.2.0/24'
Resources:
# Secret to store the base64-encoded credentials
CredentialsSecret:
Type: AWS::SecretsManager::Secret
Properties:
Name: !Sub 'CredentialsSecret-${AWS::StackName}'
SecretString: !Base64 { "Fn::Sub": "${CredentialsJson}" }
# VPC stuff if we need to make one
VPC:
Condition: CreateVPC
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VPCCIDR
EnableDnsSupport: true
EnableDnsHostnames: true
PublicSubnetOne:
Condition: CreateVPC
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone:
Fn::Select:
- 0
- Fn::GetAZs: ''
VpcId: !Ref VPC
CidrBlock: !FindInMap ['SubnetConfig', 'PublicOne', 'CIDR']
MapPublicIpOnLaunch: true
PublicSubnetTwo:
Condition: CreateVPC
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone:
Fn::Select:
- 1
- Fn::GetAZs: ''
VpcId: !Ref VPC
CidrBlock: !FindInMap ['SubnetConfig', 'PublicTwo', 'CIDR']
MapPublicIpOnLaunch: true
InternetGateway:
Condition: CreateVPC
Type: AWS::EC2::InternetGateway
GatewayAttachment:
Condition: CreateVPC
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref InternetGateway
PublicRouteTable:
Condition: CreateVPC
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
PublicRoute:
Condition: CreateVPC
DependsOn: GatewayAttachment
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: '0.0.0.0/0'
GatewayId: !Ref InternetGateway
PublicSubnetOneRouteTableAssociation:
Condition: CreateVPC
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnetOne
RouteTableId: !Ref PublicRouteTable
PublicSubnetTwoRouteTableAssociation:
Condition: CreateVPC
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnetTwo
RouteTableId: !Ref PublicRouteTable
# ECS cluster to run stuff
ECSCluster:
Type: AWS::ECS::Cluster
Properties:
CapacityProviders:
- FARGATE
DefaultCapacityProviderStrategy:
- CapacityProvider: FARGATE
Weight: 1
# SGs for Fargate and API Gateway
FargateContainerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Access to the Fargate containers
VpcId: !If [CreateVPC, !Ref VPC, !Ref VPCID]
ApiGatewaySecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: API Gateway traffic for 1Password Connect
VpcId: !If [CreateVPC, !Ref VPC, !Ref VPCID]
# Security group rules
ApiGatewayEgress: # lets API Gateway talk to Fargate
Type: AWS::EC2::SecurityGroupEgress
Properties:
GroupId: !Ref ApiGatewaySecurityGroup
IpProtocol: tcp
FromPort: 8080
ToPort: 8080
DestinationSecurityGroupId: !Ref FargateContainerSecurityGroup
ConnectIngress:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref FargateContainerSecurityGroup
IpProtocol: tcp
FromPort: 8080
ToPort: 8080
SourceSecurityGroupId: !Ref ApiGatewaySecurityGroup
ConnectSelfIngress:
Type: AWS::EC2::SecurityGroupIngress
Properties:
Description: Ingress from other containers in the same security group
GroupId: !Ref FargateContainerSecurityGroup
IpProtocol: -1
SourceSecurityGroupId: !Ref FargateContainerSecurityGroup
ConnectPublicEgress:
Type: AWS::EC2::SecurityGroupEgress
Properties:
GroupId: !Ref FargateContainerSecurityGroup
IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0
Description: HTTPS to external services (e.g., Docker Hub)
# IAM roles for ECS
ECSTaskExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: ecs-tasks.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: AmazonECSTaskExecutionRolePolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- ecr:GetAuthorizationToken
- ecr:BatchCheckLayerAvailability
- ecr:GetDownloadUrlForLayer
- ecr:BatchGetImage
- logs:CreateLogStream
- logs:PutLogEvents
Resource: '*'
- PolicyName: SecretAccess
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- secretsmanager:GetSecretValue
Resource: !Sub 'arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:CredentialsSecret-${AWS::StackName}*'
ECSRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: ecs-tasks.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: ECSPermissions
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- ec2:AttachNetworkInterface
- ec2:CreateNetworkInterface
- ec2:DeleteNetworkInterface
- ec2:DescribeNetworkInterfaces
- ec2:DetachNetworkInterface
Resource: '*'
# Logs in CloudWatch
CloudWatchLogsGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: onepassword-connect
RetentionInDays: 30
# Service discovery for internal routing
ServiceDiscoveryNamespace:
Type: AWS::ServiceDiscovery::PrivateDnsNamespace
Properties:
Description: Private DNS namespace for 1Password Connect
Vpc: !If [CreateVPC, !Ref VPC, !Ref VPCID]
Name: onepassword
ConnectServiceDiscovery:
Type: AWS::ServiceDiscovery::Service
DependsOn:
- ServiceDiscoveryNamespace
Properties:
DnsConfig:
DnsRecords:
- TTL: 60
Type: SRV
Name: connect
NamespaceId: !Ref ServiceDiscoveryNamespace
HealthCheckCustomConfig:
FailureThreshold: 1
# Task def for the Connect containers
TaskDefinition:
Type: AWS::ECS::TaskDefinition
DependsOn:
- ECSTaskExecutionRole
- CredentialsSecret
- CloudWatchLogsGroup
Properties:
Cpu: 256
Memory: 512
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
ExecutionRoleArn: !Ref ECSTaskExecutionRole
TaskRoleArn: !Ref ECSRole
Volumes:
- Name: data
ContainerDefinitions:
- Name: connect-api
Image: 1password/connect-api:latest
PortMappings:
- ContainerPort: 8080
Protocol: tcp
MountPoints:
- ContainerPath: /home/opuser/.op/data
SourceVolume: data
Secrets:
- Name: OP_SESSION
ValueFrom: !Ref CredentialsSecret
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-group: !Ref CloudWatchLogsGroup
awslogs-region: !Ref AWS::Region
awslogs-stream-prefix: connect-api
- Name: connect-sync
Image: 1password/connect-sync:latest
MountPoints:
- ContainerPath: /home/opuser/.op/data
SourceVolume: data
Environment:
- Name: OP_HTTP_PORT
Value: '8081'
Secrets:
- Name: OP_SESSION
ValueFrom: !Ref CredentialsSecret
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-group: !Ref CloudWatchLogsGroup
awslogs-region: !Ref AWS::Region
awslogs-stream-prefix: connect-sync
# ECS service to run the containers
ConnectService:
Type: AWS::ECS::Service
DependsOn:
- TaskDefinition
- ApiGatewayStage
- ConnectServiceDiscovery
- FargateContainerSecurityGroup
Properties:
ServiceName: connect
Cluster: !Ref ECSCluster
LaunchType: FARGATE
DesiredCount: 1
TaskDefinition: !Ref TaskDefinition
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: ENABLED
SecurityGroups:
- !Ref FargateContainerSecurityGroup
Subnets: !If
- CreateVPC
- [!Ref PublicSubnetOne, !Ref PublicSubnetTwo]
- !Ref PublicSubnets
ServiceRegistries:
- ContainerName: connect-api
ContainerPort: 8080
RegistryArn: !GetAtt ConnectServiceDiscovery.Arn
# API Gateway setup
VpcLink:
Type: AWS::ApiGatewayV2::VpcLink
Properties:
Name: connect-vpc-link
SecurityGroupIds:
- !Ref ApiGatewaySecurityGroup
SubnetIds: !If
- CreateVPC
- [!Ref PublicSubnetOne, !Ref PublicSubnetTwo]
- !Ref PublicSubnets
ApiGateway:
Type: AWS::ApiGatewayV2::Api
Properties:
Name: onepassword-connect
ProtocolType: HTTP
Description: API Gateway for 1Password Connect
ApiGatewayIntegration:
Type: AWS::ApiGatewayV2::Integration
DependsOn:
- ApiGateway
- VpcLink
Properties:
ApiId: !Ref ApiGateway
IntegrationType: HTTP_PROXY
IntegrationMethod: ANY
ConnectionType: VPC_LINK
ConnectionId: !Ref VpcLink
IntegrationUri: !GetAtt ConnectServiceDiscovery.Arn
PayloadFormatVersion: 1.0
ApiGatewayRoute:
Type: AWS::ApiGatewayV2::Route
DependsOn:
- ApiGateway
- ApiGatewayIntegration
Properties:
ApiId: !Ref ApiGateway
RouteKey: $default
AuthorizationType: NONE
Target: !Join
- /
- - integrations
- !Ref ApiGatewayIntegration
ApiGatewayStage:
Type: AWS::ApiGatewayV2::Stage
DependsOn:
- ApiGateway
- ApiGatewayRoute
Properties:
StageName: $default
ApiId: !Ref ApiGateway
AutoDeploy: true
Outputs:
ExternalUrl:
Description: The URL of the API Gateway for 1Password Connect
Value: !GetAtt ApiGateway.ApiEndpoint
Export:
Name: !Join [ ':', [ !Ref AWS::StackName, 'ExternalUrl' ] ]
================================================
FILE: examples/beta/azure-container-apps-arm/README.md
================================================
# Deploy 1Password Connect Server on Azure Container Apps using the Azure Portal
_Learn how to deploy 1Password Connect Server on the [Azure Container Apps](https://azure.microsoft.com/en-us/products/container-apps/#overview) service._
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:
- **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/).
- **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.
- **Automatic TLS certificate management:** Azure Container Apps automatically handles TLS certificate management on your behalf.
## Before you begin
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.
> [!NOTE]
> If you don't have an Azure account, you can sign up for a free trial with starting credit: https://azure.microsoft.com/free/
## Step 1: Download the template file
1. Download the [ARM(Azure Resource Manager) template](./aca-op-connect-server-template.json) file using the download icon at the top right.
## Step 2: Create the Container App
1. Sign in to the Azure Portal and go to the [Deploy a custom template](https://portal.azure.com/#create/Microsoft.Template) page.
2. Click **Build your own template in the editor**.
3. Click **Load file** and upload the template file you downloaded earlier. Then click **Save**.
4. Fill out the following fields:
- **Subscription** : Choose the subscription you prefer.
- **Resource group**: Choose an existing Resource Group or create a new one using **Create new** button.
- **Region**: Choose the region you prefer.
- **Container App Name**: Enter a name you'd like to use, default will be `op-connect-con-app`.
- **Container App Env Name**: Enter a name you'd like to use, default will be `op-connect-con-env`.
- **Container App Log Analytics Name**: Enter a name you'd like to use, default will be `op-connect-con-app-log-analytics`.
- **Credentials** : Paste the entire contents of the `1password-credentials.json` file.
5. Click **Review + create**.
6. Once the validation succeeds, click **Create**. It is expected to take a couple of minutes to complete the deployment.
## Step 3: Test your Connect Server
Once your deployment is complete, click **Go to resource group** and click on the container app you created.
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.
```sh
curl --silent --show-error --request GET --header "Accept: application/json" \
--header "Authorization: Bearer mF_9.B5f-4.1JqM" \
https://op-connect.example.com/v1/vaults
```
Example JSON response:
```json
[
{
"attributeVersion": 1,
"contentVersion": 9,
"createdAt": "2025-03-21T13:06:35Z",
"id": "hgm3sn37vkj3lsdkeouq46wyca",
"items": 5,
"name": "REDACTED",
"type": "USER_CREATED",
"updatedAt": "2025-04-18T16:38:40Z"
},
{
"attributeVersion": 1,
"contentVersion": 2,
"createdAt": "2025-03-21T02:15:33Z",
"id": "mn4w65hq6ar7nc2fc7qgoru3ki",
"items": 1,
"name": "REDACTED",
"type": "USER_CREATED",
"updatedAt": "2025-03-21T02:16:32Z"
},
{
"attributeVersion": 1,
"contentVersion": 94,
"createdAt": "2025-03-21T15:20:41Z",
"id": "z4s56cbejab6q6urod3l5icqky",
"items": 3,
"name": "REDACTED",
"type": "USER_CREATED",
"updatedAt": "2025-04-29T02:49:13Z"
}
]
```
## Step 4: Integrate with your application
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.
## Update your Connect Server in the Azure Portal
> [!TIP]
> Check for 1Password Connect Server updates on the [1Password releases page](https://releases.1password.com/).
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.
2. Click **Edit and deploy**.
3. Select the checkbox next to your **connect-api** container, then choose **Edit**.
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/).
5. Repeat for the **connect-sync** container, updating `1password/connect-sync:latest` to the same version.
6. Select **Save**.
7. Select **Create** to deploy a new revision using the updated images.
8. Test your Connect Server URL with your access token to verify the update.
## Get help
### How to update the **credentials** secret
To use a new `1password-credentials.json` credentials file for your Connect Server, replace the secret in your Container App:
Replace your credentials secret using the Azure Portal
1. Open the Azure Portal and go to the [Container Apps](https://portal.azure.com/#view/HubsExtension/BrowseResource/resourceType/Microsoft.App%2FcontainerApps) page.
2. Choose **Secrets** from the Settings section in the sidebar.
3. Edit the **credentials** secret and paste the entire contents of your new `1password-credentials.json` file.
4. Select the checkbox and click **Save**.
5. Choose the **Revisions** from the Application section in the sidebar.
6. Click your current active revision and choose **Restart** in the details pane.
7. Test your Connect Server URL with your new access token to [test your Connect Server](#step-3-test-your-connect-server).
================================================
FILE: examples/beta/azure-container-apps-arm/aca-op-connect-server-template.json
================================================
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"logAnalyticsWorkspaceName": {
"type": "string",
"defaultValue": "op-connect-con-app-log-analytics",
"metadata": {
"description": "The name of the Log Analytics Workspace."
}
},
"containerAppName": {
"type": "string",
"defaultValue": "op-connect-con-app",
"metadata": {
"description": "The name of the Container App."
}
},
"containerAppEnvName": {
"type": "string",
"defaultValue": "op-connect-con-env",
"metadata": {
"description": "The name of the Container App Environment."
}
},
"credentials": {
"type": "SecureString",
"metadata": {
"description": "The plain text contents of 1password-credentials.json."
}
}
},
"resources": [
{
"type": "Microsoft.OperationalInsights/workspaces",
"apiVersion": "2022-10-01",
"name": "[parameters('logAnalyticsWorkspaceName')]",
"location": "[resourceGroup().location]",
"properties": {
"sku": {
"name": "PerGB2018"
}
}
},
{
"type": "Microsoft.App/managedEnvironments",
"apiVersion": "2023-05-01",
"name": "[parameters('containerAppEnvName')]",
"location": "[resourceGroup().location]",
"dependsOn": [
"[resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName'))]"
],
"properties": {
"appLogsConfiguration": {
"destination": "log-analytics",
"logAnalyticsConfiguration": {
"customerId": "[reference(resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName'))).customerId]",
"sharedKey": "[listKeys(resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName')), '2022-10-01').primarySharedKey]"
}
},
"zoneRedundant": false
}
},
{
"type": "Microsoft.App/containerApps",
"apiVersion": "2023-05-01",
"name": "[parameters('containerAppName')]",
"location": "[resourceGroup().location]",
"dependsOn": [
"[resourceId('Microsoft.App/managedEnvironments', parameters('containerAppEnvName'))]"
],
"properties": {
"managedEnvironmentId": "[resourceId('Microsoft.App/managedEnvironments', parameters('containerAppEnvName'))]",
"configuration": {
"secrets": [
{
"name": "credentials",
"value": "[parameters('credentials')]"
}
],
"activeRevisionsMode": "Single",
"ingress": {
"external": true,
"targetPort": 8080,
"allowInsecure": false,
"traffic": [
{
"latestRevision": true,
"weight": 100
}
]
}
},
"template": {
"containers": [
{
"name": "connect-api",
"image": "docker.io/1password/connect-api:latest",
"resources": {
"cpu": 0.25,
"memory": "0.5Gi"
},
"volumeMounts": [
{
"volumeName": "data",
"mountPath": "/home/opuser/.op/data"
},
{
"volumeName": "credentials",
"mountPath": "/home/opuser/.op/1password-credentials.json",
"subPath": "1password-credentials.json"
}
]
},
{
"name": "connect-sync",
"image": "docker.io/1password/connect-sync:latest",
"resources": {
"cpu": 0.25,
"memory": "0.5Gi"
},
"env": [
{
"name": "OP_HTTP_PORT",
"value": "8081"
}
],
"volumeMounts": [
{
"volumeName": "data",
"mountPath": "/home/opuser/.op/data"
},
{
"volumeName": "credentials",
"mountPath": "/home/opuser/.op/1password-credentials.json",
"subPath": "1password-credentials.json"
}
]
}
],
"volumes": [
{
"name": "data",
"storageType": "EmptyDir"
},
{
"name": "credentials",
"storageType": "Secret",
"secrets": [
{
"secretRef": "credentials",
"path": "1password-credentials.json"
}
]
}
],
"scale": {
"minReplicas": 1,
"maxReplicas": 1
}
}
}
}
]
}
================================================
FILE: examples/beta/docker/README.md
================================================
# [Beta] Deploy 1Password Connect Server using Docker Swarm
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.
## In this folder
- [`README.md`](./README.md): the document that you are reading. 👋😃
- [`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
- [`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
- [`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
- [`compose.custom-tls.yaml`](./compose.custom-tls.yaml): optional configuration for enabling a self-managed TLS certificate
- [`connect-api.env`](./connect-api.env): an environment file used to customize the connect-api service
- [`connect-sync.env`](./connect-sync.env): an environment file used to customize the connect-sync service
## Overview
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.
## Prerequisites
- AMD64/ARM64 VM or bare metal server with a Docker-supported Linux distribution (e.g. Ubuntu, Debian, Fedora, etc.)
- Docker Engine (see [Docker Engine installation overview](https://docs.docker.com/engine/install/#server)) installed on the Linux server
- a public DNS A record pointing to the Linux server
- SSH access to the Linux server
- [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
## Get started
> [!Note]
> Before proceeding, review the [Get Started](https://developer.1password.com/docs/connect/get-started/) page from our 1Password Developer page.
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`.
### 🛠️ Prepare the Linux server
On the Linux machine that you will be using as the Docker host for your Connect server:
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.
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.
### 👨💻 Prepare your desktop
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:
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.
2. Open your preferred terminal. Clone this repository and switch to this directory:
```sh
git clone https://github.com/1Password/connect.git
cd ./connect/examples/beta/docker
```
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.
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:
```dotenv
# ...
OP_TLS_DOMAIN=op-connect-server.example.com
# ...
```
Save the file.
5. Create a Docker context to use for connecting to the Linux server:
_Example command:_
```sh
docker context create op-connect \
--description "1Password Connect Server Docker host" \
--docker host=ssh://user@op-connect-server.example.com
```
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.
> **Note**
>
> 🔑 You can store your SSH keys in 1Password and authenticate this and other SSH workflows without writing a
> private key to disk using the [1Password SSH agent](https://developer.1password.com/docs/ssh/get-started).
6. Switch to the Docker context you just created to connect to the Docker host:
```sh
docker context use op-connect
```
### 🪄 Create a swarm
Run this command to create the swarm:
```sh
docker swarm init # --advertise-addr 192.0.2.1
```
> **Note**
>
> Additional nodes may be optionally added to a swarm for fault tolerance. This command adds the Linux server as the
> first (and only) node in the swarm, but Docker _requires_ a unique IP address to advertise to other nodes (even if
> no other nodes will be added). See [How nodes work](https://docs.docker.com/engine/swarm/how-swarm-mode-works/nodes/)
> in the Docker documentation for more details.
>
> If multiple IP addresses are detected, Docker returns an error; uncomment the `--advertise-addr` parameter (delete
> `#`), replace the example IP (`192.0.2.1`) with the appropriate IP address, and run the command again.
## Deploy 1Password Connect server
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:
```sh
docker stack config --compose-file ./compose.template.yaml \ --compose-file ./compose.lets-encrypt.yaml |
docker stack deploy --compose-file - op-connect
```
## Test your Connect server
Run this command to view logs from the service for the Connect server container:
```sh
docker service logs op-connect_api --raw
```
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.
```sh
curl --silent --show-error --request GET --header "Accept: application/json" \
--header "Authorization: Bearer mF_9.B5f-4.1JqM" \
https:/op-connect.example.com/health/v1/vaults
```
Example JSON response:
```json
[
{
"attributeVersion": 1,
"contentVersion": 9,
"createdAt": "2025-03-21T13:06:35Z",
"id": "hgm3sn37vkj3lsdkeouq46wyca",
"items": 5,
"name": "REDACTED",
"type": "USER_CREATED",
"updatedAt": "2025-04-18T16:38:40Z"
},
{
"attributeVersion": 1,
"contentVersion": 2,
"createdAt": "2025-03-21T02:15:33Z",
"id": "mn4w65hq6ar7nc2fc7qgoru3ki",
"items": 1,
"name": "REDACTED",
"type": "USER_CREATED",
"updatedAt": "2025-03-21T02:16:32Z"
},
{
"attributeVersion": 1,
"contentVersion": 94,
"createdAt": "2025-03-21T15:20:41Z",
"id": "z4s56cbejab6q6urod3l5icqky",
"items": 3,
"name": "REDACTED",
"type": "USER_CREATED",
"updatedAt": "2025-04-29T02:49:13Z"
}
]
```
## Update 1Password Connect server
Swarm mode in Docker Engine uses a declarative service model. Services will automatically restart tasks when updating their configuration.
Use the Docker context from [Prepare your desktop](#-prepare-your-desktop) to connect to your Docker host and manage your stack.
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:
- https://hub.docker.com/r/1password/connect-api/tags
- https://hub.docker.com/r/1password/connect-sync/tags
```sh
docker service update op-connect_api --image 1password/connect-api:latest && \
docker service update op-connect_sync --image 1password/connect-sync:latest
```
## Rotate credentials
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.
For example, if you create new credentials for the Connect server:
1. Scale down the `op-connect_api` and `op-connect_sync services to shut down the running Connect server.
```sh
docker service scale op-connect_api=0 op-connect_sync=0
```
2. Update the service definition to unmount the the `1password-credentials.json` Docker secret:
```sh
docker service update --secret-rm credentials op-connect_api && \
docker service update --secret-rm credentials op-connect_sync
```
3. Remove the secret from the swarm:
```sh
docker secret rm credentials
```
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:
```sh
docker secret create credentials ./1password-credentials.json
```
5. Update the service to mount the new `1password-credentials.json` secret:
```sh
docker service update --secret-add source=credentials,target=/home/opuser/.op/1password-credentials.json,uid="999",gid="999",mode=0440 op-connect_api && \
docker service update --secret-add source=credentials,target=/home/opuser/.op/1password-credentials.json,uid="999",gid="999",mode=0440 op-connect_sync
```
6. Scale the `op-connect_api` and `op-connect_sync` services back up to reboot your Connect server:
```sh
docker service scale op-connect_api=1 op-connect_sync=1
```
[Test your Connect server](#-test-your-Connect server) using the new bearer token associated with the new `1password-credentials.json` file.
A similar process can be used to update the values for any other Docker secrets used in your configuration.
## Appendix: Customize your Connect server
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.
For example, to reboot your Connect server with debug logging enabled:
```sh
docker service update op-connect_api \
--env-add OP_DEBUG=1
```
To turn off debug logging and inject some color into the logs in your console:
```sh
docker service update op-connect_api \
--env-rm OP_DEBUG \
--env-add OP_PRETTY_LOGS=1
```
_Pretty logs pair nicely with the `--raw` parameter of the `docker service logs` command). 🤩_
### 🔒 Advanced TLS options
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.
Other supported options include:
#### External load balancer or reverse proxy
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:
```sh
docker stack config \
--compose-file ./compose.template.yaml \
--compose-file ./compose.http.yaml |
docker stack deploy --compose-file - op-connect
```
#### Self-managed TLS certificate
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.
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:
```sh
docker stack config \
--compose-file ./compose.template.yaml \
--compose-file ./compose.custom-tls.yaml |
docker stack deploy --compose-file - op-connect
```
================================================
FILE: examples/beta/docker/compose.custom-tls.yaml
================================================
services:
api:
environment: [OP_TLS_CERT_FILE=/home/opuser/.op/tls.crt, OP_TLS_KEY_FILE=/home/opuser/.op/tls.key]
ports: [443:8443]
secrets:
- source: op-tls-crt
target: /home/opuser/.op/tls.crt
uid: "999"
gid: "999"
mode: 0440
- source: op-tls-key
target: /home/opuser/.op/tls.key
uid: "999"
gid: "999"
mode: 0440
secrets:
op-tls-crt:
file: certificate.pem
name: op-tls-crt
op-tls-key:
file: key.pem
name: op-tls-key
================================================
FILE: examples/beta/docker/compose.http.yaml
================================================
services:
api:
ports: [80:8080]
================================================
FILE: examples/beta/docker/compose.lets-encrypt.yaml
================================================
services:
api:
environment:
OP_TLS_USE_LETSENCRYPT: 1
ports: [443:8443]
================================================
FILE: examples/beta/docker/compose.template.yaml
================================================
services:
api:
image: 1password/connect-api
networks: [op-connect]
deploy:
replicas: 1
restart_policy:
condition: any
secrets:
- source: credentials
target: /home/opuser/.op/1password-credentials.json
uid: "999"
gid: "999"
mode: 0400
volumes:
- data:/home/opuser/.op/data
env_file: connect-api.env
user: "999:999"
sync:
image: 1password/connect-sync
networks: [op-connect]
deploy:
replicas: 1
restart_policy:
condition: any
secrets:
- source: credentials
target: /home/opuser/.op/1password-credentials.json
uid: "999"
gid: "999"
mode: 0400
volumes:
- data:/home/opuser/.op/data
env_file: connect-sync.env
user: "999:999"
volumes:
data:
secrets:
credentials:
file: 1password-credentials.json
name: credentials
networks:
op-connect:
================================================
FILE: examples/beta/docker/connect-api.env
================================================
# # 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.
# OP_TLS_DOMAIN=connect.example.com
# # The logging level of the container.
# # Acceptable values: info, error, debug
# # Default value: info
# OP_LOG_LEVEL=debug
# The time (in seconds) to wait for the initial sync to complete.
# Acceptable values: A time duration (for example, 1h, 30m, 20s).
# Default value: 10s (10 seconds)
# OP_SYNC_TIMEOUT=1m
# # ---Manual bus configuration---
# # The port for listening to incoming bus connections from other containers.
# # Acceptable values: Any available port number.
# # Default value: A random free port
# OP_BUS_PORT=11223
# # A comma-separated list of [hostname]:[bus port] pairs of other containers to connect to.
# # Acceptable values: A comma-separated list of [hostname]:[bus port] pairs.
# # Default value: No default because this variable is optional.
# OP_BUS_PEERS=sync:11223
================================================
FILE: examples/beta/docker/connect-sync.env
================================================
# # The logging level of the container.
# # Acceptable values: info, error, debug
# # Default value: info
# OP_LOG_LEVEL=debug
# # ---Manual bus configuration---
# # The port for listening to incoming bus connections from other containers.
# # Acceptable values: Any available port number.
# # Default value: A random free port
# OP_BUS_PORT=11223
# # A comma-separated list of [hostname]:[bus port] pairs of other containers to connect to.
# # Acceptable values: A comma-separated list of [hostname]:[bus port] pairs.
# # Default value: No default because this variable is optional.
# OP_BUS_PEERS=api:11223
================================================
FILE: examples/docker/compose/README.md
================================================
# Docker Compose
The `docker-compose.yaml` file in this directory is a simple example of how to run 1Password Connect using Docker Compose.
## Getting Started
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:
```yaml
volumes:
- "/1password-credentials.json:/home/opuser/.op/1password-credentials.json"
```
### Start the Docker containers:
```bash
docker-compose up [-d]
```
The Connect API will be accessible through port `8080` on the host machine:
```curl
curl \
-H "Accept: application/json" \
-H "Authorization: Bearer $OP_API_TOKEN" \
http://localhost:8080/v1/vaults
```
================================================
FILE: examples/docker/compose/docker-compose.yaml
================================================
version: "3.4"
services:
op-connect-api:
image: 1password/connect-api:latest
ports:
- "8080:8080"
volumes:
- "./1password-credentials.json:/home/opuser/.op/1password-credentials.json"
- "data:/home/opuser/.op/data"
op-connect-sync:
image: 1password/connect-sync:latest
ports:
- "8081:8080"
volumes:
- "./1password-credentials.json:/home/opuser/.op/1password-credentials.json"
- "data:/home/opuser/.op/data"
volumes:
data:
================================================
FILE: examples/kubernetes/README.md
================================================
# Kubernetes Deployment
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.
Included in this folder are:
- [`op-connect-deployment.yaml`](op-connect-deployment.yaml): Deployment resource to create the Pod for 1Password Connect
- `connect-sync` container to sync vault(s) from 1Password.com
- `connect-api` container to serve 1Password Connect API
- Shared ephemeral volume for data
- [`op-connect-service.yaml`](op-connect-service.yaml): Service resource for connecting to the above Pod
- [`op-connect-issuer.yaml`](op-connect-issuer.yaml): cert-manager ClusterIssuer resource to manage TLS certificates using [Let's Encrypt](https://letsencrypt.org/)
- [`op-connect-ingress.yaml`](op-connect-ingress.yaml): Ingress resource to route TLS requests to 1Password Connect
## Prerequisites
- 1Password Connect server credentials file (see [Quick Start](/README.md#quick-start) in the root of this repository)
- Kubernetes cluster with available [ingress controller](https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/), e.g.:
- [Ingress NGINX Controller](https://kubernetes.github.io/ingress-nginx/deploy/)
- [GKE Ingress](https://cloud.google.com/kubernetes-engine/docs/concepts/ingress)
- [AWS Load Balancer Controller](https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.4/deploy/installation/)
- [Azure Application Gateway Ingress Controller](https://learn.microsoft.com/en-us/azure/application-gateway/tutorial-ingress-controller-add-on-new)
- [cert-manager](https://cert-manager.io) installed on your cluster (see [Installation - cert-manager Documentation](https://cert-manager.io/docs/installation/))
- A DNS provider in which you can create public records.
## Getting started
1. If you haven't already done so, save your `1password-credentials.json` file locally.
2. Create a Kubernetes Secret for the credentials file:
```sh
kubectl create secret generic op-credentials --from-file=1password-credentials.json=./1password-credentials.json
```
> **Note**
>
> If saved with a different filename or somewhere other than the working
> directory, replace `./1password-credentials.json` with the path to your
> credentials file, i.e.:
>
> ```sh
> kubectl create secret generic op-credentials --from-file=1password-credentials.json=path/to/credentials-file.json
> ```
3. Clone this repository and switch to this directory:
```sh
git clone https://github.com/1Password/connect.git
cd ./connect/examples/kubernetes
```
4. Edit [`op-connect-issuer.yaml`](op-connect-issuer.yaml). Replace the value for `spec.acme.email` with a valid email address:
```yaml
...
spec:
acme:
...
email: user@example.com
...
```
Save the file.
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):
```yaml
...
spec:
rules:
- host: connect.example.com
...
tls:
- hosts:
- connect.example.com
...
```
> **Warning**
>
> Depending on your chosen ingress controller, you may also need to add
> additional annotations or make other changes to this manifest.
Save the file.
## Deploy 1Password Connect
1. Apply the manifests to your cluster:
```sh
kubectl apply -f .
```
> **Note**
>
> The `.` in this command applies all `.yaml` and `.json` files in the
> working directory. If this includes the 1Password Connect credentials
> file, the output returned by `kubectl` may include something similar to:
>
>>```output
>> error: error validating "1password-credentials.json":...
>> ```
>
> This error can safely be ignored.
2. Get the IP address of the ingress controller:
```sh
kubectl get ing/connect-ingress
```
The IP address for this Ingress resource will be listed under the `ADDRESS` column.
> **Note**
>
> Depending on your cluster or cloud provider, it may be several minutes
> before an IP address is assigned. If there is no IP address listed yet,
> run the same command again in a few minutes.
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).
## Test the connection
Confirm public access by connecting using HTTPS from outside your cluster:
> **Note**
>
> You will need to set the value of the `$OP_API_TOKEN` variable to an active token for this Connect environment.
```sh
curl \
-X GET \
-H "Accept: application/json" \
-H "Authorization: Bearer $OP_API_TOKEN" \
https://connect.example.com/v1/vaults
```
This will also intialize the connection to 1Password.com and complete the first valut data sync.
## Appendix
### Updating 1Password Connect
When a new version of 1Password Connect is released, set the image version for each of the containers to rollout a new revision:
```sh
OP_CONNECT_VERSION=1.5.7; kubectl set image deploy/op-connect \
connect-api=1password/connect-api:$OP_CONNECT_VERSION \
connect-sync=1password/connect-sync:$OP_CONNECT_VERSION
```
================================================
FILE: examples/kubernetes/op-connect-deployment.yaml
================================================
apiVersion: apps/v1
kind: Deployment
metadata:
name: op-connect
spec:
selector:
matchLabels:
app: op-connect
template:
metadata:
labels:
app: op-connect
spec:
volumes:
- name: shared-data
emptyDir: {}
- name: credentials
secret:
secretName: op-credentials
initContainers:
- name: sqlite-permissions
image: alpine:3.12
command:
- "/bin/sh"
- "-c"
args:
- "mkdir -p /home/opuser/.op/data && chown -R 999 /home/opuser && chmod -R 700 /home/opuser && chmod -f -R 600 /home/opuser/.op/config || :"
volumeMounts:
- mountPath: /home/opuser/.op/data
name: shared-data
containers:
- name: connect-api
image: 1password/connect-api:latest
resources:
limits:
memory: "128Mi"
cpu: "0.2"
ports:
- name: connect-api
containerPort: 8080
env:
- name: OP_SESSION
value: /home/opuser/.config/1password-credentials.json
volumeMounts:
- mountPath: /home/opuser/.op/data
name: shared-data
- mountPath: /home/opuser/.config
name: credentials
readOnly: true
livenessProbe:
httpGet:
path: /heartbeat
port: connect-api
initialDelaySeconds: 3
periodSeconds: 3
readinessProbe:
httpGet:
path: /health
port: connect-api
initialDelaySeconds: 5
periodSeconds: 5
- name: connect-sync
image: 1password/connect-sync:latest
resources:
limits:
memory: "128Mi"
cpu: "0.2"
ports:
- name: connect-sync
containerPort: 8081
env:
- name: OP_SESSION
value: /home/opuser/.config/1password-credentials.json
- name: OP_HTTP_PORT
value: "8081"
volumeMounts:
- mountPath: /home/opuser/.op/data
name: shared-data
- mountPath: /home/opuser/.config
name: credentials
readOnly: true
livenessProbe:
httpGet:
path: /heartbeat
port: connect-sync
initialDelaySeconds: 3
periodSeconds: 3
readinessProbe:
httpGet:
path: /health
port: connect-sync
initialDelaySeconds: 5
periodSeconds: 5
================================================
FILE: examples/kubernetes/op-connect-ingress.yaml
================================================
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: connect-ingress
annotations:
# Use cert-manager ClusterIssuer
cert-manager.io/cluster-issuer: letsencrypt
# Allow cert-manager to edit this Ingress resource in-place
acme.cert-manager.io/http01-edit-in-place: "true"
# Issue temporary self-signed cert
cert-manager.io/issue-temporary-certificate: "true"
spec:
# You may need to specify the IngressClass if your cluster does not have a
# default IngressClass, e.g. for Ingress NGINX Controller, uncomment the
# below line:
# ingressClassName: nginx
rules:
- host: connect.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: op-connect
port:
name: connect-api
tls:
- hosts:
- connect.example.com
secretName: connect-cert
================================================
FILE: examples/kubernetes/op-connect-issuer.yaml
================================================
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt
spec:
acme:
# You *must* replace this value with a valid email address.
# Let's Encrypt will use this to contact you about expiring
# certificates, and issues related to your account.
email: user@example.com
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
# Secret resource that will be used to store the account's private key.
name: letsencrypt-issuer-account-key
solvers:
# Use HTTP01 solver
- http01:
ingress:
# Edit existing Ingress
name: connect-ingress
================================================
FILE: examples/kubernetes/op-connect-service.yaml
================================================
apiVersion: v1
kind: Service
metadata:
name: op-connect
spec:
selector:
app: op-connect
ports:
- name: connect-api
port: 8080
targetPort: connect-api