暴露在互联网上的服务器非常容易被恶意程序进行端口扫描,以前也整理过一篇 VPS 安全设置 的文章,但都是一些比较基础的设置,能够绕过一些简单的端口扫描,但是并不能从根本上解决端口扫描的问题。
Port knocking 通过防火墙的帮助能够实现,只有你按照特定方式请求后才开放端口,增加了一层保护。主要防止恶意攻击者通过端口扫描来对机器进行攻击。
这篇文章就通过 knockd
的使用来介绍一下 Port knocking 。
Note: 本文只演示 IPV4 下的配置。
Port knocking 类似于
在配置 knockd 之前,首先需要了解 iptables 的基本使用。
允许本地回环地址,允许内部的网络访问
sudo iptables -A INPUT -i lo -j ACCEPT
允许当前活跃的连接继续保持
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED, RELATED -j ACCEPT
允许本地 80 端口
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
除此之外组织所有的访问 (需要特别注意,输入这行命令前一定要非常清楚你即将做的事情)
sudo iptables -A INPUT -j DROP
查看
sudo iptables -S
持久化 iptables 设定
sudo apt-get install iptables-persistent
sudo service iptables-persistent start
sudo apt install knockd
管理员打开
sudo vim /etc/knockd.conf
编辑如下内容
[options]
UseSyslog
[openSSH]
sequence = 7000,8000,9000
seq_timeout = 5
command = /sbin/iptables -A INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
tcpflags = syn
[closeSSH]
sequence = 9000,8000,7000
seq_timeout = 5
command = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
tcpflags = syn
解释
在最上方 options 中可以看到 UseSyslog,表示的是 knockd 会将日志记录到系统的日志中,路径是 /var/log/messages
如果想要指定一个可以使用
LogFile = /path/to/log/file
在下方是两块配置,名字可以是任意,例子中的两块配置分别是打开 SSH 默认端口,和关闭 SSH 端口。
敲门的顺序由 sequence
来指定。当然正常配置时请换用尽量随机的端口。
seq_timeout
和 tcpflags
分别用来做校验,请求包需要满足这两个条件,而 command
则是指定了满足条件后的动作。
在该例子中就是打开 SSH 默认的 22 端口。但是如果仔细看就会发现 iptables 中使用的是 -A
参数,会将这一条规则放到最后,那如果在 DROP 后面就不会有作用了,所以需要改成
command = /sbin/iptables -I INPUT 1 -s %IP% -p tcp --dport 22 -j ACCEPT
使用 -I
来插入到规则列表开头。
修改配置
sudo vi /etc/default/knockd
修改内容
START_KNOCKD = 1
启动服务
sudo service knockd start
此时去测试
ssh root@server_ip_address
就会发现 22 端口被拒绝了。
然后在本地计算机上
for x in 7000 8000 9000; do nmap -Pn --host_timeout 201 --max-retries 0 -p $x server_ip_address; done
然后在 ssh 连过去。
完事之后
for x in 9000 8000 7000; do nmap -Pn --host_timeout 201 --max-retries 0 -p $x server_ip_address; done
当然你可以在客户端直接使用 knockd
knock server_ip_address 7000 8000 9000
结束时
knock server_ip_address 9000 8000 7000
[options]
UseSyslog
[SSH]
sequence = 5438,3428,3280,4479
tcpflags = syn
seq_timeout = 15
start_command = /sbin/iptables -I INPUT 1 -s %IP% -p tcp --dport 22 -j ACCEPT
cmd_timeout = 10
stop_command = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
当时安装系统的时候就直接插入了一块硬盘,安装在了第一块机械硬盘上面,虽然读写也没有遇到什么瓶颈,但是记录以做备份,可以用于将系统迁移到 SSD 上。
下面的方法未经验证,慎用。应用可以迁移,但是一些配置可能无法成功备份到另一块硬盘中。
2020 年 1 月 10 号更新。
我的 NAS 没有组 RAID,因为磁盘也不是一次买的,所以是一次次增加到 NAS 中的,每块磁盘都是独立的 Volumn,在这种情况下系统盘损坏也就无法从其他盘恢复了。
上面的方法在硬盘还能正常运行的时候可以试试,但是从昨天起发现 NAS 系统盘突然只读,而无法写入,经过一系列诊断初步感觉是硬盘故障了,所以赶紧备份系统盘数据,也幸亏 NAS 在要磁盘完全挂之前还保存了读的功能,所以赶紧 ssh 到后台 rsync 系统盘数据到其他盘。
备份完普通文件剩下的配置和应用就比较难备份了,前前后后花了很长时间去尝试和配置这些应用,但是最糟糕的可能就是要从头开始了。这么多应用和配置都要从头配置,想想就头疼。
在网上经过一番调查,QNAP QTS 系统和数据是分开存储的,所以即使系统盘挂掉,其他磁盘不用其他操作,可以更换新的系统盘,然后安装新系统,然后在新系统中恢复原来的数据。
先在”存储与快照总管”,安全卸载存储区。所有的盘都卸载掉,然后关机。换一个用来安装系统和应用的硬盘,重装系统。装好之后插上旧数据硬盘。在存储与快照总管那里,扫描闲置磁盘。然后原来的数据就恢复了。
安全卸载磁盘:
恢复磁盘:
该方法也适用于将存储池移动到其他 NAS 中。1
对于我这种只用了一块磁盘一个静态卷来安装系统的,恢复起来就比较麻烦了,大思路就是:
/share/CACHEDEV1_DATA/.qpkg/
目录下应用数据及配置在 设置中进行如下操作开启 jQuery 支持:
记录一下使用 Tampermonkey 调试本地脚本。
首先设置 Tampermonkey 插件的设置
Allow access to file URLs
然后在文件中使用 @require
引入外部文件。
// ==UserScript==
// @name Debug Userscript
// @namespace https://github.com/einverne/userscripts
// @version 0.1
// @description This is a debug script to load userscripts from local file system. NOTICE, you need to turn on Allow access to file URLs to @require local file https://www.tampermonkey.net/documentation.php
// @author Ein Verne
// @match http*://*
// @include http://*
// @include https://*
// @include *
// @grant GM_xmlhttpRequest
// @grant GM_addStyle
// @grant GM_getResourceText
// @require https://unpkg.com/dexie@latest/dist/dexie.js
// @require file:///home/einverne/Git/userscripts/douban_export/douban_exporter.user.js
// ==/UserScript==
(function () {
'use strict';
console.log("debug script start here");
// Your code here...
})();
很多人在程序中会处理 non-ASCII 字符,在日志中,在终端显示中等等。
检查 locales 是否安装
dpkg -l locales
如果 locales 之前显示 ii
表示已经安装了,否则
sudo apt install locales
重新配置
dpkg-reconfigure locales
使用命令 locale
查看配置
locale
LANG=en_US.UTF-8
LANGUAGE=en_US:en
LC_CTYPE=en_US.UTF-8
LC_NUMERIC=zh_CN.UTF-8
LC_TIME=zh_CN.UTF-8
LC_COLLATE="en_US.UTF-8"
LC_MONETARY=zh_CN.UTF-8
LC_MESSAGES="en_US.UTF-8"
LC_PAPER=zh_CN.UTF-8
LC_NAME=zh_CN.UTF-8
LC_ADDRESS=zh_CN.UTF-8
LC_TELEPHONE=zh_CN.UTF-8
LC_MEASUREMENT=zh_CN.UTF-8
LC_IDENTIFICATION=zh_CN.UTF-8
LC_ALL=
在 .bashrc
中放入如下设置:
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8
某些情况下在终端 less /path/to/logfile
会显示 \u
开头的中文编码,这是 unicode 编码,大部分情况下 less 会使用 UTF-8 去查看文件。但是如果文件编码格式非 UTF-8 那么可能会有些问题。
使用 file /path/to/file
来查看文件的编码。
BPMN 中可执行的工作流包含一系列不同类型的节点,这些节点可以被用来连接生成有序的工作流。BPMN 2.0 规范定义了三种主要的类型:
jBPM6 并没有实现 BPMN 2.0 规范中的所有元素和属性,但是提供了大部分重要的子集。
Events
Activities
Gateways
本文主要内容总结自 jboss jbpm 官方文档的第 8 章内容。
Linux Mint 在连接 802.1x EAP 网络时,一直无法弹出用户名密码弹窗,导致一直无法连接这些网络。今天查了一下,需要手动进行连接。
打开 Network Manager,选择 Connect to Hidden Network.
选择连接即可。
前提条件
打开目录
/data/media/0/netease/cloudmusic/
看到 AD 目录,去除写入权限即可。
Root Explorer 如果打开的是 /sdcard
下面的目录可能无法设置权限。
自定义 namespaces 可以让用户有一种更方便的方式来定义 Bean。
Spring 提供了一些开箱及用的方式,比如 <mvc:annotation-driven/>
可以参考这篇文章 来查看该配置的作用。
Spring 从 2.0 开始可以支持自定义扩展 XML Schema。
在了解自定义 XML Schema 之前首先要熟悉一下 Spring 的 XML Schema 配置。最简单的配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- bean definitions here -->
</beans>
如果要引入 util schema 需要这样修改
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> <!-- bean definitions here -->
</beans>
实现自己的 XML
xsd
NamespaceHandler
接口BeanDefinitionParser
接口,可多个官方文档举了一个简单的例子,比如想要在 context 中定义
<myns:dateformat id="dateFormat"
pattern="yyyy-MM-dd HH:mm"
lenient="true"/>
这样的代码,那么需要做下面一些事情。
定义如下 dateformat.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.mycompany.com/schema/myns"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:beans="http://www.springframework.org/schema/beans"
targetNamespace="http://www.mycompany.com/schema/myns"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xsd:import namespace="http://www.springframework.org/schema/beans"/>
<xsd:element name="dateformat">
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="beans:identifiedType">
<xsd:attribute name="lenient" type="xsd:boolean"/>
<xsd:attribute name="pattern" type="xsd:string" use="required"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
</xsd:schema>
编写 NamespaceHandler 来处理特定 namespace 下的元素。NamespaceHandler 在这个例子中应该处理好 myns:dateformat
的解析工作。
NamespaceHandler 接口非常简单,有三个方法:
init()
初始化 NamespaceHandlerBeanDefinition parse(Element, ParserContext)
会被 Spring 在顶层元素处理时调用BeanDefinitionHolder decorate(Node, BeanDefinitionHolder, ParserContext)
处理属性或者嵌套元素时使用比如:
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
public class MyNamespaceHandler extends NamespaceHandlerSupport {
public void init() {
registerBeanDefinitionParser("dateformat", new SimpleDateFormatBeanDefinitionParser());
}
}
BeanDefinitionParser 会被 NamespaceHandler 内部使用,当解析特定的元素时会对应不同的解析器。比如这个例子中 dateformat 使用了 SimpleDateFormatBeanDefinitionParser 。
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
import java.text.SimpleDateFormat;
public class SimpleDateFormatBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { 1
protected Class getBeanClass(Element element) {
return SimpleDateFormat.class; 2
}
protected void doParse(Element element, BeanDefinitionBuilder bean) {
// this will never be null since the schema explicitly requires that a value be supplied
String pattern = element.getAttribute("pattern");
bean.addConstructorArg(pattern);
// this however is an optional property
String lenient = element.getAttribute("lenient");
if (StringUtils.hasText(lenient)) {
bean.addPropertyValue("lenient", Boolean.valueOf(lenient));
}
}
}
所有的编程都已经结束,剩下来的就是如何让 Spring XML 感知到所做的修改,将自定义内容注册到 Spring 中。
要实现这一点,需要考虑两点
在 resources 下创建 META-INF 目录,并创建如下两个文件
定义 XML Schema 到 Handler 类映射,这个例子中
http\://www.mycompany.com/schema/myns=org.springframework.samples.xml.MyNamespaceHandler
定义 XML Schema 到自定义 XSD 文件映射
http\://www.mycompany.com/schema/myns/myns.xsd=org/springframework/samples/xml/myns.xsd
当做完这些后,那么上面定义的内容就和如下的定义可以实现完全相同的功能。
<bean id="dateFormat" class="java.text.SimpleDateFormat">
<constructor-arg value="yyyy-HH-dd HH:mm"/>
<property name="lenient" value="true"/>
</bean>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:myns="http://www.mycompany.com/schema/myns"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.mycompany.com/schema/myns http://www.mycompany.com/schema/myns/myns.xsd">
<!-- as a top-level bean -->
<myns:dateformat id="defaultDateFormat" pattern="yyyy-MM-dd HH:mm" lenient="true"/>
<bean id="jobDetailTemplate" abstract="true">
<property name="dateFormat">
<!-- as an inner bean -->
<myns:dateformat pattern="HH:mm MM-dd-yyyy"/>
</property>
</bean>
</beans>
代码见 https://github.com/einverne/thrift-swift-demo/tree/master/spring-mvc-demo
Bash 命令中一些常见的习惯。
> file redirects stdout to file
1> file redirects **stdout** to file
2> file redirects **stderr** to file
&> file redirects stdout and stderr to file
/dev/null
is the null device it takes any input you want and throws it away. It can be used to suppress any output.
Using 2>&1
will redirect stderr to whatever value is set to stdout (and 1>&2 will do the opposite).