---
title: 章 13. 安全性
part: 部 III. 系統管理
prev: books/handbook/boot
next: books/handbook/jails
---

[[security]]
= 安全性
:doctype: book
:toc: macro
:toclevels: 1
:icons: font
:sectnums:
:sectnumlevels: 6
:source-highlighter: rouge
:experimental:
:skip-front-matter:
:toc-title: 目录
:table-caption: 表
:figure-caption: 图
:example-caption: 例 
:xrefstyle: basic
:relfileprefix: ../
:outfilesuffix:
:sectnumoffset: 13

ifeval::["{backend}" == "html5"]
:imagesdir: ../../../images/books/handbook/security/
endif::[]

ifeval::["{backend}" == "pdf"]
:imagesdir: ../../../../static/images/books/handbook/security/
endif::[]

ifeval::["{backend}" == "epub3"]
:imagesdir: ../../../../static/images/books/handbook/security/
endif::[]

include::shared/authors.adoc[]
include::shared/releases.adoc[]
include::shared/zh-tw/mailing-lists.adoc[]
include::shared/zh-tw/urls.adoc[]
include::shared/zh-tw/teams.adoc[]

toc::[]

[[security-synopsis]]
== 概述

不論實體或虛擬，安全性這個主題大到有整個產業圍繞著它，上百個標準案例已經被用來搛寫如何確保系統與網路的安全性。身為 FreeBSD 必須了解如何避免攻擊與入侵。

在此章會討論幾個基本原理及技術。FreeBSD 系統的安全性有許多層面，且有許多第三方工具可以用來增加安全性。

讀完這章，您將了解：

* 基礎 FreeBSD 系統安全概念。
* FreeBSD 中的幾種加密 (Crypt) 機制。
* 如何設定一次性密碼認證。
* 如何設定 man:inetd[8] 中的 TCP Wrapper。
* 如何在 FreeBSD 設定 Kerberos。
* 如何設定 IPsec 並且建立 VPN。
* 如何在 FreeBSD 設定並使用 OpenSSH。
* 如何使用檔案系統 ACL。
* 如何使用 pkg 來稽查從 Port 套件集安裝的第三方軟體套件。
* 如何利用 FreeBSD 安全報告。
* 什麼是程序追蹤 (Process Accounting) 以及如何在 FreeBSD 開啟。
* 如何使用登入類別或資源限制資料庫控制使用者資源。

在開始閱讀這章之前，您需要：

* 了解 FreeBSD 基礎及網路概念。

其他的安全性議題會在本操作手冊的其他處說明。例如 強制存取控制 (Mandatory Access Control, MAC) 會在 crossref:mac[mac,強制存取控制 (MAC)] 討論及網路防火牆會在 crossref:firewalls[firewalls,防火牆] 討論。

[[security-intro]]
== 簡介

保安是每個人的責任，任何系統中的弱點都可讓入侵者取得對關鍵資訊的存取權並導致整個網路的浩劫。資訊安全的其中一個核心原則便是 CIA 三字訣，代表著資訊系統的機密性 (Confidentiality)、完整性 (Integrity) 以及可用性 (Availability)。

CIA 三字訣是電腦安全的基石，就如同客戶與使用者期望他們的資料得到保護一樣重要。例如，一個客戶會期望他們的信用卡資訊被安全的保存 (機密性)、他們的訂單不會在私底下被竄改 (完整性) 以及他們隨時可以存取他們的訂單資訊 (可用性)。

要提供 CIA，安全專家會應用防禦深度的策略。防禦深度的概念是增加數個保全階層來避免單一階層失效便導致整個安全系統瓦解。例如，系統管理者不能直接打開防火牆與評估網路或系統的安全性，還要同時稽查帳號、檢查 Binary 的完整性與確保未被安裝惡意工具。要執行有效的保安策略，必須了解威脅以及如何抵禦威脅。

什麼威脅影響到電腦安全性? 威脅並不僅限於在遠端嘗試未經授權存取系統的遠端攻擊者，威脅也包含員工、惡意軟體、未經許可的網路裝置、天然災害、安全性漏洞甚至是公司競爭對手。

系統與網路可以被未經授權存取，有時是因為意外，或是因遠端攻擊者，或在某些案例中，是因商業間諜或者前員工。做為使用者，重要的是做好防範準備以及當有失誤造成安全漏洞能夠承認並回報可能的問題給安全團隊。做為管理者，重要的是了解威脅並準備在發生時能夠減緩威脅。

當要應用保安到系統上時，建議由基本帳號以及系統設定開始保全，接著確保網路層，使其遵守系統政策以及組織的安全程序。許多組織已經有涵蓋科技裝置設置的安全性政策，該政策應包含工作站、桌上型電腦、行動裝置、手機、上線伺服器、開發伺服器的安全設置。在大多數案例中，也都已經有標準操作程序 (SOP)，當有疑慮時，請向安全團隊諮詢。

簡介接下來的部份將說明如何在 FreeBSD 系統上執行這些基礎的安全設置。本章接下來的部份將介紹在 FreeBSD 系統執行安全性政策時會用到的特定工具。

[[security-accounts]]
=== 防止登入

要確保一個系統的安全最好的起點便是做好帳號的稽查，確保 `root` 使用了一個強而有力的密碼，並這個密碼未在其他地方使用過，然後關閉任何無須登入存取權的帳號。

要防止登入存取帳號有兩種方法，第一種是鎖定帳號，以下範例會鎖定 `toor` 帳號：

[source,bash]
....
# pw lock toor
....

第二種防止登入存取的方式是狀 Shell 更改為 [.filename]#/usr/sbin/nologin#，只有超級使用者可以更改其他使用者的 Shell：

[source,bash]
....
# chsh -s /usr/sbin/nologin toor
....

[.filename]#/usr/sbin/nologin# shell 可以避免系統分配 Shell 給嘗試登入的使用者。

[[security-accountmgmt]]
=== 帳號升級授權

在有一些案例，需要與其他使用者共用系統管理權限，FreeBSD 有兩種方式可以處理這種情況。第一種，也是較不建議的方式，是與 `wheel` 群組的成員共用 root 的密碼，這種方式使用者可以在需要超級使用者的存取權時輸入 `su` 然後輸入 `wheel` 的密碼，在完成需要管理存取權的指令之後，使用應輸入 `exit` 離開。要加入使用者到這個群組，可編輯 [.filename]#/etc/group# 然後加入該使用者到 `wheel` 項目的最後，使用者必須以逗號字元分隔並不可有空白。

第二種方式，也是較建議的方式，安裝 package:security/sudo[] 套件或 Port 來提升權限。這個軟體提供了額外的稽查、更細微的使用者控制，然後可以設定鎖定使用者只能執行特定需權限的指令。

在安裝之後，使用 `visudo` 來編輯 [.filename]#/usr/local/etc/sudoers#。這個範例會建立新 `webadmin` 群組，並加入 `trhodes` 帳號到該群組，然後設定該群組可重新啟動 package:apache24[] 的存取權：

[source,bash]
....
# pw groupadd webadmin -M trhodes -g 6000
# visudo
%webadmin ALL=(ALL) /usr/sbin/service apache24 *
....

[[security-passwords]]
=== 密碼編碼方式

密碼是資訊科技的必要之惡，當必須使用密碼時，應要有複雜且強大的雜湊機制來加密儲存在密碼資料庫中的密碼。FreeBSD 支援 DES, MD5, SHA256, SHA512 以及 Blowfish 雜湊演算法於其 `crypt()` 程式庫。預設使用 SHA512，不建議改成更不安全的雜湊演算法，但可改成更安全的 Blowfish 演算法。

[NOTE]
====
Blowfish 不是 AES 的一部份且不符合任何聯邦資訊處理標準 (Federal Information Processing Standards, FIPS)，在某些環境可能不會允許使用這種加密方式。
====

要知道目前用何種雜湊演算法來加密某位使用者密碼，超級使用者可以檢視在 FreeBSD 密碼資料庫中該使用者的雜湊，每個雜湊的一開始便會以符號標示其用來加密密碼所使用的雜湊機制。若使用 DES 則開始不會有任何符號，而 MD5 的符號則是 `$`，SHA256 及 SHA512 的符號是 `$6$`，Blowfish 的符號是 `$2a$`。在以下例子中 `dru` 的密碼使以預設的 SHA512 演算法加密，因為其雜湊的開始為 `$6$`。注意，該加密過的雜湊，不是原來的密碼，會儲存於密碼資料庫中：

[source,bash]
....
# grep dru /etc/master.passwd
dru:$6$pzIjSvCAn.PBYQBA$PXpSeWPx3g5kscj3IMiM7tUEUSPmGexxta.8Lt9TGSi2lNQqYGKszsBPuGME0:1001:1001::0:0:dru:/usr/home/dru:/bin/csh
....

雜湊機制是設定在該使用者的登入類別 (Login class)，以此為例，該使用者屬於 `default` 登入類別，且雜湊演算法是以下行設定在 [.filename]#/etc/login.conf#：

[.programlisting]
....
        :passwd_format=sha512:\
....

要更改演算法為 Blowfish，可修改該行如下：

[.programlisting]
....
        :passwd_format=blf:\
....

然後依 <<users-limiting>> 中所描述的方式執行 `cap_mkdb /etc/login.conf`。注意，這個動作不會影響任何已存在的密碼雜湊，但這代表必須要求所有使用者執行 `passwd` 來更改其密碼才有辦法重新加密所有密碼。

針對遠端登入，應使用雙重認證 (Two-factor authentication)，舉例來說您同時要 "有某樣東西"，如：鑰匙，以及 "知道某個資訊"，如：密碼。自從 OpenSSH 是 FreeBSD 基礎系統的一部份，所有來算網路的登入應透過加密過的連線且使用以金鑰為基礎的認証來替代密碼。要了解更多資訊請參考 <<openssh>>。Kerberos 的使用者可能會需要多做一些額外的更改才能在其網路上使用 OpenSSH，這些更改在 <<kerberos5>> 中會有說明。

[[security-pwpolicy]]
=== 強制密碼政策

強制在本地帳號使用高強度密碼的政策是系統安全的基礎之一。在 FreeBSD 密碼長度、密碼強度以及密碼複雜性可使用內建的可插拔認証模組 (Pluggable Authentication Modules, PAM) 來執行。

本節將示範如何設定密碼長度下限與上限以及使用 [.filename]#pam_passwdqc.so# 來強制使用混合字元的密碼，此模組可在使用者更改其密碼時強制要求。

要設定此模組，需要先成為超級使用者，然後取消註解在 [.filename]#/etc/pam.d/passwd# 中含有 `pam_passwdqc.so` 的行。然後編輯該行來配合密碼政策：

[.programlisting]
....
password        requisite       pam_passwdqc.so         min=disabled,disabled,disabled,12,10 similar=deny retry=3 enforce=users
....

這個例子會設定新密碼所需符合的需求。`min` 設定可以控制密碼長度下限，它有五個值因為這個模組根據密碼的複雜度定義了五種類型。而複雜度是由必須在密碼中存在的字元類型來定義，例如：文字、數字、符號以及大小寫，這些密碼類型在 man:pam_passwdqc[8] 有詳細的說明。在這個例子，密碼類型的前三項為關閉的，代表不會接受只滿足這些複雜度的密碼，不論長度為何。`12` 設定密碼政策可接受滿足三種字元類型複雜度且至少 12 個字元的密碼，`10` 設定密碼政策接受滿足四種字元類型複雜度且至少 10 個字元的密碼。

`similar` 設定則會拒絕以使用者前一次類似的密碼。`retry` 設定會提供使用者三次輸入新密碼的機會。

一這個檔案儲存之後，更改密碼的使用者將會看到如下的訊息：

[source,bash]
....
% passwd
Changing local password for trhodes
Old Password:

You can now choose the new password.
A valid password should be a mix of upper and lower case letters,
digits and other characters.  You can use a 12 character long
password with characters from at least 3 of these 4 classes, or
a 10 character long password containing characters from all the
classes.  Characters that form a common pattern are discarded by
the check.
Alternatively, if no one else can see your terminal now, you can
pick this as your password: "trait-useful&knob".
Enter new password:
....

若輸入了一個不符何密碼政策的密碼，則會被拒絕並顯示警告，然後使用者會有機會再重試，直到超過設定的允許重試次數。

大多數密碼政策會讓密碼在多日過後過期。要在 FreeBSD 設定密碼年齡日期，可在 [.filename]#/etc/login.conf# 中該使用者的登入類別設定 `passwordtime`。在 `default` 登入類別已有設定範例：

[.programlisting]
....
#       :passwordtime=90d:\
....

因此，要設定此登入類別的密碼在 90 天之後過期只需要移除註解符號 (`#`)，然後儲存編輯結果並執行 `cap_mkdb /etc/login.conf`。

要在個別使用者設定期限，可將有效日期或到期的天數與使用者名稱傳給 `pw`：

[source,bash]
....
# pw usermod -p 30-apr-2015 -n trhodes
....

如這個例子，有效日期的格式為天、月以及年。要取得更多資訊可參考 man:pw[8]。

[[security-rkhunter]]
=== 偵測 Root 工具 (Rootkit)

_rootkit_ 指的是嘗試未經授權取得系統 `root` 存取權的軟體。一旦安裝之後，這個惡意軟體將可以光明正大的開啟給另一個給攻擊者進入的大門。現實上，一但系統已被 rootkit 滲透且執行了搜索動作之後，該系統就應該從頭重新安裝，因為即使非常謹真的資安或系統工程式也可能會遺漏攻擊者留下的動西。

rootkit 對管理者而言唯一有幫助的是：一但偵測到，便代表某處已經被滲透，但這類型的應用程式躲藏的非常好，本節將會示範一個可以用來偵測 rootkit 的工具，package:security/rkhunter[]。

安裝此套件或 Port 之後，系統便可使用以下指令檢查。該指令提供許多資訊且會需要手動按下 kbd:[ENTER] 確認：

[source,bash]
....
# rkhunter -c
....

該程序完成之後，目前狀態的訊息便會顯示在畫面上。這個訊息包含了已檢查過多少檔案、可疑的檔案、可能的 rootkit 以及其他更多資訊。在檢查的過程中，可能會產生一些有關隱藏檔案、OpenSSH 通訊協定選擇及已安裝軟體已知漏洞版本的通用的安全性警告、這些問題可以立即處理或在更詳細的分析之後再處理。

