Skip to content
Search

SonatypeNexus:Apt

lastmod 2025-02-14created 2018-09-0421 edits

A Nexus Repository 3 plugin that allows usage of apt repositories.

Build

## Clone the project:
$ git clone https://github.com/sonatype-nexus-community/nexus-repository-apt
## Build the plugin:
$ cd nexus-repository-apt
$ mvn

Docker

Build with docker and create an image based on nexus repository 3

$ docker build -t nexus-repository-apt:3.11.0 .

Run a docker container from that image

$ docker run -d -p 8081:8081 --name nexus-repo nexus-repository-apt:3.11.0

Upload package

Manually upload a package to a new created repo:

$ curl -u user:pass -X POST -H "Content-Type: multipart/form-data" --data-binary "@package.deb" http://nexus_url:8081/repository/repo_name/

When using --data-binary in curl, you must put @ in front of the file name.

How to use

APT Settings APT Browse Select apt (hosted) as the repository type, then generate a GPG Key as follows.

$ gpg --default-new-key-algo rsa4096 --gen-key

Enter a user name (5 or more characters) and an E-mail, and leave the Passphrase empty for convenience.

Then export the GPG Key to a file. Put the user name you entered above in the [KEYSPEC] part.

$ gpg -ao public.gpg --export [KEYSPEC]
$ gpg -ao private.gpg --export-secret-key [KEYSPEC]

Enter the APT Distribution name and the private key. (Assume the distribution name is stable)

WARNING

Enter the private key in the PGP signing key pair field shown in the screenshot on the right.
Also, when entering the private key, you must remove -----BEGIN PGP PRIVATE KEY BLOCK----- and -----END PGP PRIVATE KEY BLOCK-----.

On the client, add the repository above to the /etc/apt/sources.list file.

deb https://nexus.local:8080/repository/my/ stable main

WARNING

For the components after the URL, append the list of Directories under the /metadata/dists path.

On the client, add the public key to apt.

$ apt-key add public.gpg

Update the apt repository.

$ apt-get -o Acquire::https::nexus.local::Verify-Peer=false update

Troubleshooting

What is "Signing Key"?

When creating a new repository, you can find out what to enter in the Signing Key field as follows.

1. The Exception appears to come from where the readSecretKey() function is used in the plugin repository's ./src/main/java/net/staticsnow/nexus/repository/apt/internal/gpg/AptSigningFacet.java file. So check that function:

  private PGPSecretKey readSecretKey() throws IOException, PGPException {
    PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(
        PGPUtil.getDecoderStream(new ByteArrayInputStream(config.keypair.getBytes())),
        new JcaKeyFingerprintCalculator());

    Iterator<PGPSecretKeyRing> keyRings = pgpSec.getKeyRings();
    while (keyRings.hasNext()) {
      PGPSecretKeyRing keyRing = (PGPSecretKeyRing) keyRings.next();

      Iterator<PGPSecretKey> keys = keyRing.getSecretKeys();
      while (keys.hasNext()) {
        PGPSecretKey key = (PGPSecretKey) keys.next();

        if (key.isSigningKey()) {
          return key;
        }
      }
    }

    throw new IllegalStateException("Can't find signing key in key ring.");
  }

2. Check the PGPUtil.getDecoderStream function, which uses the ByteArray from config.keypair.getBytes() as its input. [https://www.borelly.net/cb/docs/javaBC-1.4.8/pg/org/bouncycastle/openpgp/PGPUtil.html#getDecoderStream(java.io.InputStream PGPUtil.getDecoderStream]

Return either an ArmoredInputStream or a BCPGInputStream based on whether the initial characters of the stream are binary PGP encodings or not.

3. Check the library's implementation. In Github - bcgit/bc-java, the path is pg/src/main/java/org/bouncycastle/openpgp/PGPUtil.java.

    /**
     * Obtains a stream that can be used to read PGP data from the provided stream.
     * <p>
     * If the initial bytes of the underlying stream are binary PGP encodings, then the stream will
     * be returned directly, otherwise an {@link ArmoredInputStream} is used to wrap the provided
     * stream and remove ASCII-Armored encoding.
     * </p>
     *
     * @param in the stream to be checked and possibly wrapped.
     * @return a stream that will return PGP binary encoded data.
     * @throws IOException if an error occurs reading the stream, or initialising the
     * {@link ArmoredInputStream}.
     */
    public static InputStream getDecoderStream(
        InputStream in)
        throws IOException
    {
        if (!in.markSupported())
        {
            in = new BufferedInputStreamExt(in);
        }

        in.mark(READ_AHEAD);

        int ch = in.read();


        if ((ch & 0x80) != 0)
        {
            in.reset();

            return in;
        }
        else
        {
            if (!isPossiblyBase64(ch))
            {
                in.reset();

                return new ArmoredInputStream(in);
            }

            byte[] buf = new byte[READ_AHEAD];
            int count = 1;
            int index = 1;

            buf[0] = (byte)ch;
            while (count != READ_AHEAD && (ch = in.read()) >= 0)
            {
                if (!isPossiblyBase64(ch))
                {
                    in.reset();

                    return new ArmoredInputStream(in);
                }

                if (ch != '\n' && ch != '\r')
                {
                    buf[index++] = (byte)ch;
                }

                count++;
            }

            in.reset();

            //
            // nothing but new lines, little else, assume regular armoring
            //
            if (count < 4)
            {
                return new ArmoredInputStream(in);
            }

            //
            // test our non-blank data
            //
            byte[] firstBlock = new byte[8];

            System.arraycopy(buf, 0, firstBlock, 0, firstBlock.length);

            try
            {
                byte[] decoded = Base64.decode(firstBlock);

                //
                // it's a base64 PGP block.
                //
                if ((decoded[0] & 0x80) != 0)
                {
                    return new ArmoredInputStream(in, false);
                }

                return new ArmoredInputStream(in);
            }
            catch (DecoderException e)
            {
                throw new IOException(e.getMessage());
            }
        }
    }

4. Looking at the function and the Exception message on upload, you should enter the Secret Key as a Base64 string.

See also

Favorite site

author Zer0 sitemap