Skip to main content

Session Manager로 접속하기

SSM으로 접속하는 두 가지 방법은 아래와 같습니다.

  1. EC2 SSM Agent 설치 - EC2 인스턴스 프로필 설정 - SSM 접속
  2. EC2 SSM Agent 설치 - EC2 인스턴스 프로필 설정 - VPC 엔드포인트 생성 - SSM 접속 + log 확인

아래 진행하는 내용은 두 번째 방법입니다.

엔드포인트 보안그룹 인바운드 규칙

note

엔드포인트 생성시 서브넷을 VPC와 서브넷을 선택하게 됩니다. 생성된 엔드포인트는 서브넷 안에 위치하게 됩니다. 따라서 엔드포인트 입장에서 서브넷의 IPv4를 소스 IP로 하는 트래픽이 엔드포인트로 들어오기 때문에 엔드포인트가 위치한 서브넷의 IPv4 CIDR 대역을 허용해주거나 더 큰 범위로 VPC CIDR 대역을 소스 IP로 설정해줍니다.

여러 엔드포인트를 각각의 서브넷에 배포할 경우 인바운드 보안 그룹의 소스 IP를 서브넷의 IPv4로 설정한다면 엔드포인트 개수만큼 보안 그룹을 생성하여 적용해야 합니다. 이러한 경우 운영 오버헤드가 커질 수 있으므로 VPC의 CIDR 대역을 소스 IP로 허용하는 인바운드 규칙을 가지는 보안그룹 하나를 각각의 엔드포인트에 적용할 수 있습니다.

warning

엔드포인트를 배포하지 않고 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)를 선택하여 각각 엔드포인트를 생성해줍니다.

note

👨🏻‍💻 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으로 곧장 접속하는 방법이 추적이 용이하며 보다 안전하다고 할 수 있습니다.

caution

AWS VPC 엔드포인트는 무료가 아니며 시간당 비용이 청구됩니다.

AWS PrivateLink 요금 - Amazon Web Services

플러그인 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

    Step 4: Install SSM Agent for a hybrid environment (Linux)

  • 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 접속 환경을 설정합니다.

terraform/provision-ec2-ssm at master · ghdwlsgur/terraform