docker-jail 0.1.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.
@@ -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: []