Skip to content
Open
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
5 changes: 5 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ jobs:
if: matrix.security != 'none'
run: |
./forge security --mode ${{ matrix.security }}
- name: Setup TFTP server
run: |
./forge tftp
- name: Apply fapolicyd workarounds
# https://access.redhat.com/solutions/7072618 / https://issues.redhat.com/browse/RHEL-37912
# https://github.com/theforeman/foreman-fapolicyd/blob/develop/15-foreman-container.rules
Expand Down Expand Up @@ -160,6 +163,8 @@ jobs:
--add-feature google \
--add-feature remote-execution \
--add-feature bmc \
--add-feature tftp \
--tftp-servername 127.0.0.1 \
${{ matrix.iop == 'enabled' && '--add-feature iop' || '' }}
- name: Run tests
run: |
Expand Down
3 changes: 3 additions & 0 deletions development/playbooks/tftp/metadata.obsah.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
Comment thread
evgeni marked this conversation as resolved.
help: |
Setup TFTP server
21 changes: 21 additions & 0 deletions development/playbooks/tftp/tftp.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
- name: TFTP
hosts:
- quadlet
become: true
tasks:
- name: Install packages
ansible.builtin.package:
name:
- tftp-server
- tftp
state: present

- name: Start TFTP services
ansible.builtin.systemd:
name: "{{ item }}"
state: started
enabled: true
with_items:
- tftp.socket
- tftp.service
13 changes: 9 additions & 4 deletions docs/user/parameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,15 @@ There are multiple use cases from the users perspective that dictate what parame
| `--add-feature bmc` | Enable BMC feature | `--foreman-proxy-bmc` |
| `--bmc-ipmi-implementation` | IPMI implementation to use for BMC | `--foreman-proxy-bmc-default-provider` |
| `--bmc-redfish-verify-ssl` | Verify SSL certificates for Redfish BMC connections | `--foreman-proxy-bmc-redfish-verify-ssl` |
| `--add-feature tftp` | Enable TFTP feature | `--foreman-proxy-tftp` |
| `--tftp-servername` | IP address of the TFTP server (required when enabling TFTP) | `--foreman-proxy-tftp-servername` |
| `--tftp-root` | Directory to serve TFTP files from | `--foreman-proxy-tftp-root` |

### Unmapped

| foreman-installer Parameter | Description | Reason |
| --------------------------- | ----------- | ------ |
| `--foreman-proxy-tftp-managed` | Installer managed TFTP | not supported |

### Undetermined

Expand Down Expand Up @@ -144,10 +153,6 @@ There are multiple use cases from the users perspective that dictate what parame
| `--foreman-proxy-plugin-dns-route53-aws-access-key` | | foreman_proxy::plugin::dns_route53 | aws_access_key |
| `--foreman-proxy-plugin-dns-route53-aws-secret-key` | | foreman_proxy::plugin::dns_route53 | aws_secret_key |
| `--foreman-proxy-httpboot` | | foreman_proxy | httpboot |
| `--foreman-proxy-tftp` | | foreman_proxy | tftp |
| `--foreman-proxy-tftp-servername` | | foreman_proxy | tftp_servername |
| `--foreman-proxy-tftp-managed` | | foreman_proxy | tftp_managed |
| `--foreman-proxy-tftp-root` | | foreman_proxy | tftp_root |
| `--foreman-proxy-plugin-dhcp-remote-isc-dhcp-config` | | foreman_proxy::plugin::dhcp_remote_isc | config |
| `--foreman-proxy-plugin-dhcp-remote-isc-dhcp-leases` | | foreman_proxy::plugin::dhcp_remote_isc | dhcp_config |
| `--foreman-proxy-plugin-dhcp-remote-isc-key-name` | | foreman_proxy::plugin::dhcp_remote_isc | key_name |
Expand Down
4 changes: 4 additions & 0 deletions src/features.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,7 @@ bmc:
description: Power management for bare metal hosts (IPMI, Redfish)
foreman_proxy:
plugin_name: bmc
tftp:
description: Enable TFTP feature on Foreman Proxy
foreman_proxy:
plugin_name: tftp
10 changes: 9 additions & 1 deletion src/playbooks/deploy/metadata.obsah.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,21 @@ variables:
parameter: --bmc-redfish-verify-ssl
help: Verify SSL certificates for Redfish BMC connections.
type: Boolean
foreman_proxy_tftp_root:
parameter: --tftp-root
help: Directory to serve TFTP files from.

