Linux SystemD Servisi
TL;DR — JAR'ı
mvn packageile üret,sudo ./install.shçalıştır, env dosyasını düzenle,sudo systemctl start mersel-dss-signer. Tamam.
Mersel DSS Signer API'yi Linux (Ubuntu / Debian / RHEL / Rocky / Alma) üzerinde hardened, otomatik-restart edebilen ve journalctl ile takip edilebilen bir SystemD servisi olarak çalıştırmak içindir.
İçindekiler
- 1. Klasör İçeriği
- 2. Hızlı Kurulum (otomatik)
- 3. Manuel Kurulum
- 4. Env Dosyası
- 5. Operasyon Komutları
- 6. PFX vs HSM Senaryoları
- 7. Hardening Notları
- 8. Reverse Proxy (nginx) Önerisi
- 9. Sorun Giderme
- 10. Kaldırma
1. Klasör İçeriği
devops/systemd/
├── mersel-dss-signer.service # SystemD unit dosyası (hardened)
├── mersel-dss-signer.env.example # EnvironmentFile şablonu
├── install.sh # Otomatik kurulum (user + dizinler + unit)
├── uninstall.sh # Temiz kaldırma (--purge ile env+log+user dahil)
└── README.md # Bu dosya
2. Hızlı Kurulum (otomatik)
# 1) Repo kökünden JAR üret
mvn clean package -DskipTests
# 2) Kurulum script'ini çalıştır (signer user + dizinler + unit yerleştirir)
sudo ./devops/systemd/install.sh
# 3) Env dosyasını düzenle — CERTIFICATE_PIN, PFX_PATH / PKCS11_LIBRARY vs.
sudo nano /etc/mersel-dss-signer/mersel-dss-signer.env
# 4) Servisi başlat
sudo systemctl start mersel-dss-signer
# 5) Sağlık kontrolü
curl http://localhost:8085/actuator/health
sudo journalctl -u mersel-dss-signer -f
install.sh idempotent'tir: birden çok kez çalıştırılabilir, mevcut env dosyasının üzerine yazmaz.
3. Manuel Kurulum
Script'i kullanmak istemiyorsan adımlar:
# Kullanıcı
sudo useradd --system --no-create-home \
--home-dir /opt/mersel-dss-signer \
--shell /usr/sbin/nologin signer
# Dizinler
sudo install -d -o signer -g signer -m 0755 /opt/mersel-dss-signer/{logs,work}
sudo install -d -o signer -g signer -m 0755 /var/log/mersel-dss-signer
sudo install -d -o root -g signer -m 0750 /etc/mersel-dss-signer/certs
# JAR
sudo install -o signer -g signer -m 0644 \
target/mersel-dss-signer-api-*.jar \
/opt/mersel-dss-signer/mersel-dss-signer-api.jar
# Unit
sudo install -o root -g root -m 0644 \
devops/systemd/mersel-dss-signer.service \
/etc/systemd/system/mersel-dss-signer.service
# Env
sudo install -o root -g signer -m 0640 \
devops/systemd/mersel-dss-signer.env.example \
/etc/mersel-dss-signer/mersel-dss-signer.env
sudo nano /etc/mersel-dss-signer/mersel-dss-signer.env # düzenle
# Etkinleştir + başlat
sudo systemctl daemon-reload
sudo systemctl enable --now mersel-dss-signer
4. Env Dosyası
/etc/mersel-dss-signer/mersel-dss-signer.env dosyasında secret değerler tutulur. Dosya izni 0640, sahibi root:signer olmalıdır — başka kullanıcılar PIN'i okuyamamalı.
sudo chmod 0640 /etc/mersel-dss-signer/mersel-dss-signer.env
sudo chown root:signer /etc/mersel-dss-signer/mersel-dss-signer.env
sudo ls -l /etc/mersel-dss-signer/mersel-dss-signer.env
# -rw-r----- 1 root signer ...
Önemli Değişkenler
| Değişken | Senaryo | Açıklama |
|---|---|---|
CERTIFICATE_PIN |
her senaryo | PFX parolası veya HSM PIN'i |
PFX_PATH |
PFX | PFX dosyasının tam yolu (/etc/mersel-dss-signer/certs/...) |
CERTIFICATE_ALIAS |
her senaryo | Alias (default: 1) |
PKCS11_LIBRARY |
HSM | AKİS sürücüsü yolu (.so) |
PKCS11_SLOT / PKCS11_SLOT_LIST_INDEX |
HSM | Slot seçimi (-1 = auto) |
IS_TUBITAK_TSP |
TSP | TÜBİTAK TSP modu (otomatik tespit, açıkça override edilebilir) |
TS_SERVER_HOST |
TSP | TSP endpoint URL |
TS_USER_ID / TS_USER_PASSWORD |
TSP | TÜBİTAK abone kimlik bilgileri |
XADES_SIGNING_TIME_ZONE |
XAdES | SigningTime timezone (default +03:00, issue #7) |
SERVER_PORT |
runtime | HTTP port (default 8085) |
LOG_LEVEL |
runtime | INFO / DEBUG / WARN |
SPRING_PROFILES_ACTIVE |
runtime | Genelde boş bırakın; test profili için local,pfx-kurum01-rsa2048 vb. (bkz. docs/RUN_PROFILES.md) |
Tam şablon: mersel-dss-signer.env.example
5. Operasyon Komutları
# Durum
sudo systemctl status mersel-dss-signer
# Logları izle (real-time)
sudo journalctl -u mersel-dss-signer -f
# Son 200 satır
sudo journalctl -u mersel-dss-signer -n 200 --no-pager
# Hata seviyesinde filtrele
sudo journalctl -u mersel-dss-signer -p err --since "1 hour ago"
# Restart
sudo systemctl restart mersel-dss-signer
# Durdur / Başlat
sudo systemctl stop mersel-dss-signer
sudo systemctl start mersel-dss-signer
# Boot'ta otomatik başlama on/off
sudo systemctl enable mersel-dss-signer
sudo systemctl disable mersel-dss-signer
# Env değişikliğini uygula (Spring property dosyası bunu okur):
sudo systemctl restart mersel-dss-signer
# Unit dosyası değişikliği sonrası:
sudo systemctl daemon-reload && sudo systemctl restart mersel-dss-signer
6. PFX vs HSM Senaryoları
PFX (Yumuşak Keystore)
Production PFX'i dağıtım sunucusuna manuel kopyala:
# Operatör laptop'undan
scp production.pfx admin@signer-prod:/tmp/
# Sunucuda
sudo install -o signer -g signer -m 0400 \
/tmp/production.pfx \
/etc/mersel-dss-signer/certs/production.pfx
sudo rm /tmp/production.pfx
Env dosyasında:
PFX_PATH=/etc/mersel-dss-signer/certs/production.pfx
CERTIFICATE_PIN=__GERCEK_PAROLA__
CERTIFICATE_ALIAS=1
HSM (AKİS / PKCS#11)
PC/SC + AKİS sürücüsü kurulumu (Ubuntu örneği):
# PC/SC daemon
sudo apt-get install -y pcscd pcsc-tools libccid
# AKİS sürücüsü — KamuSM resmi tar.gz veya distro paketi
# Manuel kurulum sonrası:
ls -la /usr/local/lib/libakisp11.so
# Servisleri başlat
sudo systemctl enable --now pcscd
# Kart algılama testi
pcsc_scan
# "AKIA Smartcard" veya benzeri tag görüyor olmalısın
Env dosyasında:
PKCS11_LIBRARY=/usr/local/lib/libakisp11.so
PKCS11_SLOT=-1
PKCS11_SLOT_LIST_INDEX=-1
PKCS11_NULL_INIT_ARGS=false
CERTIFICATE_PIN=__KART_PIN__
pcscdbağımlılığı: Unit dosyasındaWants=pcscd.servicevar. PFX-only deployment'larda zararsız — pcscd yoksa yine kalkar.
7. Hardening Notları
Unit dosyası şu kısıtları aktif eder:
| Direktif | Etki |
|---|---|
NoNewPrivileges=true |
setuid/setgid binary'ler yetki yükseltemez |
ProtectSystem=strict |
/usr, /boot, /etc salt-okunur |
ProtectHome=true |
/home, /root, /run/user görünmez |
PrivateTmp=true |
İzole /tmp (host /tmp'i etkilemez) |
ProtectKernelTunables/Modules=true |
sysctl/modprobe yasak |
RestrictNamespaces/Realtime/SUIDSGID=true |
Sandbox sıkılaştırma |
LockPersonality=true |
personality() syscall yasak |
LimitNOFILE=65536 |
TLS handshake bol soket için |
MemoryDenyWriteExecute=false Neden?
JVM'in JIT'i code cache'i RWX mapping ile tutar (G1 GC, C1/C2). MDWX=true verirsen JVM SIGSEGV ile crash eder. Bunu açık bırakmak güvenlik kaybı değil — JIT compilation'ın doğal davranışıdır.
PrivateDevices=no Neden?
true verirsen /dev/bus/usb görünmez, AKİS smart card okuyucu çalışmaz. PFX-only deployment'ta true'ya çevirebilirsin (ek hardening).
Ek Kısıt (opsiyonel)
Sadece localhost'tan bağlanılacaksa (örn. nginx önde):
[Service]
IPAddressAllow=127.0.0.1 ::1
IPAddressDeny=any
8. Reverse Proxy (nginx) Önerisi
Spring Boot built-in Tomcat'i public expose etmek yerine nginx arkasına alın:
upstream signer_api {
server 127.0.0.1:8085 max_fails=3 fail_timeout=30s;
keepalive 16;
}
server {
listen 443 ssl http2;
server_name sign-api.example.gov.tr;
ssl_certificate /etc/letsencrypt/live/sign-api.example.gov.tr/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/sign-api.example.gov.tr/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
# PDF / XML upload'ları için (application.properties default'u 200MB)
client_max_body_size 200M;
client_body_timeout 120s;
proxy_read_timeout 300s;
# Health endpoint dışarıya açılmasın
location /actuator/ {
allow 10.0.0.0/8;
allow 127.0.0.1;
deny all;
proxy_pass http://signer_api;
}
location / {
proxy_pass http://signer_api;
proxy_http_version 1.1;
proxy_set_header Connection "";
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;
}
}
9. Sorun Giderme
Servis kalkmıyor — "Permission denied"
sudo journalctl -u mersel-dss-signer -n 50
# Genelde PFX_PATH veya PKCS11_LIBRARY izinleri sorun
sudo ls -la /etc/mersel-dss-signer/certs/
sudo -u signer cat /etc/mersel-dss-signer/certs/production.pfx >/dev/null && echo OK
signer kullanıcısı PFX'i okuyamıyorsa: chown signer:signer ve chmod 0400.
"PKCS11 library not found"
# Sürücü kurulu mu?
ls -la /usr/local/lib/libakisp11.so /usr/lib/x86_64-linux-gnu/libakisp11.so 2>/dev/null
# pcscd çalışıyor mu?
systemctl status pcscd
# Kart görünüyor mu?
sudo -u signer pcsc_scan -n
"Address already in use" — port 8085
sudo ss -tlnp | grep 8085
# Başka süreç tutuyor — durdur veya SERVER_PORT'u env dosyasında değiştir
Env değişikliği uygulanmıyor
systemctl restart SHART, sadece reload yetmez:
sudo systemctl restart mersel-dss-signer
Spring Boot başlangıçta crash — "OOM"
Default heap 1g. Yüksek concurrency için env dosyasında:
JAVA_OPTS=-Xms512m -Xmx2g -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/mersel-dss-signer
journalctl boş çıkıyor
Log unit identifier'ı SHART eşleşmeli:
sudo journalctl -t mersel-dss-signer -n 100 # SyslogIdentifier ile
10. Kaldırma
# Servisi kaldır — env ve loglar KORUNUR
sudo ./devops/systemd/uninstall.sh
# Komple temizlik — env + log + user dahil
sudo ./devops/systemd/uninstall.sh --purge
İlgili Dokümanlar
- docs/RUN_PROFILES.md — Spring profile mimarisi
- devops/docker/ — Docker deployment (alternatif)
- devops/monitoring/ — Prometheus + Grafana entegrasyonu
- Ana dokümantasyon
Windows Servisi
TL;DR — JAR'ı build et,
.env'i doldur,Install-Service.ps1çalıştır (admin PowerShell). Tamam.
Mersel DSS Signer API'yi Windows Server (2019 / 2022) veya Windows 10/11 üzerinde otomatik başlayan, restart-on-failure ve Event Viewer'a düşen bir Windows servisi olarak çalıştırmak içindir.
İki yol gösteriyoruz:
- Birincil: WinSW — açık kaynak, XML config, .NET Framework 4.6.1+. Production için önerimiz.
- Alternatif: NSSM — yine yaygın, GUI'li wrapper. Hızlı PoC için.
İçindekiler
- 1. Klasör İçeriği
- 2. Ön Koşullar
- 3. Hızlı Kurulum (WinSW)
- 4. Env Dosyası
- 5. Manuel Kurulum (WinSW)
- 6. NSSM Alternatifi
- 7. Operasyon Komutları
- 8. PFX vs HSM Senaryoları
- 9. Güvenlik (NTFS ACL, Service Account)
- 10. Reverse Proxy (IIS / nginx) Önerisi
- 11. Sorun Giderme
- 12. Kaldırma
1. Klasör İçeriği
devops/windows-service/
├── mersel-dss-signer.xml # WinSW yapılandırma şablonu
├── mersel-dss-signer.env.example # Env şablonu (CRLF satır sonu)
├── Install-Service.ps1 # Otomatik kurulum (WinSW indirir, XML inject eder)
├── Uninstall-Service.ps1 # Temiz kaldırma (-KeepLogs, -Purge)
└── README.md # Bu dosya
2. Ön Koşullar
| Bileşen | Sürüm | Kontrol |
|---|---|---|
| Windows | Server 2019/2022 veya 10/11 | winver |
| .NET Framework | 4.6.1+ (WinSW NET461 build için) | Sistemde default |
| JDK / JRE | 8+ | java -version |
| Maven | 3.6+ (sadece build için) | mvn -v |
| PowerShell | 5.1+ | $PSVersionTable.PSVersion |
| İnternet | WinSW download için (release sayfası) | İlk install'da |
Admin haklarıyla PowerShell aç:
Start-Process powershell -Verb runAs
ExecutionPolicy kısıtlıysa script'ler için bir kerelik geçiş:
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
3. Hızlı Kurulum (WinSW)
# 1) Repo kökünden JAR üret
mvn clean package -DskipTests
# 2) Env şablonunu kopyala ve düzenle
cd .\devops\windows-service
Copy-Item .\mersel-dss-signer.env.example .\mersel-dss-signer.env
notepad .\mersel-dss-signer.env
# → CERTIFICATE_PIN, PFX_PATH / PKCS11_LIBRARY, TS_USER_* doldurulur
# 3) Servisi kur (admin PowerShell şart)
.\Install-Service.ps1
# 4) Sağlık kontrolü
Get-Service mersel-dss-signer
Invoke-WebRequest http://localhost:8085/actuator/health -UseBasicParsing |
Select-Object -ExpandProperty Content
Script otomatik olarak:
C:\Program Files\mersel-dss-signer\dizinini hazırlar- WinSW binary'sini GitHub'tan indirir (
v2.12.0default,-WinSwVersionile değiştirilebilir) - JAR'ı kopyalar
.envdosyasındaki KEY=VALUE'ları XML'in<env>bloklarına inject eder- XML dosyasına restrictive NTFS ACL uygular (sadece SYSTEM + Administrators okuyabilir)
winsw install+winsw startçağırır
4. Env Dosyası
mersel-dss-signer.env operatör tarafından oluşturulur (.example'dan kopyalanır). Repo'ya commit edilmez — .gitignore zaten *.env'i tutar.
Önemli Değişkenler
| Değişken | Senaryo | Açıklama |
|---|---|---|
CERTIFICATE_PIN |
her senaryo | PFX parolası veya HSM PIN'i |
PFX_PATH |
PFX | PFX dosyasının tam yolu |
CERTIFICATE_ALIAS |
her senaryo | Alias (default: 1) |
PKCS11_LIBRARY |
HSM | AKİS DLL yolu (C:\Windows\System32\akisp11.dll) |
PKCS11_SLOT / PKCS11_SLOT_LIST_INDEX |
HSM | -1 = auto |
IS_TUBITAK_TSP |
TSP | TÜBİTAK TSP modu (otomatik tespit) |
TS_USER_ID / TS_USER_PASSWORD |
TSP | TÜBİTAK abone kimlik bilgileri |
XADES_SIGNING_TIME_ZONE |
XAdES | +03:00 (default), Z (ETSI saf yorumu) — issue #7 |
SERVER_PORT |
runtime | HTTP port (default 8085) |
LOG_LEVEL |
runtime | INFO / DEBUG |
SPRING_PROFILES_ACTIVE |
runtime | Production'da boş; test için local,pfx-kurum01-rsa2048 (docs/RUN_PROFILES.md) |
Tam liste: mersel-dss-signer.env.example
Önemli:
.envdosyasını XML'e inject ettiğimiz için sonradan.env'i değiştirsen bile servis görmez. Değişiklik sonrasıInstall-Service.ps1'i yeniden çalıştır — XML güncellenir ve servis restart edilir.
5. Manuel Kurulum (WinSW)
Script kullanmak istemiyorsan ya da troubleshooting yapıyorsan:
# 1) İndir
$installDir = "C:\Program Files\mersel-dss-signer"
New-Item -Path $installDir -ItemType Directory -Force
$winsw = "$installDir\mersel-dss-signer.exe"
Invoke-WebRequest `
-Uri "https://github.com/winsw/winsw/releases/download/v2.12.0/WinSW.NET461.exe" `
-OutFile $winsw
# 2) JAR + XML kopyala
Copy-Item .\target\mersel-dss-signer-api-*.jar `
"$installDir\mersel-dss-signer-api.jar"
Copy-Item .\devops\windows-service\mersel-dss-signer.xml `
"$installDir\mersel-dss-signer.xml"
# 3) XML'i düzenle (env değerleri MANUEL inject)
notepad "$installDir\mersel-dss-signer.xml"
# <env name="CERTIFICATE_PIN" value="..."/> vs.
# yorum işaretlerini kaldır ve değerleri yaz
# 4) Servis kayıt + başlat
& $winsw install
& $winsw start
# 5) Kontrol
Get-Service mersel-dss-signer
6. NSSM Alternatifi
NSSM grafik yardımıyla servis tanımı yapan basit bir wrapper'dır. WinSW'e göre daha az feature'lı (no <env> injection, no XML-driven config) ama hızlı POC için iyi.
# 1) NSSM indir (https://nssm.cc/download)
$nssm = "C:\Tools\nssm.exe"
# 2) GUI ile interactive ekran
& $nssm install mersel-dss-signer
# Application path : java
# Arguments : -Xms256m -Xmx1g -jar "C:\Program Files\mersel-dss-signer\mersel-dss-signer-api.jar"
# Startup directory: C:\Program Files\mersel-dss-signer
# I/O tab : stdout / stderr → logs\nssm-out.log, logs\nssm-err.log
# Environment tab : CERTIFICATE_PIN, PFX_PATH, ...
# 3) Otomatik başlatma
& $nssm set mersel-dss-signer Start SERVICE_AUTO_START
& $nssm set mersel-dss-signer AppRestartDelay 10000
& $nssm set mersel-dss-signer AppExit Default Restart
# 4) Başlat
Start-Service mersel-dss-signer
Production önerisi: WinSW kullan. XML versiyonlanır, env injection script ile audit-trail'e girer; NSSM ise registry-only yapılandırma tutar, "neyin canlıda olduğunu" görmek için
nssm getile her property'yi tek tek sorgulamak gerekir.
7. Operasyon Komutları
# Durum
Get-Service mersel-dss-signer
sc.exe query mersel-dss-signer
# Detaylı durum (WinSW)
& "C:\Program Files\mersel-dss-signer\mersel-dss-signer.exe" status
# Restart
Restart-Service mersel-dss-signer
# Stop / Start
Stop-Service mersel-dss-signer
Start-Service mersel-dss-signer
# Logları izle (real-time)
Get-Content "C:\Program Files\mersel-dss-signer\logs\mersel-dss-signer.out.log" `
-Wait -Tail 50
# Son 200 satır
Get-Content "C:\Program Files\mersel-dss-signer\logs\mersel-dss-signer.out.log" `
-Tail 200
# Event Viewer'da görüntüle
Get-EventLog -LogName Application -Source "mersel-dss-signer" -Newest 50
# Env değişikliği yaptın → Install scriptini yeniden çalıştır:
.\Install-Service.ps1
8. PFX vs HSM Senaryoları
PFX (Yumuşak Keystore)
PFX'i C:\ProgramData\mersel-dss-signer\certs\ altına koy ve NTFS ACL ile kilitle:
$pfx = "C:\ProgramData\mersel-dss-signer\certs\production.pfx"
$dir = Split-Path -Parent $pfx
New-Item -Path $dir -ItemType Directory -Force
Copy-Item .\production.pfx $pfx
# Sadece SYSTEM + Administrators
icacls $pfx /inheritance:r `
/grant:r "NT AUTHORITY\SYSTEM:F" `
/grant:r "BUILTIN\Administrators:F"
Env dosyasında:
PFX_PATH=C:\ProgramData\mersel-dss-signer\certs\production.pfx
CERTIFICATE_PIN=__GERCEK_PAROLA__
CERTIFICATE_ALIAS=1
HSM (AKİS Windows)
Kritik 32-bit/64-bit uyumu: 64-bit JVM kullanıyorsan akisp11.dll da 64-bit olmalı. KamuSM resmi installer x86 (32-bit) DLL kurabilir; bu durumda 64-bit JVM UnsatisfiedLinkError atar. Çözüm:
# JVM 64-bit mi?
java -d64 -version
# Çıktıda "64-Bit" görmeli
# DLL bit-genişliği
[System.Reflection.AssemblyName]::GetAssemblyName("C:\Windows\System32\akisp11.dll").ProcessorArchitecture
# veya dumpbin /headers C:\Windows\System32\akisp11.dll | findstr machine
Smart card servisi:
Get-Service SCardSvr
# Status: Running olmalı (WinSW XML'inde Depend tanımlı)
# Kart algılama
certutil -scinfo
Env dosyasında:
PKCS11_LIBRARY=C:\Windows\System32\akisp11.dll
PKCS11_SLOT=-1
PKCS11_SLOT_LIST_INDEX=-1
PKCS11_NULL_INIT_ARGS=false
CERTIFICATE_PIN=__KART_PIN__
9. Güvenlik (NTFS ACL, Service Account)
XML dosyası
Install-Service.ps1 XML'e restrictive ACL uygular — sadece SYSTEM + Administrators okuyabilir. Doğrula:
icacls "C:\Program Files\mersel-dss-signer\mersel-dss-signer.xml"
# Çıktıda Users veya Everyone GÖRÜNMEMELİ
Service Account (önerilen)
Default'ta servis LocalSystem olarak çalışır (yüksek yetki). Production'da dedicated low-privilege account kullan:
# 1) Local user oluştur (veya Domain'de AD account)
$secPwd = Read-Host -AsSecureString "svc_mersel_signer parolası"
New-LocalUser -Name "svc_mersel_signer" `
-Password $secPwd `
-Description "Mersel DSS Signer servis hesabı" `
-PasswordNeverExpires `
-UserMayNotChangePassword
# 2) "Log on as a service" hakkı ver
# Local Security Policy → Local Policies → User Rights Assignment →
# "Log on as a service" → Add User → svc_mersel_signer
# (Veya `secedit` ile programatik olarak — uzun, atlıyoruz)
# 3) Servisi bu hesapla kur
$secPwd2 = Read-Host -AsSecureString "svc_mersel_signer parolası (tekrar)"
.\Install-Service.ps1 -ServiceAccount ".\svc_mersel_signer" `
-ServicePassword $secPwd2
# 4) İzinler — service account log + cert dizinine yazabilsin
icacls "C:\Program Files\mersel-dss-signer\logs" `
/grant ".\svc_mersel_signer:(OI)(CI)M"
icacls "C:\ProgramData\mersel-dss-signer\certs" `
/grant ".\svc_mersel_signer:R"
Windows Firewall
Sign API'yi sadece localhost'tan kabul etmek için (reverse proxy önde):
New-NetFirewallRule -DisplayName "Mersel DSS Signer (localhost only)" `
-Direction Inbound -Protocol TCP `
-LocalPort 8085 -RemoteAddress 127.0.0.1,::1 `
-Action Allow
# Outbound default allow — KamuSM TSP/OCSP/AIA için açık kalır
10. Reverse Proxy (IIS / nginx) Önerisi
Spring Boot'un built-in Tomcat'ini doğrudan dış dünyaya açmak yerine bir reverse proxy arkasına alın.
IIS (Windows Server'da default)
URL Rewrite + ARR (Application Request Routing) modüllerini kur, sonra web.config:
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="ReverseProxyToSigner" stopProcessing="true">
<match url="(.*)" />
<action type="Rewrite" url="http://localhost:8085/{R:1}" />
</rule>
</rules>
</rewrite>
<security>
<requestFiltering>
<!-- application.properties default'u 200 MB -->
<requestLimits maxAllowedContentLength="209715200" />
</requestFiltering>
</security>
<httpProtocol>
<customHeaders>
<add name="Strict-Transport-Security" value="max-age=31536000; includeSubDomains" />
</customHeaders>
</httpProtocol>
</system.webServer>
</configuration>
nginx (Linux subsystem veya container)
Bkz. SystemD README — Reverse Proxy bölümü.
11. Sorun Giderme
Servis kalkmıyor — Event Viewer
# Event Viewer → Windows Logs → Application
Get-EventLog -LogName Application -Source "mersel-dss-signer" -Newest 20 |
Format-List Source, EventID, Message
WinSW log dosyaları:
$logDir = "C:\Program Files\mersel-dss-signer\logs"
Get-ChildItem $logDir
# mersel-dss-signer.out.log → stdout (Spring Boot)
# mersel-dss-signer.err.log → stderr
# mersel-dss-signer.wrapper.log → WinSW kendi log'u
"java is not recognized"
java PATH'te değil. Çözüm: Install-Service.ps1 -JavaHome ile tam yol ver:
.\Install-Service.ps1 -JavaHome "C:\Program Files\Eclipse Adoptium\jdk-8.0.412.8-hotspot"
Servis sürekli restart oluyor (crash loop)
Get-Service "Running" → "Stopped" arası geziyor. WinSW XML'de <onfailure action="restart"> üç kez denedikten sonra durur.
# Wrapper log'unda crash sebebini ara
Get-Content "C:\Program Files\mersel-dss-signer\logs\mersel-dss-signer.wrapper.log" -Tail 100
# "Java application has exited with code N" → exit code Spring Boot hatası
Genelde:
CERTIFICATE_PINyanlış — PFX/HSM yetkilendirme ekranıPKCS11_LIBRARYyolu yanlış — DLL bulunamadı- Port 8085 başka süreç tarafından tutuluyor —
Get-NetTCPConnection -LocalPort 8085
"UnsatisfiedLinkError" — PKCS#11
64-bit / 32-bit uyumsuzluğu. Bkz. Bölüm 8 — HSM.
Env değişikliği uygulanmıyor
mersel-dss-signer.env'i değiştirip Restart-Service yetmez — XML'e inject edilmiş hâli yeniden üretmek gerekir:
# Install scriptini yeniden çalıştır
.\Install-Service.ps1
# script servisi durdurur, XML'i yenisiyle değiştirir, yeniden başlatır
Smart card görünmüyor
# PC/SC servisi
Get-Service SCardSvr
Restart-Service SCardSvr
# Kart okuyucu listesi
certutil -scinfo
# Sürücü tanımlı mı?
Get-PnpDevice -Class SmartCardReader
12. Kaldırma
# Default — install dizini silinir, ProgramData korunur
.\Uninstall-Service.ps1
# Logları post-mortem için %TEMP%'e yedekle
.\Uninstall-Service.ps1 -KeepLogs
# Komple temizlik — env + cert dizini DAHİL
.\Uninstall-Service.ps1 -Purge
İlgili Dokümanlar
- docs/RUN_PROFILES.md — Spring profile mimarisi
- devops/systemd/ — Linux SystemD muadili
- devops/docker/ — Docker deployment (Linux container'da hızlı PoC)
- devops/monitoring/ — Prometheus + Grafana
- WinSW resmi dokümanı
- NSSM resmi dokümanı
- Ana dokümantasyon
Docker Deployment
Mersel DSS Signer API'nin container imajı ve compose stack'i.
İçerik
devops/docker/
├── Dockerfile # Production multi-stage (maven build → JRE 8 runtime)
├── Dockerfile.pkcs11-tests # Integration test image (softhsm2 + opensc + JDK)
├── docker-compose.yml # sign-api + prometheus + grafana (+ alertmanager profile)
├── .dockerignore
├── .env.example # Production env şablonu
├── .env.test.kurum1 # Test Kurum 1 (RSA-2048, PIN 614573)
├── .env -> .env.test.kurum1 # Symlink → default development env
├── unix/start-test-kurum.sh # Parametreli helper (./start-test-kurum.sh 2 ec384)
├── windows/start-test-kurum.ps1 # PowerShell muadili
└── README.md
Not: Mevcut dosya tek parametreli helper script'tir (kurum_no + cert_type). Eski README'deki
start-test-kurum1.sh,start-test-kurum2.shgibi ayrı dosyalar konsolide edildi.
Hızlı Başlangıç
En hızlı yol — varsayılan test sertifikası
cd devops/docker
docker-compose up -d
Varsayılan olarak .env -> .env.test.kurum1 symlink'i devreye girer (RSA-2048 test PFX, network kapalı).
Parametreli helper ile farklı kurum / algoritma
# Unix/Linux/macOS
./unix/start-test-kurum.sh 1 # Kurum 1 RSA-2048 (default)
./unix/start-test-kurum.sh 2 rsa # Kurum 2 RSA-2048
./unix/start-test-kurum.sh 2 ec384 # Kurum 2 EC-P384
./unix/start-test-kurum.sh 3 rsa # Kurum 3 RSA-2048
./unix/start-test-kurum.sh 3 ec384 # Kurum 3 EC-P384
# Windows (PowerShell)
.\windows\start-test-kurum.ps1 1
.\windows\start-test-kurum.ps1 2 ec384
.\windows\start-test-kurum.ps1 3 rsa
Script .env.temp üretir ve docker-compose --env-file .env.temp up -d çağırır. Compose'un kendi env dosyasını kirletmez.
Production
# .env.example'dan kendi env dosyanı oluştur
cp .env.example .env.production
nano .env.production # PFX_PATH, CERTIFICATE_PIN, TS_USER_*, vb.
# Production env ile başlat
docker-compose --env-file .env.production up -d
PFX dosyasını volumed olarak içeri al:
# docker-compose.yml içinde
volumes:
- /etc/mersel-dss-signer/certs:/app/certs:ro
Production önerisi: Bare-metal Linux'ta SystemD ile çalışmak smart card / AKİS HSM senaryolarında daha az operasyonel acı verir. Bkz.
devops/systemd/.
Endpoint'ler
| Servis | URL | Default kimlik |
|---|---|---|
| Sign API | http://localhost:8085 | — |
| Health | http://localhost:8085/actuator/health | — |
| Prometheus metrics | http://localhost:8085/actuator/prometheus | — |
| OpenAPI JSON | http://localhost:8085/api-docs | — |
| Prometheus UI | http://localhost:9090 | — |
| Grafana | http://localhost:3000 | admin / admin |
| AlertManager | http://localhost:9093 (--profile monitoring-full) |
— |
Test Sertifikaları (repo içi)
| Kurum | Algoritma | PFX | PIN |
|---|---|---|---|
| Kurum 1 | RSA-2048 | testkurum01_rsa2048@test.com.tr_614573.pfx |
614573 |
| Kurum 2 | RSA-2048 | testkurum02_rsa2048@sm.gov.tr_059025.pfx |
059025 |
| Kurum 2 | EC-P384 | testkurum02_ec384@test.com.tr_825095.pfx |
825095 |
| Kurum 3 | RSA-2048 | testkurum03_rsa2048@test.com.tr_181193.pfx |
181193 |
| Kurum 3 | EC-P384 | testkurum03_ec384@test.com.tr_540425.pfx |
540425 |
Yerleşim: resources/test-certs/ — Dockerfile build-time'da /app/test-certs/ altına kopyalar (development image için). Production'da kendi PFX'inizi bind-mount edersiniz.
Compose Profile'ları
# Default: sign-api + prometheus + grafana
docker-compose up -d
# AlertManager dahil
docker-compose --profile monitoring-full up -d
# Sadece sign-api (prometheus/grafana olmadan)
docker-compose up -d sign-api
Operasyon Komutları
# Loglar
docker-compose logs -f sign-api
# Status
docker-compose ps
# Restart
docker-compose restart sign-api
# Tek servisin env'ini gör
docker-compose exec sign-api env | grep -E "(PFX|PKCS11|TS_)"
# Container'ın içine gir
docker-compose exec sign-api sh
# Container'da Java thread dump (deadlock şüphesi için)
docker-compose exec sign-api sh -c 'kill -3 1; sleep 1; cat /proc/1/fd/1' | head -200
# Cleanup
docker-compose down # container + network
docker-compose down -v # YUKARI + volume'ler (Prometheus retention dahil)
docker-compose down --rmi all # YUKARI + image'ler
Image Build
# Standart build
docker-compose build sign-api
# No-cache (dependency güncellemesi sonrası)
docker-compose build --no-cache sign-api
# Multi-arch (CI gibi)
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t mersel/dss-signer-api:0.4.0 \
-f devops/docker/Dockerfile \
../..
Dockerfile iki-aşamalı: maven:3.8-openjdk-8 ile build, eclipse-temurin:8-jre ile runtime. Runtime image yaklaşık 250 MB, fat-jar 60-80 MB.
Production Hardening Önerileri
# docker-compose.yml içinde sign-api servisine ekle
services:
sign-api:
read_only: true
tmpfs:
- /tmp
- /app/logs:size=512m
cap_drop: [ALL]
cap_add:
- NET_BIND_SERVICE # 8085 < 1024 değilse gereksiz
security_opt:
- no-new-privileges:true
deploy:
resources:
limits:
cpus: '2'
memory: 2G
reservations:
memory: 512M
AKİS HSM senaryosunda
read_only: trueile PC/SC socket'i uyumlu çalışabilir; ama smart card için zaten bind-mount +privileged: truegerektiğinden container yaklaşımı önerilmez.
Sorun Giderme
# Container kalkmıyor — exit code'a bak
docker-compose ps -a
docker-compose logs sign-api | tail -100
# "exec format error" — multi-arch mismatch
docker inspect sign-api --format '{{.Config.Image}} {{.Image}}'
# arm64 host'ta amd64 image: docker pull --platform linux/arm64/v8 ...
# Spring Boot kalkmadı — health check 60sn start_period bekler
docker-compose logs sign-api 2>&1 | grep -i "started\|error"
# Network sorunu — sign-api ↔ prometheus
docker-compose exec prometheus wget -qO- http://sign-api:8085/actuator/prometheus | head
# Volume yetki sorunu (özellikle Linux host'ta)
docker-compose exec sign-api ls -la /app/certs /app/logs
# UID 1000 (signapi) yazabiliyor mu?
İlgili Dokümanlar
devops/README.md— Genel deployment matrisidevops/systemd/— Linux native servis (smart card için tercih)devops/windows-service/— Windows native servisdevops/monitoring/— Prometheus/Grafana detaylarıdocs/RUN_PROFILES.md— Spring profile mimarisi- Docker Deployment Guide — Online doc
Mersel DSS Signer — Run / Debug Profilleri
TL;DR — Repo'yu klonla, JDK 8+ kurulu olsun, IDE'de aç.
.run/klasöründeki "Signer · Default (KURUM01 RSA-2048)" config'iyle direkt Run veya Debug bas. PFX repo'da hazır, ortam değişkeni set etmek gerekmiyor.
Bu doküman üç farklı geliştirici kanalını anlatır:
- IntelliJ IDEA / Cursor —
.run/*.run.xmlshared configurations - VS Code —
.vscode/launch.json+.vscode/tasks.json - CLI —
scripts/dev-run.sh(POSIX) vescripts/dev-run.bat(Windows)
Üç kanal da aynı Spring profile katmanlarını kullanır; farklı bir keystore'a geçmek için tek tıkla / tek argümanla profil değişiyor.
1. Spring Profile Mimarisi
Bu repo "composable" Spring profile yaklaşımıyla çalışır. Her profile tek bir konuya sahiptir, üst üste yığılır:
| Profile | Sorumluluk |
|---|---|
local |
Network'i kapatır (CertificateChain offline, KamuSM TSP off, trusted root resolver no-op), DEBUG log seviyesini açar. Tüm dev senaryolar bunu içerir. |
pfx-kurum01-rsa2048 |
KURUM01 RSA-2048 test PFX (default sahnede otomatik aktif) |
pfx-kurum02-rsa2048 |
KURUM02 RSA-2048 (sm.gov.tr sahipli) |
pfx-kurum02-ec384 |
KURUM02 EC-P384 — ECDSA imza yolu testi |
pfx-kurum03-rsa2048 |
KURUM03 RSA-2048 |
pfx-kurum03-ec384 |
KURUM03 EC-P384 |
mali-muhur-akis-mac |
TÜBİTAK Mali Mühür AKİS macOS sürücüsü (/usr/local/lib/libakisp11.dylib) |
mali-muhur-akis-linux |
Mali Mühür AKİS Linux sürücüsü (/usr/local/lib/libakisp11.so) |
mali-muhur-akis-windows |
Mali Mühür AKİS Windows sürücüsü (C:/Windows/System32/akisp11.dll) |
Aktivasyon Kuralı
Her zaman local + bir alt-profile birlikte aktive edilir:
--spring.profiles.active=local,pfx-kurum02-ec384
--spring.profiles.active=local,mali-muhur-akis-mac
local ortak ortam ayarlarını tek bir yerden çekmemizi sağlıyor (DRY); alt-profile yalnızca keystore'a özgü override'ları taşır.
PFX vs HSM — Neden Ayrı?
- PFX profilleri repo'daki KamuSM publicly published test sertifikalarını kullanır. PIN dosya adında açıkta; geliştirici hiçbir env var girmek zorunda değil. Üç OS'te de aynı dosya çalışır —
PFX_PATHrepo-relatif. - HSM profilleri OS'in PKCS#11 sürücü yolunu farklı yerden aldığı için ayrılmıştır. Operatör akıllı kart takıp
CERTIFICATE_PINenv'ini doldurur; PFX'in aksine kart bağımlı olduğu için profile dosyasında PIN tutulmaz.
2. IntelliJ IDEA / Cursor
.run/ klasörü repo'ya commit'li; IDE bunları otomatik tanır.
Mevcut configurations
| Config adı | Profile | Notu |
|---|---|---|
Signer · Default (KURUM01 RSA-2048) |
local,pfx-kurum01-rsa2048 |
Yeni geliştirici için başlangıç noktası |
Signer · PFX RSA-2048 (KURUM02 sm.gov.tr) |
local,pfx-kurum02-rsa2048 |
sm.gov.tr sahipli alternatif RSA |
Signer · PFX EC-384 (KURUM02) |
local,pfx-kurum02-ec384 |
ECDSA r |
Signer · PFX RSA-2048 (KURUM03) |
local,pfx-kurum03-rsa2048 |
Üçüncü kurum, RSA |
Signer · PFX EC-384 (KURUM03) |
local,pfx-kurum03-ec384 |
Üçüncü kurum, EC |
Signer · Mali Mühür AKİS (macOS) |
local,mali-muhur-akis-mac |
Gerçek AKİS kartı + PIN |
Signer · Mali Mühür AKİS (Linux) |
local,mali-muhur-akis-linux |
Gerçek AKİS kartı + PIN |
Signer · Mali Mühür AKİS (Windows) |
local,mali-muhur-akis-windows |
Gerçek AKİS kartı + PIN |
CLI · List PFX Certificates (KURUM01) |
(yok — --list-certificates) |
Spring kalkmaz, sadece PFX içeriğini stdout'a basar |
Tests · Unit (mvn test) |
— | Default suite (377 test, ~50 sn) |
Tests · XAdES SigningTime (issue 7) |
— | Issue #7 regression suite |
Tests · Verifier E2E (Docker) |
— | Verifier-e2e tag'li suite (Docker gerekir) |
Run / Debug
- IntelliJ üst-sağ Run dropdown'ından config'i seç.
- Run (▶) veya Debug (🐞). Debug breakpoint'leri direkt çalışır.
- HSM senaryolarında ilk run öncesi config'i "Edit Configurations…" ile aç,
CERTIFICATE_PINenv'indeREPLACE_WITH_YOUR_PINyazısını kendi PIN'inle değiştir.
JDK ayarı: IntelliJ "Project Structure → Project SDK" sekmesinden 8+ bir JDK seçtiğin sürece bu config'ler ek JRE path'i istemez. Repo Java 8 baseline ile build edilir.
3. VS Code (Java Extension Pack)
.vscode/launch.json aynı senaryoları VS Code Java debugger'ına maple eder.
Run / Debug
- Run and Debug panelini aç (
Ctrl+Shift+D/⇧⌘D). - Üstteki dropdown'dan senaryoyu seç (örn.
Signer :: PFX EC-384 (KURUM02)). - Yeşil ▶ butonuna bas — debugger attach edilir.
HSM senaryolarında launch.json içindeki env.CERTIFICATE_PIN field'ını kendi PIN'inle değiştir.
Tasks
.vscode/tasks.json aşağıdaki Maven hedeflerini sağlar:
- mvn test (unit) — default test suite (
Ctrl+Shift+Bile çağrılan default) - mvn test - XAdES SigningTime (issue 7) — sadece SigningTime regression
- mvn test - Verifier E2E (Docker) — verifier-e2e tag'li
- mvn spring-boot:run (local default) — terminal'de Spring Boot başlatır
Ctrl+Shift+P → "Tasks: Run Task" ile çağrılır.
4. CLI (IDE'siz)
JDK + Maven kurulu makinede IDE açmadan koşmak için.
# Default (KURUM01 RSA), OS auto-detect
./scripts/dev-run.sh
# Diğer senaryolar
./scripts/dev-run.sh kurum02-ec384
./scripts/dev-run.sh kurum03-rsa2048
# Mali Mühür AKİS — OS'a göre doğru profile seçilir
CERTIFICATE_PIN=1234 ./scripts/dev-run.sh mali-muhur-akis
# Mevcut senaryoları listele
./scripts/dev-run.sh list
Windows:
REM Default
scripts\dev-run.bat
REM EC-P384
scripts\dev-run.bat kurum02-ec384
REM HSM (PIN env ile)
set CERTIFICATE_PIN=1234
scripts\dev-run.bat mali-muhur-akis
Script OSTYPE / uname ile OS tespiti yapar, mvn veya ./mvnw arar; HSM senaryosunda PIN yoksa fail-fast davranır.
5. Sertifika Listeleme (Smoke Test)
Spring container kaldırmadan PFX'in sağlıklı olduğunu hızlıca doğrulamak için:
IntelliJ: CLI · List PFX Certificates (KURUM01) config'ini çalıştır.
VS Code: CLI :: List PFX Certificates (KURUM01) debug config'ini çalıştır.
CLI (POSIX):
PFX_PATH=resources/test-certs/testkurum01_rsa2048@test.com.tr_614573.pfx \
CERTIFICATE_PIN=614573 \
mvn -q exec:java -Dexec.mainClass=io.mersel.dss.signer.api.SignatureApplication \
-Dexec.args="--list-certificates"
6. Sık Karşılaşılan Sorunlar
"PFX dosyası bulunamadı"
PFX yolu repo-relatif (resources/test-certs/...). IDE Run Configuration WORKING_DIRECTORY=$PROJECT_DIR$ koymuş; eğer dosya bulunamıyorsa çalışma dizini repo kökü değil demektir. .run/*.run.xml içindeki WORKING_DIRECTORY değerini kontrol et.
HSM "token bulunamadı"
- Kart fiziksel olarak takılı mı? (macOS:
pcsctest, Linux:pcsc_scan, Windows: cihaz yöneticisi) PKCS11_LIBRARYenv'i doğru yolu gösteriyor mu? Profile dosyasındaki default'lar OS-spesifik ama operatör farklı kurulum yaptıysa env override gerekir.- macOS'ta AKİS sürücüsü
CKR_ARGUMENTS_BADveriyorsaPKCS11_NULL_INIT_ARGS=truezatenapplication-mali-muhur-akis-mac.properties'te aktif.
"Active Profiles ekranda görünmüyor"
IntelliJ Spring Boot run config'inde "Active Profiles" alanı boşsa, "Edit Configurations…" → ilgili config'i seç → "Active Profiles" alanını kontrol et. Bu repo'daki configs local,pfx-kurum01-rsa2048 formatında (virgülle ayrılmış) bekler.
Production'a Sızma Endişesi
local profile sadece explicit aktivasyon ile yüklenir. Production deployment java -jar ile çalıştırıldığında SPRING_PROFILES_ACTIVE set edilmediği için bu dosyalar hiç parse edilmez. Test PFX'leri repo'da olmasına rağmen production env'ine taşınmaz; production'da HSM'den PIN gelir.
7. Yeni Senaryo Eklemek
- Yeni
application-pfx-<key>.propertiesveyaapplication-mali-muhur-<key>-<os>.propertiesdosyasını oluştur. .run/altına bir XML kopyala-yapıştır,ACTIVE_PROFILESdeğerini güncelle..vscode/launch.jsonarray'ine yeni entry ekle.scripts/dev-run.shvedev-run.batresolve_profileswitch'lerine ekle.- Bu dokümandaki tabloları güncelle.
Tek bir kaynaktan profile metadata yönetmek istersek (örn. JSON),
dev-run.sho JSON'u tüketen bir templating'e geçirilebilir. Şu an 5 PFX + 3 HSM kombinasyonu için manuel düzen yeterli; sahnede daha fazla cert tipi çoğalırsa o yapıyı kurarız.