Linux 网络配置

昨天升级 Ubuntu ,不知为何将网卡配置覆盖了,导致一时间无法上网,然后看了一些网络配置的文章,感觉自己需要补习一下相关知识,所以有了这篇文章。

下面就按照命令分别展开吧

ifconfig

这个命令是查看本地网络端口最常见的命令了,略

设置网卡及 IP

/etc/network/interfaces 文件中保存着本地网络网卡的相关配置

配置 DHCP 自动获取 IP

auto eth0
iface eth0 inet dhcp

假如要配置静态 IP

auto eth0               # 要设置的网卡
iface eth0 inet static  # 设置静态 IP;如果是使用自动 IP 用 dhcp,后面的不用设置,一般少用
address xxx.xxx.xxx.xxx  # IP 地址
netmask xxx.xxx.xxx.xxx  # 子网掩码
gateway xxx.xxx.xxx.xxx  # 网关

修改保存,之后使用 sudo /etc/init.d/networking restart 来使其生效。

设置 DNS

DNS 相关的配置在 /etc/resolv.conf 文件中。如果希望永久生效可以修改 /etc/resolvconf/resolv.conf.d/base 文件中

nameserver 8.8.8.8   # 希望修改成的 DNS
nameserver 8.8.4.4

然后使用 sudo /etc/init.d/resolvconf restart 来使得 DNS 生效。

resolv.conf 配置

/etc/resolv.conf 配置文件是客户端 DNS 配置,一般在该文件中配置了 DNS 服务器的 IP 地址和域名。 配置的参数格式非常简单,由关键字开头,后面接着是空格分隔的几个参数。resolv.conf 配置中主要的关键字有四个:

  • domain
  • nameserver
  • search

一个基本的配置:

domain some-example.com
nameserver 8.8.8.8
nameserver 8.8.4.4
search exmaple.com example1.com

解释:

  • domain: 指的是本地网络的名称,如果查询域名时没有包含点号,那么会自动加上网域的名称为结尾,再发送给 DNS 服务器
  • nameserver: 指定客户端进行域名解析的时候要用到的域名服务器 IP 地址,因此可以指定多个地址,客户端会按照次序进行查询请求
  • search: 非必填,举个例子来说明这个选项,当 search 设定为 example.com 时,在 DNS 解析的时候,无法对输入解析的时候,比如查询 blog,DNS 客户端会使用 search 指定的值加上需要查询的名称,即 blog.example.com 来进行解析,解析失败的时候会依次往后 blog.example1.com 查询

当设定了 domain 时,配置的地址会自动成为 search 的第一个搜索域名。

当去 ping 一个域名时,如果访问的域名无法被 DNS 解析,resolver 会将该域名加上 search 参数后面配置的内容,重新请求 DNS,知道被正确解析或尝试完 search 指定的所有列表为止。

reference


2018-08-18 linux , network , command , ifconfig

AES 加密算法

高级加密标准 (AES,Advanced Encryption Standard) 为最常见的对称加密算法。对称加密算法也就是加密和解密用相同的密钥。

将明文 P 使用加密密钥 K 加密成密文 C ,传输,然后在使用 AES 解密函数使用相同的密钥 K 解密,产生明文 P

设 AES 加密函数为 E,则 C = E(K, P), 其中 P 为明文,K 为密钥,C 为密文。也就是说,把明文 P 和密钥 K 作为加密函数的参数输入,则加密函数 E 会输出密文 C。

加密和解密用到的密钥是相同的,这种加密方式加密速度非常快,适合经常发送数据的场合。缺点是密钥的传输比较麻烦。

基本原理

AES 为分组密码,分组密码也就是把明文分成一组一组的,每组长度相等,每次加密一组数据,直到加密完整个明文。


2018-08-15 aes , encryption , 对称加密

使用 alembic 迁移数据库结构

Alembic 是一个处理数据库更改的工具,它利用 SQLAlchemy 来实现形成迁移。因为 SQLAlchemy 只会在我们使用时根据 metadata create_all 方法来创建缺少的表 ,它不会根据我们对代码的修改而更新数据库表中的列。它也不会自动帮助我们删除表。 Alembic 提供了一种更新 / 删除表,更改列名和添加新约束的方法。因为 Alembic 使用 SQLAlchemy 执行迁移,它们可用于各种后端数据库。

安装

pip install alembic

使用

初始化,使用如下命令会创建环境到 migrations 文件夹下,通常情况下使用 migrations 文件夹来存储 alembic 环境,如果想使用别的名字,相应替换为别的名字即可。注意下面命令中的 migrations 将会是存储所有迁移脚本的目录名字

