【LLM】局域网内为容器服务启用HTTPS

OpenWebUI中,语音输入需要HTTPS才能使用麦克风等硬件资源,在局域网中通过NGINX转发实现HTTPS访问。

具体包含三个部分

  • 容器部署open-webui和nginx
  • 生成ssl证书
  • 修改nginx配置文件

1、容器部署

基于docker-compose,执行docker-compose up -d即可。

其中主要的就是映射conf文件夹和ssl的路径,不映射手动添加也一样。open-webui的环境变量就看个人需求了。

yaml 复制代码
services:
  open-webui:
    image: ghcr.io/open-webui/open-webui:main
    container_name: open-webui
    restart: always
    networks:
    - openwebui
  
  nginx:
    image: nginx
    container_name: nginx
    restart: always
    volumes:
    - /nginx/conf.d:/etc/nginx/conf.d
    - /nginx/ssl:/ssl
    ports:
    - "38080:8080"
    networks:
    - openwebui

# 为空即可
networks:
  openwebui:

2、生成ssl证书

这网上教程很多,基本一个命令就搞定,这里找GPT写了一个基于Flask的页面,方便访问(问就是因为懒得敲命令 )。具体代码放最后,有需要的可以复制。生成的文件放到上面配置的ssl文件夹里。

3.修改conf

conf.d文件夹下新建default.conf,写入内容如下,修改server_name以及ssl相关文件。

由于配置了network,localtion中访问地址可以直接用docker-compose中的app名字。

重启nginx容器即可访问。

shell 复制代码
server {
	listen 8080 ssl;
	server_name yourdomain.com;
	# 配置crt
	ssl_certificate "/ssl/xxx.crt";
	# 配置key
	ssl_certificate_key "/ssl/xxx.key";
	
	location /{
		proxy_pass http://open-webui:8080;
		proxy_set_header X-Real-IP $remote_addr;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header Host $http_host;
		proxy_set_header X-NginX-Proxy true;
		proxy_redirect default;
	}
}

附.生成ssl证书代码

pip install cryptography

python 复制代码
import os
from flask import Flask, jsonify, send_from_directory, request, render_template_string
from cryptography import x509
from cryptography.x509.oid import NameOID
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend
from datetime import datetime, timedelta

app = Flask(__name__)

# SSL 证书文件夹
SSL_FOLDER = "ssl"
if not os.path.exists(SSL_FOLDER):
    os.makedirs(SSL_FOLDER)


# 生成 SSL 证书
def generate_ssl_cert(domain_name):
    key = rsa.generate_private_key(
        public_exponent=65537,
        key_size=2048,
        backend=default_backend()
    )

    subject = issuer = x509.Name([
        x509.NameAttribute(NameOID.COUNTRY_NAME, u"US"),
        x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u"California"),
        x509.NameAttribute(NameOID.LOCALITY_NAME, u"San Francisco"),
        x509.NameAttribute(NameOID.ORGANIZATION_NAME, u"My Organization"),
        x509.NameAttribute(NameOID.COMMON_NAME, domain_name),
    ])

    cert = x509.CertificateBuilder().subject_name(
        subject
    ).issuer_name(
        issuer
    ).public_key(
        key.public_key()
    ).serial_number(
        x509.random_serial_number()
    ).not_valid_before(
        datetime.utcnow()
    ).not_valid_after(
        datetime.utcnow() + timedelta(days=365)
    ).add_extension(
        x509.SubjectAlternativeName([x509.DNSName(domain_name)]),
        critical=False,
    ).sign(key, hashes.SHA256(), default_backend())

    cert_filename = f"{domain_name}.crt"
    key_filename = f"{domain_name}.key"

    cert_path = os.path.join(SSL_FOLDER, cert_filename)
    key_path = os.path.join(SSL_FOLDER, key_filename)

    with open(key_path, "wb") as f:
        f.write(key.private_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PrivateFormat.TraditionalOpenSSL,
            encryption_algorithm=serialization.NoEncryption()
        ))

    with open(cert_path, "wb") as f:
        f.write(cert.public_bytes(serialization.Encoding.PEM))

    return cert_filename, key_filename


# 首页接口,显示 HTML 页面
@app.route("/", methods=["GET"])
def index():
    return render_template_string('''
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>SSL Certificate Generator</title>
    <script>
        async function generateCert() {
            const domainName = document.getElementById('domain_name').value;
            const response = await fetch('/generate_cert', {
                method: 'POST',
                headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
                body: new URLSearchParams({ domain_name: domainName })
            });

            if (response.ok) {
                const result = await response.json();
                document.getElementById('cert_link').href = `/download_cert/${result.certificate}`;
                document.getElementById('cert_link').innerText = result.certificate;
                document.getElementById('key_link').href = `/download_cert/${result.key}`;
                document.getElementById('key_link').innerText = result.key;
            } else {
                alert('Error generating certificate');
            }
        }
    </script>
</head>
<body>
    <h1>SSL Certificate Generator</h1>
    <form οnsubmit="event.preventDefault(); generateCert();">
        <label for="domain_name">Domain Name:</label>
        <input type="text" id="domain_name" name="domain_name" required>
        <button type="submit">Generate Certificate</button>
    </form>

    <h2>Generated Files</h2>
    <p>Certificate: <a id="cert_link" href="#" target="_blank">No certificate generated</a></p>
    <p>Private Key: <a id="key_link" href="#" target="_blank">No key generated</a></p>
</body>
</html>
    ''')


# 生成证书接口
@app.route("/generate_cert", methods=["POST"])
def generate_cert():
    domain_name = request.form.get("domain_name")
    if not domain_name:
        return jsonify({"error": "Domain name is required"}), 400

    cert_filename, key_filename = generate_ssl_cert(domain_name)
    return jsonify({"certificate": cert_filename, "key": key_filename})


# 下载证书接口
@app.route("/download_cert/<filename>", methods=["GET"])
def download_cert(filename):
    return send_from_directory(SSL_FOLDER, filename)


if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000, debug=True)
相关推荐
镰刀韭菜13 小时前
【AI4S】大语言模型与化学的未来,以及整合外部工具和聊天机器人的潜力
llm·transformer·大语言模型·药物设计·分子发现·chemchat·smiles
数据智能老司机17 小时前
建构 AI Agent 应用——工具调用
架构·llm·agent
aopstudio20 小时前
llms.txt:为大模型打造的“网站说明书”
人工智能·python·llm·开发者工具
AI大模型API向量引擎2 天前
开发者必看:Luma Video API 对接教程 + 电影级视频生成技巧,NeRF 技术落地实践
llm
AndrewHZ2 天前
【3D图像技术讨论】3A游戏场景重建实战指南:从数据采集到实时渲染的开源方案
人工智能·算法·游戏·3d·开源·llm·colmap
马诗剑2 天前
Qwen3-8B 在华为昇腾平台上的保姆级微调与推理教程 (基于AutoDL)
llm
攻城狮7号3 天前
Kimi开源轻量级中间件checkpoint-engine:能20秒内更新万亿参数模型?
人工智能·llm·kimi·moonshotai·checkpoint引擎·开源中间件
支付宝体验科技3 天前
支付宝开源 UI-UG 大模型开源!既能看懂 UI 又能生成 UI?
llm
大千AI助手4 天前
MATH-500:大模型数学推理能力评估基准
人工智能·大模型·llm·强化学习·评估基准·数学推理能力·math500
麦索4 天前
大模型提示词工程是什么?
llm