<?xml version="1.0" encoding="UTF-8"?><feed xmlns="http://www.w3.org/2005/Atom"><id>https://vic.demuzere.be/articles/tags/ssh/atom.xml</id><title>Vic Demuzere - SSH</title><link href="https://vic.demuzere.be/articles/tags/ssh/atom.xml" rel="self" type="application/atom+xml" /><link href="https://vic.demuzere.be/articles/tags/ssh/" rel="alternate" type="text/html" /><updated>2020-02-11T19:15:00+00:00</updated><author><name>Vic Demuzere</name></author><entry><id>https://vic.demuzere.be/articles/environment-variables-setenv-ssh-golang/</id><title>SSH environment variables in Go</title><link href="https://vic.demuzere.be/articles/environment-variables-setenv-ssh-golang/" rel="alternate" /><updated>2025-02-08T09:27:38+00:00</updated><published>2020-02-11T19:15:00+00:00</published><content type="html"><![CDATA[<p>One of the tools I'm maintaining at work is Bunka, a Go CLI application to execute commands in parallel on a list of servers.
We're setting the <code>DEBIAN_FRONTEND</code> environment variable to <code>noninteractive</code> for all our connections as we regularly use <code>apt</code> and related commands.
(If we don't, command execution may hang as a dialog is shown and the process is waiting for user input.)</p>
<p>This week someone created a bug report because their <code>apt-get</code> commands were hanging on multiple servers.
Pretty strange, as this was the exact reason we're setting environment variables in the first place. Let's investigate!</p>
<h2 id="changing-the-environment-in-go">Changing the environment in Go<a href="#changing-the-environment-in-go" aria-label="Link to heading 'Changing the environment in Go'" data-heading-content="Changing the environment in Go" class="anchor"></a></h2>
<p>To change environment variable for the currently running process you can use the <code>Setenv</code> function provided by the standard library's <code>os</code> package.
This can be useful when you use the <code>os/exec</code> package to start subprocesses.</p>
<pre><code class="language-go">func Setenv(key, value string) error 
</code></pre>
<p>The <a href="https://pkg.go.dev/golang.org/x/crypto/ssh" title="Golang x/crypto/ssh package">ssh</a> package provides a similar function that sets an environment variable on an SSH session.
These variables will be available for any command you execute on that same session after changing calling the <code>Setenv</code> function.</p>
<pre><code class="language-go">func (s *Session) Setenv(name, value string) error
</code></pre>
<p>As this &quot;simply sets a variable&quot; I was ignoring the errors returned by the <code>Setenv</code> function. After all, what could go wrong when setting a simple environment variable? I couldn't be more wrong.</p>
<h2 id="ssh-setenv-failed">ssh: setenv failed<a href="#ssh-setenv-failed" aria-label="Link to heading 'ssh: setenv failed'" data-heading-content="ssh: setenv failed" class="anchor"></a></h2>
<p>When I actually checked the error object returned by <code>Setenv</code> it turned out not to be nil. It contained the amazingly useful message below.</p>
<pre><code>ssh: setenv failed
</code></pre>
<p>Diving in the source code for the <code>ssh</code> package we can see that the request to set an environment variable is sent directly to the SSH server, there's not much in that Go code that can go wrong. Also note that it returns the same error message regardless of what went wrong when calling <code>SendRequest</code>.</p>
<pre><code class="language-go">func (s *Session) Setenv(name, value string) error {
	msg := setenvRequest{
		Name:  name,
		Value: value,
	}
	ok, err := s.ch.SendRequest(&quot;env&quot;, true, Marshal(&amp;msg))
	if err == nil &amp;&amp; !ok {
		err = errors.New(&quot;ssh: setenv failed&quot;)
	}
	return err
}
</code></pre>
<p>So let's take a step back and do it without Go:</p>
<pre><code>[vic@local ~]$ export DEBIAN_FRONTEND=noninteractive
[vic@local ~]$ ssh -o SendEnv=DEBIAN_FRONTEND vic@remote
[vic@remote ~]# echo $DEBIAN_FRONTEND
</code></pre>
<p>Nothing, it simply doesn't work.</p>
<h2 id="allowing-environment-variables-over-ssh">Allowing environment variables over SSH<a href="#allowing-environment-variables-over-ssh" aria-label="Link to heading 'Allowing environment variables over SSH'" data-heading-content="Allowing environment variables over SSH" class="anchor"></a></h2>
<p>Turns out, you can't simply change environment variables in SSH sessions. The OpenSSH server denies those requests unless you've whitelisted the specific variable in your server settings. This is for security reasons: people with access to a restricted list of commands could set an environment variable like <code>LD_PRELOAD</code> to escape their jail. (If your user has access to a shell there are other ways to set environment variables.)</p>
<p>To allow changing an environment variable over SSH, change the <code>AcceptEnv</code> setting in your <a href="https://manpages.debian.org/unstable/openssh-server/sshd_config.5.en.html#AcceptEnv_2" title="OpenSSH sshd_config man page: AcceptEnv">OpenSSH server configuration</a> (typically <code>/etc/ssh/sshd_config</code>) to accept your environment variable:</p>
<pre><code>AcceptEnv DEBIAN_FRONTEND
</code></pre>
<p>And now it magically works!</p>
<div class="footer"><hr><p>Thanks for subscribing to my feed!</p><p>Feel free to e-mail comments or corrections to <a href="mailto:vic@demuzere.be?subject=RE%3A%20SSH%20environment%20variables%20in%20Go">vic@demuzere.be</a>.</p></div>]]></content></entry><entry><id>https://vic.demuzere.be/articles/jumphosts-in-ssh-config/</id><title>Configuring OpenSSH to use a jumphost</title><link href="https://vic.demuzere.be/articles/jumphosts-in-ssh-config/" rel="alternate" /><updated>2026-05-13T17:25:03+00:00</updated><published>2018-05-17T19:00:00+00:00</published><content type="html"><![CDATA[<p>Let's say you have an important server <em>Bulbasaur</em>.
You may want to restrict access to the SSH server to a few IP addresses as an extra layer of protection.
We can then use a secure whitelisted server (jumphost or bastion) to relay our SSH connections:</p>
<pre><code>ssh -J charmeleon.mydomain.net bulbasaur.mydomain.net
</code></pre>
<p>As we're connecting to Bulbasaur a lot, we may want to tell OpenSSH that we're using charmeleon as a jumphost for bulbasaur:</p>
<pre><code># ~/.ssh/config

