Jelajahi Sumber

Merge branch 'dellhpc:devel' into devel

Cassey Goveas 3 tahun lalu
induk
melakukan
4312561203
38 mengubah file dengan 866 tambahan dan 189 penghapusan
  1. 2 1
      .all-contributorsrc
  2. 1 1
      README.md
  3. 7 1
      control_plane/input_params/base_vars.yml
  4. 58 0
      control_plane/roles/control_plane_common/tasks/validate_provision_vars.yml
  5. 8 0
      control_plane/roles/control_plane_common/tasks/verify_omnia_params.yml
  6. 13 2
      control_plane/roles/control_plane_common/vars/main.yml
  7. 0 1
      control_plane/roles/provision_cobbler/files/Dockerfile_rocky
  8. 29 0
      control_plane/roles/provision_cobbler/files/cobbler_configurations_rocky.yml
  9. 2 2
      control_plane/roles/provision_cobbler/files/cobbler_settings
  10. 1 1
      control_plane/roles/provision_cobbler/files/temp_centos7.ks
  11. 1 1
      control_plane/roles/provision_cobbler/files/temp_rocky8.ks
  12. 30 1
      control_plane/roles/provision_cobbler/tasks/dhcp_configure.yml
  13. 4 4
      control_plane/roles/provision_cobbler/tasks/firewall_settings.yml
  14. 4 4
      control_plane/roles/provision_cobbler/tasks/provision_password.yml
  15. 1 0
      control_plane/roles/provision_cobbler/vars/main.yml
  16. 10 2
      omnia.yml
  17. 5 0
      omnia_config.yml
  18. 42 0
      omnia_security_config.yml
  19. 12 0
      roles/cluster_validation/tasks/fetch_grafana_cred.yml
  20. 0 85
      roles/cluster_validation/tasks/fetch_ipa_password.yml
  21. 43 15
      roles/cluster_validation/tasks/fetch_password.yml
  22. 65 0
      roles/cluster_validation/tasks/fetch_security_inputs.yml
  23. 36 42
      roles/cluster_validation/tasks/main.yml
  24. 27 2
      roles/cluster_validation/vars/main.yml
  25. 59 0
      roles/hostname_validation/tasks/main.yml
  26. 22 0
      roles/hostname_validation/vars/main.yml
  27. 12 10
      roles/login_common/tasks/main.yml
  28. 6 2
      roles/login_common/vars/main.yml
  29. 62 0
      roles/login_node/files/auth_failure_check.yml
  30. 19 0
      roles/login_node/handlers/main.yml
  31. 69 0
      roles/login_node/tasks/configure_alerting.yml
  32. 15 3
      roles/login_node/tasks/install_ipa_client.yml
  33. 39 0
      roles/login_node/tasks/install_snoopy.yml
  34. 46 0
      roles/login_node/tasks/ipa_configuration.yml
  35. 24 8
      roles/login_node/tasks/main.yml
  36. 26 0
      roles/login_node/tasks/session_timeout.yml
  37. 26 0
      roles/login_node/tasks/user_monitor.yml
  38. 40 1
      roles/login_node/vars/main.yml

+ 2 - 1
.all-contributorsrc

