Not to store FTP password in Vagrantfile (Vagrant push FTP strategy) - vagrant

I am developing a small site. I use vagrant for development environment and want to use it for deploy to production. Vagrant docs says that there is Vagrant push FTP strategy.
Config:
config.push.define "ftp" do |push|
push.host = "ftp.company.com"
push.username = "username"
push.password = "password"
end
Usage:
vagrant push
It is quite enough for me, but thing that is stopping me is storing ftp host, username and password in Vagrantfile that is going to my Version Control System and it is bad practice.
Can you give any workaround for this case?

Generate hashed password and store it
openssl passwd -1 "Your_password"

I found solution using config file. Inspired with this question, I moved my sensitive data to separate file. I called it ftp.yml and added to .gitignore
ftp.yml
ftp_host: "host"
ftp_user: "username"
ftp_pass: "password"
.gitignore
ftp.yml
Vagrantfile
# loading FTP config
require 'yaml'
settings = YAML.load_file 'ftp.yml'
Vagrant.configure("2") do |config|
# vm config omitted
config.push.define "ftp" do |push|
push.host = settings['ftp_host']
push.username = settings['ftp_user']
push.password = settings['ftp_pass']
end
end
It worked fine for me.

A simple solution is use of environment variable. It has the important benefit to not store password in clear text.
Vagrantfile:
config.push.define "ftp" do |push|
push.host = "ftp.company.com"
push.username = "username"
push.password = ENV["FTP_PASSWORD"]
end
And export password in environment variable before call to vagrant:
export FTP_PASSWORD='super_secret'
vagrant push
Personally, I use 1Password command-line tool to retrieve password from my vault:
export FTP_PASSWORD=$(op get item ftp.company.com | jq '.details.fields[] | \
select(.designation=="password").value' -r)
vagrant push

Related

sed script with backslashes works in console or standalone script but not a Vagrantfile

