iloves.top

加载

磁盘分区

背景

问题主要来源于同事电脑突发状况,最后确定硬盘出毛病了。

后面从其他机器换了一块机械硬盘上去,居然识别不了,bios里面识别不了,一度以为磁盘又坏了。

后面用转接线接到USB口,读取磁盘。在windows系统的磁盘管理中,可以看到磁盘,但没有分区出来,显示没有初始化,需要初始化,点击初始化后,也无法初始化成功。

一开始想到可能分区出问题了,网上找了些资料也没有解决。然后突然想到会不会是系统缘故,可能之前用在linux系统上,导致无法windows系统无法识别。

插到linux系统上,一看,/dev/sdx出来了,mount挂载也顺利,能创建文件,啥问题也没有,所以大概率是linux文件系统和windows文件系统差别的缘故,一折腾也可能把磁盘引导分区也弄出问题了。

然后想到之前,自己在安装windows和linux系统时,也遇到过类似的问题,那是由于对磁盘所在的系统很明确了,也很快就处理了。具体细节却想不起来了,大致情况就是,安装过xxx系统上的磁盘在安装另一个系统时无法识别,然后直接将磁盘抠出装到其他机子上,用个啥命令将磁盘的所有分区给删除了。

Windows删除分区

这里找了下以前的记录,只剩下几个冷冰冰的操作命令:

//输入diskpart命令
> diskpart

//磁盘信息
> list disk

//选择磁盘号
> sel disk [num]

//列出分区
> list partition

//选择分区
> sel partition [num]

//删除分区
> delete partition override

磁盘分区

第一个扇区

重要的放在前面,第一个扇区记录了磁盘分区表,开始的时候这个分区表称MBR,后面为了适应磁盘的大容量需求,增加了新的分区格式为GPT。

磁盘小于2TB,默认使用MBR,大于则默认使用GPT。

BIOS也需要读取MBR/GPT里面的启动引导程序,设备才能完成启动。

文件系统

内容略过。

windows系统一般使用ntfs,linux一般使用ext4

Linux磁盘分区

存储设备

IDE的磁盘的文件名为 /dev/hd[a-d]

SCSI、SATA、USB的磁盘文件名为 /dev/sd[a-p]

//列出所有存储设备
$ lsblk

//查看块设备的文件系统类型及UUID (fs)
$ lsblk -f

查看分区

//查看硬盘信息及分区
$ sudo fdisk -l

分区模式

//指定硬盘,进入分区模式
$ sudo fdisk /dev/<sdx设备>
  • 查看帮助:m
  • 创建分区:n
  • 1、选择主分区或者扩展分区
  • 2、选择分区编号
  • 3、按照默认,选择上一个分区的结束大小作为开始大小
  • 4、+<分区大小>,分配的空间大小
  • 5、查看分区:p
  • 6、保存:w

格式化

//格式化为ext4文件系统
$ sudo mkfs.ext4 /dev/<sdx设备>

磁盘空间使用

//列出文件系统的整体磁盘使用量(disk free)
$ df -h
    -a :列出所有的文件系统,包括系统特有的 /proc 等文件系统;
    -k :以 KBytes 的容量显示各文件系统;
    -m :以 MBytes 的容量显示各文件系统;
    -h :以人们较易阅读的 GBytes, MBytes, KBytes 等格式自行显示;
    -H :以 M=1000K 取代 M=1024K 的进位方式;
    -T :显示文件系统类型, 连同该 partition 的 filesystem 名称 (例如 ext3) 也列出;
    -i :不用硬盘容量,而以 inode 的数量来显示

//检查磁盘空间使用量(disk used)
$ du
    -a :列出所有的文件与目录容量,因为默认仅统计目录底下的文件量而已。
    -h :以人们较易读的容量格式 (G/M) 显示;
    -s :仅显示指定目录或文件的总大小,而不显示其子目录的大小。
    -S :包括子目录下的总计,与 -s 有点差别。
    -k :以 KBytes 列出容量显示;
    -m :以 MBytes 列出容量显示;

挂载

临时挂载

1、创建挂载点目录

//创建要挂载的文件夹
$ mkdir [文件夹目录及名称]

2、挂载分区

//挂载到指定目录
$ sudo mount /dev/<sdx设备> <挂载点目录>

3、卸载挂载

//根据挂载设备卸载挂载
$ umount /dev/<sdx设备>

//根据挂载点目录卸载挂载
$ umount <挂载点目录>

永久挂载

//查看块设备的文件系统类型及UUID (fs)
$ lsblk -f

//进入fstab配置,并修改配置
$ sudo vim /etc/fstab

白折腾

回到最开始的背景上来,同事电脑被判定硬盘异常的中途中,用到了三块硬盘,容量分别时1T、500G和80G。

情况比较曲折,简单来说就是换个硬盘就好了,然后又不行了,最后所有硬盘都不行了,临时买了个固态,2小时送上门,256G,就装了个固态重装下系统就好了,也没有反反复复开不了机的情况(卡死在BIOS界面)。

对此,我觉得太难以相信。硬盘没有其他的错误操作。好端端的怎么会坏呢?最后拿来了工具(SATA转USB工具)测试。

在windows系统中,1T的硬盘,删除分区,重新格式化了,最后能正常识别显示。

剩下500G和80G的,来回在windows和linux系统下折腾。主要现象为在linux系统中可以显示并使用,在windows上无法识别,就认定为是windows系统无法识别linux下的分区格式导致的。(而且插上硬盘,再给windows开机时,也卡在了BIOS界面,把硬盘拔了,就可以正常启动)

用80G的硬盘不断测试,主要就是在linux系统上使用fdisk、gdisk工具重建分区表, 重新分区,并格式化为ntfs,修改分区标志。将网上的教程试了一个遍,总之就是linux中正常,windows上异常,可以识别到磁盘时,但操作不了(尝试初始化时提示,IO设备错误无法运行此项请求),要么介质受写入保护。

然后突然想到会不会USB 扩展接口问题,接到主机的后面USB呢?

好吧,居然这这个问题卡了这么长时间(关键是已经有了一个磁盘成功的案例,第一时间没有往这方面去想)。

换上后面的USB口后,磁盘管理器上,虽然在LINUX已经为磁盘新建了分区,WINDOWS并不认账,将原分区删除,重新建一个分区,然后快速格式化一下。

解决了。本来丢垃圾桶的两块硬盘,又可以继续它们的牛马生活了,负重前行。

格式化为ntfs

ntfs为windows系统使用的文件系统,在格式化前需要安装ntfs-3g

//安装ntfs-3g
$ apt-get install ntfs-3g

//格式化为ntfs-3g
$ mkfs.ntfs /dev/sdx(磁盘设备的名称)

注意没有分区的设备无法格式化,格式化前先分区。如果设备已挂载,需要取消挂载再格式化。

(这块异常的磁盘500G,分区时默认使用linux文件系统,然后格式化成ntfs时,用很长时间)

纯GPT模式

有种说法,linux的gdisk工具将硬盘设置为了MBR/GPT的混合模式。导致windows无法识别。可以转为“纯GPT”格式。

操作如下:

//安装gdisk工具
$ apt-get install gdisk


$ gdisk -l /dev/sdx(目标分区)
可以看到分区信息为:MBR:hybrid
使用x进行扩展功能
进入扩展功能后,使用n,产生protective MBR
最后w保存并退出
重新使用gdisk -l /dev/sdx,检查发现信息变为:MBR:protective

试过这个操作,但是有没效果不清楚。毕竟最后一块硬盘没有这个操作,只是简单换成主机后面的USB口接入。
但是protective的意思为“保护”,这样是表示“纯GPT”,会不会导致磁盘被保护呢?(磁盘被保护,需要解除才能操作)

WIN解除写保护

参考前面章节中的“Windows删除分区”,select disk x 后

运行“ attributes disk”可以查看磁盘的基本属性(只读状态等信息)

清除写保护

//清除磁盘写保护
$ attributes disk clear readonly

power&edit

背景

刚好2020着上班的时间的到来,且被困在了家里。除了手机外,就没其他的设备了,一边和同学开麦玩着吃鸡手游,一边被领导催着工作,真是冰火两重天。不知前往市里的公交什么时候才能开放,然后网购了一套主机,除了上大学前的买的一台笔记本外,还是自己入手的第一台主机。

戴尔品牌,主要觉得大品牌会好一点。Dell Inc. Inspiron 3670. 说实话,一点都不好用,装好系统后卡的要死,这已经是一年后了,因为我去公司上报,设备才到。父母没接触过也不明白,还是请高中同学上门弄好的。

父亲稍有文化,我以为他可以多用用电脑,浏览网页,练习打打字啥的,但这年头还是手机方便,沉浸在短视频的世界中。