@@ -156,7 +156,8 @@
       "avatar_url": "https://avatars.githubusercontent.com/u/88071888?v=4",
       "profile": "https://github.com/cgoveas",
       "contributions": [
-        "doc"
+        "doc",
+        "bug"
       ]
     },
     {

File diff ditekan karena terlalu besar
+ 1 - 1
README.md


+ 7 - 1
control_plane/input_params/base_vars.yml

@@ -153,6 +153,12 @@ host_network_nic: "eno3"
 host_network_dhcp_start_range: "172.17.0.100"
 host_network_dhcp_end_range: "172.17.0.200"
 
+# The DNS servers IP address for the internet access of compute nodes(through DHCP routing using cobbler)
+# If this variable is empty, a separate public internet nic should be present in compute nodes for internet access.
+# Optional field
+primary_dns: ""
+secondary_dns: ""
+
 # The mapping file consists of the MAC address and its respective IP address and Hostname and Component_role(if any)
 # 2 Formats are supported for host mapping files:
 # If user wants one-touch provisioning of omnia cluster, format of csv: MAC,Hostname,IP,Component_role
@@ -175,4 +181,4 @@ ib_network_nic: "ib0"
 # The dhcp range for assigning the IPv4 address
 # Example: 172.17.0.1
 ib_network_dhcp_start_range: "172.25.0.100"
-ib_network_dhcp_end_range: "172.25.0.200"
+ib_network_dhcp_end_range: "172.25.0.200"

+ 58 - 0
control_plane/roles/control_plane_common/tasks/validate_provision_vars.yml

@@ -79,3 +79,61 @@
       - provision_os in iso_file_path | lower
     fail_msg: "{{ invalid_iso_file_path }}"
     success_msg:  "{{ valid_iso_file_path }}"
+
+- name: Warning - waiting for 30 seconds
+  pause:
+    seconds: 30
+    prompt: "{{ dns_empty_warning_msg }}"
+  when: 
+     - primary_dns | length < 1
+     - secondary_dns | length < 1  
+
+- name: Validate primary_dns is not empty when secondary_dns provided
+  fail:
+    msg: "{{ primary_dns_empty_msg }}"
+  when: 
+    - primary_dns | length < 1
+    - secondary_dns | length >1
+
+- name: Validate dns inputs
+  block:
+    - name: Assert primary_dns when not empty
+      assert:
+        that:
+          - primary_dns | ipv4
+        success_msg: "{{ primary_dns_success_msg }}"
+        fail_msg: "{{ primary_dns_fail_msg }}"
+
+    - name: Test reachability of primary_dns
+      command: ping -c3 {{ primary_dns }}
+      failed_when: false
+      changed_when: false
+      register: primary_dns_check
+
+    - name: Verify primary_dns is reachable
+      fail:
+        msg: "{{ primary_dns_not_reachable_msg }}"
+      when: ping_search_key in primary_dns_check.stdout
+
+    - name: Validate secondary_dns inputs
+      block:
+        - name: Assert secondary_dns when not empty
+          assert:
+            that:
+              - secondary_dns | ipv4
+              - secondary_dns != primary_dns
+            success_msg: "{{ secondary_dns_success_msg }}"
+            fail_msg: "{{ secondary_dns_fail_msg }}"
+
+        - name: Test reachability of secondary_dns
+          command: ping -c3 {{ secondary_dns }}
+          failed_when: false
+          changed_when: false
+          register: secondary_dns_check
+
+        - name: Verify secondary_dns is reachable
+          debug:
+            msg: "{{ secondary_dns_not_reachable_msg }}"
+          when: ping_search_key in secondary_dns_check.stdout    
+      when: secondary_dns | length > 1
+  when: primary_dns | length > 1

+ 8 - 0
control_plane/roles/control_plane_common/tasks/verify_omnia_params.yml

@@ -124,6 +124,14 @@
     fail_msg: "{{ login_node_required_fail_msg }}"
   tags: [ validate, templates ]
 
+- name: Verify the value of enable_secure_login_node
+  assert:
+    that:
+      - enable_secure_login_node == true or enable_secure_login_node == false
+    success_msg: "{{ secure_login_node_success_msg }}"
+    fail_msg: "{{ secure_login_node_fail_msg }}"
+  tags: [ validate, templates ]
+
 - name: Validate the domain name
   assert:
     that:

+ 13 - 2
control_plane/roles/control_plane_common/vars/main.yml

@@ -103,8 +103,10 @@ fail_msg_directory_manager_password: "Failed. Incorrect format provided for dire
 success_msg_ipa_admin_password: "ipa_admin_password successfully validated"
 fail_msg_ipa_admin_password: "Failed. Incorrect format provided for ipa_admin_password"
 omnia_input_config_failure_msg: "Failed. Please provide all the required parameters in omnia_config.yml for for login_node"
-login_node_required_success_msg: "Login_node_required successfully validated"
-login_node_required_fail_msg: "Failed. login_node_required can be either true or false"
+login_node_required_success_msg: "login_node_required successfully validated"
+login_node_required_fail_msg: "Failed. login_node_required should be either true or false"
+secure_login_node_success_msg: "enable_secure_login_node successfully validated"
+secure_login_node_fail_msg: "Failed. enable_secure_login_node should be either true or false"
 
 # Usage: fetch_base_inputs.yml
 base_vars_filename: "input_params/base_vars.yml"
@@ -156,6 +158,15 @@ provision_state_success_msg: "provision_state validated"
 provision_state_fail_msg: "Failed. Incorrect provision_state selected. Supported only stateful"
 enable_security_support_success_msg: "enable_security_support validated"
 enable_security_support_fail_msg: "Failed. enable_security_support only accepts boolean values true or false"
+dns_empty_warning_msg: "[WARNING] primary_dns and secondary_dns is empty. DHCP routing in compute nodes for internet access won't be configured. Stop and re-run control_plane.yml, if DHCP routing is required."
+primary_dns_empty_msg: "primary_dns is empty and secondary_dns provided. If one dns entry present, provide primary_dns only."
+primary_dns_success_msg: "primary_dns successfully validated"
+primary_dns_fail_msg: "Failed. Incorrect primary_dns provided in base_vars.yml"
+primary_dns_not_reachable_msg: "Failed. primary_dns is not reachable. Provide valid dns"
+secondary_dns_success_msg: "secondary_dns successfully validated"
+secondary_dns_fail_msg: "Failed. Incorrect secondary_dns provided in base_vars.yml"
+secondary_dns_not_reachable_msg: "[WARNING] secondary_dns is not reachable"
+ping_search_key: "100% packet loss"
 
 # Usage: fetch_sm_inputs.yml
 ib_config_file: "{{ role_path }}/../../input_params/ib_vars.yml"

+ 0 - 1
control_plane/roles/provision_cobbler/files/Dockerfile_rocky

@@ -64,7 +64,6 @@ EXPOSE 69 80 443 25151
 
 VOLUME [ "/var/www/cobbler", "/var/lib/cobbler/backup", "/mnt" ]
 
-RUN systemctl enable httpd
 RUN systemctl enable dhcpd
 
 CMD ["sbin/init"]

+ 29 - 0
control_plane/roles/provision_cobbler/files/cobbler_configurations_rocky.yml

@@ -41,11 +41,40 @@
     shell: echo "LoadModule wsgi_module modules/mod_wsgi_python3.so" >/etc/httpd/conf.d/wsgi.conf
     changed_when: false
 
+  - name: Change http port to 8000 in httpd.conf
+    replace:
+      path: "/etc/httpd/conf/httpd.conf"
+      regexp: '^Listen 80'
+      replace: 'Listen 8000'
+    changed_when: false
+
+  - name: Change http port to 8000 in cobbler.conf
+    replace:
+      path: "/etc/httpd/conf.d/cobbler.conf"
+      regexp: '^<VirtualHost.*'
+      replace: '<VirtualHost *:8000>'
+    changed_when: false
+
+  - name: Make https to listen on port 8008
+    replace:
+      path: "/etc/httpd/conf.d/ssl.conf"
+      regexp: '^Listen 443 https'
+      replace: 'Listen 8008 https'
+    changed_when: false
+
+  - name: Change https port to 8008
+    replace:
+      path: "/etc/httpd/conf.d/ssl.conf"
+      regexp: '^<VirtualHost _default_:443>'
+      replace: '<VirtualHost _default_:8008>'
+    changed_when: false
+
   - name: Enable cobbler
     command: systemctl enable {{ item }}
     with_items:
       - cobblerd
       - tftp
+      - httpd
     changed_when: false
 
   - name: Restart httpd

+ 2 - 2
control_plane/roles/provision_cobbler/files/cobbler_settings

@@ -149,7 +149,7 @@ enable_menu: true
 
 # change this port if Apache is not running plaintext on port
 # 80.  Most people can leave this alone.
-http_port: 80
+http_port: 8000
 
 # kernel options that should be present in every Cobbler installation.
 # kernel options can also be applied at the distro/profile/system
@@ -428,7 +428,7 @@ always_write_dhcp_entries: false
 
 # External proxy - used by: "get-loaders", "reposync", "signature update"
 # Eg: "http://192.168.1.1:8080" (HTTP), "https://192.168.1.1:8443" (HTTPS)
-proxy_url_ext: ""
+proxy_url_ext: "http://ip:port"
 
 # Internal proxy - used by systems to reach Cobbler for templates
 # Eg: proxy_url_int: "http://10.0.0.1:8080"

+ 1 - 1
control_plane/roles/provision_cobbler/files/temp_centos7.ks

@@ -1,7 +1,7 @@
 #version=CENTOS7
 
 # Use network installation
-url --url http://ip/cblr/links/centos-x86_64/
+url --url http://ip:port/cblr/links/centos-x86_64/
 
 # Install OS instead of upgrade
 install

+ 1 - 1
control_plane/roles/provision_cobbler/files/temp_rocky8.ks

@@ -1,7 +1,7 @@
 #version=RHEL8
 
 # Use network installation
-url --url http://ip/cblr/links/rocky-x86_64/
+url --url http://ip:port/cblr/links/rocky-x86_64/
 
 # SELinux configuration
 selinux --disabled

+ 30 - 1
control_plane/roles/provision_cobbler/tasks/dhcp_configure.yml

@@ -20,6 +20,29 @@
     mode: 0775
   tags: install
 
+- name: Configure DHCP routing settings
+  block:
+    - name: Add option routers when dns provided
+      lineinfile:
+        path: "{{ role_path }}/files/dhcp.template"
+        insertafter: "^(.*)range dynamic-bootp"
+        line: "option routers {{ hpc_ip }};"
+
+    - name: Add domain name servers when primary dns only provided
+      lineinfile:
+        path: "{{ role_path }}/files/dhcp.template"
+        insertafter: "^(.*)range dynamic-bootp"
+        line: "option domain-name-servers {{ primary_dns }};"
+      when: secondary_dns | length < 1
+
+    - name: Add domain name servers when primary and secondary dns provided
+      lineinfile:
+        path: "{{ role_path }}/files/dhcp.template"
+        insertafter: "^(.*)range dynamic-bootp"
+        line: "option domain-name-servers {{ primary_dns }}, {{ secondary_dns }};"
+      when: secondary_dns | length > 1
+  when: primary_dns | length > 1
+  
 - name: Assign subnet and netmask
   replace:
     path: "{{ role_path }}/files/dhcp.template"
@@ -70,6 +93,12 @@
     regexp: '^next_server: 127.0.0.1'
     replace: 'next_server: {{ hpc_ip }}'
 
+- name: Assign proxy_url_ext url
+  replace:
+    path: "{{ role_path }}/files/settings.yaml"
+    regexp: '^proxy_url_ext: "http://ip:port"'
+    replace: 'proxy_url_ext: "http://{{ hpc_ip }}:{{ http_port }}"'
+
 - name: Set the server_args for {{ provision_os }} in tftp file
   replace:
     path: "{{ role_path }}/files/tftp"
@@ -97,4 +126,4 @@
         path: "{{ role_path }}/files/tftp"
         regexp: '^        server_args             = -s /var/lib/tftpboot'
         replace: '        server_args             = -s /srv/tftpboot'
-  when: os_supported_leap in mgmt_os 
+  when: os_supported_leap in mgmt_os

+ 4 - 4
control_plane/roles/provision_cobbler/tasks/firewall_settings.yml

@@ -15,16 +15,16 @@
 
 #Tasks for modifying firewall configurations for Cobbler
 
-- name: Permit traffic in default zone on port 80/tcp
+- name: Permit traffic in default zone on port 8000/tcp
   firewalld:
-    port: 80/tcp
+    port: 8000/tcp
     permanent: yes
     state: enabled
   tags: install
 
-- name:  Permit traffic in default zone on port 443/tcp
+- name:  Permit traffic in default zone on port 8008/tcp
   firewalld:
-    port: 443/tcp
+    port: 8008/tcp
     permanent: yes
     state: enabled
   tags: install

+ 4 - 4
control_plane/roles/provision_cobbler/tasks/provision_password.yml

@@ -54,8 +54,8 @@
     - name: Configure kickstart file - IP
       replace:
         path: "{{ role_path }}/files/{{ cobbler_kickstart_file }}"
-        regexp: '^url --url http://ip/cblr/links/centos-x86_64/'
-        replace: url --url http://{{ hpc_ip }}/cblr/links/centos-x86_64/
+        regexp: '^url --url http://ip:port/cblr/links/centos-x86_64/'
+        replace: url --url http://{{ hpc_ip }}:{{ http_port }}/cblr/links/centos-x86_64/
       tags: install
 
     - name: Configure kickstart file - nic
@@ -79,8 +79,8 @@
     - name: Configure kickstart file - IP
       replace:
         path: "{{ role_path }}/files/{{ cobbler_kickstart_file }}"
-        regexp: '^url --url http://ip/cblr/links/rocky-x86_64/'
-        replace: url --url http://{{ hpc_ip }}/cblr/links/rocky-x86_64/
+        regexp: '^url --url http://ip:port/cblr/links/rocky-x86_64/'
+        replace: url --url http://{{ hpc_ip }}:{{ http_port }}/cblr/links/rocky-x86_64/
       tags: install
 
     - name: Configure kickstart file - nic

+ 1 - 0
control_plane/roles/provision_cobbler/vars/main.yml

@@ -57,6 +57,7 @@ rocky_host_nic:
  - ens2f1
  - ens1f0
  - ens1f1
+http_port: 8000
 
 # Usage: cobbler_image.yml
 cobbler_image_name: cobbler

+ 10 - 2
omnia.yml

@@ -19,7 +19,15 @@
   gather_facts: no
   roles:
     - cluster_validation
-    
+
+- name: Validate hostname
+  hosts: manager, login_node
+  gather_facts: false
+  any_errors_fatal: true
+  roles:
+    - hostname_validation
+  tags: freeipa
+
 - name: Validate nfs_node config
   hosts: nfs_node
   gather_facts: false
@@ -223,4 +231,4 @@
 
 - name: Passwordless SSH between manager and compute nodes
   include: control_plane/tools/passwordless_ssh.yml
-  when: hostvars['127.0.0.1']['control_plane_status']
+  when: hostvars['127.0.0.1']['control_plane_status']

+ 5 - 0
omnia_config.yml

@@ -73,3 +73,8 @@ directory_manager_password: ""
 # The IPA server requires an administrative user, named 'admin'. 
 # This user is a regular system account used for IPA server administration
 ipa_admin_password: ""
+
+# Boolean indicating whether login node is requires security features or not
+# It can be set to true or false
+# By default it is set to false indicating security features will not be configured
+enable_secure_login_node: false

+ 42 - 0
omnia_security_config.yml

@@ -0,0 +1,42 @@
+# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved.
+#
+#  Licensed under the Apache License, Version 2.0 (the "License");
+#  you may not use this file except in compliance with the License.
+#  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+---
+
+# Maximum number of consecutive failures before lockout
+# The default value of this variable can't be changed
+# Default: 3
+max_failures: 3
+
+# Period (in seconds) after which the number of failed login attempts is reset
+# Default: 60
+# Min: 30
+# Max: 60
+failure_reset_interval: 60
+
+# Period (in seconds) for which users are locked out 
+# Default: 10
+# Min: 5
+# Max: 10
+lockout_duration: 10
+
+# User sessions that have been idle for a specific period can be ended automatically
+# This variable sets session timeout to 3 minutes (180 seconds) by default
+# Min: 90
+# Max: 180
+session_timeout: 180
+
+# Email address used for sending alerts in case of authentication failure
+# If this variable is left blank, authentication failure alerts will be disabled.
+# Required value
+alert_email_address: ""

+ 12 - 0
roles/cluster_validation/tasks/fetch_grafana_cred.yml

@@ -36,6 +36,18 @@
     grafana_password: '{{ grafana_password }}'
   no_log: True
 
+- block:
+    - name: Save variables of ipa server from Management Station
+      set_fact:
+        ms_ipa_admin_password: '{{ ms_ipa_admin_password }}'
+      no_log: True
+
+    - name: Include ipa server hostname and domain name
+      include_vars: "{{ ipa_secret_file }}"
+  when:
+    - login_node_required
+    - ipa_server_ms
+
 - name: Create ansible vault key
   set_fact:
     vault_key: "{{ lookup('password', '/dev/null chars=ascii_letters') }}"

+ 0 - 85
roles/cluster_validation/tasks/fetch_ipa_password.yml

@@ -1,85 +0,0 @@
-# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved.
-#
-#  Licensed under the Apache License, Version 2.0 (the "License");
-#  you may not use this file except in compliance with the License.
-#  You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
----
-
-- name: Include base_vars of control plane
-  include_vars: "{{ role_path }}/../../control_plane/input_params/base_vars.yml"
-
-- name: Unset ipa server status on MS
-  set_fact:
-    ipa_server_ms: false
-
-- name: Check if ipa server file of MS exists
-  stat:
-    path: "{{ ipa_secret_file }}"
-  register: ms_file_exists
-
-- name: Check if ipa server is installed on MS
-  block:
-    - name: Check login_vars file is encrypted
-      command: cat "{{ role_path }}/../../control_plane/{{ login_vars_filename }}"
-      changed_when: false
-      register: config_content
-      no_log: true
-
-    - name: Decrpyt login_vars.yml
-      command: >-
-        ansible-vault decrypt "{{ role_path }}/../../control_plane/{{ login_vars_filename }}"
-        --vault-password-file "{{ role_path }}/../../control_plane/{{ vault_filename }}"
-      changed_when: false
-      when: "'$ANSIBLE_VAULT;' in config_content.stdout"
-
-    - name: Include variable file login_vars.yml
-      include_vars: "{{ role_path }}/../../control_plane/{{ login_vars_filename }}"
-      no_log: true
-
-    - name: Save variables of ipa server from Management Station
-      set_fact:
-        ms_ipa_admin_password: '{{ ipa_admin_password }}'
-
-    - name: Create ansible vault key
-      set_fact:
-        vault_key: "{{ lookup('password', '/dev/null chars=ascii_letters') }}"
-      when: "'$ANSIBLE_VAULT;' not in config_content.stdout"
-
-    - name: Save vault key
-      copy:
-        dest: "{{ role_path }}/../../control_plane/{{ vault_filename }}"
-        content: |
-          {{ vault_key }}
-        owner: root
-        force: yes
-        mode: "{{ vault_file_perm }}"
-      when: "'$ANSIBLE_VAULT;' not in config_content.stdout"
-
-    - name: Encrypt input config file
-      command: >-
-        ansible-vault encrypt "{{ role_path }}/../../control_plane/{{ login_vars_filename }}"
-        --vault-password-file "{{ role_path }}/../../control_plane/{{ vault_filename }}"
-      changed_when: false
-
-    - name: Update login_vars.yml permission
-      file:
-        path: "{{ role_path }}/../../control_plane/{{ login_vars_filename }}"
-        mode: "{{ vault_file_perm }}"
-
-    - name: Include ipa server hostname and domain name
-      include_vars: "{{ ipa_secret_file }}"
-
-    - name: Set ipa server status on MS
-      set_fact:
-        ipa_server_ms: true
-  when:
-    - enable_security_support
-    - ms_file_exists.stat.exists

+ 43 - 15
roles/cluster_validation/tasks/fetch_password.yml

@@ -63,17 +63,6 @@
       k8s_pod_network_cidr | length < 1 or
       ansible_config_file_path | length < 1
 
-- name: Validate login node parameters when login_node_reqd is set to true
-  fail:
-    msg: "{{ input_config_failure_msg }} for login_node"
-  when:
-    - ( domain_name | length < 1 or
-      realm_name | length < 1 or
-      directory_manager_password | length < 1 or
-      ipa_admin_password | length < 1 ) and
-      login_node_required and
-      not enable_security_support
-
 - name: Assert mariadb_password
   assert:
     that:
@@ -125,6 +114,41 @@
     success_msg: "{{ login_node_required_success_msg }}"
     fail_msg: "{{ login_node_required_fail_msg }}"
 
+- name: Initialize ipa_server_ms
+  set_fact:
+    ipa_server_ms: false
+
+- name: Check if ipa server file of MS exists
+  stat:
+    path: "{{ ipa_secret_file }}"
+  register: ms_file_exists
+
+- name: Set ipa server status on MS
+  set_fact:
+    ipa_server_ms: true
+  when:
+    - enable_security_support
+    - ms_file_exists.stat.exists
+
+- name: Validate login node parameters when login_node_reqd is set to true
+  fail:
+    msg: "{{ input_config_failure_msg }} for login_node"
+  when:
+    - ( domain_name | length < 1 or
+      realm_name | length < 1 or
+      directory_manager_password | length < 1 or
+      ipa_admin_password | length < 1 ) 
+    - login_node_required
+    - not ipa_server_ms
+
+- name: Verify the value of enable_secure_login_node
+  assert:
+    that:
+      - enable_secure_login_node == true or enable_secure_login_node == false
+    success_msg: "{{ secure_login_node_success_msg }}"
+    fail_msg: "{{ secure_login_node_fail_msg }}"
+  when: login_node_required
+
 - name: Login node to contain exactly 1 node
   assert:
     that:
@@ -141,7 +165,7 @@
     fail_msg: "{{ domain_name_fail_msg }}"
   when:
     - login_node_required
-    - not enable_security_support
+    - not ipa_server_ms
 
 - name: Validate the realm name
   assert:
@@ -152,7 +176,7 @@
     fail_msg: "{{ realm_name_fail_msg }}"
   when:
     - login_node_required
-    - not enable_security_support
+    - not ipa_server_ms
 
 - name: Assert directory_manager_password
   assert:
@@ -167,7 +191,7 @@
     fail_msg: "{{ fail_msg_directory_manager_password }}"
   when:
     - login_node_required
-    - not enable_security_support
+    - not ipa_server_ms
 
 - name: Assert ipa_admin_password
   assert:
@@ -182,10 +206,14 @@
     fail_msg: "{{ fail_msg_ipa_admin_password }}"
   when:
     - login_node_required
-    - not enable_security_support
+    - not ipa_server_ms
 
 - name: Encrypt input config file
   command: >-
     ansible-vault encrypt {{ role_path }}/../../{{ config_filename }}
     --vault-password-file {{ role_path }}/../../{{ config_vaultname }}
   changed_when: false
+
+- name: Fetch security inputs
+  include_tasks: fetch_security_inputs.yml
+  when: enable_secure_login_node

+ 65 - 0
roles/cluster_validation/tasks/fetch_security_inputs.yml

@@ -0,0 +1,65 @@
+# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved.
+#
+#  Licensed under the Apache License, Version 2.0 (the "License");
+#  you may not use this file except in compliance with the License.
+#  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+---
+
+- name: Include security variable file security_vars.yml
+  include_vars: "{{ security_vars_filename }}"
+
+- name: Validate max_failures
+  assert:
+    that:
+      - max_failures | int == max_failures_default_value
+    success_msg: "{{ max_failures_success_msg }}"
+    fail_msg: "{{ max_failures_fail_msg }}"
+
+- name: Validate failure_reset_interval
+  assert:
+    that:
+      - failure_reset_interval | int
+      - failure_reset_interval | int <= failure_reset_interval_max_value
+      - failure_reset_interval | int >= failure_reset_interval_min_value
+    success_msg: "{{ failure_reset_interval_success_msg }}"
+    fail_msg: "{{ failure_reset_interval_fail_msg }}"
+
+- name: Validate lockout_duration
+  assert:
+    that:
+      - lockout_duration | int
+      - lockout_duration | int <= lockout_duration_max_value
+      - lockout_duration | int >= lockout_duration_min_value
+    success_msg: "{{ lockout_duration_success_msg }}"
+    fail_msg: "{{ lockout_duration_fail_msg }}"
+
+- name: Validate session_timeout
+  assert:
+    that:
+      - session_timeout | int
+      - session_timeout | int <= session_timeout_max_value
+      - session_timeout | int >= session_timeout_min_value
+    success_msg: "{{ session_timeout_success_msg }}"
+    fail_msg: "{{ session_timeout_fail_msg }}"
+
+- name: Validate alert_email_address
+  assert:
+    that:
+      - email_search_key in alert_email_address
+      - alert_email_address | length < email_max_length
+    success_msg: "{{ alert_email_success_msg }}"
+    fail_msg: "{{ alert_email_fail_msg }}"
+  when: alert_email_address | length > 1
+
+- name: Warning - alert_email_address is empty
+  debug:
+    msg: "{{ alert_email_warning_msg }}"
+  when: alert_email_address | length < 1

+ 36 - 42
roles/cluster_validation/tasks/main.yml

@@ -1,4 +1,4 @@
-#  Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved.
+#  Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved.
 #
 #  Licensed under the Apache License, Version 2.0 (the "License");
 #  you may not use this file except in compliance with the License.
@@ -16,50 +16,47 @@
 - name: Perform validations
   include_tasks: validations.yml
 
-- name: Fetch passwords
-  include_tasks: fetch_password.yml
+- name: Fetch omnia_config.yml inputs
+  include_tasks: fetch_omnia_inputs.yml
 
-- name: Check if omnia is running from AWX
-  block:
-    - name: Initialize variables
-      set_fact:
-        control_plane_status: false
-        powervault_status: false
-        nfs_node_status: false
-
-    - name: Check AWX instance
-      command: awx --version
-      changed_when: false
-      failed_when: false
-      register: awx_version_check
+- name: Fetch control_plane credentials
+  include_tasks: fetch_control_plane_credentials.yml
+  
+- name: Initialize variables
+  set_fact:
+    control_plane_status: false
+    powervault_status: false
+    nfs_node_status: false
 
-    - name: Check AWX hostname
-      command: hostname
-      changed_when: false
-      register: awx_hostname
+- name: Check AWX instance
+  command: awx --version
+  changed_when: false
+  failed_when: false
+  register: awx_version_check
 
-    - name: Set control_plane_status
-      set_fact:
-        control_plane_status: true
-      when:
-        - not awx_version_check.failed
-        - awx_search_key in awx_hostname.stdout
+- name: Check AWX hostname
+  command: hostname
+  changed_when: false
+  register: awx_hostname
 
-    - name: Set NFS node status
-      set_fact:
-        nfs_node_status: true
-      when:
-        - control_plane_status
-        - groups['nfs_node'] | length == 1
+- name: Set control_plane_status
+  set_fact:
+    control_plane_status: true
+  when:
+    - not awx_version_check.failed
+    - awx_search_key in awx_hostname.stdout
 
-    - name: Fetch powervault status
-      include_tasks: fetch_powervault_status.yml
-      when: nfs_node_status
-
-    - name: Initialize ipa server variables
-      include_tasks: fetch_ipa_password.yml
-      when: login_node_required
+- name: Set NFS node status
+  set_fact:
+    nfs_node_status: true
+  when:
+    - control_plane_status
+    - groups['nfs_node'] | length == 1
 
+- name: Fetch powervault status
+  include_tasks: fetch_powervault_status.yml
+  when: nfs_node_status
+  
 - name: omnia.yml runing on host
   block:
     - name: Passwordless SSH status
@@ -94,6 +91,3 @@
         replace: 'log_path = /var/log/omnia.log'
       when: ansible_conf_exists.stat.exists
   when: not control_plane_status
-
-- name: Fetch grafana credentials
-  include_tasks: fetch_grafana_cred.yml

+ 27 - 2
roles/cluster_validation/vars/main.yml

@@ -40,8 +40,10 @@ fail_msg_directory_manager_password: "Failed. Incorrect format provided for dire
 success_msg_ipa_admin_password: "ipa_admin_password successfully validated"
 fail_msg_ipa_admin_password: "Failed. Incorrect format provided for ipa_admin_password"
 input_config_failure_msg: "Input parameters cannot be empty"
-login_node_required_success_msg: "Login_node_required successfully validated"
-login_node_required_fail_msg: "Failed. login_node_required can be either true or false"
+login_node_required_success_msg: "login_node_required successfully validated"
+login_node_required_fail_msg: "Failed. login_node_required should be either true or false"
+secure_login_node_success_msg: "enable_secure_login_node successfully validated"
+secure_login_node_fail_msg: "Failed. enable_secure_login_node should be either true or false"
 
 # Usage: validations.yml
 skip_tag_fail_msg: "Can't skip both slurm and kubernetes"
@@ -67,3 +69,26 @@ login_vars_filename: input_params/login_vars.yml
 vault_filename: input_params/.login_vault_key
 vault_file_perm: '0644'
 ipa_secret_file: "{{ playbook_dir }}/control_plane/roles/control_plane_security/files/.ipavars.yml"
+
+# Usage: fetch_security_inputs.yml
+security_vars_filename: "{{ playbook_dir }}/omnia_security_config.yml"
+max_failures_success_msg: "max_failures successfully validated"
+max_failures_fail_msg: "Failed. Incorrect max_failures value in security_vars.yml"
+failure_reset_interval_success_msg: "failure_reset_interval successfully validated"
+failure_reset_interval_fail_msg: "Failed. Incorrect failure_reset_interval value in security_vars.yml"
+lockout_duration_success_msg: "lockout_duration successfully validated"
+lockout_duration_fail_msg: "Failed. Incorrect lockout_duration value in security_vars.yml"
+session_timeout_success_msg: "session_timeout successfully validated"
+session_timeout_fail_msg: "Failed. Incorrect session_timeout value in security_vars.yml"
+max_failures_default_value: 3
+failure_reset_interval_min_value: 30
+failure_reset_interval_max_value: 60
+lockout_duration_min_value: 5
+lockout_duration_max_value: 10
+session_timeout_min_value: 90
+session_timeout_max_value: 180
+alert_email_success_msg: "alert_email_address successfully validated"
+alert_email_fail_msg: "Failed. Incorrect alert_email_address value in security_vars.yml"
+alert_email_warning_msg: "[WARNING] alert_email_address is empty. Authentication failure alerts won't be configured."
+email_max_length: 320
+email_search_key: "@"

+ 59 - 0
roles/hostname_validation/tasks/main.yml

@@ -0,0 +1,59 @@
+# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved.
+#
+#  Licensed under the Apache License, Version 2.0 (the "License");
+#  you may not use this file except in compliance with the License.
+#  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+---
+
+- name: Verify domain name in hostname
+  block:
+    - name: Fetch the hostname
+      command: hostname -s
+      register: machine_hostname
+      changed_when: false
+
+    - name: Verify the hostname is not blank in hostname
+      fail:
+        msg: " {{ hostname_blank_msg }}"
+      when: machine_hostname.stdout | length < 1
+
+    - name: Validate the host name
+      assert:
+        that:
+          - machine_hostname.stdout is regex(("^(([a-z]|[a-z][a-z0-9\-]*[a-z0-9])\.)*([a-z]|[a-z][a-z0-9\-]*[a-z0-9])$"))
+          - machine_hostname.stdout != "localhost"
+        success_msg: "{{ server_hostname_success }}"
+        fail_msg: "{{ server_hostname_fail }}"
+
+    - name: Fetch the domain name
+      command: hostname -d
+      register: domain_name_set
+      changed_when: false
+
+    - name: Verify the domain name is not blank in hostname
+      fail:
+        msg: " {{ domain_name_blank_msg }}"
+      when: domain_name_set.stdout | length < 1
+
+    - name: Set fact for the domain name in hostname
+      set_fact:
+        ms_domain_name: "{{ domain_name_set.stdout }}"
+
+    - name: Validate the domain name set on the host
+      assert:
+        that:
+          - hostvars['127.0.0.1']['domain_name'] == ms_domain_name
+        success_msg: "{{ server_domain_name_success }}"
+        fail_msg: "{{ server_domain_name_fail }}"
+  when:
+    - hostvars['127.0.0.1']['login_node_required']
+    - hostvars['127.0.0.1']['ipa_server_ms'] and login_node_group in group_names or
+      not hostvars['127.0.0.1']['ipa_server_ms']

+ 22 - 0
roles/hostname_validation/vars/main.yml

@@ -0,0 +1,22 @@
+# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved.
+#
+#  Licensed under the Apache License, Version 2.0 (the "License");
+#  you may not use this file except in compliance with the License.
+#  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+---
+
+domain_name_blank_msg: "Failed. Domain name is not set in hostname. It should be in hostname.domain_name format"
+server_domain_name_success: "Domain name in server hostname validated"
+server_domain_name_fail: "Failed. Domain name set is not same as domain name in omnia_config.yml"
+hostname_blank_msg: "Failed. Domain name is not set in hostname It should have hostname.domain_name format"
+server_hostname_success: "Hostname in server hostname validated"
+server_hostname_fail: "Failed. Hostname set is not valid"
+login_node_group: login_node

+ 12 - 10
roles/login_common/tasks/main.yml

@@ -1,4 +1,4 @@
-#  Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved.
+#  Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved.
 #
 #  Licensed under the Apache License, Version 2.0 (the "License");
 #  you may not use this file except in compliance with the License.
@@ -13,14 +13,16 @@
 #  limitations under the License.
 ---
 
-- name: Add ports of manager and login node to firewall
-  include_tasks: firewall_settings.yml
-  when: hostvars['127.0.0.1']['login_node_required']
+- block:   
+    - name: Add ports of manager and login node to firewall
+      include_tasks: firewall_settings.yml
 
-- name: Enable module idm in Rocky or Centos >= 8.0
-  include_tasks: enable_dnf_module.yml
-  when: hostvars['127.0.0.1']['login_node_required']
+    - name: Enable module idm in Rocky or Centos >= 8.0
+      include_tasks: enable_dnf_module.yml
 
-- name: Update Packages
-  include_tasks: update_package.yml
-  when: hostvars['127.0.0.1']['login_node_required']
+    - name: Update Packages
+      include_tasks: update_package.yml
+  when:
+    - hostvars['127.0.0.1']['login_node_required']
+    - hostvars['127.0.0.1']['ipa_server_ms'] and login_node_group in group_names or
+      not hostvars['127.0.0.1']['ipa_server_ms']

+ 6 - 2
roles/login_common/vars/main.yml

@@ -1,4 +1,4 @@
-#  Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved.
+#  Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved.
 #
 #  Licensed under the Apache License, Version 2.0 (the "License");
 #  you may not use this file except in compliance with the License.
@@ -13,6 +13,9 @@
 #  limitations under the License.
 ---
 
+# Usage: main.yml
+login_node_group: login_node
+
 # Usage: set_fqdn.yml
 etc_hosts_file_dest: /etc/hosts
 file_mode: '0644'
@@ -34,4 +37,5 @@ ntp_port1: "123/udp"
 # Usage: enable_dnf_module.yml
 os_centos: 'centos'
 os_rocky: 'rocky'
-os_version: '8.0'
+os_leap: 'leap'
+os_version: '8.0'

+ 62 - 0
roles/login_node/files/auth_failure_check.yml

@@ -0,0 +1,62 @@
+#  Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved.
+#
+#  Licensed under the Apache License, Version 2.0 (the "License");
+#  you may not use this file except in compliance with the License.
+#  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+---
+
+- name: Authentication failure alert mail
+  hosts: localhost
+  connection: local
+  vars:
+    alert_file_path: /tmp/alerting
+    auth_failure_check_time: 60
+    auth_failure_search_key: "authentication failure"
+    auth_failure_info_file: "{{ alert_file_path }}/auth_failure_{{ ansible_date_time.iso8601_basic_short }}.txt"
+    auth_failure_mail_subject: "Alert - Authentication Failure"
+    auth_failure_mail_body: "Attached the authentication failure report"
+    auth_failure_mail_sender: omnia-alert
+    file_mode: 644
+  tasks:
+    - name: Check auth failure in last {{ auth_failure_check_time }} minutes
+      shell: journalctl -u sshd --since "{{ auth_failure_check_time }} minutes ago" | grep "{{ auth_failure_search_key }}"
+      changed_when: false
+      failed_when: false
+      register: auth_failure_check
+
+    - name: Create alerting log directory
+      file:
+        path: "{{ alert_file_path }}"
+        state: directory
+        mode: "{{ file_mode }}"
+
+    - name: Save the authentication failure info
+      copy:
+        dest: "{{ auth_failure_info_file }}"
+        content: |
+          "{{ auth_failure_check.stdout }}"
+        mode: "{{ file_mode }}"
+      when: auth_failure_search_key in auth_failure_check.stdout
+
+    - name: Sent mail on auth failure
+      community.general.mail:
+        subject: "{{ auth_failure_mail_subject }}"
+        body: "{{ auth_failure_mail_body }}"
+        sender: "{{ auth_failure_mail_sender }}"
+        to: "{{ alert_email_address }}"
+        attach:
+          - "{{ auth_failure_info_file }}"
+      when: auth_failure_search_key in auth_failure_check.stdout
+
+    - name: Delete the authentication failure info file
+      file:
+        path: "{{ auth_failure_info_file }}"
+        state: absent

+ 19 - 0
roles/login_node/handlers/main.yml

@@ -0,0 +1,19 @@
+#  Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved.
+#
+#  Licensed under the Apache License, Version 2.0 (the "License");
+#  you may not use this file except in compliance with the License.
+#  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+---
+
+- name: Restart sshd
+  ansible.builtin.systemd:
+    name: sshd
+    state: restarted

+ 69 - 0
roles/login_node/tasks/configure_alerting.yml

@@ -0,0 +1,69 @@
+#  Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved.
+#
+#  Licensed under the Apache License, Version 2.0 (the "License");
+#  you may not use this file except in compliance with the License.
+#  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+---
+
+- name: Install mailx and postfix
+  package:
+    name: "{{ mail_packages }}"
+    state: present
+
+- name: Start postfix service
+  systemd:
+    name: postfix
+    state: started
+
+- block:
+    - name: Install python3
+      package:
+        name: python3
+        state: present
+
+    - name: Update pip version
+      command: pip3 install --upgrade pip
+      changed_when: true
+
+    - name: Install ansible
+      pip:
+        name: ansible
+        state: present
+        executable: pip3
+  when: os_leap in ansible_distribution | lower
+
+- block:
+    - name: Install ansible
+      package:
+        name: ansible
+        state: present
+  when: os_leap not in ansible_distribution | lower
+
+- name: Install community.general collection
+  command: ansible-galaxy collection install {{ community_general_collection }}
+  changed_when: true
+
+- name: Copy auth_failure_check.yml to login_node
+  copy:
+    src: "{{ role_path }}/files/auth_failure_check.yml"
+    dest: "{{ alerting_file_path }}"
+    mode: "{{ hosts_file_mode }}"
+
+- name: Fetch ansible-playbook path
+  command: whereis ansible-playbook
+  changed_when: false
+  register: ansible_playbook_path
+
+- name: Schedule cron job for alerting
+  cron:
+    name: Auth failure alerting
+    special_time: hourly
+    job: "{{ ansible_playbook_path.stdout.split(' ')[1] }} {{ alerting_file_path }} -e 'alert_email_address={{ hostvars['127.0.0.1']['alert_email_address'] }}'"

+ 15 - 3
roles/login_node/tasks/install_ipa_client.yml

@@ -27,18 +27,30 @@
     state: present
   tags: install
 
-- name: Set hostname of ipa server when MS has ipa server installed
+- name: Set hostname of ipa server when manager node has ipa server installed
   set_fact:
     required_ipa_admin_pwd: "{{ hostvars['127.0.0.1']['ipa_admin_password'] }}"
     required_server_hostname: "{{ hostvars[groups['manager'][0]]['server_hostname'] }}"
     required_domain_name: "{{ hostvars['127.0.0.1']['domain_name'] }}"
   when: not hostvars['127.0.0.1']['ipa_server_ms']
+  no_log: true
 
-- name: Set hostname of ipa server when manager node has ipa server installed
+- name: Set hostname of ipa server when MS has ipa server installed
   set_fact:
     required_ipa_admin_pwd: "{{ hostvars['127.0.0.1']['ms_ipa_admin_password'] }}"
     required_server_hostname: "{{ hostvars['127.0.0.1']['server_hostname'] }}"
     required_domain_name: "{{ hostvars['127.0.0.1']['server_domain'] }}"
+    ms_ip_address: "{{ hostvars['127.0.0.1']['ipaddress'] }}"
+  when: hostvars['127.0.0.1']['ipa_server_ms']
+  no_log: true
+
+- name: Add host name in hosts file
+  lineinfile:
+    dest: "{{ hosts_file_dest }}"
+    line: "{{ ms_ip_address  }} {{ required_server_hostname }}"
+    state: present
+    create: yes
+    mode: "{{ hosts_file_mode }}"
   when: hostvars['127.0.0.1']['ipa_server_ms']
 
 - name: Uninstall client if already installed
@@ -65,4 +77,4 @@
   when:
     - ( ansible_distribution | lower == os_centos ) or
       ( ansible_distribution | lower == os_rocky )
-    - ( ansible_distribution_version >= os_version )
+    - ( ansible_distribution_version >= os_version )

+ 39 - 0
roles/login_node/tasks/install_snoopy.yml

@@ -0,0 +1,39 @@
+#  Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved.
+#
+#  Licensed under the Apache License, Version 2.0 (the "License");
+#  you may not use this file except in compliance with the License.
+#  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+---
+
+- name: Install required packages
+  package:
+    name: "{{ snoopy_packages }}"
+    state: present
+
+- name: Download snoopy package
+  get_url:
+    url: "{{ snoopy_url }}"
+    dest: "{{ snoopy_path }}"
+
+- name: Change permissions
+  file:
+   path: "{{ snoopy_path }}"
+   mode: "{{ snoopy_mode }}"
+
+- name: Run the script
+  shell: sh ./install-snoopy.sh stable
+  args:
+   chdir: "{{ snoopy_path }}"
+  changed_when: false
+
+- name: Enable snoopy
+  command: snoopy-enable
+  changed_when: false

+ 46 - 0
roles/login_node/tasks/ipa_configuration.yml

@@ -0,0 +1,46 @@
+#  Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved.
+#
+#  Licensed under the Apache License, Version 2.0 (the "License");
+#  you may not use this file except in compliance with the License.
+#  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+---
+
+- name: Modify the freeipa global password policy
+  community.general.ipa_pwpolicy:
+    maxfailcount: "{{ hostvars['127.0.0.1']['max_failures'] }}"
+    failinterval: "{{ hostvars['127.0.0.1']['failure_reset_interval'] }}"
+    lockouttime: "{{ hostvars['127.0.0.1']['lockout_duration'] }}"
+    ipa_host: "{{ required_server_hostname }}"
+    ipa_user: "{{ ipa_admin_username }}"
+    ipa_pass: "{{ required_ipa_admin_pwd }}"
+
+- name: Create sysadmin group
+  community.general.ipa_group:
+    name: "{{ sysadmin_user_group }}"
+    description: "{{ sysadmin_group_description }}"
+    state: present
+    ipa_host: "{{ required_server_hostname }}"
+    ipa_user: "{{ ipa_admin_username }}"
+    ipa_pass: "{{ required_ipa_admin_pwd }}"
+    
+- name: Create sysadmin_sudo rule
+  community.general.ipa_sudorule:
+    name: "{{ sysadmin_sudo_rule }}"
+    description: "{{ sysadmin_sudo_rule_description }}"
+    cmdcategory: all        
+    hostcategory: all
+    runasgroupcategory: all
+    runasusercategory: all
+    usergroup:
+      - "{{ sysadmin_user_group }}"
+    ipa_host: "{{ required_server_hostname }}"
+    ipa_user: "{{ ipa_admin_username }}"
+    ipa_pass: "{{ required_ipa_admin_pwd }}"

+ 24 - 8
roles/login_node/tasks/main.yml

@@ -13,14 +13,30 @@
 #  limitations under the License.
 ---
 
-- name: Add ports to firewall to run slurm jobs
-  include_tasks: firewall_settings.yml
-  when:
-    - hostvars['127.0.0.1']['login_node_required']
-    - hostvars[groups['manager'][0]] != hostvars[groups['login_node'][0]]
+- block:
+    - name: Add ports to firewall to run slurm jobs
+      include_tasks: firewall_settings.yml
+    
+    - name: Install ipa client
+      include_tasks: install_ipa_client.yml
 
-- name: Install ipa client
-  include_tasks: install_ipa_client.yml
+    - block:
+        - name: FreeIPA configuration
+          include_tasks: ipa_configuration.yml
+          
+        - name: Install snoopy
+          include_tasks: install_snoopy.yml
+
+        - name: Install psacct/acct
+          include_tasks: user_monitor.yml
+
+        - name: Alert configuration
+          include_tasks: configure_alerting.yml
+          when: hostvars['127.0.0.1']['alert_email_address'] | length > 1
+        
+        - name: Session timeout configuration
+          include_tasks: session_timeout.yml
+      when: hostvars['127.0.0.1']['enable_secure_login_node']
   when:
     - hostvars['127.0.0.1']['login_node_required']
-    - hostvars[groups['manager'][0]] != hostvars[groups['login_node'][0]]
+    - hostvars[groups['manager'][0]] != hostvars[groups['login_node'][0]]

+ 26 - 0
roles/login_node/tasks/session_timeout.yml

@@ -0,0 +1,26 @@
+#  Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved.
+#
+#  Licensed under the Apache License, Version 2.0 (the "License");
+#  you may not use this file except in compliance with the License.
+#  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+---
+
+- name: Modify sshd_config for session timeout
+  ansible.builtin.lineinfile:
+    path: "{{ sshd_conf_file }}"
+    regexp: "{{ item.regexp }}"
+    line: "{{ item.line }}"
+  register: session_timeout
+  notify:
+    - Restart sshd
+  with_items:
+    - { regexp: "^.*ClientAliveInterval*", line: "ClientAliveInterval {{ hostvars['127.0.0.1']['session_timeout'] }}" }
+    - { regexp: "^.*ClientAliveCountMax*", line: "ClientAliveCountMax 0" }

+ 26 - 0
roles/login_node/tasks/user_monitor.yml

@@ -0,0 +1,26 @@
+#  Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved.
+#
+#  Licensed under the Apache License, Version 2.0 (the "License");
+#  you may not use this file except in compliance with the License.
+#  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+---
+
+- name: Install psacct on rocky
+  package:
+    name: "{{ psacct }}"
+    state: present
+  when: os_leap not in ansible_distribution | lower
+
+- name: Install acct on leap
+  package:
+    name: "{{ acct }}"
+    state: present
+  when: os_leap in ansible_distribution | lower

+ 40 - 1
roles/login_node/vars/main.yml

@@ -19,8 +19,47 @@ ipa_client_package:
   - freeipa-client
   - ipa-admintools
 
+# Usage: firewall_settings.yml
 slurmctld_tcp_port: "6817/tcp"
 slurmd_tcp_port: "6818/tcp"
 srun_tcp_port: "60001-63000/tcp"
 slurmctld_udp_port: "6817/udp"
-slurmd_udp_port: "6818/udp"
+slurmd_udp_port: "6818/udp"
+
+# Usage: install_ipa_client.yml
+hosts_file_dest: "/etc/hosts"
+hosts_file_mode: "0644"
+
+# Usage: ipa_configuration.yml
+sysadmin_sudo_rule: sysadmin_sudo
+sysadmin_sudo_rule_description: "Allow users to run sudo commands"
+sysadmin_user_group: sysadmin
+sysadmin_group_description: "User group with sudo permission"
+ipa_admin_username: admin
+
+# Usage: session_timeout.yml
+sshd_conf_file: /etc/ssh/sshd_config
+
+# Usage: configure_alerting.yml
+community_general_collection: "community.general:4.4.0"
+mail_packages:
+  - mailx
+  - postfix
+alerting_file_path: /root/auth_failure_check.yml
+
+# Usage: install_snoopy.yml
+snoopy_packages:
+  - gcc
+  - gzip
+  - make
+  - procps
+  - socat
+  - tar
+  - wget
+snoopy_mode: 0755
+snoopy_url: https://github.com/a2o/snoopy/raw/install/install/install-snoopy.sh
+snoopy_path: /var/lib
+
+# Usage: user_monitor.yml
+psacct: psacct
+acct: acct