每位管理者應了解在系統上執行了那些程式以及這些程式的用途。第三方工具如 rkhunter 與 package:sysutils/lsof[] 以及原生指令如 `netstat` 與 `ps` 可以系統上大量的資訊，記錄下那一些是正常的，當有不適當的程式出現時提出疑問，然後找出答案。雖然理想要避免滲透，但也必須偵測是否已被滲透了。

[[security-ids]]
=== Binary 檢驗

檢驗系統檔案與 Binary 是很重要的，因為它可以提供系統管理者與資安團隊有關系統變更的資訊，能夠監視系統變更的軟體應用程式稱為入侵偵測系統 (Intrusion Detection System, IDS)。

FreeBSD 原生提供了基礎的 IDS 系統，雖然每天晚上會有安全性的信件會通知管理者相關的變更，但這些資訊是儲存在本地的，這讓惡意的使用者有機會能夠修改這些資訊來隱藏其對系統的變更。也因此，會建議建立一個獨立的 Binary 簽名並將這些簽名儲存在唯度、root 擁有的目錄或在可移除的 USB 磁碟或遠端 rsync 伺服器更好。

內建 `mtree` 工具可以對一個目錄中的內容產生一個規格檔，產生規格檔會用到一個種子碼 (Seed) 或常數，然後在檢查規格是否有更改過時會也會需要使用這個種子碼或常數。這讓檢查一個檔案或 Binary 是否被修改變成可能的一件事。由於攻擊者並不知道種子碼，要仿冒或檢查檔案的校驗碼 (Checksum) 數值是幾乎不可能的。以下例子會產生一組 SHA256 雜湊，每一個在 [.filename]#/bin# 的系統 Binary 都會有一個，並姐會將這些值以隱藏黨儲存在 `root` 的家目錄，[.filename]#/root/.bin_chksum_mtree#：

[source,bash]
....
# mtree -s 3483151339707503 -c -K cksum,sha256digest -p /bin > /root/.bin_chksum_mtree
# mtree: /bin checksum: 3427012225
....

_3483151339707503_ 代表種子碼，這個值應要記錄下來且不可給其它人看。

檢視 [.filename]#/root/.bin_cksum_mtree# 應會產生類似以下的輸出結果：

[.programlisting]
....
#          user: root
#       machine: dreadnaught
#          tree: /bin
#          date: Mon Feb  3 10:19:53 2014

# .
/set type=file uid=0 gid=0 mode=0555 nlink=1 flags=none
.               type=dir mode=0755 nlink=2 size=1024 \
                time=1380277977.000000000
    \133        nlink=2 size=11704 time=1380277977.000000000 \
                cksum=484492447 \
                sha256digest=6207490fbdb5ed1904441fbfa941279055c3e24d3a4049aeb45094596400662a
    cat         size=12096 time=1380277975.000000000 cksum=3909216944 \
                sha256digest=65ea347b9418760b247ab10244f47a7ca2a569c9836d77f074e7a306900c1e69
    chflags     size=8168 time=1380277975.000000000 cksum=3949425175 \
                sha256digest=c99eb6fc1c92cac335c08be004a0a5b4c24a0c0ef3712017b12c89a978b2dac3
    chio        size=18520 time=1380277975.000000000 cksum=2208263309 \
                sha256digest=ddf7c8cb92a58750a675328345560d8cc7fe14fb3ccd3690c34954cbe69fc964
    chmod       size=8640 time=1380277975.000000000 cksum=2214429708 \
                sha256digest=a435972263bf814ad8df082c0752aa2a7bdd8b74ff01431ccbd52ed1e490bbe7
....

機器的主機名稱、建立規格檔的日期與時間、以及建立此規格檔的使用者名稱皆會記錄在此報告當中，報告當中還會有在目錄中每個 Binary 的校驗碼、大小、時間以及 SHA256 編碼。

要檢驗 Binary 簽名是否有被變更過，可使用先前產生的規格檔比對目前目錄的內容，然後儲存結果到檔案。這個指令需要當初產生原規格檔所使用的種子碼：

[source,bash]
....
# mtree -s 3483151339707503 -p /bin < /root/.bin_chksum_mtree >> /root/.bin_chksum_output
# mtree: /bin checksum: 3427012225
....

這個動作應會產生與上次建立 [.filename]#/bin# 規格檔時產生的校驗碼相同，若在此目錄的 Binary 沒有被變更過，那麼 [.filename]#/root/.bin_chksum_output# 這個輸出檔將會是空的。要模擬變更，可以使用 `touch` 更改 [.filename]#/root/.bin_chksum_output# 的日期然後再執行檢驗指令一次：

[source,bash]
....
# touch /bin/cat
# mtree -s 3483151339707503 -p /bin < /root/.bin_chksum_mtree >> /root/.bin_chksum_output
# more /root/.bin_chksum_output
cat changed
	modification time expected Fri Sep 27 06:32:55 2013 found Mon Feb  3 10:28:43 2014
....

建議對含有 Binary 以及設定檔的目錄建立規格檔，對含有敏感資料的目錄也是。通常會為 [.filename]#/bin#, [.filename]#/sbin#, [.filename]#/usr/bin#, [.filename]#/usr/sbin#, [.filename]#/usr/local/bin#, [.filename]#/etc# 及 [.filename]#/usr/local/etc# 建立規格檔。

也有更進階的 IDS 系統，例如 package:security/aide[]。大多數情況 `mtree` 已可提供管理者所需的功能。將種子碼與校驗碼結果保存在惡意使用者無法存取的地方是非常重要的一件事。更多有關 `mtree` 的資訊可在 man:mtree[8] 找到。

[[security-tuning]]
=== 系統安全性調校

在 FreeBSD，有許多系統功能可以使用 `sysctl` 調校，本節會涵蓋少數可以調校來避免阻斷服務 (Denial of Service, DoS) 攻擊的安全性功能。更多有關使用 `sysctl` 的資訊包含：如何暫時更改數值及如何在測試之後做永久更改可在 crossref:config[configtuning-sysctl,使用 sysctl(8) 調校] 找到。

[NOTE]
====
任何時間使用 crossref:config[configtuning-sysctl,使用 sysctl(8) 調校] 做的設定變更都會讓造成不想要的傷害的可能性上升，影響到系統的可用性。因此應要對所有的變更做監視，若可能的話，先在測試系統上實驗，再到上線的系統上使用。
====

預設 FreeBSD 核心會使用安全性層級 `-1` 來開機，這又稱作"不安全模式"，因為不可變 (Immutable) 檔案旗標可以被關閉且可以讀取或寫入所有的裝置。除非有使用 `sysctl` 或在啟動 Script 設定修改該值，否則安全性層級將會在 `-1`。安全性層級可以在系統啟動時透過在 [.filename]#/etc/rc.conf# 設定 `kern_securelevel_enable` 為 `YES` 以及 設定 `kern_securelevel` 的值為想要的安全層級來提升。請參考 man:security[7] 以及 man:init[8] 以取得更多與這些設定及可用的安全性層級相關的資訊。

[WARNING]
====

提高 `securelevel` 會導致 Xorg 無法執行以及造成其他問題，請做好除錯的準備。
====

`net.inet.tcp.blackhole` 以及 `net.inet.udp.blackhole` 設定可以用來丟棄在已關閉連接埠 (Port) 收到的 SYN 封包且不會回傳 RST 回應，預設的動作是會回傳 RST 來表示該連接埠已被關閉，更改預設的動作可對連接埠掃描 (用在查看在系統上執行的應用程式) 提供一定程度的保護，要這麼做可設定 `net.inet.tcp.blackhole` 為 `2` 及 `net.inet.udp.blackhole` 為 `1`。請參考 man:blackhole[4] 以取得更多有關這些設定的資訊。

`net.inet.icmp.drop_redirect` 以及 `net.inet.ip.redirect` 設定可以幫助避免 _重新導向攻擊 (Redirect attacks)_，重新導向攻擊是 DoS 的一種，會傳送大量 ICMP 類型 5 的封包，由於這些封包並不是必要的，設定 `net.inet.icmp.drop_redirect` 為 `1` 以及設定 `net.inet.ip.redirect` 為 `0` 可丟棄這些封包。

來源路由 (Source routing) 是一種偵測與存取在內部網路中不可路由位址的方法，由於不可路由位址通常是固故讓它不可路由的，因此可以關閉這個功能。要關閉這個功能可設定 `net.inet.ip.sourceroute` 以及 `net.inet.ip.accept_sourceroute` 為 `0`。

當一台在網路上的機器需要傳送訊息給所有在子網路上的主機時，會發送 ICMP 回應請求訊息到廣播位址。然而，外部的主機是沒有理由可以執行這個動作的。要拒絕所有來自外部的廣播請求可設定 `net.inet.icmp.bmcastecho` 為 `0`。

還有一些額外的設定在 man:security[7] 有說明。

[[one-time-passwords]]
== 一次性密碼

預設 FreeBSD 已內建一次性密碼 (One-time Passwords In Everything, OPIE)。OPIE 設計用來避免重送攻擊 (Replay attack)，重送攻擊指的是攻擊者發現了某位使用者的密碼，然後使用該密碼來存取系統。由於在 OPIE 的環境下，一組密碼只能被使用一次，被發現的密碼對攻擊者而言便沒有什麼作用。OPIE 使用了安全的加密方式與詰問/回應系統 (Challenge/response system) 來管理密碼。FreeBSD 在實作上預設採用 MD5 加密。

OPIE 使用了三種不同類型的密碼，第一種是一般的 UNIX(TM) 或 Kerberos 密碼，第二種是由 `opiekey` 所產生的一次性密碼，第三種是用來生一次性密碼的 "秘密密碼 (Secret password)"，秘密密碼與 UNIX(TM) 密碼無關且不應相同。

對 OPIE 來說還有另外兩個部份的資料很重要。其中一個是"種子碼 (Seed)" 或稱"金鑰 (Key)"，由兩個字母與五個數字組成。另一個則是"疊代次數 (Iteration count)"，是一個介於 1 到 100 間的數字。OPIE 會將種子碼與秘密密碼串連後，套用 MD5 加密數次後 (根據疊代次數)，再將結果轉換成六個簡短的英文單字來產生一次性密碼。認証系統會持續追蹤最後使用的一次性密碼，若使用者提供的密碼加密後與前一次的密碼相同則可通過認証。由於採用了單向的加密方式，若使用過的密碼被成功擷取也無法拿來產生之後的一次性密碼。疊代次數會在每一次登入成功之後減少，來保持使用者與登入程式間的同步。當疊代次數減少至 `1` 時，OPIE 便要重新初始化。

這個整個程序會牽涉到幾個程式。傳送疊代次數、種子碼與秘密密碼來產生一組一次性密碼或數個一次性密碼的 man:opiekey[1]。除了初始化 OPIE 之外，用來更改密碼、疊代次數或種子碼的 man:opiepasswd[1]。會讀取放在 [.filename]#/etc/opiekeys# 的相關憑証檔來列出使用者目前的疊代次數與種子碼的 man:opieinfo[1]。

本章節將介紹四種不同的操作，第一是如何在安全連線下做第一次的一次性密碼設定，第二是如何使用在不安全的連線下使用 `opiepasswd`，第三是如何在不安全的連線下登入系統，第四是如何產生數個可以被記錄或列印下來在不安全的場所使的金鑰。

=== 初始化 OPIE

第一次要初始化 OPIE，要在安全的場所執行以下指令：

[source,bash]
....
% opiepasswd -c
Adding unfurl:
Only use this method from the console; NEVER from remote. If you are using
telnet, xterm, or a dial-in, type ^C now or exit with no password.
Then run opiepasswd without the -c parameter.
Using MD5 to compute responses.
Enter new secret pass phrase:
Again new secret pass phrase:

ID unfurl OTP key is 499 to4268
MOS MALL GOAT ARM AVID COED
....

`-c` 會設定採用假設指令在安全場所執行的 Console 模式，如在使用者掌控之中的電腦或者透過 SSH 連線到一台在使用者掌控之中的電腦。

提示出現後，輸入用來產生一次性登入金鑰的秘密密碼，應使用一個不容易被猜出來的密碼，且應與使用者登入帳號所使用的密碼不同，密碼必須介於 10 到 127 個字元長度之間，然後請記住這個密碼。

`ID` 行會列出登入名稱 (`unfurl`)、預設的疊代次數 (`499`) 以及預設的種子碼 (`to4268`)。在進行登入時，系統會記住這些參數並且顯示出來，這也代表不需要另外記錄這些資訊。最後一行會列出根據這些參數與秘密密碼所產生出來的一次性密碼，在下一次登入時便要使用這個一次性密碼。

=== 在不安全連線下做初始化

要在不安全的系統上初始化或更改秘密密碼會需要某個可使用安全的連線的地方執行 `opiekey`，這可能是在某一台信任的主機上的 Shell。初始化需要設定疊代次數，100 可能是不錯的數字，種子碼可以自行指定或隨機產生，在不安全連線下要被初始化主機須使用 man:opiepasswd[1]：

[source,bash]
....
% opiepasswd

Updating unfurl:
You need the response from an OTP generator.
Old secret pass phrase:
	otp-md5 498 to4268 ext
	Response: GAME GAG WELT OUT DOWN CHAT
New secret pass phrase:
	otp-md5 499 to4269
	Response: LINE PAP MILK NELL BUOY TROY

ID mark OTP key is 499 gr4269
LINE PAP MILK NELL BUOY TROY
....

要採用預設的種子碼，可直接按下 kbd:[Return] 做初始化。接著在輸入回應之前移到安全的連線然後給予相同的加密參數產生密碼：

[source,bash]
....
% opiekey 498 to4268
Using the MD5 algorithm to compute response.
Reminder: Do not use opiekey from telnet or dial-in sessions.
Enter secret pass phrase:
GAME GAG WELT OUT DOWN CHAT
....

切換回不安全的連線，然後複製產生的一次性密碼貼上。

=== 產生單組一次性密碼

在初始化 OPIE 之後進行登入會顯示如下的提示訊息：

[source,bash]
....
% telnet example.com
Trying 10.0.0.1...
Connected to example.com
Escape character is '^]'.

FreeBSD/i386 (example.com) (ttypa)

login: <username>
otp-md5 498 gr4269 ext
Password: 
....

OPIE 的提示提供了一個很有用的功能，若在密碼提示時按下 kbd:[Return]，便會開啟回應功能並顯示輸入的內容，這個功能在嘗試手工輸入列印出來的密碼時很有用。