2022年自己亲自买配件组装了一台电脑,所以直到2024年春节,才想着弄些配件给电脑升级下,结果翻车了。电脑开不了机,想着放家里也是吃灰,就寄来上班的地方了。后面买了工具才发现,自己折腾下,机械硬件和固态硬盘都烧了。最后一番折腾,重装了linux系统,当作自己服务器了。

奔着All in One而去,但底层用的都是Ubuntu系统,利用docker部署应用。“降本增效”这四个字,就像有魔力似的,在我脑袋中转来转去。看到自己的网页,就想到以前从来没考虑过的电费,究竟有多少,我也不清楚,也没有实测过。

每次拿起手机,就开始打开淘宝、拼多多,搜索起“低功耗CPU”、“NAS主板”之类的,看来看去都不合心意,想着成本低、功耗低、性能强、扩展方便。还是算了吧,重新审视起这个机器,i3-9100 CPU @ 3.60GHz,官网搜了下TDP也才65W,也还行。

还有一台老古董联想笔记本,在默默吃灰呢?

这一年,不知不觉就过了大半,吃瓜事件也不少。Inter 13代CPU出现状况,国内docker镜像下架,无人驾驶之萝卜快跑,经济下滑就业困难。以前不觉得,现在却是深深感觉,在时代的起起伏伏面前,不过在随波逐流。

节能

为了处理功耗问,安装一个powertop来安慰一波,总比没有要好一点,但是风险也是有的。

在优化”虚拟机写回超时“的设置种,由于会影响系统在将任何数据更改写入实际磁盘之前等待的时间,当系统失去所有电源时,您将面临丢失最后几秒钟内对数据所做的所有更改的风险。

考虑到断电只是偶尔情况,网站也仅限自己一个人使用,不像商城需要面对广大的消费者,所以在节能与保护数据之间,果断选择了节能。

//安装powertop
# sudo apt install powertop

//查看power状态
#sudo powertop

在powertop信息页面上,通过”tab“键切换。powertop可以参考链接:PowerTOP – 分析和管理 Linux 中的功耗 (linux-console.net)

添加守护进程,自动配置为”Good“状态实现节能。

//运行服务
# systmctl start powertop.service

//使能服务
# systemctl enable powertop.service

以下是启动服务后的注意事项:

使用守护程序服务时需要小心,因为某些可调参数会带来数据丢失或奇怪的系统硬件行为的风险。这一点在“虚拟机写回超时”设置中表现得很明显,该设置会影响系统在将任何数据更改写入实际磁盘之前等待的时间。

当系统失去所有电源时,您将面临丢失最后几秒钟内对数据所做的所有更改的风险。因此,您必须在节能和保护数据之间做出选择。

编辑器

之前工作一直用source insight作为编辑器,差不多从大学开始用。中途也找过试用过其他的编辑器,Atom、VS code、Sublime,用来用去,还是觉得source insight用的舒服,但是偶尔的卡顿,让人很不爽,同时它也不是一款免费的编辑器。被弃的VS Code,最终还是捡起来继续用,彻底把source insight删除了。主要使用它的人多,功能也丰富。

在平时工作时,必不可少和下面的人交流,工位空间有限,会议室中用其他电脑又不好将代码工程拷贝到其他电脑上再在一台性能很不好的笔记本上安装上一个编辑器。之前也一直在寻找可以通过浏览器浏览代码工程的工具。VS Code是可以支持服务器部署的,但一直没有去研究。

这次顺便把想做的事情做了。

推动力,主要还是个人需求上。按理说,个人文档上,我已经有了若干种方案了,在线笔记,基于wiki的文档,可道云,甚至网站文章和git仓库,我都可以用作充当个人文档的记录和同步,不需要类似功能的文档方案了。

开始,部署好这些还是蛮有成就感的,不用操心家里的笔记和在公司的笔记同步问题,但是麻烦也随之而来。

网站后台主要用着个人内容物的发表,但是在后台编辑文章,真是一件苦差事,使用起来很不流畅,还不如在记事本写好,粘贴上去。涉及到一些零散的内容是无法记录的。

在线笔记,开始一段时间用的比较勤劳,直到现在也有陆续在使用,作为自己的一个知识库来使用还是蛮不错了。但编辑文档时,如果内容过长(可能有某些bug吧,内容增多后会突然变成只读模式,需要重新设置为编辑模式,偶尔加载异常),跳转和查找起来就不是很灵活,特别是当屏幕小的时候,编辑区域的空间就比较受限了。

在线笔记是基于一个开源项目Trilium搭建的,它就像是obsidian的网页版,在搭建前,都是用obsidian在本地来管理文件和记录(obsidian没有网页版,不是开源的),现在电脑资料的存档目前还是通过obsiadian在管理,却已经不用它做记录了。

wiki在我的方案中,主要用于记录零散的内容,但是每次记录比较麻烦,需要创建页面,或者在原来的页面上开启编辑,如果内容带有附件或者图片,就很麻烦,图片和内容是分散开的(图片需要单独上传,后期无法移动,如果懒人操作的话,基本上所有图片都堆在root目录中了)。在平时工作时,推荐下面的人用wiki来做开发记录,基本上只有自己在使用。

可道云,提供了一个仿window桌面的页面,有文件系统,有很多小工具,在这里存档一些表格文件和word文件还挺不错,很方便后期修改。如果平时记录的话,还不如记在本地电脑的记事本中。

git仓库,主要用来同步文件使用,但是在不同设备操作的前提需要安装git,配置账户,加上git clone、git pull、git push这些命令,用来管理代码工程还行。

coder-server,是vs code的一个服务器版本,部署后与vs code是一样的操作,支持在浏览器上使用。添加插件后,可以支持markdown文件,可以直接粘贴图片自动创建存放图片的目录(而不是通过上传方式来插入图片)操作起来很方便。

试一波先,安装好后,把未整理的内容全往coder-server上移。

安装

环境:docker下部署网页版vscode( 即code-server )

下载

估计与docker镜像源被关闭有关,现在基本上下载不了docker镜像了,所以需要更换源。

当满怀希望敲下”docker pull codercom/code-server“后,漫长的几分钟等待换来的却是”timeout“。

error pulling image configuration: download failed after...

更换docker镜像源,vim工具编辑 ”/etc/docker/daemon.json“文件,替换为如下内容(本文更新与20240830,可能过段时间就不能用了)

{
     "max-concurrent-downloads": 10,
     "max-concurrent-uploads": 5,
     "default-shm-size": "1G",
     "debug": true,
     "experimental": false,
     "registry-mirrors":[
                "https://x9r52uz5.mirror.aliyuncs.com",
                "https://dockerhub.icu",
                "https://docker.chenby.cn",
                "https://docker.1panel.live",
                "https://docker.awsl9527.cn",
                "https://docker.anyhub.us.kg",
                "https://dhub.kubesre.xyz"
        ]
}

