使用 port knocking 隐藏 SSH daemon 端口

暴露在互联网上的服务器非常容易被恶意程序进行端口扫描,以前也整理过一篇 VPS 安全设置 的文章,但都是一些比较基础的设置,能够绕过一些简单的端口扫描,但是并不能从根本上解决端口扫描的问题。

Port knocking 通过防火墙的帮助能够实现,只有你按照特定方式请求后才开放端口,增加了一层保护。主要防止恶意攻击者通过端口扫描来对机器进行攻击。

这篇文章就通过 knockd 的使用来介绍一下 Port knocking 。

Note: 本文只演示 IPV4 下的配置。

Port knocking

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

安装 Knockd

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_timeouttcpflags 分别用来做校验,请求包需要满足这两个条件,而 command 则是指定了满足条件后的动作。

在该例子中就是打开 SSH 默认的 22 端口。但是如果仔细看就会发现 iptables 中使用的是 -A 参数,会将这一条规则放到最后,那如果在 DROP 后面就不会有作用了,所以需要改成

command = /sbin/iptables -I INPUT 1 -s %IP% -p tcp --dport 22 -j ACCEPT

使用 -I 来插入到规则列表开头。

启用 Knockd

修改配置

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

reference


2019-07-19 knocking , ssh , vps , security

威联通折腾篇十四:迁移系统盘

当时安装系统的时候就直接插入了一块硬盘,安装在了第一块机械硬盘上面,虽然读写也没有遇到什么瓶颈,但是记录以做备份,可以用于将系统迁移到 SSD 上。

下面的方法未经验证,慎用。应用可以迁移,但是一些配置可能无法成功备份到另一块硬盘中。

  1. Create a small new Volume using some of the unused space in Storage Pool 1
  2. Back up my System Setting (ControlPanel>Backup/Restore>Back up System Settings)
  3. Shut down NAS (systems that access the iSCSI storage will also be shut down)
  4. Pull the 1TB drive (System Volume)
  5. Put a 2 TB drive in both bay 3 and 4 (matching the model in bay 1 and 2)
  6. Power the NAS back on
  7. Restore from the backup taken in step 2 (point to the new volume created in step 1 for the System Volume)
  8. Add the new disks to the RAID Group in Storage Pool 1 and Migrate to RAID 5 then 6.

我把我的 NAS 系统盘搞砸了

2020 年 1 月 10 号更新。

qnap dashboard disk failed

我的 NAS 没有组 RAID,因为磁盘也不是一次买的,所以是一次次增加到 NAS 中的,每块磁盘都是独立的 Volumn,在这种情况下系统盘损坏也就无法从其他盘恢复了。

qnap storage snapshots

上面的方法在硬盘还能正常运行的时候可以试试,但是从昨天起发现 NAS 系统盘突然只读,而无法写入,经过一系列诊断初步感觉是硬盘故障了,所以赶紧备份系统盘数据,也幸亏 NAS 在要磁盘完全挂之前还保存了读的功能,所以赶紧 ssh 到后台 rsync 系统盘数据到其他盘。

qnap disk health check

备份完普通文件剩下的配置和应用就比较难备份了,前前后后花了很长时间去尝试和配置这些应用,但是最糟糕的可能就是要从头开始了。这么多应用和配置都要从头配置,想想就头疼。

qnap all applications

在网上经过一番调查,QNAP QTS 系统和数据是分开存储的,所以即使系统盘挂掉,其他磁盘不用其他操作,可以更换新的系统盘,然后安装新系统,然后在新系统中恢复原来的数据。

先在”存储与快照总管”,安全卸载存储区。所有的盘都卸载掉,然后关机。换一个用来安装系统和应用的硬盘,重装系统。装好之后插上旧数据硬盘。在存储与快照总管那里,扫描闲置磁盘。然后原来的数据就恢复了。

安全卸载磁盘:

  • 主菜单,存储与快照总管,存储,存储 / 快照
  • 选择存储池
  • 管理,打开存储池管理窗口
  • 删除 > 安全卸载存储池 (注意这里千万要选择卸载存储池,而不是移除存储池)
  • 单击“是”,存储池状态更改为安全卸载中…。QTS 卸载完存储池后,将从存储与快照总管中消失。
  • 从 NAS 中移除包含存储池的硬盘

恢复磁盘:

  • 在第二个 NAS 上安装硬盘。
  • 在第二个 NAS 上,转到主菜单 > 存储与快照总管 > 存储 > 磁盘 /VJBOD
  • 选择恢复 > 扫描闲置磁盘。 此时会出现确认消息。 单击确定。
  • QTS 将扫描磁盘并检测存储池。
  • 单击应用。

该方法也适用于将存储池移动到其他 NAS 中。1

storage pool

