Collect 1Password logs
This document explains how to ingest 1Password logs to Google Security Operations using Bindplane. The parser transforms raw JSON formatted log data into a structured format compliant with the Google SecOps Unified Data Model (UDM). It specifically focuses on normalizing and enriching events related to user sign-in attempts, extracting details about the user, their location, client information, and the outcome of the attempt.
Before you begin
Ensure that you have the following prerequisites:
- Google SecOps instance
- Windows 2016 or later or Linux host with systemd
- If running behind a proxy, firewall ports are open
- Privileged access to 1Password
Get Google SecOps ingestion authentication file
- Sign in to the Google SecOps console.
- Go to SIEM Settings > Collection Agents.
- Download the Ingestion Authentication File. Save the file securely on the system where Bindplane will be installed.
Get Google SecOps customer ID
- Sign in to the Google SecOps console.
- Go to SIEM Settings > Profile.
- Copy and save the Customer IDfrom the Organization Detailssection.
Install the Bindplane agent
Windows installation
- Open the Command Promptor PowerShellas an administrator.
-
Run the following command:
msiexec / i "https://github.com/observIQ/bindplane-agent/releases/latest/download/observiq-otel-collector.msi" / quiet
Linux installation
- Open a terminal with root or sudo privileges.
-
Run the following command:
sudo sh -c " $( curl -fsSlL https://github.com/observiq/bindplane-agent/releases/latest/download/install_unix.sh ) " install_unix.sh
Additional installation resources
For additional installation options, consult the installation guide .
Configure the Bindplane agent to ingest Syslog and send to Google SecOps
- Access the configuration file:
- Locate the
config.yaml
file. Typically, it's in the/etc/bindplane-agent/
directory on Linux or in the installation directory on Windows. - Open the file using a text editor (for example,
nano
,vi
, or Notepad).
- Locate the
-
Edit the
config.yaml
file as follows:receivers : udplog : # Replace the port and IP address as required listen_address : "0.0.0.0:514" exporters : chronicle/chronicle_w_labels : compression : gzip # Adjust the path to the credentials file you downloaded in Step 1 creds : '/path/to/ingestion-authentication-file.json' # Replace with your actual customer ID from Step 2 customer_id : < customer_id > endpoint : malachiteingestion-pa.googleapis.com # Add optional ingestion labels for better organization ingestion_labels : log_type : 'ONEPASSWORD' raw_log_field : body service : pipelines : logs/source0__chronicle_w_labels-0 : receivers : - udplog exporters : - chronicle/chronicle_w_labels
-
Replace the port and IP address as required in your infrastructure.
-
Replace
<customer_id>
with the actual customer ID. -
Update
/path/to/ingestion-authentication-file.json
to the path where the authentication file was saved in the Get Google SecOps ingestion authentication file section.
Restart Bindplane agent to apply the changes
-
To restart the Bindplane agent in Linux, run the following command:
sudo systemctl restart bindplane-agent
-
To restart the Bindplane agent in Windows, you can either use the Servicesconsole or enter the following command:
net stop BindPlaneAgent && net start BindPlaneAgent
Get the 1Password API token
- Sign in to the 1Passwordweb UI.
- Go to Integrations.
- Click Directoryat the top of the page.
- Enter a name for the tokenand set the token expiration.
- In Events Reporting, click Other.
- Select the corresponding Event Types.
- Click Issue Tokento generate the access token key.
- Click Save in 1Passwordand select which vault to save your token to.
- Click View Integration Detailsto view the token.
Configure a Linux host to run the following
-
Run the following command:
import datetime import requests import os import socket import json # For more information, check out the support page: https://support.1password.com/events-reporting api_token = os.environ.get('EVENTS_API_TOKEN') url = "https://events.1password.com" if not api_token: print("Please set the EVENTS_API_TOKEN environment variable.") exit(1) start_time = datetime.datetime.now() - datetime.timedelta(hours=24) # Define the bindplane agent details syslog_server_ip = <ip-address> # Replace with your Bindplane IP syslog_server_port = <port-number> # Replace with your Bindplane port headers = { "Content-Type": "application/json", "Authorization": f"Bearer {api_token}" payload = { "limit": 20, "start_time": start_time.astimezone().replace(microsecond=0).isoformat() # Alternatively, use the cursor returned from previous responses to get any new events # payload = { "cursor": cursor } try: r = requests.post(f"{url}/api/v1/signinattempts", headers=headers, json=payload) r.raise_for_status() # Raise an exception if the request fails if r.status_code == requests.codes.ok: # Send the response to the bindplane server syslog_message = f"{json.dumps(r.json())}" with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: sock.connect((syslog_server_ip, syslog_server_port)) sock.sendall(f"{syslog_message}\n".encode()) else: print(f"Error getting sign-in attempts: status code {r.status_code}") except requests.exceptions.RequestException as e: print(f"Request error: {e}") except Exception as e: print(f"Error during syslog logging: {e}")
UDM mapping table
Log field | UDM mapping | Logic |
---|---|---|
category
|
security_result.category_details | The value is taken from the category
field in the raw log. |
client.app_name
|
principal.application | The value is taken from the client.app_name
field in the raw log. |
client.app_version
|
metadata.product_version | The value is taken from the client.app_version
field in the raw log. |
client.ip_address
|
principal.ip | The value is taken from the client.ip_address
field in the raw log. |
client.os_name
|
principal.platform | The value is taken from the client.os_name
field in the raw log and mapped to the corresponding UDM platform value (LINUX, WINDOWS, MAC). |
client.os_version
|
principal.platform_version | The value is taken from the client.os_version
field in the raw log. |
client.platform_name
|
principal.resource.attribute.labels.key: platform_name
, principal.resource.attribute.labels.value: Chrome
|
The value is taken from the client.platform_name
field in the raw log. |
client.platform_version
|
principal.asset.platform_software.platform_version | The value is taken from the client.platform_version
field in the raw log. |
country
|
principal.location.country_or_region | The value is taken from the country
field in the raw log if location.country
is not present. |
item_uuid
|
security_result.about.resource.attribute.labels.key: item_uuid
, security_result.about.resource.attribute.labels.value: nx4f2lhmafhhfkvgid6ff2fyh4
|
The value is taken from the item_uuid
field in the raw log. |
location.city
|
principal.location.city | The value is taken from the location.city
field in the raw log. |
location.country
|
principal.location.country_or_region | The value is taken from the location.country
field in the raw log. |
location.latitude
|
principal.location.region_latitude | The value is taken from the location.latitude
field in the raw log. |
location.longitude
|
principal.location.region_longitude | The value is taken from the location.longitude
field in the raw log. |
location.region
|
principal.location.name | The value is taken from the location.region
field in the raw log. |
session.ip
|
principal.ip | The value is taken from the session.ip
field in the raw log. |
session_uuid
|
network.session_id | The value is taken from the session_uuid
field in the raw log. |
target_user.email
|
target.user.email_addresses | The value is taken from the target_user.email
field in the raw log. |
target_user.uuid
|
target.user.userid | The value is taken from the target_user.uuid
field in the raw log. |
timestamp
|
metadata.event_timestamp.seconds, metadata.event_timestamp.nanos | The value is taken from the timestamp
field in the raw log and converted to seconds and nanoseconds. |
type
|
additional.fields.key: type
, additional.fields.value.string_value: mfa_ok
|
The value is taken from the type
field in the raw log. |
user.email
|
principal.user.email_addresses | The value is taken from the user.email
field in the raw log. |
user.name
|
principal.user.user_display_name | The value is taken from the user.name
field in the raw log. |
used_version
|
additional.fields.key: used_version
, additional.fields.value.string_value: 1
|
The value is taken from the used_version
field in the raw log. |
uuid
|
principal.resource.attribute.labels.key: uuid
, principal.resource.attribute.labels.value: EPNGUJLHFVHCXMJL5LJQGXTENA
|
The value is taken from the uuid
field in the raw log. |
vault_uuid
|
security_result.about.resource.attribute.labels.key: vault_uuid
, security_result.about.resource.attribute.labels.value: lddjidoxtrxteclqhubbo3pkyq
|
The value is taken from the vault_uuid
field in the raw log. |
N/A
|
extensions.auth | An empty object is created for this field. |
N/A
|
metadata.event_type | The value is set to USER_LOGIN
if category
is success
or firewall_reported_success
, STATUS_UPDATE
if no user information is present, and USER_UNCATEGORIZED
otherwise. |
N/A
|
metadata.log_type | The value is set to ONEPASSWORD
. |
N/A
|
metadata.product_name | The value is set to ONEPASSWORD
. |
N/A
|
metadata.vendor_name | The value is set to ONEPASSWORD
. |
N/A
|
security_result.action | The value is set to ALLOW
if category
is success
or firewall_reported_success
, BLOCK
if category
is credentials_failed
, mfa_failed
, modern_version_failed
, or firewall_failed
, and is left empty otherwise. |
Need more help? Get answers from Community members and Google SecOps professionals.