forked from wazuh/wazuh-docker
dashboard
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
WAZUH_VERSION=4.14.0
|
WAZUH_VERSION=4.14.0
|
||||||
WAZUH_IMAGE_VERSION=4.14.0
|
WAZUH_IMAGE_VERSION=4.14.0
|
||||||
WAZUH_TAG_REVISION=1
|
WAZUH_TAG_REVISION=1
|
||||||
FILEBEAT_TEMPLATE_BRANCH=4.14.0
|
FILEBEAT_TEMPLATE_BRANCH=v4.14.0
|
||||||
WAZUH_FILEBEAT_MODULE=wazuh-filebeat-0.4.tar.gz
|
WAZUH_FILEBEAT_MODULE=wazuh-filebeat-0.4.tar.gz
|
||||||
WAZUH_UI_REVISION=1
|
WAZUH_UI_REVISION=1
|
||||||
|
|||||||
@@ -20,13 +20,11 @@ RUN yum install wazuh-dashboard-${WAZUH_VERSION}-${WAZUH_TAG_REVISION} -y && \
|
|||||||
RUN mkdir -p $INSTALL_DIR/data/wazuh && chmod -R 775 $INSTALL_DIR/data/wazuh
|
RUN mkdir -p $INSTALL_DIR/data/wazuh && chmod -R 775 $INSTALL_DIR/data/wazuh
|
||||||
RUN mkdir -p $INSTALL_DIR/data/wazuh/config && chmod -R 775 $INSTALL_DIR/data/wazuh/config
|
RUN mkdir -p $INSTALL_DIR/data/wazuh/config && chmod -R 775 $INSTALL_DIR/data/wazuh/config
|
||||||
RUN mkdir -p $INSTALL_DIR/data/wazuh/logs && chmod -R 775 $INSTALL_DIR/data/wazuh/logs
|
RUN mkdir -p $INSTALL_DIR/data/wazuh/logs && chmod -R 775 $INSTALL_DIR/data/wazuh/logs
|
||||||
COPY config/wazuh.yml $INSTALL_DIR/data/wazuh/config/
|
|
||||||
RUN setcap 'cap_net_bind_service=-ep' /usr/share/wazuh-dashboard/node/bin/node
|
RUN setcap 'cap_net_bind_service=-ep' /usr/share/wazuh-dashboard/node/bin/node
|
||||||
RUN setcap 'cap_net_bind_service=-ep' /usr/share/wazuh-dashboard/node/fallback/bin/node
|
RUN setcap 'cap_net_bind_service=-ep' /usr/share/wazuh-dashboard/node/fallback/bin/node
|
||||||
|
|
||||||
# Generate certificates
|
# Generate certificates
|
||||||
COPY config/config.sh .
|
COPY config/config.sh .
|
||||||
COPY config/config.yml /
|
|
||||||
RUN bash config.sh
|
RUN bash config.sh
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
@@ -82,6 +80,7 @@ RUN chown 1000:1000 /*.sh
|
|||||||
|
|
||||||
# Copy Install dir from builder to current image
|
# Copy Install dir from builder to current image
|
||||||
COPY --from=builder --chown=1000:1000 $INSTALL_DIR $INSTALL_DIR
|
COPY --from=builder --chown=1000:1000 $INSTALL_DIR $INSTALL_DIR
|
||||||
|
COPY --from=builder --chown=1000:1000 /etc/wazuh-dashboard $INSTALL_DIR/config
|
||||||
|
|
||||||
# Create custom directory
|
# Create custom directory
|
||||||
RUN mkdir -p /usr/share/wazuh-dashboard/plugins/wazuh/public/assets/custom
|
RUN mkdir -p /usr/share/wazuh-dashboard/plugins/wazuh/public/assets/custom
|
||||||
|
|||||||
@@ -9,26 +9,67 @@ export CONFIG_DIR=${INSTALLATION_DIR}/config
|
|||||||
|
|
||||||
## Variables
|
## Variables
|
||||||
CERT_TOOL=wazuh-certs-tool.sh
|
CERT_TOOL=wazuh-certs-tool.sh
|
||||||
PACKAGES_URL=https://packages.wazuh.com/4.14/
|
CERT_CONFIG_FILE=config.yml
|
||||||
PACKAGES_DEV_URL=https://packages-dev.wazuh.com/4.14/
|
CERT_TOOL_VERSION="4.14"
|
||||||
|
PACKAGES_URL=https://packages.wazuh.com/$CERT_TOOL_VERSION/
|
||||||
|
PACKAGES_DEV_URL=https://packages-dev.wazuh.com/$CERT_TOOL_VERSION/
|
||||||
|
|
||||||
## Check if the cert tool exists in S3 buckets
|
download_package() {
|
||||||
CERT_TOOL_PACKAGES=$(curl --silent -I $PACKAGES_URL$CERT_TOOL | grep -E "^HTTP" | awk '{print $2}')
|
local url=$1
|
||||||
CERT_TOOL_PACKAGES_DEV=$(curl --silent -I $PACKAGES_DEV_URL$CERT_TOOL | grep -E "^HTTP" | awk '{print $2}')
|
local package=$2
|
||||||
|
local output=$2
|
||||||
|
echo "Checking $url$package ..."
|
||||||
|
if curl -fsL "$url$package" -o "$output"; then
|
||||||
|
echo "Downloaded $package from $url"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
## If cert tool exists in some bucket, download it, if not exit 1
|
# Download the tool to create the certificates
|
||||||
if [ "$CERT_TOOL_PACKAGES" = "200" ]; then
|
echo "Downloading the tool to create the certificates..."
|
||||||
curl -o $CERT_TOOL $PACKAGES_URL$CERT_TOOL
|
# Try first the prod URL, if it fails try the dev URL
|
||||||
echo "Cert tool exists in Packages bucket"
|
if download_package "$PACKAGES_URL" "$CERT_TOOL"; then
|
||||||
elif [ "$CERT_TOOL_PACKAGES_DEV" = "200" ]; then
|
:
|
||||||
curl -o $CERT_TOOL $PACKAGES_DEV_URL$CERT_TOOL
|
elif download_package "$PACKAGES_DEV_URL" "$CERT_TOOL"; then
|
||||||
echo "Cert tool exists in Packages-dev bucket"
|
:
|
||||||
else
|
else
|
||||||
echo "Cert tool does not exist in any bucket"
|
echo "The tool to create the certificates does not exist in any bucket"
|
||||||
|
echo "ERROR: certificates were not created"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
chmod 755 $CERT_TOOL && bash /$CERT_TOOL -A
|
# Download the config file for the certificate tool
|
||||||
|
echo "Downloading the config file for the certificate tool..."
|
||||||
|
# Try first the prod URL, if it fails try the dev URL
|
||||||
|
if download_package "$PACKAGES_URL" "$CERT_CONFIG_FILE"; then
|
||||||
|
:
|
||||||
|
elif download_package "$PACKAGES_DEV_URL" "$CERT_CONFIG_FILE"; then
|
||||||
|
:
|
||||||
|
else
|
||||||
|
echo "The config file for the certificate tool does not exist in any bucket"
|
||||||
|
echo "ERROR: certificates were not created"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
awk '
|
||||||
|
/^ dashboard:/ {dashboard=1}
|
||||||
|
/^ # Wazuh server nodes/ {dashboard=0}
|
||||||
|
dashboard && /^[[:space:]]*[^#].*name:/ {sub(/name:.*/, "name: dashboard")}
|
||||||
|
dashboard && /^[[:space:]]*[^#].*ip:/ {sub(/ip:.*/, "ip: \"127.0.0.1\"")}
|
||||||
|
|
||||||
|
{print}
|
||||||
|
' config.yml > config.yml.tmp && mv config.yml config.yml.bak && mv config.yml.tmp config.yml
|
||||||
|
|
||||||
|
sed -i \
|
||||||
|
-e 's/^ *ip: "<wazuh-manager-ip>"$/ ip: "127.0.0.1"/' \
|
||||||
|
-e 's/^ *ip: "<indexer-node-ip>"$/ ip: "127.0.0.1"/' \
|
||||||
|
config.yaml
|
||||||
|
|
||||||
|
chmod 700 "$CERT_CONFIG_FILE"
|
||||||
|
# Create the certificates
|
||||||
|
chmod 755 "$CERT_TOOL" && bash "$CERT_TOOL" -A
|
||||||
|
|
||||||
# Create certs directory
|
# Create certs directory
|
||||||
mkdir -p ${CONFIG_DIR}/certs
|
mkdir -p ${CONFIG_DIR}/certs
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
nodes:
|
|
||||||
# Wazuh dashboard server nodes
|
|
||||||
dashboard:
|
|
||||||
- name: demo.dashboard
|
|
||||||
ip: demo.dashboard
|
|
||||||
@@ -1,155 +0,0 @@
|
|||||||
---
|
|
||||||
#
|
|
||||||
# Wazuh app - App configuration file
|
|
||||||
# Copyright (C) 2017, Wazuh Inc.
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# Find more information about this on the LICENSE file.
|
|
||||||
#
|
|
||||||
# ======================== Wazuh app configuration file ========================
|
|
||||||
#
|
|
||||||
# Please check the documentation for more information on configuration options:
|
|
||||||
# https://documentation.wazuh.com/current/installation-guide/index.html
|
|
||||||
#
|
|
||||||
# Also, you can check our repository:
|
|
||||||
# https://github.com/wazuh/wazuh-dashboard-plugins
|
|
||||||
#
|
|
||||||
# ------------------------------- Index patterns -------------------------------
|
|
||||||
#
|
|
||||||
# Default index pattern to use.
|
|
||||||
#pattern: wazuh-alerts-*
|
|
||||||
#
|
|
||||||
# ----------------------------------- Checks -----------------------------------
|
|
||||||
#
|
|
||||||
# Defines which checks must to be consider by the healthcheck
|
|
||||||
# step once the Wazuh app starts. Values must to be true or false.
|
|
||||||
#checks.pattern : true
|
|
||||||
#checks.template: true
|
|
||||||
#checks.api : true
|
|
||||||
#checks.setup : true
|
|
||||||
#checks.metaFields: true
|
|
||||||
#
|
|
||||||
# --------------------------------- Extensions ---------------------------------
|
|
||||||
#
|
|
||||||
# Defines which extensions should be activated when you add a new API entry.
|
|
||||||
# You can change them after Wazuh app starts.
|
|
||||||
# Values must to be true or false.
|
|
||||||
#extensions.pci : true
|
|
||||||
#extensions.gdpr : true
|
|
||||||
#extensions.hipaa : true
|
|
||||||
#extensions.nist : true
|
|
||||||
#extensions.tsc : true
|
|
||||||
#extensions.audit : true
|
|
||||||
#extensions.oscap : false
|
|
||||||
#extensions.ciscat : false
|
|
||||||
#extensions.aws : false
|
|
||||||
#extensions.gcp : false
|
|
||||||
#extensions.virustotal: false
|
|
||||||
#extensions.osquery : false
|
|
||||||
#extensions.docker : false
|
|
||||||
#
|
|
||||||
# ---------------------------------- Time out ----------------------------------
|
|
||||||
#
|
|
||||||
# Defines maximum timeout to be used on the Wazuh app requests.
|
|
||||||
# It will be ignored if it is bellow 1500.
|
|
||||||
# It means milliseconds before we consider a request as failed.
|
|
||||||
# Default: 20000
|
|
||||||
#timeout: 20000
|
|
||||||
#
|
|
||||||
# -------------------------------- API selector --------------------------------
|
|
||||||
#
|
|
||||||
# Defines if the user is allowed to change the selected
|
|
||||||
# API directly from the Wazuh app top menu.
|
|
||||||
# Default: true
|
|
||||||
#api.selector: true
|
|
||||||
#
|
|
||||||
# --------------------------- Index pattern selector ---------------------------
|
|
||||||
#
|
|
||||||
# Defines if the user is allowed to change the selected
|
|
||||||
# index pattern directly from the Wazuh app top menu.
|
|
||||||
# Default: true
|
|
||||||
#ip.selector: true
|
|
||||||
#
|
|
||||||
# List of index patterns to be ignored
|
|
||||||
#ip.ignore: []
|
|
||||||
#
|
|
||||||
# ------------------------------ wazuh-monitoring ------------------------------
|
|
||||||
#
|
|
||||||
# Custom setting to enable/disable wazuh-monitoring indices.
|
|
||||||
# Values: true, false, worker
|
|
||||||
# If worker is given as value, the app will show the Agents status
|
|
||||||
# visualization but won't insert data on wazuh-monitoring indices.
|
|
||||||
# Default: true
|
|
||||||
#wazuh.monitoring.enabled: true
|
|
||||||
#
|
|
||||||
# Custom setting to set the frequency for wazuh-monitoring indices cron task.
|
|
||||||
# Default: 900 (s)
|
|
||||||
#wazuh.monitoring.frequency: 900
|
|
||||||
#
|
|
||||||
# Configure wazuh-monitoring-* indices shards and replicas.
|
|
||||||
#wazuh.monitoring.shards: 2
|
|
||||||
#wazuh.monitoring.replicas: 0
|
|
||||||
#
|
|
||||||
# Configure wazuh-monitoring-* indices custom creation interval.
|
|
||||||
# Values: h (hourly), d (daily), w (weekly), m (monthly)
|
|
||||||
# Default: d
|
|
||||||
#wazuh.monitoring.creation: d
|
|
||||||
#
|
|
||||||
# Default index pattern to use for Wazuh monitoring
|
|
||||||
#wazuh.monitoring.pattern: wazuh-monitoring-*
|
|
||||||
#
|
|
||||||
# --------------------------------- wazuh-cron ----------------------------------
|
|
||||||
#
|
|
||||||
# Customize the index prefix of predefined jobs
|
|
||||||
# This change is not retroactive, if you change it new indexes will be created
|
|
||||||
# cron.prefix: test
|
|
||||||
#
|
|
||||||
# ------------------------------ wazuh-statistics -------------------------------
|
|
||||||
#
|
|
||||||
# Custom setting to enable/disable statistics tasks.
|
|
||||||
#cron.statistics.status: true
|
|
||||||
#
|
|
||||||
# Enter the ID of the APIs you want to save data from, leave this empty to run
|
|
||||||
# the task on all configured APIs
|
|
||||||
#cron.statistics.apis: []
|
|
||||||
#
|
|
||||||
# Define the frequency of task execution using cron schedule expressions
|
|
||||||
#cron.statistics.interval: 0 0 * * * *
|
|
||||||
#
|
|
||||||
# Define the name of the index in which the documents are to be saved.
|
|
||||||
#cron.statistics.index.name: statistics
|
|
||||||
#
|
|
||||||
# Define the interval in which the index will be created
|
|
||||||
#cron.statistics.index.creation: w
|
|
||||||
#
|
|
||||||
# ------------------------------- App privileges --------------------------------
|
|
||||||
#admin: true
|
|
||||||
#
|
|
||||||
# ---------------------------- Hide manager alerts ------------------------------
|
|
||||||
# Hide the alerts of the manager in all dashboards and discover
|
|
||||||
#hideManagerAlerts: false
|
|
||||||
#
|
|
||||||
# ------------------------------- App logging level -----------------------------
|
|
||||||
# Set the logging level for the Wazuh App log files.
|
|
||||||
# Default value: info
|
|
||||||
# Allowed values: info, debug
|
|
||||||
#logs.level: info
|
|
||||||
#
|
|
||||||
# -------------------------------- Enrollment DNS -------------------------------
|
|
||||||
# Set the variable WAZUH_REGISTRATION_SERVER in agents deployment.
|
|
||||||
# Default value: ''
|
|
||||||
#enrollment.dns: ''
|
|
||||||
#
|
|
||||||
#-------------------------------- API entries -----------------------------------
|
|
||||||
#The following configuration is the default structure to define an API entry.
|
|
||||||
#
|
|
||||||
#hosts:
|
|
||||||
# - <id>:
|
|
||||||
# url: http(s)://<url>
|
|
||||||
# port: <port>
|
|
||||||
# username: <username>
|
|
||||||
# password: <password>
|
|
||||||
@@ -32,11 +32,6 @@ do
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|
||||||
grep -q 1513629884013 $dashboard_config_file
|
|
||||||
_config_exists=$?
|
|
||||||
|
|
||||||
if [[ $_config_exists -ne 0 ]]; then
|
|
||||||
cat << EOF >> $dashboard_config_file
|
cat << EOF >> $dashboard_config_file
|
||||||
hosts:
|
hosts:
|
||||||
- 1513629884013:
|
- 1513629884013:
|
||||||
@@ -46,7 +41,4 @@ hosts:
|
|||||||
password: $api_password
|
password: $api_password
|
||||||
run_as: $api_run_as
|
run_as: $api_run_as
|
||||||
EOF
|
EOF
|
||||||
else
|
|
||||||
echo "Wazuh APP already configured"
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|||||||
@@ -24,59 +24,3 @@ RUN bash config.sh
|
|||||||
# Add entrypoint
|
# Add entrypoint
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
FROM amazonlinux:2023
|
|
||||||
|
|
||||||
ENV USER="wazuh-indexer" \
|
|
||||||
GROUP="wazuh-indexer" \
|
|
||||||
NAME="wazuh-indexer" \
|
|
||||||
INSTALL_DIR="/usr/share/wazuh-indexer"
|
|
||||||
|
|
||||||
RUN yum install curl-minimal shadow-utils findutils hostname -y
|
|
||||||
|
|
||||||
RUN getent group $GROUP || groupadd -r -g 1000 $GROUP
|
|
||||||
|
|
||||||
RUN useradd --system \
|
|
||||||
--uid 1000 \
|
|
||||||
--no-create-home \
|
|
||||||
--home-dir $INSTALL_DIR \
|
|
||||||
--gid $GROUP \
|
|
||||||
--shell /sbin/nologin \
|
|
||||||
--comment "$USER user" \
|
|
||||||
$USER
|
|
||||||
|
|
||||||
WORKDIR $INSTALL_DIR
|
|
||||||
|
|
||||||
COPY config/entrypoint.sh /
|
|
||||||
|
|
||||||
COPY config/securityadmin.sh /
|
|
||||||
|
|
||||||
RUN chmod 700 /entrypoint.sh && chmod 700 /securityadmin.sh && \
|
|
||||||
mkdir -p /usr/share/wazuh-indexer && \
|
|
||||||
chown 1000:1000 /usr/share/wazuh-indexer && \
|
|
||||||
chown 1000:1000 /*.sh
|
|
||||||
|
|
||||||
COPY --from=builder --chown=1000:1000 /usr/share/wazuh-indexer /usr/share/wazuh-indexer
|
|
||||||
COPY --from=builder --chown=1000:1000 /etc/wazuh-indexer /usr/share/wazuh-indexer/config
|
|
||||||
COPY --from=builder --chown=1000:1000 /debian/wazuh-indexer/usr/share/wazuh-indexer /usr/share/wazuh-indexer
|
|
||||||
COPY --from=builder --chown=0:0 /debian/wazuh-indexer/usr/lib/systemd /usr/lib/systemd
|
|
||||||
COPY --from=builder --chown=0:0 /debian/wazuh-indexer/usr/lib/sysctl.d /usr/lib/sysctl.d
|
|
||||||
COPY --from=builder --chown=0:0 /debian/wazuh-indexer/usr/lib/tmpfiles.d /usr/lib/tmpfiles.d
|
|
||||||
|
|
||||||
RUN mkdir -p /var/lib/wazuh-indexer && chown 1000:1000 /var/lib/wazuh-indexer && \
|
|
||||||
mkdir -p /usr/share/wazuh-indexer/logs && chown 1000:1000 /usr/share/wazuh-indexer/logs && \
|
|
||||||
mkdir -p /run/wazuh-indexer && chown 1000:1000 /run/wazuh-indexer && \
|
|
||||||
mkdir -p /var/log/wazuh-indexer && chown 1000:1000 /var/log/wazuh-indexer && \
|
|
||||||
chmod 700 /usr/share/wazuh-indexer && \
|
|
||||||
chmod 700 /usr/share/wazuh-indexer/config && \
|
|
||||||
chmod 600 /usr/share/wazuh-indexer/config/jvm.options && \
|
|
||||||
chmod 600 /usr/share/wazuh-indexer/config/opensearch.yml
|
|
||||||
|
|
||||||
USER wazuh-indexer
|
|
||||||
|
|
||||||
# Services ports
|
|
||||||
EXPOSE 9200
|
|
||||||
|
|
||||||
ENTRYPOINT ["/entrypoint.sh"]
|
|
||||||
# Dummy overridable parameter parsed by entrypoint
|
|
||||||
CMD ["opensearchwrapper"]
|
|
||||||
@@ -74,6 +74,10 @@ in_indexer && /^[[:space:]]*[^#].*name:/ {sub(/name:.*/, "name: indexer")}
|
|||||||
{print}
|
{print}
|
||||||
' config.yml > config.yml.tmp && mv config.yml config.yml.bak && mv config.yml.tmp config.yml
|
' config.yml > config.yml.tmp && mv config.yml config.yml.bak && mv config.yml.tmp config.yml
|
||||||
|
|
||||||
|
sed -i \
|
||||||
|
-e 's/^ *ip: "<wazuh-manager-ip>"$/ ip: "127.0.0.1"/' \
|
||||||
|
-e 's/^ *ip: "<indexer-node-ip>"$/ ip: "127.0.0.1"/' \
|
||||||
|
config.yaml
|
||||||
|
|
||||||
chmod 700 "$CERT_CONFIG_FILE"
|
chmod 700 "$CERT_CONFIG_FILE"
|
||||||
# Create the certificates
|
# Create the certificates
|
||||||
@@ -88,10 +92,8 @@ cp -pr /wazuh-certificates/root-ca.pem ${CONFIG_DIR}/certs/root-ca.pem
|
|||||||
cp -pr /wazuh-certificates/admin.pem ${CONFIG_DIR}/certs/admin.pem
|
cp -pr /wazuh-certificates/admin.pem ${CONFIG_DIR}/certs/admin.pem
|
||||||
cp -pr /wazuh-certificates/admin-key.pem ${CONFIG_DIR}/certs/admin-key.pem
|
cp -pr /wazuh-certificates/admin-key.pem ${CONFIG_DIR}/certs/admin-key.pem
|
||||||
|
|
||||||
# Delete xms and xmx parameters in jvm.options
|
|
||||||
sed '/-Xms/d' -i /etc/wazuh-indexer/jvm.options
|
|
||||||
sed '/-Xmx/d' -i /etc/wazuh-indexer/jvm.options
|
|
||||||
sed -i 's/-Djava.security.policy=file:\/\/\/etc\/wazuh-indexer\/opensearch-performance-analyzer\/opensearch_security.policy/-Djava.security.policy=file:\/\/\/usr\/share\/wazuh-indexer\/opensearch-performance-analyzer\/opensearch_security.policy/g' /etc/wazuh-indexer/jvm.options
|
sed -i 's/-Djava.security.policy=file:\/\/\/etc\/wazuh-indexer\/opensearch-performance-analyzer\/opensearch_security.policy/-Djava.security.policy=file:\/\/\/usr\/share\/wazuh-indexer\/opensearch-performance-analyzer\/opensearch_security.policy/g' /etc/wazuh-indexer/jvm.options
|
||||||
|
sed -i 's|/etc/wazuh-indexer|/usr/share/wazuh-indexer/config|g' /etc/wazuh-indexer/opensearch.yml
|
||||||
|
|
||||||
chown -R ${USER}:${GROUP} ${CONFIG_DIR}
|
chown -R ${USER}:${GROUP} ${CONFIG_DIR}
|
||||||
chmod -R 500 ${CONFIG_DIR}/certs
|
chmod -R 500 ${CONFIG_DIR}/certs
|
||||||
|
|||||||
Reference in New Issue
Block a user