此時，要產生一次性密碼來回應登入時的提示，這必須在受信任且可安全執行 man:opiekey[1] 的系統上完成。這個指令有提供 Windows(TM), Mac OS(TM) 與 FreeBSD 版本，使用時需要疊代次數與種子碼做為在指令列的參數，剪下在要登入主機在登入時所提示的訊息。

在信任的系統上執行：

[source,bash]
....
% opiekey 498 to4268
Using the MD5 algorithm to compute response.
Reminder: Do not use opiekey from telnet or dial-in sessions.
Enter secret pass phrase:
GAME GAG WELT OUT DOWN CHAT
....

在產生一次性密碼後，回到登入畫面繼續登入。

=== 產生多組一次性密碼

有時會無法存取信任的主機或沒有安全的連線，在這種情況下，可以使用 man:opiekey[1] 來預先產生多個一次性密碼，例如：

[source,bash]
....
% opiekey -n 5 30 zz99999
Using the MD5 algorithm to compute response.
Reminder: Do not use opiekey from telnet or dial-in sessions.
Enter secret pass phrase: <secret password>
26: JOAN BORE FOSS DES NAY QUIT
27: LATE BIAS SLAY FOLK MUCH TRIG
28: SALT TIN ANTI LOON NEAL USE
29: RIO ODIN GO BYE FURY TIC
30: GREW JIVE SAN GIRD BOIL PHI
....

`-n 5` 會請求產生連續五個金鑰，而 `30` 則是指定最後一個疊代的編號。注意這些列印出的結果的順序與使用的順序__相反__。十足的偏執狂可能會想要用手寫下結果，否則就列印出清單。每一行會同時顯示疊代次數及一次性密碼，在密碼使用過後便可劃掉。

=== 限制使用 UNIX(TM) 密碼

OPIE 可以根據登入階段的 IP 位置限制使用 UNIX(TM) 密碼，相關的檔案為 [.filename]#/etc/opieaccess#，這個檔案預設便存在。請參考 man:opieaccess[5] 來取得更多有關此檔案的資訊以及當使用時要考量的安全性問題。

這裡有一個範本 [.filename]#opieaccess#：

[.programlisting]
....
permit 192.168.0.0 255.255.0.0
....

這一行允許來源 IP 位址 (容易受到詐騙的位址) 符合指定值與遮罩的使用者在任何時間可使用 UNIX(TM) 密碼登入。

若在 [.filename]#opieaccess# 中沒有符合的規則，預設會拒絕非 OPIE 的登入。

[[tcpwrappers]]
== TCP Wrapper

TCP Wrapper is a host-based access control system which extends the abilities of crossref:network-servers[network-inetd,inetd 超級伺服器]. It can be configured to provide logging support, return messages, and connection restrictions for the server daemons under the control of inetd. Refer to man:tcpd[8] for more information about TCP Wrapper and its features.

TCP Wrapper should not be considered a replacement for a properly configured firewall. Instead, TCP Wrapper should be used in conjunction with a firewall and other security enhancements in order to provide another layer of protection in the implementation of a security policy.

=== 初始設定

To enable TCP Wrapper in FreeBSD, add the following lines to [.filename]#/etc/rc.conf#:

[.programlisting]
....
inetd_enable="YES"
inetd_flags="-Ww"
....

Then, properly configure [.filename]#/etc/hosts.allow#.

[NOTE]
====
Unlike other implementations of TCP Wrapper, the use of [.filename]#hosts.deny# is deprecated in FreeBSD. All configuration options should be placed in [.filename]#/etc/hosts.allow#.
====

In the simplest configuration, daemon connection policies are set to either permit or block, depending on the options in [.filename]#/etc/hosts.allow#. The default configuration in FreeBSD is to allow all connections to the daemons started with inetd.

Basic configuration usually takes the form of `daemon : address : action`, where `daemon` is the daemon which inetd started, `address` is a valid hostname, IP address, or an IPv6 address enclosed in brackets ([ ]), and `action` is either `allow` or `deny`. TCP Wrapper uses a first rule match semantic, meaning that the configuration file is scanned from the beginning for a matching rule. When a match is found, the rule is applied and the search process stops.

For example, to allow POP3 connections via the package:mail/qpopper[] daemon, the following lines should be appended to [.filename]#hosts.allow#:

[.programlisting]
....
# This line is required for POP3 connections:
qpopper : ALL : allow
....

Whenever this file is edited, restart inetd:

[source,bash]
....
# service inetd restart
....

=== 進階設定

TCP Wrapper provides advanced options to allow more control over the way connections are handled. In some cases, it may be appropriate to return a comment to certain hosts or daemon connections. In other cases, a log entry should be recorded or an email sent to the administrator. Other situations may require the use of a service for local connections only. This is all possible through the use of configuration options known as wildcards, expansion characters, and external command execution.

Suppose that a situation occurs where a connection should be denied yet a reason should be sent to the host who attempted to establish that connection. That action is possible with `twist`. When a connection attempt is made, `twist` executes a shell command or script. An example exists in [.filename]#hosts.allow#:

[.programlisting]
....
# The rest of the daemons are protected.
ALL : ALL \
	: severity auth.info \
	: twist /bin/echo "You are not welcome to use %d from %h."
....

In this example, the message "You are not allowed to use _daemon name_ from _hostname_." will be returned for any daemon not configured in [.filename]#hosts.allow#. This is useful for sending a reply back to the connection initiator right after the established connection is dropped. Any message returned _must_ be wrapped in quote (`"`) characters.

[WARNING]
====

It may be possible to launch a denial of service attack on the server if an attacker floods these daemons with connection requests.
====

Another possibility is to use `spawn`. Like `twist`, `spawn` implicitly denies the connection and may be used to run external shell commands or scripts. Unlike `twist`, `spawn` will not send a reply back to the host who established the connection. For example, consider the following configuration:

[.programlisting]
....
# We do not allow connections from example.com:
ALL : .example.com \
	: spawn (/bin/echo %a from %h attempted to access %d >> \
	  /var/log/connections.log) \
	: deny
....

This will deny all connection attempts from `*.example.com` and log the hostname, IP address, and the daemon to which access was attempted to [.filename]#/var/log/connections.log#. This example uses the substitution characters `%a` and `%h`. Refer to man:hosts_access[5] for the complete list.

To match every instance of a daemon, domain, or IP address, use `ALL`. Another wildcard is `PARANOID` which may be used to match any host which provides an IP address that may be forged because the IP address differs from its resolved hostname. In this example, all connection requests to Sendmail which have an IP address that varies from its hostname will be denied:

[.programlisting]
....
# Block possibly spoofed requests to sendmail:
sendmail : PARANOID : deny
....

[CAUTION]
====

Using the `PARANOID` wildcard will result in denied connections if the client or server has a broken DNS setup.
====

To learn more about wildcards and their associated functionality, refer to man:hosts_access[5].

[NOTE]
====
When adding new configuration lines, make sure that any unneeded entries for that daemon are commented out in [.filename]#hosts.allow#.
====

[[kerberos5]]
== Kerberos

Kerberos is a network authentication protocol which was originally created by the Massachusetts Institute of Technology (MIT) as a way to securely provide authentication across a potentially hostile network. The Kerberos protocol uses strong cryptography so that both a client and server can prove their identity without sending any unencrypted secrets over the network. Kerberos can be described as an identity-verifying proxy system and as a trusted third-party authentication system. After a user authenticates with Kerberos, their communications can be encrypted to assure privacy and data integrity.

The only function of Kerberos is to provide the secure authentication of users and servers on the network. It does not provide authorization or auditing functions. It is recommended that Kerberos be used with other security methods which provide authorization and audit services.

The current version of the protocol is version 5, described in RFC 4120. Several free implementations of this protocol are available, covering a wide range of operating systems. MIT continues to develop their Kerberos package. It is commonly used in the US as a cryptography product, and has historically been subject to US export regulations. In FreeBSD, MITKerberos is available as the package:security/krb5[] package or port. The Heimdal Kerberos implementation was explicitly developed outside of the US to avoid export regulations. The Heimdal Kerberos distribution is included in the base FreeBSD installation, and another distribution with more configurable options is available as package:security/heimdal[] in the Ports Collection.

In Kerberos users and services are identified as "principals" which are contained within an administrative grouping, called a "realm". A typical user principal would be of the form `_user_@_REALM_` (realms are traditionally uppercase).

This section provides a guide on how to set up Kerberos using the Heimdal distribution included in FreeBSD.

For purposes of demonstrating a Kerberos installation, the name spaces will be as follows:

* The DNS domain (zone) will be `example.org`.
* The Kerberos realm will be `EXAMPLE.ORG`.

[NOTE]
====
Use real domain names when setting up Kerberos, even if it will run internally. This avoids DNS problems and assures inter-operation with other Kerberos realms.
====

=== 設定 Heimdal KDC

The Key Distribution Center (KDC) is the centralized authentication service that Kerberos provides, the "trusted third party" of the system. It is the computer that issues Kerberos tickets, which are used for clients to authenticate to servers. Because the KDC is considered trusted by all other computers in the Kerberos realm, it has heightened security concerns. Direct access to the KDC should be limited.

While running a KDC requires few computing resources, a dedicated machine acting only as a KDC is recommended for security reasons.

To begin setting up a KDC, add these lines to [.filename]#/etc/rc.conf#:

[.programlisting]
....
kdc_enable="YES"
kadmind_enable="YES"
....

Next, edit [.filename]#/etc/krb5.conf# as follows:

[.programlisting]
....
[libdefaults]
    default_realm = EXAMPLE.ORG
[realms]
    EXAMPLE.ORG = {
	kdc = kerberos.example.org
	admin_server = kerberos.example.org
    }
[domain_realm]
    .example.org = EXAMPLE.ORG
....

In this example, the KDC will use the fully-qualified hostname `kerberos.example.org`. The hostname of the KDC must be resolvable in the DNS.

Kerberos can also use the DNS to locate KDCs, instead of a `[realms]` section in [.filename]#/etc/krb5.conf#. For large organizations that have their own DNS servers, the above example could be trimmed to:

[.programlisting]
....
[libdefaults]
      default_realm = EXAMPLE.ORG
[domain_realm]
    .example.org = EXAMPLE.ORG
....

With the following lines being included in the `example.org` zone file:

[.programlisting]
....
_kerberos._udp      IN  SRV     01 00 88 kerberos.example.org.
_kerberos._tcp      IN  SRV     01 00 88 kerberos.example.org.
_kpasswd._udp       IN  SRV     01 00 464 kerberos.example.org.
_kerberos-adm._tcp  IN  SRV     01 00 749 kerberos.example.org.
_kerberos           IN  TXT     EXAMPLE.ORG
....

[NOTE]
====
In order for clients to be able to find the Kerberos services, they _must_ have either a fully configured [.filename]#/etc/krb5.conf# or a minimally configured [.filename]#/etc/krb5.conf#_and_ a properly configured DNS server.
====

Next, create the Kerberos database which contains the keys of all principals (users and hosts) encrypted with a master password. It is not required to remember this password as it will be stored in [.filename]#/var/heimdal/m-key#; it would be reasonable to use a 45-character random password for this purpose. To create the master key, run `kstash` and enter a password:

[source,bash]
....
# kstash
Master key: xxxxxxxxxxxxxxxxxxxxxxx
Verifying password - Master key: xxxxxxxxxxxxxxxxxxxxxxx
....

Once the master key has been created, the database should be initialized. The Kerberos administrative tool man:kadmin[8] can be used on the KDC in a mode that operates directly on the database, without using the man:kadmind[8] network service, as `kadmin -l`. This resolves the chicken-and-egg problem of trying to connect to the database before it is created. At the `kadmin` prompt, use `init` to create the realm's initial database:

[source,bash]
....
# kadmin -l
kadmin> init EXAMPLE.ORG
Realm max ticket life [unlimited]:
....

Lastly, while still in `kadmin`, create the first principal using `add`. Stick to the default options for the principal for now, as these can be changed later with `modify`. Type `?` at the prompt to see the available options.

[source,bash]
....
kadmin> add tillman
Max ticket life [unlimited]:
Max renewable life [unlimited]:
Attributes []:
Password: xxxxxxxx
Verifying password - Password: xxxxxxxx
....

Next, start the KDC services by running `service kdc start` and `service kadmind start`. While there will not be any kerberized daemons running at this point, it is possible to confirm that the KDC is functioning by obtaining a ticket for the principal that was just created:

[source,bash]
....
% kinit tillman
tillman@EXAMPLE.ORG's Password:
....

Confirm that a ticket was successfully obtained using `klist`:

[source,bash]
....
% klist
Credentials cache: FILE:/tmp/krb5cc_1001
	Principal: tillman@EXAMPLE.ORG

  Issued                Expires               Principal
Aug 27 15:37:58 2013  Aug 28 01:37:58 2013  krbtgt/EXAMPLE.ORG@EXAMPLE.ORG
....

The temporary ticket can be destroyed when the test is finished:

[source,bash]
....
% kdestroy
....

=== 設定伺服器使用 Kerberos

The first step in configuring a server to use Kerberos authentication is to ensure that it has the correct configuration in [.filename]#/etc/krb5.conf#. The version from the KDC can be used as-is, or it can be regenerated on the new system.

Next, create [.filename]#/etc/krb5.keytab# on the server. This is the main part of "Kerberizing" a service -- it corresponds to generating a secret shared between the service and the KDC. The secret is a cryptographic key, stored in a "keytab". The keytab contains the server's host key, which allows it and the KDC to verify each others' identity. It must be transmitted to the server in a secure fashion, as the security of the server can be broken if the key is made public. Typically, the [.filename]#keytab# is generated on an administrator's trusted machine using `kadmin`, then securely transferred to the server, e.g., with man:scp[1]; it can also be created directly on the server if that is consistent with the desired security policy. It is very important that the keytab is transmitted to the server in a secure fashion: if the key is known by some other party, that party can impersonate any user to the server! Using `kadmin` on the server directly is convenient, because the entry for the host principal in the KDC database is also created using `kadmin`.

Of course, `kadmin` is a kerberized service; a Kerberos ticket is needed to authenticate to the network service, but to ensure that the user running `kadmin` is actually present (and their session has not been hijacked), `kadmin` will prompt for the password to get a fresh ticket. The principal authenticating to the kadmin service must be permitted to use the `kadmin` interface, as specified in [.filename]#kadmind.acl#. See the section titled "Remote administration" in `info heimdal` for details on designing access control lists. Instead of enabling remote `kadmin` access, the administrator could securely connect to the KDC via the local console or man:ssh[1], and perform administration locally using `kadmin -l`.

