Blue.Wolf 发布的文章

jq: shell 脚本中JSON创建、解析工具

0x01 背景

在Linux下开发监控、自动化shell脚本是运维的日常工作之一,JSON是通用且流行的数据交换格式,在Web API中使用很普遍

0x02 问题/诉求

如何在Linux shell脚本中创建与解析JSON呢?

0x03 土办法

1. 创建JSON

变量嵌入

#!/bin/bash
FILENAME="/tmp/xxxx"
curl -i -X POST -H "Content-Type: application/json" -d '{"type": "test", "params":{"item":{"file":"'"$FILENAME"'"}}}' http://192.168.0.1/api
#!/bin/bash
header="Content-Type: application/json"
FILENAME="/tmp/xxxx"
request_body=$(< <(cat <<EOF
{
  "type": "test",
  "params": {
    "item": {
      "file": "$FILENAME"
    }
  }
}
EOF
))
curl -i -X POST -H "$header" -d "$request_body" http://192.168.0.1/api

循环拼接

#!/bin/bash

arr=""
while read var; do
  arr="\"${var}\",${arr}"
done < list

json=`echo ${arr} | sed 's/,$//g'`
echo "[${json}]"

文本内容:
$ cat list
aa
bb
cc
dd
123
r4
t55
5t
gg
1
rgs
g
f
gs

脚本输出:
$ sh t.sh
["gs","f","g","rgs","1","gg","5t","t55","r4","123","dd","cc","bb","aa"]

2. 解析JSON

awk

没有具体尝试过,可参考stackoverflow上的一则回答

0x04 完美解法 —— jq

jq is a lightweight and flexible command-line JSON processor.

jq官网

