mirror of
				https://github.com/kevinveenbirkenbach/computer-playbook.git
				synced 2025-11-04 12:18:17 +00:00 
			
		
		
		
	Solved oauth2 proxy configuration bugs
This commit is contained in:
		@@ -1,4 +1,4 @@
 | 
				
			|||||||
- name: "set _tmp_database_application_id (Needed due to lazzy loading issue)"
 | 
					- name: "set database_application_id (Needed due to lazzy loading issue)"
 | 
				
			||||||
  set_fact:
 | 
					  set_fact:
 | 
				
			||||||
    database_application_id: "{{ application_id }}"
 | 
					    database_application_id: "{{ application_id }}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
- name: "Transfering oauth2-proxy-keycloak.cfg.j2 to {{docker_compose.directories.volumes}}"
 | 
					- name: "Transfering oauth2-proxy-keycloak.cfg.j2 to {{docker_compose.directories.volumes}}"
 | 
				
			||||||
  template:
 | 
					  template:
 | 
				
			||||||
    src: oauth2-proxy-keycloak.cfg.j2
 | 
					    src: oauth2-proxy-keycloak.cfg.j2
 | 
				
			||||||
    dest: "{{docker_compose.directories.volumes}}{{applications.oauth2_proxy.configuration_file}}"
 | 
					    dest: "{{docker_compose.directories.volumes}}{{applications[application_id].configuration_file}}"
 | 
				
			||||||
  notify:
 | 
					  notify:
 | 
				
			||||||
    - docker compose project setup
 | 
					    - docker compose project setup
 | 
				
			||||||
@@ -1,20 +1,20 @@
 | 
				
			|||||||
http_address            =   "0.0.0.0:4180"
 | 
					http_address            =   "0.0.0.0:4180"
 | 
				
			||||||
cookie_secret           =   "{{ applications[application_id].credentials.oauth2_proxy_cookie_secret }}"
 | 
					cookie_secret           =   "{{ applications[oauth2_proxy_application_id].credentials.oauth2_proxy_cookie_secret }}"
 | 
				
			||||||
email_domains           =   "{{primary_domain}}"
 | 
					email_domains           =   "{{ primary_domain }}"
 | 
				
			||||||
cookie_secure           =   "true"                                                  # True is necessary to force the cookie set via https
 | 
					cookie_secure           =   "true"                                                  # True is necessary to force the cookie set via https
 | 
				
			||||||
upstreams               =   "http://{{applications[application_id].oauth2_proxy.application}}:{{applications[application_id].oauth2_proxy.port}}"
 | 
					upstreams               =   "http://{{ applications[oauth2_proxy_application_id].oauth2_proxy.application }}:{{ applications[oauth2_proxy_application_id].oauth2_proxy.port }}"
 | 
				
			||||||
cookie_domains          =   ["{{domain}}", "{{domains.keycloak}}"]                  # Required so cookie can be read on all subdomains.
 | 
					cookie_domains          =   ["{{ domain }}", "{{ domains.keycloak }}"]                  # Required so cookie can be read on all subdomains.
 | 
				
			||||||
whitelist_domains       =   [".{{primary_domain}}"]                                 # Required to allow redirection back to original requested target.
 | 
					whitelist_domains       =   [".{{ primary_domain }}"]                                 # Required to allow redirection back to original requested target.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# keycloak provider
 | 
					# keycloak provider
 | 
				
			||||||
client_secret           =   "{{oidc.client.secret}}"
 | 
					client_secret           =   "{{ oidc.client.secret }}"
 | 
				
			||||||
client_id               =   "{{oidc.client.id}}"
 | 
					client_id               =   "{{ oidc.client.id }}"
 | 
				
			||||||
redirect_url            =   "{{ web_protocol }}://{{domain}}/oauth2/callback"
 | 
					redirect_url            =   "{{ web_protocol }}://{{domain}}/oauth2/callback"
 | 
				
			||||||
oidc_issuer_url         =   "{{oidc.client.issuer_url}}"
 | 
					oidc_issuer_url         =   "{{ oidc.client.issuer_url }}"
 | 
				
			||||||
