对于公网可以访问的网站,使用 HTTPS 协议是保证安全访问的基础要求,而 SSL 证书又是 HTTPS 协议不可或缺的一环。SSL 证书需要 CA 机构颁发,当前使用了阿里云的个人测试证书(免费证书),申请后上传到服务器并更新 nginx 配置。
当前存在的问题是,阿里云的免费 SSL 证书每三个月过期一次,需要手动重新申请并部署到服务器上,流程比较复杂。在网上看到可以使用 Let's Encrypt 免费申请 SSL 证书,并实现自动续期,于是做了尝试,这里记录下相关流程。
Let’s Encrypt 是一个免费的、自动化的、开放的证书颁发机构(CA),提供免费 SSL 证书。它的目标是让所有网站都能轻松实现 HTTPS 加密,保障用户数据的安全。通过 Let’s Encrypt 可以为网站的主域名、子域名,甚至泛域名配置安全证书,而且过程完全自动化,不需要手动申请和管理证书,官方推荐使用 Certbot。
Linux 平台上可以使用 snap 或 pip 安装 Certbot,这里使用 Python + pip 的方式,参考安装流程。
1、首先安装 Python 环境。
sudo apt update
sudo apt install python3 python3-dev python3-venv libaugeas-dev gcc
2、移除旧版本的 Certbot 工具。
sudo apt-get remove certbot
3、设置 Python 虚拟环境并安装 Certbot
# 设置 Python 虚拟环境
sudo python3 -m venv /opt/certbot/
sudo /opt/certbot/bin/pip install --upgrade pip
# 安装 Certbot
sudo /opt/certbot/bin/pip install certbot certbot-nginx
# 创建符号链接方便直接访问
sudo ln -s /opt/certbot/bin/certbot /usr/bin/certbot
1、使用 Certbot 获取证书,默认 nginx 配置文件的路径为 /etc/nginx/nginx.conf,可以使用 --nginx-server-root 指定不同的配置文件位置。
sudo certbot --nginx --nginx-server-root /www/server/nginx/conf/
2、配置自动更新。由于 Let's Encrypt 的证书有效期也只有 90 天,这里可以配置一个系统定时任务,由 Certbot 每天检查证书是否过期并自动更新。
# 在/etc/crontab定时任务配置文件添加定时任务
# 如下表示每天的0和12点随机延迟0-3600秒检查并执行证书更新服务
echo "0 0,12 * * * root /opt/certbot/bin/python -c 'import random; import time; time.sleep(random.random() * 3600)' && sudo certbot renew --nginx --nginx-server-root /www/server/nginx/conf -q" | sudo tee -a /etc/crontab > /dev/null
sudo /opt/certbot/bin/pip install --upgrade certbot certbot-nginx
我的服务器中部署了一个typecho博客和两个使用docker容器的服务,其中docker容器使用端口映射,将容器中的端口映射到宿主机上的端口实现访问。
一次偶然机会发现服务器上的服务可以通过IP+端口的方式直接访问,如果有未备案的域名解析到我们服务器的IP,可能会导致云服务器厂商关停我们的服务造成一些问题。因此,我们需要禁止通过IP+端口直接访问服务。
这里的三个服务通过nginx进行转发,对不同server_name
的请求会直接转发到对应的服务进程。因此,这里有限考虑使用nginx配置来禁止IP+端口的访问。服务器上主要开放了两个端口,80和443,分别用于HTTP和HTTPS请求,在实际进行相应配置时二者也有所不同。
对于80端口,我们在nginx.conf
中添加如下配置。具体原理在于,当根据listen无法得到最佳匹配时,nginx会使用请求中的Host值匹配server_name,匹配顺序可以参考这篇博客。IP+端口进行请求时匹配到下面的server配置,直接返回403错误信息。
server {
listen 80 default_server;
server_name _;
return 403;
}
由于使用了HTTPS协议,因此还需要禁止通过IP+443端口的访问方式。具体参考了下面的博客:
具体来说,Nginx 上对于 SSL 服务器在不配置证书的时候会出现协议错误,哪怕端口上配置了其他网站也会报错。因此,我们需要随便生成一个证书进行配置,生成 SSL 证书可以使用这个网站https://myssl.com/create_test_cert.html。在nginx.conf
中添加如下配置:
server {
listen 80 default;
listen 443 default_server;
#SSL-START SSL相关配置,请勿删除或修改下一行带注释的404规则
#error_page 404/404.html;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/private.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4:!DH:!DHE;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
error_page 497 https://$host$request_uri;
#SSL-END
server_name _;
return 403;
}
配置完成后重载 Nginx 配置。
$ nginx -t
$ nginx -s reload
Docker容器通过端口映射实现服务的对外可用性,这里是通过-p host_port:container_port
实现容器上的端口到宿主机上端口的映射。具体来说,以容器的80端口映射到宿主机的8080端口为例,docker容器运行时使用了-p 8080:80
,Nginx中监听了80端口,并在对应域名访问时将请求转发到服务器的8080端口。
location / {
proxy_pass http://host_ip:8080;
}
但是,在根据上面的配置禁止了直接使用IP+80/443端口访问的方式后,发现host_ip:8080
仍然能够访问到docker容器中的服务。查询相关资料修改了iptables路由表和sfw防火墙规则后,仍然无法解决问题。防火墙上没有打开端口,但仍然可以访问。最后通过查找资料发现是docker自身的原因,下面是docker官方的介绍:
If you don't specify an IP address (i.e.,-p 80:80
instead of-p 127.0.0.1:80:80
) when publishing a container's ports, Docker publishes the port on all interfaces (address0.0.0.0
) by default. These ports are externally accessible. This also applies if you configured UFW to block this specific port, as Docker manages its own iptables rules. Read more
大致意思是说docker容器中设置端口映射时如果没有指定宿主机IP,那么默认映射到0.0.0.0
,即所有IP都可以访问。并且由于Docker镜像自行管理其路由表规则,设置宿主机防火墙也不起作用。知道原因后,我们只需要修稿docker容器的端口映射即可,对于正在运行的docker容器,修改方式参考如下博客,修改对应容器的/var/lib/docker/containers/{container_id}/hostconfig.json
中的PostBindings->HostIp
,HostIp设置为127.0.0.1
即可。
原文:https://makefiletutorial.com/
中文翻译:https://makefiletutorial.vercel.app
本文主要基于以上教程记录一些平时阅读Makefile
可能会用到的信息。
首先,Makefile用于帮助决定一个大型程序的哪些部分需要重新编译,如果有任何文件的依赖项发生改变,那么该文件将会重新编译。Makefile通常由一组规则组成,规则通常如下所示:
target: prerequisites
command
command
command
target
是文件名,以空格分隔,通常每个规则只有一个。command
通常是用于制作目标的一系列步骤,需要以制表符Tab
而不是空格开头。prerequisites
也是文件名,称为依赖项,以空格分隔,在运行针对目标的命令之前,这些文件需要存在。下面是一个Makefile
的实例:
blah: blah.o
gcc blah.o -o blah # Runs third
blah.o: blah.c
gcc -c blah.c -o blah.o # Runs second
# Typically blah.c would already exist, but I want to limit any additional required files
blah.c:
echo "int main() { return 0; }" > blah.c # Runs first
使用make
时默认会执行第一个目标blah
,或者可以指定目标进行生成make blah
。上面的Makefile在执行make
指令后按照一系列步骤进行调用:
- make
选择目标blah
,因为第一个目标是默认目标
- blah
需要blah.o
,所以搜索blah.o
目标
- blah.o
需要blah.c
,所以搜索blah.c
目标
- blah.c
没有依赖关系,所以echo
命令运行
- 然后运行该gcc -c
命令,因为所有blah.o
依赖项都已完成
- gcc
运行top命令,因为所有的blah
依赖都跑完了
- 就是这样:blah
是一个编译好的c程序
Makefile使用文件系统时间戳来判断上次编译以来的先决条件是否发生了变化。例如,如果运行上面的Makefile后删除blah.c
,那么三个目标都会被重新执行;如果只是编辑blah.c
,那么只有前两个目标会被重新执行;如果只是通过touch blah.o
更新blah.o
的时间戳,那么只有第一个目标会被重新执行;如果不做任何更改,那么所有目标都不会被执行。需要注意的是,时间戳并不总是有效,例如,您可以修改一个文件,然后将该文件的修改时间戳更改为旧的时间戳。如果你这样做了,make
会错误地猜测文件没有改变,因此可以被忽略。
如果有多个目标并希望所有目标都被执行,那么可以使用一个all
目标,如下所示
all: one two three
one:
touch one
two:
touch two
three:
touch three
clean:
rm -f one two three
当规则有多个目标时,将为每个目标都运行命令,如下所示,其中$@
是包含目标名称的自动变量
all: f1.o f2.o
f1.o f2.o:
echo $@
# Equivalent to:
# f1.o:
# echo f1.o
# f2.o:
# echo f2.o
*
通配符在Makefile
中*
和%
是通配符,但它们的含义完全不同。*
在文件系统中搜索匹配的文件名。*
使用时最好包装在wildcard
函数中,如下所示:
# Print out file information about every .c file
print: $(wildcard *.c)
ls -la $?
*
不可以直接用在变量定义中,当*
没有匹配到文件时如果没有使用wildcard
函数包装,会直接保持原样。
thing_wrong := *.o # Don't do this! '*' will not get expanded
thing_right := $(wildcard *.o)
all: one two three four
# Fails, because $(thing_wrong) is the string "*.o"
one: $(thing_wrong)
# Stays as *.o if there are no files that match this pattern :(
two: *.o
# Works as you would expect! In this case, it does nothing.
three: $(thing_right)
# Same as rule three
four: $(wildcard *.o)
%
通配符%
通配符通常在多种情况下使用:
- 在"匹配"模式下,它匹配一个或多个字符,这种匹配称为stem
。
- 在"替换"模式下,它使用匹配到的stem
并在字符串中进行替换。
- %
最常用于规则定义和某些特定函数中
C
程序:基于n.c
生成n.o
,使用命令$(CC) -c $(CPPFLAGS) $(CFLAGS) $^ -o $@
。C++
程序,基于n.cc
或n.cpp
生成n.o
,使用命令$(CXX) -c $(CPPFLAGS) $(CXXFLAGS) $^ -o $@
。n.o
生成n
,使用命令$(CC) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@
。CC
:编译C程序的程序;默认ccCXX
:编译C++程序的程序;默认g++CFLAGS
: 给 C 编译器的额外标志CXXFLAGS
: 提供给 C++ 编译器的额外标志CPPFLAGS
: 提供给 C 预处理器的额外标志LDFLAGS
: 在编译器应该调用链接器时提供给编译器的额外标志后续有时间会添加关于更多规则以及变量与函数,还有命令的相关内容。
]]># 清华镜像源
https://pypi.tuna.tsinghua.edu.cn/simple
# 豆瓣镜像源
http://pypi.douban.com/simple/
# 阿里云镜像源
http://mirrors.aliyun.com/pypi/simple/
# 中国科学技术大学镜像源
http://pypi.mirrors.ustc.edu.cn/simple/
使用方法为pip install package_name -i 镜像源
如果Ubuntu原始速度还可以的话建议不要换源,常用的有阿里云镜像、清华镜像和搜狐镜像,这里是我使用的镜像,北京外国语大学镜像。
deb https://mirrors.bfsu.edu.cn/ubuntu/ focal main restricted universe multiverse
# deb-src https://mirrors.bfsu.edu.cn/ubuntu/ focal main restricted universe multiverse
deb https://mirrors.bfsu.edu.cn/ubuntu/ focal-updates main restricted universe multiverse
# deb-src https://mirrors.bfsu.edu.cn/ubuntu/ focal-updates main restricted universe multiverse
deb https://mirrors.bfsu.edu.cn/ubuntu/ focal-backports main restricted universe multiverse
# deb-src https://mirrors.bfsu.edu.cn/ubuntu/ focal-backports main restricted universe multiverse
deb https://mirrors.bfsu.edu.cn/ubuntu/ focal-security main restricted universe multiverse
# deb-src https://mirrors.bfsu.edu.cn/ubuntu/ focal-security main restricted universe multiverse
# 预发布软件源,不建议启用
# deb https://mirrors.bfsu.edu.cn/ubuntu/ focal-proposed main restricted universe multiverse
# deb-src https://mirrors.bfsu.edu.cn/ubuntu/ focal-proposed main restricted universe multiverse
Ubuntu换源的具体操作为
cd /etc/apt
# 将原来的源文件复制一份做备份
sudo cp sources.list sources.list.cpk
sudo vim sources.list
# 删除全部内容换成上面的镜像网址(具体操作为Esc->:%d->Enter->i进入插入模式->粘贴内容->Esc->:wq)
# 保存退出后更新
sudo apt-get update
sudo apt-get upgrade
借助VcXsvr工具,具体使用方法可以参考网上的教程,如https://zhuanlan.zhihu.com/p/128507562。
]]>vim删除文件中的所有内容: ESC确保退出编辑模式,按下":"切换到命令模式然后输入%d并执行
vim删除单行内容:将光标移动到需要删除的行,ESC确保退出编辑模式,按两次d键。
vim删除多行内容:将光标移动到需要删除的第一行,ESC确保退出编辑模式。在dd命令前面加上要删除的行数。例如,如果要删除第4行以下的3行,请先移动至第四行,再按下3dd。
vim撤销&恢复:u是撤销刚才做的动作,ctrl+r 是恢复刚才撤销的动作。
参考博客https://www.cnblogs.com/liaojie970/p/6746230.html
命令格式:cp [-adfilprsu] 源文件(source) 目标文件(destination)
cp [option] source1 source2 source3 ... directory
参数说明:
-a:是指archive的意思,也说是指复制所有的目录
-d:若源文件为连接文件(link file),则复制连接文件属性而非文件本身
-f:强制(force),若有重复或其它疑问时,不会询问用户,而强制复制
-i:若目标文件(destination)已存在,在覆盖时会先询问是否真的操作
-l:建立硬连接(hard link)的连接文件,而非复制文件本身
-p:与文件的属性一起复制,而非使用默认属性
-r:递归复制,用于目录的复制操作
-s:复制成符号连接文件(symbolic link),即“快捷方式”文件
-u:若目标文件比源文件旧,更新目标文件
如将/test1目录下的file1复制到/test3目录,并将文件名改为file2,可输入以下命令:
cp /test1/file1 /test3/file2
命令格式:mv [-fiv] source destination
参数说明:
-f:force,强制直接移动而不询问
-i:若目标文件(destination)已经存在,就会询问是否覆盖
-u:若目标文件已经存在,且源文件比较新,才会更新
如将/test1目录下的file1移动到/test3 目录,并将文件名改为file2,可输入以下命令:
mv /test1/file1 /test3/file2
命令格式:rm [fir] 文件或目录
参数说明:
-f:强制删除
-i:交互模式,在删除前询问用户是否操作
-r:递归删除,常用在目录的删除
如删除/test目录下的file1文件,可以输入以下命令:
rm -i /test/file1
PowerShell创建新文件夹和新文件
mkdir 文件夹名称
new-item 文件名称.文件格式 -type file
]]>sudo apt update
sudo apt install apt-transport-https ca-certificates curl gnupg-agent software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io
# 列出所有可用的docker版本
sudo apt update
apt list -a docker-ce
# 安装docker
sudo apt install docker-ce=<VERSION> docker-ce-cli=<VERSION> containerd.io
sudo systemctl status docker
docker container run hello-world
Golang安装:参考腾讯云博客
1. 下载程序包
浏览Go官方下载页面,下载程序包并解压到/usr/local
目录下。
2. 添加环境变量
将Go目录添加到$PATH
环境变量下,可以添加到/etc/profile
或者$HOME/.profile
下,
export PATH=$PATH:/usr/local/go/bin
# 后续Go环境使用
export GOPATH=/home/ubuntu/go
export GOBIN=$GOPATH/bin
export GOPROXY=https://goproxy.io,direct
前者代表系统范围内安装,后者为当前用户安装,保存文件并激活
source /etc/profile
验证Go版本
go version
]]>