├── UNLICENSE ├── README.org └── chrome-private.sh /UNLICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | [[https://unlicense.org/][https://img.shields.io/badge/license-Unlicense-blue.svg]] 2 | 3 | ~chrome-private.sh~ is a shell wrapper that simplifies using Google Chrome ephemerally. 4 | 5 | The core concept is the disposable profile that is created on demand and (optionally) 6 | discarded on exit. Executing Chrome through the wrapper enables clear segmentation 7 | and isolation of browsing sessions into directories that can be copied / moved / 8 | kept around / deleted when no longer required. 9 | 10 | Additionally, profiles can be preconfigured with user settings / 3rd party 11 | extensions and used as templates. 12 | 13 | I initially created ~chrome-private.sh~ in order to get some semblance of 14 | control over my browsing habits and be able to better manage the enormous 15 | volume of information I found myself exposed to every day. Now, it has 16 | become invaluable as it encourages me to set clear limits, while 17 | conveniently keeping all state related to a specific area of research 18 | isolated and also speeding up experimenting with widely-disparate 19 | configurations and ways of browsing. 20 | 21 | * Features 22 | 23 | + Disables caches, Flash, +referrers+ 24 | 25 | + Disables 3D APIs / WebGL, GPU acceleration by default while allowing them to be 26 | re-enabled through command-line switches. 27 | 28 | + Enables user-specified proxy and incognito mode through command-line switches 29 | 30 | Disabling referrers through a command-line switch seems to no longer do anything 31 | in Chrome, even though the switch is recognized. Use an extension like uMatrix. 32 | 33 | * Usage 34 | First, make sure CHROME variable is correct. The default value should be ok on 35 | Linux. You can set it directly in the script itself or in ~$HOME/.chrome-private.rc~. 36 | If you are on macOS, you will need GNU mktemp (~gmktemp~) which you can get by 37 | installing coreutils from MacPorts or Homebrew. You will also need to set CHROME 38 | to the full path of the ~Google Chrome.app~ (or canary) binary. Example: 39 | 40 | #+BEGIN_SRC shell-script 41 | CHROME="${HOME}/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" 42 | #+END_SRC 43 | 44 | If ~RAMDISK~ is set, it will be used instead of ~TMP~ (highly recommended). 45 | 46 | Sample session: 47 | 48 | #+BEGIN_SRC shell-script 49 | $ chrome-private.sh --keep --name core_profile 50 | Using new profile dir: core_profile 51 | Chrome PID: 64832 52 | 53 | # Proceed to configure the browser and install extensions. 54 | # uBlock Origin and uMatrix are highly recommended. 55 | # 56 | # After quitting the browser, core_profile/ can be moved / copied 57 | # and used as a root profile (template) for subsequent ephemeral 58 | # profiles. It can be set as such in ~/.chrome-private.rc 59 | # (ROOT_PROFILE_DIR) or passed to the script as an argument 60 | # (--root-profile). 61 | 62 | $ echo ROOT_PROFILE_DIR="~/core_profile" >> ~/.chrome-private.rc 63 | 64 | # Create a new ephemeral profile that will be auto-deleted on exit 65 | # It will be an exact clone of the root profile, with the same 66 | # configuration and installed extensions. Changes to this profile 67 | # will not affect the root. 68 | 69 | $ chrome-private.sh --delete 70 | Copying reference profile from: /Users/xristos/core_profile to: /tmp/chrome.priv.prof_biG9RWIIUB 71 | Chrome PID: 65011 72 | 73 | # Quit or CTRL-C browser 74 | 75 | Deleted: /tmp/chrome.priv.prof_biG9RWIIUB 76 | 77 | # If you don't pass --delete, the ephemeral profile directory will be renamed 78 | # to {name}.deleted on exit rather than deleted. If you pass --keep the profile 79 | # directory will neither be renamed nor deleted. 80 | 81 | $ chrome-private.sh 82 | 83 | Copying reference profile from: /Users/xristos/core_profile to: /tmp/chrome.priv.prof_paoMTlHzaw 84 | Chrome PID: 65075 85 | Renamed: /tmp/chrome.priv.prof_paoMTlHzaw 86 | 87 | # 88 | # A previous session can be resumed by using --profile 89 | # 90 | 91 | $ chrome-private.sh --profile chrome.priv.prof_paoMTlHzaw 92 | 93 | Using existing profile dir: chrome.priv.prof_paoMTlHzaw 94 | Chrome PID: 65184 95 | 96 | # Tabs that were open in that session can be restored in one go through 97 | # the History menu. A profile directory that is directly specified through 98 | # the --profile argument will never be renamed / deleted on exit. 99 | #+END_SRC 100 | 101 | * License 102 | This is free and unencumbered software released into the public domain. 103 | 104 | * Author 105 | xristos (AT) sdf (DOT) org 106 | -------------------------------------------------------------------------------- /chrome-private.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # This script will spawn a new Chrome process restricted to a profile that 4 | # is created on demand and (optionally) deleted on exit. If a root profile 5 | # directory is given, it will be used as the template for the newly created 6 | # profile. 7 | # 8 | # Requirements: 9 | # 10 | # GNU coreutils 11 | # 12 | # NOTES: 13 | # 14 | # Chrome spawning takes place irrespective of any currently running Chrome 15 | # processes. The script will always spawn a new Chrome process. 16 | # 17 | # If a root profile directory is not configured/passed-in as an argument, the 18 | # created profile will be a fresh, first-time profile that does not contain 19 | # any configuration data or extensions that may be present in a different 20 | # profile for the same installed Chrome browser. 21 | # 22 | # To create a root profile, execute `chrome-private.sh --name name --keep', 23 | # and after installing extensions and modifying browser configuration, you 24 | # can copy/rename its directory and set it as ROOT_PROFILE_DIR (or pass it 25 | # as an argument with --root-profile). 26 | # 27 | # This is free and unencumbered software released into the public domain. 28 | # xristos@sdf.org 29 | 30 | set -em 31 | 32 | 33 | # 34 | # Configuration 35 | # 36 | 37 | 38 | CONFIG=~/.chrome-private.rc 39 | 40 | if [ -f "${CONFIG}" ]; then 41 | source "${CONFIG}" 42 | fi 43 | 44 | # All of the following user configuration variables can be set in 45 | # ~/.chrome-private.rc 46 | 47 | : "${CHROME:=google-chrome}" 48 | : "${TMP:=/tmp}" 49 | 50 | # A root chrome profile directory, if it exists it will be used as a template 51 | : "${ROOT_PROFILE_DIR:=}" 52 | 53 | # GNU mktemp is required, set this to gmktemp (coreutils) on macOS 54 | : "${MKTEMP:=mktemp}" 55 | : "${PROFILE_MKTEMP:=chrome.priv.prof_XXXXXXXXXX}" 56 | 57 | : "${PROXY=socks5://127.0.0.1:5060}" 58 | 59 | # Using a ramdisk rather than TMP is a good idea, but doubly so if you're going 60 | # to go heavy on number of disposable profiles. hdiutil can be used to create 61 | # a ramdisk on macOS. 62 | : "${RAMDISK:=}" 63 | 64 | # Remote debugging port 65 | : "${DEBUG_PORT:=}" 66 | 67 | 68 | # 69 | # End of user configuration 70 | # 71 | 72 | 73 | CHROME_ARGS=(--disable-bundled-ppapi-flash --disable-offline-load-stale-cache 74 | --disk-cache-size=1 --media-cache-size=1 --disk-cache-dir=/dev/null 75 | --no-first-run --no-referrers --save-page-as-mhtml --no-default-browser-check) 76 | 77 | CHROME_NO_GPU_ARGS=(--disable-gpu) 78 | CHROME_NO_3D_ARGS=(--disable-3d-apis --disable-webgl) 79 | 80 | keep=0 81 | delete=0 82 | incognito=0 83 | use_gpu=0 84 | use_3d=0 85 | use_proxy=0 86 | profile= 87 | profile_name= 88 | chrome_pid=0 89 | color=0 90 | start_color="\e[1;34m" 91 | end_color="\e[0m" 92 | basename="$(basename "${ROOT_PROFILE_DIR}")" 93 | 94 | if [ -t 1 ]; then 95 | color=1 96 | fi 97 | 98 | 99 | # 100 | # Functions 101 | # 102 | 103 | 104 | function usage { 105 | echo "Usage: $0 [--name name] [--temp-name (${PROFILE_MKTEMP})] [--keep] [--delete] [--gpu] [--3d] [--incognito] [--chrome-bin executable] [--root-profile dir] [--profile dir] [--port port] [--proxy (${PROXY})] -- chrome-arguments" 106 | echo -e " --name name of created profile directory" 107 | echo -e " this will override --temp-name if given" 108 | echo -e " --temp-name this will be passed to mktemp(1) to generate a" 109 | echo -e " disposable profile directory name" 110 | echo 111 | echo -e " --keep do not delete/rename profile directory on exit" 112 | echo -e " (implied with --profile)" 113 | echo -e " --delete delete rather than rename profile directory" 114 | echo -e " (ignored with --keep / --profile)" 115 | echo 116 | echo -e " --gpu enable gpu acceleration" 117 | echo -e " --3d enable WebGL / 3D APIs" 118 | echo 119 | echo -e " --incognito start Chrome in incognito mode" 120 | echo -e " --chrome-bin use given Chrome executable" 121 | echo -e " --root-profile use given directory as root profile directory" 122 | echo -e " --profile dir use given directory as profile directory (needs to exist)" 123 | echo -e " (--root-profile, --name and --temp-name are ignored)" 124 | echo -e " --proxy if given, Chrome will use a proxy (default or specified)" 125 | echo -e " --port use given port as remote debugging port" 126 | echo 127 | } 128 | 129 | function msg { 130 | if [ "${color}" -eq 1 ]; then 131 | printf "${start_color}$1${end_color}\n" 132 | else 133 | printf "%s\n" "$1" 134 | fi 135 | } 136 | 137 | function err { 138 | echo "Error: $*" >&2 139 | } 140 | 141 | function setup { 142 | # Optional, executed first when --profile is not given. 143 | # Currently used to setup the ram disk (if configured). 144 | if [ -d "${RAMDISK}" ]; then 145 | if [ -d "${ROOT_PROFILE_DIR}" ]; then 146 | if [ ! -d "${RAMDISK}/${basename}" ]; then 147 | # Copy the root profile to ramdisk 148 | msg "Copying reference profile from: ${ROOT_PROFILE_DIR} to: ${RAMDISK}/${basename}" 149 | cp -R "${ROOT_PROFILE_DIR}/." "${RAMDISK}/${basename}" 150 | fi 151 | ROOT_PROFILE_DIR="${RAMDISK}/${basename}" 152 | fi 153 | TMP="${RAMDISK}" 154 | fi 155 | } 156 | 157 | function cleanup { 158 | if [ -d "${profile}" ]; then 159 | if [ "${keep}" -eq 0 ]; then 160 | if [ "${delete}" -eq 0 ]; then 161 | mv "${profile}" "${profile}.deleted" 162 | msg "Renamed: ${profile}" 163 | else 164 | rm -rf "${profile}" && msg "Deleted: ${profile}" 165 | fi 166 | fi 167 | else 168 | err "Directory ${profile} does not exist" 169 | fi 170 | } 171 | 172 | function interrupt { 173 | if [ "${chrome_pid}" -ne 0 ] && ps "${chrome_pid}" &>/dev/null; then 174 | kill "${chrome_pid}" 175 | wait 176 | fi 177 | } 178 | 179 | 180 | # 181 | # Argument parsing 182 | # 183 | 184 | 185 | while :; do 186 | case "$1" in 187 | -h|--help) 188 | usage 189 | exit 0 190 | ;; 191 | --keep) 192 | keep=1 193 | ;; 194 | --delete) 195 | delete=1 196 | ;; 197 | --gpu) 198 | use_gpu=1 199 | ;; 200 | --3d) 201 | use_3d=1 202 | ;; 203 | --incognito) 204 | incognito=1 205 | ;; 206 | --name) 207 | if [ "$2" ] && [[ "$2" != --* ]]; then 208 | profile_name="$2" 209 | shift 210 | else 211 | err "--name requires an argument" 212 | exit 1 213 | fi 214 | ;; 215 | --temp-name) 216 | if [ "$2" ] && [[ "$2" != --* ]]; then 217 | PROFILE_MKTEMP="$2" 218 | shift 219 | else 220 | err "--temp-name requires an argument" 221 | exit 1 222 | fi 223 | ;; 224 | --chrome-bin) 225 | if [ "$2" ] && [[ "$2" != --* ]]; then 226 | CHROME="$2" 227 | shift 228 | else 229 | err "--chrome-bin requires an executable" 230 | exit 1 231 | fi 232 | ;; 233 | --root-profile) 234 | if [ "$2" ] && [[ "$2" != --* ]]; then 235 | ROOT_PROFILE_DIR="$2" 236 | basename="$(basename "${ROOT_PROFILE_DIR}")" 237 | shift 238 | else 239 | err "--root-profile requires a profile directory" 240 | exit 1 241 | fi 242 | ;; 243 | --profile) 244 | if [ "$2" ] && [[ "$2" != --* ]]; then 245 | profile="$2" 246 | shift 247 | else 248 | err "--profile requires a profile directory" 249 | exit 1 250 | fi 251 | ;; 252 | --proxy) 253 | use_proxy=1 254 | if [ "$2" ] && [[ "$2" != --* ]]; then 255 | PROXY="$2" 256 | shift 257 | fi 258 | ;; 259 | --port) 260 | if [ "$2" ] && [[ "$2" != --* ]]; then 261 | DEBUG_PORT="$2" 262 | shift 263 | fi 264 | ;; 265 | --) 266 | shift 267 | break 268 | ;; 269 | -?*) 270 | err "Unknown option: $1" 271 | exit 1 272 | ;; 273 | *) 274 | break 275 | esac 276 | shift 277 | done 278 | 279 | 280 | # 281 | # End of argument parsing 282 | # 283 | 284 | 285 | builtin type -P "${CHROME}" &>/dev/null || { err "${CHROME}" not found; exit 1; } 286 | 287 | if [ "${profile}" ]; then 288 | keep=1 289 | if [ -d "${profile}" ]; then 290 | msg "Using existing profile dir: ${profile}" 291 | else 292 | err "Directory ${profile} does not exist" 293 | exit 1 294 | fi 295 | else 296 | setup 297 | if [ "${profile_name}" ]; then 298 | profile="${profile_name}" 299 | else 300 | msg "Creating temporary profile in ${TMP}" 301 | profile="$("${MKTEMP}" -d -p "${TMP}" "${PROFILE_MKTEMP}")" 302 | fi 303 | 304 | if [ -d "${ROOT_PROFILE_DIR}" ]; then 305 | msg "Copying reference profile from: ${ROOT_PROFILE_DIR} to: ${profile}" 306 | cp -R "${ROOT_PROFILE_DIR}/." "${profile}" 307 | else 308 | msg "Using new profile dir: ${profile}" 309 | fi 310 | fi 311 | 312 | MY_ARGS=(--user-data-dir="${profile}") 313 | 314 | if [ "${use_proxy}" -eq 1 ]; then 315 | msg "Using proxy: ${PROXY}" 316 | MY_ARGS+=(--proxy-server="${PROXY}") 317 | fi 318 | 319 | if [ "${incognito}" -eq 1 ]; then 320 | msg "Incognito mode enabled" 321 | 322 | MY_ARGS+=(--incognito) 323 | fi 324 | 325 | if [ "${use_gpu}" -eq 0 ]; then 326 | CHROME_ARGS=("${CHROME_ARGS[@]}" "${CHROME_NO_GPU_ARGS[@]}") 327 | else 328 | msg "GPU enabled" 329 | fi 330 | 331 | if [ "${use_3d}" -eq 0 ]; then 332 | CHROME_ARGS=("${CHROME_ARGS[@]}" "${CHROME_NO_3D_ARGS[@]}") 333 | else 334 | msg "3D APIs enabled" 335 | fi 336 | 337 | if [ "${DEBUG_PORT}" ]; then 338 | MY_ARGS+=(--remote-debugging-port="${DEBUG_PORT}") 339 | msg "Remote debugging port: ${DEBUG_PORT}" 340 | fi 341 | 342 | 343 | # 344 | # Execution 345 | # 346 | 347 | 348 | trap cleanup EXIT 349 | trap interrupt INT 350 | 351 | "${CHROME}" "${CHROME_ARGS[@]}" "${MY_ARGS[@]}" "$@" 2>/dev/null & 352 | 353 | 354 | # 355 | # Post-execution 356 | # 357 | 358 | 359 | chrome_pid=$! 360 | msg "Chrome PID: ${chrome_pid}" 361 | 362 | wait 363 | --------------------------------------------------------------------------------