id: os_ssh_fips_compliant
title: Limit SSH to FIPS Compliant Connections
discussion: |
  SSH _MUST_ be configured to limit the Ciphers, HostbasedAcceptedAlgorithms, HostKeyAlgorithms, KexAlgorithms, MACs, PubkeyAcceptedAlgorithms, CASignatureAlgorithms to algorithms that are FIPS 140 validated.

  FIPS 140-3 is the current standard for validating that mechanisms used to access cryptographic modules utilize authentication that meet federal requirements.

  Operating systems utilizing encryption _MUST_ use FIPS validated mechanisms for authenticating to cryptographic modules.

  NOTE: For more information on FIPS compliance with the version of SSH included in the macOS, the manual page apple_ssh_and_fips has additional information.

  NOTE: On macOS 15.2 and higher the SSH configuration can be reset to the macOS default by running /usr/libexec/reset-ssh-configuration.
references:
  nist:
    cce:
      macos_27:
        - CCE-XXXXX-X
      macos_26:
        - CCE-95304-2
      macos_15:
        - CCE-94299-5
      macos_14:
        - CCE-92894-5
    800-53r5:
      - AC-17(2)
      - IA-7
      - SC-13
      - SC-8(1)
    800-171r3:
      - 03.13.08
      - 03.13.11
  disa:
    cci:
      - CCI-000068
      - CCI-000803
      - CCI-001453
      - CCI-002421
      - CCI-002450
    srg:
      - SRG-OS-000250-GPOS-00093
      - SRG-OS-000120-GPOS-00061
      - SRG-OS-000424-GPOS-00188
      - SRG-OS-000478-GPOS-00223
      - SRG-OS-000033-GPOS-00014
      - SRG-OS-000396-GPOS-00176
    disa_stig:
      macos_26:
        - APPL-26-000057
      macos_15:
        - APPL-15-000057
      macos_14:
        - APPL-14-000057
    cmmc:
      - AC.L2-3.1.13
      - MP.L2-3.8.6
      - SC.L2-3.13.8
      - SC.L2-3.13.11
  hhs:
    hicp:
      - 6.L.C