对于我这种只用了一块磁盘一个静态卷来安装系统的,恢复起来就比较麻烦了,大思路就是:

  • 通过 QTS 自带的备份来备份系统设置
  • 备份该盘中所有数据
  • 备份 /share/CACHEDEV1_DATA/.qpkg/ 目录下应用数据及配置
  • 再将其他数据磁盘数据卷通过上面的方法移除,然后关机,再将系统磁盘移除
  • 然后插入新的磁盘,在新磁盘上新建卷,安装系统
  • 在新系统中恢复之前备份的设置
  • 重新安装应用,然后从备份中恢复之前备份的应用数据

reference


2019-07-18 qnap , backup , system , nas

Intellij IDEA 支持 jQuery

在 设置中进行如下操作开启 jQuery 支持:

  • First press CTRL + ALT + S and go to settings.
  • Then click from the menu Languages & Frameworks
  • Select Javascript from the section below and select Libraries
  • In the open menu on the right, click on the Download button and select JQuery from the list
  • Download it and apply. It is done.

reference


2019-07-14 intellij , intellij-idea , javascript , jquery

使用 Tampermonkey 调试本地脚本

记录一下使用 Tampermonkey 调试本地脚本。

Tampermonkey 加载开发调试本地 js

首先设置 Tampermonkey 插件的设置

  • Chrome 中打开 chrome://extensions
  • 搜索 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...
})();

reference


2019-07-13 tampermonkey , userscripts , userscript

Linux 下设置编码格式 locales

很多人在程序中会处理 non-ASCII 字符,在日志中,在终端显示中等等。

locales installs

检查 locales 是否安装

dpkg -l locales

如果 locales 之前显示 ii 表示已经安装了,否则

sudo apt install locales

重新配置

dpkg-reconfigure locales

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

Q & A

某些情况下在终端 less /path/to/logfile 会显示 \u 开头的中文编码,这是 unicode 编码,大部分情况下 less 会使用 UTF-8 去查看文件。但是如果文件编码格式非 UTF-8 那么可能会有些问题。

使用 file /path/to/file 来查看文件的编码。

reference


2019-07-12 locale , unix , linux , encoding , utf8

jbpm 流程

BPMN 中可执行的工作流包含一系列不同类型的节点,这些节点可以被用来连接生成有序的工作流。BPMN 2.0 规范定义了三种主要的类型:

  • Events, 用来定义特定事件的发生。可以是起始事件(用来表示工作流的开始),结束事件(用来表示工作流的结束,或者子流程结束),中间事件(表示发生在工作流执行过程中的事件)
  • Avtivities, 定义了在工作流执行过程中需要执行的不同动作。依据不同类型的内容,存在不同类型的 tasks,并且 activities 可以被嵌套
  • Gateways, 用来定义不同路径,根据不同类型的 Gateway,这可能是 parallel.

jBPM6 并没有实现 BPMN 2.0 规范中的所有元素和属性,但是提供了大部分重要的子集。

节点

Events

  • Start Event (None, Conditional, Signal, Message, Timer)
  • End Event (None, Terminate, Error, Escalation, Signal, Message, Compensation)
  • Intermediate Catch Event (Signal, Timer, Conditional, Message)
  • Intermediate Throw Event (None, Signal, Escalation, Message, Compensation)
  • Non-interrupting Boundary Event (Escalation, Signal, Timer, Conditional, Message)
  • Interrupting Boundary Event (Escalation, Error, Signal, Timer, Conditional, Message, Compensation)

Activities

  • Script Task
  • Task
  • Service Task
  • User Task
  • Business Rule Task
  • Manual Task
  • Send Task
  • Receive Task
  • Reusable Sub-Process (Call Activity)
  • Embedded Sub-Process
  • Event Sub-Process
  • Ad-Hoc Sub-Process
  • Data-Object

Gateways

  • Exclusive
  • Inclusive
  • Parallel
  • Event-Based

本文主要内容总结自 jboss jbpm 官方文档的第 8 章内容。

reference


2019-07-12 jbpm , business-process-model

Linux Mint 连接 802.1x EAP wifi network

Linux Mint 在连接 802.1x EAP 网络时,一直无法弹出用户名密码弹窗,导致一直无法连接这些网络。今天查了一下,需要手动进行连接。

打开 Network Manager,选择 Connect to Hidden Network.

  • Wi-Fi security 中选择 WAP & WPA2 Enterprise
  • 在弹出的复杂的对话框中
  • Authentication 选择 Protected EAP(PEAP)
  • 然后输入 Username 和 Password ,证书可选

选择连接即可。

reference


2019-07-11 linux-mint , wifi , wifi-network , eap , wifi-authentication

一键去除网易云音乐广告

前提条件

  • Android 手机
  • root 权限
  • Root Explorer

打开目录

/data/media/0/netease/cloudmusic/

看到 AD 目录,去除写入权限即可。

Root Explorer 如果打开的是 /sdcard 下面的目录可能无法设置权限。

netease ads remove


2019-07-09 netease , music , ads

