github.com/crowdsecurity/crowdsec@v1.6.1/docker/docker_start.sh (about)

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