alembic init migrations

初始化过程会创建迁移环境和 alembic.ini 文件。创建成功后可以看到如下结构:

alembic
├── README
├── env.py
├── script.py.mako
└── versions
alembic.ini

在这个环境中可以找到 env.pyscript.py.mako 模板还有 versions 文件夹。versions/ 目录会存储之后的所有迁移脚本。 env.py 文件用来定义和实例化 SQLAlchemy 引擎,连接引擎并进行事务,保证当 Alembic 执行命令时被合理的调用。 script.py.mako 模板在创建迁移时被使用,他定义了迁移的基本模板。

配置

init 生成之后需要修改 env.py 如下的两个配置,才能生效。改变 sqlalchemy.url 值,配置数据库连接。

sqlalchemy.url = driver://user:pass@localhost/dbname

为了让 Alembic 追踪到数据模型的变化,需要将 target_metadata 赋值为数据库的 metadata

from flask import current_app
config.set_main_option('sqlalchemy.url',
                       current_app.config.get('SQLALCHEMY_DATABASE_URI'))
target_metadata = current_app.extensions['migrate'].db.metadata

自动创建版本

使用alembic revision -m "comment" 来创建数据库版本。命令会产生一个数据库迁移脚本。

更新数据库

升级数据库使用 alembic upgrade,降级使用 alembic downgrade,更新到最新版则使用 alembic upgrade head

查看数据库就会发现 alembic 会自动产生一个 alembic_version 的表,只有一个字段和值 version_num,记录当前数据库版本。

reference

  • 《Essential SQLAlchemy 2nd Edition 2015》

2018-08-13 python , mysql , sql , migration , db , database , alembic , flask , sqlalchemy

应用消息推送解决方案 MiPush

Android 中实现消息推送的主流方案有下面几种。

方案 原理 优点 缺点
Cloud to Device Messaging,云端推送,是 Android 系统级别的消息推送服务(Google 出品) Push 简单的、轻量级的机制,允许服务器可以通知移动应用程序直接与服务器进行通信,以便于从服务器获取应用程序更新和用户数据 依赖于 Google 官方提供的 C2DM 服务器,需要用户手机安装 Google 服务
轮询 基于 Pull 方式 实时性好 成本大,需要自己实现与服务器之间的通信 ; 到达率不确定,考虑轮询的频率:太低可能导致消息的延迟;太高,更费资源
SMS 信令推送 Push 完全的实时操作 成本高
第三方平台 Push 小米推送、华为推送 友盟推送、极光推送、云巴(基于 MQTT) 阿里云移动推送、腾讯信鸽推送、百度云推送 成本低,抵达率高 安全性低,服务会被杀

2018-08-10 mipush , android , ios , push , notification

每天学习一个命令:iotop 查看 Linux 下每个进程 IO 占用

iotop 是一个用来监控磁盘 I/O 的类似 top 的工具,Linux 下 IO 统计工具,比如 iostat, nmon 等只能统计到每个设备的读写情况,如果想要知道哪一个进程占用比较高的 IO 就要使用 iotop。 iotop 使用 Python 语言编写,要求 Python >= 2.5,Linux Kernel >= 2.6.20. (使用 python -Vuname -r 来查看)

使用这个命令最主要的一个原因就是快速找到 IO 占用比较高的程序,以便进行排查。

安装

sudo apt install iotop
sudo yum install iotop

install from source

wget http://guichaz.free.fr/iotop/files/iotop-0.6.tar.bz2
tar -xjvf iotop-0.6.tar.bz2
cd iotop-0.6/
./setup.py install

使用

直接使用,可以查看到对应进程的 IO 磁盘读写信息

iotop

只显示有实际 I/O 的进程或者线程

iotop -o

只显示进程的 I/O 数据:

iotop -P

显示某一个 PID 的 IO:

iotop -p PID

显示某一个用户的 I/O:

iotop -u USER

显示累计 IO 数据:

iotop -a

调整刷新时间为 10 秒:

iotop -d 10

快捷键

直接启动 iotop 会进入交互模式,使用如下快捷键可以控制显示。

  • 左右箭头用来改变排序,默认按照 IO 排序,可以切换为读或者写排序等等。
  • 交互式快捷键,a 用来切换累积使用量和 IO 读写速率。
  • r 改变排序顺序
  • o 只显示有 IO 输出的进程
  • q 退出

reference


2018-08-07 io , iotop , top , linux , htop

InfluxDB 使用

