基于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

效果
基于golang+openssh 服务实现一个简单的git over ssh 服务

 

 

  • 添加ssh 认证客户
    config的.ssh 文件夹中的authorized_keys 格式如上
  • clone 代码
 
git clone  ssh://git@localhost:2222/demoapp2.git demoapp2

效果
基于golang+openssh 服务实现一个简单的git over ssh 服务

 

 


push 代码

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

效果
基于golang+openssh 服务实现一个简单的git over ssh 服务

 

 

说明

如果有权限的问题就可能需要设置先权限了(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

上一篇:C++的new与malloc


下一篇:RHCSA学习第一天