2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

lambdaからEC2内のコンテナにSSH, FTP接続する

Posted at

はじめに

lambdaからEC2内のコンテナにSSH、FTP接続したときのメモです。lambdaでpythonのparamikoをインポートしようとすると下記のエラーが出て躓いてましたが、chatGPTの力を借りて解決できました。

libc.musl-x86_64.so.1: cannot open shared object file: No such file or directory

目的

  • 同じVPC内にあるlambdaからEC2内のコンテナにSSH,FTP接続する。
  • pythonのparamikoライブラリを使ってSSH接続する。
  • pythonのftplibライブラリを使ってFTP接続する。

構成図.drawio.png

環境

  • EC2

    • Red Hat Enterprise Linux
    • t2.micro
  • lambda

    • Python 3.9
    • x86_64

EC2

セキュリティグループ

一旦全てのアクセス、全てのポートからアクセス可能に設定しておく。

コンテナ作成

podman network作成

下記のコマンドを実行し、test_networkという名前のpodman networkを作成する。

podman network create test_network

serverコンテナ作成

pure-ftpdをベースに下記のDockerfileから作成する。

# ベースイメージとしてftpdを使用
FROM docker.io/stilliard/pure-ftpd

# arg
ARG PUBLICHOST
ARG USER
ARG PASSWORD

#env
ENV PUBLICHOST=$PUBLICHOST
ENV FTP_USER_NAME=$USER
ENV FTP_USER_PASS=$PASSWORD
ENV FTP_USER_HOME="/home/$USER"

# 必要なパッケージをインストール
RUN apt-get update && \
    apt-get install -y openssh-server && \
    mkdir /var/run/sshd

# パスワードのみでのSSH接続を許可する
RUN sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config
# RUN sed -i 's/#PubkeyAuthentication yes/PubkeyAuthentication yes/' /etc/ssh/sshd_config

# testuserを作成
RUN useradd -m -s /bin/bash "$FTP_USER_NAME" && \
echo "$FTP_USER_NAME:$FTP_USER_PASS" | chpasswd && \
chmod 777 -R "/home/$FTP_USER_NAME"

# SSHサービスを起動するスクリプトを作成
RUN echo '#!/bin/bash\nservice ssh start\nexec "$@"' > /start.sh && chmod +x /start.sh

# SSHとPure-FTPdの両方を起動
CMD ["sh", "-c", "/start.sh && /run.sh -l puredb:/etc/pure-ftpd/pureftpd.pdb -E -j -R -P $PUBLICHOST"]

下記コマンドでコンテナイメージをビルドする。イメージの名前はserverとした。

podman build \
    -t server \
    --build-arg PUBLICHOST=localhost \
    --build-arg USER=testuser \
    --build-arg PASSWORD=password \
    .

下記コマンドでコンテナを起動する。コンテナの名前はserverとした。
SSH用のポート22をホストの2022に、FTP用のポート21をホストの2021にそれぞれマウントする。また、コンテナのipアドレスを10.89.0.6に固定した。

podman run -d --tty \
    --name server \
    --network test_network \
    -p 2021:21 \
    -p 2022:22 \
    -p 30000-30009:30000-30009 \
    --ip 10.89.0.6 \
    --privileged \
    server

clientコンテナ作成(任意)

SSH、FTPをEC2内で確認するためにclientコンテナを作成する。

下記のDockerfileから作成する。

FROM python:3.9-alpine

RUN apk update

RUN apk --update-cache add \
    python3 \
    python3-dev \
    py3-pip \
    gcc \
    g++ \
    curl \
    bash \
    openssh-client

RUN pip install --upgrade pip
RUN pip install --upgrade setuptools

下記コマンドでコンテナイメージをビルドする。イメージの名前はclientとした。

podman build -t client .

下記コマンドでコンテナを起動する。コンテナの名前はclientとした。
また、ipアドレスを10.89.0.5に固定した。

podman run -d --tty \
    --name client \
    --network test_network \
    --ip 10.89.0.5 \
   client

lambda

アクセス権限

IAMロールを修正する。
下記2つポリシーを追加する。

  • AmazonEC2ReadOnlyAccess
  • AWSLambdaVPCAccessExecutionRole

VPC、セキュリティグループ

一旦全てのアクセス、全てのポートからアクセス可能に設定しておく。
VPCはEC2と同じものを設定する。

zipファイル作成

Dockerを使ってzipファイルを作成する。
AWSのlambdaと同じ環境でビルドしないと下記のエラーが出てpythonでparamikoがインポートできなかった。

libc.musl-x86_64.so.1: cannot open shared object file: No such file or directory

AWSのlambda python3.9のイメージ(public.ecr.aws/lambda/python:3.9)が公開されているので、これを使用する。

下記のDockerfileを使用して作成する。ここではpythonのライブラリparamiko, scp, pysftpをインストールしているが、SSH接続するだけならparamikoだけでOK。

FROM public.ecr.aws/lambda/python:3.9

RUN yum install -y gcc libffi-devel python3-devel