InfluxDB 数据库是用 Go 语言实现的一个开源分布式时序、事件和指标数据库。InfluxDB 提供类 SQL 语法。

需要注意的是 InfluxDB 单节点是免费的,但是集群版是要收费的。

安装

sudo apt install influxdb

数据库设计

正因为 InfluxDB 是一个时序数据库,在实际使用的时候有些概念需要提前知道。InfluxDB 数据库中的每一个数据都有一列 time 保存时间戳 (RFC3339 形式显示)。

time butterflies honeybees location scientist
2015-08-18T00:00:00Z 12 23 1 langstroth
2015-08-18T00:00:00Z 1 30 1 perpetua

以该数据说明,butterflieshoneybeesField keys 用来保存元数据,每一个 key 都对应这 value,下面的数字都是要存储的值。

locationscientistTags,用来存储元数据。location 有两种取值,scientist 也有两种取值。所以组合可以有四种 tag 集合。tag 是可选的。但是推荐给数据加上 tags。和 field 不同,tags 都是索引,这意味着在查询时可以更快。

measurement 可以理解成 tags, fields 和 time 列的容器。measurement 的名字是数据的描述,和关系型数据库中的表可以做对应。在 InfluxDB 中可以创建多个数据库,不同数据库中的数据文件是隔离存放的,存放在磁盘上的不同目录

每一个 measurement 可以属于不同的 retention policies(存储策略),一个 retention policy 描述了 InfluxDB 保存数据多久(DURATION)和多少副本被保存到 cluster 中(REPLICATION)。(Replication factors 不对单节点开放)

在 InfluxDB 中 series 是一组数据的集合,这些数据共享一个 retention policymeasurementtag set。理解 series 的概念才能设计好数据库的 schema

最后 point 是在相同 series 中相同 timestamp 的数据。

InfluxDB Shell

如果需要使用 shell 需要安装

sudo apt install influxdb-client

然后在终端输入

influx

进入,默认的端口是 8086。输入 help 可以查看命令列表。

influx 命令参数

influx 启动命令常用参数

influx -database 'name'  # 数据库
influx -host 'hostname'  # 默认是 localhost
influx -password 'password' # 如果有密码
influx -port 'port'     # 端口

更多参数可以使用 influx --help 查看。

基本操作

查看数据库

show databases

创建和删除数据库

create database test
drop database test

切换使用数据库

use test

表基本操作,查询,插入

显示所有表

show measurements

删除表

drop measurement test_table

插入数据

insert [measurement],[tag],[field],[time]
insert cpu,host=serverA,regin=us value=0.64

解释:

  • 如果 measurement 表不存在,自动创建。
  • hostregion 是 tag
  • value 是 0.64
  • 如果字段存在则会报错

这里还要注意 measurement 和 tag 之间不能有空格,空格后面是真正要存储的值,所以如果要存两个 field,那么就需要

insert temperature,machine=unit42,tpye=assembly external=25,internal=37

注意 type=assembly 和 external 之间的空格。

查询数据

select "host","region","value" from "cpu"
select * from "temperature"

更多的查询可以参考这里

用户管理

查看用户

show users

创建用户

create user "username" with password "password"
create user "username" with password "password" with all privileges

删除用户

drop user "username"

Python Client

安装下面的客户端

pip install influxdb

其实这个 client 也只不过是在 influxdb 提供的 HTTP 接口外封装了一层 API,如果熟悉 HTTP 的接口,可以直接调用 influxdb 提供的接口

curl -sL -I localhost:8086/ping

比如:

创建数据库

import requests
posturl = 'http://localhost:8086/query'
data = {'q': 'create DATABASE mydb'}
response = requests.post(posturl, data=data)

类似于 curl 命令如下:

curl -GET http://localhost:8086/query --data-urlencode "q=CREATE DATABASE mydb"

写数据

import requests
posturl = 'http://localhost:8086/write?db=mydb'
data="cpu_load_short,host=server01,region=us-west value=0.69"
response = requests.post(posturl, data=data)

类似于 curl 命令如下:

curl -i -XPOST 'http://localhost:8086/write?db=mydb' --data-binary 'cpu_load_short,host=server01,region=us-west value=0.64 '

如果使用 python client:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import psutil
import time
from influxdb import InfluxDBClient

host = "localhost"
port = 8086
user = "root"
password = "root"
dbname = "test"


