Search

AWS IoT Secure Tunneling 가이드

들어가며

AWS IoT Secure Tunneling 은 외부에서 접속이 불가능한 인바운드 규칙이 적용된 제한된 네트워크 환경에 설치된 디바이스와 양방향 통신을 하기 위한 솔루션입니다.
이 문서에서는 AWS IoT Secure Tunneling 에 대한 기본 지식과 적용 및 사용 방법에 대해 알아보겠습니다.
이 문서는 22년 7월 기준으로 작성되었습니다. 가이드에 대해 변경사항이 있을 수 있습니다.

AWS IoT Secure Tunneling ?

Overview & Background

먼저, 원격 사이트의 디바이스에 문제가 생겼고 지원이 필요한 상황을 생각해보겠습니다.
원격 사이트의 네트워크 제한이 없는 상태라면, 해당 디바이스에 원격으로 쉽게 접속해서 이슈를 간단히 해결할 수 있을 겁니다.
그러나 원격 사이트가 제한된 방화벽으로 접속이 되지 않는 상황이라면, 원격지에 직접 방문하여 이슈를 해결하고, 기타 운영등의 작업을 해야하는데, 이는 매우 비효율적입니다. (현재 제가 쿠버네티스를 On-premise 형태로 제공하는 사업장의 네트워크 환경이 바로 그런 상황입니다.)
위 같이 1)인바운드 트래픽이 제한된 네트워크에 연결된 2)원격지의 디바이스에 문제 해결, 새로운 버전 배포 및 기타 운영 작업을 위해 해당 디바이스에 액세스할 수 있는 방법이 필요하게 되었고, 이 솔루션 중 하나가 AWS IoT Secure Tunneling 입니다.
AWS IoT Secure Tunneling은 AWS IoT에서 관리하는 보안 연결을 통해 원격 디바이스에 양방향 통신을 만들 수 있으며,
이것이 뜻하는 바는 다음과 같습니다.
원격 사이트의 기존 인바운드 방화벽 규칙 수정 필요 없음
원격 사이트의 기존 보안 수준 유지 가능
AWS를 통한 토큰 관리 + 보안터널 사용
사고 대응 및 복구 시간 및 운영 비용을 감소
결과적으로 AWS IoT Secure Tunneling을 사용하면, 제한된 방화벽 뒤에 설치된 원격 디바이스에 액세스할 수 있고, 이를 활용해 원격에서 문제 해결, 업데이트, 운영을 보다 효율적으로 가능케합니다.
또한 2022년 6월 7일부로 서울리전에서 터널 1개당 가격을 80% 인하를 발표하여 비용적 측면으로도 보다 경제적인 방법이 될 수도 있습니다.

단점

위에서 언급한 것들은 모두 장점으로 볼 수 있고, 단점은 다음고 ㅏ같습니다.
엑세스를 시도하는 개발자의 디바이스(개발PC)와 원격 사이트의 디바이스 양쪽에 Local Proxy 애플리케이션이 있어야 함
원격 사이트의 각 디바이스마다 Device Agent가 실행 중이어야 한다
디바이스 수가 많아질 수록 요금이 늘어날 수 있다 (터널 1개는 디바이스 1개에 대한 터널)
수정 사항 또는 이슈 대응이 매번 모든 디바이스에 적용한다면 비효율적일 수 있다.

요금

터널 유지 시간은 최소 1분에서 최대 12시간으로 설정 가능
여러 클라이언트가 생성된 터널 1개를 사용해도 추가 요금 없음
터널 이용 뒤 연결을 끊은 후 이미 열려 있는 터널에 재연결하는데 추가 요금 없음
기존 ap-northeast-2 서울리전 기준 터널 1개당 6$
80% 가격 인하로 1.2$

아키텍처 요약

Concepts & How it works

용어 가이드

Source/Destination

