본문으로 바로가기
반응형

Intro

AWS Image Builder는 Image recipe에 정의된 정보로 AMI 이미지를 생성해주는 골든 이미지 관리 서비스 입니다. 골든 이미지를 생성해주기위해 Image Builder는 SSM(Systems Manager)을 사용하여 필요한 작업들을 지시하게 됩니다.

이 과정에서 Builder Instance 그리고 Test Instance를 생성하고 종료하는 과정을 거치는데, 사용자가 의도한 내용이 아닌 순수 Image Builder의 역할을 하기위해 OS내부에 했던 작업들과 보안을 고려한 Clean up하는 스크립트를 최종적으로 수행하게 됩니다.

하지만 경우에 따라서 Clean up을 생략해야하는 경우가 발생하는데요. 오늘은 Image Builder가 Clean up과정에서 어떤 작업을 수행하고 생략하기 위해 어떻게 해야하는지 알아 보도록 하겠습니다.

Image Builder의 Clean Up을 생략하려는 이유

저는 분명히 Image recipe를 통해서 .ssh폴더에 필요한 autorized_keys 를 넣어두었는데 실제 생성된 이미지를 확인해보면 autorized_keys가 사라지는 현상이 발생하였습니다. 확인을 해보니 Clean UP작업 중에 .ssh 폴더를 초기화하는 부분이 들어가 있었습니다.

Image Builder의 Clean Up 작업

Image Builder가 AMI이미지를 생성하기위해 최종적으로 Test Image Instance를 띄우고 아래와 같이 Clean up 스크립트를 수행하게 됩니다.

  • cleanup_cloudinit_files
  • cleanup_instance_files
  • cleanup_ssh_files
  • cleanup_toe_files
  • cleanup_ssm_log_files
#!/bin/bash
if [[ ! -f {{workingDirectory}}/perform_cleanup ]]; then
    echo "Skipping cleanup"
    exit 0
else
    sudo rm -f {{workingDirectory}}/perform_cleanup
fi

function cleanup() {
    FILES=("$@")
    for FILE in "${FILES[@]}"; do
        if [[ -f "$FILE" ]]; then
            echo "Deleting $FILE";
            sudo shred -zuf $FILE;
        fi;
        if [[ -f $FILE ]]; then
            echo "Failed to delete '$FILE'. Failing."
            exit 1
        fi;
    done
};

# Clean up for cloud-init files
CLOUD_INIT_FILES=(
    "/etc/sudoers.d/90-cloud-init-users"
    "/etc/locale.conf"
    "/var/log/cloud-init.log"
    "/var/log/cloud-init-output.log"
)
if [[ -f {{workingDirectory}}/skip_cleanup_cloudinit_files ]]; then
    echo "Skipping cleanup of cloud init files"
