NGINX 指令 ssl_password_file 配置详解

ssl_password_file 是 NGINX 中的一个指令,用于指定一个包含密码的文件,这个文件用于提供 SSL/TLS 私钥的密码。该指令在 NGINX 启动时会读取该文件,并用于加载私钥文件时解锁加密的私钥。如果私钥是加密的,那么 NGINX 需要密码才能使用它,密码错误会导致nginx启动或者重新加载失败。

语法

Syntax: ssl_password_file file;
Default:    —
Context:    http, server
This directive appeared in version 1.7.3.

  • <file>:指定包含密码的文件路径。这个文件应该包含一个用于解锁 SSL 私钥的密码。
  • 指定一个包含密钥密码的文件,每个密码都位于单独的一行。在加载密钥时,密码将按顺序尝试。

默认值

  • 默认情况下,ssl_password_file 指令不被启用,即没有设置密码文件。

使用场景

该指令通常在以下情况下使用:
1. 私钥加密:当生成 SSL/TLS 私钥时,如果你为私钥设置了密码保护,那么每次 NGINX 启动时,都需要提供密码才能加载私钥。

配置示例

生成四组自签名证书及私钥, 其中RSA算法两个, ECC算法两个,两组证书中一个带密码,一个不带密码。使用脚本生成

#!/bin/bash

# Set directory variables
CERTS_DIR="/etc/nginx/ssl"

# Create the certificate directory if it doesn't exist
mkdir -p $CERTS_DIR

# Set passwords for different key types
PASSWORD_RSA_WITH="password123"
PASSWORD_ECC_WITH="password789"

# Create password files for multiple certificates
echo $PASSWORD_RSA_WITH > "$CERTS_DIR/rsa_password.txt"
chmod 600 "$CERTS_DIR/rsa_password.txt"
echo $PASSWORD_ECC_WITH > "$CERTS_DIR/ecc_password.txt"
chmod 600 "$CERTS_DIR/ecc_password.txt"
cat "$CERTS_DIR/rsa_password.txt" "$CERTS_DIR/ecc_password.txt" > "$CERTS_DIR/password_file.txt"


# Set certificate information
COUNTRY="CN"
STATE="BEIJING"
LOCALITY="BEIJING"
ORG="test"
ORG_UNIT="test-unit"
EMAIL="email@example.com"

# Generate RSA private key with password
openssl genpkey -algorithm RSA -aes256 -out "$CERTS_DIR/rsa_private_key_with_password.pem" -pass pass:$PASSWORD_RSA_WITH
echo "Generated RSA private key with password: rsa_private_key_with_password.pem"

# Generate RSA private key without password
openssl genpkey -algorithm RSA -out "$CERTS_DIR/rsa_private_key_without_password.pem"
echo "Generated RSA private key without password: rsa_private_key_without_password.pem"

# Generate RSA Certificate Signing Request (CSR) with password, avoiding interactive input
COMMON_NAME="example1.com"
openssl req -new -key "$CERTS_DIR/rsa_private_key_with_password.pem" -out "$CERTS_DIR/rsa_csr_with_password.pem" \
  -passin pass:$PASSWORD_RSA_WITH \
  -subj "/C=$COUNTRY/ST=$STATE/L=$LOCALITY/O=$ORG/OU=$ORG_UNIT/CN=$COMMON_NAME/emailAddress=$EMAIL"
echo "Generated RSA CSR with password: rsa_csr_with_password.pem"

# Generate RSA CSR without password, avoiding interactive input
COMMON_NAME="example2.com"
openssl req -new -key "$CERTS_DIR/rsa_private_key_without_password.pem" -out "$CERTS_DIR/rsa_csr_without_password.pem" \
  -subj "/C=$COUNTRY/ST=$STATE/L=$LOCALITY/O=$ORG/OU=$ORG_UNIT/CN=$COMMON_NAME/emailAddress=$EMAIL"
echo "Generated RSA CSR without password: rsa_csr_without_password.pem"

# Use the RSA private key to sign the certificate
openssl x509 -req -in "$CERTS_DIR/rsa_csr_with_password.pem" -signkey "$CERTS_DIR/rsa_private_key_with_password.pem" -out "$CERTS_DIR/rsa_certificate_with_password.pem" -days 365 -passin pass:$PASSWORD_RSA_WITH
openssl x509 -req -in "$CERTS_DIR/rsa_csr_without_password.pem" -signkey "$CERTS_DIR/rsa_private_key_without_password.pem" -out "$CERTS_DIR/rsa_certificate_without_password.pem" -days 365
echo "Generated RSA certificates"

