moby-derp 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.
data/README.md ADDED
@@ -0,0 +1,231 @@
1
+ This tool is designed to securely manage a group of related containers, known
2
+ colloquially as a ["pod"](https://kubernetes.io/docs/concepts/workloads/pods/pod/),
3
+ under the Moby container management system.
4
+
5
+ It has no aspirations to be a fully-fledged multi-host container orchestation system;
6
+ instead, it is simply a means to create and maintain a pod of containers.
7
+
8
+ The most common use-case for `moby-derp` is to allow unprivileged users to
9
+ define a pod, and then allow those users to execute `moby-derp` as a privileged
10
+ user via `sudo`. Since this removes the need for random users to have direct
11
+ control over the moby daemon, a lot of potential [privilege escalation
12
+ attacks](https://fosterelli.co/privilege-escalation-via-docker.html)
13
+ facilitated by moby's security model are thwarted.
14
+
15
+
16
+ # Installation
17
+
18
+ It's a gem:
19
+
20
+ gem install moby-derp
21
+
22
+ If you're the sturdy type that likes to run from git:
23
+
24
+ rake install
25
+
26
+ Or, if you've eschewed the convenience of Rubygems entirely, then you
27
+ presumably know what to do already.
28
+
29
+
30
+ # Usage
31
+
32
+ The main interface for `moby-derp` is the command-line tool of the same name.
33
+ It takes as its sole argument a YAML file containing a whole pile of information
34
+ about the pod and how you want the containers within it to be run. A very simple
35
+ example file might look like this:
36
+
37
+ publish:
38
+ - 80:80
39
+ containers:
40
+ nginx:
41
+ image: 'nginx:latest'
42
+ mounts:
43
+ - source: nginx
44
+ target: /etc/nginx
45
+ - source: content
46
+ target: /var/www
47
+ content_puller:
48
+ image: 'example/content_puller'
49
+ mounts:
50
+ - source: content
51
+ target: /puller
52
+
53
+ For full details on exactly what can be done using the pod configuration file,
54
+ please see [`example.yml`](example.yml), which is a heavily-commented example
55
+ of every possible configuration option that can be set.
56
+
57
+ Once you have a pod config, saved in, say, `my-pod.yml`, you can tell `moby-derp`
58
+ to run it:
59
+
60
+ moby-derp ./my-pod.yml
61
+
62
+ Often, however, you won't have the ability to contact the moby daemon directly,
63
+ so you'll run `moby-derp` via `sudo`:
64
+
65
+ sudo moby-derp ./my-pod.yml
66
+
67
+ Where this comes in handy is that `sudo` can be configured to restrict the
68
+ set of commands that a given user is allowed to run. So, for instance, if the
69
+ user who wants to run this pod is named `bob` on the system, I can put this
70
+ in my `/etc/sudoers`:
71
+
72
+ bob ALL=(root) NOPASSWD: /usr/local/bin/moby-derp */my-pod.yml
73
+
74
+ Then `bob` (and *only* `bob`) can run
75
+
76
+ sudo moby-derp ./my-pod.yml
77
+
78
+ The name of the pod is taken from the name of the file, with any extension
79
+ removed. This is used as the prefix for the name of all containers in the pod.
80
+
81
+
82
+ ## System Configuration
83
+
84
+ Some aspects of `moby-derp`'s operation are security-sensitive, and thus shouldn't
85
+ be able to be modified by the ordinary user. There is a
86
+ system-wide configuration file for this purpose, by default located at
87
+ `/etc/moby-derp.yml`.
88
+
89
+ Its structure is quite simple. A full example looks like this:
90
+
91
+ mount_root: '/srv/docker'
92
+ port_whitelist:
93
+ 80: web-server
94
+ 443: web-server
95
+ 25: mail-server
96
+ 1337: bobblehead
97
+
98
+ The keys are:
99
+
100
+ * **`mount_root`**: the directory on disk where all mounts for all pods will
101
+ be stored. For security, the filesystem on which this location resides
102
+ should really be mounted `nosuid` and, if you can swing it, even `noexec`.
103
+ The directory specified by this option must already exist.
104
+
105
+ * **`port_whitelist`**: a map of port numbers and the pod which should be
106
+ allowed to map them. Any port which is not listed here cannot be explicitly
107
+ mapped by a pod, and only the pod named in the mapping can publish on the
108
+ specified port.
109
+
110
+ If you wish to modify the location of the `moby-derp` system-wide configuration
111
+ file, you can do so by setting the `MOBY_DERP_SYSTEM_CONFIG_FILE` environment
112
+ variable. Note, however, that it is a terrible idea to let ordinary users control
113
+ that environment variable, so if you want to set it, please write a small
114
+ wrapper script, like this:
115
+
116
+ #!/bin/sh
117
+
118
+ set -e
119
+
120
+ MOBY_DERP_SYSTEM_CONFIG_FILE=/opt/srv/etc/moby-derp/moby-derp.yaml
121
+
122
+ exec /usr/local/bin/moby-derp "$@"
123
+
124
+ You can also set other relevant environment variables, such as `DOCKER_HOST`,
125
+ in a like manner, if required.
126
+
127
+
128
+ ## Running `moby-derp` as a non-root user
129
+
130
+ If you are properly cautious, it is a fine idea to not allow `moby-derp` itself
131
+ root access to the system. This is possible, although it comes with a few
132
+ caveats:
133
+
134
+ * The `sudoers` config needs to be adjusted appropriately, to specify the
135
+ user that you want to run `moby-derp` as;
136
+
137
+ * The system-wide configuration file needs to be readable by whatever user
138
+ you run `moby-derp` as;
139
+
140
+ * When running `moby-derp`, the user to run it as needs to be specified on
141
+ the command line, like so:
142
+
143
+ sudo -u moby-derper moby-derp ./my-pod.yml
144
+
145
+ * The user that runs `moby-derp` must have access to the Docker control socket;
146
+ by default that means making that user a member of the `docker` group;
147
+
148
+ * The user that runs `moby-derp` must have write access to the `mount_root`
149
+ directory, so it can create pod mount roots.
150
+
151
+
152
+ # Security
153
+
154
+ This section discusses the security model and guarantees of `moby-derp`. It
155
+ isn't necessary to simply use `moby-derp` in most circumstances.
156
+
157
+ The fundamental principle of `moby-derp` is that users are given control over
158
+ a certain portion of the container and filesystem namespace, by virtue of their
159
+ ability to run `moby-derp` with a specific filename -- and nothing more. The
160
+ containers that a user can modify, and the portions of the filesystem that they
161
+ can write files to, is strictly limited by the tool.
162
+
163
+
164
+ ## Container namespace
165
+
166
+ All containers associated with a pod are named for the filename that is passed
167
+ to `moby-derp`. This means that, yes, different users need to use different
168
+ filenames. The benefit of this is that the `sudo` configuration becomes a lot
169
+ easier to audit -- the pod name is right there.
170
+
171
+ This means that no matter a user does, they cannot have any effect on any
172
+ container which is not named for the pod they're manipulating. There are also
173
+ safety valves around `moby-derp`-managed containers being labelled as such, so
174
+ that in the event that someone does inadvertently name a container in such a
175
+ way that it can be manipulated by another user via `moby-derp`, it should still
176
+ be safe from tampering.
177
+
178
+
179
+ ## Filesystem
180
+
181
+ All references to the host side of mounts within the pod are made relative to
182
+ the pod mount root, which is a subdirectory under the directory specified
183
+ under the `mount_root` system configuration named for the pod. As one would
184
+ expect, attempts to use absolute paths, or parent directory references (via
185
+ `..`) will not be looked upon kindly.
186
+
187
+ To prevent attacks around modifying file permissions in the container and then
188
+ using that on the host to escalate privileges, it is *STRONGLY* recommended
189
+ that the filesystem which holds the `mount_root` be mounted `nosuid`, and even
190
+ `noexec` if that isn't going to get in the way of your use-case. You
191
+ can also use the `userns-remap` feature if that is compatible with your use
192
+ of Moby.
193
+
194
+
195
+ ## Network ports
196
+
197
+ By default, `moby-derp` will not allow containers to publish to specific ports
198
+ on the host. The expectation is that network traffic will, for the most part,
199
+ rely on direct connection to containers, using either host-based proxies,
200
+ overlay networks, or new-fangled technologies like IPv6. This prevents
201
+ misbehaving pods from capturing ports intended for use by other pods. Pods can
202
+ still publish to ephemeral ports (using the `:containerPort` syntax, or
203
+ `publish_all: true`) if they wish.
204
+
205
+ If a pod *does* need to bind to a specific host port, then that pod/port pair
206
+ should be whitelisted in the system configuration file.
207
+
208
+
209
+ # Contributing
210
+
211
+ See [`CONTRIBUTING.md`](CONTRIBUTING.md).
212
+
213
+
214
+ # Licence
215
+
216
+ Unless otherwise stated, everything in this repo is covered by the following
217
+ copyright notice:
218
+
219
+ Copyright (C) 2019 Matt Palmer <matt@hezmatt.org>
220
+
221
+ This program is free software: you can redistribute it and/or modify it
222
+ under the terms of the GNU General Public License version 3, as
223
+ published by the Free Software Foundation.
224
+
225
+ This program is distributed in the hope that it will be useful,
226
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
227
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
228
+ GNU General Public License for more details.
229
+
230
+ You should have received a copy of the GNU General Public License
231
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
data/bin/moby-derp ADDED
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "moby_derp/system_config"
4
+ require "moby_derp/pod_config"
5
+ require "moby_derp/pod"
6
+
7
+ require "docker-api"
8
+
9
+ if ARGV.length != 1
10
+ $stderr.puts "No config file specified"
11
+ exit 1
12
+ end
13
+
14
+ logger = Logger.new($stdout)
15
+ logger.formatter = ->(s, t, p, m) { "#{m}\n" }
16
+
17
+ case ENV["MOBY_DERP_LOG_LEVEL"]
18
+ when /\Adebug|info|warn|error\z/i
19
+ logger.level = Logger.const_get(ENV["MOBY_DERP_LOG_LEVEL"].upcase.to_sym)
20
+ when /\Atrace\z/i
21
+ logger.level = Logger::DEBUG
22
+ require "tracer"
23
+ Tracer.add_filter { |_e, _f, _l, _id, _b, klass, *_| klass.to_s =~ /MobyDerp/ }
24
+ Tracer.on
25
+ else
26
+ logger.level = Logger::INFO
27
+ end
28
+
29
+ begin
30
+ system_config_file = ENV["MOBY_DERP_SYSTEM_CONFIG_FILE"] || "/etc/moby-derp.conf"
31
+ system_config = MobyDerp::SystemConfig.new(system_config_file, Docker.info, logger)
32
+ rescue MobyDerp::ConfigurationError => ex
33
+ $stderr.puts "There was an error reading the system config file #{system_config_file}: #{ex.message}"
34
+ exit 1
35
+ rescue Excon::Error::Socket
36
+ $stderr.puts "Could not connect to the Moby server. Is it running?"
37
+ exit 1
38
+ end
39
+
40
+ begin
41
+ pod_config = MobyDerp::PodConfig.new(ARGV.first, system_config)
42
+ rescue MobyDerp::ConfigurationError => ex
43
+ $stderr.puts "There was an error reading the pod config file #{ARGV.first}: #{ex.message}"
44
+ exit 1
45
+ end
46
+
47
+ pod = MobyDerp::Pod.new(pod_config)
48
+
49
+ begin
50
+ pod.run
51
+ rescue MobyDerp::Error => ex
52
+ $stderr.puts "There was an error derping #{pod.name}: #{ex.message}"
53
+ exit 1
54
+ end
55
+
56
+ exit 0
data/example.yml ADDED
@@ -0,0 +1,297 @@
1
+ # This is an example pod specification file for moby-derp. It contains all
2
+ # the possible configuration options, with comments describing their use.
3
+ #
4
+ # The name of a pod spec file is important, because the file name is used as
5
+ # the name of the pod to manage, with any extension removed. Because of the
6
+ # limitations on container names imposed by Moby, this file name must, thus,
7
+ # start with a alphanumeric character, and consist entirely of alphanumeric
8
+ # characters, underscores, and hyphens.
9
+ #
10
+
11
+ # SECTION 1: CONTAINERS
12
+ #
13
+ # This is really what we're all here for, so let's get straight into it.
14
+ #
15
+ containers:
16
+ # The containers section contains a map of container names to the
17
+ # configuration parameters of that container. Each container's name
18
+ # must consist entirely of alphanumeric characters, underscores, and
19
+ # hyphens.
20
+ #
21
+ flingle:
22
+ # The most important parameter for a container -- and, in fact,
23
+ # the *only* mandatory one -- is the container **image**. The
24
+ # value given here must either be a symbolic image reference
25
+ # (name[:tag][@digest]) or a full image ID, including the digest
26
+ # identifier (`sha256:xyzzy123`).
27
+ #
28
+ image: 'moby-derp/flingle:latest'
29
+
30
+ # By default, if a symbolic image spec is provided in the `image` option,
31
+ # `moby-derp` will pull that image and recreate the container if the image
32
+ # ID has changed from what is currently running. If, for some reason, you
33
+ # do *not* want this behaviour for a specific container, set this option to
34
+ # `false`.
35
+ #
36
+ # Note that, in all cases, if the image ID underlying a symbolic
37
+ # image spec changes in the local daemon's image store, the container will
38
+ # be restarted.
39
+ #
40
+ update_image: false
41
+
42
+ # Many moby images require a command, or at least command-line
43
+ # options, in order to run correctly. To do this, set this
44
+ # key to whatever you need to pass.
45
+ #
46
+ # You can specify the command as a string, or as an array of strings if you
47
+ # want to avoid a layer or two of shell quoting.
48
+ #
49
+ command: '--foo=bar --wombat'
50
+
51
+ # The 12-factor app concept says that all configuration should be passed
52
+ # via the environment. If that is your bag, then this section will make
53
+ # you very happy.
54
+ #
55
+ environment:
56
+ # The environment is specified as a map of variable name to variable
57
+ # values. For passing big strings, you may want to get familiar with
58
+ # YAML's many string quoting and escaping modes.
59
+ #
60
+ APP_ENV: production
61
+ # Embedding your private key like this isn't necessarily a particularly
62
+ # good idea, but as an example of where you might need multiline strings,
63
+ # it works very well.
64
+ #
65
+ PRIVATE_KEY: |
66
+ -----BEGIN PRIVATE KEY-----
67
+ AAAAsd...
68
+ -----END PRIVATE KEY-----
69
+
70
+ # Persisting data past the lifetime of a particular container is the job
71
+ # of mounts. Here you can specify filesystem locations on the host
72
+ # (relative to the pod's mount root) and where they should be
73
+ # mounted in the container.
74
+ #
75
+ # The list of mounts is an array of hashes, each of which specifies
76
+ # one mount.
77
+ #
78
+ mounts:
79
+ # The `source` parameter is a *relative* path (relative to the pod's
80
+ # mount root) on the host's filesystem. Typically it will be a
81
+ # single descriptive word, but you can delve into subdirectories if
82
+ # you so choose.
83
+ #
84
+ - source: app
85
+ # The `target` is an absolute path within the container, where the
86
+ # associated host directory should be mounted.
87
+ #
88
+ target: /app
89
+
90
+ - source: static-assets
91
+ target: /app/public
92
+
93
+ # By default, mounts are read-write -- that is, if a process in the
94
+ # container has the relevant permissions, files in the mount can
95
+ # be modified, created, and deleted. If you absolutely, positively
96
+ # want to make sure that can't happen, set `readonly: true`, and we'll
97
+ # tell Moby to make it so.
98
+ #
99
+ readonly: true
100
+
101
+ # Sometimes you need to add a label to a specific container.
102
+ # This is how you do that.
103
+ #
104
+ labels:
105
+ # It's very simple: `labelname: labelvalue`.
106
+ #
107
+ somelabel: 'Some mighty value'
108
+
109
+ # Mark the container's *root* filesystem as read-only. Not *typically*
110
+ # a good idea.
111
+ #
112
+ readonly: true
113
+
114
+ # Specify the signal that should be sent to the container in order to
115
+ # cause it to stop gracefully. Can be specified as a number, or a
116
+ # string with or without the leading `SIG`.
117
+ #
118
+ stop_signal: SIGQUIT
119
+
120
+ # How long, in seconds, to wait for the container to gracefully stop
121
+ # before whacking it with a `SIGKILL`.
122
+ #
123
+ stop_timeout: 42
124
+
125
+ # Adjust the container's restart policy.
126
+ #
127
+ restart: on-failure:42
128
+
129
+ # Here you can place limits on your containers, to prevent them
130
+ # accidentally blowing up the world. Note that, in keeping with
131
+ # moby-derp's philosophy of limited privilege, you can only reduce
132
+ # the limits on a container, not raise them above what you would
133
+ # get by default (a bit like ulimits for non-root users).
134
+ #
135
+ limits:
136
+ # How many CPUs' worth of CPU time to allow this container to
137
+ # use.
138
+ #
139
+ cpus: 1.75
140
+
141
+ # A relative "share" of CPU time, calculated as a ratio of the
142
+ # total CPU shares granted to all cgroups. The default of 1024
143
+ # is the maximum you can set.
144
+ #
145
+ cpu-shares: 72
146
+
147
+ # Set memory and swap limits.
148
+ #
149
+ # These two settings have complicated inter-relationships. You
150
+ # definitely want to refer to
151
+ # https://docs.docker.com/config/containers/resource_constraints/#--memory-swap-details
152
+ # before using these. You may also want to look at
153
+ # https://docs.docker.com/install/linux/linux-postinstall/#your-kernel-does-not-support-cgroup-swap-limit-capabilities
154
+ #
155
+ memory: 1G
156
+ memory-swap: 42M
157
+
158
+ # The "soft limit" for memory usage -- if your container's memory
159
+ # usage is above this limit, and the overall system is running low on
160
+ # memory, this container will be pressured to reduce its memory
161
+ # usage.
162
+ #
163
+ memory-reservation: 100M
164
+
165
+ # Increase a container's propensity to have its processes whacked by
166
+ # the OOM killer, if such a thing becomes necessary. The value must
167
+ # be in the range 0 to 1000.
168
+ #
169
+ oom-score-adj: 42
170
+
171
+ # Limit the number of processes this container is allowed to create.
172
+ #
173
+ pids: 65535
174
+
175
+ # The amount of shared memory the container is allowed to use.
176
+ #
177
+ shm-size: 42G
178
+
179
+ # Set ulimits.
180
+ #
181
+ # All of the below settings are related to ulimits, which are the
182
+ # old-school, but still surprisingly effective, version of resource
183
+ # limits, from before we had cgroups. All of the values take the
184
+ # form `<soft limit>[:<hard limit>]`, and if `<hard limit>` is not
185
+ # provided, it is set equal to the specified `<soft limit>`. The
186
+ # soft limit must be less than or equal to the hard limit.
187
+ #
188
+ # See `getrlimit`(2) for what each limit means, as well as
189
+ # https://docs.docker.com/engine/reference/commandline/#set-ulimits-in-container---ulimit
190
+ # for Moby-specific restrictions and caveats. As an additional
191
+ # undocumented caveat, if you want to set no limit for a resource,
192
+ # specify the string "unlimited".
193
+ #
194
+ # No value, for hard *or* soft limits, can be set higher than the
195
+ # hard limits for the `dockerd` process, although you won't know
196
+ # that you've broken the limits until `moby-derp` tries to start the
197
+ # container and the wheels fall off.
198
+ #
199
+ ulimit-core: 1:2
200
+ ulimit-cpu: 3:4
201
+ ulimit-data: 5:6
202
+ ulimit-fsize: 7:8
203
+ ulimit-memlock: 9:10
204
+ ulimit-msgqueue: 11:12
205
+ ulimit-nofile: 13:14
206
+ ulimit-rttime: 15:16
207
+ ulimit-stack: 17:18
208
+
209
+ # SECTION 2: POD-LEVEL CONFIGURATION
210
+ #
211
+ # The remainder of the configuration items in this file correspond to settings
212
+ # which apply to the pod -- either defaults for all containers in the pod, or
213
+ # else settings which only apply to the "root" container in the pod (the container
214
+ # which holds the networking, IPC, and PID namespaces).
215
+
216
+ # Set a custom hostname for the container.
217
+ #
218
+ # The default is `<podname>-<host hostname>`, which works surprisingly well
219
+ # in a majority of cases. It's certainly less wild than the Moby default
220
+ # of the short container ID.
221
+ #
222
+ hostname: bobtown
223
+
224
+ # Common environment which will apply to all containers.
225
+ #
226
+ # These environment variables will be set for all containers, including the
227
+ # pod "root" container. Individual containers can override the value for an
228
+ # environment variable, by setting their own value, but they *cannot* cause
229
+ # an environment variable to be unset.
230
+ #
231
+ common_environment:
232
+ # As is the case for container-level environment variables, each key/value
233
+ # pair in the map is one environment variable name to value mapping.
234
+ #
235
+ AWESOME: yes
236
+
237
+ # Common labels which will apply to all containers.
238
+ #
239
+ # These labels will be applied to every container in the pod, including the
240
+ # pod "root" container. Like common environment variables, an individual
241
+ # container can override the value of a common label, but cannot cause the
242
+ # label to be unset entirely.
243
+ #
244
+ common_labels:
245
+ mah_pod: is on fire
246
+
247
+ # Labels that *only* apply to the "root" container of the pod.
248
+ #
249
+ # Some labels, often those associated with a container's network config
250
+ # (like service discovery), should only be applied to the pod's "root"
251
+ # container. Those labels should be defined here.
252
+ #
253
+ root_labels:
254
+ service: over here
255
+
256
+ # Common mount definitions that should apply to all containers.
257
+ #
258
+ # If you have a mount that every container in the pod should have access
259
+ # to, you really don't want to have to specify it every time. Instead,
260
+ # you can put them here. The format is the same as the per-container
261
+ # `mounts` option.
262
+ #
263
+ common_mounts:
264
+ - source: app
265
+ target: /app
266
+ - source: static-assets
267
+ target: /app/public
268
+ readonly: true
269
+
270
+ # A list of ports in the pod to expose.
271
+ #
272
+ # Marking a port as "exposed" interacts with Moby's "publish all" option,
273
+ # as well as with service advertisement systems.
274
+ #
275
+ expose:
276
+ - 80
277
+ - 443
278
+ - 1337
279
+
280
+ # A list of port publishing specifications.
281
+ #
282
+ # If you wish to use Moby's port forward/proxying mechanisms, you do that by
283
+ # "publishing" the port(s) you want to use. Note that, to prevent errant pods
284
+ # from capturing ports they're not supposed to, by default pods can only publish
285
+ # to ephemeral ports (no `hostPort` specification). If a particular pod
286
+ # *should* be able to publish a specific host port, it needs to be whitelisted
287
+ # in the system-wide configuration (see the `port_whitelist` documentation
288
+ # in the `moby-derp` README).
289
+ #
290
+ publish:
291
+ - :80
292
+ - :1234-1237
293
+
294
+ # If you have a burning desire to have all exposed ports automatically published
295
+ # to (not-so-)randomly chosen ephemeral ports, you can set this option to `true`.
296
+ #
297
+ publish_all: true
@@ -0,0 +1,23 @@
1
+ module Docker
2
+ class Image
3
+ # https://github.com/docker/distribution/blob/master/reference/reference.go
4
+ # as at 2019-04-23
5
+ DIGEST_HEX = /[0-9a-fA-F]{32,}/
6
+ DIGEST_ALGORITHM_COMPONENT = /[A-Za-z][A-Za-z0-9]*/
7
+ DIGEST_ALGORITHM_SEPARATOR = /[+._-]/
8
+ DIGEST_ALGORITHM = /#{DIGEST_ALGORITHM_COMPONENT}(#{DIGEST_ALGORITHM_SEPARATOR}#{DIGEST_ALGORITHM_COMPONENT})*/
9
+ DIGEST = /#{DIGEST_ALGORITHM}:#{DIGEST_HEX}/
10
+
11
+ TAG = /[\w][\w.-]{0,127}/
12
+
13
+ SEPARATOR = /[_.]|__|[-]*/
14
+ ALPHANUMERIC = /[a-z0-9]+/
15
+ PATH_COMPONENT = /#{ALPHANUMERIC}(#{SEPARATOR}#{ALPHANUMERIC})*/
16
+ PORT_NUMBER = /\d+/
17
+ DOMAIN_COMPONENT = /([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])/
18
+ DOMAIN = /#{DOMAIN_COMPONENT}(\.#{DOMAIN_COMPONENT})*(:#{PORT_NUMBER})?/
19
+ NAME = /(#{DOMAIN}\/)?#{PATH_COMPONENT}(\/#{PATH_COMPONENT})*/
20
+ IMAGE_REFERENCE = /\A#{NAME}(:#{TAG})?(@#{DIGEST})?\z/
21
+ end
22
+ end
23
+
@@ -0,0 +1,33 @@
1
+ require_relative "./error"
2
+ require_relative "./logging_helpers"
3
+
4
+ require "safe_yaml"
5
+
6
+ module MobyDerp
7
+ class ConfigFile
8
+ include LoggingHelpers
9
+
10
+ attr_reader :logger
11
+
12
+ def initialize(filename)
13
+ begin
14
+ @logger.debug(logloc) { "Reading configuration file #{filename}" }
15
+ @config = SafeYAML.load(File.read(filename))
16
+ rescue Errno::ENOENT
17
+ raise ConfigurationError,
18
+ "file does not exist"
19
+ rescue Errno::EPERM
20
+ raise ConfigurationError,
21
+ "cannot read file"
22
+ rescue Psych::SyntaxError => ex
23
+ raise ConfigurationError,
24
+ "invalid YAML syntax: #{ex.message}"
25
+ end
26
+
27
+ unless @config.is_a?(Hash)
28
+ raise ConfigurationError,
29
+ "invalid file contents -- must be a map"
30
+ end
31
+ end
32
+ end
33
+ end