After installing [.filename]#/etc/krb5.conf#, use `add --random-key` in `kadmin`. This adds the server's host principal to the database, but does not extract a copy of the host principal key to a keytab. To generate the keytab, use `ext` to extract the server's host principal key to its own keytab:

[source,bash]
....
# kadmin
kadmin> add --random-key host/myserver.example.org
Max ticket life [unlimited]:
Max renewable life [unlimited]:
Principal expiration time [never]:
Password expiration time [never]:
Attributes []:
kadmin> ext_keytab host/myserver.example.org
kadmin> exit
....

Note that `ext_keytab` stores the extracted key in [.filename]#/etc/krb5.keytab# by default. This is good when being run on the server being kerberized, but the `--keytab _path/to/file_` argument should be used when the keytab is being extracted elsewhere:

[source,bash]
....
# kadmin
kadmin> ext_keytab --keytab=/tmp/example.keytab host/myserver.example.org
kadmin> exit
....

The keytab can then be securely copied to the server using man:scp[1] or a removable media. Be sure to specify a non-default keytab name to avoid inserting unneeded keys into the system's keytab.

At this point, the server can read encrypted messages from the KDC using its shared key, stored in [.filename]#krb5.keytab#. It is now ready for the Kerberos-using services to be enabled. One of the most common such services is man:sshd[8], which supports Kerberos via the GSS-API. In [.filename]#/etc/ssh/sshd_config#, add the line:

[.programlisting]
....
GSSAPIAuthentication yes
....

做完了這個變更之後，必須重新啟動 man:sshd[8] 來使新的設定值生效：`service sshd restart`。

=== 設定客戶端使用 Kerberos

As it was for the server, the client requires configuration in [.filename]#/etc/krb5.conf#. Copy the file in place (securely) or re-enter it as needed.

Test the client by using `kinit`, `klist`, and `kdestroy` from the client to obtain, show, and then delete a ticket for an existing principal. Kerberos applications should also be able to connect to Kerberos enabled servers. If that does not work but obtaining a ticket does, the problem is likely with the server and not with the client or the KDC. In the case of kerberized man:ssh[1], GSS-API is disabled by default, so test using `ssh -o GSSAPIAuthentication=yes _hostname_`.

When testing a Kerberized application, try using a packet sniffer such as `tcpdump` to confirm that no sensitive information is sent in the clear.

Various Kerberos client applications are available. With the advent of a bridge so that applications using SASL for authentication can use GSS-API mechanisms as well, large classes of client applications can use Kerberos for authentication, from Jabber clients to IMAP clients.

Users within a realm typically have their Kerberos principal mapped to a local user account. Occasionally, one needs to grant access to a local user account to someone who does not have a matching Kerberos principal. For example, `tillman@EXAMPLE.ORG` may need access to the local user account `webdevelopers`. Other principals may also need access to that local account.

The [.filename]#.k5login# and [.filename]#.k5users# files, placed in a user's home directory, can be used to solve this problem. For example, if the following [.filename]#.k5login# is placed in the home directory of `webdevelopers`, both principals listed will have access to that account without requiring a shared password:

[.programlisting]
....
tillman@example.org
jdoe@example.org
....

Refer to man:ksu[1] for more information about [.filename]#.k5users#.

=== 與 MIT 的差異

The major difference between the MIT and Heimdal implementations is that `kadmin` has a different, but equivalent, set of commands and uses a different protocol. If the KDC is MIT, the Heimdal version of `kadmin` cannot be used to administer the KDC remotely, and vice versa.

Client applications may also use slightly different command line options to accomplish the same tasks. Following the instructions at http://web.mit.edu/Kerberos/www/[http://web.mit.edu/Kerberos/www/] is recommended. Be careful of path issues: the MIT port installs into [.filename]#/usr/local/# by default, and the FreeBSD system applications run instead of the MIT versions if `PATH` lists the system directories first.

When using MIT Kerberos as a KDC on FreeBSD, the following edits should also be made to [.filename]#rc.conf#:

[.programlisting]
....
kerberos5_server="/usr/local/sbin/krb5kdc"
kadmind5_server="/usr/local/sbin/kadmind"
kerberos5_server_flags=""
kerberos5_server_enable="YES"
kadmind5_server_enable="YES"
....

=== Kerberos 提示、技巧與疑難排解

When configuring and troubleshooting Kerberos, keep the following points in mind:

* When using either Heimdal or MITKerberos from ports, ensure that the `PATH` lists the port's versions of the client applications before the system versions.
* If all the computers in the realm do not have synchronized time settings, authentication may fail. crossref:network-servers[network-ntp,NTP 時間校對] describes how to synchronize clocks using NTP.
* If the hostname is changed, the `host/` principal must be changed and the keytab updated. This also applies to special keytab entries like the `HTTP/` principal used for Apache's package:www/mod_auth_kerb[].
* All hosts in the realm must be both forward and reverse resolvable in DNS or, at a minimum, exist in [.filename]#/etc/hosts#. CNAMEs will work, but the A and PTR records must be correct and in place. The error message for unresolvable hosts is not intuitive: `Kerberos5 refuses authentication because Read req failed: Key table entry not found`.
* Some operating systems that act as clients to the KDC do not set the permissions for `ksu` to be setuid `root`. This means that `ksu` does not work. This is a permissions problem, not a KDC error.
* With MITKerberos, to allow a principal to have a ticket life longer than the default lifetime of ten hours, use `modify_principal` at the man:kadmin[8] prompt to change the `maxlife` of both the principal in question and the `krbtgt` principal. The principal can then use `kinit -l` to request a ticket with a longer lifetime.
* When running a packet sniffer on the KDC to aid in troubleshooting while running `kinit` from a workstation, the Ticket Granting Ticket (TGT) is sent immediately, even before the password is typed. This is because the Kerberos server freely transmits a TGT to any unauthorized request. However, every TGT is encrypted in a key derived from the user's password. When a user types their password, it is not sent to the KDC, it is instead used to decrypt the TGT that `kinit` already obtained. If the decryption process results in a valid ticket with a valid time stamp, the user has valid Kerberos credentials. These credentials include a session key for establishing secure communications with the Kerberos server in the future, as well as the actual TGT, which is encrypted with the Kerberos server's own key. This second layer of encryption allows the Kerberos server to verify the authenticity of each TGT.
* Host principals can have a longer ticket lifetime. If the user principal has a lifetime of a week but the host being connected to has a lifetime of nine hours, the user cache will have an expired host principal and the ticket cache will not work as expected.
* When setting up [.filename]#krb5.dict# to prevent specific bad passwords from being used as described in man:kadmind[8], remember that it only applies to principals that have a password policy assigned to them. The format used in [.filename]#krb5.dict# is one string per line. Creating a symbolic link to [.filename]#/usr/shared/dict/words# might be useful.

=== 減輕 Kerberos 的限制

Since Kerberos is an all or nothing approach, every service enabled on the network must either be modified to work with Kerberos or be otherwise secured against network attacks. This is to prevent user credentials from being stolen and re-used. An example is when Kerberos is enabled on all remote shells but the non-Kerberized POP3 mail server sends passwords in plain text.

The KDC is a single point of failure. By design, the KDC must be as secure as its master password database. The KDC should have absolutely no other services running on it and should be physically secure. The danger is high because Kerberos stores all passwords encrypted with the same master key which is stored as a file on the KDC.

A compromised master key is not quite as bad as one might fear. The master key is only used to encrypt the Kerberos database and as a seed for the random number generator. As long as access to the KDC is secure, an attacker cannot do much with the master key.

If the KDC is unavailable, network services are unusable as authentication cannot be performed. This can be alleviated with a single master KDC and one or more slaves, and with careful implementation of secondary or fall-back authentication using PAM.

Kerberos allows users, hosts and services to authenticate between themselves. It does not have a mechanism to authenticate the KDC to the users, hosts, or services. This means that a trojanned `kinit` could record all user names and passwords. File system integrity checking tools like package:security/tripwire[] can alleviate this.

=== 相關資源與延伸資訊

* http://www.faqs.org/faqs/Kerberos-faq/general/preamble.html[ The Kerberos FAQ]
* http://web.mit.edu/Kerberos/www/dialogue.html[Designing an Authentication System: a Dialog in Four Scenes]
* https://www.ietf.org/rfc/rfc4120.txt[RFC 4120, The Kerberos Network Authentication Service (V5)]
* http://web.mit.edu/Kerberos/www/[MIT Kerberos home page]
* https://www.h5l.org/[Heimdal Kerberos home page]

[[openssl]]
== OpenSSL

OpenSSL is an open source implementation of the SSL and TLS protocols. It provides an encryption transport layer on top of the normal communications layer, allowing it to be intertwined with many network applications and services.

The version of OpenSSL included in FreeBSD supports the Secure Sockets Layer 3.0 (SSLv3) and Transport Layer Security 1.0/1.1/1.2 (TLSv1/TLSv1.1/TLSv1.2) network security protocols and can be used as a general cryptographic library. In FreeBSD 12.0-RELEASE and above, OpenSSL also supports Transport Layer Security 1.3 (TLSv1.3).

OpenSSL is often used to encrypt authentication of mail clients and to secure web based transactions such as credit card payments. Some ports, such as package:www/apache24[] and package:databases/postgresql11-server[], include a compile option for building with OpenSSL. If selected, the port will add support using OpenSSL from the base system. To instead have the port compile against OpenSSL from the package:security/openssl[] port, add the following to [.filename]#/etc/make.conf#:

[.programlisting]
....
DEFAULT_VERSIONS+= ssl=openssl
....

Another common use of OpenSSL is to provide certificates for use with software applications. Certificates can be used to verify the credentials of a company or individual. If a certificate has not been signed by an external _Certificate Authority_ (CA), such as http://www.verisign.com[http://www.verisign.com], the application that uses the certificate will produce a warning. There is a cost associated with obtaining a signed certificate and using a signed certificate is not mandatory as certificates can be self-signed. However, using an external authority will prevent warnings and can put users at ease.

This section demonstrates how to create and use certificates on a FreeBSD system. Refer to crossref:network-servers[ldap-config,設定 LDAP 伺服器] for an example of how to create a CA for signing one's own certificates.

For more information about SSL, read the free https://www.feistyduck.com/books/openssl-cookbook/[OpenSSL Cookbook].

=== 產生憑証

To generate a certificate that will be signed by an external CA, issue the following command and input the information requested at the prompts. This input information will be written to the certificate. At the `Common Name` prompt, input the fully qualified name for the system that will use the certificate. If this name does not match the server, the application verifying the certificate will issue a warning to the user, rendering the verification provided by the certificate as useless.

[source,bash]
....
# openssl req -new -nodes -out req.pem -keyout cert.key -sha256 -newkey rsa:2048
Generating a 2048 bit RSA private key
..................+++
.............................................................+++
writing new private key to 'cert.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:US
State or Province Name (full name) [Some-State]:PA
Locality Name (eg, city) []:Pittsburgh
Organization Name (eg, company) [Internet Widgits Pty Ltd]:My Company
Organizational Unit Name (eg, section) []:Systems Administrator
Common Name (eg, YOUR name) []:localhost.example.org
Email Address []:trhodes@FreeBSD.org

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:Another Name
....

Other options, such as the expire time and alternate encryption algorithms, are available when creating a certificate. A complete list of options is described in man:openssl[1].

This command will create two files in the current directory. The certificate request, [.filename]#req.pem#, can be sent to a CA who will validate the entered credentials, sign the request, and return the signed certificate. The second file, [.filename]#cert.key#, is the private key for the certificate and should be stored in a secure location. If this falls in the hands of others, it can be used to impersonate the user or the server.

Alternately, if a signature from a CA is not required, a self-signed certificate can be created. First, generate the RSA key:

[source,bash]
....
# openssl genrsa -rand -genkey -out cert.key 2048
0 semi-random bytes loaded
Generating RSA private key, 2048 bit long modulus
.............................................+++
.................................................................................................................+++
e is 65537 (0x10001)
....

Use this key to create a self-signed certificate. Follow the usual prompts for creating a certificate:

[source,bash]
....
# openssl req -new -x509 -days 365 -key cert.key -out cert.crt -sha256
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:US
State or Province Name (full name) [Some-State]:PA
Locality Name (eg, city) []:Pittsburgh
Organization Name (eg, company) [Internet Widgits Pty Ltd]:My Company
Organizational Unit Name (eg, section) []:Systems Administrator
Common Name (e.g. server FQDN or YOUR name) []:localhost.example.org
Email Address []:trhodes@FreeBSD.org
....

This will create two new files in the current directory: a private key file [.filename]#cert.key#, and the certificate itself, [.filename]#cert.crt#. These should be placed in a directory, preferably under [.filename]#/etc/ssl/#, which is readable only by `root`. Permissions of `0700` are appropriate for these files and can be set using `chmod`.

=== 使用憑證

One use for a certificate is to encrypt connections to the Sendmail mail server in order to prevent the use of clear text authentication.

[NOTE]
====
Some mail clients will display an error if the user has not installed a local copy of the certificate. Refer to the documentation included with the software for more information on certificate installation.
====

In FreeBSD 10.0-RELEASE and above, it is possible to create a self-signed certificate for Sendmail automatically. To enable this, add the following lines to [.filename]#/etc/rc.conf#:

[.programlisting]
....
sendmail_enable="YES"
sendmail_cert_create="YES"
sendmail_cert_cn="localhost.example.org"
....

This will automatically create a self-signed certificate, [.filename]#/etc/mail/certs/host.cert#, a signing key, [.filename]#/etc/mail/certs/host.key#, and a CA certificate, [.filename]#/etc/mail/certs/cacert.pem#. The certificate will use the `Common Name` specified in `sendmail_cert_cn`. After saving the edits, restart Sendmail:

[source,bash]
....
# service sendmail restart
....

If all went well, there will be no error messages in [.filename]#/var/log/maillog#. For a simple test, connect to the mail server's listening port using `telnet`:

[source,bash]
....
# telnet example.com 25
Trying 192.0.34.166...
Connected to example.com.
Escape character is '^]'.
220 example.com ESMTP Sendmail 8.14.7/8.14.7; Fri, 18 Apr 2014 11:50:32 -0400 (EDT)
ehlo example.com
250-example.com Hello example.com [192.0.34.166], pleased to meet you
250-ENHANCEDSTATUSCODES
250-PIPELINING
250-8BITMIME
250-SIZE
250-DSN
250-ETRN
250-AUTH LOGIN PLAIN
250-STARTTLS
250-DELIVERBY
250 HELP
quit
221 2.0.0 example.com closing connection
Connection closed by foreign host.
....

