docker_start.sh 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  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. cscli "$1" "$2" "$obj"
  94. fi
  95. done
  96. }
  97. #-----------------------------------#
  98. if [ -n "$CERT_FILE" ] || [ -n "$KEY_FILE" ] ; then
  99. printf '%b' '\033[0;33m'
  100. echo "Warning: the variables CERT_FILE and KEY_FILE have been deprecated." >&2
  101. echo "Please use LAPI_CERT_FILE and LAPI_KEY_FILE insted." >&2
  102. echo "The old variables will be removed in a future release." >&2
  103. printf '%b' '\033[0m'
  104. LAPI_CERT_FILE=${LAPI_CERT_FILE:-$CERT_FILE}
  105. LAPI_KEY_FILE=${LAPI_KEY_FILE:-$KEY_FILE}
  106. fi
  107. # Check and prestage databases
  108. for geodb in GeoLite2-ASN.mmdb GeoLite2-City.mmdb; do
  109. # We keep the pre-populated geoip databases in /staging instead of /var,
  110. # because if the data directory is bind-mounted from the host, it will be
  111. # empty and the files will be out of reach, requiring a runtime download.
  112. # We link to them to save about 80Mb compared to cp/mv.
  113. if [ ! -e "/var/lib/crowdsec/data/$geodb" ] && [ -e "/staging/var/lib/crowdsec/data/$geodb" ]; then
  114. mkdir -p /var/lib/crowdsec/data
  115. ln -s "/staging/var/lib/crowdsec/data/$geodb" /var/lib/crowdsec/data/
  116. fi
  117. done
  118. # Check and prestage /etc/crowdsec
  119. if [ ! -e "/etc/crowdsec/local_api_credentials.yaml" ] && [ ! -e "/etc/crowdsec/config.yaml" ]; then
  120. echo "Populating configuration directory..."
  121. # don't overwrite existing configuration files, which may come
  122. # from bind-mount or even be read-only (configmaps)
  123. if [ -e /staging/etc/crowdsec ]; then
  124. mkdir -p /etc/crowdsec/
  125. # if you change this, check that it still works
  126. # under alpine and k8s, with and without tls
  127. cp -an /staging/etc/crowdsec/* /etc/crowdsec/
  128. fi
  129. fi
  130. # do this as soon as we have a config.yaml, to avoid useless warnings
  131. if istrue "$USE_WAL"; then
  132. conf_set '.db_config.use_wal = true'
  133. elif [ -n "$USE_WAL" ] && isfalse "$USE_WAL"; then
  134. conf_set '.db_config.use_wal = false'
  135. fi
  136. # regenerate local agent credentials (even if agent is disabled, cscli needs a
  137. # connection to the API)
  138. cscli machines delete "$CUSTOM_HOSTNAME" 2>/dev/null || true
  139. if isfalse "$DISABLE_LOCAL_API"; then
  140. if isfalse "$USE_TLS" || [ "$CLIENT_CERT_FILE" = "" ]; then
  141. echo "Regenerate local agent credentials"
  142. cscli machines add "$CUSTOM_HOSTNAME" --auto
  143. fi
  144. echo "Check if lapi needs to register an additional agent"
  145. # pre-registration is not needed with TLS authentication, but we can have TLS transport with user/pw
  146. if [ "$AGENT_USERNAME" != "" ] && [ "$AGENT_PASSWORD" != "" ] ; then
  147. # re-register because pw may have been changed
  148. cscli machines add "$AGENT_USERNAME" --password "$AGENT_PASSWORD" -f /dev/null --force
  149. echo "Agent registered to lapi"
  150. fi
  151. fi
  152. # ----------------
  153. lapi_credentials_path=$(conf_get '.api.client.credentials_path')
  154. conf_set_if "$LOCAL_API_URL" '.url = strenv(LOCAL_API_URL)' "$lapi_credentials_path"
  155. if istrue "$DISABLE_LOCAL_API"; then
  156. # we only use the envvars that are actually defined
  157. # in case of persistent configuration
  158. conf_set_if "$AGENT_USERNAME" '.login = strenv(AGENT_USERNAME)' "$lapi_credentials_path"
  159. conf_set_if "$AGENT_PASSWORD" '.password = strenv(AGENT_PASSWORD)' "$lapi_credentials_path"
  160. fi
  161. conf_set_if "$INSECURE_SKIP_VERIFY" '.api.client.insecure_skip_verify = env(INSECURE_SKIP_VERIFY)'
  162. # agent-only containers still require USE_TLS
  163. if istrue "$USE_TLS"; then
  164. # shellcheck disable=SC2153
  165. conf_set_if "$CACERT_FILE" '.ca_cert_path = strenv(CACERT_FILE)' "$lapi_credentials_path"
  166. conf_set_if "$CLIENT_KEY_FILE" '.key_path = strenv(CLIENT_KEY_FILE)' "$lapi_credentials_path"
  167. conf_set_if "$CLIENT_CERT_FILE" '.cert_path = strenv(CLIENT_CERT_FILE)' "$lapi_credentials_path"
  168. else
  169. conf_set '
  170. del(.ca_cert_path) |
  171. del(.key_path) |
  172. del(.cert_path)
  173. ' "$lapi_credentials_path"
  174. fi
  175. if istrue "$DISABLE_ONLINE_API"; then
  176. conf_set 'del(.api.server.online_client)'
  177. fi
  178. # registration to online API for signal push
  179. if isfalse "$DISABLE_ONLINE_API" ; then
  180. CONFIG_DIR=$(conf_get '.config_paths.config_dir')
  181. config_exists=$(conf_get '.api.server.online_client | has("credentials_path")')
  182. if isfalse "$config_exists"; then
  183. export CONFIG_DIR
  184. conf_set '.api.server.online_client = {"credentials_path": strenv(CONFIG_DIR) + "/online_api_credentials.yaml"}'
  185. cscli capi register > "$CONFIG_DIR/online_api_credentials.yaml"
  186. echo "Registration to online API done"
  187. fi
  188. fi
  189. # Enroll instance if enroll key is provided
  190. if isfalse "$DISABLE_ONLINE_API" && [ "$ENROLL_KEY" != "" ]; then
  191. enroll_args=""
  192. if [ "$ENROLL_INSTANCE_NAME" != "" ]; then
  193. enroll_args="--name $ENROLL_INSTANCE_NAME"
  194. fi
  195. if [ "$ENROLL_TAGS" != "" ]; then
  196. # shellcheck disable=SC2086
  197. for tag in ${ENROLL_TAGS}; do
  198. enroll_args="$enroll_args --tags $tag"
  199. done
  200. fi
  201. # shellcheck disable=SC2086
  202. cscli console enroll $enroll_args "$ENROLL_KEY"
  203. fi
  204. # crowdsec sqlite database permissions
  205. if [ "$GID" != "" ]; then
  206. if istrue "$(conf_get '.db_config.type == "sqlite"')"; then
  207. chown ":$GID" "$(conf_get '.db_config.db_path')"
  208. echo "sqlite database permissions updated"
  209. fi
  210. fi
  211. # XXX only with LAPI
  212. if istrue "$USE_TLS"; then
  213. agents_allowed_yaml=$(csv2yaml "$AGENTS_ALLOWED_OU") \
  214. bouncers_allowed_yaml=$(csv2yaml "$BOUNCERS_ALLOWED_OU") \
  215. conf_set_if "$CACERT_FILE" '.api.server.tls.ca_cert_path = strenv(CACERT_FILE)'
  216. conf_set_if "$LAPI_CERT_FILE" '.api.server.tls.cert_file = strenv(LAPI_CERT_FILE)'
  217. conf_set_if "$LAPI_KEY_FILE" '.api.server.tls.key_file = strenv(LAPI_KEY_FILE)'
  218. conf_set_if "$BOUNCERS_ALLOWED_OU" '.api.server.tls.bouncers_allowed_ou = env(bouncers_allowed_yaml)'
  219. conf_set_if "$AGENTS_ALLOWED_OU" '.api.server.tls.agents_allowed_ou = env(agents_allowed_yaml)'
  220. else
  221. conf_set 'del(.api.server.tls)'
  222. fi
  223. conf_set_if "$PLUGIN_DIR" '.config_paths.plugin_dir = strenv(PLUGIN_DIR)'
  224. ## Install collections, parsers, scenarios & postoverflows
  225. cscli hub update
  226. cscli_if_clean collections upgrade crowdsecurity/linux
  227. cscli_if_clean parsers upgrade crowdsecurity/whitelists
  228. cscli_if_clean parsers install crowdsecurity/docker-logs
  229. if [ "$COLLECTIONS" != "" ]; then
  230. # shellcheck disable=SC2086
  231. cscli_if_clean collections install "$COLLECTIONS"
  232. fi
  233. if [ "$PARSERS" != "" ]; then
  234. # shellcheck disable=SC2086
  235. cscli_if_clean parsers install "$PARSERS"
  236. fi
  237. if [ "$SCENARIOS" != "" ]; then
  238. # shellcheck disable=SC2086
  239. cscli_if_clean scenarios install "$SCENARIOS"
  240. fi
  241. if [ "$POSTOVERFLOWS" != "" ]; then
  242. # shellcheck disable=SC2086
  243. cscli_if_clean postoverflows install "$POSTOVERFLOWS"
  244. fi
  245. ## Remove collections, parsers, scenarios & postoverflows
  246. if [ "$DISABLE_COLLECTIONS" != "" ]; then
  247. # shellcheck disable=SC2086
  248. cscli_if_clean collections remove "$DISABLE_COLLECTIONS"
  249. fi
  250. if [ "$DISABLE_PARSERS" != "" ]; then
  251. # shellcheck disable=SC2086
  252. cscli_if_clean parsers remove "$DISABLE_PARSERS"
  253. fi
  254. if [ "$DISABLE_SCENARIOS" != "" ]; then
  255. # shellcheck disable=SC2086
  256. cscli_if_clean scenarios remove "$DISABLE_SCENARIOS"
  257. fi
  258. if [ "$DISABLE_POSTOVERFLOWS" != "" ]; then
  259. # shellcheck disable=SC2086
  260. cscli_if_clean postoverflows remove "$DISABLE_POSTOVERFLOWS"
  261. fi
  262. ## Register bouncers via env
  263. for BOUNCER in $(compgen -A variable | grep -i BOUNCER_KEY); do
  264. KEY=$(printf '%s' "${!BOUNCER}")
  265. NAME=$(printf '%s' "$BOUNCER" | cut -d_ -f3-)
  266. if [[ -n $KEY ]] && [[ -n $NAME ]]; then
  267. register_bouncer "$NAME" "$KEY"
  268. fi
  269. done
  270. ## Register bouncers via secrets
  271. shopt -s nullglob extglob
  272. for BOUNCER in /run/secrets/@(bouncer_key|BOUNCER_KEY)* ; do
  273. KEY=$(cat "${BOUNCER}")
  274. NAME=$(echo "${BOUNCER}" | awk -F "/" '{printf $NF}' | cut -d_ -f2-)
  275. if [[ -n $KEY ]] && [[ -n $NAME ]]; then
  276. register_bouncer "$NAME" "$KEY"
  277. fi
  278. done
  279. shopt -u nullglob extglob
  280. ARGS=""
  281. if [ "$CONFIG_FILE" != "" ]; then
  282. ARGS="-c $CONFIG_FILE"
  283. fi
  284. if [ "$DSN" != "" ]; then
  285. ARGS="$ARGS -dsn ${DSN}"
  286. fi
  287. if [ "$TYPE" != "" ]; then
  288. ARGS="$ARGS -type $TYPE"
  289. fi
  290. if istrue "$TEST_MODE"; then
  291. ARGS="$ARGS -t"
  292. fi
  293. if istrue "$DISABLE_AGENT"; then
  294. ARGS="$ARGS -no-cs"
  295. fi
  296. if istrue "$DISABLE_LOCAL_API"; then
  297. ARGS="$ARGS -no-api"
  298. fi
  299. if istrue "$LEVEL_TRACE"; then
  300. ARGS="$ARGS -trace"
  301. fi
  302. if istrue "$LEVEL_DEBUG"; then
  303. ARGS="$ARGS -debug"
  304. fi
  305. if istrue "$LEVEL_INFO"; then
  306. ARGS="$ARGS -info"
  307. fi
  308. conf_set_if "$METRICS_PORT" '.prometheus.listen_port=env(METRICS_PORT)'
  309. # shellcheck disable=SC2086
  310. exec crowdsec $ARGS