40

I've got a Maven project that I'm trying to configure to use the maven release plugin. Part of the release process is to use the Maven GPG Plugin to sign artifacts which requires among other things, the GPG signing key passphrase to succeed. Because these builds need to be runnable in a non interactive environment, (CI-Server) these params are passed in as arguments to maven in the form of

-Dgpg.passphrase=XXX

For snapshot builds everything works fine; the Maven GPG Plugin sees the passed in passphrase, artifacts are built, signed and deployed as expected, however, when I try to use the release plugin I get prompted for the gpg signing key password. I've read through several discussions on similar issues that stem from the release plugin forking another invocation of maven which does not receive the passed in params. The most popular fix seems to be to use the "arguments" parameter like this:

-Darguments="-Dgpg.passphrase=XXX"

Supposedly this gets passed to the forked instance but unfortunately for me it's not getting rid of the prompt.

Since signing artifacts is not an uncommon prerequisite for deploying release artifacts to public maven repos and presumably most entities producing those artifacts are using some form of CI I can't imagine I'm the only person who has encountered this problem. Has anybody found a workaround?

A NOTE ABOUT THE ACCEPTED ANSWER:

The accepted solution will -not- work with Maven 3.0 - 3.0.3 and 3.0.3 just so happens to be what installs by default with java on OSX Mountain Lion. You'll need to upgrade to 3.0.4.

Nick
  • 8,181
  • 4
  • 38
  • 63
  • If you want to make a comment about the accepted answer then please use the comment option below that answer – Dharman Feb 02 '21 at 15:29

7 Answers7

33

Just set it up in a profile in settings.xml and activate it by default:

<settings>
  <profiles>
    <profile>
      <id>gpg</id>
      <properties>
        <gpg.executable>gpg2</gpg.executable>
        <gpg.passphrase>mypassphrase</gpg.passphrase>
      </properties>
    </profile>
  </profiles>
  <activeProfiles>
    <activeProfile>gpg</activeProfile>
  </activeProfiles>
</settings>

As you can see you can do that with any property .. e.g. also other usernames and passwords for the jarsigner plugin and so on.

This should be always active. It might depend on using a newer Maven version but you can always debug this with

mvn help:active-profiles

Encrypting the password

The comments and other answers are pointing out that keeping passwords in a file is not secure... This is true to an extent, but luckily Maven allows us to make this very secure by creating one master password and then encrypting all the passwords in settings.xml with it.

Have a look at the mini guide Password Encryption for details.

Stijn de Witt
  • 40,192
  • 13
  • 79
  • 80
Manfred Moser
  • 29,539
  • 13
  • 92
  • 123
  • How would this work with a pom.xml that already defines a default profile? I can't add these properties to the corresponding default profile in pom.xml because the project is open source and I don't want the gpg signing passphrase made public along with the rest of pom.xml. – Nick Jan 10 '13 at 03:07
  • 3
    This is in settings.xml NOT in the pom. And will work just fine for all projects including your open source one. In fact the profile should NOT be in the pom ever. – Manfred Moser Jan 10 '13 at 03:37
  • Keep in mind that profiles from settings will always be inherited into the effective pom whatever is in your pom files.. – Manfred Moser Jan 10 '13 at 07:15
  • Yeah - maintaining a settings.xml outside of VCS for the CI server is not ideal but if it works its worth the ugliness. Your second comment more or less addresses my concern, which is whether having these settings in addition to an existing default profile already defined within pom.xml would make these settings additive or if they would replace that profile completely. I'll give it a try. – Nick Jan 10 '13 at 17:15
  • Hmm spent some time getting this to work but it appears that the mvn release plugin is not using those params when it invokes gpg signing. – Nick Jan 10 '13 at 18:48
  • Yes it does. You might have to change the activation. Let me ammend it above. – Manfred Moser Jan 10 '13 at 18:56
  • FYI the answer has more details now and I know it works with Maven 3.0.4 since I use it daily.. – Manfred Moser Jan 11 '13 at 18:19
  • I've been trying to get this to work but no matter what I do I still get the prompt. running mvn help:active-profiles shows that the gpg profile is active for all modules as well... – Nick Jan 14 '13 at 02:27
  • What are they versions of Maven and the gpg plugin you are using? – Manfred Moser Jan 14 '13 at 16:45
  • 2
    Maven 3.0.3 and gpg plugin 1.4 – Nick Jan 14 '13 at 18:45
  • Hm .. it works for me with 3.0.4, pgp 1.4 on a mac with Java 6 and 7. – Manfred Moser Jan 15 '13 at 05:49
  • I think I've made some progress. Basically it looks like the release plugin is ignoring settings.xml in favor of a dynamically generated version it passes in to the forked invocation. This bug describes the behavior: http://jira.codehaus.org/browse/MRELEASE-724 -- Being that this issue only affects up to Maven 3.0.3 (the version I've got) I think there's a good chance this is why I'm having no luck. – Nick Jan 18 '13 at 05:48
  • Looks like the above referenced issue is a dupe of http://jira.codehaus.org/browse/MNG-5224 – Nick Jan 18 '13 at 05:52
  • 1
    Yep - upgrading to 3.0.4 fixed the issue! – Nick Jan 18 '13 at 17:44
  • Don't put your GPG passphrase in a file. See other answer about gpg-agent – gregw Aug 08 '14 at 07:33
  • That might be better but your home directory should already only be accessible by yourself and be on an encrypted drive. By the time that is invaded .. it doesnt matter that much anymore if it is in a file accessible via the agent. But yes.. using the agent is even more secure. – Manfred Moser Aug 09 '14 at 01:08
  • doesn't work with Maven 3.5.4 (still prompting for pwd) – Alexei Vinogradov Aug 17 '18 at 16:31