I have this line:
sed -i 's/^\$cfg\['\''Servers'\''\]\[\$i\]\['\''AllowNoPassword'\''\] = .*/\$cfg\['\''Servers'\''\]\[\$i\]\['\''AllowNoPassword'\''\] = true;/' config.inc.php
Really simple line for editing my PHPMyAdmin config.inc.php AllowNoPassword to true (this is a dev environment of course).
It works perfectly in console but in a script file using vagrant it simply does not.
I do believe it is to do with the ' but I cannot understand what the difference is.
What is going on here and how to solve it?
Edit
Here is a complete example, minus a few bits of logic to simplify it and remove some private details etc:
# -*- mode: ruby -*-
# vi: set ft=ruby :
# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure("2") do |config|
# The most common configuration options are documented and commented below.
# For a complete reference, please see the online documentation at
# https://docs.vagrantup.com.
# Every Vagrant development environment requires a box. You can search for
# boxes at https://atlas.hashicorp.com/search.
config.vm.box = "ubuntu/xenial64"
# Disable automatic box update checking. If you disable this, then
# boxes will only be checked for updates when the user runs
# `vagrant box outdated`. This is not recommended.
# config.vm.box_check_update = false
# Create a forwarded port mapping which allows access to a specific port
# within the machine from a port on the host machine. In the example below,
# accessing "localhost:8080" will access port 80 on the guest machine.
# NOTE: This will enable public access to the opened port
# config.vm.network "forwarded_port", guest: 80, host: 8080
# Create a forwarded port mapping which allows access to a specific port
# within the machine from a port on the host machine and only allow access
# via 127.0.0.1 to disable public access
config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1"
config.vm.network "forwarded_port", guest: 443, host: 4343, host_ip: "127.0.0.1"
# Create a private network, which allows host-only access to the machine
# using a specific IP.
# config.vm.network "private_network", ip: "192.168.33.10"
# Create a public network, which generally matched to bridged network.
# Bridged networks make the machine appear as another physical device on
# your network.
# config.vm.network "public_network"
# Share an additional folder to the guest VM. The first argument is
# the path on the host to the actual folder. The second argument is
# the path on the guest to mount the folder. And the optional third
# argument is a set of non-required options.
# config.vm.synced_folder "../data", "/vagrant_data"
# Provider-specific configuration so you can fine-tune various
# backing providers for Vagrant. These expose provider-specific options.
# Example for VirtualBox:
#
# config.vm.provider "virtualbox" do |vb|
# # Display the VirtualBox GUI when booting the machine
# vb.gui = true
#
# # Customize the amount of memory on the VM:
# vb.memory = "1024"
# end
#
# View the documentation for the provider you are using for more
# information on available options.
# Define a Vagrant Push strategy for pushing to Atlas. Other push strategies
# such as FTP and Heroku are also available. See the documentation at
# https://docs.vagrantup.com/v2/push/atlas.html for more information.
# config.push.define "atlas" do |push|
# push.app = "YOUR_ATLAS_USERNAME/YOUR_APPLICATION_NAME"
# end
# Enable provisioning with a shell script. Additional provisioners such as
# Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the
# documentation for more information about their specific syntax and use.
config.vm.provision "shell", inline: <<-SHELL
add-apt-repository ppa:ondrej/php
apt-get update > /dev/null
yes | apt-get install zip
yes | apt-get install php7.1-fpm \
php7.1-curl \
php7.1-gd \
php7.1-mysql \
php7.1-mbstring \
php7.1-xml \
php7.1-mcrypt \
php7.1-soap \
php7.1-dev
# Set the right user for PHP7 ("ubuntu" user)
sed -i -e "s/www-data/ubuntu/g" /etc/php/7.1/fpm/pool.d/www.conf
# Fix error reporting so it is consistent with live server
sed -i 's/^error_reporting = .*/error_reporting = E_ALL/' /etc/php/7.1/fpm/php.ini
sed -i 's/^error_reporting = .*/error_reporting = E_ALL/' /etc/php/7.1/cli/php.ini
yes | apt-get install php-pear
pecl install xdebug
# Add xdebug to PHP runtime
echo 'zend_extension=xdebug.so' >> /etc/php/7.1/fpm/php.ini
echo 'zend_extension=xdebug.so' >> /etc/php/7.1/cli/php.ini
service php7.1-fpm restart
debconf-set-selections <<< 'mysql-server mysql-server/root_password password root'
debconf-set-selections <<< 'mysql-server mysql-server/root_password_again password root'
yes | apt-get -y install mysql-server
mysqladmin -u root -p'root' password ''
SHELL
$script = <<-SCRIPT
cp config.sample.inc.php config.inc.php
sed -i 's/^\$cfg\['\''Servers'\''\]\[\$i\]\['\''AllowNoPassword'\''\] = .*/\$cfg\['\''Servers'\''\]\[\$i\]\['\''AllowNoPassword'\''\] = true;/' config.inc.php
SCRIPT
config.vm.provision "shell", inline: $script, privileged: false
#config.vm.provision :shell, path: "bootstrap.sh"
end
Use an escaped heredoc:
$script = <<-'SCRIPT'
cp config.sample.inc.php config.inc.php
sed -i 's/^\$cfg\['\''Servers'\''\]\[\$i\]\['\''AllowNoPassword'\''\] = .*/\$cfg\['\''Servers'\''\]\[\$i\]\['\''AllowNoPassword'\''\] = true;/' config.inc.php
SCRIPT
The quotes around SCRIPT indicate to the Ruby interpreter that all contents should be literal -- taken precisely as given rather than prone to expansions. (Such quotes have the same meaning in shell heredocs as well).

Vagrant argv input on terminal complains about machine name