내용 이해의 기본인 Source/Destination를 명확히 파악 필요
Source : 출발지, 엑세스를 시도하는 위치, 관리자 위치
Destination : 목적지/원격지, 엑세스 하려는 위치

Tunnel

Source(출발지) 디바이스와 Destination(목적지) 디바이스 간의 양방향 통신을 가능하게 하는 논리적 경로
이는 AWS IoT Core에서 관리된다.

Source Device

목적지 디바이스에 엑세스를 하기 위한 세션을 시작하는 데 사용하는 디바이스

Destination Device

엑세스하려는 목적지/원격지의 디바이스

Client Access Token (CAT)

새로운 터널이 생성될 때 생성되는 토큰 쌍(Pair)
CAT는 출발지 및 목적지 디바이스에서 보안 터널링 서비스에 연결하는 데 사용
출발지(Source) 토큰
목적지(Destination) 토큰

Client Token

클라이언트에 의해 생성되는 고유한 값
AWS IoT Secure Tunneling이 값을 동일한 터널에 대한 모든 후속 재시도 연결에 사용할 수 있다.
이 값은 선택사항으로, 클라이언트 토큰이 제공되지 않으면 클라이언트 액세스 토큰(CAT)은 동일한 터널에 대해 한 번만 사용할 수 있다. 한 번 사용된 CAT를 사용한 후속 연결 시도는 거부된다.
미확인된 내용으로 현재는 소스 토큰을 한 번 사용하고 나면 만료되어 재발급받아야한다.

LocalProxy

로컬 프록시는 AWS가 제공하는 프록시 애플리케이션으로, WebSocket 연결을 통한 보안 터널링을 사용하며, 양쪽 데이터 스트림을 전달해주는 역할을 합니다.
Source 및 Destination 디바이스에서 실행
보안 터널과 디바이스 애플리케이션 간에 데이터 스트림을 릴레이하는 소프트웨어 프록시
Source 및 Destination이 터널로 연결되기 위해 반드시 실행되어야 하는 애플리케이션
로컬 프록시는 두 가지 모드인 Source 또는 Destination 모드로 실행됩니다.
Source 모드
Source(출발지)에서 실행되는 로컬 프록시의 모드, 즉 엑세스를 시도하는 곳에서 실행되어야하는 모드
로컬 프록시는 TCP 연결을 시작하는 클라이언트 애플리케이션과 동일한 디바이스에서 실행
특정 포트로 들어오는 모든 통신을 터널로 전달
Destination 모드
Destination(목적지)에서 실행되는 로컬 프록시의 모드, 즉 엑세스 대상이 되는 디바이스에서 실행되어야하는 모드
로컬 프록시는 원격 디바이스에서 실행
터널로부터 들어오는 모든 통신을 특정 포트로 전달
또한 터널 멀티플렉싱을 사용하면 생성된 단일 터널에 대해 한 번에 최대 3개 연결이 가능합니다.
아래 Device Agent에 관한 설명이 이어지는데, Local Proxy는 터널을 통한 연결을 WebSocket으로 연결할 뿐, 연결 자체에 MQTT를 사용하지 않습니다.
그말인 즉, MQTT 연결은 실제 터널을 통한 양방향 데이터 통신과는 무관합니다.

Device Agent

Device Agent는 AWS IoT와 연결되고, MQTT 연결을 통해 터널 알림을 수신하는 애플리케이션입니다.
Device Agent가 수행하는 기능은 다음과 같습니다.
Destination 디바이스에서 항상 실행
사전 정의된 AWS MQTT Topic을 구독 : $aws/things/thing-name/tunnels/notify
MQTT Topic을 통해 CAT가 포함된 메시지를 수신하면 Local Proxy 를 Destination 모드로 실행
Device Agent는 직접 구현할 수 있으며, AWS에서 제공하는 코드 일부를 참고하여 구현합니다.
수신 메시지 예시
{ "clientAccessToken": "목적지 CAT", "clientMode": "destination", "region": "ap-northeast-2", "services": ["SSH1"] }
Go
복사