If the `STARTTLS` line appears in the output, everything is working correctly.

[[ipsec]]
== VPN over IPsec

Internet Protocol Security (IPsec) is a set of protocols which sit on top of the Internet Protocol (IP) layer. It allows two or more hosts to communicate in a secure manner by authenticating and encrypting each IP packet of a communication session. The FreeBSD IPsec network stack is based on the http://www.kame.net/[http://www.kame.net/] implementation and supports both IPv4 and IPv6 sessions.

IPsec is comprised of the following sub-protocols:

* _Encapsulated Security Payload (ESP)_: this protocol protects the IP packet data from third party interference by encrypting the contents using symmetric cryptography algorithms such as Blowfish and 3DES.
* _Authentication Header (AH)_: this protocol protects the IP packet header from third party interference and spoofing by computing a cryptographic checksum and hashing the IP  packet header fields with a secure hashing function. This is then followed by an additional header that contains the hash, to allow the information in the packet to be authenticated.
* _IP Payload Compression Protocol (IPComp_): this protocol tries to increase communication performance by compressing the IP  payload in order to reduce the amount of data sent.

These protocols can either be used together or separately, depending on the environment.

IPsec supports two modes of operation. The first mode, _Transport Mode_, protects communications between two hosts. The second mode, _Tunnel Mode_, is used to build virtual tunnels, commonly known as Virtual Private Networks (VPNs). Consult man:ipsec[4] for detailed information on the IPsec subsystem in FreeBSD.

在 FreeBSD 11 與之後的版本預設會開啟 IPsec 功能，先前版本的 FreeBSD 可在自訂核心設定檔中加入以下選項然後依 crossref:kernelconfig[kernelconfig,設定 FreeBSD 核心] 的指示來重新編譯核心：

[source,bash]
....
options   IPSEC        #IP security
device    crypto
....

If IPsec debugging support is desired, the following kernel option should also be added:

[source,bash]
....
options   IPSEC_DEBUG  debug for IP security
....

This rest of this chapter demonstrates the process of setting up an IPsecVPN between a home network and a corporate network. In the example scenario:

* Both sites are connected to the Internet through a gateway that is running FreeBSD.
* The gateway on each network has at least one external IP address. In this example, the corporate LAN's external IP address is `172.16.5.4` and the home LAN's external IP address is `192.168.1.12`.
* The internal addresses of the two networks can be either public or private IP addresses. However, the address space must not collide. For example, both networks cannot use `192.168.1.x`. In this example, the corporate LAN's internal IP address is `10.246.38.1` and the home LAN's internal IP address is `10.0.0.5`.

=== 在 FreeBSD 上設定 VPN

To begin, package:security/ipsec-tools[] must be installed from the Ports Collection. This software provides a number of applications which support the configuration.

The next requirement is to create two man:gif[4] pseudo-devices which will be used to tunnel packets and allow both networks to communicate properly. As `root`, run the following commands, replacing _internal_ and _external_ with the real IP addresses of the internal and external interfaces of the two gateways:

[source,bash]
....
# ifconfig gif0 create
# ifconfig gif0 internal1 internal2
# ifconfig gif0 tunnel external1 external2
....

Verify the setup on each gateway, using `ifconfig`. Here is the output from Gateway 1:

[.programlisting]
....
gif0: flags=8051 mtu 1280
tunnel inet 172.16.5.4 --> 192.168.1.12
inet6 fe80::2e0:81ff:fe02:5881%gif0 prefixlen 64 scopeid 0x6
inet 10.246.38.1 --> 10.0.0.5 netmask 0xffffff00
....

Here is the output from Gateway 2:

[.programlisting]
....
gif0: flags=8051 mtu 1280
tunnel inet 192.168.1.12 --> 172.16.5.4
inet 10.0.0.5 --> 10.246.38.1 netmask 0xffffff00
inet6 fe80::250:bfff:fe3a:c1f%gif0 prefixlen 64 scopeid 0x4
....

Once complete, both internal IP addresses should be reachable using man:ping[8]:

[.programlisting]
....
priv-net# ping 10.0.0.5
PING 10.0.0.5 (10.0.0.5): 56 data bytes
64 bytes from 10.0.0.5: icmp_seq=0 ttl=64 time=42.786 ms
64 bytes from 10.0.0.5: icmp_seq=1 ttl=64 time=19.255 ms
64 bytes from 10.0.0.5: icmp_seq=2 ttl=64 time=20.440 ms
64 bytes from 10.0.0.5: icmp_seq=3 ttl=64 time=21.036 ms
--- 10.0.0.5 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max/stddev = 19.255/25.879/42.786/9.782 ms

corp-net# ping 10.246.38.1
PING 10.246.38.1 (10.246.38.1): 56 data bytes
64 bytes from 10.246.38.1: icmp_seq=0 ttl=64 time=28.106 ms
64 bytes from 10.246.38.1: icmp_seq=1 ttl=64 time=42.917 ms
64 bytes from 10.246.38.1: icmp_seq=2 ttl=64 time=127.525 ms
64 bytes from 10.246.38.1: icmp_seq=3 ttl=64 time=119.896 ms
64 bytes from 10.246.38.1: icmp_seq=4 ttl=64 time=154.524 ms
--- 10.246.38.1 ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max/stddev = 28.106/94.594/154.524/49.814 ms
....

As expected, both sides have the ability to send and receive ICMP packets from the privately configured addresses. Next, both gateways must be told how to route packets in order to correctly send traffic from either network. The following commands will achieve this goal:

[source,bash]
....
corp-net# route add 10.0.0.0 10.0.0.5 255.255.255.0
corp-net# route add net 10.0.0.0: gateway 10.0.0.5
priv-net# route add 10.246.38.0 10.246.38.1 255.255.255.0
priv-net# route add host 10.246.38.0: gateway 10.246.38.1
....

At this point, internal machines should be reachable from each gateway as well as from machines behind the gateways. Again, use man:ping[8] to confirm:

[.programlisting]
....
corp-net# ping 10.0.0.8
PING 10.0.0.8 (10.0.0.8): 56 data bytes
64 bytes from 10.0.0.8: icmp_seq=0 ttl=63 time=92.391 ms
64 bytes from 10.0.0.8: icmp_seq=1 ttl=63 time=21.870 ms
64 bytes from 10.0.0.8: icmp_seq=2 ttl=63 time=198.022 ms
64 bytes from 10.0.0.8: icmp_seq=3 ttl=63 time=22.241 ms
64 bytes from 10.0.0.8: icmp_seq=4 ttl=63 time=174.705 ms
--- 10.0.0.8 ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max/stddev = 21.870/101.846/198.022/74.001 ms

priv-net# ping 10.246.38.107
PING 10.246.38.1 (10.246.38.107): 56 data bytes
64 bytes from 10.246.38.107: icmp_seq=0 ttl=64 time=53.491 ms
64 bytes from 10.246.38.107: icmp_seq=1 ttl=64 time=23.395 ms
64 bytes from 10.246.38.107: icmp_seq=2 ttl=64 time=23.865 ms
64 bytes from 10.246.38.107: icmp_seq=3 ttl=64 time=21.145 ms
64 bytes from 10.246.38.107: icmp_seq=4 ttl=64 time=36.708 ms
--- 10.246.38.107 ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max/stddev = 21.145/31.721/53.491/12.179 ms
....

Setting up the tunnels is the easy part. Configuring a secure link is a more in depth process. The following configuration uses pre-shared (PSK) RSA keys. Other than the IP addresses, the [.filename]#/usr/local/etc/racoon/racoon.conf# on both gateways will be identical and look similar to:

[.programlisting]
....
path    pre_shared_key  "/usr/local/etc/racoon/psk.txt"; #location of pre-shared key file
log     debug;	#log verbosity setting: set to 'notify' when testing and debugging is complete

padding	# options are not to be changed
{
        maximum_length  20;
        randomize       off;
        strict_check    off;
        exclusive_tail  off;
}

timer	# timing options. change as needed
{
        counter         5;
        interval        20 sec;
        persend         1;
#       natt_keepalive  15 sec;
        phase1          30 sec;
        phase2          15 sec;
}

listen	# address [port] that racoon will listen on
{
        isakmp          172.16.5.4 [500];
        isakmp_natt     172.16.5.4 [4500];
}

remote  192.168.1.12 [500]
{
        exchange_mode   main,aggressive;
        doi             ipsec_doi;
        situation       identity_only;
        my_identifier   address 172.16.5.4;
        peers_identifier        address 192.168.1.12;
        lifetime        time 8 hour;
        passive         off;
        proposal_check  obey;
#       nat_traversal   off;
        generate_policy off;

                        proposal {
                                encryption_algorithm    blowfish;
                                hash_algorithm          md5;
                                authentication_method   pre_shared_key;
                                lifetime time           30 sec;
                                dh_group                1;
                        }
}

sainfo  (address 10.246.38.0/24 any address 10.0.0.0/24 any)	# address $network/$netmask $type address $network/$netmask $type ( $type being any or esp)
{								# $network must be the two internal networks you are joining.
        pfs_group       1;
        lifetime        time    36000 sec;
        encryption_algorithm    blowfish,3des;
        authentication_algorithm        hmac_md5,hmac_sha1;
        compression_algorithm   deflate;
}
....

For descriptions of each available option, refer to the manual page for [.filename]#racoon.conf#.

The Security Policy Database (SPD) needs to be configured so that FreeBSD and racoon are able to encrypt and decrypt network traffic between the hosts.

This can be achieved with a shell script, similar to the following, on the corporate gateway. This file will be used during system initialization and should be saved as [.filename]#/usr/local/etc/racoon/setkey.conf#.

[.programlisting]
....
flush;
spdflush;
# To the home network
spdadd 10.246.38.0/24 10.0.0.0/24 any -P out ipsec esp/tunnel/172.16.5.4-192.168.1.12/use;
spdadd 10.0.0.0/24 10.246.38.0/24 any -P in ipsec esp/tunnel/192.168.1.12-172.16.5.4/use;
....

Once in place, racoon may be started on both gateways using the following command:

[source,bash]
....
# /usr/local/sbin/racoon -F -f /usr/local/etc/racoon/racoon.conf -l /var/log/racoon.log
....

The output should be similar to the following:

[.programlisting]
....
corp-net# /usr/local/sbin/racoon -F -f /usr/local/etc/racoon/racoon.conf
Foreground mode.
2006-01-30 01:35:47: INFO: begin Identity Protection mode.
2006-01-30 01:35:48: INFO: received Vendor ID: KAME/racoon
2006-01-30 01:35:55: INFO: received Vendor ID: KAME/racoon
2006-01-30 01:36:04: INFO: ISAKMP-SA established 172.16.5.4[500]-192.168.1.12[500] spi:623b9b3bd2492452:7deab82d54ff704a
2006-01-30 01:36:05: INFO: initiate new phase 2 negotiation: 172.16.5.4[0]192.168.1.12[0]
2006-01-30 01:36:09: INFO: IPsec-SA established: ESP/Tunnel 192.168.1.12[0]->172.16.5.4[0] spi=28496098(0x1b2d0e2)
2006-01-30 01:36:09: INFO: IPsec-SA established: ESP/Tunnel 172.16.5.4[0]->192.168.1.12[0] spi=47784998(0x2d92426)
2006-01-30 01:36:13: INFO: respond new phase 2 negotiation: 172.16.5.4[0]192.168.1.12[0]
2006-01-30 01:36:18: INFO: IPsec-SA established: ESP/Tunnel 192.168.1.12[0]->172.16.5.4[0] spi=124397467(0x76a279b)
2006-01-30 01:36:18: INFO: IPsec-SA established: ESP/Tunnel 172.16.5.4[0]->192.168.1.12[0] spi=175852902(0xa7b4d66)
....

To ensure the tunnel is working properly, switch to another console and use man:tcpdump[1] to view network traffic using the following command. Replace `em0` with the network interface card as required:

[source,bash]
....
# tcpdump -i em0 host 172.16.5.4 and dst 192.168.1.12
....

Data similar to the following should appear on the console. If not, there is an issue and debugging the returned data will be required.

[.programlisting]
....
01:47:32.021683 IP corporatenetwork.com > 192.168.1.12.privatenetwork.com: ESP(spi=0x02acbf9f,seq=0xa)
01:47:33.022442 IP corporatenetwork.com > 192.168.1.12.privatenetwork.com: ESP(spi=0x02acbf9f,seq=0xb)
01:47:34.024218 IP corporatenetwork.com > 192.168.1.12.privatenetwork.com: ESP(spi=0x02acbf9f,seq=0xc)
....

At this point, both networks should be available and seem to be part of the same network. Most likely both networks are protected by a firewall. To allow traffic to flow between them, rules need to be added to pass packets. For the man:ipfw[8] firewall, add the following lines to the firewall configuration file:

[.programlisting]
....
ipfw add 00201 allow log esp from any to any
ipfw add 00202 allow log ah from any to any
ipfw add 00203 allow log ipencap from any to any
ipfw add 00204 allow log udp from any 500 to any
....

[NOTE]
====
The rule numbers may need to be altered depending on the current host configuration.
====

For users of man:pf[4] or man:ipf[8], the following rules should do the trick:

[.programlisting]
....
pass in quick proto esp from any to any
pass in quick proto ah from any to any
pass in quick proto ipencap from any to any
pass in quick proto udp from any port = 500 to any port = 500
pass in quick on gif0 from any to any
pass out quick proto esp from any to any
pass out quick proto ah from any to any
pass out quick proto ipencap from any to any
pass out quick proto udp from any port = 500 to any port = 500
pass out quick on gif0 from any to any
....

Finally, to allow the machine to start support for the VPN during system initialization, add the following lines to [.filename]#/etc/rc.conf#:

[.programlisting]
....
ipsec_enable="YES"
ipsec_program="/usr/local/sbin/setkey"
ipsec_file="/usr/local/etc/racoon/setkey.conf" # allows setting up spd policies on boot
racoon_enable="yes"
....

[[openssh]]
== OpenSSH

