nginx basic auth配置踩坑記

nginx的basic auth配置由ngx_http_auth_basic_module模塊提供,對HTTP Basic Authentication協議進行了支持,用戶可經過該配置設置用戶名和密碼對web站點進行簡單的訪問控制。nginx

basic auth配置示例:web

location / {
    auth_basic           "closed site";
    auth_basic_user_file conf/htpasswd;
}

說明:算法

  • auth_basic可設置爲off或其它字符串,爲off時表示不開啓密碼驗證
  • auth_basic_user_file 爲包含用戶名和密碼的文件,文件內容如elastic:YsEm9Tb4.RwB6

踩坑的地方就是這個密碼,官方文檔裏對支持的密碼類型進行了說明:安全

  • 採用系統函數crypt()加密的密碼;可經過htpasswd命令或者openssl passwd命令生成
  • 經過Apache提供的基於MD5的變種加密算法(apr1),一樣可經過htpasswd或者openssl passwd命令生成
  • 以「{scheme}data」格式表示的加密後的密碼,RFC
    2307中有對該格式的密碼標準進行了說明。其中scheme指的是加密算法,nginx支持的scheme有PLAIN, SHA,
    SSHA算法。

使用htpasswd或者openssl passwd命令生成的密碼當然可使得配置生效,nginx可以正常地進行密碼安全校驗,若是密碼類型不支持, 則nginx或報錯:服務器

crypt_r() failed (22: Invalid argument)

可是由於業務的須要,咱們要用代碼生成nginx的配置並下發配置到每一個雲主機中,以後拉起nginx進程。項目代碼使用go語言編寫,因此須要找一個對應的函數或者庫生成nginx支持的密碼。函數

go語言生成nginx支持的密碼

在進行自動生成密碼開發以前,思考了一下大概有三種方案能夠實現:工具

  • 項目服務器上安裝htpasswd工具或openssl, 經過代碼執行本地命令生成加密密碼
  • 直接調用Linux系統函數crypt()加密密碼
  • 使用go標準庫crypto加密密碼

首先,第一種方式是不太可取的,由於須要強依賴服務器環境,因此直接pass。下面看第二種和第三種方式的具體實現。測試

直接調用系統函數crypt()

Linux的crypt函數有兩個參數,函數定義爲:加密

char *crypt(const char *key, const char *salt);

其中參數key爲須要加密的內容,salt參數有兩種類型:code

  • 長度爲2的字符串,取值範圍爲[a-zA-Z0-9./],若是超過兩位會被忽略,而且只能支持最長8位的key,若是key超過8位,則8位以後的會被忽略
  • $id$salt$encrypted 格式,用於支持其它的加密算法, id表示算法類型,具體取值有:

    ID  | Method
       ─────────────────────────────────────────────
       1   | MD5
       2a  | Blowfish (not in mainline glibc; added in some
           | Linux distributions)
       5   | SHA-256 (since glibc 2.7)
       6   | SHA-512 (since glibc 2.7)

go語言中能夠經過import "C"方式直接調用c語言的庫函數,下面是封裝crypt函數的具體實現:

package crypt

/*
#define _GNU_SOURCE
#include <unistd.h>
*/
import "C"

import (
    "sync"
    "unsafe"
)

var (
    mu sync.Mutex
)


func Crypt(pass, salt string) (string, error) {
    c_pass := C.CString(pass)
    defer C.free(unsafe.Pointer(c_pass))

    c_salt := C.CString(salt)
    defer C.free(unsafe.Pointer(c_salt))

    mu.Lock()
    c_enc, err := C.crypt(c_pass, c_salt)
    mu.Unlock()

    if c_enc == nil {
        return "", err
    }
    defer C.free(unsafe.Pointer(c_enc))

    return C.GoString(c_enc), err
}

生成密碼的具體實現:

func main() {
    des, err := crypt.Crypt("Elastic123", "in")
    if err != nil {
        fmt.Errorf("error:", err)
        return
    }

    sha512, err := crypt.Crypt("Elastic123", "$6$SomeSaltSomePepper$")
    if err != nil {
        fmt.Errorf("error:", err)
        return
    }

    fmt.Println("des:", des)
    fmt.Println("SHA512:", sha512)
}

通過實測,上述經過調用crypt函數生成nginx支持的加密密碼實際可用,可是須要注意的是若是密碼長度超過8位,則salt參數只能選擇$id$salt$encrypted類型,在測試過程當中就是由於踩了這點坑致使nginx只能校驗密碼的前8位,無語。

由於在編寫go代碼過程當中調用了C函數庫,這種方式也須要依賴服務器所處環境,所以最好的方式是採用go標準庫中的函數對密碼進行加密。

使用crypto函數庫

go的crypto標準庫封裝了不少中加密算法,採用SHA加密算法進行密碼加密的代碼以下:

package util

import (
    "crypto/sha1"
    "encoding/base64"
)

func GetSha(password string) string {
    s := sha1.New()
    s.Write([]byte(password))
    passwordSum := []byte(s.Sum(nil))
    return base64.StdEncoding.EncodeToString(passwordSum)
}

測試過程當中經過調用GetSha()函數生成了對密碼加密的字符串,可是直接配置在nginx的conf/htpasswd文件中,reload nginx配置後測試驗證密碼是否生效,結果仍是報錯,原來如前文所述,SHA加密的密碼必須帶有「{SHA}」前綴才能夠,再次修改配置後通過驗證,成功地用代碼生成了nginx支持的對密碼加密的字符串。

相關文章
相關標籤/搜索