1. 程式人生 > 其它 >基於golang+openssh 服務實現一個簡單的git over ssh 服務

基於golang+openssh 服務實現一個簡單的git over ssh 服務

昨天看了開源的codefever 以及以前簡單學習過gogs,剛才學習下git over ssh 的實現機制
基於openssh + golang (golang 部分參考了gogs 處理)實現了一個簡單的git server (ssh 協議的)

原理說明

核心還是我們的openssh server 建立一個git 賬戶,此賬戶使用了authorized_keys的forcecommand 功能
forcecommand 中我們添加了git 的處理

  • forcecommand 程式碼
package main
import (
    "fmt"
    "log"
    "os"
    "os/exec"
    "strings"
)
func init() {
    file := "./" + "message" + ".txt"
    logFile, err := os.OpenFile(file, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0766)
    if err != nil {
        panic(err)
    }
    log.SetOutput(logFile)
    log.SetFlags(log.LstdFlags | log.Lshortfile | log.LUTC)
}
func parseSSHCmd(cmd string) (string, string) {
    ss := strings.SplitN(cmd, " ", 2)
    if len(ss) != 2 {
        return "", ""
    }
    return ss[0], strings.Replace(ss[1], "'/", "'", 1)
}
func main() {
  // forcecommand 會包含一個SSH_ORIGINAL_COMMAND 裡邊是git 的一些操作命令,包含了具體命令的處理
    println(os.Getenv("SSH_ORIGINAL_COMMAND"))
    sshCmd := os.Getenv("SSH_ORIGINAL_COMMAND")
    verb, args := parseSSHCmd(sshCmd)
    repoFullName := strings.ToLower(strings.Trim(args, "'"))
    verbs := strings.Split(verb, " ")
    var gitCmd *exec.Cmd
    gitRepoPath := fmt.Sprintf("/opt/gitrepo/%s", repoFullName)
    os.Setenv("MY_UID", "dalongrong")
    if len(verb) == 2 {
        println(verbs[0], verbs[1], gitRepoPath)
        gitCmd = exec.Command(verbs[0], verbs[1], gitRepoPath)
    } else {
        println(verbs[0], gitRepoPath)
        gitCmd = exec.Command(verb, gitRepoPath)
    }
    gitCmd.Stdin = os.Stdin
    gitCmd.Stdout = os.Stdout
    gitCmd.Stderr = os.Stderr
    if err := gitCmd.Run(); err != nil {
        log.Fatal("Internal error", "Failed to execute git command: %v", err)
    }
    return
}

docker 構建

FROM golang:1.17-alpine AS build-env
WORKDIR /go/src/app
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
ENV  GO111MODULE=on
ENV  GOPROXY=https://goproxy.cn
COPY . .
RUN apk update && apk add git \
    && go build -o git-shell
FROM alpine:latest
WORKDIR /app
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
RUN apk update && apk add ca-certificates && rm -rf /var/cache/apk/*
COPY --from=build-env /go/src/app/git-shell /app/git-shell
  • authorized_keys 配置格式
    目前比較簡單,沒有進行復雜的校驗處理
    command 部分使用了自己開發的git 處理
command="/opt/git-shell",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty  <ssh public key>
  • openssh 服務啟動
    基於docker-compose 執行
version: '3'
services:
  ssh:
    image: dalongrong/openssh-server
    build: ./
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Asia/Shanghai
      - SUDO_ACCESS=true #optional
      - PASSWORD_ACCESS=true #optional
      - USER_PASSWORD=dalongdemo #optional
      - USER_NAME=git #optional
    volumes:
      - ./config:/config
      - ./gitrepo:/opt/gitrepo
    ports:
      - "2222:2222"

使用說明

  • 建立bare git repo
    啟動之後我們需要建立git repo 注意是bare 模式的
    進入openssh 容器
su git 
cd /opt/gitrepo
git init  --bare demoapp2.git

效果

  • 新增ssh 認證客戶
    config的.ssh 資料夾中的authorized_keys 格式如上
  • clone 程式碼
git clone  ssh://git@localhost:2222/demoapp2.git demoapp2

效果


push 程式碼

cd demoapp2
touch rong.txt
git add --all
git commit -m "add"
git push

效果

說明

如果有許可權的問題就可能需要設定先許可權了(openssh 容器內執行)

chmod 0755 /opt/gitrepo  /config
chmod 700 /config/.ssh

以上是一個簡單的學習,參考了gogs、gitlab 等開源專案,基於此剛才對於git ssh server 就有了一個比較完整的瞭解
完整程式碼已經放github 了,大家可以參考,實際上目前一些開源的git 支援ssh 協議的基本都是這個套路,只是authorized_keys
的forcecommand 處理上大家好多是不一樣的,而且新版本的gitlab 已經自己寫了一個ssh server,同時推薦基於AuthorizedKeysCommand
更好的優化處ssh key 的處理了

參考資料

https://github.com/rongfengliang/write-one-git-ssh-server
https://github.com/gogs/gogs
https://github.com/PGYER/codefever
https://git-scm.com/docs/git-receive-pack
https://git-scm.com/docs/git-upload-pack
https://www.ssh.com/academy/ssh/authorized_keys/openssh
https://blog.scalesec.com/just-in-time-ssh-provisioning-7b20d9736a07
https://git-scm.com/book/zh/v2/Git-%E5%86%85%E9%83%A8%E5%8E%9F%E7%90%86-%E4%BC%A0%E8%BE%93%E5%8D%8F%E8%AE%AE
https://git-scm.com/docs/pack-protocol/2.2.3