diff --git a/.github/workflows/promote.yml b/.github/workflows/promote.yml index 6bd36f0102..31c61824b1 100644 --- a/.github/workflows/promote.yml +++ b/.github/workflows/promote.yml @@ -10,6 +10,7 @@ on: jobs: promote: + if: endsWith(github.repository, '/awx') runs-on: ubuntu-latest steps: - name: Checkout awx diff --git a/.github/workflows/stage.yml b/.github/workflows/stage.yml index 306fe3834c..7eb62c86fc 100644 --- a/.github/workflows/stage.yml +++ b/.github/workflows/stage.yml @@ -21,6 +21,7 @@ on: jobs: stage: + if: endsWith(github.repository, '/awx') runs-on: ubuntu-latest permissions: packages: write diff --git a/Makefile b/Makefile index 52cef26da6..dfb3225321 100644 --- a/Makefile +++ b/Makefile @@ -205,19 +205,7 @@ uwsgi: collectstatic @if [ "$(VENV_BASE)" ]; then \ . $(VENV_BASE)/awx/bin/activate; \ fi; \ - uwsgi -b 32768 \ - --socket 127.0.0.1:8050 \ - --module=awx.wsgi:application \ - --home=/var/lib/awx/venv/awx \ - --chdir=/awx_devel/ \ - --vacuum \ - --processes=5 \ - --harakiri=120 --master \ - --no-orphans \ - --max-requests=1000 \ - --stats /tmp/stats.socket \ - --lazy-apps \ - --logformat "%(addr) %(method) %(uri) - %(proto) %(status)" + uwsgi /etc/tower/uwsgi.ini awx-autoreload: @/awx_devel/tools/docker-compose/awx-autoreload /awx_devel/awx "$(DEV_RELOAD_COMMAND)" diff --git a/awx/api/generics.py b/awx/api/generics.py index 7d21d74ee6..361506c605 100644 --- a/awx/api/generics.py +++ b/awx/api/generics.py @@ -28,7 +28,7 @@ from rest_framework import generics from rest_framework.response import Response from rest_framework import status from rest_framework import views -from rest_framework.permissions import AllowAny +from rest_framework.permissions import IsAuthenticated from rest_framework.renderers import StaticHTMLRenderer from rest_framework.negotiation import DefaultContentNegotiation @@ -822,7 +822,7 @@ def trigger_delayed_deep_copy(*args, **kwargs): class CopyAPIView(GenericAPIView): serializer_class = CopySerializer - permission_classes = (AllowAny,) + permission_classes = (IsAuthenticated,) copy_return_serializer_class = None new_in_330 = True new_in_api_v2 = True diff --git a/awx/api/views/__init__.py b/awx/api/views/__init__.py index e81a6ebbde..609e88e155 100644 --- a/awx/api/views/__init__.py +++ b/awx/api/views/__init__.py @@ -4288,7 +4288,7 @@ class WorkflowApprovalTemplateJobsList(SubListAPIView): parent_key = 'workflow_approval_template' -class WorkflowApprovalList(ListCreateAPIView): +class WorkflowApprovalList(ListAPIView): model = models.WorkflowApproval serializer_class = serializers.WorkflowApprovalListSerializer diff --git a/awx/main/routing.py b/awx/main/routing.py index 100347f64e..c96505b7e1 100644 --- a/awx/main/routing.py +++ b/awx/main/routing.py @@ -27,8 +27,8 @@ class AWXProtocolTypeRouter(ProtocolTypeRouter): websocket_urlpatterns = [ - re_path(r'websocket/$', consumers.EventConsumer.as_asgi()), - re_path(r'websocket/broadcast/$', consumers.BroadcastConsumer.as_asgi()), + re_path(r'websocket/', consumers.EventConsumer.as_asgi()), + re_path(r'websocket/broadcast/', consumers.BroadcastConsumer.as_asgi()), ] application = AWXProtocolTypeRouter( diff --git a/awx/main/tasks/jobs.py b/awx/main/tasks/jobs.py index 733475e0eb..369e045e6f 100644 --- a/awx/main/tasks/jobs.py +++ b/awx/main/tasks/jobs.py @@ -311,7 +311,7 @@ class BaseTask(object): env['AWX_PRIVATE_DATA_DIR'] = private_data_dir if self.instance.execution_environment is None: - raise RuntimeError('The project could not sync because there is no Execution Environment.') + raise RuntimeError(f'The {self.model.__name__} could not run because there is no Execution Environment.') return env diff --git a/awx/main/tests/unit/test_tasks.py b/awx/main/tests/unit/test_tasks.py index bfec59b616..d9880be834 100644 --- a/awx/main/tests/unit/test_tasks.py +++ b/awx/main/tests/unit/test_tasks.py @@ -2008,7 +2008,7 @@ def test_project_update_no_ee(mock_me): with pytest.raises(RuntimeError) as e: task.build_env(job, {}) - assert 'The project could not sync because there is no Execution Environment' in str(e.value) + assert 'The ProjectUpdate could not run because there is no Execution Environment' in str(e.value) @pytest.mark.parametrize( diff --git a/awx/main/utils/execution_environments.py b/awx/main/utils/execution_environments.py index 02e6a8b701..7b197287b3 100644 --- a/awx/main/utils/execution_environments.py +++ b/awx/main/utils/execution_environments.py @@ -1,4 +1,5 @@ import os +import logging from pathlib import Path from django.conf import settings @@ -6,8 +7,15 @@ from django.conf import settings from awx.main.models.execution_environments import ExecutionEnvironment +logger = logging.getLogger(__name__) + + def get_control_plane_execution_environment(): - return ExecutionEnvironment.objects.filter(organization=None, managed=True).first() + ee = ExecutionEnvironment.objects.filter(organization=None, managed=True).first() + if ee == None: + logger.error('Failed to find control plane ee, there are no managed EEs without organizations') + raise RuntimeError("Failed to find default control plane EE") + return ee def get_default_execution_environment(): diff --git a/awx_collection/plugins/lookup/schedule_rruleset.py b/awx_collection/plugins/lookup/schedule_rruleset.py index 6aefde48d0..b45d861db3 100644 --- a/awx_collection/plugins/lookup/schedule_rruleset.py +++ b/awx_collection/plugins/lookup/schedule_rruleset.py @@ -196,7 +196,7 @@ class LookupModule(LookupBase): if isinstance(rule[field_name], int): rule[field_name] = [rule[field_name]] # If its not a list, we need to split it into a list - if isinstance(rule[field_name], list): + if not isinstance(rule[field_name], list): rule[field_name] = rule[field_name].split(',') for value in rule[field_name]: # If they have a list of strs we want to strip the str incase its space delineated @@ -210,7 +210,8 @@ class LookupModule(LookupBase): def process_list(self, field_name, rule, valid_list, rule_number): return_values = [] - if isinstance(rule[field_name], list): + # If its not a list, we need to split it into a list + if not isinstance(rule[field_name], list): rule[field_name] = rule[field_name].split(',') for value in rule[field_name]: value = value.strip() diff --git a/awx_collection/plugins/modules/workflow_approval.py b/awx_collection/plugins/modules/workflow_approval.py index bb8c24dfb8..dd81eeb93c 100644 --- a/awx_collection/plugins/modules/workflow_approval.py +++ b/awx_collection/plugins/modules/workflow_approval.py @@ -57,7 +57,15 @@ extends_documentation_fragment: awx.awx.auth EXAMPLES = """ -- name: Launch a workflow with a timeout of 10 seconds +- name: Create a workflow approval node + workflow_job_template_node: + identifier: approval_test + approval_node: + name: approval_jt_name + timeout: 900 + workflow: "Test Workflow" + +- name: Launch the workflow with a timeout of 10 seconds workflow_launch: workflow_template: "Test Workflow" wait: False @@ -66,7 +74,7 @@ EXAMPLES = """ - name: Wait for approval node to activate and approve workflow_approval: workflow_job_id: "{{ workflow.id }}" - name: Approve Me + name: approval_jt_name interval: 10 timeout: 20 action: deny diff --git a/awx_collection/plugins/modules/workflow_job_template.py b/awx_collection/plugins/modules/workflow_job_template.py index 41c5b66573..19954877b7 100644 --- a/awx_collection/plugins/modules/workflow_job_template.py +++ b/awx_collection/plugins/modules/workflow_job_template.py @@ -183,7 +183,21 @@ options: inventory: description: - Inventory applied as a prompt, if job template prompts for inventory - type: str + type: dict + suboptions: + name: + description: + - Name Inventory to be applied to job as launch-time prompts. + type: str + organization: + description: + - Name of key for use in model for organizational reference + type: dict + suboptions: + name: + description: + - The organization of the credentials exists in. + type: str scm_branch: description: - SCM branch applied as a prompt, if job template prompts for SCM branch @@ -544,6 +558,10 @@ EXAMPLES = ''' type: job_template execution_environment: name: My EE + inventory: + name: Test inventory + organization: + name: Default related: credentials: - name: cyberark @@ -613,10 +631,6 @@ def create_workflow_nodes(module, response, workflow_nodes, workflow_id): if workflow_node['unified_job_template']['type'] != 'workflow_approval': module.fail_json(msg="Unable to Find unified_job_template: {0}".format(search_fields)) - inventory = workflow_node.get('inventory') - if inventory: - workflow_node_fields['inventory'] = module.resolve_name_to_id('inventories', inventory) - # Lookup Values for other fields for field_name in ( @@ -645,6 +659,17 @@ def create_workflow_nodes(module, response, workflow_nodes, workflow_id): 'execution_environments', name_or_id=workflow_node['execution_environment']['name'] )['id'] + # Two lookup methods are used based on a fix added in 21.11.0, and the awx export model + if 'inventory' in workflow_node: + if 'name' in workflow_node['inventory']: + inv_lookup_data = {} + if 'organization' in workflow_node['inventory']: + inv_lookup_data['organization'] = module.resolve_name_to_id('organizations', workflow_node['inventory']['organization']['name']) + workflow_node_fields['inventory'] = module.get_one( + 'inventories', name_or_id=workflow_node['inventory']['name'], data=inv_lookup_data)['id'] + else: + workflow_node_fields['inventory'] = module.get_one('inventories', name_or_id=workflow_node['inventory'])['id'] + # Set Search fields search_fields['workflow_job_template'] = workflow_node_fields['workflow_job_template'] = workflow_id diff --git a/awx_collection/test/awx/test_completeness.py b/awx_collection/test/awx/test_completeness.py index 43e225e4b8..451c1a61d3 100644 --- a/awx_collection/test/awx/test_completeness.py +++ b/awx_collection/test/awx/test_completeness.py @@ -16,7 +16,7 @@ import glob # Normally a read-only endpoint should not have a module (i.e. /api/v2/me) but sometimes we reuse a name # For example, we have a role module but /api/v2/roles is a read only endpoint. # This list indicates which read-only endpoints have associated modules with them. -read_only_endpoints_with_modules = ['settings', 'role', 'project_update'] +read_only_endpoints_with_modules = ['settings', 'role', 'project_update', 'workflow_approval'] # If a module should not be created for an endpoint and the endpoint is not read-only add it here # THINK HARD ABOUT DOING THIS diff --git a/awx_collection/tests/integration/targets/lookup_rruleset/tasks/main.yml b/awx_collection/tests/integration/targets/lookup_rruleset/tasks/main.yml index fe5b3f755d..7f9f0271b2 100644 --- a/awx_collection/tests/integration/targets/lookup_rruleset/tasks/main.yml +++ b/awx_collection/tests/integration/targets/lookup_rruleset/tasks/main.yml @@ -95,6 +95,22 @@ - results is failed - "'In rule 2 end_on must either be an integer or in the format YYYY-MM-DD [HH:MM:SS]' in results.msg" +- name: Every Mondays + set_fact: + complex_rule: "{{ query(ruleset_plugin_name, '2022-04-30 10:30:45', rules=rrules, timezone='UTC' ) }}" + ignore_errors: True + register: results + vars: + rrules: + - frequency: 'day' + interval: 1 + byweekday: 'monday' + +- assert: + that: + - results is success + - "'DTSTART;TZID=UTC:20220430T103045 RRULE:FREQ=DAILY;BYDAY=MO;INTERVAL=1' == complex_rule" + - name: call rruleset with an invalid byweekday set_fact: diff --git a/awx_collection/tests/integration/targets/workflow_approval/tasks/main.yml b/awx_collection/tests/integration/targets/workflow_approval/tasks/main.yml new file mode 100644 index 0000000000..eaf1b3bf8d --- /dev/null +++ b/awx_collection/tests/integration/targets/workflow_approval/tasks/main.yml @@ -0,0 +1,57 @@ +--- +- name: Generate a random string for names + set_fact: + test_id: "{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}" + test_prefix: AWX-Collection-tests-workflow_approval + +- name: Generate random names for test objects + set_fact: + org_name: "{{ test_prefix }}-org-{{ test_id }}" + approval_node_name: "{{ test_prefix }}-node-{{ test_id }}" + wfjt_name: "{{ test_prefix }}-wfjt-{{ test_id }}" + +- block: + - name: Create a new organization for test isolation + organization: + name: "{{ org_name }}" + + - name: Create a workflow job template + workflow_job_template: + name: "{{ wfjt_name }}" + organization: "{{ org_name }}" + + - name: Create approval node + workflow_job_template_node: + identifier: approval_test + approval_node: + name: "{{ approval_node_name }}" # Referenced later on + timeout: 900 + workflow: "{{ wfjt_name }}" + + # Launch and approve the workflow + - name: Launch the workflow + workflow_launch: + workflow_template: "{{ wfjt_name }}" + wait: False + register: workflow_job + + - name: Wait for approval node to activate and approve + workflow_approval: + workflow_job_id: "{{ workflow_job.id }}" + name: "{{ approval_node_name }}" + interval: 10 + timeout: 20 + action: approve + register: result + + - assert: + that: + - "result is changed" + - "result is not failed" + + always: + - name: Delete the workflow job template + workflow_job_template: + name: "{{ wfjt_name }}" + state: absent + ignore_errors: True diff --git a/awx_collection/tests/integration/targets/workflow_job_template/tasks/main.yml b/awx_collection/tests/integration/targets/workflow_job_template/tasks/main.yml index 1477193e6c..e5f3366cd3 100644 --- a/awx_collection/tests/integration/targets/workflow_job_template/tasks/main.yml +++ b/awx_collection/tests/integration/targets/workflow_job_template/tasks/main.yml @@ -493,6 +493,7 @@ workflow_job_template: name: "copy_{{ wfjt_name }}" organization: Default + ask_inventory_on_launch: true survey_spec: name: Basic Survey description: Basic Survey @@ -737,6 +738,10 @@ timeout: 23 execution_environment: name: "{{ ee1 }}" + inventory: + name: Test inventory + organization: + name: Default related: credentials: - name: "{{ scm_cred_name }}" diff --git a/docs/development/kind.md b/docs/development/kind.md index 820abae182..d3a11cc4cd 100644 --- a/docs/development/kind.md +++ b/docs/development/kind.md @@ -75,7 +75,8 @@ In the root of awx-operator: -e image_version=devel \ -e image_pull_policy=Always \ -e service_type=nodeport \ - -e namespace=awx + -e namespace=awx \ + -e nodeport_port=30080 ``` Check the operator with the following commands: diff --git a/tools/ansible/roles/dockerfile/templates/Dockerfile.j2 b/tools/ansible/roles/dockerfile/templates/Dockerfile.j2 index 166f24b79f..e35234f622 100644 --- a/tools/ansible/roles/dockerfile/templates/Dockerfile.j2 +++ b/tools/ansible/roles/dockerfile/templates/Dockerfile.j2 @@ -199,11 +199,11 @@ ADD tools/ansible/roles/dockerfile/files/rsyslog.conf /var/lib/awx/rsyslog/rsysl ADD tools/ansible/roles/dockerfile/files/wait-for-migrations /usr/local/bin/wait-for-migrations ADD tools/ansible/roles/dockerfile/files/stop-supervisor /usr/local/bin/stop-supervisor +ADD tools/ansible/roles/dockerfile/files/uwsgi.ini /etc/tower/uwsgi.ini + ## File mappings {% if build_dev|bool %} ADD tools/docker-compose/launch_awx.sh /usr/bin/launch_awx.sh -ADD tools/docker-compose/nginx.conf /etc/nginx/nginx.conf -ADD tools/docker-compose/nginx.vh.default.conf /etc/nginx/conf.d/nginx.vh.default.conf ADD tools/docker-compose/start_tests.sh /start_tests.sh ADD tools/docker-compose/bootstrap_development.sh /usr/bin/bootstrap_development.sh ADD tools/docker-compose/entrypoint.sh /entrypoint.sh @@ -213,7 +213,6 @@ ADD https://raw.githubusercontent.com/containers/libpod/master/contrib/podmanima {% else %} ADD tools/ansible/roles/dockerfile/files/launch_awx.sh /usr/bin/launch_awx.sh ADD tools/ansible/roles/dockerfile/files/launch_awx_task.sh /usr/bin/launch_awx_task.sh -ADD tools/ansible/roles/dockerfile/files/uwsgi.ini /etc/tower/uwsgi.ini ADD {{ template_dest }}/supervisor.conf /etc/supervisord.conf ADD {{ template_dest }}/supervisor_task.conf /etc/supervisord_task.conf {% endif %} diff --git a/tools/ansible/roles/dockerfile/templates/supervisor.conf.j2 b/tools/ansible/roles/dockerfile/templates/supervisor.conf.j2 index e67b79fbe9..6cd821b9d0 100644 --- a/tools/ansible/roles/dockerfile/templates/supervisor.conf.j2 +++ b/tools/ansible/roles/dockerfile/templates/supervisor.conf.j2 @@ -30,8 +30,8 @@ environment = DEV_RELOAD_COMMAND='supervisorctl -c /etc/supervisord_task.conf restart all; supervisorctl restart tower-processes:daphne tower-processes:wsbroadcast' {% else %} command = /var/lib/awx/venv/awx/bin/uwsgi /etc/tower/uwsgi.ini -directory = /var/lib/awx {% endif %} +directory = /var/lib/awx autorestart = true startsecs = 30 stopasgroup=true diff --git a/tools/docker-compose/ansible/roles/sources/defaults/main.yml b/tools/docker-compose/ansible/roles/sources/defaults/main.yml index 9155cacfa9..cf5a65e0b6 100644 --- a/tools/docker-compose/ansible/roles/sources/defaults/main.yml +++ b/tools/docker-compose/ansible/roles/sources/defaults/main.yml @@ -9,6 +9,7 @@ control_plane_node_count: 1 minikube_container_group: false receptor_socket_file: /var/run/awx-receptor/receptor.sock receptor_image: quay.io/ansible/receptor:devel +ingress_path: / # Keys for signing work receptor_rsa_bits: 4096 diff --git a/tools/docker-compose/ansible/roles/sources/tasks/main.yml b/tools/docker-compose/ansible/roles/sources/tasks/main.yml index 5149364dd7..17ab4dc0c9 100644 --- a/tools/docker-compose/ansible/roles/sources/tasks/main.yml +++ b/tools/docker-compose/ansible/roles/sources/tasks/main.yml @@ -49,18 +49,11 @@ mode: '0600' with_items: - "database.py" + - "local_settings.py" - "websocket_secret.py" - "haproxy.cfg" - -- name: Delete old local_settings.py - file: - path: "{{ playbook_dir }}/../../../awx/settings/local_settings.py" - state: absent - -- name: Copy local_settings.py - copy: - src: "local_settings.py" - dest: "{{ sources_dest }}/local_settings.py" + - "nginx.conf" + - "nginx.locations.conf" - name: Get OS info for sdb shell: | diff --git a/tools/docker-compose/ansible/roles/sources/templates/docker-compose.yml.j2 b/tools/docker-compose/ansible/roles/sources/templates/docker-compose.yml.j2 index 60d5d44fb8..7badd37181 100644 --- a/tools/docker-compose/ansible/roles/sources/templates/docker-compose.yml.j2 +++ b/tools/docker-compose/ansible/roles/sources/templates/docker-compose.yml.j2 @@ -24,6 +24,7 @@ services: EXECUTION_NODE_COUNT: {{ execution_node_count|int }} AWX_LOGGING_MODE: stdout DJANGO_SUPERUSER_PASSWORD: {{ admin_password }} + UWSGI_MOUNT_PATH: {{ ingress_path }} {% if loop.index == 1 %} RUN_MIGRATIONS: 1 {% endif %} @@ -40,6 +41,8 @@ services: - "../../docker-compose/_sources/database.py:/etc/tower/conf.d/database.py" - "../../docker-compose/_sources/websocket_secret.py:/etc/tower/conf.d/websocket_secret.py" - "../../docker-compose/_sources/local_settings.py:/etc/tower/conf.d/local_settings.py" + - "../../docker-compose/_sources/nginx.conf:/etc/nginx/nginx.conf" + - "../../docker-compose/_sources/nginx.locations.conf:/etc/nginx/conf.d/nginx.locations.conf" - "../../docker-compose/_sources/SECRET_KEY:/etc/tower/SECRET_KEY" - "../../docker-compose/_sources/receptor/receptor-awx-{{ loop.index }}.conf:/etc/receptor/receptor.conf" - "../../docker-compose/_sources/receptor/receptor-awx-{{ loop.index }}.conf.lock:/etc/receptor/receptor.conf.lock" diff --git a/tools/docker-compose/ansible/roles/sources/files/local_settings.py b/tools/docker-compose/ansible/roles/sources/templates/local_settings.py.j2 similarity index 96% rename from tools/docker-compose/ansible/roles/sources/files/local_settings.py rename to tools/docker-compose/ansible/roles/sources/templates/local_settings.py.j2 index 552850e81c..0b4ffd0160 100644 --- a/tools/docker-compose/ansible/roles/sources/files/local_settings.py +++ b/tools/docker-compose/ansible/roles/sources/templates/local_settings.py.j2 @@ -46,3 +46,5 @@ SYSTEM_UUID = '00000000-0000-0000-0000-000000000000' BROADCAST_WEBSOCKET_PORT = 8013 BROADCAST_WEBSOCKET_VERIFY_CERT = False BROADCAST_WEBSOCKET_PROTOCOL = 'http' + +STATIC_URL = '{{ (ingress_path + '/static/').replace('//', '/') }}' diff --git a/tools/docker-compose/ansible/roles/sources/templates/nginx.conf.j2 b/tools/docker-compose/ansible/roles/sources/templates/nginx.conf.j2 index 327b59a2fe..ff7e2ab386 100644 --- a/tools/docker-compose/ansible/roles/sources/templates/nginx.conf.j2 +++ b/tools/docker-compose/ansible/roles/sources/templates/nginx.conf.j2 @@ -1,8 +1,7 @@ -#user awx; - worker_processes 1; -pid /tmp/nginx.pid; +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; events { worker_connections 1024; @@ -17,7 +16,7 @@ http { '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; - access_log /dev/stdout main; + access_log /var/log/nginx/access.log main; map $http_upgrade $connection_upgrade { default upgrade; @@ -25,41 +24,17 @@ http { } sendfile on; - #tcp_nopush on; - #gzip on; upstream uwsgi { - server 127.0.0.1:8050; - } + server localhost:8050; + } upstream daphne { - server 127.0.0.1:8051; + server localhost:8051; } - {% if ssl_certificate is defined %} server { - listen 8052 default_server; - server_name _; - - # Redirect all HTTP links to the matching HTTPS page - return 301 https://$host$request_uri; - } - {%endif %} - - server { - {% if (ssl_certificate is defined) and (ssl_certificate_key is defined) %} - listen 8053 ssl; - - ssl_certificate /etc/nginx/awxweb.pem; - ssl_certificate_key /etc/nginx/awxweb_key.pem; - {% elif (ssl_certificate is defined) and (ssl_certificate_key is not defined) %} - listen 8053 ssl; - - ssl_certificate /etc/nginx/awxweb.pem; - ssl_certificate_key /etc/nginx/awxweb.pem; - {% else %} - listen 8052 default_server; - {% endif %} + listen 8013 default_server; # If you have a domain name, this is where to add it server_name _; @@ -67,56 +42,35 @@ http { # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months) add_header Strict-Transport-Security max-age=15768000; + add_header X-Content-Type-Options nosniff; - # Protect against click-jacking https://www.owasp.org/index.php/Testing_for_Clickjacking_(OTG-CLIENT-009) - add_header X-Frame-Options "DENY"; + include /etc/nginx/conf.d/*.conf; + } - location /nginx_status { - stub_status on; - access_log off; - allow 127.0.0.1; - deny all; - } + server { + listen 8043 default_server ssl; - location /static/ { - alias /var/lib/awx/public/static/; - } + # If you have a domain name, this is where to add it + server_name _; + keepalive_timeout 65; - location /favicon.ico { alias /var/lib/awx/public/static/favicon.ico; } + ssl_certificate /etc/nginx/nginx.crt; + ssl_certificate_key /etc/nginx/nginx.key; - location /websocket { - # Pass request to the upstream alias - proxy_pass http://daphne; - # Require http version 1.1 to allow for upgrade requests - proxy_http_version 1.1; - # We want proxy_buffering off for proxying to websockets. - proxy_buffering off; - # http://en.wikipedia.org/wiki/X-Forwarded-For - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - # enable this if you use HTTPS: - proxy_set_header X-Forwarded-Proto https; - # pass the Host: header from the client for the sake of redirects - proxy_set_header Host $http_host; - # We've set the Host header, so we don't need Nginx to muddle - # about with redirects - proxy_redirect off; - # Depending on the request value, set the Upgrade and - # connection headers - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $connection_upgrade; - } + ssl_session_timeout 1d; + ssl_session_cache shared:SSL:50m; + ssl_session_tickets off; - location / { - # Add trailing / if missing - rewrite ^(.*)$http_host(.*[^/])$ $1$http_host$2/ permanent; - uwsgi_read_timeout 120s; - uwsgi_pass uwsgi; - include /etc/nginx/uwsgi_params; - {%- if extra_nginx_include is defined %} - include {{ extra_nginx_include }}; - {%- endif %} - proxy_set_header X-Forwarded-Port 443; - uwsgi_param HTTP_X_FORWARDED_PORT 443; - } + # intermediate configuration. tweak to your needs. + ssl_protocols TLSv1.2; + ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256'; + ssl_prefer_server_ciphers on; + + # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months) + add_header Strict-Transport-Security max-age=15768000; + add_header X-Content-Type-Options nosniff; + + + include /etc/nginx/conf.d/*.conf; } } diff --git a/tools/docker-compose/ansible/roles/sources/templates/nginx.locations.conf.j2 b/tools/docker-compose/ansible/roles/sources/templates/nginx.locations.conf.j2 new file mode 100644 index 0000000000..5d1d24b2b1 --- /dev/null +++ b/tools/docker-compose/ansible/roles/sources/templates/nginx.locations.conf.j2 @@ -0,0 +1,37 @@ +location {{ (ingress_path + '/static').replace('//', '/') }} { + alias /var/lib/awx/public/static/; +} + +location {{ (ingress_path + '/favicon.ico').replace('//', '/') }} { + alias /awx_devel/awx/public/static/favicon.ico; +} + +location {{ (ingress_path + '/websocket').replace('//', '/') }} { + # Pass request to the upstream alias + proxy_pass http://daphne; + # Require http version 1.1 to allow for upgrade requests + proxy_http_version 1.1; + # We want proxy_buffering off for proxying to websockets. + proxy_buffering off; + # http://en.wikipedia.org/wiki/X-Forwarded-For + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + # enable this if you use HTTPS: + proxy_set_header X-Forwarded-Proto https; + # pass the Host: header from the client for the sake of redirects + proxy_set_header Host $http_host; + # We've set the Host header, so we don't need Nginx to muddle + # about with redirects + proxy_redirect off; + # Depending on the request value, set the Upgrade and + # connection headers + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; +} + +location {{ ingress_path }} { + # Add trailing / if missing + rewrite ^(.*[^/])$ $1/ permanent; + uwsgi_read_timeout 120s; + uwsgi_pass uwsgi; + include /etc/nginx/uwsgi_params; +} diff --git a/tools/docker-compose/nginx.conf b/tools/docker-compose/nginx.conf deleted file mode 100644 index 348f93b727..0000000000 --- a/tools/docker-compose/nginx.conf +++ /dev/null @@ -1,31 +0,0 @@ -worker_processes 1; - -error_log /var/log/nginx/error.log warn; -pid /var/run/nginx.pid; - -events { - worker_connections 1024; -} - -http { - include /etc/nginx/mime.types; - default_type application/octet-stream; - server_tokens off; - - log_format main '$remote_addr - $remote_user [$time_local] "$request" ' - '$status $body_bytes_sent "$http_referer" ' - '"$http_user_agent" "$http_x_forwarded_for"'; - - access_log /var/log/nginx/access.log main; - - map $http_upgrade $connection_upgrade { - default upgrade; - '' close; - } - - sendfile on; - #tcp_nopush on; - #gzip on; - - include /etc/nginx/conf.d/*.conf; -} diff --git a/tools/docker-compose/nginx.vh.default.conf b/tools/docker-compose/nginx.vh.default.conf deleted file mode 100644 index 649575af34..0000000000 --- a/tools/docker-compose/nginx.vh.default.conf +++ /dev/null @@ -1,124 +0,0 @@ -upstream uwsgi { - server localhost:8050; -} - -upstream daphne { - server localhost:8051; -} - -# server { -# listen 8013 default_server; -# listen [::]:8013 default_server; -# server_name _; -# return 301 https://$host:8043$request_uri; -# } - -server { - listen 8013 default_server; - - # If you have a domain name, this is where to add it - server_name _; - keepalive_timeout 65; - - # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months) - add_header Strict-Transport-Security max-age=15768000; - add_header X-Content-Type-Options nosniff; - - location /static/ { - alias /var/lib/awx/public/static/; - } - - location /favicon.ico { alias /awx_devel/awx/public/static/favicon.ico; } - - location ~ ^/websocket { - # Pass request to the upstream alias - proxy_pass http://daphne; - # Require http version 1.1 to allow for upgrade requests - proxy_http_version 1.1; - # We want proxy_buffering off for proxying to websockets. - proxy_buffering off; - # http://en.wikipedia.org/wiki/X-Forwarded-For - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - # enable this if you use HTTPS: - proxy_set_header X-Forwarded-Proto https; - # pass the Host: header from the client for the sake of redirects - proxy_set_header Host $http_host; - # We've set the Host header, so we don't need Nginx to muddle - # about with redirects - proxy_redirect off; - # Depending on the request value, set the Upgrade and - # connection headers - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $connection_upgrade; - } - - location / { - # Add trailing / if missing - rewrite ^(.*[^/])$ $1/ permanent; - uwsgi_read_timeout 120s; - uwsgi_pass uwsgi; - include /etc/nginx/uwsgi_params; - } -} - -server { - listen 8043 default_server ssl; - - # If you have a domain name, this is where to add it - server_name _; - keepalive_timeout 65; - - ssl_certificate /etc/nginx/nginx.crt; - ssl_certificate_key /etc/nginx/nginx.key; - - ssl_session_timeout 1d; - ssl_session_cache shared:SSL:50m; - ssl_session_tickets off; - - # intermediate configuration. tweak to your needs. - ssl_protocols TLSv1.2; - ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256'; - ssl_prefer_server_ciphers on; - - # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months) - add_header Strict-Transport-Security max-age=15768000; - add_header X-Content-Type-Options nosniff; - - location /static/ { - alias /var/lib/awx/public/static/; - access_log off; - sendfile off; - } - - location /favicon.ico { alias /awx_devel/awx/public/static/favicon.ico; } - - location ~ ^/websocket { - # Pass request to the upstream alias - proxy_pass http://daphne; - # Require http version 1.1 to allow for upgrade requests - proxy_http_version 1.1; - # We want proxy_buffering off for proxying to websockets. - proxy_buffering off; - # http://en.wikipedia.org/wiki/X-Forwarded-For - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - # enable this if you use HTTPS: - proxy_set_header X-Forwarded-Proto https; - # pass the Host: header from the client for the sake of redirects - proxy_set_header Host $http_host; - # We've set the Host header, so we don't need Nginx to muddle - # about with redirects - proxy_redirect off; - # Depending on the request value, set the Upgrade and - # connection headers - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $connection_upgrade; - } - - location / { - # Add trailing / if missing - rewrite ^(.*[^/])$ $1/ permanent; - uwsgi_read_timeout 120s; - uwsgi_pass uwsgi; - include /etc/nginx/uwsgi_params; - } -}