├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── bash.bashrc ├── bin ├── README ├── apps-update ├── chaplocal ├── clone-apps ├── cps ├── get-chaplocal ├── get-help ├── get-launcher ├── jpassword └── tpl_envcp ├── build.sh ├── build └── install.sh ├── chaperone.d ├── 005-config.conf ├── 010-start.conf ├── 020-java.conf ├── 200-keybox.conf └── 300-logrotate.conf ├── etc ├── KeyBoxConfig.properties.tpl ├── README ├── jetty-start.ini.tpl ├── keydb-dist │ └── keybox.h2.db ├── log4j.xml.tpl ├── logrotate.conf ├── skel │ ├── clone │ │ ├── Dockerfile.tpl │ │ ├── bash.bashrc.tpl │ │ ├── build.sh.tpl │ │ ├── build │ │ │ ├── README.tpl │ │ │ └── install.sh.tpl │ │ └── run.sh.tpl │ ├── help │ │ ├── HELP.tpl │ │ └── LAUNCHER.tpl │ ├── launcher │ │ └── run-IMAGE.sh.tpl │ └── shell_vars.inc ├── ssl_vars.inc ├── ssleay.cnf.tpl ├── start_keybox.sh ├── startup.sh ├── update.sh └── version.inc ├── run.sh └── startup.d ├── 150-ssl-keygen.sh ├── 180-jetty-ssl.sh └── README /.gitignore: -------------------------------------------------------------------------------- 1 | KeyBox-jetty 2 | var 3 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM chapdev/chaperone-alpinejava 2 | 3 | ADD . /setup/ 4 | 5 | # Git tag version number format should be '2.85.01'. This will trigger an automated build. 6 | # The following version format is used within the Keybox distribution filename. 7 | # Also, check to be sure etc/version.inc is correct before doing a git commit. 8 | ENV KEYBOX_VERSION=2.85_03 9 | 10 | RUN /setup/build/install.sh 11 | EXPOSE 8443 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Gary J. Wisniewski 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # KeyBox Docker Image 2 | 3 | KeyBox is an open-source web-based SSH console that centrally manages administrative access to systems. Web-based administration is combined with management and distribution of user's public SSH keys. Key management and administration is based on profiles assigned to defined users. 4 | 5 | Thanks to [Sean Kavanagh](https://github.com/skavanagh) for designing and writing this excellent application. The following documentation tells you how to use the `docker-keybox` image, a lean full-featured Docker image that has the following features: 6 | 7 | * Lean 240MB image (compared to some other KeyBox images that are 600-800MB). 8 | * Works both as a self-contained image, or will automatically recognize attached storage devices so that persistent KeyBox data is not stored in the container. Makes it easy to upgrade when new images are released. 9 | * Fully configurable using environment variables, including options for logging, and all configurable Keybox options. A fully customized container can be started without the need to build a new image. 10 | * Automatically generates a self-signed SSL certificate matched to your domain, or allows you to easily add your own legitimate SSL certificate. 11 | 12 | ## Quick Start 13 | 14 | You can get started quickly using the automated-build image hosted on Docker Hub. For example, to quickly create a running self-contained KeyBox server daemon: 15 | 16 | $ docker pull garywiz/docker-keybox 17 | $ docker run -d -p 8443:8443 garywiz/docker-keybox 18 | 19 | Within a few seconds, you should be able to use KeyBox by going to `https://localhost:8443/`. The default login user is `admin` with a password of `changeme`. 20 | 21 | If you want to store persistent KeyBox data locally outside the image, you can use the built-in launcher script. Extract the launcher script from the image like this: 22 | 23 | $ docker run -i --rm garywiz/docker-keybox --task get-launcher | sh 24 | 25 | This will create a flexible launcher script that you can customize or use as a template. You can run it as a daemon: 26 | 27 | $ ./run-docker-keybox.sh -d 28 | 29 | Or, if you want to have local persistent storage: 30 | 31 | $ mkdir docker-keybox-storage 32 | $ ./run-docker-keybox.sh -d 33 | 34 | Now, all persistent data will be stored in the `docker-keybox-storage` directory. The container itself is therefore entirely disposable. 35 | 36 | The `run-docker-keybox.sh` script is designed to be self-documenting and you can edit it to change start-up options and storage options. You can get up-to-date help on the image's features like this: 37 | 38 | $ docker run -i --rm garywiz/docker-keybox --task get-help 39 | 40 | ## Full Option List 41 | 42 | If you want to invent your own start-up, or are using an orchestration tool, here is a quick view of all the configuration options piled into one command along with their defaults: 43 | 44 | $ docker run -d garywiz/docker-keybox \ 45 | -p 8443:8443 \ 46 | -e CONFIG_LOGGING=stdout \ 47 | -e CONFIG_AUTHKEYS_REFRESH=120 \ 48 | -e CONFIG_ENABLE_KEY_MANAGEMENT=true \ 49 | -e CONFIG_OTP=optional \ 50 | -e CONFIG_ENABLE_INTERNAL_AUDIT=false \ 51 | -e CONFIG_DELETE_AUDIT_AFTER=90 \ 52 | -e CONFIG_AUDIT_LOG_APPENDER="" \ 53 | -e CONFIG_FORCE_KEY_GENERATION=true \ 54 | -e CONFIG_SERVER_ALIVE_SECS=60 \ 55 | -e CONFIG_EXT_SSL_HOSTNAME=localhost 56 | 57 | * **`CONFIG_LOGGING`**: Either `stdout` (the default), `file`, or `syslog:host` (see "Logging Configuration" below). 58 | * **`CONFIG_AUTHKEYS_REFRESH`**: If key management is enabled, this is the number of seconds that `authorized_keys` files on remote hosts will be refreshed. 59 | * **`CONFIG_ENABLE_KEY_MANAGEMENT`**: If 'true', then remote servers will be managed and keys will be pushed to `authorized_keys` files. If 'false', then this is just a bastion host for terminal access. 60 | * **`CONFIG_OTP`**: Configure two-factor authentication, either 'required', 'optional', or 'disabled'. 61 | * **`CONFIG_SERVER_ALIVE_SECS`**: Specifies the interval (in seconds) which will be used for keep-alive pings sent to backend servers. Defaults to 60. 62 | * **`CONFIG_ENABLE_INTERNAL_AUDIT`**: If 'true', then internal audit logging will be enabled. Defaults to 'false'. 63 | * **`CONFIG_DELETE_AUDIT_AFTER`**: Set to the number of days internal audit logs will be kept. Defaults to 90. 64 | * **`CONFIG_AUDIT_LOG_APPENDER`**: If specified, this is a destination host which will be used for log4j audit logging. 65 | * **`CONFIG_FORCE_KEY_GENERATION`**: If 'true', then user keys will be generated and private keys will be downloaded automatically. If 'false', then no key generation will occur and users can paste-in their own public keys. 66 | * **`CONFIG_EXT_SSL_HOSTNAME`**: This is the name of the SSL host. It should match the actual hostname people will use to access the server. If set to "blank", then KeyBox will run using standard HTTP. 67 | 68 | ## Configuring Attached Storage 69 | 70 | When configuring attached storage, there are two considerations: 71 | 72 | 1. Attached storage must be mounted at `/apps/var` inside the container, whether using the Docker `-v` switch, or `--volumes-from`. 73 | 2. You will need to tell the container to match the user credentials using the `--create-user` switch ([documented here on the Chaperone site](http://garywiz.github.io/chaperone/ref/command-line.html#option-create-user)). 74 | 75 | Both are pretty easy. For example, assume you are going to store persistent data on your local drive in `/persist/keybox`. Providing the directory exists, you can just do this: 76 | 77 | $ docker run -d -v /persist/keybox:/apps/var garywiz/docker-keybox \ 78 | --create-user anyuser:/apps/var 79 | 80 | When the container starts, it will assure that all internal services run as a new user called `anyuser` whose UID/GID credentials match the credentials your host box has assigned to `/persist/keybox`. 81 | 82 | That's it! 83 | 84 | When you run the container, you'll see that all the KeyBox persistent data files have been properly created in `/persist/keybox`. 85 | 86 | ##Logging Configuration 87 | 88 | By default, all container logs will be sent to `stdout` and can be viewed using the `docker logs` command. 89 | 90 | If this isn't suitable, there are two additional options that can be specified using the `CONFIG_LOGGING` environment variable: 91 | 92 | **`CONFIG_LOGGING="file"`** - This setting will cause all logging information to be sent to `var/log/syslog.log` either inside the container or on attached storage. 93 | 94 | **`CONFIG_LOGGING="syslog:hostname"`** - In this case, you need to specify `hostname` as the destination for logging. The specified host must have a syslog-compatible daemon running on UDP port 514. 95 | 96 | ### Customizing log4j configuration 97 | 98 | Audit logs are a special type of log different from the normal access and error logs that report on login activity. 99 | By default, they will be stored in the `logs` directory either within the image, or on attached storage. 100 | You can use your own log4j to send the audit logs to another location as well as the built-in database. 101 | This is useful if you want to collect them in a log aggregation tool such as splunk or ELK. 102 | 103 | In order to customize the configuration, you will need to use attached storage (see above "Configuring Attached Storage"). 104 | Once you do, the the default log4j.xml file will be created in the `config` directory. You can modify it and restart 105 | the container to use your new configuration. 106 | 107 | ## Using Your Own SSL Keys 108 | 109 | By default, a self-signed SSL key will be generated automatically for you at start-up. If attached storage is used, the key will be generated only once. Otherwise, a new key will be created each time a new container starts. 110 | 111 | Most enterprise and production installations will often want to use their own pre-defined key. In order to do so, you'll need to: 112 | 113 | 1. Run this image using attached storage. This is where you will store your keys, and they will persist when you upgrade the container. 114 | 2. Have your certificate and private key files in standard `PEM` format (the one usually used by Certificate authorities), or convert it from PKCS12 format as described below. 115 | 3. Not be an SSL noob. I hate to say it, but it really helps if you've done this before. 116 | 117 | Here is a step-by-step guide. 118 | 119 | ####Run with Persistent Storage 120 | 121 | This is easy if you're using the provided launcher, as described above. The first thing to do is run the container once just to initialize the persistent storage directory: 122 | 123 | $ mkdir docker-keybox-storage 124 | $ ./run-docker-keybox.sh -d 125 | Using attached storage at .../docker-keybox-storage 126 | 00e9615bc51d63f9a150186482b3258d1c24b4f21ca0c781ae6e1717d9c97abc 127 | $ 128 | 129 | Now that your container is running, you should see the following in `docker-keybox-storage`: 130 | 131 | $ cd docker-keybox-storage 132 | $ ls 133 | certs config keydb log run 134 | $ 135 | 136 | Certificates are stored in the `certs` directory: 137 | 138 | $ cd certs 139 | $ ls 140 | jetty.keystore ssl-cert-localhost.crt ssl-cert-localhost.key 141 | $ 142 | 143 | The self-signed certificates is the file ending with `.crt` and the private key is the one ending with `.key`. 144 | 145 | Once you see that these are present, it's probably a good idea to stop (and even delete) your container, as all persistent data is now stored in `docker-keybox-storage`. 146 | 147 | #### Replace the keys with your own 148 | 149 | Note that the names of the certificate and keys will always look like this: `ssl-cert-.crt`, where `` will be the exact string you used with the `CONFIG_EXT_SSL_HOSTNAME` environment variable. 150 | 151 | So, if your site is going to be `https://keybox.example.com`, then make sure you edit your start-up scripts to change the hostname, then make sure your certificate and key files are correctly named as follows: 152 | 153 | ssl-cert-keybox.example.com.crt 154 | ssl-cert-keybox.example.com.key 155 | 156 | If your keys are not already in `PEM` format, you may need to convert them using SSL as [this StackOverflow answer describes for PKCS12 keys](http://stackoverflow.com/questions/15144046/need-help-converting-p12-certificate-into-pem-using-openssl). 157 | 158 | **IMPORTANT**: You will also need to delete the `jetty.keystore` file in the same directory. This will cause the container start-up scripts to recognize your new key and rebuild the Jetty keystore file for you automatically. 159 | 160 | #### Re-run the container 161 | 162 | Once you've replaced the certificates, you can simply restart the old container, or create a new container using the same attached storage location. Your new certificate will then be in use. 163 | -------------------------------------------------------------------------------- /bash.bashrc: -------------------------------------------------------------------------------- 1 | # Bash start-up file, created by chaplocal 2 | # Used only for development. 3 | 4 | export PROMPT_DIRTRIM=2 5 | cd $APPS_DIR 6 | 7 | # I typically work in the Emacs process buffer. Your mileage may vary 8 | if [ "$EMACS" == "t" ]; then 9 | stty -echo 10 | alias ls='ls --color=never' 11 | fi 12 | 13 | alias apps="cd $APPS_DIR" 14 | alias NE="stty -echo" 15 | 16 | export PATH=$JAVA_HOME/bin:$PATH 17 | -------------------------------------------------------------------------------- /bin/README: -------------------------------------------------------------------------------- 1 | Put commands which need to be executed at the command line, or by application 2 | programs here. This directory will automaticaly be included the path for all 3 | running services. 4 | 5 | Some files you may find here: 6 | 7 | chaplocal 8 | This is a template for the chaplocal program, used to set up a container 9 | development environment. 10 | 11 | get-chaplocal 12 | Executed as a --task to retrieve a custom version of the 'chaplocal' script. 13 | 14 | get-help 15 | Executed as a --task to provide help for the image (see ../etc/task-templates) 16 | 17 | get-launcher 18 | Executed as a --task to create launch scripts to launch the image 19 | (see ../etc/task-templates) 20 | -------------------------------------------------------------------------------- /bin/apps-update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- mode: python -*- 3 | 4 | """ 5 | Update an older apps directory with newly changed files. 6 | 7 | Usage: apps-update 8 | 9 | This is a utility to simplify the process of updating an existing development 10 | directory with new features and changes from the distribution images. Typically 11 | you run this from inside the container, like this (assuming your home directory 12 | is /home): 13 | 14 | # In your local apps directory... 15 | docker run -i -t --rm -v /home:/home chapdev/chaperone-lamp \ 16 | --create-user yourname:$PWD/chaperone.d \ 17 | --config $PWD/chaperone.d --task /bin/bash 18 | garyw@47de5d3ad4d6:~$ /apps/bin/apps-update /apps $APPS_DIR 19 | 20 | You'll then be prompted for a variety of options, including the ability to 21 | omit directories and view diffs on files which have changed. 22 | 23 | Although intended for the above, this is quite generic and can be used 24 | for any directory-to-directory update task. 25 | 26 | """ 27 | 28 | import os 29 | import re 30 | import difflib 31 | import shutil 32 | import signal 33 | from docopt import docopt 34 | 35 | options = docopt(__doc__) 36 | 37 | def ask(prompt, evalfunc = lambda x: x, error = None): 38 | result = None 39 | while result is None: 40 | result = input(prompt + " ") 41 | try: 42 | result = evalfunc(result) 43 | except Exception as ex: 44 | print("Try again. (" + (error or str(ex)) + ")") 45 | result = None 46 | return result 47 | 48 | def yorn(prompt): 49 | def check(x): 50 | x = x.upper() 51 | if "YES".startswith(x): 52 | return True 53 | if "NO".startswith(x): 54 | return False 55 | raise Exception("Must answer 'Yes' or 'No'") 56 | return ask(prompt, check) 57 | 58 | def getintlist(txt, maxval = None, minval = 1): 59 | if not txt: 60 | return list() 61 | vals = re.split('[\s,]', txt) 62 | result = [int(v) for v in vals] 63 | for r in result: 64 | if ((minval is not None and r < minval) or 65 | (maxval is not None and r > maxval)): 66 | raise Exception("'{0}' is out of range".format(r)) 67 | return result 68 | 69 | MSG_HELP = """For each destination file: 70 | Diffs - show the differences (will be saved in .diffs automatically) 71 | Copy - copy the file to the destination, overwriting it 72 | Keep - keep the existing file (.diffs are still created) 73 | Help/?- this message""" 74 | 75 | def do_copy_or_edit(src, dest, files, diffs): 76 | results = list() 77 | 78 | # Start with the list of files which have no changes 79 | nodiffs = [f for f in files if f not in diffs] 80 | 81 | print("There are {0.value} {0.name} with no differences.".format(pluralint(len(nodiffs), 'file'))) 82 | 83 | def opteval(v): 84 | if len(v) == 0: 85 | raise Exception("no default") 86 | v=v.upper() 87 | if any(k.startswith(v) 88 | for k in ('DIFFS', 'COPY', 'KEEP', 'HELP', '?')): 89 | return v[0] 90 | raise Exception("Not a valid option") 91 | 92 | if diffs: 93 | print("\nThese are the common files with differences...") 94 | 95 | for f,diff in diffs.items(): 96 | option = 'X' 97 | while option not in 'CK': 98 | option = ask("\n{0}:\n Diffs/Copy/Keep/Help? ".format(os.path.join(dest, f)), opteval) 99 | if option in '?H': 100 | print(MSG_HELP) 101 | elif option == 'D': 102 | diff.print() 103 | elif option in 'CK': 104 | if option == 'K': 105 | diff.operation = 'diff' 106 | results.append(diff) 107 | 108 | return results 109 | 110 | 111 | class FileBase: 112 | 113 | def __init__(self, fn, dir1, dir2): 114 | self.basis = fn 115 | self.fromname = os.path.join(dir1, fn) 116 | self.toname = os.path.join(dir2, fn) 117 | self.init() 118 | def init(self): 119 | pass 120 | 121 | class FileOpCopy(FileBase): 122 | 123 | operation = "copy" 124 | 125 | def preview(self): 126 | if self.operation == 'copy': 127 | print("COPY: {0} to {1}".format(self.fromname, self.toname)) 128 | else: 129 | print("PRESERVE: {0}".format(self.toname)) 130 | 131 | def apply(self): 132 | if self.operation != 'copy': 133 | return 134 | print("copying {0} to {1}".format(self.fromname, self.toname)) 135 | os.makedirs(os.path.dirname(self.toname), exist_ok=True) 136 | shutil.copy2(self.fromname, self.toname) 137 | 138 | class FileOpDiff(FileOpCopy): 139 | 140 | diffs = None 141 | 142 | @classmethod 143 | def create_diffs(cls, files, dir1, dir2): 144 | results = dict() 145 | for f in files: 146 | d = cls(f, dir1, dir2) 147 | if d.any_changes: 148 | results[f] = d 149 | return results 150 | 151 | @property 152 | def any_changes(self): 153 | return bool(self.diffs) 154 | 155 | def init(self): 156 | self.diff_file = self.toname + ".diffs" 157 | self.compare() 158 | 159 | def print(self): 160 | if not self.diffs: 161 | print("{0}: no differences".format(self.basis)) 162 | return 163 | print("".join(self.diffs)) 164 | 165 | def compare(self): 166 | self.diffs = list(difflib.context_diff(open(self.fromname).readlines(), 167 | open(self.toname).readlines(), 168 | self.fromname, self.toname)) 169 | 170 | def preview(self): 171 | super().preview() 172 | print(" with differences in '{0}'".format(self.diff_file)) 173 | 174 | def apply(self): 175 | super().apply() 176 | print("creating {0}".format(self.diff_file)) 177 | f = open(self.diff_file, 'w') 178 | f.write(''.join(self.diffs)) 179 | f.close() 180 | 181 | class pluralint: 182 | 183 | @property 184 | def name(self): 185 | if self.value == 1: 186 | return self.singular 187 | if self.plural: 188 | return self.plural 189 | if self.singular.endswith('y'): 190 | return self.singular[:-1] + 'ies' 191 | if self.singular.endswith('s'): 192 | return self.singular + 'es' 193 | return self.singular + 's' 194 | 195 | def __init__(self, value, name, name_plural = None): 196 | self.value = value 197 | self.singular = name 198 | self.plural = name_plural 199 | 200 | class FileTree: 201 | 202 | _rootpath = None 203 | 204 | def __init__(self, rootpath): 205 | self._rootpath = rootpath 206 | self.build_tree() 207 | 208 | def _makepath(self, path, base): 209 | return os.path.relpath(os.path.join(path, base), self._rootpath) 210 | 211 | def build_tree(self): 212 | flist = self._flist = set() 213 | dlist = self._dlist = set() 214 | for root, dirs, files in os.walk(self._rootpath): 215 | for f in files: 216 | flist.add(self._makepath(root, f)) 217 | for f in dirs: 218 | dlist.add(self._makepath(root, f)); 219 | 220 | def common_with(self, other): 221 | return sorted(self._flist.intersection(other._flist)) 222 | 223 | def new_files_in_known_dirs(self, other, omitdirs = list()): 224 | newfiles = self._flist.difference(other._flist) 225 | return sorted(f for f in newfiles if any(f.startswith(d) for d in self._dlist) 226 | and not any(f.startswith(d) for d in omitdirs)) 227 | 228 | def new_dirs(self, other): 229 | return sorted(self._dlist.difference(other._dlist)) 230 | 231 | def dump(self): 232 | for d in self._dlist: 233 | print("D", d) 234 | for f in self._flist: 235 | print("F", f) 236 | 237 | srcdir = options[''] 238 | destdir = options[''] 239 | 240 | srcapps = FileTree(srcdir) 241 | destapps = FileTree(destdir) 242 | 243 | common_files = srcapps.common_with(destapps) 244 | new_dirs = srcapps.new_dirs(destapps) 245 | 246 | def int_handler(signum, frame): 247 | print("\n^C.\nExiting. NO changes have been made to '{0}'.".format(destdir)) 248 | exit(0) 249 | 250 | signal.signal(signal.SIGINT, int_handler) 251 | 252 | print("In {0} ...".format(srcdir)) 253 | 254 | omitdirs = list() 255 | 256 | if new_dirs: 257 | print("There are {0.value} new {0.name}.".format(pluralint(len(new_dirs), 'directory'))) 258 | for i in range(len(new_dirs)): 259 | print(" {0}. {1}".format(i+1, new_dirs[i])) 260 | nv = ask("Which directories should NOT be included in the update?", lambda v: getintlist(v, len(new_dirs)+1)) 261 | if nv: 262 | omitdirs = [new_dirs[n-1] for n in nv] 263 | 264 | new_files = srcapps.new_files_in_known_dirs(destapps, omitdirs) 265 | 266 | if new_files: 267 | print("There are {0.value} new files {0.name} not present in {1}".format(pluralint(len(new_files), 'file'), destdir)) 268 | option = ask("(A)ll (N)one (C)hoose?", lambda v: v[0].upper() in 'ANC' and v[0].upper()) 269 | if option == 'N': 270 | new_files = list() 271 | elif option == 'C': 272 | nv = True 273 | while nv: 274 | for i in range(len(new_files)): 275 | print(" {0}. {1}".format(i+1, new_files[i])) 276 | nv = ask("Which files should NOT be included in the update (Enter to stop choosing)\n?", 277 | lambda v: getintlist(v, len(new_files)+1)) 278 | if nv: 279 | new_files = [new_files[i] for i in range(len(new_files)) if i+1 not in nv] 280 | print("{0.value} new {0.name} will be copied to {1}".format(pluralint(len(new_files), 'file'), destdir)) 281 | 282 | new_ops = [FileOpCopy(f, srcdir, destdir) for f in new_files] 283 | 284 | if common_files: 285 | common_diffs = FileOpDiff.create_diffs(common_files, srcdir, destdir) 286 | print("There are {0.value} {0.name} in common between {1} and {2}. ({3.value} {3.name} identical)". 287 | format(pluralint(len(common_files), 'file'), destdir, srcdir, 288 | pluralint(len(common_files) - len(common_diffs), 'is', 'are'))) 289 | option = ask("(A)ll (N)one (C)hoose?", lambda v: v[0].upper() in 'ANC' and v[0].upper()) 290 | if option == 'N': 291 | common_ops = list() 292 | elif option == 'C': 293 | common_ops = do_copy_or_edit(srcdir, destdir, common_files, common_diffs) 294 | else: 295 | common_ops = common_diffs.values() 296 | 297 | if not new_ops and not common_ops: 298 | print("There are no operations to perform.") 299 | exit(0) 300 | 301 | print("\nNothing has been done so far. Before proceeding, let's be sure...") 302 | 303 | if new_ops: 304 | print("\nThese are the new files that don't exist...") 305 | for f in new_ops: 306 | f.preview() 307 | 308 | if common_ops: 309 | print("\nThis is what we'll do with common files...") 310 | for f in common_ops: 311 | f.preview() 312 | 313 | option = yorn("\nWould you like to apply ALL the above changes?") 314 | 315 | if not option: 316 | print("No changes will be applied.") 317 | exit(0) 318 | 319 | for f in new_ops: 320 | f.apply() 321 | for f in common_ops: 322 | f.apply() 323 | -------------------------------------------------------------------------------- /bin/chaplocal: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Little script to run a chaperone docker container in the local directory 3 | # for development or other purposes. 4 | 5 | DEFAULT_IMAGE="chapdev/chaperone-baseimage" 6 | VERSTR="chaplocal version 1.31" 7 | 8 | if [ "$CHAP_SERVICE_NAME" != "" ]; then 9 | echo "The 'chaplocal' script is not intended to be run inside a container," 10 | echo "but for use on the docker host to set up local development directories." 11 | echo "Maybe you meant 'get-chaplocal'?" 12 | exit 1 13 | fi 14 | 15 | if [ "$1" == '-V' ]; then 16 | echo $VERSTR 17 | exit 18 | fi 19 | 20 | if [ "$1" == '-d' ]; then 21 | runopts="-d" 22 | shift 23 | else 24 | runopts="" 25 | fi 26 | 27 | IMAGE="$2" 28 | if [ $# == 1 ]; then 29 | IMAGE=$DEFAULT_IMAGE 30 | elif [ $# != 2 ]; then 31 | echo "" 32 | echo "Usage: chaplocal [-d] local-apps-dir [image-name]" 33 | echo "" 34 | echo "Runs the specified chaperone image and uses local-apps-dir for the apps" 35 | echo "directory. Creates a script in local-apps-dir called run.sh so you can" 36 | echo "run an interactive (default) or daemon instance." 37 | echo "" 38 | echo "Will run all container processes under the current user account with the" 39 | echo "local drive mounted as a shared volume in the container." 40 | echo "" 41 | echo "If not specified, then the image '$DEFAULT_IMAGE' will be used." 42 | echo "" 43 | exit 44 | fi 45 | 46 | APPSBASE="$1" 47 | LOCALROOT=$PWD 48 | 49 | APPSDIR=$LOCALROOT/$APPSBASE 50 | MOUNT=/home 51 | 52 | # Assure we're running in /home or /Users 53 | if [ "${PWD:0:7}" == "/Users/" ]; then 54 | MOUNT=/Users 55 | elif [ "${PWD:0:6}" != "/home/" ]; then 56 | echo "Sorry, this script is designed to operate only within the /home or /Users directory" 57 | echo "of your host. :-(" 58 | exit 1 59 | fi 60 | 61 | if [[ "$IMAGE" != *":"* ]]; then 62 | IMAGE=$IMAGE:latest 63 | fi 64 | 65 | if [ "`docker inspect $IMAGE >/dev/null 2>&1 && echo yes`" != "yes" ]; then 66 | echo "" 67 | echo Cannot find local docker image: $IMAGE 68 | echo "" 69 | echo You may want to pull the image with \"docker pull $IMAGE\" 70 | echo and then try again. 71 | exit 1 72 | fi 73 | 74 | # Extract our local UID/GID 75 | myuid=`id -u` 76 | mygid=`id -g` 77 | 78 | # Copy the boilerplate apps directory into this development directory where it can be 79 | # worked on easily. 80 | if [ ! -d $APPSDIR ]; then 81 | echo "" 82 | echo Extracting /apps default directory into $APPSDIR ... 83 | mkdir $APPSDIR 84 | SELINUX_FLAG=$(sestatus 2>/dev/null | fgrep -q enabled && echo :z) 85 | docker run -i --rm=true -v $APPSDIR:/appsdir$SELINUX_FLAG $IMAGE \ 86 | --user root --config /apps/chaperone.d --task clone-apps /appsdir $myuid:$mygid 87 | echo "You can customize the contents of $APPSDIR to tailor it for your application," 88 | echo "then use it as a template for your production image." 89 | echo "" 90 | if [ -x $APPSDIR/bin/chaplocal ]; then 91 | THEIRVER=`$APPSDIR/bin/chaplocal -V` 92 | if [ "$THEIRVER" != "$VERSTR" ]; then 93 | echo "WARNING: This image uses a different version of chaplocal." 94 | echo " Ours: $VERSTR" 95 | echo " Image: $THEIRVER" 96 | echo "" 97 | fi 98 | fi 99 | echo Executing run.sh within $APPSDIR ... 100 | echo "" 101 | elif [ ! -f $APPSDIR/run.sh ]; then 102 | echo "$APPSDIR already exists but doesn't seem to be one I created (no run.sh in there)" 103 | echo "You're on your own" 104 | exit 1 105 | else 106 | echo "" 107 | echo $APPSDIR already exists. 108 | echo Executing run.sh within $APPSDIR ... 109 | echo "" 110 | fi 111 | 112 | $APPSDIR/run.sh $runopts 113 | -------------------------------------------------------------------------------- /bin/clone-apps: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Used to clone the /apps hierarchy into a new develoment directory using 3 | # clone skeletons. 4 | 5 | DESTPATH="$1" 6 | UIDGID="$2" 7 | SKEL="$APPS_DIR/etc/skel" 8 | CLONESKEL="$SKEL/clone" 9 | 10 | if [ "$CHAP_TASK_MODE" != "1" ]; then 11 | echo "'clone-apps' should only be run in chaperone --task mode from the docker host" 12 | exit 1 13 | fi 14 | 15 | if [ "$DESTPATH" == "" -o "$UIDGID" == "" ]; then 16 | echo 'Usage: clone-apps :' 17 | exit 18 | fi 19 | 20 | if [ "${DESTPATH#/}" == "${DESTPATH}" ]; then 21 | echo "Error: must be an absolute path, $DESTPATH" 22 | exit 1 23 | fi 24 | 25 | if [ -e "$DESTPATH" ]; then 26 | if [ "$(ls $DESTPATH)" != "" ]; then 27 | echo "Error: already exists and is not empty, $DESTPATH" 28 | exit 1 29 | fi 30 | fi 31 | 32 | # Clone the actual directory 33 | (cd $APPS_DIR; tar cf - --exclude ./var .) | (mkdir -p $DESTPATH; cd $DESTPATH; tar xf -) 34 | 35 | cd $CLONESKEL 36 | skelfiles=`find . -name '*.tpl'` 37 | cd $DESTPATH 38 | 39 | source $SKEL/shell_vars.inc 40 | 41 | for tpl in $skelfiles; do 42 | tpl=${tpl#./} 43 | tdir=$(dirname $tpl) 44 | if [ "$tdir" != "." ]; then 45 | mkdir -p $tdir 46 | fi 47 | if [ ! -f "${tpl%.tpl}" ]; then 48 | tpl_envcp -a $CLONESKEL/$tpl ${tpl%.tpl} 49 | fi 50 | done 51 | 52 | chown -R $UIDGID $DESTPATH 53 | -------------------------------------------------------------------------------- /bin/cps: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # PS defaults which are a bit nicer for containers 3 | 4 | ps --forest -weo 'user,pid,ppid,pgid,vsz,rss,stat,command' $* 5 | -------------------------------------------------------------------------------- /bin/get-chaplocal: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$CHAP_SERVICE_NAME" != "" ]; then 4 | stty -onlcr 2>/dev/null # inhibits carriage return if -t was specified 5 | fi 6 | 7 | if [ "$CHAP_TASK_MODE" != "1" ]; then 8 | echo "get-chaplocal' should only be run in chaperone --task mode from the docker host" 9 | exit 1 10 | fi 11 | 12 | source $APPS_DIR/etc/version.inc 13 | 14 | if [ "$IMAGE_NAME" == "" ]; then 15 | script_text=`cat $APPS_DIR/bin/chaplocal` 16 | else 17 | script_text=`sed "s|chapdev/chaperone-baseimage|$IMAGE_NAME|" $APPS_DIR/bin/chaplocal` 18 | fi 19 | 20 | 21 | cat </dev/null 34 | exit 1 35 | fi 36 | cat >chaplocal <<'-QEOF-' 37 | $script_text 38 | -QEOF- 39 | chmod ugo+x chaplocal 40 | echo "" 41 | echo "The 'chaplocal' script is ready to use. Here is the help you get if you type" 42 | echo " ./chaplocal" 43 | echo "at the command line..." 44 | ./chaplocal 45 | ################################################################################# 46 | # 47 | # YOU SHOULD NOT BE SEEING THIS!!!!!!!!! IF SO, YOU ARE NOT PIPING INTO sh! 48 | # 49 | # Make sure you pipe the output of your docker command into sh, like this... 50 | # 51 | # docker run -i chapdev/chaperone-baseimage --command get-chaplocal | sh 52 | # ^^^^ 53 | ################################################################################# 54 | EOF 55 | -------------------------------------------------------------------------------- /bin/get-help: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$CHAP_SERVICE_NAME" != "" ]; then 4 | stty -onlcr 2>/dev/null # inhibits carriage return if -t was specified 5 | fi 6 | 7 | if [ "$CHAP_TASK_MODE" != "1" ]; then 8 | echo "get-chaplocal' should only be run in chaperone --task mode from the docker host" 9 | exit 1 10 | fi 11 | 12 | helpfile=HELP 13 | 14 | if [ "$1" != "" -a "$1" != "/bin/bash" ]; then 15 | helpfile="$1" 16 | fi 17 | 18 | function expand_template() { tpl_envcp - <$1; } 19 | skel=$APPS_DIR/etc/skel 20 | 21 | if [ ! -f "$skel/help/$helpfile.tpl" ]; then 22 | cat </dev/null # inhibits carriage return if -t was specified 5 | fi 6 | 7 | if [ "$CHAP_TASK_MODE" != "1" ]; then 8 | echo "get-chaplocal' should only be run in chaperone --task mode from the docker host" 9 | exit 1 10 | fi 11 | 12 | cat </dev/null; exit 1; }" 40 | for lf in $tplfiles; do 41 | realname=$(transform_filename $lf) 42 | contents=$(expand_template $lf) 43 | cat <$realname <<"--EOF--" 46 | $contents 47 | --EOF-- 48 | chmod 755 $realname 49 | EOF 50 | done 51 | if [ -f "$skel/help/LAUNCHER.tpl" ]; then 52 | cat </apps/etc/version.inc 26 | 27 | # Get Keybox and install it 28 | 29 | cd /setup 30 | wget --progress=dot:mega --no-check-certificate $KEYBOX_URL 31 | 32 | cd /apps 33 | tar xzf /setup/keybox-jetty-v$KEYBOX_VERSION.tar.gz 34 | 35 | # Move config files to our shared location unless they are already there (this could occur 36 | # on an upgrade or rebuild) 37 | 38 | JETTY=/apps/KeyBox-jetty/jetty 39 | 40 | # These won't exist until runtime but we define here because it simplifies 41 | # assuring relative paths are correct. 42 | VAR_KBCONFIG=/apps/var/config/KeyBoxConfig.properties 43 | VAR_JETTY_START=/apps/var/config/jetty-start.ini 44 | VAR_L4JCONFIG=/apps/var/config/log4j.xml 45 | 46 | # We need to create a symlink to the /var/config/KeyBoxConfig.properties file, since this 47 | # will need to be writable. But, the actual location may vary based upon container 48 | # configuration. 49 | 50 | cd $JETTY/keybox/WEB-INF/classes 51 | rm -rf KeyBoxConfig.properties 52 | ln -s $(relpath $VAR_KBCONFIG) 53 | 54 | # same with the log4j config 55 | 56 | cd $JETTY/keybox/WEB-INF/classes 57 | rm -rf log4j.xml 58 | ln -s $(relpath $VAR_L4JCONFIG) 59 | 60 | # Same with jetty startup so we can put the keystore in attached storage 61 | 62 | cd $JETTY 63 | rm -rf start.ini 64 | ln -s $(relpath $VAR_JETTY_START) start.ini 65 | 66 | # Move/replace the initial database directory to etc so we can initialize /var whenever a container 67 | # starts. 68 | 69 | cd $JETTY/keybox/WEB-INF/classes 70 | rm -rf /apps/etc/keydb-dist 71 | mv keydb /apps/etc/keydb-dist 72 | ln -s $(relpath /apps/var/keydb) 73 | 74 | # General cleanup 75 | cd / 76 | rm -rf /setup /tmp/* /var/tmp/* /var/cache/apk/* /root/.cache 77 | chown -R runapps: /apps # for full-container execution 78 | -------------------------------------------------------------------------------- /chaperone.d/005-config.conf: -------------------------------------------------------------------------------- 1 | # 005-config.conf 2 | # 3 | # Put container configuration variables here. This should strictly be for configuration 4 | # variables that are passed into the container. 100% of container configuraiton should 5 | # be possible by setting these variables here or on the 'docker run' command line. 6 | 7 | settings: { 8 | env_set: { 9 | # Can be set to: 10 | # stdout - all logging goes to stdout (the docker way) 11 | # file - all logging goes to a var/log/syslog.log on attached storage 12 | # syslog:host - all logging goes to the syslog host 13 | 14 | CONFIG_LOGGING: "$(CONFIG_LOGGING:-stdout)", 15 | 16 | # Refresh authorized_keys files this often. No refresh if set to 0 (note :- is for default, not minus). 17 | CONFIG_AUTHKEYS_REFRESH: "$(CONFIG_AUTHKEYS_REFRESH:-120)", 18 | 19 | # Enable key management. If 'false', then this is just a bastion host for shell access. 20 | CONFIG_ENABLE_KEY_MANAGEMENT: "$(CONFIG_ENABLE_KEY_MANAGEMENT:-true)", 21 | 22 | # Enable two-factor authentication 23 | # CONFIG_OTP can be set to 'required', 'optional', or 'disabled' 24 | # (For compatibility, CONFIG_ENABLE_OTP is still respected where 'false' -> 'disabled') 25 | CONFIG_OTP: "$(CONFIG_OTP:-$(CONFIG_ENABLE_OTP:|false|disabled|optional))", 26 | 27 | # Require that they generate (rather than provide their own) keys 28 | CONFIG_FORCE_KEY_GENERATION: "$(CONFIG_FORCE_KEY_GENERATION:-true)", 29 | 30 | # Internal auditing (CONFIG_ENABLE_INTERNAL_AUDIT is preferred, CONFIG_ENABLE_AUDIT is for backward compatibility) 31 | CONFIG_ENABLE_INTERNAL_AUDIT: "$(CONFIG_ENABLE_INTERNAL_AUDIT:-$(CONFIG_ENABLE_AUDIT:-false))", 32 | CONFIG_DELETE_AUDIT_AFTER: "$(CONFIG_DELETE_AUDIT_AFTER:-90)", 33 | 34 | # server keep alive pings 35 | CONFIG_SERVER_ALIVE_SECS: "$(CONFIG_SERVER_ALIVE_SECS:-60)", 36 | 37 | # This is the hostname of the host machine. 'localhost' is fine. However, this is used 38 | # to generate self-signed keys. So, if you'd like your keys to be stamped with a 39 | # particular FQDN, then include it in your startup configuration. 40 | CONFIG_EXT_HOSTNAME: "$(CONFIG_EXT_HOSTNAME:-localhost)", 41 | CONFIG_EXT_SSL_HOSTNAME: "$(CONFIG_EXT_SSL_HOSTNAME:-)", 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /chaperone.d/010-start.conf: -------------------------------------------------------------------------------- 1 | # 010-start.conf 2 | # 3 | # This is the first start-up file for the chaperone base images. Note that start-up files 4 | # are processed in order alphabetically, so settings in later files can override those in 5 | # earlier files. 6 | 7 | # General environmental settings. These settings apply to all services and logging entries. 8 | # There should be only one "settings" directive in each configuration file. But, any 9 | # settings encountered in subsequent configuration files can override or augment these. 10 | # Note that variables are expanded as late as possile. So, there can be variables 11 | # defined here which depend upon variables which will be defined later (such as _CHAP_SERVICE), 12 | # which is defined implicitly for each service. 13 | 14 | settings: { 15 | 16 | env_set: { 17 | 18 | 'LANG': 'en_US.UTF-8', 19 | 'LC_CTYPE': '$(LANG)', 20 | 'PATH': '$(APPS_DIR)/bin:/usr/local/bin:/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/sbin', 21 | 'RANDFILE': '/tmp/openssl.rnd', 22 | 23 | # Uncomment the below to tell startup.sh to lock-down the root account after the first 24 | # successful start. 25 | #'SECURE_ROOT': '1', 26 | 27 | # Variables starting with _CHAP are internal and won't be exported to services, 28 | # so we derive public environment variables if needed... 29 | 'APPS_DIR': '$(_CHAP_CONFIG_DIR:-/)', 30 | 'CHAP_SERVICE_NAME': '$(_CHAP_SERVICE:-)', 31 | 'CHAP_TASK_MODE': '$(_CHAP_TASK_MODE:-)', 32 | 33 | # The best use-cases will want to move $(VAR_DIR) out of the container to keep 34 | # the container emphemeral, so all references to var should always use this 35 | # environment variable. 36 | 'VAR_DIR': '$(APPS_DIR)/var', 37 | }, 38 | 39 | } 40 | 41 | # This is the startup script which manages the contents of $(APPS_DIR)/startup.d. It will 42 | # run each of the startup.d scripts in sequence. Because this is part of the special "INIT" 43 | # group, it will be run before any other service which is not in the group. This makes 44 | # it unnecessary to worry about 'before:' and 'after:' settings for startup scripts. 45 | 46 | startup.service: { 47 | type: oneshot, 48 | process_timeout: 600, # allow 10 minutes for startup in case installs need to be done 49 | command: '/bin/bash $(APPS_DIR)/etc/startup.sh', 50 | before: 'default,database,application', 51 | service_groups: 'INIT', 52 | } 53 | 54 | # Extended console logging dumps everything to stdout 55 | 56 | console-full.logging: { 57 | enabled: "$(CONFIG_LOGGING:|stdout|true|false)", 58 | selector: '*.info', 59 | stdout: true, 60 | } 61 | 62 | # Or we can send things to a file 63 | 64 | syslog-file.logging: { 65 | enabled: "$(CONFIG_LOGGING:|file|true|false)", 66 | selector: '*.info', 67 | file: '$(VAR_DIR)/log/syslog.log', 68 | } 69 | 70 | # Otherwise, if it contains "syslog:" then it is a syslog host 71 | 72 | syslog-host.logging: { 73 | enabled: "$(CONFIG_LOGGING:|syslog:*|true|false)", 74 | selector: '*.info', 75 | syslog_host: "$(CONFIG_LOGGING:/syslog://)", 76 | stdout: true, 77 | } 78 | 79 | # For the console, we include everything which is a warning except authentication 80 | # messages and daemon messages which are not errors. 81 | 82 | console.logging: { 83 | enabled: "$(CONFIG_LOGGING:|stdout|false|true)", # only if not already going to stdout 84 | stdout: true, 85 | selector: '*.warn;authpriv,auth.!*;daemon.!warn', 86 | } 87 | -------------------------------------------------------------------------------- /chaperone.d/020-java.conf: -------------------------------------------------------------------------------- 1 | # 020-java.conf 2 | # 3 | # Java specifics. Not much here, but creates a good place to add customizations. 4 | 5 | settings: { 6 | env_set: { 7 | 8 | # Figure out java location when we start rather than hardcode it. 9 | JAVA_HOME: "`readlink -f $(which java) | sed 's|/bin/java$||'`" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /chaperone.d/200-keybox.conf: -------------------------------------------------------------------------------- 1 | # 200-keybox.conf 2 | # 3 | # Keybox startup 4 | 5 | keybox.service: { 6 | type: forking, 7 | command: "$(APPS_DIR)/etc/start_keybox.sh", 8 | pidfile: "/tmp/keybox.pid", 9 | restart: true, 10 | restart_delay: 5, 11 | process_timeout: 40, # this should be long enough to wait 12 | } 13 | -------------------------------------------------------------------------------- /chaperone.d/300-logrotate.conf: -------------------------------------------------------------------------------- 1 | # 300-logrotate.conf 2 | # 3 | # This is disabled by default. 4 | # 5 | # However, you can enable this to run logrotation daily, and customize $(APPS_DIR)/etc/logrotate.conf 6 | # to indicate which logs should be rotated. 7 | 8 | logrotate.service: { 9 | enabled: false, 10 | type: cron, 11 | interval: "25 6 * * *", # uses standard crontab format. 12 | command: "/usr/sbin/logrotate -s $(VAR_DIR)/run/logrotate.status $(APPS_DIR)/etc/logrotate.conf", 13 | optional: true, # don't worry if logrotate isn't even installed 14 | directory: "$(VAR_DIR)/log", 15 | ignore_failures: true, # problems are best just investigated rather than causing problems 16 | } 17 | -------------------------------------------------------------------------------- /etc/KeyBoxConfig.properties.tpl: -------------------------------------------------------------------------------- 1 | # 2 | # KeyBox - Version: 2.85.03 3 | # 4 | # 5 | #set to true to regenerate and import SSH keys 6 | resetApplicationSSHKey=false 7 | #SSH Key Type 'dsa' or 'rsa' for generated keys 8 | sshKeyType=rsa 9 | #SSH Key Length for generated keys 10 | sshKeyLength=2048 11 | #private ssh key, leave blank to generate key pair 12 | privateKey= 13 | #public ssh key, leave blank to generate key pair 14 | publicKey= 15 | #default passphrase, leave blank for key without passphrase 16 | defaultSSHPassphrase=${randomPassphrase} 17 | #enable audit (original default = false) 18 | enableInternalAudit=%(CONFIG_ENABLE_INTERNAL_AUDIT) 19 | #keep audit logs for in days (original default = 90) 20 | deleteAuditLogAfter=%(CONFIG_DELETE_AUDIT_AFTER) 21 | #The number of seconds that the client will wait before sending a null packet to the server 22 | serverAliveInterval=%(CONFIG_SERVER_ALIVE_SECS:-60) 23 | #default timeout in minutes for websocket connection (no timeout for <=0) 24 | websocketTimeout=0 25 | #enable SSH agent forwarding 26 | agentForwarding=false 27 | #enable two-factor authentication 28 | oneTimePassword=%(CONFIG_OTP) 29 | #enable key management 30 | keyManagementEnabled=%(CONFIG_ENABLE_KEY_MANAGEMENT) 31 | #set to true to generate keys when added/managed by users and enforce strong passphrases set to false to allow users to set their own public key 32 | forceUserKeyGeneration=%(CONFIG_FORCE_KEY_GENERATION) 33 | #authorized_keys refresh interval in minutes (no refresh for <=0) 34 | authKeysRefreshInterval=%(CONFIG_AUTHKEYS_REFRESH) 35 | #Regular expression to enforce password policy 36 | passwordComplexityRegEx=((?=.*\\d)(?=.*[A-Z])(?=.*[a-z])(?=.*[!@#$%^&*()+=]).{8\,20}) 37 | #Password complexity error message 38 | passwordComplexityMsg=Passwords must be 8 to 20 characters\, contain one digit\, one lowercase\, one uppercase\, and one special character 39 | #HTTP header to identify client IP Address - 'X-FORWARDED-FOR' 40 | clientIPHeader= 41 | #specify a external authentication module (ex: ldap-ol, ldap-ad). Edit the jaas.conf to set connection details 42 | jaasModule= 43 | 44 | #H2 DB and Connection Pool settings 45 | #Path to the H2 DB file. Leave Blank to use default location which is ../WEB-INF/classes/keydb 46 | dbPath= 47 | #Max connections in the connection pool 48 | maxActive=25 49 | #When true, objects will be validated before being returned by the connection pool 50 | testOnBorrow=true 51 | #The minimum number of objects allowed in the connection pool before spawning new ones 52 | minIdle=2 53 | #The maximum amount of time (in milliseconds) to block before throwing an exception when the connection pool is exhausted 54 | maxWait=15000 55 | -------------------------------------------------------------------------------- /etc/README: -------------------------------------------------------------------------------- 1 | This is a "mini etc" directory which, as much as possible, is where all normal application and service configuration 2 | files are stored. For example, in the chaperone-lamp configuration, all MySQL and Apache configurations are stored 3 | here, but may make reference to other files on the system (such as modules and plugins). However, the normal 4 | startup files in /etc/apache2 and /etc/mysql are not used, as they expect a normal fully-booted system. 5 | 6 | System start-up is controlled by the startup.sh script, which reads additional startup files from ../startup.d. 7 | 8 | This is not built into chaperone, but rather is a custom configuration defined within chaperone.d. If you want, 9 | you can completely change the way things work and invent new startup schemes. But, this is a good place to start. 10 | -------------------------------------------------------------------------------- /etc/jetty-start.ini.tpl: -------------------------------------------------------------------------------- 1 | #=========================================================== 2 | # Jetty Startup 3 | # 4 | # Starting Jetty from this {jetty.home} is not recommended. 5 | # 6 | # A proper {jetty.base} directory should be configured, instead 7 | # of making changes to this {jetty.home} directory. 8 | # 9 | # See documentation about {jetty.base} at 10 | # http://www.eclipse.org/jetty/documentation/current/startup.html 11 | # 12 | # A demo-base directory has been provided as an example of 13 | # this sort of setup. 14 | # 15 | # $ cd demo-base 16 | # $ java -jar ../start.jar 17 | # 18 | #=========================================================== 19 | 20 | # To disable the warning message, comment the following line 21 | --module=home-base-warning 22 | 23 | 24 | # --------------------------------------- 25 | # Module: server 26 | --module=server 27 | ## 28 | ## Server Threading Configuration 29 | ## 30 | # minimum number of threads 31 | threads.min=10 32 | # maximum number of threads 33 | threads.max=200 34 | # thread idle timeout in milliseconds 35 | threads.timeout=60000 36 | # buffer size for output 37 | jetty.output.buffer.size=32768 38 | # request header buffer size 39 | jetty.request.header.size=8192 40 | # response header buffer size 41 | jetty.response.header.size=8192 42 | # should jetty send the server version header? 43 | jetty.send.server.version=true 44 | # should jetty send the date header? 45 | jetty.send.date.header=false 46 | # What host to listen on (leave commented to listen on all interfaces) 47 | #jetty.host=myhost.com 48 | # Dump the state of the Jetty server, components, and webapps after startup 49 | jetty.dump.start=false 50 | # Dump the state of the Jetty server, before stop 51 | jetty.dump.stop=false 52 | # Enable delayed dispatch optimisation 53 | jetty.delayDispatchUntilContent=false 54 | 55 | # --------------------------------------- 56 | # Module: deploy 57 | --module=deploy 58 | ## DeployManager configuration 59 | # Monitored Directory name (relative to jetty.base) 60 | # jetty.deploy.monitoredDirName=webapps 61 | 62 | 63 | # --------------------------------------- 64 | # Module: websocket 65 | --module=websocket 66 | 67 | # --------------------------------------- 68 | # Module: ext 69 | --module=ext 70 | 71 | # --------------------------------------- 72 | # Module: resources 73 | --module=resources 74 | 75 | # --------------------------------------- 76 | # Module: jsp 77 | --module=jsp 78 | # JSP Configuration 79 | 80 | # Select JSP implementation, choices are 81 | # glassfish : The reference implementation 82 | # default in jetty <= 9.1 83 | # apache : The apache version 84 | # default jetty >= 9.2 85 | jsp-impl=apache 86 | 87 | # To use a non-jdk compiler for JSP compilation when using glassfish uncomment next line 88 | # -Dorg.apache.jasper.compiler.disablejsr199=true 89 | 90 | # --------------------------------------- 91 | # Module: jstl 92 | --module=jstl 93 | # JSTL Configuration 94 | # The glassfish jsp-impl includes JSTL by default and this module 95 | # is not required to activate it. 96 | # The apache jsp-impl does not include JSTL by default and this module 97 | # is required to put JSTL on the container classpath 98 | 99 | # --------------------------------------- 100 | # Module: https or http, depending upon the setting of CONFIG_EXT_SSL_HOSTNAME 101 | 102 | %(CONFIG_EXT_SSL_HOSTNAME:|| 103 | --module=http 104 | jetty.port=8443 105 | | 106 | --module=https 107 | jetty.port=8443 108 | ) 109 | 110 | ## HTTP idle timeout in milliseconds 111 | http.timeout=30000 112 | 113 | ## HTTP Socket.soLingerTime in seconds. (-1 to disable) 114 | # http.soLingerTime=-1 115 | 116 | ## Parameters to control the number and priority of acceptors and selectors 117 | # http.selectors=1 118 | # http.acceptors=1 119 | # http.selectorPriorityDelta=0 120 | # http.acceptorPriorityDelta=0 121 | 122 | # Setup keystore 123 | jetty.keystore=../../var/certs/jetty.keystore 124 | jetty.truststore=../../var/certs/jetty.keystore 125 | jetty.keystore.password=%(`jpassword jetty %(KEYSTORE_PASSWORD) 2>&1 | sed -n '/^OBF/p'`) 126 | jetty.keymanager.password=%(`jpassword jetty %(EXPORT_PASSWORD) 2>&1 | sed -n '/^OBF/p'`) 127 | jetty.truststore.password=%(`jpassword jetty %(KEYSTORE_PASSWORD) 2>&1 | sed -n '/^OBF/p'`) 128 | -------------------------------------------------------------------------------- /etc/keydb-dist/keybox.h2.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/garywiz/docker-keybox/2c123dc2154f212df3d18eaba5ffb036b9a53564/etc/keydb-dist/keybox.h2.db -------------------------------------------------------------------------------- /etc/log4j.xml.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 26 | 27 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /etc/logrotate.conf: -------------------------------------------------------------------------------- 1 | # logrotate will run in the $(VAR_DIR)/log directory by default. 2 | # So paths here needn't be absolute. 3 | 4 | "*.log" { 5 | rotate 5 6 | daily 7 | compress 8 | } 9 | -------------------------------------------------------------------------------- /etc/skel/clone/Dockerfile.tpl: -------------------------------------------------------------------------------- 1 | # This is a template Dockerfile for creating a new image which is 2 | # derived from %(PARENT_IMAGE). Use this if you want to add features 3 | # to %(PARENT_IMAGE) and create a new image based upon it. 4 | 5 | FROM %(PARENT_IMAGE) 6 | ADD . /setup/ 7 | RUN /setup/build/install.sh 8 | -------------------------------------------------------------------------------- /etc/skel/clone/bash.bashrc.tpl: -------------------------------------------------------------------------------- 1 | # Bash start-up file, created by chaplocal 2 | 3 | export PROMPT_DIRTRIM=2 4 | cd $APPS_DIR 5 | 6 | echo "" 7 | echo "Now running inside container. Directory is: $APPS_DIR" 8 | echo "" 9 | 10 | port=${CONFIG_EXT_HTTP_PORT:-${CONFIG_EXT_PORT:-}} 11 | if [ "$port" != "" -a "$HTTPD_SERVER_NAME" != "" ]; then 12 | echo "The default '$HTTPD_SERVER_NAME' site is running at http://$CONFIG_EXT_HOSTNAME:$port/" 13 | echo "" 14 | fi 15 | -------------------------------------------------------------------------------- /etc/skel/clone/build.sh.tpl: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #Created by chaplocal on %(`date`) 3 | # the cd trick assures this works even if the current directory is not current. 4 | 5 | cd ${0%/*} 6 | if [ "$CHAP_SERVICE_NAME" != "" ]; then 7 | echo You need to run build.sh on your docker host, not inside a container. 8 | exit 9 | fi 10 | 11 | # Uncomment to default to your new derivative image name... 12 | #prodimage="%(PARENT_IMAGE:|chapdev/*|%(PARENT_IMAGE:/^chapdev/mylocal/)|%(PARENT_IMAGE))" 13 | 14 | [ "$1" != "" ] && prodimage="$1" 15 | 16 | if [ "$prodimage" == "" ]; then 17 | echo "Usage: ./build.sh " 18 | exit 1 19 | else 20 | echo Building "$prodimage" ... 21 | fi 22 | 23 | if [ ! -f Dockerfile ]; then 24 | echo "Expecting to find Dockerfile in current directory ... not found!" 25 | exit 1 26 | fi 27 | 28 | # Update the image information for the new build 29 | sed "s/^IMAGE_NAME=.*/IMAGE_NAME=${prodimage/\//\\\/}/" build/new_version.inc 30 | 31 | # Do the build 32 | docker build -t $prodimage . 33 | 34 | -------------------------------------------------------------------------------- /etc/skel/clone/build/README.tpl: -------------------------------------------------------------------------------- 1 | This directory contains a template for creating derivative images 2 | based upon '%(PARENT_IMAGE)'. 3 | 4 | Note that these build materials will NOT become a part of the newly created image. 5 | Instead, new build materials will be created if somebody wants to use 'chaplocal' 6 | to continue development. 7 | 8 | To make a complete, ready-to-go image, you should do the following: 9 | 10 | 1. Customize Dockerfile in this directory. 11 | 12 | 2. Customize install.sh by adding any additional build commands. 13 | 14 | 3. If you want to cater to developers who want to use this image, then 15 | modify the skeleton files in ../etc/skel as described below. 16 | 17 | 4. Usually, you can just create a new .git project at in the same 18 | directory where your build.sh is located. 19 | 20 | Modifying the Skelenton Files 21 | 22 | The ../etc/skel directory contains skeleton template files which are used: 23 | * To output container-specific help using "--task get-help" 24 | * To create container-specific launchers using "--task get-launcher" 25 | * To create new development directories using 'chaplocal'. 26 | 27 | At a minimum, you should modify: ../etc/skel/help and ../etc/skel/launcher 28 | to reflect the needs of your container. 29 | 30 | If you want to improve the experience for developers using your image, then 31 | also modify the templates in ../etc/clone. 'chaplocal' uses these files when 32 | it clones a new 'apps' directory for a developer. 33 | -------------------------------------------------------------------------------- /etc/skel/clone/build/install.sh.tpl: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd /setup 4 | 5 | # remove existing chaperone.d and startup.d from /apps so none linger 6 | rm -rf /apps; mkdir /apps 7 | 8 | # copy everything from setup to the root /apps except Dockerfile rebuild materials 9 | echo copying application files to /apps ... 10 | tar cf - --exclude Dockerfile --exclude ./build --exclude ./build.sh --exclude ./run.sh . | (cd /apps; tar xf -) 11 | 12 | # update the version information 13 | mv /setup/build/new_version.inc /apps/etc/version.inc 14 | 15 | # Add additional setup commands for your production image here, if any. 16 | # ... 17 | 18 | # Clean up and assure permissions are correct 19 | 20 | rm -rf /setup 21 | chown -R runapps: /apps # for full-container execution 22 | -------------------------------------------------------------------------------- /etc/skel/clone/run.sh.tpl: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #Developer's startup script 3 | #Created by chaplocal on %(`date`) 4 | 5 | IMAGE="%(PARENT_IMAGE)" 6 | INTERACTIVE_SHELL="/bin/bash" 7 | 8 | EXT_HOSTNAME=localhost 9 | 10 | # Uncomment to hardcode ports for startup. Command line still overrides. 11 | #PORTOPT="-p x:y -p x:y" 12 | 13 | usage() { 14 | echo "Usage: run.sh [-d] [-p port#] [-h] [extra-chaperone-options]" 15 | echo " Run $IMAGE as a daemon or interactively (the default)." 16 | echo " First available port will be remapped to $EXT_HOSTNAME if possible." 17 | exit 18 | } 19 | 20 | if [ "$CHAP_SERVICE_NAME" != "" ]; then 21 | echo run.sh should be executed on your docker host, not inside a container. 22 | exit 23 | fi 24 | 25 | cd ${0%/*} # go to directory of this file 26 | APPS=$PWD 27 | cd .. 28 | 29 | options="-t -i -e TERM=$TERM --rm=true" 30 | shellopt="/bin/bash --rcfile $APPS/bash.bashrc" 31 | 32 | while getopts ":-dp:n:" o; do 33 | case "$o" in 34 | d) 35 | options="-d" 36 | shellopt="" 37 | ;; 38 | n) 39 | options="$options --name $OPTARG" 40 | ;; 41 | p) 42 | PORTOPT="-p $OPTARG" 43 | ;; 44 | -) # first long option terminates 45 | break 46 | ;; 47 | *) 48 | usage 49 | ;; 50 | esac 51 | done 52 | shift $((OPTIND-1)) 53 | 54 | # remap ports according to the image, and tell the container about the lowest numbered 55 | # port used. 56 | 57 | if [ "$PORTOPT" == "" ]; then 58 | exposed=`docker inspect $IMAGE | sed -ne 's/^ *"\([0-9]*\)\/tcp".*$/\1/p' | sort -u` 59 | ncprog=`which nc 2>/dev/null` 60 | if [ "$exposed" != "" -a "$ncprog" != "" ]; then 61 | PORTOPT="" 62 | for PORT in $exposed; do 63 | if ! $ncprog -z $EXT_HOSTNAME $PORT; then 64 | [ "$PORTOPT" == "" ] && PORTOPT="--env CONFIG_EXT_PORT=$PORT" 65 | PORTOPT="$PORTOPT -p $PORT:$PORT" 66 | echo "Port $PORT available at $EXT_HOSTNAME:$PORT ..." 67 | fi 68 | done 69 | else 70 | if [ "$exposed" != "" ]; then 71 | echo "Note: '/bin/nc' not installed, so cannot detect port usage on this system." 72 | echo " Use '$0 -p x:y' to expose ports." 73 | fi 74 | fi 75 | fi 76 | 77 | # Run the image with this directory as our local apps dir. 78 | # Create a user with a uid/gid based upon the file permissions of the chaperone.d 79 | # directory. 80 | 81 | MOUNT=${PWD#/}; MOUNT=/${MOUNT%%/*} # extract user mountpoint 82 | SELINUX_FLAG=$(sestatus 2>/dev/null | fgrep -q enabled && echo :z) 83 | 84 | docker run $options -v $MOUNT:$MOUNT$SELINUX_FLAG $PORTOPT --env CONFIG_EXT_HOSTNAME=$EXT_HOSTNAME \ 85 | -e CONFIG_LOGGING=file -e EMACS="$EMACS" \ 86 | $IMAGE \ 87 | --create $USER:$APPS/chaperone.d --config $APPS/chaperone.d $* $shellopt 88 | -------------------------------------------------------------------------------- /etc/skel/help/HELP.tpl: -------------------------------------------------------------------------------- 1 | Help for Image: %(PARENT_IMAGE) Version %(IMAGE_VERSION) 2 | KeyBox: %(KEYBOX_VERSION) 3 | Chaperone: %(`chaperone --version | awk '/This is/{print $5}'`) 4 | Oracle Java: %(`java -version 2>&1 | sed -n -e 's|.*"\(.*\)"|\1|p'`) 5 | Linux: %(`cat /etc/issue | head -1 | sed -e 's/Welcome to //' -e 's/ \\.*$//'`) 6 | 7 | This docker image provides a fully-configurable KeyBox implementation. 8 | You can find out more about configuration and how to use this image at: 9 | 10 | https://github.com/garywiz/docker-keybox 11 | 12 | You can run a self-contained version of KeyBox by simply running this image: 13 | 14 | $ docker run -d -p 8443:8443 %(PARENT_IMAGE) 15 | 16 | When you do, you should have KeyBox running at https://localhost:8443. 17 | 18 | However, it's much better to ask the image to give you a copy of the 19 | standard launcher script: 20 | 21 | $ docker run -i --rm %(PARENT_IMAGE) --task get-launcher | sh 22 | 23 | You will then have a script called %(DEFAULT_LAUNCHER) which allows you 24 | to keep the keys, certificates, and other data in attached storage. 25 | 26 | So, if you say: 27 | mkdir docker-keybox-storage 28 | ./%(DEFAULT_LAUNCHER) -d 29 | 30 | Keybox will run as a daemon and all persistent data will be stored 31 | in the 'docker-keybox-storage' directory. 32 | 33 | The launcher is highly customizable and if you read it, you will see 34 | all relevant KeyBox configuration variables. You can also put your own 35 | SSL keys in attached storage so your KeyBox site will be served with 36 | a proper SSL certificate. 37 | -------------------------------------------------------------------------------- /etc/skel/help/LAUNCHER.tpl: -------------------------------------------------------------------------------- 1 | 2 | The %(DEFAULT_LAUNCHER) script is a quick-start for launching 3 | containers from the %(PARENT_IMAGE) image. 4 | 5 | To get this message again: 6 | docker run -i --rm %(PARENT_IMAGE) --task get-help LAUNCHER 7 | 8 | Or, for geeneral help on the image itself: 9 | docker run -i --rm %(PARENT_IMAGE) --task get-help 10 | 11 | Note: The default username is 'admin' with a password of 'changeme'. 12 | -------------------------------------------------------------------------------- /etc/skel/launcher/run-IMAGE.sh.tpl: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #Extracted from %(PARENT_IMAGE) on %(`date`) 3 | 4 | # Run as interactive: ./%(DEFAULT_LAUNCHER) [options] 5 | # or daemon: ./%(DEFAULT_LAUNCHER) -d [options] 6 | 7 | IMAGE="%(PARENT_IMAGE)" 8 | INTERACTIVE_SHELL="/bin/bash" # used if -d is not specified 9 | 10 | EXT_HOSTNAME=%(CONFIG_EXT_HOSTNAME:-localhost) 11 | EXT_PORT=8443 12 | 13 | # Number of seconds to refresh authorized_keys files. Set to 0 for no refresh. 14 | AUTHKEYS_REFRESH=120 15 | 16 | # Ping SSH servers this often as a keep alive 17 | SERVER_ALIVE_SECS=60 18 | 19 | # Enable two-factor authentication (either 'optional', 'required', 'disabled') 20 | CONFIG_OTP=optional 21 | 22 | # Enable key management. If 'false' then this is just a bastion host for shell access. 23 | ENABLE_KEY_MANAGEMENT=true 24 | 25 | # If 'true', then users will be forced to generate new SSL keys. If 'false', they can 26 | # provide their own keys by cutting-and-pasting their public key. (They can't currently 27 | # do both). 28 | FORCE_KEY_GENERATION=true 29 | 30 | # If 'true', then enable the internal auditing log 31 | ENABLE_INTERNAL_AUDIT=false 32 | # When internal auditing is enabled, this determines how many days audit entries will be kept 33 | DELETE_AUDIT_AFTER=90 34 | 35 | # LOGGING Can be set to: 36 | # stdout - all logging goes to stdout (the docker way) 37 | # file - all logging goes to a var/log/syslog.log on attached storage 38 | # syslog:host - all logging goes to the syslog host 39 | LOGGING=stdout 40 | 41 | # If you want to run over HTTP rather than HTTPD, then set EXT_SSL_HOSTNAME to "" below, and choose a 42 | # port for HTTP traffic. KeyBox does not support both. 43 | EXT_SSL_HOSTNAME=$EXT_HOSTNAME 44 | #EXT_PORT=8080 45 | 46 | # If this directory exists and is writable, then it will be used 47 | # as attached storage 48 | STORAGE_LOCATION="$PWD/%(IMAGE_BASENAME)-storage" 49 | STORAGE_USER="$USER" 50 | 51 | # Docker options 52 | 53 | OPTIONS="\ 54 | -e CONFIG_LOGGING=$LOGGING \ 55 | -e CONFIG_EXT_HOSTNAME=$EXT_HOSTNAME \ 56 | -e CONFIG_EXT_SSL_HOSTNAME=$EXT_SSL_HOSTNAME \ 57 | -e CONFIG_ENABLE_KEY_MANAGEMENT=$ENABLE_KEY_MANAGEMENT \ 58 | -e CONFIG_FORCE_KEY_GENERATION=$FORCE_KEY_GENERATION \ 59 | -e CONFIG_ENABLE_INTERNAL_AUDIT=$ENABLE_INTERNAL_AUDIT \ 60 | -e CONFIG_OTP=$CONFIG_OTP \ 61 | -e CONFIG_SERVER_ALIVE_SECS=$SERVER_ALIVE_SECS \ 62 | -e CONFIG_DELETE_AUDIT_AFTER=$DELETE_AUDIT_AFTER \ 63 | -p $EXT_PORT:8443" 64 | 65 | # The rest should be OK... 66 | 67 | if [ "$1" == '-d' ]; then 68 | shift 69 | docker_opt="-d $OPTIONS" 70 | INTERACTIVE_SHELL="" 71 | else 72 | docker_opt="-t -i -e TERM=$TERM --rm=true $OPTIONS" 73 | fi 74 | 75 | 76 | if [ "$STORAGE_LOCATION" != "" -a -d "$STORAGE_LOCATION" -a -w "$STORAGE_LOCATION" ]; then 77 | SELINUX_FLAG=$(sestatus 2>/dev/null | fgrep -q enabled && echo :z) 78 | docker_opt="$docker_opt -v $STORAGE_LOCATION:/apps/var$SELINUX_FLAG" 79 | chap_opt="--create $STORAGE_USER:/apps/var" 80 | echo Using attached storage at $STORAGE_LOCATION 81 | fi 82 | 83 | docker run $docker_opt $IMAGE $chap_opt $* $INTERACTIVE_SHELL 84 | -------------------------------------------------------------------------------- /etc/skel/shell_vars.inc: -------------------------------------------------------------------------------- 1 | # Variables commonly defined in get-* tasks, so that they can be 2 | # available in templates. See bin/get-help and bin/get-launcher. 3 | 4 | source $APPS_DIR/etc/version.inc 5 | export IMAGE_VERSION IMAGE_ARCH 6 | export PARENT_IMAGE=$IMAGE_NAME 7 | 8 | # The image name, without the prefix. 9 | if [[ "$PARENT_IMAGE" == chapdev/* ]]; then 10 | export IMAGE_BASENAME=${PARENT_IMAGE/*chapdev\/chaperone-/} # standard chapdev family naming 11 | else 12 | export IMAGE_BASENAME=${PARENT_IMAGE#*/} # others just strip off username 13 | fi 14 | 15 | # The name of the default launcher (this is what run-IMAGE.sh.tpl gets delivered as) 16 | export DEFAULT_LAUNCHER=run-$IMAGE_BASENAME.sh 17 | 18 | # Used by get-launcher turn .tpl pathnames into the basename delivered. 19 | 20 | function transform_filename() { 21 | base=$(basename $1) 22 | [ "$base" == "run-IMAGE.sh.tpl" ] && echo $DEFAULT_LAUNCHER && return 23 | echo "${base%.tpl}" 24 | } 25 | -------------------------------------------------------------------------------- /etc/ssl_vars.inc: -------------------------------------------------------------------------------- 1 | # Variables related to SSL configuration 2 | 3 | # Set up defaults 4 | 5 | export CERT_KEY=ssl-cert-${CONFIG_EXT_SSL_HOSTNAME}.key 6 | export CERT_PEM=ssl-cert-${CONFIG_EXT_SSL_HOSTNAME}.crt 7 | 8 | export KEYSTORE_PASSWORD=kbox-kstore-pw 9 | export EXPORT_PASSWORD=kbox-export-pw 10 | 11 | # Allow user override 12 | if [ -f $VAR_DIR/config/ssl_vars.inc ]; then 13 | source $VAR_DIR/config/ssl_vars.inc 14 | fi 15 | -------------------------------------------------------------------------------- /etc/ssleay.cnf.tpl: -------------------------------------------------------------------------------- 1 | # 2 | # SSLeay example configuration file. 3 | # 4 | 5 | RANDFILE = /dev/urandom 6 | 7 | [ req ] 8 | default_bits = 2048 9 | default_keyfile = privkey.pem 10 | distinguished_name = req_distinguished_name 11 | prompt = no 12 | policy = policy_anything 13 | req_extensions = v3_req 14 | x509_extensions = v3_req 15 | 16 | [ req_distinguished_name ] 17 | commonName = %(CONFIG_EXT_SSL_HOSTNAME) 18 | organizationName = KeyBox Docker Image 19 | countryName = US 20 | 21 | [ v3_req ] 22 | basicConstraints = CA:FALSE 23 | -------------------------------------------------------------------------------- /etc/start_keybox.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function relpath() { python3 -c "import os,sys;print(os.path.relpath(*(sys.argv[1:])))" "$@"; } 4 | 5 | # Assure all external storage is in /apps/var 6 | 7 | JETTY=$APPS_DIR/KeyBox-jetty/jetty 8 | VAR_KEYDB=$VAR_DIR/keydb 9 | 10 | # Assure all external storage and writable properties are in /apps/var 11 | 12 | if [ ! -d $VAR_KEYDB ]; then 13 | cp -a $APPS_DIR/etc/keydb-dist $VAR_KEYDB 14 | fi 15 | 16 | # Reprocess templates unconditionally at startup since config may have changed. 17 | 18 | source $APPS_DIR/etc/ssl_vars.inc # may be needed in these files 19 | 20 | mkdir -p $VAR_DIR/config 21 | 22 | VAR_KBCONFIG=$VAR_DIR/config/KeyBoxConfig.properties 23 | VAR_SSLXML=$VAR_DIR/config/jetty-start.ini 24 | VAR_L4JCONFIG=$VAR_DIR/config/log4j.xml 25 | 26 | tpl_envcp --overwrite $APPS_DIR/etc/KeyBoxConfig.properties.tpl $VAR_KBCONFIG 27 | tpl_envcp --overwrite $APPS_DIR/etc/jetty-start.ini.tpl $VAR_SSLXML 28 | 29 | # Check if the log4j config file exists and create if it doesn't. This 30 | # preserves any configuration that was customized in external storage. 31 | 32 | if [ ! -e $VAR_L4JCONFIG ]; then 33 | tpl_envcp --overwrite $APPS_DIR/etc/log4j.xml.tpl $VAR_L4JCONFIG 34 | fi 35 | 36 | # Now go ahead and fork off Jetty and keybox 37 | 38 | cd $APPS_DIR/KeyBox-jetty/jetty 39 | rm -f /tmp/start.properties 40 | java -Xms1024m -Xmx1024m -jar start.jar --exec-properties=/tmp/start.properties & 41 | 42 | sleep 1 43 | 44 | if [ "$(jobs)" == "" ]; then 45 | wait $! 46 | joberror=$? 47 | echo "Keybox did not start. Exit code = $joberror" 48 | exit $joberror 49 | fi 50 | 51 | while [ "$ok" != "yes" -a "$1" != "-n" ]; do 52 | checkurl="https://localhost:8443" 53 | [ "$CONFIG_EXT_SSL_HOSTNAME" == "" ] && checkurl="http://localhost:8443" 54 | if [[ "$(wget -q -t 1 --no-check-certificate -O- $checkurl)" =~ /.*loginSubmit_auth.*/ ]]; then 55 | echo "Valid login page detected -- KeyBox looks ready" 56 | ok="yes" 57 | elif [ "$ok" == "nnnnnnn" ]; then 58 | echo "KeyBox did not respond with valid results after 15 seconds." 59 | [ "$(jobs)" != "" ] && kill %1 60 | exit 1 61 | else 62 | sleep 2 63 | ok="n$ok" 64 | fi 65 | done 66 | 67 | echo PID file /tmp/keybox.pid created with PID $! 68 | echo $! >/tmp/keybox.pid 69 | disown -a 70 | -------------------------------------------------------------------------------- /etc/startup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # A quick script to initialize the system. This is not equivalent 3 | # to the classic /etc/init.d type initailization, but more of a preparation 4 | # phase so that services have what they need to run. 5 | 6 | # We publish two variables for use in startup scripts: 7 | # 8 | # CONTAINER_INIT=1 if we are initializing the container for the first time 9 | # VAR_INIT=1 if we are initializing the $VAR_DIR for the first time 10 | # 11 | # Both may be relevant, since it's possible that the $VAR_DIR may be on a mount point 12 | # so it can be reused when starting up containers which refer to it. 13 | 14 | function dolog() { logger -t startup.sh -p info $*; } 15 | function critlog() { logger -t startup.sh -p crit $*; } 16 | 17 | var_setup_file="$VAR_DIR/run/var_setup.done" 18 | cont_setup_file="/container_setup.done" 19 | 20 | export CONTAINER_INIT=0 21 | export VAR_INIT=0 22 | 23 | # Assure anything lingering that might interfere with restart is gone 24 | rm -rf /tmp/*.pid /tmp/*.sock 25 | 26 | if [ ! -f $cont_setup_file ]; then 27 | dolog "initializing container for the first time" 28 | CONTAINER_INIT=1 29 | sudo bash -c "date >$cont_setup_file" 30 | fi 31 | 32 | if [ ! -f $var_setup_file ]; then 33 | dolog "initializing $VAR_DIR for the first time" 34 | VAR_INIT=1 35 | mkdir -p $VAR_DIR >&/dev/null 36 | if [ ! -w $VAR_DIR ]; then 37 | critlog "$VAR_DIR is not writable by user '$USER' -- cannot complete set-up" 38 | exit 1 39 | fi 40 | mkdir -p $VAR_DIR/run $VAR_DIR/log 41 | chmod 777 $VAR_DIR/run $VAR_DIR/log 42 | date >$var_setup_file 43 | fi 44 | 45 | if [ -d $APPS_DIR/startup.d ]; then 46 | for sf in $( find $APPS_DIR/startup.d -type f -perm +100 \! -name '*~' | sort ); do 47 | dolog "running $sf..." 48 | $sf 49 | done 50 | fi 51 | 52 | if [ "$SECURE_ROOT" == "1" -a $CONTAINER_INIT == 1 ]; then 53 | dolog locking down root account 54 | sudo passwd -l root 55 | sudo sed '/NOPASSWD/ d' -i /etc/sudoers 56 | fi 57 | -------------------------------------------------------------------------------- /etc/update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script is used to do diff-based updates to this image based 4 | # upon the template scripts in the original chaperone-alpinejava 5 | # image. Usually, there is no reason to do this, as most of the 6 | # scripts have been highly customized, but if you really want to 7 | # get some new features that are important, this can help. 8 | 9 | # Run me from etc, I assume the main directory is above us 10 | cd .. 11 | 12 | docker run -i -t --rm -v /home:/home chapdev/chaperone-alpinejava \ 13 | --create-user $USER:$PWD/chaperone.d --config $PWD/chaperone.d \ 14 | --task apps-update /apps $PWD 15 | -------------------------------------------------------------------------------- /etc/version.inc: -------------------------------------------------------------------------------- 1 | # Standard version information from chapdev/chaperone-alpinejava, where we started out 2 | 3 | IMAGE_ARCH=U14 4 | IMAGE_VERSION=1.0.10 5 | IMAGE_NAME=garywiz/docker-keybox 6 | 7 | # The following will be updated by build/install.sh when an image is made. 8 | KEYBOX_VERSION=2.84_03 9 | 10 | export KEYBOX_VERSION # So it's available in templates 11 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #DEVELOPER's startup script - only used for container development 3 | #Created by chaplocal on Mon Aug 17 23:41:27 UTC 2015 4 | 5 | IMAGE="garywiz/docker-keybox" 6 | INTERACTIVE_SHELL="/bin/bash" 7 | 8 | # Uncomment to include port settings 9 | #PORTOPT="-p x:y" 10 | 11 | EXT_HOSTNAME=localhost 12 | 13 | usage() { 14 | echo "Usage: run.sh [-d] [-p port#] [-h] [extra-chaperone-options]" 15 | echo " Run $IMAGE as a daemon or interactively (the default)." 16 | echo " First available port will be remapped to $EXT_HOSTNAME if possible." 17 | exit 18 | } 19 | 20 | if [ "$CHAP_SERVICE_NAME" != "" ]; then 21 | echo run.sh should be executed on your docker host, not inside a container. 22 | exit 23 | fi 24 | 25 | cd ${0%/*} # go to directory of this file 26 | APPS=$PWD 27 | cd .. 28 | 29 | options="-t -i -e TERM=$TERM --rm=true" 30 | shellopt="/bin/bash --rcfile $APPS/bash.bashrc" 31 | 32 | while getopts ":-dp:" o; do 33 | case "$o" in 34 | d) 35 | options="-d" 36 | shellopt="" 37 | ;; 38 | p) 39 | PORTOPT="-p $OPTARG" 40 | ;; 41 | -) # first long option terminates 42 | break 43 | ;; 44 | *) 45 | usage 46 | ;; 47 | esac 48 | done 49 | shift $((OPTIND-1)) 50 | 51 | # remap ports according to the image, and tell the container about the lowest numbered 52 | # port used. 53 | 54 | if [ "$PORTOPT" == "" ]; then 55 | exposed=`docker inspect $IMAGE | sed -ne 's/^ *"\([0-9]*\)\/tcp".*$/\1/p' | sort -u` 56 | ncprog=`which nc` 57 | if [ "$exposed" != "" -a "$ncprog" != "" ]; then 58 | PORTOPT="" 59 | for PORT in $exposed; do 60 | if ! $ncprog -z $EXT_HOSTNAME $PORT; then 61 | [ "$PORTOPT" == "" ] && PORTOPT="--env CONFIG_EXT_PORT=$PORT" 62 | PORTOPT="$PORTOPT -p $PORT:$PORT" 63 | echo "Port $PORT available at $EXT_HOSTNAME:$PORT ..." 64 | fi 65 | done 66 | else 67 | if [ "$exposed" != "" ]; then 68 | echo "Note: '/bin/nc' not installed, so cannot detect port usage on this system." 69 | echo " Use '$0 -p x:y' to expose ports." 70 | fi 71 | fi 72 | fi 73 | 74 | # Run the image with this directory as our local apps dir. 75 | # Create a user with a uid/gid based upon the file permissions of the chaperone.d 76 | # directory. 77 | 78 | MOUNT=${PWD#/}; MOUNT=/${MOUNT%%/*} # extract user mountpoint 79 | docker run $options -v $MOUNT:$MOUNT $PORTOPT -e CONFIG_EXT_HOSTNAME=$EXT_HOSTNAME -e CONFIG_LOGGING=file \ 80 | -e EMACS=$EMACS \ 81 | $IMAGE \ 82 | --create $USER:$APPS/chaperone.d --config $APPS/chaperone.d $* $shellopt 83 | -------------------------------------------------------------------------------- /startup.d/150-ssl-keygen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Takes care of setting up SSL by generating a snakeoil key (self-signed) whenever 4 | # CONFIG_EXT_SSL_HOSTNAME is defined. You'll need to reconfigure your webserver with 5 | # actual keys if you want to serve https properly. 6 | 7 | # Only if we have CONFIG_EXT_SSL_HOSTNAME... 8 | 9 | if [ "$CONFIG_EXT_SSL_HOSTNAME" != "" ]; then 10 | # Generate self-signed certs if they aren't here. 11 | 12 | certpem=$VAR_DIR/certs/ssl-cert-$CONFIG_EXT_SSL_HOSTNAME.crt 13 | certkey=$VAR_DIR/certs/ssl-cert-$CONFIG_EXT_SSL_HOSTNAME.key 14 | 15 | if [ ! -f $certpem ]; then 16 | template="$APPS_DIR/etc/ssleay.cnf.tpl" 17 | 18 | # # should be a less common char 19 | # problem is that openssl virtually accepts everything and we need to 20 | # sacrifice one char. 21 | 22 | TMPFILE="$(mktemp)" || exit 1 23 | 24 | tpl_envcp --overwrite $template $TMPFILE 25 | 26 | # create the certificate. 27 | 28 | mkdir -p $VAR_DIR/certs 29 | 30 | openssl req -config $TMPFILE -new -x509 -days 3650 -nodes -out $certpem -keyout $certkey 31 | 32 | chmod 644 $certpem 33 | chmod 640 $certkey 34 | 35 | #rm -rf $TMPFILE 36 | fi 37 | 38 | fi 39 | 40 | -------------------------------------------------------------------------------- /startup.d/180-jetty-ssl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This creates a Jetty keystore by importing the standard PEM certificates and keys 3 | 4 | if [ "$CONFIG_EXT_SSL_HOSTNAME" == "" ]; then 5 | exit 6 | fi 7 | 8 | keystore_path=$VAR_DIR/certs/jetty.keystore 9 | 10 | [ -f $keystore_path ] && exit 11 | 12 | source $APPS_DIR/etc/ssl_vars.inc 13 | 14 | cert_path=$VAR_DIR/certs/$CERT_PEM 15 | cert_key=$VAR_DIR/certs/$CERT_KEY 16 | 17 | if [ ! -f $cert_path -o ! -f $cert_key ]; then 18 | echo ERROR: Either "$cert_path" or "$cert_key" seem to be missing - no SSL keys packaged 19 | exit 1 20 | fi 21 | 22 | # First, package up both certs 23 | cd $VAR_DIR/certs 24 | openssl pkcs12 -inkey $cert_key -in $cert_path -export -out $cert_path.pkcs12 -passout "pass:$EXPORT_PASSWORD" 25 | 26 | # Then create a new keystore 27 | echo -e "$KEYSTORE_PASSWORD\n$KEYSTORE_PASSWORD\n$EXPORT_PASSWORD\n" | \ 28 | $JAVA_HOME/bin/keytool -importkeystore -srckeystore $cert_path.pkcs12 -srcstoretype PKCS12 -destkeystore $keystore_path 29 | 30 | # Get rid of PKCS12 package 31 | rm $cert_path.pkcs12 32 | -------------------------------------------------------------------------------- /startup.d/README: -------------------------------------------------------------------------------- 1 | Files in this directory are executed upon container startup by the ../etc/startup.sh script. 2 | 3 | There are two modes: 4 | 5 | 1. When the container is first set up, CONTAINER_INIT=="1" and the script can use 'su' without a 6 | password. This is so that any setup activities can be performed which require full access 7 | to the system. 8 | 9 | 2. On subsequent boots (if the container is stopped and started), the same scripts will be 10 | run with CONTAINER_INIT=="0". However, root access is locked down if env var SECURE_ROOT=1. 11 | 12 | Note that SECURE_ROOT is not defined by default. 13 | 14 | In all cases, scripts are run as either root, or the user specified by --user on the 15 | chaperone command line. 16 | --------------------------------------------------------------------------------