Host bulbasaur.mydomain.net
	ProxyJump charmeleon.mydomain.net
</code></pre>
<p>With this configuration in place, we can simply use <code>ssh bulbasaur.mydomain.net</code>. This also works for scp, rsync and everything else that uses your local SSH client.</p>
<h2 id="working-with-legacy">Working with legacy<a href="#working-with-legacy" aria-label="Link to heading 'Working with legacy'" data-heading-content="Working with legacy" class="anchor"></a></h2>
<p>On versions of OpenSSH before 7.3 the <code>ProxyJump</code> option is not available.
In that case you'll have to use an alternative configuration using the <code>ProxyCommand</code> option to specify the exact command OpenSSH has to execute to set up the tunnel.</p>
<pre><code>Host bulbasaur.mydomain.net
	ProxyCommand ssh charmeleon.mydomain.net -W %h:%p
</code></pre>
<h2 id="oops-wildcards">Oops: wildcards<a href="#oops-wildcards" aria-label="Link to heading 'Oops: wildcards'" data-heading-content="Oops: wildcards" class="anchor"></a></h2>
<p>Watch out when you're using wildcards in your ssh config:</p>
<pre><code>Host *.mydomain.net
	ProxyJump charmeleon.mydomain.net
</code></pre>
<p>Using <code>ssh server.mydomain.net</code> now causes a loop as the connection to the jumphost also matches the block and tries to use the jumphost.</p>
<p>An easy way to solve this is an alias for the jumphost:</p>
<pre><code>Host charmeleon
	HostName charmeleon.mydomain.net

Host *.mydomain.net
	ProxyJump charmeleon
</code></pre>
<div class="footer"><hr><p>Thanks for subscribing to my feed!</p><p>Feel free to e-mail comments or corrections to <a href="mailto:vic@demuzere.be?subject=RE%3A%20Configuring%20OpenSSH%20to%20use%20a%20jumphost">vic@demuzere.be</a>.</p></div>]]></content></entry></feed>