编辑并保存后,需要重启docker。其中出现过一个小插曲,换源地址后,不小心少了一个”[“,然后直接重启主机服务器,开始一段时间还能用,第二天docker就启动不了了(当然那次换的是其他的源,没有下载成功)。

//重新载入配置
# sudo systemctl daemon-reload

//重启启动docker
# sudo systemctl restart docker

//检查docker信息
# docker info

然后就是开始下载coder-server

//获取镜像
# docker pull codercom/code-server

挂载

安装前,网上也搜了一些命令示例,大差不差,如果使用过docker,基本上都知道命令的功能。主要差异还是体现在挂载数据的目录上,如果弄错了,容器直接就罢工了。为什么不直接去镜像官网查看资料,下载镜像就费些功夫,进官网更麻烦了。

所以还是按照老套路,先安装一个不挂载数据卷的测试,进入容器检查应用所在的目录,直接把目录拷贝到主机上,下次重新安装时挂载上去。其他的信息则是通过”docker inspect”命令检查一遍,确保按照自己的目录来挂载,方便管理。

//启动一个测试容器
# docker run -p xxx.xxx.xxx.xxx:xxx:8080  -e PASSWORD=xxx --name xxx --restart=always -d codercom/code-server

为了安全,这里将IP、端口、密码、容器名字隐去了。

进入容器检查code-server应用的位置,研究过后才发现,容器内有一个用户名coder,是root用户,应用文件存放在/home/coder目录下,该目录的内容是”.xxx“的隐藏文件,直接用”ls -l“是无法看到的。

直接将coder目录拷贝到主机,由于权限问题,需要用到sudo。

//拷贝容器目录到主机
# sudo docker cp 容器ID:/home/coder /xxx/coder

//更改目录权限
# sudo chmod -R 777 /xxx/coder

目录挂载

1、为了方便在容器后使用内网,而不直接走域名的公网IP,需要将hosts文件映射到容器内部:-v /xxx/hosts:/etc/hosts

2、为了保证容器内容时间与主机在同一时区:-v /xxx/Shanghai:/etc/localtime

3、应用所在目录(由于直接映射目录,里面包含了登录密码,所以后面重新创建容器后,密码还是按照原来的,而不是使用新的):-v /xxx/coder:/home/coder

启动容器

创建并启动容器前,可以把原来创建测试的先删除

//停止容器运行
# docker stop 容器ID

//删除容器
# docker rm 容器ID

创建并启动code-server容器

//部署code-server
# docker run \
  -p xxx.xxx.xxx.xxx:xxx:8080 \
  -v /xxx/hosts:/etc/hosts \
  -v /xxx/Shanghai:/etc/localtime \
  -v /xxx/coder:/home/coder \
  -e PASSWORD=xxx \
  --name xxx \
  --restart=always \
  -d codercom/code-server

nginx反代理

location / {
  proxy_pass http://xxx.xxx.xxx.xxx:xxxx;
  proxy_set_header Host $host;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection upgrade;
  proxy_set_header Accept-Encoding gzip;
}

按照上面配置好nginx反代理后,在浏览器上输入域名即可访问。

拒绝访问

在安装md插件Office Viewer (Markdown Editor) 后,创建xxx.md测试文件,打开文件,发现无法显示。

页面显示“ xxx 拒绝连接”。

开始并没有往iframe网页嵌套的安全策略上去想,觉得可能是nginx配置缺少了什么东西,在网上看是查找起解决方法,一直没有结果。

最后打开浏览器的开发者工具侧栏页,然后一边试图访问xxx.md文件。

然后看到了几个警告,“An iframe whilc has…”的内容让我立刻想起,为了统一网页的风格,而在每个nginx配置文件添加的“add_header X-Frame-Options”和“add_header Content-Security-Policy”。是否将本域名添加到这两个配置上就可以呢?试过后确实可以正常显示xxx.md文件了。如下所示:

    add_header X-Frame-Options "ALLOW-FROM https://域名1/ https://域名2/";
    add_header Content-Security-Policy "frame-ancestors https://域名1/ https://域名2";

域名1,是为了域名1的网页可以嵌套这个coder-server的页面,域名2(也就是coder-sever所使用的域名)是为了处理打开coder-server部分内容显示“xxx拒绝连接”的问题。

终于松了口气,开始享受网页版的VS CODE编辑器吧。

code-server设置

1、显示Menu Bar:View -> Appearance -> Menu Bar

2、中文支持:搜索”Chinese – 汉化“插件,并安装。

  • 打开显示(View)菜单,选择命令面板(Command Palette)菜单项或(Ctrl+Shift+P)快捷键 打开命令面板。
  • 在命令面板中,输入Configure Display Language,选择Configure Display Language命令,显示已安装的语言包列表。
  • 在已安装的语言包列表中选择需要切换的语言包。
  • 根据提示重启VScode完成切换

3、修改主题颜色:设置 -> 主题 -> 颜色主题(我选的Quiet Light,比较好看)

4、设置不显示隐藏文件夹(开始使用时可以看到CODER下一堆”.xxx”的隐藏文件,这个就是coder的一些配置文件):设置 -> 用户 文件 files:Exclude -> Add Pattern **/.*

5、git clone代码时,”=server certificate verification failed. CAfile: none CRLfile: none=“错误提示。

git config --global http.sslverify false
git config --global https.sslverify false

6、代码跳转(没有弄出来),参考:关于在Vscode安装clangd的教程(分别在linux和windows)[很详细,很细节,很全!]【Windows端:缺少 language enginee的解决方法】_vscode clangd-CSDN博客

嵌套网页

需求来源

网站中,有一部分是通过第三应用搭建起来的服务,我在这里给它统称为应用服务。虽然都是自己所有,但是在网站中在导航栏中,通过新标签方式打开这些网址,总是少了点意思。

部分应用服务是开源的,但我也不能专门去研究它们,并把它们融入到自己网站系统中。为了和这个网站统一起来,我能想到的方法就是,新建一个页面,在页面中嵌套这些应用服务。

不负我所望,iframe就是为嵌套第三方网页而生。

iframe的html源码参考:

<div style="position: relative; width: 100%;
    padding-top: calc(100%);
    border: 1px black none;">
    <iframe src="被嵌套入页面的网址" title="页面名称"
    allowfullscreen=true
    style="position: absolute; width: 100%; height: 100%; top: 0;">
    </iframe>
</div>

安全问题

页面添加好源码后,大部分情况并不会如你所愿。主要是为了安全考虑。

试想一下,如果所有网站都允许被其他网站的页面所嵌套,访问者在访问时,对于两个一样界面的页面,会不会产生困扰,哪个才是真正的目标网站。如果非法页面加上恶意引导信息,不仅降低原网站的信用,还可能会对访问者造成损失。当然,这只是我的一个简单估量。

在这里,我并不需要嵌套来自其他拥有者的网站,只需要嵌套同一级域名下的各个二级域名的网站,所有有很大的操作空间,所谓遇水搭桥,逢山开路。

安全限制

X-Frame-Options

X-Frame-Options 是一个 HTTP 响应头部,用于防止网站被嵌入到其他网站的 iframe 中。该协议定义了一些选项,使网站可以控制在哪些网站中可以嵌入自己的内容,从而防止网站被点击劫持攻击。

有三种选项:

deny
 表示该页面不允许在 frame 中展示,即便是在相同域名的页面中嵌套也不允许。

sameorigin
 表示该页面可以在相同域名(相同的子域名)页面的frame中展示。

allow-from *uri*
 表示该页面可以在指定来源的frame中展示。

在nginx的server中做如下配置,以允许页面在指定页面链接中嵌套。

    add_header X-Frame-Options "ALLOW-FROM https://net.iloves.top/";
    add_header Content-Security-Policy "frame-ancestors https://net.iloves.top/";

很好,大部分的嵌套,已经可以成功显示了。然而,还是有部分显示“xxx拒绝了我们的连接请求”,那请接着往下看CSP。

CSP

CSP 是一种安全功能,通过指定允许哪些域将网站内容嵌入框架或 iframe 来帮助防止跨站点脚本 (XSS) 攻击。

简单的说就是提供地址给第三方,让对方信任你的网址才能嵌套。

这个通过nginx也能解决。(前提是第三方得是你自己)

在nginx的location中做如下配置,以隐藏来自该网站的两个响应头:“Content-Security-Policy” 和 “X-Frame-Options”。

	proxy_hide_header Content-Security-Policy;
        proxy_hide_header X-Frame-Options;

RSYNC

剧情介绍

搭建了一个linux主机,用来充当服务器,存档文件和同步代码使用。

主机很老旧,跑ubuntu绰绰有余了,只要能跑,就不是问题。但是硬盘存储一直是个隐患,哪天主机偷偷抽烟了,或者精神异常,说不定数据全没了。

开始每隔一段时间,就对里面的数据打包备份了,终究还是同一块硬盘,同一个主机。

然后就又搞了一台能跑的机子,装上ubuntu系统,用来做异机备份,降低风险。

经过一顿搜查,选择了rsync工具。

一顿折腾,都弄好了,测试也通过,就上线使用了。

过了段时间,连上SSH,看了一眼备份的文件,居然没有更改,顿时就凌乱了,得赶紧补下羊圈。

检查了下备份服务器的log (目录:/var/log/rsyncd),之前的备份还是正常的,没看到啥毛病。就在给主机换了个地方的后的前几天,就出现异常了。

name lookup failed for xxx.xxx.xxx.xxx: Name or service not known

然后在原服务器终端上,手动运行备份命令

sudo rsync -avzPt --delete /mnt/archive/data_bk/    xxx@192.168.1.xxx::sync_data_bk     --password-file=/etc/rsync/rsyncd.secrets
sudo rsync -avzPt --delete /mnt/data/               xxx@192.168.1.xxx::sync_data_docker --password-file=/etc/rsync/rsyncd.secrets
sudo rsync -avzPt --delete /mnt/archive/data_share/ xxx@192.168.1.xxx::sync_data_share  --password-file=/etc/rsync/rsyncd.secrets

出现异常

rsync: [sender] failed to connect to 192.168.1.xxx (192.168.1.xxx): Connection refused (111)
rsync error: error in socket IO (code 10) at clientserver.c(139) [sender=3.2.7]

排查才发现备份服务器的rsync服务没有启动。(当时搭建好后没有设置开启自启)

***@***:/var/log$ ps -ef | grep rsync
***+   11311   11162  0 14:45 pts/0    00:00:00 grep --color=auto rsync

***@***:/var/log$ service rsync restart
==== AUTHENTICATING FOR org.freedesktop.systemd1.manage-units ===
重新启动“rsync.service”需要认证。
Authenticating as: worksrvbp,,, (worksrvbp)
Password:
==== AUTHENTICATION COMPLETE ===

***@***:/var/log$ ps -ef | grep rsync
root       11323       1  0 14:46 ?        00:00:00 /usr/bin/rsync --daemon --no-detach
***+   11325   11162  0 14:46 pts/0    00:00:00 grep --color=auto rsync

可以看到服务restart前后,rsync的两个不同状态。

开机自启

如下设置,没有触发开机自启?

//启用rsync服务
$ systemctl enable rsync

//设置
$ vim /etc/default/rsync
  RSYNC_ENABLE=true

前期回顾

参考资料

备份配置

备份服务器的配置文件:/etc/rsyncd.conf

配置参考

# sample rsyncd.conf configuration file

# GLOBAL OPTIONS

#motd file=/etc/motd
log file=/var/log/rsyncd
# for pid file, do not use /var/run/rsync.pid if
# you are going to run rsync out of the init.d script.
# The init.d script does its own pid file handling,
# so omit the "pid file" line completely in that case.
pid file=/var/run/rsyncd.pid
#syslog facility=daemon
#socket options=

# MODULE OPTIONS
\[sync_test\]

        comment = public archive
        path = /mnt/data/sync_test
        use chroot = yes
#       max connections=10
        lock file = /var/lock/rsyncd
# the default for read only is yes...
        read only = no
        list = yes
        uid = root
        gid = root
#       exclude =
#       exclude from =
#       include =
#       include from =
        auth users = 备份服务设置的账号
        secrets file = /mnt/data/sys/rsync/rsyncd.secrets
        strict modes = yes
        hosts allow = 192.168.xxx.xxx
#       hosts deny =
        ignore errors = no
        ignore nonreadable = yes
        transfer logging = yes
        log format = %t: host %h (%a) %o %f (%l bytes). Total %b bytes.
        timeout = 600
        refuse options = checksum dry-run
        dont compress = *.gz *.tgz *.zip *.z *.rpm *.deb *.iso *.bz2 *.tbz

定时同步

源文件所在主机,设置

//配置定时任务
sudo crontab -e
(选择3)

//间隔1分钟同步文件到备份目录
*/1 * * * * rsync -avzPt --delete /mnt/reserve/sync_test/ 备份服务设置的账号@192.168.xxx.xxx::sync_test --password-file=/etc/rsync/rsyncd.secrets

//每天5点00分执行备份
0 5 * * * rsync -avzPt --delete /mnt/reserve/sync_test/ 备份服务设置的账号@192.168.xxx.xxx::sync_test --password-file=/etc/rsync/rsyncd.secrets

//任务列表
sudo crontab -l

ACME.SH的NAMESILO泛域名证书申请

需求背景

自从搭建网站后,为了支持HTTPS访问,需要申请SSL证书。由于最开始建站使用了腾讯云的VPS(Virtual Private Server 虚拟专用服务器),所以也直接在腾讯云平台申请了若干个SSL免费证书。原来的有效期是一年,这个时间还能接受,快到期了就重新申请一下。但是后面SSL证书的有效期改了,变成了90天。对于计划长期运营的平台而言,这个期限太短了,如果没有及时更新SSL证书,服务就无法使用了。

随着后面需要继续申请证书,找了其他平台,基本上也是90天的。每增加一个子域名就要申请一个证书,也比较麻烦,最好就是获取一个泛域名的证书。愿意付一些费用的话,这自然不是问题,各大VPS提供商都有方案。

网上也有不少的自动更新证书的教程,但一直没有去使用,这次终于决定一补前坑。

服务环境

主机环境:linux的ubuntu系统

域名服务商:namesilo (https://www.namesilo.com/)

参考资料

这里先提供一下,自己从网上获取的参考资料。

从上面的资料,稍微研究下,基本就可以完成一个自动更新的免费的泛域名SSL证书获取了。

操作记录

(说明:这里仅记录了在namesilo通过acme.sh申请泛域名证书的操作,其他平台的参考上方的“参考资料”即可完成。)

下载脚本

在获取acme.sh脚本前,先在系统上安装curl工具,用来下载文件。

sudo apt-get update
sudo apt install curl

下载acme.sh工具

curl https://get.acme.sh | sh

下载时出现异常,无法访问“raw.githubusercontent.com”

报错内容为

curl: (60) SSL: no alternative certificate subject name matches target host name 'raw.githubusercontent.com'

这里需要做一些工作以访问该链接

访问链接:Website Overview: Daily Visitors, Safety Status, Traffic Rank, Competitor Analysis (ipaddress.com)

查询 raw.githubusercontent.com 对应的IP,并更新到hosts中

//编辑hosts文件
sudo vim /etc/hosts 
(后面的操作,知道的自然知道)

//重启网络
/etc/init.d/network restart

参考

185.199.108.133 raw.githubusercontent.com
185.199.109.133 raw.githubusercontent.com
185.199.110.133 raw.githubusercontent.com
185.199.111.133 raw.githubusercontent.com

重新使用curl下载acme.sh脚本

下载完成后,文件目录为:~/.acme.sh (位于home目录中)

API KEY

使用账号登录namesilo平台。

访问 https://www.namesilo.com/account/api-manager 获取API KEY。

获取时,不能勾选 “Generate key for read-only access”,勾选接收条款后,点击“Generate”,获取API KEY。(保存好Key信息)

忘记后,重新生成即可,但前面生成的key就无法使用了。

IP Restrictions

在后面生成证书时,由于某些失误,怀疑没有添加ip名单的缘故,开启了IP限制。但是操作是在几个不同的时间段完成了,所以如果开启了IP限制,需要通过IP查询网站,查询自己主机的公网IP,并将IP填入。不然在生成证书时,会报错,如下:

Unable to find domain specified

进一步检查log,可以发现错误如下:

this API account cannot be accessed from your IP

发现这个报错时,可以检查IP限制是否开启、主机公网IP是否与允许的IP一致这两个方面。(那就不要开启IP Restrictions了)

生成证书

进入.acme.sh目录,注册账号

acme.sh  --register-account  -m 邮箱地址 --server zerossl

注册后会返回ACCOUNT_THUMBPRINT,目前自己也不清楚这个可以用来干什么,先保存好信息。

acme.sh支持好几个ssl厂商,包括let’s encrypt,这里使用的是zerossl,主要是操作简单。

添加Namesilo_Key环境变量

//将上面查询到的api key添加到环境变量中
export Namesilo_Key="api key"

生成证书

./acme.sh --issue --dns dns_namesilo -d xxx.com -d *.xxx.com --dnssleep 900

等待创建完成,创建成功后,显示如下:

-----END CERTIFICATE-----
[2024年 08月 20日 星期二 10:14:15 CST] Your cert is in: 
[2024年 08月 20日 星期二 10:14:15 CST] Your cert key is in: 
[2024年 08月 20日 星期二 10:14:15 CST] The intermediate CA cert is in: 
[2024年 08月 20日 星期二 10:14:15 CST] And the full-chain cert is in: 

同时会自动创建cronjob,每天00:00自动检测所有证书,如果快过期了需要更新则会自动更新证书(这点我还没确认)

Namesilo_Key

这里是一个巨坑

初次操作,如果你已经添加过一次Namesilo_Key,也许你突然想到自己在生成key时,勾选了“Generate key for read-only access”,这意味着你只有读的权力,而无法写入。所以你不得不更新Namesilo_Key。

也许你会重新使用一次 export Namesilo_Key=”api key”,但是后面你无论怎么操作,都会发现“Unable to add the DNS record.”,检查Log,可以看到“ Invalid API Key (Permission denied)”。

这让人百思不得其解,即使在网上查询这个问题,你也可以看到,对namesilo的吐槽。

如果熟悉linux,就会想到 export 是用来添加一个临时变量,具体一点就是添加的变量只有在当前终端有效,关闭终端后,就无效了。

然后你会想着把它添加为永久变量,确实,可以把它添加到配置文件中,即使重启系统,你也可以用 echo $Namesilo_Key,打印出它的值。但这并无法解决“Unable to add the DNS record. Invalid API Key (Permission denied)”的问题。

其实重复输入export Namesilo_Key=”api key”这行代码,并不会执行覆盖操作,只会保存第一次保存的key。如果需要修改需要到 ~/.acme.sh/目录下 修改 account.conf 文件,在空白行或者末尾处,添加 export Namesilo_Key=”yourkey”

(我被这一点坑的确实惨)

使用证书

不要直接让nginx使用~/.acme.sh/目录下的证书,因为这个文件是会变化的,使用方式如下:

# 根据nginx目录修改位置
acme.sh --install-cert -d xxx.com -d *.xxx.com \
--fullchain-file /etc/nginx/cert/xxx.com.crt \
--key-file       /etc/nginx/cert/xxx.com.key  \
--reloadcmd     "service nginx force-reload"

证书更新后会自动调用reloadcmd

docker容器

终于,当我信心满满地敲下命令,使用命令更新上证书时。它报错了,找不到nginx服务。才想到,我是使用docker容器部署nginx服务的,主机空间上就不存在nginx了。但这也不影响我对它的使用,进入容器中,重新按照上面的操作设置一边,最后将容器打包成镜像,并保存到本地备用。

查看容器运行状态及ID:

docker ps -a

进入容器:

docker exec -it 容器ID bash

容器内缺少工具,需要安装一些工具,如vim、curl等。

创建证书的操作,同上。

容器镜像

//从当前运行的容器中提交镜像
$ docker commit -p <容器ID> <镜像名称>

//查询镜像
$ docker image ls

//导出到指定目录
$ docker save <镜像名称或镜像ID> > /xxx/<镜像名称>.tar

//镜像删除
$ docker image rm <image>

使用与查看

在nginx配置文件中修改xxx.conf配置文件,证书名字更改为新申请的证书。

重启nginx服务(docker容器部署的,则重启容器),在浏览器中输入网址,点击小锁头标志,点击“连接安全”,最后点击右上角的信息图标即可看到证书相关信息。

到期日期后,检查证书是否更新,判断自动更新服务是否生效。

当前部署的泛域名证书于2024年11月19日星期二 07:59:59到期。

单片机FLASH存储模块

问题背景

单片机自带的flash容量很小,100KB左右,有的更小,视不同型号而定。虽然文件系统可以一劳永逸,但是这么小的空间,完全没有必要,也难以实现,且对存储带来额外和负担,无法对flash的使用寿命带来优化。而为了存储少量数据,而增加外置flash,在硬件和控制上有带来额外的负担,也没有必要。

之前写过一篇关于flash存储模块的,不仅详细讲述了flash的特点,还仿照链表结点的方式,对数据进行管理。由于涉及到的细节太多,方案也不完善,将代码实现后,效果也不理想,对flash存储功能总是惴惴不安,无法向客户拍着胸膛保证功能的稳定性。

其实一般单片机产品需要在flash存储的数据类型也就这么点,完全不需要去设计兼容性有多么好,支持数据长度有多么长的一个存储系统。而且自己设计的存储系统,而不采用从其他嵌入式专用的文件系统移植过来的,可靠性可以得到保证,数据也可以完全把握住,毕竟自己的东西,对细节肯定是一清二楚的。

适合的才是最好的。

存储方案

根据需要存储的数据做了一个简单的类型划分,将所有的类型数据(通过bit压缩)控制在12字节长度(不足的也当作12字节)。flash按页擦除与读取,一页4096字节,那么一页可以存储4096/12 (即341)条数据(末尾多出来的就不存了)。

需要存储的数据类型约4~5种,为每种类型根据存储数据的需求划分不同的页范围。(当前设备flash总计100页)

但是在划分页范围前,我需要考虑下面这个问题。

设定我根据A数据的存储条数需求,划分0~4的序号页,当数据存储到4序号页的最后一条时,系统已经存满了,我该如何继续存入下一条?

方案一,将最开始的存入的数据删除,将后面的数据统统前移,最后面就重新留出存储空间,这个方案虽然保证了存储顺序,但是需要频繁操作flash,并不可取。

方案二,我实际上为它分配0~5的序号页,当我存满5序号页时,即使将0序号页全部擦除,在数量上我也已经满足了存储数量的上的需要,后续数据只要往0序号页存储就可以了,0序号页也再次存满时,擦除1序号页数据,依此类推,循环往复。

方案二在实现上是最容易的,存储、擦除也达到了一个良好的循环,避免频繁操作flash。

每次读取数据时,先按页读取数据,即依次读取4096个字节,读取到内存BUFF后,再一一解析,还原数据。至于为什么不是按12字节一条一条的读取,似乎flash读取并不是那么可靠,尽量减少操作次数吧,稍微牺牲一下内存空间。

代码实现

根据上述方案二的想法,代码实现参考:https://git.iloves.top/Project/Embedded_flash_mgr.git

数据的字节、存储系统的ID、不同数据类型存储的开始页和结束页,均由宏控制,可以根据需求调整。底层的flash读写擦除、延时等则需要根据不同单片平台适配。

写入位置

正当我完成并初步测试新的存储系统一切正常而沾沾自喜时,一段自问自答的对话,让我虚惊一场。

问:每次需要写入时或者重新上电后需要写入时,你怎么知道往序号为几,哪个位置写入数据呢?

答:系统初始化时,会遍历整个存储系统,直到读出连续4个0xff时,我就知道下一条数据该存在哪里了。

问:已经存满一遍后,假设正在写入第2页,而且第二页也写完最后一条了,如何?

答:若如此,整个存储系统都不存在连续的4个0xff了,直到遍历完所有页。如果设备一直保持正常工作状态,状态会在写完后即使更新到内存的结构体变量中,如果这个状态直接重新上电,就会按照默认值从开始页即开始位置写入(而开始页是没有擦除的,即使重新擦除了擦除的页也是不对的)。这是一个很严重的问题。

答:哦,不,这个问题已经不是问题了,除非恰好在程序执行到写完一天数据后立刻断电,而没有运行到下一个步骤,但这概率真的太低太低了。在英明神武的我,操作中,为了及时更新下一条数据需要存储的位置,每次写完后都会运行一次更新,本页写完最后一条后,下一页无理由擦擦擦。FF已经在下一页遍地开花了。你所说的问题已经不是问题了。

答:擦除不成功,写入失败,恰巧断电,这已经不值得我如此操心了,山高路远,江湖再见。

程序优化

模块代码只是简单的实现了方案功能,向应用层提供同步的初始化、写入数据、读取数据、打印数据的接口。虽然只是一个flash存储管理模块,内部与应用的关联很大,需要根据不同应用需求调整代码,并没有做到管理层与应用层完全的分离。(毕竟赶时间先实现功能,出个demo)。所以后续对数据的封装、回调函数注册、异步方法的实现上有很大的优化空间。

提供的数据查询方法,每次查询一个数据都是从对应的开始页查询到最后一个数据,效率上会很低(存入的数据自带时间,所以查询也是按照输入的时间查找对应的数据)。所以后续这一点上,可以通过ID判断页、判断当前是否有新写入数据来决定是否重新读取flash页来优化数据查询。

DEMO代码暂无优化安排。

程序测试

程序通过一个自动生成指令数据的终端工具向设备串口发送存储指令,测试存满后擦除开始页继续存,存满后,擦除下一页,继续存。通过dump存储的数据,测试得到了通过。

方案升级

目前方案适用于按固定间隔时间存储业务数据(便于后续业务批量查询和上报数据),对于键值更新的数据,稍微调整也能实现。

由于现有应用存入的数据自带有4字节的时间戳,所以查询数据只需要根据时间匹配即可,并没有设计单独的数据ID和区分不同的数据。

假设100页数据需要全部存满同一种类型的数据,使用uint32类型数据作为数据ID,每条数据包括ID在内,共有16字节,一页为4096字节,则一页有256条数据,100页共有25600条数据,即全部存满后ID从0开始,到25599结束。循环存储,ID不重置为0,则最大的ID值为0xffffffff,即4 294 967 295,可以存储4294967295/25600 (即167772)遍,即每页可以存储约16万次。

flash有寿命限制,sst39vf160手册上说是10万次,实际上可能远远超过,达到80万次。16万次在设计理论上已经达到flash的使用寿命了。

使用ID标记数据的方式,然后通过比较ID值大小的方式来辨别数据的新旧,在当前设备的flash上完全是可以的。

即使应用层每分钟更新同一个键值,在时间上也完全足够,实际上并没有这么频繁。

文件系统

后续方案更换后,SDK已经不支持直接操作FLASH,而是移植了一个小型化的文件系统。应用数据需要通过文件操作的方式写入存储。读写文件,操作虽然简单了不少,但是同样遇到一个问题,存储空间是有限的,不可能无限制的存储,同样对于一部分过期(超过设置存储空间大小的内容)需要删除。一个方案是直接在原数据内容上覆盖新的数据,另一个是准备两个文件,先遍历文件需要的内容转存到临时文件,然后通过复制或者临时文件更名的方式完成更新。这里个人的处理方法还是沿用了上方flash操作的一个思路,准备两个文件,一个文件写满后立刻删除另外一个文件,如此循环,虽然增加了存储空间的负担,但是减小了读写和对文件的操作。

其他场景

摄像头实时图像或视频数据、工业传感器数据采集(上K频率的数据),flash存储也许会有些挑战。

nginx配置问题

给docker部署的应用(一个开源的项目管理系统redmine)的nginx反代理配置又又访问异常了。

  • 详情:在会议室的笔记本电脑上访问xxx.xxx.xxx死后无法访问,提示 “400 Bad Request .  The plain HTTP request was sent to HTTPS port”, 但是在其他电脑上(其他三台上都可以正常访问)
  • 怪现象:另外一台主机开始使用火狐浏览器也是一样的情况,但是它换了浏览器后,可以访问,登录后却异常了,报告重定向问题
  • 后续:已经解决了

配置改动

参考博客   Nginx报错“The plain HTTP request was sent to HTTPS port”问题解决办法-CSDN博客

原文如下:

java redirect重定向https跳转http问题,如果https访问nginx通过nginx proxy_pass到http的tomcat服务正常能够访问,但是java redirect就跳转到http,导致报错“400 Bad Request: The plain HTTP request was sent to HTTPS port”。

实现流程是根据nginx的不同执行阶段,来完成Location http到https。

1)proxy_pass执行前,先设置了request head host 为https外网访问的域名+端口

2)proxy_pass执行后,tomcat结果返回response

