Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions ansible/roles/bastion-proxy/defaults/main/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
# Bastion proxy defaults

# Proxy configuration
proxy_type: squid
proxy_port: 3128
proxy_container_image: quay.io/konflux-ci/caching/squid:latest

# Cache configuration (persistent across reboots)
proxy_cache_dir: /opt/squid/cache
proxy_conf_dir: /opt/squid/conf
proxy_cache_size_mb: 10000
proxy_max_object_size_mb: 4096

# IPv6 settings
proxy_listen_ipv6: true

# ACL - which hosts can use the proxy (auto-populated from controlplane_network)
proxy_allowed_networks: "{{ controlplane_network }}"

# Computed proxy URL for use by other roles
proxy_url: "http://{{ groups['bastion'][0] }}:{{ proxy_port }}"
proxy_host: "{{ groups['bastion'][0] }}"

# No proxy list (hosts that should bypass the proxy)
proxy_no_proxy: "localhost,127.0.0.1,.{{ base_dns_name }},{{ controlplane_network | join(',') }}"
7 changes: 7 additions & 0 deletions ansible/roles/bastion-proxy/handlers/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
# Bastion proxy handlers

- name: Restart squid proxy
containers.podman.podman_pod:
name: squid-proxy
state: restarted
62 changes: 62 additions & 0 deletions ansible/roles/bastion-proxy/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
# Bastion proxy tasks - Deploy Squid forward proxy

- name: Create squid directories
file:
path: "{{ item }}"
state: directory
mode: '0777'
loop:
- "{{ proxy_cache_dir }}"
- "{{ proxy_conf_dir }}"
- /var/log/squid

- name: Template squid configuration
template:
src: squid.conf.j2
dest: "{{ proxy_conf_dir }}/squid.conf"
mode: '0644'
notify: Restart squid proxy

- name: Create squid-proxy pod
containers.podman.podman_pod:
name: squid-proxy
network: host
state: started

- name: Ensure squid-proxy pod is up
containers.podman.podman_pod:
name: squid-proxy
network: host
state: restarted

- name: Create squid proxy container in squid-proxy pod
containers.podman.podman_container:
pod: squid-proxy
network: host
name: squid
image: "{{ proxy_container_image }}"
user: root
volume:
- "{{ proxy_conf_dir }}/squid.conf:/etc/squid/squid.conf:z"
- "{{ proxy_cache_dir }}:/var/spool/squid:z"
- /var/log/squid:/var/log/squid:z
state: started

- name: Open firewall port for proxy
firewalld:
port: "{{ proxy_port }}/tcp"
permanent: true
immediate: true
state: enabled
when: ansible_facts.services['firewalld.service'] is defined

- name: Verify squid proxy is responding
uri:
url: "http://localhost:{{ proxy_port }}"
method: GET
status_code: [400, 403]
register: proxy_check
retries: 5
delay: 3
until: proxy_check.status in [400, 403]
56 changes: 56 additions & 0 deletions ansible/roles/bastion-proxy/templates/squid.conf.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Squid configuration for IPv6 forward proxy
# Generated by Jetlag bastion-proxy role

# Listening ports - IPv4 and IPv6
http_port {{ proxy_port }}
{% if proxy_listen_ipv6 %}
http_port [::]:{{ proxy_port }}
{% endif %}

# Cache configuration for container images
cache_mem 256 MB
maximum_object_size {{ proxy_max_object_size_mb }} MB
cache_dir ufs /var/spool/squid {{ proxy_cache_size_mb }} 16 256

# Logging
access_log daemon:/var/log/squid/access.log squid
cache_log /var/log/squid/cache.log

# ACL definitions
{% for network in proxy_allowed_networks %}
acl localnet src {{ network }}
{% endfor %}

# Safe ports (standard HTTP/HTTPS)
acl SSL_ports port 443
acl Safe_ports port 80
acl Safe_ports port 443
acl CONNECT method CONNECT

# Deny requests to unsafe ports
http_access deny !Safe_ports

# Deny CONNECT to non-SSL ports
http_access deny CONNECT !SSL_ports

# Allow HTTPS CONNECT tunneling (required for container registries)
http_access allow CONNECT SSL_ports

# Allow access from local networks
http_access allow localnet

