fluent-plugin-anonymizer 0.4.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.travis.yml +10 -2
- data/Appraisals +7 -0
- data/README.md +73 -54
- data/fluent-plugin-anonymizer.gemspec +2 -1
- data/lib/fluent/plugin/anonymizer.rb +2 -1
- data/lib/fluent/plugin/filter_anonymizer.rb +274 -10
- data/lib/fluent/plugin/out_anonymizer.rb +12 -4
- data/test/helper.rb +4 -1
- data/test/plugin/test_filter_anonymizer.rb +152 -19
- data/test/plugin/test_out_anonymizer.rb +14 -14
- metadata +34 -31
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a1fc717a156c6b19fea2d19f4522c3b2d8be7dda
|
4
|
+
data.tar.gz: 9c7ff4fda067571c4becc5f29c5be0a5452e9653
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: bc870e37f4a1b30b543d53497208e9af52ac1c901bc77ec32c1bb772fb30d9cd4a8ebd2eb1782550757fd4763c93e84248c8e2f2c46826e3c3b688113e1f6a5c
|
7
|
+
data.tar.gz: 0bcc4eaecfceb6f5a00a14d587bd81f77efe17d8500384d73bee0d8f4e63c0e92897fb81aaa7e5809362fdbaf2cf329a85b0361d085ffdae55ab3450993064f7
|
data/.travis.yml
CHANGED
data/Appraisals
ADDED
data/README.md
CHANGED
@@ -2,37 +2,52 @@
|
|
2
2
|
|
3
3
|
## Overview
|
4
4
|
|
5
|
-
Fluentd filter output plugin to anonymize records with [OpenSSL::
|
5
|
+
Fluentd filter output plugin to anonymize records with [OpenSSL::Digest](https://docs.ruby-lang.org/ja/latest/class/OpenSSL=3a=3aDigest.html) of MD5/SHA1/SHA256/SHA384/SHA512 algorithms. This data masking plugin protects privacy data such as UserID, Email, Phone number, IPv4/IPv6 address and so on.
|
6
|
+
|
7
|
+
## Requirements
|
8
|
+
|
9
|
+
| fluent-plugin-anonymizer | fluentd | ruby |
|
10
|
+
|--------------------|------------|--------|
|
11
|
+
| 1.0.0 | v0.14.x | >= 2.1 |
|
12
|
+
| 0.5.1 | v0.12.x | >= 1.9 |
|
13
|
+
|
6
14
|
|
7
15
|
## Installation
|
8
16
|
|
9
|
-
install with gem or
|
17
|
+
install with gem or td-agent-gem command as:
|
10
18
|
|
11
19
|
`````
|
12
|
-
|
13
|
-
gem install fluent-plugin-anonymizer
|
20
|
+
# for system installed fluentd
|
21
|
+
$ gem install fluent-plugin-anonymizer
|
14
22
|
|
15
|
-
|
16
|
-
|
23
|
+
# for td-agent2 (with fluentd v0.12)
|
24
|
+
$ sudo td-agent-gem install fluent-plugin-anonymizer -v 0.5.1
|
25
|
+
|
26
|
+
# for td-agent3 (with fluentd v0.14)
|
27
|
+
$ sudo td-agent-gem install fluent-plugin-anonymizer -v 1.0.0
|
17
28
|
`````
|
18
29
|
|
30
|
+
For more details, see [Plugin Management](https://docs.fluentd.org/v0.14/articles/plugin-management)
|
31
|
+
|
19
32
|
## Tutorial
|
20
33
|
|
21
|
-
###
|
34
|
+
### Filter Plugin
|
22
35
|
|
23
36
|
#### configuration
|
24
37
|
|
25
|
-
|
26
|
-
|
27
|
-
`````
|
38
|
+
```text
|
28
39
|
<source>
|
29
|
-
type
|
30
|
-
|
40
|
+
@type dummy
|
41
|
+
tag raw.dummy
|
42
|
+
dummy [
|
43
|
+
{"host":"10.102.3.80","member_id":"12345", "mail":"example@example.com"},
|
44
|
+
{"host":"2001:db8:0:8d3:0:8a2e::","member_id":"61f6c1b5f19e0a7f73dd52a23534085bf01f2c67","mail":"eeb890d74b8c1c4cd1e35a3ea62166e0b770f4f4"}
|
45
|
+
]
|
31
46
|
</source>
|
32
47
|
|
33
|
-
<
|
34
|
-
type anonymizer
|
35
|
-
|
48
|
+
<filter raw.**>
|
49
|
+
@type anonymizer
|
50
|
+
|
36
51
|
# Specify hashing keys with comma
|
37
52
|
sha1_keys user_id, member_id, mail
|
38
53
|
|
@@ -43,45 +58,40 @@ It is a sample to hash record with sha1 for `user_id`, `member_id` and `mail`. F
|
|
43
58
|
ipaddr_mask_keys host
|
44
59
|
ipv4_mask_subnet 24
|
45
60
|
ipv6_mask_subnet 104
|
46
|
-
|
47
|
-
# Set tag rename pattern
|
48
|
-
remove_tag_prefix test.
|
49
|
-
add_tag_prefix anonymized.
|
50
|
-
</match>
|
61
|
+
</filter>
|
51
62
|
|
52
|
-
<match
|
53
|
-
type stdout
|
63
|
+
<match raw.**>
|
64
|
+
@type stdout
|
54
65
|
</match>
|
55
|
-
|
66
|
+
```
|
56
67
|
|
57
68
|
#### result
|
58
69
|
|
59
|
-
|
60
|
-
$ echo '{"host":"10.102.3.80","member_id":"12345", "mail":"example@example.com"}' | fluent-cat test.message
|
61
|
-
$ echo '{"host":"2001:db8:0:8d3:0:8a2e:70:7344","member_id":"12345", "mail":"example@example.com"}' | fluent-cat test.message
|
70
|
+
This sample result has made with the above configuration into "fluent.conf".
|
62
71
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
72
|
+
```text
|
73
|
+
$ fluentd -c fluent.conf
|
74
|
+
2017-02-27 22:59:18.070132000 +0900 raw.dummy: {"host":"10.102.3.0","member_id":"5ab2cebb0537866c4a0cd2e2f3502c0976b788da","mail":"7e9d6dbefa72d56056c8c740b34b5c0bbfec8d87"}
|
75
|
+
2017-02-27 22:59:19.079251000 +0900 raw.dummy: {"host":"2001:db8:0:8d3:0:8a2e::","member_id":"445514dfcd82b2a8b94ec6763afa6e349e78c5f8","mail":"54608576c8d815a4ffd595a3c1fe72751ed04424"}
|
76
|
+
2017-02-27 22:59:20.086747000 +0900 raw.dummy: {"host":"10.102.3.0","member_id":"b14a8f98019ec84c6fe329d5af62c46bb45348f8","mail":"723da8084da3438d9287b44e5a714b70e10a9755"}
|
77
|
+
2017-02-27 22:59:21.094767000 +0900 raw.dummy: {"host":"2001:db8:0:8d3:0:8a2e::","member_id":"d38ebb9b96c0cbffd4136935c7f6fe9dd05980cd","mail":"b6f9d777831cbecfd2ea806f5f62f79a275bbb82"}
|
78
|
+
```
|
67
79
|
|
68
|
-
###
|
80
|
+
### Output Plugin
|
69
81
|
|
70
82
|
#### configuration
|
71
83
|
|
72
|
-
|
84
|
+
It is a sample to hash record with sha1 for `user_id`, `member_id` and `mail`. For IP address, auto-detecting IPv4/IPv6 and rounding number with 24bit(IPv4) or 104bit(IPv6) netmask using `ipaddr_mask_keys` and `ipv4_mask_subnet`, `ipv6_mask_subnet` option.
|
85
|
+
|
86
|
+
`````
|
73
87
|
<source>
|
74
|
-
type
|
75
|
-
|
76
|
-
dummy [
|
77
|
-
{"host":"10.102.3.80","member_id":"12345", "mail":"example@example.com"},
|
78
|
-
{"host":"2001:db8:0:8d3:0:8a2e::","member_id":"61f6c1b5f19e0a7f73dd52a23534085bf01f2c67","mail":"eeb890d74b8c1c4cd1e35a3ea62166e0b770f4f4"}
|
79
|
-
]
|
88
|
+
@type forward
|
89
|
+
port 24224
|
80
90
|
</source>
|
81
91
|
|
82
|
-
<
|
83
|
-
type anonymizer
|
84
|
-
|
92
|
+
<match test.message>
|
93
|
+
@type anonymizer
|
94
|
+
|
85
95
|
# Specify hashing keys with comma
|
86
96
|
sha1_keys user_id, member_id, mail
|
87
97
|
|
@@ -92,18 +102,27 @@ $ tail -f /var/log/td-agent/td-agent.log
|
|
92
102
|
ipaddr_mask_keys host
|
93
103
|
ipv4_mask_subnet 24
|
94
104
|
ipv6_mask_subnet 104
|
95
|
-
|
105
|
+
|
106
|
+
# Set tag rename pattern
|
107
|
+
tag anonymized.${tag}
|
108
|
+
remove_tag_prefix test.
|
109
|
+
</match>
|
96
110
|
|
97
|
-
<match
|
98
|
-
type stdout
|
111
|
+
<match anonymized.message>
|
112
|
+
@type stdout
|
99
113
|
</match>
|
100
|
-
|
114
|
+
`````
|
101
115
|
|
102
116
|
#### result
|
103
117
|
|
104
|
-
|
105
|
-
$
|
106
|
-
|
118
|
+
`````
|
119
|
+
$ echo '{"host":"10.102.3.80","member_id":"12345", "mail":"example@example.com"}' | fluent-cat test.message
|
120
|
+
$ echo '{"host":"2001:db8:0:8d3:0:8a2e:70:7344","member_id":"12345", "mail":"example@example.com"}' | fluent-cat test.message
|
121
|
+
|
122
|
+
$ tail -f /var/log/td-agent/td-agent.log
|
123
|
+
2014-01-06 18:30:21 +0900 anonymized.message: {"host":"10.102.3.0","member_id":"61f6c1b5f19e0a7f73dd52a23534085bf01f2c67","mail":"eeb890d74b8c1c4cd1e35a3ea62166e0b770f4f4"}
|
124
|
+
2014-01-06 18:30:22 +0900 anonymized.message: {"host":"2001:db8:0:8d3:0:8a2e::","member_id":"61f6c1b5f19e0a7f73dd52a23534085bf01f2c67","mail":"eeb890d74b8c1c4cd1e35a3ea62166e0b770f4f4"}
|
125
|
+
`````
|
107
126
|
|
108
127
|
## Parameters
|
109
128
|
|
@@ -132,6 +151,12 @@ Round number for following one or more keys. It makes easy to aggregate calculat
|
|
132
151
|
* include_tag_key (default: false)
|
133
152
|
* tag_key
|
134
153
|
|
154
|
+
set one or more option are required for editing tag name using HandleTagNameMixin.
|
155
|
+
|
156
|
+
* tag
|
157
|
+
|
158
|
+
In the case of using this option [like 'tag anonymized.${tag}' with tag placeholder](https://github.com/y-ken/fluent-plugin-anonymizer/blob/master/test/plugin/test_out_anonymizer.rb#L179), tag will be modified after these options affected. which are remove_tag_prefix, remove_tag_suffix, add_tag_prefix and add_tag_suffix.
|
159
|
+
|
135
160
|
Add original tag name into filtered record using SetTagKeyMixin.
|
136
161
|
|
137
162
|
* remove_tag_prefix
|
@@ -139,12 +164,6 @@ Add original tag name into filtered record using SetTagKeyMixin.
|
|
139
164
|
* add_tag_prefix
|
140
165
|
* add_tag_suffix
|
141
166
|
|
142
|
-
set one or more option are required for editing tag name using HandleTagNameMixin.
|
143
|
-
|
144
|
-
* tag
|
145
|
-
|
146
|
-
On using this option [like 'tag anonymized.${tag}' with tag placeholder](https://github.com/y-ken/fluent-plugin-anonymizer/blob/master/test/plugin/test_out_anonymizer.rb#L153), it will be overwrite after these options affected. which are remove_tag_prefix, remove_tag_suffix, add_tag_prefix and add_tag_suffix.
|
147
|
-
|
148
167
|
## Notes
|
149
168
|
|
150
169
|
* hashing nested value behavior is compatible with [LogStash::Filters::Anonymize](https://github.com/logstash/logstash/blob/master/lib/logstash/filters/anonymize.rb) does. For further details, please check it out the test code at [test_emit_nest_value](https://github.com/y-ken/fluent-plugin-anonymizer/blob/master/test/plugin/test_out_anonymizer.rb#L91).
|
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
6
|
spec.name = "fluent-plugin-anonymizer"
|
7
|
-
spec.version = "0.
|
7
|
+
spec.version = "1.0.0"
|
8
8
|
spec.authors = ["Kentaro Yoshida"]
|
9
9
|
spec.email = ["y.ken.studio@gmail.com"]
|
10
10
|
spec.summary = %q{Fluentd filter output plugin to anonymize records with HMAC of MD5/SHA1/SHA256/SHA384/SHA512 algorithms. This data masking plugin protects privacy data such as UserID, Email, Phone number, IPv4/IPv6 address and so on.}
|
@@ -19,6 +19,7 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.add_development_dependency "bundler"
|
20
20
|
spec.add_development_dependency "rake"
|
21
21
|
spec.add_development_dependency "test-unit", "~> 3"
|
22
|
+
spec.add_development_dependency "appraisal"
|
22
23
|
spec.add_runtime_dependency "fluentd"
|
23
24
|
spec.add_runtime_dependency "fluent-mixin-rewrite-tag-name"
|
24
25
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'fluent/config/error'
|
1
2
|
require 'openssl'
|
2
3
|
require 'ipaddr'
|
3
4
|
|
@@ -79,7 +80,7 @@ module Fluent
|
|
79
80
|
def anonymize_value(message, algorithm, salt)
|
80
81
|
case algorithm
|
81
82
|
when 'md5','sha1','sha256','sha384','sha512'
|
82
|
-
|
83
|
+
DIGEST[algorithm].call.update(salt).update(message.to_s).hexdigest
|
83
84
|
when 'ipaddr_mask'
|
84
85
|
address = IPAddr.new(message)
|
85
86
|
subnet = address.ipv4? ? @ipv4_mask_subnet : @ipv6_mask_subnet
|
@@ -1,26 +1,290 @@
|
|
1
|
-
|
1
|
+
require 'fluent/plugin/filter'
|
2
|
+
require 'openssl'
|
3
|
+
require 'uri'
|
4
|
+
require 'ipaddr'
|
5
|
+
|
6
|
+
# <filter **>
|
7
|
+
# @type mask
|
8
|
+
# # salts will be selected for field names in a deterministic way
|
9
|
+
# salt secret_salt # different salt for each fields?
|
10
|
+
# salt salt_brabra
|
11
|
+
# salts ["s1","s2","s3","s4"]
|
12
|
+
# <mask sha1>
|
13
|
+
# # key user_id
|
14
|
+
# keys ["user_id","session_id","source_ip"]
|
15
|
+
# key_pattern ^(source|src)_?ip_?(addr|address)?$
|
16
|
+
# value_pattern @mydomain\.example\.com$
|
17
|
+
# value_in_subnet 192.168.0.0/16 # naming?
|
18
|
+
# </mask>
|
19
|
+
# <mask uri_path>
|
20
|
+
# keys ["url","uri"]
|
21
|
+
# # or key_pattern
|
22
|
+
# </mask>
|
23
|
+
# <mask network>
|
24
|
+
# keys ["dest","destination","dest_ip"]
|
25
|
+
# # or key_pattern
|
26
|
+
# ipv4_mask_bits 24
|
27
|
+
# ipv6_mask_bits 104
|
28
|
+
# </mask>
|
29
|
+
# </filter>
|
30
|
+
|
31
|
+
module Fluent::Plugin
|
2
32
|
class AnonymizerFilter < Filter
|
3
|
-
Plugin.register_filter('anonymizer', self)
|
33
|
+
Fluent::Plugin.register_filter('anonymizer', self)
|
4
34
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
35
|
+
MASK_METHODS = {
|
36
|
+
md5: ->(opts){ ->(v,salt){ OpenSSL::Digest.new("md5").update(salt).update(v.to_s).hexdigest } },
|
37
|
+
sha1: ->(opts){ ->(v,salt){ OpenSSL::Digest.new("sha1").update(salt).update(v.to_s).hexdigest } },
|
38
|
+
sha256: ->(opts){ ->(v,salt){ OpenSSL::Digest.new("sha256").update(salt).update(v.to_s).hexdigest } },
|
39
|
+
sha384: ->(opts){ ->(v,salt){ OpenSSL::Digest.new("sha384").update(salt).update(v.to_s).hexdigest } },
|
40
|
+
sha512: ->(opts){ ->(v,salt){ OpenSSL::Digest.new("sha512").update(salt).update(v.to_s).hexdigest } },
|
41
|
+
uri_path: ->(opts){ ->(v,salt){
|
42
|
+
begin
|
43
|
+
uri = URI.parse(v)
|
44
|
+
if uri.absolute?
|
45
|
+
uri.path = '/'
|
46
|
+
uri.user = uri.password = uri.query = uri.fragment = nil
|
47
|
+
end
|
48
|
+
uri.to_s
|
49
|
+
rescue
|
50
|
+
v
|
51
|
+
end
|
52
|
+
} },
|
53
|
+
network: ->(opts){ ->(v,salt){
|
54
|
+
begin
|
55
|
+
addr = IPAddr.new(v)
|
56
|
+
if addr.ipv4? && opts.ipv4_mask_bits
|
57
|
+
addr.mask(opts.ipv4_mask_bits).to_s
|
58
|
+
elsif addr.ipv6? && opts.ipv6_mask_bits
|
59
|
+
addr.mask(opts.ipv6_mask_bits).to_s
|
60
|
+
else
|
61
|
+
addr.to_s
|
62
|
+
end
|
63
|
+
rescue
|
64
|
+
v
|
65
|
+
end
|
66
|
+
} },
|
67
|
+
}
|
9
68
|
|
10
|
-
|
69
|
+
config_param :salt, :string, default: nil
|
70
|
+
config_param :salts, :array, default: nil
|
71
|
+
config_section :mask, param_name: :mask_config_list, required: false, multi: true do
|
72
|
+
config_argument :method, :enum, list: MASK_METHODS.keys
|
73
|
+
config_param :salt, :string, default: nil
|
74
|
+
|
75
|
+
config_param :key, :string, default: nil
|
76
|
+
config_param :keys, :array, default: []
|
77
|
+
config_param :key_chain, :string, default: nil # for.nested.key
|
78
|
+
config_param :key_chains, :array, default: [] # ["for.nested.key","can.be.specified","twice.or.more"]
|
79
|
+
config_param :key_pattern, :string, default: nil
|
80
|
+
config_param :value_pattern, :string, default: nil
|
81
|
+
config_param :value_in_subnet, :string, default: nil # 192.168.0.0/24
|
82
|
+
|
83
|
+
config_param :mask_array_elements, :bool, default: false
|
84
|
+
config_param :ipv4_mask_bits, :integer, default: nil
|
85
|
+
config_param :ipv6_mask_bits, :integer, default: nil
|
86
|
+
end
|
87
|
+
|
88
|
+
# obsolete configuration parameters
|
89
|
+
config_param :md5_keys, :string, default: nil
|
90
|
+
config_param :sha1_keys, :string, default: nil
|
91
|
+
config_param :sha256_keys, :string, default: nil
|
92
|
+
config_param :sha384_keys, :string, default: nil
|
93
|
+
config_param :sha512_keys, :string, default: nil
|
94
|
+
config_param :hash_salt, :string, default: nil
|
95
|
+
config_param :ipaddr_mask_keys, :string, default: nil
|
96
|
+
config_param :ipv4_mask_subnet, :integer, default: 24
|
97
|
+
config_param :ipv6_mask_subnet, :integer, default: 104
|
11
98
|
|
12
99
|
def initialize
|
13
100
|
super
|
14
|
-
|
101
|
+
@salt_list = []
|
102
|
+
@salt_map = {}
|
103
|
+
@conversions = []
|
15
104
|
end
|
16
105
|
|
17
106
|
def configure(conf)
|
18
107
|
super
|
19
|
-
|
108
|
+
|
109
|
+
salt_missing = false
|
110
|
+
|
111
|
+
@salt_list << @salt
|
112
|
+
@salt_list += @salts if @salts
|
113
|
+
|
114
|
+
@masks = []
|
115
|
+
@mask_config_list.each do |c|
|
116
|
+
unless c.salt || @salt_list.size > 0
|
117
|
+
salt_missing = true
|
118
|
+
end
|
119
|
+
|
120
|
+
conv = MASK_METHODS[c.method].call(c)
|
121
|
+
[c.key || nil, *c.keys].compact.each do |key|
|
122
|
+
@masks << masker_for_key(conv, key, c)
|
123
|
+
end
|
124
|
+
[c.key_chain || nil, *c.key_chains].compact.each do |key_chain|
|
125
|
+
@masks << masker_for_key_chain(conv, key_chain.split('.'), c)
|
126
|
+
end
|
127
|
+
@masks << masker_for_key_pattern(conv, c.key_pattern, c) if c.key_pattern
|
128
|
+
@masks << masker_for_value_pattern(conv, c.value_pattern, c) if c.value_pattern
|
129
|
+
@masks << masker_for_value_in_subnet(conv, c.value_in_subnet, c) if c.value_in_subnet
|
130
|
+
end
|
131
|
+
|
132
|
+
# obsolete option handling
|
133
|
+
[[@md5_keys,:md5],[@sha1_keys,:sha1],[@sha256_keys,:sha256],[@sha384_keys,:sha384],[@sha512_keys,:sha512]].each do |param,m|
|
134
|
+
next unless param
|
135
|
+
@salt_list << (@hash_salt || '') if @salt_list.empty? # to suppress ConfigError for salt missing
|
136
|
+
conf = OpenStruct.new
|
137
|
+
conf.salt = @hash_salt || ''
|
138
|
+
conf.mask_array_elements = true
|
139
|
+
conv = MASK_METHODS[m].call(conf)
|
140
|
+
param.split(',').map(&:strip).each do |key|
|
141
|
+
if key.include?('.')
|
142
|
+
@masks << masker_for_key_chain(conv, key.split('.'), conf)
|
143
|
+
else
|
144
|
+
@masks << masker_for_key(conv, key, conf)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
if @ipaddr_mask_keys
|
149
|
+
@salt_list << (@hash_salt || '') if @salt_list.empty? # to suppress ConfigError for salt missing
|
150
|
+
conf = OpenStruct.new
|
151
|
+
conf.salt = @hash_salt || ''
|
152
|
+
conf.mask_array_elements = true
|
153
|
+
conf.ipv4_mask_bits = @ipv4_mask_subnet
|
154
|
+
conf.ipv6_mask_bits = @ipv6_mask_subnet
|
155
|
+
conv = MASK_METHODS[:network].call(conf)
|
156
|
+
@ipaddr_mask_keys.split(',').map(&:strip).each do |key|
|
157
|
+
if key.include?('.')
|
158
|
+
@masks << masker_for_key_chain(conv, key.split('.'), conf)
|
159
|
+
else
|
160
|
+
@masks << masker_for_key(conv, key, conf)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
if @masks.size < 1
|
166
|
+
raise Fluent::ConfigError, "no anonymizing operations configured"
|
167
|
+
end
|
168
|
+
if salt_missing
|
169
|
+
raise Fluent::ConfigError, "salt (or salts) required, but missing"
|
170
|
+
end
|
20
171
|
end
|
21
172
|
|
22
173
|
def filter(tag, time, record)
|
23
|
-
record
|
174
|
+
record.update(@masks.reduce(record){|r,mask| mask.call(r)})
|
175
|
+
end
|
176
|
+
|
177
|
+
def salt_determine(key)
|
178
|
+
return @salt_map[key] if @salt_map.has_key?(key)
|
179
|
+
keystr = key.to_s
|
180
|
+
if keystr.empty?
|
181
|
+
@salt_map[key] = @salt_list[0]
|
182
|
+
else
|
183
|
+
@salt_map[key] = @salt_list[(keystr[0].ord + keystr[-1].ord) % @salt_list.size]
|
184
|
+
end
|
185
|
+
@salt_map[key]
|
186
|
+
end
|
187
|
+
|
188
|
+
def mask_value(value, for_each)
|
189
|
+
if for_each && value.is_a?(Array)
|
190
|
+
value.map{|v|
|
191
|
+
yield v
|
192
|
+
}
|
193
|
+
else
|
194
|
+
yield value
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def masker_for_key(conv, key, opts)
|
199
|
+
for_each = opts.mask_array_elements
|
200
|
+
salt = opts.salt || salt_determine(key)
|
201
|
+
if for_each
|
202
|
+
->(record){
|
203
|
+
begin
|
204
|
+
if record.has_key?(key)
|
205
|
+
record[key] = mask_value(record[key], for_each){|v| conv.call(v, salt) }
|
206
|
+
end
|
207
|
+
rescue => e
|
208
|
+
log.error "unexpected error while masking value", error_class: e.class, error: e.message
|
209
|
+
end
|
210
|
+
record
|
211
|
+
}
|
212
|
+
else
|
213
|
+
->(record){
|
214
|
+
begin
|
215
|
+
if record.has_key?(key)
|
216
|
+
record[key] = conv.call(record[key], salt)
|
217
|
+
end
|
218
|
+
rescue => e
|
219
|
+
log.error "unexpected error while masking value", error_class: e.class, error: e.message
|
220
|
+
end
|
221
|
+
record
|
222
|
+
}
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def masker_for_key_chain(conv, key_chain, opts)
|
227
|
+
for_each = opts.mask_array_elements
|
228
|
+
heading = key_chain[0..-2]
|
229
|
+
container_fetcher = ->(record){ heading.reduce(record){|r,c| r && r.has_key?(c) ? r[c] : nil } }
|
230
|
+
tailing = key_chain[-1]
|
231
|
+
->(record){
|
232
|
+
begin
|
233
|
+
container = container_fetcher.call(record)
|
234
|
+
if container && container.has_key?(tailing)
|
235
|
+
container[tailing] = mask_value(container[tailing], for_each){|v| conv.call(v, opts.salt || salt_determine(tailing)) }
|
236
|
+
end
|
237
|
+
rescue => e
|
238
|
+
log.error "unexpected error while masking value", error_class: e.class, error: e.message
|
239
|
+
end
|
240
|
+
record
|
241
|
+
}
|
242
|
+
end
|
243
|
+
|
244
|
+
def masker_for_key_pattern(conv, pattern, opts)
|
245
|
+
for_each = opts.mask_array_elements
|
246
|
+
regexp = Regexp.new(pattern)
|
247
|
+
->(record){
|
248
|
+
begin
|
249
|
+
record.each_pair do |key, value|
|
250
|
+
next unless (regexp =~ key.to_s rescue nil)
|
251
|
+
record[key] = mask_value(record[key], for_each){|v| conv.call(v, opts.salt || salt_determine(key)) }
|
252
|
+
end
|
253
|
+
rescue => e
|
254
|
+
log.error "unexpected error while masking value", error_class: e.class, error: e.message
|
255
|
+
end
|
256
|
+
record
|
257
|
+
}
|
258
|
+
end
|
259
|
+
|
260
|
+
def masker_for_value_pattern(conv, pattern, opts)
|
261
|
+
regexp = Regexp.new(pattern)
|
262
|
+
->(record){
|
263
|
+
begin
|
264
|
+
record.each_pair do |key, value|
|
265
|
+
next unless (regexp =~ value.to_s rescue nil)
|
266
|
+
record[key] = conv.call(value, opts.salt || salt_determine(key))
|
267
|
+
end
|
268
|
+
rescue => e
|
269
|
+
log.error "unexpected error while masking value", error_class: e.class, error: e.message
|
270
|
+
end
|
271
|
+
record
|
272
|
+
}
|
273
|
+
end
|
274
|
+
|
275
|
+
def masker_for_value_in_subnet(conv, network_str, opts)
|
276
|
+
network = IPAddr.new(network_str)
|
277
|
+
->(record){
|
278
|
+
begin
|
279
|
+
record.each_pair do |key, value|
|
280
|
+
next unless (network.include?(value) rescue nil)
|
281
|
+
record[key] = conv.call(value, opts.salt || salt_determine(key))
|
282
|
+
end
|
283
|
+
rescue => e
|
284
|
+
log.error "unexpected error while masking value", error_class: e.class, error: e.message
|
285
|
+
end
|
286
|
+
record
|
287
|
+
}
|
24
288
|
end
|
25
289
|
end
|
26
290
|
end
|
@@ -13,10 +13,17 @@ class Fluent::AnonymizerOutput < Fluent::Output
|
|
13
13
|
define_method("router") { Fluent::Engine }
|
14
14
|
end
|
15
15
|
|
16
|
-
config_param :tag, :string, :default => nil
|
17
|
-
|
18
|
-
config_param :
|
19
|
-
|
16
|
+
config_param :tag, :string, :default => nil,
|
17
|
+
:desc => 'The output tag.'
|
18
|
+
config_param :hash_salt, :string, :default => '',
|
19
|
+
:desc => <<-DESC
|
20
|
+
This salt affects for md5_keys sha1_keys sha256_keys sha384_keys sha512_keys settings.
|
21
|
+
It is recommend to set a hash salt to prevent rainbow table attacks.
|
22
|
+
DESC
|
23
|
+
config_param :ipv4_mask_subnet, :integer, :default => 24,
|
24
|
+
:desc => 'Anonymize ipv4 addresses by subnet mask.'
|
25
|
+
config_param :ipv6_mask_subnet, :integer, :default => 104,
|
26
|
+
:desc => 'Anonymize ipv6 addresses by subnet mask.'
|
20
27
|
|
21
28
|
include Fluent::HandleTagNameMixin
|
22
29
|
include Fluent::Mixin::RewriteTagName
|
@@ -29,6 +36,7 @@ class Fluent::AnonymizerOutput < Fluent::Output
|
|
29
36
|
end
|
30
37
|
|
31
38
|
def configure(conf)
|
39
|
+
log.warn "out_anonymizer is now deprecated. It will be removed in a future release. Please consider to use filter_anonymizer."
|
32
40
|
super
|
33
41
|
@anonymizer = Fluent::Anonymizer.new(self, conf)
|
34
42
|
end
|
data/test/helper.rb
CHANGED
@@ -23,7 +23,10 @@ unless ENV.has_key?('VERBOSE')
|
|
23
23
|
end
|
24
24
|
|
25
25
|
require 'fluent/plugin/out_anonymizer'
|
26
|
-
|
26
|
+
if Fluent.const_defined?(:Filter)
|
27
|
+
require 'fluent/plugin/filter_anonymizer'
|
28
|
+
end
|
29
|
+
|
27
30
|
|
28
31
|
class Test::Unit::TestCase
|
29
32
|
end
|
@@ -1,7 +1,9 @@
|
|
1
1
|
require 'helper'
|
2
|
+
require 'fluent/test/driver/filter'
|
2
3
|
|
3
4
|
class AnonymizerFilterTest < Test::Unit::TestCase
|
4
5
|
def setup
|
6
|
+
omit_unless(Fluent.const_defined?(:Filter))
|
5
7
|
Fluent::Test.setup
|
6
8
|
@time = Fluent::Engine.now
|
7
9
|
end
|
@@ -17,19 +19,66 @@ class AnonymizerFilterTest < Test::Unit::TestCase
|
|
17
19
|
ipv4_mask_subnet 24
|
18
20
|
]
|
19
21
|
|
20
|
-
def create_driver(conf=CONFIG
|
21
|
-
Fluent::Test::
|
22
|
+
def create_driver(conf=CONFIG)
|
23
|
+
Fluent::Test::Driver::Filter.new(Fluent::Plugin::AnonymizerFilter).configure(conf)
|
22
24
|
end
|
23
25
|
|
24
26
|
def filter(conf, messages)
|
25
27
|
d = create_driver(conf)
|
26
|
-
d.run {
|
28
|
+
d.run(default_tag: 'test') {
|
27
29
|
messages.each {|message|
|
28
|
-
d.
|
30
|
+
d.feed(@time, message)
|
29
31
|
}
|
30
32
|
}
|
31
|
-
filtered = d.
|
32
|
-
filtered.map {|m| m
|
33
|
+
filtered = d.filtered
|
34
|
+
filtered.map {|m| m.last }
|
35
|
+
end
|
36
|
+
|
37
|
+
require 'ostruct'
|
38
|
+
test 'method md5 works correctly' do
|
39
|
+
conv = Fluent::Plugin::AnonymizerFilter::MASK_METHODS[:md5].call(OpenStruct.new)
|
40
|
+
digest = conv.call('value1', 'salt')
|
41
|
+
assert_equal 'd21fe9523421f12daad064fd082913fd', digest
|
42
|
+
end
|
43
|
+
test 'method sha1 works correctly' do
|
44
|
+
conv = Fluent::Plugin::AnonymizerFilter::MASK_METHODS[:sha1].call(OpenStruct.new)
|
45
|
+
digest = conv.call('value2', 'salt')
|
46
|
+
assert_equal 'd2ed8e797065322371012fd8c1a39682987ddb71', digest
|
47
|
+
end
|
48
|
+
test 'method sha256 works correctly' do
|
49
|
+
conv = Fluent::Plugin::AnonymizerFilter::MASK_METHODS[:sha256].call(OpenStruct.new)
|
50
|
+
digest = conv.call('value3', 'salt')
|
51
|
+
assert_equal 'd70daf9654b8a3ba335f8f9f9638a93e8eba6763a0012ac44a928857871abe82', digest
|
52
|
+
end
|
53
|
+
test 'method sha384 works correctly' do
|
54
|
+
conv = Fluent::Plugin::AnonymizerFilter::MASK_METHODS[:sha384].call(OpenStruct.new)
|
55
|
+
digest = conv.call('value4', 'salt')
|
56
|
+
assert_equal '646192f8b1ea905238df589a00a10598a53eb245df4ab14b7e9eccf80c37386c99abe5259ccb2ba950003423fa0790ee', digest
|
57
|
+
end
|
58
|
+
test 'method sha512 works correctly' do
|
59
|
+
conv = Fluent::Plugin::AnonymizerFilter::MASK_METHODS[:sha512].call(OpenStruct.new)
|
60
|
+
digest = conv.call('value5', 'salt')
|
61
|
+
expected = '47c82bfea3783c20e3ba3629f0f827bebf0fa65a9104ada5339e5776e5958f061fe7114bfbe1e9d410aff43c6bee8365adf4fdd072e54ab4fffad820f354f545'
|
62
|
+
assert_equal expected, digest
|
63
|
+
end
|
64
|
+
test 'method uri_path removes path, query parameters, fragment, user and password of uri strings' do
|
65
|
+
conv = Fluent::Plugin::AnonymizerFilter::MASK_METHODS[:uri_path].call(OpenStruct.new)
|
66
|
+
assert_equal '/my/path', conv.call('/my/path', '')
|
67
|
+
assert_equal 'yay/unknown/format', conv.call('yay/unknown/format', '')
|
68
|
+
assert_equal 'http://example.com/', conv.call('http://example.com/path/to/secret', '')
|
69
|
+
assert_equal 'http://example.com/', conv.call('http://example.com/path/to/secret?a=b', '')
|
70
|
+
assert_equal 'http://example.com/', conv.call('http://example.com/path/to/secret#xxx', '')
|
71
|
+
assert_equal 'http://example.com/', conv.call('http://example.com/path/to/secret?a=b#xxx', '')
|
72
|
+
assert_equal 'http://example.com/', conv.call('http://tagomoris:secret!@example.com/', '')
|
73
|
+
assert_equal 'http://example.com/', conv.call('http://tagomoris:secret!@example.com/?a=b#xxx', '')
|
74
|
+
assert_equal 'http://example.com/', conv.call('http://tagomoris:secret!@example.com/path/to/secret?a=b#xxx', '')
|
75
|
+
end
|
76
|
+
test 'method network masks ipaddresses with specified mask bit lengths' do
|
77
|
+
conf = OpenStruct.new(ipv4_mask_bits: 24, ipv6_mask_bits: 104)
|
78
|
+
conv = Fluent::Plugin::AnonymizerFilter::MASK_METHODS[:network].call(conf)
|
79
|
+
assert_equal '192.168.1.0', conv.call('192.168.1.1', '')
|
80
|
+
assert_equal '10.110.18.0', conv.call('10.110.18.9', '')
|
81
|
+
assert_equal '2001:db8:0:8d3:0:8a2e::', conv.call('2001:db8:0:8d3:0:8a2e:70:7344', '')
|
33
82
|
end
|
34
83
|
|
35
84
|
def test_configure
|
@@ -43,6 +92,91 @@ class AnonymizerFilterTest < Test::Unit::TestCase
|
|
43
92
|
assert_equal 'test_salt_string', d.instance.config['hash_salt']
|
44
93
|
end
|
45
94
|
|
95
|
+
test 'masker_for_key generates a lambda for conversion with exact key match' do
|
96
|
+
conf = OpenStruct.new(salt: 's')
|
97
|
+
plugin = create_driver.instance
|
98
|
+
conv = ->(v,salt){ "#{v.upcase}:#{salt}" } # it's dummy for test
|
99
|
+
masker = plugin.masker_for_key(conv, 'kkk', conf)
|
100
|
+
r = masker.call({"k" => "x", "kk" => "xx", "kkk" => "xxx", "kkkk" => "xxxx"})
|
101
|
+
assert_equal "x", r["k"]
|
102
|
+
assert_equal "xx", r["kk"]
|
103
|
+
assert_equal "XXX:s", r["kkk"]
|
104
|
+
assert_equal "xxxx", r["kkkk"]
|
105
|
+
assert_equal 4, r.size
|
106
|
+
end
|
107
|
+
|
108
|
+
test 'masker_for_key_chain generates a lambda for conversion with recursive key fetching' do
|
109
|
+
conf = OpenStruct.new(salt: 's')
|
110
|
+
plugin = create_driver.instance
|
111
|
+
conv = ->(v,salt){ "#{v.upcase}:#{salt}" } # it's dummy for test
|
112
|
+
masker = plugin.masker_for_key_chain(conv, 'a.b.c'.split('.'), conf)
|
113
|
+
event = {
|
114
|
+
'a' => {
|
115
|
+
'b' => { 'c' => 'v', 'd' => 'v' }
|
116
|
+
}
|
117
|
+
}
|
118
|
+
r = masker.call(event)
|
119
|
+
assert_equal 'V:s', r['a']['b']['c']
|
120
|
+
assert_equal 'v', r['a']['b']['d']
|
121
|
+
end
|
122
|
+
|
123
|
+
test 'masker_for_key_pattern generates a lambda for conversion with key pattern match' do
|
124
|
+
conf = OpenStruct.new(salt: 's')
|
125
|
+
plugin = create_driver.instance
|
126
|
+
conv = ->(v,salt){ "#{v.upcase}:#{salt}" } # it's dummy for test
|
127
|
+
masker = plugin.masker_for_key_pattern(conv, '^k+$', conf)
|
128
|
+
r = masker.call({"k" => "x", "kk" => "xx", "kkk" => "xxx", "kkk0" => "xxxx", "f" => "x", "ff" => "xx"})
|
129
|
+
assert_equal "X:s", r["k"]
|
130
|
+
assert_equal "XX:s", r["kk"]
|
131
|
+
assert_equal "XXX:s", r["kkk"]
|
132
|
+
assert_equal "xxxx", r["kkk0"]
|
133
|
+
assert_equal "x", r["f"]
|
134
|
+
assert_equal "xx", r["ff"]
|
135
|
+
assert_equal 6, r.size
|
136
|
+
end
|
137
|
+
|
138
|
+
test 'masker_for_value_pattern' do
|
139
|
+
conf = OpenStruct.new(salt: 's')
|
140
|
+
plugin = create_driver.instance
|
141
|
+
conv = ->(v,salt){ "#{v.upcase}:#{salt}" } # it's dummy for test
|
142
|
+
masker = plugin.masker_for_value_pattern(conv, '^x+$', conf)
|
143
|
+
r = masker.call({"k" => "x", "kk" => "x0", "kkk" => "xx0", "kkk0" => "xxxx", "f" => "x", "ff" => "xx"})
|
144
|
+
assert_equal "X:s", r["k"]
|
145
|
+
assert_equal "x0", r["kk"]
|
146
|
+
assert_equal "xx0", r["kkk"]
|
147
|
+
assert_equal "XXXX:s", r["kkk0"]
|
148
|
+
assert_equal "X:s", r["f"]
|
149
|
+
assert_equal "XX:s", r["ff"]
|
150
|
+
assert_equal 6, r.size
|
151
|
+
end
|
152
|
+
|
153
|
+
test 'masker_for_value_in_subnet' do
|
154
|
+
conf = OpenStruct.new(salt: 's')
|
155
|
+
plugin = create_driver.instance
|
156
|
+
conv = ->(v,salt){ "#{v.upcase}:#{salt}" } # it's dummy for test
|
157
|
+
masker = plugin.masker_for_value_in_subnet(conv, '192.168.0.0/16', conf)
|
158
|
+
r = masker.call({"k1" => "x", "k2" => "192.169.1.1", "k3" => "192.168.128.13", "f1" => "x", "f2" => "10.0.12.1", "f3" => "192.168.1.1"})
|
159
|
+
assert_equal "x", r["k1"]
|
160
|
+
assert_equal "192.169.1.1", r["k2"]
|
161
|
+
assert_equal "192.168.128.13:s", r["k3"]
|
162
|
+
assert_equal "x", r["f1"]
|
163
|
+
assert_equal "10.0.12.1", r["f2"]
|
164
|
+
assert_equal "192.168.1.1:s", r["f3"]
|
165
|
+
end
|
166
|
+
|
167
|
+
test 'filter plugin can mask specified fields' do
|
168
|
+
plugin = create_driver(<<-CONF).instance
|
169
|
+
<mask md5>
|
170
|
+
salt testing
|
171
|
+
key test
|
172
|
+
</mask>
|
173
|
+
CONF
|
174
|
+
r = plugin.filter('tag', Time.now.to_i, {"test" => "value", "name" => "fluentd plugin"})
|
175
|
+
assert_equal 2, r.size
|
176
|
+
assert_equal "fluentd plugin", r["name"]
|
177
|
+
assert_equal "6255093f2e4204e24df48ddd7f4a8abe", r["test"]
|
178
|
+
end
|
179
|
+
|
46
180
|
def test_filter
|
47
181
|
messages = [
|
48
182
|
{
|
@@ -56,11 +190,11 @@ class AnonymizerFilterTest < Test::Unit::TestCase
|
|
56
190
|
]
|
57
191
|
expected = {
|
58
192
|
'host' => '10.102.3.0',
|
59
|
-
'data_for_md5' => '
|
60
|
-
'data_for_sha1' => '
|
61
|
-
'data_for_sha256' => '
|
62
|
-
'data_for_sha384' => '
|
63
|
-
'data_for_sha512' => '
|
193
|
+
'data_for_md5' => '9138bd41172f5485f7b6eee3afcd0d62',
|
194
|
+
'data_for_sha1' => 'ee98db51658d38580b1cf788db19ad06e51a32f7',
|
195
|
+
'data_for_sha256' => 'd53d15615b19597b0f95a984a132ed5164ba9676bf3cb28e018d28feaa2ea6fd',
|
196
|
+
'data_for_sha384' => '6e9cd6d84ea371a72148b418f1a8cb2534da114bc2186d36ec6f14fd5c237b6f2e460f409dda89b7e42a14b7da8a8131',
|
197
|
+
'data_for_sha512' => 'adcf4e5d1e52f57f67d8b0cd85051158d7362103d7ed4cb6302445c2708eff4b17cb309cf5d09fd5cf76615c75652bd29d1707ce689a28e8700afd7a7439ef20'
|
64
198
|
}
|
65
199
|
filtered = filter(CONFIG, messages)
|
66
200
|
assert_equal(expected, filtered[0])
|
@@ -85,9 +219,9 @@ class AnonymizerFilterTest < Test::Unit::TestCase
|
|
85
219
|
expected = {
|
86
220
|
'host' => '10.102.0.0',
|
87
221
|
'host2' => '10.102.0.0',
|
88
|
-
'member_id' => '
|
89
|
-
'mail' => '
|
90
|
-
'telephone' => '
|
222
|
+
'member_id' => '8cb2237d0679ca88db6464eac60da96345513964',
|
223
|
+
'mail' => '914fec35ce8bfa1a067581032f26b053591ee38a',
|
224
|
+
'telephone' => 'ce164718b94212332187eb8420903b46b334d609',
|
91
225
|
'action' => 'signup'
|
92
226
|
}
|
93
227
|
filtered = filter(conf, messages)
|
@@ -118,9 +252,9 @@ class AnonymizerFilterTest < Test::Unit::TestCase
|
|
118
252
|
'host1' => '10.102.0.0'
|
119
253
|
},
|
120
254
|
'nested' => {
|
121
|
-
'data' => '
|
255
|
+
'data' => '8cb2237d0679ca88db6464eac60da96345513964',
|
122
256
|
'nested' => {
|
123
|
-
'data' => '
|
257
|
+
'data' => '8cb2237d0679ca88db6464eac60da96345513964'
|
124
258
|
}
|
125
259
|
}
|
126
260
|
}
|
@@ -142,8 +276,8 @@ class AnonymizerFilterTest < Test::Unit::TestCase
|
|
142
276
|
]
|
143
277
|
expected = {
|
144
278
|
'host' => '10.102.3.0',
|
145
|
-
'array' => ["
|
146
|
-
'hash' => '
|
279
|
+
'array' => ["e3cbba8883fe746c6e35783c9404b4bc0c7ee9eb", "a4ac914c09d7c097fe1f4f96b897e625b6922069"],
|
280
|
+
'hash' => '1a1903d78aed9403649d61cb21ba6b489249761b'
|
147
281
|
}
|
148
282
|
filtered = filter(conf, messages)
|
149
283
|
assert_equal(expected, filtered[0])
|
@@ -169,4 +303,3 @@ class AnonymizerFilterTest < Test::Unit::TestCase
|
|
169
303
|
assert_equal(expected, filtered)
|
170
304
|
end
|
171
305
|
end
|
172
|
-
|
@@ -49,11 +49,11 @@ class AnonymizerOutputTest < Test::Unit::TestCase
|
|
49
49
|
assert_equal 1, emits.length
|
50
50
|
assert_equal 'anonymized.access', emits[0][0] # tag
|
51
51
|
assert_equal '10.102.3.0', emits[0][2]['host']
|
52
|
-
assert_equal '
|
53
|
-
assert_equal '
|
54
|
-
assert_equal '
|
55
|
-
assert_equal '
|
56
|
-
assert_equal '
|
52
|
+
assert_equal '9138bd41172f5485f7b6eee3afcd0d62', emits[0][2]['data_for_md5']
|
53
|
+
assert_equal 'ee98db51658d38580b1cf788db19ad06e51a32f7', emits[0][2]['data_for_sha1']
|
54
|
+
assert_equal 'd53d15615b19597b0f95a984a132ed5164ba9676bf3cb28e018d28feaa2ea6fd', emits[0][2]['data_for_sha256']
|
55
|
+
assert_equal '6e9cd6d84ea371a72148b418f1a8cb2534da114bc2186d36ec6f14fd5c237b6f2e460f409dda89b7e42a14b7da8a8131', emits[0][2]['data_for_sha384']
|
56
|
+
assert_equal 'adcf4e5d1e52f57f67d8b0cd85051158d7362103d7ed4cb6302445c2708eff4b17cb309cf5d09fd5cf76615c75652bd29d1707ce689a28e8700afd7a7439ef20', emits[0][2]['data_for_sha512']
|
57
57
|
end
|
58
58
|
|
59
59
|
def test_emit_multi_keys
|
@@ -79,9 +79,9 @@ class AnonymizerOutputTest < Test::Unit::TestCase
|
|
79
79
|
assert_equal 'anonymized.access', emits[0][0] # tag
|
80
80
|
assert_equal '10.102.0.0', emits[0][2]['host']
|
81
81
|
assert_equal '10.102.0.0', emits[0][2]['host2']
|
82
|
-
assert_equal '
|
83
|
-
assert_equal '
|
84
|
-
assert_equal '
|
82
|
+
assert_equal '8cb2237d0679ca88db6464eac60da96345513964', emits[0][2]['member_id']
|
83
|
+
assert_equal '914fec35ce8bfa1a067581032f26b053591ee38a', emits[0][2]['mail']
|
84
|
+
assert_equal 'ce164718b94212332187eb8420903b46b334d609', emits[0][2]['telephone']
|
85
85
|
assert_equal 'signup', emits[0][2]['action']
|
86
86
|
end
|
87
87
|
|
@@ -110,8 +110,8 @@ class AnonymizerOutputTest < Test::Unit::TestCase
|
|
110
110
|
assert_equal 1, emits.length
|
111
111
|
assert_equal 'anonymized.access', emits[0][0] # tag
|
112
112
|
assert_equal '10.102.0.0', emits[0][2]['hosts']['host1']
|
113
|
-
assert_equal '
|
114
|
-
assert_equal '
|
113
|
+
assert_equal '8cb2237d0679ca88db6464eac60da96345513964', emits[0][2]['nested']['data']
|
114
|
+
assert_equal '8cb2237d0679ca88db6464eac60da96345513964', emits[0][2]['nested']['nested']['data']
|
115
115
|
end
|
116
116
|
|
117
117
|
def test_emit_nest_value
|
@@ -132,8 +132,8 @@ class AnonymizerOutputTest < Test::Unit::TestCase
|
|
132
132
|
assert_equal 1, emits.length
|
133
133
|
assert_equal 'anonymized.access', emits[0][0] # tag
|
134
134
|
assert_equal '10.102.3.0', emits[0][2]['host']
|
135
|
-
assert_equal ["
|
136
|
-
assert_equal '
|
135
|
+
assert_equal ["e3cbba8883fe746c6e35783c9404b4bc0c7ee9eb", "a4ac914c09d7c097fe1f4f96b897e625b6922069"], emits[0][2]['array']
|
136
|
+
assert_equal '1a1903d78aed9403649d61cb21ba6b489249761b', emits[0][2]['hash']
|
137
137
|
end
|
138
138
|
|
139
139
|
def test_emit_ipv6
|
@@ -170,7 +170,7 @@ class AnonymizerOutputTest < Test::Unit::TestCase
|
|
170
170
|
emits = d1.emits
|
171
171
|
assert_equal 1, emits.length
|
172
172
|
assert_equal 'anonymized.message', emits[0][0] # tag
|
173
|
-
assert_equal '
|
173
|
+
assert_equal '8cb2237d0679ca88db6464eac60da96345513964', emits[0][2]['member_id']
|
174
174
|
end
|
175
175
|
|
176
176
|
def test_emit_tag_placeholder
|
@@ -187,7 +187,7 @@ class AnonymizerOutputTest < Test::Unit::TestCase
|
|
187
187
|
emits = d1.emits
|
188
188
|
assert_equal 1, emits.length
|
189
189
|
assert_equal 'anonymized.access', emits[0][0] # tag
|
190
|
-
assert_equal '
|
190
|
+
assert_equal '8cb2237d0679ca88db6464eac60da96345513964', emits[0][2]['member_id']
|
191
191
|
end
|
192
192
|
end
|
193
193
|
|
metadata
CHANGED
@@ -1,94 +1,97 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fluent-plugin-anonymizer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: 1.0.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Kentaro Yoshida
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2017-12-17 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: bundler
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
|
-
- -
|
17
|
+
- - ">="
|
20
18
|
- !ruby/object:Gem::Version
|
21
19
|
version: '0'
|
22
20
|
type: :development
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
|
-
- -
|
24
|
+
- - ">="
|
28
25
|
- !ruby/object:Gem::Version
|
29
26
|
version: '0'
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
28
|
name: rake
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
30
|
requirements:
|
35
|
-
- -
|
31
|
+
- - ">="
|
36
32
|
- !ruby/object:Gem::Version
|
37
33
|
version: '0'
|
38
34
|
type: :development
|
39
35
|
prerelease: false
|
40
36
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
37
|
requirements:
|
43
|
-
- -
|
38
|
+
- - ">="
|
44
39
|
- !ruby/object:Gem::Version
|
45
40
|
version: '0'
|
46
41
|
- !ruby/object:Gem::Dependency
|
47
42
|
name: test-unit
|
48
43
|
requirement: !ruby/object:Gem::Requirement
|
49
|
-
none: false
|
50
44
|
requirements:
|
51
|
-
- - ~>
|
45
|
+
- - "~>"
|
52
46
|
- !ruby/object:Gem::Version
|
53
47
|
version: '3'
|
54
48
|
type: :development
|
55
49
|
prerelease: false
|
56
50
|
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
51
|
requirements:
|
59
|
-
- - ~>
|
52
|
+
- - "~>"
|
60
53
|
- !ruby/object:Gem::Version
|
61
54
|
version: '3'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: appraisal
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
62
69
|
- !ruby/object:Gem::Dependency
|
63
70
|
name: fluentd
|
64
71
|
requirement: !ruby/object:Gem::Requirement
|
65
|
-
none: false
|
66
72
|
requirements:
|
67
|
-
- -
|
73
|
+
- - ">="
|
68
74
|
- !ruby/object:Gem::Version
|
69
75
|
version: '0'
|
70
76
|
type: :runtime
|
71
77
|
prerelease: false
|
72
78
|
version_requirements: !ruby/object:Gem::Requirement
|
73
|
-
none: false
|
74
79
|
requirements:
|
75
|
-
- -
|
80
|
+
- - ">="
|
76
81
|
- !ruby/object:Gem::Version
|
77
82
|
version: '0'
|
78
83
|
- !ruby/object:Gem::Dependency
|
79
84
|
name: fluent-mixin-rewrite-tag-name
|
80
85
|
requirement: !ruby/object:Gem::Requirement
|
81
|
-
none: false
|
82
86
|
requirements:
|
83
|
-
- -
|
87
|
+
- - ">="
|
84
88
|
- !ruby/object:Gem::Version
|
85
89
|
version: '0'
|
86
90
|
type: :runtime
|
87
91
|
prerelease: false
|
88
92
|
version_requirements: !ruby/object:Gem::Requirement
|
89
|
-
none: false
|
90
93
|
requirements:
|
91
|
-
- -
|
94
|
+
- - ">="
|
92
95
|
- !ruby/object:Gem::Version
|
93
96
|
version: '0'
|
94
97
|
description:
|
@@ -98,8 +101,9 @@ executables: []
|
|
98
101
|
extensions: []
|
99
102
|
extra_rdoc_files: []
|
100
103
|
files:
|
101
|
-
- .gitignore
|
102
|
-
- .travis.yml
|
104
|
+
- ".gitignore"
|
105
|
+
- ".travis.yml"
|
106
|
+
- Appraisals
|
103
107
|
- Gemfile
|
104
108
|
- LICENSE
|
105
109
|
- README.md
|
@@ -114,27 +118,26 @@ files:
|
|
114
118
|
homepage: https://github.com/y-ken/fluent-plugin-anonymizer
|
115
119
|
licenses:
|
116
120
|
- Apache License, Version 2.0
|
121
|
+
metadata: {}
|
117
122
|
post_install_message:
|
118
123
|
rdoc_options: []
|
119
124
|
require_paths:
|
120
125
|
- lib
|
121
126
|
required_ruby_version: !ruby/object:Gem::Requirement
|
122
|
-
none: false
|
123
127
|
requirements:
|
124
|
-
- -
|
128
|
+
- - ">="
|
125
129
|
- !ruby/object:Gem::Version
|
126
130
|
version: '0'
|
127
131
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
128
|
-
none: false
|
129
132
|
requirements:
|
130
|
-
- -
|
133
|
+
- - ">="
|
131
134
|
- !ruby/object:Gem::Version
|
132
135
|
version: '0'
|
133
136
|
requirements: []
|
134
137
|
rubyforge_project:
|
135
|
-
rubygems_version:
|
138
|
+
rubygems_version: 2.2.5
|
136
139
|
signing_key:
|
137
|
-
specification_version:
|
140
|
+
specification_version: 4
|
138
141
|
summary: Fluentd filter output plugin to anonymize records with HMAC of MD5/SHA1/SHA256/SHA384/SHA512
|
139
142
|
algorithms. This data masking plugin protects privacy data such as UserID, Email,
|
140
143
|
Phone number, IPv4/IPv6 address and so on.
|