3)proxy_redirect修改response中的location中的协议http为https外网访问的协议。

注:java redirect重定向主要是通过访问tomcat服务的请求head项来决定的,默认是http协议,域名是通过读取host地址,默认host中不包括访问端口。

所以原配置做了如下修改:

1、在 proxy_pass http xxx 后面添加了proxy_redirect http:// https://;
2、删除了原来的  proxy_redirect off;

最后重启服务

修改前后配置对比

修改前

server {
    listen 80;
    server_name xxx.xxx.xxx;
    if ($host = 'xxx.xxx.xxx'){
        return 301 https://xxx.xxx.xxx$request_uri;
    }
}

server {
    listen 443 ssl;
    server_name xxx.xxx.xxx;

    index index.php index.html index.htm;

    client_max_body_size 8192M;
    client_body_buffer_size 128k;

    ssl_certificate xxx.pem;
    ssl_certificate_key xxx_key.pem;
    ssl_session_timeout 5m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
    ssl_prefer_server_ciphers on;

    location / {
       proxy_pass  http://192.168.xxx.xxx:xxxx; # 转发规则
       client_max_body_size 100m;
       proxy_redirect off;
       #下面这一步一定要加上端口号,否则如果使用非443端口,网站在ajax请求之后会跳转到错误的页面(会不带端口号)
       proxy_set_header Host $host:$server_port;
       proxy_set_header X-Real-IP $remote_addr;
       proxy_set_header REMOTE-HOST $remote_addr;
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
       proxy_set_header Upgrade $http_upgrade;
       proxy_set_header Connection $http_connection;
       proxy_http_version 1.1;
       #下面这一行配置也要加上,否则ajax请求之后会跳转到错误的页面(https会变成http)
       proxy_set_header X-Forwarded-Proto $scheme;
       index index.html index.htm;
    }
}