# Allow localhost
http_access allow localhost

# Deny all other access
http_access deny all

# DNS settings - use system resolver
dns_nameservers {{ controlplane_network[0] | ansible.utils.nthhost(1) }}

# Refresh patterns for container images (cache aggressively)
refresh_pattern -i \.(rpm|deb|tar|gz|tgz|bz2|xz)$ 10080 90% 43200
refresh_pattern . 0 20% 4320

# Shutdown behavior
shutdown_lifetime 5 seconds
32 changes: 30 additions & 2 deletions ansible/roles/create-ai-cluster/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,12 @@
],
"pull_secret": {{ pull_secret | tojson | tojson }},
"ssh_public_key": "{{ lookup('file', ssh_public_key_file) }}",
"additional_ntp_source": "{{ controlplane_network[0] | ansible.utils.nthhost(1) if (lab in cloud_labs) or use_bastion_registry else labs[lab]['ntp_server'] }}"
{% if setup_bastion_proxy | default(false) %}
"http_proxy": "http://{{ proxy_host | default(groups['bastion'][0]) }}:{{ proxy_port | default(3128) }}",
"https_proxy": "http://{{ proxy_host | default(groups['bastion'][0]) }}:{{ proxy_port | default(3128) }}",
"no_proxy": "{{ proxy_no_proxy | default('localhost,127.0.0.1,.' + base_dns_name + ',' + groups['bastion'][0] + ',' + controlplane_network | join(',')) }}",
{% endif %}
"additional_ntp_source": "{{ controlplane_network[0] | ansible.utils.nthhost(1) if (lab in cloud_labs) or use_bastion_registry or setup_bastion_proxy | default(false) else labs[lab]['ntp_server'] }}"
}

- name: SNO - Add entries in /etc/hosts
Expand Down Expand Up @@ -149,7 +154,12 @@
"pull_secret": {{ pull_secret | tojson | tojson }},
"ssh_public_key": "{{ lookup('file', ssh_public_key_file) }}",
"vip_dhcp_allocation": {{ vip_dhcp_allocation }},
"additional_ntp_source": "{{ controlplane_network[0] | ansible.utils.nthhost(1) if (lab in cloud_labs) or use_bastion_registry else labs[lab]['ntp_server'] }}",
{% if setup_bastion_proxy | default(false) %}
"http_proxy": "http://{{ proxy_host | default(groups['bastion'][0]) }}:{{ proxy_port | default(3128) }}",
"https_proxy": "http://{{ proxy_host | default(groups['bastion'][0]) }}:{{ proxy_port | default(3128) }}",
"no_proxy": "{{ proxy_no_proxy | default('localhost,127.0.0.1,.' + base_dns_name + ',' + groups['bastion'][0] + ',' + controlplane_network | join(',')) }}",
{% endif %}
"additional_ntp_source": "{{ controlplane_network[0] | ansible.utils.nthhost(1) if (lab in cloud_labs) or use_bastion_registry or setup_bastion_proxy | default(false) else labs[lab]['ntp_server'] }}",
"network_type": "OVNKubernetes"
}

Expand Down Expand Up @@ -257,6 +267,24 @@
}
when: use_bastion_registry | default(false)

- name: Patch infra-env for proxy configuration
uri:
url: "http://{{ assisted_installer_host }}:{{ assisted_installer_port }}/api/assisted-install/v2/infra-envs/{{ ai_infraenv_id }}"
method: PATCH
body_format: json
status_code: [201]
return_content: true
body: {
"proxy": {
"http_proxy": "http://{{ proxy_host | default(groups['bastion'][0]) }}:{{ proxy_port | default(3128) }}",
"https_proxy": "http://{{ proxy_host | default(groups['bastion'][0]) }}:{{ proxy_port | default(3128) }}",
"no_proxy": "{{ proxy_no_proxy | default('localhost,127.0.0.1,.' + base_dns_name + ',' + groups['bastion'][0] + ',' + controlplane_network | join(',')) }}"
}
}
when:
- setup_bastion_proxy | default(false)
- not use_bastion_registry | default(false)