작동 방식

1)Local Proxy 와 Secure Tunnel의 통신 방법,  2)Device Agent의 구현 방식에 대해 설명해야하지만, 여기서는 터널에 양방향 연결이 성립되는 순서만 짚고 넘어가겠습니다.
AWS IoT Secure Tunneling을 통해 양방향 연결이 성립되는 과정을 나타내면 아래와 같습니다.
위 과정을 순서대로 풀어보면,
1.
Destination 디바이스에서는 Device Agent가 실행 중이며, Device Agent는 미리 정의된 MQTT 토픽을 구독 중 ($aws/things/thing-name/tunnels/notify)
2.
AWS Console - AWS IoT Core - 원격작업 - 보안터널 메뉴에서 새 터널을 생성
a.
Tunnel을 생성하면 사용자는 Source(출발지)/Destination(목적지) 토큰 쌍을 받는다(CAT), 이 토큰들은 출발지/목적지 간 인증을 위해 사용
3.
Destination 토큰을 포함한 새 터널이 생성되었음을 알리는 메시지가 발행
4.
Destination 디바이스의 Device Agent(그림에서 AWS IoT Device SDK)가 새 터널 알림 메시지를 MQTT를 통해 수신
5.
 Device Agent가 Destination 토큰을 이용하여 Local Proxy를 Destination 모드로 실행
6.
WSS를 통해 Destination 디바이스와 터널이 연결
7.
사용자는 1)에서 생성된 Source 토큰을 저장
8.
 Source 디바이스에서 Source 토큰을 이용하여 Local Proxy를 Source 모드로 실행
9.
WSS를 통해 Source 디바이스와 터널이 연결 (터널 양방향 연결 완료)
10.
Source 디바이스에서 SSH를 통해 양방향 연결

적용 가이드

사전 조건
AWS IoT Thing 등록
Source 디바이스
Local Proxy가 있어야함
Destination 디바이스
최소한 443포트(TCP)의 아웃바운드 트래픽은 허용되는 환경
정의된 MQTT 토픽을 구독하는 Device Agent가 항상 실행
Local Proxy가 있어야함 (Device Agent에 포함되는 경우도 있다)

LocalProxy, DeviceAgent빌드

목적지 디바이스 내에서 빌드 하지 않는 것이 좋습니다. (cmake가 없으면 설치해야하고, 빌드 과정이 평균 30분에다가, 라이브러리 받는 양이 상당합니다.)
개인적인 경험상 실행파일만 필요할 때는 Local Proxy, Device Agent 모두 도커에 빌드용으로 리눅스 이미지를 띄우고 그 안에서 빌드 후 실행파일을 복사해오는 것강력하게 권장합니다.
또한 Source 디바이스인 개인 로컬 PC에서는Local Proxy를 도커 이미지로 빌드하고 이미지로 실행하는 것이 정신 건강에 좋습니다(…)

Local Proxy 빌드

Build Time 약 30분
Binary Size : 7.5MB
Local Proxy는 사실 Source 디바이스에서 실행할 용도의 도커이미지만 빌드하면 됩니다.
Destination 디바이스에도 Local Proxy가 필요하다고 했는데? 
→ 그 이유는 Device Agent 빌드 과정에서 설명하겠습니다.
다행히도 소스를 다운받아보면 최상위에 docker-build.sh 와 docker-run.sh 스크립트가 있다.
docker-build.sh를 실행하면, localproxy, localproxytest 바이너리가 모두 포함된 이미지를 만들어준다
docker-run.sh를 실행하면 위 이미지를 띄워준다.
docker-build.sh 를 실행하면 이미지 빌드 과정이 로그로 나오는데, 빌드 과정 로그 중간에 로그 output이 중단될 수 있다. (메시지 : output clipped log limit XXMib reached)
로그가 나오지 않더라도 에러와 함께 종료되지 않는다면, 빌드가 계속 진행 중이니 절대 종료하지말고 인내심을 갖고 기다려야한다. 넉넉히 30분이면 빌드가 완료된다.