def read_info():
    cpu_time_info = psutil.cpu_percent(1, True)
    data_list = [
        {
            'measurement': 'cpu',
            'tags': {
                'cpu': 'i7'
            },
            'fields': {
                'cpu_info_user': cpu_time_info[0],
                'cpu_info_system': cpu_time_info[1],
                'cpu_info_idle': cpu_time_info[2],
                'cpu_info_interrupt': cpu_time_info[3],
                'cpu_info_dpc': cpu_time_info[4]
            }
        }
    ]
    return data_list


def parse_db(dbs):
    l = []
    for db in dbs:
        l.append(db['name'])
    return l


if __name__ == '__main__':
    client = InfluxDBClient(host, port, user, password)  # 初始化
    dbs = client.get_list_database()
    if dbname not in parse_db(dbs):
        client.create_database(dbname)
    for i in range(20):
        client.write_points(read_info())
        time.sleep(2)

大致是这样,但是这个 client 似乎还有 bug,在使用的时候就发现 HTTP 方法不支持,Method Not Allowed 这样的错误,需要自己手动改一改

reference


2018-08-02 influxdb , database , time-series-db

一周 CP 反编译记录

记录一下反编译一周 CP Android 3.26.0.451 版本,并拿到请求 sign 加密方法的过程。反编译的过程基本上可以划分为几个步骤

  • 将 Android apk 文件反编译得到混淆的 java 代码
  • 在混淆过的代码中找到发起请求的部分
  • 找到请求发起 sign 加密部分加密方式

反编译 Android apk

关于第一步 Android 反编译的教程和工具 可以参考之前的文章

查看混淆的代码

在反编译得到混淆过后的代码之后,这个时候不能盲目的去看,之前可以抓包看下应用内发出去的请求 path,找到关注的 path,比如在这里,找到一个请求的 path,然后沿着这个请求的 path 找到了发起请求的通用方法 prepareRequest(),看到这里就能清晰的看到请求发出去的时候带的几个通用参数 user_idsm_device_id ,然后签名部分 timestampTokensign,最关键的部分就是这里的 sign 的生成方式。

prepare request

然后到这里看到生成 sign 的方法,第一眼看过去就看到了希望 SHA-1 他使用的是这个哈希算法,这个算法也主要就是用于签名校验。

sign sha1

然后可以沿着这个思路去看各个参数的值,比如这里他用到了 RequestData.buildQueryString() 这个方法

request data

大致看一下这个代码大概能猜到是将请求参数的 key value 拼接起来连成字符串返回。这个时候大致思路已经清晰,所以我用 Java 大致实现了一下 sign 生成的代码。

Java 实现

import com.jutils.base.RequestData;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

public class WeekCPTest {
	private static String sha1(String str) throws NoSuchAlgorithmException, UnsupportedEncodingException {
		MessageDigest instance = MessageDigest.getInstance("SHA-1");
		byte[] bytes = str.getBytes("UTF-8");
		instance.update(bytes, 0, bytes.length);
		return O000000o(instance.digest());
	}

	private static String O000000o(byte[] bArr) {
		StringBuilder stringBuilder = new StringBuilder();
		for (byte b : bArr) {
			int i = (b >>> 4) & 15;
			int i2 = 0;
			while (true) {
				char c = (i < 0 || i > 9) ? (char) ((i - 10) + 97) : (char) (i + 48);
				stringBuilder.append(c);
				int i3 = b & 15;
				i = i2 + 1;
				if (i2 >= 1) {
					break;
				}
				i2 = i;
				i = i3;
			}
		}
		return stringBuilder.toString();
	}

	public static String makeSign(Map<String, Object> map, Map<String, Object> map2, String str) {
		Map treeMap = new TreeMap();
		if (!(map == null || map.isEmpty())) {
			treeMap.putAll(map);
		}
		if (!(map2 == null || map2.isEmpty())) {
			treeMap.putAll(map2);
		}
		treeMap.remove("token");
		treeMap.remove("sign");
		try {
			return sha1(RequestData.buildQueryString(treeMap, null, false) + str);
		} catch (Exception e) {
			e.printStackTrace();
			return "";
		}
	}



	public static void main(String[] args) {
		HashMap<String, Object> query = new HashMap<>();
		query.put("d", "a");
		query.put("user_id", "5624198");
		query.put("timestamp", "1533102328");
		query.put("sm_device_id", "201807021511598922958a5fbc8a5de09cb9019d34a48b01c51f66d9435c31");
//		query.put("Token", "1576511275532288_5624198_1559013109_58f16d8bd82677acc30d87542f5504b0");

		query.put("start", "80");
		query.put("last_object_id", "1742");
		query.put("keyword", "");
		query.put("num", "20");
		String s = makeSign(query, new HashMap<>(), "025d25f5a69eb2818b6811ff6edb51b4");
		System.out.println(s);
	}
}