修改后

server {
    listen 80;
    server_name xxx.xxx.xxx;
    if ($host = 'xxx.xxx.xxx'){
        return 301 https://xxx.xxx.xxx$request_uri;
    }
}

server {
    listen 443 ssl;
    # ssl on;
    server_name xxx.xxx.xxx;

    index index.php index.html index.htm;

    client_max_body_size 8192M;
    client_body_buffer_size 128k;

    ssl_certificate xxx.pem;
    ssl_certificate_key xxx_key.pem;
    ssl_session_timeout 5m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
    ssl_prefer_server_ciphers on;

    location / {
       proxy_pass  http://192.168.xxx.xxx:xxxx; # 转发规则
       proxy_redirect http:// https://;
       # client_max_body_size 100m;
       # proxy_redirect off;
       #下面这一步一定要加上端口号,否则如果使用非443端口,网站在ajax请求之后会跳转到错误的页面(会不带端口号)
       proxy_set_header Host $host:$server_port;
       proxy_set_header X-Real-IP $remote_addr;
       proxy_set_header REMOTE-HOST $remote_addr;
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
       proxy_set_header Upgrade $http_upgrade;
       proxy_set_header Connection $http_connection;
       proxy_http_version 1.1;
       #下面这一行配置也要加上,否则ajax请求之后会跳转到错误的页面(https会变成http)
       proxy_set_header X-Forwarded-Proto $scheme;
       index index.html index.htm;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
       root   html;
    }
}

