From d9e7279830466f023dc6d79db67594cd58fe5bc3 Mon Sep 17 00:00:00 2001 From: Samyak Jain Date: Tue, 7 Jul 2026 12:06:10 +0530 Subject: [PATCH] Add Ansible playbook to toggle Koji HTTP access with nftables Signed-off-by: Samyak Jain --- playbooks/manual/releng/koji-http-toggle.yml | 133 +++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 playbooks/manual/releng/koji-http-toggle.yml diff --git a/playbooks/manual/releng/koji-http-toggle.yml b/playbooks/manual/releng/koji-http-toggle.yml new file mode 100644 index 0000000000..e2d4f6dd04 --- /dev/null +++ b/playbooks/manual/releng/koji-http-toggle.yml @@ -0,0 +1,133 @@ +--- +# Quick toggle for Koji HTTP access via nftables. +# Run with: -e "koji_action=block|unblock|status" -e "koji_hosts=koji|koji_stg" +# Optional: -e "koji_http_port_override=80" +- name: Toggle Koji HTTP access using nftables + hosts: "{{ koji_hosts | default('koji_stg:koji:koji_riscv') }}" + user: root + gather_facts: true + + vars_files: + - /srv/web/infra/ansible/vars/global.yml + - "{{ private }}/vars.yml" + - /srv/web/infra/ansible/vars/{{ ansible_distribution }}.yml + + vars: + koji_http_port: "{{ koji_http_port_override | default('80') }}" + nft_table: "inet filter" + nft_chain: "input" + nft_set: "koji_http_block_src" + nft_comment: "koji-http-block" + is_staging: "{{ inventory_hostname in groups['koji_stg'] or inventory_hostname in groups.get('koji_stg_rdu3', []) }}" + proxy_hostnames: "{{ is_staging | ternary(['proxy01.stg.rdu3.fedoraproject.org', 'proxy02.stg.rdu3.fedoraproject.org'], ['proxy01.rdu3.fedoraproject.org', 'proxy10.rdu3.fedoraproject.org']) }}" + + pre_tasks: + - name: Validate koji_action + ansible.builtin.fail: + msg: "Usage: -e 'koji_action=block|unblock|status'" + when: koji_action is not defined or koji_action not in ['block', 'unblock', 'status'] + tags: always + + tasks: + - name: Resolve proxy IPs + ansible.builtin.command: + cmd: "getent ahostsv4 {{ item }} | awk '{print $1; exit}'" + register: proxy_resolution + loop: "{{ proxy_hostnames }}" + changed_when: false + failed_when: proxy_resolution.stdout == "" + + - name: Set proxy IP list + ansible.builtin.set_fact: + proxy_ips: "{{ proxy_resolution.results | map(attribute='stdout') | list }}" + + - name: Block Koji HTTP from proxies + when: koji_action == 'block' + block: + - name: Ensure nftables table exists + ansible.builtin.command: + cmd: "nft add table {{ nft_table }}" + register: nft_table_add + changed_when: nft_table_add.rc == 0 + failed_when: nft_table_add.rc != 0 and 'File exists' not in nft_table_add.stderr + + - name: Ensure nftables chain exists + ansible.builtin.command: + cmd: "nft add chain {{ nft_table }} {{ nft_chain }} '{ type filter hook input priority 0; policy accept; }'" + register: nft_chain_add + changed_when: nft_chain_add.rc == 0 + failed_when: nft_chain_add.rc != 0 and 'File exists' not in nft_chain_add.stderr + + - name: Ensure nftables set exists + ansible.builtin.command: + cmd: "nft add set {{ nft_table }} {{ nft_set }} '{ type ipv4_addr; comment \"{{ nft_comment }}\"; }'" + register: nft_set_add + changed_when: nft_set_add.rc == 0 + failed_when: nft_set_add.rc != 0 and 'File exists' not in nft_set_add.stderr + + - name: Ensure drop rule exists + ansible.builtin.command: + cmd: "nft add rule {{ nft_table }} {{ nft_chain }} tcp dport {{ koji_http_port }} ip saddr @{{ nft_set }} drop comment \"{{ nft_comment }}\"" + register: nft_rule_add + changed_when: nft_rule_add.rc == 0 + failed_when: nft_rule_add.rc != 0 and 'File exists' not in nft_rule_add.stderr + + - name: Add proxy IPs to nftables set + ansible.builtin.command: + cmd: "nft add element {{ nft_table }} {{ nft_set }} '{ {{ proxy_ips | join(', ') }} }'" + register: nft_add_elements + changed_when: nft_add_elements.rc == 0 + failed_when: nft_add_elements.rc != 0 and 'File exists' not in nft_add_elements.stderr + + - name: Report block action + ansible.builtin.debug: + msg: "Blocked tcp/{{ koji_http_port }} from {{ proxy_ips | join(', ') }}" + + - name: Unblock Koji HTTP from proxies + when: koji_action == 'unblock' + block: + - name: Flush nftables set (if present) + ansible.builtin.command: + cmd: "nft flush set {{ nft_table }} {{ nft_set }}" + register: nft_set_flush + changed_when: nft_set_flush.rc == 0 + failed_when: nft_set_flush.rc != 0 and 'No such file or directory' not in nft_set_flush.stderr + + - name: Find drop rule handle + ansible.builtin.shell: + cmd: set -o pipefail && nft -a list chain {{ nft_table }} {{ nft_chain }} | awk '/tcp dport {{ koji_http_port }}.* @{{ nft_set }}.* drop/ {print $NF}' + register: rule_handle + changed_when: false + failed_when: false + + - name: Delete drop rule by handle + ansible.builtin.command: + cmd: "nft delete rule {{ nft_table }} {{ nft_chain }} handle {{ rule_handle.stdout }}" + when: rule_handle.stdout != "" + + - name: Report unblock action + ansible.builtin.debug: + msg: "Unblocked tcp/{{ koji_http_port }}" + + - name: Show current nftables status + when: koji_action == 'status' + block: + - name: Show table + ansible.builtin.command: + cmd: "nft list table {{ nft_table }}" + register: table_status + changed_when: false + failed_when: false + + - name: Show set + ansible.builtin.command: + cmd: "nft list set {{ nft_table }} {{ nft_set }}" + register: set_status + changed_when: false + failed_when: false + + - name: Report status + ansible.builtin.debug: + msg: | + {{ table_status.stdout if table_status.rc == 0 else '(no table)' }} + {{ set_status.stdout if set_status.rc == 0 else '(no set)' }} -- 2.56.0