This is my ninth day of the August Challenge.

“Operational thinking: operating system configuration of standardization and automation are introduced for a based on delivery of the operating system installation specification, also can’t directly used in the production environment, we need further optimization, such as the kernel parameters, time synchronization, screening tools, security reinforcement, etc., ensure the safety of the host at the same time, to avoid the problems caused by operating system configuration.

demand

Here, we optimize the operating system based on security, audit, management and other perspectives:

  1. The user

The server uses fixed users, including management users, application users, and log users. Here can be assigned according to the system user of the fortress machine to avoid user confusion.

  1. Software sources

Installing basic components requires a unified software source.

  1. Close the service

Disable useless services such as Selinux, iptables, Sendmail, and Postfix to minimize services.

  1. Initial directory

For details about how to create a fixed initial directory, such as the application, log, and backup directories, see the directory specifications in Operation and Maintenance Considerations: The Importance of Directory Management Specifications.

  1. Limit and kernel parameters

Number of file handles and processes

  1. The DNS and NTP

  2. Environment variables and historical command records

Record historical terminal timeout commands and perform remote backup

  1. SSH optimization

Root login SSH login Slow optimization Change the default port

  1. Security Settings

The password complexity and length, CTRL + Alt +delete, and other parameters can be expanded based on specific security audit requirements.

planning

From the above requirements, we can easily use Ansible PlayBook to orchestrate the implementation. But at this time please do not go to the top, we still according to the following scenario to plan:

  • For a new server, we can do all the initialization directly from the PlayBook;

  • For the old server, we just need to perform a separate optimization;

  • The Playbook must be compatible with multiple versions of Centos6 and 7.

Therefore, we need to take advantage of playBook’s Tag function and Gather_Facts to get the operating system version. Now we can classify our requirements into tasks:

vim task/main.yml
- include: user.yml  # User management
- include: repo.yml  # yum source
- include: init_pkg.yml  Install the base components
- include: profile.yml  # Environment variables
- include: selinux.yml  #selinux
- include: dir.yml  # Base directory
- include: limits.yml   # System parameters
- include: iptables.yml  # firewall
- include: sysctl.yml   Kernel parameters
- include: rc.local.yml   # Boot up
- include: dns.yml    #dns
- include: ntp.yml    #ntp
- include: rsyslog.yml  # Log synchronization
- include: sshd.yml  # SSH optimization
- include: safe.yml   # Security configuration
Copy the code

Yml, repo. Yml, zabbix_agent.yml, iptables.yml, and init_pkG. yml. You can obtain related information from gather_facts and perform the following operations:

when: ansible_distribution_major_version == "Seven"Or the when: ansible_distribution_major_version = ="6"
Copy the code

Yml, user. Yml, zabbix-agent. Yml, etc., can be tagged with a custom tag to facilitate subsequent personalized execution:

# Complete server configurationAnsible-playbook -b -e host_ip=10.10.2.10 -v os_init.ymlAdd users individually using tagAnsible-playbook -b -e host_ip=10.10.2.10 -v os_init.yml -t user# Separate security configuration via tagAnsible-playbook -b -e host_ip=10.10.2.10 -v os_init.yml -t safeCopy the code

Through the above two ways, we can achieve on-demand playBook, to meet the future of the diversity of demand optimization.

In addition, in order to facilitate customization, we also need to plan variables in advance, that is, to extract variable parameters into variables in different scenarios, so as to reduce the intrusion on Playbook. For example:

vim vars/main.yml
# zabbix_server addressZabbix_server_ip: 10.10.2.34System initialization group
grouplist:
  - name: app
    gid: 500
  - name: log
    gid: 501
# System initial user
userlist:
  - name: app
    pass: sljdoxf
    uid: 500
    groups: wheel
  - name: log
    pass: welsdjf
    groups: wheel
    
  .........
Copy the code

The specific implementation

1. Directory structure