KEE翻车记

翻车啦

四月份,搭建了一个开源的密码管理服务,就是为了方便公共和私有的密码管理。用的是keeweb,密码文件存储在服务器端,使用者使用浏览器查看、更新密码。

过了两个月,常用的浏览器不知道清理多少次,这次要登录其他平台的密码,就翻起密码管理工具找密码。

打开文件一看,空空如也,之前分类整理好的密码记录全没了。

登录服务器后台查看文件,密码文件还在,试了几次不死心,即使重新改了权限打开,还是空的。

之前也用过几次,明明是没有问题的,可以正常查看密码。

找到最近一次服务器数据备份包,解压打开,重新拷贝一份密码文件测试,也是空的。

猜测到当时整理好的密码压根就没有写入到服务的密码文件上,全缓存在浏览器上了。

重新看了设置,“Sync”按钮,有些刺眼,竟然不是自动同步的?还要手动同步?

无论是现在使用的“Trilium Notes”笔记工具,还是私人的另一款密码管理工具,都是自动同步的,对于keeweb还真没多少研究。

使用它最看重的一点就是,它记录密码还可以上传附件,无疑是懒人神器。

添加测试密码的记录,点了“Sync”, “Sync error: HTTP status 403”的信息显示,更是让人老血一喷,大呼“小可爱”。

403 Forbidden客户端没有访问内容的权限;也就是说,它是未经授权的,因此服务器拒绝提供请求的资源。)

怎么搞?暂时先停用,在小组内去推广使用也嫌麻烦,各顾各的吧。

开车指导

不同成员使用不同的密码文件,对自己的密码进行记录更新管理,密码文件可定义不同的访问密码,服务器端部署,web端管理。

使用方法

1、访问域名登录web界面

2、个人配置,自己的配置不影响其他人使用,更换浏览器或者清除浏览数据后需要重启配置

3、设置语言:根据个人喜欢,更多→设置→插件→加载插件库,选择中文语言插件,点击安装

4、设置主题:根据个人喜欢,在通用→外关→主题,设置自己的主题

5、设置锁定:根据个人喜好,在通用→功能→自动锁定,设置自己的锁定时间

6、右上方,点击返回应用

7、密码库访问:返回首页后,更多→WebDAV,URL中输入密码文件的路径(URL),参考略

8、点击确定,根据提示输入密码,即可使用

9、成员密码库可自行修改密码(建议修改),共享密码库不得修改密码

10、修改密码:左下角,点击密码库,在设置中修改主密码

11、注意,密码一旦修改,请记住密码,忘记密码无法登录,且无法找回密码(故拿到账号后先更改密码,再添加密码记录,并一直该密码)

添加密码文件

1、按照 ”使用方案“ 完成1~6步骤

2、在首页,点击新建,在左下角点击新建

3、设置好主密码,名称(名字的拼音),默认用户名 (名字的拼音)

4、保存到 ”文件“,浏览器自动下载到本地

5、将文件更名为 成员名字(名字的拼音),去除后缀,并上传到 服务器目录

6、修改其权限 “ sudo chmod 777 <文件> ”

7、按照 ”使用方法“ 8~9步骤,即可完成密码web化管理

8、添加自己的密码记录

gitea数据迁移

迁移需求

原gitea搭建在windows 10系统中,后面专门用Linux系统搭建了长时间运行的服务器,但是现有gitea仓库中已经存储了大量的项目源码,需要迁移到新的gitea应用中。

系统环境

  • 原gitea系统环境:windows 10
  • 原gitea应用程序:gitea-1.18.3-windows-4.0-amd64
  • 新gitea系统环境:Docker Container (Ubuntu 22.04.4)
  • 新gitea应用版本:v1.21.10

迁移步骤

  1. 使用docker安装gitea,浏览器访问页面完成后续安装。
  2. 拷贝 “gitea.db” 到 “/data/gitea“
  3. 拷贝 “gitea-repositories” 里面的内容到 “/data/git/repositories”
  4. 在账户管理中重新上传用户头像
  5. 迁移完成
  6. 测试,验证新服务是否存在异常

注意事项

  • 以上步骤,只迁移了源码仓库和账户信息,里面并没有涉及工单、文档、附件、固件等数据的迁移。
  • 如有需要,找到对应文件复制到对应目录下即可。

附录

创建gitea容器应用服务

$ docker run \
-p 192.168.1.100:7001:22 \
-p 192.168.1.100:7002:3000 \
-v /.../data/time/Shanghai:/etc/localtime \
-v /.../data/gitea/data:/data \
--name gitea \
--restart=always \
-d gitea/gitea:latest

nginx折腾

问题出现

使用nginx反代理wiki.js应用的示例如下:

#参考示例
server {
    listen 80;
    listen [::]:80;
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    ssl on;
    ssl_certificate /etc/ssl/certs/cert.pem;
    ssl_certificate_key /etc/ssl/private/key.pem;

    server_name your-domain.com;
    location / {
        proxy_pass http://localhost:6830;
        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;
    }
}

