OpenWebUI中,语音输入需要HTTPS才能使用麦克风等硬件资源,在局域网中通过NGINX转发实现HTTPS访问。
基于docker-compose
,执行docker-compose up -d
即可。
其中主要的就是映射conf文件夹和ssl的路径,不映射手动添加也一样。open-webui的环境变量就看个人需求了。
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:
这网上教程很多,基本一个命令就搞定,这里找GPT写了一个基于Flask的页面,方便访问(问就是因为懒得敲命令 )。具体代码放最后,有需要的可以复制。生成的文件放到上面配置的ssl文件夹里。
在conf.d
文件夹下新建default.conf
,写入内容如下,修改server_name
以及ssl
相关文件。
由于配置了network,localtion中访问地址可以直接用docker-compose中的app名字。
重启nginx容器即可访问。
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;
}
}
pip install cryptography
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('''
SSL Certificate Generator
SSL Certificate Generator
Generated Files
Certificate: No certificate generated
Private Key: No key generated
''')
# 生成证书接口
@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/" , 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)