aws-iot-device-client (Device Agent) 빌드

Build Time : 약 30분
Binary Size : 약 30MB
먼저 이름부터 Device Agent가 아닌 aws-iot-device-client 입니다.
지금까지 설명한 Device Agent는 어디가고 aws-iot-device-client는 무엇인가?
AWS IoT Core / AWS IoT Device Management / AWS IoT Device Defender 기능이 모두 포함된 소프트웨어
Secure Tunneling은 AWS IoT Device Management에 포함되는 내용이며, aws-iot-device-client는 터널링에 필요한 기능이 모두 포함되어 있습니다.  덕분에 Local Proxy 바이너리를 별도로 Destination 디바이스에 두지 않아도 됩니다.
그런데, aws-iot-device-client는 Mac OS에서 빌드 되지 않습니다.
따라서 도커에 빌드용 리눅스 이미지를 띄우고 그 안에서 빌드 후 실행파일을 복사해오는 방법을 사용하며, 아래 가이드대로 빌드를 진행하겠습니다.(더 쉬운 방법이 있을 수 있으나 직접 확인한 방법을 작성해두도록 하겠습니다.)
Build aws-iot-device-client
# amazonlinux 리눅스 이미지 실행 docker run -it amazonlinux:latest /bin/bash # Install prerequisites yum -y update \ && yum -y install \ tar \ bzip2 \ git \ wget \ curl \ sudo \ make \ gcc \ gcc-c++ \ softhsm \ && yum clean all \ && rm -rf /var/cache/yum # Install cppcheck yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm yum --enablerepo=epel install -y cppcheck # Install pre-built CMake (약 10분) curl -sSL https://github.com/Kitware/CMake/releases/download/v3.10.0/cmake-3.10.0.tar.gz -o cmake-3.10.0.tar.gz \ && tar -zxvf cmake-3.10.0.tar.gz \ && cd cmake-3.10.0 \ && ./bootstrap \ && make -j 4 \ && make install # Install OpenSSL 1.1.1 (약 5분) cd /tmp wget https://www.openssl.org/source/openssl-1.1.1n.tar.gz \ && tar -zxvf openssl-1.1.1n.tar.gz \ && cd openssl-1.1.1n \ && ./config \ && make \ && sudo make install # Clone and build Google Test cd /tmp curl -sSL https://github.com/google/googletest/archive/release-1.10.0.tar.gz -o release-1.10.0.tar.gz \ && tar xf release-1.10.0.tar.gz \ && cd googletest-release-1.10.0 \ && cmake -DBUILD_SHARED_LIBS=ON . \ && make \ && cp -a googletest/include/gtest /usr/include/ \ && cp -a googlemock/include/gmock /usr/include/ \ && cp -a lib/* /usr/lib64/ \ && rm -f /tmp/release-1.10.0.tar.gz # Clone and build valgrind (약 5분) cd /tmp wget https://sourceware.org/pub/valgrind/valgrind-3.19.0.tar.bz2 \ && tar jxvf valgrind-3.19.0.tar.bz2 \ && cd valgrind-3.19.0 \ && ./configure \ && make \ && make install # Install Aws Iot Device Sdk Cpp v2 (약 5~10분) mkdir /aws-iot-device-sdk cd /aws-iot-device-sdk mkdir sdk-cpp-workspace \ && cd sdk-cpp-workspace \ && git clone https://github.com/aws/aws-iot-device-sdk-cpp-v2.git \ && cd aws-iot-device-sdk-cpp-v2 \ && git checkout ac3ba3774b031dde1b988e698880d6064d53b9d9 \ && git submodule update --init --recursive \ && cd .. \ && mkdir aws-iot-device-sdk-cpp-v2-build \ && cd aws-iot-device-sdk-cpp-v2-build \ && cmake -DCMAKE_INSTALL_PREFIX="/usr" -DUSE_OPENSSL=ON -DBUILD_DEPS=ON ../aws-iot-device-sdk-cpp-v2 \ && cmake --build . --target install # Building (약 5~10분) cd / git clone https://github.com/awslabs/aws-iot-device-client cd aws-iot-device-client mkdir build cd build cmake ../ cmake --build . --target aws-iot-device-client # 실행파일을 로컬로 복사 docker cp {컨테이너id}:/aws-iot-device-client/build/aws-iot-device-client <원하는 디렉토리>
Docker
복사