[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 │ │ ├ ─ ─ the client. The XML │ │ ├ ─ ─ deploy_key │ │ │ └ ─ ─ Id_rsa.pub │ │ ├ ─ ─ epel7 - LAN. Repo │ │ ├ ─ ─ zabbix agent - 3.0.14-1. El7. X86_64. RPM │ │ ├ ─ ─ │ │ ├─ ├─ Heavy Metal Exercises - 1.1-1.el7.x86_64. RPM │ ├─ Heavy metal Exercises - 1.1-1.el7.x86_64. RPM │ ├─ Heavy Metal Exercises │ │ ├── Zabbix - 7.0.14-1.x86_64. RPM │ ├─ Zabbix - the sender - 2-1. El6. X86_64. RPM ├ ├ ─ ─ handlers │ │ └ ─ ─ the main, yml ├ ├ ─ ─ the tasks │ │ ├ ─ ─ dir. Yml │ │ ├ ─ ─ DNS. Yml │ │ ├ ─ ─ Init_pkg. Yml │ │ ├ ─ ─ the iptables. Yml │ │ ├ ─ ─ limits. Yml │ │ ├ ─ ─ the main, yml │ │ ├ ─ ─ the NTP. Yml │ │ ├ ─ ─ profile. Yml │ │ ├ ─ ─ Rc. Local. Yml │ │ ├ ─ ─ repo. Yml │ │ ├ ─ ─ rsyslog. Yml │ │ ├ ─ ─ safe. Yml │ │ ├ ─ ─ selinux. Yml │ │ ├ ─ ─ SSHD. Yml │ │ ├ ─ ─ Sysctl. yml │ ├── user. Yml │ ├── templates │ ├── var.confCopy the code

2. Vars variables


vim vars/main.yml

# zabbix_server addressZabbix_server_ip: 10.10.2.34System initialization group
grouplist:
  - name: app
    gid: 500
  - name: log
    gid: 501
# System initial user
userlist:
  - name: app
    pass: sljdoxf
    uid: 500
    groups: wheel
  - name: log
    pass: welsdjf
    groups: wheel

# the user key
sudo_key_user: app
deploy_key_user: log

# Software storage directory
soft_dir: /usr/local/src

# System initial unified directory
common_directory:
  - /backup/logs
  - /backup/apps
  - /data/logs
  - /app

#Centos7 basic toolcentos7_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 basic toolcentos6_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 - bashCopy the code

3. Task

(1) user. Yml

# Custom
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
Copy the code

(2) repl. Yml


# Modify local yum source
- 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 == "Seven"
  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
Copy the code

(3) init_pkg. Yml

Install the base package
- 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 == "Seven"
  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
Copy the code

(4) profile. Yml

# Environment variables
- 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'
Copy the code

(5) the 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
Copy the code

(6) dir. Yml

# Create a unified directory
- name: create directory
  file:
    path: "{{ item }}"
    state: directory
    owner: hcuser
    group: wheel
  with_items: "{{ common_directory }}"
Copy the code

(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
Copy the code

(8) the iptables. Yml

- name: stop firewalld
  service: enabled=false name=firewalld state=stopped
  when: ansible_distribution_major_version == "Seven"

- name: stop iptables
  service: enabled=false name=iptables state=stopped
  when: ansible_distribution_major_version == "6"
Copy the code

(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
Copy the code

(10) rc. Local. Yml

- name: chmod +x /etc/rc.d/rc.local
  file: path=/etc/rc.d/rc.local mode=0755
Copy the code

(11) DNS. Yml

- name:  config dns
  lineinfile:
    path: /etc/resolv.conf
    line: "{{ item }}"
  with_items:
    - "search test.com"
    - "nameserver 10.10.58.2"
Copy the code

(12) the 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
Copy the code

(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 the @ 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
Copy the code

(14) zabbix_agent. Yml

- name: copy zabbix-agent7 rpm file to server
  copy:
    src: "{{ item }}" 
    dest: "{{ soft_dir }}"
  with_items:
    - "The zabbix agent - 3.0.14-1. El7. X86_64. RPM." "
    - "Zabbix - release - 3.0-1. El7. Noarch. RPM." "
    - "Zabbix - the sender - 3.0.14-1. El7. X86_64. RPM." "
  when: ansible_distribution_major_version == "Seven"
  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 - the 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 - the sender - 3.0.14-1. El7. X86_64. RPM']
    state: present
  when: ansible_distribution_major_version == "Seven"
  tags: zabbix-agent

- name: copy zabbix-agent6 rpm file to server
  copy:
    src: "{{ item }}" 
    dest: "{{ soft_dir }}"
  with_items:
    - "The zabbix agent - 2-1. El6. X86_64. RPM." "
    - "Zabbix - release - 4.2-1. El6. Noarch. RPM." "
    - "Zabbix - the sender - 2-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 - the sender - 3.0.14-1. El7. X86_64. RPM']
    name: ['{{soft_dir}} / zabbix agent - 2-1. El6. X86_64. RPM'.'{{soft_dir}} / zabbix - release - 4.2-1. El6. Noarch. RPM'.'{{soft_dir}} / zabbix - the sender - 2-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
Copy the code

(15) safe. Yml

# prohibited 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 == "Seven"
  tags: safe

# prohibited 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

# Password expiration time
- 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
 
 # Turn off useless services
- name: os close service
  service:
    name: "{{ item }}"
    state: stopped 
    enabled: no
  with_items:
    - "httpd"
    - "postfix"
    - "sendmail"
  ignore_errors: yes
  tags: safe
Copy the code

Note: Users created before the password expiration policy are not subject to the expiration policy, but users created after the password expiration policy are. (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
Copy the code

Note: Please execute ssh.yml at the end. Due to SSH optimization, once there is a problem in the process, root cannot log in.

4. The templates templates

Kernel parameter optimizationkernel.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 = 1Copy the code

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

Copy the code

6.files

The files directory mainly stores all source files required by the task.

perform

# Complete server configurationAnsible-playbook -b -e host_ip=10.10.2.10 -v os_init.ymlAdd users individually using tagAnsible-playbook -b -e host_ip=10.10.2.10 -v os_init.yml -t user# Separate security configuration via tagAnsible-playbook -b -e host_ip=10.10.2.10 -v os_init.yml -t safeCopy the code

conclusion

Through the arrangement of Playbook, we realized the operation automation based on “Operating System Installation Specifications”, “Operating system Configuration specifications”, “Directory Management Specifications” and so on. The most important thing in this process is not the specific implementation of the PlayBook, but how we configure it purposefully according to the specification.