OpenSSH 是一套網路連線工具，可安全的存取遠端的主機，此外，透過 SSH 連線可以建立 TCP/IP 連線通道或安全的轉送 TCP/IP 的封包。OpenSSH 會對所有傳輸的資料做加密，可有效的避免竊聽 (Eavesdropping)、或連線劫持 (Connection hijacking) 與其他網路層的攻擊。

OpenSSH 由 OpenBSD 專案所維護且在 FreeBSD 預設會安裝，它可同時相容 SSH 版本 1 與 2 通訊協定。

當以未加密的方式在網路上傳送資料時，任何在客戶端與伺服器之間的網路竊聽程式 (Network sniffer) 皆可竊取使用者/密碼資訊或者在連線階段傳送的資料，OpenSSH 提供了數種認証與加密方式來避免這種事情發生。更多有關 OpenSSH 的資訊可於 http://www.openssh.com/[http://www.openssh.com/] 取得。

本節會簡單介紹如何使用內建的客戶端工具安全的存取其他系統及安全的傳輸檔案到 FreeBSD 系統，然後會說明如何設定在 FreeBSD 系統上的 SSH 伺服器。更多的資訊可於本章節所提及的操作手冊 (Man page) 取得。

=== 使用 SSH 客戶端工具

要登入一台 SSH 伺服器，可使用 `ssh` 然後指定在伺服器上存在的使用者名稱與 IP 位址或伺服器的主機名稱。若這是第一次連線到指定的伺服器，會提示該使用者伺服器的指紋做第一次檢驗：

[source,bash]
....
# ssh user@example.com
The authenticity of host 'example.com (10.0.0.1)' can't be established.
ECDSA key fingerprint is 25:cc:73:b5:b3:96:75:3d:56:19:49:d2:5c:1f:91:3b.
Are you sure you want to continue connecting (yes/no)? yes
Permanently added 'example.com' (ECDSA) to the list of known hosts.
Password for user@example.com: user_password
....

SSH 會在客戶端連線時利用金鑰指紋 (Key fingerprint) 系統來驗證伺服器的真偽，當使用者在第一次連線時輸入 `yes` 接受了這個金鑰指紋，便會將該金鑰的複本儲存到使用者家目錄的 [.filename]#.ssh/known_hosts#，未來嘗試登入時便會以這個存好的金鑰來驗證，若伺服器的金鑰與儲存的金鑰不同將會顯示警告訊息。若出現這個警告時，使用者應在繼續連線之前檢查金鑰變動的原因。

最近版本的 OpenSSH 預設只會接受 SSHv2 的連線。客戶端預設會盡可能使用版本 2 的通訊協定，若伺服器不支援版本 2 的通訊協定便會向下相容版本 1 的協定。要強制 `ssh` 只能使用指定的通訊協定，可使用 `-1` 或 `-2`，其他的選項在 man:ssh[1] 中有說明。

使用 man:scp[1] 可從遠端主機安全的複製一個檔案，以下範例會複製在遠端主機的 [.filename]#COPYRIGHT# 到本地主機的目前目錄：

[source,bash]
....
# scp user@example.com:/COPYRIGHT COPYRIGHT
Password for user@example.com: *******
COPYRIGHT            100% |*****************************|  4735
00:00
#
....

由於這個主機的指紋已驗證過，在提示用者輸入密碼之前伺服器的金鑰已自動檢查。

傳給 `scp` 的參數與傳給 `cp` 的參數相似。第一個參數是要複製的檔案，第二個參數是目地，由於檔案是透過網路取得，檔案參數需要使用 `user@host:<path_to_remote_file>` 格式。注意，在 `scp` 要遞迴複製目錄是使用 `-r`，如同 `cp` 使用 `-R`。

要開啟可互動的連線來複製檔案可使用 `sftp`，請參考 man:sftp[1] 來取得在 `sftp` 連線時可用的指令清單。

[[security-ssh-keygen]]
==== 以金鑰為基礎的認證

除了使用密碼之外，客戶端可以設定成使用金鑰來連線到遠端的主機。要產生 RSA 認証金鑰可使用 `ssh-keygen`。要產生成對的公鑰與私鑰，可指定金鑰的類型並依提示操作。建議使用容易記住但較難猜出的密碼來保護這個金鑰。

[source,bash]
....
% ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/user/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):  <.>
Enter same passphrase again:                 <.>
Your identification has been saved in /home/user/.ssh/id_rsa.
Your public key has been saved in /home/user/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:54Xm9Uvtv6H4NOo6yjP/YCfODryvUU7yWHzMqeXwhq8 user@host.example.com
The key's randomart image is:
+---[RSA 2048]----+
|                 |
|                 |
|                 |
|        . o..    |
|       .S*+*o    |
|      . O=Oo . . |
|       = Oo= oo..|
|      .oB.* +.oo.|
|       =OE**.o..=|
+----[SHA256]-----+
....

<.> 在此輸入密碼，密碼不可含有空白或符號。

<.> 再輸入一次密碼驗證。

私鑰會儲存於 [.filename]#~/.ssh/id_rsa# 而公鑰會儲存於 [.filename]#~/.ssh/id_rsa.pub#。__公鑰__必須複製到遠端主機的[.filename]##~/.ssh/authorized_keys## 來讓以金鑰為基礎的認証可以運作。

[WARNING]
====

許多使用者認為金鑰的設計是安全的並在產生金鑰時未使用密碼，這樣的行為其實很__危險__。管理者可以手動查看私鑰來檢查金鑰對是否受密碼保護，如果私鑰檔案中包含 `ENCRYPTED` 字詞，則代表金鑰的擁有者有使用密碼。此外，要更進一步保護最終使用者的安全，可在公鑰檔案中放入 `from`，例如，在 `ssh-rsa` 前加上 `from="192.168.10.5"` 將只允許指定的使用者由該 IP 位址登入。
====

不同版本 OpenSSH 的選項與檔案會不同，要避免發生問題請參考 man:ssh-keygen[1]。

若使用了密碼，在每次連線到伺服器時都會提示使用者輸入密碼。要將 SSH 金鑰載入到記憶體並讓每次連線時不必再輸入密碼，可使用 man:ssh-agent[1] 與 man:ssh-add[1]。

認証可用 `ssh-agent` 來管理，只要將私鑰載入，`ssh-agent` 可用在執行其他應用程式，如 Shell 或視窗管理程式。

要在 Shell 使用 `ssh-agent`，使用 Shell 做為參數來啟動 `ssh-agent`。執行 `ssh-add` 來加入識別碼，然後輸入私鑰的密碼。使用者將可使用 `ssh` 連線到任何有安裝對應公鑰的主機，例如：

[source,bash]
....
% ssh-agent csh
% ssh-add
Enter passphrase for key '/usr/home/user/.ssh/id_rsa':  <.>
Identity added: /usr/home/user/.ssh/id_rsa (/usr/home/user/.ssh/id_rsa)
%
....

<.> 輸入金鑰的密碼。

要在 Xorg 使用 `ssh-agent` 可在 [.filename]#~/.xinitrc# 加入一個設定項目，這可讓 `ssh-agent` 對所有在 Xorg 中執行的程式提供服務。[.filename]#~/.xinitrc# 範例如下：

[.programlisting]
....
exec ssh-agent startxfce4
....

這會在每次啟動 Xorg 時，反過來先執行 `ssh-agent` 再由執行 XFCE，一但 Xorg 被重新啟動，要讓所有變更生效需執行 `ssh-add` 來載入所有的 SSH 金鑰。

[[security-ssh-tunneling]]
==== SSH 通道

OpenSSH 可以建立一個通道 (Tunnel) 來封裝其他通訊協定到一個加密的連線。

以下指令會告訴 `ssh` 建立一個供 telnet 使用的通道：

[source,bash]
....
% ssh -2 -N -f -L 5023:localhost:23 user@foo.example.com
%
....

這個例子使用了以下選項：

`-2`::
強制 `ssh` 使用版本 2 的通訊協定連線到伺服器。

`-N`::
代表不需下指令、只建立通道。若省略這個選項 `ssh` 會初始化一個正常的連線。

`-f`::
強制 `ssh` 在背景執行。

`-L`::
代表這是一個本地通道，使用 _localport:remotehost:remoteport_ 格式。

`user@foo.example.com`::
在指定的遠端 SSH 伺服器要使用的登入名稱。

SSH 通道會建立一個傾聽 `localhost` 指定 `localport` 的 Socket ，然後會透過 SSH 連線轉送任何在 `localport` 接收的連線。以這個例子來說在客戶端的 Port `5023` 會被轉送到遠端主機的 Port `23`，由於 Port 23 是由 telnet 使用，所以這會透過 SSH 通道建立一個加密的 telnet 連線。

這個方法可用來包裝許多不安全的 TCP 通訊協定，例如 SMTP, POP3 以及 FTP，如下例所示。

.建立供 SMTP 使用的安全通道
[example]
====

[source,bash]
....
% ssh -2 -N -f -L 5025:localhost:25 user@mailserver.example.com
user@mailserver.example.com's password: *****
% telnet localhost 5025
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
220 mailserver.example.com ESMTP
....

這可配合 `ssh-keygen` 與另一個使用者帳號與來建立一個更無縫的 SSH 通道環境，可使用金鑰來代替手動輸入密碼，然後該通道便可以另一個使用者執行。
====

.安全存取 POP3 伺服器
[example]
====
在這個例子中有一個 SSH 伺服器會接受來自外部的連線，在同個網段下有一個郵件伺服器執行 POP3 伺服器。要使用較安全的方式檢查有沒有新郵件可建立一個 SSH 連線到 SSH 伺服器然後透過通道連線到郵件伺服器：

[source,bash]
....
% ssh -2 -N -f -L 2110:mail.example.com:110 user@ssh-server.example.com
user@ssh-server.example.com's password: ******
....

一但通道啟動並執行後，指定郵件客戶端將 POP3 請求傳送到 `localhost` 的 Port 2110，這個連線將會被安全的透過通道轉送到 `mail.example.com`。
====

.跳過防火牆
[example]
====
有些防火牆會同時過濾傳入與傳出的連線。例如，防火牆很可能會限制來自遠端主機只能存取 Port 22 與 80 來只讓 SSH 與網頁瀏覽器連線，這會使得 Port 使用 22 或 80 以外的服務無法存取。

這問題的解決方法是建立一個 SSH 連線到在防火牆防護之外主機然後使用該連線的通道連到想要使用的服務：

[source,bash]
....
% ssh -2 -N -f -L 8888:music.example.com:8000 user@unfirewalled-system.example.org
user@unfirewalled-system.example.org's password: *******
....

在這個例子中，串流 Ogg Vorbis 客戶端現在可以指向 `localhost` Port 8888，連線將會被轉送到 `music.example.com` 於 Port 8000，成功的跳過防火牆。
====

=== 開啟 SSH 伺服器

除了提供內建的 SSH 客戶端工具外，還可以設定 FreeBSD 系統為一個 SSH 伺服器，以接受來自其他 SSH 客戶端的連線。

要查看 sshd 是否正在運作，可使用 man:service[8] 指令：

[source,bash]
....
# service sshd status
....

若服務未執行，請加入下行到 [.filename]#/etc/rc.conf#。

[.programlisting]
....
sshd_enable="YES"
....

這會讓下次系統開機時啟動 OpenSSH 的 Daemon 程式 sshd。若要立即啟動：

[source,bash]
....
# service sshd start
....

在 FreeBSD 系統第一次啟動 sshd 時便會自動產生系統的主機金鑰且會顯示指紋在 Console 上，這個指紋可供使用者在第一次連線到伺服器時驗證用。

請參考 man:sshd[8] 可取得在啟動 sshd 時可用選項的清單以及更多完整有關認証、登入程序與各種設定檔的資訊。

現在，sshd 應可供所有在系統上有使用者名稱及密碼的使用者使用。

=== SSH 伺服器安全性

在 FreeBSD 廣泛使用 sshd 做為遠端管理基礎設施的同時，所有暴露在公有網路上的系統也會時常受到暴力攻擊 (Brute force attack) 與路過攻擊 (Drive by attack)。在本節會介紹一些可用來避免這些攻擊的參數。

使用在 OpenSSH 伺服器設定檔的 `AllowUsers` 關鍵字限制可以登入到 SSH 伺服器的使用者及來源是一個不錯的方式。例如要只允許來自 `192.168.1.32` 的 `root` 登入，可加入下行到 [.filename]#/etc/ssh/sshd_config#：

[.programlisting]
....
AllowUsers root@192.168.1.32
....

要允許來自任何地方的 `admin` 登入，可只列出使用者名稱，不指定 IP 位址：

[.programlisting]
....
AllowUsers admin
....

有多位使用者也應列在同一行，例如：

[.programlisting]
....
AllowUsers root@192.168.1.32 admin
....

在對 [.filename]#/etc/ssh/sshd_config# 做完變更後，執行以下指令告訴 sshd 重新載入設定檔：

[source,bash]
....
# service sshd reload
....

[NOTE]
====
在使用了這個關鍵字時，列出每一位需要登入此主機的使用者很重要，任何未被在該行指定的使用者將無法登入。同時，在 OpenSSH 伺服器設定檔使用的關鍵字是區分大小寫的，若關鍵字未正確的拼寫 (含其大小寫)，則將會被忽略，永遠要記得測試對這個檔案所做的更改來確保伺服器有如預期的方式運作。請參考 man:sshd_config[5] 來檢查拼寫以及可用的關鍵字。
====

此外，使用者可能被強制要透過公鑰與私鑰使用雙重認證 (Two factor authentication)。當需要時，使用者可以透過使用 man:ssh-keygen[1] 產生一堆金鑰然後將公鑰傳送給管理者，這個金鑰檔會如以上在客戶端章節所述的被放在 [.filename]#authorized_keys#。要強制使用者只能使用這個金鑰，可能需要設定以下選項：

[.programlisting]
....
AuthenticationMethods publickey
....

[TIP]
====

請不要將 [.filename]#/etc/ssh/sshd_config# 以及 [.filename]#/etc/ssh/ssh_config# 搞混 (注意在第一節檔名有多出個 `d`)，第一個檔案用來設定伺服器，而第二個檔案用來設定客戶端。請參考 man:ssh_config[5] 來取得可用的客戶端設定清單。
====

[[fs-acl]]
== 存取控制清單

Access Control Lists (ACLs) extend the standard UNIX(TM) permission model in a POSIX(TM).1e compatible way. This permits an administrator to take advantage of a more fine-grained permissions model.