在自己服务器上配置wiki.js的反代理如下,可以正常访问(在腾讯云中申请的免费SSL证书)

#配置1
server {
    listen  80;
    server_name  x.x.x;
    rewrite ^(.*) https://$host$1 permanent;
}

server {
    listen 443 ssl;
    server_name x.x.x;
    index index.php index.html index.htm; 
    client_max_body_size 8192M;
    client_body_buffer_size 128k;
    ssl_certificate /etc/nginx/conf.d/ssl/x.x.x_bundle.crt;
    ssl_certificate_key /etc/nginx/conf.d/ssl/x.x.x.key;
    ssl_session_timeout 10m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
    ssl_prefer_server_ciphers on;

    location / {
        proxy_pass  http://192.168.x.x:xxx;
        proxy_redirect     off;
        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_set_header   X-Forwarded-Host $server_name;
        proxy_set_header   X-Forwarded-Proto https;
        proxy_set_header   Upgrade $http_upgrade;
        proxy_set_header   Connection "upgrade";
        proxy_read_timeout 86400;
    }
}

而在另一台服务器上按如上配置,却无法访问,SSL是通过mkcert生成的证书,该证书供局域网测试使用,均有修改为对应的域名、证书名称、服务器IP和端口号。

访问异常的表现:使用x.x.x访问时,打开的页面却是部署的另一个web应用页面,页面URL的“scheme://host:port/”部分正确,但是后续的“path?query#fragment”却错误,不是目标页面的内容。多次检测nginx配置、刷新网页、重启nginx均是这样的异常。(系统、nginx版本、代理的应用及版本均相同)

然后nginx反代wiki.js的配置修改为如下,则访问正常。

#配置2
server {
    listen 80;
    server_name y.y.y;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name y.y.y;
    index index.php index.html index.htm;
    client_max_body_size 8192M;
    client_body_buffer_size 128k;
    ssl_certificate /etc/nginx/conf.d/ssl/y.y.y.pem;
    ssl_certificate_key /etc/nginx/conf.d/ssl/y.y.y.pem;
    ssl_session_timeout 5m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
    ssl_prefer_server_ciphers on;

    location / {
       proxy_pass  http://192.168.x.x:xxx; # 转发规则
       proxy_read_timeout 300;
       proxy_set_header Upgrade $http_upgrade;
       proxy_set_header Connection "upgrade";
       proxy_set_header Host $proxy_host;
       proxy_set_header X-Real-IP $remote_addr;
       proxy_set_header X-Real-PORT $proxy_port;
    }
}

(目前还未细致研究nginx的配置文件)

问题再现

后续又使用docker部署了一个应用(参考上述“#配置2“的配置修改),同样使用mksert生成证书,然后出现了和上面一样的情况。

即访问https://A.com时引用了A.com的SSL证书。

通过查找网络资料,找到一篇类似的贴子,但问题没有得到处理,帖子链接:[点击访问]

  1. 是否使用mkcert自颁发的SSL证书才会存在这种情况?(另一台服务器使用在云平台申请的免费证书,从未出现过这种异常)
  2. 查阅帖子[点击访问],提及到的“TLS SNI support enabled”(一个IP地址运行多个HTTPS服务器),检查发现该配置是支持的。
  3. 试着在新应用上使用上面的”#配置1“,重新测试,结果浏览器不再提示证书的问题,而是提醒”重定向的次数过多ERR_TOO_MANY_REDIRECTS“ 。
  4. 感觉还是nginx反代理配置存在问题。

得到解决

背景:应该部署另一个应用服务对我管理事项很有必要,所以重新对ssl、nginx处理下,现在所有应用都能正常访问了。

解决:具体是如何解决的,目前也没有一个统一结论,只能将操作和自己的一些思路贴在下面,以便参考。

nginx err

检查了nging的log,有个警告提示“proxy_headers_hash_max_size”。按照 链接 提供的方法处理该警告。

由于使用docker容器部署nginx服务,所以需要将“/etc/nginx/nginx.conf”文件拷贝到主机上修改后重新拷贝回容器(目前这里一配置没有做持久化操作)。在http区添加如下代码

proxy_headers_hash_max_size 51200;
   proxy_headers_hash_bucket_size 6400;

重新启动容器。

这个警告应该不是造成访问异常得原因,后续可以重新创建nginx容器测试验证。

mkcert ssl

由于部署得应用服务只在局域网环境下使用,所有使用mkcert工具生成ssl证书。前期创建的详细命令已经没有记录了,但都是为每个子域名单独创建了一个域名证书(创建时还添加服务器IP、localhost、127.0.0.1、::1等等)

这次折腾将之前创建的证书全删了,使用泛域名证书替换所有证书。创建命令如下:

mkcert-v1.4.4-windows-amd64.exe *.xxx.xxx xxx.xxx

使用泛域名,同时支持主域名,使用方便。之前就一直怀疑证书问题。(虽然更新后,重测访问依旧异常)

清理

为了方便检查测试是否存在异常,经常手动清理docker nginx的log文件。同时也考虑了nginx的缓存,由于缓存在容器内部,就没有进入清理(每次进入容器清理数据有点麻烦)

浏览器缓存清理,为了方便,测试使用chrome浏览器,随用随清,而平时则使用edge。(浏览器缓存清理很重要!!!)

重启ubuntu系统,有时侯确实能发现重启前后访问结果不一样的情况。

nginx的配置

1、default.config按照默认配置

2、域名跳转

server {
    listen 80;
    server_name xxx.xxx;
    if ($host = 'xxx.xxx'){
        return 301 https://www.xxx.xxx$request_uri;
    }
}

server {
    listen 80;
    server_name www.xxx.xxx;
    if ($host = 'www.xxx.xxx'){
        return 301 https://www.xxx.xxx$request_uri;
    }
}

server {
    listen 443 ssl;
    server_name xxx.xxx;
    ssl_certificate /etc/nginx/conf.d/ssl/xxx.xxx.xxx.pem;
    ssl_certificate_key /etc/nginx/conf.d/ssl/xxx.xxx.xxx_key.pem;
    if ($host = 'xxx.xxx'){
        return 301 https://www.xxx.xxx$request_uri;
    }
}

server {
    listen 443 ssl;
    server_name www.xxx.xxx;
    ssl_certificate /etc/nginx/conf.d/ssl/xxx.xxx.xxx.pem;
    ssl_certificate_key /etc/nginx/conf.d/ssl/xxx.xxx.xxx_key.pem;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }
}

3、反代理gitea配置

server {
    listen 80;
    server_name git.xxx.xxx;
    if ($host = 'git.xxx.xxx'){
        return 301 https://git.xxx.xxx$request_uri;
    }
}

server {
    listen 443 ssl;
    server_name git.xxx.xxx;

    index  index.html index.htm;

    client_max_body_size 8192M;
    client_body_buffer_size 128k;

    ssl_certificate /etc/nginx/conf.d/ssl/xxx.xxx.xxx.pem;
    ssl_certificate_key /etc/nginx/conf.d/ssl/xxx.xxx.xxx_key.pem;
    ssl_session_timeout 5m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
    ssl_prefer_server_ciphers on;

    location / {
        proxy_pass  http://xxx.xxx.xxx.xxx:xxx;
        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;
    }
}

4、反代理wiki.js

(这次又重新按照“问题出现”中的配置1写回去了)

server {
    listen  80;
    server_name  wiki.xxx.xxx;
    rewrite ^(.*) https://$host$1 permanent;
}

server {
    listen 443 ssl;
    server_name wiki.xxx.xxx;

    index index.php index.html index.htm;

    client_max_body_size 8192M;
    client_body_buffer_size 128k;

    ssl_certificate /etc/nginx/conf.d/ssl/xxx.xxx.xxx.pem;
    ssl_certificate_key /etc/nginx/conf.d/ssl/xxx.xxx.xxx_key.pem;
    ssl_session_timeout 10m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
    ssl_prefer_server_ciphers on;

    location / {
        proxy_pass  http://xxx.xxx.xxx.xxx:xxx;
        proxy_redirect     off;
        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_set_header   X-Forwarded-Host $server_name;
        proxy_set_header   X-Forwarded-Proto https;
        proxy_set_header   Upgrade $http_upgrade;
        proxy_set_header   Connection "upgrade";
        proxy_read_timeout 86400;
    }
}

在上述一系列操作了(每次更改配置测试前都需要重启nginx容器,同时检查logs),所有域名都能正常访问了,不会出现访问A时,跳到B的异常情况。

keeweb代理

keeweb是一个密码管理工具,提供80和443访问端口,开始使用docker部署时按照其他应用部署的方式,将主机未使用的端口映射到容器的80端口,然后通过反代理访问服务。(一直无法访问,SSL安全异常)

(进入keeweb容器,可以发现里面还有一个nginx。)

