• 如何创建一个简单 APT 仓库


    1. 无废话版本

    需求: 有一堆 .deb 包,想把它们做成一个 APT 仓库,这样就可以用apk install pkgname进行安装了,这样一方面自己可以规避 dpkg -i xxx.deb 时候的依赖问题,另一方面也方便了其他人

    解决方法:

    mkdir -p /opt/raspi-apt-repos/raspbian8
    cp ~/Downloads/raspbian8/*.deb /opt/raspi-apt-repos/raspbian8
    cd /opt/raspi-apt-repos/raspbian8
    # scan *.deb and create Packages file
    dpkg-scanpackages -m . > Packages
    # create  Release  file 
    apt-ftparchive release . > Release
    
    # TODO: create a GPG key with ‘gpg --gen-key’ if you have no one
    gpg --list-keys  || gpg --gen-key
    # create Release.gpg file
    gpg --armor --detach-sign --sign -o Release.gpg Release
    # create InRelease file
    gpg --clearsign -o InRelease Release
    
    # export your public key
    cd /opt/raspi-apt-repos/
    gpg --export --armor <uid> -o my-repo.gpg-key.asc
    
    # start web server (you'd better use nginx instead)
    python -m SimpleHTTPServer 4000
    
    # ===== 8&lt; ==========================================
    # on client
    wget http://210.32.142.88:4000/my-repo.gpg-key.asc && sudo apt-key add my-repo.gpg-key.asc
    echo “deb http://foo.example.com/repos/raspbian8 ./” | sudo tee /etc/apt/sources.list.d/my-repo.list
    
    1. 什么叫"简单 APT 仓库"

    这里说的是如何创建一个简单的 APT 仓库(根据 DebianRepository/Setup - Debian Wiki ,这种简单的仓库称为 trivial archive, 而复杂的那种称为 official archive.),之所以说"简单",主要是指仓库不采用 pool 结构,里面只有一个suite(jessie, jessie-backport, trusty, trusty-updates 这些东西叫做 suite),一个 component(main, nonfree, universe 这些东西叫做 component),也就是说 Packages 文件只有一个(也不提供Contents-amd64.gz 这些可有可无的文件).

    这个仓库的目录结构如下:

    .
    ├── vim_8.0.1420-0york0~14.04_amd64.deb
    ├── vim_8.1.1575-0york0~14.04_amd64.deb
    ├── vim_8.1.1575-0york0~16.04_amd64.deb
    ├── vim_8.1.1575.orig.tar.gz
    ├── vim-common_8.0.1420-0york0~14.04_all.deb
    ├── vim-common_8.1.1575-0york0~16.04_all.deb
    ├── Packages
    └── Release
    

    使用时需要在 /etc/apt/sources.list 里面添加的配置是:
    deb http://foo.example.com/repos/raspbian8 ./
    (在这个配置里, suite 的值是 ./, 当suite是路径的情况下 component 必须为空.对于sources.list的格式可以参考 sources.list (5) )

    1.1 参考: "不简单"的 APT仓库

    Debian/Ubuntu的官方仓库(以及Launchpad上面的ppa)跟上面的结构不一样,典型特征是顶层有个 dists 目录和 pool 目录.目录结构如下:

    ├── dists/
    │   ├── buster/
    │   ├── jessie/
    │   │   ├── InRelease
    │   │   ├── main/
    │   │   │   ├── binary-aarmhf/
    │   │   │   │   ├── Packages
    │   │   │   │   └── Release
    │   │   │   ├── binary-all/
    │   │   │   │   ├── Packages
    │   │   │   │   └── Release
    │   │   │   └── binary-amd64/
    │   │   │   └── Contents-amd64.gz
    │   │   │   └── Contents-armhf.gz
    │   │   ├── Release
    │   │   └── Release.gpg
    │   └── stretch/
    └── pool/
        └── main/
            ├── v/
            │   └── vim/
            │       ├── vim_8.0.1420-0york0~14.04_amd64.deb
            │       ├── vim_8.1.1575-0york0~14.04_amd64.deb
            │       ├── vim_8.1.1575-0york0~16.04_amd64.deb
            │       ├── vim_8.1.1575.orig.tar.gz
            │       ├── vim-common_8.0.1420-0york0~14.04_all.deb
            │       └── vim-common_8.1.1575-0york0~16.04_all.deb
            └── z/
                └── zim/
    

    对应的 /etc/apt/sources.list 配置大致如下:
    deb http://foo.example.com/repos/raspbian8 jessie main
    (在这个配置里,suite=jessie, component=main,一行可以配置多个component,但不能配置多个suite)

    可以看到,dists 下面按 suite 分了多个目录(stretch, jessie, buster),suite下面又按CPU架构有不同的 binary-${arch} 目录和 Contents-${arch}.gz 文件;而真正的deb包都放在 pool 目录下,对于vim这样一个软件来说多个suite/cpu的deb包是放在一起的.

    这样的好处是:

    • 将所有类型CPU的包列表(Packages或者Packages.gz文件)放在一个文件里面,这样每个机器要获取的包列表就比较小(考虑到Debian/Ubuntu所收录的软件数,这个节省是比较可观的;而 Contents-${arch}.gz 里面存放的是所有包里面的文件列表( apt-file 程序会使用到这个信息),这个文件的体积就更大了)
    • 不同套件/不同CPU可共用的deb包(主要是那些 _all.deb)和源代码包,也只在 pool/v/vim 这样的目录下存放一份,不同存放多份
    • 源代码包( .dsc, orig.tar.xz)有路径存放,这样 dget / apt source 可以取到源代码包

    不过缺点就是:这样的仓库很复杂,不靠工具是无法确定构建出一个 deb 之后要往哪里放,要更新哪些索引文件(Packages, Contents-${arch}, Release, InRelease...)

    1. 创建简单 APT 仓库的方法

    根据 DebianRepository/Setup - Debian Wiki ,有很多种工具可以用来创建 APT 仓库,但创建简单仓库(trivial archive)的方法在该页面就只看见一个,于是我就用了:

    1. 将所有 deb 放到同一个目录(假设这个目录为 /opt/raspi-apt-repos/raspbian8)
    2. cd /opt/raspi-apt-repos/raspbian8 && dpkg-scanpackages -m . > Packages

    这就完成了,是不是很简单?(其实事儿没完呢) 可以看到唯一的变化就是多了一个 Packages 文件,里面是各个deb包的相对路径\描述\大小(也就是都是客户端用 apt show xxx 可以查到的内容)

    使用:

    • 如果仅仅是本机使用,那么可以在 /etc/apt/sources.list 里面添加一行 deb file:/opt/raspi-apt-repos/raspbian8 ./ 然后就可以 apt update && apt install vim 了.
    • 如果要提供给其它机器使用,那么需要安装配置 nginx 之类的web server (临时试验可以用 python -m SimpleHttpServer 启动一个web server).假设在web server里面将 /opt/raspi-apt-repos/ 映射到了 http://foo.example.com/repos, 那么对方需要在 /etc/apt/sources.list 里面添加的配置就是 deb http://foo.example.com/repos/raspbian8 ./ 

    补充说明:

    1. dpkg-scanpackages 这个工具在 dpkg-dev 这个包里面,需要先安装这个包才能使用.这里有个好处是 Centos 的EPEL 库里面也有这个包,所以 RHEL / CentOS 上也就可以很方便地装上这个工具再按上述方法创建一个 APT 仓库(其实我工作中同时要给 Ubuntu 14.04/16.04/18.04, CentOS 6/7, SLES 12.4/15 同时提供包仓库,服务器是一台CentOS 7)

    2. 上面命令行中给dpkg-scanpackages 加了 -m 选项,这个选线的作用是如果同名的包有多个版本,都把它们采集到 Packages 文件中去,这样用户可以自己用 apt show vim 看到所有的版本,然后用 apt install foo=1.12 这样的方式来安装指定的版本.(如果你的目录内有同名的包,而又没有使用 -m 选项的话,后扫描到的包会被忽略,不进入 Packages 文件.另外需要注意的是,abc_1.1-1_i386.deb 和 abc_2.0-1_amd.deb 这种针对不同 CPU也会被认为是同名的包,其中一个会被忽略掉!)

    3. Ubuntu 16.04 / Debian 8 以上版本拒绝上述简单仓库的解决办法


    3.1 Release 文件

    如果用户端是 Ubuntu 16.04 / Debian 8 (jessie) 或者更高版本的话, 这个仓库会被 apt 拒绝接受,并报告如下的错误信息:

    $ sudo apt update
    Get:1 file:/opt/raspi-apt-repo/ubuntu18 ./ InRelease
    Ign:1 file:/opt/raspi-apt-repo/ubuntu18 ./ InRelease
    Get:2 file:/opt/raspi-apt-repo/ubuntu18 ./ Release
    Err:2 file:/opt/raspi-apt-repo/ubuntu18 ./ Release
      File not found - /opt/raspi-apt-repo/ubuntu18/./Release (2: No such file or directory)
    Hit:3 http://ports.ubuntu.com bionic InRelease
    Hit:4 http://ports.ubuntu.com bionic-updates InRelease
    Hit:5 http://ports.ubuntu.com bionic-security InRelease
    Hit:6 http://ppa.launchpad.net/ubuntu-pi-flavour-makers/ppa/ubuntu bionic InRelease
    Hit:7 http://ports.ubuntu.com bionic-backports InRelease
    Reading package lists... Done
    E: The repository 'file:/opt/raspi-apt-repo/ubuntu18 ./ Release' does not have a Release file.
    N: Updating from such a repository can't be done securely, and is therefore disabled by default.
    N: See apt-secure(8) manpage for repository creation and user configuration details.
    

    可以看到,apt 由于没有从仓库读取到 Release 文件,于是不再读取 Packages 文件,这个仓库被拒绝了 (虽然前面也试图读取 InRelease 文件并且失败了,但这个错误是可以忽略的(行首是 Ign,而读取Release 那行是 Err

    出现这个问题的原因在于, apt 对了加强安全性, 需要仓库生成一个 Release 文件,这个文件里面包含了 Packages 等文件的大小和校验和(包含MD5/SHA1/SHA256/SHA512 多种值).如果这个文件里面所描述的 Packages 大小与校验和与实际读取到的文件不一致,apt 也会拒绝这个仓库.

    Date: Sat, 29 Jun 2019 07:18:59 +0000
    MD5Sum:
     7a487761b83fc9326ba4a3186df3b950             5384 Packages
     256a066585672866ffd831926ab42b77               38 Release
    SHA1:
     92c867ff5789ae6efdcc500d59d220b7e30bc952             5384 Packages
     d1c369af68084f30a6d3cf8d98066d4a723a459c               38 Release
    SHA256:
     a9ae09e62999cd2b7b7f7fab48ae04e28ac3d2a6a6613df5e3c93bffe11d9166             5384 Packages
     2124019cd02360d2f5466d40bb097920f2415e997a9fb20c78380bb04d5c33ff               38 Release
    SHA512:
     68da75f205561ce06b327502611c0f01d6401e048e17dcf2960d96c04397fbdf20252d2ccb0e997570834572ede7a6376162caec3411560bc5523a06413f634d             5384 Packages
     b5b3f6a48013239e70c34f623f5e69594c6b11f27f8da8325067f97f5801c73208c9adb66b0d1cce0186d1102c1013684c615077d8dcebb590027390969e0b54               38 Release
    

    生成这个 Release 文件的方法是: apt-ftparchive release . > Release

    3.2 Release.gpg 和 InRelease 文件

    生成了 Release 文件之后,用户端运行 apt update,还是会碰到问题:

    $ sudo apt update
    Get:1 file:/opt/raspi-apt-repo/ubuntu18 ./ InRelease
    Ign:1 file:/opt/raspi-apt-repo/ubuntu18 ./ InRelease
    Get:2 file:/opt/raspi-apt-repo/ubuntu18 ./ Release [816 B]
    Get:2 file:/opt/raspi-apt-repo/ubuntu18 ./ Release [816 B]
    Get:3 file:/opt/raspi-apt-repo/ubuntu18 ./ Release.gpg                                        
    Ign:3 file:/opt/raspi-apt-repo/ubuntu18 ./ Release.gpg                                   
    Hit:4 http://ports.ubuntu.com bionic InRelease                                                               
    Hit:5 http://ports.ubuntu.com bionic-updates InRelease                                             
    Hit:6 http://ports.ubuntu.com bionic-security InRelease                                            
    Hit:7 http://ports.ubuntu.com bionic-backports InRelease                 
    Hit:8 http://ppa.launchpad.net/ubuntu-pi-flavour-makers/ppa/ubuntu bionic InRelease
    Reading package lists... Done                      
    E: The repository 'file:/opt/raspi-apt-repo/ubuntu18 ./ Release' is not signed.
    N: Updating from such a repository can't be done securely, and is therefore disabled by default.
    N: See apt-secure(8) manpage for repository creation and user configuration details.
    

    可以看到,Release 文件读到了,但 apt 认为 *Release is not signed”,于是认为从这个仓库安装软件还是不安全的,Packages 文件还是没有被读取.

    这个 Release.gpg又是什么东西,该怎样生成它呢?

    根据 DebianRepository/Format - Debian Wiki 的说法, The file "Release.gpg" contains a GPG signature,并且与那个 InRelease 文件有关系:

    The file "dists/$DIST/InRelease" shall contain meta-information about the distribution and checksums for the indices, possibly signed with a GPG clearsign signature (for example created by "gpg -a -s --clearsign"). For older clients there can also be a "dists/$DIST/Release" file without any signature and the file "dists/$DIST/Release.gpg" with a detached GPG signature of the "Release" file, compatible with the format used by the GPG options "-a -b -s".

    InRelease files are signed in-line while Release files should have an accompanying Release.gpg file.

    这里说 Release.gpg 是个签名文件(Technically speaking, this is an ascii-armored detached gpg signature.) ,随同 Release 出现的,比较老的客户端只认这两个文件; 而 InRelease 是内嵌签名的(也就是说,将原来 Release 的内容和 Release.gpg 的内容揉到一起了 - 注意这里不是简单地拼到一起),新的客户端才支持这个这个文件.观察一下 Debian 和 Ubuntu 的仓库 ( http://mirrors.ustc.edu.cn/debian/dists/jessie/, http://mirrors.ustc.edu.cn/ubuntu/dists/xenial/ ) , 可以看到 Debian 的仓库只有 Release 和 Release.gpg 这两个文件,而 Ubuntu 仓库里面这三个文件都有.

    下面说说如何生成这两个文件:

    1. 如果你跟我一样不知道什么是GPG数字签名,那么先仔细读一下 GPG入门 - 简书 或者 GnuPG 入门教程 | IterNull Blog ,大致知道密钥/公钥, 加密/签名, 文本签名/二进制签名,分离式签名等等概念
    2. 按照上面文档描述的方法,生成自己的gpg key (gpg --gen-key)
    3. 生成Release.gpg: gpg --armor --detach-sign --sign -o Release.gpg Release
    4. 生成InRelease: gpg --clearsign -o InRelease Release

    3.3 公钥不被信任问题

    以为问题就都解决了吗?用户方再执行一遍试试

    $ sudo apt update
    [sudo] password for bamanzi: 
    Get:1 file:/opt/raspi-apt-repo/ubuntu18 ./ InRelease
    Ign:1 file:/opt/raspi-apt-repo/ubuntu18 ./ InRelease
    Get:2 file:/opt/raspi-apt-repo/ubuntu18 ./ Release [816 B]
    Get:2 file:/opt/raspi-apt-repo/ubuntu18 ./ Release [816 B]
    Get:3 file:/opt/raspi-apt-repo/ubuntu18 ./ Release.gpg [659 B]
    Get:3 file:/opt/raspi-apt-repo/ubuntu18 ./ Release.gpg [659 B]
    Ign:3 file:/opt/raspi-apt-repo/ubuntu18 ./ Release.gpg                                          
    Hit:4 http://ports.ubuntu.com bionic InRelease                                                               
    Get:5 http://ports.ubuntu.com bionic-updates InRelease [88.7 kB]                                                                               
    Hit:6 http://ppa.launchpad.net/ubuntu-pi-flavour-makers/ppa/ubuntu bionic InRelease                                                            
    Hit:7 http://ports.ubuntu.com bionic-security InRelease                                                                                        
    Hit:8 http://ports.ubuntu.com bionic-backports InRelease                                                                                       
    Get:9 http://ports.ubuntu.com bionic-updates/main armhf Packages [508 kB]                                                                      
    Get:10 http://ports.ubuntu.com bionic-updates/universe armhf Packages [812 kB]                                                                 
    Reading package lists... Done                                                                                                                  
    W: GPG error: file:/opt/raspi-apt-repo/ubuntu18 ./ Release: The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 722D2AFAD8BAD548
    E: The repository 'file:/opt/raspi-apt-repo/ubuntu18 ./ Release' is not signed.
    N: Updating from such a repository can't be done securely, and is therefore disabled by default.
    N: See apt-secure(8) manpage for repository creation and user configuration details.
    

    可以看到, apt 还是没有读取 Packages 文件,原因是 The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 722D2AFAD8BAD548
    也就是说 InRelease / Release.gpg 虽然签名了,但由于这个签名所用的公钥没有被接受(NO_PUBKEY 722D2AFAD8BAD548)

    解决办法有三个:

    1. 用户端在执行 apt update 的时候,添加 --allow-insecure-repositories 选项;在执行 apt install pkg的时候,添加 --allow-unauthenticated 选项

    后面这个--allow-unauthenticated不加也可以,但 apt 会报告如下告警:

    WARNING: The following packages cannot be authenticated!
      foo bar baz
    Install these packages without verification [y/N]?

    需要输入 y 才能成功安装软件包(如果是自动化脚本执行安装的话, apt update -y foo 这样只添加 -y 是不够的,必须增加上面的选项)

    1. 第二个方法是在用户侧修改仓库的配置,改为 deb [trusted=yes] http://foo.example.com/repos/raspbian8 ./ ,注意这里添加了 trusted=yes 选项 .(这里的修改方法是只对此仓库添加信任选项;网上也有人说在 /etc/apt/apt.conf.d 下面某个文件里面添加 APT::Get::AllowUnauthenticated “true"Acquire::AllowInsecureRepositories "true" 这样两行,但这样影响的所有仓库,从安全的角度来说并不推荐)

    2. 第三个方法首先要服务侧将其公钥导出 (gpg --export --armor <uid> -o my-repo.gpg-key.asc )并且以某种方式将这个 my-repo.gpg-key.asc 文件提供给用户( 比如公布在仓库根目录让用户手工下载);然后用户将此公钥导入表示他信任此公钥: sudo apt-key add my-repo.gpg-key.asc

    3. 链接


  • 相关阅读:
    Redis
    Maven总结
    spring知识点总结
    网上好文搜集整理
    python 代码删除空目录
    plantUML使用指南
    python的基础操作
    八卦基础编程学习
    python历年入坑记录大全
    python实现的百度云自动下载
  • 原文地址:https://www.cnblogs.com/bamanzi/p/create-simple-apt-repo.html
Copyright © 2020-2023  润新知