docker-jail 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 634e9904e8cd35a2029b477d5b8976c79619e638
4
+ data.tar.gz: 570a3a47d63e78bdd94f8628caa13a2d9c60a4d6
5
+ SHA512:
6
+ metadata.gz: c44fcdae3de30fee6c93b9036ce8e6d125c201e6b1154ec0c4ce89cfb056ce6327a72c3268e817612f15a4990126b71a3dfae4cc7d2bf39391650b3f8e996462
7
+ data.tar.gz: b27a8bb30d69a11e4839603ef1e1960cb3071b885f179d75b249be8c19e4e156d797fc48d4d6a49fccb7d60ae688aee5725b5b525eba4eace2f1b9072e86d095
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in docker-jail.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 u10e10
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,109 @@
1
+ # DockerJail
2
+
3
+ It's easy to make a jail with Docker.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ gem install docker-jail
9
+ ```
10
+
11
+ ## Documents
12
+
13
+ This gem is documented by YARD.
14
+
15
+ ```bash
16
+ yard server --gems
17
+ ```
18
+
19
+ ### See
20
+ [Docker Engine API](https://docs.docker.com/engine/api/v1.26/)
21
+ [docker-api for ruby](https://github.com/swipely/docker-api)
22
+
23
+
24
+ ## Usage
25
+
26
+ ```ruby
27
+ require 'docker-jail'
28
+ rsecoundequire 'pp'
29
+
30
+ image = 'ruby:alpine'
31
+ user = 'nobody:nobody'
32
+ pids_limit = 10
33
+ cpus = '0' # string
34
+ memory_mb = 100 # 100MB
35
+ timeout = 10 # 10 seconds
36
+ input = StringIO.new('10')
37
+ tmpfs = {'/tmp/a': 'rw,size=65536k'}
38
+
39
+ cmd_list = ['ruby', '-e', 'puts(gets().to_i*2)']
40
+ # cmd_list = ['bash', '-c', 'time df']
41
+ cmd_list = ['sh', '-c', 'time timeout -s SIGKILL -t 2 ruby -e "puts(\"output\");exit(22)"']
42
+
43
+ opts = {cmd_list: cmd_list, image: image, user: user, workdir: '/tmp',
44
+ cpus: cpus, memory_mb: memory_mb, pids_limit: pids_limit, tmpfs: tmpfs}
45
+
46
+ puts 'Create a container'
47
+ jail = DockerJail::Simple.new(opts)
48
+
49
+ puts 'Run with a time limit'
50
+ jail.run_timeout(timeout, input) # {|s,c| puts "#{s}: #{c}"}
51
+
52
+ puts "-------------------------"
53
+ puts "Exit: #{jail.exit_code}"
54
+ puts "Time over: #{jail.timeout?}"
55
+ puts "Memory over: #{jail.oom_killed?}"
56
+
57
+ print 'Stdout: '
58
+ p jail.out
59
+ print 'Stderr: '
60
+ p jail.err
61
+ print 'State: '
62
+ pp jail.state
63
+
64
+ # require 'pry'
65
+ # binding.pry
66
+
67
+ puts 'Delete force'
68
+ jail.delete
69
+ ```
70
+
71
+
72
+ ## KnowHow
73
+
74
+
75
+ ### Share cpu resources equally
76
+ Use `cpus` options.
77
+ If your machine has Intel Hyper-Threading Technology,
78
+ You can share equally by setting two core id to `cpus`.
79
+ e.g. `'0,1' `
80
+
81
+ ##### Check core id.
82
+
83
+ ```bash
84
+ cat /proc/cpuinfo | egrep 'process|core id'
85
+ ```
86
+
87
+
88
+ ### Limit the time
89
+
90
+ The docker-jail has two ways.
91
+
92
+ * First way: use `DockerJail::Base#run_timeout` method.
93
+ This method can limit container execution time.
94
+ But container execution time contains container up/down time.
95
+
96
+ * Second way: use `timeout` command in the container.
97
+ But there may be cases where the container don't have the `timeout` command.
98
+
99
+
100
+
101
+ ### Measure the time
102
+
103
+ Use `time` command in the container.
104
+ You can get result of the `time` command from stderr.
105
+
106
+
107
+ ## License
108
+
109
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+ task :default => :spec
3
+
4
+ require 'yard'
5
+ YARD::Rake::YardocTask.new do |t|
6
+ t.files = ['lib/**/*.rb']
7
+ t.stats_options = ['--list-undoc']
8
+ end
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'docker-jail/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "docker-jail"
8
+ spec.version = DockerJail::VERSION
9
+ spec.authors = ['u+']
10
+ spec.email = ['uplus.e10@gmail.com']
11
+
12
+ spec.summary = %q{Easy run commands in docker jail}
13
+ spec.description = spec.summary
14
+ spec.homepage = 'https://github.com/u10e10/docker-jail'
15
+ spec.license = 'MIT'
16
+ spec.required_ruby_version = '>= 2.3.0'
17
+
18
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
19
+ f.match(%r{^(test|spec|features)/})
20
+ end
21
+ spec.bindir = "exe"
22
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
+ spec.require_paths = ["lib"]
24
+
25
+ spec.add_development_dependency 'bundler', '~> 1.14'
26
+ spec.add_development_dependency 'rake', '~> 10.0'
27
+ spec.add_development_dependency 'docker-api', '~> 10.0'
28
+ spec.add_development_dependency 'rspec'
29
+ spec.add_development_dependency 'yard'
30
+ end
@@ -0,0 +1,8 @@
1
+ require 'docker-jail/helper'
2
+ require 'docker-jail/version'
3
+
4
+ module DockerJail
5
+ end
6
+
7
+ require 'docker-jail/base'
8
+ require 'docker-jail/simple'
@@ -0,0 +1,139 @@
1
+ require 'docker'
2
+ require 'timeout'
3
+
4
+ module DockerJail
5
+ # Execute command in jail.
6
+ class Base
7
+ @@base_opts = {
8
+ # Image: 'centos',
9
+ # Cmd: ['ls', '-a'],
10
+ # User: 'user:group', # (defaults: root:root)
11
+ OpenStdin: true,
12
+ StdinOnce: true,
13
+ Tty: false,
14
+ AttachStdin: true,
15
+ AttachStderr: true,
16
+ NetworkDisabled: true,
17
+ # WorkingDir: '/',
18
+ HostConfig: {
19
+ # CpusetCpus: '0' # (String) e.g. '0', '0,2', '0-3',
20
+ # Memory: 0, # Memory byte size. 0 is unlimited
21
+ PidsLimit: 15,
22
+ NetworkMode: 'none',
23
+ CapDrop: 'all',
24
+ ReadonlyRootfs: true,
25
+ OomKillDisable: false,
26
+ AutoRemove: false,
27
+ # LogConfig: {Type: 'none'}
28
+ # Binds: [],
29
+ # Tmpfs: {},
30
+ }
31
+ }.freeze
32
+
33
+ # @attr_reader [Docker::Container] container docker-api's raw container
34
+ # @attr_reader [Array<String>] out stdout outputs
35
+ # @attr_reader [Array<String>] err stderr outputs
36
+ attr_reader :container, :out, :err
37
+
38
+
39
+ def self.get_all()
40
+ Docker::Container.all(all:true)
41
+ end
42
+
43
+ def self.delete_all()
44
+ Docker::Container.all(all:true).each{|c| c.delete(force:true)}
45
+ end
46
+
47
+ def self.build_option(**opts)
48
+ @@base_opts.merge(opts)
49
+ end
50
+
51
+ def self.build_container(**opts)
52
+ Docker::Container.create(build_option(opts))
53
+ end
54
+
55
+ # @option opts [Hash] opts options to create Docker container
56
+ # @see https://docs.docker.com/engine/api/v1.26/ Docker API Reference
57
+ # @example
58
+ # DockerJail::Base.new(Image: 'centos', Cmd: ['ls', '-a'], HostConfig: {Memory: 10*1024})
59
+ def initialize(**opts)
60
+ @container = self.class.build_container(opts)
61
+ end
62
+
63
+ # Run container
64
+ # @param [StringIO] input Give input stream to stdin
65
+ # @yield deprecated. Because docker-api's block behavior is unstable
66
+ # @yieldparam [String] stream_name
67
+ # @yieldparam [String] chunk stdout or stderr partial string
68
+ # @return [Array<String>, Array<String>] stdout and stderr
69
+ def run(input=nil, &block)
70
+ @out, @err = @container.tap(&:start).attach(logs: true, tty: false, stdin: input, &block)
71
+ end
72
+
73
+ # Run container with a time limit.
74
+ # Container run in the sub thread.
75
+ # the timeout value contain the container up time.
76
+ # When container run timeout, value of #out and #err is undefined
77
+ # @param (see #run)
78
+ # @param [Numeric] timeout
79
+ # execution time limit(seconds).
80
+ # It's different from StopTimeout of Docker
81
+ # @yield (see #run)
82
+ # @yieldparam (see #run)
83
+ # @return (see #run)
84
+ # @raise [Timeout::Error] When container run timeout, raise Timeout::Error
85
+ def run_timeout(timeout, input=nil, &block)
86
+ @timeout = false
87
+ # Main thread
88
+ Timeout.timeout(timeout) do
89
+ # Sub thread
90
+ return run(input, &block)
91
+ end
92
+ rescue Timeout::Error
93
+ @timeout = true
94
+ end
95
+
96
+ # Force delete container
97
+ def delete
98
+ @container.delete(force: true)
99
+ end
100
+
101
+ # @return [nil] Time unlimited
102
+ # @return [true] Time limit exceeded
103
+ # @return [false] Finished within time limit
104
+ def timeout?
105
+ @timeout
106
+ end
107
+
108
+ # @return [Hash] The container's all state and options
109
+ def json
110
+ container.json
111
+ end
112
+
113
+ # @return [Hash]
114
+ def state
115
+ json['State']
116
+ end
117
+
118
+ # Memory limit exceeded
119
+ # @return [Bool]
120
+ def oom_killed?
121
+ state['OOMKilled']
122
+ end
123
+
124
+ # @return [Integer]
125
+ def exit_code
126
+ state['ExitCode']
127
+ end
128
+
129
+ # @return [Time]
130
+ def started_at
131
+ Time.parse(state['StartedAt'])
132
+ end
133
+
134
+ # @return [Time]
135
+ def finished_at
136
+ Time.parse(state['FinishedAt'])
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,16 @@
1
+ module DockerJail
2
+ module ClassExtensions
3
+ refine Hash do
4
+
5
+ break if 2.4 <= RUBY_VERSION.to_f
6
+
7
+ def compact
8
+ reject{|k, v| v.nil? }
9
+ end
10
+
11
+ def compact!
12
+ delete_if{|k, v| v.nil? }
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,40 @@
1
+ module DockerJail
2
+ class Simple < Base
3
+ using DockerJail::ClassExtensions
4
+
5
+ # @param [String] image Container base image
6
+ # @param [Array<String>] cmd_list Execute command and parameters
7
+ # @param [String] user (nil) Docker container's default user is 'root:root'.
8
+ # @param [String] cpus (nil)
9
+ # @param [Integer] memory_mb (nil) Memory limit in MB. 0 is unlimited
10
+ # @param [Integer] pids_limit (nil)
11
+ # @param [Array<String>] binds (nil) Bind mount directories
12
+ # @param [String] workdir (nil) The working directory.
13
+ # @param [Hash] tmpfs (nil) Mount empty tmpfs
14
+ # @option opts [Hash] opts Other options to create Docker container
15
+ def initialize(image:, cmd_list:, user: nil, workdir: nil, cpus: nil, memory_mb: nil,
16
+ pids_limit: nil, binds: nil, tmpfs: nil, **other_opts)
17
+ mem_size = memory_mb&.*(1024**2)
18
+ other_host_opts = other_opts.delete(:HostConfig)
19
+
20
+ host_opts = {
21
+ CpusetCpus: cpus,
22
+ Memory: mem_size,
23
+ MemorySwap: mem_size,
24
+ PidsLimit: pids_limit,
25
+ Binds: binds,
26
+ Tmpfs: tmpfs,
27
+ }.compact.merge(other_host_opts || {})
28
+
29
+ opts = {
30
+ Image: image,
31
+ Cmd: cmd_list,
32
+ User: user,
33
+ WorkingDir: workdir,
34
+ HostConfig: host_opts,
35
+ }.compact.merge(other_opts)
36
+
37
+ super(opts.merge(other_opts || {}))
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,3 @@
1
+ module DockerJail
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,125 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: docker-jail
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - u+
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-02-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.14'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.14'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: docker-api
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
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'
69
+ - !ruby/object:Gem::Dependency
70
+ name: yard
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Easy run commands in docker jail
84
+ email:
85
+ - uplus.e10@gmail.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - Gemfile
92
+ - LICENSE.txt
93
+ - README.md
94
+ - Rakefile
95
+ - docker-jail.gemspec
96
+ - lib/docker-jail.rb
97
+ - lib/docker-jail/base.rb
98
+ - lib/docker-jail/helper.rb
99
+ - lib/docker-jail/simple.rb
100
+ - lib/docker-jail/version.rb
101
+ homepage: https://github.com/u10e10/docker-jail
102
+ licenses:
103
+ - MIT
104
+ metadata: {}
105
+ post_install_message:
106
+ rdoc_options: []
107
+ require_paths:
108
+ - lib
109
+ required_ruby_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: 2.3.0
114
+ required_rubygems_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ requirements: []
120
+ rubyforge_project:
121
+ rubygems_version: 2.6.8
122
+ signing_key:
123
+ specification_version: 4
124
+ summary: Easy run commands in docker jail
125
+ test_files: []