小知识:详解nginx basic auth配置踩坑记

nginxbasic auth配置由ngx_http_auth_basic_module模块提供,对HTTP Basic Authentication协议进行了支持,用户可通过该配置设置用户名和密码对web站点进行简单的访问控制。

basic auth配置示例:

?
1
2
3
4
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函数有两个参数,函数定义为:

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

其中参数key为需要加密的内容,salt参数有两种类型:

长度为2的字符串,取值范围为[a-zA-Z0-9./],如果超过两位会被忽略,并且只能支持最长8位的key,如果key超过8位,则8位之后的会被忽略 $id$salt$encrypted 格式,用于支持其它的加密算法, id表示算法类型,具体取值有:
?
1
2
3
4
5
6
7
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函数的具体实现:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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
}

生成密码的具体实现:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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加密算法进行密码加密的代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
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支持的对密码加密的字符串。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://segmentfault.com/a/1190000015437833

声明: 猿站网有关资源均来自网络搜集与网友提供,任何涉及商业盈利目的的均不得使用,否则产生的一切后果将由您自己承担! 本平台资源仅供个人学习交流、测试使用 所有内容请在下载后24小时内删除,制止非法恶意传播,不对任何下载或转载者造成的危害负任何法律责任!也请大家支持、购置正版! 。本站一律禁止以任何方式发布或转载任何违法的相关信息访客发现请向站长举报,会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。本网站的资源部分来源于网络,如有侵权烦请发送邮件至:2697268773@qq.com进行处理。
建站知识

小知识:详解在Docker容器中运行Spring Boot应用

2023-4-17 13:26:10

建站知识

小知识:详解Linux多线程编程(不限Linux)

2023-4-17 13:29:33

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索