Keycloak 설치
설치 준비
준비
[RockyLinux]
dnf update -y
dnf install -y curl wget tar gzip unzip vim bind-utils openssl jq policycoreutils-python-utils gnupg2
#dnf install -y java-21-openjdk java-21-openjdk-devel
- jdk 21은 /opt/jdk에 수동으로 설치한다.
[Debian/Ubuntu]
apt update
apt install -y curl wget tar gzip unzip vim dnsutils openssl jq policycoreutils-python-utils gnupg
#apt install -y openjdk-21-jdk
- jdk 21은 /opt/jdk에 수동으로 설치한다.
java -version
postgresql 설치
PostgreSQL 16 설치
[RockyLinux]
dnf module list postgresql
dnf module reset postgresql -y
dnf module enable postgresql:16 -y
dnf install -y postgresql-server postgresql-contrib
[Debian/Ubuntu]
1. 필수 패키지 설치 및 저장소 키 추가
apt update
apt install -y curl ca-certificates gnupg
install -d /etc/apt/keyrings
curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor -o /etc/apt/keyrings/postgresql.gpg
2. PostgreSQL 공식 저장소 추가
echo "deb [signed-by=/etc/apt/keyrings/postgresql.gpg] http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" | sudo tee /etc/apt/sources.list.d/pgdg.list
3. PostgreSQL 16 및 모듈(contrib) 설치
apt update
apt install -y postgresql-16 postgresql-contrib-16
PostgreSQL 초기화
[RockyLinux]
postgres --version
postgresql-setup --initdb
systemctl enable postgresql
cp -f /var/lib/pgsql/data/pg_hba.conf /var/lib/pgsql/data/pg_hba.conf.orig
[Debian/Ubuntu]
sudo -u postgres psql --version
Postgresql의 IPv4 접속 방식 변경
[RockyLinux]
vi /var/lib/pgsql/data/pg_hba.conf
host all all 127.0.0.1/32 ident
을
host all all 127.0.0.1/32 scram-sha-256
로 수정
systemctl restart postgresql
[Debian/Ubuntu]
vi /etc/postgresql/16/main/pg_hba.conf
[Debian/Ubuntu] 은 수정 필요 없음.
systemctl restart postgresql
Keycloak DB 및 계정 생성
sudo -u postgres psql
CREATE USER u_keycloak WITH PASSWORD 'Pass!234';
CREATE DATABASE db_keycloak OWNER u_keycloak ENCODING 'UTF8';
\l
\q
psql -h 127.0.0.1 -U u_keycloak -d db_keycloak
SELECT current_database();
\q
postfix 설치 (이메일 발송용)
postfix 설치
[RockyLinux]
dnf install -y postfix mailx
[Debian/Ubuntu]
apt install -y postfix mailutils (Internet Site 타입으로 설치)
postfix 설정
설정파일에서 아래 부분을 수정한다.
vi /etc/postfix/main.cf
myhostname = mail.example.com
mydomain = example.com
myorigin = $mydomain
inet_interfaces = loopback-only
inet_protocols = ipv4
mydestination = $myhostname, localhost.$mydomain, localhost
relayhost =
mynetworks = 127.0.0.0/8
smtp_tls_security_level = may
smtp_tls_loglevel = 1
postfix check
systemctl enable postfix
systemctl restart postfix
keycloak 설치
최신 압축 파일 다운로드 (필요시 버전 변경)
cd /tmp
KEYCLOAK_VERSION="26.7.2"
wget "https://github.com/keycloak/keycloak/releases/download/${KEYCLOAK_VERSION}/keycloak-${KEYCLOAK_VERSION}.tar.gz"
/opt 디렉터리에 압축 해제 및 심볼릭 링크 생성
tar -xvzf keycloak-${KEYCLOAK_VERSION}.tar.gz -C /opt/
ln -s /opt/keycloak-${KEYCLOAK_VERSION} /opt/keycloak
rm -f keycloak-${KEYCLOAK_VERSION}.tar.gz
사용자 생성
useradd --system --home-dir /opt/keycloak --shell /sbin/nologin keycloak
chown -R keycloak:keycloak /opt/keycloak-${KEYCLOAK_VERSION}
chown -R keycloak:keycloak /opt/keycloak
chmod 600 /opt/keycloak/conf/keycloak.conf
ls -ld /opt/keycloak*
Keycloak 기본 설정
cp -f /opt/keycloak/conf/keycloak.conf /opt/keycloak/conf/keycloak.conf.orig
vi /opt/keycloak/conf/keycloak.conf
# =========================================================
# Database
# =========================================================
db=postgres
db-username=u_keycloak
db-password=Pass!234
db-url=jdbc:postgresql://127.0.0.1:5432/db_keycloak
# =========================================================
# HTTP / HTTPS
# =========================================================
http-enabled=true
http-port=8080
http-host=0.0.0.0
# =========================================================
# Hostname
# =========================================================
hostname=https://sso.example.com
# =========================================================
# Reverse Proxy
# 10.10.10.1 : apache-httpd reverse proxy server
# =========================================================
proxy-headers=xforwarded
proxy-trusted-addresses=10.10.10.1
# =========================================================
# Health / Metrics
# =========================================================
health-enabled=true
metrics-enabled=true
# Management interface
http-management-host=127.0.0.1
http-management-port=9000
http-management-scheme=http
# =========================================================
# Logging
# =========================================================
log=console,file
log-level=info
log-file=/var/log/keycloak/keycloak.log
log-file-rotation-enabled=true
log-file-rotation-max-file-size=50M
log-file-rotation-max-backup-index=10
keycloak 로그 디렉터리 생성
mkdir -p /var/log/keycloak
chown keycloak:keycloak /var/log/keycloak
chmod 750 /var/log/keycloak
keycloak Build
sudo -u keycloak JAVA_HOME=/opt/jdk /opt/keycloak/bin/kc.sh build
sudo -u keycloak JAVA_HOME=/opt/jdk /opt/keycloak/bin/kc.sh show-config
초기 관리자 생성
export KEYCLOAK_BOOTSTRAP_PASSWORD='Pass!234'
sudo -u keycloak env KEYCLOAK_BOOTSTRAP_PASSWORD="$KEYCLOAK_BOOTSTRAP_PASSWORD" JAVA_HOME=/opt/jdk /opt/keycloak/bin/kc.sh bootstrap-admin user --username admin --password:env KEYCLOAK_BOOTSTRAP_PASSWORD
unset KEYCLOAK_BOOTSTRAP_PASSWORD
keycloak 수동 실행 테스트
sudo -u keycloak JAVA_HOME=/opt/jdk /opt/keycloak/bin/kc.sh start --optimized
ss -lntp | grep -E '8080|9000'
아래와 같이 나오면 정상
LISTEN 0 4096 [::ffff:127.0.0.1]:8080 *:* users:(("java",pid=1895,fd=199))
LISTEN 0 4096 [::ffff:127.0.0.1]:9000 *:* users:(("java",pid=1895,fd=200))
-
Health Check
curl http://127.0.0.1:9000/health -
Ready 확인:
curl http://127.0.0.1:9000/health/ready -
Live 확인:
curl http://127.0.0.1:9000/health/live -
Metrics 확인
curl http://127.0.0.1:9000/metrics
systemd 설정
vi /etc/systemd/system/keycloak.service
[Unit]
Description=Keycloak Identity and Access Management
Documentation=https://www.keycloak.org/
After=network-online.target postgresql.service
Wants=network-online.target
Requires=postgresql.service
[Service]
Type=simple
User=keycloak
Group=keycloak
WorkingDirectory=/opt/keycloak
# Java
Environment="JAVA_HOME=/opt/jdk"
# JVM Heap
Environment="JAVA_OPTS_KC_HEAP=-Xms1g -Xmx4g"
ExecStart=/opt/keycloak/bin/kc.sh start --optimized
Restart=on-failure
RestartSec=10
TimeoutStartSec=180
TimeoutStopSec=60
LimitNOFILE=65536
# Security hardening
PrivateTmp=true
ProtectHome=true
NoNewPrivileges=true
[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable keycloak
systemctl restart keycloak
### 로그 확인
journalctl -u keycloak
tail -f /var/log/keycloak/keycloak.log
Reverse Proxy 서버 설정
https 인증서 생성
웹서버 인증서 생성
# SSLEngine on
# SSLProtocol -all +TLSv1.2 +TLSv1.3
# SSLCertificateFile /etc/ssl/certs/keycloak.crt
# SSLCertificateKeyFile /etc/ssl/private/keycloak.key
CERT_DIR="/etc/ssl/certs"
KEY_DIR="/etc/ssl/private"
DOMAIN="localhost"
DAYS_VALID=3650
# 파일명 설정
KEY_FILE="${KEY_DIR}/keycloak.key"
CERT_FILE="${CERT_DIR}/keycloak.crt"
echo "[1/4] 디렉터리 생성 및 확인 중..."
mkdir -p "$CERT_DIR"
mkdir -p "$KEY_DIR"
echo "[2/4] 자체 서명 SSL 인증서 생성 중..."
# OpenSSL 명령어를 통한 비대화형 인증서 생성
openssl req -x509 -nodes -days $DAYS_VALID -newkey rsa:2048 \
-keyout "$KEY_FILE" \
-out "$CERT_FILE" \
-subj "/C=KR/ST=Seoul/L=Seoul/O=Development/OU=IT/CN=${DOMAIN}" \
-addext "subjectAltName=DNS:${DOMAIN},IP:127.0.0.1"
echo "[3/4] 파일 권한 설정 중..."
# 개인키 파일은 root만 읽을 수 있도록 제한
chmod 600 "$KEY_FILE"
chmod 644 "$CERT_FILE"
Reverse Proxy 서버로 Nginx를 사용한 방법 (같은 서버)
Nginx 설치
[RockyLinux]
dnf install -y nginx
[Debian/Ubuntu]
apt install -y nginx
systemctl enable nginx
Nginx Reverse Proxy 설정
[RockyLinux]
vi /etc/nginx/conf.d/keycloak.conf
[Debian/Ubuntu]
vi /etc/nginx/sites-available/keycloak.conf
server {
listen 80;
server_name sso.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name sso.example.com;
ssl_certificate /etc/ssl/certs/keycloak.crt;
ssl_certificate_key /etc/ssl/private/keycloak.key;
ssl_protocols TLSv1.2 TLSv1.3;
client_max_body_size 20m;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
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 https;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port 443;
proxy_connect_timeout 30s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
}
[Debian/Ubuntu] 사이트 활성화
rm -f /etc/nginx/sites-enabled/keycloak.conf
ln -s /etc/nginx/sites-available/keycloak.conf /etc/nginx/sites-enabled/keycloak.conf
Nginx 설정 검증
nginx -t
접속 테스트
- https://sso.example.com
- 외부에는 https(443/tcp) 외 포트는 개방하지 않는다.
Reverse Proxy 서버로 apache2를 사용한 방법 (다른 서버)
- 10.10.10.1(apache2) → 10.10.10.143(keycloak)
apache conf 작성
vi /etc/apache2/sites-available/sso.example.com.conf
<VirtualHost _default_:443>
ServerName sso.example.com
# =====================================================
# TLS
# =====================================================
SSLEngine on
SSLCertificateFile "/etc/ssl/example.com/fullchain.pem"
SSLCertificateKeyFile "/etc/ssl/example.com/privkey.pem"
SSLProtocol -all +TLSv1.2 +TLSv1.3
# =====================================================
# Reverse Proxy
# =====================================================
ProxyRequests Off
ProxyPreserveHost On
ProxyPass / http://10.10.10.143:8080/
ProxyPassReverse / http://10.10.10.143:8080/
# =====================================================
# Forwarded Headers
# =====================================================
RequestHeader set X-Forwarded-Proto "https"
RequestHeader set X-Forwarded-Port "443"
RequestHeader set X-Forwarded-Host "sso.example.com"
# =====================================================
# Timeout
# =====================================================
ProxyTimeout 60
# =====================================================
# Logs
# =====================================================
ErrorLog ${APACHE_LOG_DIR}/sso.example.com-error.log
CustomLog ${APACHE_LOG_DIR}/sso.example.com-access.log combined
</VirtualHost>
a2ensite sso.example.com.conf
Keycloak 기본 설정
설정 방법
realm(영역) 생성
- Manage realms > Create realm
- Realm name: myrealm (realm 명은 한글 금지, 공백 금지)
- Enabled: On 이제 모든 설정은 “myrealm” realm 에서 진행 된다.
비밀번호 정책 설정
- Authentication > Policies > Password policy
- Minimum length: 10
- Uppercase characters: 1
- Lowercase characters: 1
- Digits: 1
- Special characters: 1
- Not username: ON
Brute Force Detection 활성화
- Realm settings > Security defenses > Brute force detection
- Brute Force Mode: Lockout temporarily
- Max login failures: 5
- Failure reset time: 5 Hours
이메일 설정
- Realm settings > Email
- Template 적당히 설정
- Connection & Authentication
- Host: 127.0.0.1
- Allow UTF-8: Enabled 나머지는 모두 기본값 또는 OFF
사용자 직접 가입 비활성화
- Realm settings > Login
- Login screen customization: 아래 외 나머지는 모두 OFF
- Forgot password: ON (Realm settings > Email 설정 필요)
- Remember me: ON
- Email settings: 아래 외 나머지는 모두 OFF
- Email as username: ON (이메일을 ID로 사용)
- Login with email: ON (이메일로 로그인 가능)
- User info settings: 모두 OFF
그룹 생성
- Groups > Create group
- Name: employees
사용자 생성
- Users > Create new user
- Email verified: ON (이메일을 신뢰함)
- Username: myid@example.com
- Email: myid@example.com
- First name: 길동
- Last name: 홍
- Group: Join Groups > employees 선택
- Create > Save
비밀번호 설정
- Users > 사용자 선택
- Credentials > Set Password
- Temporary = OFF (ON으로 설정하면 초기 로그인 후 비밀번호 변경해야 함)
사용자 로그인 테스트
https://sso.example.com/realms/myrealm/account/
SAML 2.0 지원 여부 확인
https://sso.example.com/realms/myrealm/protocol/saml/descriptor
OIDC(OpenID Connect) 1.0 지원 여부 확인
https://sso.example.com/realms/myrealm/.well-known/openid-configuration
OAuth 2.0/OIDC의 기능
OAuth 2.0
|
| Authorization Framework
|
+--> "이 애플리케이션에 어떤 권한을 줄 것인가?"
OIDC
|
| OAuth 2.0 위에 Identity 계층 추가
|
+--> "이 사용자가 누구인가?"
[OAuth 2.0]
Access Token
↓
API 접근 권한
[OIDC]
ID Token
↓
사용자 인증 정보
↓
email
name
sub
...
