ansible自动化:操作系统初始化具体实现
读完需 10 分钟
速读需 6 分钟
《运维思索:操作系统配置规范化、自动化》一文介绍了对于一台基于安装规范交付的操作系统,还不能够直接用于生产环境,我们还需要进一步的优化,如内核参数、时间同步、排查工具、安全加固等等,保证主机安全的同时、避免因操作系统配置引发的问题。
需求
在此我们将操作系统基于安全、审计、管理等角度出发,进行了一系列优化:
用户
服务器使用固定用户,主要为管理用户、应用用户、日志用户。此处可根据堡垒机的系统用户进行分配,避免用户混乱。软件源
安装基础组件需要通过统一的软件源。关闭服务
统一关闭selinux、iptables、sendmail、postfix等无用服务,保证服务的最小化。初始目录
创建固定的初始目录,如应用、日志、备份等目录,可参照《运维思索:目录管理规范的重要性》中的目录规范。limit及内核参数
文件句柄、进程数等
DNS及NTP
环境变量及历史命令记录
终端超时
历史命令记录及远程备份ssh优化
禁止root登录
ssh登录慢优化
修改默认端口安全设置
密码复杂度及长度、禁止ctrl+alt+delete等,可按照具体的安全审计要求自行扩展。
规划
从上述需求来看,我们可以轻松利用ansible playbook来编排实现。但此时请先不要上头,我们还是来按以下场景来规划下:
对于新服务器,我们可以playbook直接执行所有初始化操作;
对于老服务器,我们只需单独执行某个优化即可;
playbook需要兼容Centos6、Centos7等多个版本;
因此,我们需要利用playbook的tag功能以及gather_facts获取操作系统版本,现将我们的需求按task进行分类:
vim task/main.yml
- include: user.yml #用户管理
- include: repo.yml #yum源
- include: init_pkg.yml #安装基础组件
- include: profile.yml #环境变量
- include: selinux.yml #selinux
- include: dir.yml #基础目录
- include: limits.yml #系统参数
- include: iptables.yml #防火墙
- include: sysctl.yml #内核参数
- include: rc.local.yml #开机启动
- include: dns.yml #dns
- include: ntp.yml #ntp
- include: rsyslog.yml #日志同步
- include: sshd.yml #ssh优化
- include: safe.yml #安全配置
(1)需要区分操作系统版本号的task,如safe.yml、repo.yml、zabbix_agent.yml、iptables.yml、init_pkg.yml,可以通过gather_facts获取相关信息,并进行以下判断:
when: ansible_distribution_major_version == "7"
或
when: ansible_distribution_major_version == "6"
(2)需要区分功能的task,如safe.yml、user.yml、zabbix-agent.yml等,可以通过tag打上自定义标签,以便后续的个性化执行:
#服务器配置完全化
ansible-playbook -b -e host_ip=10.10.2.10 -v os_init.yml
#通过tag实现单独添加用户
ansible-playbook -b -e host_ip=10.10.2.10 -v os_init.yml -t user
#通过tag实现单独安全配置
ansible-playbook -b -e host_ip=10.10.2.10 -v os_init.yml -t safe
通过以上两种方式,我们就可以通过playbook实现按需订制了,满足以后的多样性需求的优化。
另外,为方便订制,我们还需要提前规划变量,即将在不同场景下将可变参数提取为变量,这样减少了对playbook的侵入,如:
vim vars/main.yml
# zabbix_server地址
zabbix_server_ip: 10.10.2.34
# 系统初始化组
grouplist:
- name: app
gid: 500
- name: log
gid: 501
# 系统初始用户
userlist:
- name: app
pass: sljdoxf
uid: 500
groups: wheel
- name: log
pass: welsdjf
groups: wheel
.........
具体实现
1.目录结构
[root@test roles]# tree os_init/
os_init.yl
roles
├───os_init/
├ ├── files
│ │ ├── ansible_key
│ │ │ └── id_rsa.pub
│ │ ├── blueking_key
│ │ │ └── id_rsa.pub
│ │ ├── CentOS6-Base-LAN.repo
│ │ ├── CentOS7-Base-LAN.repo
│ │ ├── client.xml
│ │ ├── deploy_key
│ │ │ └── id_rsa.pub
│ │ ├── epel7-LAN.repo
│ │ ├── zabbix-agent-3.0.14-1.el7.x86_64.rpm
│ │ ├── zabbix-agent-4.2.1-1.el6.x86_64.rpm
│ │ ├── zabbix-release-3.0-1.el7.noarch.rpm
│ │ ├── zabbix-release-4.2-1.el6.noarch.rpm
│ │ ├── zabbix-sender-3.0.14-1.el7.x86_64.rpm
│ │ └── zabbix-sender-4.2.1-1.el6.x86_64.rpm
├ ├── handlers
│ │ └── main.yml
├ ├── tasks
│ │ ├── dir.yml
│ │ ├── dns.yml
│ │ ├── init_pkg.yml
│ │ ├── iptables.yml
│ │ ├── limits.yml
│ │ ├── main.yml
│ │ ├── ntp.yml
│ │ ├── profile.yml
│ │ ├── rc.local.yml
│ │ ├── repo.yml
│ │ ├── rsyslog.yml
│ │ ├── safe.yml
│ │ ├── selinux.yml
│ │ ├── sshd.yml
│ │ ├── sysctl.yml
│ │ ├── user.yml
│ │ └── zabbix_agent.yml
├ ├── templates
│ │ └── sysctl.conf.j2
└ └── vars
├── main.yml
2.vars变量
vim vars/main.yml
# zabbix_server地址
zabbix_server_ip: 10.10.2.34
# 系统初始化组
grouplist:
- name: app
gid: 500
- name: log
gid: 501
# 系统初始用户
userlist:
- name: app
pass: sljdoxf
uid: 500
groups: wheel
- name: log
pass: welsdjf
groups: wheel
#用户key
sudo_key_user: app
deploy_key_user: log
#软件存放目录
soft_dir: /usr/local/src
#系统初始统一目录
common_directory:
- /backup/logs
- /backup/apps
- /data/logs
- /app
#Centos7基础工具
centos7_pkgs:
- vim
- ntpdate
- sysstat
- lrzsz
- tree
- telnet
- wget
- gzip
- zip
- unzip
- lsof
- make
- gcc
- gcc-c++
- automake
- autoconf
- libtool
- git
- openssl
- openssl-devel
- cmake
- net-tools
- psmisc
- bash-completion
- curl
- rsync
- bash
#Centos6基础工具
centos6_pkgs:
- vim
- ntpdate
- sysstat
- lrzsz
- tree
- telnet
- wget
- gzip
- zip
- unzip
- lsof
- make
- gcc
- gcc-c++
- automake
- autoconf
- libtool
- git
- openssl
- openssl-devel
- cmake
- net-tools
- curl
- rsync
- bash
3.task 任务
(1)user.yml
# 用户订制
vim user.yml
- name: add group
group:
name: "{{ item.name }}"
gid: "{{ item.gid }}"
state: present
loop: "{{ grouplist }}"
tags: user
- name: add user
user:
name: "{{ item.name }}"
password: "{{ item.pass | password_hash('sha512') }}"
uid: "{{ item.uid }}"
group: "{{ item.group }}"
groups: "{{ item.groups }}"
state: present
loop: "{{ userlist }}"
tags: user
- name: Set up multiple authorized keys
authorized_key:
user: '{{ sudo_key_user }}'
state: present
key: '{{ item }}'
with_file:
- ansible_key/id_rsa.pub
- blueking_key/id_rsa.pub
tags: user
- name: Config /etc/sudoers
lineinfile: dest=/etc/sudoers state=present line='{{item}}'
with_items:
- "{{ sudo_key_user }} ALL=(ALL) NOPASSWD: ALL"
tags: user
(2)repl.yml
# 修改本地yum源
- name: find repo file
find:
path: /etc/yum.repos.d
patterns: '*.repo'
register: repo_to_delete
- name: delete repo file
file:
path: "{{ item.path }}"
state: absent
with_items: "{{ repo_to_delete.files }}"
- name: centos7 yum.repos for lan
copy: src={{ item.src_name }} dest=/etc/yum.repos.d/{{ item.dst_name }} force=yes
with_items:
- { src_name: "CentOS7-Base-LAN.repo", dst_name: "CentOS7-Base-LAN.repo" }
- { src_name: "epel7-LAN.repo", dst_name: "epel7-LAN.repo" }
when: ansible_distribution_major_version == "7"
tags: centos7_yum_repos
- name: centos6 yum.repos for lan
copy: src={{ item.src_name }} dest=/etc/yum.repos.d/{{ item.dst_name }} force=yes
with_items:
- { src_name: "CentOS6-Base-LAN.repo", dst_name: "CentOS6-Base-LAN.repo" }
when: ansible_distribution_major_version == "6"
tags: centos6_yum_repos
(3)init_pkg.yml
# 安装基础包
- name: centos7 init packages install
yum: name={{ centos7_pkgs }} update_cache=yes
#yum: name={{ item }} update_cache=yes
#with_items: "{{ centos7_pkgs }}"
when: ansible_distribution_major_version == "7"
tags: centos7_init_package
- name: centos6 init packages install
yum: name={{ centos6_pkgs }} update_cache=yes
#yum: name={{ item }} update_cache=yes
#with_items: "{{ centos7_pkgs }}"
when: ansible_distribution_major_version == "6"
tags: centos6_init_package
(4)profile.yml
# 环境变量
- name: /etc/profile
lineinfile:
path: /etc/profile
line: "{{ item }}"
with_items:
- "readonly TMOUT=1800"
- 'export HISTTIMEFORMAT="%F %T # "'
- "export HISTSIZE=10240"
- "export PROMPT_COMMAND=\'{ msg=$(history 1 | { read x y; echo $y; });logger -p local3.notice \"[euid=$(whoami)]\":$(who am i):[`pwd`]\"$msg\"; }\'"
- 'readonly PROMPT_COMMAND'
(5)selinux.yml
- name: stop selinux
selinux: state=disabled
tags: stop_selinux
- name: disable selinux
replace: path=/etc/selinux/config regexp=^SELINUX=enforcing replace=SELINUX=disabled
tags: disable_selinux
(6)dir.yml
# 创建统一目录
- name: create directory
file:
path: "{{ item }}"
state: directory
owner: hcuser
group: wheel
with_items: "{{ common_directory }}"
(7)limit.yml
- name: change limits
lineinfile: path=/etc/security/limits.conf line={{ item }}
with_items:
- "* soft nofile 655350"
- "* hard nofile 655350"
- "* soft nproc 65535"
- "* hard nproc 65535"
tags: limits
(8)iptables.yml
- name: stop firewalld
service: enabled=false name=firewalld state=stopped
when: ansible_distribution_major_version == "7"
- name: stop iptables
service: enabled=false name=iptables state=stopped
when: ansible_distribution_major_version == "6"
(9)sysctl.yml
- name: set sysctl.conf
template:
src: sysctl.conf.j2
dest: /etc/sysctl.conf
backup: yes
tags: set_sysctl
- name: "sysctl -p"
command: sysctl -p
ignore_errors: True
tags: reload_sysctl
(10)rc.local.yml
- name: chmod +x /etc/rc.d/rc.local
file: path=/etc/rc.d/rc.local mode=0755
(11)dns.yml
- name: config dns
lineinfile:
path: /etc/resolv.conf
line: "{{ item }}"
with_items:
- "search test.com"
- "nameserver 10.10.58.2"
(12)ntp.yml
- name: set ntp
cron: name="time sync for ntp" job="/usr/sbin/ntpdate ntp.test.com && /sbin/hwclock -w" hour="*" state=present
tags: set_ntp
(13)rsyslog.yml
- name: modify rsyslog.conf
replace:
path: /etc/rsyslog.conf
regexp: 'cron.none'
replace: 'cron.none;local3.none'
notify: restart rsyslog
tags: rsyslog
- name: add rsyslog.conf
lineinfile:
path: /etc/rsyslog.conf
line: "{{ item }}"
with_items:
- "local3.notice @10.164.201.114:514"
- ':msg, contains, "zabbix" ~'
- 'authpriv.* @10.164.201.114:514'
notify: restart rsyslog
tags: rsyslog
- name: restart rsyslog
service: enabled=true name=rsyslog state=restarted
tags: restart rsyslog
(14)zabbix_agent.yml
- name: copy zabbix-agent7 rpm file to server
copy:
src: "{{ item }}"
dest: "{{ soft_dir }}"
with_items:
- "zabbix-agent-3.0.14-1.el7.x86_64.rpm"
- "zabbix-release-3.0-1.el7.noarch.rpm"
- "zabbix-sender-3.0.14-1.el7.x86_64.rpm"
when: ansible_distribution_major_version == "7"
tags: zabbix-agent
- name: install rpm
yum:
#name: ['/usr/local/src/zabbix-agent-3.0.14-1.el7.x86_64.rpm', '/usr/local/src/zabbix-release-3.0-1.el7.noarch.rpm', '/usr/local/src/zabbix-sender-3.0.14-1.el7.x86_64.rpm']
name: ['{{ soft_dir }}/zabbix-agent-3.0.14-1.el7.x86_64.rpm', '{{ soft_dir }}/zabbix-release-3.0-1.el7.noarch.rpm', '{{ soft_dir }}/zabbix-sender-3.0.14-1.el7.x86_64.rpm']
state: present
when: ansible_distribution_major_version == "7"
tags: zabbix-agent
- name: copy zabbix-agent6 rpm file to server
copy:
src: "{{ item }}"
dest: "{{ soft_dir }}"
with_items:
- "zabbix-agent-4.2.1-1.el6.x86_64.rpm"
- "zabbix-release-4.2-1.el6.noarch.rpm"
- "zabbix-sender-4.2.1-1.el6.x86_64.rpm"
when: ansible_distribution_major_version == "6"
tags: zabbix-agent
- name: install rpm
yum:
#name: ['/usr/local/src/zabbix-agent-3.0.14-1.el7.x86_64.rpm', '/usr/local/src/zabbix-release-3.0-1.el7.noarch.rpm', '/usr/local/src/zabbix-sender-3.0.14-1.el7.x86_64.rpm']
name: ['{{ soft_dir }}/zabbix-agent-4.2.1-1.el6.x86_64.rpm', '{{ soft_dir }}/zabbix-release-4.2-1.el6.noarch.rpm', '{{ soft_dir }}/zabbix-sender-4.2.1-1.el6.x86_64.rpm']
state: present
when: ansible_distribution_major_version == "6"
tags: zabbix-agent
- name: change /etc/zabbix/zabbix_agentd.conf
lineinfile: path=/etc/zabbix/zabbix_agentd.conf regexp={{ item.conf_item }} line={{ item.conf_value }} backrefs=yes
with_items:
- { conf_item: "^Server=127.0.0.1", conf_value: "Server={{ zabbix_server_ip }}" }
- { conf_item: "^ServerActive=127.0.0.1", conf_value: "ServerActive={{ zabbix_server_ip }}" }
- { conf_item: "^Hostname=Zabbix server", conf_value: "Hostname={{ host_ip }}" }
notify: restart zabbix-agent
tags: zabbix-agent
- name: restart zabbix-agent
service: name=zabbix-agent enabled=true state=restarted
tags: zabbix-agent
(15)safe.yml
# 禁止control-alt-delete
- name: Centos7 ban control-alt-delete
file:
path: /usr/lib/systemd/system/ctrl-alt-del.target
state: absent
when: ansible_distribution_major_version == "7"
tags: safe
# 禁止control-alt-delete
- name: Centos6 ban control-alt-delete
lineinfile:
path: /etc/init/control-alt-delete.conf
regexp: "start on control-alt-delete"
line: "#start on control-alt-delete"
when: ansible_distribution_major_version == "6"
tags: safe
# 密码过期时间
- name: set authentication rule
lineinfile:
path: /etc/login.defs
regexp: "{{ item.regexp_string }}"
line: "{{ item.rule }}"
with_items:
- { regexp_string: "^PASS_MAX_DAYS", rule: "PASS_MAX_DAYS 90"}
- { regexp_string: "^PASS_MIN_DAYS", rule: "PASS_MIN_DAYS 0"}
- { regexp_string: "^PASS_MIN_LEN", rule: "PASS_MIN_LEN 5"}
- { regexp_string: "^PASS_WARN_AGE", rule: "PASS_WARN_AGE 10"}
tags: safe
# 关闭无用服务
- name: os close service
service:
name: "{{ item }}"
state: stopped
enabled: no
with_items:
- "httpd"
- "postfix"
- "sendmail"
ignore_errors: yes
tags: safe
(16)ssh.yml
- name: set sshd_config
replace: path=/etc/ssh/sshd_config regexp={{ item.regexp }} replace={{ item.replace }}
with_items:
# - { regexp: "#Port 22", replace: "Port 1022" }
- { regexp: "#UseDNS yes", replace: "UseDNS no" }
- { regexp: "#PermitRootLogin yes", replace: "PermitRootLogin no" }
- { regexp: "GSSAPIAuthentication yes", replace: "GSSAPIAuthentication no" }
notify: restart sshd
tags: ssh
- name: restart sshd
service: enabled=true name=sshd state=restarted
tags: restart_sshd
注意:请最后执行ssh.yml,由于进行了ssh优化,一旦过程中有问题,会导致root无法登录。
4.templates 模板
# 内核参数优化使用
kernel.sysrq = 1
vm.swappiness = 0
kernel.threads-max = 655350
kernel.pid_max = 65535
vm.max_map_count = 131070
net.ipv4.tcp_fin_timeout = 30
net.ipv4.neigh.default.gc_stale_time=120
net.ipv4.conf.all.rp_filter=0
net.ipv4.conf.default.rp_filter=0
net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_max_tw_buckets = 5000
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 131072
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_tw_recycle = 0
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_keepalive_time = 1200
net.ipv4.tcp_keepalive_intvl = 15
net.ipv4.tcp_keepalive_probes = 3
net.ipv4.tcp_max_orphans = 65536
net.ipv4.tcp_rmem = 4096 87380 6291456
net.ipv4.tcp_wmem = 4096 16384 4194304
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1
5.handlers
- name: restart sshd
service: name=sshd state=restarted
- name: restart rsyslog
service: name=rsyslog state=restarted
- name: restart zabbix-agent
service: name=zabbix-agent state=restarted
6.files
files目录中主要存放task所有需要的源文件。
执行
#服务器配置完全化
ansible-playbook -b -e host_ip=10.10.2.10 -v os_init.yml
#通过tag实现单独添加用户
ansible-playbook -b -e host_ip=10.10.2.10 -v os_init.yml -t user
#通过tag实现单独安全配置
ansible-playbook -b -e host_ip=10.10.2.10 -v os_init.yml -t safe
总结
经过playbook的编排,我们实现了基于《操作系统安装规范》、《操作系统配置规范》、《目录管理规范》等的操作自动化。此过程中最重要的不是playbook的具体实现,而是我们怎样去根据规范去有目的的去配置。
你与世界
只差一个
公众号