23

Having your GPG pass phrase in a file in your home directory is absolutely horrible security.

Instead, use the gpg-agent, so you only need to enter your passphrase once per session. Once installed you can setup your shell to do something like:

eval $(gpg-agent --daemon --no-grab --write-env-file $HOME/.gpg-agent-info)
export GPG_TTY=$(tty)
export GPG_AGENT_INFO

then update your plugin to enable the agent. You can do this either in the pom, or in a profile in your settings.xml may be better:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-gpg-plugin</artifactId>
  <configuration>
    <useAgent>true</useAgent>
  </configuration>
</plugin>

or it is probably better and more portable to do this in your settings:

<profile>
  <id>gpg-profile</id>
  <properties>
    <gpg.useagent>true</gpg.useagent>
  </properties>
</profile>

Then the first time in a session that the gpg passphrase is needed, a dialog is popped up. Every time after that, it uses the passphrase from the agent.

gregw
  • 2,354
  • 20
  • 21
  • 1
    This totally works and is the correct approach. Also, gpg.useagent is true by default, so I didn't have to change any plugin settings. – Shane Jun 04 '15 at 19:41
  • 2
    But does this still prompt for the pass-phrase the very first time? If so that is exactly what this question is asking to avoid because it has to work automatically in a CI environment. – D-Dᴙum Jan 24 '18 at 19:19
  • I had an interesting ride trying to figure out why my gpg stopped working. Apparently when maven forks to run the release plugin it was not getting my GPG_TTY. For whatever reason I didn't have it in .bash_profile but some other bash sourced script. So make sure the maven release fork is getting the env variable. – Adam Gent Feb 09 '23 at 21:06
7

If you don't want to have the password in clear text in your settings.xml and don't want to / can't use gpg-agent, you can setup password encryption.

You first need to setup a master password for maven (assuming maven 3.2.1+ otherwise you have to pass the password as an argument):

mvn -emp

This will return an encrypted version of the password. Store this password in ~/.m2/settings-security.xml – it should look like:

<settingsSecurity>
  <master>{inY3jdvspkeO2RUTxzQ4xHPelos+9EF1iFQyJQ=}</master>
</settingsSecurity>

Then encrypt the key password with:

mvn -ep

And use the generated encrypted password in settings.xml (the profile id needs to match the profile you use, here I have used release so you would need to run maven like mvn -P release release:prepare etc. - alternatively you can make it part of the active profiles as detailed in another answer):

<servers>
  <server>
    <id>gpg.passphrase</id>
    <passphrase>{inY3jdvspkeO2RUTxzQ4xHPelos}</passphrase>
  </server>
</servers>

<profiles>
  <profile>
    <id>release</id>
    <properties>
      <gpg.keyname>6DF60995</gpg.keyname>
    </properties>
  </profile>
</profiles>
assylias
  • 321,522
  • 82
  • 660
  • 783
3

Accepted answer did not work for me (using Maven 3.6.2). What worked for me is updating maven-gpg-plugin configuration:

