본문 바로가기

N8N 셀프 호스팅 구축기 (3) - 리눅스 계정 생성 및 도커 설치

by Tech.Knockknows 2025. 8. 22.

셀프호스팅은 클라우드 서비스와 다르게 내가 직접 서버 설정과 관리를 수행하기 때문에 잘못된 설정을 할 경우 보안적으로 치명적일 수 있습니다. 그래서 N8N을 설치하는 리눅스에서 루트(Root)가 아닌 일반 계정을 사용하는 이유는 보안을 강화하기 위해서예요. 루트(root) 계정은 시스템 전체 권한을 가지고 있기 때문에 실수나 침해 시 피해가 크기 때문에, 일반 사용자 계정으로 작업하다가 필요한 순간에만 sudo를 통해 권한을 높여 쓰는 게 안전합니다. 또한 다중 사용자 환경에서 계정별로 권한과 파일을 분리해 관리할 수 있어 운영 안정성과 추적성이 높아집니다. 

 

이전 글에서 헤츠너(Hetzner) 서버 대여까지 완료했어요.

이제 N8N 설치 전에 리눅스 일반 계정 생성sudo 권한 부여, 그리고 Docker(도커) 설치를 진행할게요.

Hetzner VPS 환경에서 보안을 높이고 자동화 배포를 쉽게 하려면 이 순서가 가장 깔끔합니다.

 

 

리눅스 계정 생성 및 sudo 권한 부여

현재 우리가 헤츠너(Hetzner)에서 만든 기본 계정은 루트(Root) 계정이에요.

루트는 리눅스 최고 관리자 계정이라 서버 내 거의 모든 설정을 바꿀 수 있어요.

그만큼 강력한 권한을 가진 만큼 보안 이슈가 생기기 쉬워서 주의가 필요해요.

 

IT업계에서는 루트 계정을 바로 쓰기보다 일반 사용자 계정을 만들고,

필요한 명령에서만 sudo로 관리자 권한을 부여해 쓰는 방식이 일반적이에요.

 

이렇게 하면 실수로 시스템을 망가뜨릴 위험을 줄이고, 감사/로그 관리도 수월해져요.

리눅스 기초 명령어가 낯설다면 아래 글을 먼저 보고 오면 좋아요.

 

2025.08.21 - [리눅스(Linux)] - 리눅스(Linux) 기본 명령어

 

그래서 이번 글에서는 루트 대신 일반 계정을 새로 만들고

해당 계정에 sudo 권한을 부여해서 안전하게 운영하는 방법을 다룹니다.

 

Docker(도커) 설치 및 개념 요약

N8N 설치 방법은 여러 가지가 있지만, Docker로 배포하면 워커나 DB 통합도 한 번에 처리할 수 있어 운영이 편해요.

특히 Hetzner 같은 VPS 환경에서는 이미지/컨테이너 단위로 버전을 고정하고,

재현 가능한 환경을 유지하기 쉽다는 장점이 있어요.

 

이 글에서는 Docker 방식으로 진행할게요.

도커가 처음이라면 아래 제가 작성한 글을 참고해주세요

 

2025.08.25 - [도커(Docker)] - Docker 및 Docker Compose 관련 기본 명령어

 

Docker 및 Docker Compose 관련 기본 명령어

