├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── Makefile
├── README.md
├── SECURITY.md
├── example
├── multiple-process-image
│ ├── Dockerfile
│ ├── Makefile
│ ├── environment
│ │ ├── default.startup.yaml
│ │ └── default.yaml
│ └── service
│ │ ├── nginx
│ │ ├── install.sh
│ │ ├── process.sh
│ │ └── startup.sh
│ │ └── php
│ │ ├── config
│ │ └── default
│ │ ├── install.sh
│ │ └── process.sh
└── single-process-image
│ ├── Dockerfile
│ ├── Makefile
│ ├── environment
│ ├── default.startup.yaml
│ └── default.yaml
│ ├── service
│ └── nginx
│ │ ├── install.sh
│ │ ├── process.sh
│ │ └── startup.sh
│ └── test-custom-env
│ └── env.yaml
├── image
├── Dockerfile
├── build.sh
├── file
│ ├── dpkg_nodoc
│ └── dpkg_nolocales
├── service-available
│ ├── :cron
│ │ ├── download.sh
│ │ ├── install.sh
│ │ ├── process.sh
│ │ └── startup.sh
│ ├── :logrotate
│ │ ├── assets
│ │ │ └── config
│ │ │ │ ├── logrotate.conf
│ │ │ │ └── logrotate_syslogng
│ │ ├── download.sh
│ │ ├── install.sh
│ │ └── startup.sh
│ ├── :runit
│ │ └── download.sh
│ ├── :ssl-tools
│ │ ├── assets
│ │ │ ├── cfssl-default-env
│ │ │ ├── default-ca
│ │ │ │ ├── README.md
│ │ │ │ ├── config
│ │ │ │ │ ├── ca-config.json
│ │ │ │ │ ├── ca-csr.json
│ │ │ │ │ └── req-csr.json.tmpl
│ │ │ │ ├── default-ca-key.pem
│ │ │ │ ├── default-ca.csr
│ │ │ │ └── default-ca.pem
│ │ │ ├── default-env
│ │ │ ├── jsonssl-default-env
│ │ │ └── tool
│ │ │ │ ├── cfssl-helper
│ │ │ │ ├── jsonssl-helper
│ │ │ │ ├── ssl-auto-renew
│ │ │ │ └── ssl-helper
│ │ ├── download.sh
│ │ └── startup.sh
│ └── :syslog-ng-core
│ │ ├── assets
│ │ └── config
│ │ │ ├── syslog-ng.conf
│ │ │ └── syslog_ng_default
│ │ ├── download.sh
│ │ ├── install.sh
│ │ ├── process.sh
│ │ └── startup.sh
└── tool
│ ├── add-multiple-process-stack
│ ├── add-service-available
│ ├── complex-bash-env
│ ├── install-service
│ ├── log-helper
│ ├── run
│ ├── setuser
│ └── wait-process
└── test
├── test.bats
└── test_helper.bash
/.gitignore:
--------------------------------------------------------------------------------
1 | /.twgit_features_subject
2 | /.twgit
3 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: bash
2 |
3 | services:
4 | - docker
5 | env:
6 | global:
7 | - NAME="osixia/light-baseimage"
8 | - VERSION="${TRAVIS_BRANCH}-dev"
9 | matrix:
10 | - TARGET_ARCH=amd64 QEMU_ARCH=x86_64
11 | - TARGET_ARCH=i386 QEMU_ARCH=i386
12 | - TARGET_ARCH=arm32v7 QEMU_ARCH=arm
13 | - TARGET_ARCH=arm64v8 QEMU_ARCH=aarch64
14 |
15 | addons:
16 | apt:
17 | # The docker manifest command was added in docker-ee version 18.x
18 | # So update our current installation and we also have to enable the experimental features.
19 | sources:
20 | - sourceline: "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
21 | key_url: "https://download.docker.com/linux/ubuntu/gpg"
22 | packages:
23 | - docker-ce
24 |
25 | before_install:
26 | - docker --version
27 | - mkdir $HOME/.docker
28 | - 'echo "{" > $HOME/.docker/config.json'
29 | - 'echo " \"experimental\": \"enabled\"" >> $HOME/.docker/config.json'
30 | - 'echo "}" >> $HOME/.docker/config.json'
31 | - sudo service docker restart
32 | # To have `DOCKER_USER` and `DOCKER_PASS`
33 | # use `travis env set`.
34 | - echo "$DOCKER_PASS" | docker login -u "$DOCKER_USER" --password-stdin
35 |
36 | install:
37 | # For cross buidling our images
38 | # This is necessary because travis-ci.org has only x86_64 machines.
39 | # If travis-ci.org gets native arm builds, probably this step is not
40 | # necessary any more.
41 | - docker run --rm --privileged multiarch/qemu-user-static:register --reset
42 | # Bats is necessary for the UT
43 | - curl -o bats.tar.gz -SL https://github.com/bats-core/bats-core/archive/v1.1.0.tar.gz
44 | - mkdir bats-core && tar -xf bats.tar.gz -C bats-core --strip-components=1
45 | - cd bats-core/
46 | - sudo ./install.sh /usr/local
47 | - cd ..
48 |
49 | before_script:
50 | # Injecting the necessary information and binaries for cross-compiling the images.
51 | # In native builds this information and binaries are not necessary and that is why
52 | # we are injecting them in the build scripts and we do not include them in the Dockerfiles
53 | - if [[ "${TARGET_ARCH}" != 'amd64' ]]; then
54 | sed -i "s/FROM debian/FROM ${TARGET_ARCH}\/debian/" image/Dockerfile;
55 | fi
56 | - if [[ "${TARGET_ARCH}" != 'amd64' ]]; then
57 | sed -i "/${TARGET_ARCH}\/debian/a COPY \
58 | --from=multiarch/qemu-user-static:x86_64-${QEMU_ARCH} \
59 | /usr/bin/qemu-${QEMU_ARCH}-static /usr/bin/" image/Dockerfile;
60 | fi
61 | - cat image/Dockerfile;
62 | # If this is a tag then change the VERSION variable to only have the
63 | # tag name and not also the commit hash.
64 | - if [ -n "$TRAVIS_TAG" ]; then
65 | VERSION=$(echo "${TRAVIS_TAG}" | sed -e 's/\(.*\)[-v]\(.*\)/\1\2/g');
66 | fi
67 | - if [ "${TRAVIS_BRANCH}" == 'master' ]; then
68 | VERSION="stable";
69 | fi
70 | # replace / with - in version
71 | - VERSION=$(echo "${VERSION}" | sed 's|/|-|g');
72 |
73 | script:
74 | - make build-nocache NAME=${NAME} VERSION=${VERSION}-${TARGET_ARCH}
75 | # Run the test and if the test fails mark the build as failed.
76 | - make test NAME=${NAME} VERSION=${VERSION}-${TARGET_ARCH}
77 |
78 | before_deploy:
79 | - docker run -d --name test_image ${NAME}:${VERSION}-${TARGET_ARCH} sleep 10
80 | - sleep 5
81 | - sudo docker ps | grep -q test_image
82 | - make tag NAME=${NAME} VERSION=${VERSION}-${TARGET_ARCH}
83 |
84 | deploy:
85 | provider: script
86 | on:
87 | all_branches: true
88 | script: make push NAME=${NAME} VERSION=${VERSION}-${TARGET_ARCH}
89 |
90 | jobs:
91 | include:
92 | - stage: Manifest creation
93 | install: skip
94 | script: skip
95 | after_deploy:
96 | - echo "$DOCKER_PASS" | docker login -u "$DOCKER_USER" --password-stdin
97 | - docker manifest create ${NAME}:${VERSION} ${NAME}:${VERSION}-amd64 ${NAME}:${VERSION}-i386 ${NAME}:${VERSION}-arm32v7 ${NAME}:${VERSION}-arm64v8;
98 | docker manifest annotate ${NAME}:${VERSION} ${NAME}:${VERSION}-amd64 --os linux --arch amd64;
99 | docker manifest annotate ${NAME}:${VERSION} ${NAME}:${VERSION}-i386 --os linux --arch 386;
100 | docker manifest annotate ${NAME}:${VERSION} ${NAME}:${VERSION}-arm32v7 --os linux --arch arm --variant v7;
101 | docker manifest annotate ${NAME}:${VERSION} ${NAME}:${VERSION}-arm64v8 --os linux --arch arm64 --variant v8;
102 |
103 | # The latest tag is coming from the stable branch of the repo
104 | - if [ "${TRAVIS_BRANCH}" == 'master' ]; then
105 | docker manifest create ${NAME}:latest ${NAME}:${VERSION}-amd64 ${NAME}:${VERSION}-i386 ${NAME}:${VERSION}-arm32v7 ${NAME}:${VERSION}-arm64v8;
106 | docker manifest annotate ${NAME}:latest ${NAME}:${VERSION}-amd64 --os linux --arch amd64;
107 | docker manifest annotate ${NAME}:latest ${NAME}:${VERSION}-i386 --os linux --arch 386;
108 | docker manifest annotate ${NAME}:latest ${NAME}:${VERSION}-arm32v7 --os linux --arch arm --variant v7;
109 | docker manifest annotate ${NAME}:latest ${NAME}:${VERSION}-arm64v8 --os linux --arch arm64 --variant v8;
110 | fi
111 |
112 | - docker manifest push ${NAME}:${VERSION};
113 | if [ "${TRAVIS_BRANCH}" == 'master' ]; then
114 | docker manifest push ${NAME}:latest;
115 | fi
116 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## [1.3.3] - 2021-03-13
4 | ### Changed
5 | - Multiple log line message are now split and log line by line for a nicest display.
6 |
7 | ### Fixed
8 | - log-level broken pipe
9 |
10 | ## [1.3.2] - 2021-02-18
11 | ### Fixed
12 | - Remove -x bash flag on log-helper tool
13 |
14 | # [1.3.1] - 2021-01-24
15 | ### Fixed
16 | - Update expired default-ca #30 #29. Thanks to @heidemn
17 |
18 | ## [1.3.0] - 2020-11-21
19 | ### Changed
20 | - Add loglevel and datetime to log messages
21 | - Upgrade CFSSL version to 1.5.0
22 |
23 | ## [1.2.0] - 2020-06-15
24 | ### Added
25 | - Add multiarch support. Thanks to @ndanyluk and @anagno !
26 | - Allow usage of additional hostnames in self signed certificate #19. Thanks to @Bobonium
27 |
28 | ### Changed
29 | - Use debian buster-slim as baseimage
30 | - Upgrade python script to python3
31 | - Upgrade CFSSL version to 1.4.1
32 |
33 | ### Fixed
34 | - Fix shellcheck errors and warnings on all scripts
35 |
36 | ## [1.1.2] - 2019-04-05
37 | ### Added
38 | - jsonssl add support for traefik >= v1.6 acme.json file
39 |
40 | ### Changed
41 | - "traefik" JSONSSL_PROFILE be becomes "traefik_up_to_v1_6"
42 | - "traefik" JSONSSL_PROFILE is now for traefik >= v1.6 acme.json file
43 | - Upgrade CFSSL version to 1.3.2
44 | - run: catch copy-service errors
45 | - KILL_PROCESS_TIMEOUT and KILL_ALL_PROCESSES_TIMEOUT to 30 seconds
46 | - make ssl-auto-renew cron log with /usr/bin/logger -t cron_ssl_auto_renew
47 | - syslog-ng config
48 |
49 | ### Fixed
50 | - my_init exits with 0 on SIGINT after runit is started
51 | - better sanitize_shenvname
52 | - exit status
53 |
54 | ## [1.1.1] - 2017-10-25
55 | ### Changed
56 | - chmod 444 logrotate config files
57 |
58 | ### Fixed
59 | - Fix jsonssl-helper get traefik ca certificate on alpine
60 |
61 | ## [1.1.0] - 2017-07-19
62 | ### Changed
63 | - Use debian stretch-slim as baseimage
64 |
65 | ## [1.0.0] - 2017-07-05
66 | ### Added
67 | - Run tool now use 2 environmen variable KILL_PROCESS_TIMEOUT and KILL_ALL_PROCESSES_TIMEOUT
68 |
69 | ### Changed
70 | - Default local to en_US.UTF-8
71 |
72 | ## [0.2.6] - 2016-11-06
73 | ### Added
74 | - Add to the 'run' tool option --dont-touch-etc-hosts Don't add in /etc/hosts a line with the container ip and $HOSTNAME environment variable value.
75 |
76 | ### Fixed
77 | - Fix wait-process script
78 |
79 | ## [0.2.5] - 2016-09-03
80 | ### Added
81 | - Add ssl-helper that allow certificate auto-renew and let choose
82 | certificate generator (cfssl-helper default, or jsonssl-helper)
83 | - Add jsonssl-helper that get certificates from a json file
84 | - Add to the 'run' tool options --run-only, --wait-first-startup, --wait-state, --cmd
85 | --keepalived becomes --keepalive-force,
86 | --keepalive now only keep alive container if all startup files and process
87 | exited without error.
88 |
89 | ### Changed
90 | - Upgrade cfssl 1.2.0
91 | - Change .yaml.startup and .json.startup files to .startup.yaml and .startup.json
92 |
93 | ### Fixed
94 | - Fix is_runit_installed check /usr/bin/sv instead of /sbin/runit #6
95 | - Fix logrotate config
96 |
97 | ## [0.2.4] - 2016-06-09
98 | ### Changed
99 | - Periodic update of debian baseimage and packages
100 |
101 | ## [0.2.3] - 2016-05-02
102 | ### Changed
103 | - Periodic update of debian baseimage and packages
104 |
105 | ## [0.2.2] - 2016-02-20
106 | ### Fixed
107 | - Fix --copy-service error if /container/run/service already exists
108 | - Fix /container/run/startup.sh file detection if no other startup files exists
109 | - Fix set_env_hostname_to_etc_hosts() on container restart
110 |
111 | ## [0.2.1] - 2016-01-25
112 | ### Added
113 | - Add cfssl as available service to generate ssl certs
114 | - Add tag #PYTHON2BASH and #JSON2BASH to convert env var to bash
115 | - Add multiple env file importation
116 | - Add setup only env file
117 | - Add json env file support
118 | - Rename my_init to run (delete previous run script)
119 | - Add run tool option --copy-service that copy /container/service to /container/run/service on startup
120 | - Add run tool option --loglevel (default : info) with possible values : none, error, warning, info, debug.
121 | - Add bash log-helper
122 |
123 | ### Changed
124 | - Container environment config directory /etc/container_environment moved to /container/environment
125 | - Container run environment is now saved in /container/run/environment
126 | - Container run environment bash export /etc/container_environment.sh moved to /container/run/environment.sh
127 | - Container state is now saved in /container/run/state
128 | - Container runit process directory /etc/service moved to /container/run/process
129 | - Container startup script directory /etc/my_init.d/ moved to /container/run/startup
130 | - Container final startup script /etc/rc.local moved to /container/run/startup.sh
131 | - Rename install-multiple-process-stack to add-multiple-process-stack
132 | - Rename install-service-available to add-service-available
133 |
134 | ### Removed
135 | - ssl-helper ssl-helper-openssl and ssl-helper-gnutls
136 | - Remove run tool option --quiet
137 |
138 | ## [0.2.0] - 2015-12-16
139 | ### Added
140 | - Makefile with build no cache
141 |
142 | ### Changed
143 | - Allow more easy image inheritance
144 |
145 | ### Fixed
146 | - Fix cron NUMBER OF HARD LINKS > 1
147 |
148 |
149 | ## [0.1.5] - 2015-11-20
150 | ### Fixed
151 | - Fix bug with host network
152 |
153 | ## [0.1.4] - 2015-11-19
154 | ### Added
155 | - Add run cmd arguments when it's a single process image
156 |
157 | ### Changed
158 | - Remove bash from command when it's a single process image
159 |
160 | ## [0.1.3] - 2015-11-06
161 | ### Added
162 | - Add hostname env variable to /etc/hosts
163 | to make the image more friendly with kubernetes again :)
164 |
165 | ## [0.1.2] - 2015-10-23
166 | ### Added
167 | - Load env.yaml file from /container/environment directory
168 | to make the image more friendly with kubernetes secrets :)
169 |
170 | ## [0.1.1] - 2015-08-18
171 | ### Added
172 | - Add python and PyYAML
173 |
174 | ### Fixed
175 | - Fix remove-service #1
176 | - Fix locales
177 | - Fix my_init
178 |
179 | ## 0.1.0 - 2015-07-23
180 | Initial release
181 |
182 | [1.3.3]: https://github.com/osixia/docker-light-baseimage/compare/v1.3.2...v1.3.3
183 | [1.3.2]: https://github.com/osixia/docker-light-baseimage/compare/v1.3.1...v1.3.2
184 | [1.3.1]: https://github.com/osixia/docker-light-baseimage/compare/v1.3.0...v1.3.1
185 | [1.3.0]: https://github.com/osixia/docker-light-baseimage/compare/v1.2.0...v1.3.0
186 | [1.2.0]: https://github.com/osixia/docker-light-baseimage/compare/v1.1.2...v1.2.0
187 | [1.1.2]: https://github.com/osixia/docker-light-baseimage/compare/v1.1.1...v1.1.2
188 | [1.1.1]: https://github.com/osixia/docker-light-baseimage/compare/v1.1.0...v1.1.1
189 | [1.1.0]: https://github.com/osixia/docker-light-baseimage/compare/v1.0.0...v1.1.0
190 | [1.0.0]: https://github.com/osixia/docker-light-baseimage/compare/v0.2.2...v1.0.0
191 | [0.2.6]: https://github.com/osixia/docker-light-baseimage/compare/v0.2.5...v0.2.6
192 | [0.2.5]: https://github.com/osixia/docker-light-baseimage/compare/v0.2.4...v0.2.5
193 | [0.2.4]: https://github.com/osixia/docker-light-baseimage/compare/v0.2.3...v0.2.4
194 | [0.2.3]: https://github.com/osixia/docker-light-baseimage/compare/v0.2.2...v0.2.3
195 | [0.2.2]: https://github.com/osixia/docker-light-baseimage/compare/v0.2.1...v0.2.2
196 | [0.2.1]: https://github.com/osixia/docker-light-baseimage/compare/v0.2.0...v0.2.1
197 | [0.2.0]: https://github.com/osixia/docker-light-baseimage/compare/v0.1.5...v0.2.0
198 | [0.1.5]: https://github.com/osixia/docker-light-baseimage/compare/v0.1.4...v0.1.5
199 | [0.1.4]: https://github.com/osixia/docker-light-baseimage/compare/v0.1.3...v0.1.4
200 | [0.1.3]: https://github.com/osixia/docker-light-baseimage/compare/v0.1.2...v0.1.3
201 | [0.1.2]: https://github.com/osixia/docker-light-baseimage/compare/v0.1.1...v0.1.2
202 | [0.1.1]: https://github.com/osixia/docker-light-baseimage/compare/v0.1.0...v0.1.1
203 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015
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 all
13 | 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 THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | NAME = osixia/light-baseimage
2 | VERSION = 1.3.3
3 |
4 | .PHONY: build build-nocache test tag-latest push push-latest release git-tag-version
5 |
6 | build:
7 | docker build -f image/Dockerfile -t $(NAME):$(VERSION) --rm image
8 |
9 | build-nocache:
10 | docker build -f image/Dockerfile -t $(NAME):$(VERSION) --no-cache --rm image
11 |
12 | test:
13 | env NAME=$(NAME) VERSION=$(VERSION) bats test/test.bats
14 |
15 | tag:
16 | docker tag $(NAME):$(VERSION) $(NAME):$(VERSION)
17 |
18 | tag-latest:
19 | docker tag $(NAME):$(VERSION) $(NAME):latest
20 |
21 | push:
22 | docker push $(NAME):$(VERSION)
23 |
24 | push-latest:
25 | docker push $(NAME):latest
26 |
27 | release: build test tag-latest push push-latest
28 |
29 | git-tag-version: release
30 | git tag -a v$(VERSION) -m "v$(VERSION)"
31 | git push origin v$(VERSION)
32 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # osixia/light-baseimage
2 |
3 | [][hub]
4 | [][hub]
5 | [](http://microbadger.com/images/osixia/light-baseimage "Get your own image badge on microbadger.com")
6 |
7 | [hub]: https://hub.docker.com/r/osixia/light-baseimage/
8 |
9 | Latest release: 1.3.3 [Changelog](CHANGELOG.md)
10 | | [Docker Hub](https://hub.docker.com/r/osixia/light-baseimage/)
11 |
12 | A **Debian 10 (Buster)** based docker image to build reliable image quickly. This image provide a simple opinionated solution to build multiple or single process image with minimum of layers and an optimized build.
13 |
14 | The aims of this image is to be used as a base for your own Docker images. It's base on the awesome work of: [phusion/baseimage-docker](https://github.com/phusion/baseimage-docker)
15 |
16 | Other base distribution are available:
17 | - [Alpine](https://github.com/osixia/docker-light-baseimage/tree/alpine) | [Docker Hub](https://hub.docker.com/r/osixia/alpine-light-baseimage/) | [](http://microbadger.com/images/osixia/alpine-light-baseimage "Get your own image badge on microbadger.com")
18 |
19 | Table of Contents
20 | - [osixia/light-baseimage](#osixialight-baseimage)
21 | - [Contributing](#contributing)
22 | - [Overview](#overview)
23 | - [Quick Start](#quick-start)
24 | - [Image directories structure](#image-directories-structure)
25 | - [Service directory structure](#service-directory-structure)
26 | - [Create a single process image](#create-a-single-process-image)
27 | - [Overview](#overview-1)
28 | - [Dockerfile](#dockerfile)
29 | - [Service files](#service-files)
30 | - [install.sh](#installsh)
31 | - [startup.sh](#startupsh)
32 | - [process.sh](#processsh)
33 | - [Environment files](#environment-files)
34 | - [default.yaml](#defaultyaml)
35 | - [default.startup.yaml](#defaultstartupyaml)
36 | - [Build and test](#build-and-test)
37 | - [Overriding default environment files at run time:](#overriding-default-environment-files-at-run-time)
38 | - [Create a multiple process image](#create-a-multiple-process-image)
39 | - [Overview](#overview-2)
40 | - [Dockerfile](#dockerfile-1)
41 | - [Service files](#service-files-1)
42 | - [install.sh](#installsh)
43 | - [process.sh](#processsh-1)
44 | - [Build and test](#build-and-test-1)
45 | - [Images Based On Light-Baseimage](#images-based-on-light-baseimage)
46 | - [Image Assets](#image-assets)
47 | - [Tools](#tools)
48 | - [Services available](#services-available)
49 | - [Advanced User Guide](#advanced-user-guide)
50 | - [Service available](#service-available)
51 | - [Fix docker mounted file problems](#fix-docker-mounted-file-problems)
52 | - [Distribution packages documentation and locales](#distribution-packages-documentation-and-locales)
53 | - [Mastering image tools](#mastering-image-tools)
54 | - [run](#run)
55 | - [Run command line options](#run-command-line-options)
56 | - [Run directory setup](#run-directory-setup)
57 | - [Startup files environment setup](#startup-files-environment-setup)
58 | - [Startup files execution](#startup-files-execution)
59 | - [Process execution](#process-execution)
60 | - [Single process image](#single-process-image)
61 | - [Multiple process image](#multiple-process-image)
62 | - [No process image](#no-process-image)
63 | - [Extra environment variables](#extra-environment-variables)
64 | - [log-helper](#log-helper)
65 | - [complex-bash-env](#complex-bash-env)
66 | - [Tests](#tests)
67 | - [Changelog](#changelog)
68 |
69 | ## Contributing
70 |
71 | If you find this image useful here's how you can help:
72 |
73 | - Send a pull request with your kickass new features and bug fixes
74 | - Help new users with [issues](https://github.com/osixia/docker-openldap/issues) they may encounter
75 | - Support the development of this image and star this repo!
76 |
77 | ## Overview
78 |
79 | This image takes all the advantages of [phusion/baseimage-docker](https://github.com/phusion/baseimage-docker) but makes programs optional which allow more lightweight images and single process images. It also define simple directory structure and files to quickly set how a program (here called service) is installed, setup and run.
80 |
81 | So major features are:
82 | - Greats building tools to minimize the image number of layers and optimize image build.
83 | - Simple way to install services and multiple process image stacks (runit, cron, syslog-ng-core and logrotate) if needed.
84 | - Getting environment variables from **.yaml** and **.json** files.
85 | - Special environment files **.startup.yaml** and **.startup.json** deleted after image startup files first execution to keep the image setup secret.
86 |
87 |
88 | ## Quick Start
89 |
90 | ### Image directories structure
91 |
92 | This image use four directories:
93 |
94 | - **/container/environment**: for environment files.
95 | - **/container/service**: for services to install, setup and run.
96 | - **/container/service-available**: for service that may be on demand downloaded, installed, setup and run.
97 | - **/container/tool**: for image tools.
98 |
99 | By the way at run time another directory is created:
100 | - **/container/run**: To store container run environment, state, startup files and process to run based on files in /container/environment and /container/service directories.
101 |
102 | But this will be dealt with in the following section.
103 |
104 | ### Service directory structure
105 |
106 | This section define a service directory that can be added in /container/service or /container/service-available.
107 |
108 | - **my-service**: root directory
109 | - **my-service/install.sh**: install script (not mandatory).
110 | - **my-service/startup.sh**: startup script to setup the service when the container start (not mandatory).
111 | - **my-service/process.sh**: process to run (not mandatory).
112 | - **my-service/finish.sh**: finish script run when the process script exit (not mandatory).
113 | - **my-service/...** add whatever you need!
114 |
115 | Ok that's pretty all to know to start building our first images!
116 |
117 | ### Create a single process image
118 |
119 | #### Overview
120 | For this example we are going to perform a basic nginx install.
121 |
122 | See complete example in: [example/single-process-image](example/single-process-image)
123 |
124 | First we create the directory structure of the image:
125 |
126 | - **single-process-image**: root directory
127 | - **single-process-image/service**: directory to store the nginx service.
128 | - **single-process-image/environment**: environment files directory.
129 | - **single-process-image/Dockerfile**: the Dockerfile to build this image.
130 |
131 | **service** and **environment** directories name are arbitrary and can be changed but make sure to adapt their name everywhere and especially in the Dockerfile.
132 |
133 | Let's now create the nginx service directory:
134 |
135 | - **single-process-image/service/nginx**: service root directory
136 | - **single-process-image/service/nginx/install.sh**: service installation script.
137 | - **single-process-image/service/nginx/startup.sh**: startup script to setup the service when the container start.
138 | - **single-process-image/service/nginx/process.sh**: process to run.
139 |
140 |
141 | #### Dockerfile
142 |
143 | In the Dockerfile we are going to:
144 | - Download nginx from apt-get.
145 | - Add the service directory to the image.
146 | - Install service and clean up.
147 | - Add the environment directory to the image.
148 | - Define ports exposed and volumes if needed.
149 |
150 |
151 | # Use osixia/light-baseimage
152 | # https://github.com/osixia/docker-light-baseimage
153 | FROM osixia/light-baseimage:1.3.3
154 |
155 | # Download nginx from apt-get and clean apt-get files
156 | RUN apt-get -y update \
157 | && LC_ALL=C DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
158 | nginx \
159 | && apt-get clean \
160 | && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
161 |
162 | # Add service directory to /container/service
163 | ADD service /container/service
164 |
165 | # Use baseimage install-service script
166 | # https://github.com/osixia/docker-light-baseimage/blob/stable/image/tool/install-service
167 | RUN /container/tool/install-service
168 |
169 | # Add default env directory
170 | ADD environment /container/environment/99-default
171 |
172 | # Set /var/www/ in a data volume
173 | VOLUME /var/www/
174 |
175 | # Expose default http and https ports
176 | EXPOSE 80 443
177 |
178 |
179 | The Dockerfile contains directives to download nginx from apt-get but all the initial setup will take place in install.sh file (called by /container/tool/install-service tool) for a better build experience. The time consuming download task is decoupled from the initial setup to make great use of docker build cache. If install.sh file is changed the builder won't have to download again nginx, and will just run install scripts.
180 |
181 | #### Service files
182 |
183 | ##### install.sh
184 |
185 | This file must only contain directives for the service initial setup. Files download and apt-get command takes place in the Dockerfile for a better image building experience (see [Dockerfile](#dockerfile)).
186 |
187 | In this example, for the initial setup we just delete the default nginx debian index file and create a custom index.html:
188 |
189 | #!/bin/bash -e
190 | # this script is run during the image build
191 |
192 | rm -rf /var/www/html/index.nginx-debian.html
193 | echo "Hi!" > /var/www/html/index.html
194 |
195 | Make sure install.sh can be executed (chmod +x install.sh).
196 |
197 | Note: The install.sh script is run during the docker build so run time environment variables can't be used to customize the setup. This is done in the startup.sh file.
198 |
199 |
200 | ##### startup.sh
201 |
202 | This file is used to make process.sh ready to be run and customize the service setup based on run time environment.
203 |
204 | For example at run time we would like to introduce ourselves so we will use an environment variable WHO_AM_I set by command line with --env. So we add WHO_AM_I value to index.html file but we want to do that only on the first container start because on restart the index.html file will already contains our name:
205 |
206 | #!/bin/bash -e
207 | FIRST_START_DONE="${CONTAINER_STATE_DIR}/nginx-first-start-done"
208 |
209 | # container first start
210 | if [ ! -e "$FIRST_START_DONE" ]; then
211 | echo "I'm ${WHO_AM_I}." >> /var/www/html/index.html
212 | touch $FIRST_START_DONE
213 | fi
214 |
215 | exit 0
216 |
217 | Make sure startup.sh can be executed (chmod +x startup.sh).
218 |
219 | As you can see we use CONTAINER_STATE_DIR variable, it contains the directory where container state is saved, this variable is automatically set by run tool. Refer to the [Advanced User Guide](#extra-environment-variables) for more information.
220 |
221 | ##### process.sh
222 |
223 | This file define the command to run:
224 |
225 | #!/bin/bash -e
226 | exec /usr/sbin/nginx -g "daemon off;"
227 |
228 | Make sure process.sh can be executed (chmod +x process.sh).
229 |
230 | *Caution: The command executed must start a foreground process otherwise the container will immediately stops.*
231 |
232 | That why we run nginx with `-g "daemon off;"`
233 |
234 | That's it we have a single process image that run nginx!
235 | We could already build and test this image but two more minutes to take advantage of environment files!
236 |
237 | #### Environment files
238 |
239 | Let's create two files:
240 | - single-process-image/environment/default.yaml
241 | - single-process-image/environment/default.startup.yaml
242 |
243 | File name *default*.yaml and *default*.startup.yaml can be changed as you want. Also in this example we are going to use yaml files but json files works too.
244 |
245 | ##### default.yaml
246 | default.yaml file define variables that can be used at any time in the container environment:
247 |
248 | WHO_AM_I: We are Anonymous. We are Legion. We do not forgive. We do not forget. Expect us.
249 |
250 | ##### default.startup.yaml
251 | default.startup.yaml define variables that are only available during the container **first start** in **startup files**.
252 | \*.startup.yaml are deleted right after startup files are processed for the first time,
253 | then all variables they contains will not be available in the container environment.
254 |
255 | This helps to keep the container configuration secret. If you don't care all environment variables can be defined in **default.yaml** and everything will work fine.
256 |
257 | But for this tutorial we will add a variable to this file:
258 |
259 | FIRST_START_SETUP_ONLY_SECRET: The database password is KawaaahBounga
260 |
261 | And try to get its value in **startup.sh** script:
262 |
263 | #!/bin/bash -e
264 | FIRST_START_DONE="${CONTAINER_STATE_DIR}/nginx-first-start-done"
265 |
266 | # container first start
267 | if [ ! -e "$FIRST_START_DONE" ]; then
268 | echo ${WHO_AM_I} >> /var/www/html/index.html
269 | touch $FIRST_START_DONE
270 | fi
271 |
272 | echo "The secret is: $FIRST_START_SETUP_ONLY_SECRET"
273 |
274 | exit 0
275 |
276 | And in **process.sh** script:
277 |
278 | #!/bin/bash -e
279 | echo "The secret is: $FIRST_START_SETUP_ONLY_SECRET"
280 | exec /usr/sbin/nginx -g "daemon off;"
281 |
282 | Ok it's time for the show!
283 |
284 | #### Build and test
285 |
286 | Build the image:
287 |
288 | docker build -t example/single-process --rm .
289 |
290 | Start a new container:
291 |
292 | docker run -p 8080:80 example/single-process
293 |
294 | Inspect the output and you should see that the secret is present in startup script:
295 | > \*\*\* Running /container/run/startup/nginx...
296 |
297 | > The secret is: The database password is Baw0unga!
298 |
299 | And the secret is not defined in the process:
300 | > \*\*\* Remove file /container/environment/99-default/default.startup.yaml [...]
301 |
302 | > \*\*\* Running /container/run/process/nginx/run...
303 |
304 | > The secret is:
305 |
306 | Yes in this case it's not really useful to have a secret variable like this, but a concrete example can be found in [osixia/openldap](https://github.com/osixia/docker-openldap) image.
307 | The admin password is available in clear text during the container first start to create a new ldap database where it is saved encrypted. After that the admin password is not available in clear text in the container environment.
308 |
309 | Ok let's check our name now, go to [http://localhost:8080/](http://localhost:8080/)
310 |
311 | You should see:
312 | > Hi! We are Anonymous. We are Legion. We do not forgive. We do not forget. Expect us.
313 |
314 | And finally, let's say who we really are, stop the previous container (ctrl+c or ctrl+d) and start a new one:
315 |
316 | docker run --env WHO_AM_I="I'm Jon Snow, what?! i'm dead?" \
317 | -p 8080:80 example/single-process
318 |
319 | Refresh [http://localhost:8080/](http://localhost:8080/) and you should see:
320 | > Hi! I'm Jon Snow, what?! i'm dead?
321 |
322 |
323 | ##### Overriding default environment files at run time:
324 | Let's create two new environment files:
325 | - single-process-image/test-custom-env/env.yaml
326 | - single-process-image/test-custom-env/env.startup.yaml
327 |
328 | env.yaml:
329 |
330 | WHO_AM_I: I'm bobby.
331 |
332 | env.startup.yaml:
333 |
334 | FIRST_START_SETUP_ONLY_SECRET: The database password is KawaaahB0unga!!!
335 |
336 | And we mount them at run time:
337 |
338 | docker run --volume $PWD/test-custom-env:/container/environment/01-custom \
339 | -p 8080:80 example/single-process
340 |
341 | Take care to link your environment files folder to `/container/environment/XX-somedir` (with XX < 99 so they will be processed before default environment files) and not directly to `/container/environment` because this directory contains predefined baseimage environment files to fix container environment (INITRD, LANG, LANGUAGE and LC_CTYPE).
342 |
343 | In the output:
344 | > \*\*\* Running /container/run/startup/nginx...
345 |
346 | > The secret is: The database password is KawaaahB0unga!!!
347 |
348 | Refresh [http://localhost:8080/](http://localhost:8080/) and you should see:
349 | > Hi! I'm bobby.
350 |
351 | ### Create a multiple process image
352 |
353 | #### Overview
354 |
355 | This example takes back the single process image example and add php7.0-fpm to run php scripts.
356 |
357 | See complete example in: [example/multiple-process-image](example/multiple-process-image)
358 |
359 | Note: it would have been ♪ ~~harder~~, faster, better, stronger ♪ to extends the previous image but to make things easier we just copied files.
360 |
361 | So here the image directory structure:
362 |
363 | - **multiple-process-image**: root directory
364 | - **multiple-process-image/service**: directory to store the nginx and php7.0-fpm service.
365 | - **multiple-process-image/environment**: environment files directory.
366 | - **multiple-process-image/Dockerfile**: the Dockerfile to build this image.
367 |
368 | **service** and **environment** directories name are arbitrary and can be changed but make sure to adapt their name in the Dockerfile.
369 |
370 | Let's now create the nginx and php directories:
371 |
372 | - **multiple-process-image/service/nginx**: nginx root directory
373 | - **multiple-process-image/service/nginx/install.sh**: service installation script.
374 | - **multiple-process-image/service/nginx/startup.sh**: startup script to setup the service when the container start.
375 | - **multiple-process-image/service/nginx/process.sh**: process to run.
376 |
377 | - **multiple-process-image/service/php**: php root directory
378 | - **multiple-process-image/service/php/install.sh**: service installation script.
379 | - **multiple-process-image/service/php/process.sh**: process to run.
380 | - **multiple-process-image/service/php/config/default**: default nginx server config with
381 |
382 | #### Dockerfile
383 |
384 | In the Dockerfile we are going to:
385 | - Add the multiple process stack
386 | - Download nginx and php7.0-fpm from apt-get.
387 | - Add the service directory to the image.
388 | - Install service and clean up.
389 | - Add the environment directory to the image.
390 | - Define ports exposed and volumes if needed.
391 |
392 |
393 | # Use osixia/light-baseimage
394 | # https://github.com/osixia/docker-light-baseimage
395 | FROM osixia/light-baseimage:1.3.3
396 |
397 | # Install multiple process stack, nginx and php7.0-fpm and clean apt-get files
398 | # https://github.com/osixia/docker-light-baseimage/blob/stable/image/tool/add-multiple-process-stack
399 | RUN apt-get -y update \
400 | && /container/tool/add-multiple-process-stack \
401 | && LC_ALL=C DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
402 | nginx \
403 | php7.0-fpm \
404 | && apt-get clean \
405 | && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
406 |
407 | # Add service directory to /container/service
408 | ADD service /container/service
409 |
410 | # Use baseimage install-service script
411 | # https://github.com/osixia/docker-light-baseimage/blob/stable/image/tool/install-service
412 | RUN /container/tool/install-service
413 |
414 | # Add default env directory
415 | ADD environment /container/environment/99-default
416 |
417 | # Set /var/www/ in a data volume
418 | VOLUME /var/www/
419 |
420 | # Expose default http and https ports
421 | EXPOSE 80 443
422 |
423 |
424 | The Dockerfile contains directives to download nginx and php7.0-fpm from apt-get but all the initial setup will take place in install.sh file (called by /container/tool/install-service tool) for a better build experience. The time consuming download task is decoupled from the initial setup to make great use of docker build cache. If an install.sh file is changed the builder will not have to download again nginx and php7.0-fpm add will just run install scripts.
425 |
426 | Maybe you already read that in the previous example ?Sorry.
427 |
428 | #### Service files
429 |
430 | Please refer to [single process image](#create-a-single-process-image) for the nginx service files description. Here just php service files are described.
431 |
432 | ##### install.sh
433 |
434 | This file must only contains directives for the service initial setup. Files download and apt-get command takes place in the Dockerfile for a better image building experience (see [Dockerfile](#dockerfile-1) ).
435 |
436 | In this example, for the initial setup we set some php default configuration, replace the default nginx server config and add phpinfo.php file:
437 |
438 | #!/bin/bash -e
439 | # this script is run during the image build
440 |
441 | # config
442 | sed -i -e "s/expose_php = On/expose_php = Off/g" /etc/php/7.0/fpm/php.ini
443 | sed -i -e "s/;cgi.fix_pathinfo=1/cgi.fix_pathinfo=0/g" /etc/php/7.0/fpm/php.ini
444 | sed -i -e "s/;listen.owner = www-data/listen.owner = www-data/g" /etc/php/7.0/fpm/php.ini
445 | sed -i -e "s/;listen.group = www-data/listen.group = www-data/g" /etc/php/7.0/fpm/php.ini
446 |
447 | # create php socket directory
448 | mkdir -p /run/php
449 |
450 | # replace default website with php service default website
451 | cp -f /container/service/php/config/default /etc/nginx/sites-available/default
452 |
453 | # create phpinfo.php
454 | echo " /var/www/html/phpinfo.php
455 |
456 |
457 |
458 | Make sure install.sh can be executed (chmod +x install.sh).
459 |
460 | ##### process.sh
461 |
462 | This file define the command to run:
463 |
464 | #!/bin/bash -e
465 | exec /usr/sbin/php-fpm7.0 --nodaemonize
466 |
467 | Make sure process.sh can be executed (chmod +x process.sh).
468 |
469 | *Caution: The command executed must start a foreground process otherwise runit (use to supervise multiple process images) will keep restarting php-fpm7.0.*
470 |
471 | That why we run php with `--nodaemonize"`
472 |
473 | ##### config/default
474 | nginx server configuration:
475 |
476 | server {
477 | listen 80 default_server;
478 | listen [::]:80 default_server;
479 |
480 | root /var/www/html;
481 |
482 | # Add index.php to the list if you are using PHP
483 | index index.html index.htm index.nginx-debian.html;
484 |
485 | server_name _;
486 |
487 | location / {
488 | # First attempt to serve request as file, then
489 | # as directory, then fall back to displaying a 404.
490 | try_files $uri $uri/ =404;
491 | }
492 |
493 | location ~ \.php$ {
494 | fastcgi_split_path_info ^(.+\.php)(/.+)$;
495 | # With php fpm:
496 | fastcgi_pass unix:/run/php/php7.0-fpm.sock;
497 | fastcgi_index index.php;
498 | include fastcgi_params;
499 | include fastcgi.conf;
500 | }
501 | }
502 |
503 | That's it we have a multiple process image that run nginx and php!
504 |
505 | #### Build and test
506 |
507 |
508 | Build the image:
509 |
510 | docker build -t example/multiple-process --rm .
511 |
512 | Start a new container:
513 |
514 | docker run -p 8080:80 example/multiple-process
515 |
516 | Go to [http://localhost:8080/phpinfo.php](http://localhost:8080/phpinfo.php)
517 |
518 | > phpinfo should be printed
519 |
520 | So we have a container with two process supervised by runit running in our container!
521 |
522 |
523 | ## Images Based On Light-Baseimage
524 |
525 | Single process images:
526 | - [osixia/openldap](https://github.com/osixia/docker-openldap)
527 | - [osixia/keepalived](https://github.com/osixia/docker-keepalived)
528 | - [osixia/tinc](https://github.com/osixia/docker-tinc)
529 | - [osixia/registry-ldap-auth](https://github.com/osixia/docker-registry-ldap-auth)
530 | - [osixia/cfssl-multirootca](https://github.com/osixia/docker-cfssl-multirootca)
531 | - [osixia/backup](https://github.com/osixia/docker-backup)
532 | - [osixia/backup-manager](https://github.com/osixia/docker-backup-manager)
533 | - [osixia/mmc-agent](https://github.com/osixia/docker-mmc-agent)
534 |
535 | Multiple process images:
536 | - [osixia/openldap-backup](https://github.com/osixia/docker-openldap-backup)
537 | - [osixia/mariadb](https://github.com/osixia/docker-mariadb)
538 | - [osixia/wordpress](https://github.com/osixia/docker-wordpress)
539 | - [osixia/roundcube](https://github.com/osixia/docker-roundcube)
540 | - [osixia/piwik](https://github.com/osixia/docker-piwik)
541 | - [osixia/phpMyAdmin](https://github.com/osixia/docker-phpMyAdmin)
542 | - [osixia/phpLDAPadmin](https://github.com/osixia/docker-phpLDAPadmin)
543 | - [osixia/keepalived-confd](https://github.com/osixia/docker-keepalived-confd)
544 | - [osixia/tinc-etcd](https://github.com/osixia/docker-tinc-etcd)
545 | - [osixia/postfix-gateway-confd](https://github.com/osixia/docker-postfix-gateway-confd)
546 | - [osixia/mmc-mail](https://github.com/osixia/docker-mmc-mail)
547 | - [osixia/mmc-web](https://github.com/osixia/docker-mmc-web)
548 |
549 | Image adding light-baseimage tools to an existing image
550 | - [osixia/gitlab](https://github.com/osixia/docker-gitlab)
551 |
552 | Send me a message to add your image in this list.
553 |
554 | ## Image Assets
555 |
556 | ### Tools
557 |
558 | All container tools are available in `/container/tool` directory and are linked in `/sbin/` so they belong to the container PATH.
559 |
560 |
561 | | Filename | Description |
562 | | ---------------- | ------------------- |
563 | | run | The run tool is defined as the image ENTRYPOINT (see [Dockerfile](image/Dockerfile)). It set environment and run startup scripts and images process. More information in the [Advanced User Guide](#run). |
564 | | setuser | A tool for running a command as another user. Easier to use than su, has a smaller attack vector than sudo, and unlike chpst this tool sets $HOME correctly.|
565 | | log-helper | A simple bash tool to print message base on the log level. |
566 | | add-service-available | A tool to download and add services in service-available directory to the regular service directory. |
567 | | add-multiple-process-stack | A tool to add the multiple process stack: runit, cron syslog-ng-core and logrotate. |
568 | | install-service | A tool that execute /container/service/install.sh and /container/service/\*/install.sh scripts. |
569 | | complex-bash-env | A tool to iterate trough complex bash environment variables created by the run tool when a table or a list was set in environment files or in environment command line argument. |
570 |
571 | ### Services available
572 |
573 | | Name | Description |
574 | | ---------------- | ------------------- |
575 | | :runit | Replaces Debian's Upstart. Used for service supervision and management. Much easier to use than SysV init and supports restarting daemons when they crash. Much easier to use and more lightweight than Upstart.
*This service is part of the multiple-process-stack.*|
576 | | :cron | Cron daemon.
*This service is part of the multiple-process-stack.*|
577 | | :syslog-ng-core | Syslog daemon so that many services - including the kernel itself - can correctly log to /var/log/syslog. If no syslog daemon is running, a lot of important messages are silently swallowed.
Only listens locally. All syslog messages are forwarded to "docker logs".
*This service is part of the multiple-process-stack.* |
578 | | :logrotate | Rotates and compresses logs on a regular basis.
*This service is part of the multiple-process-stack.*|
579 | | :ssl-tools | Add CFSSL a CloudFlare PKI/TLS swiss army knife. It's a command line tool for signing, verifying, and bundling TLS certificates. Comes with cfssl-helper tool that make it docker friendly by taking command line parameters from environment variables.
Also add jsonssl-helper to get certificates from json files, parameters are set by environment variables. |
580 |
581 |
582 | ## Advanced User Guide
583 |
584 | ### Service available
585 |
586 | A service-available is basically a normal service expect that it is in the `service-available` directory and have a `download.sh` file.
587 |
588 | To add a service-available to the current image use the `add-service-available` tool. It will process the download.sh file of services given in argument and move them to the regular service directory (/container/service).
589 |
590 | After that the service-available will be process like regular services.
591 |
592 | Here simple Dockerfile example how to add a service-available to an image:
593 |
594 | # Use osixia/light-baseimage
595 | # https://github.com/osixia/docker-light-baseimage
596 | FROM osixia/light-baseimage:1.3.3
597 |
598 | # Add cfssl and cron service-available
599 | # https://github.com/osixia/docker-light-baseimage/blob/stable/image/tool/add-service-available
600 | # https://github.com/osixia/docker-light-baseimage/blob/stable/image/service-available/:ssl-tools/download.sh
601 | # https://github.com/osixia/docker-light-baseimage/blob/stable/image/service-available/:cron/download.sh
602 | RUN apt-get -y update \
603 | && /container/tool/add-service-available :ssl-tools :cron \
604 | && LC_ALL=C DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
605 | nginx \
606 | php7.0-fpm
607 | ...
608 |
609 |
610 | Note: Most of predefined service available start with a `:` to make sure they are installed before regular services (so they can be used by regular services). The install-service tool process services in /container/service in alphabetical order.
611 |
612 | To create a service-available just create a regular service, add a download.sh file to set how the needed content is downloaded and add it to /container/service-available directory. The download.sh script is not mandatory if nothing need to be downloaded.
613 |
614 | For example a simple image example that add service-available to this baseimage: [osixia/web-baseimage](https://github.com/osixia/docker-web-baseimage)
615 |
616 |
617 | ### Fix docker mounted file problems
618 |
619 | For some reasons you will probably have to mount custom files to your container. For example in the *mutliple process image example* you can customise the nginx config by mounting your custom config to "/container/service/php/config/default" :
620 |
621 | docker run -v /data/my-nginx-config:/container/service/php/config/default example/multiple-process
622 |
623 | In this case every thing should work fine, but if the startup script makes some `sed` replacement or change file owner and permissions this can results in "Device or resource busy" error. See [Docker documentation](https://docs.docker.com/v1.4/userguide/dockervolumes/#mount-a-host-file-as-a-data-volume).
624 |
625 | sed -i "s|listen 80|listen 8080|g" /container/service/php/config/default
626 |
627 | To prevent that king of error light-baseimage provide *--copy-service* command argument :
628 |
629 | docker run -v /data/my-nginx-config:/container/service/php/config/default example/multiple-process --copy-service
630 |
631 | On startup this will copy all /container/service directory to /container/run/service.
632 |
633 |
634 | At run time you can get the container service directory with `CONTAINER_SERVICE_DIR` environment variable.
635 | If *--copy-service* is used *CONTAINER_SERVICE_DIR=/container/run/service* otherwise *CONTAINER_SERVICE_DIR=/container/service*
636 |
637 | So to always apply sed on the correct file in the startup script the command becomes :
638 |
639 | sed -i "s|listen 80|listen 8080|g" ${CONTAINER_SERVICE_DIR}/php/config/default
640 |
641 |
642 | ### Distribution packages documentation and locales
643 |
644 | This image has a configuration to prevent documentation and locales to be installed from base distribution packages repositories to make it more lightweight as possible. If you need the doc and locales remove the following files :
645 | **/etc/dpkg/dpkg.cfg.d/01_nodoc** and **/etc/dpkg/dpkg.cfg.d/01_nolocales**
646 |
647 |
648 | ### Mastering image tools
649 |
650 | #### run
651 |
652 | The *run tool* is defined as the image ENTRYPOINT (see [Dockerfile](image/Dockerfile)). It's the core tool of this image.
653 |
654 | What it does:
655 | - Setup the run directory
656 | - Set the startup files environment
657 | - Run startup files
658 | - Set process environment
659 | - Run process
660 |
661 | ##### Run command line options
662 |
663 | *Run tool* takes several options, to list them:
664 |
665 | docker run osixia/light-baseimage:1.3.3 --help
666 | usage: run [-h] [-e] [-s] [-p] [-f] [-o {startup,process,finish}]
667 | [-c COMMAND [WHEN={startup,process,finish} ...]] [-k]
668 | [--wait-state FILENAME] [--wait-first-startup] [--keep-startup-env]
669 | [--copy-service] [--dont-touch-etc-hosts] [--keepalive]
670 | [--keepalive-force] [-l {none,error,warning,info,debug,trace}]
671 | [MAIN_COMMAND [MAIN_COMMAND ...]]
672 |
673 | Initialize the system.
674 |
675 | positional arguments:
676 | MAIN_COMMAND The main command to run, leave empty to only run
677 | container process.
678 |
679 | optional arguments:
680 | -h, --help show this help message and exit
681 | -e, --skip-env-files Skip getting environment values from environment
682 | file(s).
683 | -s, --skip-startup-files
684 | Skip running /container/run/startup/* and
685 | /container/run/startup.sh file(s).
686 | -p, --skip-process-files
687 | Skip running container process file(s).
688 | -f, --skip-finish-files
689 | Skip running container finish file(s).
690 | -o {startup,process,finish}, --run-only {startup,process,finish}
691 | Run only this file type and ignore others.
692 | -c COMMAND [WHEN={startup,process,finish} ...], --cmd COMMAND [WHEN={startup,process,finish} ...]
693 | Run this command before WHEN file(s). Default before
694 | startup file(s).
695 | -k, --no-kill-all-on-exit
696 | Don't kill all processes on the system upon exiting.
697 | --wait-state FILENAME
698 | Wait until the container state file exists in
699 | /container/run/state directory before starting.
700 | Usefull when 2 containers share /container/run
701 | directory via volume.
702 | --wait-first-startup Wait until the first startup is done before starting.
703 | Usefull when 2 containers share /container/run
704 | directory via volume.
705 | --keep-startup-env Don't remove ('.startup.yaml', '.startup.json')
706 | environment files after startup scripts.
707 | --copy-service Copy /container/service to /container/run/service.
708 | Help to fix docker mounted files problems.
709 | --dont-touch-etc-hosts
710 | Don't add in /etc/hosts a line with the container ip
711 | and $HOSTNAME environment variable value.
712 | --keepalive Keep alive container if all startup files and process
713 | exited without error.
714 | --keepalive-force Keep alive container in all circonstancies.
715 | -l {none,error,warning,info,debug,trace}, --loglevel {none,error,warning,info,debug,trace}
716 | Log level (default: info)
717 |
718 | Osixia! Light Baseimage: https://github.com/osixia/docker-light-baseimage
719 |
720 |
721 | ##### Run directory setup
722 | *Run tool* will create if they not exists the following directories:
723 | - /container/run/state
724 | - /container/run/environment
725 | - /container/run/startup
726 | - /container/run/process
727 | - /container/run/service
728 |
729 | At the container first start it will search in /container/service or /container/run/service (if --copy-service option is used) all image's services.
730 |
731 | In a service directory for example /container/service/my-service:
732 | - If a startup.sh file is found, the file is linked to /container/run/startup/my-service
733 | - If a process.sh file is found, the file is linked to /container/run/process/my-service/run
734 |
735 | ##### Startup files environment setup
736 | *Run tool* takes all file in /container/environment/* and import the variables values to the container environment.
737 | The container environment is then exported to /container/run/environment and in /container/run/environment.sh
738 |
739 | ##### Startup files execution
740 | *Run tool* iterate trough /container/run/startup/* directory in alphabetical order and run scripts.
741 | After each time *run tool* runs a startup script, it resets its own environment variables to the state in /container/run/environment, and re-dumps the new environment variables to /container/run/environment.sh
742 |
743 | After all startup script *run tool* run /container/run/startup.sh if exists.
744 |
745 | ##### Process environment setup
746 | *Run tool* delete all .startup.yaml and .startup.json in /container/environment/* and clear the previous run environment (/container/run/environment is removed)
747 | Then it takes all remaining file in /container/environment/* and import the variables values to the container environment.
748 | The container environment is then exported to /container/run/environment and in /container/run/environment.sh
749 |
750 | ##### Process execution
751 |
752 | ###### Single process image
753 |
754 | *Run tool* execute the unique /container/run/process/service-name/run file.
755 |
756 | If a main command is set for example:
757 |
758 | docker run -it osixia/openldap:1.4.0 bash
759 |
760 | *Run tool* will execute the single process and the main command. If the main command exits the container exits. This is useful to debug or image development purpose.
761 |
762 | ###### Multiple process image
763 |
764 | In a multiple process image *run tool* execute runit witch supervise /container/run/process directory and start all services automatically. Runit will also relaunched them if they failed.
765 |
766 | If a main command is set for example:
767 |
768 | docker run -it osixia/phpldapadmin:0.9.0 bash
769 |
770 | *run tool* will execute runit and the main command. If the main command exits the container exits. This is still useful to debug or image development purpose.
771 |
772 | ###### No process image
773 | If a main command is set *run tool* launch it otherwise bash is launched.
774 | Example:
775 |
776 | docker run -it osixia/light-baseimage:1.3.3
777 |
778 |
779 | ##### Extra environment variables
780 |
781 | *run tool* add 3 variables to the container environment:
782 | - **CONTAINER_STATE_DIR**: /container/run/state
783 | - **CONTAINER_SERVICE_DIR**: the container service directory. By default: /container/service but if the container is started with --copy-service option: /container/run/service
784 | - **CONTAINER_LOG_LEVEL**: log level set by --loglevel option defaults to: 3 (info)
785 |
786 | #### log-helper
787 | This tool is a simple utility based on the CONTAINER_LOG_LEVEL variable to print leveled log messages.
788 |
789 | For example if the log level is info:
790 |
791 | log-helper info hello
792 |
793 | will echo:
794 | > hello
795 |
796 | log-helper debug i'm bob
797 |
798 | will echo nothing.
799 |
800 | log-helper support piped input:
801 |
802 | echo "Heyyyyy" | log-helper info
803 |
804 | > Heyyyyy
805 |
806 | Log message functions usage: `log-helper error|warning|info|debug|trace message`
807 |
808 | You can also test the log level with the level function:
809 |
810 | log-helper level eq info && echo "log level is infos"
811 |
812 | for example this will echo "log level is trace" if log level is trace.
813 |
814 | Level `function usage: log-helper level eq|ne|gt|ge|lt|le none|error|warning|info|debug|trace`
815 | Help: [http://www.tldp.org/LDP/abs/html/comparison-ops.html](http://www.tldp.org/LDP/abs/html/comparison-ops.html)
816 |
817 | #### complex-bash-env
818 | With light-baseimage you can set bash environment variable from .yaml and .json files.
819 | But bash environment variables can't store complex objects such as table that can be defined in yaml or json files, that's why they are converted to "complex bash environment variables" and complex-bash-env tool help getting those variables values easily.
820 |
821 | For example the following yaml file:
822 |
823 | FRUITS:
824 | - orange
825 | - apple
826 |
827 | will produce this bash environment variables:
828 |
829 | FRUITS=#COMPLEX_BASH_ENV:TABLE: FRUITS_ROW_1 FRUITS_ROW_2
830 | FRUITS_ROW_1=orange
831 | FRUITS_ROW_2=apple
832 |
833 | (this is done by run tool)
834 |
835 | complex-bash-env make it easy to iterate trough this variable:
836 |
837 | for fruit in $(complex-bash-env iterate FRUITS)
838 | do
839 | echo ${!fruit}
840 | done
841 |
842 | A more complete example can be found [osixia/phpLDAPadmin](https://github.com/osixia/docker-phpLDAPadmin) image.
843 |
844 | Note this yaml definition:
845 |
846 | FRUITS:
847 | - orange
848 | - apple
849 |
850 | Can also be set by command line converted in python or json:
851 |
852 | docker run -it --env FRUITS="#PYTHON2BASH:['orange','apple']" osixia/light-baseimage:1.3.3 printenv
853 | docker run -it --env FRUITS="#JSON2BASH:[\"orange\",\"apple\"]" osixia/light-baseimage:1.3.3 printenv
854 |
855 | ### Tests
856 |
857 | We use **Bats** (Bash Automated Testing System) to test this image:
858 |
859 | > [https://github.com/bats-core/bats-core](https://github.com/bats-core/bats-core)
860 |
861 | Install Bats, and in this project directory run:
862 |
863 | make test
864 |
865 | ## Changelog
866 |
867 | Please refer to: [CHANGELOG.md](CHANGELOG.md)
868 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 | | Version | Supported |
6 | | ------- | ------------------ |
7 | | >= 2.0.0 | :heavy_check_mark: |
8 | | < 2.0.0 | :x: |
9 |
10 | ## Reporting a Vulnerability
11 |
12 | If you discover a security vulnerability within this docker image,
13 | please send an email to security@osixia.net.
14 |
15 | For minor vulnerabilities feel free to add an issue here on github.
16 |
17 | Please include as many details as possible.
18 |
--------------------------------------------------------------------------------
/example/multiple-process-image/Dockerfile:
--------------------------------------------------------------------------------
1 | # Use osixia/light-baseimage
2 | # https://github.com/osixia/docker-light-baseimage
3 | FROM osixia/light-baseimage:1.3.3
4 |
5 | # Install multiple process stack, nginx and php7.0-fpm and clean apt-get files
6 | # https://github.com/osixia/docker-light-baseimage/blob/stable/image/tool/add-multiple-process-stack
7 | RUN apt-get -y update \
8 | && /container/tool/add-multiple-process-stack \
9 | && LC_ALL=C DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
10 | nginx \
11 | php7.0-fpm \
12 | && apt-get clean \
13 | && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
14 |
15 | # Add service directory to /container/service
16 | ADD service /container/service
17 |
18 | # Use baseimage install-service script
19 | # https://github.com/osixia/docker-light-baseimage/blob/stable/image/tool/install-service
20 | RUN /container/tool/install-service
21 |
22 | # Add default env directory
23 | ADD environment /container/environment/99-default
24 |
25 | # Set /var/www/ in a data volume
26 | VOLUME /var/www/
27 |
28 | # Expose default http and https ports
29 | EXPOSE 80 443
30 |
--------------------------------------------------------------------------------
/example/multiple-process-image/Makefile:
--------------------------------------------------------------------------------
1 | NAME = example/multiple-process
2 |
3 | .PHONY: build build-nocache
4 |
5 | build:
6 | docker build -t $(NAME) --rm .
7 |
8 | build-nocache:
9 | docker build -t $(NAME) --no-cache --rm .
10 |
--------------------------------------------------------------------------------
/example/multiple-process-image/environment/default.startup.yaml:
--------------------------------------------------------------------------------
1 | # This is the default image startup configuration file
2 | # this file define environment variables used during the container **first start** in **startup files**.
3 |
4 | # This file is deleted right after startup files are processed for the first time,
5 | # after that all these values will not be available in the container environment.
6 | # This helps to keep your container configuration secret.
7 | # more information : https://github.com/osixia/docker-light-baseimage
8 |
9 | FIRST_START_SETUP_ONLY_SECRET: The bdd password is Baw0unga!
10 |
--------------------------------------------------------------------------------
/example/multiple-process-image/environment/default.yaml:
--------------------------------------------------------------------------------
1 | # This is the default image configuration file
2 | # These values will persists in container environment.
3 |
4 | # All environment variables used after the container first start
5 | # must be defined here.
6 | # more information : https://github.com/osixia/docker-light-baseimage
7 |
8 | WHO_AM_I: We are Anonymous. We are Legion. We do not forgive. We do not forget. Expect us.
9 |
--------------------------------------------------------------------------------
/example/multiple-process-image/service/nginx/install.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -e
2 | # this script is run during the image build
3 |
4 | rm -rf /var/www/html/index.nginx-debian.html
5 | echo "Hi!" > /var/www/html/index.html
6 |
--------------------------------------------------------------------------------
/example/multiple-process-image/service/nginx/process.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -e
2 | echo "The secret is: $FIRST_START_SETUP_ONLY_SECRET"
3 | exec /usr/sbin/nginx -g "daemon off;"
4 |
--------------------------------------------------------------------------------
/example/multiple-process-image/service/nginx/startup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -e
2 | FIRST_START_DONE="${CONTAINER_STATE_DIR}/nginx-first-start-done"
3 |
4 | # container first start
5 | if [ ! -e "$FIRST_START_DONE" ]; then
6 | echo ${WHO_AM_I} >> /var/www/html/index.html
7 | touch $FIRST_START_DONE
8 | fi
9 |
10 | echo "The secret is: $FIRST_START_SETUP_ONLY_SECRET"
11 |
12 | exit 0
13 |
--------------------------------------------------------------------------------
/example/multiple-process-image/service/php/config/default:
--------------------------------------------------------------------------------
1 | server {
2 | listen 80 default_server;
3 | listen [::]:80 default_server;
4 |
5 | root /var/www/html;
6 |
7 | # Add index.php to the list if you are using PHP
8 | index index.html index.htm index.nginx-debian.html;
9 |
10 | server_name _;
11 |
12 | location / {
13 | # First attempt to serve request as file, then
14 | # as directory, then fall back to displaying a 404.
15 | try_files $uri $uri/ =404;
16 | }
17 |
18 | location ~ \.php$ {
19 | fastcgi_split_path_info ^(.+\.php)(/.+)$;
20 | # With php fpm:
21 | fastcgi_pass unix:/run/php/php7.0-fpm.sock;
22 | fastcgi_index index.php;
23 | include fastcgi_params;
24 | include fastcgi.conf;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/example/multiple-process-image/service/php/install.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -e
2 | # this script is run during the image build
3 |
4 | # config
5 | sed -i -e "s/expose_php = On/expose_php = Off/g" /etc/php/7.0/fpm/php.ini
6 | sed -i -e "s/;cgi.fix_pathinfo=1/cgi.fix_pathinfo=0/g" /etc/php/7.0/fpm/php.ini
7 | sed -i -e "s/;listen.owner = www-data/listen.owner = www-data/g" /etc/php/7.0/fpm/php.ini
8 | sed -i -e "s/;listen.group = www-data/listen.group = www-data/g" /etc/php/7.0/fpm/php.ini
9 |
10 | # create php socket directory
11 | mkdir -p /run/php
12 |
13 | # replace default website with php service default website
14 | cp -f /container/service/php/config/default /etc/nginx/sites-available/default
15 |
16 | # create phpinfo.php
17 | echo " /var/www/html/phpinfo.php
18 |
--------------------------------------------------------------------------------
/example/multiple-process-image/service/php/process.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -e
2 | exec /usr/sbin/php-fpm7.0 --nodaemonize
3 |
--------------------------------------------------------------------------------
/example/single-process-image/Dockerfile:
--------------------------------------------------------------------------------
1 | # Use osixia/light-baseimage
2 | # https://github.com/osixia/docker-light-baseimage
3 | FROM osixia/light-baseimage:1.3.3
4 |
5 | # Download nginx from apt-get and clean apt-get files
6 | RUN apt-get -y update \
7 | && LC_ALL=C DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
8 | nginx \
9 | && apt-get clean \
10 | && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
11 |
12 | # Add service directory to /container/service
13 | ADD service /container/service
14 |
15 | # Use baseimage install-service script
16 | # https://github.com/osixia/docker-light-baseimage/blob/stable/image/tool/install-service
17 | RUN /container/tool/install-service
18 |
19 | # Add default env directory
20 | ADD environment /container/environment/99-default
21 |
22 | # Set /var/www/ in a data volume
23 | VOLUME /var/www/
24 |
25 | # Expose default http and https ports
26 | EXPOSE 80 443
27 |
--------------------------------------------------------------------------------
/example/single-process-image/Makefile:
--------------------------------------------------------------------------------
1 | NAME = example/single-process
2 |
3 | .PHONY: build build-nocache
4 |
5 | build:
6 | docker build -t $(NAME) --rm .
7 |
8 | build-nocache:
9 | docker build -t $(NAME) --no-cache --rm .
10 |
--------------------------------------------------------------------------------
/example/single-process-image/environment/default.startup.yaml:
--------------------------------------------------------------------------------
1 | # This is the default image startup configuration file
2 | # this file define environment variables used during the container **first start** in **startup files**.
3 |
4 | # This file is deleted right after startup files are processed for the first time,
5 | # after that all these values will not be available in the container environment.
6 | # This helps to keep your container configuration secret.
7 | # more information : https://github.com/osixia/docker-light-baseimage
8 |
9 | FIRST_START_SETUP_ONLY_SECRET: The bdd password is Baw0unga!
10 |
--------------------------------------------------------------------------------
/example/single-process-image/environment/default.yaml:
--------------------------------------------------------------------------------
1 | # This is the default image configuration file
2 | # These values will persists in container environment.
3 |
4 | # All environment variables used after the container first start
5 | # must be defined here.
6 | # more information : https://github.com/osixia/docker-light-baseimage
7 |
8 | WHO_AM_I: We are Anonymous. We are Legion. We do not forgive. We do not forget. Expect us.
9 |
--------------------------------------------------------------------------------
/example/single-process-image/service/nginx/install.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -e
2 | # this script is run during the image build
3 |
4 | rm -rf /var/www/html/index.nginx-debian.html
5 | echo "Hi!" > /var/www/html/index.html
6 |
--------------------------------------------------------------------------------
/example/single-process-image/service/nginx/process.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -e
2 | echo "The secret is: $FIRST_START_SETUP_ONLY_SECRET"
3 | exec /usr/sbin/nginx -g "daemon off;"
4 |
--------------------------------------------------------------------------------
/example/single-process-image/service/nginx/startup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -e
2 | FIRST_START_DONE="${CONTAINER_STATE_DIR}/nginx-first-start-done"
3 |
4 | # container first start
5 | if [ ! -e "$FIRST_START_DONE" ]; then
6 | echo ${WHO_AM_I} >> /var/www/html/index.html
7 | touch $FIRST_START_DONE
8 | fi
9 |
10 | echo "The secret is: $FIRST_START_SETUP_ONLY_SECRET"
11 |
12 | exit 0
13 |
--------------------------------------------------------------------------------
/example/single-process-image/test-custom-env/env.yaml:
--------------------------------------------------------------------------------
1 | WHO_AM_I: I'm bobby.
2 |
--------------------------------------------------------------------------------
/image/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM debian:buster-slim
2 |
3 | COPY . /container
4 | RUN /container/build.sh
5 |
6 | ENV LANG="en_US.UTF-8" \
7 | LANGUAGE="en_US:en" \
8 | LC_ALL="en_US.UTF-8"
9 |
10 | ENTRYPOINT ["/container/tool/run"]
11 |
--------------------------------------------------------------------------------
/image/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh -ex
2 |
3 | ## Add bash tools to /sbin
4 | ln -s /container/tool/* /sbin/
5 |
6 | mkdir -p /container/service
7 | mkdir -p /container/environment /container/environment/startup
8 | chmod 700 /container/environment/ /container/environment/startup
9 |
10 | groupadd -g 8377 docker_env
11 |
12 | # dpkg options
13 | cp /container/file/dpkg_nodoc /etc/dpkg/dpkg.cfg.d/01_nodoc
14 | cp /container/file/dpkg_nolocales /etc/dpkg/dpkg.cfg.d/01_nolocales
15 |
16 | # General config
17 | export LC_ALL=C
18 | export DEBIAN_FRONTEND=noninteractive
19 | MINIMAL_APT_GET_INSTALL='apt-get install -y --no-install-recommends'
20 |
21 | ## Prevent initramfs updates from trying to run grub and lilo.
22 | ## https://journal.paul.querna.org/articles/2013/10/15/docker-ubuntu-on-rackspace/
23 | ## http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=594189
24 | export INITRD=no
25 | printf no > /container/environment/INITRD
26 |
27 | apt-get update
28 |
29 | ## Fix some issues with APT packages.
30 | ## See https://github.com/dotcloud/docker/issues/1024
31 | dpkg-divert --local --rename --add /sbin/initctl
32 | ln -sf /bin/true /sbin/initctl
33 |
34 | ## Replace the 'ischroot' tool to make it always return true.
35 | ## Prevent initscripts updates from breaking /dev/shm.
36 | ## https://journal.paul.querna.org/articles/2013/10/15/docker-ubuntu-on-rackspace/
37 | ## https://bugs.launchpad.net/launchpad/+bug/974584
38 | dpkg-divert --local --rename --add /usr/bin/ischroot
39 | ln -sf /bin/true /usr/bin/ischroot
40 |
41 | ## Install apt-utils.
42 | $MINIMAL_APT_GET_INSTALL apt-utils apt-transport-https ca-certificates locales procps dirmngr gnupg iproute2 python3-minimal python3-yaml
43 |
44 | ## Upgrade all packages.
45 | apt-get dist-upgrade -y --no-install-recommends -o Dpkg::Options::="--force-confold"
46 |
47 | # fix locale
48 | echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen
49 | locale-gen en_US
50 | update-locale LANG=en_US.UTF-8 LC_CTYPE=en_US.UTF-8
51 |
52 | printf en_US.UTF-8 > /container/environment/LANG
53 | printf en_US.UTF-8 > /container/environment/LANGUAGE
54 | printf en_US.UTF-8 > /container/environment/LC_CTYPE
55 |
56 | apt-get clean
57 | rm -rf /tmp/* /var/tmp/*
58 | rm -rf /var/lib/apt/lists/*
59 |
60 | # Remove useless files
61 | rm -rf /container/file
62 | rm -rf /container/build.sh /container/Dockerfile
63 |
--------------------------------------------------------------------------------
/image/file/dpkg_nodoc:
--------------------------------------------------------------------------------
1 | path-exclude /usr/share/doc/*
2 | # we need to keep copyright files for legal reasons
3 | path-include /usr/share/doc/*/copyright
4 | path-exclude /usr/share/man/*
5 | path-exclude /usr/share/groff/*
6 | path-exclude /usr/share/info/*
7 | # lintian stuff is small, but really unnecessary
8 | path-exclude /usr/share/lintian/*
9 | path-exclude /usr/share/linda/*
10 |
--------------------------------------------------------------------------------
/image/file/dpkg_nolocales:
--------------------------------------------------------------------------------
1 | path-exclude /usr/share/locale/*
2 | path-include /usr/share/locale/en*
3 |
--------------------------------------------------------------------------------
/image/service-available/:cron/download.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh -e
2 |
3 | # download cron from apt-get
4 | LC_ALL=C DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends cron
5 |
--------------------------------------------------------------------------------
/image/service-available/:cron/install.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh -e
2 |
3 | chmod 600 /etc/crontab
4 |
5 | # Fix https://github.com/phusion/baseimage-docker/issues/345
6 | sed -i 's/^\s*session\s\+required\s\+pam_loginuid.so/# &/' /etc/pam.d/cron
7 |
8 | ## Remove useless cron entries.
9 | # Checks for lost+found and scans for mtab.
10 | rm -f /etc/cron.daily/standard
11 | rm -f /etc/cron.daily/upstart
12 | rm -f /etc/cron.daily/dpkg
13 | rm -f /etc/cron.daily/password
14 | rm -f /etc/cron.weekly/fstrim
15 |
--------------------------------------------------------------------------------
/image/service-available/:cron/process.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh -e
2 | log-helper level eq trace && set -x
3 |
4 | exec /usr/sbin/cron -f
5 |
--------------------------------------------------------------------------------
/image/service-available/:cron/startup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh -e
2 | log-helper level eq trace && set -x
3 |
4 | # prevent NUMBER OF HARD LINKS > 1 error
5 | # https://github.com/phusion/baseimage-docker/issues/198
6 | touch /etc/crontab /etc/cron.d /etc/cron.daily /etc/cron.hourly /etc/cron.monthly /etc/cron.weekly
7 |
8 | find /etc/cron.d/ -exec touch {} \;
9 | find /etc/cron.daily/ -exec touch {} \;
10 | find /etc/cron.hourly/ -exec touch {} \;
11 | find /etc/cron.monthly/ -exec touch {} \;
12 | find /etc/cron.weekly/ -exec touch {} \;
13 |
--------------------------------------------------------------------------------
/image/service-available/:logrotate/assets/config/logrotate.conf:
--------------------------------------------------------------------------------
1 | # see "man logrotate" for details
2 | # rotate log files weekly
3 | weekly
4 |
5 | # use the syslog group by default, since this is the owning group
6 | # of /var/log/syslog.
7 | # su root syslog
8 |
9 | # keep 4 weeks worth of backlogs
10 | rotate 4
11 |
12 | # create new (empty) log files after rotating old ones
13 | create
14 |
15 | # uncomment this if you want your log files compressed
16 | #compress
17 |
18 | # packages drop log rotation information into this directory
19 | include /etc/logrotate.d
20 |
21 | # no packages own wtmp, or btmp -- we'll rotate them here
22 | /var/log/wtmp {
23 | missingok
24 | monthly
25 | create 0664 root utmp
26 | rotate 1
27 | }
28 |
29 | /var/log/btmp {
30 | missingok
31 | monthly
32 | create 0660 root utmp
33 | rotate 1
34 | }
35 |
36 | # system-specific logs may be configured here
37 |
--------------------------------------------------------------------------------
/image/service-available/:logrotate/assets/config/logrotate_syslogng:
--------------------------------------------------------------------------------
1 | /var/log/syslog {
2 | rotate 7
3 | daily
4 | missingok
5 | notifempty
6 | delaycompress
7 | compress
8 | postrotate
9 | if [ -f /var/run/syslog-ng.pid ]; then
10 | kill -HUP `cat /var/run/syslog-ng.pid`
11 | fi
12 | endscript
13 | }
14 |
15 | /var/log/mail.info
16 | /var/log/mail.warn
17 | /var/log/mail.err
18 | /var/log/mail.log
19 | /var/log/daemon.log
20 | /var/log/kern.log
21 | /var/log/auth.log
22 | /var/log/user.log
23 | /var/log/lpr.log
24 | /var/log/cron.log
25 | /var/log/debug
26 | /var/log/messages {
27 | rotate 4
28 | weekly
29 | missingok
30 | notifempty
31 | compress
32 | delaycompress
33 | sharedscripts
34 | postrotate
35 | if [ -f /var/run/syslog-ng.pid ]; then
36 | kill -HUP `cat /var/run/syslog-ng.pid`
37 | fi
38 | endscript
39 | }
--------------------------------------------------------------------------------
/image/service-available/:logrotate/download.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh -e
2 |
3 | # download logrotate from apt-get
4 | LC_ALL=C DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends logrotate
5 |
--------------------------------------------------------------------------------
/image/service-available/:logrotate/install.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh -e
2 |
3 | rm -f /etc/logrotate.conf
4 | rm -f /etc/logrotate.d/syslog-ng
5 |
--------------------------------------------------------------------------------
/image/service-available/:logrotate/startup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh -e
2 | log-helper level eq trace && set -x
3 | ln -sf "${CONTAINER_SERVICE_DIR}/:logrotate/assets/config/logrotate.conf" /etc/logrotate.conf
4 | ln -sf "${CONTAINER_SERVICE_DIR}/:logrotate/assets/config/logrotate_syslogng" /etc/logrotate.d/syslog-ng
5 |
6 | chmod 444 -R "${CONTAINER_SERVICE_DIR}"/:logrotate/assets/config/*
7 |
--------------------------------------------------------------------------------
/image/service-available/:runit/download.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh -e
2 |
3 | # download runit from apt-get
4 | LC_ALL=C DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends runit
5 |
--------------------------------------------------------------------------------
/image/service-available/:ssl-tools/assets/cfssl-default-env:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | #
3 | # Default CA config
4 | #
5 | CFSSL_DEFAULT_CACERT="${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/default-ca/default-ca.pem"
6 | CFSSL_DEFAULT_CA_KEY="${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/default-ca/default-ca-key.pem"
7 | CFSSL_DEFAULT_CA_CONFIG="${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/default-ca/config/ca-config.json"
8 | CFSSL_DEFAULT_CSR="${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/default-ca/config/req-csr.json.tmpl"
9 |
10 | # default csr file params
11 | CFSSL_DEFAULT_CA_CSR_CN=${CFSSL_DEFAULT_CA_CSR_CN:-${HOSTNAME}}
12 |
13 | CFSSL_DEFAULT_CA_CSR_KEY_ALGO=${CFSSL_DEFAULT_CA_CSR_KEY_ALGO:-"ecdsa"}
14 | CFSSL_DEFAULT_CA_CSR_KEY_SIZE=${CFSSL_DEFAULT_CA_CSR_KEY_SIZE:-384}
15 |
16 | CFSSL_DEFAULT_CA_CSR_ORGANIZATION=${CFSSL_DEFAULT_CA_CSR_ORGANIZATION:-"A1A Car Wash"}
17 | CFSSL_DEFAULT_CA_CSR_ORGANIZATION_UNIT=${CFSSL_DEFAULT_CA_CSR_ORGANIZATION_UNIT:-"Information Technology Dep."}
18 | CFSSL_DEFAULT_CA_CSR_LOCATION=${CFSSL_DEFAULT_CA_CSR_LOCATION:-"Albuquerque"}
19 | CFSSL_DEFAULT_CA_CSR_STATE=${CFSSL_DEFAULT_CA_CSR_STATE:-"New Mexico"}
20 | CFSSL_DEFAULT_CA_CSR_COUNTRY=${CFSSL_DEFAULT_CA_CSR_COUNTRY:-"US"}
21 |
22 | #
23 | # General CFSSL config
24 | #
25 |
26 | CFSSL_RETRY=${CFSSL_RETRY:-3}
27 | CFSSL_RETRY_DELAY=${CFSSL_RETRY_DELAY:-1}
28 |
29 | # remote config
30 | CFSSL_REMOTE=${CFSSL_REMOTE:-}
31 | CFSSL_REMOTE_HTTPS_CA_CERT=${CFSSL_REMOTE_HTTPS_CA_CERT:-}
32 |
33 | # local config
34 | CFSSL_CA_CERT=${CFSSL_CA_CERT:-${CFSSL_DEFAULT_CACERT}}
35 | CFSSL_CA_KEY=${CFSSL_CA_KEY:-${CFSSL_DEFAULT_CA_KEY}}
36 |
37 | # gencert
38 | CFSSL_CSR=${CFSSL_CSR:-${CFSSL_DEFAULT_CSR}}
39 | CFSSL_CSR_JSON=${CFSSL_CSR_JSON:-}
40 | CFSSL_CONFIG=${CFSSL_CONFIG:-${CFSSL_CA_CONFIG}}
41 | CFSSL_CONFIG_JSON=${CFSSL_CONFIG_JSON:-${CFSSL_CA_CONFIG_JSON}}
42 | CFSSL_HOSTNAME=${CFSSL_HOSTNAME:-${HOSTNAME}}
43 | CFSSL_PROFILE=${CFSSL_PROFILE:-}
44 | CFSSL_LABEL=${CFSSL_LABEL:-}
45 |
--------------------------------------------------------------------------------
/image/service-available/:ssl-tools/assets/default-ca/README.md:
--------------------------------------------------------------------------------
1 | # How to generate the default CA:
2 | cfssl gencert -initca config/ca-csr.json | cfssljson -bare default-ca
3 |
--------------------------------------------------------------------------------
/image/service-available/:ssl-tools/assets/default-ca/config/ca-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "signing": {
3 | "default": {
4 | "usages": [
5 | "signing",
6 | "key encipherment",
7 | "server auth",
8 | "client auth"
9 | ],
10 | "expiry": "8760h"
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/image/service-available/:ssl-tools/assets/default-ca/config/ca-csr.json:
--------------------------------------------------------------------------------
1 | {
2 | "CN": "docker-light-baseimage",
3 | "key": {
4 | "algo": "ecdsa",
5 | "size": 384
6 | },
7 | "names": [
8 | {
9 | "O": "A1A Car Wash",
10 | "OU": "Information Technology Dep.",
11 | "L": "Albuquerque",
12 | "ST": "New Mexico",
13 | "C": "US"
14 | }
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/image/service-available/:ssl-tools/assets/default-ca/config/req-csr.json.tmpl:
--------------------------------------------------------------------------------
1 | {
2 | "CN": "{{ CFSSL_DEFAULT_CA_CSR_CN }}",
3 | "hosts": [
4 | "{{ CFSSL_DEFAULT_CA_CSR_CN }}"
5 | ],
6 | "key": {
7 | "algo": "{{ CFSSL_DEFAULT_CA_CSR_KEY_ALGO }}",
8 | "size": {{ CFSSL_DEFAULT_CA_CSR_KEY_SIZE }}
9 | },
10 | "names": [
11 | {
12 | "O": "{{ CFSSL_DEFAULT_CA_CSR_ORGANIZATION }}",
13 | "OU": "{{ CFSSL_DEFAULT_CA_CSR_ORGANIZATION_UNIT }}",
14 | "L": "{{ CFSSL_DEFAULT_CA_CSR_LOCATION }}",
15 | "ST": "{{ CFSSL_DEFAULT_CA_CSR_STATE }}",
16 | "C": "{{ CFSSL_DEFAULT_CA_CSR_COUNTRY }}"
17 | }
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/image/service-available/:ssl-tools/assets/default-ca/default-ca-key.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN EC PRIVATE KEY-----
2 | MIGkAgEBBDABfvSnlC9AZQjMRTc5o/BcUQCoBkVN8y17VaezYR709tqPptcQ9fC9
3 | 4wtM1qDVho2gBwYFK4EEACKhZANiAATWvTsmK1cEzy4711tv5oRRTJkAGUhYsoKP
4 | YV6p8M/zQ8tGbkCrFBc0nnelFzbtXkIDB00rsFotk3W4El/KWs/sNkBs5tkFoUBZ
5 | HAPeqc01M40Gpw77qoFVIU1rJiNOFNk=
6 | -----END EC PRIVATE KEY-----
7 |
--------------------------------------------------------------------------------
/image/service-available/:ssl-tools/assets/default-ca/default-ca.csr:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE REQUEST-----
2 | MIIBkTCCARYCAQAwgZYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpOZXcgTWV4aWNv
3 | MRQwEgYDVQQHEwtBbGJ1cXVlcnF1ZTEVMBMGA1UEChMMQTFBIENhciBXYXNoMSQw
4 | IgYDVQQLExtJbmZvcm1hdGlvbiBUZWNobm9sb2d5IERlcC4xHzAdBgNVBAMTFmRv
5 | Y2tlci1saWdodC1iYXNlaW1hZ2UwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATWvTsm
6 | K1cEzy4711tv5oRRTJkAGUhYsoKPYV6p8M/zQ8tGbkCrFBc0nnelFzbtXkIDB00r
7 | sFotk3W4El/KWs/sNkBs5tkFoUBZHAPeqc01M40Gpw77qoFVIU1rJiNOFNmgADAK
8 | BggqhkjOPQQDAwNpADBmAjEApeMZVHllW2JJGkaFQ6DAJXTKvISiPwj8L41AeSJk
9 | LkrdH/eq6toM06sWkSCTdsJxAjEAlJKESvBJA3MZPmUGhG4AqZ70nHTvz0GJ1fsB
10 | T5TnyBv0ERmNCCQo3AaHJLkSqDfo
11 | -----END CERTIFICATE REQUEST-----
12 |
--------------------------------------------------------------------------------
/image/service-available/:ssl-tools/assets/default-ca/default-ca.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIICrjCCAjWgAwIBAgIUcun3KuyYiVryQCfOcWz7gNP0x/AwCgYIKoZIzj0EAwMw
3 | gZYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpOZXcgTWV4aWNvMRQwEgYDVQQHEwtB
4 | bGJ1cXVlcnF1ZTEVMBMGA1UEChMMQTFBIENhciBXYXNoMSQwIgYDVQQLExtJbmZv
5 | cm1hdGlvbiBUZWNobm9sb2d5IERlcC4xHzAdBgNVBAMTFmRvY2tlci1saWdodC1i
6 | YXNlaW1hZ2UwHhcNMjEwMTE2MTE0MjAwWhcNMjYwMTE1MTE0MjAwWjCBljELMAkG
7 | A1UEBhMCVVMxEzARBgNVBAgTCk5ldyBNZXhpY28xFDASBgNVBAcTC0FsYnVxdWVy
8 | cXVlMRUwEwYDVQQKEwxBMUEgQ2FyIFdhc2gxJDAiBgNVBAsTG0luZm9ybWF0aW9u
9 | IFRlY2hub2xvZ3kgRGVwLjEfMB0GA1UEAxMWZG9ja2VyLWxpZ2h0LWJhc2VpbWFn
10 | ZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABNa9OyYrVwTPLjvXW2/mhFFMmQAZSFiy
11 | go9hXqnwz/NDy0ZuQKsUFzSed6UXNu1eQgMHTSuwWi2TdbgSX8paz+w2QGzm2QWh
12 | QFkcA96pzTUzjQanDvuqgVUhTWsmI04U2aNCMEAwDgYDVR0PAQH/BAQDAgEGMA8G
13 | A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNcSeGQ+1u3nsr2BcYY2jVecyBQlMAoG
14 | CCqGSM49BAMDA2cAMGQCMBHppmoY8E2fv0PIg8lR3Xq4bKNTH7cG3WEbR10NHPeJ
15 | NHtBrXWsnjAouXKFGS+1vgIwAVP1gZCPOTvChfTF8uOHW7RZ3UnC3xcJlGaOrC7s
16 | uElSBnLT7DIT3uBSxmIegHNH
17 | -----END CERTIFICATE-----
18 |
--------------------------------------------------------------------------------
/image/service-available/:ssl-tools/assets/default-env:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | SSL_HELPER_TOOL=${SSL_HELPER_TOOL:-"cfssl-helper"}
3 |
4 | SSL_HELPER_AUTO_RENEW=${SSL_HELPER_AUTO_RENEW:-false}
5 | SSL_HELPER_AUTO_RENEW_CRON_EXP=${SSL_HELPER_AUTO_RENEW_CRON_EXP:-"0 0 * * *"} # every day at 00:00
6 | SSL_HELPER_AUTO_RENEW_SERVICES_IMPACTED=${SSL_HELPER_AUTO_RENEW_SERVICES_IMPACTED:-}
7 | SSL_HELPER_AUTO_RENEW_FROM_FILES=${SSL_HELPER_AUTO_RENEW_FROM_FILES:-false}
8 | SSL_HELPER_AUTO_RENEW_CERT_FROM_FILE=${SSL_HELPER_AUTO_RENEW_CERT_FROM_FILE:-}
9 | SSL_HELPER_AUTO_RENEW_KEY_FROM_FILE=${SSL_HELPER_AUTO_RENEW_KEY_FROM_FILE:-}
10 | SSL_HELPER_AUTO_RENEW_CA_CERT_FROM_FILE=${SSL_HELPER_AUTO_RENEW_CA_CERT_FROM_FILE:-}
11 |
--------------------------------------------------------------------------------
/image/service-available/:ssl-tools/assets/jsonssl-default-env:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | JSONSSL_FILE_DEFAULT="${CONTAINER_SERVICE_DIR}/ssl-tools/assets/certs/certs.json"
3 |
4 | JSONSSL_FILE=${JSONSSL_FILE:-} # don't set default immediatly because we print a warning in jsonssl-helper
5 | JSONSSL_HOSTNAME=${JSONSSL_HOSTNAME:-${HOSTNAME}}
6 | JSONSSL_PROFILE=${JSONSSL_PROFILE:-} # traefik / traefik_up_to_v1_6
7 |
8 | JSONSSL_GET_CA_CERT_CMD=${JSONSSL_GET_CA_CERT_CMD:-}
9 | JSONSSL_GET_CERT_CMD=${JSONSSL_GET_CERT_CMD:-}
10 | JSONSSL_GET_KEY_CMD=${JSONSSL_GET_KEY_CMD:-}
11 |
--------------------------------------------------------------------------------
/image/service-available/:ssl-tools/assets/tool/cfssl-helper:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | log-helper level eq trace && set -x
3 |
4 | # This tool helps to generate tls certificates with cfssl
5 | # It takes cfssl configuration from environment variable.
6 | # See cfssl-default-env file
7 |
8 | PREFIX=$1
9 | CERT_FILE=$2
10 | KEY_FILE=$3
11 | CA_FILE=$4
12 |
13 | log-helper debug "cfssl-helper is launched, everybody on the floor!"
14 |
15 | # before 0.2.5 retro compatibility, will be removed.
16 | mkdir -p "${CONTAINER_SERVICE_DIR}/:cfssl/assets/default-ca"
17 | ln -sf "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/default-ca/default-ca.pem" "${CONTAINER_SERVICE_DIR}/:cfssl/assets/default-ca/default-ca.pem"
18 |
19 | if [ -z "${PREFIX}" ] || [ -z "${CERT_FILE}" ] || [ -z "${KEY_FILE}" ] || [ -z "${CA_FILE}" ]; then
20 | log-helper error "Usage: cfssl-helper prefix cert_file key_file ca_file"
21 | exit 1
22 | fi
23 |
24 | if [ ! -e "${CERT_FILE}" ] && [ ! -e "${KEY_FILE}" ]; then
25 |
26 | log-helper info "No certificate file and certificate key provided, generate:"
27 | log-helper info "${CERT_FILE} and ${KEY_FILE}"
28 |
29 | LOG_LEVEL_PARAM=""
30 |
31 | case ${CONTAINER_LOG_LEVEL} in
32 | 0 )
33 | LOG_LEVEL_PARAM="-loglevel 4";;
34 | 1 )
35 | LOG_LEVEL_PARAM="-loglevel 3";;
36 | 2 )
37 | LOG_LEVEL_PARAM="-loglevel 2";;
38 | 3 )
39 | LOG_LEVEL_PARAM="-loglevel 1";;
40 | 4 )
41 | LOG_LEVEL_PARAM="-loglevel 0";;
42 | 5 )
43 | LOG_LEVEL_PARAM="-loglevel 0";;
44 | esac
45 |
46 | # set env vars
47 | PREFIX=${PREFIX^^} # uppercase
48 |
49 | # search for prefixed env var first
50 |
51 | # set prefix variable name
52 | # example : PREFIX_CFSSL_REMOTE='MARIADB_CFSSL_REMOTE'
53 | PREFIX_CFSSL_REMOTE=${PREFIX}_CFSSL_REMOTE
54 | PREFIX_CFSSL_REMOTE_HTTPS_CA_CERT=${PREFIX}_CFSSL_REMOTE_HTTPS_CA_CERT
55 | PREFIX_CFSSL_CA_CERT=${PREFIX}_CFSSL_CA_CERT
56 | PREFIX_CFSSL_CA_KEY=${PREFIX}_CFSSL_CA_KEY
57 | PREFIX_CFSSL_CSR=${PREFIX}_CFSSL_CSR
58 | PREFIX_CFSSL_CSR_JSON=${PREFIX}_CFSSL_CSR_JSON
59 | PREFIX_CFSSL_CONFIG=${PREFIX}_CFSSL_CONFIG
60 | PREFIX_CFSSL_CONFIG_JSON=${PREFIX}_CFSSL_CONFIG_JSON
61 | PREFIX_CFSSL_HOSTNAME=${PREFIX}_CFSSL_HOSTNAME
62 | PREFIX_CFSSL_PROFILE=${PREFIX}_CFSSL_PROFILE
63 | PREFIX_CFSSL_LABEL=${PREFIX}_CFSSL_LABEL
64 | PREFIX_CFSSL_RETRY=${PREFIX}_CFSSL_RETRY
65 | PREFIX_CFSSL_RETRY_DELAY=${PREFIX}_CFSSL_RETRY_DELAY
66 |
67 | # assign CFSSL_REMOTE=${!PREFIX_CFSSL_REMOTE} if value is not empty otherwise CFSSL_REMOTE=CFSSL_REMOTE
68 | CFSSL_REMOTE=${!PREFIX_CFSSL_REMOTE:-$CFSSL_REMOTE}
69 | CFSSL_REMOTE_HTTPS_CA_CERT=${!PREFIX_CFSSL_REMOTE_HTTPS_CA_CERT:-$CFSSL_REMOTE_HTTPS_CA_CERT}
70 | CFSSL_CA_CERT=${!PREFIX_CFSSL_CA_CERT:-$CFSSL_CA_CERT}
71 | CFSSL_CA_KEY=${!PREFIX_CFSSL_CA_KEY:-$CFSSL_CA_KEY}
72 | CFSSL_CSR=${!PREFIX_CFSSL_CSR:-$CFSSL_CSR}
73 | CFSSL_CSR_JSON=${!PREFIX_CFSSL_CSR_JSON:-$CFSSL_CSR_JSON}
74 | CFSSL_CONFIG=${!PREFIX_CFSSL_CONFIG:-$CFSSL_CONFIG}
75 | CFSSL_CONFIG_JSON=${!PREFIX_CFSSL_CONFIG_JSON:-$CFSSL_CONFIG_JSON}
76 | CFSSL_HOSTNAME=${!PREFIX_CFSSL_HOSTNAME:-$CFSSL_HOSTNAME}
77 | CFSSL_PROFILE=${!PREFIX_CFSSL_PROFILE:-$CFSSL_PROFILE}
78 | CFSSL_LABEL=${!PREFIX_CFSSL_LABEL:-$CFSSL_LABEL}
79 | CFSSL_RETRY=${!PREFIX_CFSSL_RETRY:-$CFSSL_RETRY}
80 | CFSSL_RETRY_DELAY=${!PREFIX_CFSSL_RETRY_DELAY:-$CFSSL_RETRY_DELAY}
81 |
82 | source "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/cfssl-default-env"
83 |
84 | # set csr file
85 | CSR_FILE="/tmp/csr-file"
86 | if [ -n "${CFSSL_CSR_JSON}" ]; then
87 | log-helper debug "use CFSSL_CSR_JSON value as csr file"
88 | echo "${CFSSL_CSR_JSON}" > "${CSR_FILE}"
89 | elif [ -n "${CFSSL_CSR}" ]; then
90 | log-helper debug "use ${CFSSL_CSR} as csr file"
91 | cp -f "${CFSSL_CSR}" "${CSR_FILE}"
92 |
93 | # it's the default csr
94 | if [ "${CFSSL_CSR}" = "${CFSSL_DEFAULT_CSR}" ]; then
95 | sed -i "s|{{ CFSSL_DEFAULT_CA_CSR_CN }}|${CFSSL_DEFAULT_CA_CSR_CN}|g" "${CSR_FILE}"
96 | sed -i "s|{{ CFSSL_DEFAULT_CA_CSR_KEY_ALGO }}|${CFSSL_DEFAULT_CA_CSR_KEY_ALGO}|g" "${CSR_FILE}"
97 | sed -i "s|{{ CFSSL_DEFAULT_CA_CSR_KEY_SIZE }}|${CFSSL_DEFAULT_CA_CSR_KEY_SIZE}|g" "${CSR_FILE}"
98 | sed -i "s|{{ CFSSL_CERT_ORGANIZATION_UNIT }}|${CFSSL_CERT_ORGANIZATION_UNIT}|g" "${CSR_FILE}"
99 | sed -i "s|{{ CFSSL_DEFAULT_CA_CSR_ORGANIZATION }}|${CFSSL_DEFAULT_CA_CSR_ORGANIZATION}|g" "${CSR_FILE}"
100 | sed -i "s|{{ CFSSL_DEFAULT_CA_CSR_ORGANIZATION_UNIT }}|${CFSSL_DEFAULT_CA_CSR_ORGANIZATION_UNIT}|g" "${CSR_FILE}"
101 | sed -i "s|{{ CFSSL_DEFAULT_CA_CSR_LOCATION }}|${CFSSL_DEFAULT_CA_CSR_LOCATION}|g" "${CSR_FILE}"
102 | sed -i "s|{{ CFSSL_DEFAULT_CA_CSR_STATE }}|${CFSSL_DEFAULT_CA_CSR_STATE}|g" "${CSR_FILE}"
103 | sed -i "s|{{ CFSSL_DEFAULT_CA_CSR_COUNTRY }}|${CFSSL_DEFAULT_CA_CSR_COUNTRY}|g" "${CSR_FILE}"
104 | fi
105 | else
106 | log-helper error "error: no csr file provided"
107 | log-helper error "CFSSL_CSR_JSON and CFSSL_CSR are empty"
108 | exit 1
109 | fi
110 |
111 | # generate cert
112 | CONFIG_FILE="/tmp/config-file"
113 | CERT_NAME="cert"
114 |
115 | REMOTE_PARAM=""
116 | CA_CERT_PARAM=""
117 | CA_KEY_PARAM=""
118 | CONFIG_PARAM=""
119 | HOSTNAME_PARAM=""
120 | PROFILE_PARAM=""
121 | LABEL_PARAM=""
122 |
123 | if [ -n "${CFSSL_REMOTE}" ]; then
124 | REMOTE_PARAM="-remote=${CFSSL_REMOTE}"
125 |
126 | # add remote https ca cert to known certificates if not empty
127 | if [ -n "${CFSSL_REMOTE_HTTPS_CA_CERT}" ]; then
128 | if [ -e "${CFSSL_REMOTE_HTTPS_CA_CERT}" ]; then
129 | [[ ! -d "/etc/ssl/certs/" ]] && mkdir -p /etc/ssl/certs/
130 | cat "${CFSSL_REMOTE_HTTPS_CA_CERT}" >> /etc/ssl/certs/ca-certificates.crt
131 | else
132 | log-helper error "error: remote https ca cert file ${CFSSL_REMOTE_HTTPS_CA_CERT} not found"
133 | fi
134 | fi
135 |
136 | else
137 |
138 | # files path with : may cause issue with cfssl tools due to :
139 | # ReadBytes - https://github.com/cloudflare/cfssl/blob/master/helpers/helpers.go#L573
140 | # : is used to split env from file path
141 | # so we copy ca cert and key to tmp
142 | if [ -n "${CFSSL_CA_CERT}" ]; then
143 |
144 | CFSSL_CA_CERT_FILE="/tmp/ca-cert-file"
145 | cp -f "${CFSSL_CA_CERT}" "${CFSSL_CA_CERT_FILE}"
146 | chmod 644 "${CFSSL_CA_CERT_FILE}"
147 |
148 | CA_CERT_PARAM="-ca ${CFSSL_CA_CERT_FILE}"
149 | fi
150 |
151 | if [ -n "${CFSSL_CA_KEY}" ]; then
152 |
153 | CFSSL_CA_KEY_FILE="/tmp/ca-key-file"
154 | cp -f "${CFSSL_CA_KEY}" "${CFSSL_CA_KEY_FILE}"
155 | chmod 600 "${CFSSL_CA_CERT_FILE}"
156 |
157 | CA_KEY_PARAM="-ca-key ${CFSSL_CA_KEY_FILE}"
158 | fi
159 |
160 | fi
161 |
162 | if [ -n "${CFSSL_CONFIG_JSON}" ]; then
163 | log-helper debug "use CFSSL_CONFIG_JSON value as config file"
164 | echo "${CFSSL_CONFIG_JSON}" > "${CONFIG_FILE}"
165 | CONFIG_PARAM="-config ${CONFIG_FILE}"
166 |
167 | elif [ -n "${CFSSL_CONFIG}" ]; then
168 | log-helper debug "use ${CFSSL_CONFIG} as config file"
169 | cp -f "${CFSSL_CONFIG}" "${CONFIG_FILE}"
170 | CONFIG_PARAM="-config ${CONFIG_FILE}"
171 | fi
172 |
173 | if [ -n "$ADDITIONAL_HOSTNAMES" ]; then
174 | log-helper debug "additional hostnames found"
175 | CFSSL_HOSTNAME="${CFSSL_HOSTNAME},${ADDITIONAL_HOSTNAMES}"
176 | fi
177 |
178 | [[ -n "${CFSSL_HOSTNAME}" ]] && HOSTNAME_PARAM="-hostname ${CFSSL_HOSTNAME}"
179 | [[ -n "${CFSSL_PROFILE}" ]] && PROFILE_PARAM="-profile ${CFSSL_PROFILE}"
180 | [[ -n "${CFSSL_LABEL}" ]] && LABEL_PARAM="-label ${CFSSL_LABEL}"
181 |
182 | retry=0
183 | while [ $retry -lt "${CFSSL_RETRY}" ]; do
184 | log-helper debug "cfssl gencert ${LOG_LEVEL_PARAM} ${REMOTE_PARAM} ${CA_CERT_PARAM} ${CA_KEY_PARAM} ${CONFIG_PARAM} ${HOSTNAME_PARAM} ${PROFILE_PARAM} ${LABEL_PARAM} ${CSR_FILE} | cfssljson -bare /tmp/${CERT_NAME}"
185 | eval cfssl gencert "${LOG_LEVEL_PARAM}" "${REMOTE_PARAM}" "${CA_CERT_PARAM}" "${CA_KEY_PARAM}" "${CONFIG_PARAM}" "${HOSTNAME_PARAM}" "${PROFILE_PARAM}" "${LABEL_PARAM}" "${CSR_FILE}" | cfssljson -bare "/tmp/${CERT_NAME}" && break
186 | sleep "${CFSSL_RETRY_DELAY}"
187 | ((retry++))
188 | done
189 |
190 | # move generated files
191 | [[ ! -e "/tmp/${CERT_NAME}.pem" ]] && exit 1
192 | log-helper debug "move /tmp/${CERT_NAME}.pem to ${CERT_FILE}"
193 | mv "/tmp/${CERT_NAME}.pem" "${CERT_FILE}"
194 |
195 | log-helper debug "move /tmp/${CERT_NAME}-key.pem to ${KEY_FILE}"
196 | mv "/tmp/${CERT_NAME}-key.pem" "${KEY_FILE}"
197 |
198 | # if ca file don't exists
199 | if [ ! -e "${CA_FILE}" ]; then
200 |
201 | if [ -n "${CFSSL_REMOTE}" ]; then
202 | log-helper debug "Get CA certificate from ${CFSSL_REMOTE}"
203 |
204 | retry=0
205 | while [ $retry -lt "${CFSSL_RETRY}" ]; do
206 | log-helper debug "cfssl info ${LOG_LEVEL_PARAM} ${REMOTE_PARAM} ${CONFIG_PARAM} ${PROFILE_PARAM} ${LABEL_PARAM}"
207 | eval cfssl info "${LOG_LEVEL_PARAM}" "${REMOTE_PARAM}" "${CONFIG_PARAM}" "${PROFILE_PARAM}" "${LABEL_PARAM}" | sed -e "s/.*certificate\":\"\(.*-----\)\".*/\1/g" | sed 's/\\n/\n/g' > "${CA_FILE}" && log-helper debug "CA certificate returned save as ${CA_FILE}" && break
208 | sleep "${CFSSL_RETRY_DELAY}"
209 | ((retry++))
210 | done
211 |
212 | [[ ! -e "${CA_FILE}" ]] && exit 1
213 |
214 | elif [ -n "${CFSSL_CA_CERT}" ]; then
215 | log-helper info "Link ${CFSSL_CA_CERT} to ${CA_FILE}"
216 | ln -sf "${CFSSL_CA_CERT}" "${CA_FILE}"
217 | fi
218 |
219 | fi
220 |
221 | # delete tmp files
222 | rm -f /tmp/${CERT_NAME}.csr ${CONFIG_FILE} "${CSR_FILE}"
223 | [[ -e "${CFSSL_CA_CERT_FILE}" ]] && rm "${CFSSL_CA_CERT_FILE}"
224 | [[ -e "${CFSSL_CA_KEY_FILE}" ]] && rm "${CFSSL_CA_KEY_FILE}"
225 |
226 | log-helper debug "done :)"
227 |
228 | elif [ ! -e "${KEY_FILE}" ]; then
229 | log-helper error "Certificate file ${CERT_FILE} exists but not key file ${KEY_FILE}"
230 | exit 1
231 | elif [ ! -e "${CERT_FILE}" ]; then
232 | log-helper error "Key file ${KEY_FILE} exists but not certificate file ${CERT_FILE}"
233 | exit 1
234 | else
235 | log-helper debug "Files ${CERT_FILE} and ${KEY_FILE} exists, fix files permissions"
236 | chmod 644 "${CERT_FILE}"
237 | chmod 600 "${KEY_FILE}"
238 | fi
239 |
--------------------------------------------------------------------------------
/image/service-available/:ssl-tools/assets/tool/jsonssl-helper:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | log-helper level eq trace && set -x
3 |
4 | # This tool helps get certificates from json files
5 | # like kubernetes secrets or traefik acme.json
6 | # It takes its configuration from environment variable.
7 | # See json-default-env file
8 |
9 | PREFIX=$1
10 | CERT_FILE=$2
11 | KEY_FILE=$3
12 | CA_FILE=$4
13 |
14 | log-helper debug "jsonssl-helper is launched, everybody on the floor!"
15 |
16 | if [ -z "${PREFIX}" ] || [ -z "${CERT_FILE}" ] || [ -z "${KEY_FILE}" ] || [ -z "${CA_FILE}" ]; then
17 | log-helper error "Usage: jsonssl-helper prefix cert_file key_file ca_file"
18 | exit 1
19 | fi
20 |
21 | if [ ! -e "${CERT_FILE}" ] && [ ! -e "${KEY_FILE}" ]; then
22 |
23 | # set env vars
24 | PREFIX=${PREFIX^^} # uppercase
25 |
26 | # search for prefixed env var first
27 |
28 | # set prefix variable name
29 | # example : PREFIX_JSONSSL_FILE='MARIADB_JSONSSL_FILE'
30 | PREFIX_JSONSSL_FILE=${PREFIX}_JSONSSL_FILE
31 | PREFIX_JSONSSL_HOSTNAME=${PREFIX}_JSONSSL_HOSTNAME
32 |
33 | PREFIX_JSONSSL_PROFILE=${PREFIX}_JSONSSL_PROFILE
34 | PREFIX_JSONSSL_GET_CA_CERT_CMD=${PREFIX}_JSONSSL_GET_CA_CERT_CMD
35 | PREFIX_JSONSSL_GET_CERT_CMD=${PREFIX}_JSONSSL_GET_CERT_CMD
36 | PREFIX_JSONSSL_GET_KEY_CMD=${PREFIX}_JSONSSL_GET_KEY_CMD
37 |
38 | # assign JSONSSL_FILE=${!PREFIX_JSONSSL_FILE} if value is not empty otherwise JSONSSL_FILE=JSONSSL_FILE
39 | JSONSSL_FILE=${!PREFIX_JSONSSL_FILE:-$JSONSSL_FILE}
40 | JSONSSL_HOSTNAME=${!PREFIX_JSONSSL_HOSTNAME:-$JSONSSL_HOSTNAME}
41 |
42 | JSONSSL_PROFILE=${!PREFIX_JSONSSL_PROFILE:-$JSONSSL_PROFILE}
43 | JSONSSL_GET_CA_CERT_CMD=${!PREFIX_JSONSSL_GET_CA_CERT_CMD:-$JSONSSL_GET_CA_CERT_CMD}
44 | JSONSSL_GET_CERT_CMD=${!PREFIX_JSONSSL_GET_CERT_CMD:-$JSONSSL_GET_CERT_CMD}
45 | JSONSSL_GET_KEY_CMD=${!PREFIX_JSONSSL_GET_KEY_CMD:-$JSONSSL_GET_KEY_CMD}
46 |
47 | source "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/jsonssl-default-env"
48 |
49 | if [ -z "${JSONSSL_FILE}" ]; then
50 | log-helper info "Variable JSONSSL_FILE is empty, set to default location:"
51 | log-helper info "JSONSSL_FILE=${JSONSSL_FILE_DEFAULT}"
52 | JSONSSL_FILE=${JSONSSL_FILE_DEFAULT}
53 | fi
54 |
55 | if [ ! -e "${JSONSSL_FILE}" ]; then
56 | log-helper error "JSONSSL_FILE file '${JSONSSL_FILE}' not found"
57 | exit 1
58 | fi
59 |
60 | # Json file profile, only traefik for now
61 | if [ "${JSONSSL_PROFILE,,}" = "traefik" ]; then
62 | # Let's Encrypt CA certificate is in cert file after the domain certificate.
63 | # So we took what's after the first cert.
64 | JSONSSL_GET_CA_CERT_CMD="awk '{if(found) print} /END CERTIFICATE/{found=1}' ${CERT_FILE}"
65 |
66 | JSONSSL_GET_CERT_CMD="cat ${JSONSSL_FILE} | jq -r '[.Certificates[]] | map(select(.Domain.Main == \"${JSONSSL_HOSTNAME}\")) | .[0].Certificate' | base64 -d"
67 | JSONSSL_GET_KEY_CMD="cat ${JSONSSL_FILE} | jq -r '[.Certificates[]] | map(select(.Domain.Main == \"${JSONSSL_HOSTNAME}\")) | .[0].Key' | base64 -d"
68 | elif [ "${JSONSSL_PROFILE,,}" = "traefik_up_to_v1_6" ]; then
69 | # Let's Encrypt CA certificate is in cert file after the domain certificate.
70 | # So we took what's after the first cert.
71 | JSONSSL_GET_CA_CERT_CMD="awk '{if(found) print} /END CERTIFICATE/{found=1}' ${CERT_FILE}"
72 |
73 | JSONSSL_GET_CERT_CMD="cat ${JSONSSL_FILE} | jq -r '[.[\"DomainsCertificate\"].Certs[].Certificate] | map(select(.Domain == \"${JSONSSL_HOSTNAME}\")) | .[0].Certificate' | base64 -d"
74 | JSONSSL_GET_KEY_CMD="cat ${JSONSSL_FILE} | jq -r '[.[\"DomainsCertificate\"].Certs[].Certificate] | map(select(.Domain == \"${JSONSSL_HOSTNAME}\")) | .[0].PrivateKey' | base64 -d"
75 | fi
76 |
77 | log-helper debug "Run JSONSSL_GET_CERT_CMD: ${JSONSSL_GET_CERT_CMD}"
78 | log-helper debug "put return in ${CERT_FILE}"
79 | eval "${JSONSSL_GET_CERT_CMD}" > "${CERT_FILE}"
80 |
81 | if [ ! -s "$CERT_FILE" ]; then
82 | log-helper error "Generated file '${CERT_FILE}' is empty"
83 | log-helper error "Set loglevel to debug for more information"
84 | exit 1
85 | fi
86 |
87 | log-helper debug "Run JSONSSL_GET_KEY_CMD: ${JSONSSL_GET_KEY_CMD}"
88 | log-helper debug "put return in ${KEY_FILE}"
89 | eval "$JSONSSL_GET_KEY_CMD" > "${KEY_FILE}"
90 |
91 | if [ ! -s "${KEY_FILE}" ]; then
92 | log-helper error "Generated file '${KEY_FILE}' is empty"
93 | log-helper error "Set loglevel to debug for more information"
94 | exit 1
95 | fi
96 |
97 | # if CA cert doesn't exist
98 | if [ ! -e "$CA_FILE" ]; then
99 | log-helper debug "Run JSONSSL_GET_CA_CERT_CMD: ${JSONSSL_GET_CA_CERT_CMD}"
100 | log-helper debug "put return in ${CA_FILE}"
101 | eval "$JSONSSL_GET_CA_CERT_CMD" > "${CA_FILE}"
102 |
103 | if [ ! -s "$CA_FILE" ]; then
104 | log-helper error "Generated file '${CA_FILE}' is empty"
105 | log-helper error "Set loglevel to debug for more information"
106 | exit 1
107 | fi
108 | fi
109 |
110 | log-helper debug "done :)"
111 |
112 | elif [ ! -e "${KEY_FILE}" ]; then
113 | log-helper error "Certificate file ${CERT_FILE} exists but not key file ${KEY_FILE}"
114 | exit 1
115 | elif [ ! -e "${CERT_FILE}" ]; then
116 | log-helper error "Key file ${KEY_FILE} exists but not certificate file ${CERT_FILE}"
117 | exit 1
118 | else
119 | log-helper debug "Files ${CERT_FILE} and ${KEY_FILE} exists, fix files permissions"
120 | chmod 644 "${CERT_FILE}"
121 | chmod 600 "${KEY_FILE}"
122 | fi
123 |
--------------------------------------------------------------------------------
/image/service-available/:ssl-tools/assets/tool/ssl-auto-renew:
--------------------------------------------------------------------------------
1 | #!/bin/bash -e
2 |
3 | # This file aims to be called by a cron task
4 | # and not directly. See ssl-helper.
5 |
6 | source /container/run/environment.sh
7 |
8 | SSL_HELPER_TOOL=$1
9 | PREFIX=$2
10 | CERT_FILE=$3
11 | KEY_FILE=$4
12 | CA_FILE=$5
13 | IMPACTED_SERVICES=$6
14 | JSONSSL_FILE=$7
15 | FROM_FILES=$8
16 | CERT_FROM_FILE=$9
17 | KEY_FROM_FILE=${10}
18 | CA_CERT_FROM_FILE=${11}
19 |
20 | function stop_impacted_services() {
21 | # Stop impacted services
22 | if [ -n "${IMPACTED_SERVICES}" ]; then
23 | log-helper info "Services to stop: ${IMPACTED_SERVICES}"
24 |
25 | impacted_services_table=("${IMPACTED_SERVICES}")
26 | for service in "${impacted_services_table[@]}"
27 | do
28 | log-helper info "Stopping ${service}..."
29 | sv stop "/container/run/process/${service}"
30 | done
31 |
32 | log-helper info "All services are stopped"
33 | fi
34 | }
35 |
36 | function start_impacted_services() {
37 | # restart impacted services
38 | if [ -n "${IMPACTED_SERVICES}" ]; then
39 |
40 | impacted_services_table=("${IMPACTED_SERVICES}")
41 | for service in "${impacted_services_table[@]}"
42 | do
43 | log-helper info "Starting ${service}..."
44 | sv start "/container/run/process/${service}"
45 | done
46 |
47 | log-helper info "All services are started"
48 | fi
49 | }
50 |
51 | # renew from container files
52 | if [ "${FROM_FILES,,}" = "true" ]; then
53 |
54 | log-helper info "Check renew from files"
55 | renew=false
56 |
57 | # File previous md5
58 | CERT_PREVIOUS_MD5=$(cat "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/md5${CERT_FILE}.md5") || true
59 | KEY_PREVIOUS_MD5=$(cat "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/md5${KEY_FILE}.md5") || true
60 | CA_CERT_PREVIOUS_MD5=$(cat "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/md5${CA_FILE}.md5") || true
61 |
62 | # from file current md5
63 | FROM_CERT_MD5=$(md5sum "${CERT_FROM_FILE}" | awk '{ print $1 }')
64 | FROM_KEY_MD5=$(md5sum "${KEY_FROM_FILE}" | awk '{ print $1 }')
65 | FROM_CA_CERT_MD5=$(md5sum "${CA_CERT_FROM_FILE}" | awk '{ print $1 }')
66 |
67 | [[ "$CERT_PREVIOUS_MD5" != "$FROM_CERT_MD5" ]] && renew=true
68 | [[ "$KEY_PREVIOUS_MD5" != "$FROM_KEY_MD5" ]] && renew=true
69 | [[ "$CA_CERT_PREVIOUS_MD5" != "$FROM_CA_CERT_MD5" ]] && renew=true
70 |
71 | if ! $renew; then
72 | log-helper info "Certificate files are identicals"
73 | exit 0
74 | fi
75 |
76 | log-helper info "Certificate files are differents"
77 |
78 | stop_impacted_services
79 |
80 | if [ "${CERT_FROM_FILE}" != "${CERT_FILE}" ]; then
81 | log-helper info "Copy ${CERT_FROM_FILE} to ${CERT_FILE}"
82 | cp -f "${CERT_FROM_FILE}" "${CERT_FILE}"
83 | fi
84 |
85 | if [ "${KEY_FROM_FILE}" != "${KEY_FILE}" ]; then
86 | log-helper info "Copy ${KEY_FROM_FILE} to ${KEY_FILE}"
87 | cp -f "${KEY_FROM_FILE}" "${KEY_FILE}"
88 | fi
89 |
90 | if [ "${CA_CERT_FROM_FILE}" != "${CA_FILE}" ]; then
91 | log-helper info "Copy ${CA_CERT_FROM_FILE} to ${CA_FILE}"
92 | cp -f "${CA_CERT_FROM_FILE}" "${CA_FILE}"
93 | fi
94 |
95 | log-helper info "Update file md5 with new values"
96 | echo "${FROM_CERT_MD5}" > "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/md5${CERT_FILE}.md5"
97 | echo "${FROM_KEY_MD5}" > "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/md5${KEY_FILE}.md5"
98 | echo "${FROM_CA_CERT_MD5}" > "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/md5${CA_FILE}.md5"
99 |
100 | start_impacted_services
101 |
102 | # renew with cfssl or jsonssl
103 | else
104 | log-helper info "Check renew for cfssl or jsonssl"
105 |
106 | cert_ok=false
107 | ca_ok=false
108 |
109 | # the certificate will expired in the next day
110 | if openssl x509 -checkend 259200 -noout -in "${CERT_FILE}"; then
111 | log-helper info "The certificate '${CERT_FILE}' is ok for the next 3 days at least."
112 | cert_ok=true
113 | fi
114 |
115 | if openssl x509 -checkend 259200 -noout -in "${CA_FILE}"; then
116 | log-helper info "The CA certificate '${CA_FILE}' is ok for the next 3 days at least."
117 | ca_ok=true
118 | fi
119 |
120 | if [ "${SSL_HELPER_TOOL}" = "jsonssl-helper" ]; then
121 | log-helper info "Check if ${JSONSSL_FILE} has changed"
122 | JSONSSL_FILE_PREVIOUS_MD5=$(cat "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/md5${JSONSSL_FILE}.md5") || true
123 | JSONSSL_FILE_MD5=$(md5sum "${JSONSSL_FILE}" | awk '{ print $1 }')
124 |
125 | [[ "${JSONSSL_FILE_PREVIOUS_MD5}" != "${JSONSSL_FILE_MD5}" ]] && cert_ok=false
126 | fi
127 |
128 | if ${cert_ok} && ${ca_ok}; then
129 | log-helper info "Nothing to do :)"
130 | exit 0
131 | fi
132 |
133 | log-helper info "Auto-renew on the way!"
134 |
135 | stop_impacted_services
136 |
137 | log-helper info "Remove certificate files"
138 | rm -f "${CERT_FILE}" "${KEY_FILE}" "${CA_FILE}"
139 |
140 | log-helper info "Regenerate certificate with ${SSL_HELPER_TOOL}"
141 | ${SSL_HELPER_TOOL} "${PREFIX}" "${CERT_FILE}" "${KEY_FILE}" "${CA_FILE}"
142 |
143 | start_impacted_services
144 |
145 | if [ "${SSL_HELPER_TOOL}" = "jsonssl-helper" ]; then
146 | log-helper info "Update file md5 with new values"
147 | echo "${JSONSSL_FILE_MD5}" > "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/md5${JSONSSL_FILE}.md5"
148 | fi
149 |
150 | fi
151 |
152 | log-helper info "Auto-renew finished! Champagne!"
153 |
--------------------------------------------------------------------------------
/image/service-available/:ssl-tools/assets/tool/ssl-helper:
--------------------------------------------------------------------------------
1 | #!/bin/bash -e
2 | log-helper level eq trace && set -x
3 |
4 | # This tool helps to generate tls certificates with cfssl
5 | # or get certificates from a json file
6 |
7 | PREFIX=$1
8 | CERT_FILE=$2
9 | KEY_FILE=$3
10 | CA_FILE=$4
11 |
12 | log-helper debug "Hi! I'm ssl-helper, what button should i press ?"
13 |
14 | # set env vars
15 | PREFIX=${PREFIX^^} # uppercase
16 |
17 | PREFIX_SSL_HELPER_TOOL=${PREFIX}_SSL_HELPER_TOOL
18 | PREFIX_SSL_HELPER_AUTO_RENEW=${PREFIX}_SSL_HELPER_AUTO_RENEW
19 | PREFIX_SSL_HELPER_AUTO_RENEW_CRON_EXP=${PREFIX}_SSL_HELPER_AUTO_RENEW_CRON_EXP
20 | PREFIX_SSL_HELPER_AUTO_RENEW_SERVICES_IMPACTED=${PREFIX}_SSL_HELPER_AUTO_RENEW_SERVICES_IMPACTED
21 | PREFIX_SSL_HELPER_AUTO_RENEW_FROM_FILES=${PREFIX}_SSL_HELPER_AUTO_RENEW_FROM_FILES
22 | PREFIX_SSL_HELPER_AUTO_RENEW_CERT_FROM_FILE=${PREFIX}_SSL_HELPER_AUTO_RENEW_CERT_FROM_FILE
23 | PREFIX_SSL_HELPER_AUTO_RENEW_KEY_FROM_FILE=${PREFIX}_SSL_HELPER_AUTO_RENEW_KEY_FROM_FILE
24 | PREFIX_SSL_HELPER_AUTO_RENEW_CA_CERT_FROM_FILE=${PREFIX}_SSL_HELPER_AUTO_RENEW_CA_CERT_FROM_FILE
25 |
26 | SSL_HELPER_TOOL=${!PREFIX_SSL_HELPER_TOOL:-$SSL_HELPER_TOOL}
27 | SSL_HELPER_AUTO_RENEW=${!PREFIX_SSL_HELPER_AUTO_RENEW:-$SSL_HELPER_AUTO_RENEW}
28 | SSL_HELPER_AUTO_RENEW_CRON_EXP=${!PREFIX_SSL_HELPER_AUTO_RENEW_CRON_EXP:-$SSL_HELPER_AUTO_RENEW_CRON_EXP}
29 | SSL_HELPER_AUTO_RENEW_SERVICES_IMPACTED=${!PREFIX_SSL_HELPER_AUTO_RENEW_SERVICES_IMPACTED:-$SSL_HELPER_AUTO_RENEW_SERVICES_IMPACTED}
30 | SSL_HELPER_AUTO_RENEW_FROM_FILES=${!PREFIX_SSL_HELPER_AUTO_RENEW_FROM_FILES:-$SSL_HELPER_AUTO_RENEW_FROM_FILES}
31 | SSL_HELPER_AUTO_RENEW_CERT_FROM_FILE=${!PREFIX_SSL_HELPER_AUTO_RENEW_CERT_FROM_FILE:-$SSL_HELPER_AUTO_RENEW_CERT_FROM_FILE}
32 | SSL_HELPER_AUTO_RENEW_KEY_FROM_FILE=${!PREFIX_SSL_HELPER_AUTO_RENEW_KEY_FROM_FILE:-$SSL_HELPER_AUTO_RENEW_KEY_FROM_FILE}
33 | SSL_HELPER_AUTO_RENEW_CA_CERT_FROM_FILE=${!PREFIX_SSL_HELPER_AUTO_RENEW_CA_CERT_FROM_FILE:-$SSL_HELPER_AUTO_RENEW_CA_CERT_FROM_FILE}
34 |
35 | source "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/default-env"
36 |
37 | # call the certificate tool cfssl-helper (default) or jsonssl-helper
38 | ${SSL_HELPER_TOOL,,} "${PREFIX}" "${CERT_FILE}" "${KEY_FILE}" "${CA_FILE}"
39 |
40 | # auto-renew certificates just before it expired
41 | # or if source files have changed
42 | if [ "${SSL_HELPER_AUTO_RENEW,,}" = "true" ]; then
43 |
44 | # only for multiple process images (uses cron)
45 | if [ ! -e "/container/multiple_process_stack_added" ]; then
46 | log-helper error "auto-renew is available only with multiple process images"
47 | exit 1
48 | fi
49 |
50 | # if SSL_HELPER_AUTO_RENEW_FROM_FILES=true check certificate source files
51 | if [ "${SSL_HELPER_AUTO_RENEW_FROM_FILES,,}" = "true" ]; then
52 |
53 | [[ -z "${SSL_HELPER_AUTO_RENEW_CERT_FROM_FILE}" ]] && SSL_HELPER_AUTO_RENEW_CERT_FROM_FILE=${CERT_FILE}
54 | [[ -z "${SSL_HELPER_AUTO_RENEW_KEY_FROM_FILE}" ]] && SSL_HELPER_AUTO_RENEW_KEY_FROM_FILE=${KEY_FILE}
55 | [[ -z "${SSL_HELPER_AUTO_RENEW_CA_CERT_FROM_FILE}" ]] && SSL_HELPER_AUTO_RENEW_CA_CERT_FROM_FILE=${CA_FILE}
56 |
57 | if [ ! -e "${SSL_HELPER_AUTO_RENEW_CERT_FROM_FILE}" ] || [ ! -e "${SSL_HELPER_AUTO_RENEW_KEY_FROM_FILE}" ] || [ ! -e "${SSL_HELPER_AUTO_RENEW_CA_CERT_FROM_FILE}" ]; then
58 | log-helper error "with SSL_HELPER_AUTO_RENEW_FROM_FILES=true the following files must exists:"
59 | log-helper error "SSL_HELPER_AUTO_RENEW_CERT_FROM_FILE=${SSL_HELPER_AUTO_RENEW_CERT_FROM_FILE}"
60 | log-helper error "SSL_HELPER_AUTO_RENEW_KEY_FROM_FILE=${SSL_HELPER_AUTO_RENEW_KEY_FROM_FILE}"
61 | log-helper error "SSL_HELPER_AUTO_RENEW_CA_CERT_FROM_FILE=${SSL_HELPER_AUTO_RENEW_CA_CERT_FROM_FILE}"
62 | exit 1
63 | fi
64 |
65 | mkdir -p "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/md5$(dirname "${CERT_FILE}")"
66 | mkdir -p "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/md5$(dirname "${KEY_FILE}")"
67 | mkdir -p "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/md5$(dirname "${CA_FILE}")"
68 |
69 | # calculate certificates files md5
70 | md5sum "${CERT_FILE}" | awk '{ print $1 }' > "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/md5${CERT_FILE}.md5"
71 | md5sum "${KEY_FILE}" | awk '{ print $1 }' > "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/md5${KEY_FILE}.md5"
72 | md5sum "${CA_FILE}" | awk '{ print $1 }' > "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/md5${CA_FILE}.md5"
73 |
74 | fi
75 |
76 | if [ "${SSL_HELPER_TOOL,,}" = "jsonssl-helper" ]; then
77 |
78 | PREFIX_JSONSSL_FILE=${PREFIX}_JSONSSL_FILE
79 | JSONSSL_FILE=${!PREFIX_JSONSSL_FILE:-$JSONSSL_FILE}
80 |
81 | source "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/jsonssl-default-env"
82 |
83 | if [ -z "${JSONSSL_FILE}" ]; then
84 | JSONSSL_FILE=${JSONSSL_FILE_DEFAULT}
85 | fi
86 |
87 | # calculate jsonssl file md5
88 | mkdir -p "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/md5$(dirname "${JSONSSL_FILE}")"
89 | md5sum "${JSONSSL_FILE}" | awk '{ print $1 }' > "${CONTAINER_SERVICE_DIR}/:ssl-tools/assets/md5${JSONSSL_FILE}.md5"
90 |
91 | fi
92 |
93 | # add cron job
94 | echo "${SSL_HELPER_AUTO_RENEW_CRON_EXP} root /usr/sbin/ssl-auto-renew ${SSL_HELPER_TOOL,,} ${PREFIX} ${CERT_FILE} ${KEY_FILE} ${CA_FILE} \"${SSL_HELPER_AUTO_RENEW_SERVICES_IMPACTED}\" \"${JSONSSL_FILE}\" \"${SSL_HELPER_AUTO_RENEW_FROM_FILES}\" \"${SSL_HELPER_AUTO_RENEW_CERT_FROM_FILE}\" \"${SSL_HELPER_AUTO_RENEW_KEY_FROM_FILE}\" \"${SSL_HELPER_AUTO_RENEW_CA_CERT_FROM_FILE}\" 2>&1 | /usr/bin/logger -t cron_ssl_auto_renew" > "/etc/cron.d/${PREFIX}"
95 | chmod 600 "/etc/cron.d/${PREFIX}"
96 |
97 | # disable auto-renew if it was added
98 | elif [ -e "/etc/cron.d/${PREFIX}" ]; then
99 | rm -f "/etc/cron.d/${PREFIX}"
100 | fi
101 |
--------------------------------------------------------------------------------
/image/service-available/:ssl-tools/download.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -e
2 |
3 | UARCH=$(uname -m)
4 | echo "Architecture is ${UARCH}"
5 |
6 | case "${UARCH}" in
7 |
8 | "x86_64")
9 | HOST_ARCH="amd64"
10 | ;;
11 |
12 | "arm64" | "aarch64")
13 | HOST_ARCH="arm64"
14 | ;;
15 |
16 | "armv7l" | "armv6l" | "armhf")
17 | HOST_ARCH="arm"
18 | ;;
19 |
20 | "i386")
21 | HOST_ARCH="386"
22 | ;;
23 |
24 | *)
25 | echo "Architecture not supported. Exiting."
26 | exit 1
27 | ;;
28 | esac
29 |
30 | echo "Going to use ${HOST_ARCH} cfssl binaries"
31 |
32 | # download curl and ca-certificate from apt-get if needed
33 | to_install=()
34 |
35 | if [ "$(dpkg-query -W -f='${Status}' curl 2>/dev/null | grep -c "ok installed")" -eq 0 ]; then
36 | to_install+=("curl")
37 | fi
38 |
39 | if [ "$(dpkg-query -W -f='${Status}' ca-certificates 2>/dev/null | grep -c "ok installed")" -eq 0 ]; then
40 | to_install+=("ca-certificates")
41 | fi
42 |
43 | if [ ${#to_install[@]} -ne 0 ]; then
44 | LC_ALL=C DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends "${to_install[@]}"
45 | fi
46 |
47 | LC_ALL=C DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends openssl jq
48 |
49 | # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=923479
50 | if [[ "${HOST_ARCH}" == 'arm' ]]; then
51 | LC_ALL=C DEBIAN_FRONTEND=noninteractive c_rehash
52 | fi
53 |
54 | echo "Download cfssl ..."
55 | echo "curl -o /usr/sbin/cfssl -SL https://github.com/osixia/cfssl/releases/download/1.5.0/cfssl_linux-${HOST_ARCH}"
56 | curl -o /usr/sbin/cfssl -SL "https://github.com/osixia/cfssl/releases/download/1.5.0/cfssl_linux-${HOST_ARCH}"
57 | chmod 700 /usr/sbin/cfssl
58 |
59 | echo "Download cfssljson ..."
60 | echo "curl -o /usr/sbin/cfssljson -SL https://github.com/osixia/cfssl/releases/download/1.5.0/cfssljson_linux-${HOST_ARCH}"
61 | curl -o /usr/sbin/cfssljson -SL "https://github.com/osixia/cfssl/releases/download/1.5.0/cfssljson_linux-${HOST_ARCH}"
62 | chmod 700 /usr/sbin/cfssljson
63 |
64 | echo "Project sources: https://github.com/cloudflare/cfssl"
65 |
66 | # remove tools installed to download cfssl
67 | if [ ${#to_install[@]} -ne 0 ]; then
68 | apt-get remove -y --purge --auto-remove "${to_install[@]}"
69 | fi
70 |
--------------------------------------------------------------------------------
/image/service-available/:ssl-tools/startup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh -e
2 | log-helper level eq trace && set -x
3 |
4 | chmod 700 "${CONTAINER_SERVICE_DIR}"/:ssl-tools/assets/tool/*
5 | ln -sf "${CONTAINER_SERVICE_DIR}"/:ssl-tools/assets/tool/* /usr/sbin
6 |
--------------------------------------------------------------------------------
/image/service-available/:syslog-ng-core/assets/config/syslog-ng.conf:
--------------------------------------------------------------------------------
1 | @version: 3.19
2 | @include "scl.conf"
3 |
4 | # Syslog-ng configuration file, compatible with default Debian syslogd
5 | # installation.
6 |
7 | # First, set some global options.
8 | options { chain_hostnames(off); flush_lines(0); use_dns(no); dns-cache(no); use_fqdn(no);
9 | owner("root"); group("adm"); perm(0640); stats_freq(0);
10 | bad_hostname("^gconfd$");
11 | };
12 |
13 | ########################
14 | # Sources
15 | ########################
16 | # This is the default behavior of sysklogd package
17 | # Logs may come from unix stream, but not from another machine.
18 | #
19 | source s_src {
20 | unix-dgram("/dev/log");
21 | internal();
22 | };
23 |
24 | # If you wish to get logs from remote machine you should uncomment
25 | # this and comment the above source line.
26 | #
27 | #source s_net { tcp(ip(127.0.0.1) port(1000)); };
28 |
29 | ########################
30 | # Destinations
31 | ########################
32 | # First some standard logfile
33 | #
34 | destination d_auth { file("/var/log/auth.log"); };
35 | destination d_cron { file("/var/log/cron.log"); };
36 | destination d_daemon { file("/var/log/daemon.log"); };
37 | destination d_kern { file("/var/log/kern.log"); };
38 | destination d_lpr { file("/var/log/lpr.log"); };
39 | destination d_mail { file("/var/log/mail.log"); };
40 | destination d_syslog { file("/var/log/syslog"); };
41 | destination d_user { file("/var/log/user.log"); };
42 | destination d_uucp { file("/var/log/uucp.log"); };
43 |
44 | # This files are the log come from the mail subsystem.
45 | #
46 | destination d_mailinfo { file("/var/log/mail.info"); };
47 | destination d_mailwarn { file("/var/log/mail.warn"); };
48 | destination d_mailerr { file("/var/log/mail.err"); };
49 |
50 | # Logging for INN news system
51 | #
52 | destination d_newscrit { file("/var/log/news/news.crit"); };
53 | destination d_newserr { file("/var/log/news/news.err"); };
54 | destination d_newsnotice { file("/var/log/news/news.notice"); };
55 |
56 | # Some 'catch-all' logfiles.
57 | #
58 | destination d_debug { file("/var/log/debug"); };
59 | destination d_error { file("/var/log/error"); };
60 | destination d_messages { file("/var/log/messages"); };
61 |
62 | # The named pipe /dev/xconsole is for the nsole' utility. To use it,
63 | # you must invoke nsole' with the -file' option:
64 | #
65 | # $ xconsole -file /dev/xconsole [...]
66 | #
67 | destination d_xconsole { pipe("/dev/xconsole"); };
68 |
69 | # Send the messages to an other host
70 | #
71 | #destination d_net { tcp("127.0.0.1" port(1000) log_fifo_size(1000)); };
72 |
73 | # Debian only
74 | destination d_ppp { file("/var/log/ppp.log"); };
75 |
76 | # stdout for docker
77 | destination d_stdout { ##SYSLOG_OUTPUT_MODE_DEV_STDOUT##("/dev/stdout"); };
78 |
79 | ########################
80 | # Filters
81 | ########################
82 | # Here's come the filter options. With this rules, we can set which
83 | # message go where.
84 |
85 | filter f_dbg { level(debug); };
86 | filter f_info { level(info); };
87 | filter f_notice { level(notice); };
88 | filter f_warn { level(warn); };
89 | filter f_err { level(err); };
90 | filter f_crit { level(crit .. emerg); };
91 |
92 | filter f_debug { level(debug) and not facility(auth, authpriv, news, mail); };
93 | filter f_error { level(err .. emerg) ; };
94 | filter f_messages { level(info,notice,warn) and
95 | not facility(auth,authpriv,cron,daemon,mail,news); };
96 |
97 | filter f_auth { facility(auth, authpriv) and not filter(f_debug); };
98 | filter f_cron { facility(cron) and not filter(f_debug); };
99 | filter f_daemon { facility(daemon) and not filter(f_debug); };
100 | filter f_kern { facility(kern) and not filter(f_debug); };
101 | filter f_lpr { facility(lpr) and not filter(f_debug); };
102 | filter f_local { facility(local0, local1, local3, local4, local5,
103 | local6, local7) and not filter(f_debug); };
104 | filter f_mail { facility(mail) and not filter(f_debug); };
105 | filter f_news { facility(news) and not filter(f_debug); };
106 | filter f_syslog3 { not facility(auth, authpriv, mail) and not filter(f_debug); };
107 | filter f_user { facility(user) and not filter(f_debug); };
108 | filter f_uucp { facility(uucp) and not filter(f_debug); };
109 |
110 | filter f_cnews { level(notice, err, crit) and facility(news); };
111 | filter f_cother { level(debug, info, notice, warn) or facility(daemon, mail); };
112 |
113 | filter f_ppp { facility(local2) and not filter(f_debug); };
114 | filter f_console { level(warn .. emerg); };
115 |
116 | ########################
117 | # Log paths
118 | ########################
119 | log { source(s_src); filter(f_auth); destination(d_auth); };
120 | log { source(s_src); filter(f_cron); destination(d_cron); };
121 | log { source(s_src); filter(f_daemon); destination(d_daemon); };
122 | log { source(s_src); filter(f_kern); destination(d_kern); };
123 | log { source(s_src); filter(f_lpr); destination(d_lpr); };
124 | log { source(s_src); filter(f_syslog3); destination(d_syslog); destination(d_stdout); };
125 | log { source(s_src); filter(f_user); destination(d_user); };
126 | log { source(s_src); filter(f_uucp); destination(d_uucp); };
127 |
128 | log { source(s_src); filter(f_mail); destination(d_mail); };
129 | #log { source(s_src); filter(f_mail); filter(f_info); destination(d_mailinfo); };
130 | #log { source(s_src); filter(f_mail); filter(f_warn); destination(d_mailwarn); };
131 | #log { source(s_src); filter(f_mail); filter(f_err); destination(d_mailerr); };
132 |
133 | log { source(s_src); filter(f_news); filter(f_crit); destination(d_newscrit); };
134 | log { source(s_src); filter(f_news); filter(f_err); destination(d_newserr); };
135 | log { source(s_src); filter(f_news); filter(f_notice); destination(d_newsnotice); };
136 | #log { source(s_src); filter(f_cnews); destination(d_console_all); };
137 | #log { source(s_src); filter(f_cother); destination(d_console_all); };
138 |
139 | #log { source(s_src); filter(f_ppp); destination(d_ppp); };
140 |
141 | log { source(s_src); filter(f_debug); destination(d_debug); };
142 | log { source(s_src); filter(f_error); destination(d_error); };
143 | log { source(s_src); filter(f_messages); destination(d_messages); };
144 |
145 | # All messages send to a remote site
146 | #
147 | #log { source(s_src); destination(d_net); };
148 |
149 | ###
150 | # Include all config files in /etc/syslog-ng/conf.d/
151 | ###
152 | @include "/etc/syslog-ng/conf.d/*.conf"
153 |
--------------------------------------------------------------------------------
/image/service-available/:syslog-ng-core/assets/config/syslog_ng_default:
--------------------------------------------------------------------------------
1 | # If a variable is not set here, then the corresponding
2 | # parameter will not be changed.
3 | # If a variables is set, then every invocation of
4 | # syslog-ng's init script will set them using dmesg.
5 |
6 | # log level of messages which should go to console
7 | # see syslog(3) for details
8 | #
9 | #CONSOLE_LOG_LEVEL=1
10 |
11 | # Command line options to syslog-ng
12 | SYSLOGNG_OPTS="--no-caps"
13 |
--------------------------------------------------------------------------------
/image/service-available/:syslog-ng-core/download.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh -e
2 |
3 | # download syslog-ng-core from apt-get
4 | LC_ALL=C DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends syslog-ng-core
5 |
--------------------------------------------------------------------------------
/image/service-available/:syslog-ng-core/install.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh -e
2 |
3 | mkdir -p /var/lib/syslog-ng
4 | rm -f /etc/default/syslog-ng
5 |
6 | touch /var/log/syslog
7 | chmod u=rw,g=r,o= /var/log/syslog
8 | rm -f /etc/syslog-ng/syslog-ng.conf
9 |
--------------------------------------------------------------------------------
/image/service-available/:syslog-ng-core/process.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh -e
2 | log-helper level eq trace && set -x
3 |
4 | PIDFILE="/var/run/syslog-ng.pid"
5 | SYSLOGNG_OPTS=""
6 |
7 | [ -r /etc/default/syslog-ng ] && . /etc/default/syslog-ng
8 |
9 | exec /usr/sbin/syslog-ng --pidfile "$PIDFILE" -F $SYSLOGNG_OPTS
10 |
--------------------------------------------------------------------------------
/image/service-available/:syslog-ng-core/startup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh -e
2 | log-helper level eq trace && set -x
3 |
4 | ln -sf "${CONTAINER_SERVICE_DIR}/:syslog-ng-core/assets/config/syslog_ng_default" /etc/default/syslog-ng
5 | ln -sf "${CONTAINER_SERVICE_DIR}/:syslog-ng-core/assets/config/syslog-ng.conf" /etc/syslog-ng/syslog-ng.conf
6 |
7 | # If /dev/log is either a named pipe or it was placed there accidentally,
8 | # e.g. because of the issue documented at https://github.com/phusion/baseimage-docker/pull/25,
9 | # then we remove it.
10 | if [ ! -S /dev/log ]; then rm -f /dev/log; fi
11 | if [ ! -S /var/lib/syslog-ng/syslog-ng.ctl ]; then rm -f /var/lib/syslog-ng/syslog-ng.ctl; fi
12 |
13 | # determine output mode on /dev/stdout because of the issue documented at https://github.com/phusion/baseimage-docker/issues/468
14 | if [ -p /dev/stdout ]; then
15 | sed -i 's/##SYSLOG_OUTPUT_MODE_DEV_STDOUT##/pipe/' /etc/syslog-ng/syslog-ng.conf
16 | else
17 | sed -i 's/##SYSLOG_OUTPUT_MODE_DEV_STDOUT##/file/' /etc/syslog-ng/syslog-ng.conf
18 | fi
19 |
20 | # If /var/log is writable by another user logrotate will fail
21 | /bin/chown root:root /var/log
22 | /bin/chmod 0755 /var/log
23 |
--------------------------------------------------------------------------------
/image/tool/add-multiple-process-stack:
--------------------------------------------------------------------------------
1 | #!/bin/sh -e
2 | echo "Install the multiple process stack: runit, syslog-ng-core, logrotate and cron"
3 | /container/tool/add-service-available :runit :syslog-ng-core :logrotate :cron
4 | touch /container/multiple_process_stack_added
5 |
--------------------------------------------------------------------------------
/image/tool/add-service-available:
--------------------------------------------------------------------------------
1 | #!/bin/sh -e
2 |
3 | # Usage :
4 | # RUN /container/tool/add-service-available [service1] [service2] ...
5 |
6 | SERVICE_DIR="/container/service"
7 | SERVICE_AVAILABLE_DIR="/container/service-available"
8 | DOWNLOAD_FILENAME="download.sh"
9 |
10 | for i in "$@"
11 | do
12 |
13 | echo "add-service-available: ${i}"
14 | if [ -d "${SERVICE_AVAILABLE_DIR}/${i}" ]; then
15 |
16 | if [ -f "${SERVICE_AVAILABLE_DIR}/${i}/${DOWNLOAD_FILENAME}" ]; then
17 | echo "run ${SERVICE_AVAILABLE_DIR}/${i}/${DOWNLOAD_FILENAME}"
18 | ${SERVICE_AVAILABLE_DIR}/"${i}"/"${DOWNLOAD_FILENAME}"
19 | echo "remove ${SERVICE_AVAILABLE_DIR}/${i}/${DOWNLOAD_FILENAME}"
20 | rm -f "${SERVICE_AVAILABLE_DIR}/${i}/${DOWNLOAD_FILENAME}"
21 | fi
22 |
23 | echo "move ${SERVICE_AVAILABLE_DIR}/${i} to ${SERVICE_DIR}/${i}"
24 | mv "${SERVICE_AVAILABLE_DIR}/${i}" "${SERVICE_DIR}/${i}"
25 |
26 | else
27 | echo "service-available: ${i} not found in ${SERVICE_AVAILABLE_DIR}/${i}"
28 | exit 1
29 | fi
30 | done
31 |
--------------------------------------------------------------------------------
/image/tool/complex-bash-env:
--------------------------------------------------------------------------------
1 | #!/bin/bash -e
2 |
3 | call=$1
4 |
5 | function iterate() {
6 | local env_var_name=$1
7 | local env_var=${!env_var_name}
8 |
9 | if [ "$(complex-bash-env isTable "$env_var")" = true ]; then
10 | complex-bash-env stripTablePrefix "${env_var}"
11 | else
12 | echo "${env_var_name}"
13 | fi
14 | }
15 |
16 | function isTable() {
17 | local env_var=$1
18 | if [ "$(echo "${env_var}" | grep "#COMPLEX_BASH_ENV:TABLE:" -c )" -eq 1 ]; then
19 | echo true
20 | else
21 | echo false
22 | fi
23 | }
24 |
25 | function isRow() {
26 | local env_var=$1
27 | if [ "$(echo "${env_var}" | grep "#COMPLEX_BASH_ENV:ROW:" -c )" -eq 1 ]; then
28 | echo true
29 | else
30 | echo false
31 | fi
32 | }
33 |
34 | function getRowKey() {
35 | local env_var=$1
36 | local row_key_var_name
37 | row_key_var_name=$(complex-bash-env getRowKeyVarName "$env_var")
38 | echo "${!row_key_var_name}"
39 | }
40 |
41 | function getRowValue() {
42 | local env_var=$1
43 | local row_value_var_name
44 | row_value_var_name=$(complex-bash-env getRowValueVarName "$env_var")
45 | echo "${!row_value_var_name}"
46 | }
47 |
48 | function getRowKeyVarName() {
49 | local env_var=$1
50 | local row=($(complex-bash-env getRow "$env_var"))
51 | echo "${row[0]}"
52 | }
53 |
54 | function getRowValueVarName() {
55 | local env_var=$1
56 | local row=($(complex-bash-env getRow "$env_var"))
57 | echo "${row[1]}"
58 | }
59 |
60 | function getRow() {
61 | local env_var
62 | env_var=$1
63 | if [ "$(complex-bash-env isRow "$env_var")" = true ]; then
64 | local env_var
65 | env_var=$(complex-bash-env stripRowPrefix "$env_var")
66 | echo "${env_var}"
67 | else
68 | echo "$env_var is not a complex bash env row"
69 | exit 1
70 | fi
71 | }
72 |
73 | function stripTablePrefix() {
74 | local env_var=$1
75 | stripPrefix "$env_var" "#COMPLEX_BASH_ENV:TABLE:"
76 | }
77 |
78 | function stripRowPrefix() {
79 | local env_var=$1
80 | stripPrefix "$env_var" "#COMPLEX_BASH_ENV:ROW:"
81 | }
82 |
83 | function stripPrefix() {
84 | local env_var=$1
85 | local prefix=$2
86 | local r=${env_var#$prefix}
87 | echo "${r}"
88 | }
89 |
90 | shift
91 | $call "$@"
92 |
--------------------------------------------------------------------------------
/image/tool/install-service:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3 -u
2 | import os, os.path, subprocess
3 |
4 | SERVICE_DIR = "/container/service"
5 | INSTALL_FILENAME = "install.sh"
6 | PROCESS_FILENAME = "process.sh"
7 | nb_process = 0
8 |
9 | print("install-service")
10 | # Auto run global install script if available
11 | if os.path.isfile(SERVICE_DIR + os.sep + INSTALL_FILENAME):
12 | print(("run " + SERVICE_DIR + os.sep + INSTALL_FILENAME))
13 | subprocess.call([SERVICE_DIR + os.sep + INSTALL_FILENAME],shell=True)
14 |
15 | print(("remove " + SERVICE_DIR + os.sep + INSTALL_FILENAME + "\n"))
16 | os.remove(SERVICE_DIR + os.sep + INSTALL_FILENAME)
17 |
18 | # Process install script of services in /container/service
19 | for service in sorted(os.listdir(SERVICE_DIR)):
20 |
21 | if os.path.isfile(SERVICE_DIR + os.sep + service + os.sep + INSTALL_FILENAME):
22 | print(("run " + SERVICE_DIR + os.sep + service + os.sep + INSTALL_FILENAME))
23 | subprocess.call([SERVICE_DIR + os.sep + service + os.sep + INSTALL_FILENAME],shell=True)
24 |
25 | print(("remove " + SERVICE_DIR + os.sep + service + os.sep + INSTALL_FILENAME))
26 | os.remove(SERVICE_DIR + os.sep + service + os.sep + INSTALL_FILENAME)
27 |
28 | if os.path.isfile(SERVICE_DIR + os.sep + service + os.sep + PROCESS_FILENAME):
29 | nb_process += 1
30 |
31 |
32 | print((str(nb_process) + " process found."))
33 |
34 | # Multiple process image
35 | if nb_process > 1:
36 | if not os.path.exists("/container/multiple_process_stack_added"):
37 | print("This image has multiple process.")
38 | subprocess.call(["apt-get update"],shell=True)
39 | subprocess.call(["/container/tool/add-multiple-process-stack"],shell=True)
40 | print("For better image build process consider adding:")
41 | print("\"/container/tool/add-multiple-process-stack\" after an apt-get update in your Dockerfile.")
42 |
--------------------------------------------------------------------------------
/image/tool/log-helper:
--------------------------------------------------------------------------------
1 | #!/bin/bash -e
2 |
3 | # log helper base on environment variable CONTAINER_LOG_LEVEL
4 | # CONTAINER_LOG_LEVEL environment variable is set by run tool based on --log-level argument (info by default)
5 | # or you can set it directly with docker --env argument
6 |
7 | # Usage example: log-helper info CONTAINER_LOG_LEVEL is info or more
8 | # the message "CONTAINER_LOG_LEVEL is info or more" will be printed only if log level is info, debug or trace
9 |
10 | LOG_LEVEL_NONE=0
11 | LOG_LEVEL_ERROR=1
12 | LOG_LEVEL_WARNING=2
13 | LOG_LEVEL_INFO=3
14 | LOG_LEVEL_DEBUG=4
15 | LOG_LEVEL_TRACE=5
16 |
17 | # default log level if CONTAINER_LOG_LEVEL is not set -> info
18 | log_level=${CONTAINER_LOG_LEVEL:-${LOG_LEVEL_INFO}}
19 |
20 | call=$1 # function to call (error, warning, info, debug, trace, level)
21 | if [[ ! "$call" =~ ^(error|warning|info|debug|trace|level)$ ]]; then
22 | echo "Error: Function $call not found"
23 | echo "Allowed functions are: error, warning, info, debug, trace, level"
24 | echo "usage example: log-helper info hello !"
25 | exit 1
26 | fi
27 |
28 |
29 | echo_msg="" # message to print if required log level is set
30 | echo_param="" # echo command parameters
31 |
32 | function print_log(){
33 | local level_txt=$1
34 | local message=$2
35 | local date=$(date +"%Y-%m-%d %T")
36 |
37 | readarray -t messages <<<"$message"
38 |
39 | for m in "${messages[@]}"; do
40 | echo "*** ${level_txt} | ${date} | ${m}"
41 | done
42 | }
43 |
44 | function error() {
45 |
46 | # getEchoParams no matter what level it is to not break pipes
47 | getEchoParams $@
48 |
49 | if [ $log_level -ge 1 ]; then
50 | echo $echo_param "$(print_log " ERROR " "$echo_msg")"
51 | fi
52 | }
53 |
54 | function warning() {
55 |
56 | # getEchoParams no matter what level it is to not break pipes
57 | getEchoParams $@
58 |
59 | if [ $log_level -ge 2 ]; then
60 | echo $echo_param "$(print_log "WARNING" "$echo_msg")"
61 | fi
62 | }
63 |
64 | function info() {
65 |
66 | # getEchoParams no matter what level it is to not break pipes
67 | getEchoParams $@
68 |
69 | if [ $log_level -ge 3 ]; then
70 | echo $echo_param "$(print_log " INFO " "$echo_msg")"
71 | fi
72 | }
73 |
74 | function debug() {
75 |
76 | # getEchoParams no matter what level it is to not break pipes
77 | getEchoParams $@
78 |
79 | if [ $log_level -ge 4 ]; then
80 | echo $echo_param "$(print_log " DEBUG " "$echo_msg")"
81 | fi
82 | }
83 |
84 | function trace() {
85 |
86 | # getEchoParams no matter what level it is to not break pipes
87 | getEchoParams $@
88 |
89 | if [ $log_level -ge 5 ]; then
90 | echo $echo_param "$(print_log " TRACE " "$echo_msg")"
91 | fi
92 | }
93 |
94 | function getMsgFromStdin() {
95 | if [ -z "$2" ]; then
96 | echo_msg=$(cat)
97 | fi
98 | }
99 |
100 | function getEchoParams() {
101 |
102 | echo_msg="$@"
103 |
104 | if [[ "$1" =~ ^(-e|-n|-E)$ ]]; then
105 | echo_param=$1
106 | echo_msg=${echo_msg#$1 }
107 | fi
108 |
109 | # read from pipe if echo_msg is empty
110 | [[ -n "$echo_msg" ]] || getMsgFromStdin
111 | }
112 |
113 | function level() {
114 |
115 | local operator=$1
116 | local loglevel_str=$2
117 | local loglevel_str=${loglevel_str^^} # uppercase
118 |
119 | if [[ ! "$operator" =~ ^(eq|ne|gt|ge|lt|le)$ ]]; then
120 | echo "Error: Operator $operator not allowed"
121 | echo "Allowed operators are: eq, ne, gt, ge, lt, le"
122 | echo "Help: http://www.tldp.org/LDP/abs/html/comparison-ops.html"
123 | exit 1
124 | fi
125 |
126 | if [ -z "$loglevel_str" ]; then
127 | echo "Error: No log level provided"
128 | echo "Allowed log level are: none, error, warning, info, debug, trace"
129 | echo "usage example: log-helper level eq info"
130 | exit 1
131 | fi
132 |
133 | local log_level_var=LOG_LEVEL_$loglevel_str
134 |
135 | if [ $log_level -$operator ${!log_level_var} ]; then
136 | exit 0
137 | else
138 | exit 1
139 | fi
140 | }
141 |
142 | shift
143 | $call "$@"
144 |
--------------------------------------------------------------------------------
/image/tool/run:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3 -u
2 | # -*- coding: utf-8 -*-
3 |
4 | import os, os.path, sys, stat, signal, errno, argparse, time, json, re, yaml, ast, socket, shutil, pwd, grp
5 | from datetime import datetime
6 |
7 | KILL_PROCESS_TIMEOUT = int(os.environ.get('KILL_PROCESS_TIMEOUT', 30))
8 | KILL_ALL_PROCESSES_TIMEOUT = int(os.environ.get('KILL_ALL_PROCESSES_TIMEOUT', 30))
9 |
10 | LOG_LEVEL_NONE = 0
11 | LOG_LEVEL_ERROR = 1
12 | LOG_LEVEL_WARNING = 2
13 | LOG_LEVEL_INFO = 3
14 | LOG_LEVEL_DEBUG = 4
15 | LOG_LEVEL_TRACE = 5
16 |
17 | SHENV_NAME_WHITELIST_REGEX = re.compile('\W')
18 |
19 | log_level = None
20 |
21 | environ_backup = dict(os.environ)
22 | terminated_child_processes = {}
23 |
24 | IMPORT_STARTUP_FILENAME="startup.sh"
25 | IMPORT_PROCESS_FILENAME="process.sh"
26 | IMPORT_FINISH_FILENAME="finish.sh"
27 |
28 | IMPORT_ENVIRONMENT_DIR="/container/environment"
29 | IMPORT_FIRST_STARTUP_ENVIRONMENT_DIR="/container/environment/startup"
30 |
31 | ENV_FILES_YAML_EXTENSIONS = ('.yaml', '.startup.yaml')
32 | ENV_FILES_JSON_EXTENSIONS = ('.json', '.startup.json')
33 | ENV_FILES_STARTUP_EXTENSIONS = ('.startup.yaml', '.startup.json')
34 |
35 | IMPORT_SERVICE_DIR="/container/service"
36 |
37 | RUN_DIR="/container/run"
38 | RUN_STATE_DIR = RUN_DIR + "/state"
39 | RUN_ENVIRONMENT_DIR = RUN_DIR + "/environment"
40 | RUN_ENVIRONMENT_FILE_EXPORT = RUN_DIR + "/environment.sh"
41 | RUN_STARTUP_DIR = RUN_DIR + "/startup"
42 | RUN_STARTUP_FINAL_FILE = RUN_DIR + "/startup.sh"
43 | RUN_PROCESS_DIR = RUN_DIR + "/process"
44 | RUN_SERVICE_DIR = RUN_DIR + "/service"
45 |
46 | ENVIRONMENT_LOG_LEVEL_KEY = 'CONTAINER_LOG_LEVEL'
47 | ENVIRONMENT_SERVICE_DIR_KEY = 'CONTAINER_SERVICE_DIR'
48 | ENVIRONMENT_STATE_DIR_KEY = 'CONTAINER_STATE_DIR'
49 |
50 | class AlarmException(Exception):
51 | pass
52 |
53 | def write_log(level, message):
54 | now = datetime.now()
55 | for line in message.splitlines():
56 | sys.stderr.write("*** %s | %s | %s\n" % (level, now.strftime("%Y-%m-%d %H:%M:%S"), line))
57 |
58 | def error(message):
59 | if log_level >= LOG_LEVEL_ERROR:
60 | write_log(" ERROR ", message)
61 |
62 | def warning(message):
63 | if log_level >= LOG_LEVEL_WARNING:
64 | write_log("WARNING", message)
65 |
66 | def info(message):
67 | if log_level >= LOG_LEVEL_INFO:
68 | write_log(" INFO ", message)
69 |
70 | def debug(message):
71 | if log_level >= LOG_LEVEL_DEBUG:
72 | write_log(" DEBUG ", message)
73 |
74 | def trace(message):
75 | if log_level >= LOG_LEVEL_TRACE:
76 | write_log(" TRACE ", message)
77 |
78 | def debug_env_dump():
79 | debug("------------ Environment dump ------------")
80 | for name, value in list(os.environ.items()):
81 | debug(name + " = " + value)
82 | debug("------------------------------------------")
83 |
84 | def ignore_signals_and_raise_keyboard_interrupt(signame):
85 | signal.signal(signal.SIGTERM, signal.SIG_IGN)
86 | signal.signal(signal.SIGINT, signal.SIG_IGN)
87 | raise KeyboardInterrupt(signame)
88 |
89 | def raise_alarm_exception():
90 | raise AlarmException('Alarm')
91 |
92 | def listdir(path):
93 | try:
94 | result = os.stat(path)
95 | except OSError:
96 | return []
97 | if stat.S_ISDIR(result.st_mode):
98 | return sorted(os.listdir(path))
99 | else:
100 | return []
101 |
102 | def is_exe(path):
103 | try:
104 | return os.path.isfile(path) and os.access(path, os.X_OK)
105 | except OSError:
106 | return False
107 |
108 | def xstr(s):
109 | if s is None:
110 | return ''
111 | return str(s)
112 |
113 | def set_env_hostname_to_etc_hosts():
114 | try:
115 | if "HOSTNAME" in os.environ:
116 | socket_hostname = socket.gethostname()
117 |
118 | if os.environ["HOSTNAME"] != socket_hostname:
119 | ip_address = socket.gethostbyname(socket_hostname)
120 | with open("/etc/hosts", "a") as myfile:
121 | myfile.write(ip_address+" "+os.environ["HOSTNAME"]+"\n")
122 | except:
123 | warning("set_env_hostname_to_etc_hosts: failed at some point...")
124 |
125 | def python_dict_to_bash_envvar(name, python_dict):
126 |
127 | for value in python_dict:
128 | python_to_bash_envvar(name+"_KEY", value)
129 | python_to_bash_envvar(name+"_VALUE", python_dict.get(value))
130 |
131 | values = "#COMPLEX_BASH_ENV:ROW: "+name+"_KEY "+name+"_VALUE"
132 | os.environ[name] = xstr(values)
133 | trace("python2bash : set : " + name + " = "+ os.environ[name])
134 |
135 | def python_list_to_bash_envvar(name, python_list):
136 |
137 | values="#COMPLEX_BASH_ENV:TABLE:"
138 |
139 | i=1
140 | for value in python_list:
141 | child_name = name + "_ROW_" + str(i)
142 | values += " " + child_name
143 | python_to_bash_envvar(child_name, value)
144 | i = i +1
145 |
146 | os.environ[name] = xstr(values)
147 | trace("python2bash : set : " + name + " = "+ os.environ[name])
148 |
149 | def python_to_bash_envvar(name, value):
150 |
151 | try:
152 | value = ast.literal_eval(value)
153 | except:
154 | pass
155 |
156 | if isinstance(value, list):
157 | python_list_to_bash_envvar(name,value)
158 |
159 | elif isinstance(value, dict):
160 | python_dict_to_bash_envvar(name,value)
161 |
162 | else:
163 | os.environ[name] = xstr(value)
164 | trace("python2bash : set : " + name + " = "+ os.environ[name])
165 |
166 | def decode_python_envvars():
167 | _environ = dict(os.environ)
168 | for name, value in list(_environ.items()):
169 | if value.startswith("#PYTHON2BASH:") :
170 | value = value.replace("#PYTHON2BASH:","",1)
171 | python_to_bash_envvar(name, value)
172 |
173 | def decode_json_envvars():
174 | _environ = dict(os.environ)
175 | for name, value in list(_environ.items()):
176 | if value.startswith("#JSON2BASH:") :
177 | value = value.replace("#JSON2BASH:","",1)
178 | try:
179 | value = json.loads(value)
180 | python_to_bash_envvar(name,value)
181 | except:
182 | os.environ[name] = xstr(value)
183 | warning("failed to parse : " + xstr(value))
184 | trace("set : " + name + " = "+ os.environ[name])
185 |
186 | def decode_envvars():
187 | decode_json_envvars()
188 | decode_python_envvars()
189 |
190 | def generic_import_envvars(path, override_existing_environment):
191 | if not os.path.exists(path):
192 | trace("generic_import_envvars "+ path+ " don't exists")
193 | return
194 | new_env = {}
195 | for envfile in listdir(path):
196 | filePath = path + os.sep + envfile
197 | if os.path.isfile(filePath) and "." not in envfile:
198 | name = os.path.basename(envfile)
199 | with open(filePath, "r") as f:
200 | # Text files often end with a trailing newline, which we
201 | # don't want to include in the env variable value. See
202 | # https://github.com/phusion/baseimage-docker/pull/49
203 | value = re.sub('\n\Z', '', f.read())
204 | new_env[name] = value
205 | trace("import " + name + " from " + filePath)
206 |
207 | for name, value in list(new_env.items()):
208 | if override_existing_environment or name not in os.environ:
209 | os.environ[name] = value
210 | trace("set : " + name + " = "+ os.environ[name])
211 | else:
212 | debug("ignore : " + name + " = " + xstr(value) + " (keep " + name + " = " + os.environ[name] + " )")
213 |
214 | def import_run_envvars():
215 | clear_environ()
216 | generic_import_envvars(RUN_ENVIRONMENT_DIR, True)
217 |
218 | def import_envvars():
219 | generic_import_envvars(IMPORT_ENVIRONMENT_DIR, False)
220 | generic_import_envvars(IMPORT_FIRST_STARTUP_ENVIRONMENT_DIR, False)
221 |
222 | def export_run_envvars(to_dir = True):
223 | if to_dir and not os.path.exists(RUN_ENVIRONMENT_DIR):
224 | warning("export_run_envvars: "+RUN_ENVIRONMENT_DIR+" don't exists")
225 | return
226 | shell_dump = ""
227 | for name, value in list(os.environ.items()):
228 | if name in ['USER', 'GROUP', 'UID', 'GID', 'SHELL']:
229 | continue
230 | if to_dir:
231 | with open(RUN_ENVIRONMENT_DIR + os.sep + name, "w") as f:
232 | f.write(value)
233 | trace("export " + name + " to " + RUN_ENVIRONMENT_DIR + os.sep + name)
234 | shell_dump += "export " + sanitize_shenvname(name) + "=" + shquote(value) + "\n"
235 |
236 | with open(RUN_ENVIRONMENT_FILE_EXPORT, "w") as f:
237 | f.write(shell_dump)
238 | trace("export "+RUN_ENVIRONMENT_FILE_EXPORT)
239 |
240 | def create_run_envvars():
241 | set_dir_env()
242 | set_log_level_env()
243 | import_envvars()
244 | import_env_files()
245 | decode_envvars()
246 | export_run_envvars()
247 |
248 | def clear_run_envvars():
249 | try:
250 | shutil.rmtree(RUN_ENVIRONMENT_DIR)
251 | os.makedirs(RUN_ENVIRONMENT_DIR)
252 | os.chmod(RUN_ENVIRONMENT_DIR, 700)
253 | except:
254 | warning("clear_run_envvars: failed at some point...")
255 |
256 | def print_env_files_order(file_extensions):
257 |
258 | if not os.path.exists(IMPORT_ENVIRONMENT_DIR):
259 | warning("print_env_files_order "+IMPORT_ENVIRONMENT_DIR+" don't exists")
260 | return
261 |
262 | to_print = 'Caution: previously defined variables will not be overriden.\n'
263 |
264 | file_found = False
265 | for subdir, _, files in sorted(os.walk(IMPORT_ENVIRONMENT_DIR)):
266 | for file in files:
267 | filepath = subdir + os.sep + file
268 | if filepath.endswith(file_extensions):
269 | file_found = True
270 | filepath = subdir + os.sep + file
271 | to_print += filepath + '\n'
272 |
273 | if file_found:
274 | if log_level < LOG_LEVEL_DEBUG:
275 | to_print+='\nTo see how this files are processed and environment variables values,\n'
276 | to_print+='run this container with \'--loglevel debug\''
277 |
278 | info('Environment files will be proccessed in this order : \n' + to_print)
279 |
280 | def import_env_files():
281 |
282 | if not os.path.exists(IMPORT_ENVIRONMENT_DIR):
283 | warning("import_env_files: "+IMPORT_ENVIRONMENT_DIR+" don't exists")
284 | return
285 |
286 | file_extensions = ENV_FILES_YAML_EXTENSIONS + ENV_FILES_JSON_EXTENSIONS
287 | print_env_files_order(file_extensions)
288 |
289 | for subdir, _, files in sorted(os.walk(IMPORT_ENVIRONMENT_DIR)):
290 | for file in files:
291 | if file.endswith(file_extensions):
292 | filepath = subdir + os.sep + file
293 |
294 | try:
295 | with open(filepath, "r") as f:
296 |
297 | debug("process environment file : " + filepath)
298 |
299 | if file.endswith(ENV_FILES_YAML_EXTENSIONS):
300 | env_vars = yaml.load(f)
301 |
302 | elif file.endswith(ENV_FILES_JSON_EXTENSIONS):
303 | env_vars = json.load(f)
304 |
305 | for name, value in list(env_vars.items()):
306 | if not name in os.environ:
307 | if isinstance(value, list) or isinstance(value, dict):
308 | os.environ[name] = '#PYTHON2BASH:' + xstr(value)
309 | else:
310 | os.environ[name] = xstr(value)
311 | trace("set : " + name + " = "+ os.environ[name])
312 | else:
313 | debug("ignore : " + name + " = " + xstr(value) + " (keep " + name + " = " + os.environ[name] + " )")
314 | except:
315 | warning('failed to parse: ' + filepath)
316 |
317 | def remove_startup_env_files():
318 |
319 | if os.path.isdir(IMPORT_FIRST_STARTUP_ENVIRONMENT_DIR):
320 | try:
321 | shutil.rmtree(IMPORT_FIRST_STARTUP_ENVIRONMENT_DIR)
322 | except:
323 | warning("remove_startup_env_files: failed to remove "+IMPORT_FIRST_STARTUP_ENVIRONMENT_DIR)
324 |
325 | if not os.path.exists(IMPORT_ENVIRONMENT_DIR):
326 | warning("remove_startup_env_files: "+IMPORT_ENVIRONMENT_DIR+" don't exists")
327 | return
328 |
329 | for subdir, _, files in sorted(os.walk(IMPORT_ENVIRONMENT_DIR)):
330 | for file in files:
331 | filepath = subdir + os.sep + file
332 | if filepath.endswith(ENV_FILES_STARTUP_EXTENSIONS):
333 | try:
334 | os.remove(filepath)
335 | info("Remove file "+filepath)
336 | except:
337 | warning("remove_startup_env_files: failed to remove "+filepath)
338 |
339 | def restore_environ():
340 | clear_environ()
341 | trace("Restore initial environment")
342 | os.environ.update(environ_backup)
343 |
344 | def clear_environ():
345 | trace("Clear existing environment")
346 | os.environ.clear()
347 |
348 | def set_startup_scripts_env():
349 | debug("Set environment for startup files")
350 | clear_run_envvars() # clear previous environment
351 | create_run_envvars() # create run envvars with all env files
352 |
353 | def set_process_env(keep_startup_env = False):
354 | debug("Set environment for container process")
355 | if not keep_startup_env:
356 | remove_startup_env_files()
357 | clear_run_envvars()
358 |
359 | restore_environ()
360 | create_run_envvars() # recreate env var without startup env files
361 |
362 | def setup_run_directories(args):
363 |
364 | directories = (RUN_PROCESS_DIR, RUN_STARTUP_DIR, RUN_STATE_DIR, RUN_ENVIRONMENT_DIR)
365 | for directory in directories:
366 | if not os.path.exists(directory):
367 | os.makedirs(directory)
368 |
369 | if directory == RUN_ENVIRONMENT_DIR:
370 | os.chmod(directory, 700)
371 |
372 | if not os.path.exists(RUN_ENVIRONMENT_FILE_EXPORT):
373 | open(RUN_ENVIRONMENT_FILE_EXPORT, 'a').close()
374 | os.chmod(RUN_ENVIRONMENT_FILE_EXPORT, 640)
375 | uid = pwd.getpwnam("root").pw_uid
376 | gid = grp.getgrnam("docker_env").gr_gid
377 | os.chown(RUN_ENVIRONMENT_FILE_EXPORT, uid, gid)
378 |
379 | if state_is_first_start():
380 |
381 | if args.copy_service:
382 | copy_service_to_run_dir()
383 |
384 | set_dir_env()
385 |
386 | base_path = os.environ[ENVIRONMENT_SERVICE_DIR_KEY]
387 | nb_service = len(listdir(base_path))
388 |
389 | if nb_service > 0 :
390 | info("Search service in " + ENVIRONMENT_SERVICE_DIR_KEY + " = "+base_path+" :")
391 | for d in listdir(base_path):
392 | d_path = base_path + os.sep + d
393 | if os.path.isdir(d_path):
394 | if is_exe(d_path + os.sep + IMPORT_STARTUP_FILENAME):
395 | info('link ' + d_path + os.sep + IMPORT_STARTUP_FILENAME + ' to ' + RUN_STARTUP_DIR + os.sep + d)
396 | try:
397 | os.symlink(d_path + os.sep + IMPORT_STARTUP_FILENAME, RUN_STARTUP_DIR + os.sep + d)
398 | except OSError as detail:
399 | warning('failed to link ' + d_path + os.sep + IMPORT_STARTUP_FILENAME + ' to ' + RUN_STARTUP_DIR + os.sep + d + ': ' + xstr(detail))
400 |
401 | if is_exe(d_path + os.sep + IMPORT_PROCESS_FILENAME):
402 | info('link ' + d_path + os.sep + IMPORT_PROCESS_FILENAME + ' to ' + RUN_PROCESS_DIR + os.sep + d + os.sep + 'run')
403 |
404 | if not os.path.exists(RUN_PROCESS_DIR + os.sep + d):
405 | os.makedirs(RUN_PROCESS_DIR + os.sep + d)
406 | else:
407 | warning('directory ' + RUN_PROCESS_DIR + os.sep + d + ' already exists')
408 |
409 | try:
410 | os.symlink(d_path + os.sep + IMPORT_PROCESS_FILENAME, RUN_PROCESS_DIR + os.sep + d + os.sep + 'run')
411 | except OSError as detail:
412 | warning('failed to link ' + d_path + os.sep + IMPORT_PROCESS_FILENAME + ' to ' + RUN_PROCESS_DIR + os.sep + d + os.sep + 'run : ' + xstr(detail))
413 |
414 | if not args.skip_finish_files and is_exe(d_path + os.sep + IMPORT_FINISH_FILENAME):
415 | info('link ' + d_path + os.sep + IMPORT_FINISH_FILENAME + ' to ' + RUN_PROCESS_DIR + os.sep + d + os.sep + 'finish')
416 |
417 | if not os.path.exists(RUN_PROCESS_DIR + os.sep + d):
418 | os.makedirs(RUN_PROCESS_DIR + os.sep + d)
419 |
420 | try:
421 | os.symlink(d_path + os.sep + IMPORT_FINISH_FILENAME, RUN_PROCESS_DIR + os.sep + d + os.sep + 'finish')
422 | except OSError as detail:
423 | warning('failed to link ' + d_path + os.sep + IMPORT_FINISH_FILENAME + ' to ' + RUN_PROCESS_DIR + os.sep + d + os.sep + 'finish : ' + xstr(detail))
424 |
425 | def set_dir_env():
426 | if state_is_service_copied_to_run_dir():
427 | os.environ[ENVIRONMENT_SERVICE_DIR_KEY] = RUN_SERVICE_DIR
428 | else:
429 | os.environ[ENVIRONMENT_SERVICE_DIR_KEY] = IMPORT_SERVICE_DIR
430 | trace("set : " + ENVIRONMENT_SERVICE_DIR_KEY + " = " + os.environ[ENVIRONMENT_SERVICE_DIR_KEY])
431 |
432 | os.environ[ENVIRONMENT_STATE_DIR_KEY] = RUN_STATE_DIR
433 | trace("set : " + ENVIRONMENT_STATE_DIR_KEY + " = " + os.environ[ENVIRONMENT_STATE_DIR_KEY])
434 |
435 | def set_log_level_env():
436 | os.environ[ENVIRONMENT_LOG_LEVEL_KEY] = xstr(log_level)
437 | trace("set : "+ENVIRONMENT_LOG_LEVEL_KEY+" = " + os.environ[ENVIRONMENT_LOG_LEVEL_KEY])
438 |
439 | def copy_service_to_run_dir():
440 |
441 | if os.path.exists(RUN_SERVICE_DIR):
442 | warning("Copy "+IMPORT_SERVICE_DIR+" to "+RUN_SERVICE_DIR + " ignored")
443 | warning(RUN_SERVICE_DIR + " already exists")
444 | return
445 |
446 | info("Copy "+IMPORT_SERVICE_DIR+" to "+RUN_SERVICE_DIR)
447 |
448 | try:
449 | shutil.copytree(IMPORT_SERVICE_DIR, RUN_SERVICE_DIR)
450 | except shutil.Error as e:
451 | warning(e)
452 |
453 | state_set_service_copied_to_run_dir()
454 |
455 | def state_set_service_copied_to_run_dir():
456 | open(RUN_STATE_DIR+"/service-copied-to-run-dir", 'a').close()
457 |
458 | def state_is_service_copied_to_run_dir():
459 | return os.path.exists(RUN_STATE_DIR+'/service-copied-to-run-dir')
460 |
461 | def state_set_first_startup_done():
462 | open(RUN_STATE_DIR+"/first-startup-done", 'a').close()
463 |
464 | def state_is_first_start():
465 | return os.path.exists(RUN_STATE_DIR+'/first-startup-done') == False
466 |
467 | def state_set_startup_done():
468 | open(RUN_STATE_DIR+"/startup-done", 'a').close()
469 |
470 | def state_reset_startup_done():
471 | try:
472 | os.remove(RUN_STATE_DIR+"/startup-done")
473 | except OSError:
474 | pass
475 |
476 | def is_multiple_process_container():
477 | return len(listdir(RUN_PROCESS_DIR)) > 1
478 |
479 | def is_single_process_container():
480 | return len(listdir(RUN_PROCESS_DIR)) == 1
481 |
482 | def get_container_process():
483 | for p in listdir(RUN_PROCESS_DIR):
484 | return RUN_PROCESS_DIR + os.sep + p + os.sep + 'run'
485 |
486 | def is_runit_installed():
487 | return os.path.exists('/usr/bin/sv')
488 |
489 | _find_unsafe = re.compile(r'[^\w@%+=:,./-]').search
490 |
491 | def shquote(s):
492 | """Return a shell-escaped version of the string *s*."""
493 | if not s:
494 | return "''"
495 | if _find_unsafe(s) is None:
496 | return s
497 |
498 | # use single quotes, and put single quotes into double quotes
499 | # the string $'b is then quoted as '$'"'"'b'
500 | return "'" + s.replace("'", "'\"'\"'") + "'"
501 |
502 | def sanitize_shenvname(s):
503 | return re.sub(SHENV_NAME_WHITELIST_REGEX, "_", s)
504 |
505 | # Waits for the child process with the given PID, while at the same time
506 | # reaping any other child processes that have exited (e.g. adopted child
507 | # processes that have terminated).
508 | def waitpid_reap_other_children(pid):
509 | global terminated_child_processes
510 |
511 | status = terminated_child_processes.get(pid)
512 | if status:
513 | # A previous call to waitpid_reap_other_children(),
514 | # with an argument not equal to the current argument,
515 | # already waited for this process. Return the status
516 | # that was obtained back then.
517 | del terminated_child_processes[pid]
518 | return status
519 |
520 | done = False
521 | status = None
522 | while not done:
523 | try:
524 | # https://github.com/phusion/baseimage-docker/issues/151#issuecomment-92660569
525 | this_pid, status = os.waitpid(pid, os.WNOHANG)
526 | if this_pid == 0:
527 | this_pid, status = os.waitpid(-1, 0)
528 | if this_pid == pid:
529 | done = True
530 | else:
531 | # Save status for later.
532 | terminated_child_processes[this_pid] = status
533 | except OSError as e:
534 | if e.errno == errno.ECHILD or e.errno == errno.ESRCH:
535 | return None
536 | else:
537 | raise
538 | return status
539 |
540 | def stop_child_process(name, pid, signo = signal.SIGTERM, time_limit = KILL_PROCESS_TIMEOUT):
541 | info("Shutting down %s (PID %d)..." % (name, pid))
542 | try:
543 | os.kill(pid, signo)
544 | except OSError:
545 | pass
546 | signal.alarm(time_limit)
547 | try:
548 | try:
549 | waitpid_reap_other_children(pid)
550 | except OSError:
551 | pass
552 | except AlarmException:
553 | warning("%s (PID %d) did not shut down in time. Forcing it to exit." % (name, pid))
554 | try:
555 | os.kill(pid, signal.SIGKILL)
556 | except OSError:
557 | pass
558 | try:
559 | waitpid_reap_other_children(pid)
560 | except OSError:
561 | pass
562 | finally:
563 | signal.alarm(0)
564 |
565 | def run_command_killable(command):
566 | status = None
567 | debug_env_dump()
568 | pid = os.spawnvp(os.P_NOWAIT, command[0], command)
569 | try:
570 | status = waitpid_reap_other_children(pid)
571 | except BaseException:
572 | warning("An error occurred. Aborting.")
573 | stop_child_process(command[0], pid)
574 | raise
575 | if status != 0:
576 | if status is None:
577 | error("%s exited with unknown status\n" % command[0])
578 | else:
579 | error("%s failed with status %d\n" % (command[0], os.WEXITSTATUS(status)))
580 | sys.exit(1)
581 |
582 | def run_command_killable_and_import_run_envvars(command):
583 | run_command_killable(command)
584 | import_run_envvars()
585 | export_run_envvars(False)
586 |
587 | def kill_all_processes(time_limit):
588 | info("Killing all processes...")
589 | try:
590 | os.kill(-1, signal.SIGTERM)
591 | except OSError:
592 | pass
593 | signal.alarm(time_limit)
594 | try:
595 | # Wait until no more child processes exist.
596 | done = False
597 | while not done:
598 | try:
599 | os.waitpid(-1, 0)
600 | except OSError as e:
601 | if e.errno == errno.ECHILD:
602 | done = True
603 | else:
604 | raise
605 | except AlarmException:
606 | warning("Not all processes have exited in time. Forcing them to exit.")
607 | try:
608 | os.kill(-1, signal.SIGKILL)
609 | except OSError:
610 | pass
611 | finally:
612 | signal.alarm(0)
613 |
614 | def container_had_startup_script():
615 | return (len(listdir(RUN_STARTUP_DIR)) > 0 or is_exe(RUN_STARTUP_FINAL_FILE))
616 |
617 | def run_startup_files(args):
618 |
619 | # Run /container/run/startup/*
620 | for name in listdir(RUN_STARTUP_DIR):
621 | filename = RUN_STARTUP_DIR + os.sep + name
622 | if is_exe(filename):
623 | info("Running %s..." % filename)
624 | run_command_killable_and_import_run_envvars([filename])
625 |
626 | # Run /container/run/startup.sh.
627 | if is_exe(RUN_STARTUP_FINAL_FILE):
628 | info("Running "+RUN_STARTUP_FINAL_FILE+"...")
629 | run_command_killable_and_import_run_envvars([RUN_STARTUP_FINAL_FILE])
630 |
631 | def wait_for_process_or_interrupt(pid):
632 | status = waitpid_reap_other_children(pid)
633 | return (True, status)
634 |
635 | def run_process(args, background_process_name, background_process_command):
636 | background_process_pid = run_background_process(background_process_name,background_process_command)
637 | background_process_exited = False
638 | exit_status = None
639 |
640 | if len(args.main_command) == 0:
641 | background_process_exited, exit_status = wait_background_process(background_process_name, background_process_pid)
642 | else:
643 | exit_status = run_foreground_process(args.main_command)
644 |
645 | return background_process_pid, background_process_exited, exit_status
646 |
647 | def run_background_process(name, command):
648 | info("Running "+ name +"...")
649 | pid = os.spawnvp(os.P_NOWAIT, command[0], command)
650 | debug("%s started as PID %d" % (name, pid))
651 | return pid
652 |
653 | def wait_background_process(name, pid):
654 | exit_code = None
655 | exit_status = None
656 | process_exited = False
657 |
658 | process_exited, exit_code = wait_for_process_or_interrupt(pid)
659 | if process_exited:
660 | if exit_code is None:
661 | info(name + " exited with unknown status")
662 | exit_status = 1
663 | else:
664 | exit_status = os.WEXITSTATUS(exit_code)
665 | info("%s exited with status %d" % (name, exit_status))
666 | return (process_exited, exit_status)
667 |
668 | def run_foreground_process(command):
669 | exit_code = None
670 | exit_status = None
671 |
672 | info("Running %s..." % " ".join(command))
673 | pid = os.spawnvp(os.P_NOWAIT, command[0], command)
674 | try:
675 | exit_code = waitpid_reap_other_children(pid)
676 | if exit_code is None:
677 | info("%s exited with unknown status." % command[0])
678 | exit_status = 1
679 | else:
680 | exit_status = os.WEXITSTATUS(exit_code)
681 | info("%s exited with status %d." % (command[0], exit_status))
682 | except KeyboardInterrupt:
683 | stop_child_process(command[0], pid)
684 | raise
685 | except BaseException:
686 | error("An error occurred. Aborting.")
687 | stop_child_process(command[0], pid)
688 | raise
689 |
690 | return exit_status
691 |
692 | def shutdown_runit_services():
693 | debug("Begin shutting down runit services...")
694 | os.system("/usr/bin/sv -w %d force-stop %s/* > /dev/null" % (KILL_PROCESS_TIMEOUT, RUN_PROCESS_DIR))
695 |
696 | def wait_for_runit_services():
697 | debug("Waiting for runit services to exit...")
698 | done = False
699 | while not done:
700 | done = os.system("/usr/bin/sv status "+RUN_PROCESS_DIR+"/* | grep -q '^run:'") != 0
701 | if not done:
702 | time.sleep(0.1)
703 | shutdown_runit_services()
704 |
705 | def run_multiple_process_container(args):
706 | if not is_runit_installed():
707 | error("Error: runit is not installed and this is a multiple process container.")
708 | return
709 |
710 | background_process_exited=False
711 | background_process_pid=None
712 |
713 | try:
714 | runit_command=["/usr/bin/runsvdir", "-P", RUN_PROCESS_DIR]
715 | background_process_pid, background_process_exited, exit_status = run_process(args, "runit daemon", runit_command)
716 |
717 | sys.exit(exit_status)
718 | finally:
719 | shutdown_runit_services()
720 | if not background_process_exited:
721 | stop_child_process("runit daemon", background_process_pid)
722 | wait_for_runit_services()
723 |
724 | def run_single_process_container(args):
725 | background_process_exited=False
726 | background_process_pid=None
727 |
728 | try:
729 | container_process=get_container_process()
730 | background_process_pid, background_process_exited, exit_status = run_process(args, container_process, [container_process])
731 |
732 | sys.exit(exit_status)
733 | finally:
734 | if not background_process_exited:
735 | stop_child_process(container_process, background_process_pid)
736 |
737 | def run_no_process_container(args):
738 | if len(args.main_command) == 0:
739 | args.main_command=['bash'] # run bash by default
740 |
741 | exit_status = run_foreground_process(args.main_command)
742 | sys.exit(exit_status)
743 |
744 | def run_finish_files():
745 |
746 | # iterate process dir to find finish files
747 | for name in listdir(RUN_PROCESS_DIR):
748 | filename = RUN_PROCESS_DIR + os.sep + name + os.sep + "finish"
749 | if is_exe(filename):
750 | info("Running %s..." % filename)
751 | run_command_killable_and_import_run_envvars([filename])
752 |
753 | def wait_states(states):
754 | for state in states:
755 | filename = RUN_STATE_DIR + os.sep + state
756 | info("Wait state: " + state)
757 |
758 | while not os.path.exists(filename):
759 | time.sleep(0.1)
760 | debug("Check file " + filename)
761 | pass
762 | debug("Check file " + filename + " [Ok]")
763 |
764 | def run_cmds(args, when):
765 | debug("Run commands before " + when + "...")
766 | if len(args.cmds) > 0:
767 |
768 | for cmd in args.cmds:
769 | if (len(cmd) > 1 and cmd[1] == when) or (len(cmd) == 1 and when == "startup"):
770 | info("Running '"+cmd[0]+"'...")
771 | run_command_killable_and_import_run_envvars(cmd[0].split())
772 |
773 | def main(args):
774 |
775 | info(ENVIRONMENT_LOG_LEVEL_KEY + " = " + xstr(log_level) + " (" + log_level_switcher_inv.get(log_level) + ")")
776 | state_reset_startup_done()
777 |
778 | if args.set_env_hostname_to_etc_hosts:
779 | set_env_hostname_to_etc_hosts()
780 |
781 | wait_states(args.wait_states)
782 | setup_run_directories(args)
783 |
784 | if not args.skip_env_files:
785 | set_startup_scripts_env()
786 |
787 | run_cmds(args,"startup")
788 |
789 | if not args.skip_startup_files and container_had_startup_script():
790 | run_startup_files(args)
791 |
792 | state_set_startup_done()
793 | state_set_first_startup_done()
794 |
795 | if not args.skip_env_files:
796 | set_process_env(args.keep_startup_env)
797 |
798 | run_cmds(args,"process")
799 |
800 | debug_env_dump()
801 |
802 | if is_single_process_container() and not args.skip_process_files:
803 | run_single_process_container(args)
804 |
805 | elif is_multiple_process_container() and not args.skip_process_files:
806 | run_multiple_process_container(args)
807 |
808 | else:
809 | run_no_process_container(args)
810 |
811 | # Parse options.
812 | parser = argparse.ArgumentParser(description = 'Initialize the system.', epilog='Osixia! Light Baseimage: https://github.com/osixia/docker-light-baseimage')
813 | parser.add_argument('main_command', metavar = 'MAIN_COMMAND', type = str, nargs = '*',
814 | help = 'The main command to run, leave empty to only run container process.')
815 | parser.add_argument('-e', '--skip-env-files', dest = 'skip_env_files',
816 | action = 'store_const', const = True, default = False,
817 | help = 'Skip getting environment values from environment file(s).')
818 | parser.add_argument('-s', '--skip-startup-files', dest = 'skip_startup_files',
819 | action = 'store_const', const = True, default = False,
820 | help = 'Skip running '+RUN_STARTUP_DIR+'/* and '+RUN_STARTUP_FINAL_FILE + ' file(s).')
821 | parser.add_argument('-p', '--skip-process-files', dest = 'skip_process_files',
822 | action = 'store_const', const = True, default = False,
823 | help = 'Skip running container process file(s).')
824 | parser.add_argument('-f', '--skip-finish-files', dest = 'skip_finish_files',
825 | action = 'store_const', const = True, default = False,
826 | help = 'Skip running container finish file(s).')
827 | parser.add_argument('-o', '--run-only', type=str, choices=["startup","process","finish"], dest = 'run_only', default = None,
828 | help = 'Run only this file type and ignore others.')
829 | parser.add_argument('-c', '--cmd', metavar=('COMMAND', 'WHEN={startup,process,finish}'), dest = 'cmds', type = str,
830 | action = 'append', default = [], nargs = "+",
831 | help = 'Run COMMAND before WHEN file(s). Default before startup file(s).')
832 | parser.add_argument('-k', '--no-kill-all-on-exit', dest = 'kill_all_on_exit',
833 | action = 'store_const', const = False, default = True,
834 | help = 'Don\'t kill all processes on the system upon exiting.')
835 | parser.add_argument('--wait-state', metavar = 'FILENAME', dest = 'wait_states', type = str,
836 | action = 'append', default=[],
837 | help = 'Wait until the container FILENAME file exists in '+RUN_STATE_DIR+' directory before starting. Usefull when 2 containers share '+RUN_DIR+' directory via volume.')
838 | parser.add_argument('--wait-first-startup', dest = 'wait_first_startup',
839 | action = 'store_const', const = True, default = False,
840 | help = 'Wait until the first startup is done before starting. Usefull when 2 containers share '+RUN_DIR+' directory via volume.')
841 | parser.add_argument('--keep-startup-env', dest = 'keep_startup_env',
842 | action = 'store_const', const = True, default = False,
843 | help = 'Don\'t remove ' + xstr(ENV_FILES_STARTUP_EXTENSIONS) + ' environment files after startup scripts.')
844 | parser.add_argument('--copy-service', dest = 'copy_service',
845 | action = 'store_const', const = True, default = False,
846 | help = 'Copy '+IMPORT_SERVICE_DIR+' to '+RUN_SERVICE_DIR+'. Help to fix docker mounted files problems.')
847 | parser.add_argument('--dont-touch-etc-hosts', dest = 'set_env_hostname_to_etc_hosts',
848 | action = 'store_const', const = False, default = True,
849 | help = 'Don\'t add in /etc/hosts a line with the container ip and $HOSTNAME environment variable value.')
850 | parser.add_argument('--keepalive', dest = 'keepalive',
851 | action = 'store_const', const = True, default = False,
852 | help = 'Keep alive container if all startup files and process exited without error.')
853 | parser.add_argument('--keepalive-force', dest = 'keepalive_force',
854 | action = 'store_const', const = True, default = False,
855 | help = 'Keep alive container in all circonstancies.')
856 | parser.add_argument('-l', '--loglevel', type=str, choices=["none","error","warning","info","debug","trace"], dest = 'log_level', default = "info",
857 | help = 'Log level (default: info)')
858 |
859 | args = parser.parse_args()
860 |
861 | log_level_switcher = {"none": LOG_LEVEL_NONE,"error": LOG_LEVEL_ERROR,"warning": LOG_LEVEL_WARNING,"info": LOG_LEVEL_INFO,"debug": LOG_LEVEL_DEBUG, "trace": LOG_LEVEL_TRACE}
862 | log_level_switcher_inv = {LOG_LEVEL_NONE: "none",LOG_LEVEL_ERROR:"error",LOG_LEVEL_WARNING:"warning",LOG_LEVEL_INFO:"info",LOG_LEVEL_DEBUG:"debug",LOG_LEVEL_TRACE:"trace"}
863 | log_level = log_level_switcher.get(args.log_level)
864 |
865 | # Run only arg
866 | if args.run_only != None:
867 | if args.run_only == "startup" and args.skip_startup_files:
868 | error("Error: When '--run-only startup' is set '--skip-startup-files' can't be set.")
869 | sys.exit(1)
870 | elif args.run_only == "process" and args.skip_startup_files:
871 | error("Error: When '--run-only process' is set '--skip-process-files' can't be set.")
872 | sys.exit(1)
873 | elif args.run_only == "finish" and args.skip_startup_files:
874 | error("Error: When '--run-only finish' is set '--skip-finish-files' can't be set.")
875 | sys.exit(1)
876 |
877 | if args.run_only == "startup":
878 | args.skip_process_files = True
879 | args.skip_finish_files = True
880 | elif args.run_only == "process":
881 | args.skip_startup_files = True
882 | args.skip_finish_files = True
883 | elif args.run_only == "finish":
884 | args.skip_startup_files = True
885 | args.skip_process_files = True
886 |
887 | # wait for startup args
888 | if args.wait_first_startup:
889 | args.wait_states.insert(0, 'first-startup-done')
890 |
891 | # Run main function.
892 | signal.signal(signal.SIGTERM, lambda signum, frame: ignore_signals_and_raise_keyboard_interrupt('SIGTERM'))
893 | signal.signal(signal.SIGINT, lambda signum, frame: ignore_signals_and_raise_keyboard_interrupt('SIGINT'))
894 | signal.signal(signal.SIGALRM, lambda signum, frame: raise_alarm_exception())
895 |
896 | exit_code = 0
897 |
898 | try:
899 | main(args)
900 |
901 | except SystemExit as err:
902 | exit_code = err.code
903 | if args.keepalive and err.code == 0:
904 | try:
905 | info("All process have exited without error, keep container alive...")
906 | while True:
907 | time.sleep(60)
908 | pass
909 | except:
910 | error("Keep alive process ended.")
911 |
912 | except KeyboardInterrupt:
913 | warning("Init system aborted.")
914 | exit(2)
915 |
916 | finally:
917 |
918 | run_cmds(args,"finish")
919 |
920 | # for multiple process images finish script are run by runit
921 | if not args.skip_finish_files and not is_multiple_process_container():
922 | run_finish_files()
923 |
924 | if args.keepalive_force:
925 | try:
926 | info("All process have exited, keep container alive...")
927 | while True:
928 | time.sleep(60)
929 | pass
930 | except:
931 | error("Keep alive process ended.")
932 |
933 | if args.kill_all_on_exit:
934 | kill_all_processes(KILL_ALL_PROCESSES_TIMEOUT)
935 |
936 | exit(exit_code)
937 |
--------------------------------------------------------------------------------
/image/tool/setuser:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 |
3 | '''
4 | Copyright (c) 2013-2015 Phusion Holding B.V.
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in
14 | all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | THE SOFTWARE.
23 | '''
24 |
25 | import sys
26 | import os
27 | import pwd
28 |
29 |
30 | def abort(message):
31 | sys.stderr.write("setuser: %s\n" % message)
32 | sys.exit(1)
33 |
34 |
35 | def main():
36 | '''
37 | A simple alternative to sudo that executes a command as a user by setting
38 | the user ID and user parameters to those described by the system and then
39 | using execvp(3) to execute the command without the necessity of a TTY
40 | '''
41 |
42 | username = sys.argv[1]
43 | try:
44 | user = pwd.getpwnam(username)
45 | except KeyError:
46 | abort("user %s not found" % username)
47 | os.initgroups(username, user.pw_gid)
48 | os.setgid(user.pw_gid)
49 | os.setuid(user.pw_uid)
50 | os.environ['USER'] = username
51 | os.environ['HOME'] = user.pw_dir
52 | os.environ['UID'] = str(user.pw_uid)
53 | try:
54 | os.execvp(sys.argv[2], sys.argv[2:])
55 | except OSError as e:
56 | abort("cannot execute %s: %s" % (sys.argv[2], str(e)))
57 |
58 | if __name__ == '__main__':
59 |
60 | if len(sys.argv) < 3:
61 | sys.stderr.write("Usage: /sbin/setuser USERNAME COMMAND [args..]\n")
62 | sys.exit(1)
63 |
64 | main()
65 |
--------------------------------------------------------------------------------
/image/tool/wait-process:
--------------------------------------------------------------------------------
1 | #!/bin/sh -e
2 |
3 | # wait startup to finish
4 | log-helper debug "Waits until startup is complete..."
5 | while ! test -f /container/run/state/startup-done
6 | do
7 | sleep 0.5
8 | done
9 |
10 | for process in "$@"
11 | do
12 | # wait service
13 | log-helper debug "Waits for process ${process} to be started..."
14 | while ! pgrep -c "${process}" > /dev/null
15 | do
16 | sleep 0.5
17 | done
18 | done
19 |
--------------------------------------------------------------------------------
/test/test.bats:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bats
2 | load test_helper
3 |
4 | @test "image build" {
5 |
6 | run build_image
7 | [ "$status" -eq 0 ]
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/test/test_helper.bash:
--------------------------------------------------------------------------------
1 | setup() {
2 | IMAGE_NAME="$NAME:$VERSION"
3 | }
4 |
5 | # function relative to the current container / image
6 | build_image() {
7 | #disable outputs
8 | docker build -t $IMAGE_NAME $BATS_TEST_DIRNAME/../image &> /dev/null
9 | }
10 |
11 | run_image() {
12 | CONTAINER_ID=$(docker run $@ -d $IMAGE_NAME)
13 | CONTAINER_IP=$(get_container_ip_by_cid $CONTAINER_ID)
14 | }
15 |
16 | start_container() {
17 | start_containers_by_cid $CONTAINER_ID
18 | }
19 |
20 | stop_container() {
21 | stop_containers_by_cid $CONTAINER_ID
22 | }
23 |
24 | remove_container() {
25 | remove_containers_by_cid $CONTAINER_ID
26 | }
27 |
28 | clear_container() {
29 | stop_containers_by_cid $CONTAINER_ID
30 | remove_containers_by_cid $CONTAINER_ID
31 | }
32 |
33 | wait_process() {
34 | wait_process_by_cid $CONTAINER_ID $@
35 | }
36 |
37 | # generic functions
38 | get_container_ip_by_cid() {
39 | local IP=$(docker inspect -f "{{ .NetworkSettings.IPAddress }}" $1)
40 | echo "$IP"
41 | }
42 |
43 | start_containers_by_cid() {
44 | for cid in "$@"
45 | do
46 | #disable outputs
47 | docker start $cid &> /dev/null
48 | done
49 | }
50 |
51 | stop_containers_by_cid() {
52 | for cid in "$@"
53 | do
54 | #disable outputs
55 | docker stop $cid &> /dev/null
56 | done
57 | }
58 |
59 | remove_containers_by_cid() {
60 | for cid in "$@"
61 | do
62 | #disable outputs
63 | docker rm $cid &> /dev/null
64 | done
65 | }
66 |
67 | clear_containers_by_cid() {
68 | stop_containers_by_cid $@
69 | remove_containers_by_cid $@
70 | }
71 |
72 | wait_process_by_cid() {
73 | cid=$1
74 | docker exec $cid /container/tool/wait-process ${@:2}
75 | }
76 |
--------------------------------------------------------------------------------