33-clair-deploy.yml 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. ---
  2. # Tasks required by 15-clair-deploy.adoc.
  3. - name: Ensure backup file is readable by postgresql containers.
  4. hosts: registry.ocp4.example.com
  5. gather_subset: min
  6. become: yes
  7. tasks:
  8. - name: Ensure backup file is owned by quay user.
  9. ansible.builtin.file:
  10. path: /local/backups/clair.backup
  11. owner: quay
  12. group: quay
  13. mode: 0644
  14. - name: Prepare registry VM to run Clair services.
  15. hosts: registry.ocp4.example.com
  16. gather_subset: min
  17. remote_user: quay
  18. tasks:
  19. - name: Ensure the podman network is there.
  20. containers.podman.podman_network_info:
  21. name: quay
  22. register: quay_net
  23. ignore_errors: yes
  24. - ansible.builtin.assert:
  25. that:
  26. - not quay_net.failed
  27. - quay_net.networks is defined
  28. - quay_net.networks is iterable
  29. - quay_net.networks | length == 1
  30. fail_msg: "FATAL: Podman network 'quay' does not exist for 'quay' user. Ensure you deployed Quay before running this playbook."
  31. success_msg: "OK, network 'quay' found."
  32. - name: Ensure the quay service is defined.
  33. ansible.builtin.stat:
  34. path: "{{ ansible_facts['user_dir'] }}/.config/systemd/user/quay.service"
  35. get_attributes: no
  36. get_checksum: no
  37. get_mime: no
  38. register: quay_svc_unit
  39. - ansible.builtin.assert:
  40. that:
  41. - not quay_svc_unit.failed
  42. - quay_svc_unit.stat.exists
  43. fail_msg: "FATAL: User service 'quay.service' not found for 'quay' user. Ensure you deployed Quay before running this playbook."
  44. success_msg: "OK, service 'quay.service' found."
  45. - name: Ensure the quay-pg service is defined.
  46. ansible.builtin.stat:
  47. path: "{{ ansible_facts['user_dir'] }}/.config/systemd/user/quay-pg.service"
  48. get_attributes: no
  49. get_checksum: no
  50. get_mime: no
  51. register: quay_pg_svc_unit
  52. - ansible.builtin.assert:
  53. that:
  54. - not quay_pg_svc_unit.failed
  55. - quay_pg_svc_unit.stat.exists
  56. fail_msg: "FATAL: User service 'quay-pg.service' not found for 'quay' user. Ensure you deployed Quay before running this playbook."
  57. success_msg: "OK, service 'quay-pg.service' found."
  58. - name: Ensure Quay PostgreSQL is running.
  59. ansible.builtin.systemd_service:
  60. name: quay-pg
  61. scope: user
  62. state: started
  63. - name: Check whether the clair database exists.
  64. containers.podman.podman_container_exec:
  65. name: postgresql
  66. command: psql -d postgres -U postgres -t -A -c "SELECT datname FROM pg_database WHERE datname = 'clair'"
  67. register: pg_clair
  68. changed_when: no
  69. - name: Create the clair database if necessary.
  70. containers.podman.podman_container_exec:
  71. name: postgresql
  72. command: 'psql -d postgres -U postgres -c "CREATE DATABASE clair OWNER quay"'
  73. when:
  74. - pg_clair is defined
  75. - pg_clair.stdout_lines | length == 0
  76. - name: Create the uuid-ossp extension if necessary.
  77. containers.podman.podman_container_exec:
  78. name: postgresql
  79. command: psql -d clair -U postgres -c 'CREATE EXTENSION IF NOT EXISTS "uuid-ossp"'
  80. register: pg_ext
  81. changed_when:
  82. - not "already exists" in pg_ext.stderr
  83. - name: Pull all the images if necessary.
  84. containers.podman.podman_image:
  85. name: "{{ registry_host }}/quay/clair-rhel8:v{{ quay_version }}"
  86. pull: yes
  87. state: present
  88. # TODO: Make loop labels nicer.
  89. - name: Patch Quay config if necessary.
  90. ansible.builtin.lineinfile:
  91. path: "{{ ansible_facts['user_dir'] }}/config/config.yaml"
  92. insertafter: "{{ item.after }}"
  93. regexp: "{{ item.fixre }}"
  94. line: "{{ item.value }}"
  95. loop:
  96. - after: "^FEATURE_MAILING: false$"
  97. fixre: "^FEATURE_SECURITY_SCANNER: .*$"
  98. value: "FEATURE_SECURITY_SCANNER: true"
  99. - after: "^SECRET_KEY: .*$"
  100. fixre: "^SECURITY_SCANNER_INDEXING_INTERVAL: .*$"
  101. value: "SECURITY_SCANNER_INDEXING_INTERVAL: 30"
  102. - after: "^SECURITY_SCANNER_INDEXING_INTERVAL: .*$"
  103. fixre: "^SECURITY_SCANNER_V4_PSK: .*$"
  104. value: "SECURITY_SCANNER_V4_PSK: NjA1aWhnNWk4MWhqNw=="
  105. - after: "^SECURITY_SCANNER_V4_PSK: .*$"
  106. fixre: "^SECURITY_SCANNER_V4_ENDPOINT: .*$"
  107. value: "SECURITY_SCANNER_V4_ENDPOINT: http://clair:8081"
  108. notify:
  109. - restart quay and wait for ready
  110. - name: Create Clair config directory if necessary.
  111. ansible.builtin.file:
  112. path: "{{ ansible_facts['user_dir'] }}/clair"
  113. state: directory
  114. mode: 0775
  115. - name: Publish Clair config if necessary.
  116. ansible.builtin.copy:
  117. dest: "{{ ansible_facts['user_dir'] }}/clair/config.yaml"
  118. content: |
  119. http_listen_addr: :8081
  120. introspection_addr: :8088
  121. #log_level: debug # too noisy
  122. log_level: info
  123. indexer:
  124. connstring: host=postgresql port=5432 dbname=clair user=quay password=secret sslmode=disable
  125. scanlock_retry: 10
  126. layer_scan_concurrency: 5
  127. migrations: true
  128. scanner:
  129. repo:
  130. rhel-repository-scanner:
  131. repo2cpe_mapping_file: /data/repository-to-cpe.json
  132. package:
  133. rhel_containerscanner:
  134. name2repos_mapping_file: /data/container-name-repos-map.json
  135. airgap: true
  136. matcher:
  137. connstring: host=postgresql port=5432 dbname=clair user=quay password=secret sslmode=disable
  138. max_conn_pool: 100
  139. migrations: true
  140. indexer_addr: clair-indexer
  141. disable_updaters: true
  142. notifier:
  143. connstring: host=postgresql port=5432 dbname=clair user=quay password=secret sslmode=disable
  144. delivery_interval: 1m
  145. poll_interval: 5m
  146. migrations: true
  147. updaters:
  148. config:
  149. rhel:
  150. ignore_unpatched: false
  151. auth:
  152. psk:
  153. key: "NjA1aWhnNWk4MWhqNw=="
  154. iss: ["quay"]
  155. metrics:
  156. name: "prometheus"
  157. mode: 0664
  158. notify:
  159. - restart quay and wait for ready
  160. - restart clair
  161. - name: Ensure same TLS trust will be used for Clair as for workstation.
  162. ansible.builtin.copy:
  163. src: /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
  164. dest: "{{ ansible_facts['user_dir'] }}/tls-ca-bundle.pem"
  165. mode: 0664
  166. notify:
  167. - restart clair
  168. - name: Ensure Clair service unit is there.
  169. ansible.builtin.template:
  170. dest: "{{ ansible_facts['user_dir'] }}/.config/systemd/user/clair.service"
  171. src: "templates/clair.service.j2"
  172. - name: Reload systemd.
  173. ansible.builtin.systemd_service:
  174. daemon_reload: yes
  175. scope: user
  176. - name: Verify that the vulnerability definitions exist.
  177. containers.podman.podman_container_exec:
  178. name: postgresql
  179. command: psql -d clair -U postgres -t -c 'SELECT COUNT(id) FROM vuln'
  180. ignore_errors: yes
  181. register: clair_vuln
  182. - name: Remember the number of vulnerability definitions in clair.
  183. ansible.builtin.set_fact:
  184. clair_nvuln: "{{ clair_vuln.stdout_lines[0] | default(0) | trim | int }}"
  185. - name: Import clair backup if vulnerabilities seem to be missing.
  186. block:
  187. - name: Ensure clair service is stopped.
  188. ansible.builtin.systemd_service:
  189. name: clair
  190. scope: user
  191. state: stopped
  192. - name: Ensure clair container is stopped.
  193. containers.podman.podman_container:
  194. name: clair
  195. state: stopped
  196. - name: Create a temporary pgpass file
  197. ansible.builtin.copy:
  198. dest: /tmp/pgpass
  199. owner: quay
  200. group: quay
  201. mode: 0600
  202. content: |
  203. postgresql:5432:clair:postgres:verysecret
  204. - name: Ensure the pgpass file is owned by postgres user of the container.
  205. become_method: containers.podman.podman_unshare
  206. become: yes
  207. ansible.builtin.file:
  208. path: /tmp/pgpass
  209. state: file
  210. owner: 26
  211. - name: Run pg_restore in a clair_import container.
  212. containers.podman.podman_container:
  213. name: clair_import
  214. image: "{{ registry_host }}/rhel9/postgresql-15:latest"
  215. rm: yes
  216. detach: no
  217. network:
  218. - quay
  219. volumes:
  220. - /local/backups/clair.backup:/clair.backup:Z
  221. - /tmp/pgpass:/var/lib/pgsql/.pgpass:Z
  222. command:
  223. - pg_restore
  224. - -dclair
  225. - -Upostgres
  226. - -hpostgresql
  227. - -c
  228. - /clair.backup
  229. state: started
  230. register: clair_import
  231. ignore_errors: yes
  232. failed_when: "FATAL" in clair_import.stderr_lines
  233. # TODO: probably use a regex here
  234. - debug: var=clair_import
  235. - name: Restore the ownership of the file.
  236. become_method: containers.podman.podman_unshare
  237. become: yes
  238. ansible.builtin.file:
  239. path: /tmp/pgpass
  240. state: file
  241. owner: 0
  242. - name: Remove the pgpass file
  243. ansible.builtin.file:
  244. path: /tmp/pgpass
  245. state: absent
  246. when:
  247. - clair_nvuln < 5000000
  248. - name: Enable services and start them.
  249. ansible.builtin.systemd_service:
  250. name: clair
  251. scope: user
  252. state: started
  253. enabled: yes
  254. handlers:
  255. - name: restart quay
  256. listen: restart quay and wait for ready
  257. ansible.builtin.systemd_service:
  258. name: quay
  259. scope: user
  260. state: restarted
  261. - name: wait for quay to become ready again
  262. listen: restart quay and wait for ready
  263. ansible.builtin.uri:
  264. method: GET
  265. url: https://registry.ocp4.example.com/
  266. headers:
  267. Accept: application/json
  268. Content-Type: application/json
  269. validate_certs: no
  270. status_code:
  271. - 200
  272. - 404
  273. - 502
  274. register: startup_wait
  275. until: startup_wait.status == 200
  276. retries: 30
  277. delay: 5
  278. - name: restart clair
  279. ansible.builtin.systemd_service:
  280. name: clair
  281. scope: user
  282. state: restarted
  283. ...