ติดตั้ง Keycloak SSO Single Sign-On บน VPS

ติดตั้ง Keycloak SSO บน VPS — Single Sign-On สำหรับทุก App

มิถุนายน 2026 · อ่าน 9 นาที

Keycloak คือ Identity and Access Management (IAM) แบบ open-source จาก Red Hat ที่ให้คุณทำ Single Sign-On (SSO) — login ครั้งเดียวแล้วเข้าได้ทุก application รองรับ OpenID Connect, OAuth 2.0 และ SAML 2.0 มีหน้า login สำเร็จรูป, จัดการ user/role ผ่าน admin console, ต่อ Google/GitHub Social Login และตั้ง MFA ได้โดยไม่ต้องเขียน code auth เองเลย บทความนี้สอนติดตั้งด้วย Docker, ตั้ง realm และ connect แอป Node.js/PHP ให้ใช้ Keycloak login

ความต้องการของระบบ

ติดตั้ง Keycloak ด้วย Docker Compose

mkdir -p /opt/keycloak && cd /opt/keycloak

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

services:
  postgres:
    image: postgres:15
    restart: unless-stopped
    environment:
      POSTGRES_DB: keycloak
      POSTGRES_USER: keycloak
      POSTGRES_PASSWORD: CHANGE_ME_DB_PASS
    volumes:
      - postgres_data:/var/lib/postgresql/data

  keycloak:
    image: quay.io/keycloak/keycloak:24.0
    restart: unless-stopped
    command: start
    environment:
      KC_DB: postgres
      KC_DB_URL: jdbc:postgresql://postgres:5432/keycloak
      KC_DB_USERNAME: keycloak
      KC_DB_PASSWORD: CHANGE_ME_DB_PASS
      KC_HOSTNAME: auth.yourdomain.com
      KC_PROXY: edge
      KC_HTTP_ENABLED: "true"
      KEYCLOAK_ADMIN: admin
      KEYCLOAK_ADMIN_PASSWORD: CHANGE_ME_ADMIN_PASS
    ports:
      - "127.0.0.1:8080:8080"
    depends_on:
      - postgres

volumes:
  postgres_data:
EOF

docker-compose up -d

ตั้ง Nginx Reverse Proxy

nano /etc/nginx/sites-available/keycloak

server {
    listen 80;
    server_name auth.yourdomain.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name auth.yourdomain.com;

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

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_buffer_size 128k;
        proxy_buffers 4 256k;
        proxy_busy_buffers_size 256k;
    }
}

ln -s /etc/nginx/sites-available/keycloak /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx

# ขอ SSL
certbot --nginx -d auth.yourdomain.com

ตั้ง Realm และ Client

เปิด Admin Console ที่ https://auth.yourdomain.com/admin แล้วทำตามขั้นตอน:

# ขั้นตอนผ่าน UI (Admin Console)
# 1. สร้าง Realm ใหม่:
#    "Create Realm" → ตั้งชื่อ เช่น "myapp"

# 2. สร้าง Client สำหรับ application:
#    Clients → Create client
#    Client ID: myapp-backend
#    Client type: OpenID Connect
#    Valid redirect URIs: https://myapp.com/*
#    Web origins: https://myapp.com

# 3. สร้าง Role:
#    Realm roles → Create role → "admin" / "user"

# 4. สร้าง User:
#    Users → Add user → ตั้ง username/email
#    Credentials tab → Set password
#    Role Mappings → กำหนด role

เชื่อม Application กับ Keycloak (Node.js)

# ติดตั้ง Keycloak adapter
npm install keycloak-connect express-session

# app.js
const Keycloak = require('keycloak-connect');
const session = require('express-session');
const express = require('express');

const app = express();
const memoryStore = new session.MemoryStore();

app.use(session({
  secret: 'your-session-secret',
  resave: false,
  saveUninitialized: true,
  store: memoryStore
}));

const keycloak = new Keycloak({ store: memoryStore }, {
  realm: 'myapp',
  'auth-server-url': 'https://auth.yourdomain.com',
  'ssl-required': 'external',
  resource: 'myapp-backend',
  'public-client': true
});

app.use(keycloak.middleware());

// Protected route — ต้อง login ก่อน
app.get('/dashboard', keycloak.protect(), (req, res) => {
  res.json({ user: req.kauth.grant.access_token.content });
});

// Protected route สำหรับ admin เท่านั้น
app.get('/admin', keycloak.protect('realm:admin'), (req, res) => {
  res.json({ message: 'Admin area' });
});

ตั้ง Social Login (Google)

# ใน Admin Console:
# Identity Providers → Add provider → Google
# ใส่ Client ID และ Client Secret จาก Google Console
# (console.cloud.google.com → APIs & Services → Credentials)

# Redirect URI ที่ต้องใส่ใน Google Console:
# https://auth.yourdomain.com/realms/myapp/broker/google/endpoint

# ตั้ง Auto-link existing account:
# Identity Providers → Google → Mappers → Add mapper
# Type: Hardcoded Role → role ที่ต้องการ assign

เปิดใช้ MFA (TOTP)

# ใน Admin Console:
# Authentication → Required Actions
# เปิด "Configure OTP" → Set as Default Action

