Authelia 2FA SSO สำหรับ VPS

📅 4 ตุลาคม 2026 · 🏷️ Security, Authentication, SSO

ติดตั้ง Authelia: 2FA + SSO สำหรับ VPS Linux

Authelia คือ authentication server แบบ open-source ที่ให้คุณเพิ่ม Two-Factor Authentication (2FA) และ Single Sign-On (SSO) ให้กับทุกแอปพลิเคชันที่รันบน VPS ของคุณ โดยทำงานร่วมกับ reverse proxy อย่าง Nginx หรือ Traefik ได้อย่างลงตัว

แทนที่แต่ละแอปจะต้องจัดการ login ของตัวเอง Authelia ทำหน้าที่เป็น "ประตูรักษาความปลอดภัย" กลาง — ผู้ใช้ login ครั้งเดียว ได้สิทธิ์เข้าถึงทุก service ที่อนุญาต พร้อม 2FA ป้องกันการเข้าถึงโดยไม่ได้รับอนุญาต

ทำไมต้องใช้ Authelia?

คุณสมบัติรายละเอียด
Single Sign-OnLogin ครั้งเดียว เข้าถึงทุก service
2FA/MFAรองรับ TOTP (Google Authenticator), WebAuthn, Push notification
Access Controlกำหนด policy ละเอียดถึง path-level
Brute-Force Protectionล็อก account หลัง login ผิดซ้ำ
Open Sourceฟรี self-hosted ไม่มีค่าใช้จ่ายรายเดือน

สิ่งที่ต้องเตรียมก่อนเริ่ม

โครงสร้าง Authelia ที่จะติดตั้ง

เราจะใช้ Docker Compose รัน Authelia คู่กับ Redis (สำหรับ session) และ LLDAP (สำหรับจัดการ user) ทั้งหมดอยู่หลัง Nginx reverse proxy

VPS
├── Nginx (reverse proxy + auth_request)
│   ├── authelia.example.com → Authelia
│   ├── app1.example.com → App1 (protected)
│   └── app2.example.com → App2 (protected)
└── Docker Compose
    ├── authelia (authentication server)
    ├── redis (session storage)
    └── lldap (user directory)

ขั้นตอนที่ 1: สร้าง Directory Structure

mkdir -p ~/authelia/{config,data}
cd ~/authelia

ขั้นตอนที่ 2: สร้าง docker-compose.yml

cat > docker-compose.yml << 'EOF'
version: '3.8'

services:
  authelia:
    image: authelia/authelia:latest
    container_name: authelia
    restart: unless-stopped
    volumes:
      - ./config:/config
      - ./data:/data
    environment:
      - AUTHELIA_JWT_SECRET_FILE=/config/jwt_secret
      - AUTHELIA_SESSION_SECRET_FILE=/config/session_secret
      - AUTHELIA_STORAGE_ENCRYPTION_KEY_FILE=/config/storage_encryption_key
    ports:
      - "9091:9091"
    depends_on:
      - redis
    networks:
      - authelia_net

  redis:
    image: redis:7-alpine
    container_name: authelia_redis
    restart: unless-stopped
    volumes:
      - redis_data:/data
    networks:
      - authelia_net

volumes:
  redis_data:

networks:
  authelia_net:
    driver: bridge
EOF

ขั้นตอนที่ 3: สร้าง Secret Files

# สร้าง secret keys แบบสุ่ม
openssl rand -hex 64 > ~/authelia/config/jwt_secret
openssl rand -hex 64 > ~/authelia/config/session_secret
openssl rand -hex 64 > ~/authelia/config/storage_encryption_key

