You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

EC2实例LND节点SSL证书主机名不匹配问题求助

问题:LND节点SSL证书主机名不匹配导致Django API访问失败

错误信息

HTTPSConnectionPool(host='ec2-aa-aaa-aaa-aa.eu-central-1.compute.amazonaws.com', port=8080): Max retries exceeded with url: /v1/getinfo (Caused by SSLError(CertificateError("hostname 'ec2-aa-aaa-aa-aa.eu-central-1.compute.amazonaws.com' doesn't match either of 'ip-bbb-bb-bb-bb', 'localhost', 'unix'...))

其中ec2-aa-aaa-aaa-aa.eu-central-1.compute.amazonaws.com是EC2公网域名,ip-bbb-bb-bb-bb是EC2私有IP。

原因分析

LND默认生成的TLS证书仅包含私有IP、localhost、unix作为SAN(Subject Alternative Names),当你用公网域名/公网IP访问LND的REST接口时,SSL验证会检查主机名是否在证书的SAN列表中,不匹配就会抛出这个错误。

解决方案

1. 重新生成包含公网IP/域名的LND证书(推荐生产环境使用)

  • 停止LND服务:
    sudo systemctl stop lnd
    
  • 删除旧的TLS证书文件:
    rm ~/.lnd/tls.cert ~/.lnd/tls.key
    
  • 编辑LND配置文件~/.lnd/lnd.conf,添加以下配置项:
    tlsextraip=<你的EC2公网IP>
    tlsextradomain=<你的EC2公网域名>
    
  • 重启LND服务,它会自动生成包含新SAN的证书:
    sudo systemctl start lnd
    
  • 重新通过SFTP下载新的tls.cert到你的Django项目目录,替换旧证书后再测试API访问。

2. 临时跳过SSL验证(仅用于测试,生产环境禁用)

如果只是临时测试,可以在Django的请求代码中关闭SSL验证(以requests库为例):

import requests

response = requests.get(
    "https://ec2-aa-aaa-aaa-aa.eu-central-1.compute.amazonaws.com:8080/v1/getinfo",
    headers={"Grpc-Metadata-macaroon": "<你的macaroon编码>"},
    verify=False  # 跳过验证,生产环境不要用
)

⚠️ 注意:这种方法会暴露中间人攻击风险,绝对不能在生产环境使用。

3. 配置Nginx反向代理(适合生产环境,使用可信证书)

在EC2实例上安装Nginx,申请Let's Encrypt证书(匹配公网域名),然后配置反向代理到LND的8080端口:

  • 安装Nginx和Certbot:
    sudo apt update && sudo apt install nginx certbot python3-certbot-nginx
    
  • 申请证书:
    sudo certbot --nginx -d ec2-aa-aaa-aaa-aa.eu-central-1.compute.amazonaws.com
    
  • 修改Nginx配置文件(比如/etc/nginx/sites-available/lnd):
    server {
        listen 443 ssl;
        server_name ec2-aa-aaa-aaa-aa.eu-central-1.compute.amazonaws.com;
    
        ssl_certificate /etc/letsencrypt/live/ec2-aa-aaa-aaa-aa.eu-central-1.compute.amazonaws.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/ec2-aa-aaa-aaa-aa.eu-central-1.compute.amazonaws.com/privkey.pem;
    
        location / {
            proxy_pass http://localhost:8080;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            # 传递macaroon header,或者在Django请求中携带
        }
    }
    
  • 启用配置并重启Nginx:
    sudo ln -s /etc/nginx/sites-available/lnd /etc/nginx/sites-enabled/
    sudo systemctl restart nginx
    

之后Django访问Nginx的443端口即可,证书是可信且匹配公网域名的。

4. 修改本地hosts文件(仅本地测试用)

在本地机器的hosts文件中添加映射,把公网域名指向EC2私有IP:

ip-bbb-bb-bb-bb    ec2-aa-aaa-aaa-aa.eu-central-1.compute.amazonaws.com

这样访问公网域名时会解析到私有IP,匹配证书中的SAN,但仅适合本地测试,其他机器无法使用。


你提供的EC2安全组规则

–   sgr-0b6e4646f963ac348   All         All     0.0.0.0/0   
–   sgr-0c9433a6bdbc5c986   8333        TCP     ::/0    
–   sgr-0ab3df01e90304321   8333        TCP     0.0.0.0/0       
–   sgr-002bc487360643667   443         TCP     0.0.0.0/0   
–   sgr-08bad3cd41c9d3365   80          TCP     0.0.0.0/0   
–   sgr-005fa2007f73f3466   28333       TCP     0.0.0.0/0   
–   sgr-0fba32539580d3071   0 - 65535   TCP     0.0.0.0/0
–   sgr-092c6691e0dbdf9d3   All         ICMP    0.0.0.0/0
–   sgr-000bdb5bee77e7314   8000        TCP     0.0.0.0/0   
–   sgr-0112ef3fdd16c2b75   22  TCP 0.0.0.0/0

你使用的SSH连接代码

import boto3
import paramiko
import os
import base64, codecs

# Define the instance's ID and the key pair name
instance_id = "<i-blahblahblah>"
key_name = "<some_keyname>"

# Create a session using your AWS credentials
session = boto3.Session(
    aws_access_key_id="<some_access_key_is>",
    aws_secret_access_key="<some_aws_secret_access_key>",
    region_name="eu-central-1",
)

# Use the session to create an EC2 client
ec2 = session.client("ec2")

# Retrieve the IP address of the instance
instance = ec2.describe_instances(InstanceIds=[instance_id])["Reservations"][0][
    "Instances"
][0]
ip_address = instance["PublicIpAddress"]
print(ip_address)
# Use the session to create a SSM client
ssm = session.client("ssm")

# Start a session to the instance using SSM
response = ssm.start_session(Target=instance_id)

# Use paramiko to SSH into the instance
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
key_name = "<path_to_key>"
ssh.connect(ip_address, username="ubuntu", key_filename=f"{key_name}.pem")

# Execute the command to retrieve the environment variable
stdin, stdout, stderr = ssh.exec_command("echo $VARNAME")
my_variable = stdout.read().strip().decode()
# Read the output of the command
sftp_client = ssh.open_sftp()
remote_path = "/home/ubuntu/.lnd/data/chain/bitcoin/mainnet/admin.macaroon"
remote_file = sftp_client.open(remote_path, "r")

# Read contents of file
file_contents = remote_file.read()
encoded_hex = codecs.encode(file_contents, "hex")
encoded_string = encoded_hex.decode("utf-8")


# Now you can use the decoded string in your API
print(encoded_string)

# Get the path to the certificate file on the EC2 instance
remote_cert_file = "/home/ubuntu/.lnd/tls.cert"
# Transfer the certificate file from the EC2 instance to the local machine
local_cert_file = "tls.cert"
sftp = ssh.open_sftp()
sftp.get(remote_cert_file, local_cert_file)

# Specify the path to the local certificate file for SSL verification
cert_file_path = os.path.abspath(local_cert_file)
print(cert_file_path)
sftp.close()
# Close file and SFTP client
remote_file.close()

stdin.close()
# Close the SSH connection and SSM session
ssh.close()
ssm.terminate_session(SessionId=response["SessionId"])
response = {"file_contents": file_contents}

内容的提问来源于stack exchange,提问作者Pau1aAm7

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 01:42:23