Navigation

Automagicing your Samba Dance!

In a previous blog post we looked in to the wonderful reverse engineered world of Samba. We saw the practical uses and how this piece of open source software with a hilarious and inspiring backstory can be utilised in almost any environment.

In this post we are going to get down and dirty with some code and see how we can automate the build of this paradigm.

Automate ALL the Things!

DevSecOps is all about automation, we can use the same tooling as the DevOps crowd are using and either employ them in the same way, or as this blog develops it will prove how we can use them in our own way to satisfy our goals within the environment.

Some tools that we can use to provision the required packages and configuration on to our instances are below, each will look at how we can quickly get set up and running!

Ansible

Ansible is a fun little project, using SSH keys to communicate with Linux servers and now has support for running on more modern Windows instances.

Ansible has a wide array of preconfigured roles that the community has worked on and most of the time we can use galaxy to find and reuse there roles that will do a lot of the heavy lifting for us, allowing us to work smarter and not harder.

We can create a playbook to configure samba with the following yaml:

---
- name: Install and configure Samba
  hosts: all
  become: true
  tasks:
  - name: Install the Samba package
    package:
      name: samba
      state: present

  - name: Create the Samba configuration file
    template:
      src: samba.conf.j2
      dest: /etc/samba/samba.conf

  - name: Start and enable the Samba service
    service:
      name: smbd
      state: started
      enabled: true
Of course most of the configuration would come from the samba configuration file which is in the jinja2 format:

[global]
workgroup = {{ samba_workgroup }}
server string = {{ samba_server_string }}
security = {{ samba_security }}

[{{ samba_share_name }}]
path = {{ samba_share_path }}
valid users = {{ samba_valid_users }}
read only = {{ samba_readonly }}
With our example configuration we will need to add some variables:

samba_workgroup: MYGROUP
samba_server_string: Samba Server
samba_security: user
samba_share_name: myshare
samba_share_path: /mnt/shared
samba_valid_users: user1,user2
samba_readonly: no

You can run this playbook by using the ansible-playbook command and specifying the playbook file. For example, ansible-playbook install_samba.yml

Cinc/Chef

With any given chef cookbook you can get most of the files that you will need to roll your own cookbook with:

$ chef generate cookbook samba

From this skeleton you can add to the default.rb recipe:

# samba/recipes/default.rb

package 'samba' do
  action :install
end

template '/etc/samba/samba.conf' do
  source 'samba.conf.erb'
  owner 'root'
  group 'root'
  mode '0644'
  variables(
    workgroup: 'MYGROUP',
    server_string: 'Samba Server',
    security: 'user',
    share_name: 'myshare',
    share_path: '/mnt/shared',
    valid_users: 'user1,user2',
    readonly: 'no'
  )
end

service 'smbd' do
  action [:enable, :start]
end
which provides the instruction to install the software, on to the configuration. Stored under the templates directory we can add the following, in erb this time:

# samba/templates/default/samba.conf.erb

[global]
workgroup = <%= @workgroup %>
server string = <%= @server_string %>
security = <%= @security %>

[<%= @share_name %>]
path = <%= @share_path %>
valid users = <%= @valid_users %>
read only = <%= @readonly %>
The variables are stored in the recipe (hardcoding is not great!) but in practice these could be applied to node variables or stored on the chef server alongside the deploy. If we wanted to break these out we could store them as attributes:

# samba/attributes/default.rb

default['samba']['workgroup'] = 'MYGROUP'
default['samba']['server_string'] = 'Samba Server'
default['samba']['security'] = 'user'
default['samba']['share_name'] = 'myshare'
default['samba']['share_path'] = '/mnt/shared'
default['samba']['valid_users'] = 'user1,user2'
default['samba']['readonly'] = 'no'

And update our recipe to use the variables instead. 

Puppet

Much the same with puppet we can begin our module with:

$ puppet module generate myorg-samba

to get the boiler plate code for our module, we can then create a class within our module to contain all the installation logic:

# myorg-samba/manifests/init.pp
class samba {
  package { 'samba':
    ensure => installed,
  }
  file { '/etc/samba/samba.conf':
    ensure  => file,
    content => template('myorg-samba/samba.conf.erb'),
    owner   => 'root',
    group   => 'root',
    mode    => '0644',
  }
  service { 'smbd':
    ensure => running,
    enable => true,
  }
}

and then our template which is once again in the erb format:

# myorg-samba/templates/samba.conf.erb

[global]
workgroup = <%= @workgroup %>
server string = <%= @server_string %>
security = <%= @security %>

[<%= @share_name %>]
path = <%= @share_path %>
valid users = <%= @valid_users %>
read only = <%= @readonly %>


and finally we can register our class and module either in a manifest to instantiate the instance with a .pp file or the puppet master file hold these variables and provide them to the requesting instance.