I am trying to pass in arguments (via known ruby methods) to my vagrant up command line, but am getting machine not found errors. What is the correct way to do this in Vagrant?
Vagrantfile
# -*- mode: ruby -*-
# vi: set ft=ruby :
# Parse options
options = {}
options[:keyfile] = ARGV[1] || false # Your Github authentication keyfile
options[:boxtype] = ARGV[2] || 'virtualbox' # Type of virtual appliance to load
options[:version] = ARGV[3] || 'latest' # Box version to load (not used currently)
ARGV.delete_at(1)
ARGV.delete_at(1)
ARGV.delete_at(1)
Vagrant.configure("2") do | config |
puts ("==> [info] Looking for #{options[:keyfile]}")
if File.file?(options[:keyfile])
config.vm.provision :shell, :inline => "echo -e '#{File.read(options[:keyfile])}' > '/home/vagrant/.ssh/GitKey'"
else
puts ("==> [error] The require RSA key: #{options[:keyfile]} does not exist, exiting.")
abort
end
end
Error
$ vagrant up ~/.ssh/github_rsa
The machine with the name '/Users/ehime/.ssh/github_rsa' was not found configured for
this Vagrant environment.
EDIT
Trying this a different way gives me some slightly more promising results
require 'optparse'
require 'ostruct'
....
options = OpenStruct.new
OptionParser.new do | opt |
opt.on('-v', '--version VERSION', 'Box version to load (not used currently)') { | o | options.version = o }
opt.on('-k', '--keyfile KEYFILE', 'Your Github authentication keyfile') { | o | options.keyfile = o }
opt.on('-b', '--boxfile BOXTYPE', 'Type of virtual appliance to load') { | o | options.boxtype = o }
end.parse!
Vagrant.configure("2") do | config |
puts ("==> [info] Looking for #{options.keyfile}")
if File.file?(options.keyfile)
config.vm.provision :shell, :inline => "echo -e '#{File.read(options.keyfile)}' > '/home/vagrant/.ssh/GitKey'"
else
puts ("==> [error] The require RSA key: #{options.keyfile} does not exist, exiting.")
abort
end
....
Gets me pretty close as well, but it needs the flags unset somehow so they don't conflict with vagrant. At least the help flag works
$ vagrant up -k /Users/ehime/.ssh/github_rsa
==> [info] Looking for /Users/ehime/.ssh/github_rsa
An invalid option was specified. The help for this command
is available below.
Usage: vagrant up [options] [name]
Options:
--[no-]provision Enable or disable provisioning
--provision-with x,y,z Enable only certain provisioners, by type.
--[no-]destroy-on-error Destroy machine if any fatal error happens (default to true)
--[no-]parallel Enable or disable parallelism if provider supports it
--provider PROVIDER Back the machine with a specific provider
-h, --help Print this help
Help
$ vagrant up -h
Usage: vagrant up [options] [name]
Options:
--[no-]provision Enable or disable provisioning
--provision-with x,y,z Enable only certain provisioners, by type.
--[no-]destroy-on-error Destroy machine if any fatal error happens (default to true)
--[no-]parallel Enable or disable parallelism if provider supports it
--provider PROVIDER Back the machine with a specific provider
-h, --help Print this help
Usage: vagrant [options]
-v, --version VERSION Box version to load (not used currently)
-k, --keyfile KEYFILE Your Github authentication keyfile
-b, --boxfile BOXTYPE Type of virtual appliance to load
The Vagrantfile is not executed directly so you cannot just pass in the arguments as you would with a normal script. vagrant looks for the file inside cwd() and bring it in.
Would go the route of the env vars or a template file which you generate before running vagrant.

Adding VM /etc/host entries that point to host machine with Vagrant and Puphpet

