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; }
说明:算法
踩坑的地方就是这个密码,官方文档里对支持的密码类型进行了说明:安全
使用htpasswd或者openssl passwd命令生成的密码当然可使得配置生效,nginx可以正常地进行密码安全校验,若是密码类型不支持, 则nginx或报错:服务器
crypt_r() failed (22: Invalid argument)
可是由于业务的须要,咱们要用代码生成nginx的配置并下发配置到每一个云主机中,以后拉起nginx进程。项目代码使用go语言编写,因此须要找一个对应的函数或者库生成nginx支持的密码。函数
在进行自动生成密码开发以前,思考了一下大概有三种方案能够实现:工具
首先,第一种方式是不太可取的,由于须要强依赖服务器环境,因此直接pass。下面看第二种和第三种方式的具体实现。测试
Linux的crypt函数有两个参数,函数定义为:加密
char *crypt(const char *key, const char *salt);
其中参数key为须要加密的内容,salt参数有两种类型:code
$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标准库中的函数对密码进行加密。
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支持的对密码加密的字符串。