The FreeBSD [.filename]#GENERIC# kernel provides ACL support for UFS file systems. Users who prefer to compile a custom kernel must include the following option in their custom kernel configuration file:

[.programlisting]
....
options UFS_ACL
....

If this option is not compiled in, a warning message will be displayed when attempting to mount a file system with ACL support. ACLs rely on extended attributes which are natively supported in UFS2.

This chapter describes how to enable ACL support and provides some usage examples.

=== 開啟 ACL 支援

ACLs are enabled by the mount-time administrative flag, `acls`, which may be added to [.filename]#/etc/fstab#. The mount-time flag can also be automatically set in a persistent manner using man:tunefs[8] to modify a superblock ACLs flag in the file system header. In general, it is preferred to use the superblock flag for several reasons:

* The superblock flag cannot be changed by a remount using `mount -u` as it requires a complete `umount` and fresh `mount`. This means that ACLs cannot be enabled on the root file system after boot. It also means that ACL support on a file system cannot be changed while the system is in use.
* Setting the superblock flag causes the file system to always be mounted with ACLs enabled, even if there is not an [.filename]#fstab# entry or if the devices re-order. This prevents accidental mounting of the file system without ACL support.

[NOTE]
====
It is desirable to discourage accidental mounting without ACLs enabled because nasty things can happen if ACLs are enabled, then disabled, then re-enabled without flushing the extended attributes. In general, once ACLs are enabled on a file system, they should not be disabled, as the resulting file protections may not be compatible with those intended by the users of the system, and re-enabling ACLs may re-attach the previous ACLs to files that have since had their permissions changed, resulting in unpredictable behavior.
====

File systems with ACLs enabled will show a plus (`+`) sign in their permission settings:

[.programlisting]
....
drwx------  2 robert  robert  512 Dec 27 11:54 private
drwxrwx---+ 2 robert  robert  512 Dec 23 10:57 directory1
drwxrwx---+ 2 robert  robert  512 Dec 22 10:20 directory2
drwxrwx---+ 2 robert  robert  512 Dec 27 11:57 directory3
drwxr-xr-x  2 robert  robert  512 Nov 10 11:54 public_html
....

In this example, [.filename]#directory1#, [.filename]#directory2#, and [.filename]#directory3# are all taking advantage of ACLs, whereas [.filename]#public_html# is not.

=== 使用 ACL

File system ACLs can be viewed using `getfacl`. For instance, to view the ACL settings on [.filename]#test#:

[source,bash]
....
% getfacl test
	#file:test
	#owner:1001
	#group:1001
	user::rw-
	group::r--
	other::r--
....

To change the ACL settings on this file, use `setfacl`. To remove all of the currently defined ACLs from a file or file system, include `-k`. However, the preferred method is to use `-b` as it leaves the basic fields required for ACLs to work.

[source,bash]
....
% setfacl -k test
....

To modify the default ACL entries, use `-m`:

[source,bash]
....
% setfacl -m u:trhodes:rwx,group:web:r--,o::--- test
....

In this example, there were no pre-defined entries, as they were removed by the previous command. This command restores the default options and assigns the options listed. If a user or group is added which does not exist on the system, an `Invalid argument` error will be displayed.

Refer to man:getfacl[1] and man:setfacl[1] for more information about the options available for these commands.

[[security-pkg]]
== 監視第三方安全性問題

In recent years, the security world has made many improvements to how vulnerability assessment is handled. The threat of system intrusion increases as third party utilities are installed and configured for virtually any operating system available today.

Vulnerability assessment is a key factor in security. While FreeBSD releases advisories for the base system, doing so for every third party utility is beyond the FreeBSD Project's capability. There is a way to mitigate third party vulnerabilities and warn administrators of known security issues. A FreeBSD add on utility known as pkg includes options explicitly for this purpose.

pkg polls a database for security issues. The database is updated and maintained by the FreeBSD Security Team and ports developers.

Please refer to crossref:ports[pkgng-intro,instructions] for installing pkg.

Installation provides man:periodic[8] configuration files for maintaining the pkg audit database, and provides a programmatic method of keeping it updated. This functionality is enabled if `daily_status_security_pkgaudit_enable` is set to `YES` in man:periodic.conf[5]. Ensure that daily security run emails, which are sent to ``root``'s email account, are being read.

After installation, and to audit third party utilities as part of the Ports Collection at any time, an administrator may choose to update the database and view known vulnerabilities of installed packages by invoking:

[source,bash]
....
# pkg audit -F
....

pkg displays messages any published vulnerabilities in installed packages:

[.programlisting]
....
Affected package: cups-base-1.1.22.0_1
Type of problem: cups-base -- HPGL buffer overflow vulnerability.
Reference: <https://www.FreeBSD.org/ports/portaudit/40a3bca2-6809-11d9-a9e7-0001020eed82.html>

1 problem(s) in your installed packages found.

You are advised to update or deinstall the affected package(s) immediately.
....

By pointing a web browser to the displayed URL, an administrator may obtain more information about the vulnerability. This will include the versions affected, by FreeBSD port version, along with other web sites which may contain security advisories.

pkg is a powerful utility and is extremely useful when coupled with package:ports-mgmt/portmaster[].

[[security-advisories]]
== FreeBSD 安全報告

Like many producers of quality operating systems, the FreeBSD Project has a security team which is responsible for determining the End-of-Life (EoL) date for each FreeBSD release and to provide security updates for supported releases which have not yet reached their EoL. More information about the FreeBSD security team and the supported releases is available on the https://www.FreeBSD.org/security[FreeBSD security page].

One task of the security team is to respond to reported security vulnerabilities in the FreeBSD operating system. Once a vulnerability is confirmed, the security team verifies the steps necessary to fix the vulnerability and updates the source code with the fix. It then publishes the details as a "Security Advisory". Security advisories are published on the https://www.FreeBSD.org/security/advisories/[FreeBSD website] and mailed to the http://lists.FreeBSD.org/mailman/listinfo/freebsd-security-notifications[freebsd-security-notifications], http://lists.FreeBSD.org/mailman/listinfo/freebsd-security[freebsd-security], and http://lists.FreeBSD.org/mailman/listinfo/freebsd-announce[freebsd-announce] mailing lists.

This section describes the format of a FreeBSD security advisory.

=== 安全報告的格式

Here is an example of a FreeBSD security advisory:

[.programlisting]
....
=============================================================================
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

=============================================================================
FreeBSD-SA-14:04.bind                                       Security Advisory
                                                          The FreeBSD Project

Topic:          BIND remote denial of service vulnerability

Category:       contrib
Module:         bind
Announced:      2014-01-14
Credits:        ISC
Affects:        FreeBSD 8.x and FreeBSD 9.x
Corrected:      2014-01-14 19:38:37 UTC (stable/9, 9.2-STABLE)
                2014-01-14 19:42:28 UTC (releng/9.2, 9.2-RELEASE-p3)
                2014-01-14 19:42:28 UTC (releng/9.1, 9.1-RELEASE-p10)
                2014-01-14 19:38:37 UTC (stable/8, 8.4-STABLE)
                2014-01-14 19:42:28 UTC (releng/8.4, 8.4-RELEASE-p7)
                2014-01-14 19:42:28 UTC (releng/8.3, 8.3-RELEASE-p14)
CVE Name:       CVE-2014-0591

For general information regarding FreeBSD Security Advisories,
including descriptions of the fields above, security branches, and the
following sections, please visit <URL:http://security.FreeBSD.org/>.

I.   Background

BIND 9 is an implementation of the Domain Name System (DNS) protocols.
The named(8) daemon is an Internet Domain Name Server.

II.  Problem Description

Because of a defect in handling queries for NSEC3-signed zones, BIND can
crash with an "INSIST" failure in name.c when processing queries possessing
certain properties.  This issue only affects authoritative nameservers with
at least one NSEC3-signed zone.  Recursive-only servers are not at risk.

III. Impact

An attacker who can send a specially crafted query could cause named(8)
to crash, resulting in a denial of service.

IV.  Workaround

No workaround is available, but systems not running authoritative DNS service
with at least one NSEC3-signed zone using named(8) are not vulnerable.

V.   Solution

Perform one of the following:

1) Upgrade your vulnerable system to a supported FreeBSD stable or
release / security branch (releng) dated after the correction date.

2) To update your vulnerable system via a source code patch:

The following patches have been verified to apply to the applicable
FreeBSD release branches.

a) Download the relevant patch from the location below, and verify the
detached PGP signature using your PGP utility.

[FreeBSD 8.3, 8.4, 9.1, 9.2-RELEASE and 8.4-STABLE]
# fetch http://security.FreeBSD.org/patches/SA-14:04/bind-release.patch
# fetch http://security.FreeBSD.org/patches/SA-14:04/bind-release.patch.asc
# gpg --verify bind-release.patch.asc

[FreeBSD 9.2-STABLE]
# fetch http://security.FreeBSD.org/patches/SA-14:04/bind-stable-9.patch
# fetch http://security.FreeBSD.org/patches/SA-14:04/bind-stable-9.patch.asc
# gpg --verify bind-stable-9.patch.asc

b) Execute the following commands as root:

# cd /usr/src
# patch < /path/to/patch

Recompile the operating system using buildworld and installworld as
described in <URL:https://www.FreeBSD.org/handbook/makeworld.html>.

Restart the applicable daemons, or reboot the system.

3) To update your vulnerable system via a binary patch:

Systems running a RELEASE version of FreeBSD on the i386 or amd64
platforms can be updated via the freebsd-update(8) utility:

# freebsd-update fetch
# freebsd-update install

VI.  Correction details

The following list contains the correction revision numbers for each
affected branch.

Branch/path                                                      Revision
- -------------------------------------------------------------------------
stable/8/                                                         r260646
releng/8.3/                                                       r260647
releng/8.4/                                                       r260647
stable/9/                                                         r260646
releng/9.1/                                                       r260647
releng/9.2/                                                       r260647
- -------------------------------------------------------------------------

To see which files were modified by a particular revision, run the
following command, replacing NNNNNN with the revision number, on a
machine with Subversion installed:

# svn diff -cNNNNNN --summarize svn://svn.freebsd.org/base

Or visit the following URL, replacing NNNNNN with the revision number:

<URL:https://svnweb.freebsd.org/base?view=revision&revision=NNNNNN>

VII. References

<URL:https://kb.isc.org/article/AA-01078>

<URL:http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-0591>

The latest revision of this advisory is available at
<URL:http://security.FreeBSD.org/advisories/FreeBSD-SA-14:04.bind.asc>
-----BEGIN PGP SIGNATURE-----

iQIcBAEBCgAGBQJS1ZTYAAoJEO1n7NZdz2rnOvQP/2/68/s9Cu35PmqNtSZVVxVG
ZSQP5EGWx/lramNf9566iKxOrLRMq/h3XWcC4goVd+gZFrvITJSVOWSa7ntDQ7TO
XcinfRZ/iyiJbs/Rg2wLHc/t5oVSyeouyccqODYFbOwOlk35JjOTMUG1YcX+Zasg
ax8RV+7Zt1QSBkMlOz/myBLXUjlTZ3Xg2FXVsfFQW5/g2CjuHpRSFx1bVNX6ysoG
9DT58EQcYxIS8WfkHRbbXKh9I1nSfZ7/Hky/kTafRdRMrjAgbqFgHkYTYsBZeav5
fYWKGQRJulYfeZQ90yMTvlpF42DjCC3uJYamJnwDIu8OhS1WRBI8fQfr9DRzmRua
OK3BK9hUiScDZOJB6OqeVzUTfe7MAA4/UwrDtTYQ+PqAenv1PK8DZqwXyxA9ThHb
zKO3OwuKOVHJnKvpOcr+eNwo7jbnHlis0oBksj/mrq2P9m2ueF9gzCiq5Ri5Syag
Wssb1HUoMGwqU0roS8+pRpNC8YgsWpsttvUWSZ8u6Vj/FLeHpiV3mYXPVMaKRhVm
067BA2uj4Th1JKtGleox+Em0R7OFbCc/9aWC67wiqI6KRyit9pYiF3npph+7D5Eq
7zPsUdDd+qc+UTiLp3liCRp5w6484wWdhZO6wRtmUgxGjNkxFoNnX8CitzF8AaqO
UWWemqWuz3lAZuORQ9KX
=OQzQ
-----END PGP SIGNATURE-----
....

Every security advisory uses the following format:

* Each security advisory is signed by the PGP key of the Security Officer. The public key for the Security Officer can be verified at crossref:pgpkeys[pgpkeys,OpenPGP 金鑰].
* The name of the security advisory always begins with `FreeBSD-SA-` (for FreeBSD Security Advisory), followed by the year in two digit format (`14:`), followed by the advisory number for that year (`04.`), followed by the name of the affected application or subsystem (`bind`). The advisory shown here is the fourth advisory for 2014 and it affects BIND.
* The `Topic` field summarizes the vulnerability.
* The `Category` refers to the affected part of the system which may be one of `core`, `contrib`, or `ports`. The `core` category means that the vulnerability affects a core component of the FreeBSD operating system. The `contrib` category means that the vulnerability affects software included with FreeBSD, such as BIND. The `ports` category indicates that the vulnerability affects software available through the Ports Collection.
* The `Module` field refers to the component location. In this example, the `bind` module is affected; therefore, this vulnerability affects an application installed with the operating system.
* The `Announced` field reflects the date the security advisory was published. This means that the security team has verified that the problem exists and that a patch has been committed to the FreeBSD source code repository.
* The `Credits` field gives credit to the individual or organization who noticed the vulnerability and reported it.
* The `Affects` field explains which releases of FreeBSD are affected by this vulnerability.
* The `Corrected` field indicates the date, time, time offset, and releases that were corrected. The section in parentheses shows each branch for which the fix has been merged, and the version number of the corresponding release from that branch. The release identifier itself includes the version number and, if appropriate, the patch level. The patch level is the letter `p` followed by a number, indicating the sequence number of the patch, allowing users to track which patches have already been applied to the system.
* The `CVE Name` field lists the advisory number, if one exists, in the public http://cve.mitre.org[cve.mitre.org] security vulnerabilities database.
* The `Background` field provides a description of the affected module.
* The `Problem Description` field explains the vulnerability. This can include information about the flawed code and how the utility could be maliciously used.
* The `Impact` field describes what type of impact the problem could have on a system.
* The `Workaround` field indicates if a workaround is available to system administrators who cannot immediately patch the system .
* The `Solution` field provides the instructions for patching the affected system. This is a step by step tested and verified method for getting a system patched and working securely.
* The `Correction Details` field displays each affected Subversion branch with the revision number that contains the corrected code.
* The `References` field offers sources of additional information regarding the vulnerability.

