Compare commits

...

9 Commits

Author SHA1 Message Date
Stefan Allius
5c5c3bc926 Merge pull request #14 from s-allius/reduze-size
Reduze size
2023-10-07 23:10:40 +02:00
Stefan Allius
2cf7a2db36 Version 0.2.0 2023-10-07 23:08:39 +02:00
Stefan Allius
3225566b9b fix formating of a log message 2023-10-07 21:24:49 +02:00
Stefan Allius
fa567f68c0 - disable DEBUG log for releases
- support building of release candidates
2023-10-07 21:14:57 +02:00
Stefan Allius
e1536cb697 adapt log levels, optimize expensive hex dump logs 2023-10-07 21:03:49 +02:00
Stefan Allius
b06d832504 set log level to DEBUG for dev versions 2023-10-07 20:58:18 +02:00
Stefan Allius
ed14ed484b add build support for release candidates (rc) 2023-10-07 20:55:26 +02:00
Stefan Allius
ddba3f6285 optimize and update some comments 2023-10-07 16:39:39 +02:00
Stefan Allius
8264cc6d00 reduce continer size ans security attack surface 2023-10-07 16:20:40 +02:00
7 changed files with 69 additions and 44 deletions

View File

@@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
## [0.2.0] - 2023-10-07
This version halves the size of the Docker image and reduces the attack surface for security vulnerabilities, by omitting unneeded code. The feature set is exactly the same as the previous release version 0.1.0.
### Changes
- move from slim-bookworm to an alpine base image
- install python requirements with pip wheel
- disable DEBUG log for releases
- support building of release candidates
## [0.1.0] - 2023-10-06 ## [0.1.0] - 2023-10-06
- refactoring of the connection classes - refactoring of the connection classes

View File