Spring 自定义 namespace and handlers

自定义 namespaces 可以让用户有一种更方便的方式来定义 Bean。

Spring 提供了一些开箱及用的方式,比如 <mvc:annotation-driven/> 可以参考这篇文章 来查看该配置的作用。

Spring 从 2.0 开始可以支持自定义扩展 XML Schema。

XML Schema-based configuration

在了解自定义 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

实现自己的 XML

  • 创建 XML Schema 文件 xsd
  • 自定义处理器,实现 NamespaceHandler 接口
  • 自定义解析器,实现 BeanDefinitionParser 接口,可多个
  • 注册到 Spring 容器中

官方文档举了一个简单的例子,比如想要在 context 中定义

<myns:dateformat id="dateFormat"
    pattern="yyyy-MM-dd HH:mm"
    lenient="true"/>

这样的代码,那么需要做下面一些事情。

定义 XML

定义如下 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

编写 NamespaceHandler 来处理特定 namespace 下的元素。NamespaceHandler 在这个例子中应该处理好 myns:dateformat 的解析工作。

NamespaceHandler 接口非常简单,有三个方法:

  • init() 初始化 NamespaceHandler
  • BeanDefinition 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

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));
        }
    }
}

Registering the handler and the schema

所有的编程都已经结束,剩下来的就是如何让 Spring XML 感知到所做的修改,将自定义内容注册到 Spring 中。

要实现这一点,需要考虑两点

  • 注册自定义的 NamespaceHandler
  • 注册 XSD 文件

在 resources 下创建 META-INF 目录,并创建如下两个文件

  • spring.handlers 包含对应的 XML Schema URI 到 Handler 类的映射
  • spring.schemas 包含 xsd 文件路径

META-INF/spring.handlers

定义 XML Schema 到 Handler 类映射,这个例子中

http\://www.mycompany.com/schema/myns=org.springframework.samples.xml.MyNamespaceHandler

META-INF/spring.schemas

定义 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 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: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

reference


2019-07-05 spring , java , java-web , spring-mvc

shell script idiom

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).


2019-07-03 bash , shell , stdout , stderr , pipeline

电子书

最近文章

  • 我的笔记法(借助 Zettelkasten 和 Obsidian) 在上次总结了2020读书笔记 之后,Tai 问我如何通过 Zettelkasten 和 Obsidian 来实践我的笔记。这里就也正好总结一下我自己的方法,希望可以在分享过程,或者和大家的讨论中来获取更多有效率方法。
  • 迟到的「给编程初学者的一封信」 这些天翻箱倒柜,翻出来一些大学时候的文档,其中一篇是当时上外教课时打印给我们的材料,虽然可以看得到当时也在上面做过笔记,但现在已经完全不记得有这样一份文档的存在了。但回过头再看文档的内容,每一句话都是非常珍贵的建议,是一份那个时期完全需要读一下的材料,但当时却并没有好好珍惜。这份文档的名字叫做「An open letter to those who want to start programming」,如果记得没错的话这份文档交到我手上的时候,应该就是我刚去学习如何编程的时候。如果当时就能够理解这一份文档内容的话,这些年来我肯定能少走不少的弯路。
  • 2020 读书记录 2020 年注定是会在历史上留下不平凡的一年,年初的疫情到年底问题依然还在,而且在不确定疫苗的情况,在加上英国的病毒变异情况,不清楚到什么时候才是结束。再回头看今年的大事小事,从年初的李文亮事件,到年末的蛋壳,以及阿里被禁止上A股,有些事情发生地太突然,来不及思考,但只有思考,不仅是在事前的还是事后的思考都有其价值。
  • 图片管理工具 Eagle 远在移动互联网还没有那么发达的今天,Google 曾经收购过一家图片管理与分享的网站叫做 Picasa,Picasa 同时提供了一个跨平台的照片管理工具 Picasa Desktop,用这个工具不仅可以非常方便的管理本地的图片文件,也可以非常方便的分享到 Picasa Web 上,然而随着移动互联网的到来,以及 Google 的转型,Picasa 的服务在 Google 变得没有那么重要,随即在 2016 年停止了服务,我的图片管理也被迫迁移到了 Google Photos。然而一切都开始变得不方便,Google Photos 内自动备份的照片,相册开始无法管理,并且 Google 停止了桌面版的开发,同样使得在桌面上管理图片变得困难,这些年来尝试了 Lightroom,TagSpaces 等等工具,都没有找到特别舒心的。
  • 我的 Obsidian 笔记跨设备同步方案 自从半年前发现了 Obsidian 这款笔记软件,我就开始大量的使用该应用做笔记,有人说过:「工具是开发者方法论的固化」。这么多年了我一直有一种工具控的倾向,往往同一个需求会对比可能的所有方案,最后再决定一个,但是近些年来我越来越倾向于「简单就是好」,并且数据要由自己掌控的「工具选择逻辑」。