| @ -0,0 +1,278 @@ | |||
| # The Premis | |||
| Last week I started hosting my own git-forge to track sync all of my | |||
| git projects. Between school, open-source communities, and personal | |||
| projects, I have accumulated a dubious amount of git projects. | |||
| Although most of my content gets hosted on Github, I also had a fair | |||
| quantity of local projects, stuff on scattered GitLab instances, and | |||
| other places. I decided to use Gitea to mirror all of my repositories | |||
| and keep them in a central location that I can quickly search for | |||
| them. | |||
|  | |||
| For simplicity, I decided to host my Gitea instance on a DigitalOcean | |||
| droplet using docker and add SSL encryption using a reverse Nginx | |||
| proxy using a let's encrypt. | |||
| # Installation | |||
| The first step was to set up a VM in the cloud. I used a base Debian | |||
| server on Digital Ocean. I tend to use Debian for servers because it | |||
| is stable and has an extensive collection of packages. | |||
| ## Docker and Docker Compose | |||
| I included the commands necessary to install docker bellow. These | |||
| instructions may get stale, but the gist is going to remain the same | |||
| for the foreseeable future. We are adding the docker's certificates to | |||
| our package manager and then installing it. | |||
| ``` | |||
| apt update | |||
| apt upgrade | |||
| apt install apt-transport-https ca-certificates curl software-properties-common gnupg2 | |||
| curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add - | |||
| add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | |||
| apt update | |||
| apt install docker-ce | |||
| ``` | |||
| Install Docker-Compose | |||
| ``` | |||
| curl -L https://github.com/docker/compose/releases/download/1.25.0-rc2/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose | |||
| chmod +x /usr/local/bin/docker-compose | |||
| ``` | |||
| ## Gitea over Docker Compose | |||
| For simplicity, I am running Gitea using docker-compose because it | |||
| makes it portable between systems and makes for a straightforward | |||
| install. You can find the official Gitea instructions on this | |||
| [here](https://docs.gitea.io/en-us/install-with-docker/). | |||
| The just of the running entails creating a file called | |||
| docker-compose.yml with the following contents: | |||
| ```yml | |||
| version: "2" | |||
| networks: | |||
| gitea: | |||
| external: false | |||
| services: | |||
| server: | |||
| image: gitea/gitea:latest | |||
| environment: | |||
| - USER_UID=1000 | |||
| - USER_GID=1000 | |||
| restart: always | |||
| networks: | |||
| - gitea | |||
| volumes: | |||
| - ./gitea:/data | |||
| - /etc/timezone:/etc/timezone:ro | |||
| - /etc/localtime:/etc/localtime:ro | |||
| ports: | |||
| - "3000:3000" | |||
| - "222:22" | |||
| ``` | |||
| Once we have our docker-compose.yml saved, we can launch it when we | |||
| are in the same directory as it using docker-compose. After this | |||
| launches we now have our Gitea server running on port 3000 | |||
| ``` | |||
| docker-compose build | |||
| docker-compose up | |||
| ``` | |||
| ## Nginx and Let's Encrypt | |||
| Although our server is running publicly on port 3000, we don't want to | |||
| use it yet because, by default, it is running on HTTP, which is not | |||
| encrypted. HTTP makes it possible for people sitting in the middle of | |||
| your connection to listen to your traffic and see all the passwords | |||
| and data that you send to your Gitea server. We are going to be using | |||
| a [Nginx](https://nginx.org/) reverse proxy with [Let's | |||
| Encrypt](https://letsencrypt.org/) to add HTTPS encryption. | |||
| Installing Nginx is easy because it is in most Linux package managers. | |||
| ``` | |||
| apt-get install nginx | |||
| ``` | |||
| Next, we tell Systemd to start Nginx on startup. | |||
| ``` | |||
| systemctl enable nginx | |||
| ``` | |||
| Next we modify the Nginx config file to add a reverse proxy. This will | |||
| forward all traffic on git.jrtechs.net to the localhosts's port 3000. | |||
| ``` | |||
| vim /etc/nginx/sites-available/default | |||
| ``` | |||
| Add this content to the very bottom of the default config file | |||
| changing "git.jrtechs.net." | |||
| ``` | |||
| server | |||
| { | |||
| listen 80; | |||
| server_name git.jrtechs.net; | |||
| location / { | |||
| proxy_pass http://localhost:3000; | |||
| proxy_http_version 1.1; | |||
| proxy_set_header Upgrade $http_upgrade; | |||
| proxy_set_header Connection 'upgrade'; | |||
| proxy_set_header Host $host; | |||
| proxy_cache_bypass $http_upgrade; | |||
| } | |||
| } | |||
| ``` | |||
| This command tests the Nginx file you just modified to make sure it is | |||
| syntactically correct. | |||
| ``` | |||
| nginx -t | |||
| ``` | |||
| Next, we reload Nginx starting the reverse Nginx proxy we just | |||
| created. | |||
| ``` | |||
| /etc/init.d/nginx reload | |||
| ``` | |||
| Now that Nginx got configured, we can set-up Certbot for encryption. | |||
| ``` | |||
| sudo add-apt-repository ppa:certbot/certbot | |||
| sudo apt install python-certbot-nginx | |||
| ``` | |||
| This step is beautiful because it has the Let's Encrypt Certbot modify | |||
| the Nginx configuration files to make it work over https. When | |||
| prompted, select the option that redirects all HTTP traffic to https. | |||
| ``` | |||
| systemctl stop nginx | |||
| certbot --authenticator standalone --installer nginx -d git.jrtechs.net | |||
| systemctl start nginx | |||
| ``` | |||
| Now you should be able to access your Gitea instance using your domain | |||
| name. By default, the first user that logs into the Gitea instance | |||
| becomes an administrator. | |||
| # Results | |||
| Now you have a private git-forge that you can access anywhere in the | |||
| world over HTTPS and quickly search and display all your git profiles. | |||
|  | |||
| The profile for each user you create on Gitea looks shockingly similar | |||
| to Github's. | |||
|  | |||
| One beautiful thing about Gitea is that you can easily import and | |||
| mirror git repositories from other sources. | |||
|  | |||
| # Transferring Github Projects to Gitea Instance | |||
| Although it is possible to import every single repository you have in | |||
| Github manually, this quickly becomes a nuisance if you have 60+ | |||
| repositories on Github like me. | |||
| I found and modified a python script that imports all your Github | |||
| repositories into Gitea. What is particularly dandy about this script | |||
| is that it uses the Github API to get both your public and private | |||
| repositories. If the repository is private on Github, it gets | |||
| transferred over as a private repository into Gitea. This script also | |||
| mirrors in every repo from your organizations. I posted this script on | |||
| my | |||
| [Github](https://github.com/jrtechs/dot_files/tree/master/docker/gitea). | |||
| To run this script, you need to generate API tokens for both Github | |||
| and Gitea. Instructions for the Gitea API tokens are found | |||
| [here](https://docs.gitea.io/en-us/api-usage/), and the Github token | |||
| can be found | |||
| [here](https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line). | |||
| The generated tokens get placed in ".gitea-api" and ".github-token" | |||
| ```python | |||
| #!/usr/bin/env python -B | |||
| from github import Github # https://github.com/PyGithub/PyGithub | |||
| import requests | |||
| import json | |||
| import sys | |||
| import os | |||
| gitea_url = "http://127.0.0.1:3000/api/v1" | |||
| # generage gitea token https://docs.gitea.io/en-us/api-usage/ | |||
| gitea_token = open(os.path.expanduser("~/.gitea-api")).read().strip() | |||
| session = requests.Session() # Gitea | |||
| session.headers.update({ | |||
| "Content-type" : "application/json", | |||
| "Authorization" : "token {0}".format(gitea_token), | |||
| }) | |||
| r = session.get("{0}/user".format(gitea_url)) | |||
| if r.status_code != 200: | |||
| print("Cannot get user details", file=sys.stderr) | |||
| exit(1) | |||
| gitea_uid = json.loads(r.text)["id"] | |||
| github_username = "jrtechs" | |||
| github_token = open(os.path.expanduser("~/.github-token")).read().strip() | |||
| gh = Github(github_token) | |||
| for repo in gh.get_user().get_repos(): | |||
| m = { | |||
| "repo_name" : repo.full_name.replace("/", "-"), | |||
| "description" : repo.description or "not really known", | |||
| "clone_addr" : repo.clone_url, | |||
| "mirror" : True, | |||
| "private" : repo.private, | |||
| "uid" : gitea_uid, | |||
| } | |||
| if repo.private: | |||
| m["auth_username"] = github_username | |||
| m["auth_password"] = "{0}".format(github_token) | |||
| jsonstring = json.dumps(m) | |||
| r = session.post("{0}/repos/migrate".format(gitea_url), data=jsonstring) | |||
| if r.status_code != 201: # if not CREATED | |||
| if r.status_code == 409: # repository exists | |||
| continue | |||
| print(r.status_code, r.text, jsonstring) | |||
| ``` | |||
| Sit back and have some tea because this script can take a hot second | |||
| to run. | |||