provider                =   "oidc"
 | 
					provider                =   "oidc"
 | 
				
			||||||
provider_display_name   =   "Keycloak"
 | 
					provider_display_name   =   "Keycloak"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# role restrictions
 | 
					# role restrictions
 | 
				
			||||||
#cookie_roles           =   "realm_access.roles"
 | 
					#cookie_roles           =   "realm_access.roles"
 | 
				
			||||||
#allowed_groups         =   "{{applications.oauth2_proxy.allowed_roles}}"           # This is not correct here. needs to be placed in applications @todo move there when implementing
 | 
					#allowed_groups         =   "{{ applications[oauth2_proxy_application_id].allowed_roles }}"           # This is not correct here. needs to be placed in applications @todo move there when implementing
 | 
				
			||||||
# @see https://chatgpt.com/share/67f42607-bf68-800f-b587-bd56fe9067b5
 | 
					# @see https://chatgpt.com/share/67f42607-bf68-800f-b587-bd56fe9067b5
 | 
				
			||||||
							
								
								
									
										1
									
								
								roles/docker-oauth2-proxy/vars/main.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								roles/docker-oauth2-proxy/vars/main.yml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					application_id: oauth2-proxy
 | 
				
			||||||
@@ -7,8 +7,13 @@
 | 
				
			|||||||
    src:  "{{ vhost_template_src }}"
 | 
					    src:  "{{ vhost_template_src }}"
 | 
				
			||||||
    dest: "{{ configuration_destination }}"
 | 
					    dest: "{{ configuration_destination }}"
 | 
				
			||||||
  notify: restart nginx
 | 
					  notify: restart nginx
 | 
				
			||||||
  
 | 
					
 | 
				
			||||||
 | 
					- name: "set oauth2_proxy_application_id (Needed due to lazzy loading issue)"
 | 
				
			||||||
 | 
					  set_fact:
 | 
				
			||||||
 | 
					    oauth2_proxy_application_id: "{{ application_id }}"
 | 
				
			||||||
 | 
					  when: "{{applications[application_id].get('features', {}).get('oauth2', False)}}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- name: "include the docker-oauth2-proxy role {{domain}}"
 | 
					- name: "include the docker-oauth2-proxy role {{domain}}"
 | 
				
			||||||
  include_role:
 | 
					  include_role:
 | 
				
			||||||
    name: docker-oauth2-proxy
 | 
					    name: docker-oauth2-proxy
 | 
				
			||||||
  when: final_oauth2_enabled | bool
 | 
					  when: "{{applications[application_id].get('features', {}).get('oauth2', False)}}"
 | 
				
			||||||
@@ -1,2 +1 @@
 | 
				
			|||||||
configuration_destination:  "{{nginx.directories.http.servers}}{{domain}}.conf"
 | 
					configuration_destination:  "{{nginx.directories.http.servers}}{{domain}}.conf"
 | 
				
			||||||