<plugin>
  <artifactId>maven-gpg-plugin</artifactId>
 ....
  <configuration>
    <useAgent>true</useAgent>
    <passphrase>${env.GPG_PASSPHRASE}</passphrase>
    <gpgArguments>
      <arg>--batch</arg>
      <arg>--pinentry-mode</arg>
      <arg>loopback</arg>
    </gpgArguments>
  </configuration>
....
</plugin>
atahan
  • 706
  • 1
  • 10
  • 11
3

Combining the answers here and the plugin official documentation, i managed to get it working with an encrypted passphrase (documented here) in settings.xml.

settings.xml:

<?xml version="1.0" encoding="UTF-8"?>
 <settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd">
   <servers>
     <server>
       <id>YOUR_KEY_ID</id>
       <passphrase>YOUR_KEY_ENCRYPTED_PASSPHRASE</passphrase>
     </server>
   </servers>
   <profiles>
     <profile>
       <id>my-release</id>
       <activation>
         <!-- I run explicitly with "-Pmy-release". Change this to "true" if you don't want to. -->
         <activeByDefault>false</activeByDefault>
       </activation>
       <properties>
         <gpg.keyname>YOUR_KEY_ID</gpg.keyname>
       </properties>
     </profile>
   </profiles>
 </settings>

maven-gpg-plugin section in pom.xml:

<!-- Sign with GPG (properties are in ~/.m2/settings.xml) -->
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-gpg-plugin</artifactId>
  <version>1.6</version>
  <executions>
    <execution>
      <id>sign-artifacts</id>
      <phase>verify</phase>
      <goals>
        <goal>sign</goal>
      </goals>
      <configuration>
        <useAgent>true</useAgent>
        <keyname>${gpg.keyname}</keyname>
        <passphraseServerId>${gpg.keyname}</passphraseServerId>
        <gpgArguments>
          <arg>--batch</arg>
          <arg>--pinentry-mode</arg>
          <arg>loopback</arg>
        </gpgArguments>
      </configuration>
    </execution>
  </executions>
</plugin>

Maven version: 3.6.3

yevgenykuz
  • 51
  • 4
  • Yours was the only answer, which worked for me. Thanks a lot! – Ilya Sazonov May 01 '21 at 14:20
  • This answer is clearly the best (the extra args made it work for mac) I thought I would add that make sure the environment variable of `GPG_TTY=$(tty)` is getting set and that when maven releases plugin forks it can see it. – Adam Gent Feb 09 '23 at 21:00
1

GPG password in settings.xml is working solution, but it is open and this is bad. Alternative solution, I had used in my projects, is as follows:

stty -echo && printf "GPG password: " && read gpgPwd && printf '\n' && stty echo
mvn release:prepare -Darguments="-Dgpg.passphrase=$gpgPwd"
git push
git push --tags
mvn release:perform -Darguments="-Dgpg.passphrase=$gpgPwd"
unset gpgPwd

Additional required configurations:

export GPG_TTY=$(tty) (in the ~/.bash_profile)
maven-release-plugin/configuration/pushChanges=false (in the root pom.xml)
ursa
  • 4,404
  • 1
  • 24
  • 38
0

Configuration for maven-gpg-plugin is many time complicated and depends on native operating system feature.

Instead of fighting against all odds with maven-gpg-plugin we can use another plugin which is designed for use in CI/CD environment.

You can try https://www.simplify4u.org/sign-maven-plugin/, which is can replacement maven-gpg-plugin.

All configuration parameters for sign-maven-plugin can be delivered as environment variable.

In maven project we replace maven-gpg-plugin with:

<plugins>
    <plugin>
        <groupId>org.simplify4u.plugins</groupId>
        <artifactId>sign-maven-plugin</artifactId>
        <version><!-- check releases page --></version>
        <executions>
            <execution>
                <goals>
                    <goal>sign</goal>
                </goals>
            </execution>
        </executions>
    </plugin>
</plugins>

In CI system we setup environment variables for build:

  • SIGN_KEY - armored GPG/PGP key
  • SIGN_KEY_ID - key id in hex format - if not provided first key from SIGN_KEY will be used
  • SIGN_KEY_PASS - passphrase to decrypt private key

Also for sign-maven-plugin we needn't special profiles to activate / deactivate signing by default if configuration items are not available plugin skip execution without error.

Slawomir Jaranowski
  • 7,381
  • 3
  • 25
  • 33