Session Manager로 접속하기
SSM으로 접속하는 두 가지 방법은 아래와 같습니다.
- EC2 SSM Agent 설치 - EC2 인스턴스 프로필 설정 - SSM 접속
- EC2 SSM Agent 설치 - EC2 인스턴스 프로필 설정 - VPC 엔드포인트 생성 - SSM 접속 + log 확인
아래 진행하는 내용은 두 번째 방법입니다.
엔드포인트 보안그룹 인바운드 규칙
엔드포인트 생성시 서브넷을 VPC와 서브넷을 선택하게 됩니다. 생성된 엔드포인트는 서브넷 안에 위치하게 됩니다. 따라서 엔드포인트 입장에서 서브넷의 IPv4를 소스 IP로 하는 트래픽이 엔드포인트로 들어오기 때문에 엔드포인트가 위치한 서브넷의 IPv4 CIDR 대역을 허용해주거나 더 큰 범위로 VPC CIDR 대역을 소스 IP로 설정해줍니다.
여러 엔드포인트를 각각의 서브넷에 배포할 경우 인바운드 보안 그룹의 소스 IP를 서브넷의 IPv4로 설정한다면 엔드포인트 개수만큼 보안 그룹을 생성하여 적용해야 합니다. 이러한 경우 운영 오버헤드가 커질 수 있으므로 VPC의 CIDR 대역을 소스 IP로 허용하는 인바운드 규칙을 가지는 보안그룹 하나를 각각의 엔드포인트에 적용할 수 있습니다.
엔드포인트를 배포하지 않고 EC2에 SSM Agent 설치와 인스턴스 프로필 설정을 통해서도 충분히 SSM으로 접속할 수 있습니다. SSM 접속은 프라이빗 링크를 통하여 EC2 보안그룹에 아무 적용을 받지 않으므로 EC2 보안그룹에 모든 설정이 거부되어 있어도 접속가능합니다.
하지만 위 설정과 더불어 엔드포인트를 통해 접속한다면 다릅니다. 인터페이스 엔드포인트 보안그룹이 엔드포인트가 위치한 VPC CIDR 대역이나 서브넷의 IPv4를 소스 IP로 허용하지 않는다면 SSM 접속이 불가능합니다.
그러면 왜 우리는 엔드포인트를 통해서 SSM 접속을 하려는 것일까요 ? 위의 내용대로라면 엔드포인트 없이도 SSM 접속이 가능하고 엔드포인트를 생성하면 엔드포인트 비용이 추가되며 보안그룹도 설정해줘야 하는데 말입니다.
이는 인터페이스 엔드포인트 서비스를 통해서 다른 AWS 서비스에 접근할 수 있기 때문입니다. 게이트웨이 엔드포인트(S3, DynamoDB)와의 차이점이기도 하죠. EC2 인스턴스 프로필이 적용된 역할에 다른 AWS 서비스에 접근할 수 있도록 충분한 권한만 부여하기만 하면 됩니다.
EC2 Role
- 프라이빗 서브넷 안에 있는 인스턴스에 부여할 역할에는 관리형 정책인 AmazonEC2RoleforSSM정책이 필요합니다. 역할을 생성하고 위 정책을 연결한 후 EC2에 인스턴스 프로필을 적용합니다.
인터넷을 통하여 들어오는 트래픽이 아니기 때문에 NACL을 지나지 않으므로 private 서브넷의 NACL 인바운드 규칙과 아웃바운드 규칙은 모두 거부된 상태입니다.
EC2 VPC 엔드포인트 서비스
SSM을 통한 연결에는 VPC 엔드포인트가 필요한데 생성시 필요한 서비스 속성명은 아래와 같습니다.
- com.amazonaws.ap-southeast-1.ssm
- com.amazonaws.ap-southeast-1.ec2messages
- com.amazonaws.ap-southeast-1.ssmmessages
위 3개의 서비스를 검색한 후 적용하고 프라이빗 EC2 인스턴스가 속한 VPC와 VPC에 적용된 보안 그룹(default)를 선택하여 각각 엔드포인트를 생성해줍니다.
👨🏻💻 AWS CLI Command
- [VPC 리전]에 위치한 [인스턴스 아이디] 접속
aws ssm start-session --target [인스턴스 아이디] --region [VPC 리전]
- 가동중인 인스턴스 아이디 확인하기
aws-run ec2 describe-instances \
 --query 'Reservations[].Instances[].InstanceId' \
 --filters Name=instance-state-code,Values=16