설정 및 사용 방법

AWS Thing 등록

Destination 디바이스를 Thing으로 등록
Thing 인증서 및 키 다운로드
디바이스 인증서: *-certificate.pem.crt
퍼블릭 키 파일: *-public.pem.key
프라이빗 키 파일: *-private.pem.key
루트 CA 인증서: AmazonRootCA1.pem
참고 이미지

Destination 디바이스 설정

빌드한 aws-iot-device-client 바이너리를 디바이스로 복사
Thing 인증서와 키 파일을 적당한 위치에 복사
aws-iot-device-client.conf 파일 설정
주요 설정 값
key
value
note
endpoint
아래 명령어로 endpoint 확인 가능 aws iot describe-endpoint --endpoint-type iot:Data-ATS
cert
"{path}/*-certificate.pem.crt"
Thing 디바이스 인증서
key
"{path}/*-private.pem.key"
프라이빗 키 파일
root-ca
"{path}/AmazonRootCA1.pem"
루트 CA 인증서
thing-name
"cj-qa-cluster-la02"
등록한 Thing 이름 (Destination 디바이스)
tunneling
"tunneling": { "enabled": true }
보안 터널링을 사용하려면 true로 설정
aws-iot-device-client.conf 파일 예시
{ "endpoint": "aelv30kqvomx9-ats.iot.ap-northeast-2.amazonaws.com", "cert": "/root/swgong/certs/0b6a7b8937445cc68c4598fc301d319614e5e51ba0a2c34111a8bf51d105664b-certificate.pem.crt", "key": "/root/swgong/certs/0b6a7b8937445cc68c4598fc301d319614e5e51ba0a2c34111a8bf51d105664b-private.pem.key", "root-ca": "/root/swgong/certs/AmazonRootCA1.pem", "thing-name": "cj-qa-cluster-la02", "logging": { "level": "DEBUG", "type": "STDOUT", "file": "/var/log/aws-iot-device-client/aws-iot-device-client.log", "enable-sdk-logging": false, "sdk-log-level": "TRACE", "sdk-log-file": "/var/log/aws-iot-device-client/sdk.log" }, "jobs": { "enabled": false, "handler-directory": "/etc/.aws-iot-device-client/jobs" }, "tunneling": { "enabled": true }, "device-defender": { "enabled": true, "interval": 300 }, "fleet-provisioning": { "enabled": false, "template-name": "", "template-parameters": "", "csr-file": "", "device-key": "" }, "samples": { "pub-sub": { "enabled": false, "publish-topic": "", "publish-file": "/etc/.aws-iot-device-client/pubsub/publish-file.txt", "subscribe-topic": "", "subscribe-file": "/etc/.aws-iot-device-client/pubsub/subscribe-file.txt" } }, "config-shadow": { "enabled": false }, "sample-shadow": { "enabled": false, "shadow-name": "", "shadow-input-file": "", "shadow-output-file": "" } }
YAML
복사
File & Directory Permission 설정
파일 권한 설정
디바이스 인증서: chmod 644
프라이빗 키: chmod 600
루트 CA 인증서: chmod 644
config 파일: chmod 640
디렉토리 권한 설정
디바이스 인증서 / 프라이빗 키 / 루트 CA 인증서 디렉토리: chmod 700
하나의 디렉토리에 위 3개를 모두 넣어두고 권한 설정하기
config 파일 / aws-iot-device-client 바이너리가 있는 디렉토리: chmod 745