else
    echo "Cleaning up cloud init files"
    cleanup "${CLOUD_INIT_FILES[@]}"
    if [[ $( sudo find /var/lib/cloud -type f | sudo wc -l ) -gt 0 ]]; then
        echo "Deleting files within /var/lib/cloud/*"
        sudo find /var/lib/cloud -type f -exec shred -zuf {} \;
    fi;

    if [[ $( sudo ls /var/lib/cloud | sudo wc -l ) -gt 0 ]]; then
        echo "Deleting /var/lib/cloud/*"
        sudo rm -rf /var/lib/cloud/* || true
    fi;
fi;

# Clean up for temporary instance files
INSTANCE_FILES=(
    "/etc/.updated"
    "/etc/aliases.db"
    "/etc/hostname"
    "/var/lib/misc/postfix.aliasesdb-stamp"
    "/var/lib/postfix/master.lock"
    "/var/spool/postfix/pid/master.pid"
    "/var/.updated"
    "/var/cache/yum/x86_64/2/.gpgkeyschecked.yum"
)
if [[ -f {{workingDirectory}}/skip_cleanup_instance_files ]]; then
    echo "Skipping cleanup of instance files"
else
    echo "Cleaning up instance files"
    cleanup "${INSTANCE_FILES[@]}"
fi;

# Clean up for ssh files
SSH_FILES=(
    "/etc/ssh/ssh_host_rsa_key"
    "/etc/ssh/ssh_host_rsa_key.pub"
    "/etc/ssh/ssh_host_ecdsa_key"
    "/etc/ssh/ssh_host_ecdsa_key.pub"
    "/etc/ssh/ssh_host_ed25519_key"
    "/etc/ssh/ssh_host_ed25519_key.pub"
    "/root/.ssh/authorized_keys"
)
if [[ -f {{workingDirectory}}/skip_cleanup_ssh_files ]]; then
    echo "Skipping cleanup of ssh files"
else
    echo "Cleaning up ssh files"
    cleanup "${SSH_FILES[@]}"
    USERS=$(ls /home/)
    for user in $USERS; do
        echo Deleting /home/"$user"/.ssh/authorized_keys;
        sudo find /home/"$user"/.ssh/authorized_keys -type f -exec shred -zuf {} \;
    done
    for user in $USERS; do
        if [[ -f /home/"$user"/.ssh/authorized_keys ]]; then
            echo Failed to delete /home/"$user"/.ssh/authorized_keys;
            exit 1
        fi;
    done;
fi;

# Clean up for instance log files
INSTANCE_LOG_FILES=(
    "/var/log/audit/audit.log"
    "/var/log/boot.log"
    "/var/log/dmesg"
    "/var/log/cron"
)
if [[ -f {{workingDirectory}}/skip_cleanup_ssh_files ]]; then
    echo "Skipping cleanup of instance log files"
else
    echo "Cleaning up instance log files"
    cleanup "${INSTANCE_LOG_FILES[@]}"
fi;

# Clean up for TOE files
if [[ -f {{workingDirectory}}/skip_cleanup_toe_files ]]; then
    echo "Skipping cleanup of TOE files"
else
    echo "Cleaning TOE files"
    if [[ $( sudo find {{workingDirectory}}/TOE_* -type f | sudo wc -l) -gt 0 ]]; then
        echo "Deleting files within {{workingDirectory}}/TOE_*"
        sudo find {{workingDirectory}}/TOE_* -type f -exec shred -zuf {} \;
    fi
    if [[ $( sudo find {{workingDirectory}}/TOE_* -type f | sudo wc -l) -gt 0 ]]; then
        echo "Failed to delete {{workingDirectory}}/TOE_*"
        exit 1
    fi
    if [[ $( sudo find {{workingDirectory}}/TOE_* -type d | sudo wc -l) -gt 0 ]]; then
        echo "Deleting {{workingDirectory}}/TOE_*"
        sudo rm -rf {{workingDirectory}}/TOE_*
    fi
    if [[ $( sudo find {{workingDirectory}}/TOE_* -type d | sudo wc -l) -gt 0 ]]; then
        echo "Failed to delete {{workingDirectory}}/TOE_*"
        exit 1
    fi
fi

# Clean up for ssm log files
if [[ -f {{workingDirectory}}/skip_cleanup_ssm_log_files ]]; then
    echo "Skipping cleanup of ssm log files"
else
    echo "Cleaning up ssm log files"
    if [[ $( sudo find /var/log/amazon/ssm -type f | sudo wc -l) -gt 0 ]]; then
        echo "Deleting files within /var/log/amazon/ssm/*"
        sudo find /var/log/amazon/ssm -type f -exec shred -zuf {} \;
    fi
    if [[ $( sudo find /var/log/amazon/ssm -type f | sudo wc -l) -gt 0 ]]; then
        echo "Failed to delete /var/log/amazon/ssm"
        exit 1
    fi
    if [[ -d "/var/log/amazon/ssm" ]]; then
        echo "Deleting /var/log/amazon/ssm/*"
        sudo rm -rf /var/log/amazon/ssm
    fi
    if [[ -d "/var/log/amazon/ssm" ]]; then
        echo "Failed to delete /var/log/amazon/ssm"
        exit 1
    fi
fi

if [[ $( sudo find /var/log/sa/sa* -type f | sudo wc -l ) -gt 0 ]]; then
    echo "Deleting /var/log/sa/sa*"
    sudo shred -zuf /var/log/sa/sa*
fi
if [[ $( sudo find /var/log/sa/sa* -type f | sudo wc -l ) -gt 0 ]]; then
    echo "Failed to delete /var/log/sa/sa*"
    exit 1
fi

if [[ $( sudo find /var/lib/dhclient/dhclient*.lease -type f | sudo wc -l ) -gt 0 ]]; then
        echo "Deleting /var/lib/dhclient/dhclient*.lease"
        sudo shred -zuf /var/lib/dhclient/dhclient*.lease
fi
if [[ $( sudo find /var/lib/dhclient/dhclient*.lease -type f | sudo wc -l ) -gt 0 ]]; then
        echo "Failed to delete /var/lib/dhclient/dhclient*.lease"
        exit 1
fi

if [[ $( sudo find /var/tmp -type f | sudo wc -l) -gt 0 ]]; then
        echo "Deleting files within /var/tmp/*"
        sudo find /var/tmp -type f -exec shred -zuf {} \;
fi
if [[ $( sudo find /var/tmp -type f | sudo wc -l) -gt 0 ]]; then
        echo "Failed to delete /var/tmp"
        exit 1
fi
if [[ $( sudo ls /var/tmp | sudo wc -l ) -gt 0 ]]; then
        echo "Deleting /var/tmp/*"
        sudo rm -rf /var/tmp/*
fi

# Shredding is not guaranteed to work well on rolling logs

if [[ -f "/var/lib/rsyslog/imjournal.state" ]]; then
        echo "Deleting /var/lib/rsyslog/imjournal.state"
        sudo shred -zuf /var/lib/rsyslog/imjournal.state
        sudo rm -f /var/lib/rsyslog/imjournal.state
fi

if [[ $( sudo ls /var/log/journal/ | sudo wc -l ) -gt 0 ]]; then
        echo "Deleting /var/log/journal/*"
        sudo find /var/log/journal/ -type f -exec shred -zuf {} \;
        sudo rm -rf /var/log/journal/*
fi

sudo touch /etc/machine-id

Image Builder의 Clean Up을 생략하는 방법

이미 눈치 채신 분들도 있겠지만 위에서 보여드린 Clean Up 스크립트를 자세히보면 skip 이라고 되어있는 부분이 있습니다. 즉 .ssh 를 초기화 시키고 싶지 않다고 하면 workingDirectory에 skip_cleanup_ssh_files이라는 파일을 생성해 두면 됩니다.

  • skip_cleanup_cloudinit_files
  • skip_cleanup_instance_files
  • skip_cleanup_ssh_files
  • skip_cleanup_toe_files
  • skip_cleanup_ssm_log_files

Clean Up전체를 수행하고 싶지 않다고하면 perform_cleanup 이라는 파일을 workingDirectory에 생성하면 됩니다.

workingDirecotry경로가 어디이고 언제 Skip 파일을 생성하면 되나요?

workingDirectory는 image recipe를 생성할때 정의하는 작업 경로 인데요. 기본 값이 /tmp 이며 변경이 가능합니다.

skip 파일은 image recipe가 수행하는 component에서 생성하면 되는데요. component는 실제로 골든이미지에서 수행할 작업들은 yaml 형태로 정의해 놓는 설정 파일 이며 쉘스크립트를 수행할 수 있습니다. 여기서 skip 파일을 생성하는 명령어 touch skip_cleanup_ssh_files 를 추가를 합니다.

  • components 예시
name: BuildAMIByAnsiblegDocument
description: This is build by ansible
schemaVersion: 1.0

constants:

phases:
  - name: skip clean up
    steps:
      - name: skipCleanUp
        action: ExecuteBash
        inputs:
          commands:
            - touch skip_cleanup_ssh_files

마치며..

오늘 같이 일하는 동료에게 이런말을 들었습니다. “AWS는 갑질이 너무 심한거같다” 이게 어떤 의미에서 갑질이냐고 물어보니 2가지가 있었는데요.

첫째 Document 정리가 명확하지 않아서 따로 정리해야한다. 둘째 서비스자체가 고객이 스터디를 해야하는 구조로 되어있다.

기존 레거시 환경에서의 벤더도 비슷하지 않냐라는 질문에 들은 답변은 살짝 충격이었습니다. 자세히 말씀드릴 수는 없지만 정리해보면 레거시환경의 벤더들은 도큐먼트를 고객에게 맞춰서 포트폴리오 형태로 제공해주고 서비스 자체도 고객이 최대한 사용하기 쉽게 돼어있다 라는 것이었습니다.

저도 오늘 동료에게 들은 2가지 AWS갑질(?) 때문에 호되게 당한 하루 였습니다. Document에도 정확히 가이드 되고 있지 않고 있으며 왜 이렇게 어렵게 만들어놨지 라는 생각을 하며 이 글을 마칩니다. (그냥 옵션으로 설정 하게 해주면 좋지 않았을까요...)

https://docs.aws.amazon.com/imagebuilder/latest/userguide/security-best-practices.html

반응형