* [PATCH v1 0/6] Enable users to set an alternate ssh port
@ 2025-10-02 20:21 Chuck Lever
2025-10-02 20:21 ` [PATCH v1 1/6] ansible.cfg: Allow the use of alternate ssh ports Chuck Lever
` (6 more replies)
0 siblings, 7 replies; 10+ messages in thread
From: Chuck Lever @ 2025-10-02 20:21 UTC (permalink / raw)
To: kdevops; +Cc: Chuck Lever
From: Chuck Lever <chuck.lever@oracle.com>
Particularly in the cloud, provisioning publicly visible instances
with port 22 for ssh is bad security practice. Almost as soon as
kdevops brings up such an instance, it comes under attack on port
22.
So, my thought is to enable the use of an alternate port for Ansible
control. This series provides that flexibility; the default is still
to use port 22.
I've tested this with RHEL 9 on AWS, Azure, GCE, and OCI, and with
Debian 12 on AWS; and I tested with Fedora and guestfs.
One of the side benefits of this series is that now all of the
cloud providers except Lambda make use of a cloud-init script. We
can introduce additional capabilities there, going forward.
Also pushed to the linux-kdevops/kdevops alternate-ssh-port branch.
Chuck Lever (6):
ansible.cfg: Allow the use of alternate ssh ports
base_image: Make the semanage command available on base images
guestfs: Refactor the construction of the virt-sysprep command line
guestfs: Use the alternate ssh port for Ansible control
terraform: Hoist the AWS cloud-init script into terraform/
terraform: Use the alternate ssh port for Ansible control
kconfigs/Kconfig.ansible_cfg | 41 ++++++++
playbooks/nixos.yml | 2 +-
.../ansible_cfg/templates/ansible.cfg.j2 | 3 +
.../base_image/templates/virt-builder.j2 | 2 +-
.../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/guestfs/tasks/bringup/main.yml | 44 +++++----
.../roles/terraform/templates/ssh_config.j2 | 2 +-
scripts/update_ssh_config_guestfs.py | 3 +-
scripts/update_ssh_config_lambdalabs.py | 18 +++-
terraform/aws/main.tf | 7 +-
terraform/aws/templates/script.sh | 55 -----------
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 | 97 +++++++++++++++++++
terraform/shared.tf | 6 ++
23 files changed, 289 insertions(+), 94 deletions(-)
delete mode 100755 terraform/aws/templates/script.sh
create mode 100755 terraform/scripts/cloud-init.sh
--
2.51.0
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCH v1 1/6] ansible.cfg: Allow the use of alternate ssh ports 2025-10-02 20:21 [PATCH v1 0/6] Enable users to set an alternate ssh port Chuck Lever @ 2025-10-02 20:21 ` Chuck Lever 2025-10-02 20:21 ` [PATCH v1 2/6] base_image: Make the semanage command available on base images Chuck Lever ` (5 subsequent siblings) 6 siblings, 0 replies; 10+ messages in thread From: Chuck Lever @ 2025-10-02 20:21 UTC (permalink / raw) To: kdevops; +Cc: Chuck Lever From: Chuck Lever <chuck.lever@oracle.com> 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 <chuck.lever@oracle.com> --- 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 c04532e818b1..e3fd02f18d2d 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 f3f6c723f937..deb1a559dc2c 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 %} -- 2.51.0 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v1 2/6] base_image: Make the semanage command available on base images 2025-10-02 20:21 [PATCH v1 0/6] Enable users to set an alternate ssh port Chuck Lever 2025-10-02 20:21 ` [PATCH v1 1/6] ansible.cfg: Allow the use of alternate ssh ports Chuck Lever @ 2025-10-02 20:21 ` Chuck Lever 2025-10-02 20:21 ` [PATCH v1 3/6] guestfs: Refactor the construction of the virt-sysprep command line Chuck Lever ` (4 subsequent siblings) 6 siblings, 0 replies; 10+ messages in thread From: Chuck Lever @ 2025-10-02 20:21 UTC (permalink / raw) To: kdevops; +Cc: Chuck Lever From: Chuck Lever <chuck.lever@oracle.com> 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 <chuck.lever@oracle.com> --- 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 6805679a8959..83005e53251b 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 run-command useradd {{ kdevops_uid }} -s /bin/bash -m kdevops append-line /etc/sudoers.d/kdevops:kdevops ALL=(ALL) NOPASSWD: ALL edit /etc/default/grub:s/^GRUB_CMDLINE_LINUX_DEFAULT=.*/GRUB_CMDLINE_LINUX_DEFAULT="console=ttyS0"/ -- 2.51.0 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v1 3/6] guestfs: Refactor the construction of the virt-sysprep command line 2025-10-02 20:21 [PATCH v1 0/6] Enable users to set an alternate ssh port Chuck Lever 2025-10-02 20:21 ` [PATCH v1 1/6] ansible.cfg: Allow the use of alternate ssh ports Chuck Lever 2025-10-02 20:21 ` [PATCH v1 2/6] base_image: Make the semanage command available on base images Chuck Lever @ 2025-10-02 20:21 ` Chuck Lever 2025-10-02 20:21 ` [PATCH v1 4/6] guestfs: Use the alternate ssh port for Ansible control Chuck Lever ` (3 subsequent siblings) 6 siblings, 0 replies; 10+ messages in thread From: Chuck Lever @ 2025-10-02 20:21 UTC (permalink / raw) To: kdevops; +Cc: Chuck Lever From: Chuck Lever <chuck.lever@oracle.com> 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 <chuck.lever@oracle.com> --- .../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 e5fcbb2e1f21..ce7e4122d6e9 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 -- 2.51.0 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v1 4/6] guestfs: Use the alternate ssh port for Ansible control 2025-10-02 20:21 [PATCH v1 0/6] Enable users to set an alternate ssh port Chuck Lever ` (2 preceding siblings ...) 2025-10-02 20:21 ` [PATCH v1 3/6] guestfs: Refactor the construction of the virt-sysprep command line Chuck Lever @ 2025-10-02 20:21 ` Chuck Lever 2025-10-02 20:21 ` [PATCH v1 5/6] terraform: Hoist the AWS cloud-init script into terraform/ Chuck Lever ` (2 subsequent siblings) 6 siblings, 0 replies; 10+ messages in thread From: Chuck Lever @ 2025-10-02 20:21 UTC (permalink / raw) To: kdevops; +Cc: Chuck Lever From: Chuck Lever <chuck.lever@oracle.com> 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 <chuck.lever@oracle.com> --- 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 bdc9b1e8555e..262855202ca6 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 ce7e4122d6e9..81bac7ceb6aa 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 143ff4fc24c4..40f1ccad02b6 100755 --- a/scripts/update_ssh_config_guestfs.py +++ b/scripts/update_ssh_config_guestfs.py @@ -21,7 +21,7 @@ from pathlib import Path 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)) -- 2.51.0 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v1 5/6] terraform: Hoist the AWS cloud-init script into terraform/ 2025-10-02 20:21 [PATCH v1 0/6] Enable users to set an alternate ssh port Chuck Lever ` (3 preceding siblings ...) 2025-10-02 20:21 ` [PATCH v1 4/6] guestfs: Use the alternate ssh port for Ansible control Chuck Lever @ 2025-10-02 20:21 ` Chuck Lever 2025-10-02 20:21 ` [PATCH v1 6/6] terraform: Use the alternate ssh port for Ansible control Chuck Lever 2025-10-03 0:50 ` [PATCH v1 0/6] Enable users to set an alternate ssh port Luis Chamberlain 6 siblings, 0 replies; 10+ messages in thread From: Chuck Lever @ 2025-10-02 20:21 UTC (permalink / raw) To: kdevops; +Cc: Chuck Lever From: Chuck Lever <chuck.lever@oracle.com> 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 <chuck.lever@oracle.com> --- 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 ac6c6846f20a..949b2febcf0a 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 -- 2.51.0 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v1 6/6] terraform: Use the alternate ssh port for Ansible control 2025-10-02 20:21 [PATCH v1 0/6] Enable users to set an alternate ssh port Chuck Lever ` (4 preceding siblings ...) 2025-10-02 20:21 ` [PATCH v1 5/6] terraform: Hoist the AWS cloud-init script into terraform/ Chuck Lever @ 2025-10-02 20:21 ` Chuck Lever 2025-10-02 20:25 ` Chuck Lever 2025-10-03 0:50 ` [PATCH v1 0/6] Enable users to set an alternate ssh port Luis Chamberlain 6 siblings, 1 reply; 10+ messages in thread From: Chuck Lever @ 2025-10-02 20:21 UTC (permalink / raw) To: kdevops; +Cc: Chuck Lever From: Chuck Lever <chuck.lever@oracle.com> 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 <chuck.lever@oracle.com> --- .../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 4b20667f0686..fc9c94441ded 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 7ce0f6170e22..9c3ac0a0f7f6 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 c6093aeff634..950e12b786fb 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 4fd8cad634aa..a4ba26fc1d7e 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 0839bfacfb24..5f3ceed19b9a 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 3df0e3a4da24..a50468072e59 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 5e8adf0253a1..ba62a2209d4c 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 5b9ab0aa82e6..265f85c2315b 100755 --- a/scripts/update_ssh_config_lambdalabs.py +++ b/scripts/update_ssh_config_lambdalabs.py @@ -11,7 +11,7 @@ from pathlib import Path 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]} <action> <hostname> <ip_address> <username> <config_file> <ssh_key> [provider_name]" + f"Usage: {sys.argv[0]} <action> <hostname> <ip_address> <username> <config_file> <ssh_key> [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 949b2febcf0a..0de2e53710cb 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 8dcead78b5fd..eb609933f2ad 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 816f43098e88..254ecb6a6803 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 a78866c7c8c2..1d736f0c503a 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 15660aa02614..399a05621ee3 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 6e31e2f07dd5..c9037ca734f9 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 926afe99faf3..86c8a67ec13a 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 88e87a27378d..488becd0f797 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" -- 2.51.0 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH v1 6/6] terraform: Use the alternate ssh port for Ansible control 2025-10-02 20:21 ` [PATCH v1 6/6] terraform: Use the alternate ssh port for Ansible control Chuck Lever @ 2025-10-02 20:25 ` Chuck Lever 0 siblings, 0 replies; 10+ messages in thread From: Chuck Lever @ 2025-10-02 20:25 UTC (permalink / raw) To: Chuck Lever, kdevops On 10/2/25 4:21 PM, Chuck Lever wrote: > From: Chuck Lever <chuck.lever@oracle.com> > > When provisioning guestfs instances, make use of the Ansible ssh Ooops. Copy-pasta : should be "When provisioning terraform guests," > port setting, in case it is set to something other than port 22. > > Generated-by: Claude AI > Signed-off-by: Chuck Lever <chuck.lever@oracle.com> > --- > .../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 4b20667f0686..fc9c94441ded 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 7ce0f6170e22..9c3ac0a0f7f6 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 c6093aeff634..950e12b786fb 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 4fd8cad634aa..a4ba26fc1d7e 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 0839bfacfb24..5f3ceed19b9a 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 3df0e3a4da24..a50468072e59 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 5e8adf0253a1..ba62a2209d4c 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 5b9ab0aa82e6..265f85c2315b 100755 > --- a/scripts/update_ssh_config_lambdalabs.py > +++ b/scripts/update_ssh_config_lambdalabs.py > @@ -11,7 +11,7 @@ from pathlib import Path > > > 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]} <action> <hostname> <ip_address> <username> <config_file> <ssh_key> [provider_name]" > + f"Usage: {sys.argv[0]} <action> <hostname> <ip_address> <username> <config_file> <ssh_key> [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 949b2febcf0a..0de2e53710cb 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 8dcead78b5fd..eb609933f2ad 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 816f43098e88..254ecb6a6803 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 a78866c7c8c2..1d736f0c503a 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 15660aa02614..399a05621ee3 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 6e31e2f07dd5..c9037ca734f9 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 926afe99faf3..86c8a67ec13a 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 88e87a27378d..488becd0f797 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" -- Chuck Lever ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v1 0/6] Enable users to set an alternate ssh port 2025-10-02 20:21 [PATCH v1 0/6] Enable users to set an alternate ssh port Chuck Lever ` (5 preceding siblings ...) 2025-10-02 20:21 ` [PATCH v1 6/6] terraform: Use the alternate ssh port for Ansible control Chuck Lever @ 2025-10-03 0:50 ` Luis Chamberlain 2025-10-03 13:44 ` Chuck Lever 6 siblings, 1 reply; 10+ messages in thread From: Luis Chamberlain @ 2025-10-03 0:50 UTC (permalink / raw) To: Chuck Lever; +Cc: kdevops, Chuck Lever On Thu, Oct 02, 2025 at 04:21:34PM -0400, Chuck Lever wrote: > From: Chuck Lever <chuck.lever@oracle.com> > > Particularly in the cloud, provisioning publicly visible instances > with port 22 for ssh is bad security practice. Almost as soon as > kdevops brings up such an instance, it comes under attack on port > 22. > > So, my thought is to enable the use of an alternate port for Ansible > control. This series provides that flexibility; the default is still > to use port 22. > > I've tested this with RHEL 9 on AWS, Azure, GCE, and OCI, and with > Debian 12 on AWS; and I tested with Fedora and guestfs. > > One of the side benefits of this series is that now all of the > cloud providers except Lambda make use of a cloud-init script. We > can introduce additional capabilities there, going forward. > > Also pushed to the linux-kdevops/kdevops alternate-ssh-port branch. This is awesome security best practice, thanks! For cloud providers which support it, why don't we just make it a default? Luis ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v1 0/6] Enable users to set an alternate ssh port 2025-10-03 0:50 ` [PATCH v1 0/6] Enable users to set an alternate ssh port Luis Chamberlain @ 2025-10-03 13:44 ` Chuck Lever 0 siblings, 0 replies; 10+ messages in thread From: Chuck Lever @ 2025-10-03 13:44 UTC (permalink / raw) To: Luis Chamberlain; +Cc: kdevops, Chuck Lever On 10/2/25 8:50 PM, Luis Chamberlain wrote: > On Thu, Oct 02, 2025 at 04:21:34PM -0400, Chuck Lever wrote: >> From: Chuck Lever <chuck.lever@oracle.com> >> >> Particularly in the cloud, provisioning publicly visible instances >> with port 22 for ssh is bad security practice. Almost as soon as >> kdevops brings up such an instance, it comes under attack on port >> 22. >> >> So, my thought is to enable the use of an alternate port for Ansible >> control. This series provides that flexibility; the default is still >> to use port 22. >> >> I've tested this with RHEL 9 on AWS, Azure, GCE, and OCI, and with >> Debian 12 on AWS; and I tested with Fedora and guestfs. >> >> One of the side benefits of this series is that now all of the >> cloud providers except Lambda make use of a cloud-init script. We >> can introduce additional capabilities there, going forward. >> >> Also pushed to the linux-kdevops/kdevops alternate-ssh-port branch. > > This is awesome security best practice, thanks! For cloud providers > which support it, why don't we just make it a default? My impression is that they all support it, but I haven't tested OpenStack or Lambda Labs because I don't have tenancies for those providers/stacks (also haven't tested Nixos). Let me think about how to deal with the OCI "use an existing VCN" setting, where the existing VCN likely has a security rule specific to port 22. I can merge this series as is then follow up with one or two patches to change the default ssh port when I figure that out. -- Chuck Lever ^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2025-10-03 13:44 UTC | newest] Thread overview: 10+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-10-02 20:21 [PATCH v1 0/6] Enable users to set an alternate ssh port Chuck Lever 2025-10-02 20:21 ` [PATCH v1 1/6] ansible.cfg: Allow the use of alternate ssh ports Chuck Lever 2025-10-02 20:21 ` [PATCH v1 2/6] base_image: Make the semanage command available on base images Chuck Lever 2025-10-02 20:21 ` [PATCH v1 3/6] guestfs: Refactor the construction of the virt-sysprep command line Chuck Lever 2025-10-02 20:21 ` [PATCH v1 4/6] guestfs: Use the alternate ssh port for Ansible control Chuck Lever 2025-10-02 20:21 ` [PATCH v1 5/6] terraform: Hoist the AWS cloud-init script into terraform/ Chuck Lever 2025-10-02 20:21 ` [PATCH v1 6/6] terraform: Use the alternate ssh port for Ansible control Chuck Lever 2025-10-02 20:25 ` Chuck Lever 2025-10-03 0:50 ` [PATCH v1 0/6] Enable users to set an alternate ssh port Luis Chamberlain 2025-10-03 13:44 ` Chuck Lever
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox