[Linux] SSH ์ ‘์† - OTP ์ ์šฉ with Shell Script/Ansible

2026. 1. 30. 00:13ยทOS&Server/Linux

๋น„๋ฐ€๋ฒˆํ˜ธ๋‚˜ ํ‚ค๋ฅผ ์ด์šฉํ•ด ์„œ๋ฒ„์— ์ ‘์†ํ•˜์ง€๋งŒ ์ด๊ฒƒ๋งŒ์œผ๋กœ๋Š” ๋ณด์•ˆ์„ฑ์ด ๋†’์ง€ ์•Š๋‹ค. ๊ทธ๋ž˜์„œ OTP๋ฅผ ์„ค์ •ํ•˜๋ ค๊ณ  ํ•œ๋‹ค.
๋‚˜๋Š” ์—ฌ๋Ÿฌ ๋Œ€ ์„œ๋ฒ„์— ์ผ๊ด„์ ์œผ๋กœ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•ด Shell Script๋‚˜ Ansible์„ ์ด์šฉํ•˜๋ ค๊ณ  ํ•œ๋‹ค. ๋‚ด๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” OS๋Š” Rocky Linux 9.7์ด๋‹ค. Rocky 9๋ฒ„์ „๊ณผ Rocky 8๋ฒ„์ „์€ google authenticator ์„ค์ • ๋ฐฉ๋ฒ•์— ์กฐ๊ธˆ ์ฐจ์ด๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— 8๋ฒ„์ „์€ ๋‹ค๋ฅธ ์ž๋ฃŒ๋ฅผ ์ฐธ๊ณ ํ•˜๊ธธ ๋ฐ”๋ž€๋‹ค.

๋จผ์ € ๋‹จ๊ณ„๋ณ„๋กœ ์„ค๋ช…ํ•˜๊ณ  ๋งˆ์ง€๋ง‰์— Shell Script์™€ Ansible playbook์„ ์ž‘์„ฑํ•˜๊ฒ ๋‹ค.


1๏ธโƒฃํŒจํ‚ค์ง€ ์„ค์น˜

google authenticator๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋‹ค์Œ ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•ด ํ•„์š”ํ•œ ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•˜๋ฉด ๋œ๋‹ค.

sudo dnf insatll epel-release google-authenticator qrencode-libs -y

2๏ธโƒฃGoogle Authenticator ๊ตฌ์„ฑ

ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ–ˆ๋‹ค๋ฉด ๋‹ค์Œ ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•ด์„œ QR์ฝ”๋“œ๋ฅผ ์Šค์บ”ํ•ด ์•ฑ์— ๋“ฑ๋กํ•˜๋ฉด ๋œ๋‹ค.

google-authenticator -t -d -f -r 3 -R 30 -W -s .ssh/google

QR์ฝ”๋“œ๋ฅผ ์Šค์บ”ํ•˜๋ฉด OTP์ฝ”๋“œ๋ฅผ ์ž…๋ ฅํ•˜๋ผ๋Š” ๋ฌธ๊ตฌ๊ฐ€ ๋‚˜์˜ค๋ฏ€๋กœ ์ž…๋ ฅํ•˜๋ฉด ๋œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์œ„ ๋ช…๋ น์„œ ์‹คํ–‰ ์‹œ .ssh ํด๋”๊ฐ€ ์—†๋‹ค๋ฉด ์ƒ์„ฑํ•œ ํ›„ ์‹คํ–‰ํ•˜๋ฉด ๋œ๋‹ค.

3๏ธโƒฃSSH ๊ตฌ์„ฑ

/etc/pam.d/sshd๋ฅผ ์ˆ˜์ •ํ•ด์•ผ ํ•˜๋ฏ€๋กœ ๋ฐฑ์—…๋ณธ์„ ๋งŒ๋“ค์–ด ๋‘๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

cp /etc/pam.d/sshd /etc/pam.d/sshd.$HOSTNAME

๊ทธ๋ฆฌ๊ณ  /etc/pam.d/sshd์— auth required pam_google_authenticator.so secret=${HOME}/.ssh/google์„ ์ถ”๊ฐ€ํ•˜๋ฉด ๋œ๋‹ค. ๋‚˜๋Š” OTP ์ธ์ฆ์„ ํ•œ ํ›„ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•  ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ˆœ์„œ๋กœ ์ž‘์„ฑํ–ˆ๋‹ค.

๋น„๋ฐ€๋ฒˆํ˜ธ ์ž…๋ ฅ ํ›„ OTP ์ธ์ฆ์„ ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด auth include postlogin ๋’ค์— ๋†“์œผ๋ฉด ๋  ๊ฒƒ์ด๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์ด๋ฒˆ์—๋Š” /etc/ssh/sshd_config.d ๊ฒฝ๋กœ์— 10.gauth.conf๋ฅผ ์ƒ์„ฑํ•ด ChallengeResponseAuthentication yes๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด ๋œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ฐ™์€ ๊ฒฝ๋กœ์— 50-redhat.conf์—์„œ ChallengeResponseAuthentication no๋Š” ์ฃผ์„ ์ฒ˜๋ฆฌํ•œ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ sudo systemctl restart sshd๋ฅผ ํ•˜๋ฉด ์„ค์ •์ด ๋๋‚œ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ์ ‘์† ์‹œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ™”๋ฉด์ด ๋‚˜์™€์„œ OTP๋ฅผ ์ž…๋ ฅํ•˜๊ณ  ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด์„œ ์ ‘์†ํ•˜๋ฉด ๋œ๋‹ค.

4๏ธโƒฃShell Script

#!/bin/bash

set -e

# ํŒจํ‚ค์ง€ ์„ค์น˜
sudo dnf install -y epel-release
sudo dnf makecache
sudo dnf install -y google-authenticator qrencode qrencode-libs

# Google Authenticator ์ดˆ๊ธฐํ™” (TOTP, ์žฌ์‚ฌ์šฉ ๊ธˆ์ง€, ์†๋„ ์ œํ•œ, ๋Œ€ํ™”ํ˜• ์—†์ด)
# -f : yes to all questions
# -t : TOTP ์‚ฌ์šฉ
# -d : ๋””๋ ‰ํ† ๋ฆฌ ๋ฐฑ์—…
# -r 3 : ์žฌ์‹œ๋„ 3ํšŒ
# -R 30 : ์žฌ์‹œ๋„ ๊ฐ„๊ฒฉ 30์ดˆ
# -W : ์†๋„ ์ œํ•œ
google-authenticator -t -d -f -r 3 -R 30 -W -s .ssh/google

# PAM ์„ค์ •: ์—†์œผ๋ฉด ์ถ”๊ฐ€
if ! grep -q "^auth required pam_google_authenticator.so" /etc/pam.d/sshd
then
    # password-auth ์ด์ „์— ์‚ฝ์ž…
    sudo sed -i '/^auth\s\+substack\s\+password-auth/i auth required pam_google_authenticator.so secret=${HOME}/.ssh/google' /etc/pam.d/sshd
fi

sudo tee /etc/ssh/sshd_config.d/10.gauth.conf > /dev/null <<'EOF'
ChallengeResponseAuthentication yes
EOF

# SSH ์„ค์ • ์ ์šฉ

sudo sed -i -E 's|^(\s*)ChallengeResponseAuthentication(.*)|\1#ChallengeResponseAuthentication\2|' /etc/ssh/sshd_config.d/50-redhat.conf


# SSHD ์žฌ์‹œ์ž‘
sudo systemctl restart sshd

echo "Google Authenticator ๋ฐ SSH ์„ค์ • ์™„๋ฃŒ."

5๏ธโƒฃAnsible Playbook

- name: Set Multi Factor Authentication
  hosts: inventory๋กœ ์ •์˜ํ•œ ํ˜ธ์ŠคํŠธ ๋ฆฌ์ŠคํŠธ
  become: true
  become_method: sudo

  tasks:
  - name: Install epel-release package
    dnf:
      name: epel-release
      state: present

  - name: Update dnf cache after enabling EPEL
    dnf:
      update_cache: yes

  - name: Install google authentication
    dnf:
      name: google-authenticator
      state: present

  - name: Install qrencode
    dnf:
      name: qrencode
      state: present

  - name: Crete ~/.ssh directory (Optional)
    file:
      path: ~/.ssh
      state: directory
      mode: "0700"

  - name: Create /etc/ssh/sshd_config.d/10.gauth.conf
    copy:
      dest: /etc/ssh/sshd_config.d/10.gauth.conf
      content: |
        ChallengeResponseAuthentication yes
      mode: '0644'

  - name: Configure Google Authenticator
    command:
      cmd: google-authenticator -t -d -f -r 3 -R 30 -W -s .ssh/google
      stdin: -1
    become: true
    become_user: lucy

  - name: Update /etc/pam.d/sshd
    lineinfile:
      path: /etc/pam.d/sshd
      regexp: '^auth\s+required\s+pam_google_authenticator\.so'
      line: "auth required pam_google_authenticator.so secret=${HOME}/.ssh/google"
      insertbefore: '^auth\s+substack\s+password-auth'
      state: present
      backup: true

  - name: Update /etc/ssh/sshd_config.d/50-redhat.conf
    lineinfile:
      path: /etc/ssh/sshd_config.d/50-redhat.conf
      regexp: "^#?ChallengeResponseAuthentication"
      backup: true
      line: "#ChallengeResponseAuthentication no"
      state: present

  - name: restart sshd
    service:
      name: sshd
      state: restarted

์ด๋ ‡๊ฒŒ Shell Script์™€ Ansible playbook์„ ์ž‘์„ฑํ•ด๋ณด์•˜๋‹ค. ๋‘ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋ชจ๋‘ ์ž‘์„ฑํ–ˆ์ง€๋งŒ ์ด ๊ฒฝ์šฐ์—๋Š” Shell Script๋กœ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์ด ๋” ๋‚˜์„ ๊ฒƒ ๊ฐ™๋‹ค.

๊ทธ๋ž˜๋„ Ansible๋กœ ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด google-authenticator ๋ช…๋ น์–ด๋ฅผ ansible๋กœ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์€ ์ถ”์ฒœํ•˜์ง€ ์•Š๋Š”๋‹ค. ์•ฑ์— ๋“ฑ๋ก์„ ํ•ด์•ผ ํ•˜๋Š”๋ฐ ์ด ๊ฒฝ์šฐ์—๋Š” qrcode๊ฐ€ ๋‚˜์˜ค์ง€ ์•Š์•„์„œ ์„œ๋ฒ„์—์„œ ํŒŒ์ผ์„ ์ฐพ์•„ secret code๋ฅผ ์ง์ ‘ ์ž…๋ ฅํ•ด ์•ฑ์— ๋“ฑ๋กํ•ด์•ผ ํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋ฏ€๋กœ google-authenticator ๋ช…๋ น์–ด๋Š” ๋”ฐ๋กœ ์ˆ˜ํ–‰ํ•ด์„œ sshd.service๋ฅผ ์žฌ์‹œ์ž‘ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค.

์ €์ž‘์žํ‘œ์‹œ ๋น„์˜๋ฆฌ ๋ณ€๊ฒฝ๊ธˆ์ง€ (์ƒˆ์ฐฝ์—ด๋ฆผ)

