--- title: Server Deploing Sheet tags: deploy description: --- #### *VPS* <!-- Put the link to this slide here so people can follow --> --- ## ### Initial managment ```typescript= apt update & apt upgrade useradd -m user & usermod -aG sudo user passwd user mkdir config && cd config mkdir iptables vpn uwsgi nginx fail2ban manuals docker wireguard user=[username] ``` ### SSH #### Configure ```bash= cd ~/config/ssh/ ln -s /home/$user/config/ssh/sshd_config /etc/ssh/sshd_config.d/ssh_config ssh-keygen -A ``` #### SSHD config (sed '/^ *#/d; /^ *$/d' clean_conf) ```bash= Port [port] AddressFamily inet ListenAddress [ip] Protocol 2 HostKey /etc/ssh/ssh_host_rsa_key RekeyLimit 1G SyslogFacility AUTH LogLevel INFO LoginGraceTime 1m PermitRootLogin no StrictModes yes AllowUsers $user MaxAuthTries 5 MaxSessions 4 PubkeyAuthentication yes AuthorizedKeysFile /home/$user/.ssh/authorized_keys /home/$user/.ssh/authorized_keys2 IgnoreRhosts yes PasswordAuthentication no ChallengeResponseAuthentication no UsePAM no X11Forwarding no PrintMotd no PrintLastLog yes Compression no ClientAliveInterval 60 ClientAliveCountMax 3 TCPKeepAlive no AcceptEnv LANG LC_* Subsystem sftp /usr/lib/openssh/sftp-server DenyGroups no-ssh ``` ```bash= # check config /usr/sbin/sshd -t sudo systemctl restart sshd sudo systemctl status sshd ``` #### Edit ssh welcome message ```bash= sudo apt install update-motd echo "" > /snap/core18/2066/motd ``` ### Iptables #### Iptables params explanation ```bash= # The order of passing the tables https://ittricks.ru/administrirovanie/linux/531/iptables-poryadok-proxozhdeniya-tablic-i-cepochek ``` #### ```bash= # create main iptables conf file cd configs/iptables/ nano ~/config/iptables/iptables_rules.ipv4 sudo apt-get install iptables-persistent ``` #### Disable TOR nodes and IPtables config ```bash= # https://bozza.ru/art-297.html # https://losst.ru/upravlenie-sluzhbami-linux # https://losst.ru/kak-dobavit-skript-v-avtozagruzku-ubuntu ``` apt update & apt -y install ipset ```bash= systemctl edit --force --full tornodescollector ``` ```bash= [Unit] After=iptables.service After=ip6tables.service Description=TOR nodes collector After=multi-user.target [Service] Type=oneshot RemainAfterExit=yes ExecStart=/root/tornodescollector.sh [Install] WantedBy=basic.target #systemctl edit --force tornodescollector ``` ```bash= nano /root/tornodescollector.sh ``` ```bash= #!/bin/bash # Block Tor Exit nodes EXTERNAL_IP="IP" # EXTERNAL_IP=$(curl ident.me) - DANGEROUS! ipset -N TORSET iphash 2> /dev/null # curl "https://check.torproject.org/torbulkexitlist?ip=${EXTERNAL_IP}" | xargs -n 1 ipset -A TORSET /usr/bin/curl -sSL "https://check.torproject.org/torbulkexitlist?ip=${EXTERNAL_IP}" 2> /dev/null | xargs -n 1 ipset -A TORSET 2> /dev/null systemctl restart iptables ``` ```bash= sudo systemctl daemon-reload ``` ```bash= sudo systemctl enable tornodescollector ``` #### /etc/iptables/rules.v4 for IPv4 ```bash= # Generated by iptables-save v1.6.0 on Sat Jun 1 11:06:45 2019 *nat :PREROUTING ACCEPT [0:0] :INPUT ACCEPT [0:0] :OUTPUT ACCEPT [0:0] :POSTROUTING ACCEPT [0:0] -A POSTROUTING -s 10.8.0.0/24 -j MASQUERADE COMMIT # Completed on Sat Jun 1 11:06:45 2019 # Generated by iptables-save v1.6.0 on Sat Jun 1 11:06:45 2019 *mangle :PREROUTING ACCEPT [0:0] :INPUT ACCEPT [0:0] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [0:0] :POSTROUTING ACCEPT [0:0] COMMIT # Completed on Sat Jun 1 11:06:45 2019 # Generated by iptables-save v1.6.0 on Sat Jun 1 11:06:45 2019 *filter :INPUT ACCEPT [0:0] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [0:0] :SRV-Firewall-1-INPUT - [0:0]:TOR - [0:0] -A INPUT -j TOR -A TOR -m set --match-set TORSET src -j DROP -A INPUT -j VPN-Firewall-1-INPUT -A FORWARD -j VPN-Firewall-1-INPUT -A VPN-Firewall-1-INPUT -s 127.0.0.1/32 -p icmp -m icmp --icmp-type any -j ACCEPT -A VPN-Firewall-1-INPUT -s 109.252.0.0/16 -p icmp -m icmp --icmp-type 8 -j ACCEPT -A VPN-Firewall-1-INPUT -p tcp -m tcp --dport 80 -j ACCEPT -A VPN-Firewall-1-INPUT -p tcp -m tcp --dport 443 -j ACCEPT -A VPN-Firewall-1-INPUT -p tcp -m tcp --dport 44300 -j ACCEPT -A VPN-Firewall-1-INPUT -s 109.252.0.0/16 -p udp -m udp --dport 51820 -j ACCEPT -A VPN-Firewall-1-INPUT -s 176.59.0.0/16 -p udp -m udp --dport 51820 -j ACCEPT -A VPN-Firewall-1-INPUT -s 212.3.128.0/19 -p udp -m udp --dport 51820 -j ACCEPT -A VPN-Firewall-1-INPUT -s 94.25.0.0/16 -p udp -m udp --dport 51820 -j ACCEPT -A VPN-Firewall-1-INPUT -s 109.252.0.0/16 -p tcp -m tcp --dport 44327 -j ACCEPT -A VPN-Firewall-1-INPUT -s 176.59.0.0/16 -p tcp -m tcp --dport 44327 -j ACCEPT -A VPN-Firewall-1-INPUT -s 212.3.128.0/19 -p tcp -m tcp --dport 44327 -j ACCEPT -A VPN-Firewall-1-INPUT -s 94.26.0.0/16 -p tcp -m tcp --dport 44327 -j ACCEPT -A VPN-Firewall-1-INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT -A VPN-Firewall-1-INPUT -s 109.252.0.0/16 -p tcp -m state --state NEW -m tcp --dport 43900 -j ACCEPT -A VPN-Firewall-1-INPUT -s 109.252.0.0/16 -p udp -m state --state NEW -m udp --dport 44000 -j ACCEPT -A VPN-Firewall-1-INPUT -i tun+ -j ACCEPT -A VPN-Firewall-1-INPUT -i lo -j ACCEPT -A VPN-Firewall-1-INPUT -i eth0 -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -m multiport --dports 80,443,44327 -m connlimit --connlimit-above 10 --connlimit-mask 9 --connlimit-sad> -A VPN-Firewall-1-INPUT -p icmp -m icmp --icmp-type 8 -j DROP -A VPN-Firewall-1-INPUT -i eth0 -p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG FIN,SYN,RST,PSH,ACK,URG -j DROP -A VPN-Firewall-1-INPUT -i eth0 -p tcp -j DROP -A VPN-Firewall-1-INPUT -i eth0 -p udp -j DROP # Completed on Sat Jun 1 11:06:45 2019 ``` #### /etc/iptables/rules.v6 for IPv6. ```bash= # Generated by ip6tables-save v1.8.7 on Sun Jun 6 01:25:37 2021 *filter :INPUT DROP [0:0] :FORWARD DROP [0:0] :OUTPUT ACCEPT [0:0] -A INPUT -i lo -j ACCEPT -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -A INPUT -p ipv6-icmp -j DROP -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT -A INPUT -p tcp -j DROP -A INPUT -p udp -j DROP COMMIT # Completed on Sun Jun 6 01:25:37 2021 #ip6tables rules ip6tables -P INPUT DROP ip6tables -P FORWARD DROP ip6tables -P OUTPUT ACCEPT ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT ip6tables -I INPUT 1 -i lo -j ACCEPT ip6tables -A INPUT -p icmpv6 -j ACCEPT ip6tables -A INPUT -p tcp --dport http -j ACCEPT ip6tables -A INPUT -p tcp --dport https -j ACCEPT ip6tables-save > /etc/ip6tables.conf # manual mode iptables-resotre < /etc/iptables/rules.v4 ``` #### Disable ipv6 ```bash sudo nano /etc/sysctl.conf #add at the end of the config file net.ipv6.conf.all.disable_ipv6 = 1 sudo sysctl -p sudo systemctl restart iptables ``` ```bash= /etc/iptables/rules.v4 # all other version are depricated #nano ~/config/iptables/iptables_restore.ipv4 ## --> ###!/bin/sh ##/sbin/iptables-restore < /home/$user/config/iptables/iptables_rules.ipv4 ## -- #sudo ln -s sudo ln -s /home/$user/config/iptables/iptables_restore.ipv4 /etc/network/if-pre-up.d/iptables_restore ``` ### Wireguard install ```bash= sudo add-apt-repository ppa:wireguard/wireguard sudo apt install wireguard ``` ### Rkhunter install ```bash= # https://habr.com/ru/company/first/blog/242865/ # by root sudo apt -y install rkhunter mailutils # optional (errors) nano /etc/rkhunter.conf MIRRORS_MODE=1 ---> MIRRORS_MODE=0 UPDATE_MIRRORS=0 ---> UPDATE_MIRRORS=1 WEB_CMD="/bin/false" ---> WEB_CMD="" MAIL_CMD=echo "Rkhunter warnings were received" | mail -s "rkhunter status message" mail -aFrom:mail # Add mail for alerts nano /etc/rkhunter.conf MAIL-ON-WARNING="mail.com" # manual launch rkhunter -c --enable all --disable none --rwo # collect hashes of sys files rkhunter --propupd # add to crontab job sudo nano /etc/cron.daily/rkhunters #!/bin/sh ( /usr/bin/rkhunter --versioncheck /usr/bin/rkhunter --update /usr/bin/rkhunter -c --sk --rwo) | mail -s "rkhunter scan status from $(date)" $mail -aFrom:$mail chmod +x /etc/cron.daily/rkhunters # or @daily /usr/bin/rkhunter --cronjob --update --quiet # parse rkhunter log file sudo cat /var/log/rkhunter.log | grep -A5 "\[ Warning \]" # for false-positive # SCRIPTWHITELIST="/sbin/ifup /sbin/ifdown /usr/bin/groups /usr/bin/ldd /usr/bin/whatis" # for hidden files and folders # ALLOWHIDDENDIR="/etc/.java" # ALLOWHIDDENFILE="/etc/.java" # add mail newsletter ## https://www.dmosk.ru/miniinstruktions.php?mini=postfix-over-yandex sudo nano /etc/postfix/main.cf --- relayhost = smtp_sasl_auth_enable = yes smtp_sasl_password_maps = hash:/etc/postfix/private/sasl_passwd smtp_sasl_security_options = noanonymous smtp_sasl_type = cyrus smtp_sasl_mechanism_filter = login smtp_sender_dependent_authentication = yes sender_dependent_relayhost_maps = hash:/etc/postfix/private/sender_relay smtp_tls_CAfile = /etc/postfix/ca.pem smtp_use_tls = yes --- # create private config files mkdir /etc/postfix/private sudo nano /etc/postfix/private/sender_relay --- @[domain] smtp.yandex.ru --- sudo nano /etc/postfix/private/sasl_passwd --- [smtp.yandex.ru]:587 $mailName@$domain:$pass --- postmap /etc/postfix/private/{sasl_passwd,sender_relay} # get certificate of the smtp server openssl s_client -starttls smtp -crlf -connect smtp.yandex.ru:587 #to file /etc/postfix/ca.pem # restart postfix service sudo systemctl restart postfix # test mail server echo "Test text" | mail -s "Test title" $toWhomEmail -aFrom:$FromWhoEmail # set default hashes sudo rkhunter --propupd ``` #### Replace OpenSSH header ```bash= sshd -V cp /usr/sbin/sshd /tmp & cd /tmp strings -t d -a -n 7 sshd | grep OpenSSH dd if=./sshd bs=1 skip= [address] count=[count] | od -A n -c dd if=./sshd bs=1 count=[address] of=sshd.1 dd if=./sshd bs=1 skip=[address] count=[count] of=sshd.2 dd if=./sshd bs=1 skip=[address] count=999999999 of=sshd.3 od -A n -c sshd.2 sed -i 's/SEARCH_REGEX/REPLACEMENT/g' sshd.2 #nano sshd.2 #tr -d '\n' < title > sshd.2 cat sshd.* > sshd.new strings -t d -a -n 7 sshd.new | grep OpenSSH chmod 755 ./sshd.new cp /usr/sbin/sshd /usr/sbin/sshd.bak rm /usr/sbin/sshd cp /tmp/sshd.new /usr/sbin/sshd # Example sshd -V strings -t d -a -n 7 sshd | grep OpenSSH dd if=./sshd bs=1 skip=526539 count=29 | od -A n -c dd if=./sshd bs=1 count=526539 of=sshd.1 dd if=./sshd bs=1 skip=526539 count=30 of=sshd.2 dd if=./sshd bs=1 skip=526569 count=999999999 of=sshd.3 #SGCMS Fingerprint: X64_vsd12d #SGCMS FGPT:X64_St24aDr49zRms2 # check the title dd if=./sshd.2 bs=1 skip=0 count=999999999 | d -A n -c sed -i 's/OpenSSH_8.4p1 Ubuntu-5ubuntu1/SGCMS FGPT:X64_St24aDr49zRms2/g' sshd.2 cat sshd.* > sshd.new chmod 755 ./sshd.new strings -t d -a -n 7 sshd.new | grep OpenSSH chmod +x sshd.new cp /tmp/sshd.new /user/sbin/sshd # explanation (with errors! wrong indents!) # https://poweruser.guru/questions/782382/%D1%81%D0%BA%D1%80%D1%8B%D1%82%D1%8C-%D0%B2%D0%B5%D1%80%D1%81%D0%B8%D1%8E-ssh ``` ### Fail2ban #### Manual ``` #https://www.dmosk.ru/instruktions.php?object=fail2ban ``` #### Configure application ```bash= sudo apt install fail2ban sudo systemctl start fail2ban sudo systemctl enable fail2ban # /etc/fail2ban/fail2ban.conf # /etc/fail2ban/jail.conf - jail config # port configs # SSH #sudo nano /etc/fail2ban/jail.d/ssh.conf cd ~/config/fail2ban nano ssh.conf # --- [ssh] enabled = true port = ssh filter = sshd action = iptables[name=sshd] logpath = /var/log/auth.log # NGINX # sudo nano /etc/fail2ban/jail.d/nginx.conf # --- [nginx] enabled = true port = http,https filter = nginx-http-auth action = iptables-multiport[name=nginx, port="http,https", protocol=tcp] logpath = /var/log/nginx/error.log #create symlinks to configs sudo ln -s /home/$user/config/fail2ban/ssh.conf /etc/fail2ban/jail.d/ssh.conf sudo ln -s /home/$user/config/fail2ban/nginx.conf /etc/fail2ban/jail.d/nginx.conf sudo systemctl restart fail2ban.service ``` #### fail2ban commands ```bash= # Check rules and configs are being used fail2ban-client status # Check details and ban statistic of the current rules fail2ban-client status ssh # Check /var/log/auth.log for IP sudo cat auth.log | grep -E -o "([0-9]{1,3}[\.]){3}[0-9]{1,3}" # Check /var/log/auth.log for IPv6 sudo cat auth.log | grep -E -o "(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))" ``` ### DJANGO ```bash= sudo apt update sudo apt install python3-pip sudo -H pip3 install --upgrade pip sudo pip3 install virtualenv virtualenvwrapper #echo "export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3" >> ~/.bashrc echo "export WORKON_HOME=home/$user/config/environment" >> ~/.bashrc echo "source /usr/local/bin/virtualenvwrapper.sh" >> ~/.bashrc export PYTHONPATH='/home/$user/config/django/lib/python3.4' source ~/.bashrc cd ~/config/django/ mkvirtualenv [project_name] pip3 install django # test django server sudo `which python3` manage.py runserver 0.0.0.0:8080 #deactivate env deactivate #activate env source bin/activate ``` #### UWSGI (I) ```bash= sudo apt-get install python3-dev sudo -H pip3 install uwsgi # test UWSGI #sudo uwsgi --http :8080 --home /home/$(whoami)/config/environment/$(whoami) --chdir /home/$(whoami)/config/djanago/$project/ -w $project.wsgi cd ~/config/uwsgi sudo mkdir -p /etc/uwsgi/sites sudo ln -s /home/$(whoami)/config/uwsgi/$project.ini /etc/uwsgi/sites/ ``` #### UWSGI project config file ```bash= [uwsgi] uid = $user project = $project base = /home/%(uid)/config/django chdir = %(base)/%(project) module = %(project).wsgi:application master = true callable = $project pidfile = /run/uwsgi/%(project).pid env = LANG=en_US.UTF-8 env = DJANGO_SETTINGS_MODULE=$project.settings pythonpath = %(base)/%(project) plugins = python3 virtualenv = /home/$user/config/environment/$project socket = /run/uwsgi/%(project).socket chmod-socket = 660 chown-socket = %(uid):www-data processes = 5 enable-threads = true vacuum = true no-orphans = true autoload = true #touch-reload = %(base)/%(porject)/touch.reload harakiri = 30 buffer-size = 32768 ``` ```bash= # uwsgi service config creating sudo ln -s /home/$(whoami)/config/uwsgi/uwsgi.service /etc/systemd/system/uwsgi.service ``` #### UWSGI service config file ```bash= [Unit] Description=uWSGI Emperor service [Service] ExecStartPre=/bin/bash -c 'mkdir -p /run/uwsgi; chown $user:www-data /run/uwsgi' ExecStart=/usr/local/bin/uwsgi --emperor /etc/uwsgi/sites Restart=always KillSignal=SIGQUIT Type=notify NotifyAccess=all [Install] WantedBy=multi-user.targe ``` ### NGINX #### Configure server ```bash= server=[server_url] sudo apt-get install nginx -y sudo nano /etc/nginx/nginx.conf cd ~/config/nginx sudo ln -s /home/$(whoami)/config/nginx/nginx.conf /etc/nginx/sites-available/$(echo $project) sudo ln -s /etc/nginx/sites-available/$(echo $project) /etc/nginx/sites-enabled sudo usermod -a -G www-data $(whoami) sudo mkdir -p /var/www/$project/static sudo chown -R $(whoami):www-data /var/www/$project sudo chmod -R 755 /var/www/$project ``` #### NGINX server config ```bash= server { client_body_buffer_size 1K; client_header_buffer_size 1k; client_max_body_size 10M; large_client_header_buffers 2 1k; keepalive_timeout 5 5; send_timeout 10; if ($host !~ ^($server)$ ) { return 444; } if ( $http_referer ~* (babes|forsale|girl|jewerly|love|nude|nudit|organic|poker|porn|sex|teen|oral|anal) ) { return 403; } location /media { valid_referers none blocked $server; if ($invalid_referer) {return 444;} alias /var/www/$project/media; } location /static { alias /var/www/$project/static; } location / { uwsgi_pass unix:///run/uwsgi/$server.socket; uwsgi_param QUERY_STRING $query_string; uwsgi_param REQUEST_METHOD $request_method; uwsgi_param CONTENT_TYPE $content_type; uwsgi_param CONTENT_LENGTH $content_length; uwsgi_param REQUEST_URI $request_uri; uwsgi_param PATH_INFO $document_uri; uwsgi_param DOCUMENT_ROOT $document_root; uwsgi_param SERVER_PROTOCOL $server_protocol; uwsgi_param REQUEST_SCHEME $scheme; uwsgi_param HTTPS $https if_not_empty; uwsgi_param REMOTE_ADDR $remote_addr; uwsgi_param REMOTE_PORT $remote_port; uwsgi_param SERVER_PORT $server_port; uwsgi_param SERVER_NAME $server_name; } listen 443 ssl; # managed by Certbot ssl_certificate /etc/letsencrypt/live/$server.io/fullchain.pem; # managed by Certbot ssl_certificate_key /etc/letsencrypt/live/$server.io/privkey.pem; # managed by Certbot include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot } server { if ($host = $server) { return 301 https://$host$request_uri; } # managed by Certbot listen 80; server_name $server; return 404; # managed by Certbot } sudo nginx -t sudo systemctl enable nginx sudo systemctl enable uwsgi ``` #### Let`s Encrypt certificate gaining ```bash= sudo snap install certbot --classic #sudo apt install python-certbot-nginx sudo ln -s /snap/bin/certbot /usr/bin/certbot sudo certbot run --nginx # enable auto renewal sudo certbot renew --dry-run # root cd /etc/nginx/ nano acme # --- location /.well-known { root /var/www/crypt; } mkdir&cd /var/www/crypt/.well-known nano example.html curl -L http://$server/.well-known/example.html cat /etc/letsencrypt/live/*/cert.pem | openssl x509 -text | grep -o 'DNS:[^,]*' | cut -f2 -d: ``` TLS cetificates ```bash= openssl req -x509 -out localhost.crt -keyout localhost.key \ -newkey rsa:2048 -nodes -sha256 \ -subj '/CN=localhost' -extensions EXT -config <( \ printf "[dn]\nCN=localhost\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:localhost\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth" ```