commit 8c81737b875d387f4a3b8ac1b7d19b4360636c4d Author: Adam Štrauch Date: Sat Sep 27 00:25:22 2025 +0200 Initial commit diff --git a/.gitea/workflows/main.yml b/.gitea/workflows/main.yml new file mode 100644 index 0000000..b193122 --- /dev/null +++ b/.gitea/workflows/main.yml @@ -0,0 +1,21 @@ +name: Build a dev image + +on: + push: + branches: [ main ] + +jobs: + build: + runs-on: [dev, amd64] + env: + IMAGE: gitea.ceperka.net/rosti/mgm + TAG: dev + steps: + - uses: actions/checkout@v4 + - name: docker login + run: | + docker login gitea.ceperka.net -u "${{ secrets.REPO_USERNAME }}" -p "${{ secrets.REPO_PASSWORD }}" + - name: Build + run: task build REPO=$IMAGE VERSION=$TAG + - name: Push + run: task push REPO=$IMAGE VERSION=$TAG diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f63fe8a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,38 @@ +FROM alpine:3.22 + +RUN apk update && apk upgrade && apk add --no-cache \ + git \ + docker \ + bash \ + fish \ + zsh \ + wget \ + curl \ + htop \ + vim \ + nano \ + tmux \ + openssh-server \ + iproute2 + +# Download ttyd +RUN wget -O /usr/local/bin/ttyd https://github.com/tsl0922/ttyd/releases/download/1.7.7/ttyd.x86_64 && chmod +x /usr/local/bin/ttyd + +# Configure SSH +RUN mkdir -p /var/run/sshd && \ + sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config && \ + sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config + +# Copy entrypoint script +COPY entrypoint.sh /app/ +COPY service.ssh.sh /app/ +COPY service.ttyd.sh /app/ +RUN chmod +x /app/entrypoint.sh /app/service.ssh.sh /app/service.ttyd.sh + +RUN mkdir -p /srv/stack +WORKDIR /srv/stack + +EXPOSE 22 1234 + +ENTRYPOINT ["/app/entrypoint.sh"] + diff --git a/LICENCE b/LICENCE new file mode 100644 index 0000000..432c4e3 --- /dev/null +++ b/LICENCE @@ -0,0 +1,10 @@ +Copyright (c) 2025, Roští.cz, s.r.o. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by the Roští.cz, s.r.o. + Neither the name of the Roští.cz, s.r.o. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY Roští.cz, s.r.o. AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Roští.cz, s.r.o. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..55f41db --- /dev/null +++ b/README.md @@ -0,0 +1,162 @@ +# MGM Image + +A lightweight Alpine-based Docker image that provides SSH and web terminal access through ttyd. Perfect for development environments, remote debugging, or containerized workspaces. + +## Features + +- 🐧 **Alpine Linux** - Minimal base image for small footprint +- 🔒 **SSH Server** - Full SSH access on port 22 +- 🌐 **Web Terminal** - Browser-based terminal via ttyd on port 1234 +- 🐚 **Fish Shell** - Modern shell with auto-suggestions and syntax highlighting +- 🔧 **Development Tools** - Git, curl, wget, htop, vim, nano included +- 🚦 **Signal Handling** - Proper Docker signal propagation for graceful shutdowns + +## Quick Start + +### Environment Variables + +| Variable | Required | Default | Description | +|----------|----------|---------|-------------| +| `TTYD_PASSWORD` | ✅ Yes | - | Password for web terminal authentication | + +### Running the Container + +```bash +docker run -d \ + --name mgm-container \ + -p 2222:22 \ + -p 1234:1234 \ + -e TTYD_PASSWORD=your_secure_password \ + gitea.ceperka.net/rosti/mgm:dev +``` + +### Accessing Services + +- **SSH**: `ssh root@localhost -p 2222` +- **Web Terminal**: http://localhost:1234 (username: `tty`, password: your `TTYD_PASSWORD`) + +## Building + +This project uses [Task](https://taskfile.dev) for build automation. + +### Prerequisites + +- Docker +- Task (optional, you can use docker commands directly) + +### Build Commands + +```bash +# Build the image +task build + +# Push to registry +task push + +# Or use Docker directly +docker build -t gitea.ceperka.net/rosti/mgm:dev . +``` + +## Architecture + +The container runs two services managed by a bash-based process supervisor: + +``` +entrypoint.sh +├── service.ssh.sh # SSH daemon (/usr/sbin/sshd -D) +└── service.ttyd.sh # Web terminal (ttyd + fish shell) +``` + +### Process Management + +- **Signal Handling**: SIGTERM/SIGINT signals are properly propagated to child processes +- **Graceful Shutdown**: Services receive SIGTERM first, then SIGKILL after timeout +- **Process Monitoring**: Parent process waits for all children and handles exits +- **No Dependencies**: Pure bash implementation, no external process managers + +### Security Considerations + +- SSH is configured to allow root login with password authentication +- ttyd is bound to `127.0.0.1` (localhost) for security - use reverse proxy if needed +- Set a strong `TTYD_PASSWORD` as it protects web terminal access +- Consider using SSH keys instead of passwords in production + +## Development + +### File Structure + +``` +├── Dockerfile # Alpine-based image definition +├── entrypoint.sh # Main entrypoint with process management +├── service.ssh.sh # SSH service wrapper +├── service.ttyd.sh # ttyd service wrapper +├── Taskfile.yml # Build automation +└── README.md # This file +``` + +### Customization + +You can extend this image for your specific needs: + +```dockerfile +FROM gitea.ceperka.net/rosti/mgm:dev + +# Add your tools +RUN apk add --no-cache python3 nodejs + +# Copy your configurations +COPY custom-config/ /etc/ + +# Set your working directory +WORKDIR /workspace +``` + +## Troubleshooting + +### Container won't start + +1. Check if `TTYD_PASSWORD` is set: + ```bash + docker logs + ``` + +2. Verify ports aren't already in use: + ```bash + netstat -tulpn | grep -E ':(22|1234)' + ``` + +### SSH connection refused + +1. Check if SSH service is running: + ```bash + docker exec ps aux | grep sshd + ``` + +2. Verify SSH host keys were generated: + ```bash + docker exec ls -la /etc/ssh/ssh_host_* + ``` + +### Web terminal not accessible + +1. Check ttyd service status: + ```bash + docker exec ps aux | grep ttyd + ``` + +2. Verify ttyd is listening: + ```bash + docker exec netstat -tulpn | grep 1234 + ``` + +## Contributing + +1. Fork the repository +2. Create your feature branch (`git checkout -b feature/amazing-feature`) +3. Commit your changes (`git commit -m 'Add some amazing feature'`) +4. Push to the branch (`git push origin feature/amazing-feature`) +5. Open a Pull Request + +## License + +This project is open source. Please check the license file for more details. diff --git a/Taskfile.yml b/Taskfile.yml new file mode 100644 index 0000000..e247dee --- /dev/null +++ b/Taskfile.yml @@ -0,0 +1,15 @@ +# https://taskfile.dev + +version: '3' + +vars: + IMAGE: gitea.ceperka.net/rosti/mgm + TAG: dev + +tasks: + build: + cmds: + - docker build -t {{ .IMAGE }}:{{ .TAG }} . + push: + cmds: + - docker push {{ .IMAGE }}:{{ .TAG }} diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100644 index 0000000..9fe214c --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,74 @@ +#!/bin/bash + +# Entrypoint script to start SSH and ttyd services + +set -e + +if [ -z "$TTYD_TOKEN" ]; then + echo "Warning: TTYD_TOKEN not set, exiting." + exit 1 +fi + +if [ -z "$SET_SHELL" ]; then + echo "Warning: SET_SHELL not set, using default '/bin/bash'." + export SET_SHELL="/bin/bash" +fi + +echo "Initializing services..." + +# Generate SSH host keys if they don't exist +if [ ! -f /etc/ssh/ssh_host_rsa_key ]; then + echo "Generating SSH host keys..." + ssh-keygen -A +fi + +# Array to store child PIDs +declare -a CHILD_PIDS=() + +# Function to handle shutdown gracefully +shutdown_handler() { + echo "Shutting down services..." + + # Send SIGTERM to all child processes + for pid in "${CHILD_PIDS[@]}"; do + if kill -0 "$pid" 2>/dev/null; then + echo "Sending SIGTERM to PID: $pid" + kill -TERM "$pid" 2>/dev/null || true + fi + done + + # Wait a bit for graceful shutdown + sleep 2 + + # Force kill any remaining processes + for pid in "${CHILD_PIDS[@]}"; do + if kill -0 "$pid" 2>/dev/null; then + echo "Force killing PID: $pid" + kill -KILL "$pid" 2>/dev/null || true + fi + done + + echo "All services stopped" + exit 0 +} + +# Set up signal handlers +trap shutdown_handler SIGTERM SIGINT + +# Start services in background and collect PIDs +echo "Starting services..." + +echo "Starting SSH service..." +/app/service.ssh.sh & +CHILD_PIDS+=($!) +echo "SSH service started with PID: $!" + +echo "Starting ttyd service..." +/app/service.ttyd.sh & +CHILD_PIDS+=($!) +echo "ttyd service started with PID: $!" + +echo "Both services are running. PIDs: ${CHILD_PIDS[*]}" + +# Wait for all background processes +wait diff --git a/service.ssh.sh b/service.ssh.sh new file mode 100644 index 0000000..bb20d03 --- /dev/null +++ b/service.ssh.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +exec /usr/sbin/sshd -D diff --git a/service.ttyd.sh b/service.ttyd.sh new file mode 100644 index 0000000..74fe93b --- /dev/null +++ b/service.ttyd.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +exec /usr/local/bin/ttyd -p 1234 -b /$TTYD_TOKEN/terminal -w /srv/stack -W $SET_SHELL