'OS&Server > Linux' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[Linux] Rocky Linux ์„ค์น˜  (0) 2025.12.08
[Linux] Shell Script๋ฅผ ์ด์šฉํ•˜์—ฌ ๋„คํŠธ์›Œํฌ IP ์„ค์ •  (0) 2025.06.25
[Linux] Shell Script๋ฅผ ์ด์šฉํ•˜์—ฌ ํŒจํ‚ค์ง€ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ๋‹ค์šด๋กœ๋“œ  (0) 2025.06.23
[Linux] Shell Script๋ฅผ ์ด์šฉํ•˜์—ฌ Timezone ์„ค์ •  (0) 2025.06.22
[Linux] Shell Script์„ ํ™œ์šฉํ•˜์—ฌ SSH ํ‚ค ๋ณต์‚ฌ  (1) 2025.06.18
'OS&Server/Linux' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€
  • [Linux] Rocky Linux ์„ค์น˜
  • [Linux] Shell Script๋ฅผ ์ด์šฉํ•˜์—ฌ ๋„คํŠธ์›Œํฌ IP ์„ค์ •
  • [Linux] Shell Script๋ฅผ ์ด์šฉํ•˜์—ฌ ํŒจํ‚ค์ง€ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ๋‹ค์šด๋กœ๋“œ
  • [Linux] Shell Script๋ฅผ ์ด์šฉํ•˜์—ฌ Timezone ์„ค์ •
The Engineer, Lucy
The Engineer, Lucy
  • The Engineer, Lucy
    Growing up for My Future๐Ÿ’•
    The Engineer, Lucy
    • Instagram
    • GitHub
  • ์ „์ฒด
    ์˜ค๋Š˜
    ์–ด์ œ
    • ๋ถ„๋ฅ˜ ์ „์ฒด๋ณด๊ธฐ (197)
      • OS&Server (30)
        • Linux (28)
        • WEB | WAS (2)
      • Architecture (9)
      • Cloud (31)
        • AWS (3)
        • GCP (4)
      • Container (12)
        • Docker (4)
        • Kubernetes (8)
      • IaC | DevOps (10)
        • IaC (6)
        • DevOps (4)
      • Observability (6)
      • CS (17)
        • Data Structure (0)
        • Algorithms (1)
        • Operating System (3)
        • Network (11)
        • Database System (2)
      • Coding Test (99)
        • Algorithms (91)
        • SQL (7)
      • ETC (7)
  • ๋ธ”๋กœ๊ทธ ๋ฉ”๋‰ด

    • ํ™ˆ
    • ํƒœ๊ทธ
    • ๋ฐฉ๋ช…๋ก
  • ๊ณต์ง€์‚ฌํ•ญ

  • ๋งํฌ

    • Lucy's Instagram
    • Lucy's GitHub
  • ์ธ๊ธฐ ๊ธ€

  • ํƒœ๊ทธ

    ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค
    Baekjoon
    Kubernetes
    ์˜ค๋ธ”์™„
    ์‰˜ ์Šคํฌ๋ฆฝํŠธ
    docker
    ๋„ˆ๋น„์šฐ์„ ํƒ์ƒ‰
    ๋„คํŠธ์›Œํฌ ๊ธฐ์ดˆ ์ง€์‹
    ๋„์ปค
    ๋„คํŠธ์›Œํฌ
    ์ฝ”๋”ฉํ…Œ์ŠคํŠธ ๊ณต๋ถ€
    Java
    terraform
    ์ž๋ฐ”
    ๋ฐฑ์ค€
    ์…ธ ์Šคํฌ๋ฆฝํŠธ
    programmers
    ํ”„๋กœ๊ทธ๋ž˜๋จธ์Šค
    K8s
    ๋‹ค์ด๋‚˜๋ฏน ํ”„๋กœ๊ทธ๋ž˜๋ฐ
    ํ‹ฐ์Šคํ† ๋ฆฌ์ฑŒ๋ฆฐ์ง€
    ๋ฆฌ๋ˆ…์Šค
    cs ๊ธฐ์ดˆ ์ง€์‹ ์ •๋ฆฌ
    network
    Linux
    bfs
    AWS
    Shell
    ๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ 2๊ธ‰
    Shell Script
  • ์ตœ๊ทผ ๋Œ“๊ธ€

  • ์ตœ๊ทผ ๊ธ€

  • hELLOยท Designed By์ •์ƒ์šฐ.v4.10.3
The Engineer, Lucy
[Linux] SSH ์ ‘์† - OTP ์ ์šฉ with Shell Script/Ansible
์ƒ๋‹จ์œผ๋กœ

ํ‹ฐ์Šคํ† ๋ฆฌํˆด๋ฐ”