This commit is contained in:
commit
8c81737b87
8 changed files with 326 additions and 0 deletions
21
.gitea/workflows/main.yml
Normal file
21
.gitea/workflows/main.yml
Normal file
|
@ -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
|
38
Dockerfile
Normal file
38
Dockerfile
Normal file
|
@ -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"]
|
||||||
|
|
10
LICENCE
Normal file
10
LICENCE
Normal file
|
@ -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.
|
162
README.md
Normal file
162
README.md
Normal file
|
@ -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 <container-name>
|
||||||
|
```
|
||||||
|
|
||||||
|
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 <container-name> ps aux | grep sshd
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Verify SSH host keys were generated:
|
||||||
|
```bash
|
||||||
|
docker exec <container-name> ls -la /etc/ssh/ssh_host_*
|
||||||
|
```
|
||||||
|
|
||||||
|
### Web terminal not accessible
|
||||||
|
|
||||||
|
1. Check ttyd service status:
|
||||||
|
```bash
|
||||||
|
docker exec <container-name> ps aux | grep ttyd
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Verify ttyd is listening:
|
||||||
|
```bash
|
||||||
|
docker exec <container-name> 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.
|
15
Taskfile.yml
Normal file
15
Taskfile.yml
Normal file
|
@ -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 }}
|
74
entrypoint.sh
Normal file
74
entrypoint.sh
Normal file
|
@ -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
|
3
service.ssh.sh
Normal file
3
service.ssh.sh
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
exec /usr/sbin/sshd -D
|
3
service.ttyd.sh
Normal file
3
service.ttyd.sh
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
exec /usr/local/bin/ttyd -p 1234 -b /$TTYD_TOKEN/terminal -w /srv/stack -W $SET_SHELL
|
Loading…
Reference in a new issue