Source 디바이스 설정

docker-build.sh를 통해서 Local Proxy 바이너리가 있는 이미지 준비
REPOSITORY TAG IMAGE ID CREATED SIZE aws-iot-securetunneling-localproxy latest caba3cfa2e9e 9 days ago 196MB
Bash
복사

AWS IoT Secure Tunneling 테스트

이제 모든 사전 설정이 완료되었고, 실제 Secure Tunneling 테스트를 해보겠습니다.
1.
Destination 디바이스에서 aws-iot-device-client를 실행
a.
./aws-iot-device-client --config-file aws-iot-device-client.conf
b.
정상적으로 실행되었다면, 아래 로그의 마지막 처럼 Subscribed to tunnel notification topic 이라는 메시지를 확인할 수 있습니다.
[DEBUG] {Retry.cpp}: Retryable function starting, it will retry until success [INFO] {SharedCrtResourceManager.cpp}: Establishing MQTT connection with client id cj-qa-cluster-la02... [INFO] {SharedCrtResourceManager.cpp}: MQTT connection established with return code: 0 [INFO] {SharedCrtResourceManager.cpp}: Shared MQTT connection is ready! [INFO] {Main.cpp}: Provisioning with Secure Elements is disabled [INFO] {Main.cpp}: Config shadow is disabled [INFO] {Main.cpp}: Jobs is disabled [INFO] {Main.cpp}: Secure Tunneling is enabled [INFO] {Main.cpp}: Device Defender is enabled [INFO] {Main.cpp}: Sample shadow is disabled [INFO] {Main.cpp}: Pub Sub is disabled [INFO] {Main.cpp}: Sensor Publish is disabled [INFO] {SharedCrtResourceManager.cpp}: Starting Device Client features. [INFO] {SecureTunnelingFeature.cpp}: Running Secure Tunneling! [INFO] {Main.cpp}: Client base has been notified that Secure Tunneling has started [INFO] {DeviceDefender.cpp}: Starting Device Defender [INFO] {DeviceDefender.cpp}: Device Defender task builder interval: 300 [DEBUG] {DeviceDefender.cpp}: Device Defender task build finished [DEBUG] {DeviceDefender.cpp}: Device Defender StartTask() async called [INFO] {Main.cpp}: Client base has been notified that Device Defender has started [DEBUG] {SecureTunnelingFeature.cpp}: Subscribed to tunnel notification topic
Bash
복사
2.
AWS Console - AWS IoT Core - 원격작업 - 보안터널 메뉴에서 새 터널을 생성
a.
아래를 참고하여 터널 이름, SSH를 사용할 것이므로 SSH등록한 Destination 디바이스를 사물로 선택하고, 터널 유지시간을 설정합니다.
b.
Tunnel을 생성하면 사용자는 Source(출발지)/Destination(목적지) 토큰 쌍을 받는다(CAT), 이 때 꼭 다운로드 해두어야 합니다.
3.
터널 생성이 완료되고, 터널 정보를 확인하면 아래 그림과 같다. 만약 Destination 디바이스의 aws-iot-device-client가 실행 중이고, 터널이 생성된 메시지를 토픽으로부터 수신했다면 대상(Destination) 연결 상태가 연결됨으로 표시됩니다.
a.
a.
그리고, Destination 디바이스의 aws-iot-device-client의 로그를 볼 때, SecureTunnelingContext::OnConnectionComplete 와 같은 메시지가 있다면, Destination 디바이스쪽 터널은 연결 성공이 됐음을 뜻합니다.
[DEBUG] {SecureTunnelingFeature.cpp}: Received MQTT Tunnel Notification [DEBUG] {SecureTunnelingFeature.cpp}: Region=ap-northeast-2, Service=SSH [DEBUG] {SecureTunnelingContext.cpp}: SecureTunnelingContext::OnConnectionComplete [DEBUG] {SecureTunnelingContext.cpp}: SecureTunnelingContext::OnStreamStart
Docker
복사
4.
마지막으로 자신의 로컬 pc에서 ./docker-run.sh -p 5555 명령어로 빌드해 둔 localproxy 이미지를 실행합니다.
a.
실행해서 파일을 살펴보면 localproxy 바이너리가 있는 것을 확인 할 수 있습니다.
b.
아래 명령어로 localproxy를 소스 모드로 실행합니다. -r : 리전 -s : 소스모드로 실행하며, 리스닝 하는 포트 설정 -b : 0.0.0.0 은 컨테이너 밖에서 localhost로 접근하기 위한 것으로 필수로 설 - t : 터널 생성시 다운로드 했던 소스토큰 ./localproxy -r <region> -s <port> -b 0.0.0.0 -t <source_token>
c.
연결이 잘 되면 소스 모드 localproxy는 다음과 같은 로그를 볼 수 있고,
d.
AWS 콘솔에서 터널 정보를 보면 소스 연결 상태가 연결됨으로 바뀐 것을 확인할 수 있습니다.
5.
터널 양방향으로 연결이 확인되면, 로컬 PC의 컨테이너 외부에서 다음 명령어로 접속을 확인해보겠습니다.
a.
ssh -i ~/.ssh/onprem_rsa onprem1@localhost -p 5555
b.
방화벽으로 접속할 수 없었던 외부 서버에 접속이 성공했습니다!