[[security-accounting]]
== 程序追蹤

Process accounting is a security method in which an administrator may keep track of system resources used and their allocation among users, provide for system monitoring, and minimally track a user's commands.

Process accounting has both positive and negative points. One of the positives is that an intrusion may be narrowed down to the point of entry. A negative is the amount of logs generated by process accounting, and the disk space they may require. This section walks an administrator through the basics of process accounting.

[NOTE]
====
If more fine-grained accounting is needed, refer to crossref:audit[audit,安全事件稽查].
====

=== 開啟並使用程序追蹤

Before using process accounting, it must be enabled using the following commands:

[source,bash]
....
# sysrc accounting_enable=yes
# service accounting start
....

The accounting information is stored in files located in [.filename]#/var/account#, which is automatically created, if necessary, the first time the accounting service starts. These files contain sensitive information, including all the commands issued by all users. Write access to the files is limited to `root`, and read access is limited to `root` and members of the `wheel` group. To also prevent members of `wheel` from reading the files, change the mode of the [.filename]#/var/account# directory to allow access only by `root`.

Once enabled, accounting will begin to track information such as CPU statistics and executed commands. All accounting logs are in a non-human readable format which can be viewed using `sa`. If issued without any options, `sa` prints information relating to the number of per-user calls, the total elapsed time in minutes, total CPU and user time in minutes, and the average number of I/O operations. Refer to man:sa[8] for the list of available options which control the output.

To display the commands issued by users, use `lastcomm`. For example, this command prints out all usage of `ls` by `trhodes` on the `ttyp1` terminal:

[source,bash]
....
# lastcomm ls trhodes ttyp1
....

Many other useful options exist and are explained in man:lastcomm[1], man:acct[5], and man:sa[8].

[[security-resourcelimits]]
== 限制資源

FreeBSD provides several methods for an administrator to limit the amount of system resources an individual may use. Disk quotas limit the amount of disk space available to users. Quotas are discussed in crossref:disks[quotas,磁碟配額].

Limits to other resources, such as CPU and memory, can be set using either a flat file or a command to configure a resource limits database. The traditional method defines login classes by editing [.filename]#/etc/login.conf#. While this method is still supported, any changes require a multi-step process of editing this file, rebuilding the resource database, making necessary changes to [.filename]#/etc/master.passwd#, and rebuilding the password database. This can become time consuming, depending upon the number of users to configure.

`rctl` can be used to provide a more fine-grained method for controlling resource limits. This command supports more than user limits as it can also be used to set resource constraints on processes and jails.

This section demonstrates both methods for controlling resources, beginning with the traditional method.

[[users-limiting]]
=== 設定登入類別

In the traditional method, login classes and the resource limits to apply to a login class are defined in [.filename]#/etc/login.conf#. Each user account can be assigned to a login class, where `default` is the default login class. Each login class has a set of login capabilities associated with it. A login capability is a `_name_=_value_` pair, where _name_ is a well-known identifier and _value_ is an arbitrary string which is processed accordingly depending on the _name_.

[NOTE]
====
Whenever [.filename]#/etc/login.conf# is edited, the [.filename]#/etc/login.conf.db# must be updated by executing the following command:

[source,bash]
....
# cap_mkdb /etc/login.conf
....

====

Resource limits differ from the default login capabilities in two ways. First, for every limit, there is a _soft_ and _hard_ limit. A soft limit may be adjusted by the user or application, but may not be set higher than the hard limit. The hard limit may be lowered by the user, but can only be raised by the superuser. Second, most resource limits apply per process to a specific user.

<<resource-limits>> lists the most commonly used resource limits. All of the available resource limits and capabilities are described in detail in man:login.conf[5].
[[resource-limits]]
.登入類別限制資源類型
[cols="1,1", frame="none", options="header"]
|===
| 限制資源
| 說明

|coredumpsize
|The limit on the size of a core file generated by a program is subordinate to other limits on disk usage, such as `filesize` or disk quotas. This limit is often used as a less severe method of controlling disk space consumption. Since users do not generate core files and often do not delete them, this setting may save them from running out of disk space should a large program crash.

|cputime
|The maximum amount of CPU time a user's process may consume. Offending processes will be killed by the kernel. This is a limit on CPU _time_ consumed, not the percentage of the CPU as displayed in some of the fields generated by `top` and `ps`.

|filesize
|The maximum size of a file the user may own. Unlike disk quotas (crossref:disks[quotas,磁碟配額]), this limit is enforced on individual files, not the set of all files a user owns.

|maxproc
|The maximum number of foreground and background processes a user can run. This limit may not be larger than the system limit specified by `kern.maxproc`. Setting this limit too small may hinder a user's productivity as some tasks, such as compiling a large program, start lots of processes.

|memorylocked
|The maximum amount of memory a process may request to be locked into main memory using man:mlock[2]. Some system-critical programs, such as man:amd[8], lock into main memory so that if the system begins to swap, they do not contribute to disk thrashing.

|memoryuse
|The maximum amount of memory a process may consume at any given time. It includes both core memory and swap usage. This is not a catch-all limit for restricting memory consumption, but is a good start.

|openfiles
|The maximum number of files a process may have open. In FreeBSD, files are used to represent sockets and IPC channels, so be careful not to set this too low. The system-wide limit for this is defined by `kern.maxfiles`.

|sbsize
|The limit on the amount of network memory a user may consume. This can be generally used to limit network communications.

|stacksize
|The maximum size of a process stack. This alone is not sufficient to limit the amount of memory a program may use, so it should be used in conjunction with other limits.
|===

There are a few other things to remember when setting resource limits:

* Processes started at system startup by [.filename]#/etc/rc# are assigned to the `daemon` login class.
* Although the default [.filename]#/etc/login.conf# is a good source of reasonable values for most limits, they may not be appropriate for every system. Setting a limit too high may open the system up to abuse, while setting it too low may put a strain on productivity.
* Xorg takes a lot of resources and encourages users to run more programs simultaneously.
* Many limits apply to individual processes, not the user as a whole. For example, setting `openfiles` to `50` means that each process the user runs may open up to `50` files. The total amount of files a user may open is the value of `openfiles` multiplied by the value of `maxproc`. This also applies to memory consumption.

For further information on resource limits and login classes and capabilities in general, refer to man:cap_mkdb[1], man:getrlimit[2], and man:login.conf[5].

=== 開啟並設定資源限制

The `kern.racct.enable` tunable must be set to a non-zero value. Custom kernels require specific configuration:

[.programlisting]
....
options         RACCT
options         RCTL
....

Once the system has rebooted into the new kernel, `rctl` may be used to set rules for the system.

Rule syntax is controlled through the use of a subject, subject-id, resource, and action, as seen in this example rule:

[.programlisting]
....
user:trhodes:maxproc:deny=10/user
....

In this rule, the subject is `user`, the subject-id is `trhodes`, the resource, `maxproc`, is the maximum number of processes, and the action is `deny`, which blocks any new processes from being created. This means that the user, `trhodes`, will be constrained to no greater than `10` processes. Other possible actions include logging to the console, passing a notification to man:devd[8], or sending a sigterm to the process.

Some care must be taken when adding rules. Since this user is constrained to `10` processes, this example will prevent the user from performing other tasks after logging in and executing a `screen` session. Once a resource limit has been hit, an error will be printed, as in this example:

[source,bash]
....
% man test
    /usr/bin/man: Cannot fork: Resource temporarily unavailable
eval: Cannot fork: Resource temporarily unavailable
....

As another example, a jail can be prevented from exceeding a memory limit. This rule could be written as:

[source,bash]
....
# rctl -a jail:httpd:memoryuse:deny=2G/jail
....

Rules will persist across reboots if they have been added to [.filename]#/etc/rctl.conf#. The format is a rule, without the preceding command. For example, the previous rule could be added as:

[.programlisting]
....
# Block jail from using more than 2G memory:
jail:httpd:memoryuse:deny=2G/jail
....

To remove a rule, use `rctl` to remove it from the list:

[source,bash]
....
# rctl -r user:trhodes:maxproc:deny=10/user
....

A method for removing all rules is documented in man:rctl[8]. However, if removing all rules for a single user is required, this command may be issued:

[source,bash]
....
# rctl -r user:trhodes
....

Many other resources exist which can be used to exert additional control over various `subjects`. See man:rctl[8] to learn about them.

[[security-sudo]]
== 使用 Sudo 分享管理權限

系統管理者通常會要能夠授予額外的權限給其他使用者，以讓這些使用者可以執行需權限的工作。要讓團隊成員可以存取 FreeBSD 系統來完成其特定的工作對所有管理者都會帶來挑戰，這些團隊成員通常只需要比一般使用者多出一些存取權限便可作業，但他們總是會告訴管理者若沒有超級使用者的存取權便無法完成其工作。幸好，有工具可以管理這類的需求，這樣便不需提供這麼大的權限給一般使用者。

到目前為止，安全性章節已說明了如何允許已授權的使用者存取以及嘗試防止未經授權的存取，而現在有另一個問題，是由已授權的使用者擁有權限存取系統資源造成的。在很多的情況，使用者會需要存取應用程式啟動 Script 的權限或是管理者團隊需要維護系統，以往會使用標準的使用者與群組、檔案權限、甚至是 man:su[1] 指令來管理存取權，但當應用程式需要更多存取權，更多使用者需要使用系統資源時，便需要更好的解決方案，目前最常用來解決此問題的應用程式便是 Sudo。

Sudo 讓管理者可以對系統指令的存取設下更嚴格的限制並提供進階的記錄功能。如同其他工具，它可自 Port 套件集取得，於其中的 package:security/sudo[]，或使用 man:pkg[8] 工具取得，若要使用 man:pkg[8] 工具可：

[source,bash]
....
# pkg install sudo
....

安裝完成之後，可用安裝的 `visudo` 以文字編輯器開啟設定檔，強烈建議使用 `visudo` 來編輯設定檔，由於它有內建的語法檢查程式可在檔案儲存之前檢驗是否有誤。

設定檔由個小節所組成，透過這些小節可做常廣泛的設定，在以下的範例中，網站應用程式維護人員 user1 需要啟動、停止與重新啟動名稱為 _webservice_ 的網站應用程式 。要授權此使用者執行這些工作的權限，可加入此行到 [.filename]#/usr/local/etc/sudoers# 的最後：

[.programlisting]
....
user1   ALL=(ALL)       /usr/sbin/service webservice *
....

現在使用者可使用此指令來啟動 _webservice_：

[source,bash]
....
% sudo /usr/sbin/service webservice start
....

雖然這項設定可以讓一位使用者存取 webservice 服務，但在大部份組織中會有一整個網站小組負責管理該服務，因此也可以一行來授予整個群組存取權，以下步驟會建立一個網站群組、加入使用者到這個群組，然後讓該群組中的所有成員能夠管理服務：

[source,bash]
....
# pw groupadd -g 6001 -n webteam
....

同樣使用 man:pw[8] 指令來加入該使用到 webteam 群組：

[source,bash]
....
# pw groupmod -m user1 -n webteam
....

最後，在 [.filename]#/usr/local/etc/sudoers# 中的這行設定可以讓 webteam 群組的所有成員可以管理 _webservice_：

[.programlisting]
....
%webteam   ALL=(ALL)       /usr/sbin/service webservice *
....

與 man:su[1] 不同的是 Sudo 只需要一般使用者的密碼，這有一個使用者不需要共用密碼的優點，在大多數安全稽查都會發現共用密碼的問題且這種情況只有壞處可言。

使用 Sudo 允許使用者執行應用程式只需要輸入使用者自己的密碼，這更安全且提供比 man:su[1] 更佳的控制權，因為 man:su[1] 只要輸入 `root` 密碼之後該使用者便可取得所有的 `root` 權限。

[TIP]
====

大多數組織已正在導入或已導入雙重認証 (Two factor authentication)，在這個情境下使用者可以不用輸入密碼，Sudo 提供了 `NOPASSWD` 變數來供這個情境使用，可將該設定加入到上述的設定將可允許所有 _webteam_ 群組的成員不需要輸入密碼便可管理該服務：

[.programlisting]
....
%webteam   ALL=(ALL)       NOPASSWD: /usr/sbin/service webservice *
....

====

[[security-sudo-loggin]]
=== 記錄輸出

採用 Sudo 的另一個優點是能夠開啟連線階段的記錄。使用內建立記錄機制與內含的 sudoreplay 指令，所有透過 Sudo 初始化的指令會被記錄下來供往後檢驗用。要開啟這個功能要加入預設記錄目錄的項目，在以下範例中使用了使用者變數來做目錄名稱，也還有許多其他記錄檔名稱慣例，可參考 sudoreplay 的操作手冊來取得進一步資訊。

[.programlisting]
....
Defaults iolog_dir=/var/log/sudo-io/%{user}
....

[TIP]
====

這個目錄會在記錄功能設定之後自動建立，最好讓系統以預設的權限來建立目錄比較保險，除此之外，這個設定項目也會記錄使用 sudoreplay 指令的管理者，要更改設定請閱讀並取消在 [.filename]#sudoers# 中記錄選項的註解。
====

一旦這個設定加入至 [.filename]#sudoers# 檔案之後，所有的使用者設定項目便可加上記錄存取動作的項目，在 _webteam_ 項目加入額外設定之後的範例如下：

[.programlisting]
....
%webteam ALL=(ALL) NOPASSWD: LOG_INPUT: LOG_OUTPUT: /usr/sbin/service webservice *
....

從此之後，所有 _webteam_ 修改 _webservice_ 應用程式狀態的成員將會被記錄下來。要列出先前與目前連線階段的記錄可：

[source,bash]
....
# sudoreplay -l
....

在輸出結果中要重播指定連線階段的記錄可搜尋 `TSID=` 項目，然後傳送給 sudoreplay 且不加其他選項便可以一般速度重播連線階段，例如：

[source,bash]
....
# sudoreplay user1/00/00/02
....

[WARNING]
====

雖然所有連線階段都會被記錄，但任何管理者都可以移除連線階段，使得沒人知道它們做了什麼事，所以非常值得在入侵偵測系統 (IDS) 或類似的軟體加入每日檢查，以便在有人為修改時通知其他管理人員。
====

`sudoreplay` 的擴充空間非常大，請參考說明文件來取得更多資訊。
