{"id":20670941,"url":"https://github.com/shuklaritvik06/server-imp","last_synced_at":"2025-03-10T17:21:47.800Z","repository":{"id":234319819,"uuid":"780434389","full_name":"shuklaritvik06/server-imp","owner":"shuklaritvik06","description":"Server Setup","archived":false,"fork":false,"pushed_at":"2024-04-01T13:34:06.000Z","size":41,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-09T13:29:34.566Z","etag":null,"topics":["production","server","tutorial"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/shuklaritvik06.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2024-04-01T13:28:39.000Z","updated_at":"2024-09-13T16:45:30.000Z","dependencies_parsed_at":"2024-04-18T21:46:56.408Z","dependency_job_id":null,"html_url":"https://github.com/shuklaritvik06/server-imp","commit_stats":null,"previous_names":["shuklaritvik06/server-imp"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shuklaritvik06%2Fserver-imp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shuklaritvik06%2Fserver-imp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shuklaritvik06%2Fserver-imp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shuklaritvik06%2Fserver-imp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/shuklaritvik06","download_url":"https://codeload.github.com/shuklaritvik06/server-imp/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":242889440,"owners_count":20201965,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["production","server","tutorial"],"created_at":"2024-11-16T20:24:17.057Z","updated_at":"2025-03-10T17:21:47.782Z","avatar_url":"https://github.com/shuklaritvik06.png","language":"Shell","readme":"# Production ready server setup.\n\n## Creating a Linux instance\n\n## Generate a SSH key (if not already done)\n\nOpen a terminal and run the following command\n\n```bash\nssh-keygen\n```\n\nYou will be prompted to save and name the key.\n\n```bash\nGenerating public/private rsa key pair. Enter file in which to save the key (/Users/USER/.ssh/id_rsa)\n```\n\nNext you will be asked to create and confirm a passphrase for the key (highly recommended):\n\n```\nEnter passphrase (empty for no passphrase):\nEnter same passphrase again:\n```\n\nThis will generate two files, by default called id_rsa and id_rsa.pub. Next, add this public key.\n\nCopy the public key in clipboard to be later used when creating a DO droplet.\n\n```bash\ncat id_rsa.pub | pbcopy\n```\n\n## Create a CENTOS 8.x droplet on digital ocean\n\nI chose the following paramters for my droplet, only the things you need to select are written here, rest everything is default.\n\n| Selection         | Value                                      |\n| ----------------- | ------------------------------------------ |\n| Image             | CentOS 8.x                                 |\n| Plan              | Basic (5$)                                 |\n| Add block storage | None                                       |\n| Datacenter region | Bangalore (choose which is nearest to you) |\n| Authentication    | SSH                                        |\n\nFor Adding SSH keys to DO, click NEW SSH key, and follow simple instructions.\n\nFinally click on create Droplet and sit back and relax for a moment.\n\n## SSH into your droplet with **root user**\n\nGet the IP of the droplet from your DO droplets page, and then ssh into it.\n\n```bash\nssh root@XXX.XXX.XXX.XXX\n# XXX.XXX.XXX.XXX represents your IP\n\nThe authenticity of host '139.59.47.191 (139.59.47.191)' can't be established.\nECDSA key fingerprint is SHA256:rup2QTATg6cOBDcMiP0abSmsOO+4eAMKA4q/Z7O2xVc.\nAre you sure you want to continue connecting (yes/no)?\n\n# type yes and press enter\n```\n\nNow you will or might get error like this\n\n```bash\nWarning: Permanently added '139.59.47.191' (ECDSA) to the list of known hosts.\nroot@139.59.47.191: Permission denied (publickey,gssapi-keyex,gssapi-with-mic).\n```\n\nNow add the generated ssh key in\n\n```bash\nssh-add ~/.ssh/do_youtube\n# Identity added: /Users/killer/.ssh/do_youtube (jhondoe@iMac.local)\n```\n\nFinally now you can SSH into your remote machine\n\n```bash\nssh root@XXX.XXX.XXX.XXX\n```\n\nHuh, long work, but your droplet is up and running and time to play with it...and make it **production ready**.\n\n### Update the OS using **dnf** package manager (comes pre-install with centos 8.x)\n\n```bash\ndnf update -y\n```\n\nIt will really take sometime, so sit back relax and have a cup of tea ☕️, (Note: I like tea but you can also have coffee or a beer).\n\n### Install packages using **dnf**\n\n```bash\ndnf install curl vim git wget '@Development tools' nmap net-tools\n```\n\nOnce again, it will take sometime, so sit back relax and have a second cup of tea ☕️, (Note: I like tea but you can also have coffee or a beer).\n\n### Adding EPEL to CentOS 8\n\n```bash\ndnf install epel-release\ndnf upgrade\n```\n\n### Start and install automatic updates\n\n```bash\ndnf install dnf-automatic -y\nsystemctl enable --now dnf-automatic.timer\nsystemctl list-timers *dnf-*\n```\n\n### Install Snapd from\n\n(https://snapcraft.io/docs/installing-snapd)\n\n```bash\nsudo dnf install snapd -y\nsudo systemctl enable --now snapd.socket\nsudo ln -s /var/lib/snapd/snap /snap\n```\n\n### Setup sudo user with root priveleges\n\n- create a User with your choice of USERNAME, we will make this user have **sudo** access\n  ```bash\n  adduser USERNAME\n  ```\n- add this user to the wheel group using `usermod -aG wheel USERNAME` (wheel group is always present in centos and has sudo access)\n\n```bash\n  usermod -aG wheel USERNAME\n```\n\n- Allow wheel group to have password-less sudo\n\n  - edit file `/etc/sudoers` (using `visudo`)\n\n  ```bash\n  visudo /etc/sudoers\n  ```\n\n  - `%wheel ALL=(ALL) NOPASSWD: ALL`\n  - validate using `/usr/sbin/visudo -cf /etc/sudoers`\n\n    OR\n\n- add a file called USERNAME with the content below to only have this user to use password-less sudo\n\n```bash\nvisudo /etc/sudoers.d/USERNAME\nUSERNAME ALL=(ALL) NOPASSWD: ALL\n# validate using\n/usr/sbin/visudo -cf /etc/sudoers\n```\n\n### Set authorized_key for remote user USERNAME using rsync\n\n```\nrsync --archive --chown=USERNAME:USERNAME ~/.ssh /home/USERNAME\n```\n\n###### OR\n\n```bash\n# (700 only current user can Read Write Execute)\n# (600 only current user can Read Write)\n  su USERNAME\n  mkdir .ssh\n  chmod 700 .ssh\n  touch .ssh/authorized_keys\n  chmod 600 .ssh/authorized_keys\n# copy **PUBLIC** SSH KEY in this file created above\n```\n\nFinally exit out of your remote machine and SSH into your remote machine as this sudouser USERNAME\n\n```bash\nssh USERNAME@XXX.XXX.XXX.XXX\n```\n\n### Setup sshd_config\n\n```bash\ncd /etc/ssh\ncat /etc/ssh/ssh_config\n#create a file here like ssh_config.conf\n/etc/ssh/ssh_config.d/*.conf\n# with following content\nPasswordAuthentication no\nPermitrootlogin no\n# Save and quit and validate...\n\n# optionally, but not recommended to directly edit the sshd_config, though you can\n# edit `sshd_config`\n# PermitRootLogin no (OR without-password)\n# PasswordAuthentication no\n# AllowUsers killer (etc...)\n# Or you can also use 'DenyUsers' directive to deny particular users, (am not doing it)\n# Finally\n\n```\n\nValidate `sshd_config`\n\n```bash\nsshd -T -f /etc/ssh/sshd_config\n# OR (from any path)\nsshd -t\n```\n\nReload SSHD service\n\n```bash\nsystemctl reload sshd\n```\n\nOptionally, for this new user, create ssh public/private to be used on github for private repos.\n\n```bash\n# run this command when loggin into ssh with this new user only\nssh-keygen -t ed25519\n```\n\n### (Optional) Delete other user **centos**\n\n```bash\nuserdel centos\n\n# You can also delete that user's home directory and mail spool by using the -r flag with the command\n# recommended\nuserdel -r centos\n```\n\n### (Optional) Install **fish** terminal\n\nI recommend installing fish shell, since it prettifies the terminal session and provides with autocompletion and much more.\n\n```bash\nsudo dnf install fish -y\n# List all present shells\ncat /etc/shells\n# change shell to fish instead of bash\nsudo usermod --shell $(which fish) USERNAME\n# exit and ssh again, to use fish shell\n```\n\n### Install Nodejs\n\n(https://nodejs.org/en/download/)\n\nThere are two options to install nodejs, one is using dnf and other using snaps\n\n1. To see a list of available streams, where **stream** corresponds to the major version of Node.js.\n   For example, to install Node.js 14:\n\n```bash\ndnf module list nodejs\n# Choose the version using:\nsudo dnf module enable nodejs:14 -y\nsudo dnf module install nodejs:14 -y\nnode --version\nnpm --version\n```\n\n######OR\n\n2. using snap craft (https://github.com/nodejs/snap)\n\n```\nsudo snap install node --classic --channel=14\n```\n\n### Setup PM2 (process manager for node applications)\n\nInstall and run the startup script\n\n```bash\n# ssh as current USER not as root\nsudo npm install -g pm2\n```\n\nEither use `pm2 startup` and follow the instructions\nOR use this script below\n\nStart and enable pm2 as a service\n\n```bash\nsudo env PATH=$PATH:/usr/local/bin pm2 startup systemd -u USERNAME --hp /home/USERNAME\n```\n\nCheck status of pm2\n\n```\nsystemctl status pm2-USERNAME\n```\n\nIMPORTANT NOTE: If the Active status is not **Active: active (running)** and is something like _failed_ or _activating_ , then execute the following commands in order (just for info, this is related to SELINUX), and everything should be fine, else if still it creates a problem you can make an issue in this repo only after trying out the fix.\n\n```bash\n# Change to root user\nsudo su\n# The following start command will timeout\nsystemctl start pm2-USERNAME\n# Then do this\nausearch -c 'systemd' --raw | audit2allow -M my-systemd\nsemodule -i my-systemd.pp\n# NOTE: this is NOT a mistake, it has to be done twice\nsystemctl start pm2-USERNAME\n# Again, the above start command will timeout\nausearch -c 'systemd' --raw | audit2allow -M my-systemd\nsemodule -i my-systemd.pp\nsystemctl start pm2-USERNAME\n# Finally the above start command will start the pm2 service, and verify it with\nsystemctl status pm2-USERNAME\n# sudo reboot\n```\n\n### Start the Nodejs application\n\n- Login as the user created above with root privilages\n\n- Install yarn global packages\n\n```\nsudo npm install -g yarn\n```\n\n- Starting the Nodejs application\n\n```bash\n# Clone the repo any dir, example: inside this folder `~/application`\ncd ~/application\ngit clone REPO_URL application\nyarn install\n# To start pm2 in cluster mode, ie, to start 2 instances of application use (-i 2) or else simple pm2 start app.js\nPORT=4000 pm2 start app.js -n my_app\npm2 save\n```\n\n- list application using `pm2 monit`\n\n### NGINX as Reverse proxy\n\n- Install Nginx (make sure epel-release is already installed, though already done above)\n\n```bash\ndnf install nginx\n```\n\n- **Enable SELinux** for Nginx httpd_t (IMPORTANT)\n\n```bash\nsemanage permissive -a httpd_t\n# OR\nsudo setsebool -P httpd_can_network_connect on\n\n# Other SELinux commands\ngetenforce\n# read selinux context (type) of files and processes\n# -P = persist setting\nsudo setsebool -P httpd_can_network_connect on\n# To read home dirs, specially for app public directory served through nginx\nsudo setsebool -P httpd_enable_homedirs on\nchcon -Rt httpd_sys_content_t /home/USERNAME/app/public\n```\n\n- Disable Nginx Server Header\n\n```bash\n# nginx.conf\n\nhttp {\n  # Disable emitting nginx version on error pages and in the \"Server\" response header field\n  server_tokens off;\n}\n```\n\n- create `example.conf` file inside `/etc/nginx/conf.d/`\n\n- Sample server configutaion file are present in templates folder.\n\n### SSL certificates using certbot\n\n(https://certbot.eff.org/lets-encrypt/snap-nginx.html)\n\n- Install snapd, if not already done, install Snapd from (https://snapcraft.io/docs/installing-snapd)\n\n```bash\nsudo dnf install snapd -y\nsudo systemctl enable --now snapd.socket\nsudo ln -s /var/lib/snapd/snap /snap\n```\n\n- Install certbot from snapd\n\n```bash\nsudo snap install core\nsudo snap refresh core\nsudo snap install --classic certbot\nsudo ln -s /snap/bin/certbot /usr/bin/certbot\n```\n\n- Generate the certificate using the below command\n\n```bash\nsudo certbot --nginx -d example.com,www.example.com -n -m youremail@example.com --agree-tos\n# --nginx Automatic, GETS and INSTALLS\n# -n non-interactive\n# -m for emails about certs\n```\n\nTest automatic renewal\n\n```bash\nsudo certbot renew --dry-run\n```\n\nCheck renew status timer\n\n```bash\nsystemctl list-timers *certbot*\n```\n\n### Github Webhook support for automated deployments\n\nhttps://github.com/adnanh/webhook\n\nFirst create a webhook from repository, is is located under settings of the repo.\n_settings → webhook → add webhook_\n\n- add a payload url of your choice (https://example.com/hooks/redeploy-app)\n- any secret\n- `content-type` to: `application/json`\n- just the push event\n- active\n\n**payload url**: is the url which will be called whenever a push occurs on your repo (example: https://example.com/hooks/redeploy-app)\n**secret**: is the secret used to verify that the url is triggered by github\n\nOnly then follow these steps below.\n\n1. Install webhook using binary\n   (https://github.com/adnanh/webhook/releases)\n\n```bash\n  cd ~\n  wget https://github.com/adnanh/webhook/releases/download/2.8.0/webhook-linux-amd64.tar.gz\n  # 62ab801c7337a8b83de8d6ae8d7ace81  webhook-linux-amd64.tar.gz\n  # Check and verify md5 checksum (use md5 on MacOS and md5sum on linux)\n  md5sum webhook-linux-amd64.tar.gz\n  tar -xvf webhook-linux-amd64.tar.gz\n  sudo mv webhook-linux-amd64/webhook /usr/local/bin\n  rm -rf webhook-linux-amd64.tar.gz webhook-linux-amd64\n  webhook --version\n  # Run below on to test if its able to execute and then close (Ctrl+C)\n  webhook -verbose\n```\n\n2. Create `redeploy.sh` script inside `~/webhooks` folder (you can choose ur folder name but from now i am considering its called webhooks)\n\n```bash\ncd ~\nmkdir webhooks\ncd webhooks\ntouch redeploy.sh hooks.json\n# change redeploy.sh permissions to be executable\nchmod +x redeploy.sh\n```\n\n3. Redeploy script `redeploy.sh` is present in templates folder (you can modify according to your needs)\n\n4. `hooks.json` is present in the `templates` folder of this repo (you can modify according to your needs but read documentation)\n\n- Detailed Reference @ https://github.com/adnanh/webhook/blob/master/docs/Hook-Definition.md\n\n  1. id: This is the endpoint used in url, you can change if you want or keep it as is\n     Note: In our case when creating a webhook on Github, the webhook url would look something like this:\n     https://servername.com/webhooks/github-push\n  2. execute-command: Full Path to the redeploy script (not with ~, example: /home/killer/webhooks/redeploy.sh)\n  3. command-working-directory: Full Path to the working directory, where this redeploy script needs to be executed, in case of a nodejs application, it should be the place where package.json is placed.\n\n  Refer hooks.json in templates folder\n\n  1. If you don't understand just leave it as is, and ONLY change SOME_SUPER_SECRET_FROM_GITHUB_WHILE_CREATING_WEBHOOK, and only specify the branch name to deploy\n  2. Only redeploy on a push to the master branch. Change this value if your branch has a different name\n\n5. Try your hook is testing mode\n\n```bash\ncd ~/webhooks\nwebhook -hooks hooks.json -hotreload -verbose -ip \"127.0.0.1\" -http-methods post\n# Leave the webhook running in verbose mode and try to commit changes and it should work\n```\n\n6. If everything above works and you have automated deployment, woo-hoo, time to do some more stuff, i.e. create a systemd service for the same\n\n- create a file called `webhook.service` inside `/etc/systemd/system`\n- change permission of file webhook.service to 0644 `chmod 644 webhook.service`\n- copy the contents from the `webhook.service` file from the templates folder\n  - NOTE:\n    - **killer** with the name of user who starts this service, can be USERNAME of logged in ssh user\n    - /home/killer/webhooks/hooks.json this is my path, provide a complete path to your hooks.json created above\n- Start and enable the service using:\n  - `sudo systemctl start webhook.service` (starts the service)\n  - `sudo systemctl enable webhook.service` (Enable, so that service is auto-started on reboot)\n  - `sudo systemctl status webhook.service` (Check status, and make sure its running and enabled)\n- If the service is NOT active, it is due to selinux, use the command below and troubleshoot, and it will provide you with details as to what to execute and IT WILL WORK.\n  - `journalctl -xe`\n- In case you want to reload/restart/stop/disbale service:\n  - `sudo systemctl reload webhook.service`\n  - `sudo systemctl restart webhook.service`\n  - `sudo systemctl stop webhook.service`\n  - `sudo systemctl disable webhook.service`\n\n### Fail2Ban\n\n```bash\n# Assuiming you already install epel-repo (which is already done above)\nsudo dnf install fail2ban -y\nsudo systemctl start fail2ban\nsudo systemctl enable fail2ban\n\n# To see failed ssh attempts\ntail -f /var/log/secure\ngrep 'sshd.*Failed password for' /var/log/secure\n```\n\nConfigure Fail2ban settings, create or edit the `/etc/fail2ban/jail.d/jail-sshd.conf` file using a text editor such as vi/vim or nano/emacs, with the contents from file `jail-sshd.conf` inside templated folder, save and exit file, and reload fail2ban\n\n```bash\nsudo systemctl reload fail2ban\n```\n\nHelper commands for fail2ban (sshd)\n\n```bash\nsudo fail2ban-client status sshd\nUnban an IP:\nsudo fail2ban-client set sshd unbanip x.x.x.x\nBan an IP:\nsudo fail2ban-client set sshd banip x.x.x.x\n```\n\n### Firewalld commands\n\n```bash\nsudo dnf install firewalld\n\nsudo systemctl start firewalld\nsudo systemctl enable firewalld\n\nsudo firewall-cmd --add-service http --zone public --permanent\nsudo firewall-cmd --add-service https --zone public --permanent\n\nsudo firewall-cmd --reload\n\n\n# List all ports and services by the following commands\nfirewall-cmd --list-ports\nfirewall-cmd --list-services\n\n# Note about: dhcpv6-client which allows incoming DHCP v6 responses to pass - this is the dhcpv6-client rule. If you're not running DHCP v6 on your network or you are using static IP addressing, then you can disable it.\n\n# Close all other services and ports, which are not required\n# The command refrence is below\nsudo systemctl reload firewalld\nsudo systemctl restart firewalld\nsudo firewall-cmd --state\nsudo firewall-cmd --list-services\nsudo firewall-cmd --get-zones\nsudo firewall-cmd --zone public --list-all\nsudo firewall-cmd --zone=public --permanent --add-service=http\nsudo firewall-cmd --remove-service http --zone public\nfirewall-cmd --get-default-zone\nsudo firewall-cmd --zone=public --add-port=3000/tcp\nsudo firewall-cmd --zone=public --remove-port=3000/tcp\n```\n\n## Timezones and NTP\n\n### Configure Timezones\n\nOur first step is to set our server's timezone. This is a very simple procedure that can be accomplished using the timedatectl command:\n\n- First, take a look at the available timezones by typing:\n\n```bash\nsudo timedatectl list-timezones\n```\n\nThis will give you a list of the timezones available for your server. When you find the region/timezone setting that is correct for your server, set it by typing:\n\n```bash\nsudo timedatectl set-timezone region/timezone\n```\n\n- For instance, to set it to United States eastern time, you can type:\n\n```bash\nsudo timedatectl set-timezone America/New_York\n```\n\nYour system will be updated to use the selected timezone. You can confirm this by typing:\n\n```bash\nsudo timedatectl\n```\n\n### Configure NTP Synchronization\n\nNow that you have your timezone set, we should configure NTP. This will allow your computer to stay in sync with other servers, leading to more predictability in operations that rely on having the correct time.\n\nFor NTP synchronization, we will use a service called **ntp**, which we can install from CentOS's default repositories:\n\n```bash\nsudo dnf install ntp\n```\n\nNext, you need to start the service for this session. We will also enable the service so that it is automatically started each time the server boots:\n\n```bash\nsudo systemctl start ntpd\nsudo systemctl enable ntpd\n```\n\nYour server will now automatically correct its system clock to align with the global servers.\n\n## List services\n\n```bash\nsystemctl list-unit-files\nsystemctl --type=service\n```\n\n## For Nodejs Express Applications always disable \"x-powered-by\" response header inside `app.js`\n\n```bash\napp.disable('x-powered-by');\n```\n\n## Change default SSH Port\n\nWe are going to change the default SSH port (i.e. 22) to something else like 2607\n\n```bash\n# change to root\nsudo su\n# IMPORTANT: First add port to firewall and then reload firewall\nfirewall-cmd --add-port=2607/tcp --zone=public --permanent\nfirewall-cmd --reload\n#Edit this file\nvim /etc/ssh/sshd_config\n# Find commented \"Port 22\" line and change to\nPORT 2607\n# For SELinux systems use the command below (change port accordingly)\nsemanage port -a -t ssh_port_t -p tcp 2607\nsystemctl reload sshd\n```\n\n### Debugging and Errors\n\n- For CANNOT SET LOCALE _ERROR_, login as **root** user for setting locale `utf-8`\n  - `vim /etc/environment`\n  - add these lines - LANG=en_US.utf-8 - LC_ALL=en_US.utf-8\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshuklaritvik06%2Fserver-imp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fshuklaritvik06%2Fserver-imp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshuklaritvik06%2Fserver-imp/lists"}