ติดตั้ง Keycloak SSO บน VPS — Single Sign-On สำหรับทุก App
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
ความต้องการของระบบ
- ขั้นต่ำ: 2 vCPU, RAM 2 GB (Keycloak กิน ~512 MB), disk 10 GB
- แนะนำ: RAM 4 GB สำหรับ production ที่มีผู้ใช้หลายพันคน
- Docker และ Docker Compose
- โดเมนที่ชี้มาที่ VPS พร้อม SSL (Keycloak ต้องการ HTTPS ใน production)
ติดตั้ง 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
/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 ไม่ได้มาตรฐาน