# Generate ECC private key with password
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -aes256 -out "$CERTS_DIR/ecc_private_key_with_password.pem" -pass pass:$PASSWORD_ECC_WITH
echo "Generated ECC private key with password: ecc_private_key_with_password.pem"

# Generate ECC private key without password
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out "$CERTS_DIR/ecc_private_key_without_password.pem"
echo "Generated ECC private key without password: ecc_private_key_without_password.pem"

# Generate ECC CSR with password, avoiding interactive input
COMMON_NAME="example3.com"
openssl req -new -key "$CERTS_DIR/ecc_private_key_with_password.pem" -out "$CERTS_DIR/ecc_csr_with_password.pem" \
  -passin pass:$PASSWORD_ECC_WITH \
  -subj "/C=$COUNTRY/ST=$STATE/L=$LOCALITY/O=$ORG/OU=$ORG_UNIT/CN=$COMMON_NAME/emailAddress=$EMAIL"
echo "Generated ECC CSR with password: ecc_csr_with_password.pem"

# Generate ECC CSR without password, avoiding interactive input
COMMON_NAME="example4.com"
openssl req -new -key "$CERTS_DIR/ecc_private_key_without_password.pem" -out "$CERTS_DIR/ecc_csr_without_password.pem" \
  -subj "/C=$COUNTRY/ST=$STATE/L=$LOCALITY/O=$ORG/OU=$ORG_UNIT/CN=$COMMON_NAME/emailAddress=$EMAIL"
echo "Generated ECC CSR without password: ecc_csr_without_password.pem"

# Use the ECC private key to sign the certificate
openssl x509 -req -in "$CERTS_DIR/ecc_csr_with_password.pem" -signkey "$CERTS_DIR/ecc_private_key_with_password.pem" -out "$CERTS_DIR/ecc_certificate_with_password.pem" -days 365 -passin pass:$PASSWORD_ECC_WITH
openssl x509 -req -in "$CERTS_DIR/ecc_csr_without_password.pem" -signkey "$CERTS_DIR/ecc_private_key_without_password.pem" -out "$CERTS_DIR/ecc_certificate_without_password.pem" -days 365
echo "Generated ECC certificates"

# Print the generated file paths
echo "RSA Certificate (with password): $CERTS_DIR/rsa_certificate_with_password.pem"
echo "RSA Private Key (with password): $CERTS_DIR/rsa_private_key_with_password.pem"
echo "RSA Certificate (without password): $CERTS_DIR/rsa_certificate_without_password.pem"
echo "RSA Private Key (without password): $CERTS_DIR/rsa_private_key_without_password.pem"
echo "ECC Certificate (with password): $CERTS_DIR/ecc_certificate_with_password.pem"
echo "ECC Private Key (with password): $CERTS_DIR/ecc_private_key_with_password.pem"
echo "ECC Certificate (without password): $CERTS_DIR/ecc_certificate_without_password.pem"
echo "ECC Private Key (without password): $CERTS_DIR/ecc_private_key_without_password.pem"

ssl_password_file 来指定该文件:

user  root;
worker_processes  1;

error_log  logs/error.log  debug;

#pid        logs/nginx.pid;

events {
    worker_connections  1024;
}

