op commands failing from cronjob/bash script

CoreyPaul
CoreyPaul
Community Member
edited August 2020 in CLI

I suspect the following is more of a Linux question, but maybe someone here has had the same issue already.

I have a Python script that syncs "Secrets" (user/pass combos) from an IPAM software called NetBox to 1Pass. To launch the .py script I am using another bash script that is regularly called by a cronjob. When I launch the bash script from the command line, everything works completely fine. However, when launching the bash script (using the full path) from a cronjob, the script fails to run the op commands correctly. I'm thinking this is related to the eval and environment variable in some way.

One thing to note, for testing, in order to automate inputting the Master Password for signin, I have another script from elsewhere that temporarily decrypts the password, adds a plaintext file in base64 named op.mp and then deletes it after the sync script has run. You'll see that in the first two lines copied below.

cat /home/testuser/nornir/auth/op.mp | base64 --decode | op signin
eval $(cat /home/testuser/nornir/auth/op.mp | base64 --decode | op signin testuser)
op delete vault netbox
op create vault --allow-admins-to-manage true --description "Synced entries from NetBox" netbox

/usr/bin/python3 /home/testuser/nornir/update_1pass.py

if [ $? -eq 0 ]; then
    logger --rfc3164 -P 514 -n 10.10.10.10 "NetBox device secrets have been synced with 1Password"
else
    logger --rfc3164 -P 514 -n 10.10.10.10 "Failed to sync NetBox device secrets with 1Password"
fi

Any idea what could be causing this to fail specifically by launching the bash/python script via crontab/cronjob? Is there another recommended way to do this?


1Password Version: 1.4.0
Extension Version: Not Provided
OS Version: 4.19.0-8-amd64 #1 SMP Debian 4.19.98-1+deb10u1 (20
Sync Type: Not Provided

Comments

  • CoreyPaul
    CoreyPaul
    Community Member

    Well, I figured it out. I added all the op commands directly into the python script itself using os.system()

    You can disregard/delete this post!

  • felix_1p
    felix_1p
    1Password Alumni

    Hi @CoreyPaul ! I'm glad you were able to figure this out on your own.

    One reason why it might fail is that it might not find the op binary since PATH might be set to a different value than in your "normal" shell. But if that was the case then your solution wouldn't work either.

    What errors did you originally get?

  • jr_jeff
    jr_jeff
    Community Member

    @CoreyPaul - Any chance you would be willing to share your python script. I want to do the same between my iCloud Keychain passwords (mostly Safari) and 1Password. Most of my devices are iOS or Mac but I do have a work laptop with Windows and trying to find a way to synchronize.

  • CoreyPaul
    CoreyPaul
    Community Member
    edited August 2020

    @jr_jeff no problem, pasted below is the relevant portion, only omitting all of my custom NetBox API calls that pulls together a list of devices/secrets.

    import subprocess
    import requests
    import json
    import os
    import csv
    
    
    #
    # Sign in to 1Password CLI
    #
    
    op_signin = """
    cat /home/testuser/nornir/auth/op.mp | base64 --decode | op signin
    sleep 2
    OP_SESSION_testuser=$(cat /home/testuser/nornir/auth/op.mp | base64 --decode | op signin testuser --raw)
    export OP_SESSION_testuser
    sleep 2
    op delete vault netbox
    sleep 2
    op create vault --allow-admins-to-manage true --description "Synced entries from NetBox" netbox
    sleep 2
    """
    
    os.system(op_signin)
    
    
    #
    # Create entries in 1Password
    #
    
    with open("devices.csv") as f:
        for line in csv.DictReader(
            f, fieldnames=("device_name", "device_ip", "username", "password")
        ):
            devices = line
            cmd = (
                "SESSION_KEY=$(cat /home/testuser/nornir/auth/op.mp | base64 --decode | op signin testuser --raw) \n"
                + "op create item login title="
                + devices["device_name"]
                + " username="
                + devices["username"]
                + " password="
                + "'"
                + devices["password"]
                + "'"
                + " --url="
                + devices["device_ip"]
                + " --vault netbox"
                + " --session $SESSION_KEY"
            )
            os.system(cmd)
    
    
    #
    # Delete files and clean up
    #
    
    os.system("op signout")
    os.system("rm -rf devices.csv")
    os.system(
        'logger --rfc3164 -P 514 -n 10.10.10.100 "Finished syncing NetBox secrets to 1Password"'
    )
    
    

    Couple things to note here:

    • devices.csv is simply a comma separated file with one line per device listing device_name,device_ip,username,password. This file is created from my IPAM software by the script and then deleted afterwords. You would need to write a separate script that creates a similar file for your own devices.

    • The variable op_signin is a list of commands launched by os.system(op_signin). You'll see that I am deleting and recreating my vault called netbox everytime the script is launched (nightly). This completely rebuilds the vault and assigns the vault/items new UUIDs every time. I found this more convenient than checking for prexisting items everytime. It also requires that all my other scripts never store UUIDs and can only poll the op command line at runtime by item/device name.

    • op.mp is the 1Pass master password for the account being used. It's just a text file with the password stored as a base64 string. This file is created by the root user moments before launching the script. What you don't see is another file, /auth/op.mp.encrypted that is decrypted by the root user and used to create the decrypted op.mp file, which is then chmod to allow testuser to access it. After the python script is finished, the root user then deletes the op.mp leaving only the inaccessible op.mp.encrypted file to remain. I know this is a long away around proper security, but it separates user access and at least allows a somewhat obfuscated way of storing the 1Pass master password on a server and automating all of this with cronjobs.

  • ag_yaron
    ag_yaron
    1Password Alumni

    Thanks for sharing that script and info @CoreyPaul !
    Much appreciated :+1:

This discussion has been closed.