Docker & Docker Compose 기본 개념과 명령어 정리 안녕하세요.오늘은 Docker 및 Docker Compose 관련 기본 개념과 기본 명령어를 살펴볼게요. Docker 🐳 란?Docker는 애플리케이션과 실행 환경(라이브러리, 설정

knockknows.com

 

리눅스 유저 계정 + Docker 설치 스크립트(자동화)

실제 일반 유저 계정 생성, sudo 권한 부여, SSH 키 등록, 루트/비밀번호 로그인 차단,

그리고 Docker & Compose 설치를 한 번에 처리하는 스크립트를 사용할게요.

 

Hetzner VPS뿐 아니라 대부분의 우분투 서버에서도 그대로 적용할 수 있어요.

아래 스크립트에서 설정되는 항목은 다음과 같아요.

  • 일반 유저 계정 생성
  • sudo 권한 부여
  • SSH 공개키 등록
  • SSH 비밀번호 로그인 차단(보안 강화)
  • 루트 SSH 로그인 차단(보안 강화)
  • Docker Engine + Buildx + Compose V2 설치
  • 신규 유저의 docker 그룹 추가(루트 없이 docker 사용)

 

유저 생성 및 도커 설치 스크립트

더보기
bash install.sh
#!/usr/bin/env bash
# Ubuntu: Interactive user creation + SSH hardening + Docker(Compose V2) install
# - Create/update user (sudo 부여, 비밀번호 설정, SSH 공개키 등록)
# - SSH 하드닝: PasswordAuthentication=no, PermitRootLogin=no
# - Docker Engine + Buildx + Compose V2 설치(공식문서 방식) 및 docker 그룹 추가
# Run: sudo bash setup_user_ssh_docker.sh

set -euo pipefail

#-------------------------
# Root check
#-------------------------
if [[ $EUID -ne 0 ]]; then
  echo "이 스크립트는 root 권한이 필요합니다. sudo로 재실행합니다..."
  exec sudo -E bash "$0"
fi

#-------------------------
# Helpers
#-------------------------
validate_username() {
  local u="$1"
  [[ "$u" =~ ^[a-zA-Z][a-zA-Z0-9._-]{0,31}$ ]]
}

read_password_twice() {
  local p1 p2
  while true; do
    read -rs -p "비밀번호 입력: " p1; echo
    read -rs -p "비밀번호 확인: " p2; echo
    if [[ -z "$p1" ]]; then
      echo "❌ 비밀번호가 비어 있습니다."; continue
    fi
    if [[ "$p1" != "$p2" ]]; then
      echo "❌ 비밀번호가 일치하지 않습니다. 다시 시도하세요."; continue
    fi
    if (( ${#p1} < 8 )); then
      echo "⚠️ 8자 이상을 권장합니다."
      read -r -p "그래도 이 비밀번호를 사용할까요? (y/N): " yn
      [[ "${yn,,}" == "y" || "${yn,,}" == "yes" ]] || continue
    fi
    PASSWORD="$p1"
    break
  done
}

#-------------------------
# Gather inputs
#-------------------------
while true; do
  read -r -p "생성(또는 업데이트)할 사용자명: " USERNAME || true
  if ! validate_username "$USERNAME"; then
    echo "❌ 사용자명 형식이 올바르지 않습니다. (예: n8n-user, dev01)"
    continue
  fi
  if id "$USERNAME" &>/dev/null; then
    echo "ℹ️ 사용자 '$USERNAME' 가 이미 존재합니다. (비밀번호/SSH키/권한을 업데이트합니다)"
    USER_EXISTS=true
  else
    USER_EXISTS=false
  fi
  break
done

read -r -p "이름/설명(비워두면 건너뜀): " FULLNAME || true

echo
echo "=== 비밀번호 설정 ==="
read_password_twice

echo
echo "=== SSH 공개키 입력 (한 줄당 하나, 빈 줄 입력 시 종료) ==="
echo "예: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... user@host"
AUTH_KEYS=""
while IFS= read -r line; do
  [[ -z "$line" ]] && break
  AUTH_KEYS+="$line"$'\n'
done
if [[ -z "$AUTH_KEYS" ]]; then
  echo "❌ 최소 1개의 SSH 공개키가 필요합니다. 스크립트를 종료합니다."
  exit 1
fi

#-------------------------
# Create / Update user with sudo
#-------------------------
if [[ "$USER_EXISTS" == false ]]; then
  if [[ -n "${FULLNAME:-}" ]]; then
    adduser --gecos "$FULLNAME" --disabled-password "$USERNAME"
  else
    adduser --gecos "" --disabled-password "$USERNAME"
  fi
  echo "👤 사용자 생성 완료: $USERNAME"
else
  echo "👤 기존 사용자 갱신: $USERNAME"
fi

# Set password
echo "$USERNAME:$PASSWORD" | chpasswd
echo "🔑 비밀번호 설정 완료"

# Ensure bash + home
usermod -s /bin/bash "$USERNAME" >/dev/null 2>&1 || true
USER_HOME="$(eval echo ~"$USERNAME")"
mkdir -p "$USER_HOME"
chown -R "$USERNAME:$USERNAME" "$USER_HOME"
chmod 755 "$USER_HOME"

# Grant sudo
usermod -aG sudo "$USERNAME"
echo "🛠 sudo 권한 부여 완료"

# Install SSH keys
mkdir -p "$USER_HOME/.ssh"
chmod 700 "$USER_HOME/.ssh"
touch "$USER_HOME/.ssh/authorized_keys"
TMPF="$(mktemp)"
{ cat "$USER_HOME/.ssh/authorized_keys"; printf "%s" "$AUTH_KEYS"; } \
  | awk 'NF && !seen[$0]++' > "$TMPF"
mv "$TMPF" "$USER_HOME/.ssh/authorized_keys"
chmod 600 "$USER_HOME/.ssh/authorized_keys"
chown -R "$USERNAME:$USERNAME" "$USER_HOME/.ssh"
echo "🔐 SSH 공개키 설정 완료"

# Sanity check before hardening
if ! grep -qE '.+' "$USER_HOME/.ssh/authorized_keys"; then
  echo "❌ authorized_keys가 비어 있습니다. SSH 하드닝을 중단합니다."
  exit 1
fi

#-------------------------
# SSH hardening (key-only + no root login)
#-------------------------
SSHD_CONF=/etc/ssh/sshd_config
DROPIN_DIR=/etc/ssh/sshd_config.d
DROPIN_FILE="$DROPIN_DIR/10-hardening.conf"
BACKUP="/etc/ssh/sshd_config.$(date +%F).bak"

cp -a "$SSHD_CONF" "$BACKUP"
echo "🗄  sshd_config 백업: $BACKUP"

mkdir -p "$DROPIN_DIR"
cat > "$DROPIN_FILE" <<'EOF'
# Hardened by setup_user_ssh_docker.sh
# 키 로그인만 허용 / 루트 로그인 금지
PasswordAuthentication no
ChallengeResponseAuthentication no
PubkeyAuthentication yes
PermitRootLogin no
UsePAM yes
EOF

# Validate & restart SSH
sshd -t
systemctl restart ssh
echo "♻️ SSH 하드닝 적용 완료 (PasswordAuthentication=no, PermitRootLogin=no)"

#-------------------------
# Docker (Engine + Buildx + Compose V2) official way
#-------------------------
echo "🐳 Docker 설치를 진행합니다 (공식 리포지토리)."

apt-get update
apt-get install -y ca-certificates curl

install -m 0755 -d /etc/apt/keyrings
if [[ ! -f /etc/apt/keyrings/docker.asc ]]; then
  curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
  chmod a+r /etc/apt/keyrings/docker.asc
fi

# Add repo if not exists
if [[ ! -f /etc/apt/sources.list.d/docker.list ]]; then
  echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" \
    | tee /etc/apt/sources.list.d/docker.list > /dev/null
fi

apt-get update
apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

systemctl enable --now docker
echo "✅ Docker 설치 완료"

# Add user to docker group
usermod -aG docker "$USERNAME" || true
echo "👥 '$USERNAME' 사용자를 docker 그룹에 추가했습니다 (재로그인 필요)."

# Quick versions
echo "---- Docker 버전 ----"
docker --version || true
echo "---- Compose 버전 ----"
docker compose version || true

echo
echo "🎉 모든 작업 완료!"
echo "- 사용자: $USERNAME (sudo 권한, SSH 키 등록)"
echo "- SSH: 키 로그인만 허용, 루트 로그인 금지"
echo "- Docker: Engine/Buildx/Compose V2 설치 및 docker 그룹 추가(재로그인 필요)"
echo
echo "안전 팁: 현재 세션은 유지한 채 새 터미널에서 다음으로 재접속 확인 후 기존 세션을 종료하세요."
echo "  ssh -i <개인키> ${USERNAME}@<서버IP>"

 

서버 접속 및 스크립트 작성

먼저 이전 글에서 만들었던 Hetzner VPS 서버에 SSH 키로 접속해요.

서버 접속 후 vi 에디터 실행 화면

접속했다면 아래와 같이 vi로 스크립트 파일을 만들어요.

CLI n8n@hetzner:~
vi user_add_docker.sh
  1. vi가 실행되면 i 키를 눌러 입력 모드로 전환해요.
  2. 위 스크립트를 복사해 붙여넣어요.
  3. ESC:wq로 저장 후 종료해요.

2번까지 완료했다면 아래처럼 스크립트가 잘 작성돼 있을 거예요.

vi 편집기에서 작성된 스크립트 화면

vi 사용법은 아래 리눅스 기초 글을 참고하세요.

2025.08.21 - [리눅스(Linux)] - 리눅스(Linux) 기본 명령어

 

권한 부여 및 실행

스크립트에 실행 권한을 추가하고 실행해요.

CLI n8n@hetzner:~
chmod +x user_add_docker.sh
./user_add_docker.sh
  • chmod(Change Mode): 파일/디렉토리 권한 변경 명령
  • +x: 실행 권한 부여

스크립트는 대화형으로 사용자명 / 설명(옵션) / 비밀번호 / SSH 공개키를 입력받아요.

필수는 사용자명·비밀번호·공개키예요.

사용자 계정 생성 및 스크립트 실행 예시 화면

예시로 저는 사용자명을 autokor_n8n으로 설정했어요.

 

공개키 생성은 이전 글을 참고하세요.

2025.08.21 - [N8N] - N8N 셀프 호스팅 구축기 (2) - 헤츠너(Hetzner) VPS 생성

루트 계정과는 다른 SSH 공개키를 쓰는 걸 추천해요. 공개키 입력 후에는 엔터 두 번 입력하면 스크립트가 진행돼요.

 

실행 검증(도커/계정/sudo 확인)

설치가 끝나면 먼저 현재 세션에서 도커가 동작하는지 확인해요.

CLI n8n@hetzner:~
docker

아래처럼 메시지가 출력되면 정상이에요.

도커 명령어 실행 결과 화면

다음으로 신규 생성한 계정으로 접속해 sudodocker가 정상인지 확인해요.

 

CLI n8n@hetzner:~
ssh <신규 계정 명>@<헤츠너 서버 IP> -i <키 경로>
sudo -s
exit
docker

 

'sudo -s' 입력 시 스크립트에서 설정한 비밀번호를 물어요.

성공하면 프롬프트가 해당 계정에서 root로 바뀌고, exit로 원래 계정으로 돌아올 수 있어요. 

sudo 권한 확인 화면

그다음 docker 명령이 루트 없이 실행되면 설정이 잘 끝난 거예요.

docker 명령 루트 없이 실행 확인 화면

 


 

정리 및 주의사항 (Hetzner 요금/보안 팁)

이 글에서는 Hetzner VPS에서 리눅스 일반 계정 생성sudo 권한 부여SSH 보안 강화Docker & Compose 설치까지

한 번에 자동화하는 방법을 정리했어요. 

 

이 글을 따라했을 때 주의 사항은 다음과 같아요.

  • SSH-KEY 관리 : 패스워드를 통한 SSH 로그인을 비활성화 했어요. SSH Key를 분실할 경우 서버를 다시 만들어야 해요.
  • 유저 패스워드 관리 : SSH 로그인 후 일반 유저에서 sudo 권한을 쓰기 위해서 패스워드는 세팅했어요. 패스워드 분실 시 sudo 명령어 사용이 불가능해요.
  • Root SSH 로그인 차단 : 보안을 위해 루트(Root) 계정 SSH 로그인을 비활성화했어요.

다음 글에서는 Docker Compose로 N8N을 설치/실행하는 과정을 다룰게요.

 


 

이전 글 참고

2025.08.20 - [N8N] - N8N 셀프 호스팅 구축기 (1) - 헤츠너(Hetzner) 가입

2025.08.21 - [N8N] - N8N 셀프 호스팅 구축기 (2) - 헤츠너(Hetzner) VPS 생성