chmod 600 ~/authelia/config/*_secret ~/authelia/config/storage_encryption_key

ขั้นตอนที่ 4: สร้าง configuration.yml

cat > ~/authelia/config/configuration.yml << 'EOF'
server:
  host: 0.0.0.0
  port: 9091

log:
  level: info

theme: light

jwt_secret_file: /config/jwt_secret

default_redirection_url: https://example.com

totp:
  issuer: example.com
  period: 30
  skew: 1

authentication_backend:
  file:
    path: /config/users_database.yml
    password:
      algorithm: bcrypt
      iterations: 12

access_control:
  default_policy: deny
  rules:
    # เปิดให้ทุกคนเข้า public area
    - domain: "www.example.com"
      policy: bypass
    # ต้องการ 2FA สำหรับ admin tools
    - domain: "*.example.com"
      subject: "group:admins"
      policy: two_factor
    # ต้องการแค่ login สำหรับ user ทั่วไป
    - domain: "app1.example.com"
      subject: "group:users"
      policy: one_factor

session:
  name: authelia_session
  secret_file: /config/session_secret
  expiration: 1h
  inactivity: 15m
  domain: example.com
  redis:
    host: redis
    port: 6379

regulation:
  max_retries: 5
  find_time: 2m
  ban_time: 10m

storage:
  local:
    path: /data/db.sqlite3
  encryption_key_file: /config/storage_encryption_key

notifier:
  filesystem:
    filename: /data/notification.txt
EOF
หมายเหตุ: แทน example.com ด้วย domain จริงของคุณทุกจุด สำหรับ production ควรใช้ SMTP notifier แทน filesystem

ขั้นตอนที่ 5: สร้าง Users Database

# สร้าง password hash สำหรับ user
docker run --rm authelia/authelia:latest authelia hash-password 'your_secure_password'
# ได้ผลลัพธ์ เช่น: $argon2id$v=19$m=65536,t=3,p=4$...

cat > ~/authelia/config/users_database.yml << 'EOF'
users:
  admin:
    displayname: "Administrator"
    password: "$argon2id$v=19$m=65536,t=3,p=4$YOUR_HASH_HERE"
    email: [email protected]
    groups:
      - admins
      - users

  alice:
    displayname: "Alice"
    password: "$argon2id$v=19$m=65536,t=3,p=4$ALICE_HASH_HERE"
    email: [email protected]
    groups:
      - users
EOF

ขั้นตอนที่ 6: ตั้งค่า Nginx

เพิ่ม location block สำหรับ Authelia และ auth_request ให้ทุก virtual host ที่ต้องการป้องกัน

# /etc/nginx/snippets/authelia.conf
location /internal/authelia/authz {
    internal;
    proxy_pass http://127.0.0.1:9091/api/authz/auth-request;
    proxy_pass_request_body off;
    proxy_set_header Content-Length "";
    proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Real-IP $remote_addr;
}

# /etc/nginx/sites-available/app1.example.com
server {
    listen 443 ssl http2;
    server_name app1.example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    include /etc/nginx/snippets/authelia.conf;

    location / {
        auth_request /internal/authelia/authz;
        auth_request_set $target_url $scheme://$http_host$request_uri;
        error_page 401 =302 https://authelia.example.com/?rd=$target_url;

        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

# Virtual host สำหรับ Authelia เอง
server {
    listen 443 ssl http2;
    server_name authelia.example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:9091;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

ขั้นตอนที่ 7: Start Authelia

cd ~/authelia
docker compose up -d

# ดู logs
docker compose logs -f authelia

ขั้นตอนที่ 8: ตั้งค่า TOTP (2FA)

เมื่อ login ครั้งแรก Authelia จะให้ลงทะเบียน TOTP QR code ให้สแกนด้วย Google Authenticator หรือ Authy ได้เลย

# ตรวจสอบว่า user ลงทะเบียน TOTP แล้ว
docker exec authelia authelia storage user totp export --config /config/configuration.yml

ตรวจสอบว่า Authelia ทำงานถูกต้อง

# ตรวจสอบ container กำลังรัน
docker compose ps

# ทดสอบ health endpoint
curl -s http://127.0.0.1:9091/api/health | python3 -m json.tool

# ตรวจสอบ Redis connection
docker exec authelia_redis redis-cli ping

# ดู Authelia logs แบบ real-time
docker compose logs -f authelia --tail=50

# ทดสอบ auth endpoint (ควรได้ 401 เพราะไม่มี session)
curl -I http://127.0.0.1:9091/api/authz/auth-request
Tip: ถ้า Authelia ไม่ start ให้เช็ค docker compose logs authelia — ปัญหาที่พบบ่อยคือ permission ของ config files หรือ secret files ไม่ถูกต้อง ให้รัน chmod 600 ~/authelia/config/*

แก้ปัญหาที่พบบ่อย

Error: "level=fatal msg=\"configuration: secrets: error loading secret\""

ไฟล์ secret มี permission ไม่ถูกต้อง หรือ Authelia container อ่านไม่ได้

# แก้ permission
chmod 600 ~/authelia/config/jwt_secret
chmod 600 ~/authelia/config/session_secret
chmod 600 ~/authelia/config/storage_encryption_key
# restart container
docker compose restart authelia

Error: "dial tcp redis:6379: connect: connection refused"

Redis container ยังไม่พร้อม หรือชื่อ service ใน docker-compose ไม่ตรงกับ configuration.yml

# ตรวจสอบชื่อ service Redis ใน docker-compose.yml
docker compose ps
# ชื่อใน session.redis.host ต้องตรงกับชื่อ service
# รัน Redis แยกก่อน
docker compose up -d redis
sleep 3
docker compose up -d authelia

Error: "auth_request module returned 500"

Nginx ติดต่อ Authelia ไม่ได้ หรือ proxy_pass URL ไม่ถูกต้อง

# ตรวจสอบว่า Authelia bind ที่ port 9091
ss -tlnp | grep 9091
# ตรวจสอบ nginx config
nginx -t
# ดู nginx error log
tail -50 /var/log/nginx/error.log

Error: "level=error msg=\"session: error reading session\""

Session secret เปลี่ยนแปลงหลัง restart ทำให้ session เก่าอ่านไม่ได้

# ล้าง Redis session ทั้งหมด (user ต้อง login ใหม่)
docker exec authelia_redis redis-cli FLUSHDB
docker compose restart authelia

Error: "totp: failed to verify code"

นาฬิกา server กับมือถือคลาดเคลื่อนเกิน 30 วินาที

# ตรวจสอบเวลา server
timedatectl status
# ซิงค์เวลา
timedatectl set-ntp true
# หรือบังคับ sync
chronyc makestep

คำถามที่พบบ่อย

Authelia ต่างจาก Keycloak อย่างไร?

Authelia เน้นความเบาและง่าย เหมาะกับ VPS resource น้อย ใช้ config file แทน database และรองรับ 2FA + SSO ผ่าน reverse proxy โดยตรง ส่วน Keycloak ครอบคลุม feature มากกว่า (SAML, OAuth2 server เต็มรูปแบบ) แต่กิน RAM สูงกว่ามาก (512MB ขึ้นไป)

ใช้ Authelia กับ Traefik ได้ไหม?

ได้ และ Traefik รองรับ Authelia แบบ native ผ่าน forwardAuth middleware เพียงเพิ่ม labels ใน docker-compose ของแต่ละ service แทนการแก้ไข config file ทำให้ง่ายกว่าใช้กับ Nginx มาก

ถ้า Authelia ล่มแล้วจะเข้า service ได้ไหม?

ไม่ได้ เพราะ auth_request ของ Nginx จะ return error เมื่อ upstream ตอบไม่ได้ วิธีแก้คือรัน Authelia บน Docker Swarm หรือตั้ง restart: always และ health check ใน docker-compose เพื่อให้ container restart อัตโนมัติเมื่อล่ม