From 292ca2ef6381e99012c383e13bcf58d2c55e25c2 Mon Sep 17 00:00:00 2001 From: Elia El Lazkani Date: Tue, 8 Oct 2019 00:09:33 +0200 Subject: [PATCH] Second commit --- .gitignore | 2 + .yamllint | 33 +++++++++++++ README.md | 46 ++++++++++++++++++ .../openpolicyagent_config_generate.py | 19 ++++++++ action_plugins/openpolicyagent_merge.py | 20 ++++++++ defaults/main.yml | 33 +++++++++++++ handlers/main.yml | 9 ++++ library/openpolicyagent_config_generate | 0 library/openpolicyagent_merge | 0 meta/main.yml | 23 +++++++++ molecule/default/Dockerfile.j2 | 22 +++++++++ molecule/default/INSTALL.rst | 22 +++++++++ molecule/default/molecule.yml | 28 +++++++++++ molecule/default/playbook.yml | 33 +++++++++++++ molecule/default/tests/test_default.py | 15 ++++++ molecule/requirements.txt | 3 ++ tasks/configuration.yml | 48 +++++++++++++++++++ tasks/install.yml | 26 ++++++++++ tasks/main.yml | 17 +++++++ tasks/prepare.yml | 23 +++++++++ templates/opa.env.j2 | 0 templates/opa.service.j2 | 11 +++++ vars/debian.yml | 3 ++ vars/main.yml | 3 ++ vars/redhat.yml | 3 ++ 25 files changed, 442 insertions(+) create mode 100644 .gitignore create mode 100644 .yamllint create mode 100644 README.md create mode 100644 action_plugins/openpolicyagent_config_generate.py create mode 100644 action_plugins/openpolicyagent_merge.py create mode 100644 defaults/main.yml create mode 100644 handlers/main.yml create mode 100644 library/openpolicyagent_config_generate create mode 100644 library/openpolicyagent_merge create mode 100644 meta/main.yml create mode 100644 molecule/default/Dockerfile.j2 create mode 100644 molecule/default/INSTALL.rst create mode 100644 molecule/default/molecule.yml create mode 100644 molecule/default/playbook.yml create mode 100644 molecule/default/tests/test_default.py create mode 100644 molecule/requirements.txt create mode 100644 tasks/configuration.yml create mode 100644 tasks/install.yml create mode 100644 tasks/main.yml create mode 100644 tasks/prepare.yml create mode 100644 templates/opa.env.j2 create mode 100644 templates/opa.service.j2 create mode 100644 vars/debian.yml create mode 100644 vars/main.yml create mode 100644 vars/redhat.yml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ecc4de4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.pyc +*__pycache__/ diff --git a/.yamllint b/.yamllint new file mode 100644 index 0000000..8827676 --- /dev/null +++ b/.yamllint @@ -0,0 +1,33 @@ +--- +# Based on ansible-lint config +extends: default + +rules: + braces: + max-spaces-inside: 1 + level: error + brackets: + max-spaces-inside: 1 + level: error + colons: + max-spaces-after: -1 + level: error + commas: + max-spaces-after: -1 + level: error + comments: disable + comments-indentation: disable + document-start: disable + empty-lines: + max: 3 + level: error + hyphens: + level: error + indentation: disable + key-duplicates: enable + line-length: disable + new-line-at-end-of-file: disable + new-lines: + type: unix + trailing-spaces: disable + truthy: disable diff --git a/README.md b/README.md new file mode 100644 index 0000000..17759cf --- /dev/null +++ b/README.md @@ -0,0 +1,46 @@ +ansible-role-openpolicyagent +============================ + +The `ansible-role-openpolicyagent` ansible role installs and configures `Open Policy Agent `_. + +Requirements +------------ + +The role doesn't require any extra python requiremnets to use. + +To run `molecule` on the other hand, you might need to do the following. + +``` +$ pip install -r molecule/requirements.txt +``` + +Role Variables +-------------- + +| Variable | Default | Description | +|:--------------------------------------------------------|:--------------------------------------------------------------------------------------------:|:-------------------------------------------------| +| `openpolicyagent_version` | `v0.14.2` | OPA version | +| `openpolicyagent_home` | `/opt/opa` | OPA home directory | +| `openpolicyagent_bin` | `/opt/opa/bin` | OPA binary path | +| `openpolicyagent_name` | `opa` | OPA name of file to download | +| `openpolicyagent_user` | `opa` | OPA user to create and use | +| `openpolicyagent_group` | `opa` | OPA group to create and use | +| `openpolicyagent_base_url` | `Link `_ | OPA download base URL | +| `openpolicyagent_url` | `Link `_ | OPA download URL | +| `openpolicyagent_config_path` | `/etc/opa` | OPA configuration base path | +| `openpolicyagent_config_d_path` | `/etc/opa/opa.d/` | OPA config.d path | +| `openpolicyagent_config_file` | `/etc/opa/config.yml` | OPA configuration file path | +| `*_openpolicyagent_services` | `[]` | OPA Services | +| `*_openpolicyagent_labels` | `{}` | OPA Labels | +| `*_openpolicyagent_bundles` | `[]` | OPA Bundles | +| `*_openpolicyagent_plugins` | `{}` | OPA Plugins | +| `openpolicyagent_config_default_decision` | `/system/main` | OPA Default Decision configuration | +| `openpolicyagent_config_default_authorization_decision` | `/system/authz/allow` | OPA Default Authorization Decision configuration | +| `openpolicyagent_config_decision_logs` | `{}` | OPA Decision Logs configuration | +| `openpolicyagent_config_status` | `{}` | OPA Status configuration | +| `openpolicyagent_config_discovery` | `{}` | OPA Discovery configuration | + +License +------- + +BSD 2 Clause diff --git a/action_plugins/openpolicyagent_config_generate.py b/action_plugins/openpolicyagent_config_generate.py new file mode 100644 index 0000000..454d86d --- /dev/null +++ b/action_plugins/openpolicyagent_config_generate.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python + +from ansible.plugins.action import ActionBase + + +class ActionModule(ActionBase): + def run(self, tmp=None, task_vars=None): + config = self._task.args.get('config') + varname = self._task.args.get('var_name') + _config = {} + + for config_item in config: + if config[config_item]: + _config[config_item] = self._templar.template(config[config_item]) + + return dict( + ansible_facts={varname: self._templar.template(_config)}, + changed=False + ) diff --git a/action_plugins/openpolicyagent_merge.py b/action_plugins/openpolicyagent_merge.py new file mode 100644 index 0000000..59a8b9d --- /dev/null +++ b/action_plugins/openpolicyagent_merge.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python + +from ansible.plugins.action import ActionBase + + +class ActionModule(ActionBase): + def run(self, tmp=None, task_vars=None): + suffix = self._task.args.get('suffix') + varname = self._task.args.get('var_name') + mergetype = self._task.args.get('type', 'dict') + + merged = {} if mergetype == 'dict' else [] + mergefunc = merged.update if mergetype == 'dict' else merged.extend + for var in (v for v in sorted(task_vars.keys()) if v.endswith(suffix)): + mergefunc(self._templar.template(task_vars[var])) + + return dict( + ansible_facts={varname: self._templar.template(merged)}, + changed=False + ) diff --git a/defaults/main.yml b/defaults/main.yml new file mode 100644 index 0000000..a9ae8a0 --- /dev/null +++ b/defaults/main.yml @@ -0,0 +1,33 @@ +--- +openpolicyagent_version: v0.14.2 + +openpolicyagent_home: /opt/opa +openpolicyagent_bin: "{{ openpolicyagent_home }}/bin" +openpolicyagent_name: opa + +openpolicyagent_user: opa +openpolicyagent_group: opa + +openpolicyagent_base_url: https://github.com/open-policy-agent/opa/releases/download +openpolicyagent_url: "{{ openpolicyagent_base_url }}/{{ openpolicyagent_version }}/opa_linux_amd64" + +openpolicyagent_config_path: /etc/opa +openpolicyagent_config_d_path: /etc/opa/opa.d/ +openpolicyagent_config_file: "{{ openpolicyagent_config_path }}/config.yml" + +openpolicyagent_config: + services: "{{ openpolicyagent_services_merged }}" + labels: "{{ openpolicyagent_labels_merged }}" + bundles: "{{ openpolicyagent_bundles_merged }}" + status: "{{ openpolicyagent_config_status }}" + decision_logs: "{{ openpolicyagent_config_decision_logs }}" + discovery: "{{ openpolicyagent_config_discovery }}" + default_decision: "{{ openpolicyagent_config_default_decision }}" + default_authorization_decision: "{{ openpolicyagent_config_default_authorization_decision }}" + plugins: "{{ openpolicyagent_plugins_merged }}" + +openpolicyagent_config_default_decision: /system/main +openpolicyagent_config_default_authorization_decision: /system/authz/allow +openpolicyagent_config_decision_logs: {} +openpolicyagent_config_status: {} +openpolicyagent_config_discovery: {} diff --git a/handlers/main.yml b/handlers/main.yml new file mode 100644 index 0000000..7218cad --- /dev/null +++ b/handlers/main.yml @@ -0,0 +1,9 @@ +--- +- name: Reload systemctl + command: systemctl daemon-reload + +- name: Restart OPA + systemd: + name: opa + state: restarted + diff --git a/library/openpolicyagent_config_generate b/library/openpolicyagent_config_generate new file mode 100644 index 0000000..e69de29 diff --git a/library/openpolicyagent_merge b/library/openpolicyagent_merge new file mode 100644 index 0000000..e69de29 diff --git a/meta/main.yml b/meta/main.yml new file mode 100644 index 0000000..c526831 --- /dev/null +++ b/meta/main.yml @@ -0,0 +1,23 @@ +--- +galaxy_info: + author: Elia El Lazkani + description: Ansible role to deploy and configure Open Policy Agent + license: BSD 2-Clause + min_ansible_version: 2.7 + platforms: + - name: CentOS + version: + - 7 + - 8 + - name: Ubuntu + version: + - 16.04 + - 16.10 + - 17.04 + - 17.10 + - 18.04 + - 18.10 + - 19.04 + + galaxy_tags: [] +dependencies: [] diff --git a/molecule/default/Dockerfile.j2 b/molecule/default/Dockerfile.j2 new file mode 100644 index 0000000..0de39e6 --- /dev/null +++ b/molecule/default/Dockerfile.j2 @@ -0,0 +1,22 @@ +# Molecule managed + +{% if item.registry is defined %} +FROM {{ item.registry.url }}/{{ item.image }} +{% else %} +FROM {{ item.image }} +{% endif %} + +{% if item.env is defined %} +{% for var, value in item.env.items() %} +{% if value %} +ENV {{ var }} {{ value }} +{% endif %} +{% endfor %} +{% endif %} + +RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates iproute2 && apt-get clean; \ + elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash iproute && dnf clean all; \ + elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl bash iproute && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \ + elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml iproute2 && zypper clean -a; \ + elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates; \ + elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates iproute2 && xbps-remove -O; fi diff --git a/molecule/default/INSTALL.rst b/molecule/default/INSTALL.rst new file mode 100644 index 0000000..6a44bde --- /dev/null +++ b/molecule/default/INSTALL.rst @@ -0,0 +1,22 @@ +******* +Docker driver installation guide +******* + +Requirements +============ + +* Docker Engine + +Install +======= + +Please refer to the `Virtual environment`_ documentation for installation best +practices. If not using a virtual environment, please consider passing the +widely recommended `'--user' flag`_ when invoking ``pip``. + +.. _Virtual environment: https://virtualenv.pypa.io/en/latest/ +.. _'--user' flag: https://packaging.python.org/tutorials/installing-packages/#installing-to-the-user-site + +.. code-block:: bash + + $ pip install 'molecule[docker]' diff --git a/molecule/default/molecule.yml b/molecule/default/molecule.yml new file mode 100644 index 0000000..caf6807 --- /dev/null +++ b/molecule/default/molecule.yml @@ -0,0 +1,28 @@ +--- +dependency: + name: galaxy +driver: + name: docker +lint: + name: yamllint +platforms: + - name: opa-xenial + image: geerlingguy/docker-ubuntu1604-ansible + command: /lib/systemd/systemd + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:ro + privileged: yes + - name: opa-centos7 + image: geerlingguy/docker-centos7-ansible + command: /usr/lib/systemd/systemd + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:ro + privileged: yes +provisioner: + name: ansible + lint: + name: ansible-lint +verifier: + name: testinfra + lint: + name: flake8 diff --git a/molecule/default/playbook.yml b/molecule/default/playbook.yml new file mode 100644 index 0000000..5d33aab --- /dev/null +++ b/molecule/default/playbook.yml @@ -0,0 +1,33 @@ +--- +- name: Converge + hosts: all + post_tasks: + - name: Copy example_conf + copy: + src: example_conf/ + dest: "{{ openpolicyagent_config_d_path }}" + owner: "{{ openpolicyagent_user }}" + group: "{{ openpolicyagent_group }}" + + roles: + - role: ansible-role-openpolicyagent + vars: + local__openpolicyagent_services: + - name: "{{ ansible_hostname }}" + url: "http://{{ ansible_fqdn }}" + - name: "OPA" + url: "http://{{ ansible_fqdn }}/OPA" + + local__openpolicyagent_labels: + host: "{{ ansible_fqdn }}" + service: "OPA" + app: "OPA" + environment: "molecule" + + openpolicyagent_config_decision_logs: + service: "OPA" + reporting: + min_delay_seconds: 300 + max_delay_seconds: 600 + + diff --git a/molecule/default/tests/test_default.py b/molecule/default/tests/test_default.py new file mode 100644 index 0000000..9e0e189 --- /dev/null +++ b/molecule/default/tests/test_default.py @@ -0,0 +1,15 @@ +import os + +import testinfra.utils.ansible_runner + +testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( + os.environ['MOLECULE_INVENTORY_FILE'] +).get_hosts('all') + + +def test_hosts_file(host): + f = host.file('/etc/hosts') + + assert f.exists + assert f.user == 'root' + assert f.group == 'root' diff --git a/molecule/requirements.txt b/molecule/requirements.txt new file mode 100644 index 0000000..ee4a119 --- /dev/null +++ b/molecule/requirements.txt @@ -0,0 +1,3 @@ +molecule==2.22 +ansible==2.8.5 +docker==4.1.0 diff --git a/tasks/configuration.yml b/tasks/configuration.yml new file mode 100644 index 0000000..74bbd0b --- /dev/null +++ b/tasks/configuration.yml @@ -0,0 +1,48 @@ +--- +- name: Create OPA configuration directory + file: + path: "{{ openpolicyagent_config_path }}" + state: directory + owner: "{{ openpolicyagent_user }}" + group: "{{ openpolicyagent_group }}" + mode: "0755" + +- name: Create OPA config.d directory + file: + path: "{{ openpolicyagent_config_d_path }}" + state: directory + owner: "{{ openpolicyagent_user }}" + group: "{{ openpolicyagent_group }}" + mode: "0755" + +- name: Merge OPA dictionary configuration + openpolicyagent_merge: + suffix: _openpolicyagent_{{ item }} + var_name: openpolicyagent_{{ item }}_merged + type: list + loop: + - services + - bundles + +- name: Merge OPA dictionary configuration + openpolicyagent_merge: + suffix: _openpolicyagent_{{ item }} + var_name: openpolicyagent_{{ item }}_merged + loop: + - labels + - plugins + +- name: Generate final configuration + openpolicyagent_config_generate: + config: "{{ openpolicyagent_config }}" + var_name: openpolicyagent_config + +- name: Deploy generated configuration + copy: + content: "{{ openpolicyagent_config | to_nice_yaml(indent=2) }}" + dest: "{{ openpolicyagent_config_file }}" + owner: "{{ openpolicyagent_user }}" + group: "{{ openpolicyagent_group }}" + mode: "0600" + validate: "{{ openpolicyagent_bin }}/opa run --addr :8182 --shutdown-grace-period 1 --config-file '%s'" + notify: Restart OPA diff --git a/tasks/install.yml b/tasks/install.yml new file mode 100644 index 0000000..5ea913c --- /dev/null +++ b/tasks/install.yml @@ -0,0 +1,26 @@ +--- +- name: Download OPA binaries + get_url: + url: "{{ openpolicyagent_url }}" + dest: "{{ openpolicyagent_bin }}/{{ openpolicyagent_name }}_{{ openpolicyagent_version }}" + mode: '0755' + owner: "{{ openpolicyagent_user }}" + group: "{{ openpolicyagent_group }}" + checksum: "{{ vars_openpolicyagent_checksum[openpolicyagent_version] }}" + +- name: Symlink the binary to the current version + file: + src: "{{ openpolicyagent_bin}}/{{ openpolicyagent_name}}_{{ openpolicyagent_version }}" + dest: "{{ openpolicyagent_bin }}/{{ openpolicyagent_name }}" + state: link + +- name: Deploy systemctl environment configuration + template: + src: templates/opa.env.j2 + dest: "{{ vars_openpolicyagent_env_var_path }}/opa" + +- name: Deploy systemctl service configuration + template: + src: templates/opa.service.j2 + dest: "{{ vars_openpolicyagent_service_path }}/opa.service" + notify: Reload systemctl diff --git a/tasks/main.yml b/tasks/main.yml new file mode 100644 index 0000000..58d8a53 --- /dev/null +++ b/tasks/main.yml @@ -0,0 +1,17 @@ +--- +- name: Gather variables for each operating system + include_vars: "{{ item }}" + tags: + - configuration + with_first_found: + - "{{ ansible_distribution | lower }}-{{ ansible_distribution_version | lower }}.yml" + - "{{ ansible_distribution | lower }}-{{ ansible_distribution_major_version | lower }}.yml" + - "{{ ansible_os_family | lower }}-{{ ansible_distribution_major_version | lower }}.yml" + - "{{ ansible_distribution | lower }}.yml" + - "{{ ansible_os_family | lower }}.yml" + +- include_tasks: prepare.yml +- include_tasks: install.yml +- include_tasks: configuration.yml + tags: + - configuration diff --git a/tasks/prepare.yml b/tasks/prepare.yml new file mode 100644 index 0000000..e10fec6 --- /dev/null +++ b/tasks/prepare.yml @@ -0,0 +1,23 @@ +--- +- name: Create OPA group + group: + name: "{{ openpolicyagent_group }}" + state: present + +- name: Create OPA user + user: + name: "{{ openpolicyagent_user }}" + comment: "Open Policy Agent user" + group: "{{ openpolicyagent_group }}" + shell: /bin/nologin + create_home: yes + home: "{{ openpolicyagent_home }}" + state: present + +- name: Create OPA bin directory + file: + path: "{{ openpolicyagent_bin }}" + owner: "{{ openpolicyagent_user }}" + group: "{{ openpolicyagent_group }}" + mode: "0755" + state: directory diff --git a/templates/opa.env.j2 b/templates/opa.env.j2 new file mode 100644 index 0000000..e69de29 diff --git a/templates/opa.service.j2 b/templates/opa.service.j2 new file mode 100644 index 0000000..0f37ffe --- /dev/null +++ b/templates/opa.service.j2 @@ -0,0 +1,11 @@ +[Unit] +Description=Open Policy Agent Daemon +Requires=network.target +After=syslog.target network.target +[Service] +Type=simple +User={{ openpolicyagent_user }} +EnvironmentFile={{ vars_openpolicyagent_env_var_path }}/opa +ExecStart={{ openpolicyagent_bin }}/opa run --server --config-file {{ openpolicyagent_config_file }} --watch {{ openpolicyagent_config_d_path }} +[Install] +WantedBy=multi-user.target diff --git a/vars/debian.yml b/vars/debian.yml new file mode 100644 index 0000000..a5c953c --- /dev/null +++ b/vars/debian.yml @@ -0,0 +1,3 @@ +--- +vars_openpolicyagent_env_var_path: /etc/default +vars_openpolicyagent_service_path: /etc/systemd/system diff --git a/vars/main.yml b/vars/main.yml new file mode 100644 index 0000000..2eae11e --- /dev/null +++ b/vars/main.yml @@ -0,0 +1,3 @@ +--- +vars_openpolicyagent_checksum: + v0.14.2: sha512:6d07aa4cfdf7c40d894bb99811db117f5e9ccc891711859210864f319f1c01aca59c9c0943db914b7637e8b70e64b0581523e5c03da58603caaa245b486d4fe3 diff --git a/vars/redhat.yml b/vars/redhat.yml new file mode 100644 index 0000000..f59c544 --- /dev/null +++ b/vars/redhat.yml @@ -0,0 +1,3 @@ +--- +vars_openpolicyagent_env_var_path: /etc/sysconfig +vars_openpolicyagent_service_path: /etc/systemd/system