@evgeni evgeni Jun 3, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ewouds comment in jira made me realize that this description is wrong.
We also use /var/lib/tftpboot for the httpboot module (https://github.com/theforeman/smart-proxy/blob/3e249dd019dd36f41b546fe44c045ab055c1da6c/modules/httpboot/httpboot_plugin.rb#L13) and do not allow users to configure that in Puppet.

It is unclear to me how the HTTPBoot thing obtains the files (seems there is no code for that), so maybe it relies on the TFTP module to place the files and just exposes them over HTTP instead of TFTP?

(This is not blocking the feature here, just food for further thought going forward)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also faced similar doubts while just scaffolding httpboot feature, and i realised its use /var/lib/tftp which is not created by httpboot, which makes me think why we don't add tftp as dependency for httpboot. for full context you can refer #518 (comment)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

which makes me think why we don't add tftp as dependency for httpboot.

You may find theforeman/smart-proxy@db33bc6 interesting.

Perhaps we should call it the netboot directory? I already had https://github.com/theforeman/puppet-foreman_proxy/blob/master/manifests/tftp/netboot.pp in the old installer.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"interesting" as in "wait, what!?"? ;-)

How is the directory populated, when the TFTP feature is off, but HTTPBoot is on?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Magic? In theforeman/puppet-foreman_proxy@e4b7763 I wrote:

To get the netboot files, the TFTP feature must still be enabled.

It was a long discussion that was rather tedious. I'm not sure the whole use case of netbooting with HTTPBoot without TFTP was ever really well supported.

@arvind4501 arvind4501 Jun 5, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is the directory populated, when the TFTP feature is off, but HTTPBoot is on?

AFAIK, no population happens, just configuration, As of today, by default httpboot enablement will add :root_dir: /var/lib/tftpboot in config, and still be indirectly dependent on tftp to create and populate that directory

type: AbsolutePath
foreman_proxy_tftp_servername:
parameter: --tftp-servername
help: Server name or IP address for TFTP. TFTP clients typically do not have DNS available, so an IP address is recommended.

constraints:
required_together:
- [certificates_custom_server_certificate, certificates_custom_server_key, certificates_custom_server_ca_certificate]
forbidden_if:
- [certificates_source, installer, [certificates_custom_server_certificate, certificates_custom_server_key, certificates_custom_server_ca_certificate]]

required_in_list:
- [[[features, tftp]], [foreman_proxy_tftp_servername]]

include:
- _certificate_source
Expand Down
4 changes: 4 additions & 0 deletions src/roles/foreman_proxy/defaults/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,7 @@ foreman_proxy_foreman_server_url: "https://{{ ansible_facts['fqdn'] }}"
# BMC settings
foreman_proxy_bmc_ipmi_implementation: ipmitool
foreman_proxy_bmc_redfish_verify_ssl: true

# TFTP settings
foreman_proxy_tftp_root: /var/lib/tftpboot
foreman_proxy_tftp_servername: "{{ undef(hint='You must specify a TFTP servername') }}"
21 changes: 21 additions & 0 deletions src/roles/foreman_proxy/tasks/feature/tftp.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
- name: TFTP root directory
notify:
- Restart Foreman Proxy
- Refresh Foreman Proxy
block:
- name: Create TFTP root directory
ansible.builtin.file:
path: "{{ foreman_proxy_tftp_root }}"
state: directory
mode: "0777"

- name: Mount the directory into foreman-proxy container
ansible.builtin.copy:
dest: /etc/containers/systemd/foreman-proxy.container.d/tftp-root.conf
content: |
[Container]
Volume={{ foreman_proxy_tftp_root }}:/var/lib/tftpboot:rw
mode: '0644'
owner: root
group: root
2 changes: 2 additions & 0 deletions src/roles/foreman_proxy/tasks/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
sdnotify: true
network: host
hostname: "{{ ansible_facts['hostname'] }}.local"
security_opt:
- "label=disable"
secrets:
- 'foreman-proxy-settings-yml,type=mount,target=/etc/foreman-proxy/settings.yml'
- 'foreman-proxy-ssl-ca,type=mount,target=/etc/foreman-proxy/ssl_ca.pem'
Expand Down
4 changes: 4 additions & 0 deletions src/roles/foreman_proxy/templates/settings.d/tftp.yml.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
:enabled: {{ feature_enabled }}
:tftproot: /var/lib/tftpboot
:tftp_servername: {{ foreman_proxy_tftp_servername }}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this fails to render (due to the undef) when the foreman-proxy feature is present, but tftp is not. that's a bug in how we render the templates and I propose #566 to fix it (also ran into this when doing #532 )

23 changes: 23 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from requests.adapters import HTTPAdapter

SSH_CONFIG = './.tmp/ssh-config'
FOREMAN_PROXY_PORT = 8443


class UserParameters:
Expand Down Expand Up @@ -268,6 +269,28 @@ def get_connection_with_tls_context(self, request, verify, proxies=None, cert=No
return conn


@pytest.fixture(scope="module")
def proxy_request(server, certificates, server_fqdn):
Comment thread
stejskalleos marked this conversation as resolved.
port = FOREMAN_PROXY_PORT

def _request(path, method=None, data=None, return_body=False):
curl_opts = (
f"--cacert {certificates['server_ca_certificate']} "
f"--cert {certificates['client_certificate']} "
f"--key {certificates['client_key']} "
f"--silent "
)
if not return_body:
curl_opts += "--output /dev/null --write-out '%{http_code}' "
if method:
curl_opts += f"-X {method} "
if data:
curl_opts += f"-d '{data}' "
return server.run(f"curl {curl_opts}https://{server_fqdn}:{port}/{path}")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return server.run(f"curl {curl_opts}https://{server_fqdn}:{port}/{path}")
return server.run(f"curl {curl_opts}https://{server_fqdn}:{FOREMAN_PROXY_PORT}/{path}")

then you don't need to do port = FOREMAN_PROXY_PORT above


return _request


@pytest.fixture(scope="module")
def local_request(ssh_config, server_fqdn):
session = requests.Session()
Expand Down
31 changes: 21 additions & 10 deletions tests/foreman_proxy_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,18 @@

import pytest

FOREMAN_PROXY_PORT = 8443
from tests.conftest import FOREMAN_PROXY_PORT


@pytest.fixture(scope="module")
def proxy_v2_features(server, certificates, server_fqdn):
cmd = server.run(
f"curl --cacert {certificates['server_ca_certificate']} "
f"--cert {certificates['client_certificate']} "
f"--key {certificates['client_key']} "
f"--silent https://{server_fqdn}:{FOREMAN_PROXY_PORT}/v2/features"
)
def proxy_v2_features(proxy_request):
cmd = proxy_request("v2/features", return_body=True)
assert cmd.succeeded, f"Failed to query /v2/features: {cmd.stderr}"
return json.loads(cmd.stdout)


def test_foreman_proxy_features(server, certificates, server_fqdn, enabled_features):
cmd = server.run(f"curl --cacert {certificates['server_ca_certificate']} --silent https://{server_fqdn}:{FOREMAN_PROXY_PORT}/features")
def test_foreman_proxy_features(proxy_request, enabled_features):
cmd = proxy_request("features", return_body=True)
assert cmd.succeeded
features = json.loads(cmd.stdout)
assert "logs" in features
Expand All @@ -29,6 +24,10 @@ def test_foreman_proxy_features(server, certificates, server_fqdn, enabled_featu
assert "bmc" in features
else:
assert "bmc" not in features
if 'tftp' in enabled_features:
assert "tftp" in features
else:
assert "tftp" not in features


def test_foreman_proxy_service(server):
Expand Down Expand Up @@ -56,6 +55,18 @@ def test_foreman_proxy_client_auth_to_foreman(server, certificates, server_fqdn)
assert cmd.stdout == '201'


@pytest.mark.feature('tftp')
def test_tftp_write_and_fetch(proxy_request, server):
test_mac = "aa:bb:cc:dd:ee:ff"
cmd = proxy_request(f"tftp/{test_mac}", data="syslinux_config=foremanctl+test+probe")
assert cmd.succeeded
assert cmd.stdout == '200', f"Expected HTTP 200 when creating TFTP PXE config, got {cmd.stdout}"

Comment thread
stejskalleos marked this conversation as resolved.
cmd = server.run("tftp 127.0.0.1 -c get pxelinux.cfg/01-aa-bb-cc-dd-ee-ff /tmp/foremanctl_tftp_test_download")
Comment thread
stejskalleos marked this conversation as resolved.
assert cmd.succeeded, f"TFTP get failed: {cmd.stdout}"
assert server.file("/tmp/foremanctl_tftp_test_download").content_string == "foremanctl test probe"


@pytest.mark.feature('bmc')
def test_bmc_capabilities(proxy_v2_features):
assert 'bmc' in proxy_v2_features
Expand Down
Loading