Matrix Docker Ansible eploy
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

539 lines
30 KiB

  1. # SPDX-FileCopyrightText: 2026 Slavi Pantaleev
  2. #
  3. # SPDX-License-Identifier: AGPL-3.0-or-later
  4. ---
  5. # MAS exits on a bad startup configuration rather than retrying in-process. The restart
  6. # counter guards that failure mode; live health, OIDC, JWKS, and schema checks carry the
  7. # stronger proof that the stable process is actually using what this role rendered.
  8. - name: Verify Matrix Authentication Service
  9. hosts: all
  10. become: true
  11. vars_files:
  12. - "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/../../../molecule-shared/vars.yml"
  13. - "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/../../../molecule-shared/playbook-context.yml"
  14. gather_facts: false
  15. vars:
  16. matrix_authentication_service_config: >-
  17. {{ matrix_authentication_service_config_file.content | b64decode | from_yaml }}
  18. matrix_authentication_service_web_listener: >-
  19. {{ matrix_authentication_service_config.http.listeners
  20. | selectattr('name', 'equalto', 'web') | first | default({}) }}
  21. matrix_authentication_service_internal_listener: >-
  22. {{ matrix_authentication_service_config.http.listeners
  23. | selectattr('name', 'equalto', 'internal') | first | default({}) }}
  24. matrix_authentication_service_discovery_body: >-
  25. {{ matrix_authentication_service_discovery_http.stdout_lines[:-1] | join('\n') }}
  26. matrix_authentication_service_discovery: >-
  27. {{ matrix_authentication_service_discovery_body | from_json }}
  28. matrix_authentication_service_jwks_body: >-
  29. {{ matrix_authentication_service_jwks_http.stdout_lines[:-1] | join('\n') }}
  30. matrix_authentication_service_jwks: >-
  31. {{ matrix_authentication_service_jwks_body | from_json }}
  32. matrix_authentication_service_jwks_kids: >-
  33. {{ matrix_authentication_service_jwks['keys'] | map(attribute='kid') | list }}
  34. matrix_authentication_service_labels_lines: >-
  35. {{ (matrix_authentication_service_labels_file.content | b64decode).splitlines() }}
  36. matrix_authentication_service_runtime: >-
  37. {{ (matrix_authentication_service_container_inspect.stdout | from_json) | first }}
  38. matrix_authentication_service_config_mounts: >-
  39. {{ matrix_authentication_service_runtime.Mounts
  40. | selectattr('Destination', 'equalto', '/config.yaml') | list }}
  41. matrix_authentication_service_keys_mounts: >-
  42. {{ matrix_authentication_service_runtime.Mounts
  43. | selectattr('Destination', 'equalto', '/keys') | list }}
  44. tasks:
  45. # Read the pin from the role itself so Renovate changes what this assertion expects.
  46. - name: Load the role's defaults under a separate name
  47. ansible.builtin.include_vars:
  48. file: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/main.yml"
  49. name: matrix_authentication_service_role_defaults
  50. - name: Wait for the Matrix Authentication Service unit to become active
  51. ansible.builtin.systemd_service:
  52. name: matrix-authentication-service.service
  53. register: matrix_authentication_service_service
  54. until: matrix_authentication_service_service.status.ActiveState == 'active'
  55. retries: 30
  56. delay: 5
  57. failed_when: false
  58. - name: Assert the Matrix Authentication Service process is stable
  59. ansible.builtin.assert:
  60. that:
  61. - matrix_authentication_service_service.status.ActiveState == 'active'
  62. - matrix_authentication_service_service.status.NRestarts is defined
  63. - matrix_authentication_service_service.status.NRestarts | int == 0
  64. fail_msg: >-
  65. matrix-authentication-service.service is
  66. {{ matrix_authentication_service_service.status.ActiveState | default('unknown') }}
  67. after {{ matrix_authentication_service_service.status.NRestarts | default('?') }} restart(s)
  68. success_msg: "Matrix Authentication Service is active and has not restarted"
  69. # The internal listener binds loopback inside the MAS container. Sharing its network
  70. # namespace is the only way to probe it without changing the deployment contract.
  71. - name: Request the prefixed internal health endpoint
  72. ansible.builtin.command:
  73. argv:
  74. - docker
  75. - run
  76. - --rm
  77. - --network=container:matrix-authentication-service
  78. - "{{ molecule_shared_image_curl }}"
  79. - --silent
  80. - --show-error
  81. - --write-out
  82. - "\nHTTP_STATUS=%{http_code}"
  83. - http://[::1]:8081/molecule-auth/health
  84. register: matrix_authentication_service_health_http
  85. changed_when: false
  86. until: "'HTTP_STATUS=200' in matrix_authentication_service_health_http.stdout"
  87. retries: 24
  88. delay: 5
  89. failed_when: false
  90. - name: Assert the internal listener reports healthy
  91. ansible.builtin.assert:
  92. that:
  93. - matrix_authentication_service_health_http.rc == 0
  94. - matrix_authentication_service_health_http.stdout_lines[-1] == 'HTTP_STATUS=200'
  95. - matrix_authentication_service_health_http.stdout_lines[0] == 'ok'
  96. fail_msg: >-
  97. MAS did not report healthy on its prefixed, loopback-only internal listener
  98. ({{ matrix_authentication_service_health_http.stdout | default('no output') }})
  99. success_msg: "MAS reports healthy on the prefixed internal listener"
  100. - name: Request the unprefixed internal health path
  101. ansible.builtin.command:
  102. argv:
  103. - docker
  104. - run
  105. - --rm
  106. - --network=container:matrix-authentication-service
  107. - "{{ molecule_shared_image_curl }}"
  108. - --silent
  109. - --output
  110. - /dev/null
  111. - --write-out
  112. - "HTTP_STATUS=%{http_code}"
  113. - http://[::1]:8081/health
  114. register: matrix_authentication_service_unprefixed_health_http
  115. changed_when: false
  116. failed_when: false
  117. - name: Assert the internal health resource exists only below the configured prefix
  118. ansible.builtin.assert:
  119. that:
  120. - matrix_authentication_service_unprefixed_health_http.rc == 0
  121. - matrix_authentication_service_unprefixed_health_http.stdout == 'HTTP_STATUS=404'
  122. fail_msg: "The unprefixed health path answered, so the configured prefix is not proven"
  123. success_msg: "The internal listener rejects the unprefixed health path"
  124. # Probed through the private network because the role deliberately publishes no host port.
  125. - name: Request the prefixed OpenID Provider discovery document
  126. ansible.builtin.command:
  127. argv:
  128. - docker
  129. - run
  130. - --rm
  131. - --network={{ matrix_authentication_service_container_network }}
  132. - "{{ molecule_shared_image_curl }}"
  133. - --silent
  134. - --show-error
  135. - --write-out
  136. - "\nHTTP_STATUS=%{http_code}"
  137. - http://matrix-authentication-service:8080/molecule-auth/.well-known/openid-configuration
  138. register: matrix_authentication_service_discovery_http
  139. changed_when: false
  140. until: "'HTTP_STATUS=200' in matrix_authentication_service_discovery_http.stdout"
  141. retries: 24
  142. delay: 5
  143. failed_when: false
  144. - name: Assert MAS serves a real OpenID Provider discovery document
  145. ansible.builtin.assert:
  146. that:
  147. - matrix_authentication_service_discovery_http.rc == 0
  148. - matrix_authentication_service_discovery_http.stdout_lines[-1] == 'HTTP_STATUS=200'
  149. - matrix_authentication_service_discovery is mapping
  150. - matrix_authentication_service_discovery.issuer == 'https://mas.molecule.local/molecule-auth/'
  151. - matrix_authentication_service_discovery.authorization_endpoint == 'https://mas.molecule.local/molecule-auth/authorize'
  152. - matrix_authentication_service_discovery.token_endpoint == 'https://mas.molecule.local/molecule-auth/oauth2/token'
  153. - matrix_authentication_service_discovery.jwks_uri == 'https://mas.molecule.local/molecule-auth/oauth2/keys.json'
  154. - matrix_authentication_service_discovery.userinfo_endpoint == 'https://mas.molecule.local/molecule-auth/oauth2/userinfo'
  155. - "'openid' in matrix_authentication_service_discovery.scopes_supported"
  156. - "'authorization_code' in matrix_authentication_service_discovery.grant_types_supported"
  157. - "'S256' in matrix_authentication_service_discovery.code_challenge_methods_supported"
  158. fail_msg: >-
  159. MAS did not return the configured, prefixed OIDC discovery contract
  160. ({{ matrix_authentication_service_discovery_http.stdout | default('no output') }})
  161. success_msg: "MAS advertises its configured prefixed OpenID Provider contract"
  162. - name: Request the unprefixed discovery path
  163. ansible.builtin.command:
  164. argv:
  165. - docker
  166. - run
  167. - --rm
  168. - --network={{ matrix_authentication_service_container_network }}
  169. - "{{ molecule_shared_image_curl }}"
  170. - --silent
  171. - --output
  172. - /dev/null
  173. - --write-out
  174. - "HTTP_STATUS=%{http_code}"
  175. - http://matrix-authentication-service:8080/.well-known/openid-configuration
  176. register: matrix_authentication_service_unprefixed_discovery_http
  177. changed_when: false
  178. failed_when: false
  179. - name: Assert discovery exists only below the configured prefix
  180. ansible.builtin.assert:
  181. that:
  182. - matrix_authentication_service_unprefixed_discovery_http.rc == 0
  183. - matrix_authentication_service_unprefixed_discovery_http.stdout == 'HTTP_STATUS=404'
  184. fail_msg: "The unprefixed discovery path answered, so the configured prefix is not proven"
  185. success_msg: "The public listener rejects the unprefixed discovery path"
  186. - name: Request the JWKS advertised by discovery
  187. ansible.builtin.command:
  188. argv:
  189. - docker
  190. - run
  191. - --rm
  192. - --network={{ matrix_authentication_service_container_network }}
  193. - "{{ molecule_shared_image_curl }}"
  194. - --silent
  195. - --show-error
  196. - --write-out
  197. - "\nHTTP_STATUS=%{http_code}"
  198. - http://matrix-authentication-service:8080/molecule-auth/oauth2/keys.json
  199. register: matrix_authentication_service_jwks_http
  200. changed_when: false
  201. failed_when: false
  202. - name: Assert JWKS exposes all role-managed signing keys
  203. ansible.builtin.assert:
  204. that:
  205. - matrix_authentication_service_jwks_http.rc == 0
  206. - matrix_authentication_service_jwks_http.stdout_lines[-1] == 'HTTP_STATUS=200'
  207. - matrix_authentication_service_jwks is mapping
  208. - matrix_authentication_service_jwks['keys'] is sequence
  209. - matrix_authentication_service_jwks['keys'] | length == 4
  210. - "'default-rsa' in matrix_authentication_service_jwks_kids"
  211. - "'default-ecdsa-p256' in matrix_authentication_service_jwks_kids"
  212. - "'default-ecdsa-p384' in matrix_authentication_service_jwks_kids"
  213. - "'default-ecdsa-k256' in matrix_authentication_service_jwks_kids"
  214. - (matrix_authentication_service_jwks['keys'] | selectattr('kid', 'equalto', 'default-rsa') | first).kty == 'RSA'
  215. - (matrix_authentication_service_jwks['keys'] | selectattr('kid', 'equalto', 'default-rsa') | first).n | length > 100
  216. - (matrix_authentication_service_jwks['keys'] | selectattr('kid', 'equalto', 'default-ecdsa-p256') | first).crv == 'P-256'
  217. - (matrix_authentication_service_jwks['keys'] | selectattr('kid', 'equalto', 'default-ecdsa-p384') | first).crv == 'P-384'
  218. - (matrix_authentication_service_jwks['keys'] | selectattr('kid', 'equalto', 'default-ecdsa-k256') | first).crv == 'secp256k1'
  219. fail_msg: "MAS did not derive the expected public JWKS from all four managed keys"
  220. success_msg: "MAS exposes all four role-managed signing keys in its public JWKS"
  221. - name: Read the rendered MAS configuration
  222. ansible.builtin.slurp:
  223. src: "{{ matrix_authentication_service_config_path }}/config.yaml"
  224. register: matrix_authentication_service_config_file
  225. - name: Read the rendered MAS configuration identity
  226. ansible.builtin.stat:
  227. path: "{{ matrix_authentication_service_config_path }}/config.yaml"
  228. register: matrix_authentication_service_config_stat
  229. - name: Assert the parsed listener and public URL configuration
  230. ansible.builtin.assert:
  231. that:
  232. - matrix_authentication_service_config.http.listeners | length == 2
  233. - matrix_authentication_service_web_listener.resources | map(attribute='name') | list == ['discovery', 'human', 'oauth', 'compat', 'graphql', 'assets']
  234. - "matrix_authentication_service_web_listener.binds == [{'address': '[::]:8080'}]"
  235. - matrix_authentication_service_web_listener.prefix == '/molecule-auth'
  236. - not matrix_authentication_service_web_listener.proxy_protocol
  237. - "matrix_authentication_service_internal_listener.resources == [{'name': 'health'}]"
  238. - "matrix_authentication_service_internal_listener.binds == [{'host': 'localhost', 'port': 8081}]"
  239. - matrix_authentication_service_internal_listener.prefix == '/molecule-auth'
  240. - matrix_authentication_service_config.http.public_base == 'https://mas.molecule.local/molecule-auth/'
  241. - matrix_authentication_service_config.http.issuer == 'https://mas.molecule.local/molecule-auth/'
  242. - matrix_authentication_service_config.http.trusted_proxies == ['198.51.100.0/24', '2001:db8:1234::/48']
  243. fail_msg: "The parsed MAS listener or public URL configuration is incorrect"
  244. success_msg: "The parsed MAS configuration carries the non-default listener contract"
  245. - name: Assert the parsed Postgres pool configuration
  246. ansible.builtin.assert:
  247. that:
  248. - matrix_authentication_service_config.database.host == matrix_authentication_service_config_database_host
  249. - matrix_authentication_service_config.database.port == matrix_authentication_service_config_database_port
  250. - matrix_authentication_service_config.database.username == matrix_authentication_service_config_database_username
  251. - matrix_authentication_service_config.database.password == matrix_authentication_service_config_database_password
  252. - matrix_authentication_service_config.database.database == matrix_authentication_service_config_database_database
  253. - matrix_authentication_service_config.database.ssl_mode == 'disable'
  254. - matrix_authentication_service_config.database.max_connections == 7
  255. - matrix_authentication_service_config.database.min_connections == 2
  256. - matrix_authentication_service_config.database.connect_timeout == 17
  257. fail_msg: "The parsed MAS configuration does not point at scenario Postgres"
  258. success_msg: "The parsed MAS configuration points at scenario Postgres"
  259. no_log: true
  260. - name: Assert the parsed Matrix and cryptographic configuration
  261. ansible.builtin.assert:
  262. that:
  263. - matrix_authentication_service_config.matrix.homeserver == 'molecule.local'
  264. - matrix_authentication_service_config.matrix.endpoint == 'http://matrix.molecule.local:8008'
  265. - matrix_authentication_service_config.matrix.secret == matrix_authentication_service_config_matrix_secret
  266. - matrix_authentication_service_config.secrets.encryption == matrix_authentication_service_config_secrets_encryption
  267. - matrix_authentication_service_config.secrets['keys'] | map(attribute='kid') | list == ['default-rsa', 'default-ecdsa-p256', 'default-ecdsa-p384', 'default-ecdsa-k256']
  268. - matrix_authentication_service_config.secrets['keys'] | map(attribute='key_file') | list == ['/keys/rsa-2048.priv.pem', '/keys/ecdsa-p256.priv.pem', '/keys/ecdsa-p384.priv.pem', '/keys/ecdsa-k256.priv.pem']
  269. fail_msg: "The parsed MAS Matrix or signing-key configuration is incorrect"
  270. success_msg: "The parsed MAS configuration carries its Matrix secrets and managed keys"
  271. no_log: true
  272. - name: Assert the parsed non-default account behavior
  273. ansible.builtin.assert:
  274. that:
  275. - not matrix_authentication_service_config.account.displayname_change_allowed
  276. - matrix_authentication_service_config.account.password_registration_enabled
  277. - not matrix_authentication_service_config.account.password_registration_email_required
  278. - matrix_authentication_service_config.account.password_recovery_enabled
  279. - matrix_authentication_service_config.account.login_with_email_allowed
  280. - matrix_authentication_service_config.passwords.minimum_complexity == 2
  281. - matrix_authentication_service_config.email.from == '"Molecule MAS" <auth@molecule.local>'
  282. - matrix_authentication_service_config.email.transport == 'blackhole'
  283. - matrix_authentication_service_config.clients == []
  284. - "'upstream_oauth2' not in matrix_authentication_service_config"
  285. - "'captcha' not in matrix_authentication_service_config"
  286. fail_msg: "The parsed MAS configuration does not carry the scenario account behavior"
  287. success_msg: "The parsed MAS configuration carries the non-default account behavior"
  288. - name: Assert the rendered configuration file identity
  289. ansible.builtin.assert:
  290. that:
  291. - matrix_authentication_service_config_stat.stat.uid | int == matrix_user_uid | int
  292. - matrix_authentication_service_config_stat.stat.gid | int == matrix_user_gid | int
  293. - matrix_authentication_service_config_stat.stat.mode == '0644'
  294. fail_msg: "config.yaml has the wrong ownership or mode"
  295. success_msg: "config.yaml has the playbook identity and expected mode"
  296. - name: Read the managed signing-key identities
  297. ansible.builtin.stat:
  298. path: "{{ matrix_authentication_service_data_keys_path }}/{{ key_file }}"
  299. loop:
  300. - rsa-2048.priv.pem
  301. - ecdsa-p256.priv.pem
  302. - ecdsa-p384.priv.pem
  303. - ecdsa-k256.priv.pem
  304. loop_control:
  305. loop_var: key_file
  306. register: matrix_authentication_service_key_stats
  307. - name: Assert every managed signing key is non-empty and private
  308. ansible.builtin.assert:
  309. that:
  310. - key_stat.stat.exists
  311. - key_stat.stat.isreg
  312. - key_stat.stat.size | int > 200
  313. - key_stat.stat.uid | int == matrix_user_uid | int
  314. - key_stat.stat.gid | int == matrix_user_gid | int
  315. - key_stat.stat.mode == '0600'
  316. fail_msg: "A managed MAS signing key is missing, empty, exposed, or misowned"
  317. success_msg: "The managed MAS signing key is present and private"
  318. loop: "{{ matrix_authentication_service_key_stats.results }}"
  319. loop_control:
  320. loop_var: key_stat
  321. label: "{{ key_stat.key_file }}"
  322. - name: Validate every managed signing key with OpenSSL
  323. ansible.builtin.command:
  324. argv:
  325. - openssl
  326. - pkey
  327. - -in
  328. - "{{ matrix_authentication_service_data_keys_path }}/{{ key_file }}"
  329. - -check
  330. - -noout
  331. loop:
  332. - rsa-2048.priv.pem
  333. - ecdsa-p256.priv.pem
  334. - ecdsa-p384.priv.pem
  335. - ecdsa-k256.priv.pem
  336. loop_control:
  337. loop_var: key_file
  338. register: matrix_authentication_service_key_validation
  339. changed_when: false
  340. - name: Assert OpenSSL accepts every managed signing key
  341. ansible.builtin.assert:
  342. that:
  343. - key_validation.rc == 0
  344. fail_msg: "OpenSSL rejected a role-managed MAS signing key"
  345. success_msg: "OpenSSL accepts the role-managed MAS signing key"
  346. loop: "{{ matrix_authentication_service_key_validation.results }}"
  347. loop_control:
  348. loop_var: key_validation
  349. label: "{{ key_validation.key_file }}"
  350. # These tables can only exist after MAS has reached the named host, authenticated as
  351. # the non-default role, selected the non-default database, and run genuine migrations.
  352. - name: List the tables MAS created in Postgres
  353. ansible.builtin.command:
  354. argv:
  355. - docker
  356. - exec
  357. - matrix-postgres-molecule
  358. - psql
  359. - --username={{ matrix_authentication_service_config_database_username }}
  360. - --dbname={{ matrix_authentication_service_config_database_database }}
  361. - --tuples-only
  362. - --no-align
  363. - --command=SELECT tablename FROM pg_tables WHERE schemaname = 'public'
  364. register: matrix_authentication_service_tables
  365. changed_when: false
  366. - name: Count successful MAS migrations in Postgres
  367. ansible.builtin.command:
  368. argv:
  369. - docker
  370. - exec
  371. - matrix-postgres-molecule
  372. - psql
  373. - --username={{ matrix_authentication_service_config_database_username }}
  374. - --dbname={{ matrix_authentication_service_config_database_database }}
  375. - --tuples-only
  376. - --no-align
  377. - --command=SELECT count(*) FROM _sqlx_migrations WHERE success
  378. register: matrix_authentication_service_migrations
  379. changed_when: false
  380. - name: Assert MAS genuinely migrated the configured Postgres database
  381. ansible.builtin.assert:
  382. that:
  383. - matrix_authentication_service_tables.rc == 0
  384. - "'_sqlx_migrations' in matrix_authentication_service_table_names"
  385. - "'users' in matrix_authentication_service_table_names"
  386. - "'user_sessions' in matrix_authentication_service_table_names"
  387. - "'oauth2_clients' in matrix_authentication_service_table_names"
  388. - "'oauth2_access_tokens' in matrix_authentication_service_table_names"
  389. - "'upstream_oauth_providers' in matrix_authentication_service_table_names"
  390. - matrix_authentication_service_table_names | length > 30
  391. - matrix_authentication_service_migrations.rc == 0
  392. - matrix_authentication_service_migrations.stdout | trim | int > 100
  393. fail_msg: >-
  394. MAS did not genuinely migrate {{ matrix_authentication_service_config_database_database }}
  395. ({{ matrix_authentication_service_table_names | length }} tables,
  396. {{ matrix_authentication_service_migrations.stdout | trim | default('?') }} migrations)
  397. success_msg: "MAS genuinely migrated the configured Postgres database"
  398. vars:
  399. matrix_authentication_service_table_names: >-
  400. {{ matrix_authentication_service_tables.stdout_lines | select | list }}
  401. - name: Read the Traefik labels the role rendered
  402. ansible.builtin.slurp:
  403. src: "{{ matrix_authentication_service_config_path }}/labels"
  404. register: matrix_authentication_service_labels_file
  405. - name: Assert the rendered labels carry the non-default public routing contract
  406. ansible.builtin.assert:
  407. that:
  408. - "'traefik.enable=true' in matrix_authentication_service_labels_lines"
  409. - "'traefik.docker.network=matrix-authentication-service-molecule' in matrix_authentication_service_labels_lines"
  410. - "'traefik.http.services.matrix-authentication-service.loadbalancer.server.port=8080' in matrix_authentication_service_labels_lines"
  411. - "'traefik.http.routers.matrix-authentication-service.rule=Host(`mas.molecule.local`) && PathPrefix(`/molecule-auth`)' in matrix_authentication_service_labels_lines"
  412. - "'traefik.http.routers.matrix-authentication-service.priority=742' in matrix_authentication_service_labels_lines"
  413. - "'traefik.http.middlewares.matrix-authentication-service-slashless-redirect.redirectregex.regex=(/molecule-auth)$' in matrix_authentication_service_labels_lines"
  414. - "'traefik.http.middlewares.matrix-authentication-service-add-headers.headers.customresponseheaders.X-Molecule-MAS=covered' in matrix_authentication_service_labels_lines"
  415. - "'traefik.http.routers.matrix-authentication-service.middlewares=matrix-authentication-service-slashless-redirect,matrix-authentication-service-add-headers' in matrix_authentication_service_labels_lines"
  416. - "'traefik.http.routers.matrix-authentication-service.entrypoints=web' in matrix_authentication_service_labels_lines"
  417. - "'traefik.http.routers.matrix-authentication-service.tls=false' in matrix_authentication_service_labels_lines"
  418. - "'molecule.mas.coverage=enabled' in matrix_authentication_service_labels_lines"
  419. fail_msg: "The rendered labels do not carry the configured public MAS route"
  420. success_msg: "The rendered labels carry the configured public MAS route"
  421. - name: Assert the rendered labels carry the compatibility-layer route
  422. ansible.builtin.assert:
  423. that:
  424. - "'traefik.http.middlewares.matrix-authentication-service-add-prefix.addprefix.prefix=/molecule-auth' in matrix_authentication_service_labels_lines"
  425. - "'traefik.http.routers._internalmatrix-authentication-service-public-compatibility-layer.rule=Host(`matrix.molecule.local`) && PathRegexp(`^/_matrix/client/(?P<version>([^/]+))/(?P<endpoint>(login|logout|refresh))`)' in matrix_authentication_service_labels_lines"
  426. - "'traefik.http.routers._internalmatrix-authentication-service-public-compatibility-layer.priority=743' in matrix_authentication_service_labels_lines"
  427. - "'traefik.http.routers._internalmatrix-authentication-service-public-compatibility-layer.middlewares=matrix-authentication-service-add-prefix' in matrix_authentication_service_labels_lines"
  428. - "'traefik.http.routers._internalmatrix-authentication-service-public-compatibility-layer.entrypoints=web' in matrix_authentication_service_labels_lines"
  429. - "'traefik.http.routers._internalmatrix-authentication-service-public-compatibility-layer.tls=false' in matrix_authentication_service_labels_lines"
  430. fail_msg: "The rendered labels do not carry the configured compatibility route"
  431. success_msg: "The rendered labels carry the configured compatibility-layer route"
  432. - name: Inspect the running MAS container
  433. ansible.builtin.command:
  434. argv:
  435. - docker
  436. - container
  437. - inspect
  438. - matrix-authentication-service
  439. register: matrix_authentication_service_container_inspect
  440. changed_when: false
  441. - name: Assert the running container uses the exact image pinned by the role
  442. ansible.builtin.assert:
  443. that:
  444. - matrix_authentication_service_runtime.Config.Image == 'ghcr.io/element-hq/matrix-authentication-service:' + matrix_authentication_service_role_defaults.matrix_authentication_service_version
  445. fail_msg: "The running MAS container does not use the exact role-pinned image"
  446. success_msg: "The running MAS container uses the exact role-pinned image"
  447. - name: Assert the running container uses the playbook identity
  448. ansible.builtin.assert:
  449. that:
  450. - matrix_authentication_service_runtime.Config.User == (matrix_user_uid | string) + ':' + (matrix_user_gid | string)
  451. fail_msg: "The running MAS container does not use the playbook UID and GID"
  452. success_msg: "The running MAS container uses the playbook UID and GID"
  453. - name: Assert the running container security isolation
  454. ansible.builtin.assert:
  455. that:
  456. - matrix_authentication_service_runtime.HostConfig.ReadonlyRootfs is sameas true
  457. - matrix_authentication_service_runtime.HostConfig.CapDrop == ['ALL']
  458. - not matrix_authentication_service_runtime.HostConfig.Privileged
  459. fail_msg: "The running MAS container lacks read-only, capability-dropped isolation"
  460. success_msg: "The running MAS container has read-only, capability-dropped isolation"
  461. - name: Assert the running container mounts only configuration and keys read-only
  462. ansible.builtin.assert:
  463. that:
  464. - matrix_authentication_service_runtime.Mounts | length == 2
  465. - matrix_authentication_service_config_mounts | length == 1
  466. - matrix_authentication_service_config_mounts[0].RW is sameas false
  467. - matrix_authentication_service_keys_mounts | length == 1
  468. - matrix_authentication_service_keys_mounts[0].RW is sameas false
  469. fail_msg: "The running MAS container's config or key mount contract is incorrect"
  470. success_msg: "The running MAS container mounts only configuration and keys read-only"
  471. - name: Assert the running container has only its dedicated network
  472. ansible.builtin.assert:
  473. that:
  474. - matrix_authentication_service_runtime.NetworkSettings.Networks is mapping
  475. - matrix_authentication_service_runtime.NetworkSettings.Networks | length == 1
  476. - matrix_authentication_service_container_network in matrix_authentication_service_runtime.NetworkSettings.Networks
  477. fail_msg: "The running MAS container has unexpected network attachments"
  478. success_msg: "The running MAS container has only its dedicated network"
  479. - name: Assert the running container publishes no host ports
  480. ansible.builtin.assert:
  481. that:
  482. - matrix_authentication_service_runtime.HostConfig.PortBindings | default({}, true) | length == 0
  483. - matrix_authentication_service_runtime.NetworkSettings.Ports | default({}, true) | length == 0
  484. - not matrix_authentication_service_runtime.HostConfig.PublishAllPorts
  485. fail_msg: "The running MAS container unexpectedly publishes a host port"
  486. success_msg: "The running MAS container publishes no host ports"
  487. - name: Assert Docker accepted the role's exact routing labels
  488. ansible.builtin.assert:
  489. that:
  490. - matrix_authentication_service_runtime.Config.Labels is mapping
  491. - matrix_authentication_service_runtime.Config.Labels['molecule.mas.coverage'] == 'enabled'
  492. - matrix_authentication_service_runtime.Config.Labels['traefik.docker.network'] == 'matrix-authentication-service-molecule'
  493. - matrix_authentication_service_runtime.Config.Labels['traefik.http.services.matrix-authentication-service.loadbalancer.server.port'] == '8080'
  494. - matrix_authentication_service_runtime.Config.Labels['traefik.http.routers.matrix-authentication-service.rule'] == 'Host(`mas.molecule.local`) && PathPrefix(`/molecule-auth`)'
  495. - matrix_authentication_service_runtime.Config.Labels['traefik.http.routers.matrix-authentication-service.priority'] == '742'
  496. - matrix_authentication_service_runtime.Config.Labels['traefik.http.routers._internalmatrix-authentication-service-public-compatibility-layer.priority'] == '743'
  497. - matrix_authentication_service_runtime.Config.Labels['traefik.http.middlewares.matrix-authentication-service-add-headers.headers.customresponseheaders.X-Molecule-MAS'] == 'covered'
  498. fail_msg: "Docker did not attach the exact labels rendered by the role"
  499. success_msg: "Docker attached the exact routing labels rendered by the role"