여기까지 완료하였다면 이제부터 SSM을 통해 EC2 인스턴스에 접속이 가능해집니다. 여기서 더 알아볼 것은 인스턴스에 접속한 세션 사용자의 기록을 남기는 것인데 CloudWatch를 사용합니다.
CloudWatch 로그 그룹 생성
- 로그 그룹 이름: /ec2/ssm
Systems Manager 기본 설정 편집
세션 매니저 -> 세션 관리자 -> 기본 설정 편집
- CloudWatch Logging Enable
- Allow only encrypted CloudWatch log groups
- CloudWatch 로그 그룹 Disable
- CloudWatch 로그 그룹 /ec2/ssm 선택
VPC 엔드포인트 생성 (추가 logs):
- 이름 태그: logs-endpoint
- 서비스 범주: AWS 서비스
- 서비스: com.amazonaws.ap-southeast-1.logs
- VPC: EC2가 속한 VPC
- 서브넷: EC2가 속한 가용영역 및 서브넷
- IP 주소 유형: IPv4
- 보안 그룹: VPC Security Group (default) - HTTPS, 보안 그룹 새로 생성시 VPC CIDR 블록 허용
여기까지 진행할 경우 생성된 VPC 엔드포인트 서비스는 아래와 같습니다.
- com.amazonaws.ap-southeast-1.logs(+)
- com.amazonaws.ap-southeast-1.ssm
- com.amazonaws.ap-southeast-1.ec2messages
- com.amazonaws.ap-southeast-1.ssmmessages
여기까지 설정을 완료하셨을 경우 CloudWatch - 로그 그룹 (/ec2/ssm) - 로그 스트림에서 아래와 같이 로그를 확인할 수 있습니다.
{
  "eventVersion": "1.0",
  "eventTime": "2022-07-01T05:37:37Z",
  "awsRegion": "ap-southeast-1",
  "target": {
    "id": "EC2 인스턴스 아이디"
  },
  "userIdentity": {
    "arn": "arn:aws:iam::AWS 어카운트 아이디:user/AWS 계정명"
  },
  "runAsUser": "ssm-user",
  "sessionId": "AWS 계정명-SSM 세션아이디",
  "sessionData": [
    "sh-4.2$ sh-4.2$ ls",
    "[        nl-util-addr",
    "a2p        nm",
    "ac        nohup",
    "acpi_listen       nproc",
    "addr2line       nroff",
    "alias        nsenter",
    "amazon-linux-extras      nslookup",
    "amazon-ssm-agent      nss-policy-check",
    "apropos        nsupdate",
    "ar        numfmt",
    "arch        objcopy",
    "as        objdump",
    "at        od",
    "atq        oldfind",
    "atrm        on_ac_power",
    "attr        open",
    "aulast        openssl",
    "aulastlog       openvt",
    "ausyscall       os-prober",
    "auvirt        p11-kit",
    "awk        package-cleanup",
    "aws        passwd",
    "aws_completer       paste",
    "base64        pathchk",
    "basename       peekfd",
    "bash        perl",
    "bashbug        perl5.16.3",
    "bashbug-64       perlbug",
    "batch        perldoc",
    "bc        perlthanks",
    "bg        pgawk",
    "blkiomon       pgrep",
    "blkparse       pic",
    "blkrawverify       piconv",
    "blktrace       pidstat",
    "bno_plot.py       pinentry",
    "bond2team       pinentry-curses",
    "bootctl        ping",
    "btrace        ping6",
    "btrecord       pinky",
    "btreplay       pip-3",
    "btt        pip-3.7",
    "bunzip2        pip3",
    "busctl        pip3.7",
    "bzcat        pk12util",
    "bzcmp        pkcs1-conv",
    "bzdiff        pkg-config",
    "bzgrep        pkill",
    "bzip2        pl2pm",
    "bzip2recover       pldd",
    "bzless        plymouth",
    "bzmore        pm-is-supported",
    "c++filt        pmap",
    "c2ph        pod2html",
    "ca-legacy       pod2man",
    "cal        pod2text",
    "captoinfo       pod2usage",
    "cat        post-grohtml",
    "catchsegv       powernow-k8-decode",
    "catman        pr",
    "cd        pre-grohtml",
    "centrino-decode       preconv",
    "certutil       printenv",
    "chacl        printf",
    "chage        prlimit",
    "chardetect       prtstat",
    "chattr        ps",
    "chcon        psed",
    "chgrp        psfaddtable",
    "chmem        psfgettable",
    "chmod        psfstriptable",
    "chown        psfxtable",
    "chronyc        pstree",
    "chrt        pstree.x11",
    "chvt        pstruct",
    "cifsiostat       ptx",
    "cksum        pwd",
    "clear        pwdx",
    "cloud-id       pwmake",
    "cloud-init       pwscore",
    "cloud-init-per       pydoc",
    "cmp        pydoc3",
    "cmsutil        pydoc3.7",
    "col        pyrsa-decrypt",
    "colcrt        pyrsa-decrypt-2",
    "colrm        pyrsa-decrypt-bigfile",
    "column        pyrsa-decrypt-bigfile-2",
    "comm        pyrsa-encrypt",
    "command        pyrsa-encrypt-2",
    "consolehelper       pyrsa-encrypt-bigfile",
    "coredumpctl       pyrsa-encrypt-bigfile-2",
    "cp        pyrsa-keygen",
    "cpio        pyrsa-keygen-2",
    "cpupower       pyrsa-priv2pub",
    "crlutil        pyrsa-priv2pub-2",
    "crontab        pyrsa-sign",
    "csh        pyrsa-sign-2",
    "csplit        pyrsa-verify",
    "csslint-0.6       pyrsa-verify-2",
    "curl        pystache",
    "cut        pystache-3",
    "cvtsudoers       pystache-test",
    "date        pystache-test-3",
    "db_archive       python",
    "db_checkpoint       python-config",
    "db_deadlock       python2",
    "db_dump        python2-config",
    "db_dump185       python2.7",
    "db_hotbackup       python2.7-config",
    "db_load        python3",
    "db_log_verify       python3.7",
    "db_printlog       python3.7m",
    "db_recover       pyvenv",
    "db_replicate       pyvenv-3.7",
    "db_stat        quota",
    "db_tuner       quotasync",
    "db_upgrade       ranlib",
    "db_verify       raw",
    "dbus-cleanup-sockets      rdate",
    "dbus-daemon       read",
    "dbus-monitor       readelf",
    "dbus-run-session      readlink",
    "dbus-send       realpath",
    "dbus-test-tool       recode-sr-latin",
    "dbus-update-activation-environment  red",
    "dbus-uuidgen       rename",
    "dc        renice",
    "dd        repo-graph",
    "deallocvt       repo-rss",
    "debuginfo-install      repoclosure",
    "delv        repodiff",
    "df        repomanage",
    "dgawk        repoquery",
    "diff        reposync",
    "diff3        repotrack",
    "dig        reset",
    "dir        resizecons",
    "dircolors       rev",
    "dirname        rm",
    "dmesg        rmail",
    "dnsdomainname       rmail.postfix",
    "domainname       rmdir",
    "dracut        rnano",
    "du        rngtest",
    "dumpkeys       rpcgen",
    "dwp        rpm",
    "easy_install       rpm2cpio",
    "easy_install-2.7      rpmdb",
    "easy_install-3.7      rpmkeys",
    "ec2-metadata       rpmquery",
    "echo        rpmverify",
    "ed        rst2html",
    "egrep        rst2html-3",
    "eject        rst2html-3.7",
    "elfedit        rst2html4-3",
    "enable-ec2-spot-hibernation     rst2html4-3.7",
    "env        rst2html5-3",
    "envsubst       rst2html5-3.7",
    "eqn        rst2latex",
    "ex        rst2latex-3",
    "expand        rst2latex-3.7",
    "expr        rst2man",
    "factor        rst2man-3",
    "fallocate       rst2man-3.7",
    "false        rst2odt",
    "fc        rst2odt-3",
    "fg        rst2odt-3.7",
    "fgconsole       rst2odt_prepstyles",
    "fgrep        rst2odt_prepstyles-3",
    "file        rst2odt_prepstyles-3.7",
    "fincore        rst2pseudoxml",
    "find        rst2pseudoxml-3",
    "find-repos-of-install      rst2pseudoxml-3.7",
    "find2perl       rst2s5",
    "findmnt        rst2s5-3",
    "fipscheck       rst2s5-3.7",
    "fipshmac       rst2xetex",
    "flock        rst2xetex-3",
    "fmt        rst2xetex-3.7",
    "fold        rst2xml",
    "free        rst2xml-3",
    "funzip        rst2xml-3.7",
    "gapplication       rstpep2html",
    "gawk        rstpep2html-3",
    "gdbm_dump       rstpep2html-3.7",
    "gdbm_load       rsync",
    "gdbmtool       rsyslog-recover-qi.pl",
    "gdbus        run-parts",
    "gencat        runcon",
    "genl-ctrl-list       rvi",
    "geoiplookup       rview",
    "geoiplookup6       rvim",
    "geoipupdate       s2p",
    "geqn        sadf",
    "getconf        sar",
    "getent        scl",
    "getfacl        scl_enabled",
    "getfattr       scl_source",
    "getkeycodes       scp",
    "getopt        screen",
    "getopts        script",
    "gettext        scriptreplay",
    "gettext.sh       sdiff",
    "gio        secon",
    "gio-querymodules-64      sed",
    "glib-compile-schemas      seq",
    "gmake        setarch",
    "gneqn        setfacl",
    "gnroff        setfattr",
    "gpasswd        setfont",
    "gpg        setkeycodes",
    "gpg-agent       setleds",
    "gpg-connect-agent      setmetamode",
    "gpg-error       setpriv",
    "gpg-zip        setserial",
    "gpg2        setsid",
    "gpgconf        setterm",
    "gpgparsemail       setup",
    "gpgsplit       setup-nsssysinit",
    "gpgv        setup-nsssysinit.sh",
    "gpgv2        setvtrgb",
    "gpic        sexp-conv",
    "gprof        sftp",
    "grep        sg",
    "groff        sh",
    "grops        sha1sum",
    "grotty        sha224sum",
    "groups        sha256sum",
    "growpart       sha384sum",
    "grub2-amazon-setup      sha512sum",
    "grub2-editenv       show-changed-rco",
    "grub2-file       show-installed",
    "grub2-menulst2cfg      showconsolefont",
    "grub2-mkimage       showkey",
    "grub2-mkpasswd-pbkdf2      shred",
    "grub2-mkrelpath       shuf",
    "grub2-mount       signver",
    "grub2-script-check      sim_lsmplugin",
    "gsettings       simc_lsmplugin",
    "gsoelim        size",
    "gtar        skill",
    "gtbl        slabtop",
    "gtroff        sleep",
    "gunzip        slogin",
    "gzexe        snice",
    "gzip        soelim",
    "h2ph        sort",
    "head        sotruss",
    "hexdump        splain",
    "hibagent       split",
    "hibinit-agent       sprof",
    "host        sqlite3",
    "hostid        ssh",
    "hostname       ssh-add",
    "hostnamectl       ssh-agent",
    "hunspell       ssh-copy-id",
    "i386        ssh-keygen",
    "iconv        ssh-keyscan",
    "id        ssltap",
    "idiag-socket-details      ssm-agent-worker",
    "idn        ssm-cli",
    "igawk        ssm-document-worker",
    "info        ssm-session-logger",
    "infocmp        ssm-session-worker",
    "infokey        stap-merge",
    "infotocap       stap-report",
    "install        stapbpf",
    "ionice        stapdyn",
    "iostat        staprun",
    "ipcalc        stapsh",
    "ipcmk        stat",
    "ipcrm        stdbuf",
    "ipcs        strace",
    "iptables-xml       strace-log-merge",
    "isosize        strings",
    "jobs        strip",
    "join        stty",
    "journalctl       su",
    "jp.py        sudo",
    "jp.py-2        sudoedit",
    "jp.py-2.7       sudoreplay",
    "json_reformat       sum",
    "json_verify       sync",
    "jsonpointer       systemctl",
    "jsonschema       systemd-analyze",
    "kbd_mode       systemd-ask-password",
    "kbdinfo        systemd-cat",
    "kbdrate        systemd-cgls",
    "kernel-install       systemd-cgtop",
    "keyctl        systemd-coredumpctl",
    "kill        systemd-delta",
    "killall        systemd-detect-virt",
    "kmod        systemd-escape",
    "last        systemd-firstboot",
    "lastb        systemd-hwdb",
    "lastcomm       systemd-inhibit",
    "lastlog        systemd-loginctl",
    "lchfn        systemd-machine-id-setup",
    "lchsh        systemd-notify",
    "ld        systemd-nspawn",
    "ld.bfd        systemd-path",
    "ld.gold        systemd-run",
    "ldd        systemd-stdio-bridge",
    "less        systemd-sysv-convert",
    "lessecho       systemd-tmpfiles",
    "lesskey        systemd-tty-ask-password-agent",
    "lesspipe.sh       tabs",
    "lexgrog        tac",
    "link        tail",
    "linux-boot-prober      tapestat",
    "linux32        tar",
    "linux64        taskset",
    "ln        tbl",
    "loadkeys       tcptraceroute",
    "loadunimap       tcsh",
    "locale        teamd",
    "localectl       teamdctl",
    "localedef       teamnl",
    "locate        tee",
    "logger        test",
    "login        tic",
    "loginctl       time",
    "logname        timedatectl",
    "look        timeout",
    "ls        tload",
    "lsattr        toe",
    "lsblk        top",
    "lscpu        touch",
    "lsinitrd       tput",
    "lsipc        tr",
    "lslocks        tracepath",
    "lslogins       tracepath6",
    "lsmcli        traceroute",
    "lsmd        traceroute6",
    "lsmem        troff",
    "lsns        true",
    "lua        truncate",
    "luac        trust",
    "lz4        tset",
    "lz4c        tsort",
    "lz4cat        tty",
    "machinectl       turbostat",
    "mailq        tzselect",
    "mailq.postfix       udevadm",
    "make        ul",
    "makedb        umask",
    "man        umount",
    "mandb        unalias",
    "manpath        uname",
    "mapscrn        uname26",
    "mcookie        unexpand",
    "md5sum        unicode_start",
    "mdig        unicode_stop",
    "mesg        uniq",
    "mkdir        unlink",
    "mkfifo        unlz4",
    "mkinitrd       unshare",
    "mknod        unxz",
    "mktemp        unzip",
    "modutil        unzipsfx",
    "more        update-ca-trust",
    "mount        update-mime-database",
    "mountpoint       updatedb",
    "mpstat        uptime",
    "msgattrib       urlgrabber",
    "msgcat        users",
    "msgcmp        usleep",
    "msgcomm        utmpdump",
    "msgconv        uuidgen",
    "msgen        vdir",
    "msgexec        verify_blkparse",
    "msgfilter       verifytree",
    "msgfmt        vi",
    "msggrep        view",
    "msghack        vim",
    "msginit        vimdiff",
    "msgmerge       vimtutor",
    "msgunfmt       vlock",
    "msguniq        vmstat",
    "mv        w",
    "namei        wait",
    "nano        wall",
    "needs-restarting      watch",
    "neqn        watchgnupg",
    "netstat        wc",
    "nettle-hash       wdctl",
    "nettle-lfib-stream      wget",
    "newaliases       whatis",
    "newaliases.postfix      whereis",
    "newgrp        which",
    "nf-ct-add       whiptail",
    "nf-ct-list       who",
    "nf-exp-add       whoami",
    "nf-exp-delete       write",
    "nf-exp-list       x86_64",
    "nf-log        x86_energy_perf_policy",
    "nf-monitor       xargs",
    "nf-queue       xgettext",
    "nfsiostat-sysstat      xmlcatalog",
    "ngettext       xmllint",
    "nice        xmlwf",
    "nisdomainname       xxd",
    "nl        xz",
    "nl-addr-add       xzcat",
    "nl-addr-delete       xzcmp",
    "nl-addr-list       xzdec",
    "nl-class-add       xzdiff",
    "nl-class-delete       xzegrep",
    "nl-class-list       xzfgrep",
    "nl-classid-lookup      xzgrep",
    "nl-cls-add       xzless",
    "nl-cls-delete       xzmore",
    "nl-cls-list       yes",
    "nl-fib-lookup       ypdomainname",
    "nl-link-enslave       yum",
    "nl-link-ifindex2name      yum-builddep",
    "nl-link-list       yum-config-manager",
    "nl-link-name2ifindex      yum-debug-dump",
    "nl-link-release       yum-debug-restore",
    "nl-link-set       yum-groups-manager",
    "nl-link-stats       yumdownloader",
    "nl-list-caches       zcat",
    "nl-list-sockets       zcmp",
    "nl-monitor       zdiff",
    "nl-neigh-add       zegrep",
    "nl-neigh-delete       zfgrep",
    "nl-neigh-list       zforce",
    "nl-neightbl-list      zgrep",
    "nl-pktloc-lookup      zip",
    "nl-qdisc-add       zipcloak",
    "nl-qdisc-delete       zipgrep",
    "nl-qdisc-list       zipinfo",
    "nl-route-add       zipnote",
    "nl-route-delete       zipsplit",
    "nl-route-get       zless",
    "nl-route-list       zmore",
    "nl-rule-list       znew",
    "nl-tctree-list       zsoelim"
  ]
}
로그 스트림에는 AWS계정이름-세션아이디로 세션을 접속시마다 로그 스트림이 새로 생성되는 것을 확인하실 수 있습니다. 명령어를 실행한 시점부터 관련된 모든 기록 또한 추적할 수 있습니다. SSH 프로토콜을 사용하여 바스티온 호스트를 통해 프라이빗 인스턴스로 접속하는 방법보다 VPC 엔드포인트를 생성하여 SSM으로 곧장 접속하는 방법이 추적이 용이하며 보다 안전하다고 할 수 있습니다.
AWS VPC 엔드포인트는 무료가 아니며 시간당 비용이 청구됩니다.
플러그인 Agent로 접속하기 with gossm
- 
EC2 생성 
- 
EC2 인스턴스 역할 생성 (관리형 정책 적용 AmazonEC2RoleforSSM) 
- 
EC2 인스턴스 프로필 수정 
- 
Linux 플러그인 확인 sudo yum search "find"
- 
Session Manager Plugin Agent 다운로드 curl "[https://s3.amazonaws.com/session-manager-downloads/plugin/latest/linux_64bit/session-manager-plugin.rpm](https://s3.amazonaws.com/session-manager-downloads/plugin/latest/linux_64bit/session-manager-plugin.rpm)" -o "session-manager-plugin.rpm"sudo yum install -y session-manager-plugin.rpm
- 
SSM CLI Tool(gossm) 다운로드 brew tap gjbae1212/gossm
 brew install gossm
 brew upgrade gossm
- 
CLI 실행 후 리전 선택 gossm start
 gossm start -r ap-northeast-2
아래 Terraform은 첫 번째 방법으로 SSM 접속 환경을 설정합니다.