在 Windows 環境下運行應用程式時,將其註冊為系統服務能大幅提升管理效率。
這篇文章將介紹 NSSM(Non-Sucking Service Manager)這個強大工具,
手把手教你如何快速建立 Windows 服務。
除了介紹 NSSM 與基礎的服務建立步驟,我們還會帶你瞭解 Windows 服務背後的權限原理,
探討如何安全地授予一般權限使用者開啟或關閉指定服務的權限,讓團隊協作更靈活,DevOps 流程更順暢。無論你是初學者或經驗豐富的系統管理員,這篇文章都能幫助你提升 Windows 服務管理的效率,讓你對 Windows 服務有更近一步的瞭解。
TL;DR
使用 NSSM (Non-Sucking Service Manager) 註冊服務
nssm 為了以後設定方便,使用指令來操作
假設我有一個 Windows 服務,名叫 MyService 你可以這麼做
(執行以下指令需使用 cmd 系統管理員權限)
nssm install "MyService" "C:\Java\bin\java.exe" "-jar C:\MyService\app.jar"
nssm set MyService AppDirectory "C:\MyService\"
nssm set MyService Description "This is my service"
每行指令說明:
- 使用 nssm 註冊服務,用雙引號把所需要的參數括弧包起來
- 設定程式起始路徑
- 設定服務的說明
然後是最關鍵的調整權限:
(使用系統管理員的 Powershell 來執行)
Adjust-ServicePermissions.ps1 -Username myuser -ServiceName MyService
程式片段在此:
https://gist.github.com/j796160836/72346b43a315055caeebb69d7c3db76f
用法很簡單,就二個參數:
Username帶入指定的使用者(一般使用者)的帳號名稱ServiceName帶入指定的 Windows 服務名稱
會給你對應的提示,按下 y 開始執行套用。
這邊已經把程式用 Script 包裝好了,帶入所需的參數即可。
接下來,我們來細講這些東西
nssm 服務管理器介紹與使用
nssm 當初的取名很有趣:Non-Sucking Service Manager
因為原作者覺得Windows 內建的 Windows 服務註冊工具實在都太難用了,
太爛、太 Suck 了!所以原作者想寫一個不難用的 (Non-Sucking) 服務管理器,故得名。
大部分的一般應用程式,都可以用 nssm 來註冊 Windows 服務。
nssm 他有 GUI 圖形介面,但為了以後設定方便,甚至做成 init scripts,建議還是使用指令來操作,以下也都是介紹指令。
你可以用以下的操作:
nssm 註冊安裝服務
假設你要單獨執行的指令 (測試指令) 如下:
C:\Java\bin\java.exe -jar C:\MyService\app.jar
註冊服務
欲把上述指令註冊成 Windows 服務,名字叫做 MyService
(這邊要用 cmd 系統管理員執行)
nssm install MyService "C:\Java\bin\java.exe" "-jar C:\MyService\app.jar"
要用雙引號把所需要的參數括弧包起來
設定服務起始路徑 (Startup Path)
設定程式起始路徑
(這邊要用 cmd 系統管理員執行)
nssm set MyService AppDirectory "C:\MyService\"
設定服務說明
設定 MyService 服務的說明
(這邊要用 cmd 系統管理員執行)
nssm set MyService Description "This is my service"
這二個指令都很直覺,就不細講了
移除服務
如果不小心弄錯了,可以用這個指令解除註冊
(這邊要用 cmd 系統管理員執行)
nssm remove MyService
實測只限於使用 nssm 註冊的服務
授予一般使用者開關指定服務的權限(手動步驟)
通常來說,Windows 服務只能管理員帳號 (Administrators)
才能做開關,但這樣權限實在太大了
基於「最小化權限原則」,我們能不能讓一般使用者,針對特定服務授予開啟與關閉的權限呢?
答案是可以的!但實在有點複雜…
我們先列出手動操作的步驟。
Step 1. 列出使用者的 sid
在 cmd 執行該指令,取得 sid
wmic useraccount where name='Tony' get sid
(假設你建立的一般使用者叫做 Tony)
記錄一下過程
wmic useraccount where name='Tony' get sid
SID
S-1-5-xx-xxxxxxxx-xxxxxxxxx-xxxxxxxxxx-xx07
你就會得到類似這樣的 SID 先記下來備用
Step 2. 列出預設權限
先記下預設權限,這很重要 (以下呈現的結果供參考,以你實際的為主)
在 cmd 執行該指令,列出指定服務的權限
sc sdshow myService
D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;SU)
在 cmd 執行該指令,列出 Service Control Manager (SCM) 的權限
sc sdshow SCMANAGER
D:(A;;CC;;;AU)(A;;CCLCRPRC;;;IU)(A;;CCLCRPRC;;;SU)(A;;CCLCRPWPRC;;;SY)(A;;KA;;;BA)(A;;CC;;;AC)
Step 3. 手工調整權限
主要觀念就是:
把預設的權限都留下,這些是系統管理員 (Administrator) 使用的。
我們手動再加上我們需要的權限
權限分為 D: 開頭的區域與 S: 開頭的區域,在拼組字串時要注意
我們把預設權限
D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;SU)
在 D: 開頭的區域,加上這段
(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;S-1-5-xx-xxxxxxxx-xxxxxxxxx-xxxxxxxxxx-xx07)
這邊的 S-1-5-xx-xxxxxxxx-xxxxxxxxx-xxxxxxxxxx-xx07 就是剛剛查的 sid
記得要保留 S: 開頭的區域
變成這樣
D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;SU)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;S-1-5-xx-xxxxxxxx-xxxxxxxxx-xxxxxxxxxx-xx07)S:(AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;WD)
串上 sc sdset 指令,變成這樣
(這段需要系統管理員的命令提示字元 (cmd) 才能執行)
sc sdset myService D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;SU)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;S-1-5-xx-xxxxxxxx-xxxxxxxxx-xxxxxxxxxx-xx07)S:(AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;WD)
SCMANAGER 的部分也是如此
預設權限
D:(A;;CC;;;AU)(A;;CCLCRPRC;;;IU)(A;;CCLCRPRC;;;SU)(A;;CCLCRPWPRC;;;SY)(A;;KA;;;BA)(A;;CC;;;AC)
在 D: 開頭的區域,加上這段
(A;;CCLCSWRPWPDTLOCRRC;;;S-1-5-xx-xxxxxxxx-xxxxxxxxx-xxxxxxxxxx-xx07)
變成
D:(A;;CC;;;AU)(A;;CCLCRPRC;;;IU)(A;;CCLCRPRC;;;SU)(A;;CCLCRPWPRC;;;SY)(A;;KA;;;BA)(A;;CC;;;AC)(A;;CCLCSWRPWPDTLOCRRC;;;S-1-5-xx-xxxxxxxx-xxxxxxxxx-xxxxxxxxxx-xx07)
最後串上 sc sdset 指令,並執行
(這段需要系統管理員的命令提示字元 (cmd) 才能執行)
sc sdset SCMANAGER D:(A;;CC;;;AU)(A;;CCLCRPRC;;;IU)(A;;CCLCRPRC;;;SU)(A;;CCLCRPWPRC;;;SY)(A;;KA;;;BA)(A;;CCLCSWRPWPDTLOCRRC;;;S-1-5-xx-xxxxxxxx-xxxxxxxxx-xxxxxxxxxx-xx07)
Step 4. 驗證
使用 cmd 指令執行開啟服務,關閉服務
net start MyService
net stop MyService
或使用 PowerShell:
Start-Service -Name MyService
Stop-Service -Name MyService
應該都要能夠執行
再來使用 Ansible 來測試
playbook.yml
- name: Windows Service testing
hosts: jenkins
gather_facts: no
tasks:
- name: Stop service
ansible.windows.win_service:
name: MyService
state: stopped
- name: Start service
ansible.windows.win_service:
name: MyService
start_mode: delayed
state: started
inventory
[jenkins]
192.168.1.3 ansible_user=MY_USERNAME ansible_password='MY_PASSWORD' ansible_connection=winrm ansible_winrm_transport=basic ansible_winrm_server_cert_validation=ignore ansible_port=5985
執行 ansible playbook
export ANSIBLE_HOST_KEY_CHECKING=False && ansible-playbook -v -i inventory playbook.yml
授予一般使用者開關指定服務的權限(程式步驟)
剛剛以上很複雜的步驟,我已經幫你包成 Adjust-ServicePermissions.ps1 程式了
(使用系統管理員的 Powershell 來執行)
Adjust-ServicePermissions.ps1 -Username myuser -ServiceName MyService
程式片段在此:
https://gist.github.com/j796160836/72346b43a315055caeebb69d7c3db76f
用法二個參數:
Username帶入指定的使用者(一般使用者)的帳號名稱ServiceName帶入指定的 Windows 服務名稱
會給你對應的提示,按下 y 開始執行套用。
如果有成功的話,恭喜你!與自動化更近一步!
Troubleshooting
補充一下,若執行 Powershell 遇到權限問題
PS C:\Users\user\Downloads> .\Adjust-ServicePermissions.ps1
.\Adjust-ServicePermissions.ps1 : C:\Users\user\Downloads\Adjust-ServicePermissions.ps1 檔案無法載入。檔案 C:\Users
\user\Downloads\Adjust-ServicePermissions.ps1 未經數位簽署。您無法在目前的系統上執行此指令碼。如需有關執行指令碼及
設定執行原則的詳細資訊,請參閱 about_Execution_Policies (網址為 http://go.microsoft.com/fwlink/?LinkID=135170)。
位於 線路:1 字元:1
+ .\Adjust-ServicePermissions.ps1
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : SecurityError: (:) [], PSSecurityException
+ FullyQualifiedErrorId : UnauthorizedAccess
那你就在 Powershell 設定這個
(使用系統管理員的 Powershell 來執行)
Set-ExecutionPolicy RemoteSigned
SDDL (安全性描述元定義語言) 學習
這邊我們就深入探討一下 SDDL (Security Descriptor Definition Language, 安全性描述元定義語言) 與剛剛講的這些東西,若沒有要深入調整也沒關係,瞭解原理總是好的。
安全描述符 (Security Descriptor) 定義了服務的權限,包括了誰可以存取服務以及可以執行哪些操作。
SDDL 結構
SDDL 字串由兩個主要部分組成:
- DACL (Discretionary Access Control List):以
D:開頭,定義了對象的存取控制條目 (ACE)。 - SACL (System Access Control List):以
S:開頭,定義了審核條目。
DACL 部分
D: 開頭的字串表示 DAC (Discretionary Access Control List),後面是一組 ACE (Access Control Entries, 對象的存取控制條目),每個 ACE 定義了誰擁有什麼權限。
ACE 結構
每個 ACE 的結構如下:
(A;;<Permissions>;;;<SID>)
A:表示這是一個允許 (Allow) 的 ACE。<Permissions>:定義授予的權限。<SID>:定義授權的安全主體 (Security Identifier)。
分析自行建立的服務 (myService) 的權限
這是剛剛 sc sdshow myService 所出現的字串
D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;SU)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;S-1-5-xx-xxxxxxxx-xxxxxxxxx-xxxxxxxxxx-xx07)S:(AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;WD)
分析 DACL (myService)
我們把 D: 開頭的部分拿出來
D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)
(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)
(A;;CCLCSWLOCRRC;;;IU)
(A;;CCLCSWLOCRRC;;;SU)
(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;S-1-5-xx-xxxxxxxx-xxxxxxxxx-xxxxxxxxxx-xx07)
-
(A;;CCLCSWRPWPDTLOCRRC;;;SY)SY:表示 LocalSystem 帳戶。CCLCSWRPWPDTLOCRRC:這是一組權限,分別代表:CC:建立子項目(CREATE_CHILD)。LC:列出子項目(LIST_CHILDREN)。SW:寫入(SELF_WRITE)。RP:讀取參數(READ_PROPERTY)。WP:寫入參數(WRITE_PROPERTY)。DT:刪除樹(DELETE_TREE)。LO:列出項目(LIST_OBJECT)。CR:控制存取(CONTROL_ACCESS)。RC:讀取安全描述元(READ_CONTROL)。
- 簡而言之:LocalSystem 帳戶擁有完全控制權限。
-
(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)BA:表示 Administrators 群組。CCDCLCSWRPWPDTLOCRSDRCWDWO:這是一組更高級的權限,包含:CC:建立子項目(CREATE_CHILD)。DC:刪除子項目(DELETE_CHILD)。LC:列出子項目(LIST_CHILDREN)。SW:寫入(SELF_WRITE)。RP:讀取參數(READ_PROPERTY)。WP:寫入參數(WRITE_PROPERTY)。DT:刪除樹(DELETE_TREE)。LO:列出項目(LIST_OBJECT)。CR:控制存取(CONTROL_ACCESS)。SD:刪除(STANDARD_DELETE)。RC:讀取安全描述元(READ_CONTROL)。WD:修改存取控制清單(WRITE_DAC)。WO:修改擁有者(WRITE_OWNER)。
- 簡而言之:Administrators 群組擁有完全控制權限。
-
(A;;CCLCSWLOCRRC;;;IU)IU:表示互動使用者 (Interactive Users)。CCLCSWLOCRRC:這是一組有限的權限,允許讀取和列舉操作。CC:建立子項目(CREATE_CHILD)。LC:列出子項目(LIST_CHILDREN)。SW:寫入(SELF_WRITE)。LO:列出項目(LIST_OBJECT)。CR:控制存取(CONTROL_ACCESS)。RC:讀取安全描述元(READ_CONTROL)。
- 簡而言之:互動使用者擁有基本的讀取和列舉權限。
-
(A;;CCLCSWLOCRRC;;;SU)SU:表示服務使用者 (Service Users)。CCLCSWLOCRRC:同上,與互動使用者相同的權限。- 簡而言之:服務使用者擁有基本的讀取和列舉權限。
-
(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;S-1-5-xx-xxxxxxxx-xxxxxxxxx-xxxxxxxxxx-xx07)- 這組是我們新增的權限
S-1-5-xx-xxxxxxxx-xxxxxxxxx-xxxxxxxxxx-xx07:就是剛剛查詢的 sid。CCDCLCSWRPWPDTLOCRSDRCWDWO:同 Administrators,就不贅述了。- 簡而言之:新增這個一般使用者,擁有完全控制權限,也是我們要達到的效果。
SACL 部分 (myService)
S: 表示 SACL (System Access Control List),定義了審核條目。
我們把 S: 開頭的部分拿出來
S:(AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;WD)
SACL 結構
(AU;FA;<Permissions>;;;<SID>)
AU:表示審核條目 (Audit Entry)。FA:表示完全瀏覽 (Full Access)。<Permissions>:定義需要審核的權限。<SID>:定義審核的對象。
分析 SACL
SACL 就相對沒那麼重要,不過還是帶一下
S:(AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;WD)
AU:表示審核條目。FA:表示完全瀏覽。CCDCLCSWRPWPDTLOCRSDRCWDWO:定義了需要審核的權限,與 Administrators 的權限相同。WD:表示 Everyone 群組。- 簡而言之:對 Everyone 群組的所有操作進行審核。
分析 SCMANAGER 的權限
接下來繼續看 sc sdshow SCMANAGER 命令顯示的安全描述符定義語言 (SDDL) 字串。
SCMANAGER 是服務控制管理器 (Service Control Manager)
D:(A;;CC;;;AU)(A;;CCLCRPRC;;;IU)(A;;CCLCRPRC;;;SU)(A;;CCLCRPWPRC;;;SY)(A;;KA;;;BA)(A;;CC;;;AC)S:(AU;FA;KA;;;
WD)(AU;OIIOFA;GA;;;WD)
讓我們逐段分析這些字串。
DACL 部分 (SCMANAGER)
DACL 包含了多個瀏覽控制項 (ACE),每個項目用括號包圍:
D:(A;;CC;;;AU)
(A;;CCLCRPRC;;;IU)
(A;;CCLCRPRC;;;SU)
(A;;CCLCRPWPRC;;;SY)
(A;;KA;;;BA)
(A;;CC;;;AC)
(A;;CCLCSWRPWPRC;;;S-1-5-xx-xxxxxxxx-xxxxxxxxx-xxxxxxxxxx-xx07)
-
(A;;CC;;;AU)A:允許 (Allow)CC:建立子項目(CREATE_CHILD)權限AU:已驗證的用戶 (Authenticated Users)
-
(A;;CCLCRPRC;;;IU)A:允許 (Allow)CCLCRPRC:多個權限的組合:CC:建立子項目(CREATE_CHILD)。LC:列出子項目(LIST_CHILDREN)。RP:讀取參數(READ_PROPERTY)。RC:讀取安全描述元(READ_CONTROL)。
IU:互動式用戶 (Interactive Users)
-
(A;;CCLCRPRC;;;SU)A:允許 (Allow)CCLCRPRC:同上- 適用於
SU:服務用戶 (Service Users)
-
(A;;CCLCRPWPRC;;;SY)A:允許 (Allow)CCLCRPWPRC同上並加上WP:寫入參數(WRITE_PROPERTY)。
SY:系統 (System)
-
(A;;KA;;;BA)A:允許 (Allow)KA:所有權限 (KEY_ALL_ACCESS)。BA:內建管理員 (Built-in Administrators)
-
(A;;CC;;;AC)A:允許 (Allow)CC:建立子項目(CREATE_CHILD)。AC:所有應用程序包 (All Application Packages)
-
(A;;CCLCSWRPWPRC;;;S-1-5-xx-xxxxxxxx-xxxxxxxxx-xxxxxxxxxx-xx07)- 這組是我們新增的權限
A:允許 (Allow)CCLCSWRPWPRC:這組權限為:CC:建立子項目(CREATE_CHILD)。LC:列出子項目(LIST_CHILDREN)。SW:寫入(SELF_WRITE)。RP:讀取參數(READ_PROPERTY)。WP:寫入參數(WRITE_PROPERTY)。RC:讀取安全描述元(READ_CONTROL)。
S-1-5-xx-xxxxxxxx-xxxxxxxxx-xxxxxxxxxx-xx07:就是剛剛查詢的 sid。
SACL 部分解析 (SCMANAGER)
S:(AU;FA;KA;;;WD)
(AU;OIIOFA;GA;;;WD)
SACL 部分定義了審計規則:
-
(AU;FA;KA;;;WD)AU:審計 (Audit)FA:AUDIT_FAILURE(失敗審計)KA:KEY_ALL_ACCESS (所有權限)WD:所有人 (Everyone)
-
(AU;OIIOFA;GA;;;WD)AU:審計OIIOFA:OI:物件繼承 (OBJECT_INHERIT)。IO:僅繼承(INHERIT_ONLY)。FA:失敗審計 (AUDIT_FAILURE)。
GA:一般通用存取 (GENERIC_ALL)。WD:所有人 (Everyone)
說明
這個安全描述符表明:
- 管理員擁有完全控制權
- 系統有廣泛的讀取、寫入和控制權限
- 已認證用戶、交互式用戶和服務用戶有有限的瀏覽權限
- 所有應用程式包可以建立子項目
- 任何人嘗試完全瀏覽並失敗時會被記錄審計訊息
總結
這段 SDDL 字串的含義是:
- DACL:
- LocalSystem 帳戶擁有完全控制權限。
- Administrators 群組擁有完全控制權限。
- 互動使用者和服務使用者擁有基本的讀取和列舉權限。
- SACL:
- 對 Everyone 群組的所有操作進行審核。
這些設定用於控制服務的安全性,確保只有授權的使用者或群組可以執行特定操作,並記錄未經授權的瀏覽嘗試。
權限設定小結
myService 服務
- 關鍵權限:
- LocalSystem 和 Administrators:完全控制
- 互動使用者和服務使用者:僅讀取和列舉
- 特定使用者 (指定SID):完全控制
-審核:追蹤所有使用者的操作嘗試
SCMANAGER 服務
- 權限分層:
- 管理員和系統:高級存取權限
- 已驗證/互動/服務使用者:有限權限
- 所有應用程式包:僅建立子項目
- 特定使用者:自定義權限集
- 審核:記錄失敗操作,含繼承規則
這種分層設計確保服務安全性,將完全控制權限限制在管理員和系統中,同時能為特定使用者提供自定義權限。
參考資料
後記:因為 SDDL 部份的參考資料有一點少,
有部分概念部分使用 AI 輔助,如有錯誤,還煩請不吝指正。












