# Disable lab interface manifests, skip if SNO has sno_use_lab_dhcp=true
- name: Include custom manifests to disable the lab interface (RH Labs & BYOL)
include_tasks: 01_manifest_update.yml
Expand Down
14 changes: 14 additions & 0 deletions ansible/roles/validate-vars/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,20 @@
- enable_bond_vlan | default(false)
- bond_vlan_id | default(10) < 1 or bond_vlan_id | default(10) > 4094

- name: Validate setup_bastion_proxy and setup_bastion_registry are mutually exclusive
fail:
msg: "setup_bastion_proxy and setup_bastion_registry cannot both be true. Use either a forward proxy OR a local mirror registry, not both."
when:
- setup_bastion_proxy | default(false)
- setup_bastion_registry | default(false)

- name: Validate setup_bastion_proxy and use_bastion_registry are mutually exclusive
fail:
msg: "setup_bastion_proxy and use_bastion_registry cannot both be true. Use either a forward proxy OR a local mirror registry, not both."
when:
- setup_bastion_proxy | default(false)
- use_bastion_registry | default(false)

- name: Check for RHEL/Centos (Bastion Validation)
fail:
msg: "Expecting RHEL or Centos for a Bastion OS"
Expand Down
4 changes: 3 additions & 1 deletion ansible/setup-bastion.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@
when:
- setup_bastion_registry
- sync_ocp_release | default(false)
- role: bastion-proxy
when: setup_bastion_proxy | default(false)
- bastion-assisted-installer
- role: bastion-gogs
when: setup_bastion_gogs
- role: bastion-disconnected-haproxy
when: use_bastion_registry
when: use_bastion_registry or setup_bastion_proxy | default(false)
5 changes: 5 additions & 0 deletions ansible/vars/all.sample.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ setup_bastion_registry: false
# Use in conjunction with ipv6 based clusters
use_bastion_registry: false

# Set to enable a forward proxy (Squid) for IPv6 clusters to access external registries
# This is an alternative to setup_bastion_registry - use one or the other
# When enabled, cluster nodes use the proxy to reach quay.io, registry.redhat.io, etc.
setup_bastion_proxy: false

# Reset iDRAC service using badfish container (pulls and uses badfish container
# to clear job queue and reset iDRAC service)
# reset_idrac: false
Expand Down
12 changes: 8 additions & 4 deletions scripts/self-sched-deploy/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -189,10 +189,14 @@ inventory-run:
sed -i 's/^use_bastion_registry: true$$/use_bastion_registry: false/' $(JETLAG_ROOT)/ansible/vars/all.yml; \
elif [ "$$NETWORK_STACK" = "ipv6" ]; then \
sed -i '/^# Single Stack IPv6:/,/^$$/{/^# [a-z]/s/^# //; /^# - /s/^# //;}' $(JETLAG_ROOT)/ansible/vars/all.yml; \
sed -i 's/^setup_bastion_registry: false$$/setup_bastion_registry: true/' $(JETLAG_ROOT)/ansible/vars/all.yml; \
sed -i 's/^use_bastion_registry: false$$/use_bastion_registry: true/' $(JETLAG_ROOT)/ansible/vars/all.yml; \
echo 'sync_operator_index: true' >> $(JETLAG_ROOT)/ansible/vars/all.yml; \
echo 'sync_ocp_release: true' >> $(JETLAG_ROOT)/ansible/vars/all.yml; \
if [ "$$IPV6_MODE" = "disconnected" ]; then \
sed -i 's/^setup_bastion_registry: false$$/setup_bastion_registry: true/' $(JETLAG_ROOT)/ansible/vars/all.yml; \
sed -i 's/^use_bastion_registry: false$$/use_bastion_registry: true/' $(JETLAG_ROOT)/ansible/vars/all.yml; \
echo 'sync_operator_index: true' >> $(JETLAG_ROOT)/ansible/vars/all.yml; \
echo 'sync_ocp_release: true' >> $(JETLAG_ROOT)/ansible/vars/all.yml; \
else \
sed -i 's/^setup_bastion_proxy: false$$/setup_bastion_proxy: true/' $(JETLAG_ROOT)/ansible/vars/all.yml; \
fi; \
elif [ "$$NETWORK_STACK" = "dual" ]; then \
sed -i '/^# Dual Stack/,/^$$/{/^# [a-z]/s/^# //; /^# - /s/^# //;}' $(JETLAG_ROOT)/ansible/vars/all.yml; \
sed -i 's/^setup_bastion_registry: true$$/setup_bastion_registry: false/' $(JETLAG_ROOT)/ansible/vars/all.yml; \
Expand Down
6 changes: 5 additions & 1 deletion scripts/self-sched-deploy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ The interactive prompts collect the following:
- **Cluster Type**: `mno` (Multi-Node OpenShift) or `sno` (Single-Node OpenShift)
- **Worker Count**: Number of worker nodes (MNO only)
- **Network Stack**: `ipv4`, `ipv6`, or `dual` (dual-stack)
- **IPv6 Mode** (IPv6 only): `proxy` (connected via forward proxy) or `disconnected` (local mirror registry)