platforms:
  macOS:
    '27.0': {}
    '26.0':
      benchmarks:
        - name: disa_stig
          severity: high
      enforcement_info:
        fix:
          shell: |-
            if [ -f /etc/ssh/crypto.conf ] && /usr/bin/grep -q "Include /etc/ssh/crypto.conf" /etc/ssh/ssh_config.d/100-macos.conf 2>/dev/null; then
              /bin/ln -fs /etc/ssh/crypto/fips.conf /etc/ssh/crypto.conf
            fi
            include_dir=$(/usr/bin/awk '/^Include/ {print $2}' /etc/ssh/ssh_config | /usr/bin/tr -d '*')

            fips_ssh_config=("Ciphers aes128-gcm@openssh.com" "HostbasedAcceptedAlgorithms ecdsa-sha2-nistp256,ecdsa-sha2-nistp256-cert-v01@openssh.com" "HostKeyAlgorithms ecdsa-sha2-nistp256-cert-v01@openssh.com,sk-ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp256,sk-ecdsa-sha2-nistp256@openssh.com" "KexAlgorithms ecdh-sha2-nistp256" "MACs hmac-sha2-256-etm@openssh.com,hmac-sha2-256" "PubkeyAcceptedAlgorithms ecdsa-sha2-nistp256,ecdsa-sha2-nistp256-cert-v01@openssh.com,sk-ecdsa-sha2-nistp256-cert-v01@openssh.com" "CASignatureAlgorithms ecdsa-sha2-nistp256,sk-ecdsa-sha2-nistp256@openssh.com")
            for ssh_config in $fips_ssh_config; do
              ssh_setting=$(echo $ssh_config | /usr/bin/cut -d " " -f1)
              /usr/bin/grep -qEi "^$ssh_setting" "${include_dir}01-mscp-ssh.conf" && /usr/bin/sed -i "" "s/^$ssh_setting.*/${ssh_config}/" "${include_dir}01-mscp-ssh.conf" || echo "$ssh_config" >> "${include_dir}01-mscp-ssh.conf"
              for u in $(/usr/bin/dscl . list /users shell | /usr/bin/egrep -v '(^_)|(root)|(/usr/bin/false)' | /usr/bin/awk '{print $1}'); do
                config=$(/usr/bin/sudo -u $u /usr/bin/ssh -Gv . 2>&1)
                configfiles=$(echo "$config" | /usr/bin/awk '/Reading configuration data/ {print $NF}'| /usr/bin/tr -d '\r')
                configarray=( ${(f)configfiles} )
                if ! echo $config | /usr/bin/grep -q -i "$ssh_config" ; then
                  for c in $configarray; do
                    if [[ "$c" == "/etc/ssh/crypto.conf" ]]; then
                      continue
                    fi

                    /usr/bin/sudo -u $u /usr/bin/grep -qEi "^$ssh_setting" "$c" && /usr/bin/sed -i "" "s/^$ssh_setting.*/${ssh_config}/I" "$c"
                    if [[ "$c" =~ ".ssh/config" ]]; then
                      if /usr/bin/grep -qEi "$ssh_setting" "$c" 2> /dev/null; then
                        old_file=$(cat ~$u/.ssh/config)
                        echo "$ssh_config" > ~$u/.ssh/config
                        echo "$old_file" >> ~$u/.ssh/config
                      fi
                    fi
                  done
                fi
              done
            done
    '15.0':
      benchmarks:
        - name: disa_stig
          severity: high
      enforcement_info:
        fix:
          shell: "if [ -f /etc/ssh/crypto.conf ] && /usr/bin/grep -q \"Include /etc/ssh/crypto.conf\" /etc/ssh/ssh_config.d/100-macos.conf 2>/dev/null; then\n  /bin/ln -fs /etc/ssh/crypto/fips.conf /etc/ssh/crypto.conf\nfi\ninclude_dir=$(/usr/bin/awk '/^Include/ {print $2}' /etc/ssh/ssh_config | /usr/bin/tr -d '*')\n\nfips_ssh_config=(\"Ciphers aes128-gcm@openssh.com\" \"HostbasedAcceptedAlgorithms ecdsa-sha2-nistp256,ecdsa-sha2-nistp256-cert-v01@openssh.com\" \"HostKeyAlgorithms ecdsa-sha2-nistp256-cert-v01@openssh.com,sk-ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp256,sk-ecdsa-sha2-nistp256@openssh.com\" \"KexAlgorithms ecdh-sha2-nistp256\" \"MACs hmac-sha2-256-etm@openssh.com,hmac-sha2-256\" \"PubkeyAcceptedAlgorithms ecdsa-sha2-nistp256,ecdsa-sha2-nistp256-cert-v01@openssh.com,sk-ecdsa-sha2-nistp256-cert-v01@openssh.com\" \"CASignatureAlgorithms ecdsa-sha2-nistp256,sk-ecdsa-sha2-nistp256@openssh.com\")\nfor ssh_config in $fips_ssh_config; do\n  ssh_setting=$(echo $ssh_config | /usr/bin/cut -d \" \" -f1)\n  /usr/bin/grep -qEi \"^$ssh_setting\" \"${include_dir}01-mscp-ssh.conf\" && /usr/bin/sed -i \"\" \"s/^$ssh_setting.*/${ssh_config}/\" \"${include_dir}01-mscp-ssh.conf\" || echo \"$ssh_config\" >> \"${include_dir}01-mscp-ssh.conf\"\n  for u in $(/usr/bin/dscl . list /users shell | /usr/bin/egrep -v '(^_)|(root)|(/usr/bin/false)' | /usr/bin/awk '{print $1}'); do\n    config=$(/usr/bin/sudo -u $u /usr/bin/ssh -Gv . 2>&1)\n    configfiles=$(echo \"$config\" | /usr/bin/awk '/Reading configuration data/ {print $NF}'| /usr/bin/tr -d '\\r')\n    configarray=( ${(f)configfiles} )\n    if ! echo $config | /usr/bin/grep -q -i \"$ssh_config\" ; then\n      for c in $configarray; do\n        if [[ \"$c\" == \"/etc/ssh/crypto.conf\" ]]; then\n          continue\n        fi\n \n        /usr/bin/sudo -u $u /usr/bin/grep -qEi \"^$ssh_setting\" \"$c\" && /usr/bin/sed -i \"\" \"s/^$ssh_setting.*/${ssh_config}/I\" \"$c\"\n        if [[ \"$c\" =~ \".ssh/config\" ]]; then\n          if /usr/bin/grep -qEi \"$ssh_setting\" \"$c\" 2> /dev/null; then\n            old_file=$(cat ~$u/.ssh/config)\n            echo \"$ssh_config\" > ~$u/.ssh/config\n            echo \"$old_file\" >> ~$u/.ssh/config\n          fi\n        fi\n      done\n    fi\n  done\ndone"
    enforcement_info:
      check:
        shell: |-
          fips_ssh_config=("Ciphers aes128-gcm@openssh.com" "HostbasedAcceptedAlgorithms ecdsa-sha2-nistp256,ecdsa-sha2-nistp256-cert-v01@openssh.com" "HostKeyAlgorithms ecdsa-sha2-nistp256-cert-v01@openssh.com,sk-ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp256,sk-ecdsa-sha2-nistp256@openssh.com" "KexAlgorithms ecdh-sha2-nistp256" "MACs hmac-sha2-256-etm@openssh.com,hmac-sha2-256" "PubkeyAcceptedAlgorithms ecdsa-sha2-nistp256,ecdsa-sha2-nistp256-cert-v01@openssh.com,sk-ecdsa-sha2-nistp256-cert-v01@openssh.com" "CASignatureAlgorithms ecdsa-sha2-nistp256,sk-ecdsa-sha2-nistp256@openssh.com")
          total=0
          ret="pass"
          for config in $fips_ssh_config; do
            if [[ "$ret" == "fail" ]]; then
              break
            fi
            for u in $(/usr/bin/dscl . list /users shell | /usr/bin/egrep -v '(^_)|(root)|(/usr/bin/false)' | /usr/bin/awk '{print $1}'); do
              sshCheck=$(/usr/bin/sudo -u $u /usr/bin/ssh -G . | /usr/bin/grep -ci "$config")
              if [[ "$sshCheck" == "0" ]]; then
                ret="fail"
                break
              fi
            done
          done
          echo $ret
        result:
          string: pass
      fix:
        shell: |-
          if [ -f /etc/ssh/crypto.conf ] && /usr/bin/grep -q "Include /etc/ssh/crypto.conf" /etc/ssh/ssh_config.d/100-macos.conf 2>/dev/null; then
            /bin/ln -fs /etc/ssh/crypto/fips.conf /etc/ssh/crypto.conf
          fi
          include_dir=$(/usr/bin/awk '/^Include/ {print $2}' /etc/ssh/ssh_config | /usr/bin/tr -d '*')

          fips_ssh_config=("Ciphers aes128-gcm@openssh.com" "HostbasedAcceptedAlgorithms ecdsa-sha2-nistp256,ecdsa-sha2-nistp256-cert-v01@openssh.com" "HostKeyAlgorithms ecdsa-sha2-nistp256-cert-v01@openssh.com,sk-ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp256,sk-ecdsa-sha2-nistp256@openssh.com" "KexAlgorithms ecdh-sha2-nistp256" "MACs hmac-sha2-256-etm@openssh.com,hmac-sha2-256" "PubkeyAcceptedAlgorithms ecdsa-sha2-nistp256,ecdsa-sha2-nistp256-cert-v01@openssh.com,sk-ecdsa-sha2-nistp256-cert-v01@openssh.com" "CASignatureAlgorithms ecdsa-sha2-nistp256,sk-ecdsa-sha2-nistp256@openssh.com")
          for ssh_config in $fips_ssh_config; do
            ssh_setting=$(echo $ssh_config | /usr/bin/cut -d " " -f1)
            /usr/bin/grep -qEi "^$ssh_setting" "${include_dir}01-mscp-ssh.conf" && /usr/bin/sed -i "" "s/^$ssh_setting.*/${ssh_config}/" "${include_dir}01-mscp-ssh.conf" || echo "$ssh_config" >> "${include_dir}01-mscp-ssh.conf"
            for u in $(/usr/bin/dscl . list /users shell | /usr/bin/egrep -v '(^_)|(root)|(/usr/bin/false)' | /usr/bin/awk '{print $1}'); do
              config=$(/usr/bin/sudo -u $u /usr/bin/ssh -Gv . 2>&1)
              configfiles=$(echo "$config" | /usr/bin/awk '/Reading configuration data/ {print $NF}'| /usr/bin/tr -d '\r')
              configarray=( ${(f)configfiles} )
              if ! echo $config | /usr/bin/grep -q -i "$ssh_config" ; then
                for c in $configarray; do
                  if [[ "$c" == "/etc/ssh/crypto.conf" ]]; then
                    continue
                  fi

                  /usr/bin/sudo -u $u /usr/bin/grep -qEi "^$ssh_setting" "$c" && /usr/bin/sed -i "" "s/^$ssh_setting.*/${ssh_config}/I" "$c"
                  if [[ "$c" =~ ".ssh/config" ]]; then
                    if /usr/bin/grep -qEi "$ssh_setting" "$c" 2> /dev/null; then
                      old_file=$(cat ~$u/.ssh/config)
                      echo "$ssh_config" > ~$u/.ssh/config
                      echo "$old_file" >> ~$u/.ssh/config
                    fi
                  fi
                done
              fi
            done
          done
tags:
  - 800-53r5_moderate
  - 800-53r5_high
  - 800-53r5_low
  - 800-171
  - cnssi-1253_low
  - cnssi-1253_high
  - cmmc_lvl2
  - cnssi-1253_moderate
  - hicp_lp