# site.pp
node 'node1' {
  include samba
  $workgroup = 'MYGROUP'
  $server_string = 'Samba Server'
  $security = 'user'
  $share_name = 'myshare'
  $share_path = '/mnt/shared'
  $valid_users = 'user1,user2'
  $readonly = 'no'
}
 

That's the server what about domain joining?

Most of the connection should happen automatically, in a Cloud Environment we will need to go from a bare Operating System to a provisioned and connected one, this is where can use the Cloud Providers "Boot Strap" mechanism that will allow us to kick off one of the above Provisioners to get our instance configured and connected into the Network.

AWS

Most of the time you will be using samba on a Linux instance, in AWS you can bootstrap your instance with userdata. With a server now automated and running you can provide the following to your AWS EC2 instance:

#!/bin/bash

# Install the necessary packages
yum install -y samba-client samba-common-tools

# Join the domain
net ads join -U Administrator -S samba.example.com

# Create a credentials file for use with the 'net' command
echo "username=Administrator" > /etc/samba/credentials
echo "password=mypassword" >> /etc/samba/credentials
chmod 600 /etc/samba/credentials

# Configure the system to use the credentials file
echo "session    required     pam_mkhomedir.so skel=/etc/skel/ umask=0022" >> /etc/pam.d/common-session
echo "session    required     pam_unix.so" >> /etc/pam.d/common-session
echo "session    optional     pam_winbind.so" >> /etc/pam.d/common-session
echo "session    required     pam_winbind.so use_first_pass" >> /etc/pam.d/common-session
echo "auth       required     pam_winbind.so use_first_pass" >> /etc/pam.d/common-auth
echo "account    required     pam_winbind.so" >> /etc/pam.d/common-account

# Configure the system to use winbind for authentication
echo "winbind use default domain = yes" >> /etc/samba/smb.conf
echo "winbind offline logon = yes" >> /etc/samba/smb.conf
echo "winbind enum users = yes" >> /etc/samba/smb.conf
echo "winbind enum groups = yes" >> /etc/samba/smb.conf

# Start and enable the winbind service
systemctl start winbind
systemctl enable winbind

Which would enable you to bind to the domain 

Azure

In Azure you have a similar concept of userdata but they call it custom script extension. Taking the above mentioned script you can save it into Azure and run the following with your instances.

# Attach the custom script extension to the VM
az vm extension set --resource-group myResourceGroup --vm-name myVm --name customScript --publisher Microsoft.Azure.Extensions --settings '{"fileUris":["https://raw.githubusercontent.com/myorg/samba-join-script/master/join_samba_domain.sh"],"commandToExecute":"sh join_samba_domain.sh"}'

GCP

GCP again is similar to AWS as you can provide this user data to the fleet on start up with a start up script, the following will create a VM in AWS that uses this data.

gcloud compute instances create my-vm \
  --project=my-project \
  --zone=us-central1-a \
  --metadata-from-file startup-script=join_samba_domain.sh

Docker/K8s

I would not suggest this as a usecase as I think this level of virtualisation is beyond identity, we should not be authenticating to containers, but for demo purposes and completeness lets make a Dockerfile.


FROM ubuntu:latest

RUN apt-get update && apt-get install -y samba-client samba-common-tools

RUN echo "username=Administrator" > /etc/samba/credentials
RUN echo "password=mypassword" >> /etc/samba/credentials
RUN chmod 600 /etc/samba/credentials

RUN echo 'session    required     pam_mkhomedir.so skel=/etc/skel/ umask=0022' >> /etc/pam.d/common-session
RUN echo 'session    required     pam_unix.so' >> /etc/pam.d/common-session
RUN echo 'session    optional     pam_winbind.so' >> /etc/pam.d/common-session
RUN echo 'session    required     pam_winbind.so use_first_pass' >> /etc/pam.d/common-session
RUN echo 'auth       required     pam_winbind.so use_first_pass' >> /etc/pam.d/common-auth
RUN echo 'account    required     pam_winbind.so' >> /etc/pam.d/common-account

RUN echo "winbind use default domain = yes" >> /etc/samba/smb.conf

Now you know how to dance the Samba!

In the previous blog we looked at the installation of Samba and it's basic configuration, now with this blog post we can automate and scale that paradigm to much larger operations. As well as this we can look into using other automatic methods of provisioning our users, we can integrate the Samba instance with PAM and use a wider array of LDAP type products to enable automated syncing of our passwds.


We can even change things up to make the passwd command redundant in favour of the smbpasswd command to update our details across the network.

Assigning permissions to these shares can be tricky when you are working with big organization with multiple groups and users. In such case you can check /etc/samba/smb.conf.example for some sample configurations or the man page of smb.conf for a clear understanding

Wrap Up!

Hopefully with the above you can see how easy it is to set up your own Domain with centralised control and how you can use it to administrate your network (home and enterprise) much easier!