Desbloqueando el Poder del VPS

Una guía a configurar y mantener tu propio servidor

@larrygf

@hiancd

¿Qué es un VPS?

  •  Exponer Proyectos
  • Ahorro en Servicios (VPN, share file,...)
  •  Mantener Servicios Propios
  •  Diversión

Para que un VPS?

De que vamos a hablar?

Configuración y Seguridad

Alojar Servicios y utilidades

Alojar Servicios y código propio

Obtener y configurar

  • Obtener VPS
  • Obtener y Configurar DNS
  • Crear Usuario

💰¿Donde lo encuentro?💰

~ 5$

¿Donde obtengo el Dominio?

Inscribir el DNS

Acceso al VPS

ssh root@ejemplito.dev

Instalación

# Programas basicos
apt update
apt install procps apparmor micro gpg htop zsh git \
				 apt-transport-https ca-certificates curl \
                 software-properties-common ufw lsb-release \
                 fail2ban
# Docker (https://docs.docker.com/engine/install/ubuntu/#install-using-the-repository)
mkdir -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg\
	 -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc

# Add the repository to Apt sources:
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
apt-get update

apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Cloudflare DNS

Mejora la latencia y la serguridad

# /etc/resolv.conf
nameserver 1.1.1.1
#/etc/NetworkManager/NetworkManager.conf
[main]
dns=none

Asegurar Acceso Remoto

# /etc/ssh/sshd_config root:root:0600

# Deny root login
PermitRootLogin no

# Deny auth by password
PasswordAuthentication no

# Use linux users
UsePAM yes

# No window forwarding
X11Forwarding no

HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_ecdsa_key
HostKey /etc/ssh/ssh_host_ed25519_key
SyslogFacility AUTHPRIV
AuthorizedKeysFile	.ssh/authorized_keys
ChallengeResponseAuthentication no
GSSAPIAuthentication yes
GSSAPICleanupCredentials no
Banner /etc/issue.net
AcceptEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES
AcceptEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT
AcceptEnv LC_IDENTIFICATION LC_ALL LANGUAGE
AcceptEnv XMODIFIERS
Subsystem	sftp	/usr/libexec/openssh/sftp-server
# /etc/fail2ban/jail.conf root:root:0644
[DEFAULT]
# Ban hosts for one hour:
bantime = 1h

# A host is banned if it has generated "maxretry" during the last "findtime":
findtime = 10m
maxretry = 5

# "ignoreip" can be an IP address, a CIDR mask, or a DNS host. Fail2Ban will not
# ban a host which matches an address in this list.
ignoreip = 127.0.0.1/8

# "backend" specifies the backend used to get files modification.
# Options are "pyinotify", "gamin", "polling", "systemd" and "auto".
backend = auto

# "usedns" specifies whether DNS lookups are performed.
# Options are "yes", "warn", "no" (default).
usedns = no

# "destemail" is the destination email for sendmail actions.
destemail = root@localhost

# "mta" is the Mail Transfer Agent used by Fail2Ban to send emails.
# Options are "sendmail" (default), "mail", "qmail", "postfix", "exim", "sendmail-whois",
# "mail-whois", "mail-buffered", "mail-whois-lines", "mail-whois-lines-buffered".
mta = sendmail

# "protocol" by default is set to TCP.
protocol = tcp

# "chain" specifies the iptables chain to which the Fail2Ban rules should be added.
chain = INPUT

# Action to take when banning an IP. See action.d directory for other options
action = %(action_)s

[sshd]
enabled = true
port = ssh
logpath = %(sshd_log)s
maxretry = 3
bantime = 2h

[sshd-ddos]
enabled = true
port = ssh
logpath = %(sshd_log)s
maxretry = 6
bantime = 1d

[apache-auth]
enabled = true
port = http,https
logpath = %(apache_error_log)s
maxretry = 5
bantime = 2h

[apache-badbots]
enabled = true
port = http,https
logpath = %(apache_access_log)s
bantime = 2d
maxretry = 3

Configuración de Usuario

# Add docker group
sudo addgroup docker

USER=ejemplito

# Crear usuario y añadirlo al grupo docker
useradd -m -s /bin/zsh -G docker -G sudo $USER
passwd $USER

mkdir -p /home/$USER/.ssh

# Crear carpetas necesarias
chown -R $USER:$USER /home/$USER/.ssh
chmod 700 /home/$USER/.ssh
DOMAIN=ejemplito.com
USER=ejemplito

ssh-copy-id $USER@$DOMAIN

ssh $USER@$DOMAIN

# (In the remote machine) Test that you can everything is working and then:
sudo reboot