$ jq -h
jq - commandline JSON processor [version 1.5-1-a5b5cbe]
Usage: jq [options] <jq filter> [file...]

    jq is a tool for processing JSON inputs, applying the
    given filter to its JSON text inputs and producing the
    filter's results as JSON on standard output.
    The simplest filter is ., which is the identity filter,
    copying jq's input to its output unmodified (except for
    formatting).
    For more advanced filters see the jq(1) manpage ("man jq")
    and/or https://stedolan.github.io/jq

    Some of the options include:
     -c        compact instead of pretty-printed output;
     -n        use `null` as the single input value;
     -e        set the exit status code based on the output;
     -s        read (slurp) all inputs into an array; apply filter to it;
     -r        output raw strings, not JSON texts;
     -R        read raw strings, not JSON texts;
     -C        colorize JSON;
     -M        monochrome (don't colorize JSON);
     -S        sort keys of objects on output;
     --tab    use tabs for indentation;
     --arg a v    set variable $a to value <v>;
     --argjson a v    set variable $a to JSON value <v>;
     --slurpfile a f    set variable $a to an array of JSON texts read from <f>;
    See the manpage for more options.

1. 创建JSON

变量嵌入

$ FILENAME="/tmp/xxxx"
$ jq -n -c -M --arg v "$FILENAME" '{"type":"test","params":{"item":{"file": $v}}}'
{"type":"test","params":{"item":{"file":"/tmp/xxxx"}}}

// 去掉 -c 参数可格式化输出json
$ jq -n -M --arg v "$FILENAME" '{"type":"test","params":{"item":{"file": $v}}}'
{
  "type": "test",
  "params": {
    "item": {
      "file": "/tmp/xxxx"
    }
  }
}

循环拼接

$ jq -R -M -c -s 'split("\n")' < list
["aa","bb","cc","dd","123","r4","t55","5t","gg","1","rgs","g","f","gs",""]

数组末尾的空字符串,可以另想办法处理下。

2. 解析JSON

这块可直接学习jq官方教程,由浅入深,讲的很详细。

        jq '.foo'
Input    {"foo": 42, "bar": "less interesting data"}
Output    42

        jq '.foo'
Input    {"notfoo": true, "alsonotfoo": false}
Output    null

        jq '.["foo"]'
Input    {"foo": 42}
Output    42

        jq '.[0]'
Input    [{"name":"JSON", "good":true}, {"name":"XML", "good":false}]
Output    {"name":"JSON", "good":true}

        jq '.[2]'
Input    [{"name":"JSON", "good":true}, {"name":"XML", "good":false}]
Output    null

        jq '.[2:4]'
Input    ["a","b","c","d","e"]
Output    ["c", "d"]

        jq '.[2:4]'
Input    "abcdefghi"
Output    "cd"

        jq '.[:3]'
Input    ["a","b","c","d","e"]
Output    ["a", "b", "c"]

        jq '.[-2:]'
Input    ["a","b","c","d","e"]
Output    ["d", "e"]

        jq '.[-2]'
Input    [1,2,3]
Output    2

【生活杂文】漫谈阅读与人文素养

题外话

最近迷上了「微信读书」APP,看了池老师的《人生元编程》和白岩松的《白说》,感受和共鸣蛮多的,然后前几天看了韩寒来的那期天天向上,说了不少关于写作的东西,最后就是自己最近很想总结、表达一些东西,so……博客除了写技术性文章外(其实也并不常更新(⊙o⊙) 逃……),打算写一个【生活杂文】的分类,不知道会写多少,有想说的了就胡乱写写。

关于标题「漫谈」

漫谈漫谈,就是漫天乱谈,谈错了不要紧。万一谈得对,就算蒙上了。
——摘抄自TK教主的微博


清明放假回家,发现家里人微信用的挺多的,「家族群」都建起来了。原以为用微信沟通算是一个新潮的东西,老家乡下应该不会那么快普及,毕竟爸妈、叔叔、伯伯们都没用QQ,只有同辈的弟弟妹妹们才玩QQ,现在看来是我太 naive 了。家里微信普及的好处就是爸妈都开始用微信找我了,我也是用微信语音聊天代替以前的打电话回家,另外亲戚们互相间的交流也多了,舅舅姨姨们时常视频通话唠家常,也是极好的事,感谢微信,连接了亲朋好友,加深了感情。


不知是最近看的人文类书比较多了还是怎么的,突然发现自己变得「不争」了。有时中午和同事一起吃饭,聊到一些热门事件或者讨论些东西,当自己和他人的想法不同时,自己并不会和他“理论”。同时,对一些新闻事情发表观点时,我都是说:“我觉得……可能……”,说话也不会带有很坚决的东西,例如“肯定”、“一定”,而是说“可能”、“也许”、“应该”。

清明回家坐车路途中也有些思考,关于工作、职场,感觉自己慢慢褪去了刚毕业时的激情,当初在工作中对很多事情总是据理力争,和老大也会发生一些交流上的摩擦,后来慢慢的发现、感受到公司中的层级观念时,也是开始变得「不争」了。举个例子,开会讨论到某些我负责的工作时,我发表观点:“我觉得应该先执行A 后执行B”,而老大的观点是先B后A,此时我会表达我这样安排的逻辑和思考,并提出如果先B后A 可能会出现的状况,大概10分钟阐述结束后,老大仍然觉得是先B后A。刚毕业那回,我可能会再讲10分钟,甚至跟老大“争论”,而现在就不会了,反而有时我会换位思考老大这样考虑的理由是什么。

也不知道是书看的多了有所感悟,还是被职场的无奈给改变的,管它呢

logstash 修改Gem源为淘宝源后不生效问题

环境:
CentOS 6 x86_64
logstash 1.5.4

问题描述:

/opt/logstash 下的 Gemfile、Gemfile.jruby-1.9.lock 都修改了 source 为 https://ruby.taobao.org,但是执行 /opt/logstash/bin/plugin install logstash-input-beats 还是连接的 https://rubygems.org/ 并且因为地址被墙而无法下载插件,如下图

1457933293336.png

解决办法:

google一番有说用以下命令可以解决,但机器上没有 Ruby 环境,gem 命令不存在,并且 logstash 用的自带的 jruby ,想想应该会配置在某个地方,遂决定探究一下。

gem sources remove https://rubygems.org
gem sources add https://ruby.taobao.org

很自然的想到先全量 grep 下这个地址(字符串),命令如下:

grep -nr 'https://rubygems.org' /opt/logstash/

可以看到找到了很多文件,但 /opt/logstash/vendor/bundle/jruby/1.9/gems/logstash- 开头的都是已经安装的插件目录,可以忽略不看。

剩下的一个个仔细的瞄过去,发现一个比较像的文件/opt/logstash/vendor/jruby/lib/ruby/shared/rubygems/defaults.rb,因为有字符串 DEFAULT_HOST

1457933923101.png

vi 打开文件看一下,开头有以下配置项

1457934120376.png

通过注释 “An Array of the default sources that come with RubyGems” 可以知道,应该就是这个配置文件了。修改两处 https://rubygems.orghttps://ruby.taobao.org :wq保存文件,再次执行 /opt/logstash/bin/plugin install logstash-input-beats 即可成功下载插件并安装。

1457934399274.png

总结

因为 rubygems.org 被墙,logstash 下载安装插件时需要将以下文件中的gems地址https://rubygems.org替换为淘宝源地址https://ruby.taobao.org

/opt/logstash/Gemfile
/opt/logstash/Gemfile.jruby-1.9.lock
/opt/logstash/vendor/jruby/lib/ruby/shared/rubygems/defaults.rb

【翻译】ElasticSearch手册:索引模块-合并(Index Modules » Merge)

官方手册 英文原文:index-modules-merge


合并

在elasticsearch中,每个分片都是一个Lucene索引,每个Lucene索引又被分解成段。段是索引内部的存储元件,索引数据就存在里面,并且存储的位置是永久不变的除非被删除。定期执行分段合并,将分段的数据合并到大段里面,保持索引的大小到极点直到删除。

大量的分段留在Lucene索引里面,意味着较慢的搜索和占用更多的内存。分段合并设计用来减少分段数量,但是合并动作是非常昂贵的,尤其是在低IO环境中,可能会受到存储性能上的限制。

一、合并策略(Policy)

索引合并策略模块用于控制分片中哪个分段要合并。它有几种策略类型,默认的设置为 分层 政策。

1、分层(tiered)

合并后的分段大小大致相等,每层的分段数必须小于所允许的值。这个类似于log_bytes_size的合并策略,但该合并策略能够合并非相邻分段,并且多少分段会被一次性合并取决于该层共允许多少分段,该合并策略也不是从头到尾的合并(即:级联合并)。

这个策略有以下设置:

设置项 简要描述
index.merge.policy.expunge_deletes_allowed 当触发删除操作时,我们只合并这个分段,如果它的删除百分比超过了这个阈值。默认是10.
index.merge.policy.floor_segment 分段的大小四舍五入后比这个值还小,即视为等于(约等于)需要合并的大小。
这是为了防止频繁地刷新微小分段,在一个允许有长尾巴(tail)的索引中。该值默认是:2mb
index.merge.policy.max_merge_at_once 单次合并允许操作的最大分段数。Default is10.
index.merge.policy.max_merge_at_once_explicit 单次合并允许操作的最大分段数,在optimize操作或删除操作( expungeDeletes )的时候。Default is30.
index.merge.policy.max_merged_segment 在正常的合并过程(不显式的优化optimize)中产生的分段的最大大小。此设置是近似值:合并后的段大小的预估值是由被合并分段的大小计算出来的(删除文档补偿百分比) Default is5gb.
index.merge.policy.segments_per_tier 每层所允许的分段数。较小的值意味着更多的合并,但是存在较少的分段。默认:10。请注意,这个值必须 >= max_merge_at_once 不然就会强制执行太多的合并。
index.reclaim_deletes_weight 控制如何积极回收更多的删除合并是好的。值越高,越有利于选择合并回收删除。值为0.0意味着删除不影响合并选择。Defaults to2.0.
index.compound_format 是否让存储使用复合格式. Defaults tofalse.

对于正常的合并,该策略首先计算出(“预算”)在这个索引中允许多少个分段。如果该索引的分段数超出预算,则该策略对分段进行排序通过降低大小(通过排序找到,合并后能删除多的分段),即找到成本最低的合并。合并成本的测量是靠以下三个参数:合并的偏移量,总的合并大小和删除回收的比例(删除后能回收的空间),对合并来说更低的偏移、更小的尺寸和更多的回收删除后的空间是有利的。

如果合并后将产生一个大小大于max_merged_segment的分段,则该策略将合并大小较少的分段,保持合并后的分段大小保持在预算之内。

注意,这可能意味着大量的分片将拥有超过千兆字节的数据,默认的max_merged_segment(5GB)可能会导致在一个索引上有许多分段,并导致搜索变慢。使用索引分段API 可以看到该索引有多少分段,解决方式可以是增大max_merged_segment的值,或者运行索引的(在低峰期)优化请求(optimize)。

2、日志字节大小(log_byte_size)

注意!

该策略已过时
将在 2.0 版本后移除该策略,来支持分层策略(即上节内容)。

一个合并策略,将分段合并成字节大小级别成倍增加的分段,其中每个级别有比合并因素的值更小的分段。每当遇到额外的分段(超出合并因素的上界),所有分段将在等级范围内被合并。

这个策略有以下设置:

Setting Description
index.merge.policy.merge_factor 确定在索引操作的时候,多长时间索引的分段进行合并。数值越小,建立索引时使用较少的RAM,搜索未优化的索引速度较快,但索引速度较慢。较大的值,在索引过程中使用更多的RAM,搜索未优化的索引速度较慢,但索引速度较快。因此,较大的值(大于10),最好的用于批处理创建索引;较小的值(低于10),用于以交互方式保持的索引(即一边索引一边查询)。Defaults to 10.
index.merge.policy.min_merge_size 设置分段的最低级别的下限值。任何低于此大小的分段都被认为是在同一级别上(即使它们的大小差异很大),并且将被合并只要他们有合并的机会。这将有效地截断小分段的“长尾巴”,否则将被创建成一个单一等级。如果你设置过大,它会极大地提高合并成本,在索引创建的过程中(如果你刷新许多小的部分)。Defaults to 1.6mb
index.merge.policy.max_merge_size 设置分段的最大文件大小(衡量段的文件的总字节大小),与其它分段合并后。默认为无限制。
index.merge.policy.maxMergeDocs 确定分段的最大文档数(统计总数),与其它分段合并后。默认为无限制。

3、日志文档(log_doc)

注意!

该策略已过时
将在 2.0 版本后移除该策略,来支持分层策略(即第一节内容)。

一个合并策略,将分段合并成文档数量级别成倍增加的分段,其中每个级别有比合并因素的值更小的分段。每当遇到额外的分段(超出合并因素的上界),所有分段将在等级范围内被合并。

Setting Description
index.merge.policy.merge_factor 确定在索引操作的时候,多长时间索引的分段进行合并。数值越小,建立索引时使用较少的RAM,搜索未优化的索引速度较快,但索引速度较慢。较大的值,在索引过程中使用更多的RAM,搜索未优化的索引速度较慢,但索引速度较快。因此,较大的值(大于10),最好的用于批处理创建索引;较小的值(低于10),用于以交互方式保持的索引(即一边索引一边查询)。Defaults to 10.
index.merge.policy.min_merge_docs 这将有效地截断小分段的“长尾巴”,否则将被创建成一个单一等级。如果你设置过大,它会极大地提高合并成本,在索引创建的过程中(如果你刷新许多小的部分)。Defaults to 1000.
index.merge.policy.max_merge_docs 确定可与其它分段合成的分段的最大文档数量(分段的文档数量统计)。默认为无限制。

二、合并调度(Scheduling)

合并调度(并发合并调度 ConcurrentMergeScheduler)用于控制合并操作的执行,一旦它们需要被执行时(按照合并策略)。

1、并发合并调度(ConcurrentMergeScheduler)

合并操作在独立的线程中执行,当达到最大合并线程数时,后续的合并操作需要等待前面的线程结束才能执行。

该调度程序支持以下设置:

Setting Description
index.merge.scheduler.max_thread_count 合并操作可用的最大线程数。默认是:Math.max(1, Math.min(3, Runtime.getRuntime().availableProcessors() / 2)),这在具有SSD支持时运行良好。如果你的索引运行在旋转盘片驱动器(spinning platter drives),最好把这个设置成 1.

2、串行合并调度(SerialMergeScheduler)

一个合并调度,简单的顺序使用合并线程(阻断触发 合并操作和索引操作)。

Ruby on Rails 入门及CVE-2016-0752远程代码执行漏洞研究

0x00 Ruby on Rails 简介


Ruby 是什么?

你说呢?

PHP是最好的语言。

Rails 是什么?

Rails 是使用 Ruby 语言编写的网页程序开发框架,目的是为开发者提供常用组件,简化网页程序的开发。只需编写较少的代码,就能实现其他编程语言或框架难以企及的功能。经验丰富的 Rails 程序员会发现,Rails 让程序开发变得更有乐趣。

0x01 Ruby on Rails 环境搭建


主要参考ruby社区文章:https://ruby-china.org/wiki/install_ruby_guide

文章中部分命令有误(估计是过时了),贴下我执行的命令情况:

$ curl -L https://get.rvm.io | bash -s stable

$ gpg2 --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3

$ curl -L https://get.rvm.io | bash -s stable

$ source /etc/profile.d/rvm.sh

$ rvm -v
rvm 1.26.11 (latest) by Wayne E. Seguin <wayneeseguin@gmail.com>, Michal Papis <mpapis@gmail.com> [https://rvm.io/]

第一步 RVM 安装完成,接下来用 RVM 安装 Ruby 环境;

$ rvm install 2.2.4

ruby官网查看版本号,我选择的是:Previous stable: Ruby 2.2.4,没选最新的 2.3.0 ,怕有坑,新手入门还是选择历史稳定版好点。

1454039747505.png

安装完成,设置下默认版本

$ rvm 2.2.4 --default

测试下配置是否正确

$ ruby -v
ruby 2.2.4p230 (2015-12-16 revision 53155) [x86_64-linux]

$ gem -v
2.4.8

$ gem source -r https://rubygems.org
https://rubygems.org/ removed from sources

$ gem source -a https://ruby.taobao.org
https://ruby.taobao.org added to sources

安装 Bundler

$ gem install bundler

至此,Ruby 环境就已经安装好了,接下来安装 Rails。

$ gem install rails

然后测试安装是否成功。

$ rails -v
Rails 4.2.5.1

0x02 创建第一个应用(Hello World)

Rails 提供了多个被称为“生成器”的脚本,可以简化开发,生成某项操作需要的所有文件。其中一个是新程序生成器,生成一个 Rails 程序骨架,不用自己一个一个新建文件。

$ mkdir -p /home/learn-ruby/workspace && cd /home/learn-ruby/workspace

$ rails new hello_world

$ ll
total 4
drwxr-xr-x 12 root root 4096 Jan 29 00:11 hello_world

$ cd hello_world/

$ ll
total 60
drwxr-xr-x 8 root root 4096 Jan 29 00:10 app
drwxr-xr-x 2 root root 4096 Jan 29 00:11 bin
drwxr-xr-x 5 root root 4096 Jan 29 00:10 config
-rw-r--r-- 1 root root  153 Jan 29 00:10 config.ru
drwxr-xr-x 2 root root 4096 Jan 29 00:10 db
-rw-r--r-- 1 root root 1504 Jan 29 00:10 Gemfile
-rw-r--r-- 1 root root 3952 Jan 29 00:11 Gemfile.lock
drwxr-xr-x 4 root root 4096 Jan 29 00:10 lib
drwxr-xr-x 2 root root 4096 Jan 29 00:10 log
drwxr-xr-x 2 root root 4096 Jan 29 00:10 public
-rw-r--r-- 1 root root  249 Jan 29 00:10 Rakefile
-rw-r--r-- 1 root root  478 Jan 29 00:10 README.rdoc
drwxr-xr-x 8 root root 4096 Jan 29 00:10 test
drwxr-xr-x 3 root root 4096 Jan 29 00:10 tmp
drwxr-xr-x 3 root root 4096 Jan 29 00:10 vendor

可以看到 hello_world 文件夹中有很多自动生成的文件和文件夹,这些组成一个 Rails 程序。下面简单介绍默认生成的文件和文件夹的作用:

文件/文件夹 作用
app/ 存放程序的控制器、模型、视图、帮助方法、邮件和静态资源文件。本文主要关注的是这个文件夹。
bin/ 存放运行程序的 rails 脚本,以及其他用来部署或运行程序的脚本。
config/ 设置程序的路由,数据库等。详情参阅“设置 Rails 程序”一文。
config.ru 基于 Rack 服务器的程序设置,用来启动程序。
db/ 存放当前数据库的模式,以及数据库迁移文件。
Gemfile, Gemfile.lock 这两个文件用来指定程序所需的 gem 依赖件,用于 Bundler gem。关于 Bundler 的详细介绍,请访问 Bundler 官网
lib/ 程序的扩展模块。
log/ 程序的日志文件。
public/ 唯一对外开放的文件夹,存放静态文件和编译后的资源文件。
Rakefile 保存并加载可在命令行中执行的任务。任务在 Rails 的各组件中定义。如果想添加自己的任务,不要修改这个文件,把任务保存在 lib/tasks 文件夹中。
README.rdoc 程序的简单说明。你应该修改这个文件,告诉其他人这个程序的作用,如何安装等。
test/ 单元测试,固件等测试用文件。详情参阅“测试 Rails 程序”一文。
tmp/ 临时文件,例如缓存,PID,会话文件。
vendor/ 存放第三方代码。经常用来放第三方 gem。

要使程序能够被访问,我们需要先开启web server

$ rails server

上述命令会启动 WEBrick,这是 Ruby 内置的服务器。要查看程序,请打开一个浏览器窗口,访问 http://localhost:3000。应该会看到默认的 Rails 信息页面:

rails_welcome.png

PS:
Q:如何使程序监听在 0.0.0.0 ?
A:在 config/boot.rb 文件中添加如下代码,然后再次执行 rails server 即可。

require 'rails/commands/server'

module Rails
  class Server
    alias :default_options_bk :default_options
    def default_options
      default_options_bk.merge!(Host: '0.0.0.0')
    end
  end
end

Q:如何更改监听端口?
A:rails server -p 12345 加 -p 参数指定端口号。

接下来开始写自己的程序,Rails 也是基于 MVC 模式进行开发的,那我们首先创建个控制器(Controller)和视图(View)。

控制器用来接受向程序发起的请求。路由决定哪个控制器会接受到这个请求。一般情况下,每个控制器都有多个路由,对应不同的动作。动作用来提供视图中需要的数据。

视图的作用是,以人类能看懂的格式显示数据。有一点要特别注意,数据是在控制器中获取的,而不是在视图中。视图只是把数据显示出来。默认情况下,视图使用 eRuby(嵌入式 Ruby)语言编写,经由 Rails 解析后,再发送给用户。

Rails 很高级,创建控制器可用 控制器生成器 工具来自动创建,只需告诉生成器,我想要个名为“welcome”的控制器和一个名为“index”的动作,命令如下所示:

$ rails generate controller welcome index

生成的控制器文件在 app/controllers/welcome_controller.rb,内容如下:

class WelcomeController < ApplicationController
  def index
  end
end

视图文件在 app/views/welcome/index.html.erb ,我们编辑它,将内容修改为:

<h1>Hello, Rails!</h1>

然后访问看下效果:

1454049918392.png

至此,我们已经写完第一个 Rails 应用了。

0x03 CVE-2016-0752 远程代码执行漏洞研究

从上文我们可以看到,在控制器中并没有说明要加载哪个视图文件,这是Rails 的一个特性。

Rails controllers 的设计中,会根据被调用的方法,隐式的渲染对应的 view 文件。例如上文中,当调用 welcomeindex 方法时,如果代码中没有明确指定要渲染的 view file,框架就会隐式的渲染 index.html.erb 文件。

CVE-2016-0752 漏洞用到的是渲染方法(render method)。Rails 的文档中给出了多种调用渲染方法的方式,包括使用 file: 选项来明确指定要渲染的 view file 路径。

来看一段示例代码:

def show
  render params[:template]
end

第一眼看,这段代码非常简单,主要功能为渲染 template 参数指定的 view 模板。但Rails如何查找指定的模板呢?在 views 目录下查找? 在应用的 root 目录下查找?或是在其他位置?;该参数的值是一个模板的名称?是一个有特定后缀的文件名?或是一个完整的文件路径?

后续内容 http://drops.wooyun.org/papers/12519