Problem Shooting

aws-iot-device-client(Device Agent) 실행 에러

에러 메세지
해결 방법
MQTT Connection failed with error: aws-c-io: AWS_IO_TLS_ERROR_NEGOTIATION_FAILURE, TLS (SSL) negotiation failed
• ATS 주소 사용 (https://github.com/awslabs/aws-iot-device-client/issues/188) • Amazon Trust Services(ATS) 서명 인증서를 사용하는 엔드포인트로 aws-iot-device-client.conf의 endpoint 값을 변경
MQTT Connection failed with error: libaws-c-mqtt: AWS_ERROR_MQTT_UNEXPECTED_HANGUP, The connection was closed unexpectedly.
• AWS IoT Thing (Destination 디바이스)에 정책 연결 및 설정 필요 • 팀 계정 내에는 테스트할 때 생성해 둔 aws-iot-secure-tunneling 정책을 검색해서 적용 ```json "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "iot:Publish", "iot:Subscribe", "iot:Connect", "iot:Receive" ], "Resource": [ "*" ] }, { "Effect": "Allow", "Action": [ "iot:OpenTunnel", "iot:CloseTunnel", "iot:DescribeTunnel", "iot:ListTunnels" ], "Resource": [ "arn:aws:iot:aws-region:aws-account-id:tunnel/*" ] } ] } ```

Local Proxy 에러 (Source 디바이스 측)

에러 메세지
해결 방법
Could not perform SSL handshake with proxy server: certificate verify failed
로컬에서 바로 local proxy를 수행하지말고 컨테이너를 띄워서 사용하기
[error] Proxy server rejected web socket upgrade request: (HTTP/1.1 400 Bad Request) "Invalid access token: The access token was previously used and can not be used again."
원인 : 한 번 Local Proxy에 소스 모드로 사용된 토큰은 연결 해제후 만료됨 재발급 요청하여 소스토큰을 다시 받는다. (터널이 열려있는 동안 가능) `aws iotsecuretunneling rotate-tunnel-access-token --region <region> --tunnel-id <tunnel-id>` response ```json { "tunnelArn": "arn:aws:iot:ap-northeast-2:902870808158:tunnel/662f58b2-5de7-42fb-8e49-249ee4b130ad", "sourceAccessToken": "AQGAAXjPmWINZ6_vXM.................r_vAAyAxxgjkhcSnXPiZOMViCnjvOaIvuoA8aD9iob1JPeQ==" } ```

참고