MoreRSS

site iconLala | 荒岛修改

一个应用分享、教程类的博客,主要是那些需要自部署的。
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

Lala | 荒岛的 RSS 预览

Docker部署的Open WebUI连接非Docker部署的Ollama

2025-06-12 07:34:39

之前网上已经有很多文章提到过Ollama API未经授权访问的事情。。其实大概就是很多人把Ollama直接监听到0.0.0.0了。。我估计大多数人也不是错误配置,基本上可以说都只是为了将Ollama与其他的程序进行对接时才这样配置的。因为解决Ollama与其他程序对接问题最简单的办法就是监听0.0.0.0。

但是这就暴露出来一个安全问题,由于Ollama官方一直不愿意(#1053 #8536 #9131)搞个API鉴权的功能出来,这就导致如果把Ollama部署在有公网的机器上,监听0.0.0.0就等于是自杀。。

其实早在这些文章发布前我自己就经历过一次,当时是拿来测试用的,测试完了忘记改回来了,没过几天就被别人拿来白嫖了= =

然后我最近又部署了一个Ollama,没有用Docker,但是对接的Open WebUI是用Docker部署的,又遇到这个问题了,有过上次的教训这次就想彻底解决掉这问题。。

首先在Open WebUI的compose内肯定是要配置host.docker.internal:host-gateway的,这个功能其实就是在容器系统内的hosts文件里添加一行如下配置:

172.17.0.1 host.docker.internal

其中172.17.0.1是宿主机在Docker默认桥接网络中的网关地址。有了这个配置后,容器内部的应用只需请求http://host.docker.internal,就能访问宿主机的服务了。

但是如果宿主机的服务只监听127.0.0.1(环回接口),host.docker.internal能访问到宿主机的这个服务吗?很明显不行啊,因为容器系统内部的127.0.0.1是容器自己,不是宿主机。所以这个时候直接将服务监听到0.0.0.0,那host.docker.internal就能访问到了,但问题就如之前说的Ollama没鉴权,直接暴露在公网会有安全问题。

监听0.0.0.0对于有公网的环境来说不适用,那还有一个办法就是配置Docker容器使用主机网络(Host Network)但是我也不想用这种方案,我还是想用默认的桥接网络。我想到一个办法就是用Socat弄一个端口转发,把172.17.0.1:11434的流量转发到127.0.0.1:11434,可不可行?我也不知道,但是我试了一下确实能用。。。

这里记录下完整的部署过程。安装Docker和需要用到的软件包:

apt -y update
apt -y install socat curl nginx python3-certbot-nginx
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh

创建目录和compose文件:

mkdir -p /opt/openwebui && cd /opt/openwebui && nano docker-compose.yml

写入如下内容:

services:
  openwebui:
    image: ghcr.io/open-webui/open-webui:main
    container_name: openwebui
    extra_hosts:
      - "host.docker.internal:host-gateway"
    ports:
      - "50000:8080"
    volumes:
      - open-webui:/app/backend/data
volumes:
  open-webui:

启动Open WebUI:

docker compose up -d

配置NGINX反向代理:

nano /etc/nginx/sites-available/openwebui

写入如下内容:

server {
    listen 80;
    server_name openwebui.example.com;

    location / {
        proxy_pass http://127.0.0.1:50000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_buffering off;
        client_max_body_size 0;
        proxy_read_timeout 10m;
    }
}

启用站点:

ln -s /etc/nginx/sites-available/openwebui /etc/nginx/sites-enabled/openwebui

签发SSL证书:

certbot --nginx

访问Open WebUI创建管理员账号:

接下来安装Ollama:

curl -L https://ollama.com/download/ollama-linux-amd64.tgz -o ollama-linux-amd64.tgz
tar -C /usr -xzf ollama-linux-amd64.tgz

添加一个用户,后续用这个用户来运行Ollama:

useradd -r -s /bin/false -U -m -d /usr/share/ollama ollama
usermod -a -G ollama $(whoami)

新建systemd配置文件:

nano /etc/systemd/system/ollama.service

写入如下内容:

[Unit]
Description=Ollama Service
After=network-online.target

[Service]
ExecStart=/usr/bin/ollama serve
User=ollama
Group=ollama
Restart=always
RestartSec=3
Environment="PATH=$PATH"

[Install]
WantedBy=multi-user.target

启动并设置Ollama开机自启:

systemctl enable --now ollama

最后用Socat配置一个端口转发,新建systemd配置文件:

nano /etc/systemd/system/socat-ollama.service

写入如下内容:

[Unit]
Description=Socat port forward 11434 on 172.17.0.1 to 127.0.0.1:11434
After=network.target

[Service]
Type=simple
ExecStart=/usr/bin/socat TCP4-LISTEN:11434,bind=172.17.0.1,fork,reuseaddr TCP4:127.0.0.1:11434
Restart=always
RestartSec=2
LimitNOFILE=4096

[Install]
WantedBy=multi-user.target

启动并设置开机自启:

systemctl enable --now socat-ollama

Open WebUI的配置:

测试:

总结:

方案1:Ollama直接监听0.0.0.0,不适用公网环境。
方案2:配置Docker容器使用主机网络,比较简单的解决方案,但降低了Docker网络的隔离性。
方案3:配置一个端口转发,既可以用在公网环境,也不需要使用主机网络,保障了Docker网络的隔离性。
方案4(未实践):搭建One API,然后用One API中转。
方案5(未实践):不确定这种能不能与Open WebUI兼容,https://github.com/ParisNeo/ollama_proxy_server

IntelliSSH:一个Web SSH客户端,支持AI上下文感知

2025-06-11 09:50:33

IntelliSSH介绍(摘自项目页面)

A secure and user-friendly web app for managing Linux servers with Artifical Intelligence via SSH—right from your browser + SFTP Browser in Terminal.

安装Docker和需要用到的软件包:

apt -y update
apt -y install git curl nginx python3-certbot-nginx
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh

创建目录和compose文件:

mkdir -p /opt/intellissh && cd /opt/intellissh && nano docker-compose.yml

写入如下内容:

services:
  intellissh:
    image: clusterzx/intellissh:latest
    container_name: intellissh
    restart: unless-stopped
    ports:
      - 65000:3000
    volumes:
      - ./data:/app/server/data

启动:

docker compose up -d

查看初始的默认管理员账号密码:

docker compose logs

程序的复制粘贴功能需要SSL的支持才能用,所以我们现在配置NGINX反向代理:

nano /etc/nginx/sites-available/intellissh

写入如下内容:

server {
    listen 80;
    server_name intellissh.example.com;
    client_max_body_size 0;
    location / {
        proxy_pass http://127.0.0.1:65000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        # WebSocket support
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

启用站点:

ln -s /etc/nginx/sites-available/intellissh /etc/nginx/sites-enabled/intellissh

签发SSL证书:

certbot --nginx

登录之后修改CORS的域名(intellissh.example.com),并关闭用户注册功能(如果你不想公开给别人用的话)

AI支持OPENAI/OLLAMA,以及兼容OPENAI的API:

简单试了下AI上下文感知:

该有的功能基本都有,目前使用下来感觉还可以,就是复制粘贴的功能有点问题,按钮有时会跑出边界导致按不到= =好像作者已经在修复了。。

no matching host key type found. Their offer: ssh-rsa

2025-06-04 10:36:04

昨天发现又有一台甲骨文ARM引导丢了,然后按照之前的这篇文章创建控制台连接后一直连不上,报错:

Unable to negotiate with UNKNOWN port 65535: no matching host key type found. Their offer: ssh-rsa

应该是SSH版本的问题,解决办法,新建一个SSH配置文件:

nano /etc/ssh/ssh_config.d/fix-oracle-console.conf

写入如下内容:

HostKeyAlgorithms = +ssh-rsa
PubkeyAcceptedAlgorithms = +ssh-rsa

重载SSH服务:

systemctl reload sshd

尝鲜Caddy配置ECH

2025-05-27 21:10:52

前几天看到Caddy支持ECH了,然后部署了一个逝了下,坑有点多,遂记录下整个部署过程。。。

配置过程中的问题:

我一开始看到这个版本发布说明里面写了Caddy实现了全自动的ECH,但是需要一个DNS插件,一般都是用CloudFlare。

默认情况下Caddy是不含这些DNS插件的,需要我们自己用xcaddy编译,但是我当时偷了个懒不想自己编译(嫌麻烦),因为Caddy官网有一个下载页面,可以根据你选择的插件在线帮你编译一个Caddy,我当时就用的这个方式:

后续在配置过程中就发现,如果域名本身存在SRV记录的话,ECH的DNS解析记录配置就会失败:#6975

这问题是上游libdns导致的,目前要彻底解决的话最终还是得自己用xcaddy编译- -因为我当时不想编译就临时换了个没有SRV记录的域名。

使用过程中的问题:

等到我全部配置好了,发现一个奇怪的问题:火狐浏览器正常,谷歌浏览器报TLS错误。。

整他妈半天发现这是因为Go标准库里面的BUG导致的:#6898#6968#71642

最终的最终,还是得自己用xcaddy编译一遍,你说这是偷的个什么懒。。

题外话:如果你不急的话,可以再等一等。不出意外的话,下个版本的Caddy应该就没这些问题了。

我还是倾向于使用官方的APT Repo来安装Caddy,因为这样安装后就不用自己去写systemd服务了,后续版本管理也容易:

apt update
apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list
apt update
apt install caddy

还需要安装xcaddy:

apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/xcaddy/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-xcaddy-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/xcaddy/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-xcaddy.list
apt update
apt install xcaddy

以及Golang:

wget https://go.dev/dl/go1.24.3.linux-amd64.tar.gz
tar -C /usr/local -xzf go1.24.3.linux-amd64.tar.gz
echo 'export PATH=$PATH:/usr/local/go/bin' > /etc/profile.d/golang.sh
source /etc/profile.d/golang.sh

编译Caddy:

xcaddy build \
--with github.com/caddy-dns/cloudflare \
--with github.com/libdns/libdns@master

将编译好的二进制文件移动并重命名为caddy.custom:

mv ./caddy /usr/bin/caddy.custom

依次执行下面的命令,将自编译的版本替换掉APT Repo里面的版本:

dpkg-divert --divert /usr/bin/caddy.default --rename /usr/bin/caddy
update-alternatives --install /usr/bin/caddy caddy /usr/bin/caddy.default 10
update-alternatives --install /usr/bin/caddy caddy /usr/bin/caddy.custom 50
systemctl restart caddy

执行下面的命令检查CloudFlare DNS插件是否启用:

caddy list-modules

正常的话会有类似显示:

dns.providers.cloudflare
  Non-standard modules: 1

现在可以开始配置ECH了,编辑Caddy的配置文件:

nano /etc/caddy/Caddyfile

写入如下内容:

{
  dns cloudflare {env.CLOUDFLARE_API_KEY}
  ech ech.example.net
}

ech-test.example.com {
  respond "ECH Test!"
}

doh.example.com {
  log {
    output file /var/log/caddy/doh.example.com.access.log
    format console
  }
  reverse_proxy /dns-query* https://dns.google {
    header_up Host dns.google
    header_up X-Real-IP {remote_host}
    header_up X-Forwarded-For {remote_host}
    header_up X-Forwarded-Proto {scheme}
  }
}

注:

这里额外配置了一个DoH服务,因为ECH不但要服务端支持,客户端也需要支持才行,目前主流的浏览器:火狐、谷歌都是需要配置一个安全DNS服务器才会启用ECH支持,又因为国内现在把很多DoH服务器都屏蔽了,所以这里简单的用反向代理临时弄了个DoH服务。这不是必须的,如果你可以使用其他DoH服务的话可以省略这段配置。

将{env.CLOUDFLARE_API_KEY}修改为你的CloudFlare API KEY。

ech.example.net、ech-test.example.com两个域名均添加DNS A记录指向这台Caddy所在的服务器IP。

其实可以用“外部”、“内部”来简单解释一下。ech.example.net=外部,ech-test.example.com=内部。

ech.example.net是外部看到的SNI,ech-test.example.com是实际要访问的SNI。

配置完成后重载Caddy使新的配置生效:

systemctl reload caddy

如果正常的话,应该能在CloudFlare的DNS记录界面看到一条新的https记录(我这里是两条记录,因为我额外配置的DoH服务在同一个域名):

到这里服务端的配置就全部完成了。接下来是客户端的配置。客户端这边只需要修改浏览器配置让其使用DoH服务即可。

火狐:

谷歌:

谷歌浏览器一定要勾选“一律使用安全连接”,不然会出现有时支持ECH有时候又不支持的问题。。

配置完成后可以访问:https://tls-ech.dev/,进行检查。

最后用Wireshark抓包看看配置是否正常,在Wireshark内新建一个显示过滤器:

tls.handshake.extensions_server_name

当我用浏览器访问ech-test.example.com时,SNI应该是ech.example.net:

推荐个开源的SSH客户端:Termora

2025-05-27 18:09:11

Termora是一个终端模拟器和SSH客户端,支持Windows,macOS和Linux。

介绍:https://github.com/TermoraDev/termora/blob/main/README.zh_CN.md

下载:https://github.com/TermoraDev/termora/releases

下了个便携版一用就喜欢上了,真的非常好用,多的我也不说了,有需要的可以试一下。

Pangolin:开源Zero Trust Tunnel

2025-05-19 11:54:30

Pangolin介绍(摘自官方项目页面)

Pangolin is a self-hosted tunneled reverse proxy server with identity and access control, designed to securely expose private resources on distributed networks. Acting as a central hub, it connects isolated networks — even those behind restrictive firewalls — through encrypted tunnels, enabling easy access to remote services without opening ports.

如果你用过CloudFlare的Zero Trust Tunnel,那就可以简单理解为Pangolin是一个开源替代品。主要作用就是内网穿透,只不过加上了很多实用的功能,例如:身份验证、访问控制、自动HTTPS等。Pangolin的核心还是WireGuard,对,又是WireGuard=。=

虽然Pangolin用了WireGuard,但是一定不要将Pangolin和一众基于WireGuard的VPN混为一谈,Pangolin的目标不是组网,而是暴露内网的服务,并且通过反向代理来提供更多的周边功能。

下面记录下Pangolin部署的步骤。开始前需要做一些准备工作。

1、一个域名做好通配符解析。例如:*.example.com

2、一台小VPS,内存1GB就足够了。系统选择Debian12。如果你肉身在中国,最好选择亚太地区(境外)的VPS,这样可以省去很多不必要的麻烦,例如:域名备案、Docker代理配置等。

3、系统的TCP 80/TCP 443/UDP 51820端口不能被占用。

安装Docker:

apt -y update
apt -y install git curl
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh

创建目录和compose文件:

mkdir -p /opt/pangolin && cd /opt/pangolin && nano docker-compose.yml

写入如下内容:

services:
  pangolin:
    image: fosrl/pangolin:1.3.2
    container_name: pangolin
    restart: unless-stopped
    volumes:
      - ./config:/app/config
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3001/api/v1/"]
      interval: "3s"
      timeout: "3s"
      retries: 15

  gerbil:
    image: fosrl/gerbil:1.0.0
    container_name: gerbil
    restart: unless-stopped
    depends_on:
      pangolin:
        condition: service_healthy
    command:
      - --reachableAt=http://gerbil:3003
      - --generateAndSaveKeyTo=/var/config/key
      - --remoteConfig=http://pangolin:3001/api/v1/gerbil/get-config
      - --reportBandwidthTo=http://pangolin:3001/api/v1/gerbil/receive-bandwidth
    volumes:
      - ./config/:/var/config
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    ports:
      - 51820:51820/udp
      - 443:443 # Port for traefik because of the network_mode
      - 80:80 # Port for traefik because of the network_mode

  traefik:
    image: traefik:v3.3.3
    container_name: traefik
    restart: unless-stopped
    network_mode: service:gerbil # Ports appear on the gerbil service
    depends_on:
      pangolin:
        condition: service_healthy
    command:
      - --configFile=/etc/traefik/traefik_config.yml
    volumes:
      - ./config/traefik:/etc/traefik:ro # Volume to store the Traefik configuration
      - ./config/letsencrypt:/letsencrypt # Volume to store the Let's Encrypt certificates

networks:
  default:
    driver: bridge
    name: pangolin

创建config目录并在目录内新建一个用于Pangolin的配置文件:

mkdir config && cd config && nano config.yml

写入如下配置,需要修改的位置写了注释:

app:
  dashboard_url: "https://pangolin.example.com" # 在这里配置你的域名,请使用子域名。
  log_level: "info"
  save_logs: true

domains:
  imlala-test:
    base_domain: "example.com" # 在这里配置你的域名,请使用根域名。
    cert_resolver: "letsencrypt"
    prefer_wildcard_cert: false

server:
  external_port: 3000
  internal_port: 3001
  next_port: 3002
  internal_hostname: "pangolin"
  session_cookie_name: "p_session_token"
  resource_access_token_param: "p_token"
  resource_access_token_headers:
    id: "P-Access-Token-Id"
    token: "P-Access-Token"
  resource_session_request_param: "p_session_request"
  secret: "RAQrAI4YVwvTJzDGu5icA530SRA+98WEFW0HgGyq0tI=" # 使用命令openssl rand -base64 32生成

traefik:
  cert_resolver: "letsencrypt"
  http_entrypoint: "web"
  https_entrypoint: "websecure"

gerbil:
  start_port: 51820
  base_endpoint: "gerbil.example.com" # 在这里配置你的域名,请使用子域名。
  use_subdomain: false
  block_size: 24
  site_block_size: 30
  subnet_group: 100.89.137.0/20

rate_limits:
  global:
    window_minutes: 1
    max_requests: 100

email:
  smtp_host: "mail.example.com"
  smtp_port: 587
  smtp_user: "smtp"
  smtp_pass: "password"
  no_reply: "[email protected]"

users:
  server_admin:
    email: "[email protected]" # 管理员账号
    password: "password" # 管理员密码

flags:
  require_email_verification: true
  disable_signup_without_invite: true
  disable_user_create_org: true
  allow_raw_resources: true
  allow_base_domain_resources: true

注意事项:

1、如果没有配置SMTP,可以将require_email_verification改为false。

2、disable_signup_without_invite我改成了true,这意味着关闭了用户注册的功能,仅限私人使用。

继续在config目录内创建一个名为traefik的目录并进入该目录:

mkdir traefik && cd traefik

新建traefik_config.yml配置文件:

nano traefik_config.yml

写入如下配置,需要修改的位置写了注释:

api:
  insecure: true
  dashboard: true

providers:
  http:
    endpoint: "http://pangolin:3001/api/v1/traefik-config"
    pollInterval: "5s"
  file:
    filename: "/etc/traefik/dynamic_config.yml"

experimental:
  plugins:
    badger:
      moduleName: "github.com/fosrl/badger"
      version: "v1.1.0"

log:
  level: "INFO"
  format: "common"

certificatesResolvers:
  letsencrypt:
    acme:
      httpChallenge:
        entryPoint: web
      email: [email protected] # 在这里配置你的邮箱地址
      storage: "/letsencrypt/acme.json"
      caServer: "https://acme-v02.api.letsencrypt.org/directory"

entryPoints:
  web:
    address: ":80"
  websecure:
    address: ":443"
    transport:
      respondingTimeouts:
        readTimeout: "30m"
    http:
      tls:
        certResolver: "letsencrypt"

serversTransport:
  insecureSkipVerify: true

新建dynamic_config.yml配置文件:

nano dynamic_config.yml

写入如下配置,需要修改的位置写了注释:

http:
  middlewares:
    redirect-to-https:
      redirectScheme:
        scheme: https

  routers:
    # HTTP to HTTPS redirect router
    main-app-router-redirect:
      rule: "Host(`pangolin.example.com`)" # 在这里配置你的域名,必须与config.yml内的dashboard_url保持一致
      service: next-service
      entryPoints:
        - web
      middlewares:
        - redirect-to-https

    # Next.js router (handles everything except API and WebSocket paths)
    next-router:
      rule: "Host(`pangolin.example.com`) && !PathPrefix(`/api/v1`)" # 在这里配置你的域名,必须与config.yml内的dashboard_url保持一致
      service: next-service
      entryPoints:
        - websecure
      tls:
        certResolver: letsencrypt

    # API router (handles /api/v1 paths)
    api-router:
      rule: "Host(`pangolin.example.com`) && PathPrefix(`/api/v1`)" # 在这里配置你的域名,必须与config.yml内的dashboard_url保持一致
      service: api-service
      entryPoints:
        - websecure
      tls:
        certResolver: letsencrypt

    # WebSocket router
    ws-router:
      rule: "Host(`pangolin.example.com`)" # 在这里配置你的域名,必须与config.yml内的dashboard_url保持一致
      service: api-service
      entryPoints:
        - websecure
      tls:
        certResolver: letsencrypt

  services:
    next-service:
      loadBalancer:
        servers:
          - url: "http://pangolin:3002" # Next.js server

    api-service:
      loadBalancer:
        servers:
          - url: "http://pangolin:3000" # API/WebSocket server

启动:

docker compose up -d

如果一切正常你将可以访问Pangolin的登录界面:

登录进去后需要创建一个组织,组织是一种收集站点、用户和资源的方式:

接下来需要创建站点,站点实际上是指你希望通过隧道进行代理的远程位置。例如:你家里的电脑、小主机、NAS等。

这里我用家里的Windows电脑演示一下,名字就随便起个,比如Home Lab Windows之类的,需要注意的是Tunnel类型,这里我选择的是Newt(Pangolin推荐的)

它会回显一个ID和Secret,这个只显示一次,务必保存好,当你每次使用Newt连接的时候都需要用到:

现在需要在Windows安装Newt,Pangolin提供Newt的二进制文件,下载一下就行:

https://github.com/fosrl/newt/releases/download/1.1.3/newt_windows_amd64.exe

运行Newt:

./newt \
--id your_id \
--secret your_secret \
--endpoint https://pangolin.example.com

如果能够成功建立连接,那么在Pangolin的控制台应该就能看到这台设备目前是“在线”状态:

接下来需要在Pangolin的控制台创建“资源”,“资源”就相当于你想要暴露出去的任意服务,一个服务可以是一个资源,也可以是多个服务共用一个资源。这里我拿qBittorrent演示。

假设现在我想把这台Windows电脑上的qBittorrent Web UI暴露出去:

资源类型这里选择HTTPS Resource,然后填写一个子域名,例如:qbittorrent.example.com:

在Proxy->Targets Configuration这里填写这台Windows电脑的内网IP,端口填写qBittorrent Web UI监听的端口:

qBittorrent配置,去掉CSRF保护勾选,不然Pangolin反代后会报401未授权:

这样当我在外面的时候,就可以通过qbittorrent.example.com访问到家里的qBittorrent了。

我这里只是简单的演示,实际上Pangolin还有一个非常强大的功能:身份验证。它支持PIN验证、密码验证、一次性密码(邮件)认证等。有需要的可以自己部署之后试试。