I know how to use vagrant-hostsupdater to add entries into the host's /etc/hosts file that point to the VM, but I'm actually trying to find a dynamic way to go the OTHER direction. On my machine, I have MySQL installed with a large db. I don't want to put this inside the VM, I need the VM to be able to access it.
I can easily set it up manually. After vagrant up, I can ssh into the VM and edit the /etc/hosts there and make an entry like hostmachine.local and point to my IP address at the time. However, as I move from home to work my host machine will change so I constantly have to update that entry.
Is there a way within an .erb file or somehow to make a vagrant up take the IP of the host machine and make such an entry in a VM hosts file?
Here's one way to do it. Since the Vagrantfile is a Ruby script, we can use a bit of logic to find the local hostname and IP address. Then we use those in a simple provisioning script which adds them to the guest /etc/hosts file.
Example Vargrantfile:
# -*- mode: ruby -*-
# vi: set ft=ruby :
# test setting the host IP address into the guest /etc/hosts
# determine host IP address - may need some other magic here
# (ref: http://stackoverflow.com/questions/5029427/ruby-get-local-ip-nix)
require 'socket'
def my_first_private_ipv4
Socket.ip_address_list.detect{|intf| intf.ipv4_private?}
end
ip = my_first_private_ipv4.ip_address()
# determine host name - may need some other magic here
hostname = `hostname`
script = <<SCRIPT
echo "#{ip} #{hostname}" | tee -a /etc/hosts
SCRIPT
VAGRANTFILE_API_VERSION = "2"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.box = "hashicorp/precise64"
config.vm.hostname = "iptest"
config.vm.provision :shell, :inline => script
config.vm.provider "virtualbox" do |vb|
# vb.gui = true
vb.name = "iptest"
vb.customize ["modifyvm", :id, "--memory", "1000"]
end
end
Note: the echo | tee -a command adding to /etc/hosts will keep appending if you provision multiple times (without destroying the VM). You might need a better solution there if you run into that.
Another possible solution is to use the vagrant-hosts plugin. Host IP can be found the same way BrianC showed in his answer.
Vagrantfile:
# -*- mode: ruby -*-
# vi: set ft=ruby :
require 'socket'
def my_first_private_ipv4
Socket.ip_address_list.detect{|intf| intf.ipv4_private?}
end
host_ip = my_first_private_ipv4.ip_address()
Vagrant.configure(2) do |config|
config.vm.define "web", primary: true do |a|
a.vm.box = "ubuntu/trusty64"
a.vm.hostname = "web.local"
a.vm.provider "virtualbox" do |vb|
vb.memory = 2048
vb.cpus = 1
end
a.vm.provision :hosts do |provisioner|
provisioner.add_host host_ip, ['host.machine']
end
end
end
Provisioner will add a row to the VM's /etc/hosts file, mapping host machine's IP address to host.machine. Running provisioner multiple times will not result in duplicate lines in /etc/hosts.
I actually found a simpler solution given my situation, running the VM on a Mac and the fact that my local.db.yml file is not part of the source code. Instead of using a name/IP I was actually just able to go to Mac's System Preferences and find my local network name for my computer, i.e. Kris-White-Mac.local
This resolves both inside and outside of the VM, so by putting that name instead of localhost or 127.0.0.1, it works even when my IP changes.

Where to store Ansible host file on Mac OS X