# หรือบังคับ MFA ต่อ Role:
# Authentication → Flows → Browser flow
# OTP Form → Required

# User จะถูกบังคับตั้ง TOTP ผ่าน Google Authenticator
# ตอน login ครั้งแรกหลังเปิด Required Action

สำหรับ VPS ที่ใช้รัน Keycloak ควรเลือก server ที่มี SSD และ RAM เพียงพอ — บริการ VPS ไทยของ asiagb มีตัวเลือก RAM 4 GB ขึ้นไปในราคาที่จับต้องได้ พร้อม support ภาษาไทยสำหรับตั้งค่า DNS ก่อนเชื่อม Keycloak กับโดเมน และถ้าต้องการตรวจสุขภาพ SSL ของ auth.yourdomain.com ก่อน go-live ใช้ dnsxray.com ตรวจ certificate chain ได้ฟรี


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

# ตรวจ container ทั้งหมดรัน
docker-compose ps

# ดู log keycloak
docker-compose logs -f keycloak --tail=50

# ตรวจ OIDC discovery endpoint
curl -s https://auth.yourdomain.com/realms/myapp/.well-known/openid-configuration | python3 -m json.tool | head -20

# ตรวจ token endpoint พร้อม grant test
curl -s -X POST \
  "https://auth.yourdomain.com/realms/myapp/protocol/openid-connect/token" \
  -d "client_id=myapp-backend&username=testuser&password=testpass&grant_type=password" \
  | python3 -m json.tool | grep access_token | head -3

# ตรวจ admin API
curl -s "https://auth.yourdomain.com/admin/realms/myapp/users" \
  -H "Authorization: Bearer $(cat /tmp/admin_token.txt)" | python3 -m json.tool
Tip: Keycloak มี Health Check endpoint ที่ /health และ /health/ready — ใช้ตั้ง Docker healthcheck หรือ Uptime Kuma เพื่อแจ้งเตือนเมื่อ Keycloak ล่ม ก่อน user มาแจ้ง

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

Error: "Invalid redirect_uri" เมื่อ login

Redirect URI ที่ app ส่งมาไม่ตรงกับที่ตั้งใน Client config

# ใน Admin Console: Clients → [client] → Valid Redirect URIs
# ใส่ URL ที่ถูกต้อง เช่น:
# https://myapp.com/*
# http://localhost:3000/*  (สำหรับ development)

# ระวัง trailing slash — "https://myapp.com" ≠ "https://myapp.com/"
# ถ้าไม่แน่ใจใส่ทั้งสองแบบ

Keycloak ขึ้น "HTTPS required" แต่ตั้ง SSL แล้ว

Keycloak ไม่เห็น X-Forwarded-Proto จาก reverse proxy

# ตรวจ Nginx ส่ง header ถูกต้อง
proxy_set_header X-Forwarded-Proto $scheme;

# ใน Keycloak config ต้องตั้ง KC_PROXY=edge
# ถ้าตั้งแล้วยังเป็น admin console ลอง:
# Realm Settings → General → Require SSL → "External requests only"

Keycloak ช้ามากตอนเริ่มต้น

JVM warmup ปกติใช้เวลา 30–60 วินาทีครั้งแรก

# เพิ่ม JVM heap size ถ้า RAM มีเพียงพอ
# ใน docker-compose.yml environment:
JAVA_OPTS_APPEND: "-Xms512m -Xmx1024m"

# ตั้ง health check timeout ให้นาน
healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost:8080/health/ready"]
  interval: 30s
  timeout: 10s
  retries: 5
  start_period: 90s

Error: "Failed to connect to database" ตอนเริ่ม

Keycloak เริ่มก่อน PostgreSQL พร้อม ต้องตั้ง depends_on ให้ถูกต้อง

# ใน docker-compose.yml
services:
  keycloak:
    depends_on:
      postgres:
        condition: service_healthy

  postgres:
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "keycloak"]
      interval: 10s
      timeout: 5s
      retries: 5

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

Keycloak กับ Authelia ต่างกันอย่างไร?

Keycloak เป็น full IAM solution — มี user management, role/group, social login, SAML และ fine-grained access control ในตัว เหมาะกับองค์กรที่ต้องการระบบ identity เต็มรูปแบบ Authelia เป็น authentication middleware ที่เบากว่า เหมาะสำหรับ protect web app ผ่าน forward auth ใน Nginx/Traefik โดยไม่ต้องแก้ code app

Keycloak รองรับผู้ใช้กี่คน?

ตัว Keycloak รองรับได้หลักล้าน user ต่อ realm แต่ performance ขึ้นกับ database และ RAM ของ server VPS RAM 4 GB + PostgreSQL SSD รองรับ login หลักพัน concurrent users ได้สบาย สำหรับ scale ใหญ่กว่านี้ต้องตั้ง Keycloak cluster

Migrate user จาก app เดิมมา Keycloak ได้ไหม?

ได้ — ผ่าน User Federation สำหรับ LDAP/Active Directory หรือ import ด้วย Admin API สำหรับ user ใน database เดิม รองรับ custom user storage provider ที่เขียน Java plugin เองได้ถ้า source ไม่ได้มาตรฐาน