From f11bfe7e2066b3d48dacbfc722077a66359884ef Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 30 Sep 2025 21:31:14 -0400 Subject: [PATCH 1/6] ansible.cfg: Allow the use of alternate ssh ports Users can now configure the Anisble SSH port via a new Kconfig menu option. The default remains port 22 (standard SSH) to avoid breaking existing setups. This configuration setting applies globally to all hosts in the Ansible inventory. The new port setting is not used yet. The review concern is whether the proposed new Kconfig option is the best user interface for this setting. Generated-by: Claude AI Signed-off-by: Chuck Lever --- kconfigs/Kconfig.ansible_cfg | 41 +++++++++++++++++++ .../ansible_cfg/templates/ansible.cfg.j2 | 3 ++ 2 files changed, 44 insertions(+) diff --git a/kconfigs/Kconfig.ansible_cfg b/kconfigs/Kconfig.ansible_cfg index c04532e81..e3fd02f18 100644 --- a/kconfigs/Kconfig.ansible_cfg +++ b/kconfigs/Kconfig.ansible_cfg @@ -316,6 +316,47 @@ config ANSIBLE_CFG_INVENTORY endif # ANSIBLE_CFG_INVENTORY_CUSTOM +config ANSIBLE_CFG_SSH_PORT_SET_BY_CLI + bool + default $(shell, scripts/check-cli-set-var.sh ANSIBLE_CFG_SSH_PORT) + +config ANSIBLE_CFG_SSH_PORT_CUSTOM + bool "Enable a custom Ansible SSH port setting" + default n + help + When this setting is enabled, specify the SSH port for + Ansible to use when connecting to target nodes. + + When this setting is disabled, kdevops uses the default + SSH port (22), which can be overridden with + "ANSIBLE_CFG_SSH_PORT=NN" on the "make" command line. + + This is useful when your target hosts use a non-standard + SSH port for security or network configuration reasons. + +if ANSIBLE_CFG_SSH_PORT_CUSTOM + +config ANSIBLE_CFG_SSH_PORT + int "Ansible SSH port" + output yaml + help + Set the SSH port for Ansible to use when connecting to target + nodes. The default port is 22. + + https://docs.ansible.com/ansible/latest/collections/ansible/builtin/ssh_connection.html#parameter-remote_port + +endif # ANSIBLE_CFG_SSH_PORT_CUSTOM + +if !ANSIBLE_CFG_SSH_PORT_CUSTOM + +config ANSIBLE_CFG_SSH_PORT + int + output yaml + default 22 if !ANSIBLE_CFG_SSH_PORT_SET_BY_CLI + default $(shell, ./scripts/append-makefile-vars-int.sh $(ANSIBLE_CFG_SSH_PORT)) if ANSIBLE_CFG_SSH_PORT_SET_BY_CLI + +endif # !ANSIBLE_CFG_SSH_PORT_CUSTOM + if DISTRO_OPENSUSE config ANSIBLE_CFG_RECONNECTION_RETRIES diff --git a/playbooks/roles/ansible_cfg/templates/ansible.cfg.j2 b/playbooks/roles/ansible_cfg/templates/ansible.cfg.j2 index f3f6c723f..deb1a559d 100644 --- a/playbooks/roles/ansible_cfg/templates/ansible.cfg.j2 +++ b/playbooks/roles/ansible_cfg/templates/ansible.cfg.j2 @@ -47,7 +47,10 @@ playbook_on_stats_msg_color = bright green [callback_profile_tasks] summary_only = true {% endif %} +[ssh_connection] +remote_port = {{ ansible_cfg_ssh_port }} {% if ansible_facts['distribution'] == 'openSUSE' %} + [connection] retries = {{ ansible_cfg_reconnection_retries }} {% endif %} From e995b427e6bbc1f290796e0159c6d7d73d71b894 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 1 Oct 2025 12:45:45 -0400 Subject: [PATCH 2/6] base_image: Make the semanage command available on base images semanage is needed to adjust SELinux settings on sshd before the first boot, and does not appear to be in the downloaded raw OS images. Generated-by: Claude AI Signed-off-by: Chuck Lever --- playbooks/roles/base_image/templates/virt-builder.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playbooks/roles/base_image/templates/virt-builder.j2 b/playbooks/roles/base_image/templates/virt-builder.j2 index 277c94141..608dc31f7 100644 --- a/playbooks/roles/base_image/templates/virt-builder.j2 +++ b/playbooks/roles/base_image/templates/virt-builder.j2 @@ -12,7 +12,7 @@ mkdir {{ target_dir }} copy-in {{ guestfs_distro_source_and_dest_file }}:{{ target_dir }} {% endif %} -install sudo,qemu-guest-agent,python3,bash +install sudo,qemu-guest-agent,python3,bash,policycoreutils-python-utils # get rid of any rescue initramfs images, and prevent new ones from being generated uninstall dracut-config-rescue From e421b887998859d1f0c4a650234136bb57128e17 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 1 Oct 2025 12:48:51 -0400 Subject: [PATCH 3/6] guestfs: Refactor the construction of the virt-sysprep command line Refactor: The virt-sysprep command line arguments are the same for both invocations. I'm about to add more complexity. There's no sense in duplicating that. Generated-by: Claude AI Signed-off-by: Chuck Lever --- .../roles/guestfs/tasks/bringup/main.yml | 35 ++++++++----------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/playbooks/roles/guestfs/tasks/bringup/main.yml b/playbooks/roles/guestfs/tasks/bringup/main.yml index e5fcbb2e1..ce7e4122d 100644 --- a/playbooks/roles/guestfs/tasks/bringup/main.yml +++ b/playbooks/roles/guestfs/tasks/bringup/main.yml @@ -61,35 +61,30 @@ register: host_timezone delegate_to: localhost + - name: Build virt-sysprep command arguments for each target node + ansible.builtin.set_fact: + virt_sysprep_args: >- + {{ + [ + "virt-sysprep", + "-a", root_image, + "--hostname", inventory_hostname, + "--ssh-inject", "kdevops:file:" + ssh_key + ".pub", + "--timezone", host_timezone.stdout + ] + }} + - name: Build the root image for each target node (as root) become: true become_method: ansible.builtin.sudo ansible.builtin.command: - argv: - - "virt-sysprep" - - "-a" - - "{{ root_image }}" - - "--hostname" - - "{{ inventory_hostname }}" - - "--ssh-inject" - - "kdevops:file:{{ ssh_key }}.pub" - - "--timezone" - - "{{ host_timezone.stdout }}" + argv: "{{ virt_sysprep_args }}" when: - libvirt_uri_system|bool - name: Build the root image for each target node (non-root) ansible.builtin.command: - argv: - - "virt-sysprep" - - "-a" - - "{{ root_image }}" - - "--hostname" - - "{{ inventory_hostname }}" - - "--ssh-inject" - - "kdevops:file:{{ ssh_key }}.pub" - - "--timezone" - - "{{ host_timezone.stdout }}" + argv: "{{ virt_sysprep_args }}" when: - not libvirt_uri_system|bool From 0f601bed71a332f710e325189d9d908480ebdd0a Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 30 Sep 2025 21:49:00 -0400 Subject: [PATCH 4/6] guestfs: Use the alternate ssh port for Ansible control When provisioning guestfs instances, make use of the Ansible ssh port setting, in case it is set to something other than port 22. Generated-by: Claude AI Signed-off-by: Chuck Lever --- playbooks/nixos.yml | 2 +- playbooks/roles/guestfs/tasks/bringup/main.yml | 11 ++++++++++- scripts/update_ssh_config_guestfs.py | 3 ++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/playbooks/nixos.yml b/playbooks/nixos.yml index bdc9b1e85..262855202 100644 --- a/playbooks/nixos.yml +++ b/playbooks/nixos.yml @@ -430,7 +430,7 @@ python3 {{ playbook_dir }}/../scripts/update_ssh_config_nixos.py update \ {{ item }} \ {{ nixos_vm_ips[item] }} \ - 22 \ + {{ ansible_cfg_ssh_port }} \ kdevops \ {{ nixos_ssh_config_file | default(ansible_env.HOME + '/.ssh/config') }} \ {{ ssh_key_path_for_config.stdout | trim }} \ diff --git a/playbooks/roles/guestfs/tasks/bringup/main.yml b/playbooks/roles/guestfs/tasks/bringup/main.yml index ce7e4122d..81bac7ceb 100644 --- a/playbooks/roles/guestfs/tasks/bringup/main.yml +++ b/playbooks/roles/guestfs/tasks/bringup/main.yml @@ -71,7 +71,16 @@ "--hostname", inventory_hostname, "--ssh-inject", "kdevops:file:" + ssh_key + ".pub", "--timezone", host_timezone.stdout - ] + ] + ( + [ + "--run-command", "sed -i '/^#*Port /d' /etc/ssh/sshd_config", + "--append-line", "/etc/ssh/sshd_config:Port " + (ansible_cfg_ssh_port | string), + "--firstboot-command", "semanage port -a -t ssh_port_t -p tcp " + (ansible_cfg_ssh_port | string) + " 2>/dev/null || semanage port -m -t ssh_port_t -p tcp " + (ansible_cfg_ssh_port | string) + "; systemctl restart sshd", + "--firstboot-command", "if command -v firewall-cmd >/dev/null 2>&1 && systemctl is-enabled firewalld >/dev/null 2>&1; then firewall-cmd --permanent --add-port=" + (ansible_cfg_ssh_port | string) + "/tcp && firewall-cmd --reload; fi", + "--firstboot-command", "if command -v ufw >/dev/null 2>&1 && systemctl is-active ufw >/dev/null 2>&1; then ufw allow " + (ansible_cfg_ssh_port | string) + "/tcp; fi" + ] + if ansible_cfg_ssh_port | int != 22 else [] + ) }} - name: Build the root image for each target node (as root) diff --git a/scripts/update_ssh_config_guestfs.py b/scripts/update_ssh_config_guestfs.py index 143ff4fc2..40f1ccad0 100755 --- a/scripts/update_ssh_config_guestfs.py +++ b/scripts/update_ssh_config_guestfs.py @@ -21,7 +21,7 @@ ssh_template = """Host {name} {addr} HostName {addr} User kdevops - Port 22 + Port {port} IdentityFile {sshkey} UserKnownHostsFile /dev/null StrictHostKeyChecking no @@ -97,6 +97,7 @@ def main(): context = { "name": name, "addr": addr, + "port": extra_vars.get("ansible_cfg_ssh_port", 22), "sshkey": f"{extra_vars['guestfs_path']}/{name}/ssh/id_ed25519", } sshconf.write(ssh_template.format(**context)) From f12a1908e10bdec028ecfc4c11624b1e40cefc64 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 2 Oct 2025 16:02:53 -0400 Subject: [PATCH 5/6] terraform: Hoist the AWS cloud-init script into terraform/ I'm about to add the use of a cloud-init script to the other cloud providers. Place the cloud-init script used currently by AWS in a more generic location. Generated-by: Claude AI Signed-off-by: Chuck Lever --- terraform/aws/main.tf | 2 +- terraform/{aws/templates/script.sh => scripts/cloud-init.sh} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename terraform/{aws/templates/script.sh => scripts/cloud-init.sh} (100%) diff --git a/terraform/aws/main.tf b/terraform/aws/main.tf index ac6c6846f..949b2febc 100644 --- a/terraform/aws/main.tf +++ b/terraform/aws/main.tf @@ -76,7 +76,7 @@ resource "aws_key_pair" "kdevops_keypair" { data "template_file" "script_user_data" { count = local.kdevops_num_boxes - template = file("templates/script.sh") + template = file("../scripts/cloud-init.sh") vars = { user_data_log_dir = var.user_data_log_dir diff --git a/terraform/aws/templates/script.sh b/terraform/scripts/cloud-init.sh similarity index 100% rename from terraform/aws/templates/script.sh rename to terraform/scripts/cloud-init.sh From c1f14865bfc2dbec063447d48d953057b1065e87 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 30 Sep 2025 21:43:39 -0400 Subject: [PATCH 6/6] terraform: Use the alternate ssh port for Ansible control When provisioning terraform instances, make use of the Ansible ssh port setting, in case it is set to something other than port 22. Generated-by: Claude AI Signed-off-by: Chuck Lever --- .../templates/aws/terraform.tfvars.j2 | 1 + .../templates/azure/terraform.tfvars.j2 | 1 + .../templates/gce/terraform.tfvars.j2 | 1 + .../templates/lambdalabs/terraform.tfvars.j2 | 1 + .../templates/oci/terraform.tfvars.j2 | 1 + .../templates/openstack/terraform.tfvars.j2 | 1 + .../roles/terraform/templates/ssh_config.j2 | 2 +- scripts/update_ssh_config_lambdalabs.py | 18 ++++++-- terraform/aws/main.tf | 5 ++- terraform/azure/main.tf | 9 +++- terraform/gce/main.tf | 23 +++++++++- terraform/lambdalabs/main.tf | 43 +++++++++++++++++- terraform/oci/main.tf | 11 ++++- terraform/openstack/main.tf | 11 ++++- terraform/scripts/cloud-init.sh | 44 ++++++++++++++++++- terraform/shared.tf | 6 +++ 16 files changed, 162 insertions(+), 16 deletions(-) diff --git a/playbooks/roles/gen_tfvars/templates/aws/terraform.tfvars.j2 b/playbooks/roles/gen_tfvars/templates/aws/terraform.tfvars.j2 index 4b20667f0..fc9c94441 100644 --- a/playbooks/roles/gen_tfvars/templates/aws/terraform.tfvars.j2 +++ b/playbooks/roles/gen_tfvars/templates/aws/terraform.tfvars.j2 @@ -18,6 +18,7 @@ aws_ebs_volume_throughput = {{ terraform_aws_ebs_volume_throughput }} ssh_config_pubkey_file = "{{ kdevops_terraform_ssh_config_pubkey_file }}" ssh_config_user = "{{ kdevops_terraform_ssh_config_user }}" ssh_config = "{{ sshconfig }}" +ssh_config_port = {{ ansible_cfg_ssh_port }} ssh_config_update = "{{ kdevops_terraform_ssh_config_update | lower }}" ssh_config_use_strict_settings = "{{ kdevops_terraform_ssh_config_update_strict | lower }}" diff --git a/playbooks/roles/gen_tfvars/templates/azure/terraform.tfvars.j2 b/playbooks/roles/gen_tfvars/templates/azure/terraform.tfvars.j2 index 7ce0f6170..9c3ac0a0f 100644 --- a/playbooks/roles/gen_tfvars/templates/azure/terraform.tfvars.j2 +++ b/playbooks/roles/gen_tfvars/templates/azure/terraform.tfvars.j2 @@ -13,6 +13,7 @@ azure_managed_disks_tier = "{{ terraform_azure_managed_disks_tier }}" ssh_config_pubkey_file = "{{ kdevops_terraform_ssh_config_pubkey_file }}" ssh_config_user = "{{ kdevops_terraform_ssh_config_user }}" ssh_config = "{{ sshconfig }}" +ssh_config_port = {{ ansible_cfg_ssh_port }} ssh_config_update = "{{ kdevops_terraform_ssh_config_update | lower }}" ssh_config_use_strict_settings = "{{ kdevops_terraform_ssh_config_update_strict | lower }}" diff --git a/playbooks/roles/gen_tfvars/templates/gce/terraform.tfvars.j2 b/playbooks/roles/gen_tfvars/templates/gce/terraform.tfvars.j2 index c6093aeff..950e12b78 100644 --- a/playbooks/roles/gen_tfvars/templates/gce/terraform.tfvars.j2 +++ b/playbooks/roles/gen_tfvars/templates/gce/terraform.tfvars.j2 @@ -20,6 +20,7 @@ gce_disk_throughput = {{ terraform_gce_disk_throughput }} ssh_config_pubkey_file = "{{ kdevops_terraform_ssh_config_pubkey_file }}" ssh_config_user = "{{ kdevops_terraform_ssh_config_user }}" ssh_config = "{{ sshconfig }}" +ssh_config_port = {{ ansible_cfg_ssh_port }} ssh_config_update = "{{ kdevops_terraform_ssh_config_update | lower }}" ssh_config_use_strict_settings = "{{ kdevops_terraform_ssh_config_update_strict | lower }}" diff --git a/playbooks/roles/gen_tfvars/templates/lambdalabs/terraform.tfvars.j2 b/playbooks/roles/gen_tfvars/templates/lambdalabs/terraform.tfvars.j2 index 4fd8cad63..a4ba26fc1 100644 --- a/playbooks/roles/gen_tfvars/templates/lambdalabs/terraform.tfvars.j2 +++ b/playbooks/roles/gen_tfvars/templates/lambdalabs/terraform.tfvars.j2 @@ -7,6 +7,7 @@ ssh_config_pubkey_file = "{{ kdevops_terraform_ssh_config_pubkey_file }}" ssh_config_privkey_file = "{{ kdevops_terraform_ssh_config_privkey_file }}" ssh_config_user = "{{ kdevops_terraform_ssh_config_user }}" ssh_config = "{{ sshconfig }}" +ssh_config_port = {{ ansible_cfg_ssh_port }} # Use unique SSH config file per directory to avoid conflicts ssh_config_name = "{{ kdevops_ssh_config_prefix }}{{ topdir_path_sha256sum[:8] }}" diff --git a/playbooks/roles/gen_tfvars/templates/oci/terraform.tfvars.j2 b/playbooks/roles/gen_tfvars/templates/oci/terraform.tfvars.j2 index 0839bfacf..5f3ceed19 100644 --- a/playbooks/roles/gen_tfvars/templates/oci/terraform.tfvars.j2 +++ b/playbooks/roles/gen_tfvars/templates/oci/terraform.tfvars.j2 @@ -25,6 +25,7 @@ oci_sparse_volume_device_file_name = "{{ terraform_oci_sparse_volume_device_file ssh_config_pubkey_file = "{{ kdevops_terraform_ssh_config_pubkey_file }}" ssh_config_user = "{{ kdevops_terraform_ssh_config_user }}" ssh_config = "{{ sshconfig }}" +ssh_config_port = {{ ansible_cfg_ssh_port }} ssh_config_update = "{{ kdevops_terraform_ssh_config_update | lower }}" ssh_config_use_strict_settings = "{{ kdevops_terraform_ssh_config_update_strict | lower }}" diff --git a/playbooks/roles/gen_tfvars/templates/openstack/terraform.tfvars.j2 b/playbooks/roles/gen_tfvars/templates/openstack/terraform.tfvars.j2 index 3df0e3a4d..a50468072 100644 --- a/playbooks/roles/gen_tfvars/templates/openstack/terraform.tfvars.j2 +++ b/playbooks/roles/gen_tfvars/templates/openstack/terraform.tfvars.j2 @@ -7,6 +7,7 @@ ssh_pubkey_name = "{{ terraform_openstack_ssh_pubkey_name }}" ssh_config_pubkey_file = "{{ kdevops_terraform_ssh_config_pubkey_file }}" ssh_config_user = "{{ kdevops_terraform_ssh_config_user }}" ssh_config = "{{ sshconfig }}" +ssh_config_port = {{ ansible_cfg_ssh_port }} ssh_config_update = "{{ kdevops_terraform_ssh_config_update | lower }}" ssh_config_use_strict_settings = "{{ kdevops_terraform_ssh_config_update_strict | lower }}" diff --git a/playbooks/roles/terraform/templates/ssh_config.j2 b/playbooks/roles/terraform/templates/ssh_config.j2 index 5e8adf025..ba62a2209 100644 --- a/playbooks/roles/terraform/templates/ssh_config.j2 +++ b/playbooks/roles/terraform/templates/ssh_config.j2 @@ -1,7 +1,7 @@ Host {{ item.key }} {{ item.value }} HostName {{ item.value }} User {{ kdevops_terraform_ssh_config_user }} - Port 22 + Port {{ ansible_cfg_ssh_port }} IdentityFile {{ kdevops_terraform_ssh_config_privkey_file }} {% if ssh_config_kexalgorithms %} KexAlgorithms {{ ssh_config_kexalgorithms }} diff --git a/scripts/update_ssh_config_lambdalabs.py b/scripts/update_ssh_config_lambdalabs.py index 5b9ab0aa8..265f85c23 100755 --- a/scripts/update_ssh_config_lambdalabs.py +++ b/scripts/update_ssh_config_lambdalabs.py @@ -11,7 +11,7 @@ def update_ssh_config( - action, hostname, ip_address, username, config_file, ssh_key, provider_name + action, hostname, ip_address, username, config_file, ssh_key, provider_name, port=22 ): """ Update SSH configuration file with Lambda Labs instance details. @@ -24,6 +24,7 @@ def update_ssh_config( config_file: SSH config file path ssh_key: Path to SSH private key provider_name: Provider name for comments + port: SSH port number (default: 22) """ config_file = os.path.expanduser(config_file) ssh_key = os.path.expanduser(ssh_key) @@ -33,7 +34,7 @@ def update_ssh_config( Host {hostname} {ip_address} \tHostName {ip_address} \tUser {username} -\tPort 22 +\tPort {port} \tIdentityFile {ssh_key} \tUserKnownHostsFile /dev/null \tStrictHostKeyChecking no @@ -90,7 +91,7 @@ def main(): """Main entry point.""" if len(sys.argv) < 7: print( - f"Usage: {sys.argv[0]} [provider_name]" + f"Usage: {sys.argv[0]} [provider_name] [port]" ) print(" action: 'update' or 'remove'") print(" hostname: Instance hostname") @@ -99,6 +100,7 @@ def main(): print(" config_file: SSH config file path") print(" ssh_key: Path to SSH private key") print(" provider_name: Optional provider name (default: 'Lambda Labs')") + print(" port: Optional SSH port (default: 22)") sys.exit(1) action = sys.argv[1] @@ -108,9 +110,17 @@ def main(): config_file = sys.argv[5] ssh_key = sys.argv[6] provider_name = sys.argv[7] if len(sys.argv) > 7 else "Lambda Labs" + port = int(sys.argv[8]) if len(sys.argv) > 8 else 22 update_ssh_config( - action, hostname, ip_address, username, config_file, ssh_key, provider_name + action, + hostname, + ip_address, + username, + config_file, + ssh_key, + provider_name, + port, ) diff --git a/terraform/aws/main.tf b/terraform/aws/main.tf index 949b2febc..0de2e5371 100644 --- a/terraform/aws/main.tf +++ b/terraform/aws/main.tf @@ -39,8 +39,8 @@ resource "aws_security_group" "kdevops_sec_group" { cidr_blocks = [ "0.0.0.0/0", ] - from_port = 22 - to_port = 22 + from_port = var.ssh_config_port + to_port = var.ssh_config_port protocol = "tcp" } @@ -82,6 +82,7 @@ data "template_file" "script_user_data" { user_data_log_dir = var.user_data_log_dir user_data_enabled = var.user_data_enabled ssh_config_user = var.ssh_config_user + ssh_config_port = var.ssh_config_port new_hostname = element(var.kdevops_nodes, count.index), } } diff --git a/terraform/azure/main.tf b/terraform/azure/main.tf index 8dcead78b..eb609933f 100644 --- a/terraform/azure/main.tf +++ b/terraform/azure/main.tf @@ -43,7 +43,7 @@ resource "azurerm_network_security_group" "kdevops_sg" { access = "Allow" protocol = "Tcp" source_port_range = "*" - destination_port_range = "22" + destination_port_range = tostring(var.ssh_config_port) source_address_prefix = "*" destination_address_prefix = "*" } @@ -89,6 +89,13 @@ resource "azurerm_linux_virtual_machine" "kdevops_vm" { size = var.azure_vmsize admin_username = var.ssh_config_user disable_password_authentication = true + custom_data = base64encode(templatefile("${path.module}/../scripts/cloud-init.sh", { + user_data_log_dir = "/var/log/kdevops" + user_data_enabled = "yes" + ssh_config_user = var.ssh_config_user + ssh_config_port = var.ssh_config_port + new_hostname = element(var.kdevops_nodes, count.index) + })) os_disk { # Note: yes using the names like the ones below is better however it also diff --git a/terraform/gce/main.tf b/terraform/gce/main.tf index 816f43098..254ecb6a6 100644 --- a/terraform/gce/main.tf +++ b/terraform/gce/main.tf @@ -3,6 +3,19 @@ data "google_compute_image" "kdevops_image" { family = var.gce_image_family } +resource "google_compute_firewall" "kdevops_ssh" { + name = "kdevops-allow-ssh" + network = "default" + + allow { + protocol = "tcp" + ports = [tostring(var.ssh_config_port)] + } + + source_ranges = ["0.0.0.0/0"] + target_tags = ["kdevops-ssh"] +} + resource "google_compute_instance" "kdevops_instance" { count = local.kdevops_num_boxes name = element(var.kdevops_nodes, count.index) @@ -33,7 +46,15 @@ resource "google_compute_instance" "kdevops_instance" { ssh-keys = format("%s:%s", var.ssh_config_user, file(var.ssh_config_pubkey_file)) } - metadata_startup_script = "echo hi > /test.txt" + metadata_startup_script = templatefile("${path.module}/../scripts/cloud-init.sh", { + user_data_log_dir = "/var/log/kdevops" + user_data_enabled = "yes" + ssh_config_user = var.ssh_config_user + ssh_config_port = var.ssh_config_port + new_hostname = element(var.kdevops_nodes, count.index) + }) + + tags = ["kdevops-ssh"] } module "kdevops_compute_disks" { diff --git a/terraform/lambdalabs/main.tf b/terraform/lambdalabs/main.tf index a78866c7c..1d736f0c5 100644 --- a/terraform/lambdalabs/main.tf +++ b/terraform/lambdalabs/main.tf @@ -88,7 +88,7 @@ resource "null_resource" "ansible_update_ssh_config_hosts" { for_each = var.ssh_config_update ? toset(var.kdevops_nodes) : [] provisioner "local-exec" { - command = "python3 ${path.module}/../../scripts/update_ssh_config_lambdalabs.py update ${each.key} ${lambdalabs_instance.kdevops[each.key].ip} ${local.ssh_user} ${var.ssh_config_name} ${var.ssh_config_privkey_file} 'Lambda Labs'" + command = "python3 ${path.module}/../../scripts/update_ssh_config_lambdalabs.py update ${each.key} ${lambdalabs_instance.kdevops[each.key].ip} ${local.ssh_user} ${var.ssh_config_name} ${var.ssh_config_privkey_file} 'Lambda Labs' ${var.ssh_config_port}" } triggers = { @@ -113,6 +113,43 @@ resource "null_resource" "remove_ssh_config" { } } +# Configure SSH port if not using default port 22 +resource "null_resource" "configure_ssh_port" { + for_each = var.ssh_config_port != 22 ? toset(var.kdevops_nodes) : [] + + connection { + type = "ssh" + host = lambdalabs_instance.kdevops[each.key].ip + user = local.ssh_user + port = 22 + private_key = file(pathexpand(var.ssh_config_privkey_file)) + } + + provisioner "remote-exec" { + inline = [ + "echo 'Waiting for system to be ready...'", + "sudo cloud-init status --wait || true", + "echo 'Configuring SSH to listen on port ${var.ssh_config_port}'", + "sudo sed -i '/^[#[:space:]]*Port/d' /etc/ssh/sshd_config", + "echo 'Port ${var.ssh_config_port}' | sudo tee -a /etc/ssh/sshd_config", + "if [ -d /etc/selinux ] && sudo sestatus 2>/dev/null | grep -q 'SELinux status.*enabled'; then if ! command -v semanage >/dev/null 2>&1; then sudo yum install -y policycoreutils-python-utils 2>&1 || sudo dnf install -y policycoreutils-python-utils 2>&1 || true; fi; if command -v semanage >/dev/null 2>&1; then sudo semanage port -a -t ssh_port_t -p tcp ${var.ssh_config_port} 2>&1 || sudo semanage port -m -t ssh_port_t -p tcp ${var.ssh_config_port} 2>&1 || true; fi; fi", + "if command -v firewall-cmd >/dev/null 2>&1 && sudo systemctl is-enabled firewalld >/dev/null 2>&1; then sudo firewall-cmd --permanent --add-port=${var.ssh_config_port}/tcp && sudo firewall-cmd --reload; fi", + "if command -v ufw >/dev/null 2>&1 && sudo systemctl is-active ufw >/dev/null 2>&1; then sudo ufw allow ${var.ssh_config_port}/tcp; fi", + "sudo systemctl restart sshd", + "echo 'SSH port configuration completed'" + ] + } + + depends_on = [ + lambdalabs_instance.kdevops, + null_resource.ansible_update_ssh_config_hosts + ] + + triggers = { + instance_id = lambdalabs_instance.kdevops[each.key].id + } +} + # Ansible provisioning resource "null_resource" "ansible_provision" { for_each = toset(var.kdevops_nodes) @@ -121,6 +158,7 @@ resource "null_resource" "ansible_provision" { type = "ssh" host = lambdalabs_instance.kdevops[each.key].ip user = local.ssh_user + port = var.ssh_config_port private_key = file(pathexpand(var.ssh_config_privkey_file)) } @@ -145,7 +183,8 @@ resource "null_resource" "ansible_provision" { depends_on = [ lambdalabs_instance.kdevops, - null_resource.ansible_update_ssh_config_hosts + null_resource.ansible_update_ssh_config_hosts, + null_resource.configure_ssh_port ] triggers = { diff --git a/terraform/oci/main.tf b/terraform/oci/main.tf index 15660aa02..399a05621 100644 --- a/terraform/oci/main.tf +++ b/terraform/oci/main.tf @@ -35,6 +35,13 @@ resource "oci_core_instance" "kdevops_instance" { metadata = { ssh_authorized_keys = file(var.ssh_config_pubkey_file) + user_data = base64encode(templatefile("${path.module}/../scripts/cloud-init.sh", { + user_data_log_dir = "/var/log/kdevops" + user_data_enabled = "yes" + ssh_config_user = var.ssh_config_user + ssh_config_port = var.ssh_config_port + new_hostname = element(var.kdevops_nodes, count.index) + })) } preemptible_instance_config { @@ -155,8 +162,8 @@ resource "oci_core_security_list" "kdevops_security_list" { source_type = "CIDR_BLOCK" stateless = false tcp_options { - min = 22 - max = 22 + min = var.ssh_config_port + max = var.ssh_config_port } } ingress_security_rules { diff --git a/terraform/openstack/main.tf b/terraform/openstack/main.tf index 6e31e2f07..c9037ca73 100644 --- a/terraform/openstack/main.tf +++ b/terraform/openstack/main.tf @@ -19,8 +19,8 @@ resource "openstack_compute_secgroup_v2" "kdevops_security_group" { # SSH rule { - from_port = 22 - to_port = 22 + from_port = var.ssh_config_port + to_port = var.ssh_config_port ip_protocol = "tcp" cidr = "0.0.0.0/0" } @@ -62,6 +62,13 @@ resource "openstack_compute_instance_v2" "kdevops_instances" { flavor_name = var.flavor_name key_pair = var.ssh_pubkey_name security_groups = [openstack_compute_secgroup_v2.kdevops_security_group.name] + user_data = templatefile("${path.module}/../scripts/cloud-init.sh", { + user_data_log_dir = "/var/log/kdevops" + user_data_enabled = "yes" + ssh_config_user = var.ssh_config_user + ssh_config_port = var.ssh_config_port + new_hostname = element(var.kdevops_nodes, count.index) + }) network { name = var.public_network_name } diff --git a/terraform/scripts/cloud-init.sh b/terraform/scripts/cloud-init.sh index 926afe99f..86c8a67ec 100755 --- a/terraform/scripts/cloud-init.sh +++ b/terraform/scripts/cloud-init.sh @@ -49,7 +49,49 @@ if [ "$USERDATA_ENABLED" != "yes" ]; then fi run_cmd_admin echo "cloud-init: kdevops script user data processing enabled" -run_cmd_admin echo "Nothing to do..." + +# Configure SSH port if not using default port 22 +SSH_PORT="${ssh_config_port}" +if [ "$SSH_PORT" != "22" ]; then + run_cmd_admin echo "Configuring SSH to listen on port $SSH_PORT" + + # Update sshd_config to use alternate port + run_cmd_admin sed -i '/^[#[:space:]]*Port/d' /etc/ssh/sshd_config + echo "Port $SSH_PORT" | run_cmd_admin tee -a /etc/ssh/sshd_config > /dev/null + + # Configure SELinux if present + if [ -d /etc/selinux ] && sestatus 2>/dev/null | grep -q "SELinux status.*enabled"; then + # Install semanage if not available (RHEL/CentOS/Rocky/AlmaLinux) + if ! command -v semanage >/dev/null 2>&1; then + run_cmd_admin yum install -y policycoreutils-python-utils 2>&1 || run_cmd_admin dnf install -y policycoreutils-python-utils 2>&1 || true + fi + + # Try to add the port first, if it fails (already exists), modify it + if command -v semanage >/dev/null 2>&1; then + run_cmd_admin semanage port -a -t ssh_port_t -p tcp $SSH_PORT 2>&1 || run_cmd_admin semanage port -m -t ssh_port_t -p tcp $SSH_PORT 2>&1 || true + run_cmd_admin echo "SELinux port configuration completed" + else + run_cmd_admin echo "WARNING: semanage not available, SELinux may block port $SSH_PORT" + fi + fi + + # Configure firewalld if present and enabled + if command -v firewall-cmd >/dev/null 2>&1 && systemctl is-enabled firewalld >/dev/null 2>&1; then + run_cmd_admin firewall-cmd --permanent --add-port=$SSH_PORT/tcp + run_cmd_admin firewall-cmd --reload + fi + + # Configure ufw if present and active + if command -v ufw >/dev/null 2>&1 && systemctl is-active ufw >/dev/null 2>&1; then + run_cmd_admin ufw allow $SSH_PORT/tcp + fi + + # Restart sshd to apply changes + run_cmd_admin systemctl restart sshd + run_cmd_admin echo "SSH port configuration completed" +else + run_cmd_admin echo "Using default SSH port 22, no configuration needed" +fi # Add more functionality below if you see fit. Be sure to use a variable # to allow to easily enable / disable each mechanism. diff --git a/terraform/shared.tf b/terraform/shared.tf index 88e87a273..488becd0f 100644 --- a/terraform/shared.tf +++ b/terraform/shared.tf @@ -44,6 +44,12 @@ variable "ssh_config_kexalgorithms" { default = "" } +variable "ssh_config_port" { + description = "SSH port to use for remote connections and firewall rules" + type = number + default = 22 +} + variable "private_net_enabled" { description = "Is the private network enabled?" default = "false"