docker_start.sh 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. #!/bin/bash
  2. # shellcheck disable=SC2292 # allow [ test ] syntax
  3. # shellcheck disable=SC2310 # allow "if function..." syntax with -e
  4. set -e
  5. shopt -s inherit_errexit
  6. # match true, TRUE, True, tRuE, etc.
  7. istrue() {
  8. case "$(echo "$1" | tr '[:upper:]' '[:lower:]')" in
  9. true) return 0 ;;
  10. *) return 1 ;;
  11. esac
  12. }
  13. isfalse() {
  14. if istrue "$1"; then
  15. return 1
  16. else
  17. return 0
  18. fi
  19. }
  20. if istrue "$DEBUG"; then
  21. set -x
  22. export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
  23. fi
  24. if istrue "$CI_TESTING"; then
  25. echo "githubciXXXXXXXXXXXXXXXXXXXXXXXX" >/etc/machine-id
  26. fi
  27. #- DEFAULTS -----------------------#
  28. export CONFIG_FILE="${CONFIG_FILE:=/etc/crowdsec/config.yaml}"
  29. export CUSTOM_HOSTNAME="${CUSTOM_HOSTNAME:=localhost}"
  30. #- HELPER FUNCTIONS ----------------#
  31. # csv2yaml <string>
  32. # generate a yaml list from a comma-separated string of values
  33. csv2yaml() {
  34. [ -z "$1" ] && return
  35. echo "$1" | sed 's/,/\n- /g;s/^/- /g'
  36. }
  37. # wrap cscli with the correct config file location
  38. cscli() {
  39. command cscli -c "$CONFIG_FILE" "$@"
  40. }
  41. # conf_get <key> [file_path]
  42. # retrieve a value from a file (by default $CONFIG_FILE)
  43. conf_get() {
  44. if [ $# -ge 2 ]; then
  45. yq e "$1" "$2"
  46. else
  47. yq e "$1" "$CONFIG_FILE"
  48. fi
  49. }
  50. # conf_set <yq_expression> [file_path]
  51. # evaluate a yq command (by default on $CONFIG_FILE),
  52. # create the file if it doesn't exist
  53. conf_set() {
  54. if [ $# -ge 2 ]; then
  55. YAML_FILE="$2"
  56. else
  57. YAML_FILE="$CONFIG_FILE"
  58. fi
  59. if [ ! -f "$YAML_FILE" ]; then
  60. install -m 0600 /dev/null "$YAML_FILE"
  61. fi
  62. yq e "$1" -i "$YAML_FILE"
  63. }
  64. # conf_set_if(): used to update the configuration
  65. # only if a given variable is provided
  66. # conf_set_if "$VAR" <yq_expression> [file_path]
  67. conf_set_if() {
  68. if [ "$1" != "" ]; then
  69. shift
  70. conf_set "$@"
  71. fi
  72. }
  73. # register_bouncer <bouncer_name> <bouncer_key>
  74. register_bouncer() {
  75. if ! cscli bouncers list -o json | sed '/^ *"name"/!d;s/^ *"name": "\(.*\)",/\1/' | grep -q "^${1}$"; then
  76. if cscli bouncers add "$1" -k "$2" > /dev/null; then
  77. echo "Registered bouncer for $1"
  78. else
  79. echo "Failed to register bouncer for $1"
  80. fi
  81. fi
  82. }
  83. # Call cscli to manage objects ignoring taint errors
  84. # $1 can be collections, parsers, etc.
  85. # $2 can be install, remove, upgrade
  86. # $3 is a list of object names separated by space
  87. cscli_if_clean() {
  88. # loop over all objects
  89. for obj in $3; do
  90. if cscli "$1" inspect "$obj" -o json | yq -e '.tainted // false' >/dev/null 2>&1; then
  91. echo "Object $1/$obj is tainted, skipping"
  92. else
  93. # # Too verbose? Only show errors if not in debug mode
  94. # if [ "$DEBUG" != "true" ]; then
  95. # error_only=--error
  96. # fi
  97. error_only=""
  98. echo "Running: cscli $error_only $1 $2 \"$obj\""
  99. # shellcheck disable=SC2086
  100. cscli $error_only "$1" "$2" "$obj"
  101. fi
  102. done
  103. }
  104. # Output the difference between two lists
  105. # of items separated by spaces
  106. difference() {
  107. list1="$1"
  108. list2="$2"
  109. # split into words
  110. # shellcheck disable=SC2086
  111. set -- $list1
  112. for item in "$@"; do
  113. found=false
  114. for i in $list2; do
  115. if [ "$item" = "$i" ]; then
  116. found=true
  117. break
  118. fi
  119. done
  120. if [ "$found" = false ]; then
  121. echo "$item"
  122. fi
  123. done
  124. }
  125. #-----------------------------------#
  126. if [ -n "$CERT_FILE" ] || [ -n "$KEY_FILE" ] ; then
  127. printf '%b' '\033[0;33m'
  128. echo "Warning: the variables CERT_FILE and KEY_FILE have been deprecated." >&2
  129. echo "Please use LAPI_CERT_FILE and LAPI_KEY_FILE insted." >&2
  130. echo "The old variables will be removed in a future release." >&2
  131. printf '%b' '\033[0m'
  132. export LAPI_CERT_FILE=${LAPI_CERT_FILE:-$CERT_FILE}
  133. export LAPI_KEY_FILE=${LAPI_KEY_FILE:-$KEY_FILE}
  134. fi
  135. # Check and prestage databases
  136. for geodb in GeoLite2-ASN.mmdb GeoLite2-City.mmdb; do
  137. # We keep the pre-populated geoip databases in /staging instead of /var,
  138. # because if the data directory is bind-mounted from the host, it will be
  139. # empty and the files will be out of reach, requiring a runtime download.
  140. # We link to them to save about 80Mb compared to cp/mv.
  141. if [ ! -e "/var/lib/crowdsec/data/$geodb" ] && [ -e "/staging/var/lib/crowdsec/data/$geodb" ]; then
  142. mkdir -p /var/lib/crowdsec/data
  143. ln -s "/staging/var/lib/crowdsec/data/$geodb" /var/lib/crowdsec/data/
  144. fi
  145. done
  146. # Check and prestage /etc/crowdsec
  147. if [ ! -e "/etc/crowdsec/local_api_credentials.yaml" ] && [ ! -e "/etc/crowdsec/config.yaml" ]; then
  148. echo "Populating configuration directory..."
  149. # don't overwrite existing configuration files, which may come
  150. # from bind-mount or even be read-only (configmaps)
  151. if [ -e /staging/etc/crowdsec ]; then
  152. mkdir -p /etc/crowdsec/
  153. # if you change this, check that it still works
  154. # under alpine and k8s, with and without tls
  155. cp -an /staging/etc/crowdsec/* /etc/crowdsec/
  156. fi
  157. fi
  158. # do this as soon as we have a config.yaml, to avoid useless warnings
  159. if istrue "$USE_WAL"; then
  160. conf_set '.db_config.use_wal = true'
  161. elif [ -n "$USE_WAL" ] && isfalse "$USE_WAL"; then
  162. conf_set '.db_config.use_wal = false'
  163. fi
  164. lapi_credentials_path=$(conf_get '.api.client.credentials_path')
  165. if isfalse "$DISABLE_LOCAL_API"; then
  166. # generate local agent credentials (even if agent is disabled, cscli needs a
  167. # connection to the API)
  168. if ( isfalse "$USE_TLS" || [ "$CLIENT_CERT_FILE" = "" ] ); then
  169. if yq -e '.login==strenv(CUSTOM_HOSTNAME)' "$lapi_credentials_path" && ( cscli machines list -o json | yq -e 'any_c(.machineId==strenv(CUSTOM_HOSTNAME))' >/dev/null ); then
  170. echo "Local agent already registered"
  171. else
  172. echo "Generate local agent credentials"
  173. # if the db is persistent but the credentials are not, we need to
  174. # delete the old machine to generate new credentials
  175. cscli machines delete "$CUSTOM_HOSTNAME" >/dev/null 2>&1 || true
  176. cscli machines add "$CUSTOM_HOSTNAME" --auto
  177. fi
  178. fi
  179. echo "Check if lapi needs to register an additional agent"
  180. # pre-registration is not needed with TLS authentication, but we can have TLS transport with user/pw
  181. if [ "$AGENT_USERNAME" != "" ] && [ "$AGENT_PASSWORD" != "" ] ; then
  182. # re-register because pw may have been changed
  183. cscli machines add "$AGENT_USERNAME" --password "$AGENT_PASSWORD" -f /dev/null --force
  184. echo "Agent registered to lapi"
  185. fi
  186. fi
  187. # ----------------
  188. conf_set_if "$LOCAL_API_URL" '.url = strenv(LOCAL_API_URL)' "$lapi_credentials_path"
  189. if istrue "$DISABLE_LOCAL_API"; then
  190. # we only use the envvars that are actually defined
  191. # in case of persistent configuration
  192. conf_set_if "$AGENT_USERNAME" '.login = strenv(AGENT_USERNAME)' "$lapi_credentials_path"
  193. conf_set_if "$AGENT_PASSWORD" '.password = strenv(AGENT_PASSWORD)' "$lapi_credentials_path"
  194. fi
  195. conf_set_if "$INSECURE_SKIP_VERIFY" '.api.client.insecure_skip_verify = env(INSECURE_SKIP_VERIFY)'
  196. # agent-only containers still require USE_TLS
  197. if istrue "$USE_TLS"; then
  198. # shellcheck disable=SC2153
  199. conf_set_if "$CACERT_FILE" '.ca_cert_path = strenv(CACERT_FILE)' "$lapi_credentials_path"
  200. conf_set_if "$CLIENT_KEY_FILE" '.key_path = strenv(CLIENT_KEY_FILE)' "$lapi_credentials_path"
  201. conf_set_if "$CLIENT_CERT_FILE" '.cert_path = strenv(CLIENT_CERT_FILE)' "$lapi_credentials_path"
  202. else
  203. conf_set '
  204. del(.ca_cert_path) |
  205. del(.key_path) |
  206. del(.cert_path)
  207. ' "$lapi_credentials_path"
  208. fi
  209. if istrue "$DISABLE_ONLINE_API"; then
  210. conf_set 'del(.api.server.online_client)'
  211. fi
  212. # registration to online API for signal push
  213. if isfalse "$DISABLE_ONLINE_API" ; then
  214. CONFIG_DIR=$(conf_get '.config_paths.config_dir')
  215. export CONFIG_DIR
  216. config_exists=$(conf_get '.api.server.online_client | has("credentials_path")')
  217. if isfalse "$config_exists"; then
  218. conf_set '.api.server.online_client = {"credentials_path": strenv(CONFIG_DIR) + "/online_api_credentials.yaml"}'
  219. cscli capi register > "$CONFIG_DIR/online_api_credentials.yaml"
  220. echo "Registration to online API done"
  221. fi
  222. fi
  223. # Enroll instance if enroll key is provided
  224. if isfalse "$DISABLE_ONLINE_API" && [ "$ENROLL_KEY" != "" ]; then
  225. enroll_args=""
  226. if [ "$ENROLL_INSTANCE_NAME" != "" ]; then
  227. enroll_args="--name $ENROLL_INSTANCE_NAME"
  228. fi
  229. if [ "$ENROLL_TAGS" != "" ]; then
  230. # shellcheck disable=SC2086
  231. for tag in ${ENROLL_TAGS}; do
  232. enroll_args="$enroll_args --tags $tag"
  233. done
  234. fi
  235. # shellcheck disable=SC2086
  236. cscli console enroll $enroll_args "$ENROLL_KEY"
  237. fi
  238. # crowdsec sqlite database permissions
  239. if [ "$GID" != "" ]; then
  240. if istrue "$(conf_get '.db_config.type == "sqlite"')"; then
  241. chown ":$GID" "$(conf_get '.db_config.db_path')"
  242. echo "sqlite database permissions updated"
  243. fi
  244. fi
  245. # XXX only with LAPI
  246. if istrue "$USE_TLS"; then
  247. agents_allowed_yaml=$(csv2yaml "$AGENTS_ALLOWED_OU")
  248. export agents_allowed_yaml
  249. bouncers_allowed_yaml=$(csv2yaml "$BOUNCERS_ALLOWED_OU")
  250. export bouncers_allowed_yaml
  251. conf_set_if "$CACERT_FILE" '.api.server.tls.ca_cert_path = strenv(CACERT_FILE)'
  252. conf_set_if "$LAPI_CERT_FILE" '.api.server.tls.cert_file = strenv(LAPI_CERT_FILE)'
  253. conf_set_if "$LAPI_KEY_FILE" '.api.server.tls.key_file = strenv(LAPI_KEY_FILE)'
  254. conf_set_if "$BOUNCERS_ALLOWED_OU" '.api.server.tls.bouncers_allowed_ou = env(bouncers_allowed_yaml)'
  255. conf_set_if "$AGENTS_ALLOWED_OU" '.api.server.tls.agents_allowed_ou = env(agents_allowed_yaml)'
  256. else
  257. conf_set 'del(.api.server.tls)'
  258. fi
  259. conf_set_if "$PLUGIN_DIR" '.config_paths.plugin_dir = strenv(PLUGIN_DIR)'
  260. ## Install collections, parsers, scenarios & postoverflows
  261. cscli hub update
  262. cscli_if_clean collections upgrade crowdsecurity/linux
  263. cscli_if_clean parsers upgrade crowdsecurity/whitelists
  264. cscli_if_clean parsers install crowdsecurity/docker-logs
  265. cscli_if_clean parsers install crowdsecurity/cri-logs
  266. if [ "$COLLECTIONS" != "" ]; then
  267. # shellcheck disable=SC2086
  268. cscli_if_clean collections install "$(difference "$COLLECTIONS" "$DISABLE_COLLECTIONS")"
  269. fi
  270. if [ "$PARSERS" != "" ]; then
  271. # shellcheck disable=SC2086
  272. cscli_if_clean parsers install "$(difference "$PARSERS" "$DISABLE_PARSERS")"
  273. fi
  274. if [ "$SCENARIOS" != "" ]; then
  275. # shellcheck disable=SC2086
  276. cscli_if_clean scenarios install "$(difference "$SCENARIOS" "$DISABLE_SCENARIOS")"
  277. fi
  278. if [ "$POSTOVERFLOWS" != "" ]; then
  279. # shellcheck disable=SC2086
  280. cscli_if_clean postoverflows install "$(difference "$POSTOVERFLOWS" "$DISABLE_POSTOVERFLOWS")"
  281. fi
  282. ## Remove collections, parsers, scenarios & postoverflows
  283. if [ "$DISABLE_COLLECTIONS" != "" ]; then
  284. # shellcheck disable=SC2086
  285. cscli_if_clean collections remove "$DISABLE_COLLECTIONS"
  286. fi
  287. if [ "$DISABLE_PARSERS" != "" ]; then
  288. # shellcheck disable=SC2086
  289. cscli_if_clean parsers remove "$DISABLE_PARSERS"
  290. fi
  291. if [ "$DISABLE_SCENARIOS" != "" ]; then
  292. # shellcheck disable=SC2086
  293. cscli_if_clean scenarios remove "$DISABLE_SCENARIOS"
  294. fi
  295. if [ "$DISABLE_POSTOVERFLOWS" != "" ]; then
  296. # shellcheck disable=SC2086
  297. cscli_if_clean postoverflows remove "$DISABLE_POSTOVERFLOWS"
  298. fi
  299. ## Register bouncers via env
  300. for BOUNCER in $(compgen -A variable | grep -i BOUNCER_KEY); do
  301. KEY=$(printf '%s' "${!BOUNCER}")
  302. NAME=$(printf '%s' "$BOUNCER" | cut -d_ -f3-)
  303. if [[ -n $KEY ]] && [[ -n $NAME ]]; then
  304. register_bouncer "$NAME" "$KEY"
  305. fi
  306. done
  307. ## Register bouncers via secrets (Swarm only)
  308. shopt -s nullglob extglob
  309. for BOUNCER in /run/secrets/@(bouncer_key|BOUNCER_KEY)* ; do
  310. KEY=$(cat "${BOUNCER}")
  311. NAME=$(echo "${BOUNCER}" | awk -F "/" '{printf $NF}' | cut -d_ -f2-)
  312. if [[ -n $KEY ]] && [[ -n $NAME ]]; then
  313. register_bouncer "$NAME" "$KEY"
  314. fi
  315. done
  316. shopt -u nullglob extglob
  317. ARGS=""
  318. if [ "$CONFIG_FILE" != "" ]; then
  319. ARGS="-c $CONFIG_FILE"
  320. fi
  321. if [ "$DSN" != "" ]; then
  322. ARGS="$ARGS -dsn ${DSN}"
  323. fi
  324. if [ "$TYPE" != "" ]; then
  325. ARGS="$ARGS -type $TYPE"
  326. fi
  327. if istrue "$TEST_MODE"; then
  328. ARGS="$ARGS -t"
  329. fi
  330. if istrue "$DISABLE_AGENT"; then
  331. ARGS="$ARGS -no-cs"
  332. fi
  333. if istrue "$DISABLE_LOCAL_API"; then
  334. ARGS="$ARGS -no-api"
  335. fi
  336. if istrue "$LEVEL_TRACE"; then
  337. ARGS="$ARGS -trace"
  338. fi
  339. if istrue "$LEVEL_DEBUG"; then
  340. ARGS="$ARGS -debug"
  341. fi
  342. if istrue "$LEVEL_INFO"; then
  343. ARGS="$ARGS -info"
  344. fi
  345. conf_set_if "$METRICS_PORT" '.prometheus.listen_port=env(METRICS_PORT)'
  346. # shellcheck disable=SC2086
  347. exec crowdsec $ARGS