mkdir -p "$WEBAPPS"/{manager,host-manager,docs,examples,ROOT}
for app in manager host-manager docs examples; do
echo "<html>Apache Tomcat/10.1.30 - $app</html>" > "$WEBAPPS/$app/index.html"
done
ls "$WEBAPPS" | sort
# manager·host-manager·docs·examples 는 운영에 필요 없고 공격 경로만 넓힙니다.
rm -rf "$WEBAPPS/manager" "$WEBAPPS/host-manager" "$WEBAPPS/docs" "$WEBAPPS/examples"
cat > "$WEBAPPS/ROOT/index.html" <<'EOF'
<html><body>서비스 준비됨</body></html>
EOF
ls "$WEBAPPS" | sort== 1. 기본 웹앱 제거 ==
설치 직후 webapps 목록:
docs
examples
host-manager
manager
ROOT
제거 후 webapps 목록:
ROOT네 개의 기본 앱을 지우고 ROOT 만 우리 서비스 페이지로 남겼습니다. 실제 서버에서도 이 절차는 DRY_RUN 과 무관하게 그대로 파일을 지웁니다.
sed -i 's/showReport="true" showServerInfo="true"/showReport="false" showServerInfo="false"/' "$XML"
sed -i 's#<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" URIEncoding="UTF-8" />#<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" URIEncoding="UTF-8" server="WebServer" />#' "$XML"
# 종료 포트는 기본 비밀번호("SHUTDOWN")를 그대로 쓰는 경우가 많아 값을 바꿉니다.
sed -i 's/shutdown="SHUTDOWN"/shutdown="8f2c1e9a-shutdown"/' "$XML"
validate "$XML"
# 더 강하게 막으려면 포트 자체를 끕니다.
sed -i 's/port="8005" shutdown=/port="-1" shutdown=/' "$XML"
validate "$XML"== 2. 버전 노출 차단과 종료 포트 보호 ==
XML 유효성 검사 통과: server.xml
7: <Valve className="org.apache.catalina.valves.ErrorReportValve" showReport="false" showServerInfo="false" />
4: <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" URIEncoding="UTF-8" server="WebServer" />
2:<Server port="8005" shutdown="8f2c1e9a-shutdown">
XML 유효성 검사 통과: server.xml
2:<Server port="-1" shutdown="8f2c1e9a-shutdown">
XML 유효성 검사 통과: server.xmlsed 로 한 줄씩 고치고 매번 XML 유효성을 검사했습니다. 마지막 줄에서 종료 포트가 -1 로 완전히 꺼진 것을 확인할 수 있습니다.
run chmod -R a-w "$BASE/tomcat-inst/$INST/conf"
run chmod -R u+w "$BASE/tomcat-inst/$INST/logs" "$BASE/tomcat-inst/$INST/temp" "$BASE/tomcat-inst/$INST/work"
run chown -R tomcat:tomcat "$BASE/tomcat-inst/$INST"
cat > "$WEBXML" <<'EOF'
<session-config>
<session-timeout>30</session-timeout>
<cookie-config>
<http-only>true</http-only>
<secure>true</secure>
</cookie-config>
</session-config>
EOF
validate "$WEBXML"== 3. 파일 권한 점검 ==
[dry] chmod -R a-w /app/tomcat-inst/app1/conf
[dry] chmod -R u+w /app/tomcat-inst/app1/logs /app/tomcat-inst/app1/temp /app/tomcat-inst/app1/work
[dry] chmod -R a-w /app/tomcat-inst/app1/webapps
[dry] chown -R tomcat:tomcat /app/tomcat-inst/app1
점검 체크리스트: conf 쓰기 금지, webapps 는 배포 계정 소유, 실행 계정은 tomcat
== 4. 세션 쿠키 보안 설정 ==
XML 유효성 검사 통과: web.xml
XML 유효성 검사 통과: context.xml
4: <session-timeout>30</session-timeout>
6: <http-only>true</http-only>
7: <secure>true</secure>권한 명령은 실제 서버 계정 모델이 있어야 뜻이 있어 흐름만 보여줍니다. web.xml·context.xml 은 항상 실제로 생성되고 검증됩니다.
run bash -c "jcmd \$(cat $PIDFILE) Thread.print > $LOGDIR/threaddump.txt"
run bash -c "jcmd \$(cat $PIDFILE) GC.heap_dump $LOGDIR/heapdump.hprof"
bash -n "$HEALTH" && echo " health_check.sh 문법 확인: 이상 없음"
run ln -sfn "$BASE/tomcat/apache-tomcat-10.1.31" "$BASE/tomcat/current"
run systemctl restart "tomcat@$INST"
run ln -sfn "$BASE/tomcat/apache-tomcat-10.1.30" "$BASE/tomcat/current"
run systemctl restart "tomcat@$INST"== 5. 스레드 덤프와 힙 덤프 ==
[dry] bash -c jcmd $(cat /app/tomcat-inst/app1/temp/catalina.pid) Thread.print > /app/logs/app1/threaddump.txt
[dry] bash -c jcmd $(cat /app/tomcat-inst/app1/temp/catalina.pid) GC.heap_dump /app/logs/app1/heapdump.hprof
스레드 덤프는 응답 지연 때, 힙 덤프는 메모리 누수 의심 때 뜹니다
힙 덤프는 -XX:+HeapDumpOnOutOfMemoryError 로 OOM 시 자동으로도 남습니다
== 6. 점검 체크리스트 스크립트 ==
health_check.sh 문법 확인: 이상 없음
== 7. 업그레이드와 롤백 ==
[dry] ln -sfn /app/tomcat/apache-tomcat-10.1.31 /app/tomcat/current
[dry] systemctl restart tomcat@app1
점검 체크리스트로 확인 후 문제가 있으면 즉시 링크를 되돌립니다
[dry] ln -sfn /app/tomcat/apache-tomcat-10.1.30 /app/tomcat/current
[dry] systemctl restart tomcat@app1
롤백은 배포와 똑같이 링크 전환 한 번으로 끝납니다jcmd 는 대상 프로세스가 실제로 떠 있어야 동작하므로 PID 파일 값을 그대로 넘기는 형태만 보였습니다. 업그레이드와 롤백은 ln -sfn 한 줄로 대칭을 이룹니다.