# Ahora de vuelta 
echo -e "\nHost ejemplito\n  HostName $DOMAIN\n  User $USER" >> ~/.ssh/config
sudo systemctl start ufw
sudo systemctl enable ufw

sudo ufw allow OpenSSH
sudo ufw allow 443/tcp

# OpenVPN
sudo ufw allow 1195/udp

Log Rotation

# /etc/logrotate.d/large_logfiles
/var/log/daemon.log
/var/log/syslog
{
    rotate 7
    daily
    size 100M
    missingok
    notifempty
    compress
    delaycompress
    sharedscripts
    postrotate
        /bin/kill -HUP cat /var/run/syslogd.pid 2> /dev/null 2> /dev/null || true
    endscript
}

ZSH Config

https://github.com/ohmyzsh/ohmyzsh.git
https://github.com/romkatv/powerlevel10k.git
https://github.com/zsh-users/zsh-syntax-highlighting.git
https://github.com/zsh-users/zsh-autosuggestions.git
# ~/.zshrc

# Enable Powerlevel10k instant prompt. Should stay close to the top of ~/.zshrc.
# Initialization code that may require console input (password prompts, [y/n]
# confirmations, etc.) must go above this block; everything else may go below.
if [[ -r "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" ]]; then
  source "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh"
fi

# If you come from bash you might have to change your $PATH.
# export PATH=$HOME/bin:/usr/local/bin:$PATH

# Path to your oh-my-zsh installation.
export ZSH="/home/{{ user }}/.oh-my-zsh"

# Set name of the theme to load --- if set to "random", it will
# load a random theme each time oh-my-zsh is loaded, in which case,
# to know which specific one was loaded, run: echo $RANDOM_THEME
# See https://github.com/ohmyzsh/ohmyzsh/wiki/Themes
ZSH_THEME="powerlevel10k/powerlevel10k"

# Set list of themes to pick from when loading at random
# Setting this variable when ZSH_THEME=random will cause zsh to load
# a theme from this variable instead of looking in $ZSH/themes/
# If set to an empty array, this variable will have no effect.
# ZSH_THEME_RANDOM_CANDIDATES=( "robbyrussell" "agnoster" )

# Uncomment the following line to use case-sensitive completion.
# CASE_SENSITIVE="true"

# Uncomment the following line to use hyphen-insensitive completion.
# Case-sensitive completion must be off. _ and - will be interchangeable.
# HYPHEN_INSENSITIVE="true"

# Uncomment the following line to disable bi-weekly auto-update checks.
# DISABLE_AUTO_UPDATE="true"

# Uncomment the following line to automatically update without prompting.
# DISABLE_UPDATE_PROMPT="true"

# Uncomment the following line to change how often to auto-update (in days).
# export UPDATE_ZSH_DAYS=13

# Uncomment the following line if pasting URLs and other text is messed up.
# DISABLE_MAGIC_FUNCTIONS="true"

# Uncomment the following line to disable colors in ls.
# DISABLE_LS_COLORS="true"

# Uncomment the following line to disable auto-setting terminal title.
# DISABLE_AUTO_TITLE="true"

# Uncomment the following line to enable command auto-correction.
# ENABLE_CORRECTION="true"

# Uncomment the following line to display red dots whilst waiting for completion.
# COMPLETION_WAITING_DOTS="true"

# Uncomment the following line if you want to disable marking untracked files
# under VCS as dirty. This makes repository status check for large repositories
# much, much faster.
# DISABLE_UNTRACKED_FILES_DIRTY="true"

# Uncomment the following line if you want to change the command execution time
# stamp shown in the history command output.
# You can set one of the optional three formats:
# "mm/dd/yyyy"|"dd.mm.yyyy"|"yyyy-mm-dd"
# or set a custom format using the strftime function format specifications,
# see 'man strftime' for details.
# HIST_STAMPS="mm/dd/yyyy"

# Would you like to use another custom folder than $ZSH/custom?
# ZSH_CUSTOM=/path/to/new-custom-folder

# Which plugins would you like to load?
# Standard plugins can be found in $ZSH/plugins/
# Custom plugins may be added to $ZSH_CUSTOM/plugins/
# Example format: plugins=(rails git textmate ruby lighthouse)
# Add wisely, as too many plugins slow down shell startup.
plugins=(
	git 
	debian
	colored-man-pages
	colorize
	docker-compose
	docker
	npm
	rsync
	sudo
	themes
	helm
	kubectl
	zsh-syntax-highlighting
	zsh-autosuggestions
	)

source $ZSH/oh-my-zsh.sh

# User configuration

# export MANPATH="/usr/local/man:$MANPATH"

# You may need to manually set your language environment
# export LANG=en_US.UTF-8