### Paths
- **Pull Secret**: Path to your `pull-secret.txt` file (default: jetlag root)
Expand Down Expand Up @@ -136,7 +137,10 @@ When you run any target, the tool checks for an existing assignment in `vars/sta
Standard IPv4-only deployment. No special requirements.

### IPv6 Single-Stack
IPv6-only deployment. Automatically enables bastion registry for disconnected installation.
IPv6-only deployment. When selected, an additional prompt asks for the connectivity mode:

- **proxy** (default): Connected deployment via a Squid forward proxy on the bastion. The bastion's dual-stack connectivity provides outbound access for the IPv6 cluster. No local registry mirror or image syncing required.
- **disconnected**: Full disconnected deployment with a local mirror registry (`setup_bastion_registry`, `use_bastion_registry`) and image syncing (`sync_operator_index`, `sync_ocp_release`).

### Dual-Stack
Both IPv4 and IPv6. Standard connected installation using the IPv4 network.
Expand Down
22 changes: 22 additions & 0 deletions scripts/self-sched-deploy/prompt-config.sh
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,21 @@ collect_jetlag_config() {
3|dual) NETWORK_STACK="dual" ;;
esac

# IPv6 connectivity mode
if [[ "$NETWORK_STACK" == "ipv6" ]]; then
echo ""
echo "IPv6 connectivity mode:"
echo "1) proxy - Connected via forward proxy (Squid on bastion)"
echo "2) disconnected - Disconnected with local mirror registry"
prompt_with_options "Select IPv6 mode" "proxy, disconnected" "${IPV6_MODE:-proxy}" IPV6_MODE

# Normalize input
case "$IPV6_MODE" in
1|proxy) IPV6_MODE="proxy" ;;
2|disconnected) IPV6_MODE="disconnected" ;;
esac
fi

print_header "Pull Secret"

# Default pull secret path is in jetlag root
Expand Down Expand Up @@ -241,6 +256,7 @@ DEPLOY_PLAYBOOK="${DEPLOY_PLAYBOOK}"

# Network Configuration
NETWORK_STACK="${NETWORK_STACK}"
IPV6_MODE="${IPV6_MODE}"

# Paths
PULL_SECRET_PATH="${PULL_SECRET_PATH}"
Expand Down Expand Up @@ -304,6 +320,9 @@ display_jetlag_summary() {
echo "OCP Build: $OCP_BUILD"
echo "OCP Version: $OCP_VERSION"
echo "Network Stack: $NETWORK_STACK"
if [[ "$NETWORK_STACK" == "ipv6" ]]; then
echo "IPv6 Mode: $IPV6_MODE"
fi
echo "Pull Secret: $PULL_SECRET_PATH"
if [[ -n "$BASTION_ROOT_PASSWORD" ]]; then
echo "Bastion Password: (provided)"
Expand Down Expand Up @@ -333,6 +352,9 @@ display_full_summary() {
echo "Worker Nodes: $WORKER_NODE_COUNT"
echo "Hosts to Reserve: $NUM_HOSTS"
echo "Network Stack: $NETWORK_STACK"
if [[ "$NETWORK_STACK" == "ipv6" ]]; then
echo "IPv6 Mode: $IPV6_MODE"
fi
echo "Pull Secret: $PULL_SECRET_PATH"
echo "Workload: $WORKLOAD_NAME"
if [[ -n "$BASTION_ROOT_PASSWORD" ]]; then
Expand Down