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

Get started
--- 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