考虑到其已经提供了443端口,那么我就可以直接通过容器内部的443端口访问,而不是去访问内部的80端口。

重新创建容器命令:

$ docker run \
-p 192.xxx.xxx.xxx:8006:443 \
-p 192.xxx.xxx.xxx:8007:80 \
-v /.../Shanghai:/etc/localtime \
-v /xxx/keeweb/pwd:/etc/nginx/external \
--name keeweb \
--restart=always \
-d antelle/keeweb

开始创建的时候由于习惯用80端口,所以并没有加上 “-p 192.xxx.xxx.xxx:8006:443 ”这一行。

同理创建容器后,nginx反代理,将http更改为https即可,配置如下:

server {
    listen 80;
    server_name xxx.xxx.xxx;
    if ($host = 'xxx.xxx.xxx'){
        return 301 https://xxx.xxx.xxx$request_uri;
    }
}

server {
    listen 443 ssl;
    server_name xxx.xxx.xxx;

    index index.php index.html index.htm;

    client_max_body_size 8192M;
    client_body_buffer_size 128k;

    ssl_certificate /etc/nginx/conf.d/ssl/xxx.xxx.xxx.pem;
    ssl_certificate_key /etc/nginx/conf.d/ssl/xxx.xxx.xxx_key.pem;
    ssl_session_timeout 5m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
    ssl_prefer_server_ciphers on;

    location / {
       proxy_pass  https://192.xxx.xxx.xxx:8006; # 转发规则
       proxy_set_header Host $host;
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

测试,访问已经正常。

相关学习

nginx的学习链接:链接 (可以对nginx有一个基本的了解)

Docker Postgres

需求

在搭建wiki时,按照网上教程使用docker搭建wiki.js,同时使用docker搭建mysql做数据存储。

浏览官网时,注意到wiki.js的后续升级,不再支持mysql等数据库,仅支持postgres数据库。

为了便于后续升级,避免数据在不同数据库间转移(目前我能想到的就是直接将帖子的重要内容拷贝下来粘贴到新的wiki页面上),需要将mysql替换成postgres(即数据库PostgreSQL)。

环境

系统:Docker Container (Ubuntu 22.04.4)

前提

1、了解Linux命令、文件权限、文件组织结构。

2、了解docker基本命令,掌握创建、进入、删除容器的操作和数据卷的挂载。

步骤

(个人并没有使用docker compose工具管理配置)

(按照个人习惯,一般先使用docker建立测试的应用容器,检查使用到的端口和应用数据文件,然后规划好映射到主机的端口和待挂载的数据卷,拷贝需要的文件到主机,最后建立持续运行的容器)

安装postgres

获取最新的镜像文件

docker pull postgres

运行测试

docker run -e POSTGRES_PASSWORD=password --name postgres --restart always -d postgres

端口规划192.168.1.111:7000:5432

挂载卷规划:(同时拷贝容器内的文件到主机目录中)

/mnt/.../localtime/Shanghai:/etc/localtime
/mnt/.../postgresql/data:/var/lib/postgresql/data
/mnt/.../conf/postgresql:/etc/postgresql
/mnt/.../conf/postgresql-common:/etc/postgresql-common
/mnt/.../init.d/postgresql:/etc/init.d/postgresql

安装:(删除原来安装用于测试的容器)

(换行后的完整指令参考)

docker run \
-p 192.168.1.111:7000:5432 \
-e POSTGRES_PASSWORD=password \
-v /mnt/.../localtime/Shanghai:/etc/localtime \
-v /mnt/.../postgresql/data:/var/lib/postgresql/data \
-v /mnt/.../conf/postgresql:/etc/postgresql \
-v /mnt/.../conf/postgresql-common:/etc/postgresql-common \
-v /mnt/.../init.d/postgresql:/etc/init.d/postgresql \
--name postgresql_wiki \
--restart=always \
-d postgre

(不换行的完整指令参考)

docker run -p 192.168.1.111:7000:5432 -e POSTGRES_PASSWORD=password -v /mnt/…/localtime/Shanghai:/etc/localtime -v /mnt/…/postgresql/data:/var/lib/postgresql/data -v /mnt/…/conf/postgresql:/etc/postgresql -v /mnt/…/conf/postgresql-common:/etc/postgresql-common -v /mnt/…/init.d/postgresql:/etc/init.d/postgresql --name postgresql_wiki --restart=always -d postgres

创建数据库

1、进入容器内部(命令自行到网上查询)

2、登录并创建数据库

//登录数据库
psql -Upostgres

//创建数据库postgres_wiki
CREATE DATABASE postgres_wiki;

3、查看已经存在的数据库 \l

4、退出数据库 \q

5、退出容器 exit

安装wiki.js

步骤与安装postgres类似,这里直接提供最终指令

(换行后的完整指令参考)

docker run \
-p 192.168.1.111:7001:3443 \
-p 192.168.1.111:7002:3000 \
-e "DB_TYPE=postgres" \
-e "DB_HOST=192.168.1.111" \
-e "DB_PORT=7000" \
-e "DB_USER=postgres" \
-e "DB_PASS=password" \
-e "DB_NAME=postgres_wiki" \
-v /mnt/.../localtime/Shanghai:/etc/localtime \
-v /mnt/.../wiki:/wiki \
--name wiki \
--restart=always \
-d requarks/wiki

(不换行的完整指令参考)

docker run -p 192.168.1.111:7001:3443 -p 192.168.1.111:7002:3000 -e "DB_TYPE=postgres" -e "DB_HOST=192.168.1.111" -e "DB_PORT=7000" -e "DB_USER=postgres" -e "DB_PASS=password" \-e "DB_NAME=postgres_wiki" -v /mnt/…/localtime/Shanghai:/etc/localtime -v /mnt/…/wiki:/wiki --name wiki --restart=always -d requarks/wiki

经验总结

postgres的账号密码

使用docker安装时设置了密码,但进入容器登录并没有用到密码 ,与mysql不同的是,postgres是不存在root账号的,它默认使用名为“postgres”的账号。

使用指令alter role postgres with password 'password';设置密码后重新登录数据库,系统也没有提示我需要输入密码。

(这个问题,后续没有继续研究)

错误的挂载卷

开始安装时,使用-v /mnt/.../postgresql:/var/lib/postgresql,按照自己的对数据卷的理解,容器内的“/var/lib/postgresql”目录所有内容直接被替换成了主机目录下的“/mnt/.../postgresql”的内容。

但是删除测试容器,重新建立容器时发现以下现象:

1、利用现有数据重新建立容器,不指定POSTGRES_PASSWORD密码,docker容器运行异常。

2、删除原有容器,利用现有数据重新建立容器后,进入数据库查看,发现建立的数据库消失了。

3、在第2点上重新测试,建立容器后,重启容器(非删除重建),创建的数据库并没有消失。

对于第一点,开始我并未在意,因为我下次创建容器指定POSTGRES_PASSWORD参数即可,并不影响服务的使用。

但是第2点却意味着,如果我正式使用该服务,当我试图重建容器,数据会消失,无法保存。这让我不禁思考,是否我挂载数据卷错误,或者没有保存数据库到主机?

而第3点却说明,我已经保存数据库到了主机,不然按照docker的原理,重启容器,数据会丢失。(纠正:容器不是沙箱,docker重启容器并不会丢失数据,数据卷挂载目的是数据持久化,避免删除容器后丢失数据)

我使用inspect命令查看容器,发现了问题所在。容器的配置详情如下:

在Mounts详情中,有一个使用了系统命名的数据卷(第一个红框)。说明它是系统自动挂载的,并非按照我指定的路径挂载。

同样的在Volume详情中,它也明确指出了需要挂载的目标路径为“/var/lib/postgresql/data”,而我却试图用一个更大范围的路径“/var/lib/postgresql”去包含它,所以系统就自动创建一个数据卷并挂载到容器上。

这就能解释第2点为何重建容器后原建的数据库不存在了,每次重建容器,系统就自动挂载了一个新的数据卷,自然数据就看不到了(实际上还存在主机的docker目录上)。

因此创建数据卷前还需要使用“inspect”命令查清楚需要挂载的卷,将一切数据掌握在自己手中。

数据卷清理

在“错误的挂载卷”章节意外发现默认挂载卷的存在,让我想起诸多容器测试验证时,均用到了默认挂载的方式。这意味着,现在有许多未使用的数据卷作为垃圾占据着主机的存储空间,这是之前一直未想到的。

列出挂载卷

docker volume ls

清除无主数据卷

docker volume prune

也可以进入/var/lib/docker/volumes目录查看创建的默认数据卷,但不推荐使用rm命令删除,避免误删。

当你尝试进入/var/lib/docker/volumes目录,是否会遇到sudo cd的操作呢,这就要用到sudo -i提升用户权限(或者使用sudo -s打开特殊shell)。