How to disable Ansible's password obfuscating feature - automation

I have a simple Ansible playbook to
Fetch a database connection config from an RestAPI,
Extract the config object from the payload,
Using the config JSON (as request body) to create a PUT request to another RestAPI.
At the 3rd stage I found that the database username and password combination is wrong. Later, while I print the outputs, I have found that the password has been replaced with a string named "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER".
After some googling, I found that this is a security feature by Ansible. Unfortunately, I haven't found any configuration or something like this to disable this feature. Is it possible to disable this feature? Or any other workaround?
---
- name: my-playbook
gather_facts: no
hosts: all
vars_files:
- secret
tasks:
- name: Fetch the config payload from the API
uri:
url: "{{get_config}}"
method: GET
user: "{{username}}"
password: "{{password}}"
validate_certs: no
return_content: yes
status_code: 200
body_format: json
register: config
- name: Extract the config object
set_fact:
config_raw: "{{ config.json | json_query(jmesquery) }}"
vars:
jmesquery: '{{name}}.config'
- name: print the config
debug:
msg: "{{config_raw}}"
- name: Creating object using config
uri:
url: "{{create_ocject}}"
method: PUT
user: "{{username}}"
password: "{{password}}"
validate_certs: no
body: "{{config_raw}}"
body_format: json
return_content: yes
status_code: 200
headers:
Content-Type: "application/json"
register: test_res
- name: output value
debug:
msg: "{{test_res.json}}"

Related

Ansible uri module loop on files

I'm using the Ansible uri module to make a PUT API call and using all files in a directory as parameters.
I have a list of files in a directory, and I want to use the name and the content of each file in the API call
First of all i tried to list all files.
- name: "Find pipeline files in folder"
find:
paths: "/app/pipelines"
patterns: "pipeline-*.json"
file_type: "file"
register: pipe_files
- debug:
var: pipe_files
Then I want to make a loop on each file in the directory and call the API
- name: PUT PIPE
uri:
method: PUT
headers:
Content-Type: "application/json"
url: "https://api_url/**FILE_NAME**"
user: "user"
password: "user_pass"
body_format: json
body: "{{ lookup('file','/app/pipelines/**FILE_NAME.json**') }}"
validate_certs: no
force_basic_auth: yes
validate_certs: no
return_content: yes
register: pipeline_created
until: pipeline_created.status == 200
When I deploy the content, I don't have the exact filename, how can I make the loop on each file to call the API?
Best regards,
Thanks in advance.
pipe_files is a register from a find task. You can have a look at returned values in the find module documentation. You can also examine your debug task output to better get accustomed with the content of the variable.
Anyway. The list of file objects returned will be in pipe_files.files. Each element is a dict where the information you need is in the path key.
You may test with
- name: PUT pipeline
uri:
method: PUT
headers:
Content-Type: "application/json"
url: "https://api_url/{{ item.path | basename }}" # depends on input list content
user: "user"
password: "user_pass"
body_format: json
body: "{{ lookup('file', item.path) }}" # content
validate_certs: no
force_basic_auth: yes
validate_certs: no
return_content: yes
until: pipeline_created.status == 200
loop: "{{ pipe_files.files }}"
register: pipeline_created # result will become a list

GCP API Gateway: Path parameters are being passed as query params

