awx-operator icon indicating copy to clipboard operation
awx-operator copied to clipboard

Add pg password encrypt functionality

Open davidtluong opened this issue 2 years ago • 7 comments

In the following KCS, we allow the passing of encrypted postgres password in credentials.py: https://access.redhat.com/solutions/4309941

In the operator, this is not possible at the moment as any changes to that file is not persistent. I have added a variable to toggle adding the from awx.main.utils import decrypt_value, get_encryption_key in order to enable the functionality. By default, I have disabled it.

davidtluong avatar Jun 27 '22 21:06 davidtluong

I wonder if we should we enable this option or defer to Secret encryption in the underlying k8s cluster to make this happen?

Zokormazo avatar Jun 28 '22 07:06 Zokormazo

Secret encryption would still leave it unencrytped in the credentials.py when mounted wouldn't it?

Also, when using this, I believe it'll only work with external databases, otherwise that decrypt_value function will be inserted into the postgres password for our postgresql deployment

davidtluong avatar Jul 01 '22 14:07 davidtluong

Secret encryption would still leave it unencrytped in the credentials.py when mounted wouldn't it?

Also, when using this, I believe it'll only work with external databases, otherwise that decrypt_value function will be inserted into the postgres password for our postgresql deployment

Oh true, you are right, nvm

Zokormazo avatar Jul 01 '22 14:07 Zokormazo

For your concern here:

We still need to use the decrypt_value and get_encryption_key functions in the credentials.py.j2 template to get the password value.

I figured the customer would've copied the whole value of that into the PG_PASSWORD field, for example, I would imagine their secret looks like this:

apiVersion: v1
data:
...
  password: decrypt_value(get_encryption_key('value'),'$encrypted$AESCBC$Z0FBQUFBQmNONU9BbGQ1VjJyNDJRVTRKaFRIR09Ib2U5TGdaYVRfcXFXRjlmdmpZNjdoZVpEZ21QRWViMmNDOGJaM0dPeHN2b194NUxvQ1M5X3dSc1gxQ29TdDBKRkljWHc9PQ==')

As for the boolean, I can change it no problem, I just thought since no_log was configured the same way that this would carry over as well.

As for the blank line, would that still show up if the condition is false?

davidtluong avatar Jul 08 '22 14:07 davidtluong

I figured the customer would've copied the whole value of that into the PG_PASSWORD field, for example, I would imagine their secret looks like this:

This won't work because the awx_postgres_pass variable is quoted in the credentials.py.j2 [1] so the decrypt_value and get_encryption_key functions won't be executed.

As for the blank line, would that still show up if the condition is false?

it won't show up, I don't remember why I put that comment, so you can dismiss it

[1] https://github.com/ansible/awx-operator/blob/devel/roles/installer/templates/credentials.py.j2#L7

dsavineau avatar Jul 12 '22 20:07 dsavineau

So let me see if I have this right:

  • The goal here is for the user to have a deployment that does not have the PG Password exposed in plaintext in the credentials.py file inside the task and web containers. For VM installs, we have the KCS article, but that won't work in openshift.

For operator deployments, to have this value encrypted in the resulting credentials.py file with this PR, the user needs to:

  1. Create an AutomationController CR with a pg config secret using the unencrypted password
  2. Exec into the container and use the symmetric key (SECRET_KEY) unique to that deployment to encrypt the PG password using awx-manage and the steps in the KCS article.
  3. Modify the pg secret to include the encrypted value
  4. Restart the deployment: oc rollout deployment
  5. It will re-create the credentials.py file mounted into the container with the encrypted PG password.

There are currently 3 other places where the awx_postgres_pass is used, the backup and restore roles, and the migrate_data.yml logic.

Once the pg 12 --> 13 upgrade logic lands that will be another. The backup and restore ones are not a problem, but the upgrade and migrate data ones are because the PG_PASSWORD value must be decrypted by the operator before being used.

So, while the awx-task container is still up, we will need to have a k8s_exec task that will decrypt it and register the output as a variable. @rh-dluong

rooftopcellist avatar Jul 18 '22 21:07 rooftopcellist

So, I'm wracking my brain and it seems there's a bit of a hitch with this. I'm assume the way the decrypt works is that we use the SECRET_KEY to decrypt, however, due to these migrate_data task(s), this kinda shoots down this whole thing.

I've tested this, and it does decrypt the var successfully as a mockup:

- name: Set old postgres password if encrypted
  kubernetes.core.k8s_exec:
    namespace: dluong-aap
    pod: dluong-controller-5c5b7477c5-hq27x
    container:  dluong-controller-task
    command: |
      awx-manage shell_plus -c """
      from awx.main.utils import decrypt_value, get_encryption_key
      decrypted_value = decrypt_value(get_encryption_key('value'),'{{postgresql_password}}')
      print(decrypted_value)
      """
  register: decrypt_old_postgres
  when: "'encrypted' in postgresql_password"

- name: debug
  debug:
    var: decrypt_old_postgres.stdout_lines[-1]

However, in order for this to work with migrating data, we would need to probably, when backing up, unencrypt the password if it is encrypted, and store it in the config file since there is no reference to the old tower/controller app environment in the postgresql secret. Though, that kinda seems against the spirit of having this in the first place. And then when restoring or migrating, they will need to manually encrypt it again. This seems like the only feasible way this PR can move on functionally.

Also, you can ignore this next part, this is just for me to track where we're setting some things later depending on how we want to go about this:

tasks/creation.yml:    - include_tasks: postgres.yml
tasks/creation.yml:    - include_tasks: secrets.yml
tasks/secrets.yml:  include_tasks: dump_generated_secret.yml
tasks/secrets.yml:  include_tasks: dump_secret.yml
tasks/secrets.yml:  include_tasks: dump_secret.yml
tasks/secrets.yml:  include_tasks: dump_secret.yml
tasks/secrets.yml:      bash -c "echo '{{ secrets | to_yaml }}' > {{ backup_dir }}/secrets.yml"
tasks/main.yml:  include_tasks: creation.yml

davidtluong avatar Jul 20 '22 20:07 davidtluong