@@ -2,40 +2,41 @@ ARG SERVICE_NAME="tsun-proxy"
ARG UID=1000 ARG UID=1000
ARG GID=1000 ARG GID=1000
# set base image (host OS) #
FROM python:3.11-slim-bookworm AS builder # first stage for our base image
FROM python:3.11-alpine AS base
USER root USER root
# install gosu for a better su+exec command RUN apk update && \
RUN set -eux; \ apk upgrade
apt-get update; \ RUN apk add --no-cache su-exec
apt-get install -y gosu; \
rm -rf /var/lib/apt/lists/*; \
# verify that the binary works
gosu nobody true
RUN pip install --upgrade pip #
# second stage for building wheels packages
FROM base as builder
RUN apk add --no-cache build-base && \
python -m pip install --no-cache-dir -U pip wheel
# copy the dependencies file to the working directory # copy the dependencies file to the root dir and install requirements
COPY ./requirements.txt . COPY ./requirements.txt /root/
RUN python -OO -m pip wheel --no-cache-dir --wheel-dir=/root/wheels -r /root/requirements.txt
# install dependencies
RUN pip install --user -r requirements.txt
# #
# second unnamed stage # third stage for our runtime image
FROM python:3.11-slim-bookworm FROM base as runtime
ARG SERVICE_NAME ARG SERVICE_NAME
ARG VERSION ARG VERSION
ARG UID ARG UID
ARG GID ARG GID
ARG LOG_LVL
ENV VERSION=$VERSION ENV VERSION=$VERSION
ENV SERVICE_NAME=$SERVICE_NAME ENV SERVICE_NAME=$SERVICE_NAME
ENV UID=$UID ENV UID=$UID
ENV GID=$GID ENV GID=$GID
ENV LOG_LVL=$LOG_LVL
# set the working directory in the container # set the working directory in the container
@@ -43,18 +44,16 @@ WORKDIR /home/$SERVICE_NAME
# update PATH environment variable # update PATH environment variable
ENV HOME=/home/$SERVICE_NAME ENV HOME=/home/$SERVICE_NAME
ENV PATH=/home/$SERVICE_NAME/.local:$PATH
VOLUME ["/home/$SERVICE_NAME/log", "/home/$SERVICE_NAME/config"] VOLUME ["/home/$SERVICE_NAME/log", "/home/$SERVICE_NAME/config"]
# copy only the dependencies installation from the 1st stage image # install the requirements from the wheels packages from the builder stage
COPY --from=builder --chown=$SERVICE_NAME:$SERVICE_NAME /root/.local /home/$SERVICE_NAME/.local COPY --from=builder /root/wheels /root/wheels
COPY --from=builder /usr/sbin/gosu /usr/sbin/gosu RUN python -m pip install --no-cache --no-index /root/wheels/* && \
rm -rf /root/wheels
COPY entrypoint.sh /root/entrypoint.sh
RUN chmod +x /root/entrypoint.sh
# copy the content of the local src and config directory to the working directory # copy the content of the local src and config directory to the working directory
COPY --chmod=0700 entrypoint.sh /root/entrypoint.sh
COPY config . COPY config .
COPY src . COPY src .

View File

@@ -9,20 +9,22 @@ arr=(${VERSION//./ })
MAJOR=${arr[0]} MAJOR=${arr[0]}
IMAGE=tsun-gen3-proxy IMAGE=tsun-gen3-proxy
if [[ $1 == dev ]];then if [[ $1 == dev ]] || [[ $1 == rc ]] ;then
IMAGE=docker.io/sallius/${IMAGE} IMAGE=docker.io/sallius/${IMAGE}
VERSION=${VERSION}-dev VERSION=${VERSION}-$1
elif [[ $1 == rel ]];then elif [[ $1 == rel ]];then
IMAGE=ghcr.io/s-allius/${IMAGE} IMAGE=ghcr.io/s-allius/${IMAGE}
else else
echo argument missing! echo argument missing!
echo try: $0 '[dev|rel]' echo try: $0 '[dev|rc|rel]'
exit 1 exit 1
fi fi
echo version: $VERSION build-date: $BUILD_DATE image: $IMAGE echo version: $VERSION build-date: $BUILD_DATE image: $IMAGE
if [[ $1 == dev ]];then if [[ $1 == dev ]];then
docker build --build-arg "VERSION=${VERSION}" --label "org.label-schema.build-date=${BUILD_DATE}" --label "org.opencontainers.image.version=${VERSION}" -t ${IMAGE}:latest app docker build --build-arg "VERSION=${VERSION}" --build-arg "LOG_LVL=DEBUG" --label "org.label-schema.build-date=${BUILD_DATE}" --label "org.opencontainers.image.version=${VERSION}" -t ${IMAGE}:latest app
elif [[ $1 == rc ]];then
docker build --no-cache --build-arg "VERSION=${VERSION}" --label "org.label-schema.build-date=${BUILD_DATE}" --label "org.opencontainers.image.version=${VERSION}" -t ${IMAGE}:latest app
elif [[ $1 == rel ]];then elif [[ $1 == rel ]];then
docker build --no-cache --build-arg "VERSION=${VERSION}" --label "org.label-schema.build-date=${BUILD_DATE}" --label "org.opencontainers.image.version=${VERSION}" -t ${IMAGE}:latest -t ${IMAGE}:${MAJOR} -t ${IMAGE}:${VERSION} app docker build --no-cache --build-arg "VERSION=${VERSION}" --label "org.label-schema.build-date=${BUILD_DATE}" --label "org.opencontainers.image.version=${VERSION}" -t ${IMAGE}:latest -t ${IMAGE}:${MAJOR} -t ${IMAGE}:${VERSION} app
docker push ghcr.io/s-allius/tsun-gen3-proxy:latest docker push ghcr.io/s-allius/tsun-gen3-proxy:latest

View File

@@ -10,17 +10,15 @@ echo "#"
if [ "$user" = '0' ]; then if [ "$user" = '0' ]; then
mkdir -p /home/$SERVICE_NAME/log /home/$SERVICE_NAME/config mkdir -p /home/$SERVICE_NAME/log /home/$SERVICE_NAME/config
if id $SERVICE_NAME ; then if ! id $SERVICE_NAME &> /dev/null; then
echo "user still exists"
else
addgroup --gid $GID $SERVICE_NAME 2> /dev/null addgroup --gid $GID $SERVICE_NAME 2> /dev/null
adduser --ingroup $SERVICE_NAME --shell /bin/false --disabled-password --no-create-home --comment "" --uid $UID $SERVICE_NAME adduser -G $SERVICE_NAME -s /bin/false -D -H -g "" -u $UID $SERVICE_NAME
fi fi
chown -R $SERVICE_NAME:$SERVICE_NAME /home/$SERVICE_NAME || true chown -R $SERVICE_NAME:$SERVICE_NAME /home/$SERVICE_NAME || true
echo "######################################################" echo "######################################################"
echo "#" echo "#"
exec gosu $SERVICE_NAME "$@" exec su-exec $SERVICE_NAME "$@"
else else
exec "$@" exec "$@"
fi fi

View File

@@ -21,7 +21,7 @@ class AsyncStream(Message):
Our puplic methods Our puplic methods
''' '''
def set_serial_no(self, serial_no : str): def set_serial_no(self, serial_no : str):
logger.info(f'SerialNo: {serial_no}') logger.debug(f'SerialNo: {serial_no}')
if self.unique_id != serial_no: if self.unique_id != serial_no:
@@ -40,7 +40,7 @@ class AsyncStream(Message):
if not inverters['allow_all']: if not inverters['allow_all']:
self.unique_id = None self.unique_id = None
logger.error('ignore message from unknow inverter!') logger.warning(f'ignore message from unknow inverter! (SerialNo: {serial_no})')
return return
self.unique_id = serial_no self.unique_id = serial_no
@@ -67,7 +67,7 @@ class AsyncStream(Message):
except (ConnectionResetError, except (ConnectionResetError,
ConnectionAbortedError, ConnectionAbortedError,
RuntimeError) as error: RuntimeError) as error:
logger.error(f'In loop for {self.addr}: {error}') logger.warning(f'In loop for {self.addr}: {error}')
self.close() self.close()
return return
except Exception: except Exception:

View File

@@ -18,6 +18,7 @@ def hex_dump_memory(level, info, data, num):
lines = [] lines = []
lines.append(info) lines.append(info)
tracer = logging.getLogger('tracer') tracer = logging.getLogger('tracer')
if not tracer.isEnabledFor(level): return
#data = list((num * ctypes.c_byte).from_address(ptr)) #data = list((num * ctypes.c_byte).from_address(ptr))
@@ -294,7 +295,7 @@ class Message(metaclass=IterRegistry):
def msg_unknown(self): def msg_unknown(self):
logger.error (f"Unknow Msg: ID:{self.msg_id}") logger.warning (f"Unknow Msg: ID:{self.msg_id}")
self.forward(self._recv_buffer, self.header_len+self.data_len) self.forward(self._recv_buffer, self.header_len+self.data_len)

View File

@@ -32,8 +32,16 @@ def handle_SIGTERM(loop):
logging.info('Shutdown complete') logging.info('Shutdown complete')
def get_log_level() -> int:
'''checks if LOG_LVL is set in the environment and returns the corresponding logging.LOG_LEVEL'''
log_level = os.getenv('LOG_LVL', 'INFO')
if log_level== 'DEBUG':
log_level = logging.DEBUG
elif log_level== 'WARN':
log_level = logging.WARNING
else:
log_level = logging.INFO
return log_level
if __name__ == "__main__": if __name__ == "__main__":
@@ -41,11 +49,17 @@ if __name__ == "__main__":
# Setup our daily, rotating logger # Setup our daily, rotating logger
# #
serv_name = os.getenv('SERVICE_NAME', 'proxy') serv_name = os.getenv('SERVICE_NAME', 'proxy')
version = os.getenv('VERSION', 'unknown') version = os.getenv('VERSION', 'unknown')
logging.config.fileConfig('logging.ini') logging.config.fileConfig('logging.ini')
logging.info(f'Server "{serv_name} - {version}" will be started') logging.info(f'Server "{serv_name} - {version}" will be started')
logging.getLogger().setLevel(logging.DEBUG if __debug__ else logging.INFO)
# set lowest-severity for 'root', 'msg', 'conn' and 'data' logger
log_level = get_log_level()
logging.getLogger().setLevel(log_level)
logging.getLogger('msg').setLevel(log_level)
logging.getLogger('conn').setLevel(log_level)
logging.getLogger('data').setLevel(log_level)
# read config file # read config file
Config.read() Config.read()