I am trying to get started with Ansible to provision my Vagrantbox, but I can’t figure out how to deal with host files.
According to the documentation the should be storred in /etc/ansible/hosts, but I can’t find this on my system (Mac OS X). I also seen examples where the host.ini file situated in the document root adjacent to the vagrant file.
So my question is where would you store your hostfile for setting up a single vagrant box?
While Ansible will try /etc/ansible/hosts by default, there are several ways to tell ansible where to look for an alternate inventory file :
use the -i command line switch and pass your inventory file path
add inventory = path_to_hostfile in the [defaults] section of your ~/.ansible.cfg configuration file
use export ANSIBLE_HOSTS=path_to_hostfile as suggested by DomaNitro in his answer
Now you don't mention if you want to use the ansible provisionner available in vagrant, or if you want to provision your vagrant host manually.
Let's go for the Vagrant ansible provisionner first :
Create a directory (e.g. test), and create a Vagrant file inside :
Vagrantfile:
Vagrant.configure("2") do |config|
config.vm.box = "precise64-v1.2"
config.vm.box_url = "http://files.vagrantup.com/precise64.box"
config.vm.define :webapp do |webapp|
webapp.vm.hostname = "webapp.local"
webapp.vm.network :private_network, ip: "192.168.123.2"
webapp.vm.provider "virtualbox" do |v|
v.customize ["modifyvm", :id, "--memory", 200, "--name", "vagrant-docs", "--natdnshostresolver1", "on"]
end
end
#
# Provisionning
#
config.vm.provision :ansible do |ansible|
ansible.playbook = "provision.yml"
ansible.inventory_path = "hosts"
ansible.sudo = true
#
# Use anible.tags if you want to restrict what `vagrant provision`
# Here is a list of possible tags
# ansible.tags = "foo bar"
#
# Use ansible.verbose to see detailled output for ansible runs
# ansible.verbose = 'vvv'
#
# Customize your stuff here
ansible.extra_vars = {
some_var: 42,
foo: "bar",
}
end
end
Now when you run vagrant up (or vagrant provision), Vangrant's ansible provionner will look for a file name hosts in the same directory as Vagrantfile, and will try to apply the provision.yml playbook.
You can also run it manually, without resorting to Vagrant's ansible provisionner :
ansible-playbook -i hosts provision.yml --ask-pass --sudo
Note that Vagrant+Virtualbox+Ansible trio does not always get along well. There are some versions combinations that are problematic. Try to upgrade to the latests versions if you experience issues (especially regarding network).
{shameless_plug} You can find an more extensive example mixing vagrant and ansible here {/shameless_plug}
Good luck !
If you used Brew to install Ansible, you'll most likely find the default hosts file at /usr/local/etc/ansible/hosts. But, as others pointed out, you may just want to change the default.
I like to use bash environment variables as my base project is shared with other users.
you can simply export ANSIBLE_HOSTS=/pathTo/inventory/ this can be a host file or a directory with multi files.
You can also use write it in your ~/.bash_profile so its persistent
A bunch of other variables can set that way instead of maintaining a conf file for more info check the source in ansible/lib/ansible/constants.py
Here is a description what to do after installing Ansible on Mac, it worked for me: ansible-tips-and-tricks.readthedocs.io
I downloaded the ansible.cfg file to
/Users/"yourUser"/.ansible
and afterwards you can edit the ansible.cfg file by uncommenting the
inventory = /Users/"yourUser"/.ansible
line and specifying the path to the ansible folder like shown above. You can create the hosts file in this folder then as well. To try it out locally, you can put
localhost ansible_connection=local
in the hosts file and try it out with
ansible -m ping all
If you use Vagrant's ansible provisioner, Vagrant will automatically generate an Ansible hosts file (called vagrant_ansible_inventory_default) and configure ansible-playbook to use that file. It looks like this:
# Generated by Vagrant
default ansible_ssh_host=127.0.0.1 ansible_ssh_port=2222
It calls the Vagrant host "default", so your plays should either refer to "default" or "all".
On Mac i used sudo find / -type d -name "ansible" 2> /dev/null to find it,
but i don't have .../ansible/hosts folder from the box, maybe because i installed using brew as i read above, so i created at path/etc/ansible/hosts

Testing ssh connection

an important part of my project is to log in into remote server with ssh and do something with files on it:
Net::SSH.start(#host, #username, :password => #password) do |ssh|
ssh.exec!(rename_files_on_remote_server)
end
How to test it?
I think I can have local ssh server on and check file names on it (maybe it could be in my test/spec directory).
Or maybe someone could point me better solution?
I think it's enough to test that you're sending the correct commands to the ssh server. You're application presumably doesn't implement the server - so you have to trust that the server is correctly working and tested.
If you do implement the server then you'd need to test that, but as far as the SSH stuff goes, i'd do some mocking like this (RSpec 2 syntax):
describe "SSH Access" do
let (:ssh_connection) { mock("SSH Connection") }
before (:each) do
Net::SSH.stub(:start) { ssh_connection }
end
it "should send rename commands to the connection" do
ssh_connection.should_receive(:exec!).ordered.with("expected command")
ssh_connection.should_receive(:exec!).ordered.with("next expected command")
SSHAccessClass.rename_files!
end
end
Your suggested solution is similar to how I've done it before:
Log into the local machine. For convenience you could use 'localhost' or '127.0.0.1', but for a better simulation of network activity you might want to use the full hostname. On Mac OS and Linux you can grab the host easily by using:
`hostname`
or
require 'socket'
hostname = Socket.gethostname
which should be universal.
From there create or touch a file on the local machine after logging in, so you can test for the change with your test code.

Resources