当前位置: 首页 > news >正文

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

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

具体包含三个部分

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

1、容器部署

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

services:open-webui:image: ghcr.io/open-webui/open-webui:maincontainer_name: open-webuirestart: alwaysnetworks:- openwebuinginx:image: nginxcontainer_name: nginxrestart: alwaysvolumes:- /nginx/conf.d:/etc/nginx/conf.d- /nginx/ssl:/sslports:- "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容器即可访问。

server {listen 8080 ssl;server_name yourdomain.com;# 配置crtssl_certificate "/ssl/xxx.crt";# 配置keyssl_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

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, timedeltaapp = 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"}), 400cert_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)

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Spring中Bean的相关注解
  • ROM修改进阶教程------如何修改固件 线刷转卡刷 卡刷转线刷 操作中的一些注意事项
  • C++20中头文件bit的使用
  • k8s的环境配置
  • 【Linux】【Vim】Vim 基础
  • Python 数据分析— Pandas 基本操作(下)
  • 使用QT编写有图形界面的TCP局域网聊天室(app)
  • Excel怎么截图?快速捕捉工作表的多种方法
  • 17. 什么是MyBatis中的TypeHandler?如何自定义TypeHandler处理复杂类型?
  • CentOS 7 安装yum使用报错:Cannot find a valid baseurl for repo: base/7/x86_6
  • 使用Python中的`zip()`函数
  • WPF的**逻辑树**和**可视树**。
  • ARM 工业计算机搭载 FUXA 组态软件:开启智能制造新时代
  • STL-stack/queue/deque(容器适配器)
  • 直播相关03-录制麦克风声音, ffmpeg 命名,使用命令行完成录音
  • 《网管员必读——网络组建》(第2版)电子课件下载
  • 4月23日世界读书日 网络营销论坛推荐《正在爆发的营销革命》
  • classpath对获取配置文件的影响
  • EventListener原理
  • in typeof instanceof ===这些运算符有什么作用
  • Kibana配置logstash,报表一体化
  • LintCode 31. partitionArray 数组划分
  • Linux中的硬链接与软链接
  • Object.assign方法不能实现深复制
  • SpingCloudBus整合RabbitMQ
  • Spring-boot 启动时碰到的错误
  • Tornado学习笔记(1)
  • ⭐ Unity 开发bug —— 打包后shader失效或者bug (我这里用Shader做两张图片的合并发现了问题)
  • WebSocket使用
  • 汉诺塔算法
  • 腾讯视频格式如何转换成mp4 将下载的qlv文件转换成mp4的方法
  • 通过获取异步加载JS文件进度实现一个canvas环形loading图
  • 小李飞刀:SQL题目刷起来!
  • 一、python与pycharm的安装
  • 看到一个关于网页设计的文章分享过来!大家看看!
  • ​ 轻量应用服务器:亚马逊云科技打造全球领先的云计算解决方案
  • ​zookeeper集群配置与启动
  • ​学习一下,什么是预包装食品?​
  • ‌‌雅诗兰黛、‌‌兰蔻等美妆大品牌的营销策略是什么?
  • #stm32驱动外设模块总结w5500模块
  • #如何使用 Qt 5.6 在 Android 上启用 NFC
  • $emit传递多个参数_PPC和MIPS指令集下二进制代码中函数参数个数的识别方法
  • %check_box% in rails :coditions={:has_many , :through}
  • (C语言)逆序输出字符串
  • (pytorch进阶之路)扩散概率模型
  • (附源码)ssm高校社团管理系统 毕业设计 234162
  • (含react-draggable库以及相关BUG如何解决)固定在左上方某盒子内(如按钮)添加可拖动功能,使用react hook语法实现
  • (四) 虚拟摄像头vivi体验
  • (原創) X61用戶,小心你的上蓋!! (NB) (ThinkPad) (X61)
  • (转)甲方乙方——赵民谈找工作
  • .a文件和.so文件
  • .NET 5种线程安全集合
  • .net core控制台应用程序初识
  • .Net 访问电子邮箱-LumiSoft.Net,好用
  • .NET 线程 Thread 进程 Process、线程池 pool、Invoke、begininvoke、异步回调