http {
    #large_client_header_buffers 4 16k;

    # Global settings for SSL password file
    ssl_password_file /etc/nginx/ssl/password_file.txt;

    # Default SSL settings for all server blocks
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    # Server block for example1.com using RSA certificate with password
    server {
        listen 443 ssl;
        server_name example1.com;

        ssl_certificate /etc/nginx/ssl/rsa_certificate_with_password.pem;
        ssl_certificate_key /etc/nginx/ssl/rsa_private_key_with_password.pem;

        # Other SSL-related settings
        #ssl_password_file /etc/nginx/ssl/rsa_password.txt;

        location / {
            # Custom location settings for example1.com
            #root /var/www/html;
            #index index.html;
            return 200 "This is the response for example3.com\n";
        }
    }

    # Server block for example2.com using RSA certificate without password
    server {
        listen 443 ssl;
        server_name example2.com;

        ssl_certificate /etc/nginx/ssl/rsa_certificate_without_password.pem;
        ssl_certificate_key /etc/nginx/ssl/rsa_private_key_without_password.pem;

        # Other SSL-related settings
        #ssl_password_file /etc/nginx/ssl/rsa_password_without.txt;

        location / {
            # Custom location settings for example2.com
            #root /var/www/html;
            #index index.html;
            return 200 "This is the response for example3.com\n";
        }
    }

    # Server block for example3.com using ECC certificate with password
    server {
        listen 443 ssl;
        server_name example3.com;

        ssl_certificate /etc/nginx/ssl/ecc_certificate_with_password.pem;
        ssl_certificate_key /etc/nginx/ssl/ecc_private_key_with_password.pem;

        # Other SSL-related settings
        #ssl_password_file /etc/nginx/ssl/ecc_password.txt;

        location / {
            # Custom location settings for example3.com
            #root /var/www/html;
            #index index.html;
            return 200 "This is the response for example3.com\n";
        }
    }

    # Server block for example4.com using ECC certificate without password
    server {
        listen 443 ssl;
        server_name example4.com;

        ssl_certificate /etc/nginx/ssl/ecc_certificate_without_password.pem;
        ssl_certificate_key /etc/nginx/ssl/ecc_private_key_without_password.pem;

        # Other SSL-related settings
        #ssl_password_file /etc/nginx/ssl/ecc_password_without.txt;

        location / {
            # Custom location settings for example4.com
            #root /var/www/html;
            #index index.html;
            return 200 "This is the response for example4.com\n";
        }
    }
}

在这个配置中,NGINX 会在启动时自动读取 /etc/nginx/ssl_password.txt 文件,提取其中的密码,并使用它解锁 server.key 文件,以便加载和使用 SSL 私钥。

验证一下

curl --resolve example1.com:443:127.0.0.1 -H "Host: example1.com" https://example1.com -k -v
curl --resolve example2.com:443:127.0.0.1 -H "Host: example2.com" https://example2.com -k -v
curl --resolve example3.com:443:127.0.0.1 -H "Host: example3.com" https://example3.com -k -v
curl --resolve example4.com:443:127.0.0.1 -H "Host: example4.com" https://example4.com -k -v

以其中一个为例可以看到具体使用的证书信息

[root@iZ8vbd88lmglnbsnad85q3Z nginx-quic]# curl --resolve example1.com:443:127.0.0.1 -H "Host: example1.com" https://example1.com -k -v
* Added example1.com:443:127.0.0.1 to DNS cache
* Hostname example1.com was found in DNS cache
*   Trying 127.0.0.1:443...
* Connected to example1.com (127.0.0.1) port 443
* ALPN: curl offers http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / X25519 / RSASSA-PSS
* ALPN: server accepted http/1.1
* Server certificate:
*  subject: C=CN; ST=BEIJING; L=BEIJING; O=test; OU=test-unit; CN=example1.com; emailAddress=email@example.com
*  start date: Dec  1 12:54:50 2024 GMT
*  expire date: Dec  1 12:54:50 2025 GMT
*  issuer: C=CN; ST=BEIJING; L=BEIJING; O=test; OU=test-unit; CN=example1.com; emailAddress=email@example.com
*  SSL certificate verify result: self-signed certificate (18), continuing anyway.
*   Certificate level 0: Public key type RSA (2048/112 Bits/secBits), signed using sha256WithRSAEncryption
* using HTTP/1.x
> GET / HTTP/1.1
> Host: example1.com
> User-Agent: curl/8.6.0
> Accept: */*
>
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* old SSL session ID is stale, removing
< HTTP/1.1 200 OK
< Server: nginx/1.25.4
< Date: Tue, 03 Dec 2024 13:53:19 GMT
< Content-Type: text/plain
< Content-Length: 38
< Connection: keep-alive
<
This is the response for example3.com
* Connection #0 to host example1.com left intact

安全性注意事项

  • 确保 ssl_password_file 文件的权限设置非常严格,因为它包含私钥的密码。如果设置启动用户为 nginx, 可以设置用户限制只有 nginx 用户才能读取该文件。例如,你可以使用以下命令来限制文件权限:
    chmod 600 /etc/nginx/ssl_password.txt
    chown nginx:nginx /etc/nginx/ssl_password.txt
    

小结

ssl_password_file 是加载加密私钥的有效工具,可提高自动化部署效率。
-确保密码文件权限设置正确,以免泄露私钥密码。
-在部署中建议结合密码管理工具,进一步提升安全性。

Comments are closed.