# Preferred editor for local and remote sessions
# if [[ -n $SSH_CONNECTION ]]; then
#   export EDITOR='vim'
# else
#   export EDITOR='mvim'
# fi

# Compilation flags
# export ARCHFLAGS="-arch x86_64"

# Set personal aliases, overriding those provided by oh-my-zsh libs,
# plugins, and themes. Aliases can be placed here, though oh-my-zsh
# users are encouraged to define aliases within the ZSH_CUSTOM folder.
# For a full list of active aliases, run alias.
#
# Example aliases
# alias zshconfig="mate ~/.zshrc"
# alias ohmyzsh="mate ~/.oh-my-zsh"
# source /usr/share/zsh/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
# source /usr/share/zsh/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh

# To customize prompt, run p10k configure or edit ~/.p10k.zsh.
[[ ! -f ~/.p10k.zsh ]] || source ~/.p10k.zsh
# source <(kubectl completion zsh)
# source <(helm completion zsh)
echo export ANSIBLE_INVENTORY=/home/$USER/.inventory >> ~/.zshrc
# ~/.inventory

[own]
ejemplito ansible_user=ejemplito

Hosting de servicios

...

sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy
# /etc/caddy/Caddyfile

ejemplito.com {
	respond "Hello HTTPS"
}
# /home/ejemplito/services/portainer/docker-compose.yml

version: "3.3"

services:
  portainer:
    image: portainer/portainer-ce:latest
    restart: unless-stopped
    ports:
      - "127.0.0.1:9000:9000"
    networks:
      - services
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - portainer_data:/data
    # (optional) These labels are for Homepage automatic integration
    labels:
      - homepage.group=Services
      - homepage.name=Portainer
      - homepage.icon=portainer
      - homepage.href=https://portainer.ejemplito.com
      - homepage.widget.type=portainer
      - homepage.widget.env=2
      - homepage.widget.url=https://portainer:9443
 #     - homepage.widget.key=---------------------

volumes:
  portainer_data:

networks:
  services:
    name: services
    external: true
# /etc/caddy/Caddyfile

...

import /home/ejemplito/services/*/Caddyfile
# /home/ejemplito/services/portainer/Caddyfile

portainer.nubecita.dev {
        reverse_proxy * {
                to 127.0.0.1:9000
                lb_policy       least_conn
                lb_try_duration 10s
                fail_duration   5s
        }
}
# /home/ejemplito/services/homepage/docker-compose

version: "3.3"
services:
  homepage:
    image: ghcr.io/gethomepage/homepage:latest
    ports:
      - "127.0.0.1:9001:3000"
    networks:
      - services
    volumes:
      - ./config:/app/config
      - /var/run/docker.sock:/var/run/docker.sock
    restart: unless-stopped

networks:
  services:
    name: services
    external: true
# /home/ejemplito/services/homepage/config/docker.yml

my-docker:
  socket: /var/run/docker.sock
# /home/ejemplito/services/homepage/config/settings.yml

title: Nubecita Homepage
showStats: true
# /home/ejemplito/services/homepage/config/widgets.yml

- resources:
    cpu: true
    memory: true
    disk: /

Links

Apps Security

ansible-playbook ejemplito.yml
---
- name: Deploy Project to Remote Server and Run Docker-Compose
  hosts: own
  become: true
  vars:
    project_src: "../proyectico/"
    project_dest: "proyectico/"
    caddyfile_content: |
      proyectico.ejemplito.com {
        reverse_proxy localhost:8601
      }

  tasks:
    - name: Copy project directory to remote server
      ansible.builtin.copy:
        src: "{{ project_src }}"
        dest: "{{ project_dest }}"
        owner: "{{ ansible_user }}"
        group: "{{ ansible_user }}"
        mode: '0755'
        remote_src: no

    - name: Run docker-compose up
      ansible.builtin.shell: |
        cd {{ project_dest }}
        docker compose up --build -d
      args:
        chdir: "{{ project_dest }}"
      register: docker_compose_output

    - name: Debug docker-compose output
      ansible.builtin.debug:
        var: docker_compose_output.stdout_lines

    - name: Create Caddyfile for reverse proxy
      ansible.builtin.copy:
        content: "{{ caddyfile_content }}"
        dest: "{{ project_dest }}/Caddyfile"
        owner: "{{ ansible_user }}"
        group: "{{ ansible_user }}"
        mode: '0644'

    - name: Restart Caddy
      ansible.builtin.systemd:
        name: caddy
        state: restarted

¿Qué falta?

Desbloqueando el Poder del VPS

Una guía a configurar y mantener tu propio servidor

@larrygf

@hiancd

Desbloqueando el poder del VPS

By Hian Cañizares

Desbloqueando el poder del VPS

  • 113