然后使用抓包工具 抓出一个请求,将参数凭借起来使用这个 Test 跑一下,发现是一致的。这个时候再使用 Python 实现一下。

def __sha1(self, str):
    m = hashlib.sha1()
    m.update(str)
    return m.hexdigest()

def __sign(self, params, secret_key, data={}):
    """
    通过请求参数和 secret_key 生成 sign
    :param params: 请求参数
    :param secret_key: 加密 key 和 用户绑定,在登录或者刷新 token 中获取
    :return:
    """
    to_sign = ""
    merged_dict = params.copy()
    merged_dict.update(data)
    if "sign" in merged_dict.keys():
        merged_dict.pop("sign")
    if "token" in merged_dict.keys():
        merged_dict.pop("token")
    for key in sorted(merged_dict.iterkeys()):
        value = merged_dict.get(key)
        logger.info("params %s %s" % (key, value))
        pair = str(key) + "=" + str(merged_dict.get(key))
        if to_sign == '':
            to_sign = to_sign + pair
        else:
            to_sign = to_sign + '&' + pair
    return self.__sha1(to_sign + secret_key)

然后这个时候就发现其实破解一周 CP 的难点不在 sign 而在拿到 secret_key,这个 secret_key 只有在登录和刷新 token 的接口中才会返回。


2018-08-01 android , decompiler , crack , sign

修正关于 HTTP Header 的错误认识

HTTP 请求的 Header 是不区分大小写的!,一直以为 HTTP 请求的请求头是有区分大小的,知道今天调试发现 Spring 将 header 全部处理成小写,然后有人提了 Bug 58464 然后看到 Stackoverflow 上面有人回答。

HTTP/1.1 和 HTTP/2 都是 case-insensitivt 都是不区分大小写的。


2018-07-30 http , header , web , java-web , spring

解决 failed to create bus connection no such file or directory 错误

今天在修改 hostname 使用 sudo hostnamectl set-hostname ds 命令时遇到问题:

Failed to create bus connection: No such file or directory

查了一通之后发现缺少 dbus

sudo apt-get install dbus

安装 dbus 然后再修改即可,使用 hostnamectl 方式来修改 hostname 不需要重启,直接推出登录,然后就可以实现了。

D-Bus 是一种高级的进程间通信机制,它由 freedesktop.org 项目提供,使用 GPL 许可证发行。D-Bus 最主要的用途是在 Linux 桌面环境为进程提供通信,同时能将 Linux 桌面环境和 Linux 内核事件作为消息传递到进程。D-Bus 的主要概率为总线,注册后的进程可通过总线接收或传递消息,进程也可注册后等待内核事件响应,例如等待网络状态的转变或者计算机发出关机指令。目前,D-Bus 已被大多数 Linux 发行版所采用,开发者可使用 D-Bus 实现各种复杂的进程间通信任务。


2018-07-29 linux , problem , dbus , hostnamectl

Vim 的颜色主题

Retro groove color scheme for Vim


2018-07-27 vim , color-scheme , color , scheme

电子书

最近文章

  • 给网站加上实时聊天对话框 tawk.to 使用记录 tawk.to 是一个可以在网页上添加客户聊天对话框的应用。用户可以通过 tawk.to 泡泡快速地得到支持。
  • 下载 YouTube 视频方法总结 之前就简单地介绍过使用yt-dlp 来下载 YouTube 视频,yt-dlp 是自从 youtube-dl 不再更新之后有人接手开发的新工具。但这篇文章重点是在于下载 YouTube 视频,我会整理一下我目前了解的所有可视化,命令行,Telegram bot 等等工具。
  • Tailscale 出口节点功能配置流量出口 之前的文章中介绍过 Tailscale ,是一个功能非常强大的虚拟组网的工具,底层使用更高级的 [[WireGuard]] 协议进行通信。之前的文章中只简单的介绍了一下 Tailscale 的使用,但是过去的时间里面 Tailscale 又更新了很多的新特性,这篇文章就介绍其中的一个特性 Exit Nodes。
  • Porkbun 免费领取一年 app wiki 等域名 [[Porkbun]] 通常被人戏称为「猪肉包」,是一家新成立于美国俄勒冈州波特兰市的域名注册商,母公司是 Top Level Design,后者是 design, ink 和 wiki 三个顶级域名后缀的管理局。这家域名注册商虽然成立时间比较短,但是胜在价格实惠。短短几年时间就打开了知名度。
  • 《负动产时代》读书笔记 怎么知道的这一本书