final_oauth2_enabled:       "{{applications[application_id].get('features', {}).get('oauth2', False)}}"
 | 
					 | 
				
			||||||
							
								
								
									
										92
									
								
								tests/unit/test_configuration_filters.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								tests/unit/test_configuration_filters.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,92 @@
 | 
				
			|||||||
 | 
					# tests/unit/test_configuration_filters.py
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import unittest
 | 
				
			||||||
 | 
					from filter_plugins.configuration_filters import (
 | 
				
			||||||
 | 
					    is_feature_enabled,
 | 
				
			||||||
 | 
					    get_csp_whitelist,
 | 
				
			||||||
 | 
					    get_csp_flags,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestConfigurationFilters(unittest.TestCase):
 | 
				
			||||||
 | 
					    def setUp(self):
 | 
				
			||||||
 | 
					        # Sample applications data for testing
 | 
				
			||||||
 | 
					        self.applications = {
 | 
				
			||||||
 | 
					            'app1': {
 | 
				
			||||||
 | 
					                'features': {
 | 
				
			||||||
 | 
					                    'oauth2': True,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                'csp': {
 | 
				
			||||||
 | 
					                    'whitelist': {
 | 
				
			||||||
 | 
					                        # directive with a list
 | 
				
			||||||
 | 
					                        'script-src': ['https://example.com'],
 | 
				
			||||||
 | 
					                        # directive with a single string
 | 
				
			||||||
 | 
					                        'connect-src': 'https://api.example.com',
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    'flags': {
 | 
				
			||||||
 | 
					                        # both flags for script-src
 | 
				
			||||||
 | 
					                        'script-src': {
 | 
				
			||||||
 | 
					                            'unsafe_eval': True,
 | 
				
			||||||
 | 
					                            'unsafe_inline': False,
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                        # only unsafe_inline for style-src
 | 
				
			||||||
 | 
					                        'style-src': {
 | 
				
			||||||
 | 
					                            'unsafe_inline': True,
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            'app2': {
 | 
				
			||||||
 | 
					                # no features or csp defined
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Tests for is_feature_enabled
 | 
				
			||||||
 | 
					    def test_is_feature_enabled_true(self):
 | 
				
			||||||
 | 
					        self.assertTrue(is_feature_enabled(self.applications, 'oauth2', 'app1'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_is_feature_enabled_false_missing_feature(self):
 | 
				
			||||||
 | 
					        self.assertFalse(is_feature_enabled(self.applications, 'nonexistent', 'app1'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_is_feature_enabled_false_missing_app(self):
 | 
				
			||||||
 | 
					        self.assertFalse(is_feature_enabled(self.applications, 'oauth2', 'unknown_app'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Tests for get_csp_whitelist
 | 
				
			||||||
 | 
					    def test_get_csp_whitelist_returns_list_as_is(self):
 | 
				
			||||||
 | 
					        result = get_csp_whitelist(self.applications, 'app1', 'script-src')
 | 
				
			||||||
 | 
					        self.assertEqual(result, ['https://example.com'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_get_csp_whitelist_wraps_string_in_list(self):
 | 
				
			||||||
 | 
					        result = get_csp_whitelist(self.applications, 'app1', 'connect-src')
 | 
				
			||||||
 | 
					        self.assertEqual(result, ['https://api.example.com'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_get_csp_whitelist_empty_when_not_defined(self):
 | 
				
			||||||
 | 
					        result = get_csp_whitelist(self.applications, 'app1', 'frame-src')
 | 
				
			||||||
 | 
					        self.assertEqual(result, [])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_get_csp_whitelist_empty_when_app_missing(self):
 | 
				
			||||||
 | 
					        result = get_csp_whitelist(self.applications, 'nonexistent_app', 'script-src')
 | 
				
			||||||
 | 
					        self.assertEqual(result, [])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Tests for get_csp_flags
 | 
				
			||||||
 | 
					    def test_get_csp_flags_includes_unsafe_eval(self):
 | 
				
			||||||
 | 
					        result = get_csp_flags(self.applications, 'app1', 'script-src')
 | 
				
			||||||
 | 
					        self.assertIn("'unsafe-eval'", result)
 | 
				
			||||||
 | 
					        self.assertNotIn("'unsafe-inline'", result)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_get_csp_flags_includes_unsafe_inline(self):
 | 
				
			||||||
 | 
					        result = get_csp_flags(self.applications, 'app1', 'style-src')
 | 
				
			||||||
 | 
					        self.assertIn("'unsafe-inline'", result)
 | 
				
			||||||
 | 
					        self.assertNotIn("'unsafe-eval'", result)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_get_csp_flags_empty_when_none_configured(self):
 | 
				
			||||||
 | 
					        result = get_csp_flags(self.applications, 'app1', 'connect-src')
 | 
				
			||||||
 | 
					        self.assertEqual(result, [])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_get_csp_flags_empty_when_app_missing(self):
 | 
				
			||||||
 | 
					        result = get_csp_flags(self.applications, 'nonexistent_app', 'script-src')
 | 
				
			||||||
 | 
					        self.assertEqual(result, [])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == '__main__':
 | 
				
			||||||
 | 
					    unittest.main()
 | 
				
			||||||
		Reference in New Issue
	
	Block a user