I'm trying to use GCP API Gateway to create a single endpoint for a couple of my backend services (A,B,C,D), each with their own path structure. I have the Gateway configured for one of the services as follows:
swagger: '2.0'
info:
title: <TITLE>
description: <DESC>
version: 1.0.0
schemes:
- https
produces:
- application/json
paths:
/service_a/match/{id_}:
get:
summary: <SUMMARY>
description: <DESC>
operationId: match_id_
parameters:
- required: true
type: string
name: id_
in: path
- required: true
type: boolean
default: false
name: bool_first
in: query
- required: false
type: boolean
default: false
name: bool_Second
in: query
x-google-backend:
address: <cloud_run_url>/match/{id_}
deadline: 60.0
responses:
'200':
description: Successful Response
'422':
description: Validation
This deploys just fine. But when I hit the endpoint gateway_url/service_a/match/123, it gets routed to cloud_run_url/match/%7Bid_%7D?id_=123 instead of cloud_run_url/match/123.
How can I fix this?
Editing my answer as I misunderstood the issue.
It seems like the { are getting leaked from your configuration as ASCII code, so when you call
x-google-backend:
address: <cloud_run_url>/match/{id_}
deadline: 60.0
it doesn't show the correct ID.
So this should be a leak issue from your yaml file and you can approach this the same way as in this thread about using path params

Retry Ansible URI PUT API Call with a POST API Call

Hello I am trying to run a PUT API call via Ansible URI module for a particular API Endpoint in my application, using a dictionary that contains the json files and that is defined as:
example: { 'example1' : 'v1', 'example2': 'v2''}
- name: Update existing
block:
- name: update existing
uri:
url: "{{url}}/api/{{item.key}}/"
method: PUT
body: "{{ lookup('file', 'example/{{item.key}}/{{item.value}}.json') }}"
status_code: 200
body_format: json
headers:
Content-Type: "application/json"
Authorization: "Token {{ token.json.token }}"
with_dict: "{{ example }}"
register: result
For the PUT api call, this api endpoint will fail is the {{item.key}} does not exist, e.g. if
"{{url}}/api/{{item.key}}/" endpoint does not exist, hence it will give a 4xx error.
Given the task fails and I get a 4xx error when the api endpoint for the item does not exist, I want to run a POST command for that same json file.
How can I do this in ansible, to retry a task that failed but only specifically for that {{item.key}} and {{item.value}} in dictionary?
or
Is there a better way to do this to retry a failed PUT with a POST command
I want to use the ansible URI module
Thanks!
You can ignore the error case and then loop through result.results with filtering to keep only errors. You can have access to the original item with item.item:
- name: update existing with PUT
uri:
url: "{{url}}/api/{{item.key}}/"
method: PUT
body: "{{ lookup('file', 'example/{{item.key}}/{{item.value}}.json') }}"
status_code:
- 200
- 404 # supposing it's the "normal" error case
body_format: json
headers:
Content-Type: "application/json"
Authorization: "Token {{ token.json.token }}"
loop: "{{ example | items2dict }}"
register: result
- name: update existing with POST
uri:
url: "{{url}}/api/{{item.item.key}}/"
method: POSTT
body: "{{ lookup('file', 'example/{{item.item.key}}/{{item.item.value}}.json') }}"
status_code: 200
body_format: json
headers:
Content-Type: "application/json"
Authorization: "Token {{ token.json.token }}"
loop: "{{ result.results | rejectattr('status', '200') }}" # filter out 200 status from previous task

Display authorization header when logging in to api

I am using OpenApi do document an API, i have a path /Login which lets me specify Username and Password as parameters and returns a Bearer style JWT in the "authorization" header in the response.
For further use in the API this JWT is used with the "Authorize" mechanism so the other paths can be accessed.
Sadly the authorization header is only visible if i open the network tracing in my browser.
Therefore my question, can i somehow tweak my OpenAPI YAML so the value is displayed and can then be copy/pasted to the authorization for the rest of the API. I am using the latest swagger editor (https://editor.swagger.io/)
This is what i have specified
paths:
/Login:
post:
servers:
- url: ....
description: Local test server
tags:
- Login
summary: Logs into the API and returns the authorization.
description: Username and Password are passed in header
security: []
#- bearerAuth: []
parameters:
- $ref: '#/components/parameters/Username'
- $ref: '#/components/parameters/Password'
responses:
200:
description: Successful response
content:
application/json:
schema:
$ref: '#/components/schemas/Login'
headers:
authorization:
$ref: '#/components/headers/authorization'
parameters:
Username:
name: Username
in: header
description: Username to authorize with
schema:
type: string
Password:
name: Password
in: header
description: Password to authorize with
schema:
type: string
headers:
authorization:
schema:
type: string
description: Authorization token.

Assign variable by referencing another

In the following Ansible Playbook, I am trying to create a user's password using predefined variables from defaults/main.yml which in return calls password from vars/passwords.yml. this file will be vaulted later.
vars/passwords
---
passwords:
foobar:
password: pass1234
defaults/main.yml
users:
- username: foobar
group: barfoo
password: "{{passwords.foobar}}"
tasks/main.yml
- include_vars: passwords.yml
- name: Create user
user:
name: "{{item.username}}"
group: "{{item.group}}"
password: "{{item.password | password_hash('sha512') }}"
When I run this playbook, I get the following error:
ERROR:
{
"msg": "[{u'username': u'foobar',
u'group': u'barfoo',
u'password': u'{{passwords.username}}'}]: 'list object' has no attribute 'username'"
}
Any idea how can I achieve assigning a variable by referencing another one.
the first file you provided, has passwords as a list variable, while in your defaults/main.yml file you are expecting a dictionary variable (passwords.foobar).
please change 1st file contents to:
---
passwords:
foobar: pass1234
cant comment about the rest, it looks to me that the tasks/main.yml is missing a line, probably a line including with_items statement. I dont imply its a problem in your code, you just probably didn't paste all your code to this question.
With the current variables files (defaults and vars), the solution for me was to call the password for user bar using the username as a key. I currently have:
- include_vars: passwords.yml
- name: Create user
user:
name: "{{item.username}}"
group: "{{item.group}}"
password: "{{item.password | password_hash('sha512') }}"
the new defaults/main.yml will not have a password key/value:
users:
- username: foobar
group: barfoo
Now with vars/passwords.yml :
---
passwords:
foobar:
password: pass1234
I can edit my change my task to:
- include_vars: passwords.yml
- name: Create user
user:
name: "{{item.username}}"
group: "{{item.group}}"
password: "{{passwords[item.username].password | password_hash('sha512') }}"
This solved my problem, and allows me to vault passwords.yml.
Please let me know if you have any improvements or suggestions.