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 ID from the Organization Details section.
Install the Bindplane agent
Windows installation
- Open the Command Prompt or PowerShell as 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 Services console or enter the following command:
net stop BindPlaneAgent && net start BindPlaneAgent
Get the 1Password API token
- Sign in to the 1Password web UI.
- Go to Integrations.
- Click Directory at the top of the page.
- Enter a name for the token and set the token expiration.
- In Events Reporting, click Other.
- Select the corresponding Event Types.
- Click Issue Token to generate the access token key.
- Click Save in 1Password and select which vault to save your token to.
- Click View Integration Details to 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. |
Changes
2024-07-08
Enhancement:
- Mapped
item.actor_details.uuid
toprincipal.user.userid
. - Mapped
item.actor_details.name
toprincipal.user.user_display_name
. - Mapped
item.actor_details.email
toprincipal.email
. - Mapped
item.actor_uuid
tosecurity_result.about.resource.attribute.labels
. - Mapped
item.object_type
toadditional.fields
. - Mapped
item.object_uuid
tosecurity_result.about.resource.attribute.labels
. - Mapped
item.aux_info
toadditional.fields
. - Mapped
item.aux_uuid
tosecurity_result.about.resource.attribute.labels
. - Mapped
item.session.uuid
tonetwork.session_id
. - Mapped
item.session.device_uuid
totarget.resource.product_object_id
. - Mapped
item.session.ip
toprincipal.ip
.
2023-06-07
Enhancement:
- Remapped
target_user.name
totarget.user.user_display_name
. - Remapped
target_user.email
totarget.user.email_addresses
. - Remapped
target_user.uuid
totarget.user.userid
. - If user fields aren't present, changed
metadata.event_type
toSTATUS_UPDATE
.
2023-05-05
Enhancement:
- Added gsub function to correct format and parse the unparsed logs.
2023-04-27
Enhancement:
- Provided parsing support for multiple fields in
items
array by using loop.
2023-02-15
Enhancement:
- Unmapped
user.uuid
anduuid
. - Modified mapping of
target_user.email
fromtarget.user.userid
toprincipal.user.email_addresses
. - Modified mapping of
target_user.uuid
fromtarget.user.product_object_id
toprincipal.user.userid
.
2023-02-06
Enhancement:
- Mapped
metadata.vendor_name
andmetadata.product_name
.
2023-01-15
Enhancement:
- Mapped
uuid
toprincipal.userid
. - If
user.uuid
anduuid
are present then mapuuid
toadditional.fields
. - Mapped
location.region
toprincipal.location.name
. - Mapped
metadata.product_version
toclient.app_version
. - Mapped
principal.asset.platform_software.platform_version
toclient.platform_version
. - Mapped
type
,action
,used_version
, toadditional.fields
. - Mapped
client.platform_name
toprincipal.resource.attribute.labels
. - Mapped
vault_uuid
tosecurity_result.about.resource.attribute.labels
. - Mapped
country
toprincipal.location.country_or_region
. - If
category
is equal tosuccess
, then mapsecurity_result.action
toALLOW
. - Mapped
category
tosecurity_result.category_details
. - Remapped
target_user.email
totarget.user.userid
. - Remapped
target_user.uuid
totarget.user.product_object_id
. - Remapped
item_uuid
toabout.resource.attribute.labels
.
2022-10-07
- Newly created parser.
Need more help? Get answers from Community members and Google SecOps professionals.