RUN mkdir -p /workspace/python-pkg /workspace/python-pkg/pkg

# 必要なファイルをコピー
COPY lambda_function.py /workspace

# pythonのライブラリをインストール
WORKDIR /workspace/python-pkg
RUN pip install paramiko scp pysftp -t .

# pythonライブラリとlambda_function.pyをzipにまとめる
WORKDIR /workspace
RUN zip -r lambda-pkg.zip .

# ENTRYPOINTを上書きする
ENTRYPOINT ["/bin/bash", "-l", "-c", "cp /workspace/lambda-pkg.zip /workspace/pkg/lambda-pkg.zip"]

下記コマンドでコンテナイメージをビルドする。イメージの名前はclientとした。
ビルドする前にローカル環境にlambda_function.pyを作成しておく。

podman build -t make-lambda-pkg .

下記コマンドでコンテナを作成する。
コンテナにマウントするためのpkgディレクトリを作成しておく。
コンテナ起動時にlambdaにアップロードするためのlambda-pkg.zipファイルがマウントされたpkgディレクトリに作成される。

podman run --rm --tty \
    -v $(pwd)/pkg:/workspace/pkg:Z \
    make-lambda-pkg

関数作成

zipファイルをアップロードする。ファイルサイズが10M以上の場合はS3からアップロードする。

lambda_function.pyを下記のように作成する。

import boto3
from ftplib import FTP
import json
import os
import sys

sys.path.append('python-pkg')

import paramiko
import scp 
import pysftp

CUR_DIR = os.getcwd()

def test_ssh():
    # ログイン情報設定
    host = '172.31.15.114' # EC2 private ip
    user = 'testuser'
    password = 'password'
    port=2022

    ssh = paramiko.SSHClient()
    ssh.load_system_host_keys()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())


    # 接続
    try:
        ssh.connect(hostname=host, username=user, password=password, port=port)

        # コマンド実行例
        stdin, stdout, stderr = ssh.exec_command("echo 'Hello from container'")
        output = stdout.read().decode()
        error = stderr.read().decode()

        ssh.close()

        # 結果をログに記録
        print(f"Command Output: {output}")
        print(f"Command Error: {error}")

        return {"statusCode": 200, "body": output}
    except Exception as e:
        print(f"SSH connection failed: {e}")
        return {"statusCode": 500, "body": str(e)}
    finally:
        ssh.close()


def test_ftp():
    # ログイン情報
    host = '172.31.15.114' # EC2 private ip
    user = 'testuser'
    password = 'password'
    port = 2021

    ftp = FTP()
    # 接続
    try:
        ftp.connect(host, port)
        ftp.login(user, password)
        # ホームディレクトリのファイルリストを取得
        ret = ftp.retrlines('LIST')
        return {"result": ret}
    except Exception as e:
        print(f"FTP connection failed: {e}")
        return {"result": f"failed {e}"}
    finally:
        ftp.close() 


def lambda_handler(event, context):
    # ret = test_ftp()
    ret = test_ssh()
    
    return str(ret)

lambdaからEC2内のserverコンテナへのSSH接続

lambda_handler関数のtest_ftp部分をコメントアウトしてテストを実行する。テストイベントはなんでもOK。

def lambda_handler(event, context):
    # ret = test_ftp()
    ret = test_ssh()
    
    return str(ret)

成功していれば、下記のような出力が得られる。

Status: Succeeded
Test Event Name: test_event

Response:
"{'statusCode': 200, 'body': 'Hello from container\\n'}"

lambdaからEC2内のserverコンテナへのFTP接続

lambda_handler関数のtest_ssh部分をコメントアウトしてテストを実行する。テストイベントはなんでもOK。

def lambda_handler(event, context):
    ret = test_ftp()
    # ret = test_ssh()
    
    return str(ret)

成功していれば、下記のような出力が得られる。

Status: Succeeded
Test Event Name: test_event

Response:
"{'result': '226-Options: -l \\n226 0 matches total'}"

clientコンテナ、EC2ホスト、PCからコンテナへのSSH接続

おまけ。
各クライアントからEC2のserverコンテナへSSH接続する方法。
指定するホストやポートが異なるので注意。
ユーザにはserverコンテナのユーザを指定する。

clientコンテナから

ホストにコンテナのIPアドレスを指定。ポートはデフォルトの22。

ssh testuser@10.89.0.6 -p 22

EC2ホストから

ホストにlocalhostを指定。 ポートはコンテナにマウントした2022。

ssh testuser@localhost -p 2022

PCから

ホストにEC2のパブリックIPアドレスを指定。 ポートはコンテナにマウントした2022。EC2のpemファイルも必要。

ssh -i xxxxx.pem testuser@3.22.205.47 -p 2022

おわりに

AWSに慣れていないこともあり、時間がかかってしまいました。lambdaでpythonのライブラリを使用するときは、public.ecr.aws/lambda/python:3.9イメージを使ってビルドするのが良さそうです。

参考

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?