API token issues with non-admin user tokens

I have created a local mysql user named “svc_ansible” via the WebUI, granted the user admin-level privileges and then generated an API token for the user. When I try to post a new device using curl to contact the API endpoint, I see the following:

curl -X POST -d '{"hostname": "10.9.1.69", "version": "v2c", "community": "public"}' -H 'X-Auth-Token: notarealtoken' -H 'Content-Type: application/json' http://hostname.domain.net/api/v0/devices
{"message":"Unauthenticated."}

If I run the same command with the token assigned to the global admin user, it works:

curl -X POST -d '{"hostname": "10.9.1.69", "version": "v2c", "community": "public"}' -H "X-Auth-Token: adifferentnotrealtoken" -H 'Content-Type: application/json' http://hostname.domain.net/api/v0/devices 
{
    "status": "error",
    "message": "Already have host 10.9.1.69"

I would think it would still be a permissions issue with the svc_ansible user and/or token, but if I pass the token for this user into an ansible module I wrote for adding devices it works:

FAILED! =>

{
    "body": "{\n    \"status\": \"error\",\n    \"message\": \"Already have host 10.9.1.70\"\n}",
    "changed": false,
    "invocation": {
        "module_args": {
            "community": "public ",
            "hostname": "10.9.1.70",
            "port": 161,
            "snmpver": "v2c",
            "token": "notarealtoken",
            "url": "http://hostname.domain.net/api/v0/devices"
        }
    },
    "msg": "Unable to Add Device"
}

PLAY RECAP **********************************************************************************************************************************************************************************************************************************bp3-efw01-l001 : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
bp3-efw01-l002 : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0

the playbook:

---
- name: Test Libre NMS Custom Module in Playbook
  hosts: bp3_fw     # for loading inventory_hostname and other INV vars from sources
  connection: local  # module execution on localhost incl conn. estl.
  gather_facts: no

  tasks:
    - name: Add Devices to LibreNMS 
      libre_add_device:
        url: "{{ libre.base_url }}/api/v0/devices"   #
        hostname:  "{{ ansible_host }}"    #
        community: "{{ libre.community }} "      #
        token:  "{{ libre.api.token }}"     #
      delegate_to: localhost

group_vars/all/default.yaml:

libre:
  base_url: http://hostname.domain.net
  community: public
  api:
    user: svc_ansible
    token: notarealtoken

the module:

import json

from ansible.module_utils.urls import fetch_url

from ansible.module_utils.basic import AnsibleModule

def main():
    args = {
        "url": {"required": True, "type": "str"},
        "hostname":  {"required": True,"type": "str"},
        "community":  {"required":  True, "type": "str"},
        "token":  {"required": True, "type": "str"},
        "port":  {"required": False, "default": 161, "type": "int"},
        "snmpver":  {"required": False, "default": "v2c", "type": "str"}
}

    module = AnsibleModule(
        argument_spec=args,
        supports_check_mode=False
    )

    json_data = {
        "hostname": module.params['hostname'],
        "community": module.params['community'],
        "port": module.params['port'],
        "version": module.params['snmpver'],
    }

    headers = {
        "X-Auth-Token": module.params['token']
    }

    api = module.params['url']

    resp, info = fetch_url(module, api, data=module.jsonify(json_data), headers=headers, method="POST",)

    result = {}

    if info['status'] == 200:
        result['changed'] = True
        result['status'] = info['status']
        module.exit_json(**result)
    else:
        result['changed'] = False
        result['msg'] = "Unable to Add Device"
        result['body'] = info['body']
        module.fail_json(**result)

if __name__ == '__main__':
    main()

Since ansible returns the appropriate message in the results body when the http return code is not 200 (since this specific device is already added) and returns the “args” showing that the right values were passed in at runtime, what is the reason that when I use this token via curl it doesn’t work?

I just tried this here with a ldap user with admin rights that I assigned a API token to using your Curl syntax and it worked fine. Is there something strange in the token that needs escaping or something ? If you create a new token does the issue still occur?

Does sound very strange indeed.

I don’t think it is the curl command syntax, otherwise substituting the admin user token for the svc_ansible token in ‘X-Auth-Token: ’ would fail with the same message. the perplexing part is that the svc_ansible token works when passed to the playbook calling the module, and I just can’t put my finger on it.

the only thing that is coming to me right now (and it’s not a strong argument ATT) is that because I’m using the fetch_url(*args, **kwargs) function in the module, the function gains access to other variables because the module instance is passed in to the call, such as username/password via the underlying methods available to the AnsibleModule() class. but again…not the strongest argument since the actual username ‘svc_ansible’ is stored in group_vars/all/default.yaml as part of a user-defined dictionary that should only be accessible as a jinja2 templated variable called by the user in the playbook, which I’m not currently doing with this particular playbook. IDK, I’ll keep poking at it…

ok I’m an idiot. it helps if you pass the token with all the actual characters. I ran tcpdump on the librenms side and captured all of the individual HTTP sessions coming from curl, ansible, etc and noticed that one character was missing from the token passed into curl…no idea how that happened exactly, but now the API call works using curl and the svc_ansible token.

$ cat capture.pcap | grep e2c                            
        X-Auth-Token: e2cd854fe0e8ad8cf664b9eeb7cd1a0
        X-Auth-Token: e2cd854fe0e8ad8cf664b9ee6b7cd1a0
        X-Auth-Token: e